3D C/C++ tutorials -> OpenGL 2.1 -> Terrain, BSP tree, frustum culling, sorting, shadow mapping
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.
opengl_21_tutorials_win32_framework.h
// ----------------------------------------------------------------------------------------------------------------------------
//
// Version 2.04
//
// ----------------------------------------------------------------------------------------------------------------------------
#include <windows.h>
#ifndef WM_MOUSWHEEL
#define WM_MOUSWHEEL 0x020A
#endif
#include "glmath.h"
#include "string.h"
#include <gl/glew.h> // http://glew.sourceforge.net/
#include <gl/wglew.h>
#include <FreeImage.h> // http://freeimage.sourceforge.net/
// ----------------------------------------------------------------------------------------------------------------------------
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glew32.lib")
#pragma comment(lib, "FreeImage.lib")
// ----------------------------------------------------------------------------------------------------------------------------
extern CString ModuleDirectory, ErrorLog;
// ----------------------------------------------------------------------------------------------------------------------------
#define BUFFER_SIZE_INCREMENT 1048576
// ----------------------------------------------------------------------------------------------------------------------------
class CBuffer
{
private:
BYTE *Buffer;
int BufferSize;
private:
int Position;
public:
CBuffer();
~CBuffer();
private:
void SetDefaults();
public:
void AddData(void *Data, int DataSize);
void Empty();
void *GetData();
int GetDataSize();
};
// ----------------------------------------------------------------------------------------------------------------------------
extern int gl_max_texture_size, gl_max_texture_max_anisotropy_ext;
// ----------------------------------------------------------------------------------------------------------------------------
class CTexture
{
private:
GLuint Texture;
private:
int Width, Height;
public:
CTexture();
~CTexture();
private:
void SetDefaults();
public:
operator GLuint ();
private:
FIBITMAP *GetBitmap(char *FileName, int &Width, int &Height, int &BPP);
public:
bool LoadTexture2D(char *FileName);
bool LoadTextureCubeMap(char **FileNames);
void Destroy();
public:
int GetWidth();
int GetHeight();
};
// ----------------------------------------------------------------------------------------------------------------------------
class CShaderProgram
{
private:
GLuint VertexShader, FragmentShader;
private:
GLuint Program;
public:
GLuint *UniformLocations, *AttribLocations;
public:
CShaderProgram();
~CShaderProgram();
private:
void SetDefaults();
public:
operator GLuint ();
private:
GLuint LoadShader(char *FileName, GLenum Type);
public:
bool Load(char *VertexShaderFileName, char *FragmentShaderFileName);
void Destroy();
};
// ----------------------------------------------------------------------------------------------------------------------------
class CPlane
{
private:
vec3 N;
float ND;
int O;
public:
CPlane();
~CPlane();
public:
void Set(const vec3 &A, const vec3 &B, const vec3 &C);
bool AABBBehind(const vec3 *AABBVertices);
float AABBDistance(const vec3 *AABBVertices);
};
// ----------------------------------------------------------------------------------------------------------------------------
class CFrustum
{
private:
vec3 Vertices[8];
private:
CPlane Planes[6];
public:
CFrustum();
~CFrustum();
public:
void Set(const mat4x4 &ViewProjectionMatrixInverse);
bool AABBVisible(const vec3 *AABBVertices);
float AABBDistance(const vec3 *AABBVertices);
void Render();
};
// ----------------------------------------------------------------------------------------------------------------------------
class CCamera
{
public:
vec3 X, Y, Z, Position, Reference;
public:
mat4x4 ViewMatrix, ViewMatrixInverse, ProjectionMatrix, ProjectionMatrixInverse, ViewProjectionMatrix, ViewProjectionMatrixInverse;
public:
CFrustum Frustum;
public:
CCamera();
~CCamera();
public:
void Look(const vec3 &Position, const vec3 &Reference, bool RotateAroundReference = false);
void Move(const vec3 &Movement);
vec3 OnKeys(BYTE Keys, float FrameTime);
void OnMouseMove(int dx, int dy);
void OnMouseWheel(float zDelta);
void SetPerspective(float fovy, float aspect, float n, float f);
private:
void CalculateViewMatrix();
};
// ----------------------------------------------------------------------------------------------------------------------------
class CVertex
{
public:
vec3 Position;
vec3 Normal;
};
// ----------------------------------------------------------------------------------------------------------------------------
class CAABB
{
private:
vec3 Vertices[8];
public:
CAABB();
~CAABB();
public:
void Set(const vec3 &Min, const vec3 &Max);
bool PointInside(const vec3 &Point);
bool Visible(CFrustum &Frustum);
float Distance(CFrustum &Frustum);
void Render();
};
// ----------------------------------------------------------------------------------------------------------------------------
class CBSPTreeNode
{
private:
vec3 Min, Max;
private:
int Depth;
private:
CAABB AABB;
private:
bool Visible;
float Distance;
private:
int *Indices;
private:
int IndicesCount;
private:
GLuint IndexBufferObject;
private:
CBSPTreeNode *Children[2];
public:
CBSPTreeNode();
~CBSPTreeNode();
private:
void SetDefaults();
public:
void InitAABB(const vec3 &Min, const vec3 &Max, int Depth, float MinAABBSize);
bool CheckTriangle(CVertex *Vertices, int *Indices, int A, int B, int C);
void AllocateMemory();
bool AddTriangle(CVertex *Vertices, int *Indices, int A, int B, int C);
void ResetAABB(CVertex *Vertices);
int InitIndexBufferObject();
int CheckVisibility(CFrustum &Frustum, CBSPTreeNode **VisibleGeometryNodes, int &VisibleGeometryNodesCount);
float GetDistance();
void Render();
void RenderAABB(int Depth);
void Destroy();
};
// ----------------------------------------------------------------------------------------------------------------------------
class CBSPTree
{
private:
CBSPTreeNode *Root;
private:
CBSPTreeNode **VisibleGeometryNodes;
int VisibleGeometryNodesCount;
public:
CBSPTree();
~CBSPTree();
private:
void SetDefaults();
public:
void Init(CVertex *Vertices, int *Indices, int IndicesCount, const vec3 &Min, const vec3 &Max, float MinAABBSize = 16.0f);
void QuickSortVisibleGeometryNodes(int Left, int Right);
int CheckVisibility(CFrustum &Frustum, bool SortVisibleGeometryNodes);
void Render(bool VisualizeRenderingOrder);
void RenderAABB(int Depth);
void Destroy();
};
// ----------------------------------------------------------------------------------------------------------------------------
class CTerrain
{
private:
int Size, SizeP1;
float SizeD2;
private:
vec3 Min, Max;
private:
float *Heights;
private:
int VerticesCount, IndicesCount;
private:
GLuint VertexBufferObject, IndexBufferObject;
public:
CBSPTree BSPTree;
public:
CTerrain();
~CTerrain();
private:
void SetDefaults();
public:
bool LoadTexture2D(char *FileName, float Scale = 256.0f, float Offset = -128.0f);
bool LoadBinary(char *FileName);
bool SaveBinary(char *FileName);
int CheckVisibility(CFrustum &Frustum, bool SortVisibleGeometryNodes = true);
void Render(bool VisualizeRenderingOrder = false);
void RenderSlow();
void RenderSlowToShadowMap();
void RenderAABB(int Depth = -1);
void Destroy();
public:
int GetSize();
vec3 GetMin();
vec3 GetMax();
void GetMinMax(mat4x4 &ViewMatrix, vec3 &Min, vec3 &Max);
int GetTrianglesCount();
private:
int GetIndex(int X, int Z);
float GetHeight(int X, int Z);
public:
float GetHeight(float X, float Z);
private:
float GetHeight(float *Heights, int Size, float X, float Z);
};
// ----------------------------------------------------------------------------------------------------------------------------
#define SHADOW_MAP_SIZE 4096
// ----------------------------------------------------------------------------------------------------------------------------
class COpenGLRenderer
{
private:
int LastX, LastY, LastClickedX, LastClickedY;
private:
int Width, Height;
private:
CCamera Camera;
private:
CShaderProgram Shader;
private:
CTerrain Terrain;
private:
float LightAngle;
private:
mat4x4 LightViewMatrix, LightProjectionMatrix, ShadowMatrix;
private:
int ShadowMapSize;
GLuint ShadowMap, RotationTexture, FBO;
private:
bool Wireframe, DisplayShadowMap, RenderAABB, VisualizeRenderingOrder, SortVisibleGeometryNodes, RenderSlow;
int Depth;
public:
COpenGLRenderer();
~COpenGLRenderer();
public:
bool Init();
void Render();
void Animate(float FrameTime);
void Resize(int Width, int Height);
void Destroy();
private:
void RenderShadowMap();
void CheckCameraTerrainPosition(vec3 &Movement);
public:
void CheckCameraKeys(float FrameTime);
public:
void OnKeyDown(UINT Key);
void OnLButtonDown(int X, int Y);
void OnLButtonUp(int X, int Y);
void OnMouseMove(int X, int Y);
void OnMouseWheel(short zDelta);
void OnRButtonDown(int X, int Y);
void OnRButtonUp(int X, int Y);
};
// ----------------------------------------------------------------------------------------------------------------------------
class COpenGLView
{
private:
HWND hWnd;
HGLRC hGLRC;
private:
int Width, Height, Samples;
private:
CString Title, Resolution, MSAA, ATF, FPS, Renderer;
private:
COpenGLRenderer OpenGLRenderer;
public:
COpenGLView();
~COpenGLView();
public:
bool Init(HINSTANCE hInstance, char *Title, int Width, int Height, int Samples);
void Show(bool Maximized = false);
void MessageLoop();
void Destroy();
public:
void OnKeyDown(UINT Key);
void OnLButtonDown(int X, int Y);
void OnLButtonUp(int X, int Y);
void OnMouseMove(int X, int Y);
void OnMouseWheel(short zDelta);
void OnPaint();
void OnRButtonDown(int X, int Y);
void OnRButtonUp(int X, int Y);
void OnSize(int Width, int Height);
};
// ----------------------------------------------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
// ----------------------------------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR sCmdLine, int iShow);
opengl_21_tutorials_win32_framework.cpp
#include "opengl_21_tutorials_win32_framework.h"
// ----------------------------------------------------------------------------------------------------------------------------
CBuffer::CBuffer()
{
SetDefaults();
}
CBuffer::~CBuffer()
{
Empty();
}
void CBuffer::SetDefaults()
{
Buffer = NULL;
BufferSize = 0;
Position = 0;
}
void CBuffer::AddData(void *Data, int DataSize)
{
int Remaining = BufferSize - Position;
if(DataSize > Remaining)
{
BYTE *OldBuffer = Buffer;
int OldBufferSize = BufferSize;
int Needed = DataSize - Remaining;
BufferSize += Needed > BUFFER_SIZE_INCREMENT ? Needed : BUFFER_SIZE_INCREMENT;
Buffer = new BYTE[BufferSize];
memcpy(Buffer, OldBuffer, OldBufferSize);
delete [] OldBuffer;
}
memcpy(Buffer + Position, Data, DataSize);
Position += DataSize;
}
void CBuffer::Empty()
{
if(Buffer != NULL)
{
delete [] Buffer;
}
SetDefaults();
}
void *CBuffer::GetData()
{
return Buffer;
}
int CBuffer::GetDataSize()
{
return Position;
}
// ----------------------------------------------------------------------------------------------------------------------------
int gl_max_texture_size = 0, gl_max_texture_max_anisotropy_ext = 0;
// ----------------------------------------------------------------------------------------------------------------------------
CTexture::CTexture()
{
SetDefaults();
}
CTexture::~CTexture()
{
}
void CTexture::SetDefaults()
{
Texture = 0;
Width = 0;
Height = 0;
}
CTexture::operator GLuint ()
{
return Texture;
}
FIBITMAP *CTexture::GetBitmap(char *FileName, int &Width, int &Height, int &BPP)
{
FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(FileName);
if(fif == FIF_UNKNOWN)
{
fif = FreeImage_GetFIFFromFilename(FileName);
}
if(fif == FIF_UNKNOWN)
{
return NULL;
}
FIBITMAP *dib = NULL;
if(FreeImage_FIFSupportsReading(fif))
{
dib = FreeImage_Load(fif, FileName);
}
if(dib != NULL)
{
int OriginalWidth = FreeImage_GetWidth(dib);
int OriginalHeight = FreeImage_GetHeight(dib);
Width = OriginalWidth;
Height = OriginalHeight;
if(Width == 0 || Height == 0)
{
FreeImage_Unload(dib);
return NULL;
}
BPP = FreeImage_GetBPP(dib);
if(Width > gl_max_texture_size) Width = gl_max_texture_size;
if(Height > gl_max_texture_size) Height = gl_max_texture_size;
if(!GLEW_ARB_texture_non_power_of_two)
{
Width = 1 << (int)floor((log((float)Width) / log(2.0f)) + 0.5f);
Height = 1 << (int)floor((log((float)Height) / log(2.0f)) + 0.5f);
}
if(Width != OriginalWidth || Height != OriginalHeight)
{
FIBITMAP *rdib = FreeImage_Rescale(dib, Width, Height, FILTER_BICUBIC);
FreeImage_Unload(dib);
dib = rdib;
}
}
return dib;
}
bool CTexture::LoadTexture2D(char *FileName)
{
CString DirectoryFileName = ModuleDirectory + FileName;
int Width, Height, BPP;
FIBITMAP *dib = GetBitmap(DirectoryFileName, Width, Height, BPP);
if(dib == NULL)
{
ErrorLog.Append("Error loading texture " + DirectoryFileName + "!\r\n");
return false;
}
GLenum Format = 0;
if(BPP == 32) Format = GL_BGRA;
if(BPP == 24) Format = GL_BGR;
if(Format == 0)
{
ErrorLog.Append("Unsupported texture format (%s)!\r\n", FileName);
FreeImage_Unload(dib);
return false;
}
Destroy();
glGenTextures(1, &Texture);
this->Width = Width;
this->Height = Height;
glBindTexture(GL_TEXTURE_2D, Texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if(GLEW_EXT_texture_filter_anisotropic)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_texture_max_anisotropy_ext);
}
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, Format, GL_UNSIGNED_BYTE, FreeImage_GetBits(dib));
glBindTexture(GL_TEXTURE_2D, 0);
FreeImage_Unload(dib);
return true;
}
bool CTexture::LoadTextureCubeMap(char **FileNames)
{
int Width, Height, BPP;
FIBITMAP *dib[6];
bool Error = false;
for(int i = 0; i < 6; i++)
{
CString DirectoryFileName = ModuleDirectory + FileNames[i];
dib[i] = GetBitmap(DirectoryFileName, Width, Height, BPP);
if(dib[i] == NULL)
{
ErrorLog.Append("Error loading texture " + DirectoryFileName + "!\r\n");
Error = true;
}
}
if(Error)
{
for(int i = 0; i < 6; i++)
{
FreeImage_Unload(dib[i]);
}
return false;
}
GLenum Format = 0;
if(BPP == 32) Format = GL_BGRA;
if(BPP == 24) Format = GL_BGR;
if(Format == 0)
{
ErrorLog.Append("Unsupported texture format (%s)!\r\n", FileNames[5]);
for(int i = 0; i < 6; i++)
{
FreeImage_Unload(dib[i]);
}
return false;
}
Destroy();
glGenTextures(1, &Texture);
this->Width = Width;
this->Height = Height;
glBindTexture(GL_TEXTURE_CUBE_MAP, Texture);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if(GLEW_EXT_texture_filter_anisotropic)
{
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_texture_max_anisotropy_ext);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE);
for(int i = 0; i < 6; i++)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA8, Width, Height, 0, Format, GL_UNSIGNED_BYTE, FreeImage_GetBits(dib[i]));
}
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
for(int i = 0; i < 6; i++)
{
FreeImage_Unload(dib[i]);
}
return true;
}
void CTexture::Destroy()
{
if(Texture != 0)
{
glDeleteTextures(1, &Texture);
}
SetDefaults();
}
int CTexture::GetWidth()
{
return Width;
}
int CTexture::GetHeight()
{
return Height;
}
// ----------------------------------------------------------------------------------------------------------------------------
CShaderProgram::CShaderProgram()
{
SetDefaults();
}
CShaderProgram::~CShaderProgram()
{
}
void CShaderProgram::SetDefaults()
{
VertexShader = 0;
FragmentShader = 0;
Program = 0;
UniformLocations = NULL;
AttribLocations = NULL;
}
CShaderProgram::operator GLuint ()
{
return Program;
}
GLuint CShaderProgram::LoadShader(char *FileName, GLenum Type)
{
CString DirectoryFileName = ModuleDirectory + FileName;
FILE *File;
if(fopen_s(&File, DirectoryFileName, "rb") != 0)
{
ErrorLog.Append("Error loading file " + DirectoryFileName + "!\r\n");
return 0;
}
fseek(File, 0, SEEK_END);
long Size = ftell(File);
fseek(File, 0, SEEK_SET);
char *Source = new char[Size + 1];
fread(Source, 1, Size, File);
fclose(File);
Source[Size] = 0;
GLuint Shader = glCreateShader(Type);
glShaderSource(Shader, 1, (const char**)&Source, NULL);
delete [] Source;
glCompileShader(Shader);
int CompileStatus;
glGetShaderiv(Shader, GL_COMPILE_STATUS, &CompileStatus);
if(CompileStatus == GL_FALSE)
{
ErrorLog.Append("Error compiling shader %s!\r\n", FileName);
int InfoLogLength = 0;
glGetShaderiv(Shader, GL_INFO_LOG_LENGTH, &InfoLogLength);
if(InfoLogLength > 0)
{
char *InfoLog = new char[InfoLogLength];
int CharsWritten = 0;
glGetShaderInfoLog(Shader, InfoLogLength, &CharsWritten, InfoLog);
ErrorLog.Append(InfoLog);
delete [] InfoLog;
}
glDeleteShader(Shader);
return 0;
}
return Shader;
}
bool CShaderProgram::Load(char *VertexShaderFileName, char *FragmentShaderFileName)
{
bool Error = false;
Destroy();
Error |= ((VertexShader = LoadShader(VertexShaderFileName, GL_VERTEX_SHADER)) == 0);
Error |= ((FragmentShader = LoadShader(FragmentShaderFileName, GL_FRAGMENT_SHADER)) == 0);
if(Error)
{
Destroy();
return false;
}
Program = glCreateProgram();
glAttachShader(Program, VertexShader);
glAttachShader(Program, FragmentShader);
glLinkProgram(Program);
int LinkStatus;
glGetProgramiv(Program, GL_LINK_STATUS, &LinkStatus);
if(LinkStatus == GL_FALSE)
{
ErrorLog.Append("Error linking program (%s, %s)!\r\n", VertexShaderFileName, FragmentShaderFileName);
int InfoLogLength = 0;
glGetProgramiv(Program, GL_INFO_LOG_LENGTH, &InfoLogLength);
if(InfoLogLength > 0)
{
char *InfoLog = new char[InfoLogLength];
int CharsWritten = 0;
glGetProgramInfoLog(Program, InfoLogLength, &CharsWritten, InfoLog);
ErrorLog.Append(InfoLog);
delete [] InfoLog;
}
Destroy();
return false;
}
return true;
}
void CShaderProgram::Destroy()
{
if(Program != 0)
{
if(VertexShader != 0)
{
glDetachShader(Program, VertexShader);
}
if(FragmentShader != 0)
{
glDetachShader(Program, FragmentShader);
}
}
if(VertexShader != 0)
{
glDeleteShader(VertexShader);
}
if(FragmentShader)
{
glDeleteShader(FragmentShader);
}
if(Program != 0)
{
glDeleteProgram(Program);
}
if(UniformLocations != NULL)
{
delete [] UniformLocations;
}
if(AttribLocations != NULL)
{
delete [] AttribLocations;
}
SetDefaults();
}
// ----------------------------------------------------------------------------------------------------------------------------
CPlane::CPlane()
{
}
CPlane::~CPlane()
{
}
void CPlane::Set(const vec3 &A, const vec3 &B, const vec3 &C)
{
N = normalize(cross(B - A, C - A));
ND = dot(N, A);
O = N.z < 0.0f ? (N.y < 0.0f ? (N.x < 0.0f ? 0 : 1) : (N.x < 0.0f ? 2 : 3)) : (N.y < 0.0f ? (N.x < 0.0f ? 4 : 5) : (N.x < 0.0f ? 6 : 7));
}
bool CPlane::AABBBehind(const vec3 *AABBVertices)
{
return dot(N, AABBVertices[O]) < ND;
}
float CPlane::AABBDistance(const vec3 *AABBVertices)
{
return dot(N, AABBVertices[O]);
}
// ----------------------------------------------------------------------------------------------------------------------------
CFrustum::CFrustum()
{
}
CFrustum::~CFrustum()
{
}
void CFrustum::Set(const mat4x4 &ViewProjectionMatrixInverse)
{
vec4 A = ViewProjectionMatrixInverse * vec4(-1.0f, -1.0f, 1.0f, 1.0f);
vec4 B = ViewProjectionMatrixInverse * vec4( 1.0f, -1.0f, 1.0f, 1.0f);
vec4 C = ViewProjectionMatrixInverse * vec4(-1.0f, 1.0f, 1.0f, 1.0f);
vec4 D = ViewProjectionMatrixInverse * vec4( 1.0f, 1.0f, 1.0f, 1.0f);
vec4 E = ViewProjectionMatrixInverse * vec4(-1.0f, -1.0f, -1.0f, 1.0f);
vec4 F = ViewProjectionMatrixInverse * vec4( 1.0f, -1.0f, -1.0f, 1.0f);
vec4 G = ViewProjectionMatrixInverse * vec4(-1.0f, 1.0f, -1.0f, 1.0f);
vec4 H = ViewProjectionMatrixInverse * vec4( 1.0f, 1.0f, -1.0f, 1.0f);
Vertices[0] = vec3(A.x / A.w, A.y / A.w, A.z / A.w);
Vertices[1] = vec3(B.x / B.w, B.y / B.w, B.z / B.w);
Vertices[2] = vec3(C.x / C.w, C.y / C.w, C.z / C.w);
Vertices[3] = vec3(D.x / D.w, D.y / D.w, D.z / D.w);
Vertices[4] = vec3(E.x / E.w, E.y / E.w, E.z / E.w);
Vertices[5] = vec3(F.x / F.w, F.y / F.w, F.z / F.w);
Vertices[6] = vec3(G.x / G.w, G.y / G.w, G.z / G.w);
Vertices[7] = vec3(H.x / H.w, H.y / H.w, H.z / H.w);
Planes[0].Set(Vertices[4], Vertices[0], Vertices[2]);
Planes[1].Set(Vertices[1], Vertices[5], Vertices[7]);
Planes[2].Set(Vertices[4], Vertices[5], Vertices[1]);
Planes[3].Set(Vertices[2], Vertices[3], Vertices[7]);
Planes[4].Set(Vertices[0], Vertices[1], Vertices[3]);
Planes[5].Set(Vertices[5], Vertices[4], Vertices[6]);
}
bool CFrustum::AABBVisible(const vec3 *AABBVertices)
{
for(int i = 0; i < 6; i++)
{
if(Planes[i].AABBBehind(AABBVertices))
{
return false;
}
}
return true;
}
float CFrustum::AABBDistance(const vec3 *AABBVertices)
{
return Planes[5].AABBDistance(AABBVertices);
}
void CFrustum::Render()
{
glBegin(GL_LINES);
glVertex3fv(&Vertices[0]); glVertex3fv(&Vertices[1]);
glVertex3fv(&Vertices[2]); glVertex3fv(&Vertices[3]);
glVertex3fv(&Vertices[4]); glVertex3fv(&Vertices[5]);
glVertex3fv(&Vertices[6]); glVertex3fv(&Vertices[7]);
glVertex3fv(&Vertices[0]); glVertex3fv(&Vertices[2]);
glVertex3fv(&Vertices[1]); glVertex3fv(&Vertices[3]);
glVertex3fv(&Vertices[4]); glVertex3fv(&Vertices[6]);
glVertex3fv(&Vertices[5]); glVertex3fv(&Vertices[7]);
glVertex3fv(&Vertices[0]); glVertex3fv(&Vertices[4]);
glVertex3fv(&Vertices[1]); glVertex3fv(&Vertices[5]);
glVertex3fv(&Vertices[2]); glVertex3fv(&Vertices[6]);
glVertex3fv(&Vertices[3]); glVertex3fv(&Vertices[7]);
glEnd();
}
// ----------------------------------------------------------------------------------------------------------------------------
CCamera::CCamera()
{
X = vec3(1.0f, 0.0f, 0.0f);
Y = vec3(0.0f, 1.0f, 0.0f);
Z = vec3(0.0f, 0.0f, 1.0f);
Position = vec3(0.0f, 0.0f, 5.0f);
Reference = vec3(0.0f, 0.0f, 0.0f);
CalculateViewMatrix();
}
CCamera::~CCamera()
{
}
void CCamera::Look(const vec3 &Position, const vec3 &Reference, bool RotateAroundReference)
{
this->Position = Position;
this->Reference = Reference;
Z = normalize(Position - Reference);
GetXY(Z, X, Y);
if(!RotateAroundReference)
{
this->Reference = this->Position - Z * 0.05f;
}
CalculateViewMatrix();
}
void CCamera::Move(const vec3 &Movement)
{
Position += Movement;
Reference += Movement;
CalculateViewMatrix();
}
vec3 CCamera::OnKeys(BYTE Keys, float FrameTime)
{
float Speed = 5.0f;
if(Keys & 0x40) Speed *= 2.0f;
if(Keys & 0x80) Speed *= 0.5f;
float Distance = Speed * FrameTime;
vec3 Up(0.0f, 1.0f, 0.0f);
vec3 Right = X;
vec3 Forward = cross(Up, Right);
Up *= Distance;
Right *= Distance;
Forward *= Distance;
vec3 Movement;
if(Keys & 0x01) Movement += Forward;
if(Keys & 0x02) Movement -= Forward;
if(Keys & 0x04) Movement -= Right;
if(Keys & 0x08) Movement += Right;
if(Keys & 0x10) Movement += Up;
if(Keys & 0x20) Movement -= Up;
return Movement;
}
void CCamera::OnMouseMove(int dx, int dy)
{
float Sensitivity = 0.25f;
Position -= Reference;
if(dx != 0)
{
float DeltaX = (float)dx * Sensitivity;
X = rotate(X, DeltaX, vec3(0.0f, 1.0f, 0.0f));
Y = rotate(Y, DeltaX, vec3(0.0f, 1.0f, 0.0f));
Z = rotate(Z, DeltaX, vec3(0.0f, 1.0f, 0.0f));
}
if(dy != 0)
{
float DeltaY = (float)dy * Sensitivity;
Y = rotate(Y, DeltaY, X);
Z = rotate(Z, DeltaY, X);
if(Y.y < 0.0f)
{
Z = vec3(0.0f, Z.y > 0.0f ? 1.0f : -1.0f, 0.0f);
Y = cross(Z, X);
}
}
Position = Reference + Z * length(Position);
CalculateViewMatrix();
}
void CCamera::OnMouseWheel(float zDelta)
{
Position -= Reference;
if(zDelta < 0 && length(Position) < 500.0f)
{
Position += Position * 0.1f;
}
if(zDelta > 0 && length(Position) > 0.05f)
{
Position -= Position * 0.1f;
}
Position += Reference;
CalculateViewMatrix();
}
void CCamera::SetPerspective(float fovy, float aspect, float n, float f)
{
ProjectionMatrix = perspective(fovy, aspect, n, f);
ProjectionMatrixInverse = inverse(ProjectionMatrix);
ViewProjectionMatrix = ProjectionMatrix * ViewMatrix;
ViewProjectionMatrixInverse = ViewMatrixInverse * ProjectionMatrixInverse;
Frustum.Set(ViewProjectionMatrixInverse);
}
void CCamera::CalculateViewMatrix()
{
ViewMatrix = mat4x4(X.x, Y.x, Z.x, 0.0f, X.y, Y.y, Z.y, 0.0f, X.z, Y.z, Z.z, 0.0f, -dot(X, Position), -dot(Y, Position), -dot(Z, Position), 1.0f);
ViewMatrixInverse = inverse(ViewMatrix);
ViewProjectionMatrix = ProjectionMatrix * ViewMatrix;
ViewProjectionMatrixInverse = ViewMatrixInverse * ProjectionMatrixInverse;
Frustum.Set(ViewProjectionMatrixInverse);
}
// ----------------------------------------------------------------------------------------------------------------------------
CAABB::CAABB()
{
}
CAABB::~CAABB()
{
}
void CAABB::Set(const vec3 &Min, const vec3 &Max)
{
Vertices[0] = vec3(Min.x, Min.y, Min.z);
Vertices[1] = vec3(Max.x, Min.y, Min.z);
Vertices[2] = vec3(Min.x, Max.y, Min.z);
Vertices[3] = vec3(Max.x, Max.y, Min.z);
Vertices[4] = vec3(Min.x, Min.y, Max.z);
Vertices[5] = vec3(Max.x, Min.y, Max.z);
Vertices[6] = vec3(Min.x, Max.y, Max.z);
Vertices[7] = vec3(Max.x, Max.y, Max.z);
}
bool CAABB::PointInside(const vec3 &Point)
{
if(Point.x < Vertices[0].x) return false;
if(Point.y < Vertices[0].y) return false;
if(Point.z < Vertices[0].z) return false;
if(Point.x > Vertices[7].x) return false;
if(Point.y > Vertices[7].y) return false;
if(Point.z > Vertices[7].z) return false;
return true;
}
bool CAABB::Visible(CFrustum &Frustum)
{
return Frustum.AABBVisible(Vertices);
}
float CAABB::Distance(CFrustum &Frustum)
{
return Frustum.AABBDistance(Vertices);
}
void CAABB::Render()
{
glBegin(GL_LINES);
glVertex3fv(&Vertices[0]); glVertex3fv(&Vertices[1]);
glVertex3fv(&Vertices[2]); glVertex3fv(&Vertices[3]);
glVertex3fv(&Vertices[4]); glVertex3fv(&Vertices[5]);
glVertex3fv(&Vertices[6]); glVertex3fv(&Vertices[7]);
glVertex3fv(&Vertices[0]); glVertex3fv(&Vertices[2]);
glVertex3fv(&Vertices[1]); glVertex3fv(&Vertices[3]);
glVertex3fv(&Vertices[4]); glVertex3fv(&Vertices[6]);
glVertex3fv(&Vertices[5]); glVertex3fv(&Vertices[7]);
glVertex3fv(&Vertices[0]); glVertex3fv(&Vertices[4]);
glVertex3fv(&Vertices[1]); glVertex3fv(&Vertices[5]);
glVertex3fv(&Vertices[2]); glVertex3fv(&Vertices[6]);
glVertex3fv(&Vertices[3]); glVertex3fv(&Vertices[7]);
glEnd();
}
// ----------------------------------------------------------------------------------------------------------------------------
CBSPTreeNode::CBSPTreeNode()
{
SetDefaults();
}
CBSPTreeNode::~CBSPTreeNode()
{
}
void CBSPTreeNode::SetDefaults()
{
Min = Max = vec3(0.0f);
Depth = 0;
Indices = NULL;
IndicesCount = 0;
IndexBufferObject = 0;
Children[0] = NULL;
Children[1] = NULL;
}
void CBSPTreeNode::InitAABB(const vec3 &Min, const vec3 &Max, int Depth, float MinAABBSize)
{
this->Min = Min;
this->Max = Max;
this->Depth = Depth;
vec3 Mid = (Min + Max) / 2.0f;
vec3 Size = Max - Min;
AABB.Set(Min, Max);
if(Size.x > MinAABBSize || Size.z > MinAABBSize)
{
Children[0] = new CBSPTreeNode();
Children[1] = new CBSPTreeNode();
if(Size.x >= Size.z)
{
Children[0]->InitAABB(vec3(Min.x, Min.y, Min.z), vec3(Mid.x, Max.y, Max.z), Depth + 1, MinAABBSize);
Children[1]->InitAABB(vec3(Mid.x, Min.y, Min.z), vec3(Max.x, Max.y, Max.z), Depth + 1, MinAABBSize);
}
else
{
Children[0]->InitAABB(vec3(Min.x, Min.y, Min.z), vec3(Max.x, Max.y, Mid.z), Depth + 1, MinAABBSize);
Children[1]->InitAABB(vec3(Min.x, Min.y, Mid.z), vec3(Max.x, Max.y, Max.z), Depth + 1, MinAABBSize);
}
}
}
bool CBSPTreeNode::CheckTriangle(CVertex *Vertices, int *Indices, int A, int B, int C)
{
if(AABB.PointInside(Vertices[Indices[A]].Position))
{
if(AABB.PointInside(Vertices[Indices[B]].Position))
{
if(AABB.PointInside(Vertices[Indices[C]].Position))
{
bool BelongsToAChild = false;
if(Children[0] != NULL)
{
BelongsToAChild |= Children[0]->CheckTriangle(Vertices, Indices, A, B, C);
}
if(Children[1] != NULL && !BelongsToAChild)
{
BelongsToAChild |= Children[1]->CheckTriangle(Vertices, Indices, A, B, C);
}
if(!BelongsToAChild)
{
IndicesCount += 3;
}
return true;
}
}
}
return false;
}
void CBSPTreeNode::AllocateMemory()
{
if(IndicesCount > 0)
{
Indices = new int[IndicesCount];
IndicesCount = 0;
}
if(Children[0] != NULL)
{
Children[0]->AllocateMemory();
}
if(Children[1] != NULL)
{
Children[1]->AllocateMemory();
}
}
bool CBSPTreeNode::AddTriangle(CVertex *Vertices, int *Indices, int A, int B, int C)
{
if(AABB.PointInside(Vertices[Indices[A]].Position))
{
if(AABB.PointInside(Vertices[Indices[B]].Position))
{
if(AABB.PointInside(Vertices[Indices[C]].Position))
{
bool BelongsToAChild = false;
if(Children[0] != NULL)
{
BelongsToAChild |= Children[0]->AddTriangle(Vertices, Indices, A, B, C);
}
if(Children[1] != NULL && !BelongsToAChild)
{
BelongsToAChild |= Children[1]->AddTriangle(Vertices, Indices, A, B, C);
}
if(!BelongsToAChild)
{
this->Indices[IndicesCount++] = Indices[A];
this->Indices[IndicesCount++] = Indices[B];
this->Indices[IndicesCount++] = Indices[C];
}
return true;
}
}
}
return false;
}
void CBSPTreeNode::ResetAABB(CVertex *Vertices)
{
float MinY = Min.y, MaxY = Max.y;
Min.y = MaxY;
Max.y = MinY;
if(IndicesCount > 0)
{
for(int i = 0; i < IndicesCount; i++)
{
if(Vertices[Indices[i]].Position.y < Min.y) Min.y = Vertices[Indices[i]].Position.y;
if(Vertices[Indices[i]].Position.y > Max.y) Max.y = Vertices[Indices[i]].Position.y;
}
}
if(Children[0] != NULL)
{
Children[0]->ResetAABB(Vertices);
if(Children[0]->Min.y < Min.y) Min.y = Children[0]->Min.y;
if(Children[0]->Max.y > Max.y) Max.y = Children[0]->Max.y;
}
if(Children[1] != NULL)
{
Children[1]->ResetAABB(Vertices);
if(Children[1]->Min.y < Min.y) Min.y = Children[1]->Min.y;
if(Children[1]->Max.y > Max.y) Max.y = Children[1]->Max.y;
}
AABB.Set(Min, Max);
}
int CBSPTreeNode::InitIndexBufferObject()
{
int GeometryNodesCount = 0;
if(IndicesCount > 0)
{
glGenBuffers(1, &IndexBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, IndicesCount * sizeof(int), Indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
delete [] Indices;
Indices = NULL;
GeometryNodesCount++;
}
if(Children[0] != NULL)
{
GeometryNodesCount += Children[0]->InitIndexBufferObject();
}
if(Children[1] != NULL)
{
GeometryNodesCount += Children[1]->InitIndexBufferObject();
}
return GeometryNodesCount;
}
int CBSPTreeNode::CheckVisibility(CFrustum &Frustum, CBSPTreeNode **VisibleGeometryNodes, int &VisibleGeometryNodesCount)
{
int TrianglesRendered = 0;
Visible = AABB.Visible(Frustum);
if(Visible)
{
if(IndicesCount > 0)
{
Distance = AABB.Distance(Frustum);
VisibleGeometryNodes[VisibleGeometryNodesCount++] = this;
TrianglesRendered += IndicesCount / 3;
}
if(Children[0] != NULL)
{
TrianglesRendered += Children[0]->CheckVisibility(Frustum, VisibleGeometryNodes, VisibleGeometryNodesCount);
}
if(Children[1] != NULL)
{
TrianglesRendered += Children[1]->CheckVisibility(Frustum, VisibleGeometryNodes, VisibleGeometryNodesCount);
}
}
return TrianglesRendered;
}
float CBSPTreeNode::GetDistance()
{
return Distance;
}
void CBSPTreeNode::Render()
{
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject);
glDrawElements(GL_TRIANGLES, IndicesCount, GL_UNSIGNED_INT, NULL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
void CBSPTreeNode::RenderAABB(int Depth)
{
if(Visible)
{
if(Depth == -1 || Depth == this->Depth)
{
AABB.Render();
}
if(Children[0] != NULL)
{
Children[0]->RenderAABB(Depth);
}
if(Children[1] != NULL)
{
Children[1]->RenderAABB(Depth);
}
}
}
void CBSPTreeNode::Destroy()
{
if(Indices != NULL)
{
delete [] Indices;
}
if(IndexBufferObject != 0)
{
glDeleteBuffers(1, &IndexBufferObject);
}
if(Children[0] != NULL)
{
Children[0]->Destroy();
delete Children[0];
}
if(Children[1] != NULL)
{
Children[1]->Destroy();
delete Children[1];
}
SetDefaults();
}
// ----------------------------------------------------------------------------------------------------------------------------
CBSPTree::CBSPTree()
{
SetDefaults();
}
CBSPTree::~CBSPTree()
{
}
void CBSPTree::SetDefaults()
{
Root = NULL;
VisibleGeometryNodes = NULL;
}
void CBSPTree::Init(CVertex *Vertices, int *Indices, int IndicesCount, const vec3 &Min, const vec3 &Max, float MinAABBSize)
{
Destroy();
if(Vertices != NULL && Indices != NULL && IndicesCount > 0)
{
Root = new CBSPTreeNode();
Root->InitAABB(Min, Max, 0, MinAABBSize);
for(int i = 0; i < IndicesCount; i += 3)
{
Root->CheckTriangle(Vertices, Indices, i, i + 1, i + 2);
}
Root->AllocateMemory();
for(int i = 0; i < IndicesCount; i += 3)
{
Root->AddTriangle(Vertices, Indices, i, i + 1, i + 2);
}
Root->ResetAABB(Vertices);
int GeometryNodesCount = Root->InitIndexBufferObject();
VisibleGeometryNodes = new CBSPTreeNode*[GeometryNodesCount];
}
}
void CBSPTree::QuickSortVisibleGeometryNodes(int Left, int Right)
{
float Pivot = VisibleGeometryNodes[(Left + Right) / 2]->GetDistance();
int i = Left, j = Right;
while(i <= j)
{
while(VisibleGeometryNodes[i]->GetDistance() < Pivot) i++;
while(VisibleGeometryNodes[j]->GetDistance() > Pivot) j--;
if(i <= j)
{
if(i != j)
{
CBSPTreeNode *Temp = VisibleGeometryNodes[i];
VisibleGeometryNodes[i] = VisibleGeometryNodes[j];
VisibleGeometryNodes[j] = Temp;
}
i++;
j--;
}
}
if(Left < j)
{
QuickSortVisibleGeometryNodes(Left, j);
}
if(i < Right)
{
QuickSortVisibleGeometryNodes(i, Right);
}
}
int CBSPTree::CheckVisibility(CFrustum &Frustum, bool SortVisibleGeometryNodes)
{
int TrianglesRendered = 0;
VisibleGeometryNodesCount = 0;
if(Root != NULL)
{
TrianglesRendered = Root->CheckVisibility(Frustum, VisibleGeometryNodes, VisibleGeometryNodesCount);
if(SortVisibleGeometryNodes)
{
if(VisibleGeometryNodesCount > 1)
{
QuickSortVisibleGeometryNodes(0, VisibleGeometryNodesCount - 1);
}
}
}
return TrianglesRendered;
}
void CBSPTree::Render(bool VisualizeRenderingOrder)
{
if(VisibleGeometryNodesCount > 0)
{
if(!VisualizeRenderingOrder)
{
for(int i = 0; i < VisibleGeometryNodesCount; i++)
{
VisibleGeometryNodes[i]->Render();
}
}
else
{
for(int i = 0; i < VisibleGeometryNodesCount; i++)
{
float Color = (float)(i + 1) / (float)VisibleGeometryNodesCount;
glColor3f(Color, Color, Color);
VisibleGeometryNodes[i]->Render();
}
}
}
}
void CBSPTree::RenderAABB(int Depth)
{
if(Root != NULL)
{
Root->RenderAABB(Depth);
}
}
void CBSPTree::Destroy()
{
if(Root != NULL)
{
Root->Destroy();
delete Root;
}
if(VisibleGeometryNodes != NULL)
{
delete [] VisibleGeometryNodes;
}
SetDefaults();
}
// ----------------------------------------------------------------------------------------------------------------------------
CTerrain::CTerrain()
{
SetDefaults();
}
CTerrain::~CTerrain()
{
}
void CTerrain::SetDefaults()
{
Size = 0;
SizeP1 = 0;
SizeD2 = 0.0f;
Min = Max = vec3(0.0f);
Heights = NULL;
VerticesCount = 0;
IndicesCount = 0;
VertexBufferObject = 0;
IndexBufferObject = 0;
}
bool CTerrain::LoadTexture2D(char *FileName, float Scale, float Offset)
{
CTexture Texture;
if(!Texture.LoadTexture2D(FileName))
{
return false;
}
if(Texture.GetWidth() != Texture.GetHeight())
{
ErrorLog.Append("Unsupported texture dimensions (%s)!\r\n", FileName);
Texture.Destroy();
return false;
}
Destroy();
Size = Texture.GetWidth();
SizeP1 = Size + 1;
SizeD2 = (float)Size / 2.0f;
VerticesCount = SizeP1 * SizeP1;
float *TextureHeights = new float[Size * Size];
glBindTexture(GL_TEXTURE_2D, Texture);
glGetTexImage(GL_TEXTURE_2D, 0, GL_GREEN, GL_FLOAT, TextureHeights);
glBindTexture(GL_TEXTURE_2D, 0);
Texture.Destroy();
for(int i = 0; i < Size * Size; i++)
{
TextureHeights[i] = TextureHeights[i] * Scale + Offset;
}
Heights = new float[VerticesCount];
int i = 0;
for(int z = 0; z <= Size; z++)
{
for(int x = 0; x <= Size; x++)
{
Heights[i++] = GetHeight(TextureHeights, Size, (float)x - 0.5f, (float)z - 0.5f);
}
}
delete [] TextureHeights;
float *SmoothedHeights = new float[VerticesCount];
i = 0;
for(int z = 0; z <= Size; z++)
{
for(int x = 0; x <= Size; x++)
{
SmoothedHeights[i] = 0.0f;
SmoothedHeights[i] += GetHeight(x - 1, z + 1) + GetHeight(x, z + 1) * 2 + GetHeight(x + 1, z + 1);
SmoothedHeights[i] += GetHeight(x - 1, z) * 2 + GetHeight(x, z) * 3 + GetHeight(x + 1, z) * 2;
SmoothedHeights[i] += GetHeight(x - 1, z - 1) + GetHeight(x, z - 1) * 2 + GetHeight(x + 1, z - 1);
SmoothedHeights[i] /= 15.0f;
i++;
}
}
delete [] Heights;
Heights = SmoothedHeights;
Min.x = Min.z = -SizeD2;
Max.x = Max.z = SizeD2;
Min.y = Max.y = Heights[0];
for(int i = 1; i < VerticesCount; i++)
{
if(Heights[i] < Min.y) Min.y = Heights[i];
if(Heights[i] > Max.y) Max.y = Heights[i];
}
CVertex *Vertices = new CVertex[VerticesCount];
i = 0;
for(int z = 0; z <= Size; z++)
{
for(int x = 0; x <= Size; x++)
{
Vertices[i].Position = vec3((float)x - SizeD2, Heights[i], SizeD2 - (float)z);
Vertices[i].Normal = normalize(vec3(GetHeight(x - 1, z) - GetHeight(x + 1, z), 2.0f, GetHeight(x, z + 1) - GetHeight(x, z - 1)));
i++;
}
}
glGenBuffers(1, &VertexBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, VerticesCount * sizeof(CVertex), Vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
IndicesCount = Size * Size * 2 * 3;
int *Indices = new int[IndicesCount];
i = 0;
for(int z = 0; z < Size; z++)
{
for(int x = 0; x < Size; x++)
{
Indices[i++] = GetIndex(x, z);
Indices[i++] = GetIndex(x + 1, z);
Indices[i++] = GetIndex(x + 1, z + 1);
Indices[i++] = GetIndex(x + 1, z + 1);
Indices[i++] = GetIndex(x, z + 1);
Indices[i++] = GetIndex(x, z);
}
}
glGenBuffers(1, &IndexBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, IndicesCount * sizeof(int), Indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
BSPTree.Init(Vertices, Indices, IndicesCount, Min, Max);
delete [] Vertices;
delete [] Indices;
return true;
}
bool CTerrain::LoadBinary(char *FileName)
{
CString DirectoryFileName = ModuleDirectory + FileName;
FILE *File;
if(fopen_s(&File, DirectoryFileName, "rb") != 0)
{
ErrorLog.Append("Error opening file " + DirectoryFileName + "!\r\n");
return false;
}
int Size;
if(fread(&Size, sizeof(int), 1, File) != 1 || Size <= 0)
{
ErrorLog.Append("Error reading file " + DirectoryFileName + "!\r\n");
fclose(File);
return false;
}
Destroy();
this->Size = Size;
SizeP1 = Size + 1;
SizeD2 = (float)Size / 2.0f;
VerticesCount = SizeP1 * SizeP1;
Heights = new float[VerticesCount];
if(fread(Heights, sizeof(float), VerticesCount, File) != VerticesCount)
{
ErrorLog.Append("Error reading file " + DirectoryFileName + "!\r\n");
fclose(File);
Destroy();
return false;
}
fclose(File);
Min.x = Min.z = -SizeD2;
Max.x = Max.z = SizeD2;
Min.y = Max.y = Heights[0];
for(int i = 1; i < VerticesCount; i++)
{
if(Heights[i] < Min.y) Min.y = Heights[i];
if(Heights[i] > Max.y) Max.y = Heights[i];
}
CVertex *Vertices = new CVertex[VerticesCount];
int i = 0;
for(int z = 0; z <= Size; z++)
{
for(int x = 0; x <= Size; x++)
{
Vertices[i].Position = vec3((float)x - SizeD2, Heights[i], SizeD2 - (float)z);
Vertices[i].Normal = normalize(vec3(GetHeight(x - 1, z) - GetHeight(x + 1, z), 2.0f, GetHeight(x, z + 1) - GetHeight(x, z - 1)));
i++;
}
}
glGenBuffers(1, &VertexBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferObject);
glBufferData(GL_ARRAY_BUFFER, VerticesCount * sizeof(CVertex), Vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
IndicesCount = Size * Size * 2 * 3;
int *Indices = new int[IndicesCount];
i = 0;
for(int z = 0; z < Size; z++)
{
for(int x = 0; x < Size; x++)
{
Indices[i++] = GetIndex(x, z);
Indices[i++] = GetIndex(x + 1, z);
Indices[i++] = GetIndex(x + 1, z + 1);
Indices[i++] = GetIndex(x + 1, z + 1);
Indices[i++] = GetIndex(x, z + 1);
Indices[i++] = GetIndex(x, z);
}
}
glGenBuffers(1, &IndexBufferObject);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, IndicesCount * sizeof(int), Indices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
BSPTree.Init(Vertices, Indices, IndicesCount, Min, Max);
delete [] Vertices;
delete [] Indices;
return true;
}
bool CTerrain::SaveBinary(char *FileName)
{
CString DirectoryFileName = ModuleDirectory + FileName;
FILE *File;
if(fopen_s(&File, DirectoryFileName, "wb+") != 0)
{
return false;
}
fwrite(&Size, sizeof(int), 1, File);
fwrite(Heights, sizeof(float), VerticesCount, File);
fclose(File);
return true;
}
int CTerrain::CheckVisibility(CFrustum &Frustum, bool SortVisibleGeometryNodes)
{
return BSPTree.CheckVisibility(Frustum, SortVisibleGeometryNodes);
}
void CTerrain::Render(bool VisualizeRenderingOrder)
{
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferObject);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(CVertex), (void*)(sizeof(vec3) * 0));
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, sizeof(CVertex), (void*)(sizeof(vec3) * 1));
BSPTree.Render(VisualizeRenderingOrder);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void CTerrain::RenderSlow()
{
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferObject);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(CVertex), (void*)(sizeof(vec3) * 0));
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, sizeof(CVertex), (void*)(sizeof(vec3) * 1));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject);
glDrawElements(GL_TRIANGLES, IndicesCount, GL_UNSIGNED_INT, NULL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void CTerrain::RenderSlowToShadowMap()
{
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferObject);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(CVertex), (void*)(sizeof(vec3) * 0));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferObject);
glDrawElements(GL_TRIANGLES, IndicesCount, GL_UNSIGNED_INT, NULL);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void CTerrain::RenderAABB(int Depth)
{
BSPTree.RenderAABB(Depth);
}
void CTerrain::Destroy()
{
if(Heights != NULL)
{
delete [] Heights;
}
if(VertexBufferObject != 0)
{
glDeleteBuffers(1, &VertexBufferObject);
}
if(IndexBufferObject != 0)
{
glDeleteBuffers(1, &IndexBufferObject);
}
BSPTree.Destroy();
SetDefaults();
}
int CTerrain::GetSize()
{
return Size;
}
vec3 CTerrain::GetMin()
{
return Min;
}
vec3 CTerrain::GetMax()
{
return Max;
}
void CTerrain::GetMinMax(mat4x4 &ViewMatrix, vec3 &Min, vec3 &Max)
{
int i = 0;
for(int z = 0; z <= Size; z++)
{
for(int x = 0; x <= Size; x++)
{
vec4 Position = ViewMatrix * vec4((float)x - SizeD2, Heights[i], SizeD2 - (float)z, 1.0f);
if(i == 0)
{
Min.x = Max.x = Position.x;
Min.y = Max.y = Position.y;
Min.z = Max.z = Position.z;
}
else
{
if(Position.x < Min.x) Min.x = Position.x;
if(Position.y < Min.y) Min.y = Position.y;
if(Position.z < Min.z) Min.z = Position.z;
if(Position.x > Max.x) Max.x = Position.x;
if(Position.y > Max.y) Max.y = Position.y;
if(Position.z > Max.z) Max.z = Position.z;
}
i++;
}
}
}
int CTerrain::GetTrianglesCount()
{
return IndicesCount / 3;
}
int CTerrain::GetIndex(int X, int Z)
{
return SizeP1 * Z + X;
}
float CTerrain::GetHeight(int X, int Z)
{
return Heights[GetIndex(X < 0 ? 0 : X > Size ? Size : X, Z < 0 ? 0 : Z > Size ? Size : Z)];
}
float CTerrain::GetHeight(float X, float Z)
{
Z = -Z;
X += SizeD2;
Z += SizeD2;
float Size = (float)this->Size;
if(X < 0.0f) X = 0.0f;
if(X > Size) X = Size;
if(Z < 0.0f) Z = 0.0f;
if(Z > Size) Z = Size;
int ix = (int)X, ixp1 = ix + 1;
int iz = (int)Z, izp1 = iz + 1;
float fx = X - (float)ix;
float fz = Z - (float)iz;
float a = GetHeight(ix, iz);
float b = GetHeight(ixp1, iz);
float c = GetHeight(ix, izp1);
float d = GetHeight(ixp1, izp1);
float ab = a + (b - a) * fx;
float cd = c + (d - c) * fx;
return ab + (cd - ab) * fz;
}
float CTerrain::GetHeight(float *Heights, int Size, float X, float Z)
{
float SizeM1F = (float)Size - 1.0f;
if(X < 0.0f) X = 0.0f;
if(X > SizeM1F) X = SizeM1F;
if(Z < 0.0f) Z = 0.0f;
if(Z > SizeM1F) Z = SizeM1F;
int ix = (int)X, ixp1 = ix + 1;
int iz = (int)Z, izp1 = iz + 1;
int SizeM1 = Size - 1;
if(ixp1 > SizeM1) ixp1 = SizeM1;
if(izp1 > SizeM1) izp1 = SizeM1;
float fx = X - (float)ix;
float fz = Z - (float)iz;
int izMSize = iz * Size, izp1MSize = izp1 * Size;
float a = Heights[izMSize + ix];
float b = Heights[izMSize + ixp1];
float c = Heights[izp1MSize + ix];
float d = Heights[izp1MSize + ixp1];
float ab = a + (b - a) * fx;
float cd = c + (d - c) * fx;
return ab + (cd - ab) * fz;
}
// ----------------------------------------------------------------------------------------------------------------------------
COpenGLRenderer::COpenGLRenderer()
{
LightAngle = 22.5f;
Wireframe = false;
DisplayShadowMap = false;
RenderAABB = false;
VisualizeRenderingOrder = false;
SortVisibleGeometryNodes = true;
RenderSlow = false;
Depth = -1;
}
COpenGLRenderer::~COpenGLRenderer()
{
}
bool COpenGLRenderer::Init()
{
bool Error = false;
if(!GLEW_EXT_framebuffer_object)
{
ErrorLog.Append("GL_EXT_framebuffer_object not supported!\r\n");
Error = true;
}
Error |= !Shader.Load("glsl120shader.vs", "glsl120shader.fs");
Error |= !Terrain.LoadBinary("terrain1.bin");
if(Error)
{
return false;
}
Shader.UniformLocations = new GLuint[2];
Shader.UniformLocations[0] = glGetUniformLocation(Shader, "ShadowMatrix");
Shader.UniformLocations[1] = glGetUniformLocation(Shader, "LightDirection");
glUseProgram(Shader);
glUniform1i(glGetUniformLocation(Shader, "ShadowMap"), 0);
glUniform1i(glGetUniformLocation(Shader, "RotationTexture"), 1);
glUniform1f(glGetUniformLocation(Shader, "Scale"), 1.0f / 64.0f);
glUniform1f(glGetUniformLocation(Shader, "Radius"), 1.0f / 1024.0f);
glUseProgram(0);
ShadowMapSize = SHADOW_MAP_SIZE > gl_max_texture_size ? gl_max_texture_size : SHADOW_MAP_SIZE;
glGenTextures(1, &ShadowMap);
glBindTexture(GL_TEXTURE_2D, ShadowMap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, ShadowMapSize, ShadowMapSize, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
srand(GetTickCount());
vec4 *RotationTextureData = new vec4[4096];
float RandomAngle = 3.14f * 2.0f * (float)rand() / (float)RAND_MAX;
for(int i = 0; i < 4096; i++)
{
RotationTextureData[i].x = cos(RandomAngle);
RotationTextureData[i].y = sin(RandomAngle);
RotationTextureData[i].z = -RotationTextureData[i].y;
RotationTextureData[i].w = RotationTextureData[i].x;
RotationTextureData[i] *= 0.5f;
RotationTextureData[i] += 0.5f;
RandomAngle += 3.14f * 2.0f * (float)rand() / (float)RAND_MAX;
}
glGenTextures(1, &RotationTexture);
glBindTexture(GL_TEXTURE_2D, RotationTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_FLOAT, RotationTextureData);
glBindTexture(GL_TEXTURE_2D, 0);
delete [] RotationTextureData;
glGenFramebuffersEXT(1, &FBO);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
glDrawBuffers(0, NULL); glReadBuffer(GL_NONE);
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ShadowMap, 0);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
RenderShadowMap();
float Height = Terrain.GetHeight(0.0f, 0.0f);
Camera.Look(vec3(0.0f, Height + 1.75f, 0.0f), vec3(0.0f, Height + 1.75f, -1.0f));
return true;
}
void COpenGLRenderer::Render()
{
glViewport(0, 0, Width, Height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&Camera.ProjectionMatrix);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&Camera.ViewMatrix);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
if(!RenderSlow)
{
Terrain.CheckVisibility(Camera.Frustum, SortVisibleGeometryNodes);
}
if(Wireframe)
{
glColor3f(0.0f, 0.0f, 0.0f);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
if(RenderSlow)
{
Terrain.RenderSlow();
}
else
{
Terrain.Render();
}
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
glColor3f(1.0f, 1.0f, 1.0f);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, ShadowMap);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, RotationTexture);
glUseProgram(Shader);
if(RenderSlow)
{
Terrain.RenderSlow();
}
else
{
Terrain.Render(VisualizeRenderingOrder);
}
glUseProgram(0);
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
if(!RenderSlow && RenderAABB)
{
glColor3f(0.0f, 1.0f, 0.0f);
Terrain.RenderAABB(Depth);
}
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
if(DisplayShadowMap)
{
glViewport(16, 16, 256, 256);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glColor3f(1.0f, 1.0f, 1.0f);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ShadowMap);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f);
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
}
void COpenGLRenderer::Animate(float FrameTime)
{
}
void COpenGLRenderer::Resize(int Width, int Height)
{
this->Width = Width;
this->Height = Height;
Camera.SetPerspective(45.0f, (float)Width / (float)Height, 0.125f, 1024.0f);
}
void COpenGLRenderer::Destroy()
{
Shader.Destroy();
Terrain.Destroy();
glDeleteTextures(1, &ShadowMap);
glDeleteTextures(1, &RotationTexture);
if(GLEW_EXT_framebuffer_object)
{
glDeleteFramebuffersEXT(1, &FBO);
}
}
void COpenGLRenderer::RenderShadowMap()
{
vec3 LightPosition = rotate(vec3((float)Terrain.GetSize(), 0.0f, 0.0f), -LightAngle, vec3(0.0f, 1.0f, -1.0f));
vec3 LightDirection = normalize(LightPosition);
LightViewMatrix = look(LightPosition, vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f));
vec3 Min, Max;
Terrain.GetMinMax(LightViewMatrix, Min, Max);
LightProjectionMatrix = ortho(Min.x, Max.x, Min.y, Max.y, -Max.z, -Min.z);
ShadowMatrix = BiasMatrix * LightProjectionMatrix * LightViewMatrix;
glUseProgram(Shader);
glUniformMatrix4fv(Shader.UniformLocations[0], 1, GL_FALSE, &ShadowMatrix);
glUniform3fv(Shader.UniformLocations[1], 1, &LightDirection);
glUseProgram(0);
glViewport(0, 0, ShadowMapSize, ShadowMapSize);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
glClear(GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&LightProjectionMatrix);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&LightViewMatrix);
glEnable(GL_DEPTH_TEST);
Terrain.RenderSlowToShadowMap();
glDisable(GL_DEPTH_TEST);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
void COpenGLRenderer::CheckCameraTerrainPosition(vec3 &Movement)
{
vec3 CameraPosition = Camera.Reference + Movement, Min = Terrain.GetMin(), Max = Terrain.GetMax();
if(CameraPosition.x < Min.x) Movement += vec3(Min.x - CameraPosition.x, 0.0f, 0.0f);
if(CameraPosition.x > Max.x) Movement += vec3(Max.x - CameraPosition.x, 0.0f, 0.0f);
if(CameraPosition.z < Min.z) Movement += vec3(0.0f, 0.0f, Min.z - CameraPosition.z);
if(CameraPosition.z > Max.z) Movement += vec3(0.0f, 0.0f, Max.z - CameraPosition.z);
CameraPosition = Camera.Reference + Movement;
float Height = Terrain.GetHeight(CameraPosition.x, CameraPosition.z);
Movement += vec3(0.0f, Height + 1.75f - Camera.Reference.y, 0.0f);
}
void COpenGLRenderer::CheckCameraKeys(float FrameTime)
{
BYTE Keys = 0x00;
if(GetKeyState('W') & 0x80) Keys |= 0x01;
if(GetKeyState('S') & 0x80) Keys |= 0x02;
if(GetKeyState('A') & 0x80) Keys |= 0x04;
if(GetKeyState('D') & 0x80) Keys |= 0x08;
// if(GetKeyState('R') & 0x80) Keys |= 0x10;
// if(GetKeyState('F') & 0x80) Keys |= 0x20;
if(GetKeyState(VK_SHIFT) & 0x80) Keys |= 0x40;
if(GetKeyState(VK_CONTROL) & 0x80) Keys |= 0x80;
if(Keys & 0x3F)
{
vec3 Movement = Camera.OnKeys(Keys, FrameTime * 0.5f);
CheckCameraTerrainPosition(Movement);
Camera.Move(Movement);
}
}
void COpenGLRenderer::OnKeyDown(UINT Key)
{
switch(Key)
{
case VK_F1:
Wireframe = !Wireframe;
break;
case VK_F2:
DisplayShadowMap = !DisplayShadowMap;
break;
case VK_F3:
RenderAABB = !RenderAABB;
break;
case VK_F4:
VisualizeRenderingOrder = !VisualizeRenderingOrder;
break;
case VK_F5:
SortVisibleGeometryNodes = !SortVisibleGeometryNodes;
break;
case VK_F6:
RenderSlow = !RenderSlow;
break;
case VK_F7:
Terrain.SaveBinary("terrain-saved.bin");
break;
case '1':
if(Terrain.LoadBinary("terrain1.bin")) { vec3 Movement; CheckCameraTerrainPosition(Movement); Camera.Move(Movement); RenderShadowMap(); }
break;
case '2':
if(Terrain.LoadTexture2D("terrain2.jpg", 32.0f, -16.0f)) { vec3 Movement; CheckCameraTerrainPosition(Movement); Camera.Move(Movement); RenderShadowMap(); }
break;
case '3':
if(Terrain.LoadTexture2D("terrain3.jpg", 128.0f, -64.0f)) { vec3 Movement; CheckCameraTerrainPosition(Movement); Camera.Move(Movement); RenderShadowMap(); }
break;
case '4':
if(Terrain.LoadTexture2D("terrain4.jpg", 128.0f, -64.0f)) { vec3 Movement; CheckCameraTerrainPosition(Movement); Camera.Move(Movement); RenderShadowMap(); }
break;
case VK_MULTIPLY:
Depth++;
break;
case VK_DIVIDE:
if(Depth > -1) Depth--;
break;
case VK_ADD:
LightAngle += 3.75f;
RenderShadowMap();
break;
case VK_SUBTRACT:
LightAngle -= 3.75f;
RenderShadowMap();
break;
}
}
void COpenGLRenderer::OnLButtonDown(int X, int Y)
{
LastClickedX = X;
LastClickedY = Y;
}
void COpenGLRenderer::OnLButtonUp(int X, int Y)
{
if(X == LastClickedX && Y == LastClickedY)
{
}
}
void COpenGLRenderer::OnMouseMove(int X, int Y)
{
if(GetKeyState(VK_RBUTTON) & 0x80)
{
Camera.OnMouseMove(LastX - X, LastY - Y);
}
LastX = X;
LastY = Y;
}
void COpenGLRenderer::OnMouseWheel(short zDelta)
{
Camera.OnMouseWheel(zDelta);
}
void COpenGLRenderer::OnRButtonDown(int X, int Y)
{
LastClickedX = X;
LastClickedY = Y;
}
void COpenGLRenderer::OnRButtonUp(int X, int Y)
{
if(X == LastClickedX && Y == LastClickedY)
{
}
}
// ----------------------------------------------------------------------------------------------------------------------------
CString ModuleDirectory, ErrorLog;
// ----------------------------------------------------------------------------------------------------------------------------
void GetModuleDirectory()
{
char *moduledirectory = new char[256];
GetModuleFileName(GetModuleHandle(NULL), moduledirectory, 256);
*(strrchr(moduledirectory, '\\') + 1) = 0;
ModuleDirectory = moduledirectory;
delete [] moduledirectory;
}
// ----------------------------------------------------------------------------------------------------------------------------
COpenGLView::COpenGLView()
{
}
COpenGLView::~COpenGLView()
{
}
bool COpenGLView::Init(HINSTANCE hInstance, char *Title, int Width, int Height, int Samples)
{
this->Title.Set(Title);
this->Width = Width;
this->Height = Height;
WNDCLASSEX WndClassEx;
memset(&WndClassEx, 0, sizeof(WNDCLASSEX));
WndClassEx.cbSize = sizeof(WNDCLASSEX);
WndClassEx.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
WndClassEx.lpfnWndProc = WndProc;
WndClassEx.hInstance = hInstance;
WndClassEx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClassEx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
WndClassEx.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClassEx.lpszClassName = "Win32OpenGLWindowClass";
if(RegisterClassEx(&WndClassEx) == 0)
{
ErrorLog.Set("RegisterClassEx failed!");
return false;
}
DWORD Style = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
hWnd = CreateWindowEx(WS_EX_APPWINDOW, WndClassEx.lpszClassName, Title, Style, 0, 0, Width, Height, NULL, NULL, hInstance, NULL);
if(hWnd == NULL)
{
ErrorLog.Set("CreateWindowEx failed!");
return false;
}
HDC hDC = GetDC(hWnd);
if(hDC == NULL)
{
ErrorLog.Set("GetDC failed!");
return false;
}
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE;
int PixelFormat = ChoosePixelFormat(hDC, &pfd);
if(PixelFormat == 0)
{
ErrorLog.Set("ChoosePixelFormat failed!");
return false;
}
static int MSAAPixelFormat = 0;
if(SetPixelFormat(hDC, MSAAPixelFormat == 0 ? PixelFormat : MSAAPixelFormat, &pfd) == FALSE)
{
ErrorLog.Set("SetPixelFormat failed!");
return false;
}
hGLRC = wglCreateContext(hDC);
if(hGLRC == NULL)
{
ErrorLog.Set("wglCreateContext failed!");
return false;
}
if(wglMakeCurrent(hDC, hGLRC) == FALSE)
{
ErrorLog.Set("wglMakeCurrent failed!");
return false;
}
if(glewInit() != GLEW_OK)
{
ErrorLog.Set("glewInit failed!");
return false;
}
if(!GLEW_VERSION_2_1)
{
ErrorLog.Set("OpenGL 2.1 not supported!");
return false;
}
if(MSAAPixelFormat == 0 && Samples > 0)
{
if(GLEW_ARB_multisample && WGLEW_ARB_pixel_format)
{
while(Samples > 0)
{
UINT NumFormats = 0;
int PFAttribs[] =
{
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
WGL_SAMPLES_ARB, Samples,
0
};
if(wglChoosePixelFormatARB(hDC, PFAttribs, NULL, 1, &MSAAPixelFormat, &NumFormats) == TRUE && NumFormats > 0) break;
Samples--;
}
wglDeleteContext(hGLRC);
DestroyWindow(hWnd);
UnregisterClass(WndClassEx.lpszClassName, hInstance);
return Init(hInstance, Title, Width, Height, Samples);
}
else
{
Samples = 0;
}
}
this->Samples = Samples;
GetModuleDirectory();
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_max_texture_size);
if(GLEW_EXT_texture_filter_anisotropic)
{
glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_max_texture_max_anisotropy_ext);
}
if(WGLEW_EXT_swap_control)
{
wglSwapIntervalEXT(0);
}
MSAA.Set("MSAA %dx", Samples);
ATF.Set("ATF %dx", gl_max_texture_max_anisotropy_ext);
Renderer.Set((char*)glGetString(GL_RENDERER));
return OpenGLRenderer.Init();
}
void COpenGLView::Show(bool Maximized)
{
RECT dRect, wRect, cRect;
GetWindowRect(GetDesktopWindow(), &dRect);
GetWindowRect(hWnd, &wRect);
GetClientRect(hWnd, &cRect);
wRect.right += Width - cRect.right;
wRect.bottom += Height - cRect.bottom;
wRect.right -= wRect.left;
wRect.bottom -= wRect.top;
wRect.left = dRect.right / 2 - wRect.right / 2;
wRect.top = dRect.bottom / 2 - wRect.bottom / 2;
MoveWindow(hWnd, wRect.left, wRect.top, wRect.right, wRect.bottom, FALSE);
ShowWindow(hWnd, Maximized ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL);
}
void COpenGLView::MessageLoop()
{
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
}
void COpenGLView::Destroy()
{
if(GLEW_VERSION_2_1)
{
OpenGLRenderer.Destroy();
}
wglDeleteContext(hGLRC);
DestroyWindow(hWnd);
}
void COpenGLView::OnKeyDown(UINT Key)
{
OpenGLRenderer.OnKeyDown(Key);
}
void COpenGLView::OnLButtonDown(int X, int Y)
{
OpenGLRenderer.OnLButtonDown(X, Y);
}
void COpenGLView::OnLButtonUp(int X, int Y)
{
OpenGLRenderer.OnLButtonUp(X, Y);
}
void COpenGLView::OnMouseMove(int X, int Y)
{
OpenGLRenderer.OnMouseMove(X, Y);
}
void COpenGLView::OnMouseWheel(short zDelta)
{
OpenGLRenderer.OnMouseWheel(zDelta);
}
void COpenGLView::OnPaint()
{
static DWORD LastFPSTime = GetTickCount(), LastFrameTime = LastFPSTime, Frame = 0;
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
DWORD Time = GetTickCount();
float FrameTime = (Time - LastFrameTime) * 0.001f;
LastFrameTime = Time;
if(Time - LastFPSTime > 1000)
{
FPS.Set("FPS: %d", Frame);
SetWindowText(hWnd, Title + " - " + Resolution + ", " + MSAA + ", " + ATF + ", " + FPS + " - " + Renderer);
LastFPSTime = Time;
Frame = 0;
}
else
{
Frame++;
}
OpenGLRenderer.CheckCameraKeys(FrameTime);
OpenGLRenderer.Render();
OpenGLRenderer.Animate(FrameTime);
SwapBuffers(hDC);
EndPaint(hWnd, &ps);
InvalidateRect(hWnd, NULL, FALSE);
}
void COpenGLView::OnRButtonDown(int X, int Y)
{
OpenGLRenderer.OnRButtonDown(X, Y);
}
void COpenGLView::OnRButtonUp(int X, int Y)
{
OpenGLRenderer.OnRButtonUp(X, Y);
}
void COpenGLView::OnSize(int Width, int Height)
{
this->Width = Width;
this->Height = Height;
Resolution.Set("%dx%d", Width, Height);
OpenGLRenderer.Resize(Width, Height);
}
// ----------------------------------------------------------------------------------------------------------------------------
COpenGLView OpenGLView;
// ----------------------------------------------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch(uiMsg)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_KEYDOWN:
OpenGLView.OnKeyDown((UINT)wParam);
break;
case WM_LBUTTONDOWN:
OpenGLView.OnLButtonDown(LOWORD(lParam), HIWORD(lParam));
break;
case WM_LBUTTONUP:
OpenGLView.OnLButtonUp(LOWORD(lParam), HIWORD(lParam));
break;
case WM_MOUSEMOVE:
OpenGLView.OnMouseMove(LOWORD(lParam), HIWORD(lParam));
break;
case WM_MOUSWHEEL:
OpenGLView.OnMouseWheel(HIWORD(wParam));
break;
case WM_PAINT:
OpenGLView.OnPaint();
break;
case WM_RBUTTONDOWN:
OpenGLView.OnRButtonDown(LOWORD(lParam), HIWORD(lParam));
break;
case WM_RBUTTONUP:
OpenGLView.OnRButtonUp(LOWORD(lParam), HIWORD(lParam));
break;
case WM_SIZE:
OpenGLView.OnSize(LOWORD(lParam), HIWORD(lParam));
break;
default:
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
return 0;
}
// ----------------------------------------------------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR sCmdLine, int iShow)
{
char *AppName = "Terrain, BSP tree, frustum culling, sorting, shadow mapping";
if(OpenGLView.Init(hInstance, AppName, 800, 600, 4))
{
OpenGLView.Show();
OpenGLView.MessageLoop();
}
else
{
MessageBox(NULL, ErrorLog, AppName, MB_OK | MB_ICONERROR);
}
OpenGLView.Destroy();
return 0;
}
glsl120shader.vs
#version 120
uniform mat4x4 ShadowMatrix;
varying vec3 var_Normal;
void main()
{
gl_FrontColor = gl_Color;
gl_TexCoord[0] = ShadowMatrix * gl_Vertex;
var_Normal = gl_Normal;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
glsl120shader.fs
#version 120
uniform sampler2D ShadowMap, RotationTexture;
uniform vec3 LightDirection;
uniform float Scale, Radius;
varying vec3 var_Normal;
vec2 PoissonDisk[16] = vec2[](
vec2( -0.94201624, -0.39906216 ),
vec2( 0.94558609, -0.76890725 ),
vec2( -0.094184101, -0.92938870 ),
vec2( 0.34495938, 0.29387760 ),
vec2( -0.91588581, 0.45771432 ),
vec2( -0.81544232, -0.87912464 ),
vec2( -0.38277543, 0.27676845 ),
vec2( 0.97484398, 0.75648379 ),
vec2( 0.44323325, -0.97511554 ),
vec2( 0.53742981, -0.47373420 ),
vec2( -0.26496911, -0.41893023 ),
vec2( 0.79197514, 0.19090188 ),
vec2( -0.24188840, 0.99706507 ),
vec2( -0.81409955, 0.91437590 ),
vec2( 0.19984126, 0.78641367 ),
vec2( 0.14383161, -0.14100790 )
);
void main()
{
vec3 ShadowTexCoord = gl_TexCoord[0].xyz / gl_TexCoord[0].w;
ShadowTexCoord.z -= 0.005;
vec4 ScaleRotationVector = (texture2D(RotationTexture, gl_FragCoord.st * Scale) * 2.0 - 1.0) * Radius;
mat2x2 ScaleRotationMatrix = mat2x2(ScaleRotationVector.xy, ScaleRotationVector.zw);
float Shadow = 0.0;
for(int i = 0; i < 16; i++)
{
float Depth = texture2D(ShadowMap, ShadowTexCoord.st + ScaleRotationMatrix * PoissonDisk[i]).r;
if(ShadowTexCoord.z < Depth)
{
Shadow += 1.0;
}
}
Shadow /= 16.0;
vec3 Normal = normalize(var_Normal);
float NdotLD = max(0.0, dot(Normal, LightDirection));
gl_FragColor = vec4(gl_Color.rgb * (0.25 + 0.75 * NdotLD * Shadow), 1.0);
}
Download
|