RTS Game Engine

Project Stats:

Name: RTS game engine
Project type: Student project
Production Time: 8 weeks
Date: 2017/09-2017/11
Engine/language: C++
Platforms: PC
Tools: Visual Studio, Perforce, Jira
Team members: 4 programming students

Project Description

Creating an RTS game engine.

My contributions

  • Model manager
    • Loading models using Assimp
    • Making it possible to pick a model in the editor
  • Editor
    • Imgui window base class
    • Saving the state of Imgui windows
    • Entity editor window using Imgui
    • View entities in the scene by clicking its associated button in the editor
    • Ray casting for selecting entities in the editor
    • Drawing a box around a selected object
    • Camera movement in de editor
  • Fmod
    • Implementing a soundmanager with Fmod
    • Soundmanager editor window using Imgui

Model manager

The model manager is a data-driven system responsible for loading models. It stores data for loading models and textures in an XML file using cereal.

In the video, the window on the left is the entity editor and the window on the right is the model manager.

The model manager enables you to edit the model and texture paths and load them the next time the editor starts.

Editor

On the right you can see a screenshot of 5 Imgui windows that I worked on. The camera window, raycast system window, sound manager window, step timer window, and the entity editor window.

Entity editor window

the entity editor window. It can change the scale rotation and position of an entity.

Some more features of the entity editor window:

  • Selecting multiple objects
  • Editing the model/textures of an object
  • Drawing a box around selected entities
  • Highlighting the selected model in the editor

Ray AABB intersection

I started with a per triangle intersection to select models in the editor and in the game. This was way too slow so it needed optimizations. Because of time constraints, I scraped it. Instead of optimizing the triangle ray casting I implemented a simple ray box intersection.

This is the code I use for ray box casting:

Code snippets - BoxRayCast function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
bool RayCastSystem::BoxRayCast(
    const DirectX::XMFLOAT3 a_cubeMax
    , const DirectX::XMFLOAT3 a_cubeMin
    , const DirectX::XMFLOAT3 a_origin
    , const DirectX::XMFLOAT3 a_directionNormalized
    , float * out)
{
    // r.dir is unit direction vector of ray
    DirectX::XMFLOAT3 dirFrac = DirectX::XMFLOAT3();
    dirFrac.x = 1.0f / a_directionNormalized.x;
    dirFrac.y = 1.0f / a_directionNormalized.y;
    dirFrac.z = 1.0f / a_directionNormalized.z;
    // lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
    // r.org is origin of ray
    float t1 = (a_cubeMin.x - a_origin.x) * dirFrac.x;
    float t2 = (a_cubeMax.x - a_origin.x) * dirFrac.x;
    float t3 = (a_cubeMin.y - a_origin.y) * dirFrac.y;
    float t4 = (a_cubeMax.y - a_origin.y) * dirFrac.y;
    float t5 = (a_cubeMin.z - a_origin.z) * dirFrac.z;
    float t6 = (a_cubeMax.z - a_origin.z) * dirFrac.z;

    float tmin = max(max(min(t1, t2), min(t3, t4)), min(t5, t6));
    float tmax = min(min(max(t1, t2), max(t3, t4)), max(t5, t6));

    // if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us
    if (tmax < 0)
    {
        * out = tmax;
        return false;
    }

    // if tmin > tmax, ray doesn't intersect AABB
    if (tmin > tmax)
    {
        * out = tmax;
        return false;
    }

    * out = tmin;
    return true;
};

This is the code I used to create a ray:

Code snippets - ScreenPointToRay function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// x and y from -1 to 1
void Camera::ScreenPointToRay(float a_mouseX, float a_mouseY, XMVECTOR &a_rayorigin, XMVECTOR &a_rayEnd, XMVECTOR &a_unitRayDir)
{
		// camera model matrix
		float pi2 = XMVectorGetX(g_XMTwoPi);
		XMMATRIX rotXMat = XMMatrixRotationX(m_cameraRotation.x / 360.0f*pi2);
		XMMATRIX rotYMat = XMMatrixRotationY(m_cameraRotation.y / 360.0f*pi2);
		XMMATRIX translMat = XMMatrixTranslation(m_cameraPosition.x, m_cameraPosition.y, m_cameraPosition.z);
		XMMATRIX cameraModelMat = rotXMat * rotYMat * translMat;

		// camera mouse ray
		float ratio             = 16.0f / 9.0f;
		float tanFov            = tan(((m_fov / 2.0f) * 180.0f / PI));
		float halfNearZWidth    = 0.5f * m_nearZ * tanFov;
		float halfFarZWidth     = 0.5f * m_farZ * tanFov;
		m_mouseRayOrgin.z       = m_nearZ;
		m_mouseRayEnd.z         = m_farZ;
		m_mouseRayOrgin.x       = -halfNearZWidth* a_mouseX * ratio;
		m_mouseRayEnd.x         = -halfFarZWidth * a_mouseX * ratio;
		m_mouseRayOrgin.y       = -halfNearZWidth * a_mouseY;
		m_mouseRayEnd.y         = -halfFarZWidth * a_mouseY;
		a_rayorigin             = XMLoadFloat3(&m_mouseRayOrgin);
		a_rayEnd                = XMLoadFloat3(&m_mouseRayEnd);
		a_unitRayDir            = XMVector3Normalize(a_rayorigin);

		// rotate direction ray to camera rotation
		a_unitRayDir            = XMVector3Transform(a_unitRayDir, rotXMat * rotYMat);
		// transform ray orgin and end with camera model matrix
		a_rayorigin             = XMVector3Transform(a_rayorigin, cameraModelMat);
		a_rayEnd                = XMVector3Transform(a_rayEnd, cameraModelMat);
}

Bar and windows

To make accessing windows more efficient in the editor I added an EditorBar class. It takes care of handling the open/closed state of windows and saving the open/closed state.

Code from Engine.cpp that adds windows to the editor bar.

// add windows to editor bar
editorBar.AddWindow(&level);
editorBar.AddWindow(&entityEditor);
editorBar.AddWindow(m_camera);
editorBar.AddWindow(&m_Timer);
editorBar.AddWindow(&m_rayCastSystem);
editorBar.AddWindow(m_game);

Code from EditorBar.h

class EditorBar : public EditorWindow
{
public:
    EditorBar();
    ~EditorBar();
    void AddWindow(EditorWindow* a_window);
    virtual std::string WindowName()    override;
    virtual void        DisplayWindow() override;
    void LoadWindowsState();
private:
    void DisplayAttachedWindows();
    void SaveWindowsState();
    EntityManager *m_entityManager;
    std::vector<EditorWindow*> m_windows;
};

This is Code from EditorWindow.h. It is a pure virtual base class for editor windows that makes it easy to make anything an editor window and add it to the EditorBar.

class EditorWindow
{
public:
    EditorWindow();
    ~EditorWindow();

    virtual bool* IsOpen();
    void IsOpen(bool a_IsOpen);
    virtual std::string WindowName() = 0;
    virtual void DisplayWindow() = 0;
protected:
    bool m_windowOpen;
};

Loading and saving window states

I added a load and save window state button to the WindowBar that saves the open or closed state of windows. So you don’t have to reopen all the windows if you restart the engine.

Fmod

SoundManager editor

I implemented fmod and used it to create a sound manager. The sound manager window can create a list of sounds and adjust the pitches and volumes of the sounds. When the save state button is pressed the list of sounds gets saved.