3D C/C++ tutorials - Software rendering - Software shadow mapping
3D C/C++ tutorials -> Software rendering -> Software 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.
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
software_shadow_mapping.h
// ----------------------------------------------------------------------------------------------------------------------------

#include <windows.h>

#ifndef WM_MOUSWHEEL
    #define WM_MOUSWHEEL 0x020A
#endif

#include "string.h"
#include "glmath.h"

#include <FreeImage.h> // http://freeimage.sourceforge.net/

#pragma comment(lib, "FreeImage.lib")
#pragma comment(lib, "winmm.lib")

#pragma warning(disable : 4996)

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

class CTexture
{
private:
    void *Bits;

private:
    int Width, Height, Format, Pitch;

public:
    CTexture();
    ~CTexture();

private:
    void SetDefaults();

public:
    void* GetBits();
    int GetWidth();
    int GetHeight();
    int GetFormat();

public:
    void Create(int Width, int Height, int Format);
    bool LoadTexture(char *TextureFileName);
    void GetColorNearest(float s, float t, vec3 &Color);
    void GetColorBilinear(float s, float t, vec3 &Color);
    float GetShadowNearest(vec4 &TexCoord);
    float GetShadowBilinear(vec4 &TexCoord);
    void Destroy();
};

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

class CFrameBuffer
{
private:
    CTexture *ColorTexture;
    CTexture *DepthTexture;

public:
    CFrameBuffer();
    ~CFrameBuffer();

public:
    CTexture* GetColorTexture();
    void SetColorTexture(CTexture *ColorTexture);
    CTexture* GetDepthTexture();
    void SetDepthTexture(CTexture *DepthTexture);
};

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

class CCamera
{
public:
    vec3 X, Y, Z, Position, Reference;

public:
    mat4x4 ViewMatrix, ProjectionMatrix, ViewProjectionMatrix;

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 Color;
    vec2 TexCoord;
    vec3 Normal;
};

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

class CObject
{
public:
    CTexture Texture;

public:
    CVertex *Vertices;
    int VerticesCount;

public:
    vec3 Min, Max;

public:
    CObject();
    ~CObject();

private:
    void SetDefaults();

public:
    bool Load(char *Directory, char *ObjFileName);
    void Translate(const vec3 &Translation);
    void Scale(float ScaleFactor);
    void Rotate(float Angle, const vec3 &Axis);
    void Destroy();

private:
    bool ReadSource(char *Directory, char *FileName, char **Source, long &Length);
    bool ParseMtl(char *Directory, char *MtlFileName);
    void GetMinMax();
};

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

class CLight
{
public:
    vec3 Position;
    vec3 Ambient;
    vec3 Diffuse;
    float ConstantAttenuation;
    float LinearAttenuation;
    float QuadraticAttenuation;
};

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

class CFragment
{
public:
    vec4 Position;
    vec3 Color;
    vec2 TexCoord;
    vec3 Normal;
    vec3 LightDirection;
    vec4 ShadowMapTexCoord;
};

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

class CEdge
{
public:
    CFragment *Fragment1, *Fragment2;

public:
    vec4 PositionDiff;
    vec3 ColorDiff;
    vec2 TexCoordDiff;
    vec3 NormalDiff;
    vec3 LightDirectionDiff;
    vec4 ShadowMapTexCoordDiff;

public:
    void Set(CFragment *Fragment1, CFragment *Fragment2);
};

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

class CSpan
{
public:
    CFragment *Fragment1, *Fragment2;

public:
    vec4 PositionDiff;
    vec3 ColorDiff;
    vec2 TexCoordDiff;
    vec3 NormalDiff;
    vec3 LightDirectionDiff;
    vec4 ShadowMapTexCoordDiff;

public:
    void Set(CFragment *Fragment1, CFragment *Fragment2);
};

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

#define NONE 0x00

#define ANTI_ALIASING_2X2 0x01
#define ANTI_ALIASING_3X3 0x02
#define ANTI_ALIASING_4X4 0x03

#define CULL_FACE_FRONT 0x04
#define CULL_FACE_BACK 0x05

#define TEXTURE_FORMAT_BGR24 0x06
#define TEXTURE_FORMAT_DEPTH16 0x07

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

#define THREAD_SLEEP_TIME 0

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

class CThreadData
{
public:
    HANDLE Thread;
    DWORD Id;
    void *SoftwareGL;
    int ThreadId, Function;
    bool ThreadIsRunning;
};

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

class CSoftwareGL
{
private:
    int Width, Height;

private:
    int ViewportX, ViewportY, ViewportWidth, ViewportHeight;

private:
    BYTE *AntiAliasingColorBuffer;
    int AntiAliasingColorBufferWidth, AntiAliasingColorBufferHeight;

private:
    BYTE *StandardColorBuffer;
    int StandardColorBufferWidth, StandardColorBufferHeight;

private:
    BITMAPINFO BitmapInfo;

private:
    USHORT *StandardDepthBuffer;
    int StandardDepthBufferWidth, StandardDepthBufferHeight;

private:
    BYTE *ColorBuffer;
    int ColorBufferWidth, ColorBufferHeight;

private:
    USHORT *DepthBuffer;
    int DepthBufferWidth, DepthBufferHeight;

private:
    int BufferWidthM1, BufferHeightM1;

private:
    CFrameBuffer *FrameBuffer;

private:
    int AntiAliasing;
    mat4x4 ModelViewProjectionMatrix, ShadowMapMatrix;
    CLight *Light;
    int CullFace;
    bool BilinearTextureFiltering, DepthTest;
    CTexture *Texture, *ShadowMap;

private:
    CThreadData *ThreadsData;
    int ThreadsCount;

private:
    bool ColorBuffering, DepthTesting, NotDepthTesting, Texturing, Lighting, ShadowMapping;

private:
    CVertex *Vertices;
    int FirstIndex, LastIndex;

public:
    CSoftwareGL();
    ~CSoftwareGL();

public:
    int GetAntiAliasing();
    void SetAntiAliasing(int AntiAliasing);
    int GetCullFace();
    void SetCullFace(int CullFace);
    bool GetBilinearTextureFiltering();
    void SetBilinearTextureFiltering(bool BilinearTextureFiltering);
    bool GetDepthTest();
    void SetDepthTest(bool DepthTest);

public:
    int GetThreadsCount();

public:
    void BindFrameBuffer(CFrameBuffer *FrameBuffer);
    void Viewport(int X, int Y, int Width, int Height);
    void Clear(bool ClearColorBuffer = true, bool ClearDepthBuffer = true);
    void LoadModelViewProjectionMatrix(const mat4x4 &ModelViewProjectionMatrix);
    void LoadShadowMapMatrix(const mat4x4 &ShadowMapMatrix);
    void BindLight(CLight *Light);
    void BindTexture(CTexture *Texture);
    void BindShadowMap(CTexture *ShadowMap);
    void DrawTriangles(CVertex *Vertices, int FirstIndex, int Count);

private:
    void RunFunctionMultiThreadedAndWaitForCompletion(int Function);
    bool ThreadsAreRunning();
    static DWORD WINAPI ThreadProc(LPVOID lpParam);
    void DrawTriangles(int ThreadId);
    void ClipTriangle(CFragment *Fragments, int ThreadId, int ClipPlane = 1);
    void RasterizeTriangle(CFragment *Fragments, int ThreadId);
    void DrawSpansBetweenEdges(CEdge *Edge1, CEdge *Edge2, int ThreadId);
    void DrawSpan(CSpan *Span, int y);
    void BlitAntiAliasingColorBuffer2x2(int Line);
    void BlitAntiAliasingColorBuffer3x3(int Line);
    void BlitAntiAliasingColorBuffer4x4(int Line);

public:
    void ResizeBuffers(int Width, int Height);
    void SwapBuffers(HDC hDC);
};

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

class CSoftwareGLRenderer : public CSoftwareGL
{
private:
    int Width, Height;

private:
    int LastX, LastY, LastClickedX, LastClickedY;

private:
    CCamera Camera;

private:
    CTexture Texture;

private:
    CObject Object;

private:
    CVertex *Vertices;

private:
    CLight Light;
    mat4x4 LightViewMatrix, LightProjectionMatrix, LightViewProjectionMatrix;

private:
    CTexture ShadowMap;
    CFrameBuffer FrameBuffer;
    mat4x4 OrthogonalProjectionMatrix;

private:
    bool RenderObject, Texturing, Lighting, ShadowMapping, DisplayShadowMap, RotateLight;

public:
    CSoftwareGLRenderer();
    ~CSoftwareGLRenderer();

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

private:
    void RenderShadowMap();

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 CSoftwareGLView
{
private:
    char *Title;
    int Width, Height;
    HWND hWnd;
    HDC hDC;

private:
    CSoftwareGLRenderer SoftwareGLRenderer;

public:
    CSoftwareGLView();
    ~CSoftwareGLView();

public:
    bool Create(HINSTANCE hInstance, char *Title, int Width, int Height);
    void Show(bool Maximized = false);
    void MsgLoop();
    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);

// ----------------------------------------------------------------------------------------------------------------------------
software_shadow_mapping.cpp
// ----------------------------------------------------------------------------------------------------------------------------

#include "software_shadow_mapping.h"

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

CString ErrorLog, ModuleDirectory;

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

CTexture::CTexture()
{
    SetDefaults();
}

CTexture::~CTexture()
{
}

void CTexture::SetDefaults()
{
    Bits = NULL;

    Width = 0;
    Height = 0;
    Format = NONE;
    Pitch = 0;
}

void* CTexture::GetBits()
{
    return Bits;
}

int CTexture::GetWidth()
{
    return Width;
}

int CTexture::GetHeight()
{
    return Height;
}

int CTexture::GetFormat()
{
    return Format;
}

void CTexture::Create(int Width, int Height, int Format)
{
    Destroy();

    if(Width > 0 && Height > 0 && (Format == TEXTURE_FORMAT_BGR24 || Format == TEXTURE_FORMAT_DEPTH16))
    {
        switch(Format)
        {
            case TEXTURE_FORMAT_BGR24: Bits = new BYTE[Width * Height * 3]; Pitch = Width * 3; break;
            case TEXTURE_FORMAT_DEPTH16: Bits = new USHORT[Width * Height]; break;
        }

        this->Width = Width;
        this->Height = Height;
        this->Format = Format;
    }
}

bool CTexture::LoadTexture(char *TextureFileName)
{
    CString FileName = ModuleDirectory + TextureFileName;
    CString ErrorText = "Error loading file " + FileName + " -> ";

    FREE_IMAGE_FORMAT FIF = FreeImage_GetFileType(FileName);

    if(FIF == FIF_UNKNOWN)
    {
        FIF = FreeImage_GetFIFFromFilename(FileName);
    }

    if(FIF == FIF_UNKNOWN)
    {
        ErrorLog.Append(ErrorText + "FIF is FIF_UNKNOWN!" + "\r\n");
        return false;
    }

    FIBITMAP *DIB = NULL;

    if(FreeImage_FIFSupportsReading(FIF))
    {
        DIB = FreeImage_Load(FIF, FileName);
    }

    if(DIB == NULL)
    {
        ErrorLog.Append(ErrorText + "DIB is NULL!" + "\r\n");
        return false;
    }

    int Width = FreeImage_GetWidth(DIB);
    int Height = FreeImage_GetHeight(DIB);
    int Pitch = FreeImage_GetPitch(DIB);
    int BPP = FreeImage_GetBPP(DIB);

    if(Width == 0 || Height == 0)
    {
        ErrorLog.Append(ErrorText + "Width or Height is 0!" + "\r\n");
        FreeImage_Unload(DIB);
        return false;
    }

    if(BPP != 24 && BPP != 32)
    {
        ErrorLog.Append(ErrorText + "BPP is not 24 nor 32!" + "\r\n");
        FreeImage_Unload(DIB);
        return false;
    }

    BPP /= 8;

    BYTE *Bits = FreeImage_GetBits(DIB);

    if(Bits == NULL)
    {
        ErrorLog.Append(ErrorText + "Bits is NULL!" + "\r\n");
        FreeImage_Unload(DIB);
        return false;
    }

    Create(Width, Height, TEXTURE_FORMAT_BGR24);

    BYTE *Colors = (BYTE*)this->Bits;

    for(int y = 0; y < Height; y++)
    {
        for(int x = 0; x < Height; x++)
        {
            for(int i = 0; i < 3; i++)
            {
                Colors[(Width * y + x) * 3 + i] = Bits[Pitch * y + BPP * x + i];
            }
        }
    }

    FreeImage_Unload(DIB);

    return true;
}

float OD255 = 1.0f / 255.0f, OD65535 = 1.0f / 65535.0f;

void CTexture::GetColorNearest(float s, float t, vec3 &Color)
{
    if(Bits != NULL)
    {
        s -= (int)s;
        t -= (int)t;

        if(s < 0.0f) s += 1.0f;
        if(t < 0.0f) t += 1.0f;

        int x = (int)(s * Width), y = (int)(t * Height);

        if(Format == TEXTURE_FORMAT_BGR24)
        {
            BYTE *A = (BYTE*)Bits + Pitch * y + x * 3;

            Color.b = *A * OD255;
            A++;
            Color.g = *A * OD255;
            A++;
            Color.r = *A * OD255;
        }
        else if(Format == TEXTURE_FORMAT_DEPTH16)
        {
            USHORT *A = (USHORT*)Bits + Width * y + x;

            Color.b = Color.g = Color.r = *A * OD65535;
        }
    }
    else
    {
        Color.b = Color.g = Color.r = 1.0f;
    }
}

void CTexture::GetColorBilinear(float s, float t, vec3 &Color)
{
    if(Bits != NULL)
    {
        s -= (int)s;
        t -= (int)t;

        if(s < 0.0f) s += 1.0f;
        if(t < 0.0f) t += 1.0f;

        float fx = s * Width - 0.5f, fy = t * Height - 0.5f;

        if(fx < 0.0f) fx += Width;
        if(fy < 0.0f) fy += Height;

        int x0 = (int)fx, y0 = (int)fy, x1 = (x0 + 1) % Width, y1 = (y0 + 1) % Height;

        if(Format == TEXTURE_FORMAT_BGR24)
        {
            BYTE *LineAB = (BYTE*)Bits + Pitch * y0;
            BYTE *LineCD = (BYTE*)Bits + Pitch * y1;

            int x0M3 = x0 * 3, x1M3 = x1 * 3;

            BYTE *A = LineAB + x0M3;
            BYTE *B = LineAB + x1M3;
            BYTE *C = LineCD + x1M3;
            BYTE *D = LineCD + x0M3;

            float u1 = fx - x0, v1 = fy - y0, u0 = 1.0f - u1, v0 = 1.0f - v1;

            u0 *= OD255;
            u1 *= OD255;

            float u0v0 = u0 * v0, u1v0 = u1 * v0, u1v1 = u1 * v1, u0v1 = u0 * v1;

            Color.b = *A * u0v0 + *B * u1v0 + *C * u1v1 + *D * u0v1;
            A++; B++; C++; D++;
            Color.g = *A * u0v0 + *B * u1v0 + *C * u1v1 + *D * u0v1;
            A++; B++; C++; D++;
            Color.r = *A * u0v0 + *B * u1v0 + *C * u1v1 + *D * u0v1;
        }
        else if(Format == TEXTURE_FORMAT_DEPTH16)
        {
            USHORT *LineAB = (USHORT*)Bits + Width * y0;
            USHORT *LineCD = (USHORT*)Bits + Width * y1;

            USHORT *A = LineAB + x0;
            USHORT *B = LineAB + x1;
            USHORT *C = LineCD + x1;
            USHORT *D = LineCD + x0;

            float u1 = fx - x0, v1 = fy - y0, u0 = 1.0f - u1, v0 = 1.0f - v1;

            u0 *= OD65535;
            u1 *= OD65535;

            float u0v0 = u0 * v0, u1v0 = u1 * v0, u1v1 = u1 * v1, u0v1 = u0 * v1;

            Color.b = Color.g = Color.r = *A * u0v0 + *B * u1v0 + *C * u1v1 + *D * u0v1;
        }
    }
    else
    {
        Color.b = Color.g = Color.r = 1.0f;
    }
}

float CTexture::GetShadowNearest(vec4 &TexCoord)
{
    if(Bits != NULL && Format == TEXTURE_FORMAT_DEPTH16 && TexCoord.q > 0.0f)
    {
        TexCoord.q = 1.0f / TexCoord.q;

        TexCoord.s *= TexCoord.q;
        TexCoord.t *= TexCoord.q;
        TexCoord.p *= TexCoord.q;

        if(TexCoord.s >= 0.0f && TexCoord.s < 1.0f && TexCoord.t >= 0.0f && TexCoord.t < 1.0f && TexCoord.p >= 0.0f && TexCoord.p <= 1.0f)
        {
            TexCoord.p *= 65535.0f;

            int x = (int)(TexCoord.s * Width), y = (int)(TexCoord.t * Height);

            USHORT *A = (USHORT*)Bits + Width * y + x;

            if(TexCoord.p > *A)
            {
                return 0.0f;
            }
        }
    }

    return 1.0f;
}

float CTexture::GetShadowBilinear(vec4 &TexCoord)
{
    if(Bits != NULL && Format == TEXTURE_FORMAT_DEPTH16 && TexCoord.q > 0.0f)
    {
        TexCoord.q = 1.0f / TexCoord.q;

        TexCoord.s *= TexCoord.q;
        TexCoord.t *= TexCoord.q;
        TexCoord.p *= TexCoord.q;

        if(TexCoord.s >= 0.0f && TexCoord.s < 1.0f && TexCoord.t >= 0.0f && TexCoord.t < 1.0f && TexCoord.p >= 0.0f && TexCoord.p <= 1.0f)
        {
            TexCoord.p *= 65535.0f;

            float fx = TexCoord.s * Width - 0.5f, fy = TexCoord.t * Height - 0.5f;

            if(fx < 0.0f) fx = 0.0f;
            if(fy < 0.0f) fy = 0.0f;

            int x0 = (int)fx, y0 = (int)fy, x1 = x0 + 1, y1 = y0 + 1;

            if(x1 == Width) x1--;
            if(y1 == Height) y1--;

            USHORT *LineAB = (USHORT*)Bits + Width * y0;
            USHORT *LineCD = (USHORT*)Bits + Width * y1;

            float A = TexCoord.p > *(LineAB + x0) ? 0.0f : 1.0f;
            float B = TexCoord.p > *(LineAB + x1) ? 0.0f : 1.0f;
            float C = TexCoord.p > *(LineCD + x1) ? 0.0f : 1.0f;
            float D = TexCoord.p > *(LineCD + x0) ? 0.0f : 1.0f;

            float u1 = fx - x0, v1 = fy - y0, u0 = 1.0f - u1, v0 = 1.0f - v1;

            float u0v0 = u0 * v0, u1v0 = u1 * v0, u1v1 = u1 * v1, u0v1 = u0 * v1;

            return A * u0v0 + B * u1v0 + C * u1v1 + D * u0v1;
        }
    }

    return 1.0f;
}

void CTexture::Destroy()
{
    if(Bits != NULL)
    {
        switch(Format)
        {
            case TEXTURE_FORMAT_BGR24: delete [] (BYTE*)Bits; break;
            case TEXTURE_FORMAT_DEPTH16: delete [] (USHORT*)Bits; break;
        }
    }

    SetDefaults();
}

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

CFrameBuffer::CFrameBuffer()
{
    ColorTexture = NULL;
    DepthTexture = NULL;
}

CFrameBuffer::~CFrameBuffer()
{
}

CTexture* CFrameBuffer::GetColorTexture()
{
    return ColorTexture;
}

void CFrameBuffer::SetColorTexture(CTexture *ColorTexture)
{
    this->ColorTexture = NULL;

    if(ColorTexture != NULL && ColorTexture->GetBits() != NULL && ColorTexture->GetFormat() == TEXTURE_FORMAT_BGR24)
    {
        if(DepthTexture == NULL || (ColorTexture->GetWidth() == DepthTexture->GetWidth() && ColorTexture->GetHeight() == DepthTexture->GetHeight()))
        {
            this->ColorTexture = ColorTexture;
        }
    }
}

CTexture* CFrameBuffer::GetDepthTexture()
{
    return DepthTexture;
}

void CFrameBuffer::SetDepthTexture(CTexture *DepthTexture)
{
    this->DepthTexture = NULL;

    if(DepthTexture != NULL && DepthTexture->GetBits() != NULL && DepthTexture->GetFormat() == TEXTURE_FORMAT_DEPTH16)
    {
        if(ColorTexture == NULL || (DepthTexture->GetWidth() == ColorTexture->GetWidth() && DepthTexture->GetHeight() == ColorTexture->GetHeight()))
        {
            this->DepthTexture = DepthTexture;
        }
    }
}

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

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 = vec3(0.0f);

    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);
    ViewProjectionMatrix = ProjectionMatrix * ViewMatrix;
}

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);
    ViewProjectionMatrix = ProjectionMatrix * ViewMatrix;
}

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

CObject::CObject()
{
    SetDefaults();
}

CObject::~CObject()
{
}

void CObject::SetDefaults()
{
    Vertices = NULL;
    VerticesCount = 0;
}

bool CObject::Load(char *Directory, char *ObjFileName)
{
    char *ObjSource;
    long ObjLength;

    Destroy();

    if(!ReadSource(Directory, ObjFileName, &ObjSource, ObjLength)) return false;

    char *Line, *End = ObjSource + ObjLength;
    float x, y, z;
    int PositionsCount = 0, TexCoordsCount = 0, NormalsCount = 0, TrianglesCount = 0;
    int i1, i2, i3, i4, i5, i6, i7, i8, i9;
    int p = 0, tc = 0, n = 0, v = 0;

    Line = ObjSource;

    while(Line < End)
    {
        while(Line < End && (*Line == ' ' || *Line == '\t')) Line++;

        if(Line[0] == 'm' && Line[1] == 't' && Line[2] == 'l' && Line[3] == 'l' && Line[4] == 'i' && Line[5] == 'b' && (Line[6] == ' ' || Line[6] == '\t'))
        {
            char *MtlFileName = Line + 6;

            while(MtlFileName < End && (*MtlFileName == ' ' || *MtlFileName == '\t')) MtlFileName++;

            if(!ParseMtl(Directory, MtlFileName))
            {
                delete [] ObjSource;
                return false;
            }
        }
        else if(sscanf(Line, "v %f %f %f", &x, &y, &z) == 3)
        {
            PositionsCount++;
        }
        else if(sscanf(Line, "vt %f %f", &x, &y) == 2)
        {
            TexCoordsCount++;
        }
        else if(sscanf(Line, "vn %f %f %f", &x, &y, &z) == 3)
        {
            NormalsCount++;
        }
        else if(sscanf(Line, "f %d/%d/%d %d/%d/%d %d/%d/%d", &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8, &i9) == 9)
        {
            TrianglesCount++;
        }
        else if(sscanf(Line, "f %d//%d %d//%d %d//%d", &i1, &i2, &i3, &i4, &i5, &i6) == 6)
        {
            TrianglesCount++;
        }
        else if(sscanf(Line, "f %d/%d %d/%d %d/%d", &i1, &i2, &i3, &i4, &i5, &i6) == 6)
        {
            TrianglesCount++;
        }
        else if(sscanf(Line, "f %d %d %d", &i1, &i2, &i3) == 3)
        {
            TrianglesCount++;
        }

        while(Line < End && *Line != 0) Line++;
        while(Line < End && *Line == 0) Line++;
    }

    if(TrianglesCount == 0)
    {
        ErrorLog.Append("Error loading file %s!\r\n", ObjFileName);
        delete [] ObjSource;
        Destroy();
        return false;
    }

    vec3 *Positions = NULL;
    vec2 *TexCoords = NULL;
    vec3 *Normals = NULL;

    if(PositionsCount > 0) Positions = new vec3[PositionsCount];
    if(TexCoordsCount > 0) TexCoords = new vec2[TexCoordsCount];
    if(NormalsCount > 0) Normals = new vec3[NormalsCount];

    VerticesCount = TrianglesCount * 3;

    Vertices = new CVertex[VerticesCount];

    Line = ObjSource;

    while(Line < End)
    {
        while(Line < End && (*Line == ' ' || *Line == '\t')) Line++;

        if(sscanf(Line, "v %f %f %f", &x, &y, &z) == 3)
        {
            Positions[p++] = vec3(x, y, z);
        }
        else if(sscanf(Line, "vt %f %f", &x, &y) == 2)
        {
            TexCoords[tc++] = vec2(x, y);
        }
        else if(sscanf(Line, "vn %f %f %f", &x, &y, &z) == 3)
        {
            Normals[n++] = normalize(vec3(x, y, z));
        }
        else if(sscanf(Line, "f %d/%d/%d %d/%d/%d %d/%d/%d", &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8, &i9) == 9)
        {
            Vertices[v].Position = Positions[i1 - 1];
            Vertices[v].TexCoord = TexCoords[i2 - 1];
            Vertices[v].Normal = Normals[i3 - 1];
            v++;
            Vertices[v].Position = Positions[i4 - 1];
            Vertices[v].TexCoord = TexCoords[i5 - 1];
            Vertices[v].Normal = Normals[i6 - 1];
            v++;
            Vertices[v].Position = Positions[i7 - 1];
            Vertices[v].TexCoord = TexCoords[i8 - 1];
            Vertices[v].Normal = Normals[i9 - 1];
            v++;
        }
        else if(sscanf(Line, "f %d//%d %d//%d %d//%d", &i1, &i2, &i3, &i4, &i5, &i6) == 6)
        {
            Vertices[v].Position = Positions[i1 - 1];
            Vertices[v].Normal = Normals[i2 - 1];
            v++;
            Vertices[v].Position = Positions[i3 - 1];
            Vertices[v].Normal = Normals[i4 - 1];
            v++;
            Vertices[v].Position = Positions[i5 - 1];
            Vertices[v].Normal = Normals[i6 - 1];
            v++;
        }
        else if(sscanf(Line, "f %d/%d %d/%d %d/%d", &i1, &i2, &i3, &i4, &i5, &i6) == 6)
        {
            Vertices[v].Position = Positions[i1 - 1];
            if(TexCoords != NULL && i1 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i1 - 1];
            if(TexCoords != NULL && i2 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i2 - 1];
            if(Normals != NULL && i1 - 1 < NormalsCount) Vertices[v].Normal = Normals[i1 - 1];
            if(Normals != NULL && i2 - 1 < NormalsCount) Vertices[v].Normal = Normals[i2 - 1];
            v++;
            Vertices[v].Position = Positions[i3 - 1];
            if(TexCoords != NULL && i3 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i3 - 1];
            if(TexCoords != NULL && i4 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i4 - 1];
            if(Normals != NULL && i3 - 1 < NormalsCount) Vertices[v].Normal = Normals[i3 - 1];
            if(Normals != NULL && i4 - 1 < NormalsCount) Vertices[v].Normal = Normals[i4 - 1];
            v++;
            Vertices[v].Position = Positions[i5 - 1];
            if(TexCoords != NULL && i5 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i5 - 1];
            if(TexCoords != NULL && i6 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i6 - 1];
            if(Normals != NULL && i5 - 1 < NormalsCount) Vertices[v].Normal = Normals[i5 - 1];
            if(Normals != NULL && i6 - 1 < NormalsCount) Vertices[v].Normal = Normals[i6 - 1];
            v++;
        }
        else if(sscanf(Line, "f %d %d %d", &i1, &i2, &i3) == 3)
        {
            Vertices[v].Position = Positions[i1 - 1];
            if(TexCoords != NULL && i1 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i1 - 1];
            if(Normals != NULL && i1 - 1 < NormalsCount) Vertices[v].Normal = Normals[i1 - 1];
            v++;
            Vertices[v].Position = Positions[i2 - 1];
            if(TexCoords != NULL && i2 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i2 - 1];
            if(Normals != NULL && i2 - 1 < NormalsCount) Vertices[v].Normal = Normals[i2 - 1];
            v++;
            Vertices[v].Position = Positions[i3 - 1];
            if(TexCoords != NULL && i3 - 1 < TexCoordsCount) Vertices[v].TexCoord = TexCoords[i3 - 1];
            if(Normals != NULL && i3 - 1 < NormalsCount) Vertices[v].Normal = Normals[i3 - 1];
            v++;
        }

        while(Line < End && *Line != 0) Line++;
        while(Line < End && *Line == 0) Line++;
    }

    delete [] Positions;
    delete [] TexCoords;
    delete [] Normals;

    delete [] ObjSource;

    for(int i = 0; i < VerticesCount; i++)
    {
        Vertices[i].Color = vec3(1.0f);
    }

    if(NormalsCount == 0)
    {
        vec3 A, B, Normal;

        for(int i = 0; i < VerticesCount; i += 3)
        {
            int i0 = i, i1 = i + 1, i2 = i + 2;

            A = Vertices[i1].Position - Vertices[i0].Position;
            B = Vertices[i2].Position - Vertices[i0].Position;

            Normal = normalize(cross(A, B));

            Vertices[i0].Normal = Normal;
            Vertices[i1].Normal = Normal;
            Vertices[i2].Normal = Normal;
        }
    }

    GetMinMax();

    return true;
}

void CObject::Translate(const vec3 &Translation)
{
    for(int i = 0; i < VerticesCount; i++)
    {
        Vertices[i].Position += Translation;
    }

    Min += Translation;
    Max += Translation;
}

void CObject::Scale(float ScaleFactor)
{
    for(int i = 0; i < VerticesCount; i++)
    {
        Vertices[i].Position *= ScaleFactor;
    }

    Min *= ScaleFactor;
    Max *= ScaleFactor;
}

void CObject::Rotate(float Angle, const vec3 &Axis)
{
    mat3x3 RotationMatrix = mat3x3(rotate(Angle, Axis));

    for(int i = 0; i < VerticesCount; i++)
    {
        Vertices[i].Position = RotationMatrix * Vertices[i].Position;
        Vertices[i].Normal = RotationMatrix * Vertices[i].Normal;
    }

    GetMinMax();
}

void CObject::Destroy()
{
    Texture.Destroy();

    if(Vertices != NULL)
    {
        delete [] Vertices;
    }

    SetDefaults();
}

bool CObject::ReadSource(char *Directory, char *FileName, char **Source, long &Length)
{
    CString PathFileName = ModuleDirectory + Directory + FileName;

    FILE *File;

    if((File = fopen(PathFileName, "rb")) == NULL)
    {
        ErrorLog.Append("Error opening file " + PathFileName + "!\r\n");
        return false;
    }

    fseek(File, 0, SEEK_END);
    Length = ftell(File);
    fseek(File, 0, SEEK_SET);
    *Source = new char[Length + 1];
    fread(*Source, 1, Length, File);
    (*Source)[Length] = 0;

    fclose(File);

    for(long i = 0; i < Length; i++)
    {
        if((*Source)[i] == '\r' || (*Source)[i] == '\n') (*Source)[i] = 0;
    }

    return true;
}

bool CObject::ParseMtl(char *Directory, char *MtlFileName)
{
    char *MtlSource;
    long MtlLength;

    if(!ReadSource(Directory, MtlFileName, &MtlSource, MtlLength)) return false;

    char *Line = MtlSource, *End = MtlSource + MtlLength;

    bool Error = false;

    while(Line < End)
    {
        while(Line < End && (*Line == ' ' || *Line == '\t')) Line++;

        if(Line[0] == 'm' && Line[1] == 'a' && Line[2] == 'p' && Line[3] == '_' && Line[4] == 'K' && Line[5] == 'a' && (Line[6] == ' ' || Line[6] == '\t'))
        {
            char *TextureFileName = Line + 6;

            while(TextureFileName < End && (*TextureFileName == ' ' || *TextureFileName == '\t')) TextureFileName++;

            Error |= !Texture.LoadTexture(CString(Directory) + TextureFileName);
        }

        while(Line < End && *Line != 0) Line++;
        while(Line < End && *Line == 0) Line++;
    }

    delete [] MtlSource;

    return !Error;
}

void CObject::GetMinMax()
{
    Min = Max = Vertices[0].Position;

    for(int i = 1; i < VerticesCount; i++)
    {
        if(Min.x > Vertices[i].Position.x) Min.x = Vertices[i].Position.x;
        if(Min.y > Vertices[i].Position.y) Min.y = Vertices[i].Position.y;
        if(Min.z > Vertices[i].Position.z) Min.z = Vertices[i].Position.z;
        if(Max.x < Vertices[i].Position.x) Max.x = Vertices[i].Position.x;
        if(Max.y < Vertices[i].Position.y) Max.y = Vertices[i].Position.y;
        if(Max.z < Vertices[i].Position.z) Max.z = Vertices[i].Position.z;
    }
}

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

void CEdge::Set(CFragment *Fragment1, CFragment *Fragment2)
{
    if(Fragment1->Position.y <= Fragment2->Position.y)
    {
        this->Fragment1 = Fragment1;
        this->Fragment2 = Fragment2;
    }
    else
    {
        this->Fragment1 = Fragment2;
        this->Fragment2 = Fragment1;
    }
}

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

void CSpan::Set(CFragment *Fragment1, CFragment *Fragment2)
{
    if(Fragment1->Position.x <= Fragment2->Position.x)
    {
        this->Fragment1 = Fragment1;
        this->Fragment2 = Fragment2;
    }
    else
    {
        this->Fragment1 = Fragment2;
        this->Fragment2 = Fragment1;
    }
}

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

CSoftwareGL::CSoftwareGL()
{
    Width = 0;
    Height = 0;

    ViewportX = 0;
    ViewportY = 0;
    ViewportWidth = 0;
    ViewportHeight = 0;

    AntiAliasingColorBuffer = NULL;
    AntiAliasingColorBufferWidth = 0;
    AntiAliasingColorBufferHeight = 0;

    StandardColorBuffer = NULL;
    StandardColorBufferWidth = 0;
    StandardColorBufferHeight = 0;

    StandardDepthBuffer = NULL;
    StandardDepthBufferWidth = 0;
    StandardDepthBufferHeight = 0;

    ColorBuffer = NULL;
    ColorBufferWidth = 0;
    ColorBufferHeight = 0;

    DepthBuffer = NULL;
    DepthBufferWidth = 0;
    DepthBufferHeight = 0;

    FrameBuffer = NULL;

    AntiAliasing = ANTI_ALIASING_2X2;
    ModelViewProjectionMatrix = mat4x4(1.0f);
    ShadowMapMatrix = mat4x4(1.0f);
    Light = NULL;
    CullFace = CULL_FACE_BACK;
    BilinearTextureFiltering = true;
    DepthTest = true;
    Texture = NULL;
    ShadowMap = NULL;

    SYSTEM_INFO SystemInfo;

    GetSystemInfo(&SystemInfo);

    ThreadsCount = SystemInfo.dwNumberOfProcessors;

    if(ThreadsCount > 1)
    {
        ThreadsData = new CThreadData[ThreadsCount];

        for(int i = 0; i < ThreadsCount; i++)
        {
            ThreadsData[i].SoftwareGL = this;
            ThreadsData[i].ThreadId = i;
            ThreadsData[i].ThreadIsRunning = false;
            ThreadsData[i].Thread = CreateThread(NULL, 0, ThreadProc, &ThreadsData[i], 0, &ThreadsData[i].Id);
            SetThreadAffinityMask(ThreadsData[i].Thread, 1 << i);
        }

        if(THREAD_SLEEP_TIME > 0)
        {
            timeBeginPeriod(THREAD_SLEEP_TIME);
        }
    }
}

CSoftwareGL::~CSoftwareGL()
{
    if(AntiAliasingColorBuffer != NULL)
    {
        delete [] AntiAliasingColorBuffer;
    }

    if(StandardColorBuffer != NULL)
    {
        delete [] StandardColorBuffer;
    }

    if(StandardDepthBuffer != NULL)
    {
        delete [] StandardDepthBuffer;
    }

    if(ThreadsCount > 1)
    {
        for(int i = 0; i < ThreadsCount; i++)
        {
            TerminateThread(ThreadsData[i].Thread, 0);
            CloseHandle(ThreadsData[i].Thread);
        }

        delete [] ThreadsData;

        if(THREAD_SLEEP_TIME > 0)
        {
            timeEndPeriod(THREAD_SLEEP_TIME);
        }
    }
}

int CSoftwareGL::GetAntiAliasing()
{
    return AntiAliasing;
}

void CSoftwareGL::SetAntiAliasing(int AntiAliasing)
{
    if(AntiAliasing == NONE || AntiAliasing == ANTI_ALIASING_2X2 || AntiAliasing == ANTI_ALIASING_3X3 || AntiAliasing == ANTI_ALIASING_4X4)
    {
        if(this->AntiAliasing != AntiAliasing)
        {
            this->AntiAliasing = AntiAliasing;

            ResizeBuffers(Width, Height);
        }
    }
}

int CSoftwareGL::GetCullFace()
{
    return CullFace;
}

void CSoftwareGL::SetCullFace(int CullFace)
{
    if(CullFace == NONE || CullFace == CULL_FACE_FRONT || CullFace == CULL_FACE_BACK)
    {
        this->CullFace = CullFace;
    }
}

bool CSoftwareGL::GetBilinearTextureFiltering()
{
    return BilinearTextureFiltering;
}

void CSoftwareGL::SetBilinearTextureFiltering(bool BilinearTextureFiltering)
{
    this->BilinearTextureFiltering = BilinearTextureFiltering;
}

bool CSoftwareGL::GetDepthTest()
{
    return DepthTest;
}

void CSoftwareGL::SetDepthTest(bool DepthTest)
{
    this->DepthTest = DepthTest;
}

int CSoftwareGL::GetThreadsCount()
{
    return ThreadsCount;
}

void CSoftwareGL::BindFrameBuffer(CFrameBuffer *FrameBuffer)
{
    if(FrameBuffer != NULL)
    {
        if(FrameBuffer->GetColorTexture() != NULL)
        {
            ColorBuffer = (BYTE*)FrameBuffer->GetColorTexture()->GetBits();
            ColorBufferWidth = FrameBuffer->GetColorTexture()->GetWidth();
            ColorBufferHeight = FrameBuffer->GetColorTexture()->GetHeight();

            BufferWidthM1 = ColorBufferWidth - 1;
            BufferHeightM1 = ColorBufferHeight - 1;
        }
        else
        {
            ColorBuffer = NULL;
            ColorBufferWidth = 0;
            ColorBufferHeight = 0;
        }

        if(FrameBuffer->GetDepthTexture() != NULL)
        {
            DepthBuffer = (USHORT*)FrameBuffer->GetDepthTexture()->GetBits();
            DepthBufferWidth = FrameBuffer->GetDepthTexture()->GetWidth();
            DepthBufferHeight = FrameBuffer->GetDepthTexture()->GetHeight();

            BufferWidthM1 = DepthBufferWidth - 1;
            BufferHeightM1 = DepthBufferHeight - 1;
        }
        else
        {
            DepthBuffer = NULL;
            DepthBufferWidth = 0;
            DepthBufferHeight = 0;
        }

        if(FrameBuffer->GetColorTexture() == NULL && FrameBuffer->GetDepthTexture() == NULL)
        {
            BufferWidthM1 = 0;
            BufferHeightM1 = 0;
        }

        this->FrameBuffer = FrameBuffer;
    }
    else
    {
        if(AntiAliasing)
        {
            ColorBuffer = AntiAliasingColorBuffer;
            ColorBufferWidth = AntiAliasingColorBufferWidth;
            ColorBufferHeight = AntiAliasingColorBufferHeight;
        }
        else
        {
            ColorBuffer = StandardColorBuffer;
            ColorBufferWidth = StandardColorBufferWidth;
            ColorBufferHeight = StandardColorBufferHeight;
        }

        DepthBuffer = StandardDepthBuffer;
        DepthBufferWidth = StandardDepthBufferWidth;
        DepthBufferHeight = StandardDepthBufferHeight;

        BufferWidthM1 = DepthBufferWidth - 1;
        BufferHeightM1 = DepthBufferHeight - 1;
    }

    this->FrameBuffer = FrameBuffer;
}

void CSoftwareGL::Viewport(int X, int Y, int Width, int Height)
{
    ViewportX = X;
    ViewportY = Y;
    ViewportWidth = Width;
    ViewportHeight = Height;
}

void CSoftwareGL::Clear(bool ClearColorBuffer, bool ClearDepthBuffer)
{
    if(ClearColorBuffer && ColorBuffer != NULL)
    {
        memset(ColorBuffer, 0, ColorBufferWidth * ColorBufferHeight * 3);
    }

    if(ClearDepthBuffer && DepthBuffer != NULL)
    {
        memset(DepthBuffer, 255, DepthBufferWidth * DepthBufferHeight * 2);
    }
}

void CSoftwareGL::LoadModelViewProjectionMatrix(const mat4x4 &ModelViewProjectionMatrix)
{
    this->ModelViewProjectionMatrix = ModelViewProjectionMatrix;
}

void CSoftwareGL::LoadShadowMapMatrix(const mat4x4 &ShadowMapMatrix)
{
    this->ShadowMapMatrix = ShadowMapMatrix;
}

void CSoftwareGL::BindLight(CLight *Light)
{
    this->Light = Light;
}

void CSoftwareGL::BindTexture(CTexture *Texture)
{
    this->Texture = Texture;
}

void CSoftwareGL::BindShadowMap(CTexture *ShadowMap)
{
    this->ShadowMap = ShadowMap;
}

void CSoftwareGL::DrawTriangles(CVertex *Vertices, int FirstIndex, int Count)
{
    if((ColorBuffer == NULL && DepthBuffer == NULL) || ViewportWidth <= 0 || ViewportHeight <= 0 || Vertices == NULL || FirstIndex < 0 || Count < 3) return;

    ColorBuffering = ColorBuffer != NULL;
    DepthTesting = DepthBuffer != NULL && DepthTest;
    NotDepthTesting = !DepthTesting;
    Texturing = Texture != NULL;
    Lighting = Light != NULL;
    ShadowMapping = ShadowMap != NULL;

    this->Vertices = Vertices;
    this->FirstIndex = FirstIndex;
    this->LastIndex = FirstIndex + Count / 3 * 3;

    int Factor = 1;

    if(FrameBuffer == NULL && AntiAliasing)
    {
        switch(AntiAliasing)
        {
            case ANTI_ALIASING_2X2: Factor = 2; break;
            case ANTI_ALIASING_3X3: Factor = 3; break;
            case ANTI_ALIASING_4X4: Factor = 4; break;
        }
    }

    if(Factor > 1)
    {
        ViewportX *= Factor;
        ViewportY *= Factor;
        ViewportWidth *= Factor;
        ViewportHeight *= Factor;
    }

    RunFunctionMultiThreadedAndWaitForCompletion(1);

    if(Factor > 1)
    {
        ViewportX /= Factor;
        ViewportY /= Factor;
        ViewportWidth /= Factor;
        ViewportHeight /= Factor;
    }
}

void CSoftwareGL::RunFunctionMultiThreadedAndWaitForCompletion(int Function)
{
    if(ThreadsCount > 1)
    {
        for(int i = 0; i < ThreadsCount; i++)
        {
            ThreadsData[i].Function = Function;
            ThreadsData[i].ThreadIsRunning = true;
        }

        while(ThreadsAreRunning())
        {
            if(Width > 0 && Height > 0)
            {
                Sleep(THREAD_SLEEP_TIME);
            }
            else
            {
                Sleep(250);
            }
        }
    }
    else
    {
        switch(Function)
        {
            case 1: DrawTriangles(0); break;
            case 2: BlitAntiAliasingColorBuffer2x2(0); break;
            case 3: BlitAntiAliasingColorBuffer3x3(0); break;
            case 4: BlitAntiAliasingColorBuffer4x4(0); break;
        }
    }
}

bool CSoftwareGL::ThreadsAreRunning()
{
    bool ThreadsAreRunning = false;

    for(int i = 0; i < ThreadsCount; i++)
    {
        ThreadsAreRunning |= ThreadsData[i].ThreadIsRunning;
    }

    return ThreadsAreRunning;
}

DWORD WINAPI CSoftwareGL::ThreadProc(LPVOID lpParam)
{
    CThreadData *ThreadData = (CThreadData*)lpParam;
    CSoftwareGL *SoftwareGL = (CSoftwareGL*)ThreadData->SoftwareGL;

    while(1)
    {
        while(!ThreadData->ThreadIsRunning)
        {
            if(SoftwareGL->Width > 0 && SoftwareGL->Height > 0)
            {
                Sleep(THREAD_SLEEP_TIME);
            }
            else
            {
                Sleep(250);
            }
        }

        switch(ThreadData->Function)
        {
            case 1: SoftwareGL->DrawTriangles(ThreadData->ThreadId); break;
            case 2: SoftwareGL->BlitAntiAliasingColorBuffer2x2(ThreadData->ThreadId); break;
            case 3: SoftwareGL->BlitAntiAliasingColorBuffer3x3(ThreadData->ThreadId); break;
            case 4: SoftwareGL->BlitAntiAliasingColorBuffer4x4(ThreadData->ThreadId); break;
        }

        ThreadData->ThreadIsRunning = false;
    }

    return 0;
}

void CSoftwareGL::DrawTriangles(int ThreadId)
{
    float *ModelViewProjectionMatrix = &this->ModelViewProjectionMatrix;
    float *ShadowMapMatrix = &this->ShadowMapMatrix;

    // primitive assembly

    CFragment Fragments[3];

    for(int i = FirstIndex; i < LastIndex; i += 3)
    {
        CVertex *Vertex = Vertices + i;
        CFragment *Fragment = Fragments;

        for(int j = 0; j < 3; j++)
        {
            // vertex shader

            Fragment->Position.x = ModelViewProjectionMatrix[0] * Vertex->Position.x + ModelViewProjectionMatrix[4] * Vertex->Position.y + ModelViewProjectionMatrix[8] * Vertex->Position.z + ModelViewProjectionMatrix[12];
            Fragment->Position.y = ModelViewProjectionMatrix[1] * Vertex->Position.x + ModelViewProjectionMatrix[5] * Vertex->Position.y + ModelViewProjectionMatrix[9] * Vertex->Position.z + ModelViewProjectionMatrix[13];
            Fragment->Position.z = ModelViewProjectionMatrix[2] * Vertex->Position.x + ModelViewProjectionMatrix[6] * Vertex->Position.y + ModelViewProjectionMatrix[10] * Vertex->Position.z + ModelViewProjectionMatrix[14];
            Fragment->Position.w = ModelViewProjectionMatrix[3] * Vertex->Position.x + ModelViewProjectionMatrix[7] * Vertex->Position.y + ModelViewProjectionMatrix[11] * Vertex->Position.z + ModelViewProjectionMatrix[15];

            if(ColorBuffering)
            {
                Fragment->Color.r = Vertex->Color.r;
                Fragment->Color.g = Vertex->Color.g;
                Fragment->Color.b = Vertex->Color.b;

                if(Texturing)
                {
                    Fragment->TexCoord.s = Vertex->TexCoord.s;
                    Fragment->TexCoord.t = Vertex->TexCoord.t;
                }

                if(Lighting)
                {
                    Fragment->Normal.x = Vertex->Normal.x;
                    Fragment->Normal.y = Vertex->Normal.y;
                    Fragment->Normal.z = Vertex->Normal.z;

                    Fragment->LightDirection.x = Light->Position.x - Vertex->Position.x;
                    Fragment->LightDirection.y = Light->Position.y - Vertex->Position.y;
                    Fragment->LightDirection.z = Light->Position.z - Vertex->Position.z;

                    if(ShadowMapping)
                    {
                        Fragment->ShadowMapTexCoord.s = ShadowMapMatrix[0] * Vertex->Position.x + ShadowMapMatrix[4] * Vertex->Position.y + ShadowMapMatrix[8] * Vertex->Position.z + ShadowMapMatrix[12];
                        Fragment->ShadowMapTexCoord.t = ShadowMapMatrix[1] * Vertex->Position.x + ShadowMapMatrix[5] * Vertex->Position.y + ShadowMapMatrix[9] * Vertex->Position.z + ShadowMapMatrix[13];
                        Fragment->ShadowMapTexCoord.p = ShadowMapMatrix[2] * Vertex->Position.x + ShadowMapMatrix[6] * Vertex->Position.y + ShadowMapMatrix[10] * Vertex->Position.z + ShadowMapMatrix[14];
                        Fragment->ShadowMapTexCoord.q = ShadowMapMatrix[3] * Vertex->Position.x + ShadowMapMatrix[7] * Vertex->Position.y + ShadowMapMatrix[11] * Vertex->Position.z + ShadowMapMatrix[15];
                    }
                }
            }

            Vertex++;
            Fragment++;
        }

        // clipping

        ClipTriangle(Fragments, ThreadId);
    }
}

// ----------------------------------------------------------------------------------------------------------------------------
//
// clipping using homogeneous coordinates
//
// ----------------------------------------------------------------------------------------------------------------------------
//
// c equals the x or y or z coordinate of a fragment
//
// ----------------------------------------------------------------------------------------------------------------------------
//
// a fragment of an edge is in front of or lies on a negative clip plane: - w <= c
//
// a fragment of an edge is behind a negative clip plane: - w > c
//
// to find the intersection of an edge with a negative clip plane we need to find t for which: - w = c
//
// ----------------------------------------------------------------------------------------------------------------------------
//
// w = w1 + (w2 - w1) * t
// c = c1 + (c2 - c1) * t
//
// - w = c
//
// - (w1 + (w2 - w1) * t) = c1 + (c2 - c1) * t
// - w1 - (w2 - w1) * t = c1 + (c2 - c1) * t
// - (w2 - w1) * t = c1 + w1 + (c2 - c1) * t
// - (c2 - c1) * t - (w2 - w1) * t = c1 + w1
// (c2 - c1) * t + (w2 - w1) * t = - c1 - w1
// (c2 - c1 + w2 - w1) * t = - c1 - w1
// t = (- c1 - w1) / (c2 - c1 + w2 - w1)
// t = (c1 + w1) / (- c2 + c1 - w2 + w1)
// t = (c1 + w1) / (c1 - c2 + w1 - w2)
//
// ----------------------------------------------------------------------------------------------------------------------------
//
// a fragment of an edge is in front of or lies on a positive clip plane: c <= w
//
// a fragment of an edge is behind a positive clip plane: c > w
//
// to find the intersection of an edge with a positive clip plane we need to find t for which: c = w
//
// ----------------------------------------------------------------------------------------------------------------------------
//
// c = c1 + (c2 - c1) * t
// w = w1 + (w2 - w1) * t
//
// c = w
//
// c1 + (c2 - c1) * t = w1 + (w2 - w1) * t
// (c2 - c1) * t = w1 - c1 + (w2 - w1) * t
// (c2 - c1) * t - (w2 - w1) * t = w1 - c1
// ((c2 - c1) - (w2 - w1)) * t = w1 - c1
// (c2 - c1 - w2 + w1) * t = w1 - c1
// t = (w1 - c1) / (c2 - c1 - w2 + w1)
// t = (- w1 + c1) / (- c2 + c1 + w2 - w1)
// t = (c1 - w1) / (c1 - c2 - w1 + w2)
//
// ----------------------------------------------------------------------------------------------------------------------------

void CSoftwareGL::ClipTriangle(CFragment *Fragments, int ThreadId, int ClipPlane)
{
    if(ClipPlane >= 1 && ClipPlane <= 6)
    {
        int fifocp, fbcp, fbcpc = 0; // fragment in front of clip plane, fragment behind clip plane, fragments behind clip plane count

        // visibility testing

        CFragment *Fragment = Fragments;

        for(int i = 0; i < 3; i++)
        {
            bool Visible;

            switch(ClipPlane)
            {
                case 1: Visible = -Fragment->Position.w <= Fragment->Position.x; break;
                case 2: Visible =  Fragment->Position.x <= Fragment->Position.w; break;
                case 3: Visible = -Fragment->Position.w <= Fragment->Position.y; break;
                case 4: Visible =  Fragment->Position.y <= Fragment->Position.w; break;
                case 5: Visible = -Fragment->Position.w <= Fragment->Position.z; break;
                case 6: Visible =  Fragment->Position.z <= Fragment->Position.w; break;
            }

            if(Visible)
            {
                fifocp = i;
            }
            else
            {
                fbcp = i;
                fbcpc++;
            }

            Fragment++;
        }

        // clipping

        if(fbcpc == 3)
        {
            return;
        }
        else if(fbcpc == 2)
        {
            CFragment *Fragment1 = Fragments + (fifocp + 1) % 3;
            CFragment *Fragment2 = Fragments + (fifocp + 2) % 3;
            CFragment *Fragment3 = Fragments + fifocp;

            float t1, t2;

            switch(ClipPlane)
            {
                case 1:
                    t1 = (Fragment1->Position.x + Fragment1->Position.w) / (Fragment1->Position.x - Fragment3->Position.x + Fragment1->Position.w - Fragment3->Position.w);
                    t2 = (Fragment2->Position.x + Fragment2->Position.w) / (Fragment2->Position.x - Fragment3->Position.x + Fragment2->Position.w - Fragment3->Position.w);
                    break;

                case 2:
                    t1 = (Fragment1->Position.x - Fragment1->Position.w) / (Fragment1->Position.x - Fragment3->Position.x - Fragment1->Position.w + Fragment3->Position.w);
                    t2 = (Fragment2->Position.x - Fragment2->Position.w) / (Fragment2->Position.x - Fragment3->Position.x - Fragment2->Position.w + Fragment3->Position.w);
                    break;

                case 3:
                    t1 = (Fragment1->Position.y + Fragment1->Position.w) / (Fragment1->Position.y - Fragment3->Position.y + Fragment1->Position.w - Fragment3->Position.w);
                    t2 = (Fragment2->Position.y + Fragment2->Position.w) / (Fragment2->Position.y - Fragment3->Position.y + Fragment2->Position.w - Fragment3->Position.w);
                    break;

                case 4:
                    t1 = (Fragment1->Position.y - Fragment1->Position.w) / (Fragment1->Position.y - Fragment3->Position.y - Fragment1->Position.w + Fragment3->Position.w);
                    t2 = (Fragment2->Position.y - Fragment2->Position.w) / (Fragment2->Position.y - Fragment3->Position.y - Fragment2->Position.w + Fragment3->Position.w);
                    break;

                case 5:
                    t1 = (Fragment1->Position.z + Fragment1->Position.w) / (Fragment1->Position.z - Fragment3->Position.z + Fragment1->Position.w - Fragment3->Position.w);
                    t2 = (Fragment2->Position.z + Fragment2->Position.w) / (Fragment2->Position.z - Fragment3->Position.z + Fragment2->Position.w - Fragment3->Position.w);
                    break;

                case 6:
                    t1 = (Fragment1->Position.z - Fragment1->Position.w) / (Fragment1->Position.z - Fragment3->Position.z - Fragment1->Position.w + Fragment3->Position.w);
                    t2 = (Fragment2->Position.z - Fragment2->Position.w) / (Fragment2->Position.z - Fragment3->Position.z - Fragment2->Position.w + Fragment3->Position.w);
                    break;
            }

            Fragment1->Position.x += (Fragment3->Position.x - Fragment1->Position.x) * t1;
            Fragment1->Position.y += (Fragment3->Position.y - Fragment1->Position.y) * t1;
            Fragment1->Position.z += (Fragment3->Position.z - Fragment1->Position.z) * t1;
            Fragment1->Position.w += (Fragment3->Position.w - Fragment1->Position.w) * t1;

            Fragment2->Position.x += (Fragment3->Position.x - Fragment2->Position.x) * t2;
            Fragment2->Position.y += (Fragment3->Position.y - Fragment2->Position.y) * t2;
            Fragment2->Position.z += (Fragment3->Position.z - Fragment2->Position.z) * t2;
            Fragment2->Position.w += (Fragment3->Position.w - Fragment2->Position.w) * t2;

            if(ColorBuffering)
            {
                Fragment1->Color.r += (Fragment3->Color.r - Fragment1->Color.r) * t1;
                Fragment1->Color.g += (Fragment3->Color.g - Fragment1->Color.g) * t1;
                Fragment1->Color.b += (Fragment3->Color.b - Fragment1->Color.b) * t1;

                Fragment2->Color.r += (Fragment3->Color.r - Fragment2->Color.r) * t2;
                Fragment2->Color.g += (Fragment3->Color.g - Fragment2->Color.g) * t2;
                Fragment2->Color.b += (Fragment3->Color.b - Fragment2->Color.b) * t2;

                if(Texturing)
                {
                    Fragment1->TexCoord.s += (Fragment3->TexCoord.s - Fragment1->TexCoord.s) * t1;
                    Fragment1->TexCoord.t += (Fragment3->TexCoord.t - Fragment1->TexCoord.t) * t1;

                    Fragment2->TexCoord.s += (Fragment3->TexCoord.s - Fragment2->TexCoord.s) * t2;
                    Fragment2->TexCoord.t += (Fragment3->TexCoord.t - Fragment2->TexCoord.t) * t2;
                }

                if(Lighting)
                {
                    Fragment1->Normal.x += (Fragment3->Normal.x - Fragment1->Normal.x) * t1;
                    Fragment1->Normal.y += (Fragment3->Normal.y - Fragment1->Normal.y) * t1;
                    Fragment1->Normal.z += (Fragment3->Normal.z - Fragment1->Normal.z) * t1;

                    Fragment2->Normal.x += (Fragment3->Normal.x - Fragment2->Normal.x) * t2;
                    Fragment2->Normal.y += (Fragment3->Normal.y - Fragment2->Normal.y) * t2;
                    Fragment2->Normal.z += (Fragment3->Normal.z - Fragment2->Normal.z) * t2;

                    Fragment1->LightDirection.x += (Fragment3->LightDirection.x - Fragment1->LightDirection.x) * t1;
                    Fragment1->LightDirection.y += (Fragment3->LightDirection.y - Fragment1->LightDirection.y) * t1;
                    Fragment1->LightDirection.z += (Fragment3->LightDirection.z - Fragment1->LightDirection.z) * t1;

                    Fragment2->LightDirection.x += (Fragment3->LightDirection.x - Fragment2->LightDirection.x) * t2;
                    Fragment2->LightDirection.y += (Fragment3->LightDirection.y - Fragment2->LightDirection.y) * t2;
                    Fragment2->LightDirection.z += (Fragment3->LightDirection.z - Fragment2->LightDirection.z) * t2;

                    if(ShadowMapping)
                    {
                        Fragment1->ShadowMapTexCoord.s += (Fragment3->ShadowMapTexCoord.s - Fragment1->ShadowMapTexCoord.s) * t1;
                        Fragment1->ShadowMapTexCoord.t += (Fragment3->ShadowMapTexCoord.t - Fragment1->ShadowMapTexCoord.t) * t1;
                        Fragment1->ShadowMapTexCoord.p += (Fragment3->ShadowMapTexCoord.p - Fragment1->ShadowMapTexCoord.p) * t1;
                        Fragment1->ShadowMapTexCoord.q += (Fragment3->ShadowMapTexCoord.q - Fragment1->ShadowMapTexCoord.q) * t1;

                        Fragment2->ShadowMapTexCoord.s += (Fragment3->ShadowMapTexCoord.s - Fragment2->ShadowMapTexCoord.s) * t2;
                        Fragment2->ShadowMapTexCoord.t += (Fragment3->ShadowMapTexCoord.t - Fragment2->ShadowMapTexCoord.t) * t2;
                        Fragment2->ShadowMapTexCoord.p += (Fragment3->ShadowMapTexCoord.p - Fragment2->ShadowMapTexCoord.p) * t2;
                        Fragment2->ShadowMapTexCoord.q += (Fragment3->ShadowMapTexCoord.q - Fragment2->ShadowMapTexCoord.q) * t2;
                    }
                }
            }

            ClipTriangle(Fragments, ThreadId, ClipPlane + 1);
        }
        else if(fbcpc == 1)
        {
            int if1 = (fbcp + 1) % 3, if2 = (fbcp + 2) % 3;

            CFragment *Fragment1 = Fragments + if1;
            CFragment *Fragment2 = Fragments + if2;
            CFragment *Fragment3 = Fragments + fbcp;

            CFragment NewFragments[3];

            CFragment *NewFragment1 = NewFragments + if1;
            CFragment *NewFragment2 = NewFragments + if2;
            CFragment *NewFragment3 = NewFragments + fbcp;

            float t1, t2;

            switch(ClipPlane)
            {
                case 1:
                    t1 = (Fragment3->Position.x + Fragment3->Position.w) / (Fragment3->Position.x - Fragment1->Position.x + Fragment3->Position.w - Fragment1->Position.w);
                    t2 = (Fragment3->Position.x + Fragment3->Position.w) / (Fragment3->Position.x - Fragment2->Position.x + Fragment3->Position.w - Fragment2->Position.w);
                    break;

                case 2:
                    t1 = (Fragment3->Position.x - Fragment3->Position.w) / (Fragment3->Position.x - Fragment1->Position.x - Fragment3->Position.w + Fragment1->Position.w);
                    t2 = (Fragment3->Position.x - Fragment3->Position.w) / (Fragment3->Position.x - Fragment2->Position.x - Fragment3->Position.w + Fragment2->Position.w);
                    break;

                case 3:
                    t1 = (Fragment3->Position.y + Fragment3->Position.w) / (Fragment3->Position.y - Fragment1->Position.y + Fragment3->Position.w - Fragment1->Position.w);
                    t2 = (Fragment3->Position.y + Fragment3->Position.w) / (Fragment3->Position.y - Fragment2->Position.y + Fragment3->Position.w - Fragment2->Position.w);
                    break;

                case 4:
                    t1 = (Fragment3->Position.y - Fragment3->Position.w) / (Fragment3->Position.y - Fragment1->Position.y - Fragment3->Position.w + Fragment1->Position.w);
                    t2 = (Fragment3->Position.y - Fragment3->Position.w) / (Fragment3->Position.y - Fragment2->Position.y - Fragment3->Position.w + Fragment2->Position.w);
                    break;

                case 5:
                    t1 = (Fragment3->Position.z + Fragment3->Position.w) / (Fragment3->Position.z - Fragment1->Position.z + Fragment3->Position.w - Fragment1->Position.w);
                    t2 = (Fragment3->Position.z + Fragment3->Position.w) / (Fragment3->Position.z - Fragment2->Position.z + Fragment3->Position.w - Fragment2->Position.w);
                    break;

                case 6:
                    t1 = (Fragment3->Position.z - Fragment3->Position.w) / (Fragment3->Position.z - Fragment1->Position.z - Fragment3->Position.w + Fragment1->Position.w);
                    t2 = (Fragment3->Position.z - Fragment3->Position.w) / (Fragment3->Position.z - Fragment2->Position.z - Fragment3->Position.w + Fragment2->Position.w);
                    break;
            }

            NewFragment3->Position.x = Fragment3->Position.x;
            NewFragment3->Position.y = Fragment3->Position.y;
            NewFragment3->Position.z = Fragment3->Position.z;
            NewFragment3->Position.w = Fragment3->Position.w;

            NewFragment2->Position.x = Fragment2->Position.x;
            NewFragment2->Position.y = Fragment2->Position.y;
            NewFragment2->Position.z = Fragment2->Position.z;
            NewFragment2->Position.w = Fragment2->Position.w;

            Fragment3->Position.x += (Fragment1->Position.x - Fragment3->Position.x) * t1;
            Fragment3->Position.y += (Fragment1->Position.y - Fragment3->Position.y) * t1;
            Fragment3->Position.z += (Fragment1->Position.z - Fragment3->Position.z) * t1;
            Fragment3->Position.w += (Fragment1->Position.w - Fragment3->Position.w) * t1;

            NewFragment1->Position.x = Fragment3->Position.x;
            NewFragment1->Position.y = Fragment3->Position.y;
            NewFragment1->Position.z = Fragment3->Position.z;
            NewFragment1->Position.w = Fragment3->Position.w;

            NewFragment3->Position.x += (NewFragment2->Position.x - NewFragment3->Position.x) * t2;
            NewFragment3->Position.y += (NewFragment2->Position.y - NewFragment3->Position.y) * t2;
            NewFragment3->Position.z += (NewFragment2->Position.z - NewFragment3->Position.z) * t2;
            NewFragment3->Position.w += (NewFragment2->Position.w - NewFragment3->Position.w) * t2;

            if(ColorBuffering)
            {
                NewFragment3->Color.r = Fragment3->Color.r;
                NewFragment3->Color.g = Fragment3->Color.g;
                NewFragment3->Color.b = Fragment3->Color.b;

                NewFragment2->Color.r = Fragment2->Color.r;
                NewFragment2->Color.g = Fragment2->Color.g;
                NewFragment2->Color.b = Fragment2->Color.b;

                Fragment3->Color.r += (Fragment1->Color.r - Fragment3->Color.r) * t1;
                Fragment3->Color.g += (Fragment1->Color.g - Fragment3->Color.g) * t1;
                Fragment3->Color.b += (Fragment1->Color.b - Fragment3->Color.b) * t1;

                NewFragment1->Color.r = Fragment3->Color.r;
                NewFragment1->Color.g = Fragment3->Color.g;
                NewFragment1->Color.b = Fragment3->Color.b;

                NewFragment3->Color.r += (NewFragment2->Color.r - NewFragment3->Color.r) * t2;
                NewFragment3->Color.g += (NewFragment2->Color.g - NewFragment3->Color.g) * t2;
                NewFragment3->Color.b += (NewFragment2->Color.b - NewFragment3->Color.b) * t2;

                if(Texturing)
                {
                    NewFragment3->TexCoord.s = Fragment3->TexCoord.s;
                    NewFragment3->TexCoord.t = Fragment3->TexCoord.t;

                    NewFragment2->TexCoord.s = Fragment2->TexCoord.s;
                    NewFragment2->TexCoord.t = Fragment2->TexCoord.t;

                    Fragment3->TexCoord.s += (Fragment1->TexCoord.s - Fragment3->TexCoord.s) * t1;
                    Fragment3->TexCoord.t += (Fragment1->TexCoord.t - Fragment3->TexCoord.t) * t1;

                    NewFragment1->TexCoord.s = Fragment3->TexCoord.s;
                    NewFragment1->TexCoord.t = Fragment3->TexCoord.t;

                    NewFragment3->TexCoord.s += (NewFragment2->TexCoord.s - NewFragment3->TexCoord.s) * t2;
                    NewFragment3->TexCoord.t += (NewFragment2->TexCoord.t - NewFragment3->TexCoord.t) * t2;
                }

                if(Lighting)
                {
                    NewFragment3->Normal.x = Fragment3->Normal.x;
                    NewFragment3->Normal.y = Fragment3->Normal.y;
                    NewFragment3->Normal.z = Fragment3->Normal.z;

                    NewFragment2->Normal.x = Fragment2->Normal.x;
                    NewFragment2->Normal.y = Fragment2->Normal.y;
                    NewFragment2->Normal.z = Fragment2->Normal.z;

                    Fragment3->Normal.x += (Fragment1->Normal.x - Fragment3->Normal.x) * t1;
                    Fragment3->Normal.y += (Fragment1->Normal.y - Fragment3->Normal.y) * t1;
                    Fragment3->Normal.z += (Fragment1->Normal.z - Fragment3->Normal.z) * t1;

                    NewFragment1->Normal.x = Fragment3->Normal.x;
                    NewFragment1->Normal.y = Fragment3->Normal.y;
                    NewFragment1->Normal.z = Fragment3->Normal.z;

                    NewFragment3->Normal.x += (NewFragment2->Normal.x - NewFragment3->Normal.x) * t2;
                    NewFragment3->Normal.y += (NewFragment2->Normal.y - NewFragment3->Normal.y) * t2;
                    NewFragment3->Normal.z += (NewFragment2->Normal.z - NewFragment3->Normal.z) * t2;

                    NewFragment3->LightDirection.x = Fragment3->LightDirection.x;
                    NewFragment3->LightDirection.y = Fragment3->LightDirection.y;
                    NewFragment3->LightDirection.z = Fragment3->LightDirection.z;

                    NewFragment2->LightDirection.x = Fragment2->LightDirection.x;
                    NewFragment2->LightDirection.y = Fragment2->LightDirection.y;
                    NewFragment2->LightDirection.z = Fragment2->LightDirection.z;

                    Fragment3->LightDirection.x += (Fragment1->LightDirection.x - Fragment3->LightDirection.x) * t1;
                    Fragment3->LightDirection.y += (Fragment1->LightDirection.y - Fragment3->LightDirection.y) * t1;
                    Fragment3->LightDirection.z += (Fragment1->LightDirection.z - Fragment3->LightDirection.z) * t1;

                    NewFragment1->LightDirection.x = Fragment3->LightDirection.x;
                    NewFragment1->LightDirection.y = Fragment3->LightDirection.y;
                    NewFragment1->LightDirection.z = Fragment3->LightDirection.z;

                    NewFragment3->LightDirection.x += (NewFragment2->LightDirection.x - NewFragment3->LightDirection.x) * t2;
                    NewFragment3->LightDirection.y += (NewFragment2->LightDirection.y - NewFragment3->LightDirection.y) * t2;
                    NewFragment3->LightDirection.z += (NewFragment2->LightDirection.z - NewFragment3->LightDirection.z) * t2;

                    if(ShadowMapping)
                    {
                        NewFragment3->ShadowMapTexCoord.s = Fragment3->ShadowMapTexCoord.s;
                        NewFragment3->ShadowMapTexCoord.t = Fragment3->ShadowMapTexCoord.t;
                        NewFragment3->ShadowMapTexCoord.p = Fragment3->ShadowMapTexCoord.p;
                        NewFragment3->ShadowMapTexCoord.q = Fragment3->ShadowMapTexCoord.q;

                        NewFragment2->ShadowMapTexCoord.s = Fragment2->ShadowMapTexCoord.s;
                        NewFragment2->ShadowMapTexCoord.t = Fragment2->ShadowMapTexCoord.t;
                        NewFragment2->ShadowMapTexCoord.p = Fragment2->ShadowMapTexCoord.p;
                        NewFragment2->ShadowMapTexCoord.q = Fragment2->ShadowMapTexCoord.q;

                        Fragment3->ShadowMapTexCoord.s += (Fragment1->ShadowMapTexCoord.s - Fragment3->ShadowMapTexCoord.s) * t1;
                        Fragment3->ShadowMapTexCoord.t += (Fragment1->ShadowMapTexCoord.t - Fragment3->ShadowMapTexCoord.t) * t1;
                        Fragment3->ShadowMapTexCoord.p += (Fragment1->ShadowMapTexCoord.p - Fragment3->ShadowMapTexCoord.p) * t1;
                        Fragment3->ShadowMapTexCoord.q += (Fragment1->ShadowMapTexCoord.q - Fragment3->ShadowMapTexCoord.q) * t1;

                        NewFragment1->ShadowMapTexCoord.s = Fragment3->ShadowMapTexCoord.s;
                        NewFragment1->ShadowMapTexCoord.t = Fragment3->ShadowMapTexCoord.t;
                        NewFragment1->ShadowMapTexCoord.p = Fragment3->ShadowMapTexCoord.p;
                        NewFragment1->ShadowMapTexCoord.q = Fragment3->ShadowMapTexCoord.q;

                        NewFragment3->ShadowMapTexCoord.s += (NewFragment2->ShadowMapTexCoord.s - NewFragment3->ShadowMapTexCoord.s) * t2;
                        NewFragment3->ShadowMapTexCoord.t += (NewFragment2->ShadowMapTexCoord.t - NewFragment3->ShadowMapTexCoord.t) * t2;
                        NewFragment3->ShadowMapTexCoord.p += (NewFragment2->ShadowMapTexCoord.p - NewFragment3->ShadowMapTexCoord.p) * t2;
                        NewFragment3->ShadowMapTexCoord.q += (NewFragment2->ShadowMapTexCoord.q - NewFragment3->ShadowMapTexCoord.q) * t2;
                    }
                }
            }

            ClipTriangle(Fragments, ThreadId, ClipPlane + 1);
            ClipTriangle(NewFragments, ThreadId, ClipPlane + 1);
        }
        else if(fbcpc == 0)
        {
            ClipTriangle(Fragments, ThreadId, ClipPlane + 1);
        }
    }
    else if(ClipPlane == 7)
    {
        RasterizeTriangle(Fragments, ThreadId);
    }
}

void CSoftwareGL::RasterizeTriangle(CFragment *Fragments, int ThreadId)
{
    CFragment *Fragment1 = Fragments, *Fragment2 = Fragment1 + 1, *Fragment3 = Fragment2 + 1;

    // projection

    Fragment1->Position.w = 1.0f / Fragment1->Position.w;

    Fragment1->Position.x *= Fragment1->Position.w;
    Fragment1->Position.y *= Fragment1->Position.w;

    Fragment2->Position.w = 1.0f / Fragment2->Position.w;

    Fragment2->Position.x *= Fragment2->Position.w;
    Fragment2->Position.y *= Fragment2->Position.w;

    Fragment3->Position.w = 1.0f / Fragment3->Position.w;

    Fragment3->Position.x *= Fragment3->Position.w;
    Fragment3->Position.y *= Fragment3->Position.w;

    // culling

    if(CullFace)
    {
        float a = 0.0f;

        a += Fragment1->Position.x * Fragment2->Position.y - Fragment2->Position.x * Fragment1->Position.y;
        a += Fragment2->Position.x * Fragment3->Position.y - Fragment3->Position.x * Fragment2->Position.y;
        a += Fragment3->Position.x * Fragment1->Position.y - Fragment1->Position.x * Fragment3->Position.y;

        switch(CullFace)
        {
            case CULL_FACE_FRONT:
                if(a >= 0.0f) return;
                break;

            case CULL_FACE_BACK:
                if(a <= 0.0f) return;
                break;
        }
    }

    // perspective correct z and attribute values interpolation

    // http://www.comp.nus.edu.sg/~lowkl/publications/lowk_persp_interp_techrep.pdf

    Fragment1->Position.x = (Fragment1->Position.x * 0.5f + 0.5f) * ViewportWidth + ViewportX;
    Fragment1->Position.y = (Fragment1->Position.y * 0.5f + 0.5f) * ViewportHeight + ViewportY;
    Fragment1->Position.z = Fragment1->Position.z * Fragment1->Position.w * 0.5f + 0.5f;

    Fragment2->Position.x = (Fragment2->Position.x * 0.5f + 0.5f) * ViewportWidth + ViewportX;
    Fragment2->Position.y = (Fragment2->Position.y * 0.5f + 0.5f) * ViewportHeight + ViewportY;
    Fragment2->Position.z = Fragment2->Position.z * Fragment2->Position.w * 0.5f + 0.5f;

    Fragment3->Position.x = (Fragment3->Position.x * 0.5f + 0.5f) * ViewportWidth + ViewportX;
    Fragment3->Position.y = (Fragment3->Position.y * 0.5f + 0.5f) * ViewportHeight + ViewportY;
    Fragment3->Position.z = Fragment3->Position.z * Fragment3->Position.w * 0.5f + 0.5f;

    if(ColorBuffering)
    {
        Fragment1->Color.r *= Fragment1->Position.w;
        Fragment1->Color.g *= Fragment1->Position.w;
        Fragment1->Color.b *= Fragment1->Position.w;

        Fragment2->Color.r *= Fragment2->Position.w;
        Fragment2->Color.g *= Fragment2->Position.w;
        Fragment2->Color.b *= Fragment2->Position.w;

        Fragment3->Color.r *= Fragment3->Position.w;
        Fragment3->Color.g *= Fragment3->Position.w;
        Fragment3->Color.b *= Fragment3->Position.w;

        if(Texturing)
        {
            Fragment1->TexCoord.s *= Fragment1->Position.w;
            Fragment1->TexCoord.t *= Fragment1->Position.w;

            Fragment2->TexCoord.s *= Fragment2->Position.w;
            Fragment2->TexCoord.t *= Fragment2->Position.w;

            Fragment3->TexCoord.s *= Fragment3->Position.w;
            Fragment3->TexCoord.t *= Fragment3->Position.w;
        }

        if(Lighting)
        {
            Fragment1->Normal.x *= Fragment1->Position.w;
            Fragment1->Normal.y *= Fragment1->Position.w;
            Fragment1->Normal.z *= Fragment1->Position.w;

            Fragment2->Normal.x *= Fragment2->Position.w;
            Fragment2->Normal.y *= Fragment2->Position.w;
            Fragment2->Normal.z *= Fragment2->Position.w;

            Fragment3->Normal.x *= Fragment3->Position.w;
            Fragment3->Normal.y *= Fragment3->Position.w;
            Fragment3->Normal.z *= Fragment3->Position.w;

            Fragment1->LightDirection.x *= Fragment1->Position.w;
            Fragment1->LightDirection.y *= Fragment1->Position.w;
            Fragment1->LightDirection.z *= Fragment1->Position.w;

            Fragment2->LightDirection.x *= Fragment2->Position.w;
            Fragment2->LightDirection.y *= Fragment2->Position.w;
            Fragment2->LightDirection.z *= Fragment2->Position.w;

            Fragment3->LightDirection.x *= Fragment3->Position.w;
            Fragment3->LightDirection.y *= Fragment3->Position.w;
            Fragment3->LightDirection.z *= Fragment3->Position.w;

            if(ShadowMapping)
            {
                Fragment1->ShadowMapTexCoord.s *= Fragment1->Position.w;
                Fragment1->ShadowMapTexCoord.t *= Fragment1->Position.w;
                Fragment1->ShadowMapTexCoord.p *= Fragment1->Position.w;
                Fragment1->ShadowMapTexCoord.q *= Fragment1->Position.w;

                Fragment2->ShadowMapTexCoord.s *= Fragment2->Position.w;
                Fragment2->ShadowMapTexCoord.t *= Fragment2->Position.w;
                Fragment2->ShadowMapTexCoord.p *= Fragment2->Position.w;
                Fragment2->ShadowMapTexCoord.q *= Fragment2->Position.w;

                Fragment3->ShadowMapTexCoord.s *= Fragment3->Position.w;
                Fragment3->ShadowMapTexCoord.t *= Fragment3->Position.w;
                Fragment3->ShadowMapTexCoord.p *= Fragment3->Position.w;
                Fragment3->ShadowMapTexCoord.q *= Fragment3->Position.w;
            }
        }
    }

    // triangle rasterization

    // http://joshbeam.com/articles/triangle_rasterization/

    CEdge Edges[3], *Edge1 = Edges, *Edge2 = Edge1 + 1, *Edge3 = Edge2 + 1;

    Edge1->Set(Fragment1, Fragment2);
    Edge2->Set(Fragment2, Fragment3);
    Edge3->Set(Fragment3, Fragment1);

    Edge1->PositionDiff.x = Edge1->Fragment2->Position.x - Edge1->Fragment1->Position.x;
    Edge1->PositionDiff.y = Edge1->Fragment2->Position.y - Edge1->Fragment1->Position.y;
    Edge1->PositionDiff.z = Edge1->Fragment2->Position.z - Edge1->Fragment1->Position.z;
    Edge1->PositionDiff.w = Edge1->Fragment2->Position.w - Edge1->Fragment1->Position.w;

    Edge2->PositionDiff.x = Edge2->Fragment2->Position.x - Edge2->Fragment1->Position.x;
    Edge2->PositionDiff.y = Edge2->Fragment2->Position.y - Edge2->Fragment1->Position.y;
    Edge2->PositionDiff.z = Edge2->Fragment2->Position.z - Edge2->Fragment1->Position.z;
    Edge2->PositionDiff.w = Edge2->Fragment2->Position.w - Edge2->Fragment1->Position.w;

    Edge3->PositionDiff.x = Edge3->Fragment2->Position.x - Edge3->Fragment1->Position.x;
    Edge3->PositionDiff.y = Edge3->Fragment2->Position.y - Edge3->Fragment1->Position.y;
    Edge3->PositionDiff.z = Edge3->Fragment2->Position.z - Edge3->Fragment1->Position.z;
    Edge3->PositionDiff.w = Edge3->Fragment2->Position.w - Edge3->Fragment1->Position.w;

    if(ColorBuffering)
    {
        Edge1->ColorDiff.r = Edge1->Fragment2->Color.r - Edge1->Fragment1->Color.r;
        Edge1->ColorDiff.g = Edge1->Fragment2->Color.g - Edge1->Fragment1->Color.g;
        Edge1->ColorDiff.b = Edge1->Fragment2->Color.b - Edge1->Fragment1->Color.b;

        Edge2->ColorDiff.r = Edge2->Fragment2->Color.r - Edge2->Fragment1->Color.r;
        Edge2->ColorDiff.g = Edge2->Fragment2->Color.g - Edge2->Fragment1->Color.g;
        Edge2->ColorDiff.b = Edge2->Fragment2->Color.b - Edge2->Fragment1->Color.b;

        Edge3->ColorDiff.r = Edge3->Fragment2->Color.r - Edge3->Fragment1->Color.r;
        Edge3->ColorDiff.g = Edge3->Fragment2->Color.g - Edge3->Fragment1->Color.g;
        Edge3->ColorDiff.b = Edge3->Fragment2->Color.b - Edge3->Fragment1->Color.b;

        if(Texturing)
        {
            Edge1->TexCoordDiff.s = Edge1->Fragment2->TexCoord.s - Edge1->Fragment1->TexCoord.s;
            Edge1->TexCoordDiff.t = Edge1->Fragment2->TexCoord.t - Edge1->Fragment1->TexCoord.t;

            Edge2->TexCoordDiff.s = Edge2->Fragment2->TexCoord.s - Edge2->Fragment1->TexCoord.s;
            Edge2->TexCoordDiff.t = Edge2->Fragment2->TexCoord.t - Edge2->Fragment1->TexCoord.t;

            Edge3->TexCoordDiff.s = Edge3->Fragment2->TexCoord.s - Edge3->Fragment1->TexCoord.s;
            Edge3->TexCoordDiff.t = Edge3->Fragment2->TexCoord.t - Edge3->Fragment1->TexCoord.t;
        }

        if(Lighting)
        {
            Edge1->NormalDiff.x = Edge1->Fragment2->Normal.x - Edge1->Fragment1->Normal.x;
            Edge1->NormalDiff.y = Edge1->Fragment2->Normal.y - Edge1->Fragment1->Normal.y;
            Edge1->NormalDiff.z = Edge1->Fragment2->Normal.z - Edge1->Fragment1->Normal.z;

            Edge2->NormalDiff.x = Edge2->Fragment2->Normal.x - Edge2->Fragment1->Normal.x;
            Edge2->NormalDiff.y = Edge2->Fragment2->Normal.y - Edge2->Fragment1->Normal.y;
            Edge2->NormalDiff.z = Edge2->Fragment2->Normal.z - Edge2->Fragment1->Normal.z;

            Edge3->NormalDiff.x = Edge3->Fragment2->Normal.x - Edge3->Fragment1->Normal.x;
            Edge3->NormalDiff.y = Edge3->Fragment2->Normal.y - Edge3->Fragment1->Normal.y;
            Edge3->NormalDiff.z = Edge3->Fragment2->Normal.z - Edge3->Fragment1->Normal.z;

            Edge1->LightDirectionDiff.x = Edge1->Fragment2->LightDirection.x - Edge1->Fragment1->LightDirection.x;
            Edge1->LightDirectionDiff.y = Edge1->Fragment2->LightDirection.y - Edge1->Fragment1->LightDirection.y;
            Edge1->LightDirectionDiff.z = Edge1->Fragment2->LightDirection.z - Edge1->Fragment1->LightDirection.z;

            Edge2->LightDirectionDiff.x = Edge2->Fragment2->LightDirection.x - Edge2->Fragment1->LightDirection.x;
            Edge2->LightDirectionDiff.y = Edge2->Fragment2->LightDirection.y - Edge2->Fragment1->LightDirection.y;
            Edge2->LightDirectionDiff.z = Edge2->Fragment2->LightDirection.z - Edge2->Fragment1->LightDirection.z;

            Edge3->LightDirectionDiff.x = Edge3->Fragment2->LightDirection.x - Edge3->Fragment1->LightDirection.x;
            Edge3->LightDirectionDiff.y = Edge3->Fragment2->LightDirection.y - Edge3->Fragment1->LightDirection.y;
            Edge3->LightDirectionDiff.z = Edge3->Fragment2->LightDirection.z - Edge3->Fragment1->LightDirection.z;

            if(ShadowMapping)
            {
                Edge1->ShadowMapTexCoordDiff.s = Edge1->Fragment2->ShadowMapTexCoord.s - Edge1->Fragment1->ShadowMapTexCoord.s;
                Edge1->ShadowMapTexCoordDiff.t = Edge1->Fragment2->ShadowMapTexCoord.t - Edge1->Fragment1->ShadowMapTexCoord.t;
                Edge1->ShadowMapTexCoordDiff.p = Edge1->Fragment2->ShadowMapTexCoord.p - Edge1->Fragment1->ShadowMapTexCoord.p;
                Edge1->ShadowMapTexCoordDiff.q = Edge1->Fragment2->ShadowMapTexCoord.q - Edge1->Fragment1->ShadowMapTexCoord.q;

                Edge2->ShadowMapTexCoordDiff.s = Edge2->Fragment2->ShadowMapTexCoord.s - Edge2->Fragment1->ShadowMapTexCoord.s;
                Edge2->ShadowMapTexCoordDiff.t = Edge2->Fragment2->ShadowMapTexCoord.t - Edge2->Fragment1->ShadowMapTexCoord.t;
                Edge2->ShadowMapTexCoordDiff.p = Edge2->Fragment2->ShadowMapTexCoord.p - Edge2->Fragment1->ShadowMapTexCoord.p;
                Edge2->ShadowMapTexCoordDiff.q = Edge2->Fragment2->ShadowMapTexCoord.q - Edge2->Fragment1->ShadowMapTexCoord.q;

                Edge3->ShadowMapTexCoordDiff.s = Edge3->Fragment2->ShadowMapTexCoord.s - Edge3->Fragment1->ShadowMapTexCoord.s;
                Edge3->ShadowMapTexCoordDiff.t = Edge3->Fragment2->ShadowMapTexCoord.t - Edge3->Fragment1->ShadowMapTexCoord.t;
                Edge3->ShadowMapTexCoordDiff.p = Edge3->Fragment2->ShadowMapTexCoord.p - Edge3->Fragment1->ShadowMapTexCoord.p;
                Edge3->ShadowMapTexCoordDiff.q = Edge3->Fragment2->ShadowMapTexCoord.q - Edge3->Fragment1->ShadowMapTexCoord.q;
            }
        }
    }

    CEdge *LongEdge = Edge1, *ShortEdge1 = Edge2, *ShortEdge2 = Edge3;

    if(Edge2->PositionDiff.y > LongEdge->PositionDiff.y)
    {
        LongEdge = Edge2;
        ShortEdge1 = Edge3;
        ShortEdge2 = Edge1;
    }

    if(Edge3->PositionDiff.y > LongEdge->PositionDiff.y)
    {
        LongEdge = Edge3;
        ShortEdge1 = Edge1;
        ShortEdge2 = Edge2;
    }

    DrawSpansBetweenEdges(LongEdge, ShortEdge1, ThreadId);
    DrawSpansBetweenEdges(LongEdge, ShortEdge2, ThreadId);
}

void CSoftwareGL::DrawSpansBetweenEdges(CEdge *Edge1, CEdge *Edge2, int ThreadId)
{
    if(Edge1->PositionDiff.y == 0.0f || Edge2->PositionDiff.y == 0.0f) return;

    int iy1 = (int)Edge2->Fragment1->Position.y, iy2 = (int)Edge2->Fragment2->Position.y;

    if(iy1 < 0) iy1 = 0;

    while(iy1 % ThreadsCount != ThreadId) iy1++;

    if(iy2 > BufferHeightM1) iy2 = BufferHeightM1;

    float fy, factor1, factor2, ode1pdy = 1.0f / Edge1->PositionDiff.y, ode2pdy = 1.0f / Edge2->PositionDiff.y;

    CFragment Fragment1, Fragment2;

    CSpan Span;

    for(int y = iy1; y <= iy2; y += ThreadsCount)
    {
        fy = y + 0.5f;

        factor1 = (fy - Edge1->Fragment1->Position.y) * ode1pdy;

        if(factor1 < 0.0f || factor1 > 1.0f) continue;

        factor2 = (fy - Edge2->Fragment1->Position.y) * ode2pdy;

        if(factor2 < 0.0f || factor2 > 1.0f) continue;

        Fragment1.Position.x = Edge1->Fragment1->Position.x + Edge1->PositionDiff.x * factor1;
        Fragment1.Position.y = Edge1->Fragment1->Position.y + Edge1->PositionDiff.y * factor1;
        Fragment1.Position.z = Edge1->Fragment1->Position.z + Edge1->PositionDiff.z * factor1;
        Fragment1.Position.w = Edge1->Fragment1->Position.w + Edge1->PositionDiff.w * factor1;

        Fragment2.Position.x = Edge2->Fragment1->Position.x + Edge2->PositionDiff.x * factor2;
        Fragment2.Position.y = Edge2->Fragment1->Position.y + Edge2->PositionDiff.y * factor2;
        Fragment2.Position.z = Edge2->Fragment1->Position.z + Edge2->PositionDiff.z * factor2;
        Fragment2.Position.w = Edge2->Fragment1->Position.w + Edge2->PositionDiff.w * factor2;

        if(ColorBuffering)
        {
            Fragment1.Color.r = Edge1->Fragment1->Color.r + Edge1->ColorDiff.r * factor1;
            Fragment1.Color.g = Edge1->Fragment1->Color.g + Edge1->ColorDiff.g * factor1;
            Fragment1.Color.b = Edge1->Fragment1->Color.b + Edge1->ColorDiff.b * factor1;

            Fragment2.Color.r = Edge2->Fragment1->Color.r + Edge2->ColorDiff.r * factor2;
            Fragment2.Color.g = Edge2->Fragment1->Color.g + Edge2->ColorDiff.g * factor2;
            Fragment2.Color.b = Edge2->Fragment1->Color.b + Edge2->ColorDiff.b * factor2;

            if(Texturing)
            {
                Fragment1.TexCoord.s = Edge1->Fragment1->TexCoord.s + Edge1->TexCoordDiff.s * factor1;
                Fragment1.TexCoord.t = Edge1->Fragment1->TexCoord.t + Edge1->TexCoordDiff.t * factor1;

                Fragment2.TexCoord.s = Edge2->Fragment1->TexCoord.s + Edge2->TexCoordDiff.s * factor2;
                Fragment2.TexCoord.t = Edge2->Fragment1->TexCoord.t + Edge2->TexCoordDiff.t * factor2;
            }

            if(Lighting)
            {
                Fragment1.Normal.x = Edge1->Fragment1->Normal.x + Edge1->NormalDiff.x * factor1;
                Fragment1.Normal.y = Edge1->Fragment1->Normal.y + Edge1->NormalDiff.y * factor1;
                Fragment1.Normal.z = Edge1->Fragment1->Normal.z + Edge1->NormalDiff.z * factor1;

                Fragment2.Normal.x = Edge2->Fragment1->Normal.x + Edge2->NormalDiff.x * factor2;
                Fragment2.Normal.y = Edge2->Fragment1->Normal.y + Edge2->NormalDiff.y * factor2;
                Fragment2.Normal.z = Edge2->Fragment1->Normal.z + Edge2->NormalDiff.z * factor2;

                Fragment1.LightDirection.x = Edge1->Fragment1->LightDirection.x + Edge1->LightDirectionDiff.x * factor1;
                Fragment1.LightDirection.y = Edge1->Fragment1->LightDirection.y + Edge1->LightDirectionDiff.y * factor1;
                Fragment1.LightDirection.z = Edge1->Fragment1->LightDirection.z + Edge1->LightDirectionDiff.z * factor1;

                Fragment2.LightDirection.x = Edge2->Fragment1->LightDirection.x + Edge2->LightDirectionDiff.x * factor2;
                Fragment2.LightDirection.y = Edge2->Fragment1->LightDirection.y + Edge2->LightDirectionDiff.y * factor2;
                Fragment2.LightDirection.z = Edge2->Fragment1->LightDirection.z + Edge2->LightDirectionDiff.z * factor2;

                if(ShadowMapping)
                {
                    Fragment1.ShadowMapTexCoord.s = Edge1->Fragment1->ShadowMapTexCoord.s + Edge1->ShadowMapTexCoordDiff.s * factor1;
                    Fragment1.ShadowMapTexCoord.t = Edge1->Fragment1->ShadowMapTexCoord.t + Edge1->ShadowMapTexCoordDiff.t * factor1;
                    Fragment1.ShadowMapTexCoord.p = Edge1->Fragment1->ShadowMapTexCoord.p + Edge1->ShadowMapTexCoordDiff.p * factor1;
                    Fragment1.ShadowMapTexCoord.q = Edge1->Fragment1->ShadowMapTexCoord.q + Edge1->ShadowMapTexCoordDiff.q * factor1;

                    Fragment2.ShadowMapTexCoord.s = Edge2->Fragment1->ShadowMapTexCoord.s + Edge2->ShadowMapTexCoordDiff.s * factor2;
                    Fragment2.ShadowMapTexCoord.t = Edge2->Fragment1->ShadowMapTexCoord.t + Edge2->ShadowMapTexCoordDiff.t * factor2;
                    Fragment2.ShadowMapTexCoord.p = Edge2->Fragment1->ShadowMapTexCoord.p + Edge2->ShadowMapTexCoordDiff.p * factor2;
                    Fragment2.ShadowMapTexCoord.q = Edge2->Fragment1->ShadowMapTexCoord.q + Edge2->ShadowMapTexCoordDiff.q * factor2;
                }
            }
        }

        Span.Set(&Fragment1, &Fragment2);

        Span.PositionDiff.x = Span.Fragment2->Position.x - Span.Fragment1->Position.x;
        Span.PositionDiff.y = Span.Fragment2->Position.y - Span.Fragment1->Position.y;
        Span.PositionDiff.z = Span.Fragment2->Position.z - Span.Fragment1->Position.z;
        Span.PositionDiff.w = Span.Fragment2->Position.w - Span.Fragment1->Position.w;

        if(ColorBuffering)
        {
            Span.ColorDiff.r = Span.Fragment2->Color.r - Span.Fragment1->Color.r;
            Span.ColorDiff.g = Span.Fragment2->Color.g - Span.Fragment1->Color.g;
            Span.ColorDiff.b = Span.Fragment2->Color.b - Span.Fragment1->Color.b;

            if(Texturing)
            {
                Span.TexCoordDiff.s = Span.Fragment2->TexCoord.s - Span.Fragment1->TexCoord.s;
                Span.TexCoordDiff.t = Span.Fragment2->TexCoord.t - Span.Fragment1->TexCoord.t;
            }

            if(Lighting)
            {
                Span.NormalDiff.x = Span.Fragment2->Normal.x - Span.Fragment1->Normal.x;
                Span.NormalDiff.y = Span.Fragment2->Normal.y - Span.Fragment1->Normal.y;
                Span.NormalDiff.z = Span.Fragment2->Normal.z - Span.Fragment1->Normal.z;

                Span.LightDirectionDiff.x = Span.Fragment2->LightDirection.x - Span.Fragment1->LightDirection.x;
                Span.LightDirectionDiff.y = Span.Fragment2->LightDirection.y - Span.Fragment1->LightDirection.y;
                Span.LightDirectionDiff.z = Span.Fragment2->LightDirection.z - Span.Fragment1->LightDirection.z;

                if(ShadowMapping)
                {
                    Span.ShadowMapTexCoordDiff.s = Span.Fragment2->ShadowMapTexCoord.s - Span.Fragment1->ShadowMapTexCoord.s;
                    Span.ShadowMapTexCoordDiff.t = Span.Fragment2->ShadowMapTexCoord.t - Span.Fragment1->ShadowMapTexCoord.t;
                    Span.ShadowMapTexCoordDiff.p = Span.Fragment2->ShadowMapTexCoord.p - Span.Fragment1->ShadowMapTexCoord.p;
                    Span.ShadowMapTexCoordDiff.q = Span.Fragment2->ShadowMapTexCoord.q - Span.Fragment1->ShadowMapTexCoord.q;
                }
            }
        }

        DrawSpan(&Span, y);
    }
}

void CSoftwareGL::DrawSpan(CSpan *Span, int y)
{
    if(Span->PositionDiff.x == 0.0f) return;

    int ix1 = (int)Span->Fragment1->Position.x, ix2 = (int)Span->Fragment2->Position.x;

    if(ix1 < 0) ix1 = 0;

    if(ix2 > BufferWidthM1) ix2 = BufferWidthM1;

    float factor, odspdx = 1.0f / Span->PositionDiff.x, z, w;
    USHORT Depth;

    vec3 Color;
    vec2 TexCoord;
    vec3 Normal;
    vec3 LightDirection;
    vec4 ShadowMapTexCoord;

    vec3 TextureColor;
    float NdotLD, Shadow, NormalLength, LightDistance2, LightDistance, LightAttenuation;

    BYTE *ColorBuffer = this->ColorBuffer + (ColorBufferWidth * y + ix1) * 3;
    USHORT *DepthBuffer = this->DepthBuffer + DepthBufferWidth * y + ix1;

    for(int x = ix1; x <= ix2; x++)
    {
        factor = (x + 0.5f - Span->Fragment1->Position.x) * odspdx;

        if(factor >= 0.0f && factor <= 1.0f)
        {
            z = Span->Fragment1->Position.z + Span->PositionDiff.z * factor;

            Depth = (USHORT)(z * 65535.0f);

            // depth test

            if(NotDepthTesting || Depth <= *DepthBuffer)
            {
                w = 1.0f / (Span->Fragment1->Position.w + Span->PositionDiff.w * factor);

                if(ColorBuffering)
                {
                    Color.r = (Span->Fragment1->Color.r + Span->ColorDiff.r * factor) * w;
                    Color.g = (Span->Fragment1->Color.g + Span->ColorDiff.g * factor) * w;
                    Color.b = (Span->Fragment1->Color.b + Span->ColorDiff.b * factor) * w;

                    if(Texturing)
                    {
                        TexCoord.s = (Span->Fragment1->TexCoord.s + Span->TexCoordDiff.s * factor) * w;
                        TexCoord.t = (Span->Fragment1->TexCoord.t + Span->TexCoordDiff.t * factor) * w;
                    }

                    if(Lighting)
                    {
                        Normal.x = (Span->Fragment1->Normal.x + Span->NormalDiff.x * factor) * w;
                        Normal.y = (Span->Fragment1->Normal.y + Span->NormalDiff.y * factor) * w;
                        Normal.z = (Span->Fragment1->Normal.z + Span->NormalDiff.z * factor) * w;

                        LightDirection.x = (Span->Fragment1->LightDirection.x + Span->LightDirectionDiff.x * factor) * w;
                        LightDirection.y = (Span->Fragment1->LightDirection.y + Span->LightDirectionDiff.y * factor) * w;
                        LightDirection.z = (Span->Fragment1->LightDirection.z + Span->LightDirectionDiff.z * factor) * w;

                        if(ShadowMapping)
                        {
                            ShadowMapTexCoord.s = (Span->Fragment1->ShadowMapTexCoord.s + Span->ShadowMapTexCoordDiff.s * factor) * w;
                            ShadowMapTexCoord.t = (Span->Fragment1->ShadowMapTexCoord.t + Span->ShadowMapTexCoordDiff.t * factor) * w;
                            ShadowMapTexCoord.p = (Span->Fragment1->ShadowMapTexCoord.p + Span->ShadowMapTexCoordDiff.p * factor) * w;
                            ShadowMapTexCoord.q = (Span->Fragment1->ShadowMapTexCoord.q + Span->ShadowMapTexCoordDiff.q * factor) * w;
                        }
                    }

                    // fragment shader

                    if(Texturing)
                    {
                        if(BilinearTextureFiltering)
                        {
                            Texture->GetColorBilinear(TexCoord.s, TexCoord.t, TextureColor);
                        }
                        else
                        {
                            Texture->GetColorNearest(TexCoord.s, TexCoord.t, TextureColor);
                        }

                        Color.r *= TextureColor.r;
                        Color.g *= TextureColor.g;
                        Color.b *= TextureColor.b;
                    }

                    if(Lighting)
                    {
                        LightDistance2 = LightDirection.x * LightDirection.x + LightDirection.y * LightDirection.y + LightDirection.z * LightDirection.z;
                        LightDistance = sqrt(LightDistance2);

                        NdotLD = Normal.x * LightDirection.x + Normal.y * LightDirection.y + Normal.z * LightDirection.z;

                        if(NdotLD < 0.0f)
                        {
                            NdotLD = 0.0f;
                        }
                        else
                        {
                            if(ShadowMapping)
                            {
                                if(BilinearTextureFiltering)
                                {
                                    NdotLD *= ShadowMap->GetShadowBilinear(ShadowMapTexCoord);
                                }
                                else
                                {
                                    NdotLD *= ShadowMap->GetShadowNearest(ShadowMapTexCoord);
                                }
                            }

                            if(NdotLD > 0.0f)
                            {
                                NormalLength = sqrt(Normal.x * Normal.x + Normal.y * Normal.y + Normal.z * Normal.z);

                                NdotLD /= NormalLength * LightDistance;
                            }
                        }

                        LightAttenuation = 1.0f / (Light->ConstantAttenuation + Light->LinearAttenuation * LightDistance + Light->QuadraticAttenuation * LightDistance2);

                        Color.r *= (Light->Ambient.r + Light->Diffuse.r * NdotLD) * LightAttenuation;
                        Color.g *= (Light->Ambient.g + Light->Diffuse.g * NdotLD) * LightAttenuation;
                        Color.b *= (Light->Ambient.b + Light->Diffuse.b * NdotLD) * LightAttenuation;
                    }

                    // writing to color buffer

                    *ColorBuffer = /*Color.b <= 0.0f ? 0 : Color.b >= 1.0f ? 255 :*/ (BYTE)(Color.b * 255.0f); ColorBuffer++;
                    *ColorBuffer = /*Color.g <= 0.0f ? 0 : Color.g >= 1.0f ? 255 :*/ (BYTE)(Color.g * 255.0f); ColorBuffer++;
                    *ColorBuffer = /*Color.r <= 0.0f ? 0 : Color.r >= 1.0f ? 255 :*/ (BYTE)(Color.r * 255.0f); ColorBuffer++;
                }
                else
                {
                    ColorBuffer += 3;
                }

                // writing to depth buffer

                if(DepthTesting)
                {
                    *DepthBuffer = Depth; DepthBuffer++;
                }
            }
            else
            {
                ColorBuffer += 3;
                DepthBuffer++;
            }
        }
        else
        {
            ColorBuffer += 3;
            DepthBuffer++;
        }
    }
}

void CSoftwareGL::BlitAntiAliasingColorBuffer2x2(int ThreadId)
{
    int TCM2 = ThreadsCount * 2, AACBWM3 = AntiAliasingColorBufferWidth * 3;
    int LineAACB0, LineAACB1, LineSCB;
    int xm3;
    int LineAACB0Px0, LineAACB0Px1;
    int LineAACB1Px0, LineAACB1Px1;
    int LineSCBPxD2M3;
    float r, g, b;

    for(int y = ThreadId * 2; y < AntiAliasingColorBufferHeight; y += TCM2)
    {
        LineAACB0 = AACBWM3 * y;
        LineAACB1 = LineAACB0 + AACBWM3;
        LineSCB = StandardColorBufferWidth * y / 2 * 3;

        for(int x = 0; x < AntiAliasingColorBufferWidth; x += 2)
        {
            xm3 = x * 3;

            LineAACB0Px0 = LineAACB0 + xm3;
            LineAACB0Px1 = LineAACB0Px0 + 3;
            LineAACB1Px0 = LineAACB1 + xm3;
            LineAACB1Px1 = LineAACB1Px0 + 3;

            LineSCBPxD2M3 = LineSCB + x / 2 * 3;

            b = AntiAliasingColorBuffer[LineAACB0Px0++];
            g = AntiAliasingColorBuffer[LineAACB0Px0++];
            r = AntiAliasingColorBuffer[LineAACB0Px0];

            b += AntiAliasingColorBuffer[LineAACB0Px1++];
            g += AntiAliasingColorBuffer[LineAACB0Px1++];
            r += AntiAliasingColorBuffer[LineAACB0Px1];

            b += AntiAliasingColorBuffer[LineAACB1Px0++];
            g += AntiAliasingColorBuffer[LineAACB1Px0++];
            r += AntiAliasingColorBuffer[LineAACB1Px0];

            b += AntiAliasingColorBuffer[LineAACB1Px1++];
            g += AntiAliasingColorBuffer[LineAACB1Px1++];
            r += AntiAliasingColorBuffer[LineAACB1Px1];

            StandardColorBuffer[LineSCBPxD2M3++] = (BYTE)(b / 4.0f);
            StandardColorBuffer[LineSCBPxD2M3++] = (BYTE)(g / 4.0f);
            StandardColorBuffer[LineSCBPxD2M3] = (BYTE)(r / 4.0f);
        }
    }
}

void CSoftwareGL::BlitAntiAliasingColorBuffer3x3(int ThreadId)
{
    int TCM3 = ThreadsCount * 3, AACBWM3 = AntiAliasingColorBufferWidth * 3;
    int LineAACB0, LineAACB1, LineAACB2, LineSCB;
    int xm3;
    int LineAACB0Px0, LineAACB0Px1, LineAACB0Px2;
    int LineAACB1Px0, LineAACB1Px1, LineAACB1Px2;
    int LineAACB2Px0, LineAACB2Px1, LineAACB2Px2;
    int LineSCBPxD3M3;
    float r, g, b;

    for(int y = ThreadId * 3; y < AntiAliasingColorBufferHeight; y += TCM3)
    {
        LineAACB0 = AACBWM3 * y;
        LineAACB1 = LineAACB0 + AACBWM3;
        LineAACB2 = LineAACB1 + AACBWM3;
        LineSCB = StandardColorBufferWidth * y;

        for(int x = 0; x < AntiAliasingColorBufferWidth; x += 3)
        {
            xm3 = x * 3;

            LineAACB0Px0 = LineAACB0 + xm3;
            LineAACB0Px1 = LineAACB0Px0 + 3;
            LineAACB0Px2 = LineAACB0Px1 + 3;
            LineAACB1Px0 = LineAACB1 + xm3;
            LineAACB1Px1 = LineAACB1Px0 + 3;
            LineAACB1Px2 = LineAACB1Px1 + 3;
            LineAACB2Px0 = LineAACB2 + xm3;
            LineAACB2Px1 = LineAACB2Px0 + 3;
            LineAACB2Px2 = LineAACB2Px1 + 3;

            LineSCBPxD3M3 = LineSCB + x;

            b = AntiAliasingColorBuffer[LineAACB0Px0++];
            g = AntiAliasingColorBuffer[LineAACB0Px0++];
            r = AntiAliasingColorBuffer[LineAACB0Px0];

            b += AntiAliasingColorBuffer[LineAACB0Px1++];
            g += AntiAliasingColorBuffer[LineAACB0Px1++];
            r += AntiAliasingColorBuffer[LineAACB0Px1];

            b += AntiAliasingColorBuffer[LineAACB0Px2++];
            g += AntiAliasingColorBuffer[LineAACB0Px2++];
            r += AntiAliasingColorBuffer[LineAACB0Px2];

            b += AntiAliasingColorBuffer[LineAACB1Px0++];
            g += AntiAliasingColorBuffer[LineAACB1Px0++];
            r += AntiAliasingColorBuffer[LineAACB1Px0];

            b += AntiAliasingColorBuffer[LineAACB1Px1++];
            g += AntiAliasingColorBuffer[LineAACB1Px1++];
            r += AntiAliasingColorBuffer[LineAACB1Px1];

            b += AntiAliasingColorBuffer[LineAACB1Px2++];
            g += AntiAliasingColorBuffer[LineAACB1Px2++];
            r += AntiAliasingColorBuffer[LineAACB1Px2];

            b += AntiAliasingColorBuffer[LineAACB2Px0++];
            g += AntiAliasingColorBuffer[LineAACB2Px0++];
            r += AntiAliasingColorBuffer[LineAACB2Px0];

            b += AntiAliasingColorBuffer[LineAACB2Px1++];
            g += AntiAliasingColorBuffer[LineAACB2Px1++];
            r += AntiAliasingColorBuffer[LineAACB2Px1];

            b += AntiAliasingColorBuffer[LineAACB2Px2++];
            g += AntiAliasingColorBuffer[LineAACB2Px2++];
            r += AntiAliasingColorBuffer[LineAACB2Px2];

            StandardColorBuffer[LineSCBPxD3M3++] = (BYTE)(b / 9.0f);
            StandardColorBuffer[LineSCBPxD3M3++] = (BYTE)(g / 9.0f);
            StandardColorBuffer[LineSCBPxD3M3] = (BYTE)(r / 9.0f);
        }
    }
}

void CSoftwareGL::BlitAntiAliasingColorBuffer4x4(int ThreadId)
{
    int TCM4 = ThreadsCount * 4, AACBWM3 = AntiAliasingColorBufferWidth * 3;
    int LineAACB0, LineAACB1, LineAACB2, LineAACB3, LineSCB;
    int xm3;
    int LineAACB0Px0, LineAACB0Px1, LineAACB0Px2, LineAACB0Px3;
    int LineAACB1Px0, LineAACB1Px1, LineAACB1Px2, LineAACB1Px3;
    int LineAACB2Px0, LineAACB2Px1, LineAACB2Px2, LineAACB2Px3;
    int LineAACB3Px0, LineAACB3Px1, LineAACB3Px2, LineAACB3Px3;
    int LineSCBPxD4M3;
    float r, g, b;

    for(int y = ThreadId * 4; y < AntiAliasingColorBufferHeight; y += TCM4)
    {
        LineAACB0 = AACBWM3 * y;
        LineAACB1 = LineAACB0 + AACBWM3;
        LineAACB2 = LineAACB1 + AACBWM3;
        LineAACB3 = LineAACB2 + AACBWM3;
        LineSCB = StandardColorBufferWidth * y / 4 * 3;

        for(int x = 0; x < AntiAliasingColorBufferWidth; x += 4)
        {
            xm3 = x * 3;

            LineAACB0Px0 = LineAACB0 + xm3;
            LineAACB0Px1 = LineAACB0Px0 + 3;
            LineAACB0Px2 = LineAACB0Px1 + 3;
            LineAACB0Px3 = LineAACB0Px2 + 3;
            LineAACB1Px0 = LineAACB1 + xm3;
            LineAACB1Px1 = LineAACB1Px0 + 3;
            LineAACB1Px2 = LineAACB1Px1 + 3;
            LineAACB1Px3 = LineAACB1Px2 + 3;
            LineAACB2Px0 = LineAACB2 + xm3;
            LineAACB2Px1 = LineAACB2Px0 + 3;
            LineAACB2Px2 = LineAACB2Px1 + 3;
            LineAACB2Px3 = LineAACB2Px2 + 3;
            LineAACB3Px0 = LineAACB3 + xm3;
            LineAACB3Px1 = LineAACB3Px0 + 3;
            LineAACB3Px2 = LineAACB3Px1 + 3;
            LineAACB3Px3 = LineAACB3Px2 + 3;

            LineSCBPxD4M3 = LineSCB + x / 4 * 3;

            b = AntiAliasingColorBuffer[LineAACB0Px0++];
            g = AntiAliasingColorBuffer[LineAACB0Px0++];
            r = AntiAliasingColorBuffer[LineAACB0Px0];

            b += AntiAliasingColorBuffer[LineAACB0Px1++];
            g += AntiAliasingColorBuffer[LineAACB0Px1++];
            r += AntiAliasingColorBuffer[LineAACB0Px1];

            b += AntiAliasingColorBuffer[LineAACB0Px2++];
            g += AntiAliasingColorBuffer[LineAACB0Px2++];
            r += AntiAliasingColorBuffer[LineAACB0Px2];

            b += AntiAliasingColorBuffer[LineAACB0Px3++];
            g += AntiAliasingColorBuffer[LineAACB0Px3++];
            r += AntiAliasingColorBuffer[LineAACB0Px3];

            b += AntiAliasingColorBuffer[LineAACB1Px0++];
            g += AntiAliasingColorBuffer[LineAACB1Px0++];
            r += AntiAliasingColorBuffer[LineAACB1Px0];

            b += AntiAliasingColorBuffer[LineAACB1Px1++];
            g += AntiAliasingColorBuffer[LineAACB1Px1++];
            r += AntiAliasingColorBuffer[LineAACB1Px1];

            b += AntiAliasingColorBuffer[LineAACB1Px2++];
            g += AntiAliasingColorBuffer[LineAACB1Px2++];
            r += AntiAliasingColorBuffer[LineAACB1Px2];

            b += AntiAliasingColorBuffer[LineAACB1Px3++];
            g += AntiAliasingColorBuffer[LineAACB1Px3++];
            r += AntiAliasingColorBuffer[LineAACB1Px3];

            b += AntiAliasingColorBuffer[LineAACB2Px0++];
            g += AntiAliasingColorBuffer[LineAACB2Px0++];
            r += AntiAliasingColorBuffer[LineAACB2Px0];

            b += AntiAliasingColorBuffer[LineAACB2Px1++];
            g += AntiAliasingColorBuffer[LineAACB2Px1++];
            r += AntiAliasingColorBuffer[LineAACB2Px1];

            b += AntiAliasingColorBuffer[LineAACB2Px2++];
            g += AntiAliasingColorBuffer[LineAACB2Px2++];
            r += AntiAliasingColorBuffer[LineAACB2Px2];

            b += AntiAliasingColorBuffer[LineAACB2Px3++];
            g += AntiAliasingColorBuffer[LineAACB2Px3++];
            r += AntiAliasingColorBuffer[LineAACB2Px3];

            b += AntiAliasingColorBuffer[LineAACB3Px0++];
            g += AntiAliasingColorBuffer[LineAACB3Px0++];
            r += AntiAliasingColorBuffer[LineAACB3Px0];

            b += AntiAliasingColorBuffer[LineAACB3Px1++];
            g += AntiAliasingColorBuffer[LineAACB3Px1++];
            r += AntiAliasingColorBuffer[LineAACB3Px1];

            b += AntiAliasingColorBuffer[LineAACB3Px2++];
            g += AntiAliasingColorBuffer[LineAACB3Px2++];
            r += AntiAliasingColorBuffer[LineAACB3Px2];

            b += AntiAliasingColorBuffer[LineAACB3Px3++];
            g += AntiAliasingColorBuffer[LineAACB3Px3++];
            r += AntiAliasingColorBuffer[LineAACB3Px3];

            StandardColorBuffer[LineSCBPxD4M3++] = (BYTE)(b / 16.0f);
            StandardColorBuffer[LineSCBPxD4M3++] = (BYTE)(g / 16.0f);
            StandardColorBuffer[LineSCBPxD4M3] = (BYTE)(r / 16.0f);
        }
    }
}

void CSoftwareGL::ResizeBuffers(int Width, int Height)
{
    this->Width = Width;
    this->Height = Height;

    if(AntiAliasingColorBuffer != NULL)
    {
        delete [] AntiAliasingColorBuffer;

        AntiAliasingColorBuffer = NULL;
        AntiAliasingColorBufferWidth = 0;
        AntiAliasingColorBufferHeight = 0;
    }

    if(StandardColorBuffer != NULL)
    {
        delete [] StandardColorBuffer;

        StandardColorBuffer = NULL;
        StandardColorBufferWidth = 0;
        StandardColorBufferHeight = 0;
    }

    if(StandardDepthBuffer != NULL)
    {
        delete [] StandardDepthBuffer;

        StandardDepthBuffer = NULL;
        StandardDepthBufferWidth = 0;
        StandardDepthBufferHeight = 0;
    }

    if(FrameBuffer == NULL)
    {
        BufferWidthM1 = 0;
        BufferHeightM1 = 0;
    }

    if(Width > 0 && Height > 0)
    {
        int Factor = 1;

        if(AntiAliasing)
        {
            switch(AntiAliasing)
            {
                case ANTI_ALIASING_2X2: Factor = 2; break;
                case ANTI_ALIASING_3X3: Factor = 3; break;
                case ANTI_ALIASING_4X4: Factor = 4; break;
            }

            AntiAliasingColorBufferWidth = Width * Factor;
            AntiAliasingColorBufferHeight = Height * Factor;

            AntiAliasingColorBuffer = new BYTE[AntiAliasingColorBufferWidth * AntiAliasingColorBufferHeight * 3];
        }

        StandardColorBufferWidth = Width;
        StandardColorBufferHeight = Height;

        int WidthMod4 = Width % 4;

        if(WidthMod4 > 0)
        {
            StandardColorBufferWidth += 4 - WidthMod4;
        }

        StandardColorBuffer = new BYTE[StandardColorBufferWidth * StandardColorBufferHeight * 3];

        memset(&BitmapInfo, 0, sizeof(BITMAPINFO));
        BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
        BitmapInfo.bmiHeader.biPlanes = 1;
        BitmapInfo.bmiHeader.biBitCount = 24;
        BitmapInfo.bmiHeader.biCompression = BI_RGB;
        BitmapInfo.bmiHeader.biWidth = StandardColorBufferWidth;
        BitmapInfo.bmiHeader.biHeight = StandardColorBufferHeight;

        StandardDepthBufferWidth = Width * Factor;
        StandardDepthBufferHeight = Height * Factor;

        StandardDepthBuffer = new USHORT[StandardDepthBufferWidth * StandardDepthBufferHeight];
    }

    if(FrameBuffer == NULL)
    {
        if(AntiAliasing)
        {
            ColorBuffer = AntiAliasingColorBuffer;
            ColorBufferWidth = AntiAliasingColorBufferWidth;
            ColorBufferHeight = AntiAliasingColorBufferHeight;
        }
        else
        {
            ColorBuffer = StandardColorBuffer;
            ColorBufferWidth = StandardColorBufferWidth;
            ColorBufferHeight = StandardColorBufferHeight;
        }

        DepthBuffer = StandardDepthBuffer;
        DepthBufferWidth = StandardDepthBufferWidth;
        DepthBufferHeight = StandardDepthBufferHeight;

        BufferWidthM1 = DepthBufferWidth - 1;
        BufferHeightM1 = DepthBufferHeight - 1;
    }
}

void CSoftwareGL::SwapBuffers(HDC hDC)
{
    if(StandardColorBuffer != NULL)
    {
        if(AntiAliasing)
        {
            if(AntiAliasingColorBuffer != NULL)
            {
                switch(AntiAliasing)
                {
                    case ANTI_ALIASING_2X2: RunFunctionMultiThreadedAndWaitForCompletion(2); break;
                    case ANTI_ALIASING_3X3: RunFunctionMultiThreadedAndWaitForCompletion(3); break;
                    case ANTI_ALIASING_4X4: RunFunctionMultiThreadedAndWaitForCompletion(4); break;
                }
            }
        }

        StretchDIBits(hDC, 0, 0, Width, Height, 0, 0, Width, Height, StandardColorBuffer, &BitmapInfo, DIB_RGB_COLORS, SRCCOPY);
    }
}

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

CSoftwareGLRenderer::CSoftwareGLRenderer()
{
    RenderObject = true;
    Texturing = true;
    Lighting = true;
    ShadowMapping = true;
    DisplayShadowMap = true;
    RotateLight = true;
}

CSoftwareGLRenderer::~CSoftwareGLRenderer()
{
}

bool CSoftwareGLRenderer::Init()
{
    bool Error = false;

    Error |= !Texture.LoadTexture("texture.jpg");

    Error |= !Object.Load("Models\\Thor\\", "thor.obj");

    if(Error)
    {
        return false;
    }

    Object.Rotate(-90.0f, vec3(0.0f, 1.0f, 0.0f));
    Object.Scale(1.75f / (Object.Max.y - Object.Min.y));
    Object.Translate(vec3(-(Object.Min.x + Object.Max.x) / 2.0f, -0.5f, -(Object.Min.z + Object.Max.z) / 2.0f));

    Vertices = new CVertex[84];

    int v = 0;

    vec3 Offset = vec3(1.0, 0.0, 0.0);

    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;

    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;

    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;

    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;

    Vertices[v].Position = vec3( 0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f,-0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;

    Vertices[v].Position = vec3(-0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f, 0.5f) + Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;

    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3(-1.0f, 0.0f, 0.0f); v++;

    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 1.0f, 0.0f, 0.0f); v++;

    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f,-1.0f, 0.0f); v++;

    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;

    Vertices[v].Position = vec3( 0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f,-0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f,-1.0f); v++;

    Vertices[v].Position = vec3(-0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(0.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3(-0.5f, 0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3(-0.5f,-0.5f, 0.5f) - Offset; Vertices[v].Color = vec3(1.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2(0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;

    Vertices[v].Position = vec3(-5.0f,-0.5f, 5.0f); Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(-5.0f,-5.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 5.0f,-0.5f, 5.0f); Vertices[v].Color = vec3(0.0f, 1.0f, 0.0f); Vertices[v].TexCoord = vec2( 5.0f,-5.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 5.0f,-0.5f,-5.0f); Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2( 5.0f, 5.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3( 5.0f,-0.5f,-5.0f); Vertices[v].Color = vec3(0.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2( 5.0f, 5.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-5.0f,-0.5f,-5.0f); Vertices[v].Color = vec3(1.0f, 0.0f, 0.0f); Vertices[v].TexCoord = vec2(-5.0f, 5.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;
    Vertices[v].Position = vec3(-5.0f,-0.5f, 5.0f); Vertices[v].Color = vec3(0.0f, 0.0f, 1.0f); Vertices[v].TexCoord = vec2(-5.0f,-5.0f); Vertices[v].Normal = vec3( 0.0f, 1.0f, 0.0f); v++;

    Vertices[v].Position = vec3( 0.0f, 0.0f, 0.0f); Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2( 0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 1.0f, 0.0f, 0.0f); Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2( 1.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 1.0f, 1.0f, 0.0f); Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2( 1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 1.0f, 1.0f, 0.0f); Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2( 1.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 0.0f, 1.0f, 0.0f); Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2( 0.0f, 1.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;
    Vertices[v].Position = vec3( 0.0f, 0.0f, 0.0f); Vertices[v].Color = vec3(1.0f, 1.0f, 1.0f); Vertices[v].TexCoord = vec2( 0.0f, 0.0f); Vertices[v].Normal = vec3( 0.0f, 0.0f, 1.0f); v++;

    for(int i = 0; i < 78; i++)
    {
        Vertices[i].Color = vec3(1.0f);
    }

    Light.Position = vec3(0.0f, 3.0f, 4.0f) * 0.875f;
    Light.Ambient = vec3(0.25f, 0.25f, 0.25f);
    Light.Diffuse = vec3(0.75f, 0.75f, 0.75f);
    Light.ConstantAttenuation = 1.0f;
    Light.LinearAttenuation = 0.0f;
    Light.QuadraticAttenuation = 0.0f;

    LightProjectionMatrix = perspective(45.0f, 1.0f, 2.85f, 8.5f);

    ShadowMap.Create(512, 512, TEXTURE_FORMAT_DEPTH16);

    FrameBuffer.SetDepthTexture(&ShadowMap);

    RenderShadowMap();

    OrthogonalProjectionMatrix = ortho(0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);

    return true;
}

void CSoftwareGLRenderer::Animate(float FrameTime)
{
    if(RotateLight)
    {
        Light.Position = rotate(Light.Position, 11.25f * FrameTime, vec3(0.0f, 1.0f, 0.0f));

        RenderShadowMap();
    }
}

void CSoftwareGLRenderer::Render()
{
    Clear();

    LoadModelViewProjectionMatrix(Camera.ViewProjectionMatrix);

    if(Lighting)
    {
        BindLight(&Light);

        if(ShadowMapping)
        {
            LoadShadowMapMatrix(BiasMatrix * LightViewProjectionMatrix);

            BindShadowMap(&ShadowMap);
        }
    }

    if(Texturing)
    {
        BindTexture(&Texture);
    }

    DrawTriangles(Vertices, 0, 78);

    if(RenderObject)
    {
        if(Texturing)
        {
            BindTexture(&Object.Texture);
        }

        DrawTriangles(Object.Vertices, 0, Object.VerticesCount);
    }

    if(Texturing)
    {
        BindTexture(NULL);
    }

    if(Lighting)
    {
        if(ShadowMapping)
        {
            BindShadowMap(NULL);
        }

        BindLight(NULL);
    }

    if(DisplayShadowMap)
    {
        LoadModelViewProjectionMatrix(OrthogonalProjectionMatrix);

        Viewport(16, 16, 256, 256);

        BindTexture(&ShadowMap);

        SetDepthTest(false);

        DrawTriangles(Vertices, 78, 6);

        SetDepthTest(true);

        BindTexture(NULL);

        Viewport(0, 0, Width, Height);
    }
}

void CSoftwareGLRenderer::Resize(int Width, int Height)
{
    this->Width = Width;
    this->Height = Height;

    Viewport(0, 0, Width, Height);

    Camera.SetPerspective(45.0f, (float)Width / (float)Height, 0.125f, 512.0f);
}

void CSoftwareGLRenderer::Destroy()
{
    Texture.Destroy();

    Object.Destroy();

    delete [] Vertices;

    ShadowMap.Destroy();
}

void CSoftwareGLRenderer::RenderShadowMap()
{
    if((Lighting && ShadowMapping) || DisplayShadowMap)
    {
        LightViewMatrix = look(Light.Position, vec3(0.0f, 0.0f, 0.0f), vec3(0.0f, 1.0f, 0.0f));
        LightViewProjectionMatrix = LightProjectionMatrix * LightViewMatrix;

        BindFrameBuffer(&FrameBuffer);

        Viewport(0, 0, ShadowMap.GetWidth(), ShadowMap.GetHeight());

        Clear();

        LoadModelViewProjectionMatrix(LightViewProjectionMatrix);

        SetCullFace(CULL_FACE_FRONT);

        DrawTriangles(Vertices, 0, 78);

        if(RenderObject)
        {
            DrawTriangles(Object.Vertices, 0, Object.VerticesCount);
        }

        SetCullFace(CULL_FACE_BACK);

        Viewport(0, 0, Width, Height);

        BindFrameBuffer(NULL);
    }
}

void CSoftwareGLRenderer::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)
    {
        Camera.Move(Camera.OnKeys(Keys, FrameTime));
    }
}

void CSoftwareGLRenderer::OnKeyDown(UINT Key)
{
    switch(Key)
    {
        case VK_F2:
            SetBilinearTextureFiltering(!GetBilinearTextureFiltering());
            break;

        case '1':
            SetAntiAliasing(NONE);
            break;

        case '2':
            SetAntiAliasing(ANTI_ALIASING_2X2);
            break;

        case '3':
            SetAntiAliasing(ANTI_ALIASING_3X3);
            break;

        case '4':
            SetAntiAliasing(ANTI_ALIASING_4X4);
            break;

        case 'O':
            RenderObject = !RenderObject;
            if(!RotateLight) RenderShadowMap();
            break;

        case 'T':
            Texturing = !Texturing;
            break;

        case 'L':
            Lighting = !Lighting;
            if(!RotateLight) RenderShadowMap();
            break;

        case 'P':
            ShadowMapping = !ShadowMapping;
            if(!RotateLight) RenderShadowMap();
            break;

        case 'M':
            DisplayShadowMap = !DisplayShadowMap;
            if(!RotateLight) RenderShadowMap();
            break;

        case VK_SPACE:
            RotateLight = !RotateLight;
            break;
    }
}

void CSoftwareGLRenderer::OnLButtonDown(int X, int Y)
{
    LastClickedX = X;
    LastClickedY = Y;
}

void CSoftwareGLRenderer::OnLButtonUp(int X, int Y)
{
    if(X == LastClickedX && Y == LastClickedY)
    {
    }
}

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

    LastX = X;
    LastY = Y;
}

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

void CSoftwareGLRenderer::OnRButtonDown(int X, int Y)
{
    LastClickedX = X;
    LastClickedY = Y;
}

void CSoftwareGLRenderer::OnRButtonUp(int X, int Y)
{
    if(X == LastClickedX && Y == LastClickedY)
    {
    }
}

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

CSoftwareGLView::CSoftwareGLView()
{
    char *moduledirectory = new char[256];
    GetModuleFileName(GetModuleHandle(NULL), moduledirectory, 256);
    *(strrchr(moduledirectory, '\\') + 1) = 0;
    ModuleDirectory = moduledirectory;
    delete [] moduledirectory;
}

CSoftwareGLView::~CSoftwareGLView()
{
}

bool CSoftwareGLView::Create(HINSTANCE hInstance, char *Title, int Width, int 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 = "Win32OpenGLWindow";

    if(RegisterClassEx(&WndClassEx) == 0)
    {
        ErrorLog.Set("RegisterClassEx failed!");
        return false;
    }

    this->Title = Title;

    this->Width = Width;
    this->Height = Height;

    DWORD Style = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

    if((hWnd = CreateWindowEx(WS_EX_APPWINDOW, WndClassEx.lpszClassName, Title, Style, 0, 0, Width, Height, NULL, NULL, hInstance, NULL)) == NULL)
    {
        ErrorLog.Set("CreateWindowEx failed!");
        return false;
    }

    if((hDC = GetDC(hWnd)) == NULL)
    {
        ErrorLog.Set("GetDC failed!");
        return false;
    }

    return SoftwareGLRenderer.Init();
}

void CSoftwareGLView::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 CSoftwareGLView::MsgLoop()
{
    MSG Msg;

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
}

void CSoftwareGLView::Destroy()
{
    SoftwareGLRenderer.Destroy();

    DestroyWindow(hWnd);
}

void CSoftwareGLView::OnKeyDown(UINT Key)
{
    switch(Key)
    {
        case '5':
            ShowWindow(hWnd, SW_HIDE);
            Width = 400;
            Height = 300;
            Show();
            break;

        case '6':
            ShowWindow(hWnd, SW_HIDE);
            Width = 512;
            Height = 384;
            Show();
            break;

        case '7':
            ShowWindow(hWnd, SW_HIDE);
            Width = 640;
            Height = 480;
            Show();
            break;

        case '8':
            ShowWindow(hWnd, SW_HIDE);
            Width = 800;
            Height = 600;
            Show();
            break;

        case '9':
            ShowWindow(hWnd, SW_HIDE);
            Width = 1024;
            Height = 768;
            Show();
            break;

        case '0':
            ShowWindow(hWnd, SW_HIDE);
            Width = 1280;
            Height = 800;
            Show();
            break;
    }

    SoftwareGLRenderer.OnKeyDown(Key);
}

void CSoftwareGLView::OnLButtonDown(int X, int Y)
{
    SoftwareGLRenderer.OnLButtonDown(X, Y);
}

void CSoftwareGLView::OnLButtonUp(int X, int Y)
{
    SoftwareGLRenderer.OnLButtonUp(X, Y);
}

void CSoftwareGLView::OnMouseMove(int X, int Y)
{
    SoftwareGLRenderer.OnMouseMove(X, Y);
}

void CSoftwareGLView::OnMouseWheel(short zDelta)
{
    SoftwareGLRenderer.OnMouseWheel(zDelta);
}

void CSoftwareGLView::OnPaint()
{
    PAINTSTRUCT ps;

    BeginPaint(hWnd, &ps);

    static DWORD LastFPSTime = GetTickCount(), LastFrameTime = LastFPSTime;
    static int FPS = 0;

    DWORD Time = GetTickCount();

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

    LastFrameTime = Time;

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

        Text.Append(" - %dx%d", Width, Height);
        Text.Append(", BTF: "); if(SoftwareGLRenderer.GetBilinearTextureFiltering()) Text.Append("ON"); else Text.Append("OFF");
        if(SoftwareGLRenderer.GetAntiAliasing() == NONE) Text.Append(", AA: OFF");
        else if(SoftwareGLRenderer.GetAntiAliasing() == ANTI_ALIASING_2X2) Text.Append(", AA: 4x");
        else if(SoftwareGLRenderer.GetAntiAliasing() == ANTI_ALIASING_3X3) Text.Append(", AA: 9x");
        else if(SoftwareGLRenderer.GetAntiAliasing() == ANTI_ALIASING_4X4) Text.Append(", AA: 16x");
        Text.Append(", Threads: %d", SoftwareGLRenderer.GetThreadsCount());
        Text.Append(", FPS: %d", FPS);

        SetWindowText(hWnd, Text);

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

    SoftwareGLRenderer.CheckCameraKeys(FrameTime);

    SoftwareGLRenderer.Animate(FrameTime);

    SoftwareGLRenderer.Render();

    SoftwareGLRenderer.SwapBuffers(hDC);

    EndPaint(hWnd, &ps);

    InvalidateRect(hWnd, NULL, FALSE);
}

void CSoftwareGLView::OnRButtonDown(int X, int Y)
{
    SoftwareGLRenderer.OnRButtonDown(X, Y);
}

void CSoftwareGLView::OnRButtonUp(int X, int Y)
{
    SoftwareGLRenderer.OnRButtonUp(X, Y);
}

void CSoftwareGLView::OnSize(int Width, int Height)
{
    this->Width = Width;
    this->Height = Height;

    SoftwareGLRenderer.ResizeBuffers(Width, Height);

    SoftwareGLRenderer.Resize(Width, Height);
}

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

CSoftwareGLView SoftwareGLView;

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

LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uiMsg)
    {
        case WM_CLOSE:
            PostQuitMessage(0);
            break;

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

        case WM_LBUTTONDOWN:
            SoftwareGLView.OnLButtonDown(LOWORD(lParam), HIWORD(lParam));
            break;

        case WM_LBUTTONUP:
            SoftwareGLView.OnLButtonUp(LOWORD(lParam), HIWORD(lParam));
            break;

        case WM_MOUSEMOVE:
            SoftwareGLView.OnMouseMove(LOWORD(lParam), HIWORD(lParam));
            break;

        case WM_MOUSWHEEL:
            SoftwareGLView.OnMouseWheel(HIWORD(wParam));
            break;

        case WM_PAINT:
            SoftwareGLView.OnPaint();
            break;

        case WM_RBUTTONDOWN:
            SoftwareGLView.OnRButtonDown(LOWORD(lParam), HIWORD(lParam));
            break;

        case WM_RBUTTONUP:
            SoftwareGLView.OnRButtonUp(LOWORD(lParam), HIWORD(lParam));
            break;

        case WM_SIZE:
            SoftwareGLView.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)
{
    if(SoftwareGLView.Create(hInstance, "Software shadow mapping", 800, 600))
    {
        SoftwareGLView.Show();
        SoftwareGLView.MsgLoop();
    }
    else
    {
        MessageBox(NULL, ErrorLog, "Error", MB_OK | MB_ICONERROR);
    }

    SoftwareGLView.Destroy();

    return 0;
}

// ----------------------------------------------------------------------------------------------------------------------------
Download
software_shadow_mapping.zip (Visual Studio 2005 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.