2021-05-20 12:49:33 +02:00
|
|
|
// Copyright 2009-2021 Intel Corporation
|
2021-04-20 18:38:09 +02:00
|
|
|
// 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 (p0<p1) std::swap(p0,p1);
|
|
|
|
return (((uint64_t)p0) << 32) | (uint64_t)p1;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
uint32_t v0,v1; //!< start and end vertex of the edge
|
|
|
|
};
|
|
|
|
|
|
|
|
HalfEdge ()
|
|
|
|
: vtx_index(-1), next_half_edge_ofs(0), prev_half_edge_ofs(0), opposite_half_edge_ofs(0), edge_crease_weight(0),
|
|
|
|
vertex_crease_weight(0), edge_level(0), patch_type(COMPLEX_PATCH), vertex_type(REGULAR_VERTEX)
|
|
|
|
{
|
|
|
|
static_assert(sizeof(HalfEdge) == 32, "invalid half edge size");
|
|
|
|
}
|
|
|
|
|
|
|
|
__forceinline bool hasOpposite() const { return opposite_half_edge_ofs != 0; }
|
|
|
|
__forceinline void setOpposite(HalfEdge* opposite) { opposite_half_edge_ofs = int(opposite-this); }
|
|
|
|
|
|
|
|
__forceinline HalfEdge* next() { assert( next_half_edge_ofs != 0 ); return &this[next_half_edge_ofs]; }
|
|
|
|
__forceinline const HalfEdge* next() const { assert( next_half_edge_ofs != 0 ); return &this[next_half_edge_ofs]; }
|
|
|
|
|
|
|
|
__forceinline HalfEdge* prev() { assert( prev_half_edge_ofs != 0 ); return &this[prev_half_edge_ofs]; }
|
|
|
|
__forceinline const HalfEdge* prev() const { assert( prev_half_edge_ofs != 0 ); return &this[prev_half_edge_ofs]; }
|
|
|
|
|
|
|
|
__forceinline HalfEdge* opposite() { assert( opposite_half_edge_ofs != 0 ); return &this[opposite_half_edge_ofs]; }
|
|
|
|
__forceinline const HalfEdge* opposite() const { assert( opposite_half_edge_ofs != 0 ); return &this[opposite_half_edge_ofs]; }
|
|
|
|
|
|
|
|
__forceinline HalfEdge* rotate() { return opposite()->next(); }
|
|
|
|
__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 {
|
2021-05-20 12:49:33 +02:00
|
|
|
if (p->vertexHasBorder() && (p->vertex_type != HalfEdge::NON_MANIFOLD_EDGE_VERTEX)) return true;
|
2021-04-20 18:38:09 +02:00
|
|
|
p = p->next();
|
|
|
|
} while (p != this);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! calculates conservative bounds of a catmull clark subdivision face */
|
|
|
|
__forceinline BBox3fa bounds(const BufferView<Vec3fa>& 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<Vec3fa>& 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<Vec3fa>& 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<Vec3fa>& 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<Vec3fa>& 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<Vec3fa>& 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];
|
|
|
|
};
|
|
|
|
}
|