1 | // Copyright 2009-2021 Intel Corporation |
2 | // SPDX-License-Identifier: Apache-2.0 |
3 | |
4 | #pragma once |
5 | |
6 | #include "patch.h" |
7 | #include "feature_adaptive_eval_grid.h" |
8 | |
9 | namespace embree |
10 | { |
11 | namespace isa |
12 | { |
13 | struct PatchEvalGrid |
14 | { |
15 | typedef Patch3fa Patch; |
16 | typedef Patch::Ref Ref; |
17 | typedef GeneralCatmullClarkPatch3fa GeneralCatmullClarkPatch; |
18 | typedef CatmullClarkPatch3fa CatmullClarkPatch; |
19 | typedef BSplinePatch3fa BSplinePatch; |
20 | typedef BezierPatch3fa BezierPatch; |
21 | typedef GregoryPatch3fa GregoryPatch; |
22 | typedef BilinearPatch3fa BilinearPatch; |
23 | |
24 | private: |
25 | const unsigned x0,x1; |
26 | const unsigned y0,y1; |
27 | const unsigned swidth,sheight; |
28 | const float rcp_swidth, rcp_sheight; |
29 | float* const Px; |
30 | float* const Py; |
31 | float* const Pz; |
32 | float* const U; |
33 | float* const V; |
34 | float* const Nx; |
35 | float* const Ny; |
36 | float* const Nz; |
37 | const unsigned dwidth,dheight; |
38 | unsigned count; |
39 | |
40 | public: |
41 | |
42 | PatchEvalGrid (Ref patch, unsigned subPatch, |
43 | const unsigned x0, const unsigned x1, const unsigned y0, const unsigned y1, const unsigned swidth, const unsigned sheight, |
44 | float* Px, float* Py, float* Pz, float* U, float* V, |
45 | float* Nx, float* Ny, float* Nz, |
46 | const unsigned dwidth, const unsigned dheight) |
47 | : 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)), |
48 | Px(Px), Py(Py), Pz(Pz), U(U), V(V), Nx(Nx), Ny(Ny), Nz(Nz), dwidth(dwidth), dheight(dheight), count(0) |
49 | { |
50 | assert(swidth < (2<<20) && sheight < (2<<20)); |
51 | const BBox2f srange(Vec2f(0.0f,0.0f),Vec2f(float(swidth-1),float(sheight-1))); |
52 | const BBox2f erange(Vec2f(float(x0),float(y0)),Vec2f((float)x1,(float)y1)); |
53 | bool done MAYBE_UNUSED = eval(This: patch,subPatch,srange,erange); |
54 | assert(done); |
55 | assert(count == (x1-x0+1)*(y1-y0+1)); |
56 | } |
57 | |
58 | template<typename Patch> |
59 | __forceinline void evalLocalGrid(const Patch* patch, const BBox2f& srange, const int lx0, const int lx1, const int ly0, const int ly1) |
60 | { |
61 | const float scale_x = rcp(x: srange.upper.x-srange.lower.x); |
62 | const float scale_y = rcp(x: srange.upper.y-srange.lower.y); |
63 | count += (lx1-lx0)*(ly1-ly0); |
64 | |
65 | #if 0 |
66 | for (unsigned iy=ly0; iy<ly1; iy++) { |
67 | for (unsigned ix=lx0; ix<lx1; ix++) { |
68 | const float lu = select(ix == swidth -1, float(1.0f), (float(ix)-srange.lower.x)*scale_x); |
69 | const float lv = select(iy == sheight-1, float(1.0f), (float(iy)-srange.lower.y)*scale_y); |
70 | const Vec3fa p = patch->patch.eval(lu,lv); |
71 | const float u = float(ix)*rcp_swidth; |
72 | const float v = float(iy)*rcp_sheight; |
73 | const int ofs = (iy-y0)*dwidth+(ix-x0); |
74 | Px[ofs] = p.x; |
75 | Py[ofs] = p.y; |
76 | Pz[ofs] = p.z; |
77 | U[ofs] = u; |
78 | V[ofs] = v; |
79 | } |
80 | } |
81 | #else |
82 | foreach2(lx0,lx1,ly0,ly1,[&](const vboolx& valid, const vintx& ix, const vintx& iy) { |
83 | const vfloatx lu = select(m: ix == swidth -1, t: vfloatx(1.0f), f: (vfloatx(ix)-srange.lower.x)*scale_x); |
84 | const vfloatx lv = select(m: iy == sheight-1, t: vfloatx(1.0f), f: (vfloatx(iy)-srange.lower.y)*scale_y); |
85 | const Vec3vfx p = patch->patch.eval(lu,lv); |
86 | Vec3vfx n = zero; |
87 | if (unlikely(Nx != nullptr)) n = normalize_safe(patch->patch.normal(lu,lv)); |
88 | const vfloatx u = vfloatx(ix)*rcp_swidth; |
89 | const vfloatx v = vfloatx(iy)*rcp_sheight; |
90 | const vintx ofs = (iy-y0)*dwidth+(ix-x0); |
91 | if (likely(all(valid)) && all(b: iy==iy[0])) { |
92 | const unsigned ofs2 = ofs[0]; |
93 | vfloatx::storeu(ptr: Px+ofs2,v: p.x); |
94 | vfloatx::storeu(ptr: Py+ofs2,v: p.y); |
95 | vfloatx::storeu(ptr: Pz+ofs2,v: p.z); |
96 | vfloatx::storeu(ptr: U+ofs2,v: u); |
97 | vfloatx::storeu(ptr: V+ofs2,v); |
98 | if (unlikely(Nx != nullptr)) { |
99 | vfloatx::storeu(ptr: Nx+ofs2,v: n.x); |
100 | vfloatx::storeu(ptr: Ny+ofs2,v: n.y); |
101 | vfloatx::storeu(ptr: Nz+ofs2,v: n.z); |
102 | } |
103 | } else { |
104 | foreach_unique_index(valid,iy,[&](const vboolx& valid, const int iy0, const int j) { |
105 | const unsigned ofs2 = ofs[j]-j; |
106 | vfloatx::storeu(mask: valid,ptr: Px+ofs2,v: p.x); |
107 | vfloatx::storeu(mask: valid,ptr: Py+ofs2,v: p.y); |
108 | vfloatx::storeu(mask: valid,ptr: Pz+ofs2,v: p.z); |
109 | vfloatx::storeu(mask: valid,ptr: U+ofs2,v: u); |
110 | vfloatx::storeu(mask: valid,ptr: V+ofs2,v); |
111 | if (unlikely(Nx != nullptr)) { |
112 | vfloatx::storeu(mask: valid,ptr: Nx+ofs2,v: n.x); |
113 | vfloatx::storeu(mask: valid,ptr: Ny+ofs2,v: n.y); |
114 | vfloatx::storeu(mask: valid,ptr: Nz+ofs2,v: n.z); |
115 | } |
116 | }); |
117 | } |
118 | }); |
119 | #endif |
120 | } |
121 | |
122 | bool eval(Ref This, const BBox2f& srange, const BBox2f& erange, const unsigned depth) |
123 | { |
124 | if (erange.empty()) |
125 | return true; |
126 | |
127 | const int lx0 = (int) ceilf(x: erange.lower.x); |
128 | const int lx1 = (int) ceilf(x: erange.upper.x) + (erange.upper.x == x1 && (srange.lower.x < erange.upper.x || erange.upper.x == 0)); |
129 | const int ly0 = (int) ceilf(x: erange.lower.y); |
130 | const int ly1 = (int) ceilf(x: erange.upper.y) + (erange.upper.y == y1 && (srange.lower.y < erange.upper.y || erange.upper.y == 0)); |
131 | if (lx0 >= lx1 || ly0 >= ly1) |
132 | return true; |
133 | |
134 | if (!This) |
135 | return false; |
136 | |
137 | switch (This.type()) |
138 | { |
139 | case Patch::BILINEAR_PATCH: { |
140 | evalLocalGrid(patch: (Patch::BilinearPatch*)This.object(),srange,lx0,lx1,ly0,ly1); |
141 | return true; |
142 | } |
143 | case Patch::BSPLINE_PATCH: { |
144 | evalLocalGrid(patch: (Patch::BSplinePatch*)This.object(),srange,lx0,lx1,ly0,ly1); |
145 | return true; |
146 | } |
147 | case Patch::BEZIER_PATCH: { |
148 | evalLocalGrid(patch: (Patch::BezierPatch*)This.object(),srange,lx0,lx1,ly0,ly1); |
149 | return true; |
150 | } |
151 | case Patch::GREGORY_PATCH: { |
152 | evalLocalGrid(patch: (Patch::GregoryPatch*)This.object(),srange,lx0,lx1,ly0,ly1); |
153 | return true; |
154 | } |
155 | case Patch::SUBDIVIDED_QUAD_PATCH: |
156 | { |
157 | const Vec2f c = srange.center(); |
158 | const BBox2f srange0(srange.lower,c); |
159 | const BBox2f srange1(Vec2f(c.x,srange.lower.y),Vec2f(srange.upper.x,c.y)); |
160 | const BBox2f srange2(c,srange.upper); |
161 | const BBox2f srange3(Vec2f(srange.lower.x,c.y),Vec2f(c.x,srange.upper.y)); |
162 | |
163 | Patch::SubdividedQuadPatch* patch = (Patch::SubdividedQuadPatch*)This.object(); |
164 | eval(This: patch->child[0],srange: srange0,erange: intersect(a: srange0,b: erange),depth: depth+1); |
165 | eval(This: patch->child[1],srange: srange1,erange: intersect(a: srange1,b: erange),depth: depth+1); |
166 | eval(This: patch->child[2],srange: srange2,erange: intersect(a: srange2,b: erange),depth: depth+1); |
167 | eval(This: patch->child[3],srange: srange3,erange: intersect(a: srange3,b: erange),depth: depth+1); |
168 | return true; |
169 | } |
170 | case Patch::EVAL_PATCH: { |
171 | CatmullClarkPatch patch; patch.deserialize(ptr: This.object()); |
172 | FeatureAdaptiveEvalGrid(patch,srange,erange,depth,x0,x1,y0,y1,swidth,sheight,Px,Py,Pz,U,V,Nx,Ny,Nz,dwidth,dheight); |
173 | count += (lx1-lx0)*(ly1-ly0); |
174 | return true; |
175 | } |
176 | default: |
177 | assert(false); |
178 | return false; |
179 | } |
180 | } |
181 | |
182 | bool eval(Ref This, unsigned subPatch, const BBox2f& srange, const BBox2f& erange) |
183 | { |
184 | if (!This) |
185 | return false; |
186 | |
187 | switch (This.type()) |
188 | { |
189 | case Patch::SUBDIVIDED_GENERAL_PATCH: { |
190 | Patch::SubdividedGeneralPatch* patch = (Patch::SubdividedGeneralPatch*)This.object(); |
191 | assert(subPatch < patch->N); |
192 | return eval(This: patch->child[subPatch],srange,erange,depth: 1); |
193 | } |
194 | default: |
195 | assert(subPatch == 0); |
196 | return eval(This,srange,erange,depth: 0); |
197 | } |
198 | } |
199 | }; |
200 | |
201 | __forceinline unsigned patch_eval_subdivision_count (const HalfEdge* h) |
202 | { |
203 | const unsigned N = h->numEdges(); |
204 | if (N == 4) return 1; |
205 | else return N; |
206 | } |
207 | |
208 | template<typename Tessellator> |
209 | inline void patch_eval_subdivision (const HalfEdge* h, Tessellator tessellator) |
210 | { |
211 | const unsigned N = h->numEdges(); |
212 | int neighborSubdiv[GeneralCatmullClarkPatch3fa::SIZE]; // FIXME: use array_t |
213 | float levels[GeneralCatmullClarkPatch3fa::SIZE]; |
214 | for (unsigned i=0; i<N; i++) { |
215 | assert(i<GeneralCatmullClarkPatch3fa::SIZE); |
216 | neighborSubdiv[i] = h->hasOpposite() ? h->opposite()->numEdges() != 4 : 0; |
217 | levels[i] = h->edge_level; |
218 | h = h->next(); |
219 | } |
220 | if (N == 4) |
221 | { |
222 | const Vec2f uv[4] = { Vec2f(0.0f,0.0f), Vec2f(1.0f,0.0f), Vec2f(1.0f,1.0f), Vec2f(0.0f,1.0f) }; |
223 | tessellator(uv,neighborSubdiv,levels,0); |
224 | } |
225 | else |
226 | { |
227 | for (unsigned i=0; i<N; i++) |
228 | { |
229 | assert(i<MAX_PATCH_VALENCE); |
230 | static_assert(MAX_PATCH_VALENCE <= 16, "MAX_PATCH_VALENCE > 16" ); |
231 | const int h = (i >> 2) & 3, l = i & 3; |
232 | const Vec2f subPatchID((float)l,(float)h); |
233 | const Vec2f uv[4] = { 2.0f*subPatchID + (0.5f+Vec2f(0.0f,0.0f)), |
234 | 2.0f*subPatchID + (0.5f+Vec2f(1.0f,0.0f)), |
235 | 2.0f*subPatchID + (0.5f+Vec2f(1.0f,1.0f)), |
236 | 2.0f*subPatchID + (0.5f+Vec2f(0.0f,1.0f)) }; |
237 | const int neighborSubdiv1[4] = { 0,0,0,0 }; |
238 | const float levels1[4] = { 0.5f*levels[(i+0)%N], 0.5f*levels[(i+0)%N], 0.5f*levels[(i+N-1)%N], 0.5f*levels[(i+N-1)%N] }; |
239 | tessellator(uv,neighborSubdiv1,levels1,i); |
240 | } |
241 | } |
242 | } |
243 | } |
244 | } |
245 | |
246 | |