123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <Mathematics/Logger.h>
- #include <Mathematics/Matrix.h>
- #include <Mathematics/IndexAttribute.h>
- #include <Mathematics/VertexAttribute.h>
- #include <Mathematics/Vector2.h>
- #include <Mathematics/Vector3.h>
- // The Mesh class is designed to support triangulations of surfaces of a small
- // number of topologies. See the documents
- // https://www.geometrictools.com/MeshDifferentialGeometry.pdf
- // https://www.geometrictools.com/MeshFactory.pdf
- // for details.
- //
- // You must set the vertex attribute sources before calling Update().
- //
- // The semantic "position" is required and its source must be an array
- // of Real with at least 3 channels so that positions are computed as
- // Vector3<Real>.
- //
- // The positions are assumed to be parameterized by texture coordinates
- // (u,v); the position is thought of as a function P(u,v). If texture
- // coordinates are provided, the semantic must be "tcoord". If texture
- // coordinates are not provided, default texture coordinates are computed
- // internally as described in the mesh factory document.
- //
- // The frame for the tangent space is optional. All vectors in the frame
- // must have sources that are arrays of Real with at least 3 channels per
- // attribute. If normal vectors are provided, the semantic must be
- // "normal".
- //
- // Two options are supported for tangent vectors. The first option is
- // that the tangents are surface derivatives dP/du and dP/dv, which are
- // not necessarily unit length or orthogonal. The semantics must be
- // "dpdu" and "dpdv". The second option is that the tangents are unit
- // length and orthogonal, with the infrequent possibility that a vertex
- // is degenerate in that dP/du and dP/dv are linearly dependent. The
- // semantics must be "tangent" and "bitangent".
- //
- // For each provided vertex attribute, a derived class can initialize
- // that attribute by overriding one of the Initialize*() functions whose
- // stubs are defined in this class.
- namespace WwiseGTE
- {
- enum class MeshTopology
- {
- ARBITRARY,
- RECTANGLE,
- CYLINDER,
- TORUS,
- DISK,
- SPHERE
- };
- class MeshDescription
- {
- public:
- // Constructor for MeshTopology::ARBITRARY. The members topology,
- // numVertices, and numTriangles are set in the obvious manner. The
- // members numRows and numCols are set to zero. The remaining members
- // must be set explicitly by the client.
- MeshDescription(uint32_t inNumVertices, uint32_t inNumTriangles)
- :
- topology(MeshTopology::ARBITRARY),
- numVertices(inNumVertices),
- numTriangles(inNumTriangles),
- wantDynamicTangentSpaceUpdate(false),
- wantCCW(true),
- hasTangentSpaceVectors(false),
- allowUpdateFrame(false),
- numRows(0),
- numCols(0),
- rMax(0),
- cMax(0),
- rIncrement(0),
- constructed(false)
- {
- LogAssert(numVertices >= 3 && numTriangles >= 1, "Invalid input.");
- }
- // Constructor for topologies other than MeshTopology::ARBITRARY.
- // Compute the number of vertices and triangles for the mesh based on
- // the requested number of rows and columns. If the number of rows or
- // columns is invalid for the specified topology, they are modified to
- // be valid, in which case inNumRows/numRows and inNumCols/numCols can
- // differ. If the input topology is MeshTopology::ARBITRARY, then
- // inNumRows and inNumCols are assigned to numVertices and
- // numTriangles, respectively, and numRows and numCols are set to
- // zero. The remaining members must be set explicitly by the client.
- MeshDescription(MeshTopology inTopology, uint32_t inNumRows, uint32_t inNumCols)
- :
- topology(inTopology),
- wantDynamicTangentSpaceUpdate(false),
- wantCCW(true),
- hasTangentSpaceVectors(false),
- allowUpdateFrame(false),
- constructed(false)
- {
- switch (topology)
- {
- case MeshTopology::ARBITRARY:
- numVertices = inNumRows;
- numTriangles = inNumCols;
- numRows = 0;
- numCols = 0;
- rMax = 0;
- cMax = 0;
- rIncrement = 0;
- break;
- case MeshTopology::RECTANGLE:
- numRows = std::max(inNumRows, 2u);
- numCols = std::max(inNumCols, 2u);
- rMax = numRows - 1;
- cMax = numCols - 1;
- rIncrement = numCols;
- numVertices = (rMax + 1) * (cMax + 1);
- numTriangles = 2 * rMax * cMax;
- break;
- case MeshTopology::CYLINDER:
- numRows = std::max(inNumRows, 2u);
- numCols = std::max(inNumCols, 3u);
- rMax = numRows - 1;
- cMax = numCols;
- rIncrement = numCols + 1;
- numVertices = (rMax + 1) * (cMax + 1);
- numTriangles = 2 * rMax * cMax;
- break;
- case MeshTopology::TORUS:
- numRows = std::max(inNumRows, 2u);
- numCols = std::max(inNumCols, 3u);
- rMax = numRows;
- cMax = numCols;
- rIncrement = numCols + 1;
- numVertices = (rMax + 1) * (cMax + 1);
- numTriangles = 2 * rMax * cMax;
- break;
- case MeshTopology::DISK:
- numRows = std::max(inNumRows, 1u);
- numCols = std::max(inNumCols, 3u);
- rMax = numRows - 1;
- cMax = numCols;
- rIncrement = numCols + 1;
- numVertices = (rMax + 1) * (cMax + 1) + 1;
- numTriangles = 2 * rMax * cMax + numCols;
- break;
- case MeshTopology::SPHERE:
- numRows = std::max(inNumRows, 1u);
- numCols = std::max(inNumCols, 3u);
- rMax = numRows - 1;
- cMax = numCols;
- rIncrement = numCols + 1;
- numVertices = (rMax + 1) * (cMax + 1) + 2;
- numTriangles = 2 * rMax * cMax + 2 * numCols;
- break;
- }
- }
- MeshTopology topology;
- uint32_t numVertices;
- uint32_t numTriangles;
- std::vector<VertexAttribute> vertexAttributes;
- IndexAttribute indexAttribute;
- bool wantDynamicTangentSpaceUpdate; // default: false
- bool wantCCW; // default: true
- // For internal use only.
- bool hasTangentSpaceVectors;
- bool allowUpdateFrame;
- uint32_t numRows, numCols;
- uint32_t rMax, cMax, rIncrement;
- // After an attempt to construct a Mesh or Mesh-derived object,
- // examine this value to determine whether the construction was
- // successful.
- bool constructed;
- };
- template <typename Real>
- class Mesh
- {
- public:
- // Construction and destruction. This constructor is for ARBITRARY
- // topology. The vertices and indices must already be assigned by the
- // client. Derived classes use the protected constructor, but
- // assignment of vertices and indices occurs in the derived-class
- // constructors.
- Mesh(MeshDescription const& description, std::vector<MeshTopology> const& validTopologies)
- :
- mDescription(description),
- mPositions(nullptr),
- mNormals(nullptr),
- mTangents(nullptr),
- mBitangents(nullptr),
- mDPDUs(nullptr),
- mDPDVs(nullptr),
- mTCoords(nullptr),
- mPositionStride(0),
- mNormalStride(0),
- mTangentStride(0),
- mBitangentStride(0),
- mDPDUStride(0),
- mDPDVStride(0),
- mTCoordStride(0)
- {
- mDescription.constructed = false;
- for (auto const& topology : validTopologies)
- {
- if (mDescription.topology == topology)
- {
- mDescription.constructed = true;
- break;
- }
- }
- LogAssert(mDescription.indexAttribute.source != nullptr,
- "The mesh needs triangles/indices in Mesh constructor.");
- // Set sources for the requested vertex attributes.
- mDescription.hasTangentSpaceVectors = false;
- mDescription.allowUpdateFrame = mDescription.wantDynamicTangentSpaceUpdate;
- for (auto const& attribute : mDescription.vertexAttributes)
- {
- if (attribute.source != nullptr && attribute.stride > 0)
- {
- if (attribute.semantic == "position")
- {
- mPositions = reinterpret_cast<Vector3<Real>*>(attribute.source);
- mPositionStride = attribute.stride;
- continue;
- }
- if (attribute.semantic == "normal")
- {
- mNormals = reinterpret_cast<Vector3<Real>*>(attribute.source);
- mNormalStride = attribute.stride;
- continue;
- }
- if (attribute.semantic == "tangent")
- {
- mTangents = reinterpret_cast<Vector3<Real>*>(attribute.source);
- mTangentStride = attribute.stride;
- mDescription.hasTangentSpaceVectors = true;
- continue;
- }
- if (attribute.semantic == "bitangent")
- {
- mBitangents = reinterpret_cast<Vector3<Real>*>(attribute.source);
- mBitangentStride = attribute.stride;
- mDescription.hasTangentSpaceVectors = true;
- continue;
- }
- if (attribute.semantic == "dpdu")
- {
- mDPDUs = reinterpret_cast<Vector3<Real>*>(attribute.source);
- mDPDUStride = attribute.stride;
- mDescription.hasTangentSpaceVectors = true;
- continue;
- }
- if (attribute.semantic == "dpdv")
- {
- mDPDVs = reinterpret_cast<Vector3<Real>*>(attribute.source);
- mDPDVStride = attribute.stride;
- mDescription.hasTangentSpaceVectors = true;
- continue;
- }
- if (attribute.semantic == "tcoord")
- {
- mTCoords = reinterpret_cast<Vector2<Real>*>(attribute.source);
- mTCoordStride = attribute.stride;
- continue;
- }
- }
- }
- LogAssert(mPositions != nullptr, "The mesh needs positions in Mesh constructor.");
- // The initial value of allowUpdateFrame is the client request
- // about wanting dynamic tangent-space updates. If the vertex
- // attributes do not include tangent-space vectors, then dynamic
- // updates are not necessary. If tangent-space vectors are
- // present, the update algorithm requires texture coordinates
- // (mTCoords must be nonnull) or must compute local coordinates
- // (mNormals must be nonnull).
- if (mDescription.allowUpdateFrame)
- {
- if (!mDescription.hasTangentSpaceVectors)
- {
- mDescription.allowUpdateFrame = false;
- }
- if (!mTCoords && !mNormals)
- {
- mDescription.allowUpdateFrame = false;
- }
- }
- if (mDescription.allowUpdateFrame)
- {
- mUTU.resize(mDescription.numVertices);
- mDTU.resize(mDescription.numVertices);
- }
- }
- virtual ~Mesh()
- {
- }
- // No copying or assignment is allowed.
- Mesh(Mesh const&) = delete;
- Mesh& operator=(Mesh const&) = delete;
- // Member accessors.
- inline MeshDescription const& GetDescription() const
- {
- return mDescription;
- }
- // If the underlying geometric data varies dynamically, call this
- // function to update whatever vertex attributes are specified by
- // the vertex pool.
- void Update()
- {
- LogAssert(mDescription.constructed, "The Mesh object failed the construction.");
- UpdatePositions();
- if (mDescription.allowUpdateFrame)
- {
- UpdateFrame();
- }
- else if (mNormals)
- {
- UpdateNormals();
- }
- // else: The mesh has no frame data, so there is nothing to do.
- }
- protected:
- // Access the vertex attributes.
- inline Vector3<Real>& Position(uint32_t i)
- {
- char* positions = reinterpret_cast<char*>(mPositions);
- return *reinterpret_cast<Vector3<Real>*>(positions + i * mPositionStride);
- }
- inline Vector3<Real>& Normal(uint32_t i)
- {
- char* normals = reinterpret_cast<char*>(mNormals);
- return *reinterpret_cast<Vector3<Real>*>(normals + i * mNormalStride);
- }
- inline Vector3<Real>& Tangent(uint32_t i)
- {
- char* tangents = reinterpret_cast<char*>(mTangents);
- return *reinterpret_cast<Vector3<Real>*>(tangents + i * mTangentStride);
- }
- inline Vector3<Real>& Bitangent(uint32_t i)
- {
- char* bitangents = reinterpret_cast<char*>(mBitangents);
- return *reinterpret_cast<Vector3<Real>*>(bitangents + i * mBitangentStride);
- }
- inline Vector3<Real>& DPDU(uint32_t i)
- {
- char* dpdus = reinterpret_cast<char*>(mDPDUs);
- return *reinterpret_cast<Vector3<Real>*>(dpdus + i * mDPDUStride);
- }
- inline Vector3<Real>& DPDV(uint32_t i)
- {
- char* dpdvs = reinterpret_cast<char*>(mDPDVs);
- return *reinterpret_cast<Vector3<Real>*>(dpdvs + i * mDPDVStride);
- }
- inline Vector2<Real>& TCoord(uint32_t i)
- {
- char* tcoords = reinterpret_cast<char*>(mTCoords);
- return *reinterpret_cast<Vector2<Real>*>(tcoords + i * mTCoordStride);
- }
- // Compute the indices for non-arbitrary topologies. This function is
- // called by derived classes.
- void ComputeIndices()
- {
- uint32_t t = 0;
- for (uint32_t r = 0, i = 0; r < mDescription.rMax; ++r)
- {
- uint32_t v0 = i, v1 = v0 + 1;
- i += mDescription.rIncrement;
- uint32_t v2 = i, v3 = v2 + 1;
- for (uint32_t c = 0; c < mDescription.cMax; ++c, ++v0, ++v1, ++v2, ++v3)
- {
- if (mDescription.wantCCW)
- {
- mDescription.indexAttribute.SetTriangle(t++, v0, v1, v2);
- mDescription.indexAttribute.SetTriangle(t++, v1, v3, v2);
- }
- else
- {
- mDescription.indexAttribute.SetTriangle(t++, v0, v2, v1);
- mDescription.indexAttribute.SetTriangle(t++, v1, v2, v3);
- }
- }
- }
- if (mDescription.topology == MeshTopology::DISK)
- {
- uint32_t v0 = 0, v1 = 1, v2 = mDescription.numVertices - 1;
- for (unsigned int c = 0; c < mDescription.numCols; ++c, ++v0, ++v1)
- {
- if (mDescription.wantCCW)
- {
- mDescription.indexAttribute.SetTriangle(t++, v0, v2, v1);
- }
- else
- {
- mDescription.indexAttribute.SetTriangle(t++, v0, v1, v2);
- }
- }
- }
- else if (mDescription.topology == MeshTopology::SPHERE)
- {
- uint32_t v0 = 0, v1 = 1, v2 = mDescription.numVertices - 2;
- for (uint32_t c = 0; c < mDescription.numCols; ++c, ++v0, ++v1)
- {
- if (mDescription.wantCCW)
- {
- mDescription.indexAttribute.SetTriangle(t++, v0, v2, v1);
- }
- else
- {
- mDescription.indexAttribute.SetTriangle(t++, v0, v1, v2);
- }
- }
- v0 = (mDescription.numRows - 1) * mDescription.numCols;
- v1 = v0 + 1;
- v2 = mDescription.numVertices - 1;
- for (uint32_t c = 0; c < mDescription.numCols; ++c, ++v0, ++v1)
- {
- if (mDescription.wantCCW)
- {
- mDescription.indexAttribute.SetTriangle(t++, v0, v2, v1);
- }
- else
- {
- mDescription.indexAttribute.SetTriangle(t++, v0, v1, v2);
- }
- }
- }
- }
- // The Update() function allows derived classes to use algorithms
- // different from least-squares fitting to compute the normals (when
- // no tangent-space information is requested) or to compute the frame
- // (normals and tangent space). The UpdatePositions() is a stub; the
- // base-class has no knowledge about how positions should be modified.
- // A derived class, however, might choose to use dynamic updating
- // and override UpdatePositions(). The base-class UpdateNormals()
- // computes vertex normals as averages of area-weighted triangle
- // normals (nonparametric approach). The base-class UpdateFrame()
- // uses a least-squares algorithm for estimating the tangent space
- // (parametric approach).
- virtual void UpdatePositions()
- {
- }
- virtual void UpdateNormals()
- {
- // Compute normal vector as normalized weighted averages of triangle
- // normal vectors.
- // Set the normals to zero to allow accumulation of triangle normals.
- Vector3<Real> zero{ (Real)0, (Real)0, (Real)0 };
- for (uint32_t i = 0; i < mDescription.numVertices; ++i)
- {
- Normal(i) = zero;
- }
- // Accumulate the triangle normals.
- for (uint32_t t = 0; t < mDescription.numTriangles; ++t)
- {
- // Get the positions for the triangle.
- uint32_t v0, v1, v2;
- mDescription.indexAttribute.GetTriangle(t, v0, v1, v2);
- Vector3<Real> P0 = Position(v0);
- Vector3<Real> P1 = Position(v1);
- Vector3<Real> P2 = Position(v2);
- // Get the edge vectors.
- Vector3<Real> E1 = P1 - P0;
- Vector3<Real> E2 = P2 - P0;
- // Compute a triangle normal show length is twice the area of the
- // triangle.
- Vector3<Real> triangleNormal = Cross(E1, E2);
- // Accumulate the triangle normals.
- Normal(v0) += triangleNormal;
- Normal(v1) += triangleNormal;
- Normal(v2) += triangleNormal;
- }
- // Normalize the normals.
- for (uint32_t i = 0; i < mDescription.numVertices; ++i)
- {
- Normalize(Normal(i), true);
- }
- }
- virtual void UpdateFrame()
- {
- if (!mTCoords)
- {
- // We need to compute vertex normals first in order to compute
- // local texture coordinates. The vertex normals are recomputed
- // later based on estimated tangent vectors.
- UpdateNormals();
- }
- // Use the least-squares algorithm to estimate the tangent-space vectors
- // and, if requested, normal vectors.
- Matrix<2, 2, Real> zero2x2; // initialized to zero
- Matrix<3, 2, Real> zero3x2; // initialized to zero
- std::fill(mUTU.begin(), mUTU.end(), zero2x2);
- std::fill(mDTU.begin(), mDTU.end(), zero3x2);
- for (uint32_t t = 0; t < mDescription.numTriangles; ++t)
- {
- // Get the positions and differences for the triangle.
- uint32_t v0, v1, v2;
- mDescription.indexAttribute.GetTriangle(t, v0, v1, v2);
- Vector3<Real> P0 = Position(v0);
- Vector3<Real> P1 = Position(v1);
- Vector3<Real> P2 = Position(v2);
- Vector3<Real> D10 = P1 - P0;
- Vector3<Real> D20 = P2 - P0;
- Vector3<Real> D21 = P2 - P1;
- if (mTCoords)
- {
- // Get the texture coordinates and differences for the triangle.
- Vector2<Real> C0 = TCoord(v0);
- Vector2<Real> C1 = TCoord(v1);
- Vector2<Real> C2 = TCoord(v2);
- Vector2<Real> U10 = C1 - C0;
- Vector2<Real> U20 = C2 - C0;
- Vector2<Real> U21 = C2 - C1;
- // Compute the outer products.
- Matrix<2, 2, Real> outerU10 = OuterProduct(U10, U10);
- Matrix<2, 2, Real> outerU20 = OuterProduct(U20, U20);
- Matrix<2, 2, Real> outerU21 = OuterProduct(U21, U21);
- Matrix<3, 2, Real> outerD10 = OuterProduct(D10, U10);
- Matrix<3, 2, Real> outerD20 = OuterProduct(D20, U20);
- Matrix<3, 2, Real> outerD21 = OuterProduct(D21, U21);
- // Keep a running sum of U^T*U and D^T*U.
- mUTU[v0] += outerU10 + outerU20;
- mUTU[v1] += outerU10 + outerU21;
- mUTU[v2] += outerU20 + outerU21;
- mDTU[v0] += outerD10 + outerD20;
- mDTU[v1] += outerD10 + outerD21;
- mDTU[v2] += outerD20 + outerD21;
- }
- else
- {
- // Compute local coordinates and differences for the triangle.
- Vector3<Real> basis[3];
- basis[0] = Normal(v0);
- ComputeOrthogonalComplement(1, basis, true);
- Vector2<Real> U10{ Dot(basis[1], D10), Dot(basis[2], D10) };
- Vector2<Real> U20{ Dot(basis[1], D20), Dot(basis[2], D20) };
- mUTU[v0] += OuterProduct(U10, U10) + OuterProduct(U20, U20);
- mDTU[v0] += OuterProduct(D10, U10) + OuterProduct(D20, U20);
- basis[0] = Normal(v1);
- ComputeOrthogonalComplement(1, basis, true);
- Vector2<Real> U01{ Dot(basis[1], D10), Dot(basis[2], D10) };
- Vector2<Real> U21{ Dot(basis[1], D21), Dot(basis[2], D21) };
- mUTU[v1] += OuterProduct(U01, U01) + OuterProduct(U21, U21);
- mDTU[v1] += OuterProduct(D10, U01) + OuterProduct(D21, U21);
- basis[0] = Normal(v2);
- ComputeOrthogonalComplement(1, basis, true);
- Vector2<Real> U02{ Dot(basis[1], D20), Dot(basis[2], D20) };
- Vector2<Real> U12{ Dot(basis[1], D21), Dot(basis[2], D21) };
- mUTU[v2] += OuterProduct(U02, U02) + OuterProduct(U12, U12);
- mDTU[v2] += OuterProduct(D20, U02) + OuterProduct(D21, U12);
- }
- }
- for (uint32_t i = 0; i < mDescription.numVertices; ++i)
- {
- Matrix<3, 2, Real> jacobian = mDTU[i] * Inverse(mUTU[i]);
- Vector3<Real> basis[3];
- basis[0] = { jacobian(0, 0), jacobian(1, 0), jacobian(2, 0) };
- basis[1] = { jacobian(0, 1), jacobian(1, 1), jacobian(2, 1) };
- if (mDPDUs)
- {
- DPDU(i) = basis[0];
- }
- if (mDPDVs)
- {
- DPDV(i) = basis[1];
- }
- ComputeOrthogonalComplement(2, basis, true);
- if (mNormals)
- {
- Normal(i) = basis[2];
- }
- if (mTangents)
- {
- Tangent(i) = basis[0];
- }
- if (mBitangents)
- {
- Bitangent(i) = basis[1];
- }
- }
- }
- // Constructor inputs.
- // The client requests this via the constructor; however, if it is
- // requested and the vertex attributes do not contain entries for
- // "tangent", "bitangent", "dpdu", or "dpdv", then this member is
- // set to false.
- MeshDescription mDescription;
- // Copied from mVertexAttributes when available.
- Vector3<Real>* mPositions;
- Vector3<Real>* mNormals;
- Vector3<Real>* mTangents;
- Vector3<Real>* mBitangents;
- Vector3<Real>* mDPDUs;
- Vector3<Real>* mDPDVs;
- Vector2<Real>* mTCoords;
- size_t mPositionStride;
- size_t mNormalStride;
- size_t mTangentStride;
- size_t mBitangentStride;
- size_t mDPDUStride;
- size_t mDPDVStride;
- size_t mTCoordStride;
- // When dynamic tangent-space updates are requested, the update algorithm
- // requires texture coordinates (user-specified or non-local). It is
- // possible to create a vertex-adjacent set (with indices into the
- // vertex array) for each mesh vertex; however, instead we rely on a
- // triangle iteration and incrementally store the information needed for
- // the estimation of the tangent space. Each vertex has associated
- // matrices D and U, but we need to store only U^T*U and D^T*U. See the
- // PDF for details.
- std::vector<Matrix<2, 2, Real>> mUTU;
- std::vector<Matrix<3, 2, Real>> mDTU;
- };
- }
|