summaryrefslogtreecommitdiff
path: root/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp')
-rw-r--r--thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp1000
1 files changed, 1000 insertions, 0 deletions
diff --git a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp b/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp
new file mode 100644
index 0000000000..24d8ddff89
--- /dev/null
+++ b/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp
@@ -0,0 +1,1000 @@
+// 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];
+}