ASSIMP ачаалагч, олон материал, олон меш

өмнөх хувилбар нь ганц mesh-ийн хувьд байсан бол энэ удаад ерөнхий тохиолдолд хэрэглэгдэхээр болгосон. Цаашлаад sponza-г уншиж рэндэрлэх юм. Гэхдээ sponza ачаалах тохиолдолд ажиллахгүй, өөрөөр хэлбэл одоогоор bug-тай байгаа.

lib холболтын дараалал
-lSOIL -lassimp.dll -lglew32 -lglu32 -lglfw3 -lopengl32 -lglu32 -lgdi32 -luser32 -lkernel32

Texture.hpp
#ifndef TEXTURE_HPP
#define TEXTURE_HPP

#include <GL/glew.h>
#include <string>

class Texture {
public:
    Texture(const std::string& filePath);
    ~Texture();
    
    void upload2GPU();
    void deleteOnGPU();
    void bind(GLenum textureUnit);
    
    GLint width;
    GLint height;
    
    std::string filePath;
    GLuint textureID;
    
};

#endif /* TEXTURE_HPP */

Texture.cpp
#include "Texture.hpp"
#include <SOIL.h>
#include <iostream>

Texture::Texture(const std::string& filePath) {
    this->filePath = filePath;
}

Texture::~Texture() {
    deleteOnGPU();
    std::cout<<"texture "<<this->filePath<<" is deleted from GPU, ID="<<this->textureID<<std::endl;
}

void Texture::upload2GPU() {
    std::cout<<"starting to upload texture "<<this->filePath<<std::endl;
    glGenTextures(1, &this->textureID);
    glBindTexture(GL_TEXTURE_2D, this->textureID);
    unsigned char* img = SOIL_load_image(this->filePath.c_str(), &this->width, &this->height, NULL, SOIL_LOAD_AUTO);
    if (img==NULL) {
        std::cout<<"NULL image!!!"<<std::endl;
    }
    std::cout<<"image width, height ["<<this->width<<","<<this->height<<"]"<<std::endl;
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, this->width, this->height, 0, GL_RGB, GL_UNSIGNED_BYTE, img);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    SOIL_free_image_data(img);
}

void Texture::deleteOnGPU() {
    glDeleteTextures(1, &this->textureID);
}

void Texture::bind(GLenum textureUnit) {
    glActiveTexture(textureUnit);
    glBindTexture(textureUnit, this->textureID);
}

Mesh.hpp
#ifndef MESH_HPP
#define MESH_HPP

#include <glm/glm.hpp>
#include <string>
#include <map>
#include <vector>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "Texture.hpp"

struct Vertex {
    glm::vec3 position;
    glm::vec2 texCoord;
    glm::vec3 normal;

    Vertex() {
    }

    Vertex(const glm::vec3& position, const glm::vec2& texCoord, const glm::vec3& normal) {
        this->position = position;
        this->texCoord = texCoord;
        this->normal = normal;
    }
};

struct MeshEntry {
    MeshEntry();
    ~MeshEntry();
    void init(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices);

    GLuint vbo; // vertex buffer object
    GLuint ibo; // index buffer object
    unsigned int numIndices;
    unsigned int materialIndex;
};

class Mesh {
public:
    Mesh();
    ~Mesh();

    void loadMesh(const std::string& filePath);
    void render();
private:
    void initFromScene(const aiScene* scene, const std::string& filePath);
    void initMesh(unsigned int index, const aiMesh* mesh);
    void initMaterials(const aiScene* scene, const std::string& filePath);
    void clear();

    std::vector<MeshEntry> meshEntries;
    std::vector<Texture*> textures;
};

#endif /* MESH_HPP */



Mesh.cpp
#include "Mesh.hpp"
#include <iostream>

MeshEntry::MeshEntry() {
    this->vbo = 0xFFFFFFFF;
    this->ibo = 0xFFFFFFFF;
    this->numIndices = 0;
    this->materialIndex = 0xFFFFFFFF;
}

MeshEntry::~MeshEntry() {
    if (vbo != 0xFFFFFFFF) {
        glDeleteBuffers(1, &vbo);
    }
    if (ibo != 0xFFFFFFFF) {
        glDeleteBuffers(1, &ibo);
    }
}

void MeshEntry::init(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices) {
    this->numIndices = indices.size();

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof (Vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);

    glGenBuffers(1, &ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof (unsigned int) * this->numIndices, &indices[0], GL_STATIC_DRAW);
}

Mesh::Mesh() {
}

Mesh::~Mesh() {
    clear();
}

void Mesh::clear() {
    for (unsigned int i = 0; i<this->textures.size(); i++) {
        delete textures[i];
        textures[i] = NULL;
    }
}

void Mesh::loadMesh(const std::string& filePath) {
    clear();
    Assimp::Importer importer;
    const aiScene* aiScene = importer.ReadFile(filePath.c_str(), aiProcess_Triangulate | aiProcess_GenSmoothNormals | aiProcess_FlipUVs);
    if (aiScene) {
        initFromScene(aiScene, filePath);
    } else {
        std::cout << "error parsing " << filePath.c_str() << ", " << importer.GetErrorString() << std::endl;
    }
}

void Mesh::initFromScene(const aiScene* scene, const std::string& filePath) {
    this->meshEntries.resize(scene->mNumMeshes);
    this->textures.resize(scene->mNumMaterials);
    for (unsigned int i = 0; i<this->meshEntries.size(); i++) {
        const aiMesh* mesh = scene->mMeshes[i];
        initMesh(i, mesh);
    }
    initMaterials(scene, filePath);
}

void Mesh::initMesh(unsigned int index, const aiMesh* mesh) {
    this->meshEntries[index].materialIndex = mesh->mMaterialIndex;
    std::vector<Vertex> vertices;
    std::vector<unsigned int> indices;
    const aiVector3D zero3D(0.0f, 0.0f, 0.0f);
    for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
        const aiVector3D* position = &(mesh->mVertices[i]);
        const aiVector3D* normal = &(mesh->mNormals[i]);
        const aiVector3D* texCoord = mesh->HasTextureCoords(0) ? &(mesh->mTextureCoords[0][i]) : &zero3D;
        Vertex v(glm::vec3(position->x, position->y, position->z),
                glm::vec2(texCoord->x, texCoord->y),
                glm::vec3(normal->x, normal->y, normal->z));
        vertices.push_back(v);
    }
    for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
        const aiFace& face = mesh->mFaces[i];
        indices.push_back(face.mIndices[0]);
        indices.push_back(face.mIndices[1]);
        indices.push_back(face.mIndices[2]);
    }
    this->meshEntries[index].init(vertices, indices);
}

void Mesh::initMaterials(const aiScene* scene, const std::string& filePath) {
    std::string::size_type slashIndex = filePath.find_last_of("/");
    std::string dir;

    if (slashIndex == std::string::npos) {
        dir = ".";
    } else if (slashIndex == 0) {
        dir = "/";
    } else {
        dir = filePath.substr(0, slashIndex);
    }
    std::cout<<"material count "<<scene->mNumMaterials<<std::endl;
    for (unsigned int i = 0; i < scene->mNumMaterials; i++) {
        const aiMaterial* material = scene->mMaterials[i];
        this->textures[i] = NULL;
        int textureCount = material->GetTextureCount(aiTextureType_DIFFUSE);
        if (textureCount > 0) {
            std::cout<<"texture count "<<textureCount<<std::endl;
            aiString path;
            if (material->GetTexture(aiTextureType_DIFFUSE, 0, &path, NULL, NULL, NULL, NULL, NULL) == AI_SUCCESS) {
                //std::string fullPath = dir + "/" + path.data;
                std::string fullPath = path.data;
                std::cout << "texture path " << fullPath << std::endl;
                this->textures[i] = new Texture(fullPath.c_str());
                this->textures[i]->upload2GPU();
            }
        }
        if (this->textures[i] == NULL) {
            this->textures[i] = new Texture("white.png");
            this->textures[i]->upload2GPU();
        }
    }
}

void Mesh::render() {
    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    for (unsigned int i = 0; i<this->meshEntries.size(); i++) {
        glBindBuffer(GL_ARRAY_BUFFER, this->meshEntries[i].vbo);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), 0);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof (Vertex), (const GLvoid*) 12);
        glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof (Vertex), (const GLvoid*) 20);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->meshEntries[i].ibo);
        const unsigned int materialIndex = this->meshEntries[i].materialIndex;
        if (materialIndex<this->textures.size() && this->textures[materialIndex]) {
            this->textures[materialIndex]->bind(GL_TEXTURE0);
        }
        glDrawElements(GL_TRIANGLES, this->meshEntries[i].numIndices, GL_UNSIGNED_INT, 0);
    }

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
}


main.cpp
// OpenGL
#include <GL/glew.h>
#include <GLFW/glfw3.h>
// GLM
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// SOIL
#include <SOIL.h>
// assimp
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
// STL
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <vector>
// Mesh
#include "Mesh.hpp"

float deltaTime = 1.0f / 60.0f;

GLFWwindow *window;
int windowWidth = 640;
int windowHeight = 480;

GLuint program;
GLuint mvpUniform;
glm::mat4 modelMatrix;
glm::mat4 viewMatrix;
glm::mat4 projectionMatrix;

double mouseX, mouseY;
float speed = 10.09f;
float mouseSpeed = 0.04f;

glm::vec3 position = glm::vec3(0, 0, 5);
glm::vec3 direction, right, up;

float horizontalAngle = 3.14159f;
float verticalAngle = 0.0f;
float fov = 60.0f;

bool wKeyPressed;
bool sKeyPressed;
bool aKeyPressed;
bool dKeyPressed;

Mesh* mesh = NULL;

void initialize() {
    // GLFW ачаалах
    if (!glfwInit()) {
        std::cout << "Error: GLFW failed to initialize.\n";
        return;
    }

    // Цонх үүсгэх
    window = glfwCreateWindow(windowWidth, windowHeight, "OpenGL ASSIMP", NULL, NULL);
    if (!window) {
        std::cout << "Error: Failed to create window.\n";
        glfwTerminate();
        return;
    }
    glfwMakeContextCurrent(window);

    // OpenGL функцүүд ачаалах
    if (GLEW_OK != glewInit()) {
        std::cout << "Error: Failed to load OpenGL functions.\n";
        glfwTerminate();
        return;
    }
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
    
    mesh = new Mesh();
    mesh->loadMesh("phoenix_ugv.md2");
    
}

void windowSizeChange(GLFWwindow *window, int width, int height) {
    if (windowWidth != width || windowHeight != height) {
        windowWidth = width;
        windowHeight = height;

        projectionMatrix = glm::perspective(
                fov,
                (float) windowWidth / windowHeight,
                0.1f,
                100.0f
                );

        glUseProgram(program);
        glUniformMatrix4fv(mvpUniform, 1, GL_FALSE, glm::value_ptr(projectionMatrix));
        glUseProgram(0);

        glViewport(0, 0, (GLsizei) width, (GLsizei) height);
    }
}

void handleKeyboardInput(GLFWwindow *window, int key, int scancode, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, 1);
    else if (key == GLFW_KEY_W && action == GLFW_PRESS)
        wKeyPressed = true;
    else if (key == GLFW_KEY_S && action == GLFW_PRESS)
        sKeyPressed = true;
    else if (key == GLFW_KEY_A && action == GLFW_PRESS)
        aKeyPressed = true;
    else if (key == GLFW_KEY_D && action == GLFW_PRESS)
        dKeyPressed = true;
    else if (key == GLFW_KEY_W && action == GLFW_RELEASE)
        wKeyPressed = false;
    else if (key == GLFW_KEY_S && action == GLFW_RELEASE)
        sKeyPressed = false;
    else if (key == GLFW_KEY_A && action == GLFW_RELEASE)
        aKeyPressed = false;
    else if (key == GLFW_KEY_D && action == GLFW_RELEASE)
        dKeyPressed = false;
}

void getInput() {
    if (wKeyPressed)
        position += direction * deltaTime * speed;
    if (sKeyPressed)
        position -= direction * deltaTime * speed;
    if (aKeyPressed)
        position -= right * deltaTime * speed;
    if (dKeyPressed)
        position += right * deltaTime * speed;

    direction = glm::vec3
            (
            std::cos(verticalAngle) * std::sin(horizontalAngle),
            std::sin(verticalAngle),
            std::cos(verticalAngle) * std::cos(horizontalAngle)
            );
    right = glm::vec3
            (
            std::sin(horizontalAngle - 3.14159f / 2.0f),
            0.0f,
            std::cos(horizontalAngle - 3.14159f / 2.0f)
            );
    up = glm::cross(right, direction);

    glfwGetCursorPos(window, &mouseX, &mouseY);
    glfwSetCursorPos(window, windowWidth / 2, windowHeight / 2);
    horizontalAngle += mouseSpeed * deltaTime * float(windowWidth / 2 - mouseX);
    if (!up.y >= -0.5f)
        verticalAngle += mouseSpeed * deltaTime * float(windowHeight / 2 - mouseY);
    else if (up.y == -1.0f)
        up = glm::vec3(up.x, 1, up.z);
}

// GLSL shader програм ачаалагч

struct Program {
    static GLuint Load(const char* vert, const char* geom, const char* frag) {
        GLuint prog = glCreateProgram();
        if (vert) AttachShader(prog, GL_VERTEX_SHADER, vert);
        if (geom) AttachShader(prog, GL_GEOMETRY_SHADER, geom);
        if (frag) AttachShader(prog, GL_FRAGMENT_SHADER, frag);
        glLinkProgram(prog);
        CheckStatus(prog);
        return prog;
    }
private:
    static void CheckStatus(GLuint obj) {
        GLint status = GL_FALSE, len = 10;
        if (glIsShader(obj)) glGetShaderiv(obj, GL_COMPILE_STATUS, &status);
        if (glIsProgram(obj)) glGetProgramiv(obj, GL_LINK_STATUS, &status);
        if (status == GL_TRUE) return;
        if (glIsShader(obj)) glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &len);
        if (glIsProgram(obj)) glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &len);
        std::vector< char > log(len, 'X');
        if (glIsShader(obj)) glGetShaderInfoLog(obj, len, NULL, &log[0]);
        if (glIsProgram(obj)) glGetProgramInfoLog(obj, len, NULL, &log[0]);
        std::cerr << &log[0] << std::endl;
        exit(-1);
    }
    static void AttachShader(GLuint program, GLenum type, const char* src) {
        GLuint shader = glCreateShader(type);
        glShaderSource(shader, 1, &src, NULL);
        glCompileShader(shader);
        CheckStatus(shader);
        glAttachShader(program, shader);
        glDeleteShader(shader);
    }
};

#define GLSL(version, shader) "#version " #version "\n" #shader

const char* vert = GLSL
        (
        330 core,
        layout(location = 0) in vec3 position_modelspace;
        layout(location = 1) in vec2 uv;
        layout(location = 2) in vec3 normal_modelspace;

        out vec2 UV;
        out vec3 Position_worldspace;
        out vec3 Normal_cameraspace;
        out vec3 EyeDirection_cameraspace;
        out vec3 LightDirection_cameraspace;

        uniform mat4 mvp;
        uniform mat4 viewMatrix;
        uniform mat4 modelMatrix;
        uniform vec3 lightPosition_worldspace;

        void main() {
            gl_Position = mvp * vec4(position_modelspace, 1.0f);

            Position_worldspace = (modelMatrix * vec4(position_modelspace, 1.0f)).xyz;

            vec3 position_modelspaceCamera = (viewMatrix * modelMatrix * vec4(position_modelspace, 1.0f)).xyz;
            EyeDirection_cameraspace = vec3(0, 0, 0) - position_modelspaceCamera;

            vec3 lightPosition_worldspaceCamera = (viewMatrix * vec4(lightPosition_worldspace, 1.0f)).xyz;
            LightDirection_cameraspace = lightPosition_worldspaceCamera + EyeDirection_cameraspace;

            Normal_cameraspace = (viewMatrix * modelMatrix * vec4(normal_modelspace, 0.0f)).xyz;
            UV = uv;
        }
);

const char* frag = GLSL
        (
        330 core,
        in vec2 UV;
        in vec3 Position_worldspace;
        in vec3 Normal_cameraspace;
        in vec3 EyeDirection_cameraspace;
        in vec3 LightDirection_cameraspace;

        out vec3 color;

        uniform sampler2D textureSampler;
        uniform mat4 modelViewMatrix;
        uniform vec3 lightPosition_worldspace;

        void main() {
            vec3 lightColor = vec3(0.5, 1, 0.5);
            float lightPower = 50.0f;

            vec3 materialDiffuseColor = texture2D(textureSampler, UV).rgb;
            vec3 materialAmbientColor = vec3(0.1, 0.1, 0.1) * materialDiffuseColor;
            vec3 materialSpecularColor = vec3(0.3, 0.3, 0.3);

            float distance = length(lightPosition_worldspace - Position_worldspace);

            vec3 n = normalize(Normal_cameraspace);
            vec3 l = normalize(LightDirection_cameraspace);

            float cosTheta = clamp(dot(n, l), 0, 1);

            vec3 eye = normalize(EyeDirection_cameraspace);
            vec3 reflection = reflect(-l, n);

            float cosAlpha = clamp(dot(eye, reflection), 0, 1);

            color = materialAmbientColor +
            materialDiffuseColor * lightColor * lightPower * cosTheta / (distance * distance) +
            materialSpecularColor * lightColor * lightPower * pow(cosAlpha, 5) / (distance * distance);
            //color = materialDiffuseColor;
        }
);

int main(int argc, char *argv[]) {
    initialize();
    glfwSetWindowSizeCallback(window, windowSizeChange);
    glfwSetKeyCallback(window, handleKeyboardInput);

    // Z buffering
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    program = Program::Load(vert, NULL, frag);
    mvpUniform = glGetUniformLocation(program, "mvp");
    GLuint modelMatrixUniform = glGetUniformLocation(program, "modelMatrix");
    GLuint viewMatrixUniform = glGetUniformLocation(program, "viewMatrix");
    GLuint lightPositionUniform = glGetUniformLocation(program, "lightPosition_worldspace");

    while (!glfwWindowShouldClose(window)) {
        getInput();

        projectionMatrix = glm::perspective(fov, (float) windowWidth / windowHeight, 0.1f, 1000.0f);
        viewMatrix = glm::lookAt(position, position + direction, up);
        modelMatrix = glm::mat4(1.0f);

        glm::mat4 mvp = projectionMatrix * viewMatrix * modelMatrix;

        glfwPollEvents();

        // Clear screen
        glClearColor(0.1f, 0.2f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(program);
        glUniformMatrix4fv(mvpUniform, 1, GL_FALSE, glm::value_ptr(mvp));
        glUniformMatrix4fv(modelMatrixUniform, 1, GL_FALSE, glm::value_ptr(modelMatrix));
        glUniformMatrix4fv(viewMatrixUniform, 1, GL_FALSE, glm::value_ptr(viewMatrix));

        glm::vec3 lightPosition = glm::vec3(4, 45, 4);
        glUniform3f(lightPositionUniform, lightPosition.x, lightPosition.y, lightPosition.z);
        glUniform1i(glGetUniformLocation(program, "textureSampler"), 0);
        mesh->render();
        glUseProgram(0);

        glfwSwapBuffers(window);
    }

    delete mesh;
    glDeleteProgram(program);
    
    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}





Popular posts from this blog

Apache Spark + Cassandra. Hello World