Game-specific code, at the moment this includes:
-
Player controller
-
Main function, Lua initialization, etc
-
Lua helper functions
-
SDL helper functions
-
Main files for test executables.
- event.h and event_spread.cpp
-
Implements event propagation for the old strategy game.
- main_cel.cpp
-
Tests cel shading with a rendered border around Suzanne.
- load_gltf.cpp
-
Loads multiple glTF files and renders them.
- main_fps.cpp
-
Start of an FPS game.
- main_sail.cpp
-
Main file for the sailboat game.
- redcrane_decl.h
-
C API declarations
-
Includes an abstract file cache that is used to cache .obj mesh data but can be used for other things. It isn’t meant to store any generated data, only data loaded in from the filesystem. It transparently saves and loads the file in a different format that is presumably more efficient and engine- or platform-specific.
-
Includes code to find the asset directory. It isn’t used universally which is why asset loading is sometimes a pain (make sure to run redc from the src/ directory!).
|
Note
|
TODO: Make asset loading more robust |
-
Includes small wrapper around tinygltfloader that uses our error handling (log, crash or ignore).
-
Includes a lot of code that might be used anywhere. It might even be useful in other projects.
-
Implementation of the C API
-
Not actually a library of its own, all files are just linked into redc directory. This was done because unused symbols in static libs are not linked into the final executable, so it was causing problems when LuaJIT was trying to load those symbols from the executable at runtime.
-
Packages an isolated (sorta self-sufficient I guess) rendering thing. Currently an environment map seems to best fit this category, but ocean rendering is also there.
-
Basically if it needs its own shader and has some specific way that it must be rendered, it can be packaged as an effect.
-
|
Note
|
TODO: Figure out if deferred rendering changes these invariants and what we can do about it. |
-
The Ocean effect uses the implementation of Real-time water rendering using a projected grid that is implemented in src/gfx/gfxextralib.
|
Note
|
TODO: Remove Ocean code if we aren’t going to use it. |
-
Includes helper code to manage a first person camera
-
Currently includes code to load a scene from JSON (it was used a long time ago but isn’t being compiled at the moment).
|
Note
|
TODO: Remove scene loading code. |
-
Includes a runtime abstraction of OpenGL (Mesh, Textures, Shaders, etc).
-
Includes data structures to represent all kinds of mesh representations
-
Indexed mesh data where vertices are packed
-
Indexed mesh data where vertices are not packed
-
Ordered mesh data where vertices are packed / not packed, etc
-
-
Includes code to compile a tinygltf::Scene to an Asset that can be rendered.
-
This code uses a static abstraction of OpenGL. Basically a bunch of functions. I’m trying to move in a direction towards the runtime abstraction which using the IDriver and friends.
-
-
Includes code to load and convert between mesh representations.
-
Includes functions to help initialize and populate a mesh.
-
Includes an implementation of projected grid water.
-
Includes an implementation of a software / in-memory texture and other wrappers around a real texture, such as a "texture composite."
-
Includes code to load textures from PNGs.
-
Includes data structures to represent sampled keyboard input.
-
Includes code to sample the input from the user.
-
Includes means to rebind keys.
-
Includes Lua code used in engine initialization code.
-
Includes Lua code that parses the config file and loads defaults if necessary.
-
Includes Lua code that constructs the sandbox.
-
Generally, Lua code in this folder do not run with a sandbox.
-
Helper functions, mostly around the graphics abstraction.
-
Function to load a .obj directly to a Mesh_Chunk.
-
Function to load a .png directly to a Texture
-
Implementation of a mesh cache using the Fs_Cache from src/assets.
-
Executable written in C++ (see main.cpp)
-
Initialize logger
-
Parse command line arguments
-
Initialize LuaJIT
-
Initialize ffi.C with redc_* functions using redcrane_decl.h
-
-
Load Lua config
-
Switch into that directory so that all paths are relative to the so-called Mod root, where a Mod is a bundle of Lua scripts. This term will probably mean more when Lua can do more.
-
Run Lua config file and get a table back
-
Determines (using command line arguments) whether we should run in dedicated server mode, connect mode (with a provided IP and port) and local server mode (which is default). This is mostly a relic from old multiplayer code, but I would like to keep the separation between client and server in mind.
-
-
Initialize the engine using the config (see load_engine_lua in src/minilua.cpp).
-
Run the engine loop depending on the server-mode (check out the options table in lua/init_engine.lua; specifically: client_entry and server_entry).
-
Pointer is passed to Lua and a sandbox is made containing safe Lua functions and engine functions (using the c interface loaded in a previous step).
-
The lua/redcrane.lua wrapper automatically passes the engine (located on the heap) to the redc_* C functions.
-
The wrapper also manages lifetimes of returned objects using ffi.gc, etc.
-
-
See mildly-cold/client.lua for game-specific initialization code (loading the map) and the game loop.
-
Map loading creates physics and rendering component (see src/map.h).
-
The Map is a json file with information about spawns, gravity, etc. The map asset is expected to be a glTF file (which itself is JSON). The collision mesh is loaded from a CPU bufferView that is not rendered. The name of the accessors for positions and indices are given in the map json.
-
The different components are loaded separately using events spawned by the Server. The active scene waits for a new map loaded event to compile the tinygltf::Scene to an Asset.
-
The server itself waits for this event and attaches the collision data to the bullet physics world (see Server_Event_Listener in cwrap/redcrane.cpp).
-
|
Note
|
Events are boost::variants and the visitor pattern is used to handle them, mostly. I do usually like a simple union when it’s possible, but in this case I want to be able to use non-POD types, etc. This needs some thought, etc. |
|
Note
|
Having a listener for the server when the server is the thing spawning the events is a little dubious. It would be perfectly sensible to combine the physics and scene loading code because both are the job of the server. |
-
A new player is made (see Server::req_player in cwrap/redcrane.cpp)
-
The player is actually requested. The idea is to pretend there may be some latency in the Server’s ability to make a player. This is to prepare for a networked or asynchronous implementation. A new player controller is initialized here (see player.h and player.cpp).
-
Once the player is made, an event is posted about the new player, with an ID.
-
The Scene at the moment takes a new player event to mean it should be the new active player.
-
|
Note
|
The Server right now handles physics and player management, whereby the Scene references the player and keeps the active map compiled as an Asset for quick rendering. Of course, neither the Server or Client (see cwrap/redcrane.hpp) are ready to be networked. |
-
The Scene locks the camera to the active player (when applicable) (see redc_scene_step in cwrap/scene.cpp).
-
Lua manages the game loop
-
Input is polled every step.
-
The camera is locked to the player.
-
Physics step is run (bullet calls into the player controller because of the btActionInterface).
-
The scene is rendered in redc_scene_render in cwrap/scene.cpp.
-
The environment is rendered.
-
The map is rendered (loaded previously, the scene also has the concept of an "active" map and will only hold on to one).
-
Every "mesh object" associated with the scene is rendered.
-
The crosshair is rendered.
-
See cwrap/redcrane.hpp!
Holds general stuff that doesn’t fit in either Client or Server and needs to be accessible.
Includes the config (from lua init), the share / asset path, the audio driver, a mesh cache, timers for frame time calculation, and the client and server themselves (once they are initialized).
|
Note
|
Putting the mesh cache here is a bit strange. I believe it was put there because the server needed to load the map from a .obj, and it naturally would benefit from a mesh cache. Investigate as to how it could be moved into the Client struct. The audio driver could also probably be put in the client. |
Includes things related to rendering, input, and maintains a list of scenes.
It also includes a vector of peers for mesh pointers that should be uninitialized before the OpenGL is uninitialized.
|
Note
|
The default shader is not really important anymore, so we can probably remove that. |
Includes physics, map and player related stuff. These all are things that might have to be networked in the future.
|
Note
|
How can we abstract the physics in such a way that would allow it to be networked or implemented asynchronously. |
Includes a list of objects, implemented as a variant of mesh or camera objects. An object can be referenced with an ID. Cameras can be switched by calling redc_scene_set_active_camera with another ID.
Every scene technically loads its own crosshair, but since there has only ever been one scene this hasn’t been a problem. The proper solution would be to make a good user interface library that can easily render a image / crosshair to the center of the screen.
|
Note
|
The camera switching functionality is probably broken due to the way redc_scene_step modifies the active camera when there is an active player. |
Stores all the information from the JSON (see map.cpp for the implementation of the loading code and mildly-code/Library-map.json for an example of a map).
The map includes a render component and a physics component. Which are populated by the Client_Event_Visitor and the Scene_Event_Visitor respectively. It’s important to note that the Scene_Event_Visitor does not initialize the map render component, because that would be weird if there were multiple scenes.
|
Note
|
Currently the gravity value and spawns are not being properly used. (Gravity is set by default to 9.81 m/s^2). |
Stores player state (walking, jumping, grappling, etc).
Uses the bullet physics library to do some raytraces to determine if the player is on the ground.
This is a smart-pointer template. When a single peer of any number of peers is destructed, the data is destructed with it. The idea is that if the engine holds one peer and the lua code holds another, the resource will only live as long as it needs to, but not too long. If the engine needs to uninitialize, the data will go with it. If the Lua GC decides it isn’t needed, the data can also go. But to leave the data around after the engine has been uninitialized until the lua gc decides to destruct the data can be hairy.
You’ll see this a lot in redcrane.hpp in Engine, which maintains a list of random peers for any function to use. The whole thing is documented in more detail in common/peer_ptr.hpp.
Listed in order of importance probably.
-
Implement necessary functionality for glTF code in the Driver and make the switch.
-
Separate buffers from meshes.
-
Keep state, somewhat akin to Rendering_State, in the Driver implementation.
-
Try to avoid setting state and then resetting it to what we assume was the original state. If the state hasn’t changed no OpenGL calls have to be made. Have the calling code optimize for fewer state changes.
-
-
Implement better mesh caches above the driver interface that supports LOD.
-
-
Finish entity interface and implement it
-
Move existing code to use mesh_caches and start to use texture caches.
-
Make sure the interface allows for reloading of assets on the fly for LOD magic or if the context breaks for some reason and we need to reupload literally everything.
-
This may mean rendering directly through the mesh caches. The data can be cached on the disk, cached in memory, or dropped etc, but the clients shouldn’t care.
-
Maybe it’s best to just do all these abstractions through the C API?
-
How will we manage re-uploading glTF files. Is it possible to just load the buffer and keep buffer views (or anything else we need to make sense of the data) in memory too?
-
-
Have unique filenames among directories. This way, we can’t confuse cwrap/scene.cpp and gfx/scene.cpp which are both very important but distinct files.
-
Use 2 spaces for indentation. No tabs ever!
-
Keep code under 80 columns, use spaces to align when necessary.
-
Use
This_Kind_Of_Stylefor class names,CAPITAL_SNAKE_CASEfor constants, andregular_snake_casefor everything else. -
Opening braces and end braces go on a new line.
-
End private class members with an underscore_.
Here is some example code:
int game()
{
// This is not actual engine code, just made to show a lot of constructs.
bool is_running = true;
while(is_running)
{
if(key_is_pressed(KEY_ESCAPE))
{
Player_State state = get_player_state()
switch(get_player_state())
{
case Player_State::Running:
break;
case Player_State::Jumping:
{
// Usually, I try to make these comments a full sentence, spaced as such.
break;
}
case Player_State::Grappling:
break;
}
}
}
return EXIT_FAILURE;
}
// This is fine if it works:
inline void test()
{ return game() == EXIT_SUCCESS; }-
Try not to use
autotoo much, I used it for everything in the past but I’ve found code is more readable if it’s used sparingly. I still use it for iterators which I do think improves readability. -
Try to use the IDriver graphics abstraction when possible, I’m in the midst of combining the two and putting the glTF code in terms of the driver.
-
Avoid noexcept on new functions. Remove it when refactoring functions that use it.
-
Use a header guard for new files with this form
REDC_DIR0_DIR1_FILENAME_H. Old files will just stick to the pragma until I go about switching them at some point.