Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/embree/kernels/geometry/curve_intersector_oriented.h
9905 views
1
// Copyright 2009-2021 Intel Corporation
2
// SPDX-License-Identifier: Apache-2.0
3
4
#pragma once
5
6
#include "../common/ray.h"
7
#include "curve_intersector_precalculations.h"
8
#include "curve_intersector_sweep.h"
9
#include "../subdiv/linear_bezier_patch.h"
10
11
#define DBG(x)
12
13
namespace embree
14
{
15
namespace isa
16
{
17
template<typename Ray, typename Epilog, int N = VSIZEX-1, int V = VSIZEX>
18
struct TensorLinearCubicBezierSurfaceIntersector
19
{
20
const LinearSpace3fa& ray_space;
21
Ray& ray;
22
TensorLinearCubicBezierSurface3fa curve3d;
23
TensorLinearCubicBezierSurface2fa curve2d;
24
float eps;
25
const Epilog& epilog;
26
bool isHit;
27
28
__forceinline TensorLinearCubicBezierSurfaceIntersector (const LinearSpace3fa& ray_space, Ray& ray, const TensorLinearCubicBezierSurface3fa& curve3d, const Epilog& epilog)
29
: ray_space(ray_space), ray(ray), curve3d(curve3d), epilog(epilog), isHit(false)
30
{
31
const TensorLinearCubicBezierSurface3fa curve3dray = curve3d.xfm(ray_space,ray.org);
32
curve2d = TensorLinearCubicBezierSurface2fa(CubicBezierCurve2fa(curve3dray.L),CubicBezierCurve2fa(curve3dray.R));
33
const BBox2fa b2 = curve2d.bounds();
34
eps = 8.0f*float(ulp)*reduce_max(max(abs(b2.lower),abs(b2.upper)));
35
}
36
37
__forceinline Interval1f solve_linear(const float u0, const float u1, const float& p0, const float& p1)
38
{
39
if (p1 == p0) {
40
if (p0 == 0.0f) return Interval1f(u0,u1);
41
else return Interval1f(empty);
42
}
43
const float t = -p0/(p1-p0);
44
const float tt = lerp(u0,u1,t);
45
return Interval1f(tt);
46
}
47
48
__forceinline void solve_linear(const float u0, const float u1, const Interval1f& p0, const Interval1f& p1, Interval1f& u)
49
{
50
if (sign(p0.lower) != sign(p0.upper)) u.extend(u0);
51
if (sign(p0.lower) != sign(p1.lower)) u.extend(solve_linear(u0,u1,p0.lower,p1.lower));
52
if (sign(p0.upper) != sign(p1.upper)) u.extend(solve_linear(u0,u1,p0.upper,p1.upper));
53
if (sign(p1.lower) != sign(p1.upper)) u.extend(u1);
54
}
55
56
__forceinline Interval1f bezier_clipping(const CubicBezierCurve<Interval1f>& curve)
57
{
58
Interval1f u = empty;
59
solve_linear(0.0f/3.0f,1.0f/3.0f,curve.v0,curve.v1,u);
60
solve_linear(0.0f/3.0f,2.0f/3.0f,curve.v0,curve.v2,u);
61
solve_linear(0.0f/3.0f,3.0f/3.0f,curve.v0,curve.v3,u);
62
solve_linear(1.0f/3.0f,2.0f/3.0f,curve.v1,curve.v2,u);
63
solve_linear(1.0f/3.0f,3.0f/3.0f,curve.v1,curve.v3,u);
64
solve_linear(2.0f/3.0f,3.0f/3.0f,curve.v2,curve.v3,u);
65
return intersect(u,Interval1f(0.0f,1.0f));
66
}
67
68
__forceinline Interval1f bezier_clipping(const LinearBezierCurve<Interval1f>& curve)
69
{
70
Interval1f v = empty;
71
solve_linear(0.0f,1.0f,curve.v0,curve.v1,v);
72
return intersect(v,Interval1f(0.0f,1.0f));
73
}
74
75
__forceinline void solve_bezier_clipping(BBox1f cu, BBox1f cv, const TensorLinearCubicBezierSurface2fa& curve2)
76
{
77
BBox2fa bounds = curve2.bounds();
78
if (bounds.upper.x < 0.0f) return;
79
if (bounds.upper.y < 0.0f) return;
80
if (bounds.lower.x > 0.0f) return;
81
if (bounds.lower.y > 0.0f) return;
82
83
if (max(cu.size(),cv.size()) < 1E-4f)
84
{
85
const float u = cu.center();
86
const float v = cv.center();
87
TensorLinearCubicBezierSurface1f curve_z = curve3d.xfm(ray_space.row2(),ray.org);
88
const float t = curve_z.eval(u,v);
89
if (ray.tnear() <= t && t <= ray.tfar) {
90
const Vec3fa Ng = cross(curve3d.eval_du(u,v),curve3d.eval_dv(u,v));
91
BezierCurveHit hit(t,u,v,Ng);
92
isHit |= epilog(hit);
93
}
94
return;
95
}
96
97
const Vec2fa dv = curve2.axis_v();
98
const TensorLinearCubicBezierSurface1f curve1v = curve2.xfm(dv);
99
LinearBezierCurve<Interval1f> curve0v = curve1v.reduce_u();
100
if (!curve0v.hasRoot()) return;
101
102
const Interval1f v = bezier_clipping(curve0v);
103
if (isEmpty(v)) return;
104
TensorLinearCubicBezierSurface2fa curve2a = curve2.clip_v(v);
105
cv = BBox1f(lerp(cv.lower,cv.upper,v.lower),lerp(cv.lower,cv.upper,v.upper));
106
107
const Vec2fa du = curve2.axis_u();
108
const TensorLinearCubicBezierSurface1f curve1u = curve2a.xfm(du);
109
CubicBezierCurve<Interval1f> curve0u = curve1u.reduce_v();
110
int roots = curve0u.maxRoots();
111
if (roots == 0) return;
112
113
if (roots == 1)
114
{
115
const Interval1f u = bezier_clipping(curve0u);
116
if (isEmpty(u)) return;
117
TensorLinearCubicBezierSurface2fa curve2b = curve2a.clip_u(u);
118
cu = BBox1f(lerp(cu.lower,cu.upper,u.lower),lerp(cu.lower,cu.upper,u.upper));
119
solve_bezier_clipping(cu,cv,curve2b);
120
return;
121
}
122
123
TensorLinearCubicBezierSurface2fa curve2l, curve2r;
124
curve2a.split_u(curve2l,curve2r);
125
solve_bezier_clipping(BBox1f(cu.lower,cu.center()),cv,curve2l);
126
solve_bezier_clipping(BBox1f(cu.center(),cu.upper),cv,curve2r);
127
}
128
129
__forceinline bool solve_bezier_clipping()
130
{
131
solve_bezier_clipping(BBox1f(0.0f,1.0f),BBox1f(0.0f,1.0f),curve2d);
132
return isHit;
133
}
134
135
__forceinline void solve_newton_raphson(BBox1f cu, BBox1f cv)
136
{
137
Vec2fa uv(cu.center(),cv.center());
138
const Vec2fa dfdu = curve2d.eval_du(uv.x,uv.y);
139
const Vec2fa dfdv = curve2d.eval_dv(uv.x,uv.y);
140
const LinearSpace2fa rcp_J = rcp(LinearSpace2fa(dfdu,dfdv));
141
solve_newton_raphson_loop(cu,cv,uv,dfdu,dfdv,rcp_J);
142
}
143
144
__forceinline void solve_newton_raphson_loop(BBox1f cu, BBox1f cv, const Vec2fa& uv_in, const Vec2fa& dfdu, const Vec2fa& dfdv, const LinearSpace2fa& rcp_J)
145
{
146
Vec2fa uv = uv_in;
147
148
for (size_t i=0; i<200; i++)
149
{
150
const Vec2fa f = curve2d.eval(uv.x,uv.y);
151
const Vec2fa duv = rcp_J*f;
152
uv -= duv;
153
154
if (max(abs(f.x),abs(f.y)) < eps)
155
{
156
const float u = uv.x;
157
const float v = uv.y;
158
if (!(u >= 0.0f && u <= 1.0f)) return; // rejects NaNs
159
if (!(v >= 0.0f && v <= 1.0f)) return; // rejects NaNs
160
const TensorLinearCubicBezierSurface1f curve_z = curve3d.xfm(ray_space.row2(),ray.org);
161
const float t = curve_z.eval(u,v);
162
if (!(ray.tnear() <= t && t <= ray.tfar)) return; // rejects NaNs
163
const Vec3fa Ng = cross(curve3d.eval_du(u,v),curve3d.eval_dv(u,v));
164
BezierCurveHit hit(t,u,v,Ng);
165
isHit |= epilog(hit);
166
return;
167
}
168
}
169
}
170
171
__forceinline bool clip_v(BBox1f& cu, BBox1f& cv)
172
{
173
const Vec2fa dv = curve2d.eval_dv(cu.lower,cv.lower);
174
const TensorLinearCubicBezierSurface1f curve1v = curve2d.xfm(dv).clip(cu,cv);
175
LinearBezierCurve<Interval1f> curve0v = curve1v.reduce_u();
176
if (!curve0v.hasRoot()) return false;
177
Interval1f v = bezier_clipping(curve0v);
178
if (isEmpty(v)) return false;
179
v = intersect(v + Interval1f(-0.1f,+0.1f),Interval1f(0.0f,1.0f));
180
cv = BBox1f(lerp(cv.lower,cv.upper,v.lower),lerp(cv.lower,cv.upper,v.upper));
181
return true;
182
}
183
184
__forceinline bool solve_krawczyk(bool very_small, BBox1f& cu, BBox1f& cv)
185
{
186
/* perform bezier clipping in v-direction to get tight v-bounds */
187
TensorLinearCubicBezierSurface2fa curve2 = curve2d.clip(cu,cv);
188
const Vec2fa dv = curve2.axis_v();
189
const TensorLinearCubicBezierSurface1f curve1v = curve2.xfm(dv);
190
LinearBezierCurve<Interval1f> curve0v = curve1v.reduce_u();
191
if (unlikely(!curve0v.hasRoot())) return true;
192
Interval1f v = bezier_clipping(curve0v);
193
if (unlikely(isEmpty(v))) return true;
194
v = intersect(v + Interval1f(-0.1f,+0.1f),Interval1f(0.0f,1.0f));
195
curve2 = curve2.clip_v(v);
196
cv = BBox1f(lerp(cv.lower,cv.upper,v.lower),lerp(cv.lower,cv.upper,v.upper));
197
198
/* perform one newton raphson iteration */
199
Vec2fa c(cu.center(),cv.center());
200
Vec2fa f,dfdu,dfdv; curve2d.eval(c.x,c.y,f,dfdu,dfdv);
201
const LinearSpace2fa rcp_J = rcp(LinearSpace2fa(dfdu,dfdv));
202
const Vec2fa c1 = c - rcp_J*f;
203
204
/* calculate bounds of derivatives */
205
const BBox2fa bounds_du = (1.0f/cu.size())*curve2.derivative_u().bounds();
206
const BBox2fa bounds_dv = (1.0f/cv.size())*curve2.derivative_v().bounds();
207
208
/* calculate krawczyk test */
209
LinearSpace2<Vec2<Interval1f>> I(Interval1f(1.0f), Interval1f(0.0f),
210
Interval1f(0.0f), Interval1f(1.0f));
211
212
LinearSpace2<Vec2<Interval1f>> G(Interval1f(bounds_du.lower.x,bounds_du.upper.x), Interval1f(bounds_dv.lower.x,bounds_dv.upper.x),
213
Interval1f(bounds_du.lower.y,bounds_du.upper.y), Interval1f(bounds_dv.lower.y,bounds_dv.upper.y));
214
215
const LinearSpace2<Vec2f> rcp_J2(rcp_J);
216
const LinearSpace2<Vec2<Interval1f>> rcp_Ji(rcp_J2);
217
218
const Vec2<Interval1f> x(cu,cv);
219
const Vec2<Interval1f> K = Vec2<Interval1f>(Vec2f(c1)) + (I - rcp_Ji*G)*(x-Vec2<Interval1f>(Vec2f(c)));
220
221
/* test if there is no solution */
222
const Vec2<Interval1f> KK = intersect(K,x);
223
if (unlikely(isEmpty(KK.x) || isEmpty(KK.y))) return true;
224
225
/* exit if convergence cannot get proven, but terminate if we are very small */
226
if (unlikely(!subset(K,x) && !very_small)) return false;
227
228
/* solve using newton raphson iteration of convergence is guaranteed */
229
solve_newton_raphson_loop(cu,cv,c1,dfdu,dfdv,rcp_J);
230
return true;
231
}
232
233
__forceinline void solve_newton_raphson_no_recursion(BBox1f cu, BBox1f cv)
234
{
235
if (!clip_v(cu,cv)) return;
236
return solve_newton_raphson(cu,cv);
237
}
238
239
__forceinline void solve_newton_raphson_recursion(BBox1f cu, BBox1f cv)
240
{
241
unsigned int sptr = 0;
242
const unsigned int stack_size = 4;
243
unsigned int mask_stack[stack_size];
244
BBox1f cu_stack[stack_size];
245
BBox1f cv_stack[stack_size];
246
goto entry;
247
248
/* terminate if stack is empty */
249
while (sptr)
250
{
251
/* pop from stack */
252
{
253
sptr--;
254
size_t mask = mask_stack[sptr];
255
cu = cu_stack[sptr];
256
cv = cv_stack[sptr];
257
const size_t i = bscf(mask);
258
mask_stack[sptr] = mask;
259
if (mask) sptr++; // there are still items on the stack
260
261
/* process next element recurse into each hit curve segment */
262
const float u0 = float(i+0)*(1.0f/(N));
263
const float u1 = float(i+1)*(1.0f/(N));
264
const BBox1f cui(lerp(cu.lower,cu.upper,u0),lerp(cu.lower,cu.upper,u1));
265
cu = cui;
266
}
267
268
#if 0
269
solve_newton_raphson_no_recursion(cu,cv);
270
continue;
271
272
#else
273
/* we assume convergence for small u ranges and verify using krawczyk */
274
if (cu.size() < 1.0f/6.0f) {
275
const bool very_small = cu.size() < 0.001f || sptr >= stack_size;
276
if (solve_krawczyk(very_small,cu,cv)) {
277
continue;
278
}
279
}
280
#endif
281
282
entry:
283
284
/* split the curve into N segments in u-direction */
285
unsigned int mask = 0;
286
for (int i=0; i<N;)
287
{
288
int i0 = i;
289
vbool<V> valid = true;
290
TensorLinearCubicBezierSurface<Vec2vf<V>> subcurves = curve2d.clip_v(cv).template vsplit_u<V>(valid,cu,i,N);
291
292
/* slabs test in u-direction */
293
Vec2vf<V> ndv = cross(subcurves.axis_v());
294
BBox<vfloat<V>> boundsv = subcurves.template vxfm<V>(ndv).bounds();
295
valid &= boundsv.lower <= eps;
296
valid &= boundsv.upper >= -eps;
297
if (none(valid)) continue;
298
299
/* slabs test in v-direction */
300
Vec2vf<V> ndu = cross(subcurves.axis_u());
301
BBox<vfloat<V>> boundsu = subcurves.template vxfm<V>(ndu).bounds();
302
valid &= boundsu.lower <= eps;
303
valid &= boundsu.upper >= -eps;
304
if (none(valid)) continue;
305
306
mask |= movemask(valid) << i0;
307
}
308
309
if (!mask) continue;
310
311
/* push valid segments to stack */
312
assert(sptr < stack_size);
313
mask_stack [sptr] = mask;
314
cu_stack [sptr] = cu;
315
cv_stack [sptr] = cv;
316
sptr++;
317
}
318
}
319
320
__forceinline bool solve_newton_raphson_main()
321
{
322
BBox1f vu(0.0f,1.0f);
323
BBox1f vv(0.0f,1.0f);
324
solve_newton_raphson_recursion(vu,vv);
325
return isHit;
326
}
327
};
328
329
330
template<template<typename Ty> class SourceCurve, int N = VSIZEX-1, int V = VSIZEX>
331
struct OrientedCurve1Intersector1
332
{
333
//template<typename Ty> using Curve = SourceCurve<Ty>;
334
typedef SourceCurve<Vec3ff> SourceCurve3ff;
335
typedef SourceCurve<Vec3fa> SourceCurve3fa;
336
337
__forceinline OrientedCurve1Intersector1() {}
338
339
__forceinline OrientedCurve1Intersector1(const Ray& ray, const void* ptr) {}
340
341
template<typename Ray, typename Epilog>
342
__forceinline bool intersect(const CurvePrecalculations1& pre, Ray& ray,
343
RayQueryContext* context,
344
const CurveGeometry* geom, const unsigned int primID,
345
const Vec3ff& v0i, const Vec3ff& v1i, const Vec3ff& v2i, const Vec3ff& v3i,
346
const Vec3fa& n0i, const Vec3fa& n1i, const Vec3fa& n2i, const Vec3fa& n3i,
347
const Epilog& epilog) const
348
{
349
STAT3(normal.trav_prims,1,1,1);
350
SourceCurve3ff ccurve(v0i,v1i,v2i,v3i);
351
SourceCurve3fa ncurve(n0i,n1i,n2i,n3i);
352
ccurve = enlargeRadiusToMinWidth(context,geom,ray.org,ccurve);
353
TensorLinearCubicBezierSurface3fa curve = TensorLinearCubicBezierSurface3fa::fromCenterAndNormalCurve(ccurve,ncurve);
354
//return TensorLinearCubicBezierSurfaceIntersector<Ray,Epilog>(pre.ray_space,ray,curve,epilog).solve_bezier_clipping();
355
return TensorLinearCubicBezierSurfaceIntersector<Ray,Epilog,N,V>(pre.ray_space,ray,curve,epilog).solve_newton_raphson_main();
356
}
357
358
template<typename Ray, typename Epilog>
359
__forceinline bool intersect(const CurvePrecalculations1& pre, Ray& ray,
360
RayQueryContext* context,
361
const CurveGeometry* geom, const unsigned int primID,
362
const TensorLinearCubicBezierSurface3fa& curve, const Epilog& epilog) const
363
{
364
STAT3(normal.trav_prims,1,1,1);
365
//return TensorLinearCubicBezierSurfaceIntersector<Ray,Epilog>(pre.ray_space,ray,curve,epilog).solve_bezier_clipping();
366
return TensorLinearCubicBezierSurfaceIntersector<Ray,Epilog,N,V>(pre.ray_space,ray,curve,epilog).solve_newton_raphson_main();
367
}
368
};
369
370
template<template<typename Ty> class SourceCurve, int K>
371
struct OrientedCurve1IntersectorK
372
{
373
//template<typename Ty> using Curve = SourceCurve<Ty>;
374
typedef SourceCurve<Vec3ff> SourceCurve3ff;
375
typedef SourceCurve<Vec3fa> SourceCurve3fa;
376
377
struct Ray1
378
{
379
__forceinline Ray1(RayK<K>& ray, size_t k)
380
: org(ray.org.x[k],ray.org.y[k],ray.org.z[k]), dir(ray.dir.x[k],ray.dir.y[k],ray.dir.z[k]), _tnear(ray.tnear()[k]), tfar(ray.tfar[k]) {}
381
382
Vec3fa org;
383
Vec3fa dir;
384
float _tnear;
385
float& tfar;
386
387
__forceinline float& tnear() { return _tnear; }
388
//__forceinline float& tfar() { return _tfar; }
389
__forceinline const float& tnear() const { return _tnear; }
390
//__forceinline const float& tfar() const { return _tfar; }
391
};
392
393
template<typename Epilog>
394
__forceinline bool intersect(const CurvePrecalculationsK<K>& pre, RayK<K>& vray, size_t k,
395
RayQueryContext* context,
396
const CurveGeometry* geom, const unsigned int primID,
397
const Vec3ff& v0i, const Vec3ff& v1i, const Vec3ff& v2i, const Vec3ff& v3i,
398
const Vec3fa& n0i, const Vec3fa& n1i, const Vec3fa& n2i, const Vec3fa& n3i,
399
const Epilog& epilog)
400
{
401
STAT3(normal.trav_prims,1,1,1);
402
Ray1 ray(vray,k);
403
SourceCurve3ff ccurve(v0i,v1i,v2i,v3i);
404
SourceCurve3fa ncurve(n0i,n1i,n2i,n3i);
405
ccurve = enlargeRadiusToMinWidth(context,geom,ray.org,ccurve);
406
TensorLinearCubicBezierSurface3fa curve = TensorLinearCubicBezierSurface3fa::fromCenterAndNormalCurve(ccurve,ncurve);
407
//return TensorLinearCubicBezierSurfaceIntersector<Ray1,Epilog>(pre.ray_space[k],ray,curve,epilog).solve_bezier_clipping();
408
return TensorLinearCubicBezierSurfaceIntersector<Ray1,Epilog>(pre.ray_space[k],ray,curve,epilog).solve_newton_raphson_main();
409
}
410
411
template<typename Epilog>
412
__forceinline bool intersect(const CurvePrecalculationsK<K>& pre, RayK<K>& vray, size_t k,
413
RayQueryContext* context,
414
const CurveGeometry* geom, const unsigned int primID,
415
const TensorLinearCubicBezierSurface3fa& curve,
416
const Epilog& epilog)
417
{
418
STAT3(normal.trav_prims,1,1,1);
419
Ray1 ray(vray,k);
420
//return TensorLinearCubicBezierSurfaceIntersector<Ray1,Epilog>(pre.ray_space[k],ray,curve,epilog).solve_bezier_clipping();
421
return TensorLinearCubicBezierSurfaceIntersector<Ray1,Epilog>(pre.ray_space[k],ray,curve,epilog).solve_newton_raphson_main();
422
}
423
};
424
}
425
}
426
427