Helpful Fork [#3] to build using premake. Thanks to https://github.com/ignite720
- Visual Studio 2022 17.10
- Blender 3.4
DirectXTK (directxtk_desktop_win10) ver. 2021.8.2.1 - https://github.com/microsoft/DirectXTK
-
Physx ver 5.3.0 | Place PhysX headers/source into Code/Physx - https://github.com/NVIDIAGameWorks/PhysX (Easier to use https://vcpkg.io/en/index.html to download SDK [package name is physx:x64-windows])
-
FBXSDK ver. 2020.1 | Place FBXSDK headers/source into Code/FbxSdk - https://www.autodesk.com/developer-network/platform-technologies/fbx-sdk-2020-1
-
DirectXMath | Spherical Harmonics (included in repo) https://github.com/microsoft/DirectXMath/tree/main/SHMath
-
Microsoft GameInput https://www.nuget.org/packages/Microsoft.GameInput
-
Qt ver. 5.15.0 - https://download.qt.io/official_releases/qt/5.15/
(The ImGui libs are already included in the repo)
- Dear ImGui - https://github.com/ocornut/imgui
- ImGuizmo - https://github.com/CedricGuillemet/ImGuizmo
- Qt VS Tools ver. 2.10.0 - https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022
- HLSL Tools for Visual Studio (Now an official part of Visual Studio) - https://marketplace.visualstudio.com/items?itemName=TimGJones.HLSLToolsforVisualStudio
The techincal outset of the engine was to keep it simple and mostly in C++ with minimal use of other programming languages. That means usage of the C++ standard library and attempted usage of modern C++ concepts.
Below is a quick outline of various concepts the engine uses, from game object component models, rendering and asset workflows.
The engine uses an Actor/Component model similar to Unity and Unreal. The inspiration came from Rare's presentation on Actor Ticks in UE4 in Sea of Thieves
An inherited actor will be defined with a system via a macro.
struct Enemy : Actor
{
//essentially defines 'inline static ActorSystem<Enemy> system;'
ACTOR_SYSTEM(Enemy);
void Tick(float deltaTime) override;
};
Both Actors and Components have Systems. A system essentially looks like:
template <typename ActorType>
struct ActorSystem : IActorSystem
{
std::vector<ActorType*> actors;
void Tick(float deltaTime) override
{
for(auto actor : actors)
{
actor->Tick(deltaTime);
}
}
}
A 'World' is the current view of the scene on-screen. Each World will have lists of the currently active Actor and Component systems.
struct World
{
std::vector<IActorSystem*> actorSystems;
std::vector<IComponentSystem*> componentSystems;
void Tick(float deltaTime)
{
TickAllActorSystems(deltaTime);
TickAllComponentSystems(deltaTime)
}
}
For serialisiation, Actors and Components define their properties to be fetched at run-time.
struct Enemy : Actor
{
int attack = 5;
Properties GetProps() override
{
Properties props = Actor::GetProps();
props.Add<int>("Attack", &attack);
return props;
}
}
Properties
is a collection of Property
s. VEngine uses the typeid()
from Properties::Add<T>()
to infer the type.
struct Property
{
std::optional<std::type_info> type;
void* value;
}
struct Properties
{
std::map<std::string, Property> propertyMap;
template <typename T>
void Add(std::string propertyName, T* propertyValue)
{
Property property;
property.type = typeid(T);
property.value = propertyValue;
propertyMap.emplace(propertyName, property);
}
}
Using Properties
, serialisation infers property types by checking for a matching typeid()
.
void Serialiser::Serialise(Properties& properties)
{
for (Property& property : properties)
{
if (property.type == typeid(int))
{
SerialiseInt<int>(property.value);
}
}
}
Files with the .vmap
extension describe the layout of a level in-game.
Below is an example of the text format to show how properties map to values.
Enemy //This type name fetches the appropriate system (in this case an Actor class of 'Enemy').
13 //This is how many instances of the class to create.
Attack //Property name.
16 //Property value.
next //Move onto the next Enemy instance...
Attack
3
VEngine uses DirectX 11 for 3D/2D rendering and DirectWrite + Direct2D for in-game UI.
VEngine uses the official FBX SDK to import models and animations. While not very robust, details can be gleamed at
For in-game UI, an immediate approach is used. An example in-game widget would be declared like so:
struct EnemyText : Widget
{
std::string text;
void Draw() override
{
Layout layout = AlignLayoutByScreenPercent(0.1, 0.9, 0.1, 0.2);
DrawText(text, layout);
}
}
Implementations were inspired by EA's Frostbite engine.
VEngine uses a simple Global Illumination technique using light probes spread around a level uniformly. Leveraging the DirectXSH spherical harmonics library, each probe takes a cubemap snapshot of its surroundings an encodes it using spherical harmonics. Static meshes are assigned an index into the light probe map built up, while dynamic meshes find their closest probe and apply its colours per vertex normal.
References for this system were mainly taken from Bluepoint's Shadow of the Colossus and Sonic Unleashed.
- https://gdcvault.com/play/1027011/Advanced-Graphics-Summit-Lifting-the
- https://www.gdcvault.com/play/1428/Global-Illumination-in-SONIC
To import skeletal .fbx animations via Blender:
- Add an Armature and bone structure to a mesh in Blender
- hit Ctrl+P to parent the mesh to the armature and automate the weights
- Record the poses using the Timeline and Dope Sheet -> Action Editor windows
- Use the Non-Linear Animation window to organise animations
- Export as an FBX with these settings, including all animations
Note that a seperate .vmesh file of the mesh is needed without animations baked in (but with the same bone structure as the animations), assigned as the mesh of a SkeletalMeshComponent, where then the imported FBX animations are linked to.
For example:
class AnimCube : public Actor
{
public:
AnimCube()
{
skeletalMesh = CreateComponent("Skeleton", SkeletalMeshComponent("anim_cube.vmesh", "texture jpg"));
rootComponent = skeletalMesh;
}
void Start() override
{
skeletalMesh->PlayAnimation("move");
}
private:
SkeletalMeshComponent* skeletalMesh = nullptr;
}
Below is a collection of the most influential articles, tutorials, talks and concepts for development of the engine over time.
- https://www.unrealengine.com/en-US/events/unreal-fest-europe-2019/aggregating-ticks-to-manage-scale-in-sea-of-thieves
- https://www.youtube.com/watch?v=jjEsB611kxs
- https://therealmjp.github.io/posts sg-series-part-1-a-brief-and-incomplete-history-of-baked-lighting-representations/
- https://therealmjp.github.io/posts/radiosity-dx11-style/
- https://morgan3d.github.io/articles/2019-04-01-ddgi/overview.html
- https://www.gdcvault.com/play/1428/Global-Illumination-in-SONIC
- https://chetanjags.wordpress.com/2015/08/26/image-based-lighting/
- https://www.flipcode.com/archives/Light_Mapping_Theory_and_Implementation.shtml
- https://interplayoflight.wordpress.com/2021/12/28/notes-on-occlusion-and-directionality-in-image-based-lighting/
- https://help.autodesk.com/view/FBX/2020/ENU/
- https://www.gamedev.net/tutorials/_/technical/graphics-programming-and-theory/how-to-work-with-fbx-sdk-r3582/
- https://gdcvault.com/play/1023146/Math-for-Game-Programmers-Juicing
- https://asliceofrendering.com/camera/2019/11/30/ArcballCamera/
- https://blog.demofox.org/2012/09/21/anatomy-of-a-skeletal-animation-system-part-1/
- https://www.zuluonezero.net/2021/11/16/exporting-multiple-animations-from-blender-to-unity/
- https://itecnote.com/tecnote/c-algorithm-or-software-for-slicing-a-mesh/
- https://gdcvault.com/play/1026882/How-to-Dissect-an-Exploding
- http://simonschreibt.de/gat/metal-gear-rising-slicing/
- https://gdcvault.com/play/1013446/The-Neverwinter-Nights-2-Toolset
- https://gdcvault.com/play/1025962/Technical-Tools-for-Authoring-Branching