Rendering

The renderer is deferred, with PBR, shadow mapping and instancing. This is one frame captured in RenderDoc.

Final image of a XEMA frame: a procedurally generated landscape with trees and houses
Albedo render targetAlbedo
Normals render targetNormals
Position render targetPosition
The geometry pass writes albedo, normals and position to three render targets, and the lighting pass reads them together with the shadow map to draw the final image.
RenderDoc event list with several glDrawElementsInstanced calls followed by the depth-only and colour passes
Draw calls from the same frame: repeated meshes are drawn with glDrawElementsInstanced, up to 107 instances in a single call.

Procedural generation

I implemented Wave Function Collapse in the engine. The landscape in the frame above comes from it.

Wave Function Collapse demo generating nine different grids (four main ones and five transition grids) across multiple threads.

Architecture

XEMA is built on an ECS architecture, with a JobSystem for work such as asynchronous mesh loading. It has Lua scripting and uses PhysX for physics. GLFW handles the window and input, and ImGui draws the debug panels for entities, components and performance.

ImGui stats panel showing FPS, frame time and the time spent in each engine section
ImGui performance panel: frame time and time per engine section.

Platforms

The platform code is chosen at compile time: each build compiles only its own platform's source files, since a build only ever runs on one platform. I first designed it with interfaces, but dropped them because they weren't needed and every call had to go through a vtable for nothing.

Nintendo Switch: tested on an emulator

I ported it through libnx and EGL.

PlayStation 4: in progress

Running on a devkit: the ECS and rendering of simple geometry. Still missing: mesh loading, textures, instancing, lights, shadows and PBR.