bgfx/3rdparty/openctm/tools/mesh.cpp
2012-10-07 20:41:18 -07:00

214 lines
7.1 KiB
C++

//-----------------------------------------------------------------------------
// Product: OpenCTM tools
// File: mesh.cpp
// Description: Implementation of the 3D triangle mesh class.
//-----------------------------------------------------------------------------
// Copyright (c) 2009-2010 Marcus Geelnard
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//-----------------------------------------------------------------------------
#include <stdexcept>
#include <openctm.h>
#include <cmath>
#include "mesh.h"
#include "convoptions.h"
using namespace std;
/// Compute the cross product of two vectors
Vector3 Cross(Vector3 &v1, Vector3 &v2)
{
return Vector3(
v1.y * v2.z - v1.z * v2.y,
v1.z * v2.x - v1.x * v2.z,
v1.x * v2.y - v1.y * v2.x
);
}
/// Normalize a vector
Vector3 Normalize(Vector3 v)
{
float len = sqrtf(v.x * v.x + v.y * v.y + v.z * v.z);
if(len > 1e-20f)
len = 1.0f / len;
else
len = 1.0f;
return Vector3(v.x * len, v.y * len, v.z * len);
}
/// Clear the mesh
void Mesh::Clear()
{
mComment = string("");
mTexFileName = string("");
mIndices.clear();
mVertices.clear();
mNormals.clear();
mColors.clear();
mTexCoords.clear();
mOriginalNormals = true;
}
/// Automatic detection of the optimal normal calculation method
Mesh::NormalCalcAlgo Mesh::DetectNormalCalculationMethod()
{
unsigned int triCount = mIndices.size() / 3;
unsigned int vertexCount = mVertices.size();
// Calculate the mean edge length
double meanEdgeLen = 0;
for(unsigned int i = 0; i < triCount; ++ i)
{
meanEdgeLen += (mVertices[mIndices[i * 3 + 1]] - mVertices[mIndices[i * 3]]).Abs();
meanEdgeLen += (mVertices[mIndices[i * 3 + 2]] - mVertices[mIndices[i * 3 + 1]]).Abs();
meanEdgeLen += (mVertices[mIndices[i * 3]] - mVertices[mIndices[i * 3 + 2]]).Abs();
}
if(triCount > 0)
meanEdgeLen = meanEdgeLen / (3 * triCount);
// Calculate the standard deviation of the edge length
double stdDevEdgeLen = 0;
for(unsigned int i = 0; i < triCount; ++ i)
{
double len = (mVertices[mIndices[i * 3 + 1]] - mVertices[mIndices[i * 3]]).Abs();
stdDevEdgeLen += (len - meanEdgeLen) * (len - meanEdgeLen);
len = (mVertices[mIndices[i * 3 + 2]] - mVertices[mIndices[i * 3 + 1]]).Abs();
stdDevEdgeLen += (len - meanEdgeLen) * (len - meanEdgeLen);
len = (mVertices[mIndices[i * 3]] - mVertices[mIndices[i * 3 + 2]]).Abs();
stdDevEdgeLen += (len - meanEdgeLen) * (len - meanEdgeLen);
}
if(triCount > 0)
stdDevEdgeLen = sqrt(stdDevEdgeLen / (3 * triCount));
// Calculate the number of triangles that connect to each vertex
vector<int> connectCount;
connectCount.resize(vertexCount);
for(unsigned int i = 0; i < vertexCount; ++ i)
connectCount[i] = 0;
for(unsigned int i = 0; i < mIndices.size(); ++ i)
{
unsigned int idx = mIndices[i];
if(idx < vertexCount)
++ connectCount[idx];
}
// First analysis: how much variation is there in the triangle edge lengths?
double edgeVariation = 0.0;
if(meanEdgeLen > 0.0)
edgeVariation = stdDevEdgeLen / meanEdgeLen;
// Calculate the mean number of triangle connections
double meanConnectCount = 0;
for(unsigned int i = 0; i < vertexCount; ++ i)
meanConnectCount += connectCount[i];
if(vertexCount > 0)
meanConnectCount = meanConnectCount / vertexCount;
// Calculate the standard deviation of the number of triangle connections
double stdDevConnectCount = 0;
for(unsigned int i = 0; i < vertexCount; ++ i)
stdDevConnectCount += (connectCount[i] - meanConnectCount) * (connectCount[i] - meanConnectCount);
if(vertexCount > 0)
stdDevConnectCount = sqrt(stdDevConnectCount / vertexCount);
// Second analysis: how much variation is there in the number of connections
// per vertex?
double connectVariation = 0.0;
if(meanConnectCount > 0.0)
connectVariation = stdDevConnectCount / meanConnectCount;
// Normalize the different measures to their respective threshold values
edgeVariation /= 0.7;
connectVariation /= 0.3;
// Is this a CAD-like object or a organic-like object?
NormalCalcAlgo algo = ncaOrganic;
if(edgeVariation * connectVariation > 1.0)
algo = ncaCAD;
return algo;
}
/// Calculate smooth per-vertex normals
void Mesh::CalculateNormals(NormalCalcAlgo aAlgo)
{
// Determine which normal calculation algorithm to use
NormalCalcAlgo algo;
if(aAlgo == ncaAuto)
algo = DetectNormalCalculationMethod();
else
algo = aAlgo;
// The original normals are no longer preserved
mOriginalNormals = false;
// Clear the smooth normals
mNormals.resize(mVertices.size());
for(unsigned int i = 0; i < mNormals.size(); ++ i)
mNormals[i] = Vector3(0.0f, 0.0f, 0.0f);
// Calculate sum of the flat normals of the neighbouring triangles
unsigned int triCount = mIndices.size() / 3;
for(unsigned int i = 0; i < triCount; ++ i)
{
// Calculate the weighted flat normal for this triangle
Vector3 v1 = mVertices[mIndices[i * 3 + 1]] - mVertices[mIndices[i * 3]];
Vector3 v2 = mVertices[mIndices[i * 3 + 2]] - mVertices[mIndices[i * 3]];
Vector3 flatNormal = Cross(v1, v2);
if(algo == ncaOrganic)
flatNormal = Normalize(flatNormal);
// ...and add it to all three triangle vertices' smooth normals
for(unsigned int j = 0; j < 3; ++ j)
mNormals[mIndices[i * 3 + j]] += flatNormal;
}
// Normalize all the smooth normals
for(unsigned int i = 0; i < mNormals.size(); ++ i)
mNormals[i] = Normalize(mNormals[i]);
}
/// Calculate the bounding box for the mesh
void Mesh::BoundingBox(Vector3 &aMin, Vector3 &aMax)
{
if(mVertices.size() < 1)
aMin = aMax = Vector3(0.0f, 0.0f, 0.0f);
else
aMin = aMax = mVertices[0];
for(unsigned int i = 1; i < mVertices.size(); ++ i)
{
if(mVertices[i].x < aMin.x)
aMin.x = mVertices[i].x;
else if(mVertices[i].x > aMax.x)
aMax.x = mVertices[i].x;
if(mVertices[i].y < aMin.y)
aMin.y = mVertices[i].y;
else if(mVertices[i].y > aMax.y)
aMax.y = mVertices[i].y;
if(mVertices[i].z < aMin.z)
aMin.z = mVertices[i].z;
else if(mVertices[i].z > aMax.z)
aMax.z = mVertices[i].z;
}
}