left
Porting TritonForge to libgdx has been equally frustrating and rewarding, especially after so many months without writing a single line of code. But anyway, the world now renders WOOO, and lighting is now done via vertex averaging!
Game runs a lot smoother and the physics are much cleaner than they used to be thanks to libgdx delivering delta as a float. Overall I’m becoming very happy with it.
The viewport is also fully resizeable now and the in game camera expands/shrinks to accomodate it based on screen resolution (was an absolute NIGHTMARE to try and do in Slick2d before). The scale value can also be modified live without any consequences which is nice!
now to tackle the next problems…
Last update I’ll throw on this thread for a while, as to not treat it like its my own, but the libGDX port of TF is really turning out well. everything is smooth and flexible.
day/night sky and sun have been reimplemented.
block rendering masks were reimplemented.
character and animations were reimplemented.
;D
After my 3-month break I’m back with improved contact shadows, this time is an hybrid ray-march instead of a pure ray-tracer.
Still needs improvements but it’s way better than the previous one.
More tweaks to world generation have been made. Caves have a more organic form and whatnot.
Next is tweaking my lighting engine to improve performance.
Added a new part to the LibGDX tutorial series I started some time ago:
Today evening I played a bit with getting a minimalistic Java/OpenGL binding working that is capable of GraalVM native image compilation. Built a very simple glClear(), glfwSwapBuffers() example that weighs at ~8MB (main executable + single JNI shared library with GLFW and GLEW built into - will probably soon switch to GLAD). This was mainly a test that shared library loading and JNI in GraalVM native image actually works and System.loadlibrary() when in a static initializer block will apparently defer to being called at runtime.
Why GraalVM native image? => Small executable size, very small runtime footprint, very very fast startup time, no JVM dragged in the distribution/deployable (okay… the SubstrateVM is also a VM, but well… without a JIT). The only downside: GraalVM native image does not really work on Windows… (need Windows 7 SDK which cannot be installed on Windows 10…)
Next is looking deeper into LWJGL 3 and see what can be done to allow GraalVM native image compilation there.
it’d be quite interesting comparing that with a stripped down modularized java11 jdk
Today I used my gl.xml parser from LWJGLX/debug, modified it a bit so that it will spit out a JNI C file with all OpenGL functions and a Java file with the native methods counterpart. Also now using a generated GLAD loader to load OpenGL function addresses internally. Next was the problem whether native image would support reflection lookups of sun.misc.Unsafe.theUnsafe and java.nio.Buffer.address fields stored in static final fields. And indeed, it does! But only if the loading of those fields is very immediate, as in:
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.nio.Buffer;
public class MemUtil {
private static final sun.misc.Unsafe UNSAFE;
private static final long ADDRESS;
static {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
UNSAFE = (Unsafe) field.get(null);
ADDRESS = UNSAFE.objectFieldOffset(Buffer.class.getDeclaredField("address"));
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new AssertionError(e);
}
}
public static long ptr(Buffer buffer) {
return UNSAFE.getLong(buffer, ADDRESS);
}
}
Anything more complicated, like iterating over all declared fields of sun.misc.Unsafe and seeing whether one is of type sun.misc.Unsafe, makes it fail, but that’s probably because native image throws away any reflective class information other than what people are usually doing (such as direct lookup of a common field, such as “theUnsafe”).
However, there is a possibility to mark a field as being an object field offset.
Anyway, now that I had a very direct and plain Java/OpenGL binding supporting off-heap memory addresses, I was able to fill a VBO and render that via a GraalVM native-image built executable:
Wow 8MB is really low. Nice job KaiHH
Using JLink 10.8MB can be achieved for a command line interface
But for a GUI it’s 29MB according to this blog:
8MB is for the uncompressed files’ combined size. If, in addition, I tar.gz it, it’s 1,247,600 bytes.
That’s pretty incredible.
It will be interesting to see the future of what this new VM (or is it an ahead of time compiler?) can do and whether Oracle invest much into it.
Your application and all of its class/jar dependencies are ahead-of-time compiled to native code. But they still require a “runtime” to function correctly, which is linked statically into the executable. This runtime is called SubstrateVM. It contains the most basic features required by the application, such as a memory allocator with garbage collection, networking (including TLS/SSL) and threading support. It does not, however, contain a Just-In-Time compiler (which HotSpot is for OpenJDK), because… well… everything is already compiled to native code.
But the whole GraalVM Native Image hype is primarily driven by serverless (FaaS) application architectures requiring their functions/deployables to have very low cold-start times. Throughput is better with a JIT-compiled application. There have been measurements with Quarkus and Graal Native Image vs. HotSpot, and HotSpot does perform better once it is warm.
Ahead-of-Time compilation with GraalVM is all about having a small deployable (which for game development is not an important argument, I guess) and about having low startup times (which also is not an argument for game development with games out there going into the tens of gigabytes to download).
But it is an interesting thing to explore nonetheless.
On another note: Just built a maze generator and integrated that into an existing path tracing demo.
Maze generator code (produces a 2D array of bytes, where each byte is a bitfield of the possible ways to go from there into either N, S, E and/or W directions):
import static java.util.Arrays.*;
import static java.util.Collections.*;
public class MazeGenerator {
public static byte N = 1<<0, S = 1<<1, E = 1<<2, W = 1<<3;
public static byte[][] generate(int w, int h) {
return generate(new byte[w][h], w, h, 0, 0);
}
private static byte[][] generate(byte[][] maze, int w, int h, int x, int y) {
Byte[] dirs = { 0, 1, 2, 3 };
shuffle(asList(dirs));
for (int dir : dirs) {
int nx = x - (dir >> 1) * (((dir & 1) << 1) - 1);
int ny = y - ((dir >> 1) - 1) * ((dir << 1) - 1);
if (nx < 0 || ny < 0 || nx >= w || ny >= h || maze[nx][ny] != 0)
continue;
maze[x][y] |= 1 << dir;
maze[nx][ny] |= 1 << (1 - (dir & 1) + (dir >> 1 << 1));
generate(maze, w, h, nx, ny);
}
return maze;
}
}
Made a new mountain background and re-implemented parallax scrolling.
Eventually figured out it was really easy to make the mountains color change depending on the time of day as well, turned out really well.
The day before yesterday I started playing a little bit around with box2d and libgdx implementing A* pathfinding for a tiled map.
Most challenging for me was the integration between libgdx and box2d coordinates because box2d shapes have there origin centered however libgdx 0,0 is at the bottom left.
I managed to make some progress though.
Sorry for the bad gif. It takes some seconds at the beginning and ignore the weird graphic glitch on the left. That happened somehow when I converted the mp4 to the gif.
Sorry and thanks for pointing out. I fixed the link or I tried.
Redid water flow and added ores.
Also redid lighting to make it emit a circle instead of a diamond shape. (based on this solution)