3D C/C++ tutorials - OpenGL 2.1 - Deferred rendering - FXAA
3D C/C++ tutorials -> OpenGL 2.1 -> Deferred rendering - FXAA
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.
To compile and run these tutorials some or all of these libraries are required: FreeImage 3.16.0, GLEW 1.11.0, GLUT 3.7.6 / GLUT for Dev-C++, GLM 0.9.5.4
opengl_21_tutorials_win32_framework.h
// ----------------------------------------------------------------------------------------------------------------------------
//
// Version 2.02
//
// ----------------------------------------------------------------------------------------------------------------------------

#include <windows.h>

#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, Position;

public:
    CBuffer();
    ~CBuffer();

    void AddData(void *Data, int DataSize);
    void Empty();
    void *GetData();
    int GetDataSize();

private:
    void SetDefaults();
};

// ----------------------------------------------------------------------------------------------------------------------------

extern int gl_max_texture_size, gl_max_texture_max_anisotropy_ext;

// ----------------------------------------------------------------------------------------------------------------------------

class CTexture
{
protected:
    GLuint Texture;

public:
    CTexture();
    ~CTexture();

    operator GLuint ();

    bool LoadTexture2D(char *FileName);
    bool LoadTextureCubeMap(char **FileNames);
    void Destroy();

protected:
    FIBITMAP *CTexture::GetBitmap(char *FileName, int &Width, int &Height, int &BPP);
};

// ----------------------------------------------------------------------------------------------------------------------------

class CShaderProgram
{
protected:
    GLuint VertexShader, FragmentShader, Program;

public:
    GLuint *UniformLocations, *AttribLocations;

public:
    CShaderProgram();
    ~CShaderProgram();

    operator GLuint ();

    bool Load(char *VertexShaderFileName, char *FragmentShaderFileName);
    void Destroy();

protected:
    GLuint LoadShader(char *FileName, GLenum Type);
    void SetDefaults();
};

// ----------------------------------------------------------------------------------------------------------------------------

class CCamera
{
protected:
    mat4x4 *ViewMatrix, *ViewMatrixInverse;

public:
    vec3 X, Y, Z, Position, Reference;

    CCamera();
    ~CCamera();

    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 SetViewMatrixPointer(float *ViewMatrix, float *ViewMatrixInverse = NULL);

private:
    void CalculateViewMatrix();
};

// ----------------------------------------------------------------------------------------------------------------------------

class COpenGLRenderer
{
protected:
    int Width, Height;
    mat3x3 NormalMatrix;
    mat4x4 ModelMatrix, ViewMatrix, ProjectionMatrix, ProjectionBiasMatrixInverse;

protected:
    CTexture Texture[3];
    CShaderProgram Preprocess, DeferredLighting, FXAA;
    GLuint ColorBuffers[2], NormalBuffer, DepthBuffer;
    GLuint VBO, FBO;

public:
    bool CalculateFXAA, Pause;
    vec3 LightColors[4], LightPositions[4];

public:
    CString Text;

public:
    COpenGLRenderer();
    ~COpenGLRenderer();

    bool Init();
    void Render(float FrameTime);
    void Resize(int Width, int Height);
    void Destroy();

protected:
    void InitArrayBuffers();
};

// ----------------------------------------------------------------------------------------------------------------------------

class COpenGLView
{
protected:
    char *Title;
    int Width, Height, Samples;
    HWND hWnd;
    HGLRC hGLRC;

protected:
    int LastX, LastY;

public:
    COpenGLView();
    ~COpenGLView();

    bool Init(HINSTANCE hInstance, char *Title, int Width, int Height, int Samples);
    void Show(bool Maximized = false);
    void MessageLoop();
    void Destroy();

    void OnKeyDown(UINT Key);
    void OnMouseMove(int X, int Y);
    void OnMouseWheel(short zDelta);
    void OnPaint();
    void OnRButtonDown(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::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()
{
    delete [] Buffer;

    SetDefaults();
}

void *CBuffer::GetData()
{
    return Buffer;
}

int CBuffer::GetDataSize()
{
    return Position;
}

void CBuffer::SetDefaults()
{
    Buffer = NULL;

    BufferSize = 0;
    Position = 0;
}

// ----------------------------------------------------------------------------------------------------------------------------

int gl_max_texture_size = 0, gl_max_texture_max_anisotropy_ext = 0;

// ----------------------------------------------------------------------------------------------------------------------------

CTexture::CTexture()
{
    Texture = 0;
}

CTexture::~CTexture()
{
}

CTexture::operator GLuint ()
{
    return Texture;
}

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);

    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);

    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()
{
    glDeleteTextures(1, &Texture);
    Texture = 0;
}

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;
}

// ----------------------------------------------------------------------------------------------------------------------------

CShaderProgram::CShaderProgram()
{
    SetDefaults();
}

CShaderProgram::~CShaderProgram()
{
}

CShaderProgram::operator GLuint ()
{
    return Program;
}

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()
{
    glDetachShader(Program, VertexShader);
    glDetachShader(Program, FragmentShader);

    glDeleteShader(VertexShader);
    glDeleteShader(FragmentShader);

    glDeleteProgram(Program);

    delete [] UniformLocations;
    delete [] AttribLocations;

    SetDefaults();
}

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;
}

void CShaderProgram::SetDefaults()
{
    VertexShader = 0;
    FragmentShader = 0;

    Program = 0;

    UniformLocations = NULL;
    AttribLocations = NULL;
}

// ----------------------------------------------------------------------------------------------------------------------------

CCamera::CCamera()
{
    ViewMatrix = NULL;
    ViewMatrixInverse = NULL;

    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);
}

CCamera::~CCamera()
{
}

void CCamera::Look(const vec3 &Position, const vec3 &Reference, bool RotateAroundReference)
{
    this->Position = Position;
    this->Reference = Reference;

    Z = normalize(Position - Reference);
    X = normalize(cross(vec3(0.0f, 1.0f, 0.0f), Z));
    Y = cross(Z, X);

    if(!RotateAroundReference)
    {
        this->Reference = this->Position;
        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::SetViewMatrixPointer(float *ViewMatrix, float *ViewMatrixInverse)
{
    this->ViewMatrix = (mat4x4*)ViewMatrix;
    this->ViewMatrixInverse = (mat4x4*)ViewMatrixInverse;

    CalculateViewMatrix();
}

void CCamera::CalculateViewMatrix()
{
    if(ViewMatrix != NULL)
    {
        *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);

        if(ViewMatrixInverse != NULL)
        {
            *ViewMatrixInverse = inverse(*ViewMatrix);
        }
    }
}

// ----------------------------------------------------------------------------------------------------------------------------

CCamera Camera;

// ----------------------------------------------------------------------------------------------------------------------------

COpenGLRenderer::COpenGLRenderer()
{
    CalculateFXAA = true;

    Pause = false;

    Camera.SetViewMatrixPointer(&ViewMatrix);
}

COpenGLRenderer::~COpenGLRenderer()
{
}

bool COpenGLRenderer::Init()
{
    // ------------------------------------------------------------------------------------------------------------------------

    bool Error = false;

    // ------------------------------------------------------------------------------------------------------------------------

    if(!GLEW_ARB_texture_non_power_of_two)
    {
        ErrorLog.Append("GL_ARB_texture_non_power_of_two not supported!\r\n");
        Error = true;
    }

    if(!GLEW_ARB_depth_texture)
    {
        ErrorLog.Append("GLEW_ARB_depth_texture not supported!\r\n");
        Error = true;
    }

    if(!GLEW_EXT_framebuffer_object)
    {
        ErrorLog.Append("GL_EXT_framebuffer_object not supported!\r\n");
        Error = true;
    }

    // ------------------------------------------------------------------------------------------------------------------------

    char *TextureFileName[] = {"cube.jpg", "floor.jpg", "wall.jpg"};

    for(int i = 0; i < 3; i++)
    {
        Error |= !Texture[i].LoadTexture2D(TextureFileName[i]);
    }

    // ------------------------------------------------------------------------------------------------------------------------

    Error |= !Preprocess.Load("preprocess.vs", "preprocess.fs");
    Error |= !DeferredLighting.Load("deferredlighting.vs", "deferredlighting.fs");
    Error |= !FXAA.Load("FXAA.vert", "FXAA_Extreme_Quality.frag");

    // ------------------------------------------------------------------------------------------------------------------------

    if(Error)
    {
        return false;
    }

    // ------------------------------------------------------------------------------------------------------------------------

    Preprocess.UniformLocations = new GLuint[1];
    Preprocess.UniformLocations[0] = glGetUniformLocation(Preprocess, "Texturing");

    DeferredLighting.UniformLocations = new GLuint[1];
    DeferredLighting.UniformLocations[0] = glGetUniformLocation(DeferredLighting, "ProjectionBiasMatrixInverse");

    FXAA.UniformLocations = new GLuint[1];
    FXAA.UniformLocations[0] = glGetUniformLocation(FXAA, "RCPFrame");

    // ------------------------------------------------------------------------------------------------------------------------

    glUseProgram(DeferredLighting);
    glUniform1i(glGetUniformLocation(DeferredLighting, "ColorBuffer"), 0);
    glUniform1i(glGetUniformLocation(DeferredLighting, "NormalBuffer"), 1);
    glUniform1i(glGetUniformLocation(DeferredLighting, "DepthBuffer"), 2);
    glUseProgram(0);
    
    // ------------------------------------------------------------------------------------------------------------------------

    glGenTextures(2, ColorBuffers);
    glGenTextures(1, &NormalBuffer);
    glGenTextures(1, &DepthBuffer);

    // ------------------------------------------------------------------------------------------------------------------------

    glGenBuffers(1, &VBO);

    InitArrayBuffers();

    // ------------------------------------------------------------------------------------------------------------------------

    glGenFramebuffersEXT(1, &FBO);

    // ------------------------------------------------------------------------------------------------------------------------

    LightColors[0] = vec3(1.0f, 0.0f, 0.0f);
    LightPositions[0] = vec3(0.0f, 1.5f, 0.33f);
    LightColors[1] = vec3(0.0f, 1.0f, 0.0f);
    LightPositions[1] = rotate(LightPositions[0], 120.0f, vec3(0.0f, 1.0f, 0.0f));
    LightColors[2] = vec3(0.0f, 0.0f, 1.0f);
    LightPositions[2] = rotate(LightPositions[1], 120.0f, vec3(0.0f, 1.0f, 0.0f));
    LightColors[3] = vec3(1.0f, 1.0f, 1.0f);
    LightPositions[3] = vec3(0.0f, 2.75f, -4.75f);

    for(int i = 0; i < 3; i++)
    {
        glLightfv(GL_LIGHT0 + i, GL_AMBIENT, &vec4(LightColors[i] * 0.125f, 1.0f));
        glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, &vec4(LightColors[i] * 0.875f, 1.0f));
        glLightf(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, 1.0f);
        glLightf(GL_LIGHT0 + i, GL_QUADRATIC_ATTENUATION, 1.0f);
    }

    glLightfv(GL_LIGHT3, GL_AMBIENT, &vec4(LightColors[3] * 0.25f, 1.0f));
    glLightfv(GL_LIGHT3, GL_DIFFUSE, &vec4(LightColors[3] * 0.75f, 1.0f));
    glLightf(GL_LIGHT3, GL_LINEAR_ATTENUATION, 1.0f / 32.0f);
    glLightf(GL_LIGHT3, GL_QUADRATIC_ATTENUATION, 1.0f / 64.0f);

    // ------------------------------------------------------------------------------------------------------------------------

    Camera.Look(vec3(0.0f, 1.75f, 1.875f), vec3(0.0f, 1.5f, 0.0f));

    // ------------------------------------------------------------------------------------------------------------------------

    return true;

    // ------------------------------------------------------------------------------------------------------------------------
}

void COpenGLRenderer::Render(float FrameTime)
{
    // ------------------------------------------------------------------------------------------------------------------------

    GLenum Buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};

    // render scene to textures -----------------------------------------------------------------------------------------------

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
    glDrawBuffers(2, Buffers); glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ColorBuffers[0], 0);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, NormalBuffer, 0);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, DepthBuffer, 0);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(&ViewMatrix);

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glTexCoordPointer(2, GL_FLOAT, 32, (void*)0);

    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer(GL_FLOAT, 32, (void*)8);

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 32, (void*)20);

    glUseProgram(Preprocess);
    
    glUniform1i(Preprocess.UniformLocations[0], true);

    glColor3f(1.0f, 1.0f, 1.0f);

    glBindTexture(GL_TEXTURE_2D, Texture[0]);
    glDrawArrays(GL_QUADS, 0, 96);

    glBindTexture(GL_TEXTURE_2D, Texture[1]);
    glDrawArrays(GL_QUADS, 96, 4);

    glBindTexture(GL_TEXTURE_2D, Texture[2]);
    glDrawArrays(GL_QUADS, 100, 80);

    glBindTexture(GL_TEXTURE_2D, 0);

    glUniform1i(Preprocess.UniformLocations[0], false);

    glDrawArrays(GL_QUADS, 180, 4);

    glMultMatrixf(&ModelMatrix);
    glColor3f(0.33f, 0.66f, 1.0f);
    glDrawArrays(GL_QUADS, 184, 72);

    glUseProgram(0);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glDisable(GL_CULL_FACE);
    glDisable(GL_DEPTH_TEST);

    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    // set lights positions ------------------------------------------------------------------------------------------------------------------------

    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(&ViewMatrix);

    for(int i = 0; i < 4; i++)
    {
        glLightfv(GL_LIGHT0 + i, GL_POSITION, &vec4(LightPositions[i], 1.0f));
    }

    // calculate lighting -----------------------------------------------------------------------------------------------------

    if(CalculateFXAA)
    {
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
        glDrawBuffers(1, Buffers); glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);
        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ColorBuffers[1], 0);
        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, 0, 0);
        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0);
    }

    glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, ColorBuffers[0]);
    glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, NormalBuffer);
    glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, DepthBuffer);
    glUseProgram(DeferredLighting);
    glBegin(GL_QUADS);
        glVertex2f(0.0f, 0.0f);
        glVertex2f(1.0f, 0.0f);
        glVertex2f(1.0f, 1.0f);
        glVertex2f(0.0f, 1.0f);
    glEnd();
    glUseProgram(0);
    glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0);
    glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
    
    if(CalculateFXAA)
    {
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    }

    // calculate antialiasing -------------------------------------------------------------------------------------------------

    if(CalculateFXAA)
    {
        glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, ColorBuffers[1]);
        glUseProgram(FXAA);
        glBegin(GL_QUADS);
            glVertex2f(0.0f, 0.0f);
            glVertex2f(1.0f, 0.0f);
            glVertex2f(1.0f, 1.0f);
            glVertex2f(0.0f, 1.0f);
        glEnd();
        glUseProgram(0);
        glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
    }

    // rotate object and lights -----------------------------------------------------------------------------------------------

    if(!Pause)
    {
        static float a = 0.0f;

        ModelMatrix = translate(0.0f, 1.5f, 0.0f) * rotate(a, vec3(0.0f, 1.0f, 0.0f)) * rotate(a, vec3(1.0f, 0.0f, 0.0f));

        a += 22.5f * FrameTime;

        for(int i = 0; i < 3; i++)
        {
            LightPositions[i] = rotate(LightPositions[i], -180.0f * FrameTime, vec3(0.0f, 1.0f, 0.0f));
        }
    }

    // ------------------------------------------------------------------------------------------------------------------------
}

void COpenGLRenderer::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);

    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(&ProjectionMatrix);

    ProjectionBiasMatrixInverse = inverse(ProjectionMatrix) * BiasMatrixInverse;

    glBindTexture(GL_TEXTURE_2D, ColorBuffers[0]);
    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_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glBindTexture(GL_TEXTURE_2D, 0);

    glBindTexture(GL_TEXTURE_2D, ColorBuffers[1]);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glBindTexture(GL_TEXTURE_2D, 0);

    glBindTexture(GL_TEXTURE_2D, NormalBuffer);
    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_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glBindTexture(GL_TEXTURE_2D, 0);

    glBindTexture(GL_TEXTURE_2D, DepthBuffer);
    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, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    glBindTexture(GL_TEXTURE_2D, 0);

    glUseProgram(DeferredLighting);
    glUniformMatrix4fv(DeferredLighting.UniformLocations[0], 1, GL_FALSE, &ProjectionBiasMatrixInverse);
    glUseProgram(0);

    glUseProgram(FXAA);
    glUniform2f(FXAA.UniformLocations[0], 1.0f / (float)Width, 1.0f / (float)Height);
    glUseProgram(0);
}

void COpenGLRenderer::Destroy()
{
    for(int i = 0; i < 3; i++)
    {
        Texture[i].Destroy();
    }

    Preprocess.Destroy();
    DeferredLighting.Destroy();
    FXAA.Destroy();

    glDeleteBuffers(1, &VBO);

    glDeleteTextures(2, ColorBuffers);
    glDeleteTextures(1, &NormalBuffer);
    glDeleteTextures(1, &DepthBuffer);

    if(GLEW_EXT_framebuffer_object)
    {
        glDeleteFramebuffersEXT(1, &FBO);
    }
}

void COpenGLRenderer::InitArrayBuffers()
{
    CBuffer buffer;

    vec3 m;

    // cubes

    m = vec3( 0.0f, 0.5f, 0.0f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);

    m = vec3( 0.0f, 1.5f,-4.5f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);

    m = vec3(-0.625f, 0.5f,-4.5f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);

    m = vec3( 0.625f, 0.5f,-4.5f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x,-0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.5f + m.y, 0.5f + m.z), 12);

    // floor

    buffer.AddData(&vec2( 0.0f,  0.0f), 8); buffer.AddData(&vec3(0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-5.0f, 0.0f,  5.0f), 12);
    buffer.AddData(&vec2(10.0f,  0.0f), 8); buffer.AddData(&vec3(0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 5.0f, 0.0f,  5.0f), 12);
    buffer.AddData(&vec2(10.0f, 10.0f), 8); buffer.AddData(&vec3(0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 5.0f, 0.0f, -5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 10.0f), 8); buffer.AddData(&vec3(0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-5.0f, 0.0f, -5.0f), 12);

    // walls

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f,  0.0f), 12); buffer.AddData(&vec3( 5.0f, 0.0f,-5.0f), 12);
    buffer.AddData(&vec2(10.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f,  0.0f), 12); buffer.AddData(&vec3( 5.0f, 0.0f, 5.0f), 12);
    buffer.AddData(&vec2(10.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f,  0.0f), 12); buffer.AddData(&vec3( 5.0f, 3.0f, 5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f,  0.0f), 12); buffer.AddData(&vec3( 5.0f, 3.0f,-5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f,  0.0f), 12); buffer.AddData(&vec3(-5.0f, 0.0f, 5.0f), 12);
    buffer.AddData(&vec2(10.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f,  0.0f), 12); buffer.AddData(&vec3(-5.0f, 0.0f,-5.0f), 12);
    buffer.AddData(&vec2(10.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f,  0.0f), 12); buffer.AddData(&vec3(-5.0f, 3.0f,-5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f,  0.0f), 12); buffer.AddData(&vec3(-5.0f, 3.0f, 5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, -1.0f), 12); buffer.AddData(&vec3( 5.0f, 0.0f, 5.0f), 12);
    buffer.AddData(&vec2(10.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, -1.0f), 12); buffer.AddData(&vec3(-5.0f, 0.0f, 5.0f), 12);
    buffer.AddData(&vec2(10.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, -1.0f), 12); buffer.AddData(&vec3(-5.0f, 3.0f, 5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, -1.0f), 12); buffer.AddData(&vec3( 5.0f, 3.0f, 5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,  1.0f), 12); buffer.AddData(&vec3(-5.0f, 0.0f,-5.0f), 12);
    buffer.AddData(&vec2(10.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,  1.0f), 12); buffer.AddData(&vec3( 5.0f, 0.0f,-5.0f), 12);
    buffer.AddData(&vec2(10.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,  1.0f), 12); buffer.AddData(&vec3( 5.0f, 3.0f,-5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,  1.0f), 12); buffer.AddData(&vec3(-5.0f, 3.0f,-5.0f), 12);

    // pillars

    m = vec3(-2.5f, 0.0f,-2.5f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f, 0.5f + m.z), 12);

    m = vec3( 2.5f, 0.0f,-2.5f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f, 0.5f + m.z), 12);

    m = vec3( 2.5f, 0.0f, 2.5f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f, 0.5f + m.z), 12);

    m = vec3(-2.5f, 0.0f, 2.5f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f,-0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 0.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.5f + m.x, 3.0f, 0.5f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 3.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.5f + m.x, 3.0f, 0.5f + m.z), 12);

    // ceiling

    buffer.AddData(&vec2( 0.0f,  0.0f), 8); buffer.AddData(&vec3(0.0f, -1.0f, 0.0f), 12); buffer.AddData(&vec3(-5.0f, 3.0f,-5.0f), 12);
    buffer.AddData(&vec2(10.0f,  0.0f), 8); buffer.AddData(&vec3(0.0f, -1.0f, 0.0f), 12); buffer.AddData(&vec3( 5.0f, 3.0f,-5.0f), 12);
    buffer.AddData(&vec2(10.0f, 10.0f), 8); buffer.AddData(&vec3(0.0f, -1.0f, 0.0f), 12); buffer.AddData(&vec3( 5.0f, 3.0f, 5.0f), 12);
    buffer.AddData(&vec2( 0.0f, 10.0f), 8); buffer.AddData(&vec3(0.0f, -1.0f, 0.0f), 12); buffer.AddData(&vec3(-5.0f, 3.0f, 5.0f), 12);

    // rotating object

    m = vec3(0.0f);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.25f + m.x,-0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.25f + m.x,-0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.25f + m.x, 0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.25f + m.x, 0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.25f + m.x,-0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.25f + m.x,-0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.25f + m.x, 0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.25f + m.x, 0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.25f + m.x,-0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.25f + m.x,-0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.25f + m.x,-0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.25f + m.x,-0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.25f + m.x, 0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.25f + m.x, 0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.25f + m.x, 0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.25f + m.x, 0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.25f + m.x,-0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.25f + m.x,-0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.25f + m.x, 0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.25f + m.x, 0.025f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.25f + m.x,-0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.25f + m.x,-0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.25f + m.x, 0.025f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.25f + m.x, 0.025f + m.y, 0.025f + m.z), 12);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.25f + m.y,-0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.25f + m.y, 0.025f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.25f + m.y, 0.025f + m.z), 12);

    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3(-1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 1.0f, 0.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f,-1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 1.0f, 0.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f,-1.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.025f + m.y,-0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.025f + m.x,-0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 0.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.025f + m.x,-0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 1.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3( 0.025f + m.x, 0.025f + m.y, 0.25f + m.z), 12);
    buffer.AddData(&vec2( 0.0f, 1.0f), 8); buffer.AddData(&vec3( 0.0f, 0.0f, 1.0f), 12); buffer.AddData(&vec3(-0.025f + m.x, 0.025f + m.y, 0.25f + m.z), 12);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, buffer.GetDataSize(), buffer.GetData(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    buffer.Empty();
}

// ----------------------------------------------------------------------------------------------------------------------------

COpenGLRenderer OpenGLRenderer;

// ----------------------------------------------------------------------------------------------------------------------------

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 = 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);
    }

    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)
{
    switch(Key)
    {
        case VK_F1:
            OpenGLRenderer.CalculateFXAA = !OpenGLRenderer.CalculateFXAA;
            break;

        case VK_SPACE:
            OpenGLRenderer.Pause = !OpenGLRenderer.Pause;
            break;
    }
}

void COpenGLView::OnMouseMove(int X, int Y)
{
    if(GetKeyState(VK_RBUTTON) & 0x80)
    {
        Camera.OnMouseMove(LastX - X, LastY - Y);

        LastX = X;
        LastY = Y;
    }
}

void COpenGLView::OnMouseWheel(short zDelta)
{
    Camera.OnMouseWheel(zDelta);
}

void COpenGLView::OnPaint()
{
    static DWORD LastFPSTime = GetTickCount(), LastFrameTime = LastFPSTime, FPS = 0;

    PAINTSTRUCT ps;

    HDC hDC = BeginPaint(hWnd, &ps);

    DWORD Time = GetTickCount();

    float FrameTime = (Time - LastFrameTime) * 0.001f;

    LastFrameTime = Time;

    if(Time - LastFPSTime > 1000)
    {
        CString Text = Title;

        if(OpenGLRenderer.Text[0] != 0)
        {
            Text.Append(" - " + OpenGLRenderer.Text);
        }

        Text.Append(" - %dx%d", Width, Height);
        Text.Append(", ATF %dx", gl_max_texture_max_anisotropy_ext);
        Text.Append(", MSAA %dx", Samples);
        Text.Append(", FPS: %d", FPS);
        Text.Append(" - %s", glGetString(GL_RENDERER));
        
        SetWindowText(hWnd, Text);

        LastFPSTime = Time;
        FPS = 0;
    }
    else
    {
        FPS++;
    }

    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;

    float MinD = 0.25f;

    if(Keys & 0x3F)
    {
        vec3 Movement = Camera.OnKeys(Keys, FrameTime);

        if(Camera.Reference.x + Movement.x < -5.0f + MinD) Movement.x -= Camera.Reference.x + Movement.x - (-5.0f + MinD);
        if(Camera.Reference.x + Movement.x >  5.0f - MinD) Movement.x -= Camera.Reference.x + Movement.x - ( 5.0f - MinD);
        if(Camera.Reference.y + Movement.y <  0.0f + MinD) Movement.y -= Camera.Reference.y + Movement.y - ( 0.0f + MinD);
        if(Camera.Reference.y + Movement.y >  3.0f - MinD) Movement.y -= Camera.Reference.y + Movement.y - ( 3.0f - MinD);
        if(Camera.Reference.z + Movement.z < -5.0f + MinD) Movement.z -= Camera.Reference.z + Movement.z - (-5.0f + MinD);
        if(Camera.Reference.z + Movement.z >  5.0f - MinD) Movement.z -= Camera.Reference.z + Movement.z - ( 5.0f - MinD);

        Camera.Move(Movement);
    }

    if(GetKeyState(VK_NUMPAD4) & 0x80) OpenGLRenderer.LightPositions[3].x -= FrameTime;
    if(GetKeyState(VK_NUMPAD6) & 0x80) OpenGLRenderer.LightPositions[3].x += FrameTime;
    if(GetKeyState(VK_NUMPAD2) & 0x80) OpenGLRenderer.LightPositions[3].y -= FrameTime;
    if(GetKeyState(VK_NUMPAD8) & 0x80) OpenGLRenderer.LightPositions[3].y += FrameTime;
    if(GetKeyState(VK_NUMPAD9) & 0x80) OpenGLRenderer.LightPositions[3].z -= FrameTime;
    if(GetKeyState(VK_NUMPAD1) & 0x80) OpenGLRenderer.LightPositions[3].z += FrameTime;

    if(OpenGLRenderer.LightPositions[3].x < -5.0f + MinD) OpenGLRenderer.LightPositions[3].x = -5.0f + MinD;
    if(OpenGLRenderer.LightPositions[3].x >  5.0f - MinD) OpenGLRenderer.LightPositions[3].x =  5.0f - MinD;
    if(OpenGLRenderer.LightPositions[3].y <  0.0f + MinD) OpenGLRenderer.LightPositions[3].y =  0.0f + MinD;
    if(OpenGLRenderer.LightPositions[3].y >  3.0f - MinD) OpenGLRenderer.LightPositions[3].y =  3.0f - MinD;
    if(OpenGLRenderer.LightPositions[3].z < -5.0f + MinD) OpenGLRenderer.LightPositions[3].z = -5.0f + MinD;
    if(OpenGLRenderer.LightPositions[3].z >  5.0f - MinD) OpenGLRenderer.LightPositions[3].z =  5.0f - MinD;

    if(GetKeyState(VK_NUMPAD5) & 0x80) OpenGLRenderer.LightPositions[3] = vec3(0.0f, 2.75f, 0.0f);

    OpenGLRenderer.Render(FrameTime);

    SwapBuffers(hDC);

    EndPaint(hWnd, &ps);

    InvalidateRect(hWnd, NULL, FALSE);
}

void COpenGLView::OnRButtonDown(int X, int Y)
{
    LastX = X;
    LastY = Y;
}

void COpenGLView::OnSize(int Width, int Height)
{
    this->Width = Width;
    this->Height = 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_MOUSEMOVE:
            OpenGLView.OnMouseMove(LOWORD(lParam), HIWORD(lParam));
            break;

        case 0x020A: // WM_MOUSWHEEL
            OpenGLView.OnMouseWheel(HIWORD(wParam));
            break;

        case WM_KEYDOWN:
            OpenGLView.OnKeyDown((UINT)wParam);
            break;

        case WM_PAINT:
            OpenGLView.OnPaint();
            break;

        case WM_RBUTTONDOWN:
            OpenGLView.OnRButtonDown(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 = "Deferred rendering - FXAA";

    if(OpenGLView.Init(hInstance, AppName, 800, 600, 0))
    {
        OpenGLView.Show();
        OpenGLView.MessageLoop();
    }
    else
    {
        MessageBox(NULL, ErrorLog, AppName, MB_OK | MB_ICONERROR);
    }

    OpenGLView.Destroy();

    return 0;
}
preprocess.vs
#version 120

varying vec3 Normal;

void main()
{
    gl_FrontColor = gl_Color;
    gl_TexCoord[0] = gl_MultiTexCoord0;
    Normal = gl_NormalMatrix * gl_Normal;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
preprocess.fs
#version 120

uniform sampler2D Texture;
uniform bool Texturing;

varying vec3 Normal;

void main()
{
    gl_FragData[0] = gl_Color;
    if(Texturing) gl_FragData[0] *= texture2D(Texture, gl_TexCoord[0].st);
    gl_FragData[1] = vec4(normalize(Normal) * 0.5 + 0.5, 1.0);
}
deferredlighting.vs
#version 120

void main()
{
    gl_TexCoord[0] = gl_Vertex;
    gl_Position = gl_Vertex * 2.0 - 1.0;
}
deferredlighting.fs
#version 120

uniform sampler2D ColorBuffer, NormalBuffer, DepthBuffer;
uniform mat4x4 ProjectionBiasMatrixInverse;

void main()
{
    gl_FragColor = texture2D(ColorBuffer, gl_TexCoord[0].st);

    float Depth = texture2D(DepthBuffer, gl_TexCoord[0].st).r;

    if(Depth < 1.0)
    {
        vec3 Normal = normalize(texture2D(NormalBuffer, gl_TexCoord[0].st).rgb * 2.0 - 1.0);

        vec4 Position = ProjectionBiasMatrixInverse * vec4(gl_TexCoord[0].st, Depth, 1.0);
        Position /= Position.w;

        vec3 Light = vec3(0.0);

        for(int i = 0; i < 4; i++)
        {
            vec3 LightDirection = gl_LightSource[i].position.xyz - Position.xyz;

            float LightDistance2 = dot(LightDirection, LightDirection);
            float LightDistance = sqrt(LightDistance2);

            LightDirection /= LightDistance;

            float NdotLD = max(dot(Normal, LightDirection), 0.0);

            float Attenuation = gl_LightSource[i].constantAttenuation;

            Attenuation += gl_LightSource[i].linearAttenuation * LightDistance;
            Attenuation += gl_LightSource[i].quadraticAttenuation * LightDistance2;

            Light += (gl_LightSource[i].ambient.rgb + gl_LightSource[i].diffuse.rgb * NdotLD) / Attenuation;
        }

        gl_FragColor.rgb *= Light;
    }
}
Download
deferred_rendering_fxaa.zip (Visual Studio 2008 Professional)
© 2010 - 2016 Bc. Michal Belanec, michalbelanec (at) centrum (dot) sk
Last update June 25, 2016
OpenGL® is a registered trademark of Silicon Graphics Inc.