#include #include "../inc/assimp/Importer.hpp" #include "../inc/assimp/scene.h" #include "../inc/assimp/postprocess.h" #include "../Renderer.h" #include "ObjLoader.h" ObjLoader* ObjLoader::mInstance = NULL; void ObjLoader::Create() { if (mInstance == NULL) mInstance = new ObjLoader(); } void ObjLoader::Destroy() { if (mInstance != NULL) delete mInstance; mInstance = NULL; } ObjLoader* ObjLoader::Instance() { return mInstance; } //************************************ // Load OBJ file with ASSIMP library // Parse to scene, using binary file if possible //************************************ bool ObjLoader::Load(const char* fileName, Scene &scene) { mVertexOffset = 0; mIndexOffset = 0; mTextureOffset = 0; mBinaryFileName = ""; // Try to load binary scene, otherwise parse ASSIMP scene if (!Read(fileName, scene)) { // Load OBJ with ASSIMP Assimp::Importer importer; const aiScene* loadedScene = importer.ReadFile(fileName, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_FlipUVs | aiProcess_GenSmoothNormals); if (!loadedScene) { // If bad allocation, try loading without joining identical vertices const char* t = importer.GetErrorString(); if (strcmp(t, "bad allocation") == 0) loadedScene = importer.ReadFile(fileName, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals); if (!loadedScene) return false; } // Load textures, only uses diffuse texture auto s1 = std::string(fileName); auto path = s1.substr(0, s1.find_last_of("\\/")).append("/"); std::vector textures; std::vector reflectivenesses(loadedScene->mNumMaterials); textures.resize(loadedScene->mNumMaterials, std::string()); for (unsigned m = 0; m < loadedScene->mNumMaterials; ++m) { const aiMaterial* currentMat = loadedScene->mMaterials[m]; aiString dummy; aiString* textureFile = &dummy; aiGetMaterialTexture(currentMat, aiTextureType::aiTextureType_DIFFUSE, 0, textureFile); if (textureFile->data[0] != '\0') { textures[m] = std::string(path).append(textureFile->C_Str()); //aiGetMaterialInteger(currentMat, aiMaterialProperty::) /*textures[m] = Renderer::Load2DTexture(); if (mTextureOffset < 0) mTextureOffset = textures[m];*/ } float reflectivity = 1.f; aiGetMaterialFloat(currentMat, AI_MATKEY_OPACITY, &reflectivity); reflectivity = 1.f - reflectivity; reflectivenesses[m] = reflectivity; //printf("%f\n", reflectivity); } for (unsigned m = 0; m < loadedScene->mNumMeshes; ++m) { const aiMesh* currentMesh = loadedScene->mMeshes[m]; size_t currentVertex = scene.vertices.size(); size_t currentIndex = scene.indices.size(); // Fill vertex positions for (unsigned i = 0; i < currentMesh->mNumVertices; i++) { aiVector3D pos = currentMesh->mVertices[i]; scene.vertices.push_back(glm::vec3(pos.x, pos.y, pos.z)); } // Fill vertices texture coordinates // Assume 1 set of UV coordinates, AssImp supports 8 sets if (currentMesh->HasTextureCoords(0)) { // If this mesh has uv coordinates, then the whole scene should have them. scene.uvs.resize(scene.vertices.size() - currentMesh->mNumVertices); // Read the uvs for (unsigned i = 0; i < currentMesh->mNumVertices; i++) { aiVector3D UVW = currentMesh->mTextureCoords[0][i]; scene.uvs.push_back(glm::vec2(UVW.x, UVW.y)); } } // Fill empty spots if (scene.uvs.size() != 0) scene.uvs.resize(scene.vertices.size(), glm::vec2(0)); // Fill vertices normals if (currentMesh->HasNormals()) { for (unsigned i = 0; i < currentMesh->mNumVertices; i++) { aiVector3D n = currentMesh->mNormals[i]; scene.normals.push_back(glm::vec3(n.x, n.y, n.z)); } } // Fill empty spots scene.normals.resize(scene.vertices.size(), glm::vec3(0)); // Fill vertex colors (if they are set) if (currentMesh->HasVertexColors(0)) { // If this mesh has colored vertices, then the whole scene should have them. scene.colors.resize(scene.vertices.size() - currentMesh->mNumVertices); // Read the vertex coordinates for (unsigned i = 0; i < currentMesh->mNumVertices; i++) { aiColor4D col = currentMesh->mColors[0][i]; scene.colors.push_back(glm::vec3(col.r, col.g, col.b)); } } // If the scene has colors, fill them with the color white for the whole scene if (scene.colors.size() != 0) scene.colors.resize(scene.vertices.size(), glm::vec3(1)); // Fill triangle indices for (unsigned i = 0; i < currentMesh->mNumFaces; i++) { scene.indices.push_back((unsigned)(currentVertex + currentMesh->mFaces[i].mIndices[0])); scene.indices.push_back((unsigned)(currentVertex + currentMesh->mFaces[i].mIndices[1])); scene.indices.push_back((unsigned)(currentVertex + currentMesh->mFaces[i].mIndices[2])); } // Save mesh Mesh mesh; mesh.offset = (unsigned)currentIndex; mesh.size = (unsigned)(scene.indices.size() - currentIndex); mesh.texture = textures[currentMesh->mMaterialIndex]; mesh.hasUVs = currentMesh->HasTextureCoords(0); mesh.hasVertexColors = currentMesh->HasVertexColors(0); mesh.reflectivity = reflectivenesses[currentMesh->mMaterialIndex]; scene.meshes.push_back(mesh); if (mVertexOffset <= 0) mVertexOffset = currentVertex; if (mIndexOffset <= 0) mIndexOffset = currentIndex; } if (!Write(fileName, scene)) return false; textures.clear(); } // The scene pointer is deleted automatically by importer return true; } ObjLoader::ObjLoader() { mVertexOffset = 0; mIndexOffset = 0; mTextureOffset = 0; mBinaryFileName = ""; } ObjLoader::~ObjLoader() { } //************************************ // Get binary file name //************************************ void ObjLoader::GetBinaryFileName(const char* fileName) { if (mBinaryFileName.empty()) { mBinaryFileName = fileName; mBinaryFileName.append(".bin"); } } bool ReadFile(std::ifstream& sceneInFile, Scene &scene) { try { int readVertexOffset, readIndexOffset, readTextureOffset; sceneInFile.read((char*)&readVertexOffset, sizeof(int)); sceneInFile.read((char*)&readIndexOffset, sizeof(int)); sceneInFile.read((char*)&readTextureOffset, sizeof(int)); unsigned verticesSize, uvsSize, normalsSize, colorsSize, indicesSize, meshesSize; sceneInFile.read((char*)&verticesSize, sizeof(unsigned)); sceneInFile.read((char*)&uvsSize, sizeof(unsigned)); sceneInFile.read((char*)&normalsSize, sizeof(unsigned)); sceneInFile.read((char*)&colorsSize, sizeof(unsigned)); sceneInFile.read((char*)&indicesSize, sizeof(unsigned)); sceneInFile.read((char*)&meshesSize, sizeof(unsigned)); // Check if the sizes are valid. If they are not, we're probably dealing with an old file if (meshesSize > verticesSize || (colorsSize != 0 && colorsSize != verticesSize) || normalsSize != verticesSize || (uvsSize != 0 && uvsSize != verticesSize)) return false; if (!scene.uvs.empty() || uvsSize != 0) scene.uvs.resize(scene.vertices.size() + verticesSize, glm::vec2(0)); scene.normals.resize(scene.normals.size() + normalsSize); if (!scene.colors.empty() || colorsSize != 0) scene.colors.resize(scene.vertices.size() + verticesSize, glm::vec3(1)); scene.indices.resize(scene.indices.size() + indicesSize); scene.meshes.resize(scene.meshes.size() + meshesSize); scene.vertices.resize(scene.vertices.size() + verticesSize); if (verticesSize != 0) sceneInFile.read((char*)&scene.vertices[scene.vertices.size() - verticesSize], sizeof(glm::vec3) * verticesSize); if (sceneInFile.eof()) return false; if (uvsSize != 0) sceneInFile.read((char*)&scene.uvs[scene.uvs.size() - uvsSize], sizeof(glm::vec2) * uvsSize); if (sceneInFile.eof()) return false; if (normalsSize != 0) sceneInFile.read((char*)&scene.normals[scene.normals.size() - normalsSize], sizeof(glm::vec3) * normalsSize); if (sceneInFile.eof()) return false; if (colorsSize != 0) sceneInFile.read((char*)&scene.colors[scene.colors.size() - colorsSize], sizeof(glm::vec3) * colorsSize); if (sceneInFile.eof()) return false; if (indicesSize != 0) sceneInFile.read((char*)&scene.indices[scene.indices.size() - indicesSize], sizeof(unsigned) * indicesSize); if (sceneInFile.eof()) return false; for (unsigned i = 0; i < meshesSize; i++) { if (sceneInFile.eof()) return false; Mesh mesh; sceneInFile.read((char*)&mesh.offset, sizeof(unsigned)); sceneInFile.read((char*)&mesh.size, sizeof(unsigned)); unsigned int textureSize; sceneInFile.read((char*)&textureSize, sizeof(unsigned int)); char* texture = new char[textureSize + 1]; sceneInFile.read((char*)&texture[0], textureSize); texture[textureSize] = '\0'; mesh.texture = std::string(texture); if (sceneInFile.eof()) return false; sceneInFile.read((char*)&mesh.hasUVs, sizeof(bool)); if (sceneInFile.eof()) return false; sceneInFile.read((char*)&mesh.hasVertexColors, sizeof(bool)); sceneInFile.read((char*)&mesh.reflectivity, sizeof(float)); delete[] texture; scene.meshes[i] = mesh; } return true; } catch (std::exception& ex) { printf("An exception occured while reading the mesh cache file: %s", ex.what()); return false; } } //************************************ // Read scene from binary file //************************************ bool ObjLoader::Read(const char* fileName, Scene &scene) { GetBinaryFileName(fileName); std::ifstream sceneInFile(mBinaryFileName, std::ios::binary); if (sceneInFile.good()) { mVertexOffset = (unsigned)scene.vertices.size(); mIndexOffset = (unsigned)scene.indices.size(); unsigned verticesSize = (unsigned)scene.vertices.size(); unsigned uvsSize = (unsigned)scene.uvs.size(); unsigned normalsSize = (unsigned)scene.normals.size(); unsigned colorsSize = (unsigned)scene.colors.size(); unsigned indicesSize = (unsigned)scene.indices.size(); unsigned meshesSize = (unsigned)scene.meshes.size(); bool readSucces = ReadFile(sceneInFile, scene); if (!readSucces) { // Revert the scene if reading failed scene.vertices.resize(verticesSize); scene.vertices.shrink_to_fit(); scene.uvs.resize(uvsSize); scene.uvs.shrink_to_fit(); scene.normals.resize(normalsSize); scene.normals.shrink_to_fit(); scene.colors.resize(colorsSize); scene.colors.shrink_to_fit(); scene.indices.resize(indicesSize); scene.indices.shrink_to_fit(); scene.meshes.resize(meshesSize); scene.meshes.shrink_to_fit(); } sceneInFile.close(); return readSucces; } return false; } //************************************ // Write scene to binary file //************************************ bool ObjLoader::Write(const char* fileName, Scene &scene) { GetBinaryFileName(fileName); std::ofstream sceneOutFile(mBinaryFileName, std::ios::binary); unsigned verticesSize = (unsigned)scene.vertices.size(); unsigned uvsSize = (unsigned)scene.uvs.size(); unsigned normalsSize = (unsigned)scene.normals.size(); unsigned colorsSize = (unsigned)scene.colors.size(); unsigned indicesSize = (unsigned)scene.indices.size(); unsigned meshesSize = (unsigned)scene.meshes.size(); sceneOutFile.write((char*)&mVertexOffset, sizeof(int)); sceneOutFile.write((char*)&mIndexOffset, sizeof(int)); sceneOutFile.write((char*)&mTextureOffset, sizeof(int)); sceneOutFile.write((char*)&verticesSize, sizeof(unsigned)); sceneOutFile.write((char*)&uvsSize, sizeof(unsigned)); sceneOutFile.write((char*)&normalsSize, sizeof(unsigned)); sceneOutFile.write((char*)&colorsSize, sizeof(unsigned)); sceneOutFile.write((char*)&indicesSize, sizeof(unsigned)); sceneOutFile.write((char*)&meshesSize, sizeof(unsigned)); if (verticesSize > 0) sceneOutFile.write((char*)&scene.vertices[0], sizeof(glm::vec3) * verticesSize); if (uvsSize > 0) sceneOutFile.write((char*)&scene.uvs[0], sizeof(glm::vec2) * uvsSize); if (normalsSize > 0) sceneOutFile.write((char*)&scene.normals[0], sizeof(glm::vec3) * normalsSize); if (colorsSize > 0) sceneOutFile.write((char*)&scene.colors[0], sizeof(glm::vec3) * colorsSize); if (indicesSize > 0) sceneOutFile.write((char*)&scene.indices[0], sizeof(unsigned) * indicesSize); unsigned int textureSize; for (Mesh mesh : scene.meshes) { sceneOutFile.write((char*)&mesh.offset, sizeof(unsigned)); sceneOutFile.write((char*)&mesh.size, sizeof(unsigned)); textureSize = (unsigned)mesh.texture.size(); sceneOutFile.write((char*)&textureSize, sizeof(unsigned int)); sceneOutFile.write(mesh.texture.c_str(), textureSize); sceneOutFile.write((char*)&mesh.hasUVs, sizeof(bool)); sceneOutFile.write((char*)&mesh.hasVertexColors, sizeof(bool)); sceneOutFile.write((char*)&mesh.reflectivity, sizeof(float)); } sceneOutFile.close(); return true; }