Code to repair normals.

This commit is contained in:
Par Winzell 2018-02-18 14:42:30 -08:00
parent 20b1bd7051
commit b95c50a72f
4 changed files with 85 additions and 5 deletions

View File

@ -785,7 +785,6 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
} }
int polygonVertexIndex = 0; int polygonVertexIndex = 0;
for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) { for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) {
FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3); FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3);
const std::shared_ptr<FbxMaterialInfo> fbxMaterial = materials.GetMaterial(polygonIndex); const std::shared_ptr<FbxMaterialInfo> fbxMaterial = materials.GetMaterial(polygonIndex);

View File

@ -12,6 +12,7 @@
#include <unordered_map> #include <unordered_map>
#include <cmath> #include <cmath>
#include <map> #include <map>
#include <set>
#if defined( __unix__ ) #if defined( __unix__ )
#include <algorithm> #include <algorithm>
@ -22,6 +23,8 @@
#include "utils/Image_Utils.h" #include "utils/Image_Utils.h"
#include "RawModel.h" #include "RawModel.h"
extern bool verboseOutput;
bool RawVertex::operator==(const RawVertex &other) const bool RawVertex::operator==(const RawVertex &other) const
{ {
return (position == other.position) && 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; 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() void RawModel::Condense()
{ {
// Only keep surfaces that are referenced by one or more triangles. // Only keep surfaces that are referenced by one or more triangles.
@ -535,3 +548,63 @@ int RawModel::GetSurfaceById(const long surfaceId) const
} }
return -1; 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<int> RawModel::CalculateNormals()
{
Vec3f averagePos = Vec3f { 0.0f };
std::set<int> 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;
}

View File

@ -12,6 +12,7 @@
#include <unordered_map> #include <unordered_map>
#include <functional> #include <functional>
#include <set>
enum RawVertexAttribute enum RawVertexAttribute
{ {
@ -375,8 +376,12 @@ public:
// Remove unused vertices, textures or materials after removing vertex attributes, textures, materials or surfaces. // Remove unused vertices, textures or materials after removing vertex attributes, textures, materials or surfaces.
void Condense(); void Condense();
void Repair();
void TransformTextures(const std::vector<std::function<Vec2f(Vec2f)>> &transforms); void TransformTextures(const std::vector<std::function<Vec2f(Vec2f)>> &transforms);
std::set<int> CalculateBrokenNormals();
// Get the attributes stored per vertex. // Get the attributes stored per vertex.
int GetVertexAttributes() const { return vertexAttributes; } int GetVertexAttributes() const { return vertexAttributes; }
@ -428,6 +433,8 @@ public:
std::vector<RawModel> &materialModels, const int maxModelVertices, const int keepAttribs, const bool forceDiscrete) const; std::vector<RawModel> &materialModels, const int maxModelVertices, const int keepAttribs, const bool forceDiscrete) const;
private: private:
Vec3f getFaceNormal(int verts[3]) const;
long rootNodeId; long rootNodeId;
int vertexAttributes; int vertexAttributes;
std::unordered_map<RawVertex, int, VertexHasher> vertexHash; std::unordered_map<RawVertex, int, VertexHasher> vertexHash;

View File

@ -210,6 +210,7 @@ Copyright (c) 2016-2017 Oculus VR, LLC.
raw.TransformTextures(texturesTransforms); raw.TransformTextures(texturesTransforms);
} }
raw.Condense(); raw.Condense();
raw.Repair();
std::ofstream outStream; // note: auto-flushes in destructor std::ofstream outStream; // note: auto-flushes in destructor
const auto streamStart = outStream.tellp(); const auto streamStart = outStream.tellp();