// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "catmullclark_coefficients.h" namespace embree { class __aligned(32) HalfEdge { friend class SubdivMesh; public: enum PatchType : char { BILINEAR_PATCH = 0, //!< a bilinear patch REGULAR_QUAD_PATCH = 1, //!< a regular quad patch can be represented as a B-Spline IRREGULAR_QUAD_PATCH = 2, //!< an irregular quad patch can be represented as a Gregory patch COMPLEX_PATCH = 3 //!< these patches need subdivision and cannot be processed by the above fast code paths }; enum VertexType : char { REGULAR_VERTEX = 0, //!< regular vertex NON_MANIFOLD_EDGE_VERTEX = 1, //!< vertex of a non-manifold edge }; __forceinline friend PatchType max( const PatchType& ty0, const PatchType& ty1) { return (PatchType) max((int)ty0,(int)ty1); } struct Edge { /*! edge constructor */ __forceinline Edge(const uint32_t v0, const uint32_t v1) : v0(v0), v1(v1) {} /*! create an 64 bit identifier that is unique for the not oriented edge */ __forceinline operator uint64_t() const { uint32_t p0 = v0, p1 = v1; if (p0next(); } __forceinline const HalfEdge* rotate() const { return opposite()->next(); } __forceinline unsigned int getStartVertexIndex() const { return vtx_index; } __forceinline unsigned int getEndVertexIndex () const { return next()->vtx_index; } __forceinline Edge getEdge () const { return Edge(getStartVertexIndex(),getEndVertexIndex()); } /*! tests if the start vertex of the edge is regular */ __forceinline PatchType vertexType() const { const HalfEdge* p = this; size_t face_valence = 0; bool hasBorder = false; do { /* we need subdivision to handle edge creases */ if (p->hasOpposite() && p->edge_crease_weight > 0.0f) return COMPLEX_PATCH; face_valence++; /* test for quad */ const HalfEdge* pp = p; pp = pp->next(); if (pp == p) return COMPLEX_PATCH; pp = pp->next(); if (pp == p) return COMPLEX_PATCH; pp = pp->next(); if (pp == p) return COMPLEX_PATCH; pp = pp->next(); if (pp != p) return COMPLEX_PATCH; /* continue with next face */ p = p->prev(); if (likely(p->hasOpposite())) p = p->opposite(); /* if there is no opposite go the long way to the other side of the border */ else { face_valence++; hasBorder = true; p = this; while (p->hasOpposite()) p = p->rotate(); } } while (p != this); /* calculate vertex type */ if (face_valence == 2 && hasBorder) { if (vertex_crease_weight == 0.0f ) return REGULAR_QUAD_PATCH; else if (vertex_crease_weight == float(inf)) return REGULAR_QUAD_PATCH; else return COMPLEX_PATCH; } else if (vertex_crease_weight != 0.0f) return COMPLEX_PATCH; else if (face_valence == 3 && hasBorder) return REGULAR_QUAD_PATCH; else if (face_valence == 4 && !hasBorder) return REGULAR_QUAD_PATCH; else return IRREGULAR_QUAD_PATCH; } /*! tests if this edge is part of a bilinear patch */ __forceinline bool bilinearVertex() const { return vertex_crease_weight == float(inf) && edge_crease_weight == float(inf); } /*! calculates the type of the patch */ __forceinline PatchType patchType() const { const HalfEdge* p = this; PatchType ret = REGULAR_QUAD_PATCH; bool bilinear = true; ret = max(ret,p->vertexType()); bilinear &= p->bilinearVertex(); if ((p = p->next()) == this) return COMPLEX_PATCH; ret = max(ret,p->vertexType()); bilinear &= p->bilinearVertex(); if ((p = p->next()) == this) return COMPLEX_PATCH; ret = max(ret,p->vertexType()); bilinear &= p->bilinearVertex(); if ((p = p->next()) == this) return COMPLEX_PATCH; ret = max(ret,p->vertexType()); bilinear &= p->bilinearVertex(); if ((p = p->next()) != this) return COMPLEX_PATCH; if (bilinear) return BILINEAR_PATCH; return ret; } /*! tests if the face is a regular b-spline face */ __forceinline bool isRegularFace() const { return patch_type == REGULAR_QUAD_PATCH; } /*! tests if the face can be diced (using bspline or gregory patch) */ __forceinline bool isGregoryFace() const { return patch_type == IRREGULAR_QUAD_PATCH || patch_type == REGULAR_QUAD_PATCH; } /*! tests if the base vertex of this half edge is a corner vertex */ __forceinline bool isCorner() const { return !hasOpposite() && !prev()->hasOpposite(); } /*! tests if the vertex is attached to any border */ __forceinline bool vertexHasBorder() const { const HalfEdge* p = this; do { if (!p->hasOpposite()) return true; p = p->rotate(); } while (p != this); return false; } /*! tests if the face this half edge belongs to has some border */ __forceinline bool faceHasBorder() const { const HalfEdge* p = this; do { if (p->vertexHasBorder() && (p->vertex_type != HalfEdge::NON_MANIFOLD_EDGE_VERTEX)) return true; p = p->next(); } while (p != this); return false; } /*! calculates conservative bounds of a catmull clark subdivision face */ __forceinline BBox3fa bounds(const BufferView& vertices) const { BBox3fa bounds = this->get1RingBounds(vertices); for (const HalfEdge* p=this->next(); p!=this; p=p->next()) bounds.extend(p->get1RingBounds(vertices)); return bounds; } /*! tests if this is a valid patch */ __forceinline bool valid(const BufferView& vertices) const { size_t N = 1; if (!this->validRing(vertices)) return false; for (const HalfEdge* p=this->next(); p!=this; p=p->next(), N++) { if (!p->validRing(vertices)) return false; } return N >= 3 && N <= MAX_PATCH_VALENCE; } /*! counts number of polygon edges */ __forceinline unsigned int numEdges() const { unsigned int N = 1; for (const HalfEdge* p=this->next(); p!=this; p=p->next(), N++); return N; } /*! calculates face and edge valence */ __forceinline void calculateFaceValenceAndEdgeValence(size_t& faceValence, size_t& edgeValence) const { faceValence = 0; edgeValence = 0; const HalfEdge* p = this; do { /* calculate bounds of current face */ unsigned int numEdges = p->numEdges(); assert(numEdges >= 3); edgeValence += numEdges-2; faceValence++; p = p->prev(); /* continue with next face */ if (likely(p->hasOpposite())) p = p->opposite(); /* if there is no opposite go the long way to the other side of the border */ else { faceValence++; edgeValence++; p = this; while (p->hasOpposite()) p = p->opposite()->next(); } } while (p != this); } /*! stream output */ friend __forceinline std::ostream &operator<<(std::ostream &o, const HalfEdge &h) { return o << "{ " << "vertex = " << h.vtx_index << ", " << //" -> " << h.next()->vtx_index << ", " << "prev = " << h.prev_half_edge_ofs << ", " << "next = " << h.next_half_edge_ofs << ", " << "opposite = " << h.opposite_half_edge_ofs << ", " << "edge_crease = " << h.edge_crease_weight << ", " << "vertex_crease = " << h.vertex_crease_weight << ", " << //"edge_level = " << h.edge_level << " }"; } private: /*! calculates the bounds of the face associated with the half-edge */ __forceinline BBox3fa getFaceBounds(const BufferView& vertices) const { BBox3fa b = vertices[getStartVertexIndex()]; for (const HalfEdge* p = next(); p!=this; p=p->next()) { b.extend(vertices[p->getStartVertexIndex()]); } return b; } /*! calculates the bounds of the 1-ring associated with the vertex of the half-edge */ __forceinline BBox3fa get1RingBounds(const BufferView& vertices) const { BBox3fa bounds = empty; const HalfEdge* p = this; do { /* calculate bounds of current face */ bounds.extend(p->getFaceBounds(vertices)); p = p->prev(); /* continue with next face */ if (likely(p->hasOpposite())) p = p->opposite(); /* if there is no opposite go the long way to the other side of the border */ else { p = this; while (p->hasOpposite()) p = p->opposite()->next(); } } while (p != this); return bounds; } /*! tests if this is a valid face */ __forceinline bool validFace(const BufferView& vertices, size_t& N) const { const Vec3fa v = vertices[getStartVertexIndex()]; if (!isvalid(v)) return false; size_t n = 1; for (const HalfEdge* p = next(); p!=this; p=p->next(), n++) { const Vec3fa v = vertices[p->getStartVertexIndex()]; if (!isvalid(v)) return false; } N += n-2; return n >= 3 && n <= MAX_PATCH_VALENCE; } /*! tests if this is a valid ring */ __forceinline bool validRing(const BufferView& vertices) const { size_t faceValence = 0; size_t edgeValence = 0; const HalfEdge* p = this; do { /* calculate bounds of current face */ if (!p->validFace(vertices,edgeValence)) return false; faceValence++; p = p->prev(); /* continue with next face */ if (likely(p->hasOpposite())) p = p->opposite(); /* if there is no opposite go the long way to the other side of the border */ else { faceValence++; edgeValence++; p = this; while (p->hasOpposite()) p = p->opposite()->next(); } } while (p != this); return faceValence <= MAX_RING_FACE_VALENCE && edgeValence <= MAX_RING_EDGE_VALENCE; } private: unsigned int vtx_index; //!< index of edge start vertex int next_half_edge_ofs; //!< relative offset to next half edge of face int prev_half_edge_ofs; //!< relative offset to previous half edge of face int opposite_half_edge_ofs; //!< relative offset to opposite half edge public: float edge_crease_weight; //!< crease weight attached to edge float vertex_crease_weight; //!< crease weight attached to start vertex float edge_level; //!< subdivision factor for edge PatchType patch_type; //!< stores type of subdiv patch VertexType vertex_type; //!< stores type of the start vertex char align[2]; }; }