| 1 | // Copyright 2009-2021 Intel Corporation | 
| 2 | // SPDX-License-Identifier: Apache-2.0 | 
| 3 |  | 
| 4 | #pragma once | 
| 5 |  | 
| 6 | #include "primitive.h" | 
| 7 | #include "../common/scene.h" | 
| 8 |  | 
| 9 | namespace embree | 
| 10 | { | 
| 11 |   /* Stores M triangles from an indexed face set */ | 
| 12 |   template <int M> | 
| 13 |   struct TriangleMi | 
| 14 |   { | 
| 15 |     /* Virtual interface to query information about the triangle type */ | 
| 16 |     struct Type : public PrimitiveType | 
| 17 |     { | 
| 18 |       const char* name() const; | 
| 19 |       size_t sizeActive(const char* This) const; | 
| 20 |       size_t sizeTotal(const char* This) const; | 
| 21 |       size_t getBytes(const char* This) const; | 
| 22 |     }; | 
| 23 |     static Type type; | 
| 24 |  | 
| 25 |   public: | 
| 26 |  | 
| 27 |     /* primitive supports multiple time segments */ | 
| 28 |     static const bool singleTimeSegment = false; | 
| 29 |  | 
| 30 |     /* Returns maximum number of stored triangles */ | 
| 31 |     static __forceinline size_t max_size() { return M; } | 
| 32 |  | 
| 33 |     /* Returns required number of primitive blocks for N primitives */ | 
| 34 |     static __forceinline size_t blocks(size_t N) { return (N+max_size()-1)/max_size(); } | 
| 35 |  | 
| 36 |   public: | 
| 37 |  | 
| 38 |     /* Default constructor */ | 
| 39 |     __forceinline TriangleMi() {  } | 
| 40 |  | 
| 41 |     /* Construction from vertices and IDs */ | 
| 42 |     __forceinline TriangleMi(const vuint<M>& v0, | 
| 43 |                              const vuint<M>& v1, | 
| 44 |                              const vuint<M>& v2, | 
| 45 |                              const vuint<M>& geomIDs, | 
| 46 |                              const vuint<M>& primIDs) | 
| 47 | #if defined(EMBREE_COMPACT_POLYS) | 
| 48 |       : geomIDs(geomIDs), primIDs(primIDs) {} | 
| 49 | #else | 
| 50 |     : v0_(v0), v1_(v1), v2_(v2), geomIDs(geomIDs), primIDs(primIDs) {} | 
| 51 | #endif | 
| 52 |  | 
| 53 |     /* Returns a mask that tells which triangles are valid */ | 
| 54 |     __forceinline vbool<M> valid() const { return primIDs != vuint<M>(-1); } | 
| 55 |  | 
| 56 |     /* Returns if the specified triangle is valid */ | 
| 57 |     __forceinline bool valid(const size_t i) const { assert(i<M); return primIDs[i] != -1; } | 
| 58 |  | 
| 59 |     /* Returns the number of stored triangles */ | 
| 60 |     __forceinline size_t size() const { return bsf(~movemask(valid())); } | 
| 61 |  | 
| 62 |     /* Returns the geometry IDs */ | 
| 63 |     __forceinline vuint<M> geomID() const { return geomIDs; } | 
| 64 |     __forceinline unsigned int geomID(const size_t i) const { assert(i<M); return geomIDs[i]; } | 
| 65 |  | 
| 66 |     /* Returns the primitive IDs */ | 
| 67 |     __forceinline vuint<M> primID() const { return primIDs; } | 
| 68 |     __forceinline unsigned int primID(const size_t i) const { assert(i<M); return primIDs[i]; } | 
| 69 |  | 
| 70 |     /* Calculate the bounds of the triangles */ | 
| 71 |     __forceinline const BBox3fa bounds(const Scene *const scene, const size_t itime=0) const | 
| 72 |     { | 
| 73 |       BBox3fa bounds = empty; | 
| 74 |       for (size_t i=0; i<M && valid(i); i++) { | 
| 75 |         const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID(i)); | 
| 76 |         bounds.extend(mesh->bounds(primID(i),itime)); | 
| 77 |       } | 
| 78 |       return bounds; | 
| 79 |     } | 
| 80 |  | 
| 81 |     /* Calculate the linear bounds of the primitive */ | 
| 82 |     __forceinline LBBox3fa linearBounds(const Scene *const scene, size_t itime) { | 
| 83 |       return LBBox3fa(bounds(scene,itime: itime+0),bounds(scene,itime: itime+1)); | 
| 84 |     } | 
| 85 |  | 
| 86 |     __forceinline LBBox3fa linearBounds(const Scene *const scene, size_t itime, size_t numTimeSteps) | 
| 87 |     { | 
| 88 |       LBBox3fa allBounds = empty; | 
| 89 |       for (size_t i=0; i<M && valid(i); i++) | 
| 90 |       { | 
| 91 |         const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID(i)); | 
| 92 |         allBounds.extend(other: mesh->linearBounds(primID(i), itime, numTimeSteps)); | 
| 93 |       } | 
| 94 |       return allBounds; | 
| 95 |     } | 
| 96 |  | 
| 97 |     __forceinline LBBox3fa linearBounds(const Scene *const scene, const BBox1f time_range) | 
| 98 |     { | 
| 99 |       LBBox3fa allBounds = empty; | 
| 100 |       for (size_t i=0; i<M && valid(i); i++) | 
| 101 |       { | 
| 102 |         const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID(i)); | 
| 103 |         allBounds.extend(other: mesh->linearBounds(primID(i), time_range)); | 
| 104 |       } | 
| 105 |       return allBounds; | 
| 106 |     } | 
| 107 |      | 
| 108 |     /* Non-temporal store */ | 
| 109 |     __forceinline static void store_nt(TriangleMi* dst, const TriangleMi& src) | 
| 110 |     { | 
| 111 | #if !defined(EMBREE_COMPACT_POLYS) | 
| 112 |       vuint<M>::store_nt(&dst->v0_,src.v0_); | 
| 113 |       vuint<M>::store_nt(&dst->v1_,src.v1_); | 
| 114 |       vuint<M>::store_nt(&dst->v2_,src.v2_); | 
| 115 | #endif | 
| 116 |       vuint<M>::store_nt(&dst->geomIDs,src.geomIDs); | 
| 117 |       vuint<M>::store_nt(&dst->primIDs,src.primIDs); | 
| 118 |     } | 
| 119 |  | 
| 120 |     /* Fill triangle from triangle list */ | 
| 121 |     template<typename PrimRefT> | 
| 122 |     __forceinline void fill(const PrimRefT* prims, size_t& begin, size_t end, Scene* scene) | 
| 123 |     { | 
| 124 |       vuint<M> v0 = zero, v1 = zero, v2 = zero; | 
| 125 |       vuint<M> geomID = -1, primID = -1; | 
| 126 |       const PrimRefT* prim = &prims[begin]; | 
| 127 |  | 
| 128 |       for (size_t i=0; i<M; i++) | 
| 129 |       { | 
| 130 |         if (begin<end) { | 
| 131 |           geomID[i] = prim->geomID(); | 
| 132 |           primID[i] = prim->primID(); | 
| 133 | #if !defined(EMBREE_COMPACT_POLYS) | 
| 134 |           const TriangleMesh* mesh = scene->get<TriangleMesh>(prim->geomID()); | 
| 135 |           const TriangleMesh::Triangle& tri = mesh->triangle(i: prim->primID()); | 
| 136 |           unsigned int int_stride = mesh->vertices0.getStride()/4; | 
| 137 |           v0[i] = tri.v[0] * int_stride; | 
| 138 |           v1[i] = tri.v[1] * int_stride; | 
| 139 |           v2[i] = tri.v[2] * int_stride; | 
| 140 | #endif | 
| 141 |           begin++; | 
| 142 |         } else { | 
| 143 |           assert(i); | 
| 144 |           if (likely(i > 0)) { | 
| 145 |             geomID[i] = geomID[0]; | 
| 146 |             primID[i] = -1; | 
| 147 |             v0[i] = v0[0]; | 
| 148 |             v1[i] = v0[0]; | 
| 149 |             v2[i] = v0[0]; | 
| 150 |           } | 
| 151 |         } | 
| 152 |         if (begin<end) prim = &prims[begin]; | 
| 153 |       } | 
| 154 |       new (this) TriangleMi(v0,v1,v2,geomID,primID); // FIXME: use non temporal store | 
| 155 |     } | 
| 156 |  | 
| 157 |     __forceinline LBBox3fa fillMB(const PrimRef* prims, size_t& begin, size_t end, Scene* scene, size_t itime) | 
| 158 |     { | 
| 159 |       fill(prims, begin, end, scene); | 
| 160 |       return linearBounds(scene, itime); | 
| 161 |     } | 
| 162 |  | 
| 163 |     __forceinline LBBox3fa fillMB(const PrimRefMB* prims, size_t& begin, size_t end, Scene* scene, const BBox1f time_range) | 
| 164 |     { | 
| 165 |       fill(prims, begin, end, scene); | 
| 166 |       return linearBounds(scene, time_range); | 
| 167 |     } | 
| 168 |  | 
| 169 |     /* Updates the primitive */ | 
| 170 |     __forceinline BBox3fa update(TriangleMesh* mesh) | 
| 171 |     { | 
| 172 |       BBox3fa bounds = empty; | 
| 173 |       for (size_t i=0; i<M; i++) | 
| 174 |       { | 
| 175 |         if (primID(i) == -1) break; | 
| 176 |         const unsigned int primId = primID(i); | 
| 177 |         const TriangleMesh::Triangle& tri = mesh->triangle(i: primId); | 
| 178 |         const Vec3fa p0 = mesh->vertex(i: tri.v[0]); | 
| 179 |         const Vec3fa p1 = mesh->vertex(i: tri.v[1]); | 
| 180 |         const Vec3fa p2 = mesh->vertex(i: tri.v[2]); | 
| 181 |         bounds.extend(other: merge(a: BBox3fa(p0),b: BBox3fa(p1),c: BBox3fa(p2))); | 
| 182 |       } | 
| 183 |       return bounds; | 
| 184 |     } | 
| 185 |  | 
| 186 |   protected: | 
| 187 | #if !defined(EMBREE_COMPACT_POLYS) | 
| 188 |     vuint<M> v0_;         // 4 byte offset of 1st vertex | 
| 189 |     vuint<M> v1_;         // 4 byte offset of 2nd vertex | 
| 190 |     vuint<M> v2_;         // 4 byte offset of 3rd vertex | 
| 191 | #endif | 
| 192 |     vuint<M> geomIDs;    // geometry ID of mesh | 
| 193 |     vuint<M> primIDs;    // primitive ID of primitive inside mesh | 
| 194 |   }; | 
| 195 |  | 
| 196 |   namespace isa | 
| 197 |   { | 
| 198 |      | 
| 199 |   template<int M> | 
| 200 |     struct TriangleMi : public embree::TriangleMi<M> | 
| 201 |   { | 
| 202 | #if !defined(EMBREE_COMPACT_POLYS) | 
| 203 |     using embree::TriangleMi<M>::v0_; | 
| 204 |     using embree::TriangleMi<M>::v1_; | 
| 205 |     using embree::TriangleMi<M>::v2_; | 
| 206 | #endif | 
| 207 |     using embree::TriangleMi<M>::geomIDs; | 
| 208 |     using embree::TriangleMi<M>::primIDs; | 
| 209 |     using embree::TriangleMi<M>::geomID; | 
| 210 |     using embree::TriangleMi<M>::primID; | 
| 211 |     using embree::TriangleMi<M>::valid; | 
| 212 |          | 
| 213 |     /* loads a single vertex */ | 
| 214 |     template<int vid> | 
| 215 |     __forceinline Vec3f getVertex(const size_t index, const Scene *const scene) const | 
| 216 |     { | 
| 217 | #if defined(EMBREE_COMPACT_POLYS) | 
| 218 |       const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID(index)); | 
| 219 |       const TriangleMesh::Triangle& tri = mesh->triangle(primID(index)); | 
| 220 |       return (Vec3f) mesh->vertices[0][tri.v[vid]]; | 
| 221 | #else | 
| 222 |       const vuint<M>& v = getVertexOffset<vid>(); | 
| 223 |       const float* vertices = scene->vertices[geomID(index)]; | 
| 224 |       return (Vec3f&) vertices[v[index]]; | 
| 225 | #endif | 
| 226 |     } | 
| 227 |  | 
| 228 |     template<int vid, typename T> | 
| 229 |     __forceinline Vec3<T> getVertex(const size_t index, const Scene *const scene, const size_t itime, const T& ftime) const | 
| 230 |     { | 
| 231 | #if defined(EMBREE_COMPACT_POLYS) | 
| 232 |       const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID(index)); | 
| 233 |       const TriangleMesh::Triangle& tri = mesh->triangle(primID(index)); | 
| 234 |       const Vec3fa v0 = mesh->vertices[itime+0][tri.v[vid]]; | 
| 235 |       const Vec3fa v1 = mesh->vertices[itime+1][tri.v[vid]]; | 
| 236 | #else | 
| 237 |       const vuint<M>& v = getVertexOffset<vid>(); | 
| 238 |       const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID(index)); | 
| 239 |       const float* vertices0 = (const float*) mesh->vertexPtr(i: 0,itime: itime+0); | 
| 240 |       const float* vertices1 = (const float*) mesh->vertexPtr(i: 0,itime: itime+1); | 
| 241 |       const Vec3fa v0 = Vec3fa::loadu(a: vertices0+v[index]); | 
| 242 |       const Vec3fa v1 = Vec3fa::loadu(a: vertices1+v[index]); | 
| 243 | #endif | 
| 244 |       const Vec3<T> p0(v0.x,v0.y,v0.z); | 
| 245 |       const Vec3<T> p1(v1.x,v1.y,v1.z); | 
| 246 |       return lerp(p0,p1,ftime); | 
| 247 |     } | 
| 248 |  | 
| 249 |     template<int vid, int K, typename T> | 
| 250 |     __forceinline Vec3<T> getVertex(const vbool<K>& valid, const size_t index, const Scene *const scene, const vint<K>& itime, const T& ftime) const | 
| 251 |     { | 
| 252 |       Vec3<T> p0, p1; | 
| 253 |       const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID(index)); | 
| 254 |        | 
| 255 |       for (size_t mask=movemask(valid), i=bsf(v: mask); mask; mask=btc(v: mask,i), i=bsf(v: mask)) | 
| 256 |       { | 
| 257 | #if defined(EMBREE_COMPACT_POLYS) | 
| 258 |         const TriangleMesh::Triangle& tri = mesh->triangle(primID(index)); | 
| 259 |         const Vec3fa v0 = mesh->vertices[itime[i]+0][tri.v[vid]]; | 
| 260 |         const Vec3fa v1 = mesh->vertices[itime[i]+1][tri.v[vid]]; | 
| 261 | #else | 
| 262 |         const vuint<M>& v = getVertexOffset<vid>(); | 
| 263 |         const float* vertices0 = (const float*) mesh->vertexPtr(0,itime[i]+0); | 
| 264 |         const float* vertices1 = (const float*) mesh->vertexPtr(0,itime[i]+1); | 
| 265 |         const Vec3fa v0 = Vec3fa::loadu(a: vertices0+v[index]); | 
| 266 |         const Vec3fa v1 = Vec3fa::loadu(a: vertices1+v[index]); | 
| 267 | #endif | 
| 268 |         p0.x[i] = v0.x; p0.y[i] = v0.y; p0.z[i] = v0.z; | 
| 269 |         p1.x[i] = v1.x; p1.y[i] = v1.y; p1.z[i] = v1.z; | 
| 270 |       } | 
| 271 |       return (T(one)-ftime)*p0 + ftime*p1; | 
| 272 |     } | 
| 273 |  | 
| 274 |     struct Triangle { | 
| 275 |       vfloat4 v0,v1,v2; | 
| 276 |     }; | 
| 277 |      | 
| 278 | #if defined(EMBREE_COMPACT_POLYS) | 
| 279 |      | 
| 280 |     __forceinline Triangle loadTriangle(const int i, const Scene* const scene) const  | 
| 281 |     { | 
| 282 |       const unsigned int geomID = geomIDs[i]; | 
| 283 |       const unsigned int primID = primIDs[i]; | 
| 284 |       if (unlikely(primID == -1)) return { zero, zero, zero }; | 
| 285 |       const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID); | 
| 286 |       const TriangleMesh::Triangle& tri = mesh->triangle(primID); | 
| 287 |       const vfloat4 v0 = (vfloat4) mesh->vertices0[tri.v[0]]; | 
| 288 |       const vfloat4 v1 = (vfloat4) mesh->vertices0[tri.v[1]]; | 
| 289 |       const vfloat4 v2 = (vfloat4) mesh->vertices0[tri.v[2]]; | 
| 290 |       return { v0, v1, v2 }; | 
| 291 |     } | 
| 292 |  | 
| 293 |     __forceinline Triangle loadTriangle(const int i, const int itime, const TriangleMesh* const mesh) const  | 
| 294 |     { | 
| 295 |       const unsigned int primID = primIDs[i]; | 
| 296 |       if (unlikely(primID == -1)) return { zero, zero, zero }; | 
| 297 |       const TriangleMesh::Triangle& tri = mesh->triangle(primID); | 
| 298 |       const vfloat4 v0 = (vfloat4) mesh->vertices[itime][tri.v[0]]; | 
| 299 |       const vfloat4 v1 = (vfloat4) mesh->vertices[itime][tri.v[1]]; | 
| 300 |       const vfloat4 v2 = (vfloat4) mesh->vertices[itime][tri.v[2]]; | 
| 301 |       return { v0, v1, v2 }; | 
| 302 |     } | 
| 303 |      | 
| 304 | #else | 
| 305 |  | 
| 306 |     __forceinline Triangle loadTriangle(const int i, const Scene* const scene) const  | 
| 307 |     { | 
| 308 |       const float* vertices = scene->vertices[geomID(i)]; | 
| 309 |       const vfloat4 v0 = vfloat4::loadu(vertices + v0_[i]); | 
| 310 |       const vfloat4 v1 = vfloat4::loadu(vertices + v1_[i]); | 
| 311 |       const vfloat4 v2 = vfloat4::loadu(vertices + v2_[i]); | 
| 312 |       return { v0, v1, v2 }; | 
| 313 |     } | 
| 314 |  | 
| 315 |     __forceinline Triangle loadTriangle(const int i, const int itime, const TriangleMesh* const mesh) const  | 
| 316 |     { | 
| 317 |       const float* vertices = (const float*) mesh->vertexPtr(i: 0,itime); | 
| 318 |       const vfloat4 v0 = vfloat4::loadu(vertices + v0_[i]); | 
| 319 |       const vfloat4 v1 = vfloat4::loadu(vertices + v1_[i]); | 
| 320 |       const vfloat4 v2 = vfloat4::loadu(vertices + v2_[i]); | 
| 321 |       return { v0, v1, v2 }; | 
| 322 |     } | 
| 323 |      | 
| 324 | #endif | 
| 325 |  | 
| 326 |     /* Gather the triangles */ | 
| 327 |     __forceinline void gather(Vec3vf<M>& p0, Vec3vf<M>& p1, Vec3vf<M>& p2, const Scene* const scene) const; | 
| 328 |  | 
| 329 |     template<int K> | 
| 330 | #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER < 2000) // workaround for compiler bug in ICC 2019 | 
| 331 |     __noinline | 
| 332 | #else | 
| 333 |     __forceinline | 
| 334 | #endif | 
| 335 |     void gather(const vbool<K>& valid, | 
| 336 |                 Vec3vf<K>& p0, | 
| 337 |                 Vec3vf<K>& p1, | 
| 338 |                 Vec3vf<K>& p2, | 
| 339 |                 const size_t index, | 
| 340 |                 const Scene* const scene, | 
| 341 |                 const vfloat<K>& time) const | 
| 342 |     { | 
| 343 |       const TriangleMesh* mesh = scene->get<TriangleMesh>(geomID(index)); | 
| 344 |  | 
| 345 |       vfloat<K> ftime; | 
| 346 |       const vint<K> itime = mesh->timeSegment<K>(time, ftime); | 
| 347 |  | 
| 348 |       const size_t first = bsf(movemask(valid)); | 
| 349 |       if (likely(all(valid,itime[first] == itime))) | 
| 350 |       { | 
| 351 |         p0 = getVertex<0>(index, scene, itime[first], ftime); | 
| 352 |         p1 = getVertex<1>(index, scene, itime[first], ftime); | 
| 353 |         p2 = getVertex<2>(index, scene, itime[first], ftime); | 
| 354 |       } else { | 
| 355 |         p0 = getVertex<0,K>(valid, index, scene, itime, ftime); | 
| 356 |         p1 = getVertex<1,K>(valid, index, scene, itime, ftime); | 
| 357 |         p2 = getVertex<2,K>(valid, index, scene, itime, ftime); | 
| 358 |       } | 
| 359 |     } | 
| 360 |  | 
| 361 |     __forceinline void gather(Vec3vf<M>& p0, | 
| 362 |                               Vec3vf<M>& p1, | 
| 363 |                               Vec3vf<M>& p2, | 
| 364 |                               const TriangleMesh* mesh, | 
| 365 |                               const Scene *const scene, | 
| 366 |                               const int itime) const; | 
| 367 |  | 
| 368 |     __forceinline void gather(Vec3vf<M>& p0, | 
| 369 |                               Vec3vf<M>& p1, | 
| 370 |                               Vec3vf<M>& p2, | 
| 371 |                               const Scene *const scene, | 
| 372 |                               const float time) const; | 
| 373 |  | 
| 374 |  | 
| 375 | #if !defined(EMBREE_COMPACT_POLYS) | 
| 376 |     template<int N> const vuint<M>& getVertexOffset() const; | 
| 377 | #endif | 
| 378 |   }; | 
| 379 |  | 
| 380 | #if !defined(EMBREE_COMPACT_POLYS) | 
| 381 |   template<> template<> __forceinline const vuint<4>& TriangleMi<4>::getVertexOffset<0>() const { return v0_; } | 
| 382 |   template<> template<> __forceinline const vuint<4>& TriangleMi<4>::getVertexOffset<1>() const { return v1_; } | 
| 383 |   template<> template<> __forceinline const vuint<4>& TriangleMi<4>::getVertexOffset<2>() const { return v2_; } | 
| 384 | #endif | 
| 385 |    | 
| 386 |   template<> | 
| 387 |   __forceinline void TriangleMi<4>::gather(Vec3vf4& p0, | 
| 388 |                                            Vec3vf4& p1, | 
| 389 |                                            Vec3vf4& p2, | 
| 390 |                                            const Scene* const scene) const | 
| 391 |   { | 
| 392 |     const Triangle tri0 = loadTriangle(i: 0,scene); | 
| 393 |     const Triangle tri1 = loadTriangle(i: 1,scene); | 
| 394 |     const Triangle tri2 = loadTriangle(i: 2,scene); | 
| 395 |     const Triangle tri3 = loadTriangle(i: 3,scene); | 
| 396 |     transpose(r0: tri0.v0,r1: tri1.v0,r2: tri2.v0,r3: tri3.v0,c0&: p0.x,c1&: p0.y,c2&: p0.z); | 
| 397 |     transpose(r0: tri0.v1,r1: tri1.v1,r2: tri2.v1,r3: tri3.v1,c0&: p1.x,c1&: p1.y,c2&: p1.z); | 
| 398 |     transpose(r0: tri0.v2,r1: tri1.v2,r2: tri2.v2,r3: tri3.v2,c0&: p2.x,c1&: p2.y,c2&: p2.z); | 
| 399 |   } | 
| 400 |  | 
| 401 |   template<> | 
| 402 |   __forceinline void TriangleMi<4>::gather(Vec3vf4& p0, | 
| 403 |                                            Vec3vf4& p1, | 
| 404 |                                            Vec3vf4& p2, | 
| 405 |                                            const TriangleMesh* mesh, | 
| 406 |                                            const Scene *const scene, | 
| 407 |                                            const int itime) const | 
| 408 |   { | 
| 409 |     const Triangle tri0 = loadTriangle(i: 0,itime,mesh); | 
| 410 |     const Triangle tri1 = loadTriangle(i: 1,itime,mesh); | 
| 411 |     const Triangle tri2 = loadTriangle(i: 2,itime,mesh); | 
| 412 |     const Triangle tri3 = loadTriangle(i: 3,itime,mesh); | 
| 413 |     transpose(r0: tri0.v0,r1: tri1.v0,r2: tri2.v0,r3: tri3.v0,c0&: p0.x,c1&: p0.y,c2&: p0.z); | 
| 414 |     transpose(r0: tri0.v1,r1: tri1.v1,r2: tri2.v1,r3: tri3.v1,c0&: p1.x,c1&: p1.y,c2&: p1.z); | 
| 415 |     transpose(r0: tri0.v2,r1: tri1.v2,r2: tri2.v2,r3: tri3.v2,c0&: p2.x,c1&: p2.y,c2&: p2.z); | 
| 416 |   } | 
| 417 |  | 
| 418 |   template<> | 
| 419 |   __forceinline void TriangleMi<4>::gather(Vec3vf4& p0, | 
| 420 |                                            Vec3vf4& p1, | 
| 421 |                                            Vec3vf4& p2, | 
| 422 |                                            const Scene *const scene, | 
| 423 |                                            const float time) const | 
| 424 |   { | 
| 425 |     const TriangleMesh* mesh = scene->get<TriangleMesh>(i: geomID(i: 0)); // in mblur mode all geometries are identical | 
| 426 |  | 
| 427 |     float ftime; | 
| 428 |     const int itime = mesh->timeSegment(time, ftime); | 
| 429 |  | 
| 430 |     Vec3vf4 a0,a1,a2; gather(p0&: a0,p1&: a1,p2&: a2,mesh,scene,itime); | 
| 431 |     Vec3vf4 b0,b1,b2; gather(p0&: b0,p1&: b1,p2&: b2,mesh,scene,itime: itime+1); | 
| 432 |     p0 = lerp(v0: a0,v1: b0,t: vfloat4(ftime)); | 
| 433 |     p1 = lerp(v0: a1,v1: b1,t: vfloat4(ftime)); | 
| 434 |     p2 = lerp(v0: a2,v1: b2,t: vfloat4(ftime)); | 
| 435 |   } | 
| 436 |   } | 
| 437 |  | 
| 438 |   template<int M> | 
| 439 |   typename TriangleMi<M>::Type TriangleMi<M>::type; | 
| 440 |  | 
| 441 |   typedef TriangleMi<4> Triangle4i; | 
| 442 | } | 
| 443 |  |