diff --git a/src/Fbx2Raw.cpp b/src/Fbx2Raw.cpp index d8fafc7..edac809 100644 --- a/src/Fbx2Raw.cpp +++ b/src/Fbx2Raw.cpp @@ -785,7 +785,6 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std: } int polygonVertexIndex = 0; - for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) { FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3); const std::shared_ptr fbxMaterial = materials.GetMaterial(polygonIndex); diff --git a/src/RawModel.cpp b/src/RawModel.cpp index 98eb337..dcd7856 100644 --- a/src/RawModel.cpp +++ b/src/RawModel.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #if defined( __unix__ ) #include @@ -22,6 +23,8 @@ #include "utils/Image_Utils.h" #include "RawModel.h" +extern bool verboseOutput; + bool RawVertex::operator==(const RawVertex &other) const { return (position == other.position) && @@ -258,6 +261,16 @@ int RawModel::AddNode(const long id, const char *name, const long parentId) return (int) nodes.size() - 1; } +void RawModel::Repair() +{ + const auto &brokenNormalVerts = this->CalculateBrokenNormals(); + if (verboseOutput) { + fmt::printf("Repaired %lu empty normals.\n", brokenNormalVerts.size()); + } + +} + + void RawModel::Condense() { // Only keep surfaces that are referenced by one or more triangles. @@ -267,8 +280,8 @@ void RawModel::Condense() surfaces.clear(); for (auto &triangle : triangles) { - const RawSurface &surface = oldSurfaces[triangle.surfaceIndex]; - const int surfaceIndex = AddSurface(surface.name.c_str(), surface.id); + const RawSurface &surface = oldSurfaces[triangle.surfaceIndex]; + const int surfaceIndex = AddSurface(surface.name.c_str(), surface.id); surfaces[surfaceIndex] = surface; triangle.surfaceIndex = surfaceIndex; } @@ -281,8 +294,8 @@ void RawModel::Condense() materials.clear(); for (auto &triangle : triangles) { - const RawMaterial &material = oldMaterials[triangle.materialIndex]; - const int materialIndex = AddMaterial(material); + const RawMaterial &material = oldMaterials[triangle.materialIndex]; + const int materialIndex = AddMaterial(material); materials[materialIndex] = material; triangle.materialIndex = materialIndex; } @@ -535,3 +548,63 @@ int RawModel::GetSurfaceById(const long surfaceId) const } return -1; } + +Vec3f RawModel::getFaceNormal(int verts[3]) const +{ + const float l0 = (vertices[verts[1]].position - vertices[verts[0]].position ).LengthSquared(); + const float l1 = (vertices[verts[2]].position - vertices[verts[1]].position ).LengthSquared(); + const float l2 = (vertices[verts[0]].position - vertices[verts[2]].position ).LengthSquared(); + const int index = ( l0 > l1 ) ? ( l0 > l2 ? 2 : 1 ) : ( l1 > l2 ? 0 : 1 ); + + const Vec3f e0 = vertices[verts[(index + 1) % 3]].position - vertices[verts[index]].position; + const Vec3f e1 = vertices[verts[(index + 2) % 3]].position - vertices[verts[index]].position; + + auto result = Vec3f::CrossProduct(e0, e1); + if (result.LengthSquared() < FLT_MIN) { + return Vec3f { 0.0f }; + } + result.Normalize(); + return result; +} + +std::set RawModel::CalculateNormals() +{ + Vec3f averagePos = Vec3f { 0.0f }; + std::set brokenVerts; + for (int vertIx = 0; vertIx < vertices.size(); vertIx ++) { + averagePos += (vertices[vertIx].position / vertices.size()); + if (vertices[vertIx].normal.LengthSquared() < FLT_MIN) { + vertices[vertIx].normal = Vec3f { 0.0f }; + brokenVerts.emplace(vertIx); + } + } + + for (auto &triangle : triangles) { + bool relevant = false; + for (int vertIx : triangle.verts) { + relevant |= (brokenVerts.count(vertIx) > 0); + } + if (!relevant) { + continue; + } + Vec3f faceNormal = this->getFaceNormal(triangle.verts); + for (int vertIx : triangle.verts) { + if (brokenVerts.count(vertIx) > 0) { + vertices[vertIx].normal += faceNormal; + } + } + } + + for (int vertIx : brokenVerts) { + RawVertex &vertex = vertices[vertIx]; + if (vertex.normal.LengthSquared() < FLT_MIN) { + vertex.normal = vertex.position - averagePos; + if (vertex.normal.LengthSquared() < FLT_MIN) { + vertex.normal = Vec3f { 0.0f, 1.0f, 0.0f }; + continue; + } + } + vertex.normal.Normalize(); + } + return brokenVerts; +} diff --git a/src/RawModel.h b/src/RawModel.h index 1a2c6c4..72411dd 100644 --- a/src/RawModel.h +++ b/src/RawModel.h @@ -12,6 +12,7 @@ #include #include +#include enum RawVertexAttribute { @@ -375,8 +376,12 @@ public: // Remove unused vertices, textures or materials after removing vertex attributes, textures, materials or surfaces. void Condense(); + void Repair(); + void TransformTextures(const std::vector> &transforms); + std::set CalculateBrokenNormals(); + // Get the attributes stored per vertex. int GetVertexAttributes() const { return vertexAttributes; } @@ -428,6 +433,8 @@ public: std::vector &materialModels, const int maxModelVertices, const int keepAttribs, const bool forceDiscrete) const; private: + Vec3f getFaceNormal(int verts[3]) const; + long rootNodeId; int vertexAttributes; std::unordered_map vertexHash; diff --git a/src/main.cpp b/src/main.cpp index ca2bfff..3858fde 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -210,6 +210,7 @@ Copyright (c) 2016-2017 Oculus VR, LLC. raw.TransformTextures(texturesTransforms); } raw.Condense(); + raw.Repair(); std::ofstream outStream; // note: auto-flushes in destructor const auto streamStart = outStream.tellp();