#include "Model.h" #include #include #include "glm/glm.hpp" #include "glm/gtc/matrix_transform.hpp" #include "GL/glew.h" #include "Application.h" #include "Texture.h" #include "Shader.h" #include "Utility.h" namespace nf { Model::Model(AModel* model) : m_base(model->isBaseAsset) { if (model->neededTextures.size() > 32) Error("Model exceedes 32 texture limit!"); std::string obj = model->data; unsigned int startMtlPos = obj.find("newmtl"); if (startMtlPos == std::string::npos) Error("No materials found in model!"); std::string mtl = obj.substr(startMtlPos); struct TempMaterial { std::vector outVB; std::vector unindexedVB; std::vector vbIndices; std::vector outTC; std::vector unindexedTC; std::vector tcIndices; std::vector outVN; std::vector unindexedVN; std::vector vnIndices; std::vector unindexedTan; std::vector outTan; std::vector outIB; unsigned int ibCount = 0; std::string diffuseTextureName; Vec3 diffuseColor; std::string specularTextureName; std::string normalTextureName; float shininess = 1.0f; }; std::unordered_map mats; std::string currMat; while (true) { char line[500]; int result = sscanf_s(mtl.c_str(), "\n%s", line, (unsigned)_countof(line)); if (result == EOF) break; if (std::strcmp(line, "newmtl") == 0) { char matName[100]; sscanf_s(mtl.c_str(), "\nnewmtl %s\n", matName, (unsigned)_countof(matName)); currMat = matName; mats[currMat] = new TempMaterial; } else if (std::strcmp(line, "Kd") == 0) { float r = 0.0, g = 0.0, b = 0.0; sscanf_s(mtl.c_str(), "\nKd %f %f %f\n", &r, &g, &b); mats[currMat]->diffuseColor = Vec3(r, g, b); } else if (std::strcmp(line, "map_Kd") == 0) { char texName[100]; sscanf_s(mtl.c_str(), "\nmap_Kd %s\n", texName, (unsigned)_countof(texName)); mats[currMat]->diffuseTextureName = (std::string)texName; } else if (std::strcmp(line, "map_Ks") == 0) { char texName[100]; sscanf_s(mtl.c_str(), "\nmap_Ks %s\n", texName, (unsigned)_countof(texName)); mats[currMat]->specularTextureName = (std::string)texName; } else if (std::strcmp(line, "map_Bump") == 0) { char texName[100]; sscanf_s(mtl.c_str(), "\nmap_Bump %s\n", texName, (unsigned)_countof(texName)); mats[currMat]->normalTextureName = (std::string)texName; } else if (std::strcmp(line, "Ns") == 0) { float s = 0.0; sscanf_s(mtl.c_str(), "\nNs %f\n", &s); mats[currMat]->shininess = s; } unsigned int pos = mtl.find(line) + strlen(line); mtl = mtl.substr(pos); } std::string file = obj.substr(0, startMtlPos); std::vector vbRaw, tcRaw, vnRaw; std::string usingMat; bool tcPresent = false, vnPresent = false; while (true) { char line[500]; int remove = 0; int result = sscanf_s(file.c_str(), "\n%s", line, (unsigned)_countof(line)); if (result == EOF) break; if (std::strcmp(line, "v") == 0) { float x = 0.0, y = 0.0, z = 0.0; sscanf_s(file.c_str(), "\nv %f %f %f\n", &x, &y, &z); remove = 28; vbRaw.push_back(x); vbRaw.push_back(y); vbRaw.push_back(z); } else if (std::strcmp(line, "vt") == 0) { tcPresent = true; float u = 0.0, v = 0.0; sscanf_s(file.c_str(), "\nvt %f %f\n", &u, &v); remove = 18; tcRaw.push_back(u); tcRaw.push_back(v); } else if (std::strcmp(line, "vn") == 0) { vnPresent = true; float x = 0.0, y = 0.0, z = 0.0; sscanf_s(file.c_str(), "\nvn %f %f %f\n", &x, &y, &z); remove = 20; vnRaw.push_back(x); vnRaw.push_back(y); vnRaw.push_back(z); } else if (std::strcmp(line, "usemtl") == 0) { char matName[100]; sscanf_s(file.c_str(), "\nusemtl %s\n", matName, (unsigned)_countof(matName)); usingMat = matName; remove = 5; } else if (std::strcmp(line, "f") == 0) { unsigned int vertexIndex[3], uvIndex[3], vnIndex[3]; sscanf_s(file.c_str(), "\nf %d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &vnIndex[0], &vertexIndex[1], &uvIndex[1], &vnIndex[1], &vertexIndex[2], &uvIndex[2], &vnIndex[2]); remove = 15; mats[usingMat]->vbIndices.push_back(vertexIndex[0]); mats[usingMat]->vbIndices.push_back(vertexIndex[1]); mats[usingMat]->vbIndices.push_back(vertexIndex[2]); mats[usingMat]->tcIndices.push_back(uvIndex[0]); mats[usingMat]->tcIndices.push_back(uvIndex[1]); mats[usingMat]->tcIndices.push_back(uvIndex[2]); mats[usingMat]->vnIndices.push_back(vnIndex[0]); mats[usingMat]->vnIndices.push_back(vnIndex[1]); mats[usingMat]->vnIndices.push_back(vnIndex[2]); } unsigned int pos = file.find(line) + strlen(line) + remove; file = file.substr(pos); } if (!tcPresent) Error("No texture coordinates found in model!"); if (!vnPresent) Error("No normals found in model!"); for (auto& m : mats) { std::string curr = m.first; for (unsigned int i = 0; i < mats[curr]->vbIndices.size(); i++) { unsigned int vertexIndex = mats[curr]->vbIndices[i]; unsigned int uvIndex = mats[curr]->tcIndices[i]; unsigned int vnIndex = mats[curr]->vnIndices[i]; float vertexX = vbRaw[(vertexIndex - 1) * 3]; float vertexY = vbRaw[(vertexIndex - 1) * 3 + 1]; float vertexZ = vbRaw[(vertexIndex - 1) * 3 + 2]; float vertexU = tcRaw[(uvIndex - 1) * 2]; float vertexV = tcRaw[(uvIndex - 1) * 2 + 1]; float vnX = vnRaw[(vnIndex - 1) * 3]; float vnY = vnRaw[(vnIndex - 1) * 3 + 1]; float vnZ = vnRaw[(vnIndex - 1) * 3 + 2]; mats[curr]->unindexedVB.push_back(vertexX); mats[curr]->unindexedVB.push_back(vertexY); mats[curr]->unindexedVB.push_back(vertexZ); mats[curr]->unindexedTC.push_back(vertexU); mats[curr]->unindexedTC.push_back(vertexV); mats[curr]->unindexedVN.push_back(vnX); mats[curr]->unindexedVN.push_back(vnY); mats[curr]->unindexedVN.push_back(vnZ); } for (unsigned int i = 0; i * 9 < mats[curr]->unindexedVB.size(); i++) { glm::vec3 pos1(mats[curr]->unindexedVB[i * 9], mats[curr]->unindexedVB[i * 9 + 1], mats[curr]->unindexedVB[i * 9 + 2]); glm::vec2 uv1(mats[curr]->unindexedTC[i * 6], mats[curr]->unindexedTC[i * 6 + 1]); glm::vec3 pos2(mats[curr]->unindexedVB[i * 9 + 3], mats[curr]->unindexedVB[i * 9 + 4], mats[curr]->unindexedVB[i * 9 + 5]); glm::vec2 uv2(mats[curr]->unindexedTC[i * 6 + 2], mats[curr]->unindexedTC[i * 6 + 3]); glm::vec3 pos3(mats[curr]->unindexedVB[i * 9 + 6], mats[curr]->unindexedVB[i * 9 + 7], mats[curr]->unindexedVB[i * 9 + 8]); glm::vec2 uv3(mats[curr]->unindexedTC[i * 6 + 4], mats[curr]->unindexedTC[i * 6 + 5]); glm::vec3 edge1 = pos2 - pos1; glm::vec3 edge2 = pos3 - pos1; glm::vec2 delta1 = uv2 - uv1; glm::vec2 delta2 = uv3 - uv1; float f = 1.0f / (delta1.x * delta2.y - delta2.x * delta1.y); float x = f * (delta2.y * edge1.x - delta1.y * edge2.x); float y = f * (delta2.y * edge1.y - delta1.y * edge2.y); float z = f * (delta2.y * edge1.z - delta1.y * edge2.z); mats[curr]->unindexedTan.push_back(x); mats[curr]->unindexedTan.push_back(y); mats[curr]->unindexedTan.push_back(z); mats[curr]->unindexedTan.push_back(x); mats[curr]->unindexedTan.push_back(y); mats[curr]->unindexedTan.push_back(z); mats[curr]->unindexedTan.push_back(x); mats[curr]->unindexedTan.push_back(y); mats[curr]->unindexedTan.push_back(z); } } struct Vertex { float x; float y; float z; float u; float v; float vnX; float vnY; float vnZ; bool operator<(const Vertex other) const { return std::memcmp((void*)this, (void*)&other, sizeof(Vertex)) > 0; } }; std::vector vboPositions; std::vector vboTexCoords; std::vector vboNormals; std::vector vboTangents; std::vector vboMaterialIndices; std::vector vboIndices; int matCount = 0; for (auto& m : mats) { std::string curr = m.first; std::map vertexMap; for (unsigned int i = 0; i * 3 < mats[curr]->unindexedVB.size(); i++) { Vertex currVertex = { mats[curr]->unindexedVB[(i * 3)], mats[curr]->unindexedVB[(i * 3) + 1], mats[curr]->unindexedVB[(i * 3) + 2], mats[curr]->unindexedTC[(i * 2)], mats[curr]->unindexedTC[(i * 2) + 1], mats[curr]->unindexedVN[(i * 3)], mats[curr]->unindexedVN[(i * 3) + 1], mats[curr]->unindexedVN[(i * 3) + 2] }; bool found = false; found = vertexMap.find(currVertex) != vertexMap.end(); if (found) { unsigned int index = vertexMap[currVertex]; mats[curr]->outIB.push_back(index); mats[curr]->ibCount++; } else { mats[curr]->outVB.push_back(currVertex.x); mats[curr]->outVB.push_back(currVertex.y); mats[curr]->outVB.push_back(currVertex.z); mats[curr]->outTC.push_back(currVertex.u); mats[curr]->outTC.push_back(currVertex.v); mats[curr]->outVN.push_back(currVertex.vnX); mats[curr]->outVN.push_back(currVertex.vnY); mats[curr]->outVN.push_back(currVertex.vnZ); mats[curr]->outTan.push_back(mats[curr]->unindexedTan[(i * 3)]); mats[curr]->outTan.push_back(mats[curr]->unindexedTan[(i * 3 + 1)]); mats[curr]->outTan.push_back(mats[curr]->unindexedTan[(i * 3 + 2)]); unsigned int index = (mats[curr]->outVB.size() / 3) - 1; mats[curr]->outIB.push_back(index); vertexMap[currVertex] = index; mats[curr]->ibCount++; } } TempMaterial& curr2 = *m.second; ATexture* diffA = nullptr; Texture* diff = nullptr; if (curr2.diffuseTextureName.size()) { diffA = model->neededTextures[curr2.diffuseTextureName]; diff = new Texture(diffA); } ATexture* specA = nullptr; Texture* spec = nullptr; if (curr2.specularTextureName.size()) { specA = model->neededTextures[curr2.specularTextureName]; spec = new Texture(specA, true); } ATexture* normA = nullptr; Texture* norm = nullptr; if (curr2.normalTextureName.size()) { normA = model->neededTextures[curr2.normalTextureName]; norm = new Texture(normA, true); } m_materials.push_back(std::make_tuple(diff, spec, norm, (float)curr2.diffuseColor.x, (float)curr2.diffuseColor.y, (float)curr2.diffuseColor.z, curr2.shininess)); unsigned int offset = vboPositions.size() / 3; vboPositions.insert(vboPositions.end(), curr2.outVB.begin(), curr2.outVB.end()); vboTexCoords.insert(vboTexCoords.end(), curr2.outTC.begin(), curr2.outTC.end()); vboNormals.insert(vboNormals.end(), curr2.outVN.begin(), curr2.outVN.end()); vboTangents.insert(vboTangents.end(), curr2.outTan.begin(), curr2.outTan.end()); vboMaterialIndices.insert(vboMaterialIndices.end(), curr2.outVB.size() / 3, matCount); if (offset) std::for_each(curr2.outIB.begin(), curr2.outIB.end(), [offset](unsigned int& out) { out += offset; }); vboIndices.insert(vboIndices.end(), curr2.outIB.begin(), curr2.outIB.end()); delete m.second; matCount++; } if (m_materials.size() > 32) Error("Model exceedes 32 material limit!"); m_vao = new VertexArray; m_vao->addBuffer(&vboPositions[0], vboPositions.size() * sizeof(float)); m_vao->pushFloat(3); m_vao->finishBufferLayout(); m_vao->addBuffer(&vboTexCoords[0], vboTexCoords.size() * sizeof(float)); m_vao->pushFloat(2); m_vao->finishBufferLayout(); m_vao->addBuffer(&vboNormals[0], vboNormals.size() * sizeof(float)); m_vao->pushFloat(3); m_vao->finishBufferLayout(); m_vao->addBuffer(&vboTangents[0], vboTangents.size() * sizeof(float)); m_vao->pushFloat(3); m_vao->finishBufferLayout(); m_vao->addBuffer(&vboMaterialIndices[0], vboMaterialIndices.size() * sizeof(int)); m_vao->pushInt(1); m_vao->finishBufferLayout(); m_ib = new IndexBuffer(&vboIndices[0], vboIndices.size()); } void Model::render(Shader* shader, bool onlyDepth) { m_vao->bind(); m_ib->bind(); if (!onlyDepth) bindMaterials(shader); glDrawElements(GL_TRIANGLES, m_ib->getCount(), GL_UNSIGNED_INT, nullptr); } void Model::bindMaterials(Shader* shader) { int texSlot = 0; for (unsigned int i = 0; i < m_materials.size(); i++) { std::string currMatSuffix = std::to_string(i) + (std::string)"]"; Texture* diff; if ((diff = std::get<0>(m_materials[i])) != nullptr) { shader->setUniform(m_hasDiffString + currMatSuffix, true); diff->bind(texSlot); shader->setUniform(m_diffString + currMatSuffix, texSlot); texSlot++; } else { shader->setUniform(m_hasDiffString + currMatSuffix, false); glm::vec3 color(std::get<3>(m_materials[i]), std::get<4>(m_materials[i]), std::get<5>(m_materials[i])); shader->setUniform(m_diffColorString + currMatSuffix, color); } Texture* spec; if ((spec = std::get<1>(m_materials[i])) != nullptr) { shader->setUniform(m_hasSpecString + currMatSuffix, true); spec->bind(texSlot); shader->setUniform(m_specString + currMatSuffix, texSlot); texSlot++; } else shader->setUniform(m_hasSpecString + currMatSuffix, false); Texture* norm; if ((norm = std::get<2>(m_materials[i])) != nullptr) { shader->setUniform(m_hasNormString + currMatSuffix, true); norm->bind(texSlot); shader->setUniform(m_normString + currMatSuffix, texSlot); texSlot++; } else shader->setUniform(m_hasNormString + currMatSuffix, false); shader->setUniform(m_specPowerString + currMatSuffix, std::get<6>(m_materials[i])); } } bool Model::isBaseAsset() { return m_base; } Model::~Model() { for (unsigned int i = 0; i < m_materials.size(); i++) { Texture* curr; if ((curr = std::get<0>(m_materials[i])) != nullptr) Application::getApp()->getCurrentState()->m_texturesToDelete.insert(std::get<0>(m_materials[i])); if ((curr = std::get<1>(m_materials[i])) != nullptr) Application::getApp()->getCurrentState()->m_texturesToDelete.insert(std::get<1>(m_materials[i])); if ((curr = std::get<2>(m_materials[i])) != nullptr) Application::getApp()->getCurrentState()->m_texturesToDelete.insert(std::get<2>(m_materials[i])); } } }