/* 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/definitions/models.h" #include "../src/resources/TextureTypes.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) { 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.m_workingPath = (*it).substr(0, (*it).find_last_of('/')); } else { currentModel.m_workingPath = "."; } 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.m_meshes.size(); output.write((char*) &numMeshes, sizeof(uint32_t)); for (auto it1 = currentModel.m_meshes.begin(); it1 != currentModel.m_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->m_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; // Tangents vector.x = mesh->mTangents[i].x; vector.y = mesh->mTangents[i].y; vector.z = mesh->mTangents[i].z; vertex.tangentVec = vector; // Bitangents vector.x = mesh->mBitangents[i].x; vector.y = mesh->mBitangents[i].y; vector.z = mesh->mBitangents[i].z; vertex.bitangentVec = 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, static_cast(TextureType::Diffuse), ¤tMesh, model); textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end()); std::vector specularMaps = loadMaterialTextures( material, aiTextureType_SPECULAR, static_cast(TextureType::Specular), ¤tMesh, model); textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); std::vector normalMaps = loadMaterialTextures( material, aiTextureType_HEIGHT, static_cast(TextureType::Normal), ¤tMesh, model); textures.insert(textures.end(), normalMaps.begin(), normalMaps.end()); // Not entirely sure if aiTextureType_HEIGHT is correct std::vector heightMaps = loadMaterialTextures( material, aiTextureType_HEIGHT, static_cast(TextureType::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->m_workingPath + '/' + 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.m_textureId = model->textures.size(); model->textures.push_back(texture); // Add newest texture id to mesh mesh->textureIds.push_back(texture.m_textureId); } } return textures; }