SvelteKit
GitHub

About the Diamond Demo Code

Project: SteamDeck

Here is a quick whirlwind tour through the codebase. It’s BSD-3 Licensed – if you want a different license let me know – you can probably find me on linked-in or raise an issue on github. However if you wanted to use this as the basis of a tutorial, you would be better creating your own simple engine from it.

General architecture

The code is split between 'Engine' and 'Game'. The engine should be the same between games (though it is evolving) and the game is where all the game-based code goes.

The engine and game code are coupled via interface classes, which are simple classes that contain pure virtual functions. The engine expects the game code to provide classes that inherit from IScene and IGameInput, and to create 3d objects that are derived (directly or indirectly) from the abstract class Entity3d.

Game Setup

Inside main(), the following are initialised:

  • Steam
  • The OpenGL window
  • The scene to be used

The game loop is then called.

Game Loop Class

The GameLoop object contains the standard game loop. This uses a flag to determine if shadows are being used, as the scene graph has to be drawn twice when they are.

The render loop object performs the following repeatedly until the app is asked to exit:

  • Requests that the Window object (that controls the display window) initialise itself for the upcoming frame
  • Runs Steam callbacks (important if you expect SteamInput to function correctly)
  • Requests the EngineInput instance to process input
  • Requests the scene in use to perform any animation
  • If shadows are in use, it requests the scene to render from the light point of view
  • Draws the scene from the camera point of view
  • Requests the Window instance to end the render loop
  • Requests the Window instance to swap the buffers, to display the visual output

Window Class

The window class is responsible for creating the operating system window for OpenGL to use, and configuring OpenGL to use it. It works with the Render Loop instance to set up the window for each frame being rendered.

It also has callbacks for mouse, scrollwheel and keyboard events.

Game Code

The game code is primarily in two places, a Scene class and a GameInput class.

The DiamondScene class implements the interface IScene, which is used by the engine to couple with your game code for rendering and animation. The GameInput class implements IGameInput, which is used by the EngineInput instance to ask the game code to process input.

DiamondScene does not derive directly from IScene, but from ShadowedScene. This provides functionality for lightspace transforms, used for creating shadows.

When the engine wants to render a frame, it calls a DrawScene implementation, based on the IScene interface. The scene instance (here a DiamondScene instance) culls entities from the camera frustum and for each group of identical entities (due to sharing shaders), prepares the 3d entity for rendering, and then renders it. Each group of entities (with matching entity id) only needs preparing once.

This will be improved, because it is more important that grouped entities share a shader class, not an entity id.

Three-dimensional Entities

All three-dimensional entities are based on the Entiy3d virtual class. This has pure virtual functions to intialise, de-initialise, prepare for drawing, and drawing.

All the entities in this demo are based on the SelfCreatingPolyhedral class. This ensures it does not have to depend on any external mesh or mesh-loading libraries. The sphere is created the standard way, starting from a icosahedron (20 triangle sides) and splitting each triangle into four equal triangles, recursively. The vertices are then normalised to form a sphere. The diamond uses a bit of trigonometry to create its vertices.

The Entity3d objects (apart from cameras) form a sort of ‘abstract’ shape, with them being made concrete by attaching them to a SceneEntity. This object maintains the transform model for the objects, and forms part of the scene graph inside the DiamondScene class. There will only be one Diamond object (with the vertices), but there can be many diamonds in the scene by creating more SceneEntity objects using the single Diamond object.

This is true when all the diamonds match each other in terms of other properties. However, there are two spheres Entity3d objects created, one as a scene sphere (that creates shadows), and one to represent the light (which does not interact with the light source). Possibly all state should be stored in the SceneEntity, but that functionality would be implemented when it becomes necessary.

Camera

The camera is an Entity3d object, to allow it to be attached to a SceneEntity for geometric manipulation, it does not have a ‘physical’ representation in the scene graph.

It uses a perspective projection, and has methods to move it in the direction it is facing, and to look at a coordinate. When it is added to a SceneEntity instance, it can be rotated around other 3d-objects.

Limitations

The code is very much a work in progress, and is primarily used to demonstrate SteamInput. There are many limitations, some of which are listed below:

  • All frustum culling is done via bounding spheres – this is not always the best choice.
  • Large objects (larger than viewport) have not been tested against frustum culling.
  • There is a texture support class but no textures are used in the demo, and the shaders do not support them.
  • Currently there is only one light supported, a perspective-supporting omni-directional light. The class PointLight represents this light, but it is attached to a sphere SceneEntity for manipulation.
  • This light should use a cube depth map to support shadows in all directions. To save on complexity the light uses a single plane with a wide field-over-view angle.
  • Frustum culling only currently occurs for the camera, the light frustum when creating the shadow depth map does not cull.
  • The frustum of the light is currently fixed, it does not assess where the viewer is looking. This can create some artifacts when it is raised too hig.

The next article on this project is SteamInput.

© P Bentley 2023-2025. Created with svelte