1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2018 Fedor Matantsev, The appleseedhq Organization
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28
29 // Interface header.
30 #include "embreescene.h"
31
32 // appleseed.renderer headers.
33 #include "renderer/kernel/intersection/intersectionsettings.h"
34 #include "renderer/kernel/shading/shadingpoint.h"
35 #include "renderer/kernel/shading/shadingray.h"
36 #include "renderer/modeling/object/curveobject.h"
37 #include "renderer/modeling/object/meshobject.h"
38 #include "renderer/modeling/object/object.h"
39 #include "renderer/modeling/object/triangle.h"
40 #include "renderer/modeling/scene/assembly.h"
41 #include "renderer/modeling/scene/containers.h"
42
43 // appleseed.foundation headers.
44 #include "foundation/math/area.h"
45 #include "foundation/math/fp.h"
46 #include "foundation/math/intersection/raytrianglemt.h"
47 #include "foundation/math/minmax.h"
48 #include "foundation/math/scalar.h"
49 #include "foundation/math/transform.h"
50 #include "foundation/platform/sse.h"
51 #include "foundation/utility/makevector.h"
52 #include "foundation/utility/statistics.h"
53 #include "foundation/utility/stopwatch.h"
54
55 using namespace foundation;
56 using namespace renderer;
57 using namespace std;
58
59 namespace renderer
60 {
61
62 class EmbreeGeometryData
63 : public NonCopyable
64 {
65 public:
66 // Vertex data.
67 GVector3* m_vertices;
68 unsigned int m_vertices_count;
69 unsigned int m_vertices_stride;
70
71 // Primitive data.
72 uint32* m_primitives;
73 size_t m_primitives_count;
74 size_t m_primitives_stride;
75
76 // Instance data.
77 size_t m_object_instance_idx;
78 uint32 m_vis_flags;
79 unsigned int m_motion_steps_count;
80
81 RTCGeometryType m_geometry_type;
82 RTCGeometry m_geometry_handle;
83
EmbreeGeometryData()84 EmbreeGeometryData()
85 : m_vertices(nullptr)
86 , m_primitives(nullptr)
87 , m_geometry_handle(nullptr)
88 {}
89
~EmbreeGeometryData()90 ~EmbreeGeometryData()
91 {
92 delete[] m_vertices;
93 delete[] m_primitives;
94 rtcReleaseGeometry(m_geometry_handle);
95 }
96 };
97
98 namespace
99 {
collect_triangle_data(const ObjectInstance & object_instance,EmbreeGeometryData & geometry_data)100 void collect_triangle_data(
101 const ObjectInstance& object_instance,
102 EmbreeGeometryData& geometry_data)
103 {
104 assert(geometry_data.m_geometry_type == RTC_GEOMETRY_TYPE_TRIANGLE);
105
106 // Retrieve object space -> assembly space transform for the object instance.
107 const Transformd& transform = object_instance.get_transform();
108
109 // Retrieve the object.
110 Object& object = object_instance.get_object();
111
112 const MeshObject& mesh = static_cast<const MeshObject&>(object);
113 const StaticTriangleTess& tess = mesh.get_static_triangle_tess();
114
115 const unsigned int motion_steps_count = static_cast<unsigned int>(tess.get_motion_segment_count()) + 1;
116 geometry_data.m_motion_steps_count = motion_steps_count;
117
118 //
119 // Retrieve per vertex data.
120 //
121 const unsigned int vertices_count = static_cast<unsigned int>(tess.m_vertices.size());
122 geometry_data.m_vertices_count = vertices_count;
123 geometry_data.m_vertices_stride = sizeof(GVector3);
124
125 // Allocate memory for the vertices. Keep one extra vertex for padding.
126 geometry_data.m_vertices = new GVector3[vertices_count * motion_steps_count + 1];
127
128 // Retrieve assembly space vertices.
129 for (size_t i = 0; i < vertices_count; ++i)
130 {
131 const GVector3& vertex_os = tess.m_vertices[i];
132 geometry_data.m_vertices[i] = transform.point_to_parent(vertex_os);
133 }
134
135 for (size_t m = 1; m < motion_steps_count; ++m)
136 {
137 for (size_t i = 0; i < vertices_count; ++i)
138 {
139 const GVector3& vertex_os = tess.get_vertex_pose(i, m - 1);
140 geometry_data.m_vertices[vertices_count * m + i] = transform.point_to_parent(vertex_os);
141 }
142 }
143
144 //
145 // Retrieve per primitive data.
146 //
147 const size_t primitives_count = tess.m_primitives.size();
148
149 geometry_data.m_primitives = new uint32[primitives_count * 3];
150 geometry_data.m_primitives_stride = sizeof(uint32) * 3;
151 geometry_data.m_primitives_count = primitives_count;
152
153 for (size_t i = 0; i < primitives_count; ++i)
154 {
155 geometry_data.m_primitives[i * 3] = tess.m_primitives[i].m_v0;
156 geometry_data.m_primitives[i * 3 + 1] = tess.m_primitives[i].m_v1;
157 geometry_data.m_primitives[i * 3 + 2] = tess.m_primitives[i].m_v2;
158 }
159 };
160
collect_curve_data(const ObjectInstance & object_instance,EmbreeGeometryData & geometry_data)161 void collect_curve_data(
162 const ObjectInstance& object_instance,
163 EmbreeGeometryData& geometry_data)
164 {
165 //const Transformd& transform = object_instance.get_transform();
166
167 switch(geometry_data.m_geometry_type)
168 {
169 case RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE:
170 //
171 // [Note: Girish] Retrieve data from CurveObject here and push it to geometry_data.
172 //
173
174 break;
175
176 default:
177 assert(!"Unsupported geometry type.");
178 break;
179 }
180 }
181
182 // Returns minimal tnear needed to compensate double to float transition of ray fields.
get_tnear_offset(const RTCRay & ray)183 float get_tnear_offset(const RTCRay& ray)
184 {
185 float max_dir_component = max(abs(ray.dir_x), abs(ray.dir_y), abs(ray.dir_z));
186 uint32 max_origin_exp = max(
187 FP<float>::exponent(ray.org_x),
188 FP<float>::exponent(ray.org_y),
189 FP<float>::exponent(ray.org_z));
190
191 // Calculate exponent-adaptive offset.
192 // Note: float is represented in memory
193 // as 1 sign bit, 8 exponent bits and 23 mantissa bits.
194 // Higher 24th bit is always 1 in normalized form, hence it's ommited.
195 // Mantissa of constructed float will overlap no more than 11 last bits of
196 // origin components due to exponent shift.
197 // Mantissa of constructed float is just a
198 // sequence of 11 ones followed by zeroes.
199
200 const float offset = FP<float>::construct(
201 0,
202 max(static_cast<int32>(max_origin_exp - 23 + 11), 0),
203 2047UL << (23 - 11));
204
205 // Divide by max_dir_component to compensate inverse operation
206 // during intersection search. (Actual start point is org + dir * tnear)
207 return offset / max_dir_component;
208 }
209
shading_ray_to_embree_ray(const ShadingRay & shading_ray,RTCRay & embree_ray)210 void shading_ray_to_embree_ray(
211 const ShadingRay& shading_ray,
212 RTCRay& embree_ray)
213 {
214 embree_ray.org_x = static_cast<float>(shading_ray.m_org.x);
215 embree_ray.org_y = static_cast<float>(shading_ray.m_org.y);
216 embree_ray.org_z = static_cast<float>(shading_ray.m_org.z);
217
218 embree_ray.dir_x = static_cast<float>(shading_ray.m_dir.x);
219 embree_ray.dir_y = static_cast<float>(shading_ray.m_dir.y);
220 embree_ray.dir_z = static_cast<float>(shading_ray.m_dir.z);
221
222 embree_ray.tfar = static_cast<float>(shading_ray.m_tmax);
223 embree_ray.time = static_cast<float>(shading_ray.m_time.m_normalized);
224 embree_ray.mask = shading_ray.m_flags;
225
226 const float tnear_offset = get_tnear_offset(embree_ray);
227
228 embree_ray.tnear = static_cast<float>(shading_ray.m_tmin) + tnear_offset;
229 }
230 }
231
232
233 //
234 // EmbreeDevice class implementation.
235 //
236
EmbreeDevice()237 EmbreeDevice::EmbreeDevice()
238 {
239 // todo: set number of threads.
240 m_device = rtcNewDevice(nullptr);
241 };
242
~EmbreeDevice()243 EmbreeDevice::~EmbreeDevice()
244 {
245 rtcReleaseDevice(m_device);
246 }
247
248
249 //
250 // EmbreeScene class implementation.
251 //
252
EmbreeScene(const EmbreeScene::Arguments & arguments)253 EmbreeScene::EmbreeScene(const EmbreeScene::Arguments& arguments)
254 {
255 // Start stopwatch.
256 Stopwatch<DefaultWallclockTimer> stopwatch;
257 stopwatch.start();
258
259 Statistics statistics;
260
261 m_device = arguments.m_device.m_device;
262 m_scene = rtcNewScene(m_device);
263
264 rtcSetSceneBuildQuality(
265 m_scene,
266 RTCBuildQuality::RTC_BUILD_QUALITY_HIGH);
267
268 const ObjectInstanceContainer& instance_container = arguments.m_assembly.object_instances();
269
270 const size_t instance_count = instance_container.size();
271
272 m_geometry_container.reserve(instance_count);
273
274 for (size_t instance_idx = 0; instance_idx < instance_count; ++instance_idx)
275 {
276 const ObjectInstance* object_instance = instance_container.get_by_index(instance_idx);
277 assert(object_instance);
278
279 RTCGeometry geometry_handle;
280
281 // Set per instance data.
282 unique_ptr<EmbreeGeometryData> geometry_data(new EmbreeGeometryData());
283 geometry_data->m_object_instance_idx = instance_idx;
284 geometry_data->m_vis_flags = object_instance->get_vis_flags();
285
286 //
287 // Collect geometry data for the instance.
288 //
289 const char* object_model = object_instance->get_object().get_model();
290
291 if (strcmp(object_model, MeshObjectFactory().get_model()) == 0)
292 {
293 geometry_data->m_geometry_type = RTC_GEOMETRY_TYPE_TRIANGLE;
294
295 // Retrieve triangle data.
296 collect_triangle_data(*object_instance, *geometry_data);
297
298 geometry_handle = rtcNewGeometry(
299 m_device,
300 RTC_GEOMETRY_TYPE_TRIANGLE);
301
302 rtcSetGeometryBuildQuality(
303 geometry_handle,
304 RTCBuildQuality::RTC_BUILD_QUALITY_HIGH);
305
306 rtcSetGeometryTimeStepCount(
307 geometry_handle,
308 geometry_data->m_motion_steps_count);
309
310 geometry_data->m_geometry_handle = geometry_handle;
311
312 const unsigned int vertices_count = geometry_data->m_vertices_count;
313 const unsigned int vertices_stride = geometry_data->m_vertices_stride;
314
315 for (unsigned int m = 0; m < geometry_data->m_motion_steps_count; ++m)
316 {
317 // Byte offset for the current motion segment.
318 const unsigned int vertices_offset = m * vertices_count * vertices_stride;
319
320 // Set vertices.
321 rtcSetSharedGeometryBuffer(
322 geometry_handle, // geometry
323 RTC_BUFFER_TYPE_VERTEX, // buffer type
324 m, // slot
325 RTC_FORMAT_FLOAT3, // format
326 geometry_data->m_vertices, // buffer
327 vertices_offset, // byte offset
328 vertices_stride, // byte stride
329 vertices_count); // item count
330 }
331
332 // Set vertex indices.
333 rtcSetSharedGeometryBuffer(
334 geometry_handle, // geometry
335 RTC_BUFFER_TYPE_INDEX, // buffer type
336 0, // slot
337 RTC_FORMAT_UINT3, // format
338 geometry_data->m_primitives, // buffer
339 0, // byte offset
340 geometry_data->m_primitives_stride, // byte stride
341 geometry_data->m_primitives_count); // item count
342
343 rtcSetGeometryMask(
344 geometry_handle,
345 geometry_data->m_vis_flags);
346
347 rtcCommitGeometry(geometry_handle);
348 }
349 else if (strcmp(object_model, CurveObjectFactory().get_model()) == 0)
350 {
351 geometry_data->m_geometry_type = RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE;
352
353 // Retrieve curve data.
354 collect_curve_data(*object_instance, *geometry_data);
355
356 geometry_handle = rtcNewGeometry(
357 m_device,
358 RTC_GEOMETRY_TYPE_FLAT_BEZIER_CURVE);
359
360 rtcSetGeometryBuildQuality(
361 geometry_handle,
362 RTCBuildQuality::RTC_BUILD_QUALITY_HIGH);
363
364 // Set vertices. (x_pos, y_pos, z_pos, radii)
365 rtcSetSharedGeometryBuffer(
366 geometry_handle, // geometry
367 RTC_BUFFER_TYPE_INDEX, // buffer type
368 0, // slot
369 RTC_FORMAT_FLOAT4, // format
370 geometry_data->m_vertices, // buffer
371 0, // byte offset
372 geometry_data->m_vertices_stride, // byte stride
373 geometry_data->m_vertices_count); // item count
374
375 // Set vertex indices.
376 rtcSetSharedGeometryBuffer(
377 geometry_handle, // geometry
378 RTC_BUFFER_TYPE_INDEX, // buffer type
379 0, // slot
380 RTC_FORMAT_UINT4, // format
381 geometry_data->m_primitives, // buffer
382 0, // byte offset
383 geometry_data->m_primitives_stride, // byte stride
384 geometry_data->m_primitives_count); // item count
385
386 }
387 else
388 {
389 // Unsupported object type.
390 continue;
391 }
392
393 rtcAttachGeometryByID(m_scene, geometry_handle, static_cast<unsigned int>(instance_idx));
394 m_geometry_container.push_back(std::move(geometry_data));
395 }
396
397 rtcCommitScene(m_scene);
398
399 statistics.insert_time("total build time", stopwatch.measure().get_seconds());
400
401 RENDERER_LOG_DEBUG("%s",
402 StatisticsVector::make(
403 "Embree scene #" + to_string(arguments.m_assembly.get_uid()) + " statistics",
404 statistics).to_string().c_str());
405 }
406
~EmbreeScene()407 EmbreeScene::~EmbreeScene()
408 {
409 rtcReleaseScene(m_scene);
410 }
411
intersect(ShadingPoint & shading_point) const412 void EmbreeScene::intersect(ShadingPoint& shading_point) const
413 {
414 RTCIntersectContext context;
415 rtcInitIntersectContext(&context);
416
417 RTCRayHit rayhit;
418 shading_ray_to_embree_ray(shading_point.get_ray(), rayhit.ray);
419
420 rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID;
421
422 rtcIntersect1(m_scene, &context, &rayhit);
423
424 if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID)
425 {
426 assert(rayhit.hit.geomID < m_geometry_container.size());
427
428 const auto& geometry_data = m_geometry_container[rayhit.hit.geomID];
429 assert(geometry_data);
430
431 shading_point.m_bary[0] = rayhit.hit.u;
432 shading_point.m_bary[1] = rayhit.hit.v;
433
434 shading_point.m_object_instance_index = geometry_data->m_object_instance_idx;
435 // TODO: remove regions
436 shading_point.m_primitive_index = rayhit.hit.primID;
437 shading_point.m_primitive_type = ShadingPoint::PrimitiveTriangle;
438 shading_point.m_ray.m_tmax = rayhit.ray.tfar;
439
440 const uint32 v0_idx = geometry_data->m_primitives[rayhit.hit.primID * 3];
441 const uint32 v1_idx = geometry_data->m_primitives[rayhit.hit.primID * 3 + 1];
442 const uint32 v2_idx = geometry_data->m_primitives[rayhit.hit.primID * 3 + 2];
443
444 if (geometry_data->m_motion_steps_count > 1)
445 {
446 const uint32 last_motion_step_idx = geometry_data->m_motion_steps_count - 1;
447
448 const uint32 motion_step_begin_idx = static_cast<uint32>(rayhit.ray.time * last_motion_step_idx);
449 const uint32 motion_step_end_idx = motion_step_begin_idx + 1;
450
451 const uint32 motion_step_begin_offset = motion_step_begin_idx * geometry_data->m_vertices_count;
452 const uint32 motion_step_end_offset = motion_step_end_idx * geometry_data->m_vertices_count;
453
454 const float motion_step_begin_time = static_cast<float>(motion_step_begin_idx) / last_motion_step_idx;
455
456 // Linear interpolation coefficients.
457 const float p = (rayhit.ray.time - motion_step_begin_time) * last_motion_step_idx;
458 const float q = 1.0f - p;
459
460 assert(p > 0.0f && p <= 1.0f);
461
462 const TriangleType triangle(
463 Vector3d(
464 geometry_data->m_vertices[motion_step_begin_offset + v0_idx] * q
465 + geometry_data->m_vertices[motion_step_end_offset + v0_idx] * p),
466 Vector3d(
467 geometry_data->m_vertices[motion_step_begin_offset + v1_idx] * q
468 + geometry_data->m_vertices[motion_step_end_offset + v1_idx] * p),
469 Vector3d(
470 geometry_data->m_vertices[motion_step_begin_offset + v2_idx] * q
471 + geometry_data->m_vertices[motion_step_end_offset + v2_idx] * p));
472
473 shading_point.m_triangle_support_plane.initialize(triangle);
474 }
475 else
476 {
477 const TriangleType triangle(
478 Vector3d(geometry_data->m_vertices[v0_idx]),
479 Vector3d(geometry_data->m_vertices[v1_idx]),
480 Vector3d(geometry_data->m_vertices[v2_idx]));
481
482 shading_point.m_triangle_support_plane.initialize(triangle);
483 }
484 }
485 }
486
occlude(const ShadingRay & shading_ray) const487 bool EmbreeScene::occlude(const ShadingRay& shading_ray) const
488 {
489 RTCIntersectContext context;
490 rtcInitIntersectContext(&context);
491
492 RTCRay ray;
493 shading_ray_to_embree_ray(shading_ray, ray);
494
495 rtcOccluded1(
496 m_scene,
497 &context,
498 &ray);
499
500 if (ray.tfar < signed_min<float>())
501 return true;
502
503 return false;
504 }
505
EmbreeSceneFactory(const EmbreeScene::Arguments & arguments)506 EmbreeSceneFactory::EmbreeSceneFactory(const EmbreeScene::Arguments& arguments)
507 : m_arguments(arguments)
508 {
509 }
510
create()511 unique_ptr<EmbreeScene> EmbreeSceneFactory::create()
512 {
513 return unique_ptr<EmbreeScene>(new EmbreeScene(m_arguments));
514 }
515
516 } // namespace renderer
517