1001 lines
24 KiB
C++
1001 lines
24 KiB
C++
// This code is in the public domain -- castanyo@yahoo.es
|
|
|
|
#include "nvmesh.h" // pch
|
|
|
|
#include "MeshBuilder.h"
|
|
#include "TriMesh.h"
|
|
#include "QuadTriMesh.h"
|
|
#include "halfedge/Mesh.h"
|
|
#include "halfedge/Vertex.h"
|
|
#include "halfedge/Face.h"
|
|
|
|
#include "weld/Weld.h"
|
|
|
|
#include "nvmath/Box.h"
|
|
#include "nvmath/Vector.inl"
|
|
|
|
#include "nvcore/StrLib.h"
|
|
#include "nvcore/RadixSort.h"
|
|
#include "nvcore/Ptr.h"
|
|
#include "nvcore/Array.inl"
|
|
#include "nvcore/HashMap.inl"
|
|
|
|
|
|
using namespace nv;
|
|
|
|
/*
|
|
By default the mesh builder creates 3 streams (position, normal, texcoord), I'm planning to add support for extra streams as follows:
|
|
|
|
enum StreamType { StreamType_Float, StreamType_Vector2, StreamType_Vector3, StreamType_Vector4 };
|
|
|
|
uint addStream(const char *, uint idx, StreamType);
|
|
|
|
uint addAttribute(float)
|
|
uint addAttribute(Vector2)
|
|
uint addAttribute(Vector3)
|
|
uint addAttribute(Vector4)
|
|
|
|
struct Vertex
|
|
{
|
|
uint pos;
|
|
uint nor;
|
|
uint tex;
|
|
uint * attribs; // NULL or NIL terminated array?
|
|
};
|
|
|
|
All streams must be added before hand, so that you know the size of the attribs array.
|
|
|
|
The vertex hash function could be kept as is, but the == operator should be extended to test
|
|
the extra atributes when available.
|
|
|
|
That might require a custom hash implementation, or an extension of the current one. How to
|
|
handle the variable number of attributes in the attribs array?
|
|
|
|
bool operator()(const Vertex & a, const Vertex & b) const
|
|
{
|
|
if (a.pos != b.pos || a.nor != b.nor || a.tex != b.tex) return false;
|
|
if (a.attribs == NULL && b.attribs == NULL) return true;
|
|
return 0 == memcmp(a.attribs, b.attribs, ???);
|
|
}
|
|
|
|
We could use a NIL terminated array, or provide custom user data to the equals functor.
|
|
|
|
vertexMap.setUserData((void *)vertexAttribCount);
|
|
|
|
bool operator()(const Vertex & a, const Vertex & b, void * userData) const { ... }
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace
|
|
{
|
|
struct Material
|
|
{
|
|
Material() : faceCount(0) {}
|
|
Material(const String & str) : name(str), faceCount(0) {}
|
|
|
|
String name;
|
|
uint faceCount;
|
|
};
|
|
|
|
struct Vertex
|
|
{
|
|
//Vertex() {}
|
|
//Vertex(uint p, uint n, uint t0, uint t1, uint c) : pos(p), nor(n), tex0(t0), tex1(t1), col(c) {}
|
|
|
|
friend bool operator==(const Vertex & a, const Vertex & b)
|
|
{
|
|
return a.pos == b.pos && a.nor == b.nor && a.tex[0] == b.tex[0] && a.tex[1] == b.tex[1] && a.col[0] == b.col[0] && a.col[1] == b.col[1] && a.col[2] == b.col[2];
|
|
}
|
|
|
|
uint pos;
|
|
uint nor;
|
|
uint tex[2];
|
|
uint col[3];
|
|
};
|
|
|
|
struct Face
|
|
{
|
|
uint id;
|
|
uint firstIndex;
|
|
uint indexCount;
|
|
uint material;
|
|
uint group;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
namespace nv
|
|
{
|
|
// This is a much better hash than the default and greatly improves performance!
|
|
template <> struct Hash<Vertex>
|
|
{
|
|
uint operator()(const Vertex & v) const { return v.pos + v.nor + v.tex[0]/* + v.col*/; }
|
|
};
|
|
}
|
|
|
|
struct MeshBuilder::PrivateData
|
|
{
|
|
PrivateData() : currentGroup(NIL), currentMaterial(NIL), maxFaceIndexCount(0) {}
|
|
|
|
uint pushVertex(uint p, uint n, uint t0, uint t1, uint c0, uint c1, uint c2);
|
|
uint pushVertex(const Vertex & v);
|
|
|
|
Array<Vector3> posArray;
|
|
Array<Vector3> norArray;
|
|
Array<Vector2> texArray[2];
|
|
Array<Vector4> colArray[3];
|
|
|
|
Array<Vertex> vertexArray;
|
|
HashMap<Vertex, uint> vertexMap;
|
|
|
|
HashMap<String, uint> materialMap;
|
|
Array<Material> materialArray;
|
|
|
|
uint currentGroup;
|
|
uint currentMaterial;
|
|
|
|
Array<uint> indexArray;
|
|
Array<Face> faceArray;
|
|
|
|
uint maxFaceIndexCount;
|
|
};
|
|
|
|
|
|
uint MeshBuilder::PrivateData::pushVertex(uint p, uint n, uint t0, uint t1, uint c0, uint c1, uint c2)
|
|
{
|
|
Vertex v;
|
|
v.pos = p;
|
|
v.nor = n;
|
|
v.tex[0] = t0;
|
|
v.tex[1] = t1;
|
|
v.col[0] = c0;
|
|
v.col[1] = c1;
|
|
v.col[2] = c2;
|
|
return pushVertex(v);
|
|
}
|
|
|
|
uint MeshBuilder::PrivateData::pushVertex(const Vertex & v)
|
|
{
|
|
// Lookup vertex v in map.
|
|
uint idx;
|
|
if (vertexMap.get(v, &idx))
|
|
{
|
|
return idx;
|
|
}
|
|
|
|
idx = vertexArray.count();
|
|
vertexArray.pushBack(v);
|
|
vertexMap.add(v, idx);
|
|
|
|
return idx;
|
|
}
|
|
|
|
|
|
MeshBuilder::MeshBuilder() : d(new PrivateData())
|
|
{
|
|
}
|
|
|
|
MeshBuilder::~MeshBuilder()
|
|
{
|
|
nvDebugCheck(d != NULL);
|
|
delete d;
|
|
}
|
|
|
|
|
|
// Builder methods.
|
|
uint MeshBuilder::addPosition(const Vector3 & v)
|
|
{
|
|
d->posArray.pushBack(validate(v));
|
|
return d->posArray.count() - 1;
|
|
}
|
|
|
|
uint MeshBuilder::addNormal(const Vector3 & v)
|
|
{
|
|
d->norArray.pushBack(validate(v));
|
|
return d->norArray.count() - 1;
|
|
}
|
|
|
|
uint MeshBuilder::addTexCoord(const Vector2 & v, uint set/*=0*/)
|
|
{
|
|
d->texArray[set].pushBack(validate(v));
|
|
return d->texArray[set].count() - 1;
|
|
}
|
|
|
|
uint MeshBuilder::addColor(const Vector4 & v, uint set/*=0*/)
|
|
{
|
|
d->colArray[set].pushBack(validate(v));
|
|
return d->colArray[set].count() - 1;
|
|
}
|
|
|
|
void MeshBuilder::beginGroup(uint id)
|
|
{
|
|
d->currentGroup = id;
|
|
}
|
|
|
|
void MeshBuilder::endGroup()
|
|
{
|
|
d->currentGroup = NIL;
|
|
}
|
|
|
|
// Add named material, check for uniquenes.
|
|
uint MeshBuilder::addMaterial(const char * name)
|
|
{
|
|
uint index;
|
|
if (d->materialMap.get(name, &index)) {
|
|
nvDebugCheck(d->materialArray[index].name == name);
|
|
}
|
|
else {
|
|
index = d->materialArray.count();
|
|
d->materialMap.add(name, index);
|
|
|
|
Material material(name);
|
|
d->materialArray.append(material);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
void MeshBuilder::beginMaterial(uint id)
|
|
{
|
|
d->currentMaterial = id;
|
|
}
|
|
|
|
void MeshBuilder::endMaterial()
|
|
{
|
|
d->currentMaterial = NIL;
|
|
}
|
|
|
|
void MeshBuilder::beginPolygon(uint id/*=0*/)
|
|
{
|
|
Face face;
|
|
face.id = id;
|
|
face.firstIndex = d->indexArray.count();
|
|
face.indexCount = 0;
|
|
face.material = d->currentMaterial;
|
|
face.group = d->currentGroup;
|
|
|
|
d->faceArray.pushBack(face);
|
|
}
|
|
|
|
uint MeshBuilder::addVertex(uint p, uint n/*= NIL*/, uint t0/*= NIL*/, uint t1/*= NIL*/, uint c0/*= NIL*/, uint c1/*= NIL*/, uint c2/*= NIL*/)
|
|
{
|
|
// @@ In theory there's no need to add vertices before faces, but I'm adding this to debug problems in our maya exporter:
|
|
nvDebugCheck(p < d->posArray.count());
|
|
nvDebugCheck(n == NIL || n < d->norArray.count());
|
|
nvDebugCheck(t0 == NIL || t0 < d->texArray[0].count());
|
|
nvDebugCheck(t1 == NIL || t1 < d->texArray[1].count());
|
|
//nvDebugCheck(c0 == NIL || c0 < d->colArray[0].count());
|
|
if (c0 > d->colArray[0].count()) c0 = NIL; // @@ This seems to be happening in loc_swamp_catwalk.mb! No idea why.
|
|
nvDebugCheck(c1 == NIL || c1 < d->colArray[1].count());
|
|
nvDebugCheck(c2 == NIL || c2 < d->colArray[2].count());
|
|
|
|
uint idx = d->pushVertex(p, n, t0, t1, c0, c1, c2);
|
|
d->indexArray.pushBack(idx);
|
|
d->faceArray.back().indexCount++;
|
|
return idx;
|
|
}
|
|
|
|
uint MeshBuilder::addVertex(const Vector3 & pos)
|
|
{
|
|
uint p = addPosition(pos);
|
|
return addVertex(p);
|
|
}
|
|
|
|
#if 0
|
|
uint MeshBuilder::addVertex(const Vector3 & pos, const Vector3 & nor, const Vector2 & tex0, const Vector2 & tex1, const Vector4 & col0, const Vector4 & col1)
|
|
{
|
|
uint p = addPosition(pos);
|
|
uint n = addNormal(nor);
|
|
uint t0 = addTexCoord(tex0, 0);
|
|
uint t1 = addTexCoord(tex1, 1);
|
|
uint c0 = addColor(col0);
|
|
uint c1 = addColor(col1);
|
|
return addVertex(p, n, t0, t1, c0, c1);
|
|
}
|
|
#endif
|
|
|
|
// Return true if the face is valid and was added to the mesh.
|
|
bool MeshBuilder::endPolygon()
|
|
{
|
|
const Face & face = d->faceArray.back();
|
|
const uint count = face.indexCount;
|
|
|
|
// Validate polygon here.
|
|
bool invalid = count <= 2;
|
|
|
|
if (!invalid) {
|
|
// Skip zero area polygons. Or polygons with degenerate edges (which will result in zero-area triangles).
|
|
const uint first = face.firstIndex;
|
|
for (uint j = count - 1, i = 0; i < count; j = i, i++) {
|
|
uint v0 = d->indexArray[first + i];
|
|
uint v1 = d->indexArray[first + j];
|
|
|
|
uint p0 = d->vertexArray[v0].pos;
|
|
uint p1 = d->vertexArray[v1].pos;
|
|
|
|
if (p0 == p1) {
|
|
invalid = true;
|
|
break;
|
|
}
|
|
|
|
if (equal(d->posArray[p0], d->posArray[p1], FLT_EPSILON)) {
|
|
invalid = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint v0 = d->indexArray[first];
|
|
uint p0 = d->vertexArray[v0].pos;
|
|
Vector3 x0 = d->posArray[p0];
|
|
|
|
float area = 0.0f;
|
|
for (uint j = 1, i = 2; i < count; j = i, i++) {
|
|
uint v1 = d->indexArray[first + i];
|
|
uint v2 = d->indexArray[first + j];
|
|
|
|
uint p1 = d->vertexArray[v1].pos;
|
|
uint p2 = d->vertexArray[v2].pos;
|
|
|
|
Vector3 x1 = d->posArray[p1];
|
|
Vector3 x2 = d->posArray[p2];
|
|
|
|
area += length(cross(x1-x0, x2-x0));
|
|
}
|
|
|
|
if (0.5 * area < 1e-6) { // Reduce this threshold if artists have legitimate complains.
|
|
invalid = true;
|
|
}
|
|
|
|
// @@ This is not complete. We may still get zero area triangles after triangulation.
|
|
// However, our plugin triangulates before building the mesh, so hopefully that's not a problem.
|
|
|
|
}
|
|
|
|
if (invalid)
|
|
{
|
|
d->indexArray.resize(d->indexArray.size() - count);
|
|
d->faceArray.popBack();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (d->currentMaterial != NIL) {
|
|
d->materialArray[d->currentMaterial].faceCount++;
|
|
}
|
|
|
|
d->maxFaceIndexCount = max(d->maxFaceIndexCount, count);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
uint MeshBuilder::weldPositions()
|
|
{
|
|
Array<uint> xrefs;
|
|
Weld<Vector3> weldVector3;
|
|
|
|
if (d->posArray.count()) {
|
|
// Weld vertex attributes.
|
|
weldVector3(d->posArray, xrefs);
|
|
|
|
// Remap vertex indices.
|
|
const uint vertexCount = d->vertexArray.count();
|
|
for (uint v = 0; v < vertexCount; v++)
|
|
{
|
|
Vertex & vertex = d->vertexArray[v];
|
|
if (vertex.pos != NIL) vertex.pos = xrefs[vertex.pos];
|
|
}
|
|
}
|
|
|
|
return d->posArray.count();
|
|
}
|
|
|
|
uint MeshBuilder::weldNormals()
|
|
{
|
|
Array<uint> xrefs;
|
|
Weld<Vector3> weldVector3;
|
|
|
|
if (d->norArray.count()) {
|
|
// Weld vertex attributes.
|
|
weldVector3(d->norArray, xrefs);
|
|
|
|
// Remap vertex indices.
|
|
const uint vertexCount = d->vertexArray.count();
|
|
for (uint v = 0; v < vertexCount; v++)
|
|
{
|
|
Vertex & vertex = d->vertexArray[v];
|
|
if (vertex.nor != NIL) vertex.nor = xrefs[vertex.nor];
|
|
}
|
|
}
|
|
|
|
return d->norArray.count();
|
|
}
|
|
|
|
uint MeshBuilder::weldTexCoords(uint set/*=0*/)
|
|
{
|
|
Array<uint> xrefs;
|
|
Weld<Vector2> weldVector2;
|
|
|
|
if (d->texArray[set].count()) {
|
|
// Weld vertex attributes.
|
|
weldVector2(d->texArray[set], xrefs);
|
|
|
|
// Remap vertex indices.
|
|
const uint vertexCount = d->vertexArray.count();
|
|
for (uint v = 0; v < vertexCount; v++)
|
|
{
|
|
Vertex & vertex = d->vertexArray[v];
|
|
if (vertex.tex[set] != NIL) vertex.tex[set] = xrefs[vertex.tex[set]];
|
|
}
|
|
}
|
|
|
|
return d->texArray[set].count();
|
|
}
|
|
|
|
uint MeshBuilder::weldColors(uint set/*=0*/)
|
|
{
|
|
Array<uint> xrefs;
|
|
Weld<Vector4> weldVector4;
|
|
|
|
if (d->colArray[set].count()) {
|
|
// Weld vertex attributes.
|
|
weldVector4(d->colArray[set], xrefs);
|
|
|
|
// Remap vertex indices.
|
|
const uint vertexCount = d->vertexArray.count();
|
|
for (uint v = 0; v < vertexCount; v++)
|
|
{
|
|
Vertex & vertex = d->vertexArray[v];
|
|
if (vertex.col[set] != NIL) vertex.col[set] = xrefs[vertex.col[set]];
|
|
}
|
|
}
|
|
|
|
return d->colArray[set].count();
|
|
}
|
|
|
|
void MeshBuilder::weldVertices() {
|
|
|
|
if (d->vertexArray.count() == 0) {
|
|
// Nothing to do.
|
|
return;
|
|
}
|
|
|
|
Array<uint> xrefs;
|
|
Weld<Vertex> weldVertex;
|
|
|
|
// Weld vertices.
|
|
weldVertex(d->vertexArray, xrefs);
|
|
|
|
// Remap face indices.
|
|
const uint indexCount = d->indexArray.count();
|
|
for (uint i = 0; i < indexCount; i++)
|
|
{
|
|
d->indexArray[i] = xrefs[d->indexArray[i]];
|
|
}
|
|
|
|
// Remap vertex map.
|
|
foreach(i, d->vertexMap)
|
|
{
|
|
d->vertexMap[i].value = xrefs[d->vertexMap[i].value];
|
|
}
|
|
}
|
|
|
|
|
|
void MeshBuilder::optimize()
|
|
{
|
|
if (d->vertexArray.count() == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
weldPositions();
|
|
weldNormals();
|
|
weldTexCoords(0);
|
|
weldTexCoords(1);
|
|
weldColors();
|
|
|
|
weldVertices();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MeshBuilder::removeUnusedMaterials(Array<uint> & newMaterialId)
|
|
{
|
|
uint materialCount = d->materialArray.count();
|
|
|
|
// Reset face counts.
|
|
for (uint i = 0; i < materialCount; i++) {
|
|
d->materialArray[i].faceCount = 0;
|
|
}
|
|
|
|
// Count faces.
|
|
foreach(i, d->faceArray) {
|
|
Face & face = d->faceArray[i];
|
|
|
|
if (face.material != NIL) {
|
|
nvDebugCheck(face.material < materialCount);
|
|
|
|
d->materialArray[face.material].faceCount++;
|
|
}
|
|
}
|
|
|
|
// Remove unused materials.
|
|
newMaterialId.resize(materialCount);
|
|
|
|
for (uint i = 0, m = 0; i < materialCount; i++)
|
|
{
|
|
if (d->materialArray[m].faceCount > 0)
|
|
{
|
|
newMaterialId[i] = m++;
|
|
}
|
|
else
|
|
{
|
|
newMaterialId[i] = NIL;
|
|
d->materialArray.removeAt(m);
|
|
}
|
|
}
|
|
|
|
materialCount = d->materialArray.count();
|
|
|
|
// Update face material ids.
|
|
foreach(i, d->faceArray) {
|
|
Face & face = d->faceArray[i];
|
|
|
|
if (face.material != NIL) {
|
|
uint id = newMaterialId[face.material];
|
|
nvDebugCheck(id != NIL && id < materialCount);
|
|
|
|
face.material = id;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MeshBuilder::sortFacesByGroup()
|
|
{
|
|
const uint faceCount = d->faceArray.count();
|
|
|
|
Array<uint> faceGroupArray;
|
|
faceGroupArray.resize(faceCount);
|
|
|
|
for (uint i = 0; i < faceCount; i++) {
|
|
faceGroupArray[i] = d->faceArray[i].group;
|
|
}
|
|
|
|
RadixSort radix;
|
|
radix.sort(faceGroupArray);
|
|
|
|
Array<Face> newFaceArray;
|
|
newFaceArray.resize(faceCount);
|
|
|
|
for (uint i = 0; i < faceCount; i++) {
|
|
newFaceArray[i] = d->faceArray[radix.rank(i)];
|
|
}
|
|
|
|
swap(newFaceArray, d->faceArray);
|
|
}
|
|
|
|
void MeshBuilder::sortFacesByMaterial()
|
|
{
|
|
const uint faceCount = d->faceArray.count();
|
|
|
|
Array<uint> faceMaterialArray;
|
|
faceMaterialArray.resize(faceCount);
|
|
|
|
for (uint i = 0; i < faceCount; i++) {
|
|
faceMaterialArray[i] = d->faceArray[i].material;
|
|
}
|
|
|
|
RadixSort radix;
|
|
radix.sort(faceMaterialArray);
|
|
|
|
Array<Face> newFaceArray;
|
|
newFaceArray.resize(faceCount);
|
|
|
|
for (uint i = 0; i < faceCount; i++) {
|
|
newFaceArray[i] = d->faceArray[radix.rank(i)];
|
|
}
|
|
|
|
swap(newFaceArray, d->faceArray);
|
|
}
|
|
|
|
|
|
void MeshBuilder::reset()
|
|
{
|
|
nvDebugCheck(d != NULL);
|
|
delete d;
|
|
d = new PrivateData();
|
|
}
|
|
|
|
void MeshBuilder::done()
|
|
{
|
|
if (d->currentGroup != NIL) {
|
|
endGroup();
|
|
}
|
|
|
|
if (d->currentMaterial != NIL) {
|
|
endMaterial();
|
|
}
|
|
}
|
|
|
|
// Hints.
|
|
void MeshBuilder::hintTriangleCount(uint count)
|
|
{
|
|
d->indexArray.reserve(d->indexArray.count() + count * 4);
|
|
}
|
|
|
|
void MeshBuilder::hintVertexCount(uint count)
|
|
{
|
|
d->vertexArray.reserve(d->vertexArray.count() + count);
|
|
d->vertexMap.resize(d->vertexMap.count() + count);
|
|
}
|
|
|
|
void MeshBuilder::hintPositionCount(uint count)
|
|
{
|
|
d->posArray.reserve(d->posArray.count() + count);
|
|
}
|
|
|
|
void MeshBuilder::hintNormalCount(uint count)
|
|
{
|
|
d->norArray.reserve(d->norArray.count() + count);
|
|
}
|
|
|
|
void MeshBuilder::hintTexCoordCount(uint count, uint set/*=0*/)
|
|
{
|
|
d->texArray[set].reserve(d->texArray[set].count() + count);
|
|
}
|
|
|
|
void MeshBuilder::hintColorCount(uint count, uint set/*=0*/)
|
|
{
|
|
d->colArray[set].reserve(d->colArray[set].count() + count);
|
|
}
|
|
|
|
|
|
// Helpers.
|
|
void MeshBuilder::addTriangle(uint v0, uint v1, uint v2)
|
|
{
|
|
beginPolygon();
|
|
addVertex(v0);
|
|
addVertex(v1);
|
|
addVertex(v2);
|
|
endPolygon();
|
|
}
|
|
|
|
void MeshBuilder::addQuad(uint v0, uint v1, uint v2, uint v3)
|
|
{
|
|
beginPolygon();
|
|
addVertex(v0);
|
|
addVertex(v1);
|
|
addVertex(v2);
|
|
addVertex(v3);
|
|
endPolygon();
|
|
}
|
|
|
|
|
|
// Get tri mesh.
|
|
TriMesh * MeshBuilder::buildTriMesh() const
|
|
{
|
|
const uint faceCount = d->faceArray.count();
|
|
uint triangleCount = 0;
|
|
for (uint f = 0; f < faceCount; f++) {
|
|
triangleCount += d->faceArray[f].indexCount - 2;
|
|
}
|
|
|
|
const uint vertexCount = d->vertexArray.count();
|
|
TriMesh * mesh = new TriMesh(triangleCount, vertexCount);
|
|
|
|
// Build faces.
|
|
Array<TriMesh::Face> & faces = mesh->faces();
|
|
|
|
for(uint f = 0; f < faceCount; f++)
|
|
{
|
|
int firstIndex = d->faceArray[f].firstIndex;
|
|
int indexCount = d->faceArray[f].indexCount;
|
|
|
|
int v0 = d->indexArray[firstIndex + 0];
|
|
int v1 = d->indexArray[firstIndex + 1];
|
|
|
|
for(int t = 0; t < indexCount - 2; t++) {
|
|
int v2 = d->indexArray[firstIndex + t + 2];
|
|
|
|
TriMesh::Face face;
|
|
face.id = faces.count();
|
|
face.v[0] = v0;
|
|
face.v[1] = v1;
|
|
face.v[2] = v2;
|
|
faces.append(face);
|
|
|
|
v1 = v2;
|
|
}
|
|
}
|
|
|
|
// Build vertices.
|
|
Array<BaseMesh::Vertex> & vertices = mesh->vertices();
|
|
|
|
for(uint i = 0; i < vertexCount; i++)
|
|
{
|
|
BaseMesh::Vertex vertex;
|
|
vertex.id = i;
|
|
if (d->vertexArray[i].pos != NIL) vertex.pos = d->posArray[d->vertexArray[i].pos];
|
|
if (d->vertexArray[i].nor != NIL) vertex.nor = d->norArray[d->vertexArray[i].nor];
|
|
if (d->vertexArray[i].tex[0] != NIL) vertex.tex = d->texArray[0][d->vertexArray[i].tex[0]];
|
|
|
|
vertices.append(vertex);
|
|
}
|
|
|
|
return mesh;
|
|
}
|
|
|
|
// Get quad/tri mesh.
|
|
QuadTriMesh * MeshBuilder::buildQuadTriMesh() const
|
|
{
|
|
const uint faceCount = d->faceArray.count();
|
|
const uint vertexCount = d->vertexArray.count();
|
|
QuadTriMesh * mesh = new QuadTriMesh(faceCount, vertexCount);
|
|
|
|
// Build faces.
|
|
Array<QuadTriMesh::Face> & faces = mesh->faces();
|
|
|
|
for (uint f = 0; f < faceCount; f++)
|
|
{
|
|
int firstIndex = d->faceArray[f].firstIndex;
|
|
int indexCount = d->faceArray[f].indexCount;
|
|
|
|
QuadTriMesh::Face face;
|
|
face.id = f;
|
|
|
|
face.v[0] = d->indexArray[firstIndex + 0];
|
|
face.v[1] = d->indexArray[firstIndex + 1];
|
|
face.v[2] = d->indexArray[firstIndex + 2];
|
|
|
|
// Only adds triangles and quads. Ignores polygons.
|
|
if (indexCount == 3) {
|
|
face.v[3] = NIL;
|
|
faces.append(face);
|
|
}
|
|
else if (indexCount == 4) {
|
|
face.v[3] = d->indexArray[firstIndex + 3];
|
|
faces.append(face);
|
|
}
|
|
}
|
|
|
|
// Build vertices.
|
|
Array<BaseMesh::Vertex> & vertices = mesh->vertices();
|
|
|
|
for(uint i = 0; i < vertexCount; i++)
|
|
{
|
|
BaseMesh::Vertex vertex;
|
|
vertex.id = i;
|
|
if (d->vertexArray[i].pos != NIL) vertex.pos = d->posArray[d->vertexArray[i].pos];
|
|
if (d->vertexArray[i].nor != NIL) vertex.nor = d->norArray[d->vertexArray[i].nor];
|
|
if (d->vertexArray[i].tex[0] != NIL) vertex.tex = d->texArray[0][d->vertexArray[i].tex[0]];
|
|
|
|
vertices.append(vertex);
|
|
}
|
|
|
|
return mesh;
|
|
}
|
|
|
|
// Get half edge mesh.
|
|
HalfEdge::Mesh * MeshBuilder::buildHalfEdgeMesh(bool weldPositions, Error * error/*=NULL*/, Array<uint> * badFaces/*=NULL*/) const
|
|
{
|
|
if (error != NULL) *error = Error_None;
|
|
|
|
const uint vertexCount = d->vertexArray.count();
|
|
AutoPtr<HalfEdge::Mesh> mesh(new HalfEdge::Mesh());
|
|
|
|
for(uint v = 0; v < vertexCount; v++)
|
|
{
|
|
HalfEdge::Vertex * vertex = mesh->addVertex(d->posArray[d->vertexArray[v].pos]);
|
|
if (d->vertexArray[v].nor != NIL) vertex->nor = d->norArray[d->vertexArray[v].nor];
|
|
if (d->vertexArray[v].tex[0] != NIL) vertex->tex = Vector2(d->texArray[0][d->vertexArray[v].tex[0]]);
|
|
if (d->vertexArray[v].col[0] != NIL) vertex->col = d->colArray[0][d->vertexArray[v].col[0]];
|
|
}
|
|
|
|
if (weldPositions) {
|
|
mesh->linkColocals();
|
|
}
|
|
else {
|
|
// Build canonical map from position indices.
|
|
Array<uint> canonicalMap(vertexCount);
|
|
|
|
foreach (i, d->vertexArray) {
|
|
canonicalMap.append(d->vertexArray[i].pos);
|
|
}
|
|
|
|
mesh->linkColocalsWithCanonicalMap(canonicalMap);
|
|
}
|
|
|
|
const uint faceCount = d->faceArray.count();
|
|
for (uint f = 0; f < faceCount; f++)
|
|
{
|
|
const uint firstIndex = d->faceArray[f].firstIndex;
|
|
const uint indexCount = d->faceArray[f].indexCount;
|
|
|
|
HalfEdge::Face * face = mesh->addFace(d->indexArray, firstIndex, indexCount);
|
|
|
|
// @@ This is too late, removing the face here will leave the mesh improperly connected.
|
|
/*if (face->area() <= FLT_EPSILON) {
|
|
mesh->remove(face);
|
|
face = NULL;
|
|
}*/
|
|
|
|
if (face == NULL) {
|
|
// Non manifold mesh.
|
|
if (error != NULL) *error = Error_NonManifoldEdge;
|
|
if (badFaces != NULL) {
|
|
badFaces->append(d->faceArray[f].id);
|
|
}
|
|
//return NULL; // IC: Ignore error and continue building the mesh.
|
|
}
|
|
|
|
if (face != NULL) {
|
|
face->group = d->faceArray[f].group;
|
|
face->material = d->faceArray[f].material;
|
|
}
|
|
}
|
|
|
|
mesh->linkBoundary();
|
|
|
|
// We cannot fix functions here, because this would introduce new vertices and these vertices won't have the corresponding builder data.
|
|
|
|
// Maybe the builder should perform the search for T-junctions and update the vertex data directly.
|
|
|
|
// For now, we don't fix T-junctions at export time, but only during parameterization.
|
|
|
|
//mesh->fixBoundaryJunctions();
|
|
|
|
//mesh->sewBoundary();
|
|
|
|
return mesh.release();
|
|
}
|
|
|
|
|
|
bool MeshBuilder::buildPositions(Array<Vector3> & positionArray)
|
|
{
|
|
const uint vertexCount = d->vertexArray.count();
|
|
positionArray.resize(vertexCount);
|
|
|
|
for (uint v = 0; v < vertexCount; v++)
|
|
{
|
|
nvDebugCheck(d->vertexArray[v].pos != NIL);
|
|
positionArray[v] = d->posArray[d->vertexArray[v].pos];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MeshBuilder::buildNormals(Array<Vector3> & normalArray)
|
|
{
|
|
bool anyNormal = false;
|
|
|
|
const uint vertexCount = d->vertexArray.count();
|
|
normalArray.resize(vertexCount);
|
|
|
|
for (uint v = 0; v < vertexCount; v++)
|
|
{
|
|
if (d->vertexArray[v].nor == NIL) {
|
|
normalArray[v] = Vector3(0, 0, 1);
|
|
}
|
|
else {
|
|
anyNormal = true;
|
|
normalArray[v] = d->norArray[d->vertexArray[v].nor];
|
|
}
|
|
}
|
|
|
|
return anyNormal;
|
|
}
|
|
|
|
bool MeshBuilder::buildTexCoords(Array<Vector2> & texCoordArray, uint set/*=0*/)
|
|
{
|
|
bool anyTexCoord = false;
|
|
|
|
const uint vertexCount = d->vertexArray.count();
|
|
texCoordArray.resize(vertexCount);
|
|
|
|
for (uint v = 0; v < vertexCount; v++)
|
|
{
|
|
if (d->vertexArray[v].tex[set] == NIL) {
|
|
texCoordArray[v] = Vector2(0, 0);
|
|
}
|
|
else {
|
|
anyTexCoord = true;
|
|
texCoordArray[v] = d->texArray[set][d->vertexArray[v].tex[set]];
|
|
}
|
|
}
|
|
|
|
return anyTexCoord;
|
|
}
|
|
|
|
bool MeshBuilder::buildColors(Array<Vector4> & colorArray, uint set/*=0*/)
|
|
{
|
|
bool anyColor = false;
|
|
|
|
const uint vertexCount = d->vertexArray.count();
|
|
colorArray.resize(vertexCount);
|
|
|
|
for (uint v = 0; v < vertexCount; v++)
|
|
{
|
|
if (d->vertexArray[v].col[set] == NIL) {
|
|
colorArray[v] = Vector4(0, 0, 0, 1);
|
|
}
|
|
else {
|
|
anyColor = true;
|
|
colorArray[v] = d->colArray[set][d->vertexArray[v].col[set]];
|
|
}
|
|
}
|
|
|
|
return anyColor;
|
|
}
|
|
|
|
void MeshBuilder::buildVertexToPositionMap(Array<int> &map)
|
|
{
|
|
const uint vertexCount = d->vertexArray.count();
|
|
map.resize(vertexCount);
|
|
|
|
foreach (i, d->vertexArray) {
|
|
map[i] = d->vertexArray[i].pos;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
uint MeshBuilder::vertexCount() const
|
|
{
|
|
return d->vertexArray.count();
|
|
}
|
|
|
|
|
|
uint MeshBuilder::positionCount() const
|
|
{
|
|
return d->posArray.count();
|
|
}
|
|
|
|
uint MeshBuilder::normalCount() const
|
|
{
|
|
return d->norArray.count();
|
|
}
|
|
|
|
uint MeshBuilder::texCoordCount(uint set/*=0*/) const
|
|
{
|
|
return d->texArray[set].count();
|
|
}
|
|
|
|
uint MeshBuilder::colorCount(uint set/*=0*/) const
|
|
{
|
|
return d->colArray[set].count();
|
|
}
|
|
|
|
|
|
uint MeshBuilder::materialCount() const
|
|
{
|
|
return d->materialArray.count();
|
|
}
|
|
|
|
const char * MeshBuilder::material(uint i) const
|
|
{
|
|
return d->materialArray[i].name;
|
|
}
|
|
|
|
|
|
uint MeshBuilder::positionIndex(uint vertex) const
|
|
{
|
|
return d->vertexArray[vertex].pos;
|
|
}
|
|
uint MeshBuilder::normalIndex(uint vertex) const
|
|
{
|
|
return d->vertexArray[vertex].nor;
|
|
}
|
|
uint MeshBuilder::texCoordIndex(uint vertex, uint set/*=0*/) const
|
|
{
|
|
return d->vertexArray[vertex].tex[set];
|
|
}
|
|
uint MeshBuilder::colorIndex(uint vertex, uint set/*=0*/) const
|
|
{
|
|
return d->vertexArray[vertex].col[set];
|
|
}
|