1 | // Copyright 2009-2021 Intel Corporation |
2 | // SPDX-License-Identifier: Apache-2.0 |
3 | |
4 | #pragma once |
5 | |
6 | #include "patch.h" |
7 | #include "catmullclark_patch.h" |
8 | #include "bspline_patch.h" |
9 | #include "gregory_patch.h" |
10 | #include "tessellation.h" |
11 | |
12 | namespace embree |
13 | { |
14 | namespace isa |
15 | { |
16 | struct FeatureAdaptiveEvalGrid |
17 | { |
18 | typedef CatmullClark1Ring3fa CatmullClarkRing; |
19 | typedef CatmullClarkPatch3fa CatmullClarkPatch; |
20 | typedef BilinearPatch3fa BilinearPatch; |
21 | typedef BSplinePatch3fa BSplinePatch; |
22 | typedef BezierPatch3fa BezierPatch; |
23 | typedef GregoryPatch3fa GregoryPatch; |
24 | |
25 | private: |
26 | const unsigned x0,x1; |
27 | const unsigned y0,y1; |
28 | const unsigned swidth,sheight; |
29 | const float rcp_swidth, rcp_sheight; |
30 | float* const Px; |
31 | float* const Py; |
32 | float* const Pz; |
33 | float* const U; |
34 | float* const V; |
35 | float* const Nx; |
36 | float* const Ny; |
37 | float* const Nz; |
38 | const unsigned dwidth; |
39 | //const unsigned dheight; |
40 | unsigned count; |
41 | |
42 | |
43 | public: |
44 | FeatureAdaptiveEvalGrid (const GeneralCatmullClarkPatch3fa& patch, unsigned subPatch, |
45 | const unsigned x0, const unsigned x1, const unsigned y0, const unsigned y1, const unsigned swidth, const unsigned sheight, |
46 | float* Px, float* Py, float* Pz, float* U, float* V, |
47 | float* Nx, float* Ny, float* Nz, |
48 | const unsigned dwidth, const unsigned dheight) |
49 | : x0(x0), x1(x1), y0(y0), y1(y1), swidth(swidth), sheight(sheight), rcp_swidth(1.0f/(swidth-1.0f)), rcp_sheight(1.0f/(sheight-1.0f)), |
50 | Px(Px), Py(Py), Pz(Pz), U(U), V(V), Nx(Nx), Ny(Ny), Nz(Nz), dwidth(dwidth), /*dheight(dheight),*/ count(0) |
51 | { |
52 | assert(swidth < (2<<20) && sheight < (2<<20)); |
53 | const BBox2f srange(Vec2f(0.0f,0.0f),Vec2f(float(swidth-1),float(sheight-1))); |
54 | const BBox2f erange(Vec2f((float)x0,(float)y0),Vec2f((float)x1,(float)y1)); |
55 | |
56 | /* convert into standard quad patch if possible */ |
57 | if (likely(patch.isQuadPatch())) |
58 | { |
59 | CatmullClarkPatch3fa qpatch; patch.init(patch&: qpatch); |
60 | eval(patch: qpatch, srange, erange, depth: 0); |
61 | assert(count == (x1-x0+1)*(y1-y0+1)); |
62 | return; |
63 | } |
64 | |
65 | /* subdivide patch */ |
66 | unsigned N; |
67 | array_t<CatmullClarkPatch3fa,GeneralCatmullClarkPatch3fa::SIZE> patches; |
68 | patch.subdivide(patch&: patches,N_o&: N); |
69 | |
70 | if (N == 4) |
71 | { |
72 | const Vec2f c = srange.center(); |
73 | const BBox2f srange0(srange.lower,c); |
74 | const BBox2f srange1(Vec2f(c.x,srange.lower.y),Vec2f(srange.upper.x,c.y)); |
75 | const BBox2f srange2(c,srange.upper); |
76 | const BBox2f srange3(Vec2f(srange.lower.x,c.y),Vec2f(c.x,srange.upper.y)); |
77 | |
78 | #if PATCH_USE_GREGORY == 2 |
79 | BezierCurve3fa borders[GeneralCatmullClarkPatch3fa::SIZE]; patch.getLimitBorder(borders); |
80 | BezierCurve3fa border0l,border0r; borders[0].subdivide(border0l,border0r); |
81 | BezierCurve3fa border1l,border1r; borders[1].subdivide(border1l,border1r); |
82 | BezierCurve3fa border2l,border2r; borders[2].subdivide(border2l,border2r); |
83 | BezierCurve3fa border3l,border3r; borders[3].subdivide(border3l,border3r); |
84 | GeneralCatmullClarkPatch3fa::fix_quad_ring_order(patches); |
85 | eval(patches[0],srange0,intersect(srange0,erange),1,&border0l,nullptr,nullptr,&border3r); |
86 | eval(patches[1],srange1,intersect(srange1,erange),1,&border0r,&border1l,nullptr,nullptr); |
87 | eval(patches[2],srange2,intersect(srange2,erange),1,nullptr,&border1r,&border2l,nullptr); |
88 | eval(patches[3],srange3,intersect(srange3,erange),1,nullptr,nullptr,&border2r,&border3l); |
89 | #else |
90 | GeneralCatmullClarkPatch3fa::fix_quad_ring_order(patches); |
91 | eval(patch: patches[0],srange: srange0,erange: intersect(a: srange0,b: erange),depth: 1); |
92 | eval(patch: patches[1],srange: srange1,erange: intersect(a: srange1,b: erange),depth: 1); |
93 | eval(patch: patches[2],srange: srange2,erange: intersect(a: srange2,b: erange),depth: 1); |
94 | eval(patch: patches[3],srange: srange3,erange: intersect(a: srange3,b: erange),depth: 1); |
95 | #endif |
96 | } |
97 | else |
98 | { |
99 | assert(subPatch < N); |
100 | |
101 | #if PATCH_USE_GREGORY == 2 |
102 | BezierCurve3fa borders[2]; patch.getLimitBorder(borders,subPatch); |
103 | BezierCurve3fa border0l,border0r; borders[0].subdivide(border0l,border0r); |
104 | BezierCurve3fa border2l,border2r; borders[1].subdivide(border2l,border2r); |
105 | eval(patches[subPatch], srange, erange, 1, &border0l, nullptr, nullptr, &border2r); |
106 | #else |
107 | eval(patch: patches[subPatch], srange, erange, depth: 1); |
108 | #endif |
109 | |
110 | } |
111 | assert(count == (x1-x0+1)*(y1-y0+1)); |
112 | } |
113 | |
114 | FeatureAdaptiveEvalGrid (const CatmullClarkPatch3fa& patch, |
115 | const BBox2f& srange, const BBox2f& erange, const unsigned depth, |
116 | const unsigned x0, const unsigned x1, const unsigned y0, const unsigned y1, const unsigned swidth, const unsigned sheight, |
117 | float* Px, float* Py, float* Pz, float* U, float* V, |
118 | float* Nx, float* Ny, float* Nz, |
119 | const unsigned dwidth, const unsigned dheight) |
120 | : x0(x0), x1(x1), y0(y0), y1(y1), swidth(swidth), sheight(sheight), rcp_swidth(1.0f/(swidth-1.0f)), rcp_sheight(1.0f/(sheight-1.0f)), |
121 | Px(Px), Py(Py), Pz(Pz), U(U), V(V), Nx(Nx), Ny(Ny), Nz(Nz), dwidth(dwidth), /*dheight(dheight),*/ count(0) |
122 | { |
123 | eval(patch,srange,erange,depth); |
124 | } |
125 | |
126 | template<typename Patch> |
127 | void evalLocalGrid(const Patch& patch, const BBox2f& srange, const int lx0, const int lx1, const int ly0, const int ly1) |
128 | { |
129 | const float scale_x = rcp(x: srange.upper.x-srange.lower.x); |
130 | const float scale_y = rcp(x: srange.upper.y-srange.lower.y); |
131 | count += (lx1-lx0)*(ly1-ly0); |
132 | |
133 | #if 0 |
134 | for (unsigned iy=ly0; iy<ly1; iy++) { |
135 | for (unsigned ix=lx0; ix<lx1; ix++) { |
136 | const float lu = select(ix == swidth -1, float(1.0f), (float(ix)-srange.lower.x)*scale_x); |
137 | const float lv = select(iy == sheight-1, float(1.0f), (float(iy)-srange.lower.y)*scale_y); |
138 | const Vec3fa p = patch.eval(lu,lv); |
139 | const float u = float(ix)*rcp_swidth; |
140 | const float v = float(iy)*rcp_sheight; |
141 | const int ofs = (iy-y0)*dwidth+(ix-x0); |
142 | Px[ofs] = p.x; |
143 | Py[ofs] = p.y; |
144 | Pz[ofs] = p.z; |
145 | U[ofs] = u; |
146 | V[ofs] = v; |
147 | } |
148 | } |
149 | #else |
150 | foreach2(lx0,lx1,ly0,ly1,[&](const vboolx& valid, const vintx& ix, const vintx& iy) { |
151 | const vfloatx lu = select(m: ix == swidth -1, t: vfloatx(1.0f), f: (vfloatx(ix)-srange.lower.x)*scale_x); |
152 | const vfloatx lv = select(m: iy == sheight-1, t: vfloatx(1.0f), f: (vfloatx(iy)-srange.lower.y)*scale_y); |
153 | const Vec3vfx p = patch.eval(lu,lv); |
154 | Vec3vfx n = zero; |
155 | if (unlikely(Nx != nullptr)) n = normalize_safe(patch.normal(lu,lv)); |
156 | const vfloatx u = vfloatx(ix)*rcp_swidth; |
157 | const vfloatx v = vfloatx(iy)*rcp_sheight; |
158 | const vintx ofs = (iy-y0)*dwidth+(ix-x0); |
159 | if (likely(all(valid)) && all(b: iy==iy[0])) { |
160 | const unsigned ofs2 = ofs[0]; |
161 | vfloatx::storeu(ptr: Px+ofs2,v: p.x); |
162 | vfloatx::storeu(ptr: Py+ofs2,v: p.y); |
163 | vfloatx::storeu(ptr: Pz+ofs2,v: p.z); |
164 | vfloatx::storeu(ptr: U+ofs2,v: u); |
165 | vfloatx::storeu(ptr: V+ofs2,v); |
166 | if (unlikely(Nx != nullptr)) { |
167 | vfloatx::storeu(ptr: Nx+ofs2,v: n.x); |
168 | vfloatx::storeu(ptr: Ny+ofs2,v: n.y); |
169 | vfloatx::storeu(ptr: Nz+ofs2,v: n.z); |
170 | } |
171 | } else { |
172 | foreach_unique_index(valid,iy,[&](const vboolx& valid, const int iy0, const int j) { |
173 | const unsigned ofs2 = ofs[j]-j; |
174 | vfloatx::storeu(mask: valid,ptr: Px+ofs2,v: p.x); |
175 | vfloatx::storeu(mask: valid,ptr: Py+ofs2,v: p.y); |
176 | vfloatx::storeu(mask: valid,ptr: Pz+ofs2,v: p.z); |
177 | vfloatx::storeu(mask: valid,ptr: U+ofs2,v: u); |
178 | vfloatx::storeu(mask: valid,ptr: V+ofs2,v); |
179 | if (unlikely(Nx != nullptr)) { |
180 | vfloatx::storeu(mask: valid,ptr: Nx+ofs2,v: n.x); |
181 | vfloatx::storeu(mask: valid,ptr: Ny+ofs2,v: n.y); |
182 | vfloatx::storeu(mask: valid,ptr: Nz+ofs2,v: n.z); |
183 | } |
184 | }); |
185 | } |
186 | }); |
187 | #endif |
188 | } |
189 | |
190 | __forceinline bool final(const CatmullClarkPatch3fa& patch, const CatmullClarkRing::Type type, unsigned depth) |
191 | { |
192 | const unsigned max_eval_depth = (type & CatmullClarkRing::TYPE_CREASES) ? PATCH_MAX_EVAL_DEPTH_CREASE : PATCH_MAX_EVAL_DEPTH_IRREGULAR; |
193 | //#if PATCH_MIN_RESOLUTION |
194 | // return patch.isFinalResolution(PATCH_MIN_RESOLUTION) || depth>=max_eval_depth; |
195 | //#else |
196 | return depth>=max_eval_depth; |
197 | //#endif |
198 | } |
199 | |
200 | void eval(const CatmullClarkPatch3fa& patch, const BBox2f& srange, const BBox2f& erange, const unsigned depth, |
201 | const BezierCurve3fa* border0 = nullptr, const BezierCurve3fa* border1 = nullptr, const BezierCurve3fa* border2 = nullptr, const BezierCurve3fa* border3 = nullptr) |
202 | { |
203 | if (erange.empty()) |
204 | return; |
205 | |
206 | int lx0 = (int) ceilf(x: erange.lower.x); |
207 | int lx1 = (int) ceilf(x: erange.upper.x) + (erange.upper.x == x1 && (srange.lower.x < erange.upper.x || erange.upper.x == 0)); |
208 | int ly0 = (int) ceilf(x: erange.lower.y); |
209 | int ly1 = (int) ceilf(x: erange.upper.y) + (erange.upper.y == y1 && (srange.lower.y < erange.upper.y || erange.upper.y == 0)); |
210 | if (lx0 >= lx1 || ly0 >= ly1) return; |
211 | |
212 | CatmullClarkPatch::Type ty = patch.type(); |
213 | |
214 | if (unlikely(final(patch,ty,depth))) |
215 | { |
216 | if (ty & CatmullClarkRing::TYPE_REGULAR) { |
217 | RegularPatch rpatch(patch,border0,border1,border2,border3); |
218 | evalLocalGrid(patch: rpatch,srange,lx0,lx1,ly0,ly1); |
219 | return; |
220 | } else { |
221 | IrregularFillPatch ipatch(patch,border0,border1,border2,border3); |
222 | evalLocalGrid(patch: ipatch,srange,lx0,lx1,ly0,ly1); |
223 | return; |
224 | } |
225 | } |
226 | else if (ty & CatmullClarkRing::TYPE_REGULAR_CREASES) { |
227 | assert(depth > 0); |
228 | RegularPatch rpatch(patch,border0,border1,border2,border3); |
229 | evalLocalGrid(patch: rpatch,srange,lx0,lx1,ly0,ly1); |
230 | return; |
231 | } |
232 | #if PATCH_USE_GREGORY == 2 |
233 | else if (ty & CatmullClarkRing::TYPE_GREGORY_CREASES) { |
234 | assert(depth > 0); |
235 | GregoryPatch gpatch(patch,border0,border1,border2,border3); |
236 | evalLocalGrid(gpatch,srange,lx0,lx1,ly0,ly1); |
237 | } |
238 | #endif |
239 | else |
240 | { |
241 | array_t<CatmullClarkPatch3fa,4> patches; |
242 | patch.subdivide(patch&: patches); |
243 | |
244 | const Vec2f c = srange.center(); |
245 | const BBox2f srange0(srange.lower,c); |
246 | const BBox2f srange1(Vec2f(c.x,srange.lower.y),Vec2f(srange.upper.x,c.y)); |
247 | const BBox2f srange2(c,srange.upper); |
248 | const BBox2f srange3(Vec2f(srange.lower.x,c.y),Vec2f(c.x,srange.upper.y)); |
249 | |
250 | eval(patch: patches[0],srange: srange0,erange: intersect(a: srange0,b: erange),depth: depth+1); |
251 | eval(patch: patches[1],srange: srange1,erange: intersect(a: srange1,b: erange),depth: depth+1); |
252 | eval(patch: patches[2],srange: srange2,erange: intersect(a: srange2,b: erange),depth: depth+1); |
253 | eval(patch: patches[3],srange: srange3,erange: intersect(a: srange3,b: erange),depth: depth+1); |
254 | } |
255 | } |
256 | }; |
257 | |
258 | template<typename Eval, typename Patch> |
259 | bool stitch_col(const Patch& patch, int subPatch, |
260 | const bool right, const unsigned y0, const unsigned y1, const int fine_y, const int coarse_y, |
261 | float* Px, float* Py, float* Pz, float* U, float* V, float* Nx, float* Ny, float* Nz, const unsigned dx0, const unsigned dwidth, const unsigned dheight) |
262 | { |
263 | assert(coarse_y <= fine_y); |
264 | if (likely(fine_y == coarse_y)) |
265 | return false; |
266 | |
267 | const unsigned y0s = stitch(x: y0,fine: fine_y,coarse: coarse_y); |
268 | const unsigned y1s = stitch(x: y1,fine: fine_y,coarse: coarse_y); |
269 | const unsigned M = y1s-y0s+1 + VSIZEX; |
270 | |
271 | dynamic_large_stack_array(float,px,M,64*sizeof(float)); |
272 | dynamic_large_stack_array(float,py,M,64*sizeof(float)); |
273 | dynamic_large_stack_array(float,pz,M,64*sizeof(float)); |
274 | dynamic_large_stack_array(float,u,M,64*sizeof(float)); |
275 | dynamic_large_stack_array(float,v,M,64*sizeof(float)); |
276 | dynamic_large_stack_array(float,nx,M,64*sizeof(float)); |
277 | dynamic_large_stack_array(float,ny,M,64*sizeof(float)); |
278 | dynamic_large_stack_array(float,nz,M,64*sizeof(float)); |
279 | const bool has_Nxyz = Nx; assert(!Nx || (Ny && Nz)); |
280 | Eval(patch,subPatch, right,right, y0s,y1s, 2,coarse_y+1, px,py,pz,u,v, |
281 | has_Nxyz ? (float*)nx : nullptr,has_Nxyz ? (float*)ny : nullptr ,has_Nxyz ? (float*)nz : nullptr, 1,4097); |
282 | |
283 | for (unsigned y=y0; y<=y1; y++) |
284 | { |
285 | const unsigned ys = stitch(x: y,fine: fine_y,coarse: coarse_y)-y0s; |
286 | Px[(y-y0)*dwidth+dx0] = px[ys]; |
287 | Py[(y-y0)*dwidth+dx0] = py[ys]; |
288 | Pz[(y-y0)*dwidth+dx0] = pz[ys]; |
289 | U [(y-y0)*dwidth+dx0] = u[ys]; |
290 | V [(y-y0)*dwidth+dx0] = v[ys]; |
291 | if (unlikely(has_Nxyz)) { |
292 | Nx[(y-y0)*dwidth+dx0] = nx[ys]; |
293 | Ny[(y-y0)*dwidth+dx0] = ny[ys]; |
294 | Nz[(y-y0)*dwidth+dx0] = nz[ys]; |
295 | } |
296 | } |
297 | return true; |
298 | } |
299 | |
300 | template<typename Eval, typename Patch> |
301 | bool stitch_row(const Patch& patch, int subPatch, |
302 | const bool bottom, const unsigned x0, const unsigned x1, const int fine_x, const int coarse_x, |
303 | float* Px, float* Py, float* Pz, float* U, float* V, float* Nx, float* Ny, float* Nz, const unsigned dy0, const unsigned dwidth, const unsigned dheight) |
304 | { |
305 | assert(coarse_x <= fine_x); |
306 | if (likely(fine_x == coarse_x)) |
307 | return false; |
308 | |
309 | const unsigned x0s = stitch(x: x0,fine: fine_x,coarse: coarse_x); |
310 | const unsigned x1s = stitch(x: x1,fine: fine_x,coarse: coarse_x); |
311 | const unsigned M = x1s-x0s+1 + VSIZEX; |
312 | |
313 | dynamic_large_stack_array(float,px,M,32*sizeof(float)); |
314 | dynamic_large_stack_array(float,py,M,32*sizeof(float)); |
315 | dynamic_large_stack_array(float,pz,M,32*sizeof(float)); |
316 | dynamic_large_stack_array(float,u,M,32*sizeof(float)); |
317 | dynamic_large_stack_array(float,v,M,32*sizeof(float)); |
318 | dynamic_large_stack_array(float,nx,M,32*sizeof(float)); |
319 | dynamic_large_stack_array(float,ny,M,32*sizeof(float)); |
320 | dynamic_large_stack_array(float,nz,M,32*sizeof(float)); |
321 | const bool has_Nxyz = Nx; assert(!Nx || (Ny && Nz)); |
322 | Eval(patch,subPatch, x0s,x1s, bottom,bottom, coarse_x+1,2, px,py,pz,u,v, |
323 | has_Nxyz ? (float*)nx :nullptr, has_Nxyz ? (float*)ny : nullptr , has_Nxyz ? (float*)nz : nullptr, 4097,1); |
324 | |
325 | for (unsigned x=x0; x<=x1; x++) |
326 | { |
327 | const unsigned xs = stitch(x,fine: fine_x,coarse: coarse_x)-x0s; |
328 | Px[dy0*dwidth+x-x0] = px[xs]; |
329 | Py[dy0*dwidth+x-x0] = py[xs]; |
330 | Pz[dy0*dwidth+x-x0] = pz[xs]; |
331 | U [dy0*dwidth+x-x0] = u[xs]; |
332 | V [dy0*dwidth+x-x0] = v[xs]; |
333 | if (unlikely(has_Nxyz)) { |
334 | Nx[dy0*dwidth+x-x0] = nx[xs]; |
335 | Ny[dy0*dwidth+x-x0] = ny[xs]; |
336 | Nz[dy0*dwidth+x-x0] = nz[xs]; |
337 | } |
338 | } |
339 | return true; |
340 | } |
341 | |
342 | template<typename Eval, typename Patch> |
343 | void feature_adaptive_eval_grid (const Patch& patch, unsigned subPatch, const float levels[4], |
344 | const unsigned x0, const unsigned x1, const unsigned y0, const unsigned y1, const unsigned swidth, const unsigned sheight, |
345 | float* Px, float* Py, float* Pz, float* U, float* V, float* Nx, float* Ny, float* Nz, const unsigned dwidth, const unsigned dheight) |
346 | { |
347 | bool sl = false, sr = false, st = false, sb = false; |
348 | if (levels) { |
349 | sl = x0 == 0 && stitch_col<Eval,Patch>(patch,subPatch,0,y0,y1,sheight-1,int(levels[3]), Px,Py,Pz,U,V,Nx,Ny,Nz, 0 ,dwidth,dheight); |
350 | sr = x1 == swidth-1 && stitch_col<Eval,Patch>(patch,subPatch,1,y0,y1,sheight-1,int(levels[1]), Px,Py,Pz,U,V,Nx,Ny,Nz, x1-x0,dwidth,dheight); |
351 | st = y0 == 0 && stitch_row<Eval,Patch>(patch,subPatch,0,x0,x1,swidth-1,int(levels[0]), Px,Py,Pz,U,V,Nx,Ny,Nz, 0 ,dwidth,dheight); |
352 | sb = y1 == sheight-1 && stitch_row<Eval,Patch>(patch,subPatch,1,x0,x1,swidth-1,int(levels[2]), Px,Py,Pz,U,V,Nx,Ny,Nz, y1-y0,dwidth,dheight); |
353 | } |
354 | const unsigned ofs = st*dwidth+sl; |
355 | Eval(patch,subPatch,x0+sl,x1-sr,y0+st,y1-sb, swidth,sheight, Px+ofs,Py+ofs,Pz+ofs,U+ofs,V+ofs,Nx?Nx+ofs:nullptr,Ny?Ny+ofs:nullptr,Nz?Nz+ofs:nullptr, dwidth,dheight); |
356 | } |
357 | } |
358 | } |
359 | |
360 | |