diff options
Diffstat (limited to 'thirdparty/thekla_atlas/nvmesh')
48 files changed, 0 insertions, 11471 deletions
diff --git a/thirdparty/thekla_atlas/nvmesh/BaseMesh.cpp b/thirdparty/thekla_atlas/nvmesh/BaseMesh.cpp deleted file mode 100644 index f17d3b46fd..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/BaseMesh.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// This code is in the public domain -- Ignacio Castaņo <castano@gmail.com> - -#include "BaseMesh.h" -#include "Stream.h" -#include "nvmath/TypeSerialization.h" - - -namespace nv -{ - static Stream & operator<< (Stream & s, BaseMesh::Vertex & vertex) - { - return s << vertex.id << vertex.pos << vertex.nor << vertex.tex; - } - - Stream & operator<< (Stream & s, BaseMesh & mesh) - { - return s << mesh.m_vertexArray; - } -} diff --git a/thirdparty/thekla_atlas/nvmesh/BaseMesh.h b/thirdparty/thekla_atlas/nvmesh/BaseMesh.h deleted file mode 100644 index c8559511f1..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/BaseMesh.h +++ /dev/null @@ -1,72 +0,0 @@ -// This code is in the public domain -- Ignacio Castaņo <castano@gmail.com> - -#pragma once -#ifndef NV_MESH_BASEMESH_H -#define NV_MESH_BASEMESH_H - -#include "nvmesh.h" -#include "nvmath/Vector.h" -#include "nvcore/Array.h" -#include "nvcore/Hash.h" - -namespace nv -{ - - /// Base mesh without connectivity. - class BaseMesh - { - public: - struct Vertex; - - BaseMesh() {} - - BaseMesh(uint vertexNum) : - m_vertexArray(vertexNum) {} - - // Vertex methods. - uint vertexCount() const { return m_vertexArray.count(); } - const Vertex & vertexAt(uint i) const { return m_vertexArray[i]; } - Vertex & vertexAt(uint i) { return m_vertexArray[i]; } - const Array<Vertex> & vertices() const { return m_vertexArray; } - Array<Vertex> & vertices() { return m_vertexArray; } - - friend Stream & operator<< (Stream & s, BaseMesh & obj); - - protected: - - Array<Vertex> m_vertexArray; - }; - - - /// BaseMesh vertex. - struct BaseMesh::Vertex - { - Vertex() : id(NIL), pos(0.0f), nor(0.0f), tex(0.0f) {} - - uint id; // @@ Vertex should be an index into the vertex data. - Vector3 pos; - Vector3 nor; - Vector2 tex; - }; - - inline bool operator==(const BaseMesh::Vertex & a, const BaseMesh::Vertex & b) - { - return a.pos == b.pos && a.nor == b.nor && a.tex == b.tex; - } - - inline bool operator!=(const BaseMesh::Vertex & a, const BaseMesh::Vertex & b) - { - return a.pos != b.pos && a.nor != b.nor && a.tex != b.tex; - } - - template <> struct Hash<BaseMesh::Vertex> - { - uint operator()(const BaseMesh::Vertex & v) const - { - return Hash<Vector3>()(v.pos); - } - }; - -} // nv namespace - -#endif // NV_MESH_BASEMESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp b/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp deleted file mode 100644 index 24d8ddff89..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.cpp +++ /dev/null @@ -1,1000 +0,0 @@ -// 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]; -} diff --git a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.h b/thirdparty/thekla_atlas/nvmesh/MeshBuilder.h deleted file mode 100644 index 5b3af3fc1d..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/MeshBuilder.h +++ /dev/null @@ -1,119 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_MESHBUILDER_H -#define NV_MESH_MESHBUILDER_H - -#include "nvmesh.h" -#include "nvcore/Array.h" -#include "nvmath/Vector.h" - -namespace nv -{ - class String; - class TriMesh; - class QuadTriMesh; - namespace HalfEdge { class Mesh; } - - - /// Mesh builder is a helper class for importers. - /// Ideally it should handle any vertex data, but for now it only accepts positions, - /// normals and texcoords. - class MeshBuilder - { - NV_FORBID_COPY(MeshBuilder); - NV_FORBID_HEAPALLOC(); - public: - MeshBuilder(); - ~MeshBuilder(); - - // Builder methods. - uint addPosition(const Vector3 & v); - uint addNormal(const Vector3 & v); - uint addTexCoord(const Vector2 & v, uint set = 0); - uint addColor(const Vector4 & v, uint set = 0); - - void beginGroup(uint id); - void endGroup(); - - uint addMaterial(const char * name); - void beginMaterial(uint id); - void endMaterial(); - - void beginPolygon(uint id = 0); - uint addVertex(uint p, uint n = NIL, uint t0 = NIL, uint t1 = NIL, uint c0 = NIL, uint c1 = NIL, uint c2 = NIL); - uint addVertex(const Vector3 & p); - //uint addVertex(const Vector3 & p, const Vector3 & n, const Vector2 & t0 = Vector2(0), const Vector2 & t1 = Vector2(0), const Vector4 & c0 = Vector4(0), const Vector4 & c1 = Vector4(0)); - bool endPolygon(); - - uint weldPositions(); - uint weldNormals(); - uint weldTexCoords(uint set = 0); - uint weldColors(uint set = 0); - void weldVertices(); - - void optimize(); // eliminate duplicate components and duplicate vertices. - void removeUnusedMaterials(Array<uint> & newMaterialId); - void sortFacesByGroup(); - void sortFacesByMaterial(); - - void done(); - void reset(); - - // Hints. - void hintTriangleCount(uint count); - void hintVertexCount(uint count); - void hintPositionCount(uint count); - void hintNormalCount(uint count); - void hintTexCoordCount(uint count, uint set = 0); - void hintColorCount(uint count, uint set = 0); - - // Helpers. - void addTriangle(uint v0, uint v1, uint v2); - void addQuad(uint v0, uint v1, uint v2, uint v3); - - // Get result. - TriMesh * buildTriMesh() const; - QuadTriMesh * buildQuadTriMesh() const; - - enum Error { - Error_None, - Error_NonManifoldEdge, - Error_NonManifoldVertex, - }; - - HalfEdge::Mesh * buildHalfEdgeMesh(bool weldPositions, Error * error = NULL, Array<uint> * badFaces = NULL) const; - - bool buildPositions(Array<Vector3> & positionArray); - bool buildNormals(Array<Vector3> & normalArray); - bool buildTexCoords(Array<Vector2> & texCoordArray, uint set = 0); - bool buildColors(Array<Vector4> & colorArray, uint set = 0); - void buildVertexToPositionMap(Array<int> & map); - - - // Expose attribute indices of the unified vertex array. - uint vertexCount() const; - - uint positionCount() const; - uint normalCount() const; - uint texCoordCount(uint set = 0) const; - uint colorCount(uint set = 0) const; - - uint materialCount() const; - const char * material(uint i) const; - - uint positionIndex(uint vertex) const; - uint normalIndex(uint vertex) const; - uint texCoordIndex(uint vertex, uint set = 0) const; - uint colorIndex(uint vertex, uint set = 0) const; - - private: - - struct PrivateData; - PrivateData * d; - - }; - -} // nv namespace - -#endif // NV_MESH_MESHBUILDER_H diff --git a/thirdparty/thekla_atlas/nvmesh/MeshTopology.cpp b/thirdparty/thekla_atlas/nvmesh/MeshTopology.cpp deleted file mode 100644 index e7e1dce421..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/MeshTopology.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "nvcore/Array.h" -#include "nvcore/BitArray.h" - -#include "nvmesh/MeshTopology.h" -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Edge.h" -#include "nvmesh/halfedge/Face.h" - -using namespace nv; - -void MeshTopology::buildTopologyInfo(const HalfEdge::Mesh * mesh) -{ - const uint vertexCount = mesh->colocalVertexCount(); - const uint faceCount = mesh->faceCount(); - const uint edgeCount = mesh->edgeCount(); - - nvDebug( "--- Building mesh topology:\n" ); - - Array<uint> stack(faceCount); - - BitArray bitFlags(faceCount); - bitFlags.clearAll(); - - // Compute connectivity. - nvDebug( "--- Computing connectivity.\n" ); - - m_connectedCount = 0; - - for(uint f = 0; f < faceCount; f++ ) { - if( bitFlags.bitAt(f) == false ) { - m_connectedCount++; - - stack.pushBack( f ); - while( !stack.isEmpty() ) { - - const uint top = stack.back(); - nvCheck(top != NIL); - stack.popBack(); - - if( bitFlags.bitAt(top) == false ) { - bitFlags.setBitAt(top); - - const HalfEdge::Face * face = mesh->faceAt(top); - const HalfEdge::Edge * firstEdge = face->edge; - const HalfEdge::Edge * edge = firstEdge; - - do { - const HalfEdge::Face * neighborFace = edge->pair->face; - if (neighborFace != NULL) { - stack.pushBack(neighborFace->id); - } - edge = edge->next; - } while(edge != firstEdge); - } - } - } - } - nvCheck(stack.isEmpty()); - nvDebug( "--- %d connected components.\n", m_connectedCount ); - - - // Count boundary loops. - nvDebug( "--- Counting boundary loops.\n" ); - m_boundaryCount = 0; - - bitFlags.resize(edgeCount); - bitFlags.clearAll(); - - // Don't forget to link the boundary otherwise this won't work. - for (uint e = 0; e < edgeCount; e++) - { - const HalfEdge::Edge * startEdge = mesh->edgeAt(e); - if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) - { - nvDebugCheck(startEdge->face != NULL); - nvDebugCheck(startEdge->pair->face == NULL); - - startEdge = startEdge->pair; - - m_boundaryCount++; - - const HalfEdge::Edge * edge = startEdge; - do { - bitFlags.setBitAt(edge->id / 2); - edge = edge->next; - } while(startEdge != edge); - } - } - nvDebug("--- %d boundary loops found.\n", m_boundaryCount ); - - - // Compute euler number. - m_eulerNumber = vertexCount - edgeCount + faceCount; - nvDebug("--- Euler number: %d.\n", m_eulerNumber); - - - // Compute genus. (only valid on closed connected surfaces) - m_genus = -1; - if( isClosed() && isConnected() ) { - m_genus = (2 - m_eulerNumber) / 2; - nvDebug("--- Genus: %d.\n", m_genus); - } -} - - -/*static*/ bool MeshTopology::isQuadOnly(const HalfEdge::Mesh * mesh) -{ - const uint faceCount = mesh->faceCount(); - for(uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = mesh->faceAt(f); - if (face->edgeCount() != 4) { - return false; - } - } - - return true; -} diff --git a/thirdparty/thekla_atlas/nvmesh/MeshTopology.h b/thirdparty/thekla_atlas/nvmesh/MeshTopology.h deleted file mode 100644 index c3d7477b15..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/MeshTopology.h +++ /dev/null @@ -1,66 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_MESHTOPOLOGY_H -#define NV_MESH_MESHTOPOLOGY_H - -#include <nvmesh/nvmesh.h> - -namespace nv -{ - namespace HalfEdge { class Mesh; } - class MeshAdjacency; - - /// Mesh topology information. - class MeshTopology - { - public: - MeshTopology(const HalfEdge::Mesh * mesh) { buildTopologyInfo(mesh); } - - /// Determine if the mesh is connected. - bool isConnected() const { return m_connectedCount == 1; } - - /// Determine if the mesh is closed. (Each edge is shared by two faces) - bool isClosed() const { return m_boundaryCount == 0; } - - /// Return true if the mesh has the topology of a disk. - bool isDisk() const { return isConnected() && m_boundaryCount == 1/* && m_eulerNumber == 1*/; } - - /// Return the number of connected components. - int connectedCount() const { return m_connectedCount; } - - /// Return the number of open holes. - int holeCount() const { return m_boundaryCount; } - - /// Return the genus of the mesh. - int genus() const { return m_genus; } - - /// Return the euler number of the mesh. - int euler() const { return m_eulerNumber; } - - - static bool isQuadOnly(const HalfEdge::Mesh * mesh); - - - private: - - NVMESH_API void buildTopologyInfo(const HalfEdge::Mesh * mesh); - - private: - - ///< Number of boundary loops. - int m_boundaryCount; - - ///< Number of connected components. - int m_connectedCount; - - ///< Euler number. - int m_eulerNumber; - - /// Mesh genus. - int m_genus; - }; - -} // nv namespace - -#endif // NV_MESH_MESHTOPOLOGY_H diff --git a/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.cpp b/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.cpp deleted file mode 100644 index 64a071abe9..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// This code is in the public domain -- Ignacio Castaņo <castano@gmail.com> - -#include "QuadTriMesh.h" -#include "Stream.h" - -using namespace nv; - - -bool QuadTriMesh::isQuadFace(uint i) const -{ - return m_faceArray[i].isQuadFace(); -} - -const QuadTriMesh::Vertex & QuadTriMesh::faceVertex(uint f, uint v) const -{ - if (isQuadFace(f)) nvDebugCheck(v < 4); - else nvDebugCheck(v < 3); - - const Face & face = this->faceAt(f); - return this->vertexAt(face.v[v]); -} - - -namespace nv -{ - static Stream & operator<< (Stream & s, QuadTriMesh::Face & face) - { - return s << face.id << face.v[0] << face.v[1] << face.v[2] << face.v[3]; - } - - Stream & operator<< (Stream & s, QuadTriMesh & mesh) - { - return s << mesh.m_faceArray << (BaseMesh &) mesh; - } -} - diff --git a/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.h b/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.h deleted file mode 100644 index b8465f2db0..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/QuadTriMesh.h +++ /dev/null @@ -1,60 +0,0 @@ -// This code is in the public domain -- Ignacio Castaņo <castano@gmail.com> - -#pragma once -#ifndef NV_MESH_QUADTRIMESH_H -#define NV_MESH_QUADTRIMESH_H - -#include "nvcore/Array.h" -#include "nvmath/Vector.h" -#include "nvmesh/nvmesh.h" -#include "nvmesh/BaseMesh.h" - -namespace nv -{ - class Stream; - - /// Mixed quad/triangle mesh. - class QuadTriMesh : public BaseMesh - { - public: - struct Face; - typedef BaseMesh::Vertex Vertex; - - QuadTriMesh() {}; - QuadTriMesh(uint faceCount, uint vertexCount) : BaseMesh(vertexCount), m_faceArray(faceCount) {} - - // Face methods. - uint faceCount() const { return m_faceArray.count(); } - - const Face & faceAt(uint i) const { return m_faceArray[i]; } - Face & faceAt(uint i) { return m_faceArray[i]; } - - const Array<Face> & faces() const { return m_faceArray; } - Array<Face> & faces() { return m_faceArray; } - - bool isQuadFace(uint i) const; - - const Vertex & faceVertex(uint f, uint v) const; - - friend Stream & operator<< (Stream & s, QuadTriMesh & obj); - - private: - - Array<Face> m_faceArray; - - }; - - - /// QuadTriMesh face. - struct QuadTriMesh::Face - { - uint id; - uint v[4]; - - bool isQuadFace() const { return v[3] != NIL; } - }; - -} // nv namespace - - -#endif // NV_MESH_QUADTRIMESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/TriMesh.cpp b/thirdparty/thekla_atlas/nvmesh/TriMesh.cpp deleted file mode 100644 index bf10a474fb..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/TriMesh.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// This code is in the public domain -- Ignacio Castaņo <castano@gmail.com> - -#include "TriMesh.h" - -using namespace nv; - - -/// Triangle mesh. -Vector3 TriMesh::faceNormal(uint f) const -{ - const Face & face = this->faceAt(f); - const Vector3 & p0 = this->vertexAt(face.v[0]).pos; - const Vector3 & p1 = this->vertexAt(face.v[1]).pos; - const Vector3 & p2 = this->vertexAt(face.v[2]).pos; - return normalizeSafe(cross(p1 - p0, p2 - p0), Vector3(0.0f), 0.0f); -} - -/// Get face vertex. -const TriMesh::Vertex & TriMesh::faceVertex(uint f, uint v) const -{ - nvDebugCheck(v < 3); - const Face & face = this->faceAt(f); - return this->vertexAt(face.v[v]); -} - diff --git a/thirdparty/thekla_atlas/nvmesh/TriMesh.h b/thirdparty/thekla_atlas/nvmesh/TriMesh.h deleted file mode 100644 index bc5672c1ac..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/TriMesh.h +++ /dev/null @@ -1,51 +0,0 @@ -// This code is in the public domain -- Ignacio Castaņo <castano@gmail.com> - -#pragma once -#ifndef NV_MESH_TRIMESH_H -#define NV_MESH_TRIMESH_H - -#include "nvcore/Array.h" -#include "nvmath/Vector.inl" -#include "nvmesh/nvmesh.h" -#include "nvmesh/BaseMesh.h" - -namespace nv -{ - /// Triangle mesh. - class TriMesh : public BaseMesh - { - public: - struct Face; - typedef BaseMesh::Vertex Vertex; - - TriMesh(uint faceCount, uint vertexCount) : BaseMesh(vertexCount), m_faceArray(faceCount) {} - - // Face methods. - uint faceCount() const { return m_faceArray.count(); } - const Face & faceAt(uint i) const { return m_faceArray[i]; } - Face & faceAt(uint i) { return m_faceArray[i]; } - const Array<Face> & faces() const { return m_faceArray; } - Array<Face> & faces() { return m_faceArray; } - - NVMESH_API Vector3 faceNormal(uint f) const; - NVMESH_API const Vertex & faceVertex(uint f, uint v) const; - - friend Stream & operator<< (Stream & s, BaseMesh & obj); - - private: - - Array<Face> m_faceArray; - - }; - - - /// TriMesh face. - struct TriMesh::Face - { - uint id; - uint v[3]; - }; - -} // nv namespace - -#endif // NV_MESH_TRIMESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.cpp b/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.cpp deleted file mode 100644 index 69fd1deb24..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// This code is in the public domain -- Ignacio CastaÃąo <castano@gmail.com> - -#include "nvmesh.h" // pch - -#include "Bounds.h" - -#include "nvmesh/BaseMesh.h" -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Vertex.h" - -#include "nvmath/Box.inl" - -using namespace nv; - -Box MeshBounds::box(const BaseMesh * mesh) -{ - nvCheck(mesh != NULL); - - Box bounds; - bounds.clearBounds(); - - const uint vertexCount = mesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - const BaseMesh::Vertex & vertex = mesh->vertexAt(v); - bounds.addPointToBounds( vertex.pos ); - } - - return bounds; -} - -Box MeshBounds::box(const HalfEdge::Mesh * mesh) -{ - nvCheck(mesh != NULL); - - Box bounds; - bounds.clearBounds(); - - const uint vertexCount = mesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - const HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex != NULL); - bounds.addPointToBounds( vertex->pos ); - } - - return bounds; -} - -/*Sphere MeshBounds::sphere(const HalfEdge::Mesh * mesh) -{ - // @@ TODO - return Sphere(); -}*/ diff --git a/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.h b/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.h deleted file mode 100644 index 1cb5b7b905..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/geometry/Bounds.h +++ /dev/null @@ -1,28 +0,0 @@ -// This code is in the public domain -- Ignacio CastaÃąo <castano@gmail.com> - -#pragma once -#ifndef NV_MESH_MESHBOUNDS_H -#define NV_MESH_MESHBOUNDS_H - -#include <nvmath/Sphere.h> -#include <nvmath/Box.h> - -#include <nvmesh/nvmesh.h> - -namespace nv -{ - class BaseMesh; - namespace HalfEdge { class Mesh; } - - // Bounding volumes computation. - namespace MeshBounds - { - Box box(const BaseMesh * mesh); - Box box(const HalfEdge::Mesh * mesh); - - Sphere sphere(const HalfEdge::Mesh * mesh); - } - -} // nv namespace - -#endif // NV_MESH_MESHBOUNDS_H diff --git a/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.cpp b/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.cpp deleted file mode 100644 index e0c271663b..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "Measurements.h" -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" - -using namespace nv; - -float nv::computeSurfaceArea(const HalfEdge::Mesh * mesh) -{ - float area = 0; - - for (HalfEdge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) - { - const HalfEdge::Face * face = it.current(); - area += face->area(); - } - nvDebugCheck(area >= 0); - - return area; -} - -float nv::computeParametricArea(const HalfEdge::Mesh * mesh) -{ - float area = 0; - - for (HalfEdge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) - { - const HalfEdge::Face * face = it.current(); - area += face->parametricArea(); - } - - return area; -} diff --git a/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.h b/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.h deleted file mode 100644 index 0be863b79e..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/geometry/Measurements.h +++ /dev/null @@ -1,18 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MESH_MESHMEASUREMENTS_H -#define NV_MESH_MESHMEASUREMENTS_H - -#include "nvmesh/nvmesh.h" - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - float computeSurfaceArea(const HalfEdge::Mesh * mesh); - float computeParametricArea(const HalfEdge::Mesh * mesh); - -} // nv namespace - -#endif // NV_MESH_MESHMEASUREMENTS_H diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.cpp b/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.cpp deleted file mode 100644 index 671650296c..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "Edge.h" -#include "Vertex.h" - -#include "nvmath/Vector.inl" - -using namespace nv; -using namespace HalfEdge; - -Vector3 Edge::midPoint() const -{ - return (to()->pos + from()->pos) * 0.5f; -} - -float Edge::length() const -{ - return ::length(to()->pos - from()->pos); -} - -// Return angle between this edge and the previous one. -float Edge::angle() const { - Vector3 p = vertex->pos; - Vector3 a = prev->vertex->pos; - Vector3 b = next->vertex->pos; - - Vector3 v0 = a - p; - Vector3 v1 = b - p; - - return acosf(dot(v0, v1) / (nv::length(v0) * nv::length(v1))); -} - -bool Edge::isValid() const -{ - // null face is OK. - if (next == NULL || prev == NULL || pair == NULL || vertex == NULL) return false; - if (next->prev != this) return false; - if (prev->next != this) return false; - if (pair->pair != this) return false; - return true; -} - -/* -Edge * Edge::nextBoundary() { - nvDebugCheck(this->m_pair == NULL); - -} - -Edge * Edge::prevBoundary() { - nvDebugCheck(this->m_pair == NULL); - -} -*/ - - diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.h b/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.h deleted file mode 100644 index 25c47f4860..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Edge.h +++ /dev/null @@ -1,70 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_HALFEDGE_EDGE_H -#define NV_MESH_HALFEDGE_EDGE_H - -#include "nvmath/Vector.h" - -namespace nv -{ - namespace HalfEdge { class Vertex; class Face; class Edge; } - - /// Half edge edge. - class HalfEdge::Edge - { - NV_FORBID_COPY(Edge); - public: - - uint id; - - Edge * next; - Edge * prev; // This is not strictly half-edge, but makes algorithms easier and faster. - Edge * pair; - Vertex * vertex; - Face * face; - - - // Default constructor. - Edge(uint id) : id(id), next(NULL), prev(NULL), pair(NULL), vertex(NULL), face(NULL) - { - } - - - // Vertex queries. - const Vertex * from() const { return vertex; } - Vertex * from() { return vertex; } - - const Vertex * to() const { return pair->vertex; } // This used to be 'next->vertex', but that changed often when the connectivity of the mesh changes. - Vertex * to() { return pair->vertex; } - - - // Edge queries. - void setNext(Edge * e) { next = e; if (e != NULL) e->prev = this; } - void setPrev(Edge * e) { prev = e; if (e != NULL) e->next = this; } - - // @@ Add these helpers: - //Edge * nextBoundary(); - //Edge * prevBoundary(); - - - // @@ It would be more simple to only check m_pair == NULL - // Face queries. - bool isBoundary() const { return !(face && pair->face); } - - // @@ This is not exactly accurate, we should compare the texture coordinates... - bool isSeam() const { return vertex != pair->next->vertex || next->vertex != pair->vertex; } - - bool isValid() const; - - // Geometric queries. - Vector3 midPoint() const; - float length() const; - float angle() const; - - }; - -} // nv namespace - - -#endif // NV_MESH_HALFEDGE_EDGE_H diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Face.cpp b/thirdparty/thekla_atlas/nvmesh/halfedge/Face.cpp deleted file mode 100644 index 9f6987154e..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Face.cpp +++ /dev/null @@ -1,268 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "Face.h" -#include "Vertex.h" - -#include "nvmath/Fitting.h" -#include "nvmath/Plane.h" -#include "nvmath/Vector.inl" - -#include "nvcore/Array.h" - - -using namespace nv; -using namespace HalfEdge; - -/// Get face area. -float Face::area() const -{ - float area = 0; - const Vector3 & v0 = edge->from()->pos; - - for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) - { - const Edge * e = it.current(); - - const Vector3 & v1 = e->vertex->pos; - const Vector3 & v2 = e->next->vertex->pos; - - area += length(cross(v1-v0, v2-v0)); - } - - return area * 0.5f; -} - -float Face::parametricArea() const -{ - float area = 0; - const Vector2 & v0 = edge->from()->tex; - - for (ConstEdgeIterator it(edges(edge->next)); it.current() != edge->prev; it.advance()) - { - const Edge * e = it.current(); - - const Vector2 & v1 = e->vertex->tex; - const Vector2 & v2 = e->next->vertex->tex; - - area += triangleArea(v0, v1, v2); - } - - return area * 0.5f; -} - - -/// Get boundary length. -float Face::boundaryLength() const -{ - float bl = 0; - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - bl += edge->length(); - } - - return bl; -} - - -/// Get face normal. -Vector3 Face::normal() const -{ - Vector3 n(0); - - const Vertex * vertex0 = NULL; - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - nvCheck(edge != NULL); - - if (vertex0 == NULL) - { - vertex0 = edge->vertex; - } - else if (edge->next->vertex != vertex0) - { - const HalfEdge::Vertex * vertex1 = edge->from(); - const HalfEdge::Vertex * vertex2 = edge->to(); - - const Vector3 & p0 = vertex0->pos; - const Vector3 & p1 = vertex1->pos; - const Vector3 & p2 = vertex2->pos; - - Vector3 v10 = p1 - p0; - Vector3 v20 = p2 - p0; - - n += cross(v10, v20); - } - } - - return normalizeSafe(n, Vector3(0, 0, 1), 0.0f); - - - // Get face points eliminating duplicates. - /*Array<Vector3> points(4); - - points.append(m_edge->prev()->from()->pos); - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - nvDebugCheck(edge != NULL); - - const Vector3 & p = edge->from()->pos; - if (points.back() != p) - { - points.append(edge->from()->pos); - } - } - - points.popBack(); - - if (points.count() < 3) - { - // Invalid normal. - return Vector3(0.0f); - } - else - { - // Compute regular normal. - Vector3 normal = normalizeSafe(cross(points[1] - points[0], points[2] - points[0]), Vector3(0.0f), 0.0f); - -#pragma NV_MESSAGE("TODO: make sure these three points are not colinear") - - if (points.count() > 3) - { - // Compute best fitting plane to the points. - Plane plane = Fit::bestPlane(points.count(), points.buffer()); - - // Adjust normal orientation. - if (dot(normal, plane.vector()) > 0) { - normal = plane.vector(); - } - else { - normal = -plane.vector(); - } - } - - nvDebugCheck(isNormalized(normal)); - return normal; - }*/ -} - -Vector3 Face::centroid() const -{ - Vector3 sum(0.0f); - uint count = 0; - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - sum += edge->from()->pos; - count++; - } - - return sum / float(count); -} - - -bool Face::isValid() const -{ - uint count = 0; - - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - if (edge->face != this) return false; - if (!edge->isValid()) return false; - if (!edge->pair->isValid()) return false; - count++; - } - - if (count < 3) return false; - - return true; -} - - -// Determine if this face contains the given edge. -bool Face::contains(const Edge * e) const -{ - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - if(it.current() == e) return true; - } - return false; -} - -// Returns index in this face of the given edge. -uint Face::edgeIndex(const Edge * e) const -{ - int i = 0; - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance(), i++) - { - if(it.current() == e) return i; - } - return NIL; -} - - -Edge * Face::edgeAt(uint idx) -{ - int i = 0; - for(EdgeIterator it(edges()); !it.isDone(); it.advance(), i++) { - if (i == idx) return it.current(); - } - return NULL; -} -const Edge * Face::edgeAt(uint idx) const -{ - int i = 0; - for(ConstEdgeIterator it(edges()); !it.isDone(); it.advance(), i++) { - if (i == idx) return it.current(); - } - return NULL; -} - - -// Count the number of edges in this face. -uint Face::edgeCount() const -{ - uint count = 0; - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { ++count; } - return count; -} - -// Determine if this is a boundary face. -bool Face::isBoundary() const -{ - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - nvDebugCheck(edge->pair != NULL); - - if (edge->pair->face == NULL) { - return true; - } - } - return false; -} - -// Count the number of boundary edges in the face. -uint Face::boundaryCount() const -{ - uint count = 0; - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) - { - const Edge * edge = it.current(); - nvDebugCheck(edge->pair != NULL); - - if (edge->pair->face == NULL) { - count++; - } - } - return count; -} diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Face.h b/thirdparty/thekla_atlas/nvmesh/halfedge/Face.h deleted file mode 100644 index 677f8666f0..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Face.h +++ /dev/null @@ -1,106 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_HALFEDGE_FACE_H -#define NV_MESH_HALFEDGE_FACE_H - -#include <nvmesh/halfedge/Edge.h> - -namespace nv -{ - namespace HalfEdge { class Vertex; class Face; class Edge; } - - /// Face of a half-edge mesh. - class HalfEdge::Face - { - NV_FORBID_COPY(Face); - public: - - uint id; - uint16 group; - uint16 material; - Edge * edge; - - - Face(uint id) : id(id), group(~0), material(~0), edge(NULL) {} - - float area() const; - float parametricArea() const; - float boundaryLength() const; - Vector3 normal() const; - Vector3 centroid() const; - - bool isValid() const; - - bool contains(const Edge * e) const; - uint edgeIndex(const Edge * e) const; - - Edge * edgeAt(uint idx); - const Edge * edgeAt(uint idx) const; - - uint edgeCount() const; - bool isBoundary() const; - uint boundaryCount() const; - - - // The iterator that visits the edges of this face in clockwise order. - class EdgeIterator //: public Iterator<Edge *> - { - public: - EdgeIterator(Edge * e) : m_end(NULL), m_current(e) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - m_current = m_current->next; - } - - virtual bool isDone() const { return m_end == m_current; } - virtual Edge * current() const { return m_current; } - Vertex * vertex() const { return m_current->vertex; } - - private: - Edge * m_end; - Edge * m_current; - }; - - EdgeIterator edges() { return EdgeIterator(edge); } - EdgeIterator edges(Edge * e) - { - nvDebugCheck(contains(e)); - return EdgeIterator(e); - } - - // The iterator that visits the edges of this face in clockwise order. - class ConstEdgeIterator //: public Iterator<const Edge *> - { - public: - ConstEdgeIterator(const Edge * e) : m_end(NULL), m_current(e) { } - ConstEdgeIterator(const EdgeIterator & it) : m_end(NULL), m_current(it.current()) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - m_current = m_current->next; - } - - virtual bool isDone() const { return m_end == m_current; } - virtual const Edge * current() const { return m_current; } - const Vertex * vertex() const { return m_current->vertex; } - - private: - const Edge * m_end; - const Edge * m_current; - }; - - ConstEdgeIterator edges() const { return ConstEdgeIterator(edge); } - ConstEdgeIterator edges(const Edge * e) const - { - nvDebugCheck(contains(e)); - return ConstEdgeIterator(e); - } - }; - -} // nv namespace - -#endif // NV_MESH_HALFEDGE_FACE_H diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.cpp b/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.cpp deleted file mode 100644 index 0012513bce..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.cpp +++ /dev/null @@ -1,1284 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include "nvmesh.h" // pch - -#include "Mesh.h" -#include "Edge.h" -#include "Vertex.h" -#include "Face.h" - -#include "nvmesh/TriMesh.h" -#include "nvmesh/QuadTriMesh.h" -#include "nvmesh/MeshBuilder.h" - -#include "nvmath/Vector.inl" -#include "nvcore/Array.inl" -#include "nvcore/HashMap.inl" - - -using namespace nv; -using namespace HalfEdge; - -Mesh::Mesh() : m_colocalVertexCount(0) -{ - errorCount = 0; -} - -Mesh::Mesh(const Mesh * mesh) -{ - errorCount = 0; - - // Copy mesh vertices. - const uint vertexCount = mesh->vertexCount(); - m_vertexArray.resize(vertexCount); - - for (uint v = 0; v < vertexCount; v++) - { - const Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex->id == v); - - m_vertexArray[v] = new Vertex(v); - m_vertexArray[v]->pos = vertex->pos; - m_vertexArray[v]->nor = vertex->nor; - m_vertexArray[v]->tex = vertex->tex; - } - - m_colocalVertexCount = vertexCount; - - - // Copy mesh faces. - const uint faceCount = mesh->faceCount(); - - Array<uint> indexArray(3); - - for (uint f = 0; f < faceCount; f++) - { - const Face * face = mesh->faceAt(f); - - for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { - const Vertex * vertex = it.current()->from(); - indexArray.append(vertex->id); - } - - addFace(indexArray); - indexArray.clear(); - } -} - -Mesh::~Mesh() -{ - clear(); -} - - -void Mesh::clear() -{ - deleteAll(m_vertexArray); - m_vertexArray.clear(); - - foreach(i, m_edgeMap) - { - delete m_edgeMap[i].value; - } - //deleteAll(m_edgeArray); // edgeArray only contains 1/2 of the edges! - m_edgeArray.clear(); - m_edgeMap.clear(); - - deleteAll(m_faceArray); - m_faceArray.clear(); -} - - -Vertex * Mesh::addVertex(const Vector3 & pos) -{ - nvDebugCheck(isFinite(pos)); - - Vertex * v = new Vertex(m_vertexArray.count()); - v->pos = pos; - m_vertexArray.append(v); - - return v; - -// return addVertex(m_vertexArray.count(), pos); -} - -/*Vertex * Mesh::addVertex(uint id, const Vector3 & pos) -{ - nvDebugCheck(isFinite(pos)); - - Vertex * v = new Vertex(id); - v->pos = pos; - m_vertexArray.append(v); - - return v; -}*/ - -/*void Mesh::addVertices(const Mesh * mesh) -{ -nvCheck(mesh != NULL); - -// Add mesh vertices -for (uint v = 0; v < vertexCount; v++) -{ -const Vertex * vertex = mesh->vertexAt(v); -nvDebugCheck(vertex != NULL); - -Vertex * v = addVertex(vertex->pos()); -nvDebugCheck(v != NULL); - -v->setNor(vertex->nor()); -v->setTex(vertex->tex()); -} -}*/ - - -/// Link colocal vertices based on geometric location only. -void Mesh::linkColocals() -{ - nvDebug("--- Linking colocals:\n"); - - const uint vertexCount = this->vertexCount(); - HashMap<Vector3, Vertex *> vertexMap(vertexCount); - - for (uint v = 0; v < vertexCount; v++) - { - Vertex * vertex = vertexAt(v); - - Vertex * colocal; - if (vertexMap.get(vertex->pos, &colocal)) - { - colocal->linkColocal(vertex); - } - else - { - vertexMap.add(vertex->pos, vertex); - } - } - - m_colocalVertexCount = vertexMap.count(); - - nvDebug("--- %d vertex positions.\n", m_colocalVertexCount); - - // @@ Remove duplicated vertices? or just leave them as colocals? -} - -void Mesh::linkColocalsWithCanonicalMap(const Array<uint> & canonicalMap) -{ - nvDebug("--- Linking colocals:\n"); - - uint vertexMapSize = 0; - foreach(i, canonicalMap) { - vertexMapSize = max(vertexMapSize, canonicalMap[i] + 1); - } - - Array<Vertex *> vertexMap; - vertexMap.resize(vertexMapSize, NULL); - - m_colocalVertexCount = 0; - - const uint vertexCount = this->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - Vertex * vertex = vertexAt(v); - - Vertex * colocal = vertexMap[canonicalMap[v]]; - if (colocal != NULL) - { - nvDebugCheck(vertex->pos == colocal->pos); - colocal->linkColocal(vertex); - } - else - { - vertexMap[canonicalMap[v]] = vertex; - m_colocalVertexCount++; - } - } - - nvDebug("--- %d vertex positions.\n", m_colocalVertexCount); -} - - -Face * Mesh::addFace() -{ - Face * f = new Face(m_faceArray.count()); - m_faceArray.append(f); - return f; -} - -Face * Mesh::addFace(uint v0, uint v1, uint v2) -{ - Array<uint> indexArray(3); - indexArray << v0 << v1 << v2; - return addFace(indexArray, 0, 3); -} - -Face * Mesh::addFace(uint v0, uint v1, uint v2, uint v3) -{ - Array<uint> indexArray(4); - indexArray << v0 << v1 << v2 << v3; - return addFace(indexArray, 0, 4); -} - -Face * Mesh::addFace(const Array<uint> & indexArray) -{ - return addFace(indexArray, 0, indexArray.count()); -} - - -Face * Mesh::addFace(const Array<uint> & indexArray, uint first, uint num) -{ - nvDebugCheck(first < indexArray.count()); - nvDebugCheck(num <= indexArray.count()-first); - nvDebugCheck(num > 2); - - if (!canAddFace(indexArray, first, num)) { - errorCount++; - return NULL; - } - - Face * f = new Face(m_faceArray.count()); - - Edge * firstEdge = NULL; - Edge * last = NULL; - Edge * current = NULL; - - for(uint i = 0; i < num-1; i++) - { - current = addEdge(indexArray[first+i], indexArray[first+i+1]); - nvCheck(current != NULL && current->face == NULL); - - current->face = f; - - if (last != NULL) last->setNext(current); - else firstEdge = current; - - last = current; - } - - current = addEdge(indexArray[first+num-1], indexArray[first]); - nvCheck(current != NULL && current->face == NULL); - - current->face = f; - - last->setNext(current); - current->setNext(firstEdge); - - f->edge = firstEdge; - m_faceArray.append(f); - - return f; -} - -/*void Mesh::addFaces(const Mesh * mesh) -{ -nvCheck(mesh != NULL); - -Array indexArray; -// Add faces - -}*/ - - -// Return true if the face can be added to the manifold mesh. -bool Mesh::canAddFace(const Array<uint> & indexArray, uint first, uint num) const -{ - for (uint j = num - 1, i = 0; i < num; j = i++) { - if (!canAddEdge(indexArray[first+j], indexArray[first+i])) { - errorIndex0 = indexArray[first+j]; - errorIndex1 = indexArray[first+i]; - return false; - } - } - - // We also have to make sure the face does not have any duplicate edge! - for (uint i = 0; i < num; i++) { - - int i0 = indexArray[first + i + 0]; - int i1 = indexArray[first + (i + 1)%num]; - - for (uint j = i + 1; j < num; j++) { - int j0 = indexArray[first + j + 0]; - int j1 = indexArray[first + (j + 1)%num]; - - if (i0 == j0 && i1 == j1) { - return false; - } - } - } - - return true; -} - -// Return true if the edge doesn't exist or doesn't have any adjacent face. -bool Mesh::canAddEdge(uint i, uint j) const -{ - if (i == j) { - // Skip degenerate edges. - return false; - } - - // Same check, but taking into account colocal vertices. - const Vertex * v0 = vertexAt(i); - const Vertex * v1 = vertexAt(j); - - for(Vertex::ConstVertexIterator it(v0->colocals()); !it.isDone(); it.advance()) - { - if (it.current() == v1) - { - // Skip degenerate edges. - return false; - } - } - - // Make sure edge has not been added yet. - Edge * edge = findEdge(i, j); - - return edge == NULL || edge->face == NULL; // We ignore edges that don't have an adjacent face yet, since this face could become the edge's face. -} - -Edge * Mesh::addEdge(uint i, uint j) -{ - nvCheck(i != j); - - Edge * edge = findEdge(i, j); - - if (edge != NULL) { - // Edge may already exist, but its face must not be set. - nvDebugCheck(edge->face == NULL); - - // Nothing else to do! - - } - else { - // Add new edge. - - // Lookup pair. - Edge * pair = findEdge(j, i); - - if (pair != NULL) - { - // Create edge with same id. - edge = new Edge(pair->id + 1); - - // Link edge pairs. - edge->pair = pair; - pair->pair = edge; - - // @@ I'm not sure this is necessary! - pair->vertex->setEdge(pair); - } - else - { - // Create edge. - edge = new Edge(2*m_edgeArray.count()); - - // Add only unpaired edges. - m_edgeArray.append(edge); - } - - edge->vertex = m_vertexArray[i]; - m_edgeMap.add(Key(i,j), edge); - } - - // Face and Next are set by addFace. - - return edge; -} - - -/// Find edge, test all colocals. -Edge * Mesh::findEdge(uint i, uint j) const -{ - Edge * edge = NULL; - - const Vertex * v0 = vertexAt(i); - const Vertex * v1 = vertexAt(j); - - // Test all colocal pairs. - for(Vertex::ConstVertexIterator it0(v0->colocals()); !it0.isDone(); it0.advance()) - { - for(Vertex::ConstVertexIterator it1(v1->colocals()); !it1.isDone(); it1.advance()) - { - Key key(it0.current()->id, it1.current()->id); - - if (edge == NULL) { - m_edgeMap.get(key, &edge); -#if !defined(_DEBUG) - if (edge != NULL) return edge; -#endif - } - else { - // Make sure that only one edge is found. - nvDebugCheck(!m_edgeMap.get(key)); - } - } - } - - return edge; -} - -/// Link boundary edges once the mesh has been created. -void Mesh::linkBoundary() -{ - nvDebug("--- Linking boundaries:\n"); - - int num = 0; - - // Create boundary edges. - uint edgeCount = this->edgeCount(); - for(uint e = 0; e < edgeCount; e++) - { - Edge * edge = edgeAt(e); - if (edge != NULL && edge->pair == NULL) { - Edge * pair = new Edge(edge->id + 1); - - uint i = edge->from()->id; - uint j = edge->next->from()->id; - - Key key(j,i); - nvCheck(!m_edgeMap.get(key)); - - pair->vertex = m_vertexArray[j]; - m_edgeMap.add(key, pair); - - edge->pair = pair; - pair->pair = edge; - - num++; - } - } - - // Link boundary edges. - for (uint e = 0; e < edgeCount; e++) { - Edge * edge = edgeAt(e); - if (edge != NULL && edge->pair->face == NULL) { - linkBoundaryEdge(edge->pair); - } - } - - nvDebug("--- %d boundary edges.\n", num); -} - -/// Link this boundary edge. -void Mesh::linkBoundaryEdge(Edge * edge) -{ - nvCheck(edge->face == NULL); - - // Make sure next pointer has not been set. @@ We want to be able to relink boundary edges after mesh changes. - //nvCheck(edge->next() == NULL); - - Edge * next = edge; - while(next->pair->face != NULL) { - // Get pair prev - Edge * e = next->pair->next; - while (e->next != next->pair) { - e = e->next; - } - next = e; - } - edge->setNext(next->pair); - - // Adjust vertex edge, so that it's the boundary edge. (required for isBoundary()) - if (edge->vertex->edge != edge) - { - // Multiple boundaries in the same edge. - //nvCheck( edge->vertex()->edge() == NULL || edge->vertex()->edge()->face() != NULL ); - edge->vertex->edge = edge; - } -} - - -/// Convert to tri mesh. -TriMesh * Mesh::toTriMesh() const -{ - uint triangleCount = 0; - - // Count triangle faces. - const uint faceCount = this->faceCount(); - for(uint f = 0; f < faceCount; f++) - { - const Face * face = faceAt(f); - triangleCount += face->edgeCount() - 2; - } - - TriMesh * triMesh = new TriMesh(triangleCount, vertexCount()); - - // Add vertices. - Array<TriMesh::Vertex> & vertices = triMesh->vertices(); - - const uint vertexCount = this->vertexCount(); - for(uint v = 0; v < vertexCount; v++) - { - const Vertex * vertex = vertexAt(v); - - TriMesh::Vertex triVertex; - triVertex.id = vertices.count(); - triVertex.pos = vertex->pos; - triVertex.nor = vertex->nor; - triVertex.tex = vertex->tex; - - vertices.append(triVertex); - } - - // Add triangles. - Array<TriMesh::Face> & triangles = triMesh->faces(); - - for(uint f = 0; f < faceCount; f++) - { - const Face * face = faceAt(f); - - // @@ Triangulate arbitrary polygons correctly. - const uint v0 = face->edge->vertex->id; - uint v1 = face->edge->next->vertex->id; - - for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - uint v2 = it.current()->vertex->id; - - // Skip the first two vertices. - if (v2 == v0 || v2 == v1) continue; - - TriMesh::Face triangle; - triangle.id = triangles.count(); - triangle.v[0] = v0; - triangle.v[1] = v1; - triangle.v[2] = v2; - - v1 = v2; - - triangles.append(triangle); - } - } - - return triMesh; -} - -QuadTriMesh * Mesh::toQuadTriMesh() const -{ - MeshBuilder builder; - - const uint vertexCount = this->vertexCount(); - builder.hintVertexCount(vertexCount); - - for(uint v = 0; v < vertexCount; v++) - { - const Vertex * vertex = vertexAt(v); - - builder.addPosition(vertex->pos); - builder.addNormal(vertex->nor); - builder.addTexCoord(vertex->tex); - } - - const uint faceCount = this->faceCount(); - builder.hintTriangleCount(faceCount); - - for(uint f = 0; f < faceCount; f++) - { - const Face * face = faceAt(f); - - builder.beginPolygon(); - - for(Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - uint v = it.current()->vertex->id; - builder.addVertex(v, v, v); - } - - builder.endPolygon(); - } - - builder.done(); - - return builder.buildQuadTriMesh(); -} - - -// Triangulate in place. -void Mesh::triangulate() { - - bool all_triangles = true; - - const uint faceCount = m_faceArray.count(); - for (uint f = 0; f < faceCount; f++) { - Face * face = m_faceArray[f]; - if (face->edgeCount() != 3) { - all_triangles = false; - break; - } - } - - if (all_triangles) { - return; - } - - - // Do not touch vertices, but rebuild edges and faces. - Array<Edge *> edgeArray; - Array<Face *> faceArray; - - swap(edgeArray, m_edgeArray); - swap(faceArray, m_faceArray); - m_edgeMap.clear(); - - for (uint f = 0; f < faceCount; f++) { - Face * face = faceArray[f]; - - // Trivial fan-like triangulation. - const uint v0 = face->edge->vertex->id; - uint v2, v1 = -1; - - for (Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance()) { - Edge * edge = it.current(); - v2 = edge->to()->id; - if (v2 == v0) break; - if (v1 != -1) addFace(v0, v1, v2); - v1 = v2; - } - } - - nvDebugCheck(m_faceArray.count() > faceCount); // triangle count > face count - - linkBoundary(); - - deleteAll(edgeArray); - deleteAll(faceArray); -} - - -/* -Fixing T-junctions. - -- Find T-junctions. Find vertices that are on an edge. - - This test is approximate. - - Insert edges on a spatial index to speedup queries. - - Consider only open edges, that is edges that have no pairs. - - Consider only vertices on boundaries. -- Close T-junction. - - Split edge. - -*/ -bool Mesh::splitBoundaryEdges() { - - Array<Vertex *> boundaryVertices; - - foreach(i, m_vertexArray) { - Vertex * v = m_vertexArray[i]; - if (v->isBoundary()) { - boundaryVertices.append(v); - } - } - - nvDebug("Fixing T-junctions:\n"); - - int splitCount = 0; - - foreach(v, boundaryVertices) { - Vertex * vertex = boundaryVertices[v]; - - Vector3 x0 = vertex->pos; - - // Find edges that this vertex overlaps with. - foreach(e, m_edgeArray) { - //for (uint e = 0; e < m_edgeArray.count(); e++) { - Edge * edge = m_edgeArray[e]; - if (edge != NULL && edge->isBoundary()) { - - if (edge->from() == vertex || edge->to() == vertex) { - continue; - } - - Vector3 x1 = edge->from()->pos; - Vector3 x2 = edge->to()->pos; - - Vector3 v01 = x0 - x1; - Vector3 v21 = x2 - x1; - - float l = length(v21); - float d = length(cross(v01, v21)) / l; - - if (isZero(d)) { - float t = dot(v01, v21) / (l * l); - - // @@ Snap x0 to x1 or x2, if too close? No, do vertex snapping elsewhere. - /*if (equal(t, 0.0f, 0.01f)) { - //vertex->setPos(x1); - } - else if (equal(t, 1.0f, 0.01f)) { - //vertex->setPos(x2); - } - else*/ - if (t > 0.0f + NV_EPSILON && t < 1.0f - NV_EPSILON) { - nvDebugCheck(equal(lerp(x1, x2, t), x0)); - - Vertex * splitVertex = splitBoundaryEdge(edge, t, x0); - vertex->linkColocal(splitVertex); // @@ Should we do this here? - splitCount++; - } - } - } - } - } - - nvDebug(" - %d edges split.\n", splitCount); - - nvDebugCheck(isValid()); - - return splitCount != 0; -} - - -// For this to be effective, we have to fix the boundary junctions first. -Edge * Mesh::sewBoundary(Edge * startEdge) { - nvDebugCheck(startEdge->face == NULL); - - // @@ We may want to be more conservative linking colocals in order to preserve the input topology. One way of doing that is by linking colocals only - // if the vertices next to them are linked as well. That is, by sewing boundaries after detecting them. If any pair of consecutive edges have their first - // and last vertex in the same position, then it can be linked. - - Edge * lastBoundarySeen = startEdge; - - nvDebug("Sewing Boundary:\n"); - - int count = 0; - int sewnCount = 0; - - Edge * edge = startEdge; - do { - nvDebugCheck(edge->face == NULL); - - Edge * edge_a = edge; - Edge * edge_b = edge->prev; - - Edge * pair_a = edge_a->pair; - Edge * pair_b = edge_b->pair; - - Vertex * v0a = edge_a->to(); - Vertex * v0b = edge_b->from(); - Vertex * v1a = edge_a->from(); - Vertex * v1b = edge_b->to(); - - nvDebugCheck(v1a->isColocal(v1b)); - - /* - v0b + _+ v0a - \ / - b \ / a - \|/ - v1b + v1a - */ - - // @@ This should not happen while sewing, but it may be produced somewhere else. - nvDebugCheck(edge_a != edge_b); - - if (v0a->pos == v0b->pos) { - - // Link vertices. - v0a->linkColocal(v0b); - - // Remove edges to be collapsed. - disconnect(edge_a); - disconnect(edge_b); - disconnect(pair_a); - disconnect(pair_b); - - // Link new boundary edges. - Edge * prevBoundary = edge_b->prev; - Edge * nextBoundary = edge_a->next; - if (nextBoundary != NULL) { - nvDebugCheck(nextBoundary->face == NULL); - nvDebugCheck(prevBoundary->face == NULL); - nextBoundary->setPrev(prevBoundary); - - // Make sure boundary vertex points to boundary edge. - v0a->setEdge(nextBoundary); // This updates all colocals. - } - lastBoundarySeen = prevBoundary; - - // Creat new edge. - Edge * newEdge_a = addEdge(v0a->id, v1a->id); // pair_a->from()->id, pair_a->to()->id - Edge * newEdge_b = addEdge(v1b->id, v0b->id); - - newEdge_a->pair = newEdge_b; - newEdge_b->pair = newEdge_a; - - newEdge_a->face = pair_a->face; - newEdge_b->face = pair_b->face; - - newEdge_a->setNext(pair_a->next); - newEdge_a->setPrev(pair_a->prev); - - newEdge_b->setNext(pair_b->next); - newEdge_b->setPrev(pair_b->prev); - - delete edge_a; - delete edge_b; - delete pair_a; - delete pair_b; - - edge = nextBoundary; // If nextBoundary is NULL we have closed the loop. - sewnCount++; - } - else { - edge = edge->next; - } - - count++; - } while(edge != NULL && edge != lastBoundarySeen); - - nvDebug(" - Sewn %d out of %d.\n", sewnCount, count); - - if (lastBoundarySeen != NULL) { - nvDebugCheck(lastBoundarySeen->face == NULL); - } - - return lastBoundarySeen; -} - - -// @@ We must always disconnect edge pairs simultaneously. -void Mesh::disconnect(Edge * edge) { - nvDebugCheck(edge != NULL); - - // Remove from edge list. - if ((edge->id & 1) == 0) { - nvDebugCheck(m_edgeArray[edge->id / 2] == edge); - m_edgeArray[edge->id / 2] = NULL; - } - - // Remove edge from map. @@ Store map key inside edge? - nvDebugCheck(edge->from() != NULL && edge->to() != NULL); - bool removed = m_edgeMap.remove(Key(edge->from()->id, edge->to()->id)); - nvDebugCheck(removed == true); - - // Disconnect from vertex. - if (edge->vertex != NULL) { - if (edge->vertex->edge == edge) { - if (edge->prev && edge->prev->pair) { - edge->vertex->edge = edge->prev->pair; - } - else if (edge->pair && edge->pair->next) { - edge->vertex->edge = edge->pair->next; - } - else { - edge->vertex->edge = NULL; - // @@ Remove disconnected vertex? - } - } - //edge->setVertex(NULL); - } - - // Disconnect from face. - if (edge->face != NULL) { - if (edge->face->edge == edge) { - if (edge->next != NULL && edge->next != edge) { - edge->face->edge = edge->next; - } - else if (edge->prev != NULL && edge->prev != edge) { - edge->face->edge = edge->prev; - } - else { - edge->face->edge = NULL; - // @@ Remove disconnected face? - } - } - //edge->setFace(NULL); - } - - // @@ Hack, we don't disconnect from pair, because pair needs us to remove itself from the map. - // Disconect from pair. - /*if (edge->pair != NULL) { - if (edge->pair->pair == edge) { - edge->pair->setPair(NULL); - } - //edge->setPair(NULL); - }*/ - - // Disconnect from previous. - if (edge->prev) { - if (edge->prev->next == edge) { - edge->prev->setNext(NULL); - } - //edge->setPrev(NULL); - } - - // Disconnect from next. - if (edge->next) { - if (edge->next->prev == edge) { - edge->next->setPrev(NULL); - } - //edge->setNext(NULL); - } -} - - -void Mesh::remove(Edge * edge) { - nvDebugCheck(edge != NULL); - - disconnect(edge); - - delete edge; -} - -void Mesh::remove(Vertex * vertex) { - nvDebugCheck(vertex != NULL); - - // Remove from vertex list. - m_vertexArray[vertex->id] = NULL; - - // Disconnect from colocals. - vertex->unlinkColocal(); - - // Disconnect from edges. - if (vertex->edge != NULL) { - // @@ Removing a connected vertex is asking for trouble... - if (vertex->edge->vertex == vertex) { - // @@ Connect edge to a colocal? - vertex->edge->vertex = NULL; - } - - vertex->setEdge(NULL); - } - - delete vertex; -} - -void Mesh::remove(Face * face) { - nvDebugCheck(face != NULL); - - // Remove from face list. - m_faceArray[face->id] = NULL; - - // Disconnect from edges. - if (face->edge != NULL) { - nvDebugCheck(face->edge->face == face); - - face->edge->face = NULL; - - face->edge = NULL; - } - - delete face; -} - - -void Mesh::compactEdges() { - const uint edgeCount = m_edgeArray.count(); - - uint c = 0; - for (uint i = 0; i < edgeCount; i++) { - if (m_edgeArray[i] != NULL) { - if (i != c) { - m_edgeArray[c] = m_edgeArray[i]; - m_edgeArray[c]->id = 2 * c; - if (m_edgeArray[c]->pair != NULL) { - m_edgeArray[c]->pair->id = 2 * c + 1; - } - } - c++; - } - } - - m_edgeArray.resize(c); -} - - -void Mesh::compactVertices() { - const uint vertexCount = m_vertexArray.count(); - - uint c = 0; - for (uint i = 0; i < vertexCount; i++) { - if (m_vertexArray[i] != NULL) { - if (i != c) { - m_vertexArray[c] = m_vertexArray[i]; - m_vertexArray[c]->id = c; - } - c++; - } - } - - m_vertexArray.resize(c); - - // @@ Generate xref array for external attributes. -} - - -void Mesh::compactFaces() { - const uint faceCount = m_faceArray.count(); - - uint c = 0; - for (uint i = 0; i < faceCount; i++) { - if (m_faceArray[i] != NULL) { - if (i != c) { - m_faceArray[c] = m_faceArray[i]; - m_faceArray[c]->id = c; - } - c++; - } - } - - m_faceArray.resize(c); -} - - -Vertex * Mesh::splitBoundaryEdge(Edge * edge, float t, const Vector3 & pos) { - - /* - We want to go from this configuration: - - + + - | ^ - edge |<->| pair - v | - + + - - To this one: - - + + - | ^ - e0 |<->| p0 - v | - vertex + + - | ^ - e1 |<->| p1 - v | - + + - - */ - - - Edge * pair = edge->pair; - - // Make sure boundaries are linked. - nvDebugCheck(pair != NULL); - - // Make sure edge is a boundary edge. - nvDebugCheck(pair->face == NULL); - - // Add new vertex. - Vertex * vertex = addVertex(pos); - vertex->nor = lerp(edge->from()->nor, edge->to()->nor, t); - vertex->tex = lerp(edge->from()->tex, edge->to()->tex, t); - vertex->col = lerp(edge->from()->col, edge->to()->col, t); - - disconnect(edge); - disconnect(pair); - - // Add edges. - Edge * e0 = addEdge(edge->from()->id, vertex->id); - Edge * p0 = addEdge(vertex->id, pair->to()->id); - - Edge * e1 = addEdge(vertex->id, edge->to()->id); - Edge * p1 = addEdge(pair->from()->id, vertex->id); - - // Link edges. - e0->setNext(e1); - p1->setNext(p0); - - e0->setPrev(edge->prev); - e1->setNext(edge->next); - - p1->setPrev(pair->prev); - p0->setNext(pair->next); - - nvDebugCheck(e0->next == e1); - nvDebugCheck(e1->prev == e0); - - nvDebugCheck(p1->next == p0); - nvDebugCheck(p0->prev == p1); - - nvDebugCheck(p0->pair == e0); - nvDebugCheck(e0->pair == p0); - - nvDebugCheck(p1->pair == e1); - nvDebugCheck(e1->pair == p1); - - // Link faces. - e0->face = edge->face; - e1->face = edge->face; - - // Link vertices. - edge->from()->setEdge(e0); - vertex->setEdge(e1); - - delete edge; - delete pair; - - return vertex; -} - -#if 0 -// Without introducing new vertices. -void Mesh::splitBoundaryEdge(Edge * edge, Vertex * vertex) { - - /* - We want to go from this configuration: - - | | pn - + + - | ^ - | | - edge |<->| pair - | | - v | - + + - | | pp - - To this one: - \ / - \ / - + + - | ^ - e0 |<->| p0 - v | - vertex + + - | ^ - e1 |<->| p1 - v | - + + - / \ - / \ - */ - - - Edge * pair = edge->pair; - Edge * pn = pair->next(); - Edge * pp = pair->prev(); - - // Make sure boundaries are linked. - nvDebugCheck(pair != NULL); - - // Make sure edge is a boundary edge. - nvDebugCheck(pair->face() == NULL); - - nvDebugCheck(edge->isValid()); - nvDebugCheck(pair->isValid()); - - disconnect(edge); - disconnect(pair); - - // Add edges. - Edge * e0 = addEdge(edge->from()->id(), vertex->id()); - Edge * e1 = addEdge(vertex->id(), edge->to()->id()); - - // Link faces. - e0->setFace(edge->face()); - e1->setFace(edge->face()); - - // Link pairs. - Edge * p0 = findEdge(vertex->id(), pair->to()->id()); - if (p0 == NULL) { - p0 = addEdge(vertex->id(), pair->to()->id()); - pn->setPrev(p0); - } - else { - nvDebugCheck(p0->face() != NULL); - if (e0->prev() != NULL) { - pn->setPrev(e0->prev()); - } - else { - nvDebugCheck(pn == e0); - } - } - - Edge * p1 = findEdge(pair->from()->id(), vertex->id()); - if (p1 == NULL) { - p1 = addEdge(pair->from()->id(), vertex->id()); - pp->setNext(p1); - } - else { - nvDebugCheck(p1->face() != NULL); - if (e1->next() != NULL) { - pp->setPrev(e1->next()); - } - else { - nvDebugCheck(pp == e1); - } - } - - // Link edges. - e0->setNext(e1); // e1->setPrev(e0) - - if (p0->face() == p1->face()) { // can be null - p1->setNext(p0); // p0->setPrev(p1) - } - else { - //if (p1->face() == NULL) p1->setNext( - } - - - e0->setPrev(edge->prev()); - e1->setNext(edge->next()); - - nvDebugCheck(e0->pair == p0); - nvDebugCheck(e1->pair == p1); - nvDebugCheck(p0->pair == e0); - nvDebugCheck(p1->pair == e1); - - nvDebugCheck(e0->isValid()); - nvDebugCheck(e1->isValid()); - nvDebugCheck(pp->isValid()); - nvDebugCheck(pn->isValid()); - - nvDebugCheck(e0->pair->isValid()); - nvDebugCheck(e1->pair->isValid()); - nvDebugCheck(pp->pair->isValid()); - nvDebugCheck(pn->pair->isValid()); - - nvDebugCheck(edge->face->isValid()); - - if (pn->pair->face != NULL) { - nvDebugCheck(pn->pair->face->isValid()); - } - - if (pp->pair->face() != NULL) { - nvDebugCheck(pn->pair->face->isValid()); - } - - if (p0->face != NULL) { - nvDebugCheck(p0->face->isValid()); - } - - if (p1->face() != NULL) { - nvDebugCheck(p1->face()->isValid()); - } - - nvDebugCheck(isValid()); // Only for extreme debugging. - - // Link vertices. - edge->from()->setEdge(e0); - vertex->setEdge(p0); - - delete edge; - delete pair; -} -#endif - -bool Mesh::isValid() const -{ - // Make sure all edges are valid. - const uint edgeCount = m_edgeArray.count(); - for (uint e = 0; e < edgeCount; e++) { - Edge * edge = m_edgeArray[e]; - if (edge != NULL) { - if (edge->id != 2*e) { - return false; - } - if (!edge->isValid()) { - return false; - } - - if (edge->pair->id != 2*e+1) { - return false; - } - if (!edge->pair->isValid()) { - return false; - } - } - } - - // @@ Make sure all faces are valid. - - // @@ Make sure all vertices are valid. - - return true; -} diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.h b/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.h deleted file mode 100644 index c202c2ef9a..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Mesh.h +++ /dev/null @@ -1,274 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_HALFEDGE_MESH_H -#define NV_MESH_HALFEDGE_MESH_H - -#include "nvmesh/nvmesh.h" -#include "nvcore/Array.h" -#include "nvcore/HashMap.h" - -/* -If I were to redo this again, there are a number of things that I would do differently. -- Edge map is only useful when importing a mesh to guarantee the result is two-manifold. However, when manipulating the mesh - it's a pain to maintain the map up to date. -- Edge array only points to the even vertices. There's no good reason for that. The map becomes required to traverse all edges - or you have to make sure edges are properly paired. -- Linked boundaries. It's cleaner to assume a NULL pair means a boundary edge. Makes easier to seal boundaries. The only reason - why we link boundaries is to simplify traversal, but that could be done with two helper functions (nextBoundary, prevBoundary). -- Minimize the amount of state that needs to be set in a certain way: - - boundary vertices point to boundary edge. -- Remove parenthesis! Make some members public. -- Remove member functions with side effects: - - e->setNext(n) modifies e->next and n->prev, instead use "link(e, n)", or "e->next = n, n->prev = e" -*/ - - -namespace nv -{ - class Vector3; - class TriMesh; - class QuadTriMesh; - //template <typename T> struct Hash<Mesh::Key>; - - namespace HalfEdge - { - class Edge; - class Face; - class Vertex; - - /// Simple half edge mesh designed for dynamic mesh manipulation. - class Mesh - { - public: - - Mesh(); - Mesh(const Mesh * mesh); - ~Mesh(); - - void clear(); - - Vertex * addVertex(const Vector3 & pos); - //Vertex * addVertex(uint id, const Vector3 & pos); - //void addVertices(const Mesh * mesh); - - void linkColocals(); - void linkColocalsWithCanonicalMap(const Array<uint> & canonicalMap); - void resetColocalLinks(); - - Face * addFace(); - Face * addFace(uint v0, uint v1, uint v2); - Face * addFace(uint v0, uint v1, uint v2, uint v3); - Face * addFace(const Array<uint> & indexArray); - Face * addFace(const Array<uint> & indexArray, uint first, uint num); - //void addFaces(const Mesh * mesh); - - // These functions disconnect the given element from the mesh and delete it. - void disconnect(Edge * edge); - void disconnectPair(Edge * edge); - void disconnect(Vertex * vertex); - void disconnect(Face * face); - - void remove(Edge * edge); - void remove(Vertex * vertex); - void remove(Face * face); - - // Remove holes from arrays and reassign indices. - void compactEdges(); - void compactVertices(); - void compactFaces(); - - void triangulate(); - - void linkBoundary(); - - bool splitBoundaryEdges(); // Returns true if any split was made. - - // Sew the boundary that starts at the given edge, returns one edge that still belongs to boundary, or NULL if boundary closed. - HalfEdge::Edge * sewBoundary(Edge * startEdge); - - - // Vertices - uint vertexCount() const { return m_vertexArray.count(); } - const Vertex * vertexAt(int i) const { return m_vertexArray[i]; } - Vertex * vertexAt(int i) { return m_vertexArray[i]; } - - uint colocalVertexCount() const { return m_colocalVertexCount; } - - // Faces - uint faceCount() const { return m_faceArray.count(); } - const Face * faceAt(int i) const { return m_faceArray[i]; } - Face * faceAt(int i) { return m_faceArray[i]; } - - // Edges - uint edgeCount() const { return m_edgeArray.count(); } - const Edge * edgeAt(int i) const { return m_edgeArray[i]; } - Edge * edgeAt(int i) { return m_edgeArray[i]; } - - class ConstVertexIterator; - - class VertexIterator - { - friend class ConstVertexIterator; - public: - VertexIterator(Mesh * mesh) : m_mesh(mesh), m_current(0) { } - - virtual void advance() { m_current++; } - virtual bool isDone() const { return m_current == m_mesh->vertexCount(); } - virtual Vertex * current() const { return m_mesh->vertexAt(m_current); } - - private: - HalfEdge::Mesh * m_mesh; - uint m_current; - }; - VertexIterator vertices() { return VertexIterator(this); } - - class ConstVertexIterator - { - public: - ConstVertexIterator(const Mesh * mesh) : m_mesh(mesh), m_current(0) { } - ConstVertexIterator(class VertexIterator & it) : m_mesh(it.m_mesh), m_current(it.m_current) { } - - virtual void advance() { m_current++; } - virtual bool isDone() const { return m_current == m_mesh->vertexCount(); } - virtual const Vertex * current() const { return m_mesh->vertexAt(m_current); } - - private: - const HalfEdge::Mesh * m_mesh; - uint m_current; - }; - ConstVertexIterator vertices() const { return ConstVertexIterator(this); } - - class ConstFaceIterator; - - class FaceIterator - { - friend class ConstFaceIterator; - public: - FaceIterator(Mesh * mesh) : m_mesh(mesh), m_current(0) { } - - virtual void advance() { m_current++; } - virtual bool isDone() const { return m_current == m_mesh->faceCount(); } - virtual Face * current() const { return m_mesh->faceAt(m_current); } - - private: - HalfEdge::Mesh * m_mesh; - uint m_current; - }; - FaceIterator faces() { return FaceIterator(this); } - - class ConstFaceIterator - { - public: - ConstFaceIterator(const Mesh * mesh) : m_mesh(mesh), m_current(0) { } - ConstFaceIterator(const FaceIterator & it) : m_mesh(it.m_mesh), m_current(it.m_current) { } - - virtual void advance() { m_current++; } - virtual bool isDone() const { return m_current == m_mesh->faceCount(); } - virtual const Face * current() const { return m_mesh->faceAt(m_current); } - - private: - const HalfEdge::Mesh * m_mesh; - uint m_current; - }; - ConstFaceIterator faces() const { return ConstFaceIterator(this); } - - class ConstEdgeIterator; - - class EdgeIterator - { - friend class ConstEdgeIterator; - public: - EdgeIterator(Mesh * mesh) : m_mesh(mesh), m_current(0) { } - - virtual void advance() { m_current++; } - virtual bool isDone() const { return m_current == m_mesh->edgeCount(); } - virtual Edge * current() const { return m_mesh->edgeAt(m_current); } - - private: - HalfEdge::Mesh * m_mesh; - uint m_current; - }; - EdgeIterator edges() { return EdgeIterator(this); } - - class ConstEdgeIterator - { - public: - ConstEdgeIterator(const Mesh * mesh) : m_mesh(mesh), m_current(0) { } - ConstEdgeIterator(const EdgeIterator & it) : m_mesh(it.m_mesh), m_current(it.m_current) { } - - virtual void advance() { m_current++; } - virtual bool isDone() const { return m_current == m_mesh->edgeCount(); } - virtual const Edge * current() const { return m_mesh->edgeAt(m_current); } - - private: - const HalfEdge::Mesh * m_mesh; - uint m_current; - }; - ConstEdgeIterator edges() const { return ConstEdgeIterator(this); } - - // @@ Add half-edge iterator. - - - - // Convert to tri mesh. - TriMesh * toTriMesh() const; - QuadTriMesh * toQuadTriMesh() const; - - bool isValid() const; - - public: - - // Error status: - mutable uint errorCount; - mutable uint errorIndex0; - mutable uint errorIndex1; - - private: - - bool canAddFace(const Array<uint> & indexArray, uint first, uint num) const; - bool canAddEdge(uint i, uint j) const; - Edge * addEdge(uint i, uint j); - - Edge * findEdge(uint i, uint j) const; - - void linkBoundaryEdge(Edge * edge); - Vertex * splitBoundaryEdge(Edge * edge, float t, const Vector3 & pos); - void splitBoundaryEdge(Edge * edge, Vertex * vertex); - - private: - - Array<Vertex *> m_vertexArray; - Array<Edge *> m_edgeArray; - Array<Face *> m_faceArray; - - struct Key { - Key() {} - Key(const Key & k) : p0(k.p0), p1(k.p1) {} - Key(uint v0, uint v1) : p0(v0), p1(v1) {} - void operator=(const Key & k) { p0 = k.p0; p1 = k.p1; } - bool operator==(const Key & k) const { return p0 == k.p0 && p1 == k.p1; } - - uint p0; - uint p1; - }; - friend struct Hash<Mesh::Key>; - - HashMap<Key, Edge *> m_edgeMap; - - uint m_colocalVertexCount; - - }; - /* - // This is a much better hash than the default and greatly improves performance! - template <> struct hash<Mesh::Key> - { - uint operator()(const Mesh::Key & k) const { return k.p0 + k.p1; } - }; - */ - - } // HalfEdge namespace - -} // nv namespace - -#endif // NV_MESH_HALFEDGE_MESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.cpp b/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.cpp deleted file mode 100644 index 66dad69f8a..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.cpp +++ /dev/null @@ -1,94 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "Vertex.h" - -#include "nvmath/Vector.inl" - -using namespace nv; -using namespace HalfEdge; - - -// Set first edge of all colocals. -void Vertex::setEdge(Edge * e) -{ - for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { - it.current()->edge = e; - } -} - -// Update position of all colocals. -void Vertex::setPos(const Vector3 & p) -{ - for (VertexIterator it(colocals()); !it.isDone(); it.advance()) { - it.current()->pos = p; - } -} - - -uint HalfEdge::Vertex::colocalCount() const -{ - uint count = 0; - for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) { ++count; } - return count; -} - -uint HalfEdge::Vertex::valence() const -{ - uint count = 0; - for (ConstEdgeIterator it(edges()); !it.isDone(); it.advance()) { ++count; } - return count; -} - -const HalfEdge::Vertex * HalfEdge::Vertex::firstColocal() const -{ - uint firstId = id; - const Vertex * vertex = this; - - for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) - { - if (it.current()->id < firstId) { - firstId = vertex->id; - vertex = it.current(); - } - } - - return vertex; -} - -HalfEdge::Vertex * HalfEdge::Vertex::firstColocal() -{ - Vertex * vertex = this; - uint firstId = id; - - for (VertexIterator it(colocals()); !it.isDone(); it.advance()) - { - if (it.current()->id < firstId) { - firstId = vertex->id; - vertex = it.current(); - } - } - - return vertex; -} - -bool HalfEdge::Vertex::isFirstColocal() const -{ - return firstColocal() == this; -} - -bool HalfEdge::Vertex::isColocal(const Vertex * v) const { - if (this == v) return true; - if (pos != v->pos) return false; - - for (ConstVertexIterator it(colocals()); !it.isDone(); it.advance()) - { - if (v == it.current()) { - return true; - } - } - - return false; -} - diff --git a/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.h b/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.h deleted file mode 100644 index 1c5c8d7141..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/halfedge/Vertex.h +++ /dev/null @@ -1,221 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_HALFEDGE_VERTEX_H -#define NV_MESH_HALFEDGE_VERTEX_H - -#include "nvmesh/halfedge/Edge.h" - -namespace nv -{ - namespace HalfEdge { class Vertex; class Face; class Edge; } - - // Half edge vertex. - class HalfEdge::Vertex - { - NV_FORBID_COPY(Vertex); - public: - - uint id; - - Edge * edge; - Vertex * next; - Vertex * prev; - - Vector3 pos; - Vector3 nor; - Vector2 tex; - Vector4 col; - - - Vertex(uint id) : id(id), edge(NULL), pos(0.0f), nor(0.0f), tex(0.0f), col(0.0f) { - next = this; - prev = this; - } - - - void setEdge(Edge * e); - void setPos(const Vector3 & p); - - uint colocalCount() const; - uint valence() const; - bool isFirstColocal() const; - const Vertex * firstColocal() const; - Vertex * firstColocal(); - - bool isColocal(const Vertex * v) const; - - - void linkColocal(Vertex * v) { - next->prev = v; - v->next = next; - next = v; - v->prev = this; - } - void unlinkColocal() { - next->prev = prev; - prev->next = next; - next = this; - prev = this; - } - - - // @@ Note: This only works if linkBoundary has been called. - bool isBoundary() const { - return (edge && !edge->face); - } - - - // for(EdgeIterator it(iterator()); !it.isDone(); it.advance()) { ... } - // - // EdgeIterator it(iterator()); - // while(!it.isDone()) { - // ... - // id.advance(); - // } - - // Iterator that visits the edges around this vertex in counterclockwise order. - class EdgeIterator //: public Iterator<Edge *> - { - public: - EdgeIterator(Edge * e) : m_end(NULL), m_current(e) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - m_current = m_current->pair->next; - //m_current = m_current->prev->pair; - } - - virtual bool isDone() const { return m_end == m_current; } - virtual Edge * current() const { return m_current; } - Vertex * vertex() const { return m_current->vertex; } - - private: - Edge * m_end; - Edge * m_current; - }; - - EdgeIterator edges() { return EdgeIterator(edge); } - EdgeIterator edges(Edge * e) { return EdgeIterator(e); } - - // Iterator that visits the edges around this vertex in counterclockwise order. - class ConstEdgeIterator //: public Iterator<Edge *> - { - public: - ConstEdgeIterator(const Edge * e) : m_end(NULL), m_current(e) { } - ConstEdgeIterator(EdgeIterator it) : m_end(NULL), m_current(it.current()) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - m_current = m_current->pair->next; - //m_current = m_current->prev->pair; - } - - virtual bool isDone() const { return m_end == m_current; } - virtual const Edge * current() const { return m_current; } - const Vertex * vertex() const { return m_current->to(); } - - private: - const Edge * m_end; - const Edge * m_current; - }; - - ConstEdgeIterator edges() const { return ConstEdgeIterator(edge); } - ConstEdgeIterator edges(const Edge * e) const { return ConstEdgeIterator(e); } - - - // Iterator that visits the edges around this vertex in counterclockwise order. - class ReverseEdgeIterator //: public Iterator<Edge *> - { - public: - ReverseEdgeIterator(Edge * e) : m_end(NULL), m_current(e) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - m_current = m_current->prev->pair; - } - - virtual bool isDone() const { return m_end == m_current; } - virtual Edge * current() const { return m_current; } - Vertex * vertex() const { return m_current->vertex; } - - private: - Edge * m_end; - Edge * m_current; - }; - - // Iterator that visits the edges around this vertex in counterclockwise order. - class ReverseConstEdgeIterator //: public Iterator<Edge *> - { - public: - ReverseConstEdgeIterator(const Edge * e) : m_end(NULL), m_current(e) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - m_current = m_current->prev->pair; - } - - virtual bool isDone() const { return m_end == m_current; } - virtual const Edge * current() const { return m_current; } - const Vertex * vertex() const { return m_current->to(); } - - private: - const Edge * m_end; - const Edge * m_current; - }; - - - - // Iterator that visits all the colocal vertices. - class VertexIterator //: public Iterator<Edge *> - { - public: - VertexIterator(Vertex * v) : m_end(NULL), m_current(v) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - m_current = m_current->next; - } - - virtual bool isDone() const { return m_end == m_current; } - virtual Vertex * current() const { return m_current; } - - private: - Vertex * m_end; - Vertex * m_current; - }; - - VertexIterator colocals() { return VertexIterator(this); } - - // Iterator that visits all the colocal vertices. - class ConstVertexIterator //: public Iterator<Edge *> - { - public: - ConstVertexIterator(const Vertex * v) : m_end(NULL), m_current(v) { } - - virtual void advance() - { - if (m_end == NULL) m_end = m_current; - m_current = m_current->next; - } - - virtual bool isDone() const { return m_end == m_current; } - virtual const Vertex * current() const { return m_current; } - - private: - const Vertex * m_end; - const Vertex * m_current; - }; - - ConstVertexIterator colocals() const { return ConstVertexIterator(this); } - - }; - -} // nv namespace - -#endif // NV_MESH_HALFEDGE_VERTEX_H diff --git a/thirdparty/thekla_atlas/nvmesh/nvmesh.cpp b/thirdparty/thekla_atlas/nvmesh/nvmesh.cpp deleted file mode 100644 index d007eda332..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/nvmesh.cpp +++ /dev/null @@ -1,2 +0,0 @@ -#include "nvmesh.h" // pch - diff --git a/thirdparty/thekla_atlas/nvmesh/nvmesh.h b/thirdparty/thekla_atlas/nvmesh/nvmesh.h deleted file mode 100644 index eb6819675d..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/nvmesh.h +++ /dev/null @@ -1,34 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_H -#define NV_MESH_H - -#include "nvcore/nvcore.h" - -// Function linkage -#if NVMESH_SHARED -#ifdef NVMESH_EXPORTS -#define NVMESH_API DLL_EXPORT -#define NVMESH_CLASS DLL_EXPORT_CLASS -#else -#define NVMESH_API DLL_IMPORT -#define NVMESH_CLASS DLL_IMPORT -#endif -#else -#define NVMESH_API -#define NVMESH_CLASS -#endif - -#if 1 //USE_PRECOMPILED_HEADERS // If using precompiled headers: -//#include <string.h> // strlen, strcmp, etc. -//#include "nvcore/StrLib.h" -//#include "nvcore/StdStream.h" -//#include "nvcore/Memory.h" -//#include "nvcore/Debug.h" -//#include "nvmath/Vector.h" -//#include "nvcore/Array.h" -//#include "nvcore/HashMap.h" -#endif - -#endif // NV_MESH_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp b/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp deleted file mode 100644 index 98f92cef96..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/Atlas.cpp +++ /dev/null @@ -1,1519 +0,0 @@ -// Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> - -#include "nvmesh.h" // pch - -#include "Atlas.h" -#include "Util.h" -#include "AtlasBuilder.h" -#include "AtlasPacker.h" -#include "SingleFaceMap.h" -#include "OrthogonalProjectionMap.h" -#include "LeastSquaresConformalMap.h" -#include "ParameterizationQuality.h" - -//#include "nvmesh/export/MeshExportOBJ.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" - -#include "nvmesh/MeshBuilder.h" -#include "nvmesh/MeshTopology.h" -#include "nvmesh/param/Util.h" -#include "nvmesh/geometry/Measurements.h" - -#include "nvmath/Vector.inl" -#include "nvmath/Fitting.h" -#include "nvmath/Box.inl" -#include "nvmath/ProximityGrid.h" -#include "nvmath/Morton.h" - -#include "nvcore/StrLib.h" -#include "nvcore/Array.inl" -#include "nvcore/HashMap.inl" - -using namespace nv; - - -/// Ctor. -Atlas::Atlas() -{ - failed=false; -} - -// Dtor. -Atlas::~Atlas() -{ - deleteAll(m_meshChartsArray); -} - -uint Atlas::chartCount() const -{ - uint count = 0; - foreach(c, m_meshChartsArray) { - count += m_meshChartsArray[c]->chartCount(); - } - return count; -} - -const Chart * Atlas::chartAt(uint i) const -{ - foreach(c, m_meshChartsArray) { - uint count = m_meshChartsArray[c]->chartCount(); - - if (i < count) { - return m_meshChartsArray[c]->chartAt(i); - } - - i -= count; - } - - return NULL; -} - -Chart * Atlas::chartAt(uint i) -{ - foreach(c, m_meshChartsArray) { - uint count = m_meshChartsArray[c]->chartCount(); - - if (i < count) { - return m_meshChartsArray[c]->chartAt(i); - } - - i -= count; - } - - return NULL; -} - -// Extract the charts and add to this atlas. -void Atlas::addMeshCharts(MeshCharts * meshCharts) -{ - m_meshChartsArray.append(meshCharts); -} - -void Atlas::extractCharts(const HalfEdge::Mesh * mesh) -{ - MeshCharts * meshCharts = new MeshCharts(mesh); - meshCharts->extractCharts(); - addMeshCharts(meshCharts); -} - -void Atlas::computeCharts(const HalfEdge::Mesh * mesh, const SegmentationSettings & settings, const Array<uint> & unchartedMaterialArray) -{ - failed=false; - MeshCharts * meshCharts = new MeshCharts(mesh); - meshCharts->computeCharts(settings, unchartedMaterialArray); - addMeshCharts(meshCharts); -} - - - - -#if 0 - -/// Compute a seamless texture atlas. -bool Atlas::computeSeamlessTextureAtlas(bool groupFaces/*= true*/, bool scaleTiles/*= false*/, uint w/*= 1024*/, uint h/* = 1024*/) -{ - // Implement seamless texture atlas similar to what ZBrush does. See also: - // "Meshed Atlases for Real-Time Procedural Solid Texturing" - // http://graphics.cs.uiuc.edu/~jch/papers/rtpst.pdf - - // Other methods that we should experiment with: - // - // Seamless Texture Atlases: - // http://www.cs.jhu.edu/~bpurnomo/STA/index.html - // - // Rectangular Multi-Chart Geometry Images: - // http://graphics.cs.uiuc.edu/~jch/papers/rmcgi.pdf - // - // Discrete differential geometry also provide a way of constructing - // seamless quadrangulations as shown in: - // http://www.geometry.caltech.edu/pubs/TACD06.pdf - // - -#pragma message(NV_FILE_LINE "TODO: Implement seamless texture atlas.") - - if (groupFaces) - { - // @@ TODO. - } - else - { - // @@ Create one atlas per face. - } - - if (scaleTiles) - { - // @@ TODO - } - - /* - if (!isQuadMesh(m_mesh)) { - // Only handle quads for now. - return false; - } - - // Each face is a chart. - const uint faceCount = m_mesh->faceCount(); - m_chartArray.resize(faceCount); - - for(uint f = 0; f < faceCount; f++) { - m_chartArray[f].faceArray.clear(); - m_chartArray[f].faceArray.append(f); - } - - // Map each face to a separate square. - - // Determine face layout according to width and height. - float aspect = float(m_width) / float(m_height); - - uint i = 2; - uint total = (m_width / (i+1)) * (m_height / (i+1)); - while(total > faceCount) { - i *= 2; - total = (m_width / (i+1)) * (m_height / (i+1)); - } - - uint tileSize = i / 2; - - int x = 0; - int y = 0; - - m_result = new HalfEdge::Mesh(); - - // Once you have that it's just matter of traversing the faces. - for(uint f = 0; f < faceCount; f++) { - // Compute texture coordinates. - Vector2 tex[4]; - tex[0] = Vector2(float(x), float(y)); - tex[1] = Vector2(float(x+tileSize), float(y)); - tex[2] = Vector2(float(x+tileSize), float(y+tileSize)); - tex[3] = Vector2(float(x), float(y+tileSize)); - - Array<uint> indexArray(4); - - const HalfEdge::Face * face = m_mesh->faceAt(f); - - int i = 0; - for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance(), i++) { - const HalfEdge::Edge * edge = it.current(); - const HalfEdge::Vertex * vertex = edge->from(); - - HalfEdge::Vertex * newVertex = m_result->addVertex(vertex->id(), vertex->pos()); - - newVertex->setTex(Vector3(tex[i], 0)); - newVertex->setNor(vertex->nor()); - - indexArray.append(m_result->vertexCount() + 1); - } - - m_result->addFace(indexArray); - - // Move to the next tile. - x += tileSize + 1; - if (x + tileSize > m_width) { - x = 0; - y += tileSize + 1; - } - } - */ - - return false; -} - -#endif - - -void Atlas::parameterizeCharts() -{ - foreach(i, m_meshChartsArray) { - m_meshChartsArray[i]->parameterizeCharts(); - } -} - - -float Atlas::packCharts(int quality, float texelsPerUnit, bool blockAlign, bool conservative) -{ - AtlasPacker packer(this); - packer.packCharts(quality, texelsPerUnit, blockAlign, conservative); - if (hasFailed()) - return 0; - return packer.computeAtlasUtilization(); -} - - - - -/// Ctor. -MeshCharts::MeshCharts(const HalfEdge::Mesh * mesh) : m_mesh(mesh) -{ -} - -// Dtor. -MeshCharts::~MeshCharts() -{ - deleteAll(m_chartArray); -} - - -void MeshCharts::extractCharts() -{ - const uint faceCount = m_mesh->faceCount(); - - int first = 0; - Array<uint> queue(faceCount); - - BitArray bitFlags(faceCount); - bitFlags.clearAll(); - - for (uint f = 0; f < faceCount; f++) - { - if (bitFlags.bitAt(f) == false) - { - // Start new patch. Reset queue. - first = 0; - queue.clear(); - queue.append(f); - bitFlags.setBitAt(f); - - while (first != queue.count()) - { - const HalfEdge::Face * face = m_mesh->faceAt(queue[first]); - - // Visit face neighbors of queue[first] - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - nvDebugCheck(edge->pair != NULL); - - if (!edge->isBoundary() && /*!edge->isSeam()*/ - //!(edge->from()->tex() != edge->pair()->to()->tex() || edge->to()->tex() != edge->pair()->from()->tex())) - !(edge->from() != edge->pair->to() || edge->to() != edge->pair->from())) // Preserve existing seams (not just texture seams). - { - const HalfEdge::Face * neighborFace = edge->pair->face; - nvDebugCheck(neighborFace != NULL); - - if (bitFlags.bitAt(neighborFace->id) == false) - { - queue.append(neighborFace->id); - bitFlags.setBitAt(neighborFace->id); - } - } - } - - first++; - } - - Chart * chart = new Chart(); - chart->build(m_mesh, queue); - - m_chartArray.append(chart); - } - } -} - - -/* -LSCM: -- identify sharp features using local dihedral angles. -- identify seed faces farthest from sharp features. -- grow charts from these seeds. - -MCGIM: -- phase 1: chart growth - - grow all charts simultaneously using dijkstra search on the dual graph of the mesh. - - graph edges are weighted based on planarity metric. - - metric uses distance to global chart normal. - - terminate when all faces have been assigned. -- phase 2: seed computation: - - place new seed of the chart at the most interior face. - - most interior is evaluated using distance metric only. - -- method repeates the two phases, until the location of the seeds does not change. - - cycles are detected by recording all the previous seeds and chartification terminates. - -D-Charts: - -- Uniaxial conic metric: - - N_c = axis of the generalized cone that best fits the chart. (cone can a be cylinder or a plane). - - omega_c = angle between the face normals and the axis. - - Fitting error between chart C and tringle t: F(c,t) = (N_c*n_t - cos(omega_c))^2 - -- Compactness metrics: - - Roundness: - - C(c,t) = pi * D(S_c,t)^2 / A_c - - S_c = chart seed. - - D(S_c,t) = length of the shortest path inside the chart betwen S_c and t. - - A_c = chart area. - - Straightness: - - P(c,t) = l_out(c,t) / l_in(c,t) - - l_out(c,t) = lenght of the edges not shared between C and t. - - l_in(c,t) = lenght of the edges shared between C and t. - -- Combined metric: - - Cost(c,t) = F(c,t)^alpha + C(c,t)^beta + P(c,t)^gamma - - alpha = 1, beta = 0.7, gamma = 0.5 - - - - -Our basic approach: -- Just one iteration of k-means? -- Avoid dijkstra by greedily growing charts until a threshold is met. Increase threshold and repeat until no faces left. -- If distortion metric is too high, split chart, add two seeds. -- If chart size is low, try removing chart. - - -Postprocess: -- If topology is not disk: - - Fill holes, if new faces fit proxy. - - Find best cut, otherwise. -- After parameterization: - - If boundary self-intersects: - - cut chart along the closest two diametral boundary vertices, repeat parametrization. - - what if the overlap is on an appendix? How do we find that out and cut appropiately? - - emphasize roundness metrics to prevent those cases. - - If interior self-overlaps: preserve boundary parameterization and use mean-value map. - -*/ - - -SegmentationSettings::SegmentationSettings() -{ - // Charts have no area or boundary limits right now. - maxChartArea = NV_FLOAT_MAX; - maxBoundaryLength = NV_FLOAT_MAX; - - proxyFitMetricWeight = 1.0f; - roundnessMetricWeight = 0.1f; - straightnessMetricWeight = 0.25f; - normalSeamMetricWeight = 1.0f; - textureSeamMetricWeight = 0.1f; -} - - - -void MeshCharts::computeCharts(const SegmentationSettings & settings, const Array<uint> & unchartedMaterialArray) -{ - Chart * vertexMap = NULL; - - if (unchartedMaterialArray.count() != 0) { - vertexMap = new Chart(); - vertexMap->buildVertexMap(m_mesh, unchartedMaterialArray); - - if (vertexMap->faceCount() == 0) { - delete vertexMap; - vertexMap = NULL; - } - } - - - AtlasBuilder builder(m_mesh); - - if (vertexMap != NULL) { - // Mark faces that do not need to be charted. - builder.markUnchartedFaces(vertexMap->faceArray()); - - m_chartArray.append(vertexMap); - } - - if (builder.facesLeft != 0) { - - // Tweak these values: - const float maxThreshold = 2; - const uint growFaceCount = 32; - const uint maxIterations = 4; - - builder.settings = settings; - - //builder.settings.proxyFitMetricWeight *= 0.75; // relax proxy fit weight during initial seed placement. - //builder.settings.roundnessMetricWeight = 0; - //builder.settings.straightnessMetricWeight = 0; - - // This seems a reasonable estimate. - uint maxSeedCount = max(6U, builder.facesLeft); - - // Create initial charts greedely. - nvDebug("### Placing seeds\n"); - builder.placeSeeds(maxThreshold, maxSeedCount); - nvDebug("### Placed %d seeds (max = %d)\n", builder.chartCount(), maxSeedCount); - - builder.updateProxies(); - - builder.mergeCharts(); - - #if 1 - nvDebug("### Relocating seeds\n"); - builder.relocateSeeds(); - - nvDebug("### Reset charts\n"); - builder.resetCharts(); - - if (vertexMap != NULL) { - builder.markUnchartedFaces(vertexMap->faceArray()); - } - - builder.settings = settings; - - nvDebug("### Growing charts\n"); - - // Restart process growing charts in parallel. - uint iteration = 0; - while (true) - { - if (!builder.growCharts(maxThreshold, growFaceCount)) - { - nvDebug("### Can't grow anymore\n"); - - // If charts cannot grow more: fill holes, merge charts, relocate seeds and start new iteration. - - nvDebug("### Filling holes\n"); - builder.fillHoles(maxThreshold); - nvDebug("### Using %d charts now\n", builder.chartCount()); - - builder.updateProxies(); - - nvDebug("### Merging charts\n"); - builder.mergeCharts(); - nvDebug("### Using %d charts now\n", builder.chartCount()); - - nvDebug("### Reseeding\n"); - if (!builder.relocateSeeds()) - { - nvDebug("### Cannot relocate seeds anymore\n"); - - // Done! - break; - } - - if (iteration == maxIterations) - { - nvDebug("### Reached iteration limit\n"); - break; - } - iteration++; - - nvDebug("### Reset charts\n"); - builder.resetCharts(); - - if (vertexMap != NULL) { - builder.markUnchartedFaces(vertexMap->faceArray()); - } - - nvDebug("### Growing charts\n"); - } - }; - #endif - - // Make sure no holes are left! - nvDebugCheck(builder.facesLeft == 0); - - const uint chartCount = builder.chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - Chart * chart = new Chart(); - m_chartArray.append(chart); - - chart->build(m_mesh, builder.chartFaces(i)); - } - } - - - const uint chartCount = m_chartArray.count(); - - // Build face indices. - m_faceChart.resize(m_mesh->faceCount()); - m_faceIndex.resize(m_mesh->faceCount()); - - for (uint i = 0; i < chartCount; i++) - { - const Chart * chart = m_chartArray[i]; - - const uint faceCount = chart->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - uint idx = chart->faceAt(f); - m_faceChart[idx] = i; - m_faceIndex[idx] = f; - } - } - - // Build an exclusive prefix sum of the chart vertex counts. - m_chartVertexCountPrefixSum.resize(chartCount); - - if (chartCount > 0) - { - m_chartVertexCountPrefixSum[0] = 0; - - for (uint i = 1; i < chartCount; i++) - { - const Chart * chart = m_chartArray[i-1]; - m_chartVertexCountPrefixSum[i] = m_chartVertexCountPrefixSum[i-1] + chart->vertexCount(); - } - - m_totalVertexCount = m_chartVertexCountPrefixSum[chartCount - 1] + m_chartArray[chartCount-1]->vertexCount(); - } - else - { - m_totalVertexCount = 0; - } -} - - -void MeshCharts::parameterizeCharts() -{ - ParameterizationQuality globalParameterizationQuality; - - // Parameterize the charts. - uint diskCount = 0; - const uint chartCount = m_chartArray.count(); - for (uint i = 0; i < chartCount; i++)\ - { - Chart * chart = m_chartArray[i]; - - bool isValid = false; - - if (chart->isVertexMapped()) { - continue; - } - - if (chart->isDisk()) - { - diskCount++; - - ParameterizationQuality chartParameterizationQuality; - - if (chart->faceCount() == 1) { - computeSingleFaceMap(chart->unifiedMesh()); - - chartParameterizationQuality = ParameterizationQuality(chart->unifiedMesh()); - } - else { - computeOrthogonalProjectionMap(chart->unifiedMesh()); - ParameterizationQuality orthogonalQuality(chart->unifiedMesh()); - - computeLeastSquaresConformalMap(chart->unifiedMesh()); - ParameterizationQuality lscmQuality(chart->unifiedMesh()); - - // If the orthogonal projection produces better results, just use that. - // @@ It may be dangerous to do this, because isValid() does not detect self-overlaps. - // @@ Another problem is that with very thin patches with nearly zero parametric area, the results of our metric are not accurate. - /*if (orthogonalQuality.isValid() && orthogonalQuality.rmsStretchMetric() < lscmQuality.rmsStretchMetric()) { - computeOrthogonalProjectionMap(chart->unifiedMesh()); - chartParameterizationQuality = orthogonalQuality; - } - else*/ { - chartParameterizationQuality = lscmQuality; - } - - // If conformal map failed, - - // @@ Experiment with other parameterization methods. - //computeCircularBoundaryMap(chart->unifiedMesh()); - //computeConformalMap(chart->unifiedMesh()); - //computeNaturalConformalMap(chart->unifiedMesh()); - //computeGuidanceGradientMap(chart->unifiedMesh()); - } - - //ParameterizationQuality chartParameterizationQuality(chart->unifiedMesh()); - - isValid = chartParameterizationQuality.isValid(); - - if (!isValid) - { - nvDebug("*** Invalid parameterization.\n"); -#if 0 - // Dump mesh to inspect problem: - static int pieceCount = 0; - - StringBuilder fileName; - fileName.format("invalid_chart_%d.obj", pieceCount++); - exportMesh(chart->unifiedMesh(), fileName.str()); -#endif - } - - // @@ Check that parameterization quality is above a certain threshold. - - // @@ Detect boundary self-intersections. - - globalParameterizationQuality += chartParameterizationQuality; - } - - if (!isValid) - { - //nvDebugBreak(); - // @@ Run the builder again, but only on this chart. - //AtlasBuilder builder(chart->chartMesh()); - } - - // Transfer parameterization from unified mesh to chart mesh. - chart->transferParameterization(); - - } - - nvDebug(" Parameterized %d/%d charts.\n", diskCount, chartCount); - nvDebug(" RMS stretch metric: %f\n", globalParameterizationQuality.rmsStretchMetric()); - nvDebug(" MAX stretch metric: %f\n", globalParameterizationQuality.maxStretchMetric()); - nvDebug(" RMS conformal metric: %f\n", globalParameterizationQuality.rmsConformalMetric()); - nvDebug(" RMS authalic metric: %f\n", globalParameterizationQuality.maxAuthalicMetric()); -} - - - -Chart::Chart() : m_chartMesh(NULL), m_unifiedMesh(NULL), m_isDisk(false), m_isVertexMapped(false) -{ -} - -void Chart::build(const HalfEdge::Mesh * originalMesh, const Array<uint> & faceArray) -{ - // Copy face indices. - m_faceArray = faceArray; - - const uint meshVertexCount = originalMesh->vertexCount(); - - m_chartMesh = new HalfEdge::Mesh(); - m_unifiedMesh = new HalfEdge::Mesh(); - - Array<uint> chartMeshIndices; - chartMeshIndices.resize(meshVertexCount, ~0); - - Array<uint> unifiedMeshIndices; - unifiedMeshIndices.resize(meshVertexCount, ~0); - - // Add vertices. - const uint faceCount = faceArray.count(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = originalMesh->faceAt(faceArray[f]); - nvDebugCheck(face != NULL); - - for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Vertex * vertex = it.current()->vertex; - const HalfEdge::Vertex * unifiedVertex = vertex->firstColocal(); - - if (unifiedMeshIndices[unifiedVertex->id] == ~0) - { - unifiedMeshIndices[unifiedVertex->id] = m_unifiedMesh->vertexCount(); - - nvDebugCheck(vertex->pos == unifiedVertex->pos); - m_unifiedMesh->addVertex(vertex->pos); - } - - if (chartMeshIndices[vertex->id] == ~0) - { - chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); - m_chartToOriginalMap.append(vertex->id); - m_chartToUnifiedMap.append(unifiedMeshIndices[unifiedVertex->id]); - - HalfEdge::Vertex * v = m_chartMesh->addVertex(vertex->pos); - v->nor = vertex->nor; - v->tex = vertex->tex; - } - } - } - - // This is ignoring the canonical map: - // - Is it really necessary to link colocals? - - m_chartMesh->linkColocals(); - //m_unifiedMesh->linkColocals(); // Not strictly necessary, no colocals in the unified mesh. # Wrong. - - // This check is not valid anymore, if the original mesh vertices were linked with a canonical map, then it might have - // some colocal vertices that were unlinked. So, the unified mesh might have some duplicate vertices, because firstColocal() - // is not guaranteed to return the same vertex for two colocal vertices. - //nvCheck(m_chartMesh->colocalVertexCount() == m_unifiedMesh->vertexCount()); - - // Is that OK? What happens in meshes were that happens? Does anything break? Apparently not... - - - - Array<uint> faceIndices(7); - - // Add faces. - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = originalMesh->faceAt(faceArray[f]); - nvDebugCheck(face != NULL); - - faceIndices.clear(); - - for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Vertex * vertex = it.current()->vertex; - nvDebugCheck(vertex != NULL); - - faceIndices.append(chartMeshIndices[vertex->id]); - } - - m_chartMesh->addFace(faceIndices); - - faceIndices.clear(); - - for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Vertex * vertex = it.current()->vertex; - nvDebugCheck(vertex != NULL); - - vertex = vertex->firstColocal(); - - faceIndices.append(unifiedMeshIndices[vertex->id]); - } - - m_unifiedMesh->addFace(faceIndices); - } - - m_chartMesh->linkBoundary(); - m_unifiedMesh->linkBoundary(); - - //exportMesh(m_unifiedMesh.ptr(), "debug_input.obj"); - - if (m_unifiedMesh->splitBoundaryEdges()) { - m_unifiedMesh = unifyVertices(m_unifiedMesh.ptr()); - } - - //exportMesh(m_unifiedMesh.ptr(), "debug_split.obj"); - - // Closing the holes is not always the best solution and does not fix all the problems. - // We need to do some analysis of the holes and the genus to: - // - Find cuts that reduce genus. - // - Find cuts to connect holes. - // - Use minimal spanning trees or seamster. - if (!closeHoles()) { - /*static int pieceCount = 0; - StringBuilder fileName; - fileName.format("debug_hole_%d.obj", pieceCount++); - exportMesh(m_unifiedMesh.ptr(), fileName.str());*/ - } - - m_unifiedMesh = triangulate(m_unifiedMesh.ptr()); - - //exportMesh(m_unifiedMesh.ptr(), "debug_triangulated.obj"); - - - // Analyze chart topology. - MeshTopology topology(m_unifiedMesh.ptr()); - m_isDisk = topology.isDisk(); - - // This is sometimes failing, when triangulate fails to add a triangle, it generates a hole in the mesh. - //nvDebugCheck(m_isDisk); - - /*if (!m_isDisk) { - static int pieceCount = 0; - StringBuilder fileName; - fileName.format("debug_hole_%d.obj", pieceCount++); - exportMesh(m_unifiedMesh.ptr(), fileName.str()); - }*/ - - -#if 0 - if (!m_isDisk) { - nvDebugBreak(); - - static int pieceCount = 0; - - StringBuilder fileName; - fileName.format("debug_nodisk_%d.obj", pieceCount++); - exportMesh(m_chartMesh.ptr(), fileName.str()); - } -#endif - -} - - -void Chart::buildVertexMap(const HalfEdge::Mesh * originalMesh, const Array<uint> & unchartedMaterialArray) -{ - nvCheck(m_chartMesh == NULL && m_unifiedMesh == NULL); - - m_isVertexMapped = true; - - // Build face indices. - m_faceArray.clear(); - - const uint meshFaceCount = originalMesh->faceCount(); - for (uint f = 0; f < meshFaceCount; f++) { - const HalfEdge::Face * face = originalMesh->faceAt(f); - - if (unchartedMaterialArray.contains(face->material)) { - m_faceArray.append(f); - } - } - - const uint faceCount = m_faceArray.count(); - - if (faceCount == 0) { - return; - } - - - // @@ The chartMesh construction is basically the same as with regular charts, don't duplicate! - - const uint meshVertexCount = originalMesh->vertexCount(); - - m_chartMesh = new HalfEdge::Mesh(); - - Array<uint> chartMeshIndices; - chartMeshIndices.resize(meshVertexCount, ~0); - - // Vertex map mesh only has disconnected vertices. - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = originalMesh->faceAt(m_faceArray[f]); - nvDebugCheck(face != NULL); - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Vertex * vertex = it.current()->vertex; - - if (chartMeshIndices[vertex->id] == ~0) - { - chartMeshIndices[vertex->id] = m_chartMesh->vertexCount(); - m_chartToOriginalMap.append(vertex->id); - - HalfEdge::Vertex * v = m_chartMesh->addVertex(vertex->pos); - v->nor = vertex->nor; - v->tex = vertex->tex; // @@ Not necessary. - } - } - } - - // @@ Link colocals using the original mesh canonical map? Build canonical map on the fly? Do we need to link colocals at all for this? - //m_chartMesh->linkColocals(); - - Array<uint> faceIndices(7); - - // Add faces. - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = originalMesh->faceAt(m_faceArray[f]); - nvDebugCheck(face != NULL); - - faceIndices.clear(); - - for(HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Vertex * vertex = it.current()->vertex; - nvDebugCheck(vertex != NULL); - nvDebugCheck(chartMeshIndices[vertex->id] != ~0); - - faceIndices.append(chartMeshIndices[vertex->id]); - } - - HalfEdge::Face * new_face = m_chartMesh->addFace(faceIndices); - nvDebugCheck(new_face != NULL); - } - - m_chartMesh->linkBoundary(); - - - const uint chartVertexCount = m_chartMesh->vertexCount(); - - Box bounds; - bounds.clearBounds(); - - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - bounds.addPointToBounds(vertex->pos); - } - - ProximityGrid grid; - grid.init(bounds, chartVertexCount); - - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - grid.add(vertex->pos, i); - } - - -#if 0 - // Arrange vertices in a rectangle. - vertexMapWidth = ftoi_ceil(sqrtf(float(chartVertexCount))); - vertexMapHeight = (chartVertexCount + vertexMapWidth - 1) / vertexMapWidth; - nvDebugCheck(vertexMapWidth >= vertexMapHeight); - - int x = 0, y = 0; - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - - vertex->tex.x = float(x); - vertex->tex.y = float(y); - - x++; - if (x == vertexMapWidth) { - x = 0; - y++; - nvCheck(y < vertexMapHeight); - } - } - -#elif 0 - // Arrange vertices in a rectangle, traversing grid in 3D morton order and laying them down in 2D morton order. - vertexMapWidth = ftoi_ceil(sqrtf(float(chartVertexCount))); - vertexMapHeight = (chartVertexCount + vertexMapWidth - 1) / vertexMapWidth; - nvDebugCheck(vertexMapWidth >= vertexMapHeight); - - int n = 0; - uint32 texelCode = 0; - - uint cellsVisited = 0; - - const uint32 cellCodeCount = grid.mortonCount(); - for (uint32 cellCode = 0; cellCode < cellCodeCount; cellCode++) { - int cell = grid.mortonIndex(cellCode); - if (cell < 0) continue; - - cellsVisited++; - - const Array<uint> & indexArray = grid.cellArray[cell].indexArray; - - foreach(i, indexArray) { - uint idx = indexArray[i]; - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(idx); - - //vertex->tex.x = float(n % rectangleWidth) + 0.5f; - //vertex->tex.y = float(n / rectangleWidth) + 0.5f; - - // Lay down the points in z order too. - uint x, y; - do { - x = decodeMorton2X(texelCode); - y = decodeMorton2Y(texelCode); - texelCode++; - } while (x >= U32(vertexMapWidth) || y >= U32(vertexMapHeight)); - - vertex->tex.x = float(x); - vertex->tex.y = float(y); - - n++; - } - } - - nvDebugCheck(cellsVisited == grid.cellArray.count()); - nvDebugCheck(n == chartVertexCount); - -#else - - uint texelCount = 0; - - const float positionThreshold = 0.01f; - const float normalThreshold = 0.01f; - - uint verticesVisited = 0; - uint cellsVisited = 0; - - Array<int> vertexIndexArray; - vertexIndexArray.resize(chartVertexCount, -1); // Init all indices to -1. - - // Traverse vertices in morton order. @@ It may be more interesting to sort them based on orientation. - const uint cellCodeCount = grid.mortonCount(); - for (uint cellCode = 0; cellCode < cellCodeCount; cellCode++) { - int cell = grid.mortonIndex(cellCode); - if (cell < 0) continue; - - cellsVisited++; - - const Array<uint> & indexArray = grid.cellArray[cell].indexArray; - - foreach(i, indexArray) { - uint idx = indexArray[i]; - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(idx); - - nvDebugCheck(vertexIndexArray[idx] == -1); - - Array<uint> neighbors; - grid.gather(vertex->pos, positionThreshold, /*ref*/neighbors); - - // Compare against all nearby vertices, cluster greedily. - foreach(j, neighbors) { - uint otherIdx = neighbors[j]; - - if (vertexIndexArray[otherIdx] != -1) { - HalfEdge::Vertex * otherVertex = m_chartMesh->vertexAt(otherIdx); - - if (distance(vertex->pos, otherVertex->pos) < positionThreshold && - distance(vertex->nor, otherVertex->nor) < normalThreshold) - { - vertexIndexArray[idx] = vertexIndexArray[otherIdx]; - break; - } - } - } - - // If index not assigned, assign new one. - if (vertexIndexArray[idx] == -1) { - vertexIndexArray[idx] = texelCount++; - } - - verticesVisited++; - } - } - - nvDebugCheck(cellsVisited == grid.cellArray.count()); - nvDebugCheck(verticesVisited == chartVertexCount); - - vertexMapWidth = ftoi_ceil(sqrtf(float(texelCount))); - vertexMapWidth = (vertexMapWidth + 3) & ~3; // Width aligned to 4. - vertexMapHeight = vertexMapWidth == 0 ? 0 : (texelCount + vertexMapWidth - 1) / vertexMapWidth; - //vertexMapHeight = (vertexMapHeight + 3) & ~3; // Height aligned to 4. - nvDebugCheck(vertexMapWidth >= vertexMapHeight); - - nvDebug("Reduced vertex count from %d to %d.\n", chartVertexCount, texelCount); - -#if 0 - // This lays down the clustered vertices linearly. - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - - int idx = vertexIndexArray[i]; - - vertex->tex.x = float(idx % vertexMapWidth); - vertex->tex.y = float(idx / vertexMapWidth); - } -#else - // Lay down the clustered vertices in morton order. - - Array<uint> texelCodes; - texelCodes.resize(texelCount); - - // For each texel, assign one morton code. - uint texelCode = 0; - for (uint i = 0; i < texelCount; i++) { - uint x, y; - do { - x = decodeMorton2X(texelCode); - y = decodeMorton2Y(texelCode); - texelCode++; - } while (x >= U32(vertexMapWidth) || y >= U32(vertexMapHeight)); - - texelCodes[i] = texelCode - 1; - } - - for (uint i = 0; i < chartVertexCount; i++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(i); - - int idx = vertexIndexArray[i]; - if (idx != -1) { - uint texelCode = texelCodes[idx]; - uint x = decodeMorton2X(texelCode); - uint y = decodeMorton2Y(texelCode); - - vertex->tex.x = float(x); - vertex->tex.y = float(y); - } - } - -#endif - -#endif - -} - - - -static void getBoundaryEdges(HalfEdge::Mesh * mesh, Array<HalfEdge::Edge *> & boundaryEdges) -{ - nvDebugCheck(mesh != NULL); - - const uint edgeCount = mesh->edgeCount(); - - BitArray bitFlags(edgeCount); - bitFlags.clearAll(); - - boundaryEdges.clear(); - - // Search for boundary edges. Mark all the edges that belong to the same boundary. - for (uint e = 0; e < edgeCount; e++) - { - HalfEdge::Edge * startEdge = mesh->edgeAt(e); - - if (startEdge != NULL && startEdge->isBoundary() && bitFlags.bitAt(e) == false) - { - nvDebugCheck(startEdge->face != NULL); - nvDebugCheck(startEdge->pair->face == NULL); - - startEdge = startEdge->pair; - - const HalfEdge::Edge * edge = startEdge; - do { - nvDebugCheck(edge->face == NULL); - nvDebugCheck(bitFlags.bitAt(edge->id/2) == false); - - bitFlags.setBitAt(edge->id / 2); - edge = edge->next; - } while(startEdge != edge); - - boundaryEdges.append(startEdge); - } - } -} - - -bool Chart::closeLoop(uint start, const Array<HalfEdge::Edge *> & loop) -{ - const uint vertexCount = loop.count() - start; - - nvDebugCheck(vertexCount >= 3); - if (vertexCount < 3) return false; - - nvDebugCheck(loop[start]->vertex->isColocal(loop[start+vertexCount-1]->to())); - - // If the hole is planar, then we add a single face that will be properly triangulated later. - // If the hole is not planar, we add a triangle fan with a vertex at the hole centroid. - // This is still a bit of a hack. There surely are better hole filling algorithms out there. - - Array<Vector3> points; - points.resize(vertexCount); - for (uint i = 0; i < vertexCount; i++) { - points[i] = loop[start+i]->vertex->pos; - } - - bool isPlanar = Fit::isPlanar(vertexCount, points.buffer()); - - if (isPlanar) { - // Add face and connect edges. - HalfEdge::Face * face = m_unifiedMesh->addFace(); - for (uint i = 0; i < vertexCount; i++) { - HalfEdge::Edge * edge = loop[start + i]; - - edge->face = face; - edge->setNext(loop[start + (i + 1) % vertexCount]); - } - face->edge = loop[start]; - - nvDebugCheck(face->isValid()); - } - else { - // If the polygon is not planar, we just cross our fingers, and hope this will work: - - // Compute boundary centroid: - Vector3 centroidPos(0); - - for (uint i = 0; i < vertexCount; i++) { - centroidPos += points[i]; - } - - centroidPos *= (1.0f / vertexCount); - - HalfEdge::Vertex * centroid = m_unifiedMesh->addVertex(centroidPos); - - // Add one pair of edges for each boundary vertex. - for (uint j = vertexCount-1, i = 0; i < vertexCount; j = i++) { - HalfEdge::Face * face = m_unifiedMesh->addFace(centroid->id, loop[start+j]->vertex->id, loop[start+i]->vertex->id); - nvDebugCheck(face != NULL); - } - } - - return true; -} - - -bool Chart::closeHoles() -{ - nvDebugCheck(!m_isVertexMapped); - - Array<HalfEdge::Edge *> boundaryEdges; - getBoundaryEdges(m_unifiedMesh.ptr(), boundaryEdges); - - uint boundaryCount = boundaryEdges.count(); - if (boundaryCount <= 1) - { - // Nothing to close. - return true; - } - - // Compute lengths and areas. - Array<float> boundaryLengths; - //Array<Vector3> boundaryCentroids; - - for (uint i = 0; i < boundaryCount; i++) - { - const HalfEdge::Edge * startEdge = boundaryEdges[i]; - nvCheck(startEdge->face == NULL); - - //float boundaryEdgeCount = 0; - float boundaryLength = 0.0f; - //Vector3 boundaryCentroid(zero); - - const HalfEdge::Edge * edge = startEdge; - do { - Vector3 t0 = edge->from()->pos; - Vector3 t1 = edge->to()->pos; - - //boundaryEdgeCount++; - boundaryLength += length(t1 - t0); - //boundaryCentroid += edge->vertex()->pos; - - edge = edge->next; - } while(edge != startEdge); - - boundaryLengths.append(boundaryLength); - //boundaryCentroids.append(boundaryCentroid / boundaryEdgeCount); - } - - - // Find disk boundary. - uint diskBoundary = 0; - float maxLength = boundaryLengths[0]; - - for (uint i = 1; i < boundaryCount; i++) - { - if (boundaryLengths[i] > maxLength) - { - maxLength = boundaryLengths[i]; - diskBoundary = i; - } - } - - - // Sew holes. - /*for (uint i = 0; i < boundaryCount; i++) - { - if (diskBoundary == i) - { - // Skip disk boundary. - continue; - } - - HalfEdge::Edge * startEdge = boundaryEdges[i]; - nvCheck(startEdge->face() == NULL); - - boundaryEdges[i] = m_unifiedMesh->sewBoundary(startEdge); - } - - exportMesh(m_unifiedMesh.ptr(), "debug_sewn.obj");*/ - - //bool hasNewHoles = false; - - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // @@ Close loop is wrong, after closing a loop, we do not only have to add the face, but make sure that every edge in he loop is pointing to the right place. - - // Close holes. - for (uint i = 0; i < boundaryCount; i++) - { - if (diskBoundary == i) - { - // Skip disk boundary. - continue; - } - - HalfEdge::Edge * startEdge = boundaryEdges[i]; - nvDebugCheck(startEdge != NULL); - nvDebugCheck(startEdge->face == NULL); - -#if 1 - Array<HalfEdge::Vertex *> vertexLoop; - Array<HalfEdge::Edge *> edgeLoop; - - HalfEdge::Edge * edge = startEdge; - do { - HalfEdge::Vertex * vertex = edge->next->vertex; // edge->to() - - uint i; - for (i = 0; i < vertexLoop.count(); i++) { - if (vertex->isColocal(vertexLoop[i])) { - break; - } - } - - bool isCrossing = (i != vertexLoop.count()); - - if (isCrossing) { - - HalfEdge::Edge * prev = edgeLoop[i]; // Previous edge before the loop. - HalfEdge::Edge * next = edge->next; // Next edge after the loop. - - nvDebugCheck(prev->to()->isColocal(next->from())); - - // Close loop. - edgeLoop.append(edge); - closeLoop(i+1, edgeLoop); - - // Link boundary loop. - prev->setNext(next); - vertex->setEdge(next); - - // Start over again. - vertexLoop.clear(); - edgeLoop.clear(); - - edge = startEdge; - vertex = edge->to(); - } - - vertexLoop.append(vertex); - edgeLoop.append(edge); - - edge = edge->next; - } while(edge != startEdge); - - closeLoop(0, edgeLoop); -#endif - - /* - - // Add face and connect boundary edges. - HalfEdge::Face * face = m_unifiedMesh->addFace(); - face->setEdge(startEdge); - - HalfEdge::Edge * edge = startEdge; - do { - edge->setFace(face); - - edge = edge->next(); - } while(edge != startEdge); - - */ - - - /* - uint edgeCount = 0; - HalfEdge::Edge * edge = startEdge; - do { - edgeCount++; - edge = edge->next(); - } while(edge != startEdge); - - - - // Count edges in this boundary. - uint edgeCount = 0; - HalfEdge::Edge * edge = startEdge; - do { - edgeCount++; - edge = edge->next(); - } while(edge != startEdge); - - // Trivial hole, fill with one triangle. This actually works for all convex boundaries with non colinear vertices. - if (edgeCount == 3) { - // Add face and connect boundary edges. - HalfEdge::Face * face = m_unifiedMesh->addFace(); - face->setEdge(startEdge); - - edge = startEdge; - do { - edge->setFace(face); - - edge = edge->next(); - } while(edge != startEdge); - - // @@ Implement the above using addFace, it should now work with existing edges, as long as their face pointers is zero. - - } - else { - // Ideally we should: - // - compute best fit plane of boundary vertices. - // - project boundary polygon onto plane. - // - triangulate boundary polygon. - // - add faces of the resulting triangulation. - - // I don't have a good triangulator available. A more simple solution that works in more (but not all) cases: - // - compute boundary centroid. - // - add vertex centroid. - // - connect centroid vertex with boundary vertices. - // - connect radial edges with boundary edges. - - // This should work for non-convex boundaries with colinear vertices as long as the kernel of the polygon is not empty. - - // Compute boundary centroid: - Vector3 centroid_pos(0); - Vector2 centroid_tex(0); - - HalfEdge::Edge * edge = startEdge; - do { - centroid_pos += edge->vertex()->pos; - centroid_tex += edge->vertex()->tex; - edge = edge->next(); - } while(edge != startEdge); - - centroid_pos *= (1.0f / edgeCount); - centroid_tex *= (1.0f / edgeCount); - - HalfEdge::Vertex * centroid = m_unifiedMesh->addVertex(centroid_pos); - centroid->tex = centroid_tex; - - // Add one pair of edges for each boundary vertex. - edge = startEdge; - do { - HalfEdge::Edge * next = edge->next(); - - nvCheck(edge->face() == NULL); - HalfEdge::Face * face = m_unifiedMesh->addFace(centroid->id(), edge->from()->id(), edge->to()->id()); - - if (face != NULL) { - nvCheck(edge->face() == face); - } - else { - hasNewHoles = true; - } - - edge = next; - } while(edge != startEdge); - } - */ - } - - /*nvDebugCheck(!hasNewHoles); - - if (hasNewHoles) { - // Link boundary again, in case closeHoles created new holes! - m_unifiedMesh->linkBoundary(); - }*/ - - // Because some algorithms do not expect sparse edge buffers. - //m_unifiedMesh->compactEdges(); - - // In case we messed up: - //m_unifiedMesh->linkBoundary(); - - getBoundaryEdges(m_unifiedMesh.ptr(), boundaryEdges); - - boundaryCount = boundaryEdges.count(); - nvDebugCheck(boundaryCount == 1); - - //exportMesh(m_unifiedMesh.ptr(), "debug_hole_filled.obj"); - - return boundaryCount == 1; -} - - -// Transfer parameterization from unified mesh to chart mesh. -void Chart::transferParameterization() { - nvDebugCheck(!m_isVertexMapped); - - uint vertexCount = m_chartMesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(v); - HalfEdge::Vertex * unifiedVertex = m_unifiedMesh->vertexAt(mapChartVertexToUnifiedVertex(v)); - vertex->tex = unifiedVertex->tex; - } -} - -float Chart::computeSurfaceArea() const { - return nv::computeSurfaceArea(m_chartMesh.ptr()) * scale; -} - -float Chart::computeParametricArea() const { - // This only makes sense in parameterized meshes. - nvDebugCheck(m_isDisk); - nvDebugCheck(!m_isVertexMapped); - - return nv::computeParametricArea(m_chartMesh.ptr()); -} - -Vector2 Chart::computeParametricBounds() const { - // This only makes sense in parameterized meshes. - nvDebugCheck(m_isDisk); - nvDebugCheck(!m_isVertexMapped); - - Box bounds; - bounds.clearBounds(); - - uint vertexCount = m_chartMesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) { - HalfEdge::Vertex * vertex = m_chartMesh->vertexAt(v); - bounds.addPointToBounds(Vector3(vertex->tex, 0)); - } - - return bounds.extents().xy(); -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/Atlas.h b/thirdparty/thekla_atlas/nvmesh/param/Atlas.h deleted file mode 100644 index 41cfaea9cb..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/Atlas.h +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> - -#pragma once -#ifndef NV_MESH_ATLAS_H -#define NV_MESH_ATLAS_H - -#include "nvcore/Array.h" -#include "nvcore/Ptr.h" -#include "nvmath/Vector.h" -#include "nvmesh/nvmesh.h" -#include "nvmesh/halfedge/Mesh.h" - - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - class Chart; - class MeshCharts; - class VertexMap; - - struct SegmentationSettings - { - SegmentationSettings(); - - float maxChartArea; - float maxBoundaryLength; - - float proxyFitMetricWeight; - float roundnessMetricWeight; - float straightnessMetricWeight; - float normalSeamMetricWeight; - float textureSeamMetricWeight; - }; - - - /// An atlas is a set of charts. - class Atlas - { - public: - - Atlas(); - ~Atlas(); - - uint meshCount() const { return m_meshChartsArray.count(); } - const MeshCharts * meshAt(uint i) const { return m_meshChartsArray[i]; } - MeshCharts * meshAt(uint i) { return m_meshChartsArray[i]; } - - uint chartCount() const; - const Chart * chartAt(uint i) const; - Chart * chartAt(uint i); - - // Add mesh charts and takes ownership. - void addMeshCharts(MeshCharts * meshCharts); - - void extractCharts(const HalfEdge::Mesh * mesh); - void computeCharts(const HalfEdge::Mesh * mesh, const SegmentationSettings & settings, const Array<uint> & unchartedMaterialArray); - - - // Compute a trivial seamless texture similar to ZBrush. - //bool computeSeamlessTextureAtlas(bool groupFaces = true, bool scaleTiles = false, uint w = 1024, uint h = 1024); - - void parameterizeCharts(); - - // Pack charts in the smallest possible rectangle. - float packCharts(int quality, float texelArea, bool blockAlign, bool conservative); - void setFailed() { failed = true; } - bool hasFailed() const { return failed; } - - private: - - bool failed; - Array<MeshCharts *> m_meshChartsArray; - - }; - - - // Set of charts corresponding to a single mesh. - class MeshCharts - { - public: - MeshCharts(const HalfEdge::Mesh * mesh); - ~MeshCharts(); - - uint chartCount() const { return m_chartArray.count(); } - uint vertexCount () const { return m_totalVertexCount; } - - const Chart * chartAt(uint i) const { return m_chartArray[i]; } - Chart * chartAt(uint i) { return m_chartArray[i]; } - - void computeVertexMap(const Array<uint> & unchartedMaterialArray); - - // Extract the charts of the input mesh. - void extractCharts(); - - // Compute charts using a simple segmentation algorithm. - void computeCharts(const SegmentationSettings & settings, const Array<uint> & unchartedMaterialArray); - - void parameterizeCharts(); - - uint faceChartAt(uint i) const { return m_faceChart[i]; } - uint faceIndexWithinChartAt(uint i) const { return m_faceIndex[i]; } - - uint vertexCountBeforeChartAt(uint i) const { return m_chartVertexCountPrefixSum[i]; } - - private: - - const HalfEdge::Mesh * m_mesh; - - Array<Chart *> m_chartArray; - - Array<uint> m_chartVertexCountPrefixSum; - uint m_totalVertexCount; - - Array<uint> m_faceChart; // the chart of every face of the input mesh. - Array<uint> m_faceIndex; // the index within the chart for every face of the input mesh. - }; - - - /// A chart is a connected set of faces with a certain topology (usually a disk). - class Chart - { - public: - - Chart(); - - void build(const HalfEdge::Mesh * originalMesh, const Array<uint> & faceArray); - void buildVertexMap(const HalfEdge::Mesh * originalMesh, const Array<uint> & unchartedMaterialArray); - - bool closeHoles(); - - bool isDisk() const { return m_isDisk; } - bool isVertexMapped() const { return m_isVertexMapped; } - - uint vertexCount() const { return m_chartMesh->vertexCount(); } - uint colocalVertexCount() const { return m_unifiedMesh->vertexCount(); } - - uint faceCount() const { return m_faceArray.count(); } - uint faceAt(uint i) const { return m_faceArray[i]; } - - const HalfEdge::Mesh * chartMesh() const { return m_chartMesh.ptr(); } - HalfEdge::Mesh * chartMesh() { return m_chartMesh.ptr(); } - const HalfEdge::Mesh * unifiedMesh() const { return m_unifiedMesh.ptr(); } - HalfEdge::Mesh * unifiedMesh() { return m_unifiedMesh.ptr(); } - - //uint vertexIndex(uint i) const { return m_vertexIndexArray[i]; } - - uint mapChartVertexToOriginalVertex(uint i) const { return m_chartToOriginalMap[i]; } - uint mapChartVertexToUnifiedVertex(uint i) const { return m_chartToUnifiedMap[i]; } - - const Array<uint> & faceArray() const { return m_faceArray; } - - void transferParameterization(); - - float computeSurfaceArea() const; - float computeParametricArea() const; - Vector2 computeParametricBounds() const; - - - float scale = 1.0f; - uint vertexMapWidth; - uint vertexMapHeight; - - private: - - bool closeLoop(uint start, const Array<HalfEdge::Edge *> & loop); - - // Chart mesh. - AutoPtr<HalfEdge::Mesh> m_chartMesh; - AutoPtr<HalfEdge::Mesh> m_unifiedMesh; - - bool m_isDisk; - bool m_isVertexMapped; - - // List of faces of the original mesh that belong to this chart. - Array<uint> m_faceArray; - - // Map vertices of the chart mesh to vertices of the original mesh. - Array<uint> m_chartToOriginalMap; - - Array<uint> m_chartToUnifiedMap; - }; - -} // nv namespace - -#endif // NV_MESH_ATLAS_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.cpp b/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.cpp deleted file mode 100644 index bd2140c2f3..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.cpp +++ /dev/null @@ -1,1320 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "AtlasBuilder.h" -#include "Util.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" - -#include "nvmath/Matrix.inl" -#include "nvmath/Vector.inl" - -//#include "nvcore/IntroSort.h" -#include "nvcore/Array.inl" - -#include <algorithm> // std::sort - -#include <float.h> // FLT_MAX -#include <limits.h> // UINT_MAX - -using namespace nv; - -namespace -{ - - // Dummy implementation of a priority queue using sort at insertion. - // - Insertion is o(n) - // - Smallest element goes at the end, so that popping it is o(1). - // - Resorting is n*log(n) - // @@ Number of elements in the queue is usually small, and we'd have to rebalance often. I'm not sure it's worth implementing a heap. - // @@ Searcing at removal would remove the need for sorting when priorities change. - struct PriorityQueue - { - PriorityQueue(uint size = UINT_MAX) : maxSize(size) {} - - void push(float priority, uint face) { - uint i = 0; - const uint count = pairs.count(); - for (; i < count; i++) { - if (pairs[i].priority > priority) break; - } - - Pair p = { priority, face }; - pairs.insertAt(i, p); - - if (pairs.count() > maxSize) { - pairs.removeAt(0); - } - } - - // push face out of order, to be sorted later. - void push(uint face) { - Pair p = { 0.0f, face }; - pairs.append(p); - } - - uint pop() { - uint f = pairs.back().face; - pairs.pop_back(); - return f; - } - - void sort() { - //nv::sort(pairs); // @@ My intro sort appears to be much slower than it should! - std::sort(pairs.buffer(), pairs.buffer() + pairs.count()); - } - - void clear() { - pairs.clear(); - } - - uint count() const { return pairs.count(); } - - float firstPriority() const { return pairs.back().priority; } - - - const uint maxSize; - - struct Pair { - bool operator <(const Pair & p) const { return priority > p.priority; } // !! Sort in inverse priority order! - float priority; - uint face; - }; - - - Array<Pair> pairs; - }; - - static bool isNormalSeam(const HalfEdge::Edge * edge) { - return (edge->vertex->nor != edge->pair->next->vertex->nor || edge->next->vertex->nor != edge->pair->vertex->nor); - } - - static bool isTextureSeam(const HalfEdge::Edge * edge) { - return (edge->vertex->tex != edge->pair->next->vertex->tex || edge->next->vertex->tex != edge->pair->vertex->tex); - } - -} // namespace - - -struct nv::ChartBuildData -{ - ChartBuildData(int id) : id(id) { - planeNormal = Vector3(0); - centroid = Vector3(0); - coneAxis = Vector3(0); - coneAngle = 0; - area = 0; - boundaryLength = 0; - normalSum = Vector3(0); - centroidSum = Vector3(0); - } - - int id; - - // Proxy info: - Vector3 planeNormal; - Vector3 centroid; - Vector3 coneAxis; - float coneAngle; - - float area; - float boundaryLength; - Vector3 normalSum; - Vector3 centroidSum; - - Array<uint> seeds; // @@ These could be a pointers to the HalfEdge faces directly. - Array<uint> faces; - PriorityQueue candidates; -}; - - - -AtlasBuilder::AtlasBuilder(const HalfEdge::Mesh * m) : mesh(m), facesLeft(m->faceCount()) -{ - const uint faceCount = m->faceCount(); - faceChartArray.resize(faceCount, -1); - faceCandidateArray.resize(faceCount, -1); - - // @@ Floyd for the whole mesh is too slow. We could compute floyd progressively per patch as the patch grows. We need a better solution to compute most central faces. - //computeShortestPaths(); - - // Precompute edge lengths and face areas. - uint edgeCount = m->edgeCount(); - edgeLengths.resize(edgeCount); - - for (uint i = 0; i < edgeCount; i++) { - uint id = m->edgeAt(i)->id; - nvDebugCheck(id / 2 == i); - - edgeLengths[i] = m->edgeAt(i)->length(); - } - - faceAreas.resize(faceCount); - for (uint i = 0; i < faceCount; i++) { - faceAreas[i] = m->faceAt(i)->area(); - } -} - -AtlasBuilder::~AtlasBuilder() -{ - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - delete chartArray[i]; - } -} - - -void AtlasBuilder::markUnchartedFaces(const Array<uint> & unchartedFaces) -{ - const uint unchartedFaceCount = unchartedFaces.count(); - for (uint i = 0; i < unchartedFaceCount; i++){ - uint f = unchartedFaces[i]; - faceChartArray[f] = -2; - //faceCandidateArray[f] = -2; // @@ ? - - removeCandidate(f); - } - - nvDebugCheck(facesLeft >= unchartedFaceCount); - facesLeft -= unchartedFaceCount; -} - - -void AtlasBuilder::computeShortestPaths() -{ - const uint faceCount = mesh->faceCount(); - shortestPaths.resize(faceCount*faceCount, FLT_MAX); - - // Fill edges: - for (uint i = 0; i < faceCount; i++) - { - shortestPaths[i*faceCount + i] = 0.0f; - - const HalfEdge::Face * face_i = mesh->faceAt(i); - Vector3 centroid_i = face_i->centroid(); - - for (HalfEdge::Face::ConstEdgeIterator it(face_i->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - if (!edge->isBoundary()) - { - const HalfEdge::Face * face_j = edge->pair->face; - - uint j = face_j->id; - Vector3 centroid_j = face_j->centroid(); - - shortestPaths[i*faceCount + j] = shortestPaths[j*faceCount + i] = length(centroid_i - centroid_j); - } - } - } - - // Use Floyd-Warshall algorithm to compute all paths: - for (uint k = 0; k < faceCount; k++) - { - for (uint i = 0; i < faceCount; i++) - { - for (uint j = 0; j < faceCount; j++) - { - shortestPaths[i*faceCount + j] = min(shortestPaths[i*faceCount + j], shortestPaths[i*faceCount + k]+shortestPaths[k*faceCount + j]); - } - } - } -} - - -void AtlasBuilder::placeSeeds(float threshold, uint maxSeedCount) -{ - // Instead of using a predefiened number of seeds: - // - Add seeds one by one, growing chart until a certain treshold. - // - Undo charts and restart growing process. - - // @@ How can we give preference to faces far from sharp features as in the LSCM paper? - // - those points can be found using a simple flood filling algorithm. - // - how do we weight the probabilities? - - for (uint i = 0; i < maxSeedCount; i++) - { - if (facesLeft == 0) { - // No faces left, stop creating seeds. - break; - } - - createRandomChart(threshold); - } -} - - -void AtlasBuilder::createRandomChart(float threshold) -{ - ChartBuildData * chart = new ChartBuildData(chartArray.count()); - chartArray.append(chart); - - // Pick random face that is not used by any chart yet. - uint randomFaceIdx = rand.getRange(facesLeft - 1); - uint i = 0; - for (uint f = 0; f != randomFaceIdx; f++, i++) - { - while (faceChartArray[i] != -1) i++; - } - while (faceChartArray[i] != -1) i++; - - chart->seeds.append(i); - - addFaceToChart(chart, i, true); - - // Grow the chart as much as possible within the given threshold. - growChart(chart, threshold * 0.5f, facesLeft); - //growCharts(threshold - threshold * 0.75f / chartCount(), facesLeft); -} - -void AtlasBuilder::addFaceToChart(ChartBuildData * chart, uint f, bool recomputeProxy) -{ - // Add face to chart. - chart->faces.append(f); - - nvDebugCheck(faceChartArray[f] == -1); - faceChartArray[f] = chart->id; - - facesLeft--; - - // Update area and boundary length. - chart->area = evaluateChartArea(chart, f); - chart->boundaryLength = evaluateBoundaryLength(chart, f); - chart->normalSum = evaluateChartNormalSum(chart, f); - chart->centroidSum = evaluateChartCentroidSum(chart, f); - - if (recomputeProxy) { - // Update proxy and candidate's priorities. - updateProxy(chart); - } - - // Update candidates. - removeCandidate(f); - updateCandidates(chart, f); - updatePriorities(chart); -} - -// @@ Get N best candidates in one pass. -const AtlasBuilder::Candidate & AtlasBuilder::getBestCandidate() const -{ - uint best = 0; - float bestCandidateMetric = FLT_MAX; - - const uint candidateCount = candidateArray.count(); - nvCheck(candidateCount > 0); - - for (uint i = 0; i < candidateCount; i++) - { - const Candidate & candidate = candidateArray[i]; - - if (candidate.metric < bestCandidateMetric) { - bestCandidateMetric = candidate.metric; - best = i; - } - } - - return candidateArray[best]; -} - - -// Returns true if any of the charts can grow more. -bool AtlasBuilder::growCharts(float threshold, uint faceCount) -{ -#if 1 // Using one global list. - - faceCount = min(faceCount, facesLeft); - - for (uint i = 0; i < faceCount; i++) - { - const Candidate & candidate = getBestCandidate(); - - if (candidate.metric > threshold) { - return false; // Can't grow more. - } - - addFaceToChart(candidate.chart, candidate.face); - } - - return facesLeft != 0; // Can continue growing. - -#else // Using one list per chart. - bool canGrowMore = false; - - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - if (growChart(chartArray[i], threshold, faceCount)) - { - canGrowMore = true; - } - } - - return canGrowMore; -#endif -} - -bool AtlasBuilder::growChart(ChartBuildData * chart, float threshold, uint faceCount) -{ - // Try to add faceCount faces within threshold to chart. - for (uint i = 0; i < faceCount; ) - { - if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) - { - return false; - } - - uint f = chart->candidates.pop(); - if (faceChartArray[f] == -1) - { - addFaceToChart(chart, f); - i++; - } - } - - if (chart->candidates.count() == 0 || chart->candidates.firstPriority() > threshold) - { - return false; - } - - return true; -} - - -void AtlasBuilder::resetCharts() -{ - const uint faceCount = mesh->faceCount(); - for (uint i = 0; i < faceCount; i++) - { - faceChartArray[i] = -1; - faceCandidateArray[i] = -1; - } - - facesLeft = faceCount; - - candidateArray.clear(); - - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - ChartBuildData * chart = chartArray[i]; - - const uint seed = chart->seeds.back(); - - chart->area = 0.0f; - chart->boundaryLength = 0.0f; - chart->normalSum = Vector3(0); - chart->centroidSum = Vector3(0); - - chart->faces.clear(); - chart->candidates.clear(); - - addFaceToChart(chart, seed); - } -} - - -void AtlasBuilder::updateCandidates(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - - // Traverse neighboring faces, add the ones that do not belong to any chart yet. - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current()->pair; - - if (!edge->isBoundary()) - { - uint f = edge->face->id; - - if (faceChartArray[f] == -1) - { - chart->candidates.push(f); - } - } - } -} - - -void AtlasBuilder::updateProxies() -{ - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - updateProxy(chartArray[i]); - } -} - - -namespace { - - float absoluteSum(Vector4::Arg v) - { - return fabs(v.x) + fabs(v.y) + fabs(v.z) + fabs(v.w); - } - - //#pragma message(NV_FILE_LINE "FIXME: Using the c=cos(teta) substitution, the equation system becomes linear and we can avoid the newton solver.") - - struct ConeFitting - { - ConeFitting(const HalfEdge::Mesh * m, float g, float tf, float tx) : mesh(m), gamma(g), tolf(tf), tolx(tx), F(0), D(0), H(0) { - } - - void addTerm(Vector3 N, float A) - { - const float c = cosf(X.w); - const float s = sinf(X.w); - const float tmp = dot(X.xyz(), N) - c; - - F += tmp * tmp; - - D.x += 2 * X.x * tmp; - D.y += 2 * X.y * tmp; - D.z += 2 * X.z * tmp; - D.w += 2 * s * tmp; - - H(0,0) = 2 * X.x * N.x + 2 * tmp; - H(0,1) = 2 * X.x * N.y; - H(0,2) = 2 * X.x * N.z; - H(0,3) = 2 * X.x * s; - - H(1,0) = 2 * X.y * N.x; - H(1,1) = 2 * X.y * N.y + 2 * tmp; - H(1,2) = 2 * X.y * N.z; - H(1,3) = 2 * X.y * s; - - H(2,0) = 2 * X.z * N.x; - H(2,1) = 2 * X.z * N.y; - H(2,2) = 2 * X.z * N.z + 2 * tmp; - H(2,3) = 2 * X.z * s; - - H(3,0) = 2 * s * N.x; - H(3,1) = 2 * s * N.y; - H(3,2) = 2 * s * N.z; - H(3,3) = 2 * s * s + 2 * c * tmp; - } - - Vector4 solve(ChartBuildData * chart, Vector4 start) - { - const uint faceCount = chart->faces.count(); - - X = start; - - Vector4 dX; - - do { - for (uint i = 0; i < faceCount; i++) - { - const HalfEdge::Face * face = mesh->faceAt(chart->faces[i]); - - addTerm(face->normal(), face->area()); - } - - Vector4 dX; - //solveKramer(H, D, &dX); - solveLU(H, D, &dX); - - // @@ Do a full newton step and reduce by half if F doesn't decrease. - X -= gamma * dX; - - // Constrain normal to be normalized. - X = Vector4(normalize(X.xyz()), X.w); - - } while(absoluteSum(D) > tolf || absoluteSum(dX) > tolx); - - return X; - } - - HalfEdge::Mesh const * const mesh; - const float gamma; - const float tolf; - const float tolx; - - Vector4 X; - - float F; - Vector4 D; - Matrix H; - }; - - // Unnormalized face normal assuming it's a triangle. - static Vector3 triangleNormal(const HalfEdge::Face * face) - { - Vector3 p0 = face->edge->vertex->pos; - Vector3 p1 = face->edge->next->vertex->pos; - Vector3 p2 = face->edge->next->next->vertex->pos; - - Vector3 e0 = p2 - p0; - Vector3 e1 = p1 - p0; - - return normalizeSafe(cross(e0, e1), Vector3(0), 0.0f); - } - - static Vector3 triangleNormalAreaScaled(const HalfEdge::Face * face) - { - Vector3 p0 = face->edge->vertex->pos; - Vector3 p1 = face->edge->next->vertex->pos; - Vector3 p2 = face->edge->next->next->vertex->pos; - - Vector3 e0 = p2 - p0; - Vector3 e1 = p1 - p0; - - return cross(e0, e1); - } - - // Average of the edge midpoints weighted by the edge length. - // I want a point inside the triangle, but closer to the cirumcenter. - static Vector3 triangleCenter(const HalfEdge::Face * face) - { - Vector3 p0 = face->edge->vertex->pos; - Vector3 p1 = face->edge->next->vertex->pos; - Vector3 p2 = face->edge->next->next->vertex->pos; - - float l0 = length(p1 - p0); - float l1 = length(p2 - p1); - float l2 = length(p0 - p2); - - Vector3 m0 = (p0 + p1) * l0 / (l0 + l1 + l2); - Vector3 m1 = (p1 + p2) * l1 / (l0 + l1 + l2); - Vector3 m2 = (p2 + p0) * l2 / (l0 + l1 + l2); - - return m0 + m1 + m2; - } - -} // namespace - -void AtlasBuilder::updateProxy(ChartBuildData * chart) -{ - //#pragma message(NV_FILE_LINE "TODO: Use best fit plane instead of average normal.") - - chart->planeNormal = normalizeSafe(chart->normalSum, Vector3(0), 0.0f); - chart->centroid = chart->centroidSum / float(chart->faces.count()); - - //#pragma message(NV_FILE_LINE "TODO: Experiment with conic fitting.") - - // F = (Nc*Nt - cos Oc)^2 = (x*Nt_x + y*Nt_y + z*Nt_z - cos w)^2 - // dF/dx = 2 * x * (x*Nt_x + y*Nt_y + z*Nt_z - cos w) - // dF/dy = 2 * y * (x*Nt_x + y*Nt_y + z*Nt_z - cos w) - // dF/dz = 2 * z * (x*Nt_x + y*Nt_y + z*Nt_z - cos w) - // dF/dw = 2 * sin w * (x*Nt_x + y*Nt_y + z*Nt_z - cos w) - - // JacobianMatrix({ - // 2 * x * (x*Nt_x + y*Nt_y + z*Nt_z - Cos(w)), - // 2 * y * (x*Nt_x + y*Nt_y + z*Nt_z - Cos(w)), - // 2 * z * (x*Nt_x + y*Nt_y + z*Nt_z - Cos(w)), - // 2 * Sin(w) * (x*Nt_x + y*Nt_y + z*Nt_z - Cos(w))}, {x,y,z,w}) - - // H[0,0] = 2 * x * Nt_x + 2 * (x*Nt_x + y*Nt_y + z*Nt_z - cos(w)); - // H[0,1] = 2 * x * Nt_y; - // H[0,2] = 2 * x * Nt_z; - // H[0,3] = 2 * x * sin(w); - - // H[1,0] = 2 * y * Nt_x; - // H[1,1] = 2 * y * Nt_y + 2 * (x*Nt_x + y*Nt_y + z*Nt_z - cos(w)); - // H[1,2] = 2 * y * Nt_z; - // H[1,3] = 2 * y * sin(w); - - // H[2,0] = 2 * z * Nt_x; - // H[2,1] = 2 * z * Nt_y; - // H[2,2] = 2 * z * Nt_z + 2 * (x*Nt_x + y*Nt_y + z*Nt_z - cos(w)); - // H[2,3] = 2 * z * sin(w); - - // H[3,0] = 2 * sin(w) * Nt_x; - // H[3,1] = 2 * sin(w) * Nt_y; - // H[3,2] = 2 * sin(w) * Nt_z; - // H[3,3] = 2 * sin(w) * sin(w) + 2 * cos(w) * (x*Nt_x + y*Nt_y + z*Nt_z - cos(w)); - - // @@ Cone fitting might be quite slow. - - /*ConeFitting coneFitting(mesh, 0.1f, 0.001f, 0.001f); - - Vector4 start = Vector4(chart->coneAxis, chart->coneAngle); - Vector4 solution = coneFitting.solve(chart, start); - - chart->coneAxis = solution.xyz(); - chart->coneAngle = solution.w;*/ -} - - - -bool AtlasBuilder::relocateSeeds() -{ - bool anySeedChanged = false; - - const uint chartCount = chartArray.count(); - for (uint i = 0; i < chartCount; i++) - { - if (relocateSeed(chartArray[i])) - { - anySeedChanged = true; - } - } - - return anySeedChanged; -} - - -bool AtlasBuilder::relocateSeed(ChartBuildData * chart) -{ - Vector3 centroid = computeChartCentroid(chart); - - const uint N = 10; // @@ Hardcoded to 10? - PriorityQueue bestTriangles(N); - - // Find the first N triangles that fit the proxy best. - const uint faceCount = chart->faces.count(); - for (uint i = 0; i < faceCount; i++) - { - float priority = evaluateProxyFitMetric(chart, chart->faces[i]); - bestTriangles.push(priority, chart->faces[i]); - } - - // Of those, choose the most central triangle. - uint mostCentral; - float maxDistance = -1; - - const uint bestCount = bestTriangles.count(); - for (uint i = 0; i < bestCount; i++) - { - const HalfEdge::Face * face = mesh->faceAt(bestTriangles.pairs[i].face); - Vector3 faceCentroid = triangleCenter(face); - - float distance = length(centroid - faceCentroid); - - /*#pragma message(NV_FILE_LINE "TODO: Implement evaluateDistanceToBoundary.") - float distance = evaluateDistanceToBoundary(chart, bestTriangles.pairs[i].face);*/ - - if (distance > maxDistance) - { - maxDistance = distance; - mostCentral = bestTriangles.pairs[i].face; - } - } - nvDebugCheck(maxDistance >= 0); - - // In order to prevent k-means cyles we record all the previously chosen seeds. - uint index; - if (chart->seeds.find(mostCentral, &index)) - { - // Move new seed to the end of the seed array. - uint last = chart->seeds.count() - 1; - swap(chart->seeds[index], chart->seeds[last]); - return false; - } - else - { - // Append new seed. - chart->seeds.append(mostCentral); - return true; - } -} - -void AtlasBuilder::removeCandidate(uint f) -{ - int c = faceCandidateArray[f]; - if (c != -1) { - faceCandidateArray[f] = -1; - - if (c == candidateArray.count() - 1) { - candidateArray.popBack(); - } - else { - candidateArray.replaceWithLast(c); - faceCandidateArray[candidateArray[c].face] = c; - } - } -} - -void AtlasBuilder::updateCandidate(ChartBuildData * chart, uint f, float metric) -{ - if (faceCandidateArray[f] == -1) { - const uint index = candidateArray.count(); - faceCandidateArray[f] = index; - candidateArray.resize(index + 1); - candidateArray[index].face = f; - candidateArray[index].chart = chart; - candidateArray[index].metric = metric; - } - else { - int c = faceCandidateArray[f]; - nvDebugCheck(c != -1); - - Candidate & candidate = candidateArray[c]; - nvDebugCheck(candidate.face == f); - - if (metric < candidate.metric || chart == candidate.chart) { - candidate.metric = metric; - candidate.chart = chart; - } - } - -} - - -void AtlasBuilder::updatePriorities(ChartBuildData * chart) -{ - // Re-evaluate candidate priorities. - uint candidateCount = chart->candidates.count(); - for (uint i = 0; i < candidateCount; i++) - { - chart->candidates.pairs[i].priority = evaluatePriority(chart, chart->candidates.pairs[i].face); - - if (faceChartArray[chart->candidates.pairs[i].face] == -1) - { - updateCandidate(chart, chart->candidates.pairs[i].face, chart->candidates.pairs[i].priority); - } - } - - // Sort candidates. - chart->candidates.sort(); -} - - -// Evaluate combined metric. -float AtlasBuilder::evaluatePriority(ChartBuildData * chart, uint face) -{ - // Estimate boundary length and area: - float newBoundaryLength = evaluateBoundaryLength(chart, face); - float newChartArea = evaluateChartArea(chart, face); - - float F = evaluateProxyFitMetric(chart, face); - float C = evaluateRoundnessMetric(chart, face, newBoundaryLength, newChartArea); - float P = evaluateStraightnessMetric(chart, face); - - // Penalize faces that cross seams, reward faces that close seams or reach boundaries. - float N = evaluateNormalSeamMetric(chart, face); - float T = evaluateTextureSeamMetric(chart, face); - - //float R = evaluateCompletenessMetric(chart, face); - - //float D = evaluateDihedralAngleMetric(chart, face); - // @@ Add a metric based on local dihedral angle. - - // @@ Tweaking the normal and texture seam metrics. - // - Cause more impedance. Never cross 90 degree edges. - // - - - float cost = float( - settings.proxyFitMetricWeight * F + - settings.roundnessMetricWeight * C + - settings.straightnessMetricWeight * P + - settings.normalSeamMetricWeight * N + - settings.textureSeamMetricWeight * T); - - /*cost = settings.proxyFitMetricWeight * powf(F, settings.proxyFitMetricExponent); - cost = max(cost, settings.roundnessMetricWeight * powf(C, settings.roundnessMetricExponent)); - cost = max(cost, settings.straightnessMetricWeight * pow(P, settings.straightnessMetricExponent)); - cost = max(cost, settings.normalSeamMetricWeight * N); - cost = max(cost, settings.textureSeamMetricWeight * T);*/ - - // Enforce limits strictly: - if (newChartArea > settings.maxChartArea) cost = FLT_MAX; - if (newBoundaryLength > settings.maxBoundaryLength) cost = FLT_MAX; - - // Make sure normal seams are fully respected: - if (settings.normalSeamMetricWeight >= 1000 && N != 0) cost = FLT_MAX; - - nvCheck(isFinite(cost)); - return cost; -} - - -// Returns a value in [0-1]. -float AtlasBuilder::evaluateProxyFitMetric(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - Vector3 faceNormal = triangleNormal(face); - //return square(dot(chart->coneAxis, faceNormal) - cosf(chart->coneAngle)); - - // Use plane fitting metric for now: - //return square(1 - dot(faceNormal, chart->planeNormal)); // @@ normal deviations should be weighted by face area - return 1 - dot(faceNormal, chart->planeNormal); // @@ normal deviations should be weighted by face area - - // Find distance to chart. - /*Vector3 faceCentroid = face->centroid(); - - float dist = 0; - int count = 0; - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - if (!edge->isBoundary()) { - const HalfEdge::Face * neighborFace = edge->pair()->face(); - if (faceChartArray[neighborFace->id()] == chart->id) { - dist += length(neighborFace->centroid() - faceCentroid); - count++; - } - } - } - - dist /= (count * count); - - return (1 - dot(faceNormal, chart->planeNormal)) * dist;*/ - - //return (1 - dot(faceNormal, chart->planeNormal)); -} - -float AtlasBuilder::evaluateDistanceToBoundary(ChartBuildData * chart, uint face) -{ -//#pragma message(NV_FILE_LINE "TODO: Evaluate distance to boundary metric.") - - // @@ This is needed for the seed relocation code. - // @@ This could provide a better roundness metric. - - return 0.0f; -} - -float AtlasBuilder::evaluateDistanceToSeed(ChartBuildData * chart, uint f) -{ - //const uint seed = chart->seeds.back(); - //const uint faceCount = mesh->faceCount(); - //return shortestPaths[seed * faceCount + f]; - - const HalfEdge::Face * seed = mesh->faceAt(chart->seeds.back()); - const HalfEdge::Face * face = mesh->faceAt(f); - return length(triangleCenter(seed) - triangleCenter(face)); -} - - -float AtlasBuilder::evaluateRoundnessMetric(ChartBuildData * chart, uint face, float newBoundaryLength, float newChartArea) -{ - // @@ D-charts use distance to seed. - // C(c,t) = pi * D(S_c,t)^2 / A_c - //return PI * square(evaluateDistanceToSeed(chart, face)) / chart->area; - //return PI * square(evaluateDistanceToSeed(chart, face)) / chart->area; - //return 2 * PI * evaluateDistanceToSeed(chart, face) / chart->boundaryLength; - - // Garland's Hierarchical Face Clustering paper uses ratio between boundary and area, which is easier to compute and might work as well: - // roundness = D^2/4*pi*A -> circle = 1, non circle greater than 1 - - //return square(newBoundaryLength) / (newChartArea * 4 * PI); - float roundness = square(chart->boundaryLength) / chart->area; - float newRoundness = square(newBoundaryLength) / newChartArea; - if (newRoundness > roundness) { - return square(newBoundaryLength) / (newChartArea * 4 * PI); - } - else { - // Offer no impedance to faces that improve roundness. - return 0; - } - - //return square(newBoundaryLength) / (4 * PI * newChartArea); - //return clamp(1 - (4 * PI * newChartArea) / square(newBoundaryLength), 0.0f, 1.0f); - - // Use the ratio between the new roundness vs. the previous roundness. - // - If we use the absolute metric, when the initial face is very long, then it's hard to make any progress. - //return (square(newBoundaryLength) * chart->area) / (square(chart->boundaryLength) * newChartArea); - //return (4 * PI * newChartArea) / square(newBoundaryLength) - (4 * PI * chart->area) / square(chart->boundaryLength); - - //if (square(newBoundaryLength) * chart->area) / (square(chart->boundaryLength) * newChartArea); - -} - -float AtlasBuilder::evaluateStraightnessMetric(ChartBuildData * chart, uint f) -{ - float l_out = 0.0f; - float l_in = 0.0f; - - const HalfEdge::Face * face = mesh->faceAt(f); - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - //float l = edge->length(); - float l = edgeLengths[edge->id/2]; - - if (edge->isBoundary()) - { - l_out += l; - } - else - { - uint neighborFaceId = edge->pair->face->id; - if (faceChartArray[neighborFaceId] != chart->id) { - l_out += l; - } - else { - l_in += l; - } - } - } - nvDebugCheck(l_in != 0.0f); // Candidate face must be adjacent to chart. @@ This is not true if the input mesh has zero-length edges. - - //return l_out / l_in; - float ratio = (l_out - l_in) / (l_out + l_in); - //if (ratio < 0) ratio *= 10; // Encourage closing gaps. - return min(ratio, 0.0f); // Only use the straightness metric to close gaps. - //return ratio; -} - - -float AtlasBuilder::evaluateNormalSeamMetric(ChartBuildData * chart, uint f) -{ - float seamFactor = 0.0f; - float totalLength = 0.0f; - - const HalfEdge::Face * face = mesh->faceAt(f); - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - if (edge->isBoundary()) { - continue; - } - - const uint neighborFaceId = edge->pair->face->id; - if (faceChartArray[neighborFaceId] != chart->id) { - continue; - } - - //float l = edge->length(); - float l = edgeLengths[edge->id/2]; - - totalLength += l; - - if (!edge->isSeam()) { - continue; - } - - // Make sure it's a normal seam. - if (isNormalSeam(edge)) - { - float d0 = clamp(dot(edge->vertex->nor, edge->pair->next->vertex->nor), 0.0f, 1.0f); - float d1 = clamp(dot(edge->next->vertex->nor, edge->pair->vertex->nor), 0.0f, 1.0f); - //float a0 = clamp(acosf(d0) / (PI/2), 0.0f, 1.0f); - //float a1 = clamp(acosf(d1) / (PI/2), 0.0f, 1.0f); - //l *= (a0 + a1) * 0.5f; - - l *= 1 - (d0 + d1) * 0.5f; - - seamFactor += l; - } - } - - if (seamFactor == 0) return 0.0f; - return seamFactor / totalLength; -} - - -float AtlasBuilder::evaluateTextureSeamMetric(ChartBuildData * chart, uint f) -{ - float seamLength = 0.0f; - //float newSeamLength = 0.0f; - //float oldSeamLength = 0.0f; - float totalLength = 0.0f; - - const HalfEdge::Face * face = mesh->faceAt(f); - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - /*float l = edge->length(); - totalLength += l; - - if (edge->isBoundary() || !edge->isSeam()) { - continue; - } - - // Make sure it's a texture seam. - if (isTextureSeam(edge)) - { - uint neighborFaceId = edge->pair()->face()->id(); - if (faceChartArray[neighborFaceId] != chart->id) { - newSeamLength += l; - } - else { - oldSeamLength += l; - } - }*/ - - if (edge->isBoundary()) { - continue; - } - - const uint neighborFaceId = edge->pair->face->id; - if (faceChartArray[neighborFaceId] != chart->id) { - continue; - } - - //float l = edge->length(); - float l = edgeLengths[edge->id/2]; - totalLength += l; - - if (!edge->isSeam()) { - continue; - } - - // Make sure it's a texture seam. - if (isTextureSeam(edge)) - { - seamLength += l; - } - } - - if (seamLength == 0.0f) { - return 0.0f; // Avoid division by zero. - } - - return seamLength / totalLength; -} - - -float AtlasBuilder::evaluateSeamMetric(ChartBuildData * chart, uint f) -{ - float newSeamLength = 0.0f; - float oldSeamLength = 0.0f; - float totalLength = 0.0f; - - const HalfEdge::Face * face = mesh->faceAt(f); - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - //float l = edge->length(); - float l = edgeLengths[edge->id/2]; - - if (edge->isBoundary()) - { - newSeamLength += l; - } - else - { - if (edge->isSeam()) - { - uint neighborFaceId = edge->pair->face->id; - if (faceChartArray[neighborFaceId] != chart->id) { - newSeamLength += l; - } - else { - oldSeamLength += l; - } - } - } - - totalLength += l; - } - - return (newSeamLength - oldSeamLength) / totalLength; -} - - -float AtlasBuilder::evaluateChartArea(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - //return chart->area + face->area(); - return chart->area + faceAreas[face->id]; -} - - -float AtlasBuilder::evaluateBoundaryLength(ChartBuildData * chart, uint f) -{ - float boundaryLength = chart->boundaryLength; - - // Add new edges, subtract edges shared with the chart. - const HalfEdge::Face * face = mesh->faceAt(f); - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - //float edgeLength = edge->length(); - float edgeLength = edgeLengths[edge->id/2]; - - if (edge->isBoundary()) - { - boundaryLength += edgeLength; - } - else - { - uint neighborFaceId = edge->pair->face->id; - if (faceChartArray[neighborFaceId] != chart->id) { - boundaryLength += edgeLength; - } - else { - boundaryLength -= edgeLength; - } - } - } - //nvDebugCheck(boundaryLength >= 0); - - return max(0.0f, boundaryLength); // @@ Hack! -} - -Vector3 AtlasBuilder::evaluateChartNormalSum(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - return chart->normalSum + triangleNormalAreaScaled(face); -} - -Vector3 AtlasBuilder::evaluateChartCentroidSum(ChartBuildData * chart, uint f) -{ - const HalfEdge::Face * face = mesh->faceAt(f); - return chart->centroidSum + face->centroid(); -} - - -Vector3 AtlasBuilder::computeChartCentroid(const ChartBuildData * chart) -{ - Vector3 centroid(0); - - const uint faceCount = chart->faces.count(); - for (uint i = 0; i < faceCount; i++) - { - const HalfEdge::Face * face = mesh->faceAt(chart->faces[i]); - centroid += triangleCenter(face); - } - - return centroid / float(faceCount); -} - - -void AtlasBuilder::fillHoles(float threshold) -{ - while (facesLeft > 0) - { - createRandomChart(threshold); - } -} - - -void AtlasBuilder::mergeChart(ChartBuildData * owner, ChartBuildData * chart, float sharedBoundaryLength) -{ - const uint faceCount = chart->faces.count(); - for (uint i = 0; i < faceCount; i++) - { - uint f = chart->faces[i]; - - nvDebugCheck(faceChartArray[f] == chart->id); - faceChartArray[f] = owner->id; - - owner->faces.append(f); - } - - // Update adjacencies? - - owner->area += chart->area; - owner->boundaryLength += chart->boundaryLength - sharedBoundaryLength; - - owner->normalSum += chart->normalSum; - owner->centroidSum += chart->centroidSum; - - updateProxy(owner); -} - -void AtlasBuilder::mergeCharts() -{ - Array<float> sharedBoundaryLengths; - - const uint chartCount = chartArray.count(); - for (int c = chartCount-1; c >= 0; c--) - { - sharedBoundaryLengths.clear(); - sharedBoundaryLengths.resize(chartCount, 0.0f); - - ChartBuildData * chart = chartArray[c]; - - float externalBoundary = 0.0f; - - const uint faceCount = chart->faces.count(); - for (uint i = 0; i < faceCount; i++) - { - uint f = chart->faces[i]; - const HalfEdge::Face * face = mesh->faceAt(f); - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - //float l = edge->length(); - float l = edgeLengths[edge->id/2]; - - if (edge->isBoundary()) { - externalBoundary += l; - } - else { - uint neighborFace = edge->pair->face->id; - uint neighborChart = faceChartArray[neighborFace]; - - if (neighborChart != c) { - if ((edge->isSeam() && (isNormalSeam(edge) || isTextureSeam(edge))) || neighborChart == -2) { - externalBoundary += l; - } - else { - sharedBoundaryLengths[neighborChart] += l; - } - } - } - } - } - - for (int cc = chartCount-1; cc >= 0; cc--) - { - if (cc == c) - continue; - - ChartBuildData * chart2 = chartArray[cc]; - if (chart2 == NULL) - continue; - - if (sharedBoundaryLengths[cc] > 0.8 * max(0.0f, chart->boundaryLength - externalBoundary)) { - - // Try to avoid degenerate configurations. - if (chart2->boundaryLength > sharedBoundaryLengths[cc]) - { - if (dot(chart2->planeNormal, chart->planeNormal) > -0.25) { - mergeChart(chart2, chart, sharedBoundaryLengths[cc]); - delete chart; - chartArray[c] = NULL; - break; - } - } - } - - if (sharedBoundaryLengths[cc] > 0.20 * max(0.0f, chart->boundaryLength - externalBoundary)) { - - // Compare proxies. - if (dot(chart2->planeNormal, chart->planeNormal) > 0) { - mergeChart(chart2, chart, sharedBoundaryLengths[cc]); - delete chart; - chartArray[c] = NULL; - break; - } - } - } - } - - // Remove deleted charts. - for (int c = 0; c < I32(chartArray.count()); /*do not increment if removed*/) - { - if (chartArray[c] == NULL) { - chartArray.removeAt(c); - - // Update faceChartArray. - const uint faceCount = faceChartArray.count(); - for (uint i = 0; i < faceCount; i++) { - nvDebugCheck (faceChartArray[i] != -1); - nvDebugCheck (faceChartArray[i] != c); - nvDebugCheck (faceChartArray[i] <= I32(chartArray.count())); - - if (faceChartArray[i] > c) { - faceChartArray[i]--; - } - } - } - else { - chartArray[c]->id = c; - c++; - } - } -} - - - -const Array<uint> & AtlasBuilder::chartFaces(uint i) const -{ - return chartArray[i]->faces; -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.h b/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.h deleted file mode 100644 index f25c724f7e..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasBuilder.h +++ /dev/null @@ -1,111 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MESH_ATLASBUILDER_H -#define NV_MESH_ATLASBUILDER_H - -#include "Atlas.h" - -#include "nvmath/Vector.h" -#include "nvmath/Random.h" -#include "nvmesh/nvmesh.h" - -#include "nvcore/Array.h" -#include "nvcore/BitArray.h" - - - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - struct ChartBuildData; - - struct AtlasBuilder - { - AtlasBuilder(const HalfEdge::Mesh * m); - ~AtlasBuilder(); - - void markUnchartedFaces(const Array<uint> & unchartedFaces); - - void computeShortestPaths(); - - void placeSeeds(float threshold, uint maxSeedCount); - void createRandomChart(float threshold); - - void addFaceToChart(ChartBuildData * chart, uint f, bool recomputeProxy=false); - - bool growCharts(float threshold, uint faceCount); - bool growChart(ChartBuildData * chart, float threshold, uint faceCount); - - void resetCharts(); - - void updateCandidates(ChartBuildData * chart, uint face); - - void updateProxies(); - void updateProxy(ChartBuildData * chart); - - bool relocateSeeds(); - bool relocateSeed(ChartBuildData * chart); - - void updatePriorities(ChartBuildData * chart); - - float evaluatePriority(ChartBuildData * chart, uint face); - float evaluateProxyFitMetric(ChartBuildData * chart, uint face); - float evaluateDistanceToBoundary(ChartBuildData * chart, uint face); - float evaluateDistanceToSeed(ChartBuildData * chart, uint face); - float evaluateRoundnessMetric(ChartBuildData * chart, uint face, float newBoundaryLength, float newChartArea); - float evaluateStraightnessMetric(ChartBuildData * chart, uint face); - - float evaluateNormalSeamMetric(ChartBuildData * chart, uint f); - float evaluateTextureSeamMetric(ChartBuildData * chart, uint f); - float evaluateSeamMetric(ChartBuildData * chart, uint f); - - float evaluateChartArea(ChartBuildData * chart, uint f); - float evaluateBoundaryLength(ChartBuildData * chart, uint f); - Vector3 evaluateChartNormalSum(ChartBuildData * chart, uint f); - Vector3 evaluateChartCentroidSum(ChartBuildData * chart, uint f); - - Vector3 computeChartCentroid(const ChartBuildData * chart); - - - void fillHoles(float threshold); - void mergeCharts(); - - // @@ Cleanup. - struct Candidate { - uint face; - ChartBuildData * chart; - float metric; - }; - - const Candidate & getBestCandidate() const; - void removeCandidate(uint f); - void updateCandidate(ChartBuildData * chart, uint f, float metric); - - void mergeChart(ChartBuildData * owner, ChartBuildData * chart, float sharedBoundaryLength); - - - uint chartCount() const { return chartArray.count(); } - const Array<uint> & chartFaces(uint i) const; - - const HalfEdge::Mesh * mesh; - uint facesLeft; - Array<int> faceChartArray; - Array<ChartBuildData *> chartArray; - Array<float> shortestPaths; - - Array<float> edgeLengths; - Array<float> faceAreas; - - Array<Candidate> candidateArray; // - Array<uint> faceCandidateArray; // Map face index to candidate index. - - MTRand rand; - - SegmentationSettings settings; - }; - -} // nv namespace - -#endif // NV_MESH_ATLASBUILDER_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp deleted file mode 100644 index 11e635db17..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.cpp +++ /dev/null @@ -1,1401 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "AtlasPacker.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/param/Atlas.h" -#include "nvmesh/param/Util.h" -#include "nvmesh/raster/Raster.h" - -#include "nvmath/Vector.inl" -#include "nvmath/ConvexHull.h" -#include "nvmath/Color.h" -#include "nvmath/ftoi.h" - -#include "nvcore/StrLib.h" // debug -#include "nvcore/StdStream.h" // fileOpen - -#include <float.h> // FLT_MAX -#include <limits.h> // UINT_MAX - -using namespace nv; - -#define DEBUG_OUTPUT 0 - -#if DEBUG_OUTPUT - -#include "nvimage/ImageIO.h" - -namespace -{ - const uint TGA_TYPE_GREY = 3; - const uint TGA_TYPE_RGB = 2; - const uint TGA_ORIGIN_UPPER = 0x20; - -#pragma pack(push, 1) - struct TgaHeader { - uint8 id_length; - uint8 colormap_type; - uint8 image_type; - uint16 colormap_index; - uint16 colormap_length; - uint8 colormap_size; - uint16 x_origin; - uint16 y_origin; - uint16 width; - uint16 height; - uint8 pixel_size; - uint8 flags; - - enum { Size = 18 }; //const static int SIZE = 18; - }; -#pragma pack(pop) - - static void outputDebugBitmap(const char * fileName, const BitMap & bitmap, int w, int h) - { - FILE * fp = fileOpen(fileName, "wb"); - if (fp == NULL) return; - - nvStaticCheck(sizeof(TgaHeader) == TgaHeader::Size); - TgaHeader tga; - tga.id_length = 0; - tga.colormap_type = 0; - tga.image_type = TGA_TYPE_GREY; - - tga.colormap_index = 0; - tga.colormap_length = 0; - tga.colormap_size = 0; - - tga.x_origin = 0; - tga.y_origin = 0; - tga.width = w; - tga.height = h; - tga.pixel_size = 8; - tga.flags = TGA_ORIGIN_UPPER; - - fwrite(&tga, sizeof(TgaHeader), 1, fp); - - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - uint8 color = bitmap.bitAt(i, j) ? 0xFF : 0x0; - fwrite(&color, 1, 1, fp); - } - } - - fclose(fp); - } - - static void outputDebugImage(const char * fileName, const Image & bitmap, int w, int h) - { - FILE * fp = fileOpen(fileName, "wb"); - if (fp == NULL) return; - - nvStaticCheck(sizeof(TgaHeader) == TgaHeader::Size); - TgaHeader tga; - tga.id_length = 0; - tga.colormap_type = 0; - tga.image_type = TGA_TYPE_RGB; - - tga.colormap_index = 0; - tga.colormap_length = 0; - tga.colormap_size = 0; - - tga.x_origin = 0; - tga.y_origin = 0; - tga.width = w; - tga.height = h; - tga.pixel_size = 24; - tga.flags = TGA_ORIGIN_UPPER; - - fwrite(&tga, sizeof(TgaHeader), 1, fp); - - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - Color32 color = bitmap.pixel(i, j); - fwrite(&color.r, 1, 1, fp); - fwrite(&color.g, 1, 1, fp); - fwrite(&color.b, 1, 1, fp); - } - } - - fclose(fp); - } -} - -#endif // DEBUG_OUTPUT - -inline int align(int x, int a) { - //return a * ((x + a - 1) / a); - //return (x + a - 1) & -a; - return (x + a - 1) & ~(a - 1); -} - -inline bool isAligned(int x, int a) { - return (x & (a - 1)) == 0; -} - - - -AtlasPacker::AtlasPacker(Atlas * atlas) : m_atlas(atlas), m_bitmap(256, 256) -{ - m_width = 0; - m_height = 0; - - // -- GODOT start -- - //m_debug_bitmap.allocate(256, 256); - //m_debug_bitmap.fill(Color32(0,0,0,0)); - // -- GODOT end -- -} - -AtlasPacker::~AtlasPacker() -{ -} - -// This should compute convex hull and use rotating calipers to find the best box. Currently it uses a brute force method. -static bool computeBoundingBox(Chart * chart, Vector2 * majorAxis, Vector2 * minorAxis, Vector2 * minCorner, Vector2 * maxCorner) -{ - // Compute list of boundary points. - Array<Vector2> points(16); - - HalfEdge::Mesh * mesh = chart->chartMesh(); - const uint vertexCount = mesh->vertexCount(); - - for (uint i = 0; i < vertexCount; i++) { - HalfEdge::Vertex * vertex = mesh->vertexAt(i); - if (vertex->isBoundary()) { - points.append(vertex->tex); - } - } - - // This is not valid anymore. The chart mesh may have multiple boundaries! - /*const HalfEdge::Vertex * vertex = findBoundaryVertex(chart->chartMesh()); - - // Traverse boundary. - const HalfEdge::Edge * const firstEdge = vertex->edge(); - const HalfEdge::Edge * edge = firstEdge; - do { - vertex = edge->vertex(); - - nvDebugCheck (vertex->isBoundary()); - points.append(vertex->tex); - - edge = edge->next(); - } while (edge != firstEdge);*/ - -#if 1 - Array<Vector2> hull; - if (points.size()==0) { - return false; - } - - convexHull(points, hull, 0.00001f); - - // @@ Ideally I should use rotating calipers to find the best box. Using brute force for now. - - float best_area = FLT_MAX; - Vector2 best_min; - Vector2 best_max; - Vector2 best_axis; - - const uint hullCount = hull.count(); - for (uint i = 0, j = hullCount-1; i < hullCount; j = i, i++) { - - if (equal(hull[i], hull[j])) { - continue; - } - - Vector2 axis = normalize(hull[i] - hull[j], 0.0f); - nvDebugCheck(isFinite(axis)); - - // Compute bounding box. - Vector2 box_min(FLT_MAX, FLT_MAX); - Vector2 box_max(-FLT_MAX, -FLT_MAX); - - for (uint v = 0; v < hullCount; v++) { - - Vector2 point = hull[v]; - - float x = dot(axis, point); - if (x < box_min.x) box_min.x = x; - if (x > box_max.x) box_max.x = x; - - float y = dot(Vector2(-axis.y, axis.x), point); - if (y < box_min.y) box_min.y = y; - if (y > box_max.y) box_max.y = y; - } - - // Compute box area. - float area = (box_max.x - box_min.x) * (box_max.y - box_min.y); - - if (area < best_area) { - best_area = area; - best_min = box_min; - best_max = box_max; - best_axis = axis; - } - } - - // Make sure the box contains all the input points since the convex hull is not 100% accurate. - /*const uint pointCount = points.count(); - for (uint v = 0; v < pointCount; v++) { - - Vector2 point = points[v]; - - float x = dot(best_axis, point); - if (x < best_min.x) best_min.x = x; - - float y = dot(Vector2(-best_axis.y, best_axis.x), point); - if (y < best_min.y) best_min.y = y; - }*/ - - // Consider all points, not only boundary points, in case the input chart is malformed. - for (uint i = 0; i < vertexCount; i++) { - HalfEdge::Vertex * vertex = mesh->vertexAt(i); - Vector2 point = vertex->tex; - - float x = dot(best_axis, point); - if (x < best_min.x) best_min.x = x; - if (x > best_max.x) best_max.x = x; - - float y = dot(Vector2(-best_axis.y, best_axis.x), point); - if (y < best_min.y) best_min.y = y; - if (y > best_max.y) best_max.y = y; - } - - *majorAxis = best_axis; - *minorAxis = Vector2(-best_axis.y, best_axis.x); - *minCorner = best_min; - *maxCorner = best_max; - -#else - // Approximate implementation: try 16 different directions and keep the best. - - const uint N = 16; - Vector2 axis[N]; - - float minAngle = 0; - float maxAngle = PI / 2; - - int best; - Vector2 mins[N]; - Vector2 maxs[N]; - - const int iterationCount = 1; - for (int j = 0; j < iterationCount; j++) - { - // Init predefined directions. - for (int i = 0; i < N; i++) - { - float angle = lerp(minAngle, maxAngle, float(i)/N); - axis[i].set(cosf(angle), sinf(angle)); - } - - // Compute box for each direction. - for (int i = 0; i < N; i++) - { - mins[i].set(FLT_MAX, FLT_MAX); - maxs[i].set(-FLT_MAX, -FLT_MAX); - } - - for (uint p = 0; p < points.count(); p++) - { - Vector2 point = points[p]; - - for (int i = 0; i < N; i++) - { - float x = dot(axis[i], point); - if (x < mins[i].x) mins[i].x = x; - if (x > maxs[i].x) maxs[i].x = x; - - float y = dot(Vector2(-axis[i].y, axis[i].x), point); - if (y < mins[i].y) mins[i].y = y; - if (y > maxs[i].y) maxs[i].y = y; - } - } - - // Find box with minimum area. - best = -1; - int second_best = -1; - float best_area = FLT_MAX; - float second_best_area = FLT_MAX; - - for (int i = 0; i < N; i++) - { - float area = (maxs[i].x - mins[i].x) * (maxs[i].y - mins[i].y); - - if (area < best_area) - { - second_best_area = best_area; - second_best = best; - - best_area = area; - best = i; - } - else if (area < second_best_area) - { - second_best_area = area; - second_best = i; - } - } - nvDebugCheck(best != -1); - nvDebugCheck(second_best != -1); - nvDebugCheck(best != second_best); - - if (j != iterationCount-1) - { - // Handle wrap-around during the first iteration. - if (j == 0) { - if (best == 0 && second_best == N-1) best = N; - if (best == N-1 && second_best == 0) second_best = N; - } - - if (best < second_best) swap(best, second_best); - - // Update angles. - float deltaAngle = (maxAngle - minAngle) / N; - maxAngle = minAngle + (best - 0.5f) * deltaAngle; - minAngle = minAngle + (second_best + 0.5f) * deltaAngle; - } - } - - // Compute major and minor axis, and origin. - *majorAxis = axis[best]; - *minorAxis = Vector2(-axis[best].y, axis[best].x); - *origin = mins[best]; - - // @@ If the parameterization is invalid, we could have an interior vertex outside the boundary. - // @@ In that case the returned bounding box would be incorrect. Compute updated bounds here. - /*for (uint p = 0; p < points.count(); p++) - { - Vector2 point = points[p]; - - for (int i = 0; i < N; i++) - { - float x = dot(*majorAxis, point); - float y = dot(*minorAxis, point); - } - }*/ -#endif - - return true; -} - - -void AtlasPacker::packCharts(int quality, float texelsPerUnit, bool blockAligned, bool conservative) -{ - const uint chartCount = m_atlas->chartCount(); - if (chartCount == 0) return; - - Array<float> chartOrderArray; - chartOrderArray.resize(chartCount); - - Array<Vector2> chartExtents; - chartExtents.resize(chartCount); - - float meshArea = 0; - for (uint c = 0; c < chartCount; c++) - { - Chart * chart = m_atlas->chartAt(c); - - if (!chart->isVertexMapped() && !chart->isDisk()) { - chartOrderArray[c] = 0; - - // Skip non-disks. - continue; - } - - Vector2 extents(0.0f); - - if (chart->isVertexMapped()) { - // Let's assume vertex maps are arranged in a rectangle. - //HalfEdge::Mesh * mesh = chart->chartMesh(); - - // Arrange vertices in a rectangle. - extents.x = float(chart->vertexMapWidth); - extents.y = float(chart->vertexMapHeight); - } - else { - // Compute surface area to sort charts. - float chartArea = chart->computeSurfaceArea(); - meshArea += chartArea; - //chartOrderArray[c] = chartArea; - - // Compute chart scale - float parametricArea = fabs(chart->computeParametricArea()); // @@ There doesn't seem to be anything preventing parametric area to be negative. - if (parametricArea < NV_EPSILON) { - // When the parametric area is too small we use a rough approximation to prevent divisions by very small numbers. - Vector2 bounds = chart->computeParametricBounds(); - parametricArea = bounds.x * bounds.y; - } - float scale = (chartArea / parametricArea) * texelsPerUnit; - if (parametricArea == 0) // < NV_EPSILON) - { - scale = 0; - } - nvCheck(isFinite(scale)); - - // Compute bounding box of chart. - Vector2 majorAxis, minorAxis, origin, end; - if (!computeBoundingBox(chart, &majorAxis, &minorAxis, &origin, &end)) { - m_atlas->setFailed(); - return; - } - - nvCheck(isFinite(majorAxis) && isFinite(minorAxis) && isFinite(origin)); - - // Sort charts by perimeter. @@ This is sometimes producing somewhat unexpected results. Is this right? - //chartOrderArray[c] = ((end.x - origin.x) + (end.y - origin.y)) * scale; - - // Translate, rotate and scale vertices. Compute extents. - HalfEdge::Mesh * mesh = chart->chartMesh(); - const uint vertexCount = mesh->vertexCount(); - for (uint i = 0; i < vertexCount; i++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(i); - - //Vector2 t = vertex->tex - origin; - Vector2 tmp; - tmp.x = dot(vertex->tex, majorAxis); - tmp.y = dot(vertex->tex, minorAxis); - tmp -= origin; - tmp *= scale; - if (tmp.x < 0 || tmp.y < 0) { - nvDebug("tmp: %f %f\n", tmp.x, tmp.y); - nvDebug("scale: %f\n", scale); - nvDebug("origin: %f %f\n", origin.x, origin.y); - nvDebug("majorAxis: %f %f\n", majorAxis.x, majorAxis.y); - nvDebug("minorAxis: %f %f\n", minorAxis.x, minorAxis.y); - // -- GODOT start -- - //nvDebugBreak(); - m_atlas->setFailed(); - return; - // -- GODOT end -- - } - //nvCheck(tmp.x >= 0 && tmp.y >= 0); - - vertex->tex = tmp; - - nvCheck(isFinite(vertex->tex.x) && isFinite(vertex->tex.y)); - - extents = max(extents, tmp); - } - nvDebugCheck(extents.x >= 0 && extents.y >= 0); - - // Limit chart size. - if (extents.x > 1024 || extents.y > 1024) { - float limit = max(extents.x, extents.y); - - scale = 1024 / (limit + 1); - - for (uint i = 0; i < vertexCount; i++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(i); - vertex->tex *= scale; - } - - extents *= scale; - - nvDebugCheck(extents.x <= 1024 && extents.y <= 1024); - } - - - // Scale the charts to use the entire texel area available. So, if the width is 0.1 we could scale it to 1 without increasing the lightmap usage and making a better - // use of it. In many cases this also improves the look of the seams, since vertices on the chart boundaries have more chances of being aligned with the texel centers. - - float scale_x = 1.0f; - float scale_y = 1.0f; - - float divide_x = 1.0f; - float divide_y = 1.0f; - - if (extents.x > 0) { - int cw = ftoi_ceil(extents.x); - - if (blockAligned) { - // Align all chart extents to 4x4 blocks, but taking padding into account. - if (conservative) { - cw = align(cw + 2, 4) - 2; - } - else { - cw = align(cw + 1, 4) - 1; - } - } - - scale_x = (float(cw) - NV_EPSILON); - divide_x = extents.x; - extents.x = float(cw); - } - - if (extents.y > 0) { - int ch = ftoi_ceil(extents.y); - - if (blockAligned) { - // Align all chart extents to 4x4 blocks, but taking padding into account. - if (conservative) { - ch = align(ch + 2, 4) - 2; - } - else { - ch = align(ch + 1, 4) - 1; - } - } - - scale_y = (float(ch) - NV_EPSILON); - divide_y = extents.y; - extents.y = float(ch); - } - - for (uint v = 0; v < vertexCount; v++) { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - - vertex->tex.x /= divide_x; - vertex->tex.y /= divide_y; - vertex->tex.x *= scale_x; - vertex->tex.y *= scale_y; - - nvCheck(isFinite(vertex->tex.x) && isFinite(vertex->tex.y)); - } - } - - chartExtents[c] = extents; - - // Sort charts by perimeter. - chartOrderArray[c] = extents.x + extents.y; - } - - // @@ We can try to improve compression of small charts by sorting them by proximity like we do with vertex samples. - // @@ How to do that? One idea: compute chart centroid, insert into grid, compute morton index of the cell, sort based on morton index. - // @@ We would sort by morton index, first, then quantize the chart sizes, so that all small charts have the same size, and sort by size preserving the morton order. - - //nvDebug("Sorting charts.\n"); - - // Sort charts by area. - m_radix.sort(chartOrderArray); - const uint32 * ranks = m_radix.ranks(); - - // Estimate size of the map based on the mesh surface area and given texel scale. - float texelCount = meshArea * square(texelsPerUnit) / 0.75f; // Assume 75% utilization. - if (texelCount < 1) texelCount = 1; - uint approximateExtent = nextPowerOfTwo(uint(sqrtf(texelCount))); - - //nvDebug("Init bitmap.\n"); - - // @@ Pack all charts smaller than a texel into a compact rectangle. - // @@ Start considering only 1x1 charts. Extend to 1xn charts later. - - /*for (uint i = 0; i < chartCount; i++) - { - uint c = ranks[chartCount - i - 1]; // largest chart first - - Chart * chart = m_atlas->chartAt(c); - - if (!chart->isDisk()) continue; - - if (iceil(chartExtents[c].x) == 1 && iceil(chartExtents[c].x) == 1) { - // @@ Add to - } - }*/ - - - - // Init bit map. - m_bitmap.clearAll(); - if (approximateExtent > m_bitmap.width()) { - m_bitmap.resize(approximateExtent, approximateExtent, false); - // -- GODOT start -- - //m_debug_bitmap.resize(approximateExtent, approximateExtent); - //m_debug_bitmap.fill(Color32(0,0,0,0)); - // -- GODOT end -- - } - - - int w = 0; - int h = 0; - -#if 1 - // Add sorted charts to bitmap. - for (uint i = 0; i < chartCount; i++) - { - uint c = ranks[chartCount - i - 1]; // largest chart first - - Chart * chart = m_atlas->chartAt(c); - - if (!chart->isVertexMapped() && !chart->isDisk()) continue; - - //float scale_x = 1; - //float scale_y = 1; - - BitMap chart_bitmap; - - if (chart->isVertexMapped()) { - // Init all bits to 1. - chart_bitmap.resize(ftoi_ceil(chartExtents[c].x), ftoi_ceil(chartExtents[c].y), /*initValue=*/true); - - // @@ Another alternative would be to try to map each vertex to a different texel trying to fill all the available unused texels. - } - else { - // @@ Add special cases for dot and line charts. @@ Lightmap rasterizer also needs to handle these special cases. - // @@ We could also have a special case for chart quads. If the quad surface <= 4 texels, align vertices with texel centers and do not add padding. May be very useful for foliage. - - // @@ In general we could reduce the padding of all charts by one texel by using a rasterizer that takes into account the 2-texel footprint of the tent bilinear filter. For example, - // if we have a chart that is less than 1 texel wide currently we add one texel to the left and one texel to the right creating a 3-texel-wide bitmap. However, if we know that the - // chart is only 1 texel wide we could align it so that it only touches the footprint of two texels: - - // | | <- Touches texels 0, 1 and 2. - // | | <- Only touches texels 0 and 1. - // \ \ / \ / / - // \ X X / - // \ / \ / \ / - // V V V - // 0 1 2 - - if (conservative) { - // Init all bits to 0. - chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 2, ftoi_ceil(chartExtents[c].y) + 2, /*initValue=*/false); // + 2 to add padding on both sides. - - // Rasterize chart and dilate. - drawChartBitmapDilate(chart, &chart_bitmap, /*padding=*/1); - } - else { - // Init all bits to 0. - chart_bitmap.resize(ftoi_ceil(chartExtents[c].x) + 1, ftoi_ceil(chartExtents[c].y) + 1, /*initValue=*/false); // Add half a texels on each side. - - // Rasterize chart and dilate. - drawChartBitmap(chart, &chart_bitmap, Vector2(1), Vector2(0.5)); - } - } - - int best_x, best_y; - int best_cw, best_ch; // Includes padding now. - int best_r; - findChartLocation(quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r); - - /*if (w < best_x + best_cw || h < best_y + best_ch) - { - nvDebug("Resize extents to (%d, %d).\n", best_x + best_cw, best_y + best_ch); - }*/ - - // Update parametric extents. - w = max(w, best_x + best_cw); - h = max(h, best_y + best_ch); - - w = align(w, 4); - h = align(h, 4); - - // Resize bitmap if necessary. - if (uint(w) > m_bitmap.width() || uint(h) > m_bitmap.height()) - { - //nvDebug("Resize bitmap (%d, %d).\n", nextPowerOfTwo(w), nextPowerOfTwo(h)); - m_bitmap.resize(nextPowerOfTwo(U32(w)), nextPowerOfTwo(U32(h)), false); - // -- GODOT start -- - //m_debug_bitmap.resize(nextPowerOfTwo(U32(w)), nextPowerOfTwo(U32(h))); - // -- GODOT end -- - } - - //nvDebug("Add chart at (%d, %d).\n", best_x, best_y); - - addChart(&chart_bitmap, w, h, best_x, best_y, best_r, /*debugOutput=*/NULL); - - // -- GODOT start -- - // IC: Output chart again to debug bitmap. - /*if (chart->isVertexMapped()) { - addChart(&chart_bitmap, w, h, best_x, best_y, best_r, &m_debug_bitmap); - } - else { - addChart(chart, w, h, best_x, best_y, best_r, &m_debug_bitmap); - }*/ - // -- GODOT end -- - - //float best_angle = 2 * PI * best_r; - - // Translate and rotate chart texture coordinates. - HalfEdge::Mesh * mesh = chart->chartMesh(); - const uint vertexCount = mesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - - Vector2 t = vertex->tex; - if (best_r) swap(t.x, t.y); - //vertex->tex.x = best_x + t.x * cosf(best_angle) - t.y * sinf(best_angle); - //vertex->tex.y = best_y + t.x * sinf(best_angle) + t.y * cosf(best_angle); - - vertex->tex.x = best_x + t.x + 0.5f; - vertex->tex.y = best_y + t.y + 0.5f; - - nvCheck(vertex->tex.x >= 0 && vertex->tex.y >= 0); - nvCheck(isFinite(vertex->tex.x) && isFinite(vertex->tex.y)); - } - -#if DEBUG_OUTPUT && 0 - StringBuilder fileName; - fileName.format("debug_packer_%d.tga", i); - //outputDebugBitmap(fileName.str(), m_bitmap, w, h); - outputDebugImage(fileName.str(), m_debug_bitmap, w, h); -#endif - } - -#else // 0 - - // Add sorted charts to bitmap. - for (uint i = 0; i < chartCount; i++) - { - uint c = ranks[chartCount - i - 1]; // largest chart first - - Chart * chart = m_atlas->chartAt(c); - - if (!chart->isDisk()) continue; - - Vector2 scale(1, 1); - -#if 0 // old method. - //m_padding_x = 2*padding; - //m_padding_y = 2*padding; -#else - //m_padding_x = 0; //padding; - //m_padding_y = 0; //padding; -#endif - - int bw = ftoi_ceil(chartExtents[c].x + 1); - int bh = ftoi_ceil(chartExtents[c].y + 1); - - if (chartExtents[c].x < 1.0f) { - scale.x = 0.01f; // @@ Ideally we would like to scale it to 0, but then our rasterizer would not touch any pixels. - bw = 1; - } - if (chartExtents[c].y < 1.0f) { - scale.y = 0.01f; - bh = 1; - } - - //BitMap chart_bitmap(iceil(chartExtents[c].x) + 1 + m_padding_x * 2, iceil(chartExtents[c].y) + 1 + m_padding_y * 2); - //BitMap chart_bitmap(ftoi_ceil(chartExtents[c].x/2)*2, ftoi_ceil(chartExtents[c].y/2)*2); - BitMap chart_bitmap(bw, bh); - chart_bitmap.clearAll(); - - Vector2 offset; - offset.x = 0; // (chart_bitmap.width() - chartExtents[c].x) * 0.5f; - offset.y = 0; // (chart_bitmap.height() - chartExtents[c].y) * 0.5f; - - drawChartBitmap(chart, &chart_bitmap, scale, offset); - - int best_x, best_y; - int best_cw, best_ch; - int best_r; - findChartLocation(quality, &chart_bitmap, chartExtents[c], w, h, &best_x, &best_y, &best_cw, &best_ch, &best_r); - - /*if (w < best_x + best_cw || h < best_y + best_ch) - { - nvDebug("Resize extents to (%d, %d).\n", best_x + best_cw, best_y + best_ch); - }*/ - - // Update parametric extents. - w = max(w, best_x + best_cw); - h = max(h, best_y + best_ch); - - // Resize bitmap if necessary. - if (uint(w) > m_bitmap.width() || uint(h) > m_bitmap.height()) - { - //nvDebug("Resize bitmap (%d, %d).\n", nextPowerOfTwo(w), nextPowerOfTwo(h)); - m_bitmap.resize(nextPowerOfTwo(w), nextPowerOfTwo(h), false); - m_debug_bitmap.resize(nextPowerOfTwo(w), nextPowerOfTwo(h)); - } - - //nvDebug("Add chart at (%d, %d).\n", best_x, best_y); - -#if 0 // old method. -#if _DEBUG - checkCanAddChart(chart, w, h, best_x, best_y, best_r); -#endif - - // Add chart. - addChart(chart, w, h, best_x, best_y, best_r); -#else - // Add chart reusing its bitmap. - addChart(&chart_bitmap, w, h, best_x, best_y, best_r); -#endif - - //float best_angle = 2 * PI * best_r; - - // Translate and rotate chart texture coordinates. - HalfEdge::Mesh * mesh = chart->chartMesh(); - const uint vertexCount = mesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - - Vector2 t = vertex->tex * scale + offset; - if (best_r) swap(t.x, t.y); - //vertex->tex.x = best_x + t.x * cosf(best_angle) - t.y * sinf(best_angle); - //vertex->tex.y = best_y + t.x * sinf(best_angle) + t.y * cosf(best_angle); - vertex->tex.x = best_x + t.x + 0.5f; - vertex->tex.y = best_y + t.y + 0.5f; - - nvCheck(vertex->tex.x >= 0 && vertex->tex.y >= 0); - } - -#if DEBUG_OUTPUT && 0 - StringBuilder fileName; - fileName.format("debug_packer_%d.tga", i); - //outputDebugBitmap(fileName.str(), m_bitmap, w, h); - outputDebugImage(fileName.str(), m_debug_bitmap, w, h); -#endif - } - -#endif // 0 - - //w -= padding - 1; // Leave one pixel border! - //h -= padding - 1; - - m_width = max(0, w); - m_height = max(0, h); - - nvCheck(isAligned(m_width, 4)); - nvCheck(isAligned(m_height, 4)); - - // -- GODOT start -- - //m_debug_bitmap.resize(m_width, m_height); - //m_debug_bitmap.setFormat(Image::Format_ARGB); - // -- GODOT end -- - -#if DEBUG_OUTPUT - //outputDebugBitmap("debug_packer_final.tga", m_bitmap, w, h); - //outputDebugImage("debug_packer_final.tga", m_debug_bitmap, w, h); - ImageIO::save("debug_packer_final.tga", &m_debug_bitmap); -#endif -} - - -// IC: Brute force is slow, and random may take too much time to converge. We start inserting large charts in a small atlas. Using brute force is lame, because most of the space -// is occupied at this point. At the end we have many small charts and a large atlas with sparse holes. Finding those holes randomly is slow. A better approach would be to -// start stacking large charts as if they were tetris pieces. Once charts get small try to place them randomly. It may be interesting to try a intermediate strategy, first try -// along one axis and then try exhaustively along that axis. -void AtlasPacker::findChartLocation(int quality, const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r) -{ - int attempts = 256; - if (quality == 1) attempts = 4096; - if (quality == 2) attempts = 2048; - if (quality == 3) attempts = 1024; - if (quality == 4) attempts = 512; - - if (quality == 0 || w*h < attempts) - { - findChartLocation_bruteForce(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r); - } - else - { - findChartLocation_random(bitmap, extents, w, h, best_x, best_y, best_w, best_h, best_r, attempts); - } -} - -#define BLOCK_SIZE 4 - -void AtlasPacker::findChartLocation_bruteForce(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r) -{ - int best_metric = INT_MAX; - - // Try two different orientations. - for (int r = 0; r < 2; r++) - { - int cw = bitmap->width(); - int ch = bitmap->height(); - if (r & 1) swap(cw, ch); - - for (int y = 0; y <= h + 1; y += BLOCK_SIZE) // + 1 to extend atlas in case atlas full. - { - for (int x = 0; x <= w + 1; x += BLOCK_SIZE) // + 1 not really necessary here. - { - // Early out. - int area = max(w, x+cw) * max(h, y+ch); - //int perimeter = max(w, x+cw) + max(h, y+ch); - int extents = max(max(w, x+cw), max(h, y+ch)); - - int metric = extents*extents + area; - - if (metric > best_metric) { - continue; - } - if (metric == best_metric && max(x, y) >= max(*best_x, *best_y)) { - // If metric is the same, pick the one closest to the origin. - continue; - } - - if (canAddChart(bitmap, w, h, x, y, r)) - { - best_metric = metric; - *best_x = x; - *best_y = y; - *best_w = cw; - *best_h = ch; - *best_r = r; - - if (area == w*h) - { - // Chart is completely inside, do not look at any other location. - goto done; - } - } - } - } - } - -done: - nvDebugCheck (best_metric != INT_MAX); -} - - -void AtlasPacker::findChartLocation_random(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, int minTrialCount) -{ - int best_metric = INT_MAX; - - for (int i = 0; i < minTrialCount || best_metric == INT_MAX; i++) - { - int r = m_rand.getRange(1); - int x = m_rand.getRange(w + 1); // + 1 to extend atlas in case atlas full. We may want to use a higher number to increase probability of extending atlas. - int y = m_rand.getRange(h + 1); // + 1 to extend atlas in case atlas full. - - x = align(x, BLOCK_SIZE); - y = align(y, BLOCK_SIZE); - - int cw = bitmap->width(); - int ch = bitmap->height(); - if (r & 1) swap(cw, ch); - - // Early out. - int area = max(w, x+cw) * max(h, y+ch); - //int perimeter = max(w, x+cw) + max(h, y+ch); - int extents = max(max(w, x+cw), max(h, y+ch)); - - int metric = extents*extents + area; - - if (metric > best_metric) { - continue; - } - if (metric == best_metric && min(x, y) > min(*best_x, *best_y)) { - // If metric is the same, pick the one closest to the origin. - continue; - } - - if (canAddChart(bitmap, w, h, x, y, r)) - { - best_metric = metric; - *best_x = x; - *best_y = y; - *best_w = cw; - *best_h = ch; - *best_r = r; - - if (area == w*h) - { - // Chart is completely inside, do not look at any other location. - break; - } - } - } -} - - -void AtlasPacker::drawChartBitmapDilate(const Chart * chart, BitMap * bitmap, int padding) -{ - const int w = bitmap->width(); - const int h = bitmap->height(); - const Vector2 extents = Vector2(float(w), float(h)); - - // Rasterize chart faces, check that all bits are not set. - const uint faceCount = chart->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = chart->chartMesh()->faceAt(f); - - Vector2 vertices[4]; - - uint edgeCount = 0; - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - if (edgeCount < 4) - { - vertices[edgeCount] = it.vertex()->tex + Vector2(0.5) + Vector2(float(padding), float(padding)); - } - edgeCount++; - } - - if (edgeCount == 3) - { - Raster::drawTriangle(Raster::Mode_Antialiased, extents, true, vertices, AtlasPacker::setBitsCallback, bitmap); - } - else - { - Raster::drawQuad(Raster::Mode_Antialiased, extents, true, vertices, AtlasPacker::setBitsCallback, bitmap); - } - } - - // Expand chart by padding pixels. (dilation) - BitMap tmp(w, h); - for (int i = 0; i < padding; i++) { - tmp.clearAll(); - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - bool b = bitmap->bitAt(x, y); - if (!b) { - if (x > 0) { - b |= bitmap->bitAt(x - 1, y); - if (y > 0) b |= bitmap->bitAt(x - 1, y - 1); - if (y < h-1) b |= bitmap->bitAt(x - 1, y + 1); - } - if (y > 0) b |= bitmap->bitAt(x, y - 1); - if (y < h-1) b |= bitmap->bitAt(x, y + 1); - if (x < w-1) { - b |= bitmap->bitAt(x + 1, y); - if (y > 0) b |= bitmap->bitAt(x + 1, y - 1); - if (y < h-1) b |= bitmap->bitAt(x + 1, y + 1); - } - } - if (b) tmp.setBitAt(x, y); - } - } - - swap(tmp, *bitmap); - } -} - - -void AtlasPacker::drawChartBitmap(const Chart * chart, BitMap * bitmap, const Vector2 & scale, const Vector2 & offset) -{ - const int w = bitmap->width(); - const int h = bitmap->height(); - const Vector2 extents = Vector2(float(w), float(h)); - - static const Vector2 pad[4] = { - Vector2(-0.5, -0.5), - Vector2(0.5, -0.5), - Vector2(-0.5, 0.5), - Vector2(0.5, 0.5) - }; - /*static const Vector2 pad[4] = { - Vector2(-1, -1), - Vector2(1, -1), - Vector2(-1, 1), - Vector2(1, 1) - };*/ - - // Rasterize 4 times to add proper padding. - for (int i = 0; i < 4; i++) { - - // Rasterize chart faces, check that all bits are not set. - const uint faceCount = chart->chartMesh()->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = chart->chartMesh()->faceAt(f); - - Vector2 vertices[4]; - - uint edgeCount = 0; - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - if (edgeCount < 4) - { - vertices[edgeCount] = it.vertex()->tex * scale + offset + pad[i]; - nvCheck(ftoi_ceil(vertices[edgeCount].x) >= 0); - nvCheck(ftoi_ceil(vertices[edgeCount].y) >= 0); - nvCheck(ftoi_ceil(vertices[edgeCount].x) <= w); - nvCheck(ftoi_ceil(vertices[edgeCount].y) <= h); - } - edgeCount++; - } - - if (edgeCount == 3) - { - Raster::drawTriangle(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::setBitsCallback, bitmap); - } - else - { - Raster::drawQuad(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::setBitsCallback, bitmap); - } - } - } - - // @@ This only allows us to expand the size in texel intervals. - /*if (m_padding_x != 0 && m_padding_y != 0)*/ { - - // Expand chart by padding pixels. (dilation) - BitMap tmp(w, h); - //for (int i = 0; i < 1; i++) { - tmp.clearAll(); - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - bool b = bitmap->bitAt(x, y); - if (!b) { - if (x > 0) { - b |= bitmap->bitAt(x - 1, y); - if (y > 0) b |= bitmap->bitAt(x - 1, y - 1); - if (y < h-1) b |= bitmap->bitAt(x - 1, y + 1); - } - if (y > 0) b |= bitmap->bitAt(x, y - 1); - if (y < h-1) b |= bitmap->bitAt(x, y + 1); - if (x < w-1) { - b |= bitmap->bitAt(x + 1, y); - if (y > 0) b |= bitmap->bitAt(x + 1, y - 1); - if (y < h-1) b |= bitmap->bitAt(x + 1, y + 1); - } - } - if (b) tmp.setBitAt(x, y); - } - } - - swap(tmp, *bitmap); - //} - } -} - -bool AtlasPacker::canAddChart(const BitMap * bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r) -{ - nvDebugCheck(r == 0 || r == 1); - - // Check whether the two bitmaps overlap. - - const int w = bitmap->width(); - const int h = bitmap->height(); - - if (r == 0) { - for (int y = 0; y < h; y++) { - int yy = y + offset_y; - if (yy >= 0) { - for (int x = 0; x < w; x++) { - int xx = x + offset_x; - if (xx >= 0) { - if (bitmap->bitAt(x, y)) { - if (xx < atlas_w && yy < atlas_h) { - if (m_bitmap.bitAt(xx, yy)) return false; - } - } - } - } - } - } - } - else if (r == 1) { - for (int y = 0; y < h; y++) { - int xx = y + offset_x; - if (xx >= 0) { - for (int x = 0; x < w; x++) { - int yy = x + offset_y; - if (yy >= 0) { - if (bitmap->bitAt(x, y)) { - if (xx < atlas_w && yy < atlas_h) { - if (m_bitmap.bitAt(xx, yy)) return false; - } - } - } - } - } - } - } - - return true; -} - -#if 0 -void AtlasPacker::checkCanAddChart(const Chart * chart, int w, int h, int x, int y, int r) -{ - nvDebugCheck(r == 0 || r == 1); - Vector2 extents = Vector2(float(w), float(h)); - Vector2 offset = Vector2(float(x), float(y)); - - // Rasterize chart faces, set bits. - const uint faceCount = chart->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = chart->chartMesh()->faceAt(f); - - Vector2 vertices[4]; - - uint edgeCount = 0; - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - if (edgeCount < 4) - { - Vector2 t = it.vertex()->tex; - if (r == 1) swap(t.x, t.y); - vertices[edgeCount] = t + offset; - } - edgeCount++; - } - - if (edgeCount == 3) - { - Raster::drawTriangle(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::checkBitsCallback, &m_bitmap); - } - else - { - Raster::drawQuad(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, AtlasPacker::checkBitsCallback, &m_bitmap); - } - } -} -#endif // 0 - - -static Color32 chartColor = Color32(0); -static void selectRandomColor(MTRand & rand) { - // Pick random color for this chart. @@ Select random hue, but fixed saturation/luminance? - chartColor.r = 128 + rand.getRange(127); - chartColor.g = 128 + rand.getRange(127); - chartColor.b = 128 + rand.getRange(127); - chartColor.a = 255; -} -static bool debugDrawCallback(void * param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float area) -{ - Image * image = (Image *)param; - - if (area > 0.0) { - Color32 c = image->pixel(x, y); - c.r = chartColor.r; - c.g = chartColor.g; - c.b = chartColor.b; - c.a += U8(ftoi_round(0.5f * area * 255)); - image->pixel(x, y) = c; - } - - return true; -} - -void AtlasPacker::addChart(const Chart * chart, int w, int h, int x, int y, int r, Image * debugOutput) -{ - nvDebugCheck(r == 0 || r == 1); - - nvDebugCheck(debugOutput != NULL); - selectRandomColor(m_rand); - - Vector2 extents = Vector2(float(w), float(h)); - Vector2 offset = Vector2(float(x), float(y)) + Vector2(0.5); - - // Rasterize chart faces, set bits. - const uint faceCount = chart->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = chart->chartMesh()->faceAt(f); - - Vector2 vertices[4]; - - uint edgeCount = 0; - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - if (edgeCount < 4) - { - Vector2 t = it.vertex()->tex; - if (r == 1) swap(t.x, t.y); - vertices[edgeCount] = t + offset; - } - edgeCount++; - } - - if (edgeCount == 3) - { - Raster::drawTriangle(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, debugDrawCallback, debugOutput); - } - else - { - Raster::drawQuad(Raster::Mode_Antialiased, extents, /*enableScissors=*/true, vertices, debugDrawCallback, debugOutput); - } - } -} - - -void AtlasPacker::addChart(const BitMap * bitmap, int atlas_w, int atlas_h, int offset_x, int offset_y, int r, Image * debugOutput) -{ - nvDebugCheck(r == 0 || r == 1); - - // Check whether the two bitmaps overlap. - - const int w = bitmap->width(); - const int h = bitmap->height(); - - if (debugOutput != NULL) { - selectRandomColor(m_rand); - } - - if (r == 0) { - for (int y = 0; y < h; y++) { - int yy = y + offset_y; - if (yy >= 0) { - for (int x = 0; x < w; x++) { - int xx = x + offset_x; - if (xx >= 0) { - if (bitmap->bitAt(x, y)) { - if (xx < atlas_w && yy < atlas_h) { - if (debugOutput) debugOutput->pixel(xx, yy) = chartColor; - else { - nvDebugCheck(m_bitmap.bitAt(xx, yy) == false); - m_bitmap.setBitAt(xx, yy); - } - } - } - } - } - } - } - } - else if (r == 1) { - for (int y = 0; y < h; y++) { - int xx = y + offset_x; - if (xx >= 0) { - for (int x = 0; x < w; x++) { - int yy = x + offset_y; - if (yy >= 0) { - if (bitmap->bitAt(x, y)) { - if (xx < atlas_w && yy < atlas_h) { - if (debugOutput) debugOutput->pixel(xx, yy) = chartColor; - else { - nvDebugCheck(m_bitmap.bitAt(xx, yy) == false); - m_bitmap.setBitAt(xx, yy); - } - } - } - } - } - } - } - } -} - - - -/*static*/ bool AtlasPacker::checkBitsCallback(void * param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float) -{ - BitMap * bitmap = (BitMap * )param; - - nvDebugCheck(bitmap->bitAt(x, y) == false); - - return true; -} - -/*static*/ bool AtlasPacker::setBitsCallback(void * param, int x, int y, Vector3::Arg, Vector3::Arg, Vector3::Arg, float area) -{ - BitMap * bitmap = (BitMap * )param; - - if (area > 0.0) { - bitmap->setBitAt(x, y); - } - - return true; -} - - - -float AtlasPacker::computeAtlasUtilization() const { - const uint w = m_width; - const uint h = m_height; - nvDebugCheck(w <= m_bitmap.width()); - nvDebugCheck(h <= m_bitmap.height()); - - uint count = 0; - for (uint y = 0; y < h; y++) { - for (uint x = 0; x < w; x++) { - count += m_bitmap.bitAt(x, y); - } - } - - return float(count) / (w * h); -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h b/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h deleted file mode 100644 index 845dbfb6f3..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/AtlasPacker.h +++ /dev/null @@ -1,65 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MESH_ATLASPACKER_H -#define NV_MESH_ATLASPACKER_H - -#include "nvcore/RadixSort.h" -#include "nvmath/Vector.h" -#include "nvmath/Random.h" -#include "nvimage/BitMap.h" -#include "nvimage/Image.h" - -#include "nvmesh/nvmesh.h" - - -namespace nv -{ - class Atlas; - class Chart; - - struct AtlasPacker - { - AtlasPacker(Atlas * atlas); - ~AtlasPacker(); - - void packCharts(int quality, float texelArea, bool blockAligned, bool conservative); - float computeAtlasUtilization() const; - - private: - - void findChartLocation(int quality, const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r); - void findChartLocation_bruteForce(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r); - void findChartLocation_random(const BitMap * bitmap, Vector2::Arg extents, int w, int h, int * best_x, int * best_y, int * best_w, int * best_h, int * best_r, int minTrialCount); - - void drawChartBitmapDilate(const Chart * chart, BitMap * bitmap, int padding); - void drawChartBitmap(const Chart * chart, BitMap * bitmap, const Vector2 & scale, const Vector2 & offset); - - bool canAddChart(const BitMap * bitmap, int w, int h, int x, int y, int r); - void addChart(const BitMap * bitmap, int w, int h, int x, int y, int r, Image * debugOutput); - //void checkCanAddChart(const Chart * chart, int w, int h, int x, int y, int r); - void addChart(const Chart * chart, int w, int h, int x, int y, int r, Image * debugOutput); - - - static bool checkBitsCallback(void * param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage); - static bool setBitsCallback(void * param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage); - - private: - - Atlas * m_atlas; - BitMap m_bitmap; - // -- GODOT start -- - //Image m_debug_bitmap; - // -- GODOT end -- - RadixSort m_radix; - - uint m_width; - uint m_height; - - MTRand m_rand; - - }; - -} // nv namespace - -#endif // NV_MESH_ATLASPACKER_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.cpp b/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.cpp deleted file mode 100644 index cd1e8bbb7b..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#include "nvmesh.h" // pch - -#include "LeastSquaresConformalMap.h" -#include "ParameterizationQuality.h" -#include "Util.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Face.h" - -#include "nvmath/Sparse.h" -#include "nvmath/Solver.h" -#include "nvmath/Vector.inl" - -#include "nvcore/Array.inl" - - -using namespace nv; -using namespace HalfEdge; - -namespace -{ - - // Test all pairs of vertices in the boundary and check distance. - static void findDiameterVertices(HalfEdge::Mesh * mesh, HalfEdge::Vertex ** a, HalfEdge::Vertex ** b) - { - nvDebugCheck(mesh != NULL); - nvDebugCheck(a != NULL); - nvDebugCheck(b != NULL); - - const uint vertexCount = mesh->vertexCount(); - - float maxLength = 0.0f; - - for (uint v0 = 1; v0 < vertexCount; v0++) - { - HalfEdge::Vertex * vertex0 = mesh->vertexAt(v0); - nvDebugCheck(vertex0 != NULL); - - if (!vertex0->isBoundary()) continue; - - for (uint v1 = 0; v1 < v0; v1++) - { - HalfEdge::Vertex * vertex1 = mesh->vertexAt(v1); - nvDebugCheck(vertex1 != NULL); - - if (!vertex1->isBoundary()) continue; - - float len = length(vertex0->pos - vertex1->pos); - - if (len > maxLength) - { - maxLength = len; - - *a = vertex0; - *b = vertex1; - } - } - } - - nvDebugCheck(*a != NULL && *b != NULL); - } - - // Fast sweep in 3 directions - static bool findApproximateDiameterVertices(HalfEdge::Mesh * mesh, HalfEdge::Vertex ** a, HalfEdge::Vertex ** b) - { - nvDebugCheck(mesh != NULL); - nvDebugCheck(a != NULL); - nvDebugCheck(b != NULL); - - const uint vertexCount = mesh->vertexCount(); - - HalfEdge::Vertex * minVertex[3]; - HalfEdge::Vertex * maxVertex[3]; - - minVertex[0] = minVertex[1] = minVertex[2] = NULL; - maxVertex[0] = maxVertex[1] = maxVertex[2] = NULL; - - for (uint v = 1; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex != NULL); - - if (vertex->isBoundary()) - { - minVertex[0] = minVertex[1] = minVertex[2] = vertex; - maxVertex[0] = maxVertex[1] = maxVertex[2] = vertex; - break; - } - } - - if (minVertex[0] == NULL) - { - // Input mesh has not boundaries. - return false; - } - - for (uint v = 1; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex != NULL); - - if (!vertex->isBoundary()) - { - // Skip interior vertices. - continue; - } - - if (vertex->pos.x < minVertex[0]->pos.x) minVertex[0] = vertex; - else if (vertex->pos.x > maxVertex[0]->pos.x) maxVertex[0] = vertex; - - if (vertex->pos.y < minVertex[1]->pos.y) minVertex[1] = vertex; - else if (vertex->pos.y > maxVertex[1]->pos.y) maxVertex[1] = vertex; - - if (vertex->pos.z < minVertex[2]->pos.z) minVertex[2] = vertex; - else if (vertex->pos.z > maxVertex[2]->pos.z) maxVertex[2] = vertex; - } - - float lengths[3]; - for (int i = 0; i < 3; i++) - { - lengths[i] = length(minVertex[i]->pos - maxVertex[i]->pos); - } - - if (lengths[0] > lengths[1] && lengths[0] > lengths[2]) - { - *a = minVertex[0]; - *b = maxVertex[0]; - } - else if (lengths[1] > lengths[2]) - { - *a = minVertex[1]; - *b = maxVertex[1]; - } - else - { - *a = minVertex[2]; - *b = maxVertex[2]; - } - - return true; - } - - // Conformal relations from Bruno Levy: - - // Computes the coordinates of the vertices of a triangle - // in a local 2D orthonormal basis of the triangle's plane. - static void project_triangle(Vector3::Arg p0, Vector3::Arg p1, Vector3::Arg p2, Vector2 * z0, Vector2 * z1, Vector2 * z2) - { - Vector3 X = normalize(p1 - p0, 0.0f); - Vector3 Z = normalize(cross(X, (p2 - p0)), 0.0f); - Vector3 Y = normalize(cross(Z, X), 0.0f); - - float x0 = 0.0f; - float y0 = 0.0f; - float x1 = length(p1 - p0); - float y1 = 0.0f; - float x2 = dot((p2 - p0), X); - float y2 = dot((p2 - p0), Y); - - *z0 = Vector2(x0, y0); - *z1 = Vector2(x1, y1); - *z2 = Vector2(x2, y2); - } - - // LSCM equation, geometric form : - // (Z1 - Z0)(U2 - U0) = (Z2 - Z0)(U1 - U0) - // Where Uk = uk + i.vk is the complex number - // corresponding to (u,v) coords - // Zk = xk + i.yk is the complex number - // corresponding to local (x,y) coords - // cool: no divide with this expression, - // makes it more numerically stable in - // the presence of degenerate triangles. - - static void setup_conformal_map_relations(SparseMatrix & A, int row, const HalfEdge::Vertex * v0, const HalfEdge::Vertex * v1, const HalfEdge::Vertex * v2) - { - int id0 = v0->id; - int id1 = v1->id; - int id2 = v2->id; - - Vector3 p0 = v0->pos; - Vector3 p1 = v1->pos; - Vector3 p2 = v2->pos; - - Vector2 z0, z1, z2; - project_triangle(p0, p1, p2, &z0, &z1, &z2); - - Vector2 z01 = z1 - z0; - Vector2 z02 = z2 - z0; - - float a = z01.x; - float b = z01.y; - float c = z02.x; - float d = z02.y; - nvCheck(b == 0.0f); - - // Note : 2*id + 0 --> u - // 2*id + 1 --> v - int u0_id = 2 * id0 + 0; - int v0_id = 2 * id0 + 1; - int u1_id = 2 * id1 + 0; - int v1_id = 2 * id1 + 1; - int u2_id = 2 * id2 + 0; - int v2_id = 2 * id2 + 1; - - // Note : b = 0 - - // Real part - A.setCoefficient(u0_id, 2 * row + 0, -a+c); - A.setCoefficient(v0_id, 2 * row + 0, b-d); - A.setCoefficient(u1_id, 2 * row + 0, -c); - A.setCoefficient(v1_id, 2 * row + 0, d); - A.setCoefficient(u2_id, 2 * row + 0, a); - - // Imaginary part - A.setCoefficient(u0_id, 2 * row + 1, -b+d); - A.setCoefficient(v0_id, 2 * row + 1, -a+c); - A.setCoefficient(u1_id, 2 * row + 1, -d); - A.setCoefficient(v1_id, 2 * row + 1, -c); - A.setCoefficient(v2_id, 2 * row + 1, a); - } - - - // Conformal relations from Brecht Van Lommel (based on ABF): - - static float vec_angle_cos(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) - { - Vector3 d1 = v1 - v2; - Vector3 d2 = v3 - v2; - return clamp(dot(d1, d2) / (length(d1) * length(d2)), -1.0f, 1.0f); - } - - static float vec_angle(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3) - { - float dot = vec_angle_cos(v1, v2, v3); - return acosf(dot); - } - - static void triangle_angles(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3, float *a1, float *a2, float *a3) - { - *a1 = vec_angle(v3, v1, v2); - *a2 = vec_angle(v1, v2, v3); - *a3 = PI - *a2 - *a1; - } - - static void triangle_cosines(Vector3::Arg v1, Vector3::Arg v2, Vector3::Arg v3, float *a1, float *a2, float *a3) - { - *a1 = vec_angle_cos(v3, v1, v2); - *a2 = vec_angle_cos(v1, v2, v3); - *a3 = vec_angle_cos(v2, v3, v1); - } - - static void setup_abf_relations(SparseMatrix & A, int row, const HalfEdge::Vertex * v0, const HalfEdge::Vertex * v1, const HalfEdge::Vertex * v2) - { - int id0 = v0->id; - int id1 = v1->id; - int id2 = v2->id; - - Vector3 p0 = v0->pos; - Vector3 p1 = v1->pos; - Vector3 p2 = v2->pos; - -#if 1 - // @@ IC: Wouldn't it be more accurate to return cos and compute 1-cos^2? - // It does indeed seem to be a little bit more robust. - // @@ Need to revisit this more carefully! - - float a0, a1, a2; - triangle_angles(p0, p1, p2, &a0, &a1, &a2); - - float s0 = sinf(a0); - float s1 = sinf(a1); - float s2 = sinf(a2); - - /*// Hack for degenerate triangles. - if (equal(s0, 0) && equal(s1, 0) && equal(s2, 0)) { - if (equal(a0, 0)) a0 += 0.001f; - if (equal(a1, 0)) a1 += 0.001f; - if (equal(a2, 0)) a2 += 0.001f; - - if (equal(a0, PI)) a0 = PI - a1 - a2; - if (equal(a1, PI)) a1 = PI - a0 - a2; - if (equal(a2, PI)) a2 = PI - a0 - a1; - - s0 = sinf(a0); - s1 = sinf(a1); - s2 = sinf(a2); - }*/ - - if (s1 > s0 && s1 > s2) - { - swap(s1, s2); - swap(s0, s1); - - swap(a1, a2); - swap(a0, a1); - - swap(id1, id2); - swap(id0, id1); - } - else if (s0 > s1 && s0 > s2) - { - swap(s0, s2); - swap(s0, s1); - - swap(a0, a2); - swap(a0, a1); - - swap(id0, id2); - swap(id0, id1); - } - - float c0 = cosf(a0); -#else - float c0, c1, c2; - triangle_cosines(p0, p1, p2, &c0, &c1, &c2); - - float s0 = 1 - c0*c0; - float s1 = 1 - c1*c1; - float s2 = 1 - c2*c2; - - nvDebugCheck(s0 != 0 || s1 != 0 || s2 != 0); - - if (s1 > s0 && s1 > s2) - { - swap(s1, s2); - swap(s0, s1); - - swap(c1, c2); - swap(c0, c1); - - swap(id1, id2); - swap(id0, id1); - } - else if (s0 > s1 && s0 > s2) - { - swap(s0, s2); - swap(s0, s1); - - swap(c0, c2); - swap(c0, c1); - - swap(id0, id2); - swap(id0, id1); - } -#endif - - float ratio = (s2 == 0.0f) ? 1.0f: s1/s2; - float cosine = c0 * ratio; - float sine = s0 * ratio; - - // Note : 2*id + 0 --> u - // 2*id + 1 --> v - int u0_id = 2 * id0 + 0; - int v0_id = 2 * id0 + 1; - int u1_id = 2 * id1 + 0; - int v1_id = 2 * id1 + 1; - int u2_id = 2 * id2 + 0; - int v2_id = 2 * id2 + 1; - - // Real part - A.setCoefficient(u0_id, 2 * row + 0, cosine - 1.0f); - A.setCoefficient(v0_id, 2 * row + 0, -sine); - A.setCoefficient(u1_id, 2 * row + 0, -cosine); - A.setCoefficient(v1_id, 2 * row + 0, sine); - A.setCoefficient(u2_id, 2 * row + 0, 1); - - // Imaginary part - A.setCoefficient(u0_id, 2 * row + 1, sine); - A.setCoefficient(v0_id, 2 * row + 1, cosine - 1.0f); - A.setCoefficient(u1_id, 2 * row + 1, -sine); - A.setCoefficient(v1_id, 2 * row + 1, -cosine); - A.setCoefficient(v2_id, 2 * row + 1, 1); - } - -} // namespace - - -bool nv::computeLeastSquaresConformalMap(HalfEdge::Mesh * mesh) -{ - nvDebugCheck(mesh != NULL); - - // For this to work properly, mesh should not have colocals that have the same - // attributes, unless you want the vertices to actually have different texcoords. - - const uint vertexCount = mesh->vertexCount(); - const uint D = 2 * vertexCount; - const uint N = 2 * countMeshTriangles(mesh); - - // N is the number of equations (one per triangle) - // D is the number of variables (one per vertex; there are 2 pinned vertices). - if (N < D - 4) { - return false; - } - - SparseMatrix A(D, N); - FullVector b(N); - FullVector x(D); - - // Fill b: - b.fill(0.0f); - - // Fill x: - HalfEdge::Vertex * v0; - HalfEdge::Vertex * v1; - if (!findApproximateDiameterVertices(mesh, &v0, &v1)) - { - // Mesh has no boundaries. - return false; - } - if (v0->tex == v1->tex) - { - // LSCM expects an existing parameterization. - return false; - } - - for (uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex != NULL); - - // Initial solution. - x[2 * v + 0] = vertex->tex.x; - x[2 * v + 1] = vertex->tex.y; - } - - // Fill A: - const uint faceCount = mesh->faceCount(); - for (uint f = 0, t = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = mesh->faceAt(f); - nvDebugCheck(face != NULL); - nvDebugCheck(face->edgeCount() == 3); - - const HalfEdge::Vertex * vertex0 = NULL; - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - nvCheck(edge != NULL); - - if (vertex0 == NULL) - { - vertex0 = edge->vertex; - } - else if (edge->next->vertex != vertex0) - { - const HalfEdge::Vertex * vertex1 = edge->from(); - const HalfEdge::Vertex * vertex2 = edge->to(); - - setup_abf_relations(A, t, vertex0, vertex1, vertex2); - //setup_conformal_map_relations(A, t, vertex0, vertex1, vertex2); - - t++; - } - } - } - - const uint lockedParameters[] = - { - 2 * v0->id + 0, - 2 * v0->id + 1, - 2 * v1->id + 0, - 2 * v1->id + 1 - }; - - // Solve - LeastSquaresSolver(A, b, x, lockedParameters, 4, 0.000001f); - - // Map x back to texcoords: - for (uint v = 0; v < vertexCount; v++) - { - HalfEdge::Vertex * vertex = mesh->vertexAt(v); - nvDebugCheck(vertex != NULL); - - vertex->tex = Vector2(x[2 * v + 0], x[2 * v + 1]); - } - - return true; -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.h b/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.h deleted file mode 100644 index 51fbf193c8..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/LeastSquaresConformalMap.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#pragma once -#ifndef NV_MESH_LEASTSQUARESCONFORMALMAP_H -#define NV_MESH_LEASTSQUARESCONFORMALMAP_H - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - bool computeLeastSquaresConformalMap(HalfEdge::Mesh * mesh); - -} // nv namespace - -#endif // NV_MESH_LEASTSQUARESCONFORMALMAP_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.cpp b/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.cpp deleted file mode 100644 index d6e5e30561..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "OrthogonalProjectionMap.h" - -#include "nvcore/Array.inl" - -#include "nvmath/Fitting.h" -#include "nvmath/Vector.inl" -#include "nvmath/Box.inl" -#include "nvmath/Plane.inl" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/geometry/Bounds.h" - - -using namespace nv; - -bool nv::computeOrthogonalProjectionMap(HalfEdge::Mesh * mesh) -{ - Vector3 axis[2]; - -#if 1 - - uint vertexCount = mesh->vertexCount(); - Array<Vector3> points(vertexCount); - points.resize(vertexCount); - - for (uint i = 0; i < vertexCount; i++) - { - points[i] = mesh->vertexAt(i)->pos; - } - -#if 0 - axis[0] = Fit::computePrincipalComponent_EigenSolver(vertexCount, points.buffer()); - axis[0] = normalize(axis[0]); - - Plane plane = Fit::bestPlane(vertexCount, points.buffer()); - - Vector3 n = plane.vector(); - - axis[1] = cross(axis[0], n); - axis[1] = normalize(axis[1]); -#else - // Avoid redundant computations. - float matrix[6]; - Fit::computeCovariance(vertexCount, points.buffer(), matrix); - - if (matrix[0] == 0 && matrix[3] == 0 && matrix[5] == 0) { - return false; - } - - float eigenValues[3]; - Vector3 eigenVectors[3]; - if (!nv::Fit::eigenSolveSymmetric3(matrix, eigenValues, eigenVectors)) { - return false; - } - - axis[0] = normalize(eigenVectors[0]); - axis[1] = normalize(eigenVectors[1]); -#endif - - -#else - - // IC: I thought this was generally more robust, but turns out it's not even guaranteed to return a valid projection. Imagine a narrow quad perpendicular to one plane, but rotated so that the shortest axis of - // the bounding box is in the direction of that plane. - - // Use the shortest box axis - Box box = MeshBounds::box(mesh); - Vector3 dir = box.extents(); - - if (fabs(dir.x) <= fabs(dir.y) && fabs(dir.x) <= fabs(dir.z)) { - axis[0] = Vector3(0, 1, 0); - axis[1] = Vector3(0, 0, 1); - } - else if (fabs(dir.y) <= fabs(dir.z)) { - axis[0] = Vector3(1, 0, 0); - axis[1] = Vector3(0, 0, 1); - } - else { - axis[0] = Vector3(1, 0, 0); - axis[1] = Vector3(0, 1, 0); - } -#endif - - // Project vertices to plane. - for (HalfEdge::Mesh::VertexIterator it(mesh->vertices()); !it.isDone(); it.advance()) - { - HalfEdge::Vertex * vertex = it.current(); - vertex->tex.x = dot(axis[0], vertex->pos); - vertex->tex.y = dot(axis[1], vertex->pos); - } - - return true; -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.h b/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.h deleted file mode 100644 index 54920413d5..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/OrthogonalProjectionMap.h +++ /dev/null @@ -1,15 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#pragma once -#ifndef NV_MESH_ORTHOGONALPROJECTIONMAP_H -#define NV_MESH_ORTHOGONALPROJECTIONMAP_H - -namespace nv -{ - namespace HalfEdge { class Mesh; } - - bool computeOrthogonalProjectionMap(HalfEdge::Mesh * mesh); - -} // nv namespace - -#endif // NV_MESH_ORTHOGONALPROJECTIONMAP_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp b/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp deleted file mode 100644 index 683ee603cd..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.cpp +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#include "nvmesh.h" // pch - -#include "ParameterizationQuality.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Edge.h" - -#include "nvmath/Vector.inl" - -#include "nvcore/Debug.h" - -#include <float.h> - - -using namespace nv; - -#if 0 -/* -float triangleConformalEnergy(Vector3 q[3], Vector2 p[3]) -{ -const Vector3 v1 = q[0]; -const Vector3 v2 = q[1]; -const Vector3 v3 = q[2]; - -const Vector2 w1 = p[0]; -const Vector2 w2 = p[1]; -const Vector2 w3 = p[2]; - -float x1 = v2.x() - v1.x(); -float x2 = v3.x() - v1.x(); -float y1 = v2.y() - v1.y(); -float y2 = v3.y() - v1.y(); -float z1 = v2.z() - v1.z(); -float z2 = v3.z() - v1.z(); - -float s1 = w2.x() - w1.x(); -float s2 = w3.x() - w1.x(); -float t1 = w2.y() - w1.y(); -float t2 = w3.y() - w1.y(); - -float r = 1.0f / (s1 * t2 - s2 * t1); -Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); -Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); - -Vector3 N = cross(v3-v1, v2-v1); - -// Rotate 90 around N. -} -*/ - -static float triangleConformalEnergy(Vector3 q[3], Vector2 p[3]) -{ - // Using Denis formulas: - Vector3 c0 = q[1] - q[2]; - Vector3 c1 = q[2] - q[0]; - Vector3 c2 = q[0] - q[1]; - - Vector3 N = cross(-c0, c1); - float T = length(N); // 2T - N = normalize(N, 0); - - float cot_alpha0 = dot(-c1, c2) / length(cross(-c1, c2)); - float cot_alpha1 = dot(-c2, c0) / length(cross(-c2, c0)); - float cot_alpha2 = dot(-c0, c1) / length(cross(-c0, c1)); - - Vector3 t0 = -cot_alpha1 * c1 + cot_alpha2 * c2; - Vector3 t1 = -cot_alpha2 * c2 + cot_alpha0 * c0; - Vector3 t2 = -cot_alpha0 * c0 + cot_alpha1 * c1; - - nvCheck(equal(length(t0), length(c0))); - nvCheck(equal(length(t1), length(c1))); - nvCheck(equal(length(t2), length(c2))); - nvCheck(equal(dot(t0, c0), 0)); - nvCheck(equal(dot(t1, c1), 0)); - nvCheck(equal(dot(t2, c2), 0)); - - // Gradients - Vector3 grad_u = 1.0f / T * (p[0].x * t0 + p[1].x * t1 + p[2].x * t2); - Vector3 grad_v = 1.0f / T * (p[0].y * t0 + p[1].y * t1 + p[2].y * t2); - - // Rotated gradients - Vector3 Jgrad_u = 1.0f / T * (p[0].x * c0 + p[1].x * c1 + p[2].x * c2); - Vector3 Jgrad_v = 1.0f / T * (p[0].y * c0 + p[1].y * c1 + p[2].y * c2); - - // Using Lengyel's formulas: - { - const Vector3 v1 = q[0]; - const Vector3 v2 = q[1]; - const Vector3 v3 = q[2]; - - const Vector2 w1 = p[0]; - const Vector2 w2 = p[1]; - const Vector2 w3 = p[2]; - - float x1 = v2.x - v1.x; - float x2 = v3.x - v1.x; - float y1 = v2.y - v1.y; - float y2 = v3.y - v1.y; - float z1 = v2.z - v1.z; - float z2 = v3.z - v1.z; - - float s1 = w2.x - w1.x; - float s2 = w3.x - w1.x; - float t1 = w2.y - w1.y; - float t2 = w3.y - w1.y; - - float r = 1.0f / (s1 * t2 - s2 * t1); - Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); - Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); - - Vector3 Jsdir = cross(N, sdir); - Vector3 Jtdir = cross(N, tdir); - - float x = 3; - } - - // check: sdir == grad_u - // check: tdir == grad_v - - return length(grad_u - Jgrad_v); -} -#endif // 0 - - -ParameterizationQuality::ParameterizationQuality() -{ - m_totalTriangleCount = 0; - m_flippedTriangleCount = 0; - m_zeroAreaTriangleCount = 0; - - m_parametricArea = 0.0f; - m_geometricArea = 0.0f; - - m_stretchMetric = 0.0f; - m_maxStretchMetric = 0.0f; - - m_conformalMetric = 0.0f; - m_authalicMetric = 0.0f; -} - -ParameterizationQuality::ParameterizationQuality(const HalfEdge::Mesh * mesh) -{ - nvDebugCheck(mesh != NULL); - - m_totalTriangleCount = 0; - m_flippedTriangleCount = 0; - m_zeroAreaTriangleCount = 0; - - m_parametricArea = 0.0f; - m_geometricArea = 0.0f; - - m_stretchMetric = 0.0f; - m_maxStretchMetric = 0.0f; - - m_conformalMetric = 0.0f; - m_authalicMetric = 0.0f; - - const uint faceCount = mesh->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = mesh->faceAt(f); - const HalfEdge::Vertex * vertex0 = NULL; - - Vector3 p[3]; - Vector2 t[3]; - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - - if (vertex0 == NULL) - { - vertex0 = edge->vertex; - - p[0] = vertex0->pos; - t[0] = vertex0->tex; - } - else if (edge->to() != vertex0) - { - p[1] = edge->from()->pos; - p[2] = edge->to()->pos; - t[1] = edge->from()->tex; - t[2] = edge->to()->tex; - - processTriangle(p, t); - } - } - } - - if (m_flippedTriangleCount + m_zeroAreaTriangleCount == faceCount) - { - // If all triangles are flipped, then none is. - m_flippedTriangleCount = 0; - } - - nvDebugCheck(isFinite(m_parametricArea) && m_parametricArea >= 0); - nvDebugCheck(isFinite(m_geometricArea) && m_geometricArea >= 0); - nvDebugCheck(isFinite(m_stretchMetric)); - nvDebugCheck(isFinite(m_maxStretchMetric)); - nvDebugCheck(isFinite(m_conformalMetric)); - nvDebugCheck(isFinite(m_authalicMetric)); -} - -bool ParameterizationQuality::isValid() const -{ - return m_flippedTriangleCount == 0; // @@ Does not test for self-overlaps. -} - -float ParameterizationQuality::rmsStretchMetric() const -{ - if (m_geometricArea == 0) return 0.0f; - float normFactor = sqrtf(m_parametricArea / m_geometricArea); - return sqrtf(m_stretchMetric / m_geometricArea) * normFactor; -} - -float ParameterizationQuality::maxStretchMetric() const -{ - if (m_geometricArea == 0) return 0.0f; - float normFactor = sqrtf(m_parametricArea / m_geometricArea); - return m_maxStretchMetric * normFactor; -} - -float ParameterizationQuality::rmsConformalMetric() const -{ - if (m_geometricArea == 0) return 0.0f; - return sqrtf(m_conformalMetric / m_geometricArea); -} - -float ParameterizationQuality::maxAuthalicMetric() const -{ - if (m_geometricArea == 0) return 0.0f; - return sqrtf(m_authalicMetric / m_geometricArea); -} - -void ParameterizationQuality::operator += (const ParameterizationQuality & pq) -{ - m_totalTriangleCount += pq.m_totalTriangleCount; - m_flippedTriangleCount += pq.m_flippedTriangleCount; - m_zeroAreaTriangleCount += pq.m_zeroAreaTriangleCount; - - m_parametricArea += pq.m_parametricArea; - m_geometricArea += pq.m_geometricArea; - - m_stretchMetric += pq.m_stretchMetric; - m_maxStretchMetric = max(m_maxStretchMetric, pq.m_maxStretchMetric); - - m_conformalMetric += pq.m_conformalMetric; - m_authalicMetric += pq.m_authalicMetric; -} - - -void ParameterizationQuality::processTriangle(Vector3 q[3], Vector2 p[3]) -{ - m_totalTriangleCount++; - - // Evaluate texture stretch metric. See: - // - "Texture Mapping Progressive Meshes", Sander, Snyder, Gortler & Hoppe - // - "Mesh Parameterization: Theory and Practice", Siggraph'07 Course Notes, Hormann, Levy & Sheffer. - - float t1 = p[0].x; - float s1 = p[0].y; - float t2 = p[1].x; - float s2 = p[1].y; - float t3 = p[2].x; - float s3 = p[2].y; - - float geometricArea = length(cross(q[1] - q[0], q[2] - q[0])) / 2; - float parametricArea = ((s2 - s1)*(t3 - t1) - (s3 - s1)*(t2 - t1)) / 2; - - if (isZero(parametricArea)) - { - m_zeroAreaTriangleCount++; - return; - } - - Vector3 Ss = (q[0] * (t2- t3) + q[1] * (t3 - t1) + q[2] * (t1 - t2)) / (2 * parametricArea); - Vector3 St = (q[0] * (s3- s2) + q[1] * (s1 - s3) + q[2] * (s2 - s1)) / (2 * parametricArea); - - float a = dot(Ss, Ss); // E - float b = dot(Ss, St); // F - float c = dot(St, St); // G - - // Compute eigen-values of the first fundamental form: - float sigma1 = sqrtf(0.5f * max(0.0f, a + c - sqrtf(square(a - c) + 4 * square(b)))); // gamma uppercase, min eigenvalue. - float sigma2 = sqrtf(0.5f * max(0.0f, a + c + sqrtf(square(a - c) + 4 * square(b)))); // gamma lowercase, max eigenvalue. - nvCheck(sigma2 >= sigma1); - - // isometric: sigma1 = sigma2 = 1 - // conformal: sigma1 / sigma2 = 1 - // authalic: sigma1 * sigma2 = 1 - - float rmsStretch = sqrtf((a + c) * 0.5f); - float rmsStretch2 = sqrtf((square(sigma1) + square(sigma2)) * 0.5f); - nvDebugCheck(equal(rmsStretch, rmsStretch2, 0.01f)); - - if (parametricArea < 0.0f) - { - // Count flipped triangles. - m_flippedTriangleCount++; - - parametricArea = fabsf(parametricArea); - } - - m_stretchMetric += square(rmsStretch) * geometricArea; - m_maxStretchMetric = max(m_maxStretchMetric, sigma2); - - if (!isZero(sigma1, 0.000001f)) { - // sigma1 is zero when geometricArea is zero. - m_conformalMetric += (sigma2 / sigma1) * geometricArea; - } - m_authalicMetric += (sigma1 * sigma2) * geometricArea; - - // Accumulate total areas. - m_geometricArea += geometricArea; - m_parametricArea += parametricArea; - - - //triangleConformalEnergy(q, p); -} diff --git a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.h b/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.h deleted file mode 100644 index 342e26b889..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/ParameterizationQuality.h +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#pragma once -#ifndef NV_MESH_PARAMETERIZATIONQUALITY_H -#define NV_MESH_PARAMETERIZATIONQUALITY_H - -#include <nvmesh/nvmesh.h> - -namespace nv -{ - class Vector2; - class Vector3; - - namespace HalfEdge { class Mesh; } - - // Estimate quality of existing parameterization. - NVMESH_CLASS class ParameterizationQuality - { - public: - ParameterizationQuality(); - ParameterizationQuality(const HalfEdge::Mesh * mesh); - - bool isValid() const; - - float rmsStretchMetric() const; - float maxStretchMetric() const; - - float rmsConformalMetric() const; - float maxAuthalicMetric() const; - - void operator += (const ParameterizationQuality & pq); - - private: - - void processTriangle(Vector3 p[3], Vector2 t[3]); - - private: - - uint m_totalTriangleCount; - uint m_flippedTriangleCount; - uint m_zeroAreaTriangleCount; - - float m_parametricArea; - float m_geometricArea; - - float m_stretchMetric; - float m_maxStretchMetric; - - float m_conformalMetric; - float m_authalicMetric; - - }; - -} // nv namespace - -#endif // NV_MESH_PARAMETERIZATIONQUALITY_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.cpp b/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.cpp deleted file mode 100644 index 4b205de8bf..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#include "nvmesh.h" // pch - -#include "SingleFaceMap.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Vertex.h" -#include "nvmesh/halfedge/Face.h" - -#include "nvmath/Vector.inl" - -using namespace nv; - - - -void nv::computeSingleFaceMap(HalfEdge::Mesh * mesh) -{ - nvDebugCheck(mesh != NULL); - nvDebugCheck(mesh->faceCount() == 1); - - HalfEdge::Face * face = mesh->faceAt(0); - nvCheck(face != NULL); - - Vector3 p0 = face->edge->from()->pos; - Vector3 p1 = face->edge->to()->pos; - - Vector3 X = normalizeSafe(p1 - p0, Vector3(0.0f), 0.0f); - Vector3 Z = face->normal(); - Vector3 Y = normalizeSafe(cross(Z, X), Vector3(0.0f), 0.0f); - - uint i = 0; - for (HalfEdge::Face::EdgeIterator it(face->edges()); !it.isDone(); it.advance(), i++) - { - HalfEdge::Vertex * vertex = it.vertex(); - nvCheck(vertex != NULL); - - if (i == 0) - { - vertex->tex = Vector2(0); - } - else - { - Vector3 pn = vertex->pos; - - float xn = dot((pn - p0), X); - float yn = dot((pn - p0), Y); - - vertex->tex = Vector2(xn, yn); - } - } -} - diff --git a/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.h b/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.h deleted file mode 100644 index b70719f5d8..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/SingleFaceMap.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright NVIDIA Corporation 2008 -- Ignacio Castano <icastano@nvidia.com> - -#pragma once -#ifndef NV_MESH_SINGLEFACEMAP_H -#define NV_MESH_SINGLEFACEMAP_H - -namespace nv -{ - namespace HalfEdge - { - class Mesh; - } - - void computeSingleFaceMap(HalfEdge::Mesh * mesh); - -} // nv namespace - -#endif // NV_MESH_SINGLEFACEMAP_H diff --git a/thirdparty/thekla_atlas/nvmesh/param/Util.cpp b/thirdparty/thekla_atlas/nvmesh/param/Util.cpp deleted file mode 100644 index fe7b58edf8..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/Util.cpp +++ /dev/null @@ -1,326 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh.h" // pch - -#include "Util.h" - -#include "nvmesh/halfedge/Mesh.h" -#include "nvmesh/halfedge/Face.h" -#include "nvmesh/halfedge/Vertex.h" - -#include "nvmath/Vector.inl" - -#include "nvcore/Array.inl" - - -using namespace nv; - -// Determine if the given mesh is a quad mesh. -bool nv::isQuadMesh(const HalfEdge::Mesh * mesh) -{ - nvDebugCheck(mesh != NULL); - - const uint faceCount = mesh->faceCount(); - for(uint i = 0; i < faceCount; i++) { - const HalfEdge::Face * face = mesh->faceAt(i); - if (face->edgeCount() != 4) { - return false; - } - } - - return true; -} - -bool nv::isTriangularMesh(const HalfEdge::Mesh * mesh) -{ - for (HalfEdge::Mesh::ConstFaceIterator it(mesh->faces()); !it.isDone(); it.advance()) - { - const HalfEdge::Face * face = it.current(); - if (face->edgeCount() != 3) return false; - } - return true; -} - - -uint nv::countMeshTriangles(const HalfEdge::Mesh * mesh) -{ - const uint faceCount = mesh->faceCount(); - - uint triangleCount = 0; - - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = mesh->faceAt(f); - - uint edgeCount = face->edgeCount(); - nvDebugCheck(edgeCount > 2); - - triangleCount += edgeCount - 2; - } - - return triangleCount; -} - -const HalfEdge::Vertex * nv::findBoundaryVertex(const HalfEdge::Mesh * mesh) -{ - const uint vertexCount = mesh->vertexCount(); - - for (uint v = 0; v < vertexCount; v++) - { - const HalfEdge::Vertex * vertex = mesh->vertexAt(v); - if (vertex->isBoundary()) return vertex; - } - - return NULL; -} - - -HalfEdge::Mesh * nv::unifyVertices(const HalfEdge::Mesh * inputMesh) -{ - HalfEdge::Mesh * mesh = new HalfEdge::Mesh; - - // Only add the first colocal. - const uint vertexCount = inputMesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) { - const HalfEdge::Vertex * vertex = inputMesh->vertexAt(v); - - if (vertex->isFirstColocal()) { - mesh->addVertex(vertex->pos); - } - } - - nv::Array<uint> indexArray; - - // Add new faces pointing to first colocals. - uint faceCount = inputMesh->faceCount(); - for (uint f = 0; f < faceCount; f++) { - const HalfEdge::Face * face = inputMesh->faceAt(f); - - indexArray.clear(); - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) { - const HalfEdge::Edge * edge = it.current(); - const HalfEdge::Vertex * vertex = edge->vertex->firstColocal(); - - indexArray.append(vertex->id); - } - - mesh->addFace(indexArray); - } - - mesh->linkBoundary(); - - return mesh; -} - -#include "nvmath/Basis.h" - -static bool pointInTriangle(const Vector2 & p, const Vector2 & a, const Vector2 & b, const Vector2 & c) -{ - return triangleArea(a, b, p) >= 0.00001f && - triangleArea(b, c, p) >= 0.00001f && - triangleArea(c, a, p) >= 0.00001f; -} - - -// This is doing a simple ear-clipping algorithm that skips invalid triangles. Ideally, we should -// also sort the ears by angle, start with the ones that have the smallest angle and proceed in order. -HalfEdge::Mesh * nv::triangulate(const HalfEdge::Mesh * inputMesh) -{ - HalfEdge::Mesh * mesh = new HalfEdge::Mesh; - - // Add all vertices. - const uint vertexCount = inputMesh->vertexCount(); - for (uint v = 0; v < vertexCount; v++) { - const HalfEdge::Vertex * vertex = inputMesh->vertexAt(v); - mesh->addVertex(vertex->pos); - } - - Array<int> polygonVertices; - Array<float> polygonAngles; - Array<Vector2> polygonPoints; - - const uint faceCount = inputMesh->faceCount(); - for (uint f = 0; f < faceCount; f++) - { - const HalfEdge::Face * face = inputMesh->faceAt(f); - nvDebugCheck(face != NULL); - - const uint edgeCount = face->edgeCount(); - nvDebugCheck(edgeCount >= 3); - - polygonVertices.clear(); - polygonVertices.reserve(edgeCount); - - if (edgeCount == 3) { - // Simple case for triangles. - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - const HalfEdge::Vertex * vertex = edge->vertex; - polygonVertices.append(vertex->id); - } - - int v0 = polygonVertices[0]; - int v1 = polygonVertices[1]; - int v2 = polygonVertices[2]; - - mesh->addFace(v0, v1, v2); - } - else { - // Build 2D polygon projecting vertices onto normal plane. - // Faces are not necesarily planar, this is for example the case, when the face comes from filling a hole. In such cases - // it's much better to use the best fit plane. - const Vector3 fn = face->normal(); - - Basis basis; - basis.buildFrameForDirection(fn); - - polygonPoints.clear(); - polygonPoints.reserve(edgeCount); - polygonAngles.clear(); - polygonAngles.reserve(edgeCount); - - for (HalfEdge::Face::ConstEdgeIterator it(face->edges()); !it.isDone(); it.advance()) - { - const HalfEdge::Edge * edge = it.current(); - const HalfEdge::Vertex * vertex = edge->vertex; - polygonVertices.append(vertex->id); - - Vector2 p; - p.x = dot(basis.tangent, vertex->pos); - p.y = dot(basis.bitangent, vertex->pos); - - polygonPoints.append(p); - } - polygonAngles.resize(edgeCount); - - while (polygonVertices.size() > 2) { - uint size = polygonVertices.size(); - - // Update polygon angles. @@ Update only those that have changed. - float minAngle = 2 * PI; - uint bestEar = 0; // Use first one if none of them is valid. - bool bestIsValid = false; - for (uint i = 0; i < size; i++) { - uint i0 = i; - uint i1 = (i+1) % size; // Use Sean's polygon interation trick. - uint i2 = (i+2) % size; - - Vector2 p0 = polygonPoints[i0]; - Vector2 p1 = polygonPoints[i1]; - Vector2 p2 = polygonPoints[i2]; - - float d = clamp(dot(p0-p1, p2-p1) / (length(p0-p1) * length(p2-p1)), -1.0f, 1.0f); - float angle = acosf(d); - - float area = triangleArea(p0, p1, p2); - if (area < 0.0f) angle = 2.0f * PI - angle; - - polygonAngles[i1] = angle; - - if (angle < minAngle || !bestIsValid) { - - // Make sure this is a valid ear, if not, skip this point. - bool valid = true; - for (uint j = 0; j < size; j++) { - if (j == i0 || j == i1 || j == i2) continue; - Vector2 p = polygonPoints[j]; - - if (pointInTriangle(p, p0, p1, p2)) { - valid = false; - break; - } - } - - if (valid || !bestIsValid) { - minAngle = angle; - bestEar = i1; - bestIsValid = valid; - } - } - } - - nvDebugCheck(minAngle <= 2 * PI); - - // Clip best ear: - - uint i0 = (bestEar+size-1) % size; - uint i1 = (bestEar+0) % size; - uint i2 = (bestEar+1) % size; - - int v0 = polygonVertices[i0]; - int v1 = polygonVertices[i1]; - int v2 = polygonVertices[i2]; - - mesh->addFace(v0, v1, v2); - - polygonVertices.removeAt(i1); - polygonPoints.removeAt(i1); - polygonAngles.removeAt(i1); - } - } - -#if 0 - - uint i = 0; - while (polygonVertices.size() > 2 && i < polygonVertices.size()) { - uint size = polygonVertices.size(); - uint i0 = (i+0) % size; - uint i1 = (i+1) % size; - uint i2 = (i+2) % size; - - const HalfEdge::Vertex * v0 = polygonVertices[i0]; - const HalfEdge::Vertex * v1 = polygonVertices[i1]; - const HalfEdge::Vertex * v2 = polygonVertices[i2]; - - const Vector3 p0 = v0->pos; - const Vector3 p1 = v1->pos; - const Vector3 p2 = v2->pos; - - const Vector3 e0 = p2 - p1; - const Vector3 e1 = p0 - p1; - - // If this ear forms a valid triangle, setup relations, remove v1 and repeat. - Vector3 n = cross(e0, e1); - float len = dot(fn, n); // = sin(angle) - - float angle = asin(len); - - - if (len > 0.0f) { - mesh->addFace(v0->id(), v1->id(), v2->id()); - polygonVertices.removeAt(i1); - polygonAngles.removeAt(i1); - if (i2 > i1) i2--; - // @@ Update angles at i0 and i2 - } - else { - i++; - } - } - - // @@ Create a few degenerate triangles to avoid introducing holes. - i = 0; - const uint size = polygonVertices.size(); - while (i < size - 2) { - uint i0 = (i+0) % size; - uint i1 = (i+1) % size; - uint i2 = (i+2) % size; - - const HalfEdge::Vertex * v0 = polygonVertices[i0]; - const HalfEdge::Vertex * v1 = polygonVertices[i1]; - const HalfEdge::Vertex * v2 = polygonVertices[i2]; - - mesh->addFace(v0->id(), v1->id(), v2->id()); - i++; - } -#endif - } - - mesh->linkBoundary(); - - return mesh; -} - - diff --git a/thirdparty/thekla_atlas/nvmesh/param/Util.h b/thirdparty/thekla_atlas/nvmesh/param/Util.h deleted file mode 100644 index 774563ac0b..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/param/Util.h +++ /dev/null @@ -1,18 +0,0 @@ -// This code is in the public domain -- castano@gmail.com - -#include "nvmesh/nvmesh.h" - -namespace nv { - - namespace HalfEdge { class Mesh; class Vertex; } - - bool isQuadMesh(const HalfEdge::Mesh * mesh); - bool isTriangularMesh(const HalfEdge::Mesh * mesh); - - uint countMeshTriangles(const HalfEdge::Mesh * mesh); - const HalfEdge::Vertex * findBoundaryVertex(const HalfEdge::Mesh * mesh); - - HalfEdge::Mesh * unifyVertices(const HalfEdge::Mesh * inputMesh); - HalfEdge::Mesh * triangulate(const HalfEdge::Mesh * inputMesh); - -} // nv namespace diff --git a/thirdparty/thekla_atlas/nvmesh/raster/ClippedTriangle.h b/thirdparty/thekla_atlas/nvmesh/raster/ClippedTriangle.h deleted file mode 100644 index 0947d4851c..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/raster/ClippedTriangle.h +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright NVIDIA Corporation 2007 -- Denis Kovacs <den.kovacs@gmail.com> - -#pragma once -#ifndef NV_MESH_CLIPPEDTRIANGLE_H -#define NV_MESH_CLIPPEDTRIANGLE_H - -#include <nvmath/Vector.h> - -namespace nv -{ - - class ClippedTriangle - { - public: - ClippedTriangle(Vector2::Arg a, Vector2::Arg b, Vector2::Arg c) - { - m_numVertices = 3; - m_activeVertexBuffer = 0; - - m_verticesA[0]=a; - m_verticesA[1]=b; - m_verticesA[2]=c; - - m_vertexBuffers[0] = m_verticesA; - m_vertexBuffers[1] = m_verticesB; - } - - uint vertexCount() - { - return m_numVertices; - } - - const Vector2 * vertices() - { - return m_vertexBuffers[m_activeVertexBuffer]; - } - - inline void clipHorizontalPlane(float offset, float clipdirection) - { - Vector2 * v = m_vertexBuffers[m_activeVertexBuffer]; - m_activeVertexBuffer ^= 1; - Vector2 * v2 = m_vertexBuffers[m_activeVertexBuffer]; - - v[m_numVertices] = v[0]; - - float dy2, dy1 = offset - v[0].y; - int dy2in, dy1in = clipdirection*dy1 >= 0; - uint p=0; - - for (uint k=0; k<m_numVertices; k++) - { - dy2 = offset - v[k+1].y; - dy2in = clipdirection*dy2 >= 0; - - if (dy1in) v2[p++] = v[k]; - - if ( dy1in + dy2in == 1 ) // not both in/out - { - float dx = v[k+1].x - v[k].x; - float dy = v[k+1].y - v[k].y; - v2[p++] = Vector2(v[k].x + dy1*(dx/dy), offset); - } - - dy1 = dy2; dy1in = dy2in; - } - m_numVertices = p; - - //for (uint k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n"); - } - - inline void clipVerticalPlane(float offset, float clipdirection ) - { - Vector2 * v = m_vertexBuffers[m_activeVertexBuffer]; - m_activeVertexBuffer ^= 1; - Vector2 * v2 = m_vertexBuffers[m_activeVertexBuffer]; - - v[m_numVertices] = v[0]; - - float dx2, dx1 = offset - v[0].x; - int dx2in, dx1in = clipdirection*dx1 >= 0; - uint p=0; - - for (uint k=0; k<m_numVertices; k++) - { - dx2 = offset - v[k+1].x; - dx2in = clipdirection*dx2 >= 0; - - if (dx1in) v2[p++] = v[k]; - - if ( dx1in + dx2in == 1 ) // not both in/out - { - float dx = v[k+1].x - v[k].x; - float dy = v[k+1].y - v[k].y; - v2[p++] = Vector2(offset, v[k].y + dx1*(dy/dx)); - } - - dx1 = dx2; dx1in = dx2in; - } - m_numVertices = p; - - //for (uint k=0; k<m_numVertices; k++) printf("(%f, %f)\n", v2[k].x, v2[k].y); printf("\n"); - } - - void computeAreaCentroid() - { - Vector2 * v = m_vertexBuffers[m_activeVertexBuffer]; - v[m_numVertices] = v[0]; - - m_area = 0; - float centroidx=0, centroidy=0; - for (uint k=0; k<m_numVertices; k++) - { - // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/ - float f = v[k].x*v[k+1].y - v[k+1].x*v[k].y; - m_area += f; - centroidx += f * (v[k].x + v[k+1].x); - centroidy += f * (v[k].y + v[k+1].y); - } - m_area = 0.5f * fabs(m_area); - if (m_area==0) { - m_centroid = Vector2(0.0f); - } else { - m_centroid = Vector2(centroidx/(6*m_area), centroidy/(6*m_area)); - } - } - - void clipAABox(float x0, float y0, float x1, float y1) - { - clipVerticalPlane ( x0, -1); - clipHorizontalPlane( y0, -1); - clipVerticalPlane ( x1, 1); - clipHorizontalPlane( y1, 1); - - computeAreaCentroid(); - } - - Vector2 centroid() - { - return m_centroid; - } - - float area() - { - return m_area; - } - - private: - Vector2 m_verticesA[7+1]; - Vector2 m_verticesB[7+1]; - Vector2 * m_vertexBuffers[2]; - uint m_numVertices; - uint m_activeVertexBuffer; - float m_area; - Vector2 m_centroid; - }; - -} // nv namespace - -#endif // NV_MESH_CLIPPEDTRIANGLE_H diff --git a/thirdparty/thekla_atlas/nvmesh/raster/Raster.cpp b/thirdparty/thekla_atlas/nvmesh/raster/Raster.cpp deleted file mode 100644 index d46b34f045..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/raster/Raster.cpp +++ /dev/null @@ -1,626 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -/** @file Raster.cpp - * @brief Triangle rasterization library using affine interpolation. Not - * specially optimized, but enough for my purposes. -**/ - -#include "nvmesh.h" // pch - -#include "Raster.h" -#include "ClippedTriangle.h" - -#include "nvcore/Utils.h" // min, max - -#include "nvmath/Vector.inl" -#include "nvmath/ftoi.h" - - -#define RA_EPSILON 0.00001f - -using namespace nv; -using namespace nv::Raster; - -namespace -{ - static inline float delta(float bot, float top, float ih) - { - return (bot - top) * ih; - } - - static inline Vector2 delta(Vector2::Arg bot, Vector2::Arg top, float ih) - { - return (bot - top) * ih; - } - - static inline Vector3 delta(Vector3::Arg bot, Vector3::Arg top, float ih) - { - return (bot - top) * ih; - } - - // @@ The implementation in nvmath.h should be equivalent. - static inline int iround(float f) - { - // @@ Optimize this. - return int(floorf(f+0.5f)); - //return int(round(f)); - //return int(f); - } - - /// A triangle vertex. - struct Vertex - { - Vector2 pos; // Position. - Vector3 tex; // Texcoord. (Barycentric coordinate) - }; - - - /// A triangle for rasterization. - struct Triangle - { - Triangle(Vector2::Arg v0, Vector2::Arg v1, Vector2::Arg v2, Vector3::Arg t0, Vector3::Arg t1, Vector3::Arg t2); - - bool computeDeltas(); - - bool draw(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param); - bool drawAA(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param); - bool drawC(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param); - void flipBackface(); - void computeUnitInwardNormals(); - - // Vertices. - Vector2 v1, v2, v3; - Vector2 n1, n2, n3; // unit inward normals - Vector3 t1, t2, t3; - - // Deltas. - Vector3 dx, dy; - - float sign; - bool valid; - }; - - - /// Triangle ctor. - Triangle::Triangle(Vector2::Arg v0, Vector2::Arg v1, Vector2::Arg v2, - Vector3::Arg t0, Vector3::Arg t1, Vector3::Arg t2) - { - // Init vertices. - this->v1 = v0; - this->v2 = v2; - this->v3 = v1; - - // Set barycentric coordinates. - this->t1 = t0; - this->t2 = t2; - this->t3 = t1; - - // make sure every triangle is front facing. - flipBackface(); - - // Compute deltas. - valid = computeDeltas(); - - computeUnitInwardNormals(); - } - - - /// Compute texture space deltas. - /// This method takes two edge vectors that form a basis, determines the - /// coordinates of the canonic vectors in that basis, and computes the - /// texture gradient that corresponds to those vectors. - bool Triangle::computeDeltas() - { - Vector2 e0 = v3 - v1; - Vector2 e1 = v2 - v1; - - Vector3 de0 = t3 - t1; - Vector3 de1 = t2 - t1; - - float denom = 1.0f / (e0.y * e1.x - e1.y * e0.x); - if (!isFinite(denom)) { - return false; - } - - float lambda1 = - e1.y * denom; - float lambda2 = e0.y * denom; - float lambda3 = e1.x * denom; - float lambda4 = - e0.x * denom; - - dx = de0 * lambda1 + de1 * lambda2; - dy = de0 * lambda3 + de1 * lambda4; - - return true; - } - - // compute unit inward normals for each edge. - void Triangle::computeUnitInwardNormals() - { - n1 = v1 - v2; n1 = Vector2(-n1.y, n1.x); n1 = n1 * (1.0f/sqrtf(n1.x*n1.x + n1.y*n1.y)); - n2 = v2 - v3; n2 = Vector2(-n2.y, n2.x); n2 = n2 * (1.0f/sqrtf(n2.x*n2.x + n2.y*n2.y)); - n3 = v3 - v1; n3 = Vector2(-n3.y, n3.x); n3 = n3 * (1.0f/sqrtf(n3.x*n3.x + n3.y*n3.y)); - } - - void Triangle::flipBackface() - { - // check if triangle is backfacing, if so, swap two vertices - if ( ((v3.x-v1.x)*(v2.y-v1.y) - (v3.y-v1.y)*(v2.x-v1.x)) < 0 ) { - Vector2 hv=v1; v1=v2; v2=hv; // swap pos - Vector3 ht=t1; t1=t2; t2=ht; // swap tex - } - } - - bool Triangle::draw(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param) - { - // 28.4 fixed-point coordinates - const int Y1 = iround(16.0f * v1.y); - const int Y2 = iround(16.0f * v2.y); - const int Y3 = iround(16.0f * v3.y); - - const int X1 = iround(16.0f * v1.x); - const int X2 = iround(16.0f * v2.x); - const int X3 = iround(16.0f * v3.x); - - // Deltas - const int DX12 = X1 - X2; - const int DX23 = X2 - X3; - const int DX31 = X3 - X1; - - const int DY12 = Y1 - Y2; - const int DY23 = Y2 - Y3; - const int DY31 = Y3 - Y1; - - // Fixed-point deltas - const int FDX12 = DX12 << 4; - const int FDX23 = DX23 << 4; - const int FDX31 = DX31 << 4; - - const int FDY12 = DY12 << 4; - const int FDY23 = DY23 << 4; - const int FDY31 = DY31 << 4; - - int minx, miny, maxx, maxy; - if (enableScissors) { - int frustumX0 = 0 << 4; - int frustumY0 = 0 << 4; - int frustumX1 = (int)extents.x << 4; - int frustumY1 = (int)extents.y << 4; - - // Bounding rectangle - minx = (nv::max(min3(X1, X2, X3), frustumX0) + 0xF) >> 4; - miny = (nv::max(min3(Y1, Y2, Y3), frustumY0) + 0xF) >> 4; - maxx = (nv::min(max3(X1, X2, X3), frustumX1) + 0xF) >> 4; - maxy = (nv::min(max3(Y1, Y2, Y3), frustumY1) + 0xF) >> 4; - } - else { - // Bounding rectangle - minx = (min3(X1, X2, X3) + 0xF) >> 4; - miny = (min3(Y1, Y2, Y3) + 0xF) >> 4; - maxx = (max3(X1, X2, X3) + 0xF) >> 4; - maxy = (max3(Y1, Y2, Y3) + 0xF) >> 4; - } - - // Block size, standard 8x8 (must be power of two) - const int q = 8; - - // @@ This won't work when minx,miny are negative. This code path is not used. Leaving as is for now. - nvCheck(minx >= 0); - nvCheck(miny >= 0); - - // Start in corner of 8x8 block - minx &= ~(q - 1); - miny &= ~(q - 1); - - // Half-edge constants - int C1 = DY12 * X1 - DX12 * Y1; - int C2 = DY23 * X2 - DX23 * Y2; - int C3 = DY31 * X3 - DX31 * Y3; - - // Correct for fill convention - if(DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++; - if(DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++; - if(DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++; - - // Loop through blocks - for(int y = miny; y < maxy; y += q) - { - for(int x = minx; x < maxx; x += q) - { - // Corners of block - int x0 = x << 4; - int x1 = (x + q - 1) << 4; - int y0 = y << 4; - int y1 = (y + q - 1) << 4; - - // Evaluate half-space functions - bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0; - bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0; - bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0; - bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0; - int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3); - - bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0; - bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0; - bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0; - bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0; - int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3); - - bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0; - bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0; - bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0; - bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0; - int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3); - - // Skip block when outside an edge - if(a == 0x0 || b == 0x0 || c == 0x0) continue; - - // Accept whole block when totally covered - if(a == 0xF && b == 0xF && c == 0xF) - { - Vector3 texRow = t1 + dy*(y0 - v1.y) + dx*(x0 - v1.x); - - for(int iy = y; iy < y + q; iy++) - { - Vector3 tex = texRow; - for(int ix = x; ix < x + q; ix++) - { - //Vector3 tex = t1 + dx * (ix - v1.x) + dy * (iy - v1.y); - if (!cb(param, ix, iy, tex, dx, dy, 1.0)) { - // early out. - return false; - } - tex += dx; - } - texRow += dy; - } - } - else // Partially covered block - { - int CY1 = C1 + DX12 * y0 - DY12 * x0; - int CY2 = C2 + DX23 * y0 - DY23 * x0; - int CY3 = C3 + DX31 * y0 - DY31 * x0; - Vector3 texRow = t1 + dy*(y0 - v1.y) + dx*(x0 - v1.x); - - for(int iy = y; iy < y + q; iy++) - { - int CX1 = CY1; - int CX2 = CY2; - int CX3 = CY3; - Vector3 tex = texRow; - - for(int ix = x; ix < x + q; ix++) - { - if(CX1 > 0 && CX2 > 0 && CX3 > 0) - { - if (!cb(param, ix, iy, tex, dx, dy, 1.0)) - { - // early out. - return false; - } - } - - CX1 -= FDY12; - CX2 -= FDY23; - CX3 -= FDY31; - tex += dx; - } - - CY1 += FDX12; - CY2 += FDX23; - CY3 += FDX31; - texRow += dy; - } - } - } - } - - return true; - } - - -#define PX_INSIDE 1.0f/sqrt(2.0f) -#define PX_OUTSIDE -1.0f/sqrt(2.0f) - -#define BK_SIZE 8 -#define BK_INSIDE sqrt(BK_SIZE*BK_SIZE/2.0f) -#define BK_OUTSIDE -sqrt(BK_SIZE*BK_SIZE/2.0f) - - // extents has to be multiple of BK_SIZE!! - bool Triangle::drawAA(const Vector2 & extents, bool enableScissors, SamplingCallback cb, void * param) - { - float minx, miny, maxx, maxy; - if (enableScissors) { - // Bounding rectangle - minx = floorf(max(min3(v1.x, v2.x, v3.x), 0.0f)); - miny = floorf(max(min3(v1.y, v2.y, v3.y), 0.0f)); - maxx = ceilf( min(max3(v1.x, v2.x, v3.x), extents.x-1.0f)); - maxy = ceilf( min(max3(v1.y, v2.y, v3.y), extents.y-1.0f)); - } - else { - // Bounding rectangle - minx = floorf(min3(v1.x, v2.x, v3.x)); - miny = floorf(min3(v1.y, v2.y, v3.y)); - maxx = ceilf( max3(v1.x, v2.x, v3.x)); - maxy = ceilf( max3(v1.y, v2.y, v3.y)); - } - - // There's no reason to align the blocks to the viewport, instead we align them to the origin of the triangle bounds. - minx = floorf(minx); - miny = floorf(miny); - //minx = (float)(((int)minx) & (~((int)BK_SIZE - 1))); // align to blocksize (we don't need to worry about blocks partially out of viewport) - //miny = (float)(((int)miny) & (~((int)BK_SIZE - 1))); - - minx += 0.5; miny +=0.5; // sampling at texel centers! - maxx += 0.5; maxy +=0.5; - - // Half-edge constants - float C1 = n1.x * (-v1.x) + n1.y * (-v1.y); - float C2 = n2.x * (-v2.x) + n2.y * (-v2.y); - float C3 = n3.x * (-v3.x) + n3.y * (-v3.y); - - // Loop through blocks - for(float y0 = miny; y0 <= maxy; y0 += BK_SIZE) - { - for(float x0 = minx; x0 <= maxx; x0 += BK_SIZE) - { - // Corners of block - float xc = (x0 + (BK_SIZE-1)/2.0f); - float yc = (y0 + (BK_SIZE-1)/2.0f); - - // Evaluate half-space functions - float aC = C1 + n1.x * xc + n1.y * yc; - float bC = C2 + n2.x * xc + n2.y * yc; - float cC = C3 + n3.x * xc + n3.y * yc; - - // Skip block when outside an edge - if( (aC <= BK_OUTSIDE) || (bC <= BK_OUTSIDE) || (cC <= BK_OUTSIDE) ) continue; - - // Accept whole block when totally covered - if( (aC >= BK_INSIDE) && (bC >= BK_INSIDE) && (cC >= BK_INSIDE) ) - { - Vector3 texRow = t1 + dy*(y0 - v1.y) + dx*(x0 - v1.x); - - for (float y = y0; y < y0 + BK_SIZE; y++) - { - Vector3 tex = texRow; - for(float x = x0; x < x0 + BK_SIZE; x++) - { - if (!cb(param, (int)x, (int)y, tex, dx, dy, 1.0f)) - { - return false; - } - tex += dx; - } - texRow += dy; - } - } - else // Partially covered block - { - float CY1 = C1 + n1.x * x0 + n1.y * y0; - float CY2 = C2 + n2.x * x0 + n2.y * y0; - float CY3 = C3 + n3.x * x0 + n3.y * y0; - Vector3 texRow = t1 + dy*(y0 - v1.y) + dx*(x0 - v1.x); - - for(float y = y0; y < y0 + BK_SIZE; y++) // @@ This is not clipping to scissor rectangle correctly. - { - float CX1 = CY1; - float CX2 = CY2; - float CX3 = CY3; - Vector3 tex = texRow; - - for (float x = x0; x < x0 + BK_SIZE; x++) // @@ This is not clipping to scissor rectangle correctly. - { - if (CX1 >= PX_INSIDE && CX2 >= PX_INSIDE && CX3 >= PX_INSIDE) - { - // pixel completely covered - Vector3 tex = t1 + dx * (x - v1.x) + dy * (y - v1.y); - if (!cb(param, (int)x, (int)y, tex, dx, dy, 1.0f)) - { - return false; - } - } - else if ((CX1 >= PX_OUTSIDE) && (CX2 >= PX_OUTSIDE) && (CX3 >= PX_OUTSIDE)) - { - // triangle partially covers pixel. do clipping. - ClippedTriangle ct(v1-Vector2(x,y), v2-Vector2(x,y), v3-Vector2(x,y)); - ct.clipAABox(-0.5, -0.5, 0.5, 0.5); - Vector2 centroid = ct.centroid(); - float area = ct.area(); - if (area > 0.0f) - { - Vector3 texCent = tex - dx*centroid.x - dy*centroid.y; - //nvCheck(texCent.x >= -0.1f && texCent.x <= 1.1f); // @@ Centroid is not very exact... - //nvCheck(texCent.y >= -0.1f && texCent.y <= 1.1f); - //nvCheck(texCent.z >= -0.1f && texCent.z <= 1.1f); - //Vector3 texCent2 = t1 + dx * (x - v1.x) + dy * (y - v1.y); - if (!cb(param, (int)x, (int)y, texCent, dx, dy, area)) - { - return false; - } - } - } - - CX1 += n1.x; - CX2 += n2.x; - CX3 += n3.x; - tex += dx; - } - - CY1 += n1.y; - CY2 += n2.y; - CY3 += n3.y; - texRow += dy; - } - } - } - } - - return true; - } - -} // namespace - - -/// Process the given triangle. -bool nv::Raster::drawTriangle(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[3], SamplingCallback cb, void * param) -{ - Triangle tri(v[0], v[1], v[2], Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, 1)); - - // @@ It would be nice to have a conservative drawing mode that enlarges the triangle extents by one texel and is able to handle degenerate triangles. - // @@ Maybe the simplest thing to do would be raster triangle edges. - - if (tri.valid) { - if (mode == Mode_Antialiased) { - return tri.drawAA(extents, enableScissors, cb, param); - } - if (mode == Mode_Nearest) { - return tri.draw(extents, enableScissors, cb, param); - } - } - - return true; -} - -inline static float triangleArea(Vector2::Arg v1, Vector2::Arg v2, Vector2::Arg v3) -{ - return 0.5f * (v3.x * v1.y + v1.x * v2.y + v2.x * v3.y - v2.x * v1.y - v3.x * v2.y - v1.x * v3.y); -} - -/// Process the given quad. -bool nv::Raster::drawQuad(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[4], SamplingCallback cb, void * param) -{ - bool sign0 = triangleArea(v[0], v[1], v[2]) > 0.0f; - bool sign1 = triangleArea(v[0], v[2], v[3]) > 0.0f; - - // Divide the quad into two non overlapping triangles. - if (sign0 == sign1) { - Triangle tri0(v[0], v[1], v[2], Vector3(0,0,0), Vector3(1,0,0), Vector3(1,1,0)); - Triangle tri1(v[0], v[2], v[3], Vector3(0,0,0), Vector3(1,1,0), Vector3(0,1,0)); - - if (tri0.valid && tri1.valid) { - if (mode == Mode_Antialiased) { - return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param); - } else { - return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param); - } - } - } - else - { - Triangle tri0(v[0], v[1], v[3], Vector3(0,0,0), Vector3(1,0,0), Vector3(0,1,0)); - Triangle tri1(v[1], v[2], v[3], Vector3(1,0,0), Vector3(1,1,0), Vector3(0,1,0)); - - if (tri0.valid && tri1.valid) { - if (mode == Mode_Antialiased) { - return tri0.drawAA(extents, enableScissors, cb, param) && tri1.drawAA(extents, enableScissors, cb, param); - } else { - return tri0.draw(extents, enableScissors, cb, param) && tri1.draw(extents, enableScissors, cb, param); - } - } - } - - return true; -} - - -static bool drawPoint(const Vector2 & p, const Vector2 v[2], LineSamplingCallback cb, void * param) { - - int x = ftoi_round(p.x); - int y = ftoi_round(p.y); - Vector2 ip = Vector2(float(x) + 0.5f, float(y) + 0.5f); - - float t; - - // Return minimum distance between line segment vw and point p - Vector2 dv = v[1] - v[0]; - const float l2 = nv::lengthSquared(dv); // i.e. |w-v|^2 - avoid a sqrt - if (l2 == 0.0) { - t = 0; // v0 == v1 case - } - else { - // Consider the line extending the segment, parameterized as v + t (w - v). - // We find projection of point p onto the line. - // It falls where t = [(p-v) . (w-v)] / |w-v|^2 - t = dot(ip - v[0], dv) / l2; - if (t < 0.0) { - t = 0; // Beyond the 'v0' end of the segment - } - else if (t > 1.0) { - t = 1; // Beyond the 'v1' end of the segment - } - } - - Vector2 projection = v[0] + t * dv; // Projection falls on the segment - - float d = distance(ip, projection); - - return cb(param, x, y, t, saturate(1-d)); -} - - -void nv::Raster::drawLine(bool antialias, Vector2::Arg extents, bool enableScissors, const Vector2 v[2], LineSamplingCallback cb, void * param) -{ - nvCheck(antialias == true); // @@ Not implemented. - //nvCheck(enableScissors == false); // @@ Not implemented. - - // Very crappy DDA implementation. - - Vector2 p = v[0]; - Vector2 dp, dpdy; - - float dx = v[1].x - v[0].x; - float dy = v[1].y - v[0].y; - int n; - - // Degenerate line. - if (dx == 0 && dy == 0) return; - - if (fabsf(dx) >= fabsf(dy)) { - n = iround(fabsf(dx)); - dp.x = dx / fabsf(dx); - dp.y = dy / fabsf(dx); - nvDebugCheck(fabsf(dp.y) <= 1.0f); - dpdy.x = 0; - dpdy.y = 1; - } - else { - n = iround(fabs(dy)); - dp.x = dx / fabsf(dy); - dp.y = dy / fabsf(dy); - nvDebugCheck(fabsf(dp.x) <= 1.0f); - dpdy.x = 1; - dpdy.y = 0; - } - - for (int i = 0; i <= n; i++) { - drawPoint(p, v, cb, param); - drawPoint(p + dpdy, v, cb, param); - drawPoint(p - dpdy, v, cb, param); - p += dp; - } -} - - -// Draw vertical or horizontal segments. For degenerate triangles. -/*bool nv::Raster::drawSegment(Vector2::Arg extents, bool enableScissors, const Vector2 v[2], LineSamplingCallback cb, void * param) -{ - nvCheck(enableScissors == false); - - - if (v[0].x == v[1].x) { // Vertical segment. - - } - else if (v[0].y == v[1].y) { // Horizontal segment. - int y = ftoi_round(v[0].y); - int x0 = ftoi_floor(v[0].x); - int x1 = ftoi_floor(v[0].x); - - for (int x = x0; x <= x1; x++) { - - cb(param, x, y, t, - } - } - - return false; // Not a valid segment. -} -*/ diff --git a/thirdparty/thekla_atlas/nvmesh/raster/Raster.h b/thirdparty/thekla_atlas/nvmesh/raster/Raster.h deleted file mode 100644 index 05af2ddb00..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/raster/Raster.h +++ /dev/null @@ -1,49 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#pragma once -#ifndef NV_MESH_RASTER_H -#define NV_MESH_RASTER_H - -/** @file Raster.h - * @brief Rasterization library. - * - * This is just a standard scanline rasterizer that I took from one of my old - * projects. The perspective correction wasn't necessary so I just removed it. -**/ - -#include "nvmath/Vector.h" -#include "nvmesh/nvmesh.h" - -namespace nv -{ - - namespace Raster - { - enum Mode { - Mode_Nearest, - Mode_Antialiased, - //Mode_Conservative - }; - - - /// A callback to sample the environment. Return false to terminate rasterization. - typedef bool (NV_CDECL * SamplingCallback)(void * param, int x, int y, Vector3::Arg bar, Vector3::Arg dx, Vector3::Arg dy, float coverage); - - // Process the given triangle. Returns false if rasterization was interrupted by the callback. - NVMESH_API bool drawTriangle(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[3], SamplingCallback cb, void * param); - - // Process the given quad. Returns false if rasterization was interrupted by the callback. - NVMESH_API bool drawQuad(Mode mode, Vector2::Arg extents, bool enableScissors, const Vector2 v[4], SamplingCallback cb, void * param); - - typedef bool (NV_CDECL * LineSamplingCallback)(void * param, int x, int y, float t, float d); // t is the position along the segment, d is the distance to the line. - - // Process the given line. - NVMESH_API void drawLine(bool antialias, Vector2::Arg extents, bool enableScissors, const Vector2 v[2], LineSamplingCallback cb, void * param); - - // Draw vertical or horizontal segments. For degenerate triangles. - //NVMESH_API void drawSegment(Vector2::Arg extents, bool enableScissors, const Vector2 v[2], SamplingCallback cb, void * param); - } -} - - -#endif // NV_MESH_RASTER_H diff --git a/thirdparty/thekla_atlas/nvmesh/weld/Snap.cpp b/thirdparty/thekla_atlas/nvmesh/weld/Snap.cpp deleted file mode 100644 index b6bff4d83d..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/Snap.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#include <nvcore/RadixSort.h> - -#include <nvmesh/weld/Snap.h> -#include <nvmesh/TriMesh.h> -#include <nvmesh/geometry/Bounds.h> - -using namespace nv; - -namespace { - - // Snap the given vertices. - void Snap(TriMesh::Vertex & a, TriMesh::Vertex & b, float texThreshold, float norThreshold) - { - a.pos = b.pos = (a.pos + b.pos) * 0.5f; - - if (equal(a.tex.x, b.tex.x, texThreshold) && equal(a.tex.y, b.tex.y, texThreshold)) { - b.tex = a.tex = (a.tex + b.tex) * 0.5f; - } - - if (equal(a.nor.x, b.nor.x, norThreshold) && equal(a.nor.y, b.nor.y, norThreshold) && equal(a.nor.z, b.nor.z, norThreshold)) { - b.nor = a.nor = (a.nor + b.nor) * 0.5f; - } - }; - -} // nv namespace - -uint nv::SnapVertices(TriMesh * mesh, float posThreshold, float texThreshold, float norThreshold) -{ - nvDebug("--- Snapping vertices.\n"); - - // Determine largest axis. - Box box = MeshBounds::box(mesh); - Vector3 extents = box.extents(); - - int axis = 2; - if( extents.x > extents.y ) { - if( extents.x > extents.z ) { - axis = 0; - } - } - else if(extents.y > extents.z) { - axis = 1; - } - - // @@ Use diagonal instead! - - - // Sort vertices according to the largest axis. - const uint vertexCount = mesh->vertexCount(); - nvCheck(vertexCount > 2); // Must have at least two vertices. - - // Get pos channel. - //PiMesh::Channel * pos_channel = mesh->GetChannel(mesh->FindChannel(VS_POS)); - //nvCheck( pos_channel != NULL ); - - //const PiArray<Vec4> & pos_array = pos_channel->data; - - Array<float> distArray; - distArray.resize(vertexCount); - - for(uint v = 0; v < vertexCount; v++) { - if (axis == 0) distArray[v] = mesh->vertexAt(v).pos.x; - else if (axis == 1) distArray[v] = mesh->vertexAt(v).pos.y; - else distArray[v] = mesh->vertexAt(v).pos.z; - } - - RadixSort radix; - const uint * xrefs = radix.sort(distArray.buffer(), distArray.count()).ranks(); - nvCheck(xrefs != NULL); - - uint snapCount = 0; - for(uint v = 0; v < vertexCount-1; v++) { - for(uint n = v+1; n < vertexCount; n++) { - nvDebugCheck( distArray[xrefs[v]] <= distArray[xrefs[n]] ); - - if (fabs(distArray[xrefs[n]] - distArray[xrefs[v]]) > posThreshold) { - break; - } - - TriMesh::Vertex & v0 = mesh->vertexAt(xrefs[v]); - TriMesh::Vertex & v1 = mesh->vertexAt(xrefs[n]); - - const float dist = length(v0.pos - v1.pos); - - if (dist <= posThreshold) { - Snap(v0, v1, texThreshold, norThreshold); - snapCount++; - } - } - } - - // @@ todo: debug, make sure that the distance between vertices is now >= threshold - - nvDebug("--- %u vertices snapped\n", snapCount); - - return snapCount; -}; - diff --git a/thirdparty/thekla_atlas/nvmesh/weld/Snap.h b/thirdparty/thekla_atlas/nvmesh/weld/Snap.h deleted file mode 100644 index 8e0566cda3..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/Snap.h +++ /dev/null @@ -1,18 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#ifndef NV_MESH_SNAP_H -#define NV_MESH_SNAP_H - -#include <nvmesh/nvmesh.h> -#include <nvmath/nvmath.h> - -namespace nv -{ - class TriMesh; - - NVMESH_API uint SnapVertices(TriMesh * mesh, float posThreshold=NV_EPSILON, float texThreshold=1.0f/1024, float norThreshold=NV_NORMAL_EPSILON); - -} // nv namespace - - -#endif // NV_MESH_SNAP_H diff --git a/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.cpp b/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.cpp deleted file mode 100644 index 2ba4dcae18..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.cpp +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> - -#include <nvmesh/TriMesh.h> -#include <nvmesh/QuadTriMesh.h> - -#include <nvmesh/weld/VertexWeld.h> -#include <nvmesh/weld/Weld.h> - -using namespace nv; - -// Weld trimesh vertices -void nv::WeldVertices(TriMesh * mesh) -{ - nvDebug("--- Welding vertices.\n"); - - nvCheck(mesh != NULL); - - uint count = mesh->vertexCount(); - Array<uint> xrefs; - Weld<TriMesh::Vertex> weld; - uint newCount = weld(mesh->vertices(), xrefs); - - nvDebug("--- %d vertices welded\n", count - newCount); - - - // Remap faces. - const uint faceCount = mesh->faceCount(); - for(uint f = 0; f < faceCount; f++) - { - TriMesh::Face & face = mesh->faceAt(f); - face.v[0] = xrefs[face.v[0]]; - face.v[1] = xrefs[face.v[1]]; - face.v[2] = xrefs[face.v[2]]; - } -} - - -// Weld trimesh vertices -void nv::WeldVertices(QuadTriMesh * mesh) -{ - nvDebug("--- Welding vertices.\n"); - - nvCheck(mesh != NULL); - - uint count = mesh->vertexCount(); - Array<uint> xrefs; - Weld<TriMesh::Vertex> weld; - uint newCount = weld(mesh->vertices(), xrefs); - - nvDebug("--- %d vertices welded\n", count - newCount); - - // Remap faces. - const uint faceCount = mesh->faceCount(); - for(uint f = 0; f < faceCount; f++) - { - QuadTriMesh::Face & face = mesh->faceAt(f); - face.v[0] = xrefs[face.v[0]]; - face.v[1] = xrefs[face.v[1]]; - face.v[2] = xrefs[face.v[2]]; - - if (face.isQuadFace()) - { - face.v[3] = xrefs[face.v[3]]; - } - } -} - - - -// OLD code - -#if 0 - -namespace { - -struct VertexInfo { - uint id; ///< Original vertex id. - uint normal_face_group; - uint tangent_face_group; - uint material; - uint chart; -}; - - -/// VertexInfo hash functor. -struct VertexHash : public IHashFunctor<VertexInfo> { - VertexHash(PiMeshPtr m) : mesh(m) { - uint c = mesh->FindChannel(VS_POS); - piCheck(c != PI_NULL_INDEX); - channel = mesh->GetChannel(c); - piCheck(channel != NULL); - } - - uint32 operator () (const VertexInfo & v) const { - return channel->data[v.id].GetHash(); - } - -private: - PiMeshPtr mesh; - PiMesh::Channel * channel; -}; - - -/// VertexInfo comparator. -struct VertexEqual : public IBinaryPredicate<VertexInfo> { - VertexEqual(PiMeshPtr m) : mesh(m) {} - - bool operator () (const VertexInfo & a, const VertexInfo & b) const { - - bool equal = a.normal_face_group == b.normal_face_group && - a.tangent_face_group == b.tangent_face_group && - a.material == b.material && - a.chart == b.chart; - - // Split vertex shared by different face types. - if( !equal ) { - return false; - } - - // They were the same vertex. - if( a.id == b.id ) { - return true; - } - - // Vertex equal if all the channels are equal. - return mesh->IsVertexEqual(a.id, b.id); - } - -private: - PiMeshPtr mesh; -}; - -} // namespace - - -/// Weld the vertices. -void PiMeshVertexWeld::WeldVertices(const PiMeshSmoothGroup * mesh_smooth_group, - const PiMeshMaterial * mesh_material, const PiMeshAtlas * mesh_atlas ) -{ - piDebug( "--- Welding vertices:\n" ); - - piDebug( "--- Expand mesh vertices.\n" ); - PiArray<VertexInfo> vertex_array; - - const uint face_num = mesh->GetFaceNum(); - const uint vertex_max = face_num * 3; - vertex_array.Resize( vertex_max ); - - for(uint i = 0; i < vertex_max; i++) { - - uint f = i/3; - - const PiMesh::Face & face = mesh->GetFace(f); - vertex_array[i].id = face.v[i%3]; - - // Reset face attributes. - vertex_array[i].normal_face_group = PI_NULL_INDEX; - vertex_array[i].tangent_face_group = PI_NULL_INDEX; - vertex_array[i].material = PI_NULL_INDEX; - vertex_array[i].chart = PI_NULL_INDEX; - - // Set available attributes. - if( mesh_smooth_group != NULL ) { - if( mesh_smooth_group->HasNormalFaceGroups() ) { - vertex_array[i].normal_face_group = mesh_smooth_group->GetNormalFaceGroup( f ); - } - if( mesh_smooth_group->HasTangentFaceGroups() ) { - vertex_array[i].tangent_face_group = mesh_smooth_group->GetTangentFaceGroup( f ); - } - } - if( mesh_material != NULL ) { - vertex_array[i].material = mesh_material->GetFaceMaterial( f ); - } - if( mesh_atlas != NULL && mesh_atlas->HasCharts() ) { - vertex_array[i].chart = mesh_atlas->GetFaceChart( f ); - } - } - piDebug( "--- %d vertices.\n", vertex_max ); - - piDebug( "--- Collapse vertices.\n" ); - - uint * xrefs = new uint[vertex_max]; - VertexHash hash(mesh); - VertexEqual equal(mesh); - const uint vertex_num = Weld( vertex_array, xrefs, hash, equal ); - piCheck(vertex_num <= vertex_max); - piDebug( "--- %d vertices.\n", vertex_num ); - - // Remap face indices. - piDebug( "--- Remapping face indices.\n" ); - mesh->RemapFaceIndices(vertex_max, xrefs); - - - // Overwrite xrefs to map new vertices to old vertices. - for(uint v = 0; v < vertex_num; v++) { - xrefs[v] = vertex_array[v].id; - } - - // Update vertex order. - mesh->ReorderVertices(vertex_num, xrefs); - - delete [] xrefs; -} - -#endif // 0 diff --git a/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.h b/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.h deleted file mode 100644 index 1dc2e4ba4d..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/VertexWeld.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright NVIDIA Corporation 2006 -- Ignacio Castano <icastano@nvidia.com> - -#ifndef NV_MESH_VERTEXWELD_H -#define NV_MESH_VERTEXWELD_H - -#include <nvmesh/nvmesh.h> - -namespace nv -{ - class TriMesh; - class QuadMesh; - - NVMESH_API void WeldVertices(TriMesh * mesh); - NVMESH_API void WeldVertices(QuadTriMesh * mesh); - -} // nv namespace - - -#endif // NV_MESH_VERTEXWELD_H diff --git a/thirdparty/thekla_atlas/nvmesh/weld/Weld.h b/thirdparty/thekla_atlas/nvmesh/weld/Weld.h deleted file mode 100644 index e615539461..0000000000 --- a/thirdparty/thekla_atlas/nvmesh/weld/Weld.h +++ /dev/null @@ -1,171 +0,0 @@ -// This code is in the public domain -- castanyo@yahoo.es - -#ifndef NV_MESH_WELD_H -#define NV_MESH_WELD_H - -#include "nvcore/Array.h" -#include "nvcore/Hash.h" -#include "nvcore/Utils.h" // nextPowerOfTwo - -#include <string.h> // for memset, memcmp, memcpy - -// Weld function to remove array duplicates in linear time using hashing. - -namespace nv -{ - -/// Generic welding routine. This function welds the elements of the array p -/// and returns the cross references in the xrefs array. To compare the elements -/// it uses the given hash and equal functors. -/// -/// This code is based on the ideas of Ville Miettinen and Pierre Terdiman. -template <class T, class H=Hash<T>, class E=Equal<T> > -struct Weld -{ - // xrefs maps old elements to new elements - uint operator()(Array<T> & p, Array<uint> & xrefs) - { - const uint N = p.size(); // # of input vertices. - uint outputCount = 0; // # of output vertices - uint hashSize = nextPowerOfTwo(N); // size of the hash table - uint * hashTable = new uint[hashSize + N]; // hash table + linked list - uint * next = hashTable + hashSize; // use bottom part as linked list - - xrefs.resize(N); - memset( hashTable, NIL, hashSize*sizeof(uint) ); // init hash table (NIL = 0xFFFFFFFF so memset works) - - H hash; - E equal; - for (uint i = 0; i < N; i++) - { - const T & e = p[i]; - uint32 hashValue = hash(e) & (hashSize-1); - uint offset = hashTable[hashValue]; - - // traverse linked list - while( offset != NIL && !equal(p[offset], e) ) - { - offset = next[offset]; - } - - xrefs[i] = offset; - - // no match found - copy vertex & add to hash - if( offset == NIL ) - { - // save xref - xrefs[i] = outputCount; - - // copy element - p[outputCount] = e; - - // link to hash table - next[outputCount] = hashTable[hashValue]; - - // update hash heads and increase output counter - hashTable[hashValue] = outputCount++; - } - } - - // cleanup - delete [] hashTable; - - p.resize(outputCount); - - // number of output vertices - return outputCount; - } -}; - - -/// Reorder the given array accoding to the indices given in xrefs. -template <class T> -void reorderArray(Array<T> & array, const Array<uint> & xrefs) -{ - const uint count = xrefs.count(); - Array<T> new_array; - new_array.resize(count); - - for(uint i = 0; i < count; i++) { - new_array[i] = array[xrefs[i]]; - } - - swap(array, new_array); -} - -/// Reverse the given array so that new indices point to old indices. -inline void reverseXRefs(Array<uint> & xrefs, uint count) -{ - Array<uint> new_xrefs; - new_xrefs.resize(count); - - for(uint i = 0; i < xrefs.count(); i++) { - new_xrefs[xrefs[i]] = i; - } - - swap(xrefs, new_xrefs); -} - - - -// -struct WeldN -{ - uint vertexSize; - - WeldN(uint n) : vertexSize(n) {} - - // xrefs maps old elements to new elements - uint operator()(uint8 * ptr, uint N, Array<uint> & xrefs) - { - uint outputCount = 0; // # of output vertices - uint hashSize = nextPowerOfTwo(N); // size of the hash table - uint * hashTable = new uint[hashSize + N]; // hash table + linked list - uint * next = hashTable + hashSize; // use bottom part as linked list - - xrefs.resize(N); - memset( hashTable, NIL, hashSize*sizeof(uint) ); // init hash table (NIL = 0xFFFFFFFF so memset works) - - for (uint i = 0; i < N; i++) - { - const uint8 * vertex = ptr + i * vertexSize; - uint32 hashValue = sdbmHash(vertex, vertexSize) & (hashSize-1); - uint offset = hashTable[hashValue]; - - // traverse linked list - while (offset != NIL && memcmp(ptr + offset * vertexSize, vertex, vertexSize) != 0) - { - offset = next[offset]; - } - - xrefs[i] = offset; - - // no match found - copy vertex & add to hash - if (offset == NIL) - { - // save xref - xrefs[i] = outputCount; - - // copy element - memcpy(ptr + outputCount * vertexSize, vertex, vertexSize); - - // link to hash table - next[outputCount] = hashTable[hashValue]; - - // update hash heads and increase output counter - hashTable[hashValue] = outputCount++; - } - } - - // cleanup - delete [] hashTable; - - // number of output vertices - return outputCount; - } -}; - - -} // nv namespace - -#endif // NV_MESH_WELD_H |