3D C/C++ tutorials - OpenGL 2.1 - Loading, picking and moving objects
3D C/C++ tutorials -> OpenGL 2.1 -> Loading, picking and moving objects
Use for personal or educational purposes only. Commercial and other profit uses strictly prohibited. Exploitation of content on a website or in a publication prohibited.
To compile and run these tutorials some or all of these libraries are required: FreeImage 3.16.0, GLEW 1.11.0, GLUT 3.7.6 / GLUT for Dev-C++, GLM 0.9.5.4
opengl_tutorials_win32_framework.h
...

class CObject
{
public:
    bool Movable, CullFace;
    GLenum FrontFace;
    int TrianglesCount;
    GLuint VBO[3];
    CTexture Texture;
    vec2 *TexCoords;
    vec3 *Normals, *Vertices, Color, Min, Max, Position;

public:
    CObject();
    ~CObject();

    void CreateSphere(float Radius, int Resolution = 16, bool InvertNormals = false);
    void Destroy();
    void InitVertexBuffers();
    bool Load(char *Directory, char *ObjFileName);
    void Rotate(float Angle, const vec3 &Axis);
    void Scale(float ScaleFactor);
    void Translate(const vec3 &Translation);

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

...

class COpenGLRenderer
{
protected:
    int Width, Height;
    mat4x4 Model, View, Projection, ProjectionBiasInverse;

    CShaderProgram Shader;

    int ObjectsCount, LightObjectID;
    CObject *Objects;

    int SelectedObject;
    float PlaneD;
    vec3 SelectedPoint, PlaneNormal;

public:
    bool ShowAxisGrid, WireFrame, Texturing;

public:
    COpenGLRenderer();
    ~COpenGLRenderer();

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

    void MoveSelectedObject(int x, int y);
    void SelectObject(int x, int y);
};

...
opengl_tutorials_win32_framework.cpp
...

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

CObject::~CObject()
{
}

void CObject::CreateSphere(float Radius, int Resolution, bool InvertNormals)
{
    if(Resolution < 16) Resolution = 16;

    if(Resolution % 4) Resolution += 4 - Resolution % 4;

    TrianglesCount = Resolution * (Resolution / 4  - 1) * 4 + Resolution * 2;

    AllocateMemory();

    float angle = (float)M_PI * 2.0f / Resolution;

    vec3 a, b, c, d;
    vec2 tca, tcb, tcc, tcd;

    int i = 0;

    float r = (float)Resolution, r4 = (float)Resolution / 2.0f;

    for(int y = 0; y < Resolution / 4; y++)
    {
        for(int xz = 0; xz < Resolution; xz++)
        {
            if(y < Resolution / 4 - 1)
            {
                a = vec3(- sin(angle * (xz + 0)) * cos(angle * (y + 0)), sin(angle * (y + 0)), - cos(angle * (xz + 0)) * cos(angle * (y + 0)));
                b = vec3(- sin(angle * (xz + 1)) * cos(angle * (y + 0)), sin(angle * (y + 0)), - cos(angle * (xz + 1)) * cos(angle * (y + 0)));
                c = vec3(- sin(angle * (xz + 1)) * cos(angle * (y + 1)), sin(angle * (y + 1)), - cos(angle * (xz + 1)) * cos(angle * (y + 1)));
                d = vec3(- sin(angle * (xz + 0)) * cos(angle * (y + 1)), sin(angle * (y + 1)), - cos(angle * (xz + 0)) * cos(angle * (y + 1)));

                tca = TexCoords[i] = vec2((xz + 0) / r, 0.5f + (y + 0) / r4);
                tcb = TexCoords[i] = vec2((xz + 1) / r, 0.5f + (y + 0) / r4);
                tcc = TexCoords[i] = vec2((xz + 1) / r, 0.5f + (y + 1) / r4);
                tcd = TexCoords[i] = vec2((xz + 0) / r, 0.5f + (y + 1) / r4);

                TexCoords[i] = tca; Normals[i] = a; Vertices[i++] = a * Radius;
                TexCoords[i] = tcb; Normals[i] = b; Vertices[i++] = b * Radius;
                TexCoords[i] = tcc; Normals[i] = c; Vertices[i++] = c * Radius;

                TexCoords[i] = tcc; Normals[i] = c; Vertices[i++] = c * Radius;
                TexCoords[i] = tcd; Normals[i] = d; Vertices[i++] = d * Radius;
                TexCoords[i] = tca; Normals[i] = a; Vertices[i++] = a * Radius;

                a.y = -a.y;
                b.y = -b.y;
                c.y = -c.y;
                d.y = -d.y;

                tca.y = 1.0f - tca.y;
                tcb.y = 1.0f - tcb.y;
                tcc.y = 1.0f - tcc.y;
                tcd.y = 1.0f - tcd.y;

                TexCoords[i] = tcd; Normals[i] = d; Vertices[i++] = d * Radius;
                TexCoords[i] = tcc; Normals[i] = c; Vertices[i++] = c * Radius;
                TexCoords[i] = tcb; Normals[i] = b; Vertices[i++] = b * Radius;

                TexCoords[i] = tcb; Normals[i] = b; Vertices[i++] = b * Radius;
                TexCoords[i] = tca; Normals[i] = a; Vertices[i++] = a * Radius;
                TexCoords[i] = tcd; Normals[i] = d; Vertices[i++] = d * Radius;
            }
            else
            {
                a = vec3(- sin(angle * (xz + 0)) * cos(angle * (y + 0)), sin(angle * (y + 0)), - cos(angle * (xz + 0)) * cos(angle * (y + 0)));
                b = vec3(- sin(angle * (xz + 1)) * cos(angle * (y + 0)), sin(angle * (y + 0)), - cos(angle * (xz + 1)) * cos(angle * (y + 0)));
                c = vec3(0.0f, 1.0f, 0.0f);

                tca = TexCoords[i] = vec2((xz + 0) / r, 0.5f + (y + 0) / r4);
                tcb = TexCoords[i] = vec2((xz + 1) / r, 0.5f + (y + 0) / r4);
                tcc = TexCoords[i] = vec2((xz + 0.5f) / r, 1.0f);

                TexCoords[i] = tca; Normals[i] = a; Vertices[i++] = a * Radius;
                TexCoords[i] = tcb; Normals[i] = b; Vertices[i++] = b * Radius;
                TexCoords[i] = tcc; Normals[i] = c; Vertices[i++] = c * Radius;

                a.y = -a.y;
                b.y = -b.y;
                c.y = -c.y;

                tca.y = 1.0f - tca.y;
                tcb.y = 1.0f - tcb.y;
                tcc.y = 1.0f - tcc.y;

                TexCoords[i] = tca; Normals[i] = a; Vertices[i++] = a * Radius;
                TexCoords[i] = tcc; Normals[i] = c; Vertices[i++] = c * Radius;
                TexCoords[i] = tcb; Normals[i] = b; Vertices[i++] = b * Radius;
            }
        }
    }

    if(InvertNormals)
    {
        for(int i = 0; i < TrianglesCount * 3; i++)
        {
            Normals[i] = -Normals[i];
        }
    }

    Min = vec3(-Radius, -Radius, -Radius);
    Max = vec3(Radius, Radius, Radius);
}

void CObject::Destroy()
{
    if(gl_version >= 15)
    {
        glDeleteBuffers(3, VBO);
    }

    Texture.Delete();

    delete [] TexCoords;
    delete [] Normals;
    delete [] Vertices;

    SetDefaults();
}

void CObject::InitVertexBuffers()
{
    if(gl_version >= 15)
    {
        glDeleteBuffers(3, VBO);

        glGenBuffers(3, VBO);

        glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
        glBufferData(GL_ARRAY_BUFFER, TrianglesCount * 3 * 2 * 4, TexCoords, GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
        glBufferData(GL_ARRAY_BUFFER, TrianglesCount * 3 * 3 * 4, Normals, GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);
        glBufferData(GL_ARRAY_BUFFER, TrianglesCount * 3 * 3 * 4, Vertices, GL_STATIC_DRAW);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
}

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

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

    char *Line, *End = ObjSource + ObjLength;
    float x, y, z;
    int texcoordscount = 0, normalscount = 0, verticescount = 0;
    int i1, i2, i3, i4, i5, i6, i7, i8, i9;
    int v = 0, n = 0, t = 0, T = 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, "vt %f %f", &x, &y) == 2)
        {
            texcoordscount++;
        }
        else if(sscanf(Line, "vn %f %f %f", &x, &y, &z) == 3)
        {
            normalscount++;
        }
        else if(sscanf(Line, "v %f %f %f", &x, &y, &z) == 3)
        {
            verticescount++;
        }
        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++;
    }

    vec2 *texcoords = NULL;
    vec3 *normals = NULL;
    vec3 *vertices = NULL;

    if(texcoordscount > 0) texcoords = new vec2[texcoordscount];
    if(normalscount > 0) normals = new vec3[normalscount];
    if(verticescount > 0) vertices = new vec3[verticescount];

    AllocateMemory();

    Line = ObjSource;

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

        if(sscanf(Line, "vt %f %f", &x, &y) == 2)
        {
            texcoords[t++] = vec2(x, y);
        }
        else if(sscanf(Line, "vn %f %f %f", &x, &y, &z) == 3)
        {
            normals[n++] = vec3(x, y ,z);
        }
        else if(sscanf(Line, "v %f %f %f", &x, &y, &z) == 3)
        {
            vertices[v++] = 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)
        {
            TexCoords[T] = texcoords[i2 - 1];
            Normals[T] = normals[i3 - 1];
            Vertices[T++] = vertices[i1 - 1];
            TexCoords[T] = texcoords[i5 - 1];
            Normals[T] = normals[i6 - 1];
            Vertices[T++] = vertices[i4 - 1];
            TexCoords[T] = texcoords[i8 - 1];
            Normals[T] = normals[i9 - 1];
            Vertices[T++] = vertices[i7 - 1];
        }
        else if(sscanf(Line, "f %d//%d %d//%d %d//%d", &i1, &i2, &i3, &i4, &i5, &i6) == 6)
        {
            Normals[T] = normals[i2 - 1];
            Vertices[T++] = vertices[i1 - 1];
            Normals[T] = normals[i4 - 1];
            Vertices[T++] = vertices[i3 - 1];
            Normals[T] = normals[i6 - 1];
            Vertices[T++] = vertices[i5 - 1];
        }
        else if(sscanf(Line, "f %d/%d %d/%d %d/%d", &i1, &i2, &i3, &i4, &i5, &i6) == 6)
        {
            if(texcoords != NULL && i1 - 1 < texcoordscount) TexCoords[T] = texcoords[i1 - 1];
            if(texcoords != NULL && i2 - 1 < texcoordscount) TexCoords[T] = texcoords[i2 - 1];
            if(normals != NULL && i1 - 1 < normalscount) Normals[T] = normals[i1 - 1];
            if(normals != NULL && i2 - 1 < normalscount) Normals[T] = normals[i2 - 1];
            Vertices[T++] = vertices[i1 - 1];
            if(texcoords != NULL && i3 - 1 < texcoordscount) TexCoords[T] = texcoords[i3 - 1];
            if(texcoords != NULL && i4 - 1 < texcoordscount) TexCoords[T] = texcoords[i4 - 1];
            if(normals != NULL && i3 - 1 < normalscount) Normals[T] = normals[i3 - 1];
            if(normals != NULL && i4 - 1 < normalscount) Normals[T] = normals[i4 - 1];
            Vertices[T++] = vertices[i3 - 1];
            if(texcoords != NULL && i5 - 1 < texcoordscount) TexCoords[T] = texcoords[i5 - 1];
            if(texcoords != NULL && i6 - 1 < texcoordscount) TexCoords[T] = texcoords[i6 - 1];
            if(normals != NULL && i5 - 1 < normalscount) Normals[T] = normals[i5 - 1];
            if(normals != NULL && i6 - 1 < normalscount) Normals[T] = normals[i6 - 1];
            Vertices[T++] = vertices[i5 - 1];
        }
        else if(sscanf(Line, "f %d %d %d", &i1, &i2, &i3) == 3)
        {
            if(texcoords != NULL && i1 - 1 < texcoordscount) TexCoords[T] = texcoords[i1 - 1];
            if(normals != NULL && i1 - 1 < normalscount) Normals[T] = normals[i1 - 1];
            Vertices[T++] = vertices[i1 - 1];
            if(texcoords != NULL && i2 - 1 < texcoordscount) TexCoords[T] = texcoords[i2 - 1];
            if(normals != NULL && i2 - 1 < normalscount) Normals[T] = normals[i2 - 1];
            Vertices[T++] = vertices[i2 - 1];
            if(texcoords != NULL && i3 - 1 < texcoordscount) TexCoords[T] = texcoords[i3 - 1];
            if(normals != NULL && i3 - 1 < normalscount) Normals[T] = normals[i3 - 1];
            Vertices[T++] = vertices[i3 - 1];
        }

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

    delete [] texcoords;
    delete [] normals;
    delete [] vertices;

    delete [] ObjSource;

    if(normalscount == 0)
    {
        for(int i = 0; i < TrianglesCount * 3; i += 3)
        {
            vec3 a = Vertices[i + 1] - Vertices[i];
            vec3 b = Vertices[i + 2] - Vertices[i];

            vec3 normal = normalize(cross(a, b));

            Normals[i + 0] = normal;
            Normals[i + 1] = normal;
            Normals[i + 2] = normal;
        }
    }

    GetMinMax();

    return true;
}

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

    for(int i = 0; i < TrianglesCount * 3; i++)
    {
        Normals[i] = Rotation * Normals[i];
        Vertices[i] = Rotation * Vertices[i];
    }

    GetMinMax();
}

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

    Min *= ScaleFactor;
    Max *= ScaleFactor;
}

void CObject::Translate(const vec3 &Translation)
{
    for(int i = 0; i < TrianglesCount * 3; i++)
    {
        Vertices[i] += Translation;
    }

    Min += Translation;
    Max += Translation;
}

void CObject::AllocateMemory()
{
    delete [] TexCoords;
    delete [] Normals;
    delete [] Vertices;

    TexCoords = new vec2[TrianglesCount * 3];
    Normals = new vec3[TrianglesCount * 3];
    Vertices = new vec3[TrianglesCount * 3];
}

void CObject::GetMinMax()
{
    for(int i = 0; i < TrianglesCount * 3; i++)
    {
        if(i == 0)
        {
            Min = Max = Vertices[i];
        }
        else
        {
            if(Min.x > Vertices[i].x) Min.x = Vertices[i].x;
            if(Min.y > Vertices[i].y) Min.y = Vertices[i].y;
            if(Min.z > Vertices[i].z) Min.z = Vertices[i].z;
            if(Max.x < Vertices[i].x) Max.x = Vertices[i].x;
            if(Max.y < Vertices[i].y) Max.y = Vertices[i].y;
            if(Max.z < Vertices[i].z) Max.z = Vertices[i].z;
        }
    }
}

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 *Texture2DFileName = Line + 6;

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

            Error |= !Texture.LoadTexture2D(CString(Directory) + Texture2DFileName);
        }

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

    delete [] MtlSource;

    return !Error;
}

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

    FILE *File;

    if(fopen_s(&File, PathFileName, "rb") != 0)
    {
        ErrorLog.Append("Error loading 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;
}

void CObject::SetDefaults()
{
    Movable = true;
    CullFace = true;
    FrontFace = GL_CCW;

    TrianglesCount = 0;

    for(int i = 0; i < 3; i++)
    {
        VBO[i] = 0;
    }

    TexCoords = NULL;
    Normals = NULL;
    Vertices = NULL;

    Color = vec3(1.0f, 1.0f, 1.0f);

    Position = vec3(0.0f, 0.0f, 0.0f);
}

...

COpenGLRenderer::COpenGLRenderer()
{
    ShowAxisGrid = true;
    WireFrame = false;
    Texturing = true;

    SelectedObject = -1;

    Camera.SetViewMatrixPointer(&View);
}

COpenGLRenderer::~COpenGLRenderer()
{
}

bool COpenGLRenderer::Init()
{
    /*if(gl_version < 21)
    {
        ErrorLog.Set("OpenGL 2.1 not supported!");
        return false;
    }*/

    bool Error = false;

    ObjectsCount = 6;

    Objects = new CObject[ObjectsCount];

    Error |= !Objects[0].Load("Models\\Thor\\", "thor.obj");
    Error |= !Objects[1].Load("Models\\Spongebob\\", "spongebob_bind.obj");
    Error |= !Objects[2].Load("Models\\Alien2\\", "alien2.obj");
    Error |= !Objects[3].Load("Models\\Teapot\\", "teapot.obj");

    Error |= !Objects[4].Texture.LoadTexture2D("earthmap.jpg");
    
    if(gl_version >= 21)
    {
        Error |= !Shader.Load("glsl120shader.vs", "glsl120shader.fs");
    }

    if(Error)
    {
        return false;
    }

    Objects[0].Rotate(-90.0f, vec3(0.0f, 1.0f, 0.0f));
    Objects[0].Scale(1.75f / (Objects[0].Max.y - Objects[0].Min.y));
    Objects[0].Translate(vec3(-(Objects[0].Min.x + Objects[0].Max.x) / 2.0f, -Objects[0].Min.y, -(Objects[0].Min.z + Objects[0].Max.z) / 2.0f));
    Objects[0].Position = vec3(0.5f, 0.0f, 0.0f);

    Objects[1].Scale(0.875f / (Objects[1].Max.y - Objects[1].Min.y));
    Objects[1].Translate(vec3(-(Objects[1].Min.x + Objects[1].Max.x) / 2.0f, -Objects[1].Min.y, -(Objects[1].Min.z + Objects[1].Max.z) / 2.0f));
    Objects[1].Position = vec3(-0.5f, 0.0f, 0.0f);

    Objects[2].Scale(1.0f / (Objects[2].Max.y - Objects[2].Min.y));
    Objects[2].Translate(vec3(-(Objects[2].Min.x + Objects[2].Max.x) / 2.0f, -Objects[2].Min.y, -(Objects[2].Min.z + Objects[2].Max.z) / 2.0f));
    Objects[2].Position = vec3(0.0f, 0.0f, -2.5f);

    Objects[3].Color = vec3(1.0f, 0.5f, 0.0f);
    Objects[3].CullFace = false;
    Objects[3].FrontFace = GL_CW;
    Objects[3].Scale(0.25f / (Objects[3].Max.y - Objects[3].Min.y));
    Objects[3].Translate(vec3(-(Objects[3].Min.x + Objects[3].Max.x) / 2.0f, -Objects[3].Min.y, -(Objects[3].Min.z + Objects[3].Max.z) / 2.0f));
    Objects[3].Position = vec3(0.0f, 0.0f, 0.0f);

    Objects[4].CreateSphere(0.5f, 32);
    Objects[4].Position = vec3(0.0f, 0.5f, -1.0f);

    Objects[5].CreateSphere(0.03125f, 16, true);
    Objects[5].Position = vec3(0.0f, 2.0f, 1.0f);

    LightObjectID = 5;

    for(int i = 0; i < ObjectsCount; i++)
    {
        Objects[i].InitVertexBuffers();
    }

    if(gl_version >= 21)
    {
        Shader.UniformLocations = new GLuint[1];
        Shader.UniformLocations[0] = glGetUniformLocation(Shader, "Texturing");
    }

    GLfloat LightModelAmbient[] = {0.0f, 0.0f, 0.0f, 1.0f};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, LightModelAmbient);

    GLfloat LightAmbient[] = {0.25f, 0.25f, 0.25f, 1.0f};
    glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);

    GLfloat LightDiffuse[] = {0.75f, 0.75f, 0.75f, 1.0f};
    glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);

    GLfloat MaterialAmbient[] = {0.25f, 0.25f, 0.25f, 1.0f};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, MaterialAmbient);

    GLfloat MaterialDiffuse[] = {0.75f, 0.75f, 0.75f, 1.0f};
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MaterialDiffuse);

    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);

    Camera.LookAt(vec3(0.0f, 0.875f, 0.0f), vec3(0.0f, 0.875f, 2.5f), true);

    return true;
}

void COpenGLRenderer::Render(float FrameTime)
{
    int gl_version = use_gl_version < ::gl_version ? use_gl_version : ::gl_version;

    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(&View);

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glEnable(GL_DEPTH_TEST);

    // render axises and grid -------------------------------------------------------------------------------------------------

    if(ShowAxisGrid)
    {
        glLineWidth(2.0f);

        glBegin(GL_LINES);

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

        glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(1.0f, 0.0f, 0.0f);
        glVertex3f(1.0f, 0.1f, 0.0f); glVertex3f(1.1f, -0.1f, 0.0f);
        glVertex3f(1.1f, 0.1f, 0.0f); glVertex3f(1.0f, -0.1f, 0.0f);

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

        glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 1.0f, 0.0f);
        glVertex3f(-0.05f, 1.25f, 0.0f); glVertex3f(0.0f, 1.15f, 0.0f);
        glVertex3f(0.05f,1.25f, 0.0f); glVertex3f(0.0f, 1.15f, 0.0f);
        glVertex3f(0.0f,1.15f, 0.0f); glVertex3f(0.0f, 1.05f, 0.0f);

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

        glVertex3f(0.0f,0.0f,0.0f); glVertex3f(0.0f, 0.0f, 1.0f);
        glVertex3f(-0.05f,0.1f,1.05f); glVertex3f(0.05f, 0.1f, 1.05f);
        glVertex3f(0.05f,0.1f,1.05f); glVertex3f(-0.05f, -0.1f, 1.05f);
        glVertex3f(-0.05f,-0.1f,1.05f); glVertex3f(0.05f, -0.1f, 1.05f);

        glEnd();

        glLineWidth(1.0f);

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

        glBegin(GL_LINES);

        float d = 50.0f;

        for(float i = -d; i <= d; i += 1.0f)
        {
            glVertex3f(i, 0.0f, -d);
            glVertex3f(i, 0.0f, d);
            glVertex3f(-d, 0.0f, i);
            glVertex3f(d, 0.0f, i);
        }

        glEnd();
    }

    // render objects ---------------------------------------------------------------------------------------------------------

    glEnable(GL_LIGHTING);

    glLightfv(GL_LIGHT0, GL_POSITION, &vec4(Objects[LightObjectID].Position, 1.0f));

    if(WireFrame)
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

    if(gl_version >= 21)
    {
        glUseProgram(Shader);
    }

    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);

    for(int i = 0; i < ObjectsCount; i++)
    {
        if(Objects[i].TrianglesCount <= 0) continue;

        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf(&View);
        glTranslatef(Objects[i].Position.x, Objects[i].Position.y, Objects[i].Position.z);

        if(!WireFrame && Objects[i].CullFace)
        {
            glEnable(GL_CULL_FACE);
        }

        glColor3fv(&Objects[i].Color);

        bool Texturing = this->Texturing && Objects[i].Texture;

        if(Texturing)
        {
            glEnable(GL_TEXTURE_2D);
            glBindTexture(GL_TEXTURE_2D, Objects[i].Texture);
        }
        
        if(gl_version >= 21)
        {
            glUniform1i(Shader.UniformLocations[0], Texturing ? 1 : 0);
        }

        if(gl_version >= 15)
        {
            glBindBuffer(GL_ARRAY_BUFFER, Objects[i].VBO[0]);
            glTexCoordPointer(2, GL_FLOAT, 0, NULL);

            glBindBuffer(GL_ARRAY_BUFFER, Objects[i].VBO[1]);
            glNormalPointer(GL_FLOAT, 0, NULL);

            glBindBuffer(GL_ARRAY_BUFFER, Objects[i].VBO[2]);
            glVertexPointer(3, GL_FLOAT, 0, NULL);
        }
        else
        {
            glTexCoordPointer(2, GL_FLOAT, 0, Objects[i].TexCoords);
            glNormalPointer(GL_FLOAT, 0, Objects[i].Normals);
            glVertexPointer(3, GL_FLOAT, 0, Objects[i].Vertices);
        }

        glFrontFace(Objects[i].FrontFace);

        glDrawArrays(GL_TRIANGLES, 0, Objects[i].TrianglesCount * 3);

        if(Texturing)
        {
            glBindTexture(GL_TEXTURE_2D, 0);
            glDisable(GL_TEXTURE_2D);
        }

        if(!WireFrame && Objects[i].CullFace)
        {
            glDisable(GL_CULL_FACE);
        }
    }
    
    if(gl_version >= 15)
    {
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

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

    if(gl_version >= 21)
    {
        glUseProgram(0);
    }

    if(WireFrame)
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }

    glDisable(GL_LIGHTING);

    glDisable(GL_DEPTH_TEST);
}

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

    glViewport(0, 0, Width, Height);

    Projection = PerspectiveProjectionMatrix(45.0f, (float)Width, (float)Height, 0.125f, 512.0f);
    ProjectionBiasInverse = PerspectiveProjectionMatrixInverse(Projection) * BiasMatrixInverse();

    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(&Projection);
}

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

    delete [] Objects;

    if(gl_version >= 21)
    {
        Shader.Delete();
    }
}

void COpenGLRenderer::MoveSelectedObject(int x, int y)
{
    if(SelectedObject < 0 || SelectedObject >= ObjectsCount || !Objects[SelectedObject].Movable) return;

    y = Height - 1 - y;

    float s = (float)x / (float)(Width - 1);
    float t = (float)y / (float)(Height - 1);

    vec4 Point = ViewMatrixInverse(View) * (ProjectionBiasInverse * vec4(s, t, 0.5f, 1.0f));
    Point /= Point.w;

    vec3 Ray = normalize(Point - Camera.Position);

    float NdotR = -dot(PlaneNormal, Ray);

    if(NdotR != 0.0f)
    {
        float Distance = (dot(PlaneNormal, Camera.Position) + PlaneD) / NdotR;
        
        vec3 Point = Ray * Distance + Camera.Position;

        vec3 Offset = Point - SelectedPoint;

        SelectedPoint = Point;

        Objects[SelectedObject].Position += Offset;
    }
}

void COpenGLRenderer::SelectObject(int x, int y)
{
    int gl_version = use_gl_version < ::gl_version ? use_gl_version : ::gl_version;

    y = Height - 1 - y;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glEnable(GL_DEPTH_TEST);

    glEnableClientState(GL_VERTEX_ARRAY);

    for(int i = 0; i < ObjectsCount; i++)
    {
        if(Objects[i].TrianglesCount <= 0) continue;

        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf(&View);
        glTranslatef(Objects[i].Position.x, Objects[i].Position.y, Objects[i].Position.z);

        if(Objects[i].CullFace)
        {
            glEnable(GL_CULL_FACE);
        }

        int ID = i + 1;

        glColor3ub(ID & 0xFF, (ID >> 8) & 0xFF, (ID >> 16) & 0xFF);

        if(gl_version >= 15)
        {
            glBindBuffer(GL_ARRAY_BUFFER, Objects[i].VBO[2]);
            glVertexPointer(3, GL_FLOAT, 0, NULL);
        }
        else
        {
            glVertexPointer(3, GL_FLOAT, 0, Objects[i].Vertices);
        }

        glFrontFace(Objects[i].FrontFace);

        glDrawArrays(GL_TRIANGLES, 0, Objects[i].TrianglesCount * 3);

        if(Objects[i].CullFace)
        {
            glDisable(GL_CULL_FACE);
        }
    }

    if(gl_version >= 15)
    {
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    glDisableClientState(GL_VERTEX_ARRAY);

    glDisable(GL_DEPTH_TEST);

    BYTE Pixel[4];
    
    glReadPixels(x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &Pixel);
    
    SelectedObject = (Pixel[0] | (Pixel[1] << 8) | (Pixel[2] << 16)) - 1;

    if(SelectedObject >= 0 && SelectedObject < ObjectsCount)
    {
        float s = (float)x / (float)(Width - 1);
        float t = (float)y / (float)(Height - 1);

        float Depth;

        glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &Depth);

        vec4 Point = ViewMatrixInverse(View) * (ProjectionBiasInverse * vec4(s, t, Depth, 1.0f));
        Point /= Point.w;

        SelectedPoint = Point;

        float omcospi4 = 1.0f - cos((float)M_PI / 4.0f);

        if(Camera.Z.y > -omcospi4 && Camera.Z.y < omcospi4)
        {
            PlaneNormal = normalize(vec3(Camera.Z.x, 0.0f, Camera.Z.z));
        }
        else
        {
            PlaneNormal = vec3(0.0f, 1.0f, 0.0f);
        }

        PlaneD = -dot(PlaneNormal, SelectedPoint);
    }
}

...

void CWnd::OnKeyDown(UINT Key)
{
    switch(Key)
    {
        case VK_F1:
            OpenGLRenderer.ShowAxisGrid = !OpenGLRenderer.ShowAxisGrid;
            break;

        case VK_F2:
            OpenGLRenderer.WireFrame = !OpenGLRenderer.WireFrame;
            break;

        case VK_F3:
            OpenGLRenderer.Texturing = !OpenGLRenderer.Texturing;
            break;

        case '1':
            use_gl_version = 11;
            break;

        case '2':
            use_gl_version = 15;
            break;

        case '3':
            use_gl_version = 21;
            break;

    }
}

void CWnd::OnLButtonDown(int cx, int cy)
{
    OpenGLRenderer.SelectObject(cx, cy);
}

void CWnd::OnMouseMove(int cx, int cy)
{
    if(GetKeyState(VK_LBUTTON) & 0x80)
    {
        OpenGLRenderer.MoveSelectedObject(cx, cy);
    }

    ...
}

...
glsl120shader.vs
#version 120

varying vec3 Position, Normal;

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

uniform sampler2D Texture;

uniform int Texturing;

varying vec3 Position, Normal;

void main()
{
    vec3 LightDirection = gl_LightSource[0].position.xyz - Position;
    float LightDistance2 = dot(LightDirection, LightDirection);
    float LightDistance = sqrt(LightDistance2);
    LightDirection /= LightDistance;
    float Attenuation = gl_LightSource[0].constantAttenuation;
    Attenuation += gl_LightSource[0].linearAttenuation * LightDistance;
    Attenuation += gl_LightSource[0].quadraticAttenuation * LightDistance2;
    float NdotLD = max(dot(normalize(Normal), LightDirection), 0.0);
    vec3 Light = (gl_LightSource[0].ambient.rgb + gl_LightSource[0].diffuse.rgb * NdotLD) / Attenuation;
    gl_FragColor.rgb = gl_Color.rgb;
    if(Texturing == 1) gl_FragColor.rgb *= texture2D(Texture, gl_TexCoord[0].st).rgb;
    gl_FragColor.rgb *= Light;
}
Download
loading_picking_moving_objects.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.