3D C/C++ tutorials -> OpenGL 3.3 -> Flying camera, collision detection
Use for personal or educational purposes only. Commercial and other profit uses strictly prohibited. Exploitation of content on a website or in a publication prohibited.
object.h
#include "shaderprogram.h"
// ----------------------------------------------------------------------------------------------------------------------------
#define RADIUS 0.15625f
// ----------------------------------------------------------------------------------------------------------------------------
class CTriangle
{
public:
vec3 Vertex[3], Edge[3], Normal[4];
float EdgeLength[3], D[4];
bool Close;
};
// ----------------------------------------------------------------------------------------------------------------------------
class CObject
{
public:
mat3x3 NormalMatrix;
mat4x4 ModelMatrix;
CTexture Texture;
CBuffer Buffer;
GLuint VBO;
int VertexOffset, Stride, VerticesCount, TrianglesCount;
CTriangle *Triangles;
vec3 Min, Max;
bool Close;
public:
CObject();
~CObject();
void AddData(void *Data, int DataSize);
void Destroy();
void InitVBO(int VertexOffset, int Stride);
void PrepareTriangles();
void SetModelMatrix(const mat4x4 &ModelMatrix);
private:
void SetDefaults();
};
object.cpp
#include "object.h"
// ----------------------------------------------------------------------------------------------------------------------------
CObject::CObject()
{
SetDefaults();
}
CObject::~CObject()
{
}
void CObject::AddData(void *Data, int DataSize)
{
Buffer.AddData(Data, DataSize);
}
void CObject::Destroy()
{
Texture.Destroy();
Buffer.Empty();
glDeleteBuffers(1, &VBO);
delete [] Triangles;
SetDefaults();
}
void CObject::InitVBO(int VertexOffset, int Stride)
{
this->VertexOffset = VertexOffset;
this->Stride = Stride;
Buffer.FreeUnusedMemory();
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, Buffer.GetDataSize(), Buffer.GetData(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
VerticesCount = Buffer.GetDataSize() / Stride;
TrianglesCount = VerticesCount / 3;
Triangles = new CTriangle[TrianglesCount];
PrepareTriangles();
}
void CObject::PrepareTriangles()
{
BYTE *Vertices = Buffer.GetData() + VertexOffset;
CTriangle *Triangle = Triangles;
for(int t = 0; t < TrianglesCount; t++)
{
for(int v = 0; v < 3; v++)
{
vec3 *Vertex = (vec3*)(Vertices + Stride * v);
Triangle->Vertex[v].x = ModelMatrix.M[0] * Vertex->x + ModelMatrix.M[4] * Vertex->y + ModelMatrix.M[8] * Vertex->z + ModelMatrix.M[12];
Triangle->Vertex[v].y = ModelMatrix.M[1] * Vertex->x + ModelMatrix.M[5] * Vertex->y + ModelMatrix.M[9] * Vertex->z + ModelMatrix.M[13];
Triangle->Vertex[v].z = ModelMatrix.M[2] * Vertex->x + ModelMatrix.M[6] * Vertex->y + ModelMatrix.M[10] * Vertex->z + ModelMatrix.M[14];
}
Triangle->Normal[0] = normalize(cross(Triangle->Vertex[1] - Triangle->Vertex[0], Triangle->Vertex[2] - Triangle->Vertex[0]));
Triangle->D[0] = -dot(Triangle->Normal[0], Triangle->Vertex[0]);
for(int e = 0; e < 3; e++)
{
Triangle->Edge[e] = Triangle->Vertex[(e + 1) % 3] - Triangle->Vertex[e];
Triangle->EdgeLength[e] = length(Triangle->Edge[e]);
Triangle->Edge[e] /= Triangle->EdgeLength[e];
Triangle->Normal[1 + e] = cross(Triangle->Edge[e], Triangle->Normal[0]);
Triangle->D[1 + e] = -dot(Triangle->Normal[1 + e], Triangle->Vertex[e]);
}
Vertices += Stride * 3;
Triangle++;
}
Min = Max = Triangles[0].Vertex[0];
Triangle = Triangles;
for(int t = 0; t < TrianglesCount; t++)
{
for(int v = 0; v < 3; v++)
{
if(Triangle->Vertex[v].x < Min.x) Min.x = Triangle->Vertex[v].x;
if(Triangle->Vertex[v].x > Max.x) Max.x = Triangle->Vertex[v].x;
if(Triangle->Vertex[v].y < Min.y) Min.y = Triangle->Vertex[v].y;
if(Triangle->Vertex[v].y > Max.y) Max.y = Triangle->Vertex[v].y;
if(Triangle->Vertex[v].z < Min.z) Min.z = Triangle->Vertex[v].z;
if(Triangle->Vertex[v].z > Max.z) Max.z = Triangle->Vertex[v].z;
}
Triangle++;
}
Min -= RADIUS;
Max += RADIUS;
}
void CObject::SetModelMatrix(const mat4x4 &ModelMatrix)
{
this->ModelMatrix = ModelMatrix;
NormalMatrix = transpose(inverse(mat3x3(ModelMatrix)));
PrepareTriangles();
}
void CObject::SetDefaults()
{
NormalMatrix = mat3x3();
ModelMatrix = mat4x4();
VBO = 0;
VertexOffset = 0;
Stride = 0;
VerticesCount = 0;
TrianglesCount = 0;
Triangles = NULL;
}
camera.h
...
class CCamera
{
...
public:
...
virtual bool CheckCollisions(CObject *Objects, int ObjectsCount, vec3 &Movement, int Depth = 0);
...
};
...
class CUniverseCamera : public CCamera
{
public:
...
bool CheckCollisions(CObject *Objects, int ObjectsCount, vec3 &Movement, int Depth = 0);
...
};
...
class CFlyingCamera : public CUniverseCamera
{
...
};
...
camera.cpp
...
bool CCamera::CheckCollisions(CObject *Objects, int ObjectsCount, vec3 &Movement, int Depth)
{
return false;
}
...
bool CUniverseCamera::CheckCollisions(CObject *Objects, int ObjectsCount, vec3 &Movement, int Depth)
{
if(Depth < 4)
{
vec3 Position = this->Position + Movement;
// check the distance of the camera position from each triangle plane
for(int o = 0; o < ObjectsCount; o++)
{
Objects[o].Close = false;
if(Position.x < Objects[o].Min.x) continue;
if(Position.x > Objects[o].Max.x) continue;
if(Position.y < Objects[o].Min.y) continue;
if(Position.y > Objects[o].Max.y) continue;
if(Position.z < Objects[o].Min.z) continue;
if(Position.z > Objects[o].Max.z) continue;
Objects[o].Close = true;
CTriangle *Triangle = Objects[o].Triangles;
for(int t = 0; t < Objects[o].TrianglesCount; t++)
{
float Distance = dot(Triangle->Normal[0], Position) + Triangle->D[0];
Triangle->Close = Distance > 0.0f && Distance < RADIUS;
if(Triangle->Close)
{
if(dot(Triangle->Normal[1], Position) + Triangle->D[1] < 0.0f)
{
if(dot(Triangle->Normal[2], Position) + Triangle->D[2] < 0.0f)
{
if(dot(Triangle->Normal[3], Position) + Triangle->D[3] < 0.0f)
{
Movement += Triangle->Normal[0] * (RADIUS - Distance);
CheckCollisions(Objects, ObjectsCount, Movement, Depth + 1);
return true;
}
}
}
}
Triangle++;
}
}
// check the distance of the camera position from each edge
for(int o = 0; o < ObjectsCount; o++)
{
if(!Objects[o].Close) continue;
CTriangle *Triangle = Objects[o].Triangles;
for(int t = 0; t < Objects[o].TrianglesCount; t++)
{
if(Triangle->Close)
{
for(int e = 0; e < 3; e++)
{
vec3 VCP = Position - Triangle->Vertex[e];
float EdotVCP = dot(Triangle->Edge[e], VCP);
if(EdotVCP > 0.0f && EdotVCP < Triangle->EdgeLength[e])
{
vec3 Normal = VCP - Triangle->Edge[e] * EdotVCP;
float Distance = length(Normal);
if(Distance > 0.0f && Distance < RADIUS)
{
Movement += Normal * (RADIUS / Distance - 1.0f);
CheckCollisions(Objects, ObjectsCount, Movement, Depth + 1);
return true;
}
}
}
}
Triangle++;
}
}
// check the distance of the camera position from each vertex
for(int o = 0; o < ObjectsCount; o++)
{
if(!Objects[o].Close) continue;
CTriangle *Triangle = Objects[o].Triangles;
for(int t = 0; t < Objects[o].TrianglesCount; t++)
{
if(Triangle->Close)
{
for(int v = 0; v < 3; v++)
{
vec3 Normal = Position - Triangle->Vertex[v];
float Distance = length(Normal);
if(Distance > 0.0f && Distance < RADIUS)
{
Movement += Normal * (RADIUS / Distance - 1.0f);
CheckCollisions(Objects, ObjectsCount, Movement, Depth + 1);
return true;
}
}
}
Triangle++;
}
}
}
return false;
}
...
opengl33renderer.h
...
class COpenGL33Renderer
{
...
protected:
CObject *Objects;
int ObjectsCount;
...
public:
...
virtual bool CheckCollisions(vec3 &Movement);
...
};
...
opengl33renderer.cpp
...
COpenGL33Renderer::COpenGL33Renderer()
{
Objects = NULL;
ObjectsCount = 0;
...
}
...
bool COpenGL33Renderer::CheckCollisions(vec3 &Movement)
{
if(Camera != NULL)
{
return Camera->CheckCollisions(Objects, ObjectsCount, Movement);
}
return false;
}
...
opengl33view.cpp
...
void COpenGL33View::OnPaint()
{
...
if(OpenGL33Renderer != NULL)
{
bool Animated = OpenGL33Renderer->Animate(FrameTime);
SHORT Keys = 0x0000;
...
vec3 Movement;
bool MoveCamera = OpenGL33Renderer->OnCameraKeys(Keys, FrameTime, Movement);
if(MoveCamera || Animated)
{
bool CollisionsDetected = OpenGL33Renderer->CheckCollisions(Movement);
if(MoveCamera || CollisionsDetected)
{
OpenGL33Renderer->MoveCamera(Movement);
}
}
OpenGL33Renderer->Render();
}
...
}
...
myopengl33renderer.h
#include "opengl33view.h"
...
class CMyOpenGL33Renderer : public COpenGL33Renderer
{
private:
mat4x4 ViewProjectionMatrix;
CTexture SkyBoxTexture;
CShaderProgram SkyBox, Lighting;
GLuint SkyBoxVBO, VAO;
private:
bool Pause;
public:
CMyOpenGL33Renderer();
~CMyOpenGL33Renderer();
bool Init();
void Resize(int Width, int Height);
void Render();
bool Animate(float FrameTime);
void Destroy();
void OnKeyDown(UINT Key);
};
...
myopengl33renderer.cpp
...
CMyOpenGL33Renderer::CMyOpenGL33Renderer()
{
Camera = new CFlyingCamera();
Camera->SetViewMatrixPointer(&ViewMatrix);
Pause = false;
}
CMyOpenGL33Renderer::~CMyOpenGL33Renderer()
{
delete Camera;
}
bool CMyOpenGL33Renderer::Init()
{
bool Error = false;
// ------------------------------------------------------------------------------------------------------------------------
ObjectsCount = 3;
Objects = new CObject[ObjectsCount];
// load textures ----------------------------------------------------------------------------------------------------------
char *CubeMapTextureFileNames[] = {"jajlands1_ft.jpg", "jajlands1_bk.jpg", "jajlands1_dn.jpg", "jajlands1_up.jpg", "jajlands1_rt.jpg", "jajlands1_lf.jpg"};
Error |= !SkyBoxTexture.LoadTextureCubeMap(CubeMapTextureFileNames);
Error |= !Objects[0].Texture.LoadTexture2D("grass.jpg");
Error |= !Objects[1].Texture.LoadTexture2D("crate.jpg");
Error |= !Objects[2].Texture.LoadTexture2D("metalplate.jpg");
// load shaders -----------------------------------------------------------------------------------------------------------
Error |= !SkyBox.Load("skybox.vert", "skybox.frag");
Error |= !Lighting.Load("lighting.vert", "lighting.frag");
// if an error occured, return false --------------------------------------------------------------------------------------
if(Error)
{
return false;
}
// get uniform and attribute locations ------------------------------------------------------------------------------------
SkyBox.UniformLocations = new GLuint[2];
SkyBox.UniformLocations[0] = glGetUniformLocation(SkyBox, "CameraPosition");
SkyBox.UniformLocations[1] = glGetUniformLocation(SkyBox, "ViewProjectionMatrix");
SkyBox.AttribLocations = new GLuint[1];
SkyBox.AttribLocations[0] = glGetAttribLocation(SkyBox, "vert_Position");
Lighting.UniformLocations = new GLuint[2];
Lighting.UniformLocations[0] = glGetUniformLocation(Lighting, "NormalMatrix");
Lighting.UniformLocations[1] = glGetUniformLocation(Lighting, "ModelViewProjectionMatrix");
Lighting.AttribLocations = new GLuint[3];
Lighting.AttribLocations[0] = glGetAttribLocation(Lighting, "vert_TexCoord");
Lighting.AttribLocations[1] = glGetAttribLocation(Lighting, "vert_Normal");
Lighting.AttribLocations[2] = glGetAttribLocation(Lighting, "vert_Position");
// set constant uniforms --------------------------------------------------------------------------------------------------
glUseProgram(Lighting);
glUniform1f(glGetUniformLocation(Lighting, "Light.Ambient"), 0.333333f);
glUniform1f(glGetUniformLocation(Lighting, "Light.Diffuse"), 0.666666f);
vec3 LightDirection = vec3(0.467757f, 0.424200f, -0.775409f);
glUniform3fv(glGetUniformLocation(Lighting, "Light.Direction"), 1, &LightDirection);
glUseProgram(0);
// init skybox ------------------------------------------------------------------------------------------------------------
glGenBuffers(1, &SkyBoxVBO);
glBindBuffer(GL_ARRAY_BUFFER, SkyBoxVBO);
glBufferData(GL_ARRAY_BUFFER, 432, SkyBoxVertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// init terrain -----------------------------------------------------------------------------------------------------------
vec3 GrassNormal = vec3(0.0f, 1.0f, 0.0f);
for(int i = 0; i < 6; i++)
{
Objects[0].Buffer.AddData(&GrassTexCoords[i], 8);
Objects[0].Buffer.AddData(&GrassNormal, 12);
Objects[0].Buffer.AddData(&GrassVertices[i], 12);
}
Objects[0].InitVBO(20, 32);
// init cubes -------------------------------------------------------------------------------------------------------------
for(int y = 0; y < 8; y++)
{
for(int x = 0; x < 8 - y; x++)
{
vec3 offset = vec3(-5.5f + x * 1.5f + 0.75f * y, 0.5f + y, -5.0f);
for(int i = 0; i < 36; i++)
{
Objects[1].Buffer.AddData(&CubeTexCoords[i % 6], 8);
Objects[1].Buffer.AddData(&CubeNormals[i / 6], 12);
vec3 Vertex = CubeVertices[i] + offset;
Objects[1].Buffer.AddData(&Vertex, 12);
}
}
}
Objects[1].InitVBO(20, 32);
// init tori --------------------------------------------------------------------------------------------------------------
GenerateTorus(Objects[2].Buffer, 1.0f, 0.25f, 32, 16, mat4x4());
GenerateTorus(Objects[2].Buffer, 1.0f, 0.25f, 32, 16, rotate(90.0f, vec3(0.0f, 1.0f, 0.0f)));
GenerateTorus(Objects[2].Buffer, 1.0f, 0.25f, 32, 16, rotate(90.0f, vec3(1.0f, 0.0f, 0.0f)));
Objects[2].InitVBO(20, 32);
// generate VAO -----------------------------------------------------------------------------------------------------------
glGenVertexArrays(1, &VAO);
// set camera--------------------------------------------------------------------------------------------------------------
Camera->Look(vec3(0.0f, 1.75f, 0.0f), vec3(0.0f, 1.75f, -1.0f));
// ------------------------------------------------------------------------------------------------------------------------
return true;
}
void CMyOpenGL33Renderer::Resize(int Width, int Height)
{
this->Width = Width;
this->Height = Height;
glViewport(0, 0, Width, Height);
ProjectionMatrix = perspective(45.0f, (float)Width / (float)Height, 0.125f, 512.0f);
}
void CMyOpenGL33Renderer::Render()
{
// clear frame buffer -----------------------------------------------------------------------------------------------------
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// set up matrices --------------------------------------------------------------------------------------------------------
ViewProjectionMatrix = ProjectionMatrix * ViewMatrix;
// render skybox - depth test must be disabled ----------------------------------------------------------------------------
glUseProgram(SkyBox);
glUniform3fv(SkyBox.UniformLocations[0], 1, &Camera->Position);
glUniformMatrix4fv(SkyBox.UniformLocations[1], 1, GL_FALSE, &ViewProjectionMatrix);
glBindTexture(GL_TEXTURE_CUBE_MAP, SkyBoxTexture);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, SkyBoxVBO);
glEnableVertexAttribArray(SkyBox.AttribLocations[0]);
glVertexAttribPointer(SkyBox.AttribLocations[0], 3, GL_FLOAT, GL_FALSE, 12, (void*)0);
glDrawArrays(GL_TRIANGLES, 0, 36);
glDisableVertexAttribArray(SkyBox.AttribLocations[0]);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
glUseProgram(0);
// render scene -----------------------------------------------------------------------------------------------------------
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glUseProgram(Lighting);
glBindVertexArray(VAO);
glEnableVertexAttribArray(Lighting.AttribLocations[0]);
glEnableVertexAttribArray(Lighting.AttribLocations[1]);
glEnableVertexAttribArray(Lighting.AttribLocations[2]);
for(int i = 0; i < ObjectsCount; i++)
{
ModelViewProjectionMatrix = ViewProjectionMatrix * Objects[i].ModelMatrix;
glUniformMatrix3fv(Lighting.UniformLocations[0], 1, GL_FALSE, &Objects[i].NormalMatrix);
glUniformMatrix4fv(Lighting.UniformLocations[1], 1, GL_FALSE, &ModelViewProjectionMatrix);
glBindTexture(GL_TEXTURE_2D, Objects[i].Texture);
glBindBuffer(GL_ARRAY_BUFFER, Objects[i].VBO);
glVertexAttribPointer(Lighting.AttribLocations[0], 2, GL_FLOAT, GL_FALSE, 32, (void*)0);
glVertexAttribPointer(Lighting.AttribLocations[1], 3, GL_FLOAT, GL_FALSE, 32, (void*)8);
glVertexAttribPointer(Lighting.AttribLocations[2], 3, GL_FLOAT, GL_FALSE, 32, (void*)20);
glDrawArrays(GL_TRIANGLES, 0, Objects[i].VerticesCount);
}
// unbind and disable what was bound and enabled
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glDisableVertexAttribArray(Lighting.AttribLocations[2]);
glDisableVertexAttribArray(Lighting.AttribLocations[1]);
glDisableVertexAttribArray(Lighting.AttribLocations[0]);
glBindVertexArray(0);
glUseProgram(0);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
}
bool CMyOpenGL33Renderer::Animate(float FrameTime)
{
static float Angle = 0.0f;
// increase angle - rotating object ---------------------------------------------------------------------------------------
if(!Pause)
{
Angle += 22.5f * FrameTime;
Objects[2].SetModelMatrix(translate(0.0f, 1.75f, 5.0f) * rotate(Angle, vec3(0.0f, 1.0f, 0.0f)));
}
return !Pause;
}
void CMyOpenGL33Renderer::Destroy()
{
SkyBoxTexture.Destroy();
for(int i = 0; i < ObjectsCount; i++)
{
Objects[i].Destroy();
}
delete [] Objects;
SkyBox.Destroy();
Lighting.Destroy();
glDeleteBuffers(1, &SkyBoxVBO);
glDeleteVertexArrays(1, &VAO);
}
void CMyOpenGL33Renderer::OnKeyDown(UINT Key)
{
switch(Key)
{
case 'P':
Pause = !Pause;
break;
}
COpenGL33Renderer::OnKeyDown(Key);
}
...
Download
|