// // Copyright 2014 DreamWorks Animation LLC. // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #ifndef FAR_TOPOLOGY_REFINER_H #define FAR_TOPOLOGY_REFINER_H #include "../version.h" #include "../sdc/types.h" #include "../sdc/options.h" #include "../sdc/bilinearScheme.h" #include "../sdc/catmarkScheme.h" #include "../sdc/loopScheme.h" #include "../vtr/level.h" #include "../vtr/fvarLevel.h" #include "../vtr/refinement.h" #include "../vtr/fvarRefinement.h" #include "../vtr/maskInterfaces.h" #include "../far/types.h" #include "../far/error.h" #include #include #include namespace OpenSubdiv { namespace OPENSUBDIV_VERSION { namespace Vtr { class SparseSelector; } namespace Far { template class TopologyRefinerFactory; /// /// \brief Stores topology data for a specified set of refinement options. /// class TopologyRefiner { public: /// \brief Constructor TopologyRefiner(Sdc::SchemeType type, Sdc::Options options = Sdc::Options()); /// \brief Destructor ~TopologyRefiner(); /// \brief Returns the subdivision scheme Sdc::SchemeType GetSchemeType() const { return _subdivType; } /// \brief Returns the subdivision options Sdc::Options GetSchemeOptions() const { return _subdivOptions; } /// \brief Returns true if uniform subdivision has been applied bool IsUniform() const { return _isUniform; } /// \brief Returns the number of refinement levels int GetNumLevels() const { return (int)_levels.size(); } /// \brief Returns the highest level of refinement int GetMaxLevel() const { return _maxLevel; } /// \brief Returns the maximum vertex valence in all levels int GetMaxValence() const { return _maxValence; } /// \ brief Returns true if faces have been tagged as holes bool HasHoles() const { return _hasHoles; } /// \brief Returns the total number of vertices in all levels int GetNumVerticesTotal() const { return _totalVertices; } /// \brief Returns the total number of edges in all levels int GetNumEdgesTotal() const { return _totalEdges; } /// \brief Returns the total number of edges in all levels int GetNumFacesTotal() const { return _totalFaces; } /// \brief Returns the total number of face vertices in all levels int GetNumFaceVerticesTotal() const { return _totalFaceVertices; } //@{ /// @name High-level refinement and related methods /// // // Uniform refinement // /// \brief Uniform refinement options struct UniformOptions { UniformOptions(int level) : refinementLevel(level), applyBaseFacePerFace(false), orderVerticesFromFacesFirst(false), fullTopologyInLastLevel(false) { } unsigned int refinementLevel:4, ///< Number of refinement iterations applyBaseFacePerFace:1, ///< For each refined face, record the index ///< of the base face from which it originates orderVerticesFromFacesFirst:1, ///< Order child vertices from faces first ///< instead of child vertices of vertices fullTopologyInLastLevel:1; ///< Skip topological relationships in the last ///< level of refinement that are not needed for ///< interpolation (keep false if using limit). }; /// \brief Refine the topology uniformly /// /// @param options Options controlling uniform refinement /// void RefineUniform(UniformOptions options); /// \brief Returns the options specified on refinement UniformOptions GetUniformOptions() const { return _uniformOptions; } // // Adaptive refinement // /// \brief Adaptive refinement options struct AdaptiveOptions { AdaptiveOptions(int level) : isolationLevel(level), useSingleCreasePatch(false), applyBaseFacePerFace(false), orderVerticesFromFacesFirst(false) { } unsigned int isolationLevel:4, ///< Number of iterations applied to isolate ///< extraordinary vertices and creases useSingleCreasePatch:1, ///< Use 'single-crease' patch and stop ///< isolation where applicable applyBaseFacePerFace:1, ///< For each refined face, record the index ///< of the base face from which it originates orderVerticesFromFacesFirst:1; ///< Order child vertices from faces first ///< instead of child vertices of vertices }; /// \brief Feature Adaptive topology refinement /// /// @param options Options controlling adaptive refinement /// void RefineAdaptive(AdaptiveOptions options); /// \brief Returns the options specified on refinement AdaptiveOptions GetAdaptiveOptions() const { return _adaptiveOptions; } /// \brief Unrefine the topology (keep control cage) void Unrefine(); //@{ /// @name Primvar data interpolation /// /// \anchor templating /// /// \note Interpolation methods template both the source and destination /// data buffer classes. Client-code is expected to provide interfaces /// that implement the functions specific to its primitive variable /// data layout. Template APIs must implement the following: ///

\code{.cpp} /// /// class MySource { /// MySource & operator[](int index); /// }; /// /// class MyDestination { /// void Clear(); /// void AddWithWeight(MySource const & value, float weight); /// void AddWithWeight(MyDestination const & value, float weight); /// /// // optional /// void AddVaryingWithWeight(MySource const & value, float weight); /// }; /// /// \endcode ///
/// It is possible to implement a single interface only and use it as /// both source and destination. ///

/// Primitive variable buffers are expected to be arrays of instances, /// passed either as direct pointers or with a container /// (ex. std::vector). /// Some interpolation methods however allow passing the buffers by /// reference: this allows to work transparently with arrays and /// containers (or other scheme that overload the '[]' operator) ///

/// See the /// Far tutorials for code examples. /// /// \brief Apply vertex and varying interpolation weights to a primvar /// buffer /// /// The destination buffer must allocate an array of data for all the /// refined vertices (at least GetNumVerticesTotal()-GetNumVertices(0)) /// /// @param src Source primvar buffer (\ref templating control vertex data) /// /// @param dst Destination primvar buffer (\ref templating refined vertex data) /// template void Interpolate(T const * src, U * dst) const; /// \brief Apply vertex and varying interpolation weights to a primvar /// buffer for a single level /// level of refinement. /// /// The destination buffer must allocate an array of data for all the /// refined vertices (at least GetNumVertices(level)) /// /// @param level The refinement level /// /// @param src Source primvar buffer (\ref templating control vertex data) /// /// @param dst Destination primvar buffer (\ref templating refined vertex data) /// template void Interpolate(int level, T const & src, U & dst) const; /// \brief Apply only varying interpolation weights to a primvar buffer /// /// This method can be a useful alternative if the varying primvar data /// does not need to be re-computed over time. /// /// The destination buffer must allocate an array of data for all the /// refined vertices (at least GetNumVerticesTotal()-GetNumVertices(0)) /// /// @param src Source primvar buffer (\ref templating control vertex data) /// /// @param dst Destination primvar buffer (\ref templating refined vertex data) /// template void InterpolateVarying(T const * src, U * dst) const; /// \brief Apply only varying interpolation weights to a primvar buffer /// for a single level level of refinement. /// /// This method can be a useful alternative if the varying primvar data /// does not need to be re-computed over time. /// /// The destination buffer must allocate an array of data for all the /// refined vertices (at least GetNumVertices(level)) /// /// @param level The refinement level /// /// @param src Source primvar buffer (\ref templating control vertex data) /// /// @param dst Destination primvar buffer (\ref templating refined vertex data) /// template void InterpolateVarying(int level, T const & src, U & dst) const; /// \brief Apply face-varying interpolation weights to a primvar buffer // associated with a particular face-varying channel /// template void InterpolateFaceVarying(T const * src, U * dst, int channel = 0) const; template void InterpolateFaceVarying(int level, T const & src, U & dst, int channel = 0) const; template void LimitFaceVarying(T const & src, U * dst, int channel = 0) const; /// \brief Apply vertex interpolation limit weights to a primvar buffer /// /// The source buffer must refer to an array of previously interpolated /// vertex data for the last refinement level. The destination buffer /// must allocate an array for all vertices at the last refinement level /// (at least GetNumVertices(GetMaxLevel())) /// /// @param src Source primvar buffer (refined vertex data) for last level /// /// @param dst Destination primvar buffer (vertex data at the limit) /// template void Limit(T const & src, U * dst) const; //@} //@{ /// @name Inspection of components per level /// /// \brief Returns the number of vertices at a given level of refinement int GetNumVertices(int level) const { return _levels[level]->getNumVertices(); } /// \brief Returns the number of edges at a given level of refinement int GetNumEdges(int level) const { return _levels[level]->getNumEdges(); } /// \brief Returns the number of face vertex indices at a given level of refinement int GetNumFaces(int level) const { return _levels[level]->getNumFaces(); } /// \brief Returns the number of faces marked as holes at the given level int GetNumHoles(int level) const; /// \brief Returns the number of faces at a given level of refinement int GetNumFaceVertices(int level) const { return _levels[level]->getNumFaceVerticesTotal(); } /// \brief Returns the sharpness of a given edge (at 'level' of refinement) float GetEdgeSharpness(int level, Index edge) const { return _levels[level]->getEdgeSharpness(edge); } /// \brief Returns the sharpness of a given vertex (at 'level' of refinement) float GetVertexSharpness(int level, Index vert) const { return _levels[level]->getVertexSharpness(vert); } /// \brief Returns the subdivision rule of a given vertex (at 'level' of refinement) Sdc::Crease::Rule GetVertexRule(int level, Index vert) const { return _levels[level]->getVertexRule(vert); } //@} //@{ /// @name Topological relations -- incident/adjacent components /// /// \brief Returns the vertices of a 'face' at 'level' ConstIndexArray GetFaceVertices(int level, Index face) const { return _levels[level]->getFaceVertices(face); } /// \brief Returns the edges of a 'face' at 'level' ConstIndexArray GetFaceEdges(int level, Index face) const { return _levels[level]->getFaceEdges(face); } /// \brief Returns true if 'face' at 'level' is tagged as a hole bool IsFaceHole(int level, Index face) const { return _levels[level]->isFaceHole(face); } /// \brief Returns the vertices of an 'edge' at 'level' (2 of them) ConstIndexArray GetEdgeVertices(int level, Index edge) const { return _levels[level]->getEdgeVertices(edge); } /// \brief Returns the faces incident to 'edge' at 'level' ConstIndexArray GetEdgeFaces(int level, Index edge) const { return _levels[level]->getEdgeFaces(edge); } /// \brief Returns the faces incident to 'vertex' at 'level' ConstIndexArray GetVertexFaces(int level, Index vert) const { return _levels[level]->getVertexFaces(vert); } /// \brief Returns the edges incident to 'vertex' at 'level' ConstIndexArray GetVertexEdges(int level, Index vert) const { return _levels[level]->getVertexEdges(vert); } /// \brief Returns the local face indices of edge 'edge' at 'level' ConstLocalIndexArray GetEdgeFaceLocalIndices(int level, Index edge) const { return _levels[level]->getEdgeFaceLocalIndices(edge); } /// \brief Returns the local face indices of vertex 'vert' at 'level' ConstLocalIndexArray GetVertexFaceLocalIndices(int level, Index vert) const { return _levels[level]->getVertexFaceLocalIndices(vert); } /// \brief Returns the local edge indices of vertex 'vert' at 'level' ConstLocalIndexArray GetVertexEdgeLocalIndices(int level, Index vert) const { return _levels[level]->getVertexEdgeLocalIndices(vert); } /// \brief Returns true if the given face does not contain extraordinary vertices bool FaceIsRegular(int level, Index face) const { ConstIndexArray fVerts = _levels[level]->getFaceVertices(face); Vtr::Level::VTag compFaceVertTag = _levels[level]->getFaceCompositeVTag(fVerts); return not compFaceVertTag._xordinary; } /// \brief Returns the edge with vertices 'v0' and 'v1' (or INDEX_INVALID if /// they are not connected) Index FindEdge(int level, Index v0, Index v1) const { return _levels[level]->findEdge(v0, v1); } //@} //@{ /// @name Inspection of face-varying channels and their contents: /// /// \brief Returns the number of face-varying channels in the tables int GetNumFVarChannels() const; /// \brief Returns the face-varying interpolation rule-set for a given channel Sdc::Options::FVarLinearInterpolation GetFVarLinearInterpolation(int channel = 0) const; /// \brief Returns the total number of face-varying values in all levels int GetNumFVarValuesTotal(int channel = 0) const; /// \brief Returns the number of face-varying values at a given level of refinement int GetNumFVarValues(int level, int channel = 0) const; /// \brief Returns the face-varying values of a 'face' at 'level' ConstIndexArray GetFVarFaceValues(int level, Index face, int channel = 0) const; //@} //@{ /// @name Parent-to-child relationships, /// Relationships between components in one level /// and the next (entries may be invalid if sparse): /// /// \brief Returns the child faces of face 'f' at 'level' ConstIndexArray GetFaceChildFaces(int level, Index f) const { return _refinements[level]->getFaceChildFaces(f); } /// \brief Returns the child edges of face 'f' at 'level' ConstIndexArray GetFaceChildEdges(int level, Index f) const { return _refinements[level]->getFaceChildEdges(f); } /// \brief Returns the child edges of edge 'e' at 'level' ConstIndexArray GetEdgeChildEdges(int level, Index e) const { return _refinements[level]->getEdgeChildEdges(e); } /// \brief Returns the child vertex of face 'f' at 'level' Index GetFaceChildVertex( int level, Index f) const { return _refinements[level]->getFaceChildVertex(f); } /// \brief Returns the child vertex of edge 'e' at 'level' Index GetEdgeChildVertex( int level, Index e) const { return _refinements[level]->getEdgeChildVertex(e); } /// \brief Returns the child vertex of vertex 'v' at 'level' Index GetVertexChildVertex(int level, Index v) const { return _refinements[level]->getVertexChildVertex(v); } //@} //@{ /// @name Child-to-parent or child-to-base relationships, /// Relationships between components in one level and the /// previous or base level (to be called with level > 0): /// /// \brief Returns the parent face of face 'f' at 'level' Index GetFaceParentFace(int level, Index f) const { return _refinements[level-1]->getChildFaceParentFace(f); } /// \brief Returns the base face of face 'f' at 'level' Index GetFaceBaseFace(int level, Index f) const { return _refinements[level-1]->getChildFaceBaseFace(f); } //@} //@{ /// Ptex /// /// \brief Returns the number of ptex faces in the mesh int GetNumPtexFaces() const; /// \brief Returns the ptex face index given a coarse face 'f' or -1 int GetPtexIndex(Index f) const; /// \brief Returns ptex face adjacency information for a given coarse face /// /// @param face coarse face index /// /// @param quadrant quadrant index if 'face' is not a quad (the local ptex // sub-face index). Must be less than the number of face // vertices. /// /// @param adjFaces ptex face indices of adjacent faces /// /// @param adjEdges ptex edge indices of adjacent faces /// void GetPtexAdjacency(int face, int quadrant, int adjFaces[4], int adjEdges[4]) const; //@} //@{ /// @name Debugging aides /// /// \brief Returns true if the topology of 'level' is valid bool ValidateTopology(int level) const { return _levels[level]->validateTopology(); } /// \brief Prints topology information to console void PrintTopology(int level, bool children = true) const { _levels[level]->print((children && ((int)_refinements.size() > level)) ? _refinements[level] : 0); } //@} protected: // // For use by the TopologyRefinerFactory subclasses to construct the base level: // template friend class TopologyRefinerFactory; // Topology sizing methods required before allocation: void setNumBaseFaces( int count) { _levels[0]->resizeFaces(count); } void setNumBaseEdges( int count) { _levels[0]->resizeEdges(count); } void setNumBaseVertices(int count) { _levels[0]->resizeVertices(count); } void setNumBaseFaceVertices(Index f, int count) { _levels[0]->resizeFaceVertices(f, count); } void setNumBaseEdgeFaces( Index e, int count) { _levels[0]->resizeEdgeFaces(e, count); } void setNumBaseVertexFaces( Index v, int count) { _levels[0]->resizeVertexFaces(v, count); } void setNumBaseVertexEdges( Index v, int count) { _levels[0]->resizeVertexEdges(v, count); } // Topology assignment methods to populate base level after allocation: IndexArray setBaseFaceVertices(Index f) { return _levels[0]->getFaceVertices(f); } IndexArray setBaseFaceEdges( Index f) { return _levels[0]->getFaceEdges(f); } IndexArray setBaseEdgeVertices(Index e) { return _levels[0]->getEdgeVertices(e); } IndexArray setBaseEdgeFaces( Index e) { return _levels[0]->getEdgeFaces(e); } IndexArray setBaseVertexFaces( Index v) { return _levels[0]->getVertexFaces(v); } IndexArray setBaseVertexEdges( Index v) { return _levels[0]->getVertexEdges(v); } LocalIndexArray setBaseEdgeFaceLocalIndices(Index e) { return _levels[0]->getEdgeFaceLocalIndices(e); } LocalIndexArray setBaseVertexFaceLocalIndices(Index v) { return _levels[0]->getVertexFaceLocalIndices(v); } LocalIndexArray setBaseVertexEdgeLocalIndices(Index v) { return _levels[0]->getVertexEdgeLocalIndices(v); } void populateBaseLocalIndices() { _levels[0]->populateLocalIndices(); } void setBaseEdgeNonManifold(Index e, bool b) { _levels[0]->setEdgeNonManifold(e, b); } void setBaseVertexNonManifold(Index v, bool b) { _levels[0]->setVertexNonManifold(v, b); } // Optional feature tagging methods for setting sharpness, holes, etc.: void setBaseEdgeSharpness(Index e, float s) { _levels[0]->getEdgeSharpness(e) = s; } void setBaseVertexSharpness(Index v, float s) { _levels[0]->getVertexSharpness(v) = s; } void setBaseFaceHole(Index f, bool b) { _levels[0]->setFaceHole(f, b); _hasHoles |= b; } // Optional methods for creating and assigning face-varying data channels: int createBaseFVarChannel(int numValues); int createBaseFVarChannel(int numValues, Sdc::Options const& options); IndexArray setBaseFVarFaceValues(Index face, int channel = 0); void setBaseMaxValence(int valence) { _levels[0]->setMaxValence(valence); } void initializeBaseInventory() { initializeInventory(); } protected: // // Lower level protected methods intended strictly for internal use: // friend class TopologyRefinerFactoryBase; friend class GregoryBasisFactory; friend class PatchTablesFactory; Vtr::Level & getLevel(int l) { return *_levels[l]; } Vtr::Level const & getLevel(int l) const { return *_levels[l]; } Vtr::Refinement & getRefinement(int l) { return *_refinements[l]; } Vtr::Refinement const & getRefinement(int l) const { return *_refinements[l]; } private: void selectFeatureAdaptiveComponents(Vtr::SparseSelector& selector); template void interpolateChildVertsFromFaces(Vtr::Refinement const &, T const & src, U & dst) const; template void interpolateChildVertsFromEdges(Vtr::Refinement const &, T const & src, U & dst) const; template void interpolateChildVertsFromVerts(Vtr::Refinement const &, T const & src, U & dst) const; template void varyingInterpolateChildVertsFromFaces(Vtr::Refinement const &, T const & src, U & dst) const; template void varyingInterpolateChildVertsFromEdges(Vtr::Refinement const &, T const & src, U & dst) const; template void varyingInterpolateChildVertsFromVerts(Vtr::Refinement const &, T const & src, U & dst) const; template void faceVaryingInterpolateChildVertsFromFaces(Vtr::Refinement const &, T const & src, U & dst, int channel) const; template void faceVaryingInterpolateChildVertsFromEdges(Vtr::Refinement const &, T const & src, U & dst, int channel) const; template void faceVaryingInterpolateChildVertsFromVerts(Vtr::Refinement const &, T const & src, U & dst, int channel) const; template void limit(T const & src, U * dst) const; template void faceVaryingLimit(T const & src, U * dst, int channel) const; void initializePtexIndices() const; void initializeInventory(); void updateInventory(Vtr::Level const & newLevel); void appendLevel(Vtr::Level & newLevel); void appendRefinement(Vtr::Refinement & newRefinement); private: Sdc::SchemeType _subdivType; Sdc::Options _subdivOptions; unsigned int _isUniform : 1, _hasHoles : 1, _maxLevel : 4; // Options assigned on refinement: UniformOptions _uniformOptions; AdaptiveOptions _adaptiveOptions; // Cumulative properties of all levels: int _totalVertices; int _totalEdges; int _totalFaces; int _totalFaceVertices; int _maxValence; std::vector _levels; std::vector _refinements; std::vector _ptexIndices; }; inline int TopologyRefiner::GetNumFVarChannels() const { return _levels[0]->getNumFVarChannels(); } inline Sdc::Options::FVarLinearInterpolation TopologyRefiner::GetFVarLinearInterpolation(int channel) const { return _levels[0]->getFVarOptions(channel).GetFVarLinearInterpolation(); } inline int TopologyRefiner::GetNumFVarValues(int level, int channel) const { return _levels[level]->getNumFVarValues(channel); } inline ConstIndexArray TopologyRefiner::GetFVarFaceValues(int level, Index face, int channel) const { return _levels[level]->getFVarFaceValues(face, channel); } inline int TopologyRefiner::createBaseFVarChannel(int numValues) { return _levels[0]->createFVarChannel(numValues, _subdivOptions); } inline int TopologyRefiner::createBaseFVarChannel(int numValues, Sdc::Options const& fvarOptions) { Sdc::Options options = _subdivOptions; options.SetFVarLinearInterpolation(fvarOptions.GetFVarLinearInterpolation()); return _levels[0]->createFVarChannel(numValues, options); } inline IndexArray TopologyRefiner::setBaseFVarFaceValues(Index face, int channel) { return _levels[0]->getFVarFaceValues(face, channel); } template inline void TopologyRefiner::Interpolate(T const * src, U * dst) const { for (int level=1; level<=GetMaxLevel(); ++level) { Interpolate(level, src, dst); src = dst; dst += GetNumVertices(level); } } template inline void TopologyRefiner::Interpolate(int level, T const & src, U & dst) const { assert(level>0 and level<=(int)_refinements.size()); Vtr::Refinement const & refinement = getRefinement(level-1); switch (_subdivType) { case Sdc::SCHEME_CATMARK: interpolateChildVertsFromFaces(refinement, src, dst); interpolateChildVertsFromEdges(refinement, src, dst); interpolateChildVertsFromVerts(refinement, src, dst); break; case Sdc::SCHEME_LOOP: interpolateChildVertsFromFaces(refinement, src, dst); interpolateChildVertsFromEdges(refinement, src, dst); interpolateChildVertsFromVerts(refinement, src, dst); break; case Sdc::SCHEME_BILINEAR: interpolateChildVertsFromFaces(refinement, src, dst); interpolateChildVertsFromEdges(refinement, src, dst); interpolateChildVertsFromVerts(refinement, src, dst); break; } } template inline void TopologyRefiner::interpolateChildVertsFromFaces( Vtr::Refinement const & refinement, T const & src, U & dst) const { if (refinement.getNumChildVerticesFromFaces() == 0) return; Sdc::Scheme scheme(_subdivOptions); const Vtr::Level& parent = refinement.parent(); float * fVertWeights = (float *)alloca(parent.getMaxValence()*sizeof(float)); for (int face = 0; face < parent.getNumFaces(); ++face) { Vtr::Index cVert = refinement.getFaceChildVertex(face); if (!Vtr::IndexIsValid(cVert)) continue; // Declare and compute mask weights for this vertex relative to its parent face: ConstIndexArray fVerts = parent.getFaceVertices(face); float fVaryingWeight = 1.0f / (float) fVerts.size(); Vtr::MaskInterface fMask(fVertWeights, 0, 0); Vtr::FaceInterface fHood(fVerts.size()); scheme.ComputeFaceVertexMask(fHood, fMask); // Apply the weights to the parent face's vertices: dst[cVert].Clear(); for (int i = 0; i < fVerts.size(); ++i) { dst[cVert].AddWithWeight(src[fVerts[i]], fVertWeights[i]); dst[cVert].AddVaryingWithWeight(src[fVerts[i]], fVaryingWeight); } } } template inline void TopologyRefiner::interpolateChildVertsFromEdges( Vtr::Refinement const & refinement, T const & src, U & dst) const { Sdc::Scheme scheme(_subdivOptions); const Vtr::Level& parent = refinement.parent(); const Vtr::Level& child = refinement.child(); Vtr::EdgeInterface eHood(parent); float eVertWeights[2], * eFaceWeights = (float *)alloca(parent.getMaxEdgeFaces()*sizeof(float)); for (int edge = 0; edge < parent.getNumEdges(); ++edge) { Vtr::Index cVert = refinement.getEdgeChildVertex(edge); if (!Vtr::IndexIsValid(cVert)) continue; // Declare and compute mask weights for this vertex relative to its parent edge: ConstIndexArray eVerts = parent.getEdgeVertices(edge), eFaces = parent.getEdgeFaces(edge); Vtr::MaskInterface eMask(eVertWeights, 0, eFaceWeights); eHood.SetIndex(edge); Sdc::Crease::Rule pRule = (parent.getEdgeSharpness(edge) > 0.0f) ? Sdc::Crease::RULE_CREASE : Sdc::Crease::RULE_SMOOTH; Sdc::Crease::Rule cRule = child.getVertexRule(cVert); scheme.ComputeEdgeVertexMask(eHood, eMask, pRule, cRule); // Apply the weights to the parent edges's vertices and (if applicable) to // the child vertices of its incident faces: dst[cVert].Clear(); dst[cVert].AddWithWeight(src[eVerts[0]], eVertWeights[0]); dst[cVert].AddWithWeight(src[eVerts[1]], eVertWeights[1]); dst[cVert].AddVaryingWithWeight(src[eVerts[0]], 0.5f); dst[cVert].AddVaryingWithWeight(src[eVerts[1]], 0.5f); if (eMask.GetNumFaceWeights() > 0) { for (int i = 0; i < eFaces.size(); ++i) { if (eMask.AreFaceWeightsForFaceCenters()) { assert(refinement.getNumChildVerticesFromFaces() > 0); Vtr::Index cVertOfFace = refinement.getFaceChildVertex(eFaces[i]); assert(Vtr::IndexIsValid(cVertOfFace)); dst[cVert].AddWithWeight(dst[cVertOfFace], eFaceWeights[i]); } else { Vtr::Index pFace = eFaces[i]; ConstIndexArray pFaceEdges = parent.getFaceEdges(pFace), pFaceVerts = parent.getFaceVertices(pFace); int eInFace = 0; for ( ; pFaceEdges[eInFace] != edge; ++eInFace ) ; int vInFace = eInFace + 2; if (vInFace >= pFaceVerts.size()) vInFace -= pFaceVerts.size(); Vtr::Index pVertNext = pFaceVerts[vInFace]; dst[cVert].AddWithWeight(src[pVertNext], eFaceWeights[i]); } } } } } template inline void TopologyRefiner::interpolateChildVertsFromVerts( Vtr::Refinement const & refinement, T const & src, U & dst) const { Sdc::Scheme scheme(_subdivOptions); const Vtr::Level& parent = refinement.parent(); const Vtr::Level& child = refinement.child(); Vtr::VertexInterface vHood(parent, child); float * weightBuffer = (float *)alloca(2*parent.getMaxValence()*sizeof(float)); for (int vert = 0; vert < parent.getNumVertices(); ++vert) { Vtr::Index cVert = refinement.getVertexChildVertex(vert); if (!Vtr::IndexIsValid(cVert)) continue; // Declare and compute mask weights for this vertex relative to its parent edge: ConstIndexArray vEdges = parent.getVertexEdges(vert), vFaces = parent.getVertexFaces(vert); float vVertWeight, * vEdgeWeights = weightBuffer, * vFaceWeights = vEdgeWeights + vEdges.size(); Vtr::MaskInterface vMask(&vVertWeight, vEdgeWeights, vFaceWeights); vHood.SetIndex(vert, cVert); Sdc::Crease::Rule pRule = parent.getVertexRule(vert); Sdc::Crease::Rule cRule = child.getVertexRule(cVert); scheme.ComputeVertexVertexMask(vHood, vMask, pRule, cRule); // Apply the weights to the parent vertex, the vertices opposite its incident // edges, and the child vertices of its incident faces: // // In order to improve numerical precision, its better to apply smaller weights // first, so begin with the face-weights followed by the edge-weights and the // vertex weight last. dst[cVert].Clear(); if (vMask.GetNumFaceWeights() > 0) { assert(vMask.AreFaceWeightsForFaceCenters()); for (int i = 0; i < vFaces.size(); ++i) { Vtr::Index cVertOfFace = refinement.getFaceChildVertex(vFaces[i]); assert(Vtr::IndexIsValid(cVertOfFace)); dst[cVert].AddWithWeight(dst[cVertOfFace], vFaceWeights[i]); } } if (vMask.GetNumEdgeWeights() > 0) { for (int i = 0; i < vEdges.size(); ++i) { ConstIndexArray eVerts = parent.getEdgeVertices(vEdges[i]); Vtr::Index pVertOppositeEdge = (eVerts[0] == vert) ? eVerts[1] : eVerts[0]; dst[cVert].AddWithWeight(src[pVertOppositeEdge], vEdgeWeights[i]); } } dst[cVert].AddWithWeight(src[vert], vVertWeight); dst[cVert].AddVaryingWithWeight(src[vert], 1.0f); } } // // Varying only interpolation // template inline void TopologyRefiner::InterpolateVarying(T const * src, U * dst) const { for (int level=1; level<=GetMaxLevel(); ++level) { InterpolateVarying(level, src, dst); src = dst; dst += GetNumVertices(level); } } template inline void TopologyRefiner::InterpolateVarying(int level, T const & src, U & dst) const { assert(level>0 and level<=(int)_refinements.size()); Vtr::Refinement const & refinement = getRefinement(level-1); varyingInterpolateChildVertsFromFaces(refinement, src, dst); varyingInterpolateChildVertsFromEdges(refinement, src, dst); varyingInterpolateChildVertsFromVerts(refinement, src, dst); } template inline void TopologyRefiner::varyingInterpolateChildVertsFromFaces( Vtr::Refinement const & refinement, T const & src, U & dst) const { if (refinement.getNumChildVerticesFromFaces() == 0) return; const Vtr::Level& parent = refinement.parent(); for (int face = 0; face < parent.getNumFaces(); ++face) { Vtr::Index cVert = refinement.getFaceChildVertex(face); if (!Vtr::IndexIsValid(cVert)) continue; ConstIndexArray fVerts = parent.getFaceVertices(face); float fVaryingWeight = 1.0f / (float) fVerts.size(); // Apply the weights to the parent face's vertices: dst[cVert].Clear(); for (int i = 0; i < fVerts.size(); ++i) { dst[cVert].AddVaryingWithWeight(src[fVerts[i]], fVaryingWeight); } } } template inline void TopologyRefiner::varyingInterpolateChildVertsFromEdges( Vtr::Refinement const & refinement, T const & src, U & dst) const { const Vtr::Level& parent = refinement.parent(); for (int edge = 0; edge < parent.getNumEdges(); ++edge) { Vtr::Index cVert = refinement.getEdgeChildVertex(edge); if (!Vtr::IndexIsValid(cVert)) continue; // Declare and compute mask weights for this vertex relative to its parent edge: ConstIndexArray eVerts = parent.getEdgeVertices(edge); // Apply the weights to the parent edges's vertices dst[cVert].Clear(); dst[cVert].AddVaryingWithWeight(src[eVerts[0]], 0.5f); dst[cVert].AddVaryingWithWeight(src[eVerts[1]], 0.5f); } } template inline void TopologyRefiner::varyingInterpolateChildVertsFromVerts( Vtr::Refinement const & refinement, T const & src, U & dst) const { const Vtr::Level& parent = refinement.parent(); for (int vert = 0; vert < parent.getNumVertices(); ++vert) { Vtr::Index cVert = refinement.getVertexChildVertex(vert); if (!Vtr::IndexIsValid(cVert)) continue; // Apply the weights to the parent vertex dst[cVert].Clear(); dst[cVert].AddVaryingWithWeight(src[vert], 1.0f); } } // // Face-varying only interpolation // template inline void TopologyRefiner::InterpolateFaceVarying(T const * src, U * dst, int channel) const { for (int level=1; level<=GetMaxLevel(); ++level) { InterpolateFaceVarying(level, src, dst, channel); src = dst; dst += getLevel(level).getNumFVarValues(); } } template inline void TopologyRefiner::InterpolateFaceVarying(int level, T const & src, U & dst, int channel) const { assert(level>0 and level<=(int)_refinements.size()); Vtr::Refinement const & refinement = getRefinement(level-1); switch (_subdivType) { case Sdc::SCHEME_CATMARK: faceVaryingInterpolateChildVertsFromFaces(refinement, src, dst, channel); faceVaryingInterpolateChildVertsFromEdges(refinement, src, dst, channel); faceVaryingInterpolateChildVertsFromVerts(refinement, src, dst, channel); break; case Sdc::SCHEME_LOOP: faceVaryingInterpolateChildVertsFromFaces(refinement, src, dst, channel); faceVaryingInterpolateChildVertsFromEdges(refinement, src, dst, channel); faceVaryingInterpolateChildVertsFromVerts(refinement, src, dst, channel); break; case Sdc::SCHEME_BILINEAR: faceVaryingInterpolateChildVertsFromFaces(refinement, src, dst, channel); faceVaryingInterpolateChildVertsFromEdges(refinement, src, dst, channel); faceVaryingInterpolateChildVertsFromVerts(refinement, src, dst, channel); break; } } template inline void TopologyRefiner::faceVaryingInterpolateChildVertsFromFaces( Vtr::Refinement const & refinement, T const & src, U & dst, int channel) const { if (refinement.getNumChildVerticesFromFaces() == 0) return; Sdc::Scheme scheme(_subdivOptions); const Vtr::Level& parentLevel = refinement.parent(); const Vtr::Level& childLevel = refinement.child(); const Vtr::FVarLevel& parentFVar = *parentLevel._fvarChannels[channel]; const Vtr::FVarLevel& childFVar = *childLevel._fvarChannels[channel]; float * fValueWeights = (float *)alloca(parentLevel.getMaxValence()*sizeof(float)); for (int face = 0; face < parentLevel.getNumFaces(); ++face) { Vtr::Index cVert = refinement.getFaceChildVertex(face); if (!Vtr::IndexIsValid(cVert)) continue; Vtr::Index cVertValue = childFVar.getVertexValueOffset(cVert); // The only difference for face-varying here is that we get the values associated // with each face-vertex directly from the FVarLevel, rather than using the parent // face-vertices directly. If any face-vertex has any sibling values, then we may // get the wrong one using the face-vertex index directly. // Declare and compute mask weights for this vertex relative to its parent face: ConstIndexArray fValues = parentFVar.getFaceValues(face); Vtr::MaskInterface fMask(fValueWeights, 0, 0); Vtr::FaceInterface fHood(fValues.size()); scheme.ComputeFaceVertexMask(fHood, fMask); // Apply the weights to the parent face's vertices: dst[cVertValue].Clear(); for (int i = 0; i < fValues.size(); ++i) { dst[cVertValue].AddWithWeight(src[fValues[i]], fValueWeights[i]); } } } template inline void TopologyRefiner::faceVaryingInterpolateChildVertsFromEdges( Vtr::Refinement const & refinement, T const & src, U & dst, int channel) const { Sdc::Scheme scheme(_subdivOptions); const Vtr::Level& parentLevel = refinement.parent(); const Vtr::Level& childLevel = refinement.child(); const Vtr::FVarRefinement& refineFVar = *refinement._fvarChannels[channel]; const Vtr::FVarLevel& parentFVar = *parentLevel._fvarChannels[channel]; const Vtr::FVarLevel& childFVar = *childLevel._fvarChannels[channel]; // // Allocate and intialize (if linearly interpolated) interpolation weights for // the edge mask: // float eVertWeights[2], * eFaceWeights = (float *)alloca(parentLevel.getMaxEdgeFaces()*sizeof(float)); Vtr::MaskInterface eMask(eVertWeights, 0, eFaceWeights); bool isLinearFVar = parentFVar._isLinear; if (isLinearFVar) { eMask.SetNumVertexWeights(2); eMask.SetNumEdgeWeights(0); eMask.SetNumFaceWeights(0); eVertWeights[0] = 0.5f; eVertWeights[1] = 0.5f; } Vtr::EdgeInterface eHood(parentLevel); for (int edge = 0; edge < parentLevel.getNumEdges(); ++edge) { Vtr::Index cVert = refinement.getEdgeChildVertex(edge); if (!Vtr::IndexIsValid(cVert)) continue; ConstIndexArray cVertValues = childFVar.getVertexValues(cVert); bool fvarEdgeVertMatchesVertex = childFVar.valueTopologyMatches(cVertValues[0]); if (fvarEdgeVertMatchesVertex) { // // If smoothly interpolated, compute new weights for the edge mask: // if (!isLinearFVar) { eHood.SetIndex(edge); Sdc::Crease::Rule pRule = (parentLevel.getEdgeSharpness(edge) > 0.0f) ? Sdc::Crease::RULE_CREASE : Sdc::Crease::RULE_SMOOTH; Sdc::Crease::Rule cRule = childLevel.getVertexRule(cVert); scheme.ComputeEdgeVertexMask(eHood, eMask, pRule, cRule); } // Apply the weights to the parent edges's vertices and (if applicable) to // the child vertices of its incident faces: // // Even though the face-varying topology matches the vertex topology, we need // to be careful here when getting values corresponding to the two end-vertices. // While the edge may be continuous, the vertices at their ends may have // discontinuities elsewhere in their neighborhood (i.e. on the "other side" // of the end-vertex) and so have sibling values associated with them. In most // cases the topology for an end-vertex will match and we can use it directly, // but we must still check and retrieve as needed. // // Indices for values corresponding to face-vertices are guaranteed to match, // so we can use the child-vertex indices directly. // // And by "directly", we always use getVertexValue(vertexIndex) to reference // values in the "src" to account for the possible indirection that may exist at // level 0 -- where there may be fewer values than vertices and an additional // indirection is necessary. We can use a vertex index directly for "dst" when // it matches. // Vtr::Index eVertValues[2]; parentFVar.getEdgeFaceValues(edge, 0, eVertValues); Index cVertValue = cVertValues[0]; dst[cVertValue].Clear(); dst[cVertValue].AddWithWeight(src[eVertValues[0]], eVertWeights[0]); dst[cVertValue].AddWithWeight(src[eVertValues[1]], eVertWeights[1]); if (eMask.GetNumFaceWeights() > 0) { ConstIndexArray eFaces = parentLevel.getEdgeFaces(edge); for (int i = 0; i < eFaces.size(); ++i) { if (eMask.AreFaceWeightsForFaceCenters()) { Vtr::Index cVertOfFace = refinement.getFaceChildVertex(eFaces[i]); assert(Vtr::IndexIsValid(cVertOfFace)); Vtr::Index cValueOfFace = childFVar.getVertexValueOffset(cVertOfFace); dst[cVertValue].AddWithWeight(dst[cValueOfFace], eFaceWeights[i]); } else { Vtr::Index pFace = eFaces[i]; ConstIndexArray pFaceEdges = parentLevel.getFaceEdges(pFace), pFaceVerts = parentLevel.getFaceVertices(pFace); int eInFace = 0; for ( ; pFaceEdges[eInFace] != edge; ++eInFace ) ; // Edge "i" spans vertices [i,i+1] so we want i+2... int vInFace = eInFace + 2; if (vInFace >= pFaceVerts.size()) vInFace -= pFaceVerts.size(); Vtr::Index pValueNext = parentFVar.getFaceValues(pFace)[vInFace]; dst[cVertValue].AddWithWeight(src[pValueNext], eFaceWeights[i]); } } } } else { // // Mismatched edge-verts should just be linearly interpolated between the pairs of // values for each sibling of the child edge-vertex -- the question is: which face // holds that pair of values for a given sibling? // // In the manifold case, the sibling and edge-face indices will correspond. We // will eventually need to update this to account for > 3 incident faces. // for (int i = 0; i < cVertValues.size(); ++i) { Vtr::Index eVertValues[2]; int eFaceIndex = refineFVar.getChildValueParentSource(cVert, i); assert(eFaceIndex == i); parentFVar.getEdgeFaceValues(edge, eFaceIndex, eVertValues); Index cVertValue = cVertValues[i]; dst[cVertValue].Clear(); dst[cVertValue].AddWithWeight(src[eVertValues[0]], 0.5); dst[cVertValue].AddWithWeight(src[eVertValues[1]], 0.5); } } } } template inline void TopologyRefiner::faceVaryingInterpolateChildVertsFromVerts( Vtr::Refinement const & refinement, T const & src, U & dst, int channel) const { Sdc::Scheme scheme(_subdivOptions); const Vtr::Level& parentLevel = refinement.parent(); const Vtr::Level& childLevel = refinement.child(); const Vtr::FVarRefinement& refineFVar = *refinement._fvarChannels[channel]; const Vtr::FVarLevel& parentFVar = *parentLevel._fvarChannels[channel]; const Vtr::FVarLevel& childFVar = *childLevel._fvarChannels[channel]; bool isLinearFVar = parentFVar._isLinear; float * weightBuffer = (float *)alloca(2*parentLevel.getMaxValence()*sizeof(float)); Vtr::Index * vEdgeValues = (Vtr::Index *)alloca(parentLevel.getMaxValence()*sizeof(Vtr::Index)); Vtr::VertexInterface vHood(parentLevel, childLevel); for (int vert = 0; vert < parentLevel.getNumVertices(); ++vert) { Vtr::Index cVert = refinement.getVertexChildVertex(vert); if (!Vtr::IndexIsValid(cVert)) continue; ConstIndexArray pVertValues = parentFVar.getVertexValues(vert), cVertValues = childFVar.getVertexValues(cVert); bool fvarVertVertMatchesVertex = childFVar.valueTopologyMatches(cVertValues[0]); if (isLinearFVar && fvarVertVertMatchesVertex) { dst[cVertValues[0]].Clear(); dst[cVertValues[0]].AddWithWeight(src[pVertValues[0]], 1.0f); continue; } if (fvarVertVertMatchesVertex) { // // Declare and compute mask weights for this vertex relative to its parent edge: // // (We really need to encapsulate this somewhere else for use here and in the // general case) // ConstIndexArray vEdges = parentLevel.getVertexEdges(vert); float vVertWeight; float * vEdgeWeights = weightBuffer; float * vFaceWeights = vEdgeWeights + vEdges.size(); Vtr::MaskInterface vMask(&vVertWeight, vEdgeWeights, vFaceWeights); vHood.SetIndex(vert, cVert); Sdc::Crease::Rule pRule = parentLevel.getVertexRule(vert); Sdc::Crease::Rule cRule = childLevel.getVertexRule(cVert); scheme.ComputeVertexVertexMask(vHood, vMask, pRule, cRule); // Apply the weights to the parent vertex, the vertices opposite its incident // edges, and the child vertices of its incident faces: // // Even though the face-varying topology matches the vertex topology, we need // to be careful here when getting values corresponding to vertices at the // ends of edges. While the edge may be continuous, the end vertex may have // discontinuities elsewhere in their neighborhood (i.e. on the "other side" // of the end-vertex) and so have sibling values associated with them. In most // cases the topology for an end-vertex will match and we can use it directly, // but we must still check and retrieve as needed. // // Indices for values corresponding to face-vertices are guaranteed to match, // so we can use the child-vertex indices directly. // // And by "directly", we always use getVertexValue(vertexIndex) to reference // values in the "src" to account for the possible indirection that may exist at // level 0 -- where there may be fewer values than vertices and an additional // indirection is necessary. We can use a vertex index directly for "dst" when // it matches. // // As with applying the mask to vertex data, in order to improve numerical // precision, its better to apply smaller weights first, so begin with the // face-weights followed by the edge-weights and the vertex weight last. // Vtr::Index pVertValue = pVertValues[0]; Vtr::Index cVertValue = cVertValues[0]; dst[cVertValue].Clear(); if (vMask.GetNumFaceWeights() > 0) { assert(vMask.AreFaceWeightsForFaceCenters()); ConstIndexArray vFaces = parentLevel.getVertexFaces(vert); for (int i = 0; i < vFaces.size(); ++i) { Vtr::Index cVertOfFace = refinement.getFaceChildVertex(vFaces[i]); assert(Vtr::IndexIsValid(cVertOfFace)); Vtr::Index cValueOfFace = childFVar.getVertexValueOffset(cVertOfFace); dst[cVertValue].AddWithWeight(dst[cValueOfFace], vFaceWeights[i]); } } if (vMask.GetNumEdgeWeights() > 0) { parentFVar.getVertexEdgeValues(vert, vEdgeValues); for (int i = 0; i < vEdges.size(); ++i) { dst[cVertValue].AddWithWeight(src[vEdgeValues[i]], vEdgeWeights[i]); } } dst[cVertValue].AddWithWeight(src[pVertValue], vVertWeight); } else { // // Each FVar value associated with a vertex will be either a corner or a crease, // or potentially in transition from corner to crease: // - if the CHILD is a corner, there can be no transition so we have a corner // - otherwise if the PARENT is a crease, both will be creases (no transition) // - otherwise the parent must be a corner and the child a crease (transition) // Vtr::FVarLevel::ConstValueTagArray pValueTags = parentFVar.getVertexValueTags(vert); Vtr::FVarLevel::ConstValueTagArray cValueTags = childFVar.getVertexValueTags(cVert); for (int cSibling = 0; cSibling < cVertValues.size(); ++cSibling) { int pSibling = refineFVar.getChildValueParentSource(cVert, cSibling); assert(pSibling == cSibling); Vtr::Index pVertValue = pVertValues[pSibling]; Vtr::Index cVertValue = cVertValues[cSibling]; dst[cVertValue].Clear(); if (cValueTags[cSibling].isCorner()) { dst[cVertValue].AddWithWeight(src[pVertValue], 1.0f); } else { // // We have either a crease or a transition from corner to crease -- in // either case, we need the end values for the full/fractional crease: // Index pEndValues[2]; parentFVar.getVertexCreaseEndValues(vert, pSibling, pEndValues); float vWeight = 0.75f; float eWeight = 0.125f; // // If semisharp we need to apply fractional weighting -- if made sharp because // of the other sibling (dependent-sharp) use the fractional weight from that // other sibling (should only occur when there are 2): // if (pValueTags[pSibling].isSemiSharp()) { float wCorner = pValueTags[pSibling].isDepSharp() ? refineFVar.getFractionalWeight(vert, !pSibling, cVert, !cSibling) : refineFVar.getFractionalWeight(vert, pSibling, cVert, cSibling); float wCrease = 1.0f - wCorner; vWeight = wCrease * 0.75f + wCorner; eWeight = wCrease * 0.125f; } dst[cVertValue].AddWithWeight(src[pEndValues[0]], eWeight); dst[cVertValue].AddWithWeight(src[pEndValues[1]], eWeight); dst[cVertValue].AddWithWeight(src[pVertValue], vWeight); } } } } } template inline void TopologyRefiner::Limit(T const & src, U * dst) const { if (getLevel(GetMaxLevel()).getNumVertexEdgesTotal() == 0) { Error(FAR_RUNTIME_ERROR, "Cannot compute limit points -- last level of refinement does not include full topology."); return; } switch (_subdivType) { case Sdc::SCHEME_CATMARK: limit(src, dst); break; case Sdc::SCHEME_LOOP: limit(src, dst); break; case Sdc::SCHEME_BILINEAR: limit(src, dst); break; } } template inline void TopologyRefiner::limit(T const & src, U * dst) const { // // Work in progress... // - does not support tangents yet (unclear how) // - need to verify that each vertex is "limitable", i.e.: // - is not semi-sharp, inf-sharp or non-manifold // - is "complete" wrt its parent (if refinement is sparse) // - copy (or weight by 1.0) src to dst when not "limitable" // - currently requires one refinement to get rid of N-sided faces: // - could limit regular vertices from level 0 // Sdc::Scheme scheme(_subdivOptions); Vtr::Level const & level = getLevel(GetMaxLevel()); int maxWeightsPerMask = 1 + 2 * level.getMaxValence(); float * weightBuffer = (float *)alloca(maxWeightsPerMask * sizeof(float)); // This is a bit obscure -- assign both parent and child as last level Vtr::VertexInterface vHood(level, level); for (int vert = 0; vert < level.getNumVertices(); ++vert) { ConstIndexArray vEdges = level.getVertexEdges(vert); // Incomplete vertices (present in sparse refinement) do not have their full // topological neighborhood to determine a proper limit -- just leave the // vertex at the refined location and continue to the next: // if (level._vertTags[vert]._incomplete || (vEdges.size() == 0)) { dst[vert].Clear(); dst[vert].AddWithWeight(src[vert], 1.0); continue; } // Assign the mask weights to the common buffer and compute the mask: // float * vWeights = weightBuffer, * eWeights = vWeights + 1, * fWeights = eWeights + vEdges.size(); Vtr::MaskInterface vMask(vWeights, eWeights, fWeights); // This is a bit obscure -- child vertex index will be ignored here vHood.SetIndex(vert, vert); scheme.ComputeVertexLimitMask(vHood, vMask, level.getVertexRule(vert)); // Apply the weights to the vertex, the vertices opposite its incident // edges, and the opposite vertices of its incident faces: // // As with applying refinment masks to vertex data, in order to improve // numerical precision, its better to apply smaller weights first, so // begin with the face-weights followed by the edge-weights and the vertex // weight last. dst[vert].Clear(); if (vMask.GetNumFaceWeights() > 0) { assert(!vMask.AreFaceWeightsForFaceCenters()); ConstIndexArray vFaces = level.getVertexFaces(vert); ConstLocalIndexArray vInFace = level.getVertexFaceLocalIndices(vert); for (int i = 0; i < vFaces.size(); ++i) { ConstIndexArray fVerts = level.getFaceVertices(vFaces[i]); LocalIndex vOppInFace = (vInFace[i] + 2); if (vOppInFace >= fVerts.size()) vOppInFace -= (LocalIndex)fVerts.size(); Index vertOppositeFace = level.getFaceVertices(vFaces[i])[vOppInFace]; dst[vert].AddWithWeight(src[vertOppositeFace], fWeights[i]); } } if (vMask.GetNumEdgeWeights() > 0) { for (int i = 0; i < vEdges.size(); ++i) { ConstIndexArray eVerts = level.getEdgeVertices(vEdges[i]); Index vertOppositeEdge = (eVerts[0] == vert) ? eVerts[1] : eVerts[0]; dst[vert].AddWithWeight(src[vertOppositeEdge], eWeights[i]); } } dst[vert].AddWithWeight(src[vert], vWeights[0]); } } template inline void TopologyRefiner::LimitFaceVarying(T const & src, U * dst, int channel) const { if (getLevel(GetMaxLevel()).getNumVertexEdgesTotal() == 0) { Error(FAR_RUNTIME_ERROR, "Cannot compute limit points -- last level of refinement does not include full topology."); return; } switch (_subdivType) { case Sdc::SCHEME_CATMARK: faceVaryingLimit(src, dst, channel); break; case Sdc::SCHEME_LOOP: faceVaryingLimit(src, dst, channel); break; case Sdc::SCHEME_BILINEAR: faceVaryingLimit(src, dst, channel); break; } } template inline void TopologyRefiner::faceVaryingLimit(T const & src, U * dst, int channel) const { Sdc::Scheme scheme(_subdivOptions); Vtr::Level const & level = getLevel(GetMaxLevel()); Vtr::FVarLevel const & fvarChannel = *level._fvarChannels[channel]; int maxWeightsPerMask = 1 + 2 * level.getMaxValence(); float * weightBuffer = (float *)alloca(maxWeightsPerMask * sizeof(float)); Index * indexBuffer = (Index *)alloca(level.getMaxValence() * sizeof(Index)); // This is a bit obscure -- assign both parent and child as last level Vtr::VertexInterface vHood(level, level); for (int vert = 0; vert < level.getNumVertices(); ++vert) { ConstIndexArray vEdges = level.getVertexEdges(vert); ConstIndexArray vValues = fvarChannel.getVertexValues(vert); // Incomplete vertices (present in sparse refinement) do not have their full // topological neighborhood to determine a proper limit -- just leave the // values (perhaps more than one per vertex) at the refined location. // // The same can be done if the face-varying channel is purely linear. // bool isIncomplete = (level._vertTags[vert]._incomplete || (vEdges.size() == 0)); if (isIncomplete || fvarChannel._isLinear) { for (int i = 0; i < vValues.size(); ++i) { Vtr::Index vValue = vValues[i]; dst[vValue].Clear(); dst[vValue].AddWithWeight(src[vValue], 1.0f); } continue; } bool fvarVertMatchesVertex = fvarChannel.valueTopologyMatches(vValues[0]); if (fvarVertMatchesVertex) { // Assign the mask weights to the common buffer and compute the mask: // float * vWeights = weightBuffer, * eWeights = vWeights + 1, * fWeights = eWeights + vEdges.size(); Vtr::MaskInterface vMask(vWeights, eWeights, fWeights); vHood.SetIndex(vert, vert); scheme.ComputeVertexLimitMask(vHood, vMask, level.getVertexRule(vert)); // // Apply mask to corresponding FVar values for neighboring vertices: // Vtr::Index vValue = vValues[0]; dst[vValue].Clear(); if (vMask.GetNumFaceWeights() > 0) { assert(!vMask.AreFaceWeightsForFaceCenters()); ConstIndexArray vFaces = level.getVertexFaces(vert); ConstLocalIndexArray vInFace = level.getVertexFaceLocalIndices(vert); for (int i = 0; i < vFaces.size(); ++i) { ConstIndexArray faceValues = fvarChannel.getFaceValues(vFaces[i]); LocalIndex vOppInFace = vInFace[i] + 2; if (vOppInFace >= faceValues.size()) vOppInFace -= faceValues.size(); Index vValueOppositeFace = faceValues[vOppInFace]; dst[vValue].AddWithWeight(src[vValueOppositeFace], fWeights[i]); } } if (vMask.GetNumEdgeWeights() > 0) { Index * vEdgeValues = indexBuffer; fvarChannel.getVertexEdgeValues(vert, vEdgeValues); for (int i = 0; i < vEdges.size(); ++i) { dst[vValue].AddWithWeight(src[vEdgeValues[i]], eWeights[i]); } } dst[vValue].AddWithWeight(src[vValue], vWeights[0]); } else { // // Sibling FVar values associated with a vertex will be either a corner or a crease: // for (int i = 0; i < vValues.size(); ++i) { Vtr::Index vValue = vValues[i]; dst[vValue].Clear(); if (fvarChannel.getValueTag(vValue).isCorner()) { dst[vValue].AddWithWeight(src[vValue], 1.0f); } else { Index vEndValues[2]; fvarChannel.getVertexCreaseEndValues(vert, i, vEndValues); dst[vValue].AddWithWeight(src[vEndValues[0]], 1.0f/6.0f); dst[vValue].AddWithWeight(src[vEndValues[1]], 1.0f/6.0f); dst[vValue].AddWithWeight(src[vValue], 2.0f/3.0f); } } } } } } // end namespace Far } // end namespace OPENSUBDIV_VERSION using namespace OPENSUBDIV_VERSION; } // end namespace OpenSubdiv #endif /* FAR_TOPOLOGY_REFINER_H */