1 | // Copyright 2009-2021 Intel Corporation |
2 | // SPDX-License-Identifier: Apache-2.0 |
3 | |
4 | #pragma once |
5 | |
6 | #include "catmullclark_coefficients.h" |
7 | |
8 | namespace embree |
9 | { |
10 | class __aligned(32) HalfEdge |
11 | { |
12 | friend class SubdivMesh; |
13 | public: |
14 | |
15 | enum PatchType : char { |
16 | BILINEAR_PATCH = 0, //!< a bilinear patch |
17 | REGULAR_QUAD_PATCH = 1, //!< a regular quad patch can be represented as a B-Spline |
18 | IRREGULAR_QUAD_PATCH = 2, //!< an irregular quad patch can be represented as a Gregory patch |
19 | COMPLEX_PATCH = 3 //!< these patches need subdivision and cannot be processed by the above fast code paths |
20 | }; |
21 | |
22 | enum VertexType : char { |
23 | REGULAR_VERTEX = 0, //!< regular vertex |
24 | NON_MANIFOLD_EDGE_VERTEX = 1, //!< vertex of a non-manifold edge |
25 | }; |
26 | |
27 | __forceinline friend PatchType max( const PatchType& ty0, const PatchType& ty1) { |
28 | return (PatchType) max(a: (int)ty0,b: (int)ty1); |
29 | } |
30 | |
31 | struct Edge |
32 | { |
33 | /*! edge constructor */ |
34 | __forceinline Edge(const uint32_t v0, const uint32_t v1) |
35 | : v0(v0), v1(v1) {} |
36 | |
37 | /*! create an 64 bit identifier that is unique for the not oriented edge */ |
38 | __forceinline operator uint64_t() const |
39 | { |
40 | uint32_t p0 = v0, p1 = v1; |
41 | if (p0<p1) std::swap(a&: p0,b&: p1); |
42 | return (((uint64_t)p0) << 32) | (uint64_t)p1; |
43 | } |
44 | |
45 | public: |
46 | uint32_t v0,v1; //!< start and end vertex of the edge |
47 | }; |
48 | |
49 | HalfEdge () |
50 | : vtx_index(-1), next_half_edge_ofs(0), prev_half_edge_ofs(0), opposite_half_edge_ofs(0), edge_crease_weight(0), |
51 | vertex_crease_weight(0), edge_level(0), patch_type(COMPLEX_PATCH), vertex_type(REGULAR_VERTEX) |
52 | { |
53 | static_assert(sizeof(HalfEdge) == 32, "invalid half edge size" ); |
54 | } |
55 | |
56 | __forceinline bool hasOpposite() const { return opposite_half_edge_ofs != 0; } |
57 | __forceinline void setOpposite(HalfEdge* opposite) { opposite_half_edge_ofs = int(opposite-this); } |
58 | |
59 | __forceinline HalfEdge* next() { assert( next_half_edge_ofs != 0 ); return &this[next_half_edge_ofs]; } |
60 | __forceinline const HalfEdge* next() const { assert( next_half_edge_ofs != 0 ); return &this[next_half_edge_ofs]; } |
61 | |
62 | __forceinline HalfEdge* prev() { assert( prev_half_edge_ofs != 0 ); return &this[prev_half_edge_ofs]; } |
63 | __forceinline const HalfEdge* prev() const { assert( prev_half_edge_ofs != 0 ); return &this[prev_half_edge_ofs]; } |
64 | |
65 | __forceinline HalfEdge* opposite() { assert( opposite_half_edge_ofs != 0 ); return &this[opposite_half_edge_ofs]; } |
66 | __forceinline const HalfEdge* opposite() const { assert( opposite_half_edge_ofs != 0 ); return &this[opposite_half_edge_ofs]; } |
67 | |
68 | __forceinline HalfEdge* rotate() { return opposite()->next(); } |
69 | __forceinline const HalfEdge* rotate() const { return opposite()->next(); } |
70 | |
71 | __forceinline unsigned int getStartVertexIndex() const { return vtx_index; } |
72 | __forceinline unsigned int getEndVertexIndex () const { return next()->vtx_index; } |
73 | __forceinline Edge getEdge () const { return Edge(getStartVertexIndex(),getEndVertexIndex()); } |
74 | |
75 | |
76 | /*! tests if the start vertex of the edge is regular */ |
77 | __forceinline PatchType vertexType() const |
78 | { |
79 | const HalfEdge* p = this; |
80 | size_t face_valence = 0; |
81 | bool hasBorder = false; |
82 | |
83 | do |
84 | { |
85 | /* we need subdivision to handle edge creases */ |
86 | if (p->hasOpposite() && p->edge_crease_weight > 0.0f) |
87 | return COMPLEX_PATCH; |
88 | |
89 | face_valence++; |
90 | |
91 | /* test for quad */ |
92 | const HalfEdge* pp = p; |
93 | pp = pp->next(); if (pp == p) return COMPLEX_PATCH; |
94 | pp = pp->next(); if (pp == p) return COMPLEX_PATCH; |
95 | pp = pp->next(); if (pp == p) return COMPLEX_PATCH; |
96 | pp = pp->next(); if (pp != p) return COMPLEX_PATCH; |
97 | |
98 | /* continue with next face */ |
99 | p = p->prev(); |
100 | if (likely(p->hasOpposite())) |
101 | p = p->opposite(); |
102 | |
103 | /* if there is no opposite go the long way to the other side of the border */ |
104 | else |
105 | { |
106 | face_valence++; |
107 | hasBorder = true; |
108 | p = this; |
109 | while (p->hasOpposite()) |
110 | p = p->rotate(); |
111 | } |
112 | } while (p != this); |
113 | |
114 | /* calculate vertex type */ |
115 | if (face_valence == 2 && hasBorder) { |
116 | if (vertex_crease_weight == 0.0f ) return REGULAR_QUAD_PATCH; |
117 | else if (vertex_crease_weight == float(inf)) return REGULAR_QUAD_PATCH; |
118 | else return COMPLEX_PATCH; |
119 | } |
120 | else if (vertex_crease_weight != 0.0f) return COMPLEX_PATCH; |
121 | else if (face_valence == 3 && hasBorder) return REGULAR_QUAD_PATCH; |
122 | else if (face_valence == 4 && !hasBorder) return REGULAR_QUAD_PATCH; |
123 | else return IRREGULAR_QUAD_PATCH; |
124 | } |
125 | |
126 | /*! tests if this edge is part of a bilinear patch */ |
127 | __forceinline bool bilinearVertex() const { |
128 | return vertex_crease_weight == float(inf) && edge_crease_weight == float(inf); |
129 | } |
130 | |
131 | /*! calculates the type of the patch */ |
132 | __forceinline PatchType patchType() const |
133 | { |
134 | const HalfEdge* p = this; |
135 | PatchType ret = REGULAR_QUAD_PATCH; |
136 | bool bilinear = true; |
137 | |
138 | ret = max(ty0: ret,ty1: p->vertexType()); |
139 | bilinear &= p->bilinearVertex(); |
140 | if ((p = p->next()) == this) return COMPLEX_PATCH; |
141 | |
142 | ret = max(ty0: ret,ty1: p->vertexType()); |
143 | bilinear &= p->bilinearVertex(); |
144 | if ((p = p->next()) == this) return COMPLEX_PATCH; |
145 | |
146 | ret = max(ty0: ret,ty1: p->vertexType()); |
147 | bilinear &= p->bilinearVertex(); |
148 | if ((p = p->next()) == this) return COMPLEX_PATCH; |
149 | |
150 | ret = max(ty0: ret,ty1: p->vertexType()); |
151 | bilinear &= p->bilinearVertex(); |
152 | if ((p = p->next()) != this) return COMPLEX_PATCH; |
153 | |
154 | if (bilinear) return BILINEAR_PATCH; |
155 | return ret; |
156 | } |
157 | |
158 | /*! tests if the face is a regular b-spline face */ |
159 | __forceinline bool isRegularFace() const { |
160 | return patch_type == REGULAR_QUAD_PATCH; |
161 | } |
162 | |
163 | /*! tests if the face can be diced (using bspline or gregory patch) */ |
164 | __forceinline bool isGregoryFace() const { |
165 | return patch_type == IRREGULAR_QUAD_PATCH || patch_type == REGULAR_QUAD_PATCH; |
166 | } |
167 | |
168 | /*! tests if the base vertex of this half edge is a corner vertex */ |
169 | __forceinline bool isCorner() const { |
170 | return !hasOpposite() && !prev()->hasOpposite(); |
171 | } |
172 | |
173 | /*! tests if the vertex is attached to any border */ |
174 | __forceinline bool vertexHasBorder() const |
175 | { |
176 | const HalfEdge* p = this; |
177 | do { |
178 | if (!p->hasOpposite()) return true; |
179 | p = p->rotate(); |
180 | } while (p != this); |
181 | return false; |
182 | } |
183 | |
184 | /*! tests if the face this half edge belongs to has some border */ |
185 | __forceinline bool faceHasBorder() const |
186 | { |
187 | const HalfEdge* p = this; |
188 | do { |
189 | if (p->vertexHasBorder() && (p->vertex_type != HalfEdge::NON_MANIFOLD_EDGE_VERTEX)) return true; |
190 | p = p->next(); |
191 | } while (p != this); |
192 | return false; |
193 | } |
194 | |
195 | /*! calculates conservative bounds of a catmull clark subdivision face */ |
196 | __forceinline BBox3fa bounds(const BufferView<Vec3fa>& vertices) const |
197 | { |
198 | BBox3fa bounds = this->get1RingBounds(vertices); |
199 | for (const HalfEdge* p=this->next(); p!=this; p=p->next()) |
200 | bounds.extend(other: p->get1RingBounds(vertices)); |
201 | return bounds; |
202 | } |
203 | |
204 | /*! tests if this is a valid patch */ |
205 | __forceinline bool valid(const BufferView<Vec3fa>& vertices) const |
206 | { |
207 | size_t N = 1; |
208 | if (!this->validRing(vertices)) return false; |
209 | for (const HalfEdge* p=this->next(); p!=this; p=p->next(), N++) { |
210 | if (!p->validRing(vertices)) return false; |
211 | } |
212 | return N >= 3 && N <= MAX_PATCH_VALENCE; |
213 | } |
214 | |
215 | /*! counts number of polygon edges */ |
216 | __forceinline unsigned int numEdges() const |
217 | { |
218 | unsigned int N = 1; |
219 | for (const HalfEdge* p=this->next(); p!=this; p=p->next(), N++); |
220 | return N; |
221 | } |
222 | |
223 | /*! calculates face and edge valence */ |
224 | __forceinline void calculateFaceValenceAndEdgeValence(size_t& faceValence, size_t& edgeValence) const |
225 | { |
226 | faceValence = 0; |
227 | edgeValence = 0; |
228 | |
229 | const HalfEdge* p = this; |
230 | do |
231 | { |
232 | /* calculate bounds of current face */ |
233 | unsigned int numEdges = p->numEdges(); |
234 | assert(numEdges >= 3); |
235 | edgeValence += numEdges-2; |
236 | |
237 | faceValence++; |
238 | p = p->prev(); |
239 | |
240 | /* continue with next face */ |
241 | if (likely(p->hasOpposite())) |
242 | p = p->opposite(); |
243 | |
244 | /* if there is no opposite go the long way to the other side of the border */ |
245 | else { |
246 | faceValence++; |
247 | edgeValence++; |
248 | p = this; |
249 | while (p->hasOpposite()) |
250 | p = p->opposite()->next(); |
251 | } |
252 | |
253 | } while (p != this); |
254 | } |
255 | |
256 | /*! stream output */ |
257 | friend __forceinline std::ostream &operator<<(std::ostream &o, const HalfEdge &h) |
258 | { |
259 | return o << "{ " << |
260 | "vertex = " << h.vtx_index << ", " << //" -> " << h.next()->vtx_index << ", " << |
261 | "prev = " << h.prev_half_edge_ofs << ", " << |
262 | "next = " << h.next_half_edge_ofs << ", " << |
263 | "opposite = " << h.opposite_half_edge_ofs << ", " << |
264 | "edge_crease = " << h.edge_crease_weight << ", " << |
265 | "vertex_crease = " << h.vertex_crease_weight << ", " << |
266 | //"edge_level = " << h.edge_level << |
267 | " }" ; |
268 | } |
269 | |
270 | private: |
271 | |
272 | /*! calculates the bounds of the face associated with the half-edge */ |
273 | __forceinline BBox3fa getFaceBounds(const BufferView<Vec3fa>& vertices) const |
274 | { |
275 | BBox3fa b = vertices[getStartVertexIndex()]; |
276 | for (const HalfEdge* p = next(); p!=this; p=p->next()) { |
277 | b.extend(other: vertices[p->getStartVertexIndex()]); |
278 | } |
279 | return b; |
280 | } |
281 | |
282 | /*! calculates the bounds of the 1-ring associated with the vertex of the half-edge */ |
283 | __forceinline BBox3fa get1RingBounds(const BufferView<Vec3fa>& vertices) const |
284 | { |
285 | BBox3fa bounds = empty; |
286 | const HalfEdge* p = this; |
287 | do |
288 | { |
289 | /* calculate bounds of current face */ |
290 | bounds.extend(other: p->getFaceBounds(vertices)); |
291 | p = p->prev(); |
292 | |
293 | /* continue with next face */ |
294 | if (likely(p->hasOpposite())) |
295 | p = p->opposite(); |
296 | |
297 | /* if there is no opposite go the long way to the other side of the border */ |
298 | else { |
299 | p = this; |
300 | while (p->hasOpposite()) |
301 | p = p->opposite()->next(); |
302 | } |
303 | |
304 | } while (p != this); |
305 | |
306 | return bounds; |
307 | } |
308 | |
309 | /*! tests if this is a valid face */ |
310 | __forceinline bool validFace(const BufferView<Vec3fa>& vertices, size_t& N) const |
311 | { |
312 | const Vec3fa v = vertices[getStartVertexIndex()]; |
313 | if (!isvalid(v)) return false; |
314 | size_t n = 1; |
315 | for (const HalfEdge* p = next(); p!=this; p=p->next(), n++) { |
316 | const Vec3fa v = vertices[p->getStartVertexIndex()]; |
317 | if (!isvalid(v)) return false; |
318 | } |
319 | N += n-2; |
320 | return n >= 3 && n <= MAX_PATCH_VALENCE; |
321 | } |
322 | |
323 | /*! tests if this is a valid ring */ |
324 | __forceinline bool validRing(const BufferView<Vec3fa>& vertices) const |
325 | { |
326 | size_t faceValence = 0; |
327 | size_t edgeValence = 0; |
328 | |
329 | const HalfEdge* p = this; |
330 | do |
331 | { |
332 | /* calculate bounds of current face */ |
333 | if (!p->validFace(vertices,N&: edgeValence)) |
334 | return false; |
335 | |
336 | faceValence++; |
337 | p = p->prev(); |
338 | |
339 | /* continue with next face */ |
340 | if (likely(p->hasOpposite())) |
341 | p = p->opposite(); |
342 | |
343 | /* if there is no opposite go the long way to the other side of the border */ |
344 | else { |
345 | faceValence++; |
346 | edgeValence++; |
347 | p = this; |
348 | while (p->hasOpposite()) |
349 | p = p->opposite()->next(); |
350 | } |
351 | |
352 | } while (p != this); |
353 | |
354 | return faceValence <= MAX_RING_FACE_VALENCE && edgeValence <= MAX_RING_EDGE_VALENCE; |
355 | } |
356 | |
357 | private: |
358 | unsigned int vtx_index; //!< index of edge start vertex |
359 | int next_half_edge_ofs; //!< relative offset to next half edge of face |
360 | int prev_half_edge_ofs; //!< relative offset to previous half edge of face |
361 | int opposite_half_edge_ofs; //!< relative offset to opposite half edge |
362 | |
363 | public: |
364 | float edge_crease_weight; //!< crease weight attached to edge |
365 | float vertex_crease_weight; //!< crease weight attached to start vertex |
366 | float edge_level; //!< subdivision factor for edge |
367 | PatchType patch_type; //!< stores type of subdiv patch |
368 | VertexType vertex_type; //!< stores type of the start vertex |
369 | char align[2]; |
370 | }; |
371 | } |
372 | |