This is the write-up for Assignment 03 of GAMES 6320 (Game Engineering II). Assignment 02 introduced cMesh and cEffect but still had platform-specific Graphics.d3d.cpp and Graphics.gl.cpp files. Assignment 03 removes those two files completely by moving the remaining platform-specific frame work behind a small sContext abstraction. It also adds uint16_t index buffers to cMesh, makes both cMesh and cEffect initialize from caller-provided data, and renders two objects with two effects.
Download
Release build (Direct3D x64): MyGame_Assignment03_Release_x64.zip
The ZIP contains a Direct3D Release build (
x64). Unzip it and runMyGame.exe.
Controls: none for this assignment (pressEscto close the window).
What changed
Assignment 03 removes the last two duplicated graphics frame files (Graphics.d3d.cpp and Graphics.gl.cpp) and replaces them with a single Graphics.cpp that contains no platform-specific macros or API calls.
The project now has:
- one platform-independent
Graphics.cpp; - indexed meshes using
uint16_tindices; - effect initialization driven by caller-provided data;
- mesh initialization driven by caller-provided vertex/index data;
- two meshes and two effects, drawn with two distinct colors;
- a configurable clear color instead of hard-coded black.
Platform-independent Graphics.cpp
New interfaces
I added the following platform-independent interfaces:
| Interface | Declared in | Platform implementation |
|---|---|---|
Graphics::sColor and Graphics::SubmitClearColor() |
Engine/Graphics/Graphics.h / Graphics.cpp |
shared |
sContext::InitializeViews() |
Engine/Graphics/sContext.h |
Direct3D/sContext.d3d.cpp, OpenGL/sContext.gl.cpp |
sContext::ClearBackBuffer() |
Engine/Graphics/sContext.h |
Direct3D/sContext.d3d.cpp, OpenGL/sContext.gl.cpp |
sContext::Present() |
Engine/Graphics/sContext.h |
Direct3D/sContext.d3d.cpp, OpenGL/sContext.gl.cpp |
sContext::UsesLeftHandedWinding() |
Engine/Graphics/sContext.h |
Direct3D/sContext.d3d.cpp, OpenGL/sContext.gl.cpp |
The Direct3D render-target/depth views also moved out of Graphics.d3d.cpp and into Direct3D/sContext.d3d.cpp. OpenGL simply implements the same interface with glClearColor(), glClear(), and SwapBuffers().
Clear color
Graphics.cpp specifies the background color and submits it through the new platform-independent interface:
|
At render time, the code in Graphics.cpp uses only the context interface:
|
Changing s_clearColor to { 1, 0, 0, 1 }, { 0, 1, 0, 1 }, or { 0, 0, 1, 1 } changes the background to red, green, or blue respectively.
Effect initialization with data
The effect interface no longer hard-codes shader paths:
|
The caller must provide:
- a path to a vertex shader;
- a path to a fragment shader;
- the render-state bits for this effect.
These values are used only during initialization. cEffect stores the loaded shader objects and render-state object, not the paths or the raw input arrays.
This is the relevant code in Graphics.cpp:
|
Effect memory
I measured these values with a small sizeof() program compiled against the same headers and platform macros.
| Object | Direct3D x64 | OpenGL x86 |
|---|---|---|
cEffect |
48 bytes | 16 bytes |
cRenderState |
32 bytes | 1 byte |
On Direct3D, cEffect stores two shader pointers and a cRenderState. cRenderState stores three Direct3D state-object pointers plus one byte of render-state bits. On OpenGL, cRenderState needs only the one render-state byte; cEffect stores two shader pointers, that byte, and the OpenGL program ID.
The values cannot easily be smaller because the active graphics API requires either COM state objects (Direct3D) or an OpenGL program ID and state bits. Shader paths are deliberately not stored after initialization.
Mesh initialization with data and indexed rendering
The mesh interface now accepts vertex and index data:
|
The caller must provide:
- vertex data (
x,y,z); - the number of vertices;
uint16_tindex data;- the number of indices.
The public mesh API uses right-handed winding order. cMesh.cpp asks the active platform whether it expects left-handed winding:
|
Direct3D/sContext.d3d.cpp returns true; OpenGL/sContext.gl.cpp returns false. Therefore the same vertex/index data can be used regardless of platform.
This is the relevant code in Graphics.cpp:
|
Mesh memory
| Object | Direct3D x64 | OpenGL x86 |
|---|---|---|
cMesh |
32 bytes | 20 bytes |
On Direct3D, cMesh stores a vertex-format pointer, a vertex-buffer pointer, an index-buffer pointer, and two counts. On OpenGL, it stores a VAO ID, a VBO ID, an IBO ID, and two counts. The vertex/index arrays themselves are copied into GPU buffers and are not stored on the CPU after initialization.
Two objects and two effects
Graphics.cpp creates:
- a rectangle using the animated-color fragment shader;
- a triangle using the standard white fragment shader.
Per frame:
|
The two objects occupy different areas of the screen, so both are clearly visible.
Screenshots
Direct3D Debug (x64)

OpenGL Debug (x86)

Both screenshots show two distinct objects on a non-black blue background.
Build / test
All four configurations were built cleanly with warnings treated as errors:
Debug|x64(Direct3D)Release|x64(Direct3D)Debug|x86(OpenGL)Release|x86(OpenGL)
The solution targets PlatformToolset=v143. The Visual Studio 2026 installation now has the v143 MSVC component (Microsoft.VisualStudio.Component.VC.14.44.17.14.x86.x64) installed, so the builds above were run without any command-line PlatformToolset override.
The downloadable ZIP is the Direct3D Release build.
Reflection
The important design choice was to stop treating the graphics context as raw API data and start treating it as an abstraction boundary. Graphics.cpp now sees only InitializeViews(), ClearBackBuffer(), Present(), and the winding-order query. That is enough to remove both platform-specific frame files completely.
Moving winding-order conversion into cMesh.cpp keeps the public mesh interface simple. Callers provide indices once using one documented convention; the engine performs the tiny per-triangle swap only for the platform that needs it. This is intentionally simple and is exactly the kind of runtime conversion that can later move to the asset-build step.