1 /*
2 The MIT License (MIT)
3 
4 Copyright (c) 2015 - 2017 Light Transport Entertainment, Inc.
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23 */
24 
25 #ifdef __clang__
26 #pragma clang diagnostic push
27 #pragma clang diagnostic ignored "-Wold-style-cast"
28 #pragma clang diagnostic ignored "-Wreserved-id-macro"
29 #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
30 #pragma clang diagnostic ignored "-Wcast-align"
31 #pragma clang diagnostic ignored "-Wpadded"
32 #pragma clang diagnostic ignored "-Wold-style-cast"
33 #pragma clang diagnostic ignored "-Wsign-conversion"
34 #pragma clang diagnostic ignored "-Wvariadic-macros"
35 #pragma clang diagnostic ignored "-Wc++11-extensions"
36 #pragma clang diagnostic ignored "-Wexit-time-destructors"
37 #if __has_warning("-Wcast-qual")
38 #pragma clang diagnostic ignored "-Wcast-qual"
39 #endif
40 #if __has_warning("-Wzero-as-null-pointer-constant")
41 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
42 #endif
43 #endif
44 
45 #ifdef _MSC_VER
46 #pragma warning(push)
47 #pragma warning(disable : 4324)
48 #endif
49 
50 #ifdef _WIN32
51 #  define RTCORE_API extern "C" __declspec(dllexport)
52 #else
53 #  define RTCORE_API extern "C" __attribute__ ((visibility ("default")))
54 #endif
55 
56 #include "embree2/rtcore.h"
57 #include "embree2/rtcore_ray.h"
58 
59 #ifdef __clang__
60 #pragma clang diagnostic pop
61 #endif
62 
63 #ifdef _MSC_VER
64 #pragma warning(pop)
65 #endif
66 
67 #include <cassert>
68 #include <map>
69 #include <sstream>
70 #include <string>
71 #include <vector>
72 
73 #include "nanosg.h"
74 
75 #include <stdint.h>  // Use cstint for C++11 compiler.
76 
77 namespace nanort_embree2 {
78 
79 #ifdef __clang__
80 #if __has_warning("-Wzero-as-null-pointer-constant")
81 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
82 #endif
83 #endif
84 
85 template <typename T>
lerp(T dst[3],const T v0[3],const T v1[3],const T v2[3],float u,float v)86 inline void lerp(T dst[3], const T v0[3], const T v1[3], const T v2[3], float u,
87                  float v) {
88   dst[0] = (static_cast<T>(1.0) - u - v) * v0[0] + u * v1[0] + v * v2[0];
89   dst[1] = (static_cast<T>(1.0) - u - v) * v0[1] + u * v1[1] + v * v2[1];
90   dst[2] = (static_cast<T>(1.0) - u - v) * v0[2] + u * v1[2] + v * v2[2];
91 }
92 
93 template <typename T>
vlength(const T v[3])94 inline T vlength(const T v[3]) {
95   const T d = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
96   if (std::fabs(d) > std::numeric_limits<T>::epsilon()) {
97     return std::sqrt(d);
98   } else {
99     return static_cast<T>(0.0);
100   }
101 }
102 
103 template <typename T>
vnormalize(T dst[3],const T v[3])104 inline void vnormalize(T dst[3], const T v[3]) {
105   dst[0] = v[0];
106   dst[1] = v[1];
107   dst[2] = v[2];
108   const T len = vlength(v);
109   if (std::fabs(len) > std::numeric_limits<T>::epsilon()) {
110     const T inv_len = static_cast<T>(1.0) / len;
111     dst[0] *= inv_len;
112     dst[1] *= inv_len;
113     dst[2] *= inv_len;
114   }
115 }
116 
117 template <typename T>
vcross(T dst[3],const T a[3],const T b[3])118 inline void vcross(T dst[3], const T a[3], const T b[3]) {
119   dst[0] = a[1] * b[2] - a[2] * b[1];
120   dst[1] = a[2] * b[0] - a[0] * b[2];
121   dst[2] = a[0] * b[1] - a[1] * b[0];
122 }
123 
124 template <typename T>
vsub(T dst[3],const T a[3],const T b[3])125 inline void vsub(T dst[3], const T a[3], const T b[3]) {
126   dst[0] = a[0] - b[0];
127   dst[1] = a[1] - b[1];
128   dst[2] = a[2] - b[2];
129 }
130 
131 template <typename T>
calculate_normal(T Nn[3],const T v0[3],const T v1[3],const T v2[3])132 inline void calculate_normal(T Nn[3], const T v0[3], const T v1[3],
133                              const T v2[3]) {
134   T v10[3];
135   T v20[3];
136 
137   vsub(v10, v1, v0);
138   vsub(v20, v2, v0);
139 
140   T N[3];
141   vcross(N, v10, v20);  // CCW
142   // vcross(N, v20, v10); // CC
143   vnormalize(Nn, N);
144 }
145 
146 template <typename T = float>
147 class TriMesh {
148  public:
TriMesh(const size_t num_triangles,const size_t num_vertices)149   explicit TriMesh(const size_t num_triangles, const size_t num_vertices) {
150     // Embree uses 16 bytes stride
151     stride = sizeof(float) * 4;
152     vertices.resize(num_vertices * 4);
153     faces.resize(num_triangles * 3);
154   }
155 
~TriMesh()156   ~TriMesh() {}
157 
158   std::string name;
159 
160   size_t stride;
161   std::vector<T> vertices;          /// [xyz] * num_vertices
162   std::vector<unsigned int> faces;  /// triangle x num_faces
163 
164   T pivot_xform[4][4];
165 
166   // --- Required methods in Scene::Traversal. ---
167 
168   ///
169   /// Get the geometric normal and the shading normal at `face_idx' th face.
170   ///
GetNormal(T Ng[3],T Ns[3],const unsigned int face_idx,const T u,const T v) const171   void GetNormal(T Ng[3], T Ns[3], const unsigned int face_idx, const T u,
172                  const T v) const {
173     (void)u;
174     (void)v;
175 
176     // Compute geometric normal.
177     unsigned int f0, f1, f2;
178     T v0[3], v1[3], v2[3];
179 
180     f0 = faces[3 * face_idx + 0];
181     f1 = faces[3 * face_idx + 1];
182     f2 = faces[3 * face_idx + 2];
183 
184     v0[0] = vertices[3 * f0 + 0];
185     v0[1] = vertices[3 * f0 + 1];
186     v0[2] = vertices[3 * f0 + 2];
187 
188     v1[0] = vertices[3 * f1 + 0];
189     v1[1] = vertices[3 * f1 + 1];
190     v1[2] = vertices[3 * f1 + 2];
191 
192     v2[0] = vertices[3 * f2 + 0];
193     v2[1] = vertices[3 * f2 + 1];
194     v2[2] = vertices[3 * f2 + 2];
195 
196     calculate_normal(Ng, v0, v1, v2);
197 
198     // Use geometric normal.
199     Ns[0] = Ng[0];
200     Ns[1] = Ng[1];
201     Ns[2] = Ng[2];
202   }
203 
204   // --- end of required methods in Scene::Traversal. ---
205 };
206 
207 ///
208 /// Simple handle resource management.
209 ///
210 class HandleAllocator {
211  public:
212   // id = 0 is reserved.
HandleAllocator()213   HandleAllocator() : counter_(1) { (void)_pad_; }
~HandleAllocator()214   ~HandleAllocator() {}
215 
216   ///
217   /// Allocates handle object.
218   ///
Allocate()219   uint32_t Allocate() {
220     uint32_t handle = 0;
221 
222     if (!freeList_.empty()) {
223       // Reuse previously issued handle.
224       handle = freeList_.back();
225       freeList_.pop_back();
226       return handle;
227     }
228 
229     handle = counter_;
230     assert(handle >= 1);
231     assert(handle < 0xFFFFFFFF);
232 
233     counter_++;
234 
235     return handle;
236   }
237 
238   /// Release handle object.
Release(uint32_t handle)239   void Release(uint32_t handle) {
240     if (handle == counter_ - 1) {
241       if (counter_ > 1) {
242         counter_--;
243       }
244     } else {
245       assert(handle >= 1);
246       freeList_.push_back(handle);
247     }
248   }
249 
250  private:
251   std::vector<uint32_t> freeList_;
252   uint32_t counter_;
253   uint32_t _pad_;
254 };
255 
256 class Scene {
257  public:
Scene(RTCSceneFlags sflags,RTCAlgorithmFlags aflags)258   Scene(RTCSceneFlags sflags, RTCAlgorithmFlags aflags)
259       : scene_flags_(sflags), algorithm_flags_(aflags) {
260     (void)scene_flags_;
261     (void)algorithm_flags_;
262   }
263 
~Scene()264   ~Scene() {
265     std::map<uint32_t, TriMesh<float> *>::iterator it(trimesh_map_.begin());
266     std::map<uint32_t, TriMesh<float> *>::iterator itEnd(trimesh_map_.end());
267 
268     for (; it != itEnd; it++) {
269       delete it->second;
270     }
271   }
272 
273   ///
274   /// Get scene bounding box.
275   ///
GetBounds(RTCBounds & bounds)276   void GetBounds(RTCBounds &bounds) {
277     float bmin[3], bmax[3];
278     trimesh_scene_.GetBoundingBox(bmin, bmax);
279     bounds.lower_x = bmin[0];
280     bounds.lower_y = bmin[1];
281     bounds.lower_z = bmin[2];
282 
283     bounds.upper_x = bmax[0];
284     bounds.upper_y = bmax[1];
285     bounds.upper_z = bmax[2];
286   }
287 
288   ///
289   ///
290   ///
NewTriMesh(size_t num_triangles,size_t num_vertices)291   uint32_t NewTriMesh(size_t num_triangles, size_t num_vertices) {
292     uint32_t geom_id = geom_ids_.Allocate();
293 
294     TriMesh<float> *trimesh = new TriMesh<float>(num_triangles, num_vertices);
295 
296     trimesh_map_[geom_id] = trimesh;
297 
298     return geom_id;
299   }
300 
GetTriMesh(const uint32_t geom_id)301   TriMesh<float> *GetTriMesh(const uint32_t geom_id) {
302     if (trimesh_map_.find(geom_id) != trimesh_map_.end()) {
303       return trimesh_map_[geom_id];
304     }
305     return NULL;
306   }
307 
NumShapes()308   size_t NumShapes() { return trimesh_map_.size(); }
309 
Build()310   void Build() {
311     std::map<uint32_t, TriMesh<float> *>::iterator it(trimesh_map_.begin());
312     std::map<uint32_t, TriMesh<float> *>::iterator itEnd(trimesh_map_.end());
313 
314     for (; it != itEnd; it++) {
315       nanosg::Node<float, TriMesh<float> > node(it->second);
316 
317       trimesh_scene_.AddNode(node);
318     }
319 
320     trimesh_scene_.Commit();
321   }
322 
Intersect(nanort::Ray<float> & ray,nanosg::Intersection<float> * isect_out,const bool cull_back_face)323   bool Intersect(nanort::Ray<float> &ray,
324                  nanosg::Intersection<float> *isect_out,
325                  const bool cull_back_face) {
326     return trimesh_scene_.Traverse(ray, isect_out, cull_back_face);
327   }
328 
329  private:
330   RTCSceneFlags scene_flags_;
331   RTCAlgorithmFlags algorithm_flags_;
332   HandleAllocator geom_ids_;
333 
334   nanosg::Scene<float, TriMesh<float> > trimesh_scene_;
335   std::vector<nanosg::Node<float, TriMesh<float> > > trimesh_nodes_;
336 
337   // Records triangle mesh for geom_id
338   std::map<uint32_t, TriMesh<float> *> trimesh_map_;
339 };
340 
341 class Device {
342  public:
Device(const std::string & config)343   Device(const std::string &config)
344       : config_(config), error_func_(NULL), user_ptr_(NULL) {}
345 
~Device()346   ~Device() {}
347 
SetErrorFunction(RTCErrorFunc2 func,void * user_ptr)348   void SetErrorFunction(RTCErrorFunc2 func, void *user_ptr) {
349     error_func_ = func;
350     user_ptr_ = user_ptr;
351   }
352 
AddScene(Scene * scene)353   void AddScene(Scene *scene) { scene_map_[scene] = scene; }
354 
DeleteScene(Scene * scene)355   bool DeleteScene(Scene *scene) {
356     if (scene_map_.find(scene) != scene_map_.end()) {
357       std::map<const Scene *, Scene *>::iterator it = scene_map_.find(scene);
358 
359       scene_map_.erase(it);
360 
361       delete scene;
362       return true;
363     }
364 
365     return false;
366   }
367 
368  private:
369   std::string config_;
370 
371   std::map<const Scene *, Scene *> scene_map_;
372 
373   // Callbacks
374   RTCErrorFunc2 error_func_;
375   void *user_ptr_;
376 };
377 
378 class Context {
379  public:
Context()380   Context() {}
~Context()381   ~Context() {
382     std::map<const Device *, Device *>::iterator it(device_map_.begin());
383     std::map<const Device *, Device *>::iterator itEnd(device_map_.end());
384 
385     for (; it != itEnd; it++) {
386       delete it->second;
387       it->second = NULL;
388     }
389   }
390 
NewDevice(const char * config)391   Device *NewDevice(const char *config) {
392     std::string cfg;
393     if (config) {
394       cfg = std::string(config);
395     }
396 
397     Device *device = new Device(cfg);
398 
399     device_map_[device] = device;
400 
401     return device;
402   }
403 
DeleteDevice(Device * device)404   bool DeleteDevice(Device *device) {
405     if (device_map_.find(device) != device_map_.end()) {
406       std::map<const Device *, Device *>::iterator it =
407           device_map_.find(device);
408       device_map_.erase(it);
409 
410       delete device;
411       return true;
412     }
413     return false;
414   }
415 
DeleteScene(Scene * scene)416   bool DeleteScene(Scene *scene) {
417     // Assume scene is assigned to the device uniquely
418     std::map<const Device *, Device *>::iterator it(device_map_.begin());
419     std::map<const Device *, Device *>::iterator itEnd(device_map_.end());
420 
421     for (; it != itEnd; it++) {
422       if (it->second->DeleteScene(scene)) {
423         return true;
424       }
425     }
426 
427     return false;
428   }
429 
SetError(const std::string & err)430   void SetError(const std::string &err) { error_ = err; }
431 
432  private:
433   std::string error_;
434   std::map<const Device *, Device *> device_map_;
435 };
436 
437 #ifdef __clang__
438 #pragma clang diagnostic push
439 #pragma clang diagnostic ignored "-Wexit-time-destructors"
440 #endif
441 
GetContext()442 static Context &GetContext() {
443   static Context s_ctx;
444 
445   return s_ctx;
446 }
447 
448 #ifdef __clang__
449 #pragma clang diagnostic pop
450 #endif
451 
452 // TODO(LTE): Lock to avoid thread-racing.
453 
rtcNewDevice(const char * cfg=NULL)454 RTCORE_API RTCDevice rtcNewDevice(const char *cfg = NULL) {
455   Device *device = GetContext().NewDevice(cfg);
456 
457   return reinterpret_cast<RTCDevice>(device);
458 }
459 
rtcDeleteScene(RTCScene scene)460 RTCORE_API void rtcDeleteScene(RTCScene scene) {
461   Scene *s = reinterpret_cast<Scene *>(scene);
462 
463   bool ret = GetContext().DeleteScene(s);
464 
465   if (!ret) {
466     std::stringstream ss;
467     ss << "Invalid scene : " << scene << std::endl;
468     GetContext().SetError(ss.str());
469   }
470 }
471 
rtcDeleteDevice(RTCDevice device)472 RTCORE_API void rtcDeleteDevice(RTCDevice device) {
473 #if 0
474   (void)device;
475   std::cout << "TODO: Implement rtcDeleteScene()" << std::endl;
476 #else
477   Device *dev = reinterpret_cast<Device *>(device);
478 
479   bool ret = GetContext().DeleteDevice(dev);
480 
481   if (!ret) {
482     std::stringstream ss;
483     ss << "Invalid device : " << device << std::endl;
484     GetContext().SetError(ss.str());
485   }
486 #endif
487 }
488 
rtcDeviceSetErrorFunction2(RTCDevice device,RTCErrorFunc2 func,void * userPtr)489 RTCORE_API void rtcDeviceSetErrorFunction2(RTCDevice device, RTCErrorFunc2 func,
490                                            void *userPtr) {
491   Device *ptr = reinterpret_cast<Device *>(device);
492   ptr->SetErrorFunction(func, userPtr);
493 }
494 
rtcDeviceNewScene(RTCDevice device,RTCSceneFlags flags,RTCAlgorithmFlags aflags)495 RTCORE_API RTCScene rtcDeviceNewScene(RTCDevice device, RTCSceneFlags flags,
496                                       RTCAlgorithmFlags aflags) {
497   Scene *scene = new Scene(flags, aflags);
498 
499   Device *d = reinterpret_cast<Device *>(device);
500   d->AddScene(scene);
501 
502   return reinterpret_cast<RTCScene>(scene);
503 }
504 
rtcGetBounds(RTCScene scene,RTCBounds & bounds_o)505 RTCORE_API void rtcGetBounds(RTCScene scene, RTCBounds &bounds_o) {
506   Scene *s = reinterpret_cast<Scene *>(scene);
507   s->GetBounds(bounds_o);
508 }
509 
510 #ifdef __clang__
511 #pragma clang diagnostic push
512 #pragma clang diagnostic ignored "-Wold-style-cast"
513 #endif
514 
rtcIntersect(RTCScene scene,RTCRay & rtc_ray)515 RTCORE_API void rtcIntersect(RTCScene scene, RTCRay &rtc_ray) {
516   Scene *s = reinterpret_cast<Scene *>(scene);
517 
518   nanort::Ray<float> ray;
519 
520   ray.org[0] = rtc_ray.org[0];
521   ray.org[1] = rtc_ray.org[1];
522   ray.org[2] = rtc_ray.org[2];
523 
524   ray.dir[0] = rtc_ray.dir[0];
525   ray.dir[1] = rtc_ray.dir[1];
526   ray.dir[2] = rtc_ray.dir[2];
527 
528   // TODO(LTE): .time, .mask
529 
530   ray.min_t = rtc_ray.tnear;
531   ray.max_t = rtc_ray.tfar;
532 
533   nanosg::Intersection<float> isect;
534   // FIXME(LTE): Read RTC_CONFIG_BACKFACE_CULLING from Embree configuration
535   const bool cull_back_face = false;
536   const bool hit = s->Intersect(ray, &isect, cull_back_face);
537 
538   // Overwrite members.
539   if (hit) {
540     rtc_ray.tfar = isect.t;
541     rtc_ray.u = isect.u;
542     rtc_ray.v = isect.v;
543     rtc_ray.geomID = isect.node_id;
544     rtc_ray.primID = isect.prim_id;
545     rtc_ray.instID =
546         RTC_INVALID_GEOMETRY_ID;  // Instancing is not yet supported.
547   } else {
548     rtc_ray.geomID = RTC_INVALID_GEOMETRY_ID;
549     rtc_ray.primID = RTC_INVALID_GEOMETRY_ID;
550     rtc_ray.instID = RTC_INVALID_GEOMETRY_ID;
551   }
552 
553   (void)ray;
554 }
555 
556 #ifdef __clang__
557 #pragma clang diagnostic pop
558 #endif
559 
rtcNewTriangleMesh(RTCScene scene,RTCGeometryFlags flags,size_t numTriangles,size_t numVertices,size_t numTimeSteps=1)560 RTCORE_API unsigned rtcNewTriangleMesh(
561     RTCScene scene,          //!< the scene the mesh belongs to
562     RTCGeometryFlags flags,  //!< geometry flags
563     size_t numTriangles,     //!< number of triangles
564     size_t numVertices,      //!< number of vertices
565     size_t numTimeSteps = 1  //!< number of motion blur time steps
566     ) {
567   if (numTimeSteps != 1) {
568     std::stringstream ss;
569     ss << "[rtcNewTriMesh] Motion blur is not supported. numTimeSteps : "
570        << numTimeSteps << std::endl;
571     GetContext().SetError(ss.str());
572     return 0;
573   }
574 
575   if (numTriangles < 1) {
576     std::stringstream ss;
577     ss << "[rtcNewTriMesh] Invalid numTriangles : " << numTriangles
578        << std::endl;
579     GetContext().SetError(ss.str());
580     return 0;
581   }
582 
583   if (numVertices < 1) {
584     std::stringstream ss;
585     ss << "[rtcNewTriMesh] Invalid numVertices : " << numVertices << std::endl;
586     GetContext().SetError(ss.str());
587     return 0;
588   }
589 
590   // TODO(LTE): Acquire lock?
591   Scene *s = reinterpret_cast<Scene *>(scene);
592   assert(s);
593   const uint32_t geom_id = s->NewTriMesh(numTriangles, numVertices);
594 
595   // TODO(LTE): Support flags.
596   (void)flags;
597 
598   return geom_id;
599 }
600 
rtcMapBuffer(RTCScene scene,unsigned geomID,RTCBufferType type)601 RTCORE_API void *rtcMapBuffer(RTCScene scene, unsigned geomID,
602                               RTCBufferType type) {
603   if (type == RTC_VERTEX_BUFFER) {
604   } else if (type == RTC_INDEX_BUFFER) {
605   } else {
606     std::stringstream ss;
607     ss << "[rtcMapBuffer] Unsupported type : " << type << std::endl;
608     GetContext().SetError(ss.str());
609     return NULL;
610   }
611 
612   // TODO(LTE): Acquire lock?
613   Scene *s = reinterpret_cast<Scene *>(scene);
614   assert(s);
615   TriMesh<float> *trimesh = s->GetTriMesh(geomID);
616   if (trimesh) {
617     if (type == RTC_VERTEX_BUFFER) {
618       return reinterpret_cast<void *>(trimesh->vertices.data());
619     } else if (type == RTC_INDEX_BUFFER) {
620       return reinterpret_cast<void *>(trimesh->faces.data());
621     }
622   } else {
623     std::stringstream ss;
624     ss << "[rtcMapBuffer] geomID : " << geomID << " not found in the scene."
625        << std::endl;
626     GetContext().SetError(ss.str());
627     return NULL;
628   }
629 
630   return NULL;  // never reach here.
631 }
632 
rtcUnmapBuffer(RTCScene scene,unsigned geomID,RTCBufferType type)633 RTCORE_API void rtcUnmapBuffer(RTCScene scene, unsigned geomID,
634                                RTCBufferType type) {
635   if (type == RTC_VERTEX_BUFFER) {
636   } else if (type == RTC_INDEX_BUFFER) {
637   } else {
638     std::stringstream ss;
639     ss << "[rtcUnmapBuffer] Unsupported type : " << type << std::endl;
640     GetContext().SetError(ss.str());
641     return;
642   }
643   // TODO(LTE): Release lock?
644   (void)scene;
645   (void)geomID;
646 }
647 
rtcNewInstance2(RTCScene target,RTCScene source,size_t numTimeSteps=1)648 RTCORE_API unsigned rtcNewInstance2(
649     RTCScene target,  //!< the scene the instance belongs to
650     RTCScene source,  //!< the scene to instantiate
651     size_t numTimeSteps =
652         1) {  //!< number of timesteps, one matrix per timestep
653   if (numTimeSteps != 1) {
654     std::stringstream ss;
655     ss << "[rtcNewInstance2] numTimeSteps must be 1" << std::endl;
656     GetContext().SetError(ss.str());
657     return 0;
658   }
659 
660   // TODO(LTE): Implement
661   (void)target;
662   (void)source;
663 
664   return 0;
665 }
666 
rtcSetTransform2(RTCScene scene,unsigned int geomID,RTCMatrixType layout,const float * xfm,size_t timeStep=0)667 RTCORE_API void rtcSetTransform2(
668     RTCScene scene,        //!< scene handle
669     unsigned int geomID,   //!< ID of geometry
670     RTCMatrixType layout,  //!< layout of transformation matrix
671     const float *xfm,      //!< pointer to transformation matrix
672     size_t timeStep = 0    //!< timestep to set the matrix for
673     ) {
674   // TODO(LTE): Implement
675   (void)scene;
676   (void)geomID;
677   (void)layout;
678   (void)xfm;
679   (void)timeStep;
680 }
681 
rtcUpdate(RTCScene scene,unsigned geomID)682 RTCORE_API void rtcUpdate(RTCScene scene, unsigned geomID) {
683   // TODO(LTE): Implement
684   (void)scene;
685   (void)geomID;
686 }
687 
rtcCommit(RTCScene scene)688 RTCORE_API void rtcCommit(RTCScene scene) {
689   Scene *s = reinterpret_cast<Scene *>(scene);
690   assert(s);
691 
692   s->Build();
693 }
694 
695 }  // namespace nanort_embree2
696