top of page
Game Engine Tools


The PrimeEngine is a C++ based game engine developed by Artem Kovalovs, a principal programmer at Naughty Dog. While it already supported basic rendering and movement, the tools and systems listed below are wholly original additions, written by me directly into the engine's core code.
Frustum Culling
This system optimizes performance by not rendering any meshes outside of the camera frustum. For the sake of visual clarity, meshes in this demo video are culled as soon as any vertex of theirs is outside of the frustum, rather than the whole mesh being outside.
It works by first generating a frustum. The height of the far and near planes are found by multiplying the aspect ratio by each of their known widths. The distance from the camera to the near and far planes can be found with basic trig. Once we have all of that, we can find the positions of the eight plane corners in world space by translating the camera's world position to them.
For each plane of the frustum, we can subtract two perpendicular pairs of corner positions to get two vectors whose span represents the plane. By taking the cross product of these vectors (and normalizing it!), we find the plane's normal. We then represent each of the six planes of with a position vector for a point on the plane, and a normal vector.
For meshes, code was added to store the extreme points along each axis. In a draw call, each of the eight extreme points of a mesh instance in world space are checked against the frustum planes for intersection. In a production project, this would cull meshes only if all extreme points lied outside the frustum, but for the sake of this demo they're culled when any one extreme point is outside of it.
Collision Physics
Initially, this engine supported no physics whatsoever. I developed a system to detect and manage collisions between dynamic and static meshes.
I wrote a PhysicsComponent class, which stores important information for physics and collision for each mesh. Static meshes are given box colliders, so their components store six plane objects. Dynamic meshes have sphere colliders, so their components store a center and radius. Upon creation, PhysicsComponent objects add pointers of themselves to the static PhysicsManager class, giving it direct access to all objects with physics.
Dynamic objects have movement state machines, which normally calculate and update their locations as they move. Here, instead, the state machine calculates the new location (not considering collision), then sends it to the PhysicsManager. The manager checks to see if this new position will collide with any static objects, and if it does, calculates and returns the proper new position.
To check for collisions, the dynamic object is compared to every other static object. For each comparison, the center of the sphere collider is extended the length of the radius, in each direction orthogonal to the planes of the box collider. If this results in an intersection with every plane, part of the sphere is colliding with the box, and a collision is happening.
If a collision is happening, it's determined which plane(s) the sphere is colliding with, and their normals are used to find the direction the dynamic object should be moving to properly 'slide' along the static object.
Dynamic Mesh Deformation
This system modifies shader and GPU pipeline code to allow for dynamic mesh deformation, something akin to 'wind' blowing meshes around.
To allow different parts of a mesh to be deformed differently, each mesh instance is given a new buffer that assigns wind values to each vertex. This buffer is passed into the appropriate vertex shader in the Direct3D code. In this demo, vertices higher off the ground are meant to be 'blown' more, so wind values are set equal to y-coordinates in world space, but this isn't inherent to the system.
Similar to light, 'wind' is controlled by a set of wind sources stored in the scene graph's root node. In this demo, two wind sources are attached to the soldier NPCs, and one is attached to the camera itself. The array of sources in the root node is updated every frame, when the position of the soldiers and camera are updated. Once updated, it's bound to the Direct3D pipeline as a constant buffer, available to both pixel and vertex shaders.
There are two types of wind sources: 'point wind' and 'directional wind'. Point wind sources deform meshes away from themselves, and have a stronger effect as they get closer to a susceptible mesh. Directional wind sources deform meshes away from the direction specified with a constant amount of force, regardless of distance.
In this demo, the NPCs each have a point source attached to them. Meshes deform away from them, more strongly as they get closer. The camera has a directional source, and thus deforms meshes away in the direction of the camera's forward vector.
Deformation occurs in the vertex shader, while special coloring is in the pixel shader. For this demo, wind-affected objects are drawn with a green gradient, that gets closer to black with lower wind values. This visually demonstrates the scheme of greater deformation with higher positions.
Navigation Mesh
I developed a basic navmesh system that allows for intelligent traversal by NPCs. It's managed primarily by a static NavMesh class. This class holds an array of NavCell objects, whose edges are maintained in an adjacency list. Given a start and an end cell, NavMesh can be called to perform an A* graph traversal of its adjacency list. This finds an optimal path from one cell to the other, and returns the next cell on the path from the start cell.
I modified the NPC behavior state machine to run movement through the NavMesh, rather than deciding it itself. In the current system, NPCs are spawned with an intended start cell and end cell. They move toward their intended start cell from wherever they spawn, then begin traversing the navmesh. Each time an NPC reaches a cell, it asks NavMesh to find the optimal path from its current cell to its desired end, then traveling to the next cell in this optimal path. NPCs either stop when they reach their destination, or continue cycling back and forth between their two cells.
The NavMesh is capable of blocking and unblocking cells. When a cell is blocked, it's inaccessible in any further graph searches until unblocked. This was used to implement dynamic obstacle avoidance. When an NPC is moving from one cell to another, they block both of these cells, rendering them inaccessible to others. Without the need for collision managers or direct NPC to NPC communication, this system allows for smarter pathfinding with very little overhead.
For ease of placement and level creation, I added a pipeline to send navmesh information directly from Maya to the PrimeEngine. Levels including navcells can be created directly in Maya's scene editor. Navcell properties are configured by the level designer via a python script. Once loaded by the PrimeEngine, information from Maya and the python script are used to construct the appropriate object in the core C++ code.
bottom of page