diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 4d98bfc..c33801e 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,4 +1,3 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/glad) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/stb) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/glm) -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/assimp) diff --git a/src/Controller.cpp b/src/Controller.cpp index b98022b..d4f4362 100644 --- a/src/Controller.cpp +++ b/src/Controller.cpp @@ -48,21 +48,21 @@ void Controller::run() { std::vector scene; - //Model model_backpack("res/models/backpack.obj"); - Model model_plant("res/models/plant.obj"); - //Model model_container("res/models/container.obj"); - Model model_cube("res/models/cube.obj"); - //Model model_sphere("res/models/sphere.obj"); + Model model_backpack("res/models/backpack.ffo"); + //Model model_plant("res/models/plant.obj"); + //Model model_container("res/models/container.ffo"); + Model model_cube("res/models/cube.ffo"); + //Model model_sphere("res/models/sphere.ffo"); - //Entity backpack1(&model_backpack, &shaderProgram); + Entity backpack(&model_backpack, &shaderProgram); //Entity sphere(&model_sphere, &shaderProgram); - //Entity cube(&model_container, &shaderProgram); - Entity plant(&model_plant, &shaderProgram); + //Entity container(&model_container, &shaderProgram); + //Entity plant(&model_plant, &shaderProgram); Entity lightSource(&model_cube, &lightProgram); lightSource.translate(glm::vec3(-5.0f, 1.0f, 0.0f)); lightSource.scale(0.2f); - plant.scale(5.0f); + //plant.scale(5.0f); glm::vec3 lightColor = glm::vec3(1.0f, 1.0f, 1.0f); glm::vec3 diffuseColor = lightColor * glm::vec3(1.0f); @@ -86,7 +86,7 @@ void Controller::run() { shaderProgram.setUniform("u_material.shininess", 32.0f); shaderProgram.unbind(); - scene.push_back(plant); + scene.push_back(backpack); scene.push_back(lightSource); camera->translate(glm::vec3(0.0f, 0.0f, 7.5f)); diff --git a/src/Model.cpp b/src/Model.cpp index 4b13d4f..fac9e91 100644 --- a/src/Model.cpp +++ b/src/Model.cpp @@ -1,11 +1,16 @@ #include "Model.h" #include +#include +#include Model::Model(const char* pathToModel) { - // Todo: check if model isn't already loaded --> will boost startup time drastically - // actually all models should be loaded at startup and only a handle should be given to the entites... + + std::string modelSource = pathToModel; + directory = modelSource.substr(0, modelSource.find_last_of('/')); + loadModel(pathToModel); + } Model::~Model() { @@ -25,122 +30,79 @@ void Model::draw(ShaderProgram *shaderProgram) { } void Model::loadModel(std::string pathToModel) { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(pathToModel, aiProcess_Triangulate | aiProcess_FlipUVs); //aiProcess_OptimizeMeshes ? + + std::ifstream input(pathToModel, std::ios::in | std::ios::binary); - if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { - std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl; - return; + if(!input.is_open()) { + std::cout << "Could not find model file " << pathToModel << std::endl; + return; + } + + uint32_t numTextures; + input.read((char*) &numTextures, sizeof(uint32_t)); + + std::vector textureTypes; + for(unsigned int i = 0; i < numTextures; i++) { + uint32_t currentTextureType; + input.read((char*) ¤tTextureType, sizeof(uint32_t)); + textureTypes.push_back(currentTextureType); } - directory = pathToModel.substr(0, pathToModel.find_last_of('/')); + std::vector textureSources; + for(unsigned int i = 0; i < numTextures; i++) { + std::string currentTextureSource; + for(unsigned int i = 0; i < 128; i++) { + uint8_t currentChar; + input.read((char*) ¤tChar, sizeof(uint8_t)); - processNode(scene->mRootNode, scene); -} - -void Model::processNode(aiNode *node, const aiScene *scene) { - - // Push the node's meshes into the mesh vector - for(uint32_t i = 0; i < node->mNumMeshes; i++) { - aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; - meshes.push_back(processMesh(mesh, scene)); + if(currentChar) + currentTextureSource.push_back(currentChar); + } + textureSources.push_back(currentTextureSource); } - // Process child nodes too - for(uint32_t i = 0; i < node->mNumChildren; i++) { - processNode(node->mChildren[i], scene); + for(unsigned int i = 0; i < numTextures; i++) { + std::string textureSource = directory + '/' + textureSources[i].c_str(); + Texture *newTex = new Texture(textureSource.c_str(), textureTypes[i]); + loadedTextures.push_back(newTex); } -} + // Here starts the first mesh + uint32_t numMeshes; + for(unsigned int j = 0; j < numMeshes; j++) { -Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { - std::vector vertices; - std::vector indices; - std::vector textures; + input.read((char*) &numMeshes, sizeof(uint32_t)); - for(uint32_t i = 0; i < mesh->mNumVertices; i++) { - Vertex vertex; + uint32_t numMeshVertices, numMeshIndices, numMeshTextureIds; - // Position - glm::vec3 vector; - vector.x = mesh->mVertices[i].x; - vector.y = mesh->mVertices[i].y; - vector.z = mesh->mVertices[i].z; - vertex.position = vector; + input.read((char*) &numMeshVertices, sizeof(uint32_t)); + input.read((char*) &numMeshIndices, sizeof(uint32_t)); + input.read((char*) &numMeshTextureIds, sizeof(uint32_t)); - // Normals - vector.x = mesh->mNormals[i].x; - vector.y = mesh->mNormals[i].y; - vector.z = mesh->mNormals[i].z; - vertex.normalVec = vector; + // Here starts the first Vertex data - // Texture UV mapping - if(mesh->mTextureCoords[0]) { - glm::vec2 vec; - vec.x = mesh->mTextureCoords[0][i].x; - vec.y = mesh->mTextureCoords[0][i].y; - vertex.textureCoords = vec; - } else { - vertex.textureCoords = glm::vec2(0.0f, 0.0f); + std::vector meshVertices; + for(unsigned int i = 0; i < numMeshVertices; i++) { + Vertex currentVertex; + input.read((char*) ¤tVertex, sizeof(Vertex)); + meshVertices.push_back(currentVertex); } - vertices.push_back(vertex); - } - - // Indices - for(uint32_t i = 0; i < mesh->mNumFaces; i++) { - aiFace face = mesh->mFaces[i]; - for(uint32_t j = 0; j < face.mNumIndices; j++) { - indices.push_back(face.mIndices[j]); + std::vector meshIndices; + for(unsigned int i = 0; i < numMeshIndices; i++) { + uint32_t currentIndex; + input.read((char*) ¤tIndex, sizeof(uint32_t)); + meshIndices.push_back(currentIndex); } + + std::vector meshTextures; + for(unsigned int i = 0; i < numMeshTextureIds; i++) { + uint32_t currentTextureId; + input.read((char*) ¤tTextureId, sizeof(uint32_t)); + meshTextures.push_back(loadedTextures[currentTextureId]); + } + + Mesh currentMesh(meshVertices, meshIndices, meshTextures); + meshes.push_back(currentMesh); } - - // Material - if(mesh->mMaterialIndex > 0) { - aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; - std::vector diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, texture_diffuse); - textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); - - std::vector specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, texture_specular); - textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); - - std::vector normalMaps = loadMaterialTextures(material, aiTextureType_NORMALS, texture_normal); - textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); - - std::vector heightMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, texture_height); - textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); - } - - return Mesh(vertices, indices, textures); -} - -std::vector Model::loadMaterialTextures(aiMaterial *mat, aiTextureType type, uint8_t textureType) { - - std::vector textures; - for(uint32_t i = 0; i < mat->GetTextureCount(type); i++) { - aiString filename; - mat->GetTexture(type, i, &filename); - - std::string currentPath = directory + '/' + filename.C_Str(); - - bool skip = 0; - for(uint32_t j = 0; j < loadedTextures.size(); j++) { - if(std::strcmp(loadedTextures[j]->getPath().c_str(), currentPath.c_str()) == 0) { - textures.push_back(loadedTextures[j]); - skip = 1; - break; - } - } - - if(!skip) { - Texture *texture = new Texture(currentPath.c_str(), textureType); - loadedTextures.push_back(texture); - - // Add newest texture pointer to the mesh's texture-pointer vector - Texture *new_tex = loadedTextures.back(); - textures.push_back(new_tex); - } - } - - return textures; } diff --git a/src/Model.h b/src/Model.h index 08b96ef..b06a1c2 100644 --- a/src/Model.h +++ b/src/Model.h @@ -1,10 +1,7 @@ #pragma once -#include -#include -#include - #include +#include #include "Mesh.h" @@ -22,17 +19,10 @@ private: void loadModel(std::string pathToModel); - void processNode(aiNode *node, const aiScene *scene); - Mesh processMesh(aiMesh *mesh, const aiScene *scene); - - std::vector loadMaterialTextures(aiMaterial *mat, aiTextureType type, uint8_t textureType); - - std::vector meshes; std::vector loadedTextures; - void textureFromFile(aiMaterial *mat, aiTextureType type); - std::string directory; + }; \ No newline at end of file diff --git a/src/Texture.h b/src/Texture.h index 556abac..b370811 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -2,12 +2,11 @@ #include "ShaderProgram.h" +#include "defines.h" #include #include #include -enum textureType{texture_diffuse, texture_specular, texture_normal, texture_height, TEXTURE_TYPE_NUM_ITEMS}; - class Texture { public: diff --git a/src/defines.h b/src/defines.h index 423cfd3..18c740b 100644 --- a/src/defines.h +++ b/src/defines.h @@ -4,6 +4,9 @@ #define INIT_WINDOW_WIDTH 960 #define INIT_WINDOW_HEIGHT 720 + +enum textureType{texture_diffuse, texture_specular, texture_normal, texture_height, TEXTURE_TYPE_NUM_ITEMS}; + struct Vertex { // Postition glm::vec3 position; diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 9a55e6e..1194973 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -2,4 +2,4 @@ project(obj-converter) add_executable(obj-converter main.cpp) -target_link_libraries(obj-converter assimp) \ No newline at end of file +target_link_libraries(obj-converter assimp glm) diff --git a/tools/fileformat.txt b/tools/fileformat.txt new file mode 100644 index 0000000..0b1f221 --- /dev/null +++ b/tools/fileformat.txt @@ -0,0 +1,29 @@ +NUM_TEXTURES +TYPE_DIFFUSE +TYPE_DIFFUSE +TYPE_SPECULAR +TYPE_NORMAL +TYPE_HEIGHT +./diffuse0.jpg000000000000 +./diffuse1.jpg000000000000 +./specular.jpg000000000000 +./normal.jpg00000000000000 +./height.jpg00000000000000 +NUM_VERTICES +NUM_INDICES +NUM_TEXTUREIDS + + + +NUM_VERTICES +NUM_INDICES +NUM_TEXTUREIDS + + + +NUM_VERTICES +NUM_INDICES +NUM_TEXTUREIDS + + + diff --git a/tools/main.cpp b/tools/main.cpp index d8bf0da..3a7a2a7 100644 --- a/tools/main.cpp +++ b/tools/main.cpp @@ -1,7 +1,240 @@ +/* + + This is the tool to generate model files for the Fall-Fever game. It's used to + reduce loading time of the game, and also to reduce runtime dependencies (libassimp). + + The model files do not support material colors; only textures at this point. + All path strings are 128 elements long. + +*/ + #include #include #include +#include +#include +#include +#include + +#include "../src/defines.h" +#include "primitiveModel.h" + +void processNode(aiNode *node, const aiScene *scene, Model *model); +Mesh processMesh(aiMesh *mesh, const aiScene *scene, Model *model); +std::vector loadMaterialTextures(aiMaterial *mat, aiTextureType type, uint8_t textureType, Mesh *mesh, Model *model); + int main(int argc, char** argv) { - return 1; -} \ No newline at end of file + + if(argc <= 1) { + std::cout << "Usage: " << argv[0] << " " << std::endl; + } + + std::vector modelSources; + + for(int i = 0; i < argc - 1; i++) { + modelSources.push_back(argv[i+1]); + } + + Assimp::Importer importer; + + for(auto it = modelSources.begin(); it != modelSources.end(); it++) { + unsigned int flags = + aiProcess_Triangulate | + aiProcess_FlipUVs | + aiProcess_PreTransformVertices | + aiProcess_GenNormals | + aiProcess_OptimizeMeshes | + aiProcess_OptimizeGraph | + aiProcess_JoinIdenticalVertices | + aiProcess_ImproveCacheLocality | + aiProcess_CalcTangentSpace; + + const aiScene* scene = importer.ReadFile((*it).c_str(), flags); + + if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { + std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl; + return 1; + } + + Model currentModel; + if(((*it).find('/')) < (*it).length()) { + // source includes a / + currentModel.directory = (*it).substr(0, (*it).find_last_of('/')); + } else { + currentModel.directory = "."; + } + + processNode(scene->mRootNode, scene, ¤tModel); + + std::string filenameWithoutExtension = (*it).substr(0, (*it).find_last_of('.')); + std::string outputFilename = filenameWithoutExtension + ".ffo"; + + std::ofstream output(outputFilename, std::ios::out | std::ios::binary); + + uint32_t numTextures = currentModel.textures.size(); + output.write((char*) &numTextures, sizeof(uint32_t)); + + // Write texture types in order + for(auto it1 = currentModel.textures.begin(); it1 != currentModel.textures.end(); it1++) { + uint32_t currentTextureType = (*it1).textureType; + output.write((char*) ¤tTextureType, sizeof(uint32_t)); + } + + // Write texture sources + for(auto it1 = currentModel.textures.begin(); it1 != currentModel.textures.end(); it1++) { + for(unsigned int i = 0; i < 128; i++) { + if(i < (*it1).pathToTexture.size()) { + uint8_t character = (*it1).pathToTexture[i]; + output.write((char*) &character, sizeof(uint8_t)); + } else { + uint8_t character = 0; + output.write((char*) &character, sizeof(uint8_t)); + } + + } + } + + // Write meshes + uint32_t numMeshes = currentModel.meshes.size(); + output.write((char*) &numMeshes, sizeof(uint32_t)); + for(auto it1 = currentModel.meshes.begin(); it1 != currentModel.meshes.end(); it1++) { + uint32_t numVertices = (*it1).vertices.size(); + uint32_t numIndices = (*it1).indices.size(); + uint32_t numTextureIds = (*it1).textureIds.size(); + + output.write((char*) &numVertices, sizeof(uint32_t)); + output.write((char*) &numIndices, sizeof(uint32_t)); + output.write((char*) &numTextureIds, sizeof(uint32_t)); + + Vertex *vertexData = (*it1).vertices.data(); + output.write((char*) vertexData, numVertices * sizeof(Vertex)); + + uint32_t *indexData = (*it1).indices.data(); + output.write((char*) indexData, numIndices * sizeof(uint32_t)); + + uint32_t *textureIdData = (*it1).textureIds.data(); + output.write((char*) textureIdData, numTextureIds * sizeof(uint32_t)); + } + + output.close(); + } + + return 0; +} + +void processNode(aiNode *node, const aiScene *scene, Model* model) { + + // Push the node's meshes into the mesh vector + for(uint32_t i = 0; i < node->mNumMeshes; i++) { + aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; + model->meshes.push_back(processMesh(mesh, scene, model)); + } + + // Process child nodes too + for(uint32_t i = 0; i < node->mNumChildren; i++) { + processNode(node->mChildren[i], scene, model); + } + +} + +Mesh processMesh(aiMesh *mesh, const aiScene *scene, Model *model) { + std::vector vertices; + std::vector indices; + std::vector textures; + Mesh currentMesh; + + for(uint32_t i = 0; i < mesh->mNumVertices; i++) { + Vertex vertex; + + // Position + glm::vec3 vector; + vector.x = mesh->mVertices[i].x; + vector.y = mesh->mVertices[i].y; + vector.z = mesh->mVertices[i].z; + vertex.position = vector; + + // Normals + vector.x = mesh->mNormals[i].x; + vector.y = mesh->mNormals[i].y; + vector.z = mesh->mNormals[i].z; + vertex.normalVec = vector; + + // Texture UV mapping + if(mesh->mTextureCoords[0]) { + glm::vec2 vec; + vec.x = mesh->mTextureCoords[0][i].x; + vec.y = mesh->mTextureCoords[0][i].y; + vertex.textureCoords = vec; + } else { + vertex.textureCoords = glm::vec2(0.0f, 0.0f); + } + + vertices.push_back(vertex); + } + + // Indices + for(uint32_t i = 0; i < mesh->mNumFaces; i++) { + aiFace face = mesh->mFaces[i]; + for(uint32_t j = 0; j < face.mNumIndices; j++) { + indices.push_back(face.mIndices[j]); + } + } + + // Material + if(mesh->mMaterialIndex > 0) { + aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; + + std::vector diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, texture_diffuse, ¤tMesh, model); + textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); + + std::vector specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, texture_specular, ¤tMesh, model); + textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); + + std::vector normalMaps = loadMaterialTextures(material, aiTextureType_NORMALS, texture_normal, ¤tMesh, model); + textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); + + std::vector heightMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, texture_height, ¤tMesh, model); + textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); + } + + currentMesh.vertices = vertices; + currentMesh.indices = indices; + + return currentMesh; +} + +std::vector loadMaterialTextures(aiMaterial *mat, aiTextureType type, uint8_t textureType, Mesh *mesh, Model *model) { + + std::vector textures; + for(uint32_t i = 0; i < mat->GetTextureCount(type); i++) { + aiString filename; + mat->GetTexture(type, i, &filename); + + std::string currentPath = model->directory + '/' + filename.C_Str(); + + bool skip = 0; + for(uint32_t j = 0; j < model->textures.size(); j++) { + if(std::strcmp(model->textures[j].pathToTexture.c_str(), currentPath.c_str()) == 0) { + textures.push_back(model->textures[j]); + skip = 1; + break; + } + } + + if(!skip) { + Texture texture; + texture.pathToTexture = currentPath; + texture.textureType = textureType; + // textureIds start at 0, but vector elements start at 1. + texture.textureId = model->textures.size(); + + model->textures.push_back(texture); + + // Add newest texture id to mesh + mesh->textureIds.push_back(texture.textureId); + } + } + + return textures; +} diff --git a/tools/primitiveModel.h b/tools/primitiveModel.h new file mode 100644 index 0000000..f572e97 --- /dev/null +++ b/tools/primitiveModel.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +struct Texture { + + std::string pathToTexture; + uint32_t textureType; + uint32_t textureId; + +}; + +struct Mesh { + + std::vector vertices; + std::vector indices; + std::vector textureIds; + +}; + +struct Model { + + std::vector textures; + std::vector meshes; + std::string directory; + +};