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