Kolor is a first-person 3D shooter game developed using only C++ & OpenGL. The objective of the game is to gain dominance in the world by coloring other players to your own color. Players belong to a team, represented by a captain and his color. You can Kolor(paint) all the players on the team or just kolor the master to claim the team.
I built a prototype using C# & XNA to determine if the game would be fun. XNA takes care of a lot of details, which forced me to dig deeper into the math involved. This motivated me to adopt C++/OpenGL as the only tools. This turned out to be a huge undertaking. As a part of this, I implemented my own COLLADA DAE model library.
Interesting things I did along the way:
COLLADA DAE Model Library
I needed a way to load & render 3D models for players/weapons/world. I was advised to use COLLADA DAE since it's the industry standard. I had to write my own Collada importer since there was no open source library at the time. I interpreted DAE's Directed Acyclic Graph into my own scene-graph. Take a look at:
Indexed Vertex Buffer Objects
Initially, I was using glBegin/glEnd to render models. However, when I loaded a high poly count model, the FPS crippled. I resorted to using Indexed Vertex Buffer Objects (VBOs) which helped FPS. For generating vertex indices, I ran the list of vertices through a hash-map and assign a unique index for each vertex. This can be seen in GeomMesh.cpp constructor and bool VertexExists(...) function.
Top
FrameTransform
Once the model was rendered, I wanted to create a "player" who could move around in the world, keeping track of its position and orientation. This gave birth to FrameTransform. I did this using three vectors:
My own gluLookAt
For the First-person view, I needed to position world and everything else relative to player's coordinate system. Camera class uses the OpenGL transformation stack to transform everything as per player's Coordinate system. It is a 'friend' of (Human)Player, since the camera is tightly coupled to the player.
class FrameTransform (6 DOF)
- glm::vec3 m_Origin;
- glm::vec3 m_Forward;
- glm::vec3 m_Up;
• MoveForward/Right/Up ( amt )
• RotateAround X/Y/Z ( amt )
• void ApplyActorTransform () => calls glMultMatrix(..)
class Player : public GameObject
- FrameTransform m_Transform; => Coordinate system w.r.t the world CS
class World : public GameObject (level class)
- FrameTransform m_Transform; => Coordinate system w.r.t the world CS
A first person camera helps to view the world from the player’s eyes. Thus, we need a transformation which enables the view from player’s reference. void Camera :: ApplyCameraTransform()
{
glm::mat4 l_TransformationMatrix;
m_Player->m_Transformation.GetTransformation(l_TransformationMatrix, false);
l_TransformationMatrix = glm::core::function::matrix::inverse(l_TransformationMatrix);
glMultMatrixf(glm::value_ptr(l_TransformationMatrix));
}
It is invoked as follows:
void GameGL :: paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
mp_Camera->ApplyCameraTransform();
std::list<GameObject*>::iterator it_GameObjs;
for(it_GameObjs = m_GameObjectList.begin(); it_GameObjs != m_GameObjectList.end(); it_GameObjs++) {
(*it_GameObjs)->Render();
}
...
}
Top
Coloring Players - When player and bullet collide!
As players move about and fire bullets, I manage collision detection/resolution by splitting it into broad and narrow phase.
The involved actors are :-
Cell {
List < Player*>
List < Bullet* >
Value (x,y,z) => unique value based on these three coordinates
}