| 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 | |