From 2bd9710e7f620d1badc9984c67dc653c722c030c Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 07:30:06 +0200 Subject: [PATCH 01/15] updated --- libs/yocto/yocto_bvh.cpp | 93 ++++++++++++++++++++++++++++++++------ libs/yocto/yocto_bvh.h | 28 +++++++++++- libs/yocto/yocto_shape.cpp | 3 ++ libs/yocto/yocto_shape.h | 3 -- libs/yocto/yocto_trace.cpp | 66 +++++++++++++++++++++++---- libs/yocto/yocto_trace.h | 43 ++---------------- 6 files changed, 167 insertions(+), 69 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index 571c95d02..bae45225f 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -242,7 +242,7 @@ bool intersect_scene_embree_bvh(const bvh_scene* scene, const ray3f& ray, } // namespace yocto // ----------------------------------------------------------------------------- -// IMPLEMENTATION FOR BVH +// IMPLEMENTATION FOR BVH CREATION // ----------------------------------------------------------------------------- namespace yocto { @@ -260,6 +260,71 @@ bvh_scene::~bvh_scene() { #endif } +// Create BVH +bvh_shape* add_shape(bvh_scene* scene) { + return scene->shapes.emplace_back(new bvh_shape{}); +} +bvh_instance* add_instance(bvh_scene* scene) { + return scene->instances.emplace_back(new bvh_instance{}); +} + +// set shape properties +void set_points(bvh_shape* shape, const vector& points) { + shape->points = points; +} +void set_lines(bvh_shape* shape, const vector& lines) { + shape->lines = lines; +} +void set_triangles(bvh_shape* shape, const vector& triangles) { + shape->triangles = triangles; +} +void set_quads(bvh_shape* shape, const vector& quads) { + shape->quads = quads; +} +void set_positions(bvh_shape* shape, const vector& positions) { + shape->positions = positions; +} +void set_radius(bvh_shape* shape, const vector& radius) { + shape->radius = radius; +} + +// set instance properties +void set_frame(bvh_instance* instance, const frame3f& frame) { + instance->frame = frame; +} +void set_shape(bvh_instance* instance, bvh_shape* shape) { + instance->shape = shape; +} + +// Create BVH shortcuts +bvh_shape* add_shape(bvh_scene* bvh, const vector& points, + const vector& lines, const vector& triangles, + const vector& quads, const vector& positions, + const vector& radius) { + auto shape = add_shape(bvh); + set_points(shape, points); + set_lines(shape, lines); + set_triangles(shape, triangles); + set_quads(shape, quads); + set_positions(shape, positions); + set_radius(shape, radius); + return shape; +} +bvh_instance* add_instance( + bvh_scene* bvh, const frame3f& frame, bvh_shape* shape) { + auto instance = add_instance(bvh); + instance->frame = frame; + instance->shape = shape; + return instance; +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION FOR BVH BUILD +// ----------------------------------------------------------------------------- +namespace yocto { + #if !defined(_WIN32) && !defined(_WIN64) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" @@ -502,7 +567,7 @@ static void update_bvh(bvh_tree_& bvh, const vector& bboxes) { // ----------------------------------------------------------------------------- namespace yocto { -void init_shape_bvh(bvh_shape* shape, bool embree) { +void init_bvh(bvh_shape* shape, bool embree) { #ifdef YOCTO_EMBREE // call Embree if needed if (embree) { @@ -545,13 +610,11 @@ void init_shape_bvh(bvh_shape* shape, bool embree) { build_bvh(shape->bvh, bboxes); } -void init_scene_bvh(bvh_scene* scene, bool embree) { +void init_bvh(bvh_scene* scene, bool embree) { // Make shape bvh - for (auto shape : scene->shapes) { - init_shape_bvh(shape, embree); - } + for (auto shape : scene->shapes) init_bvh(shape, embree); - // embree + // embree #ifdef YOCTO_EMBREE if (embree) { return init_scene_embree_bvh(scene); @@ -562,7 +625,7 @@ void init_scene_bvh(bvh_scene* scene, bool embree) { auto bboxes = vector(scene->instances.size()); for (auto idx = 0; idx < bboxes.size(); idx++) { auto& instance = scene->instances[idx]; - auto& shape = scene->shapes[instance->shape]; + auto& shape = instance->shape; bboxes[idx] = shape->bvh.nodes.empty() ? invalidb3f : transform_bbox(instance->frame, shape->bvh.nodes[0].bbox); @@ -629,7 +692,7 @@ void update_scene_bvh(bvh_scene* scene, const vector& updated_instances, auto bboxes = vector(scene->instances.size()); for (auto idx = 0; idx < bboxes.size(); idx++) { auto& instance = scene->instances[idx]; - auto& sbvh = scene->shapes[instance->shape]->bvh; + auto& sbvh = instance->shape->bvh; bboxes[idx] = transform_bbox(instance->frame, sbvh.nodes[0].bbox); } @@ -794,8 +857,8 @@ static bool intersect_scene_bvh(const bvh_scene* scene, const ray3f& ray_, auto& instance_ = scene->instances[scene->bvh.primitives[idx]]; auto inv_ray = transform_ray( inverse(instance_->frame, non_rigid_frames), ray); - if (intersect_shape_bvh(scene->shapes[instance_->shape], inv_ray, - element, uv, distance, find_any)) { + if (intersect_shape_bvh( + instance_->shape, inv_ray, element, uv, distance, find_any)) { hit = true; instance = scene->bvh.primitives[idx]; ray.tmax = distance; @@ -817,8 +880,8 @@ static bool intersect_instance_bvh(const bvh_scene* scene, int instance, auto& instance_ = scene->instances[instance]; auto inv_ray = transform_ray( inverse(instance_->frame, non_rigid_frames), ray); - return intersect_shape_bvh(scene->shapes[instance_->shape], inv_ray, element, - uv, distance, find_any); + return intersect_shape_bvh( + instance_->shape, inv_ray, element, uv, distance, find_any); } // Intersect ray with a bvh. @@ -943,8 +1006,8 @@ static bool overlap_scene_bvh(const bvh_scene* scene, const vec3f& pos, auto instance_ = scene->instances[primitive]; auto inv_pos = transform_point( inverse(instance_->frame, non_rigid_frames), pos); - if (overlap_shape_bvh(scene->shapes[instance_->shape], inv_pos, - max_distance, element, uv, distance, find_any)) { + if (overlap_shape_bvh(instance_->shape, inv_pos, max_distance, element, + uv, distance, find_any)) { hit = true; instance = primitive; max_distance = distance; diff --git a/libs/yocto/yocto_bvh.h b/libs/yocto/yocto_bvh.h index 05b3b1f2b..0916d8183 100644 --- a/libs/yocto/yocto_bvh.h +++ b/libs/yocto/yocto_bvh.h @@ -110,8 +110,8 @@ struct bvh_shape { // instance struct bvh_instance { - frame3f frame = identity3x4f; - int shape = -1; + frame3f frame = identity3x4f; + bvh_shape* shape = nullptr; }; // BVH data for whole shapes. This interface makes copies of all the data. @@ -128,6 +128,30 @@ struct bvh_scene { ~bvh_scene(); }; +// Create BVH +bvh_shape* add_shape(bvh_scene* scene); +bvh_instance* add_instance(bvh_scene* scene); + +// set shape properties +void set_points(bvh_shape* shape, const vector& points); +void set_lines(bvh_shape* shape, const vector& lines); +void set_triangles(bvh_shape* shape, const vector& triangles); +void set_quads(bvh_shape* shape, const vector& quads); +void set_positions(bvh_shape* shape, const vector& positions); +void set_radius(bvh_shape* shape, const vector& radius); + +// set instance properties +void set_frame(bvh_instance* instance, const frame3f& frame); +void set_shape(bvh_instance* instance, bvh_shape* shape); + +// Create BVH shortcuts +bvh_shape* add_shape(bvh_scene* bvh, const vector& points, + const vector& lines, const vector& triangles, + const vector& quads, const vector& positions, + const vector& radius); +bvh_instance* add_instance( + bvh_scene* bvh, const frame3f& frame, bvh_shape* shape); + // Build the bvh acceleration structure. void init_bvh(bvh_shape* bvh, bool embree = false); void init_bvh(bvh_scene* bvh, bool embree = false); diff --git a/libs/yocto/yocto_shape.cpp b/libs/yocto/yocto_shape.cpp index 7ee5a4153..4ea196a20 100644 --- a/libs/yocto/yocto_shape.cpp +++ b/libs/yocto/yocto_shape.cpp @@ -728,6 +728,9 @@ static pair split_middle(vector& primitives, #pragma GCC diagnostic pop #endif +// Maximum number of primitives per BVH node. +const int bvh_max_prims = 4; + // Build BVH nodes static void build_bvh(bvh_tree& bvh, vector& bboxes) { // get values diff --git a/libs/yocto/yocto_shape.h b/libs/yocto/yocto_shape.h index e4696a959..c02e46cea 100644 --- a/libs/yocto/yocto_shape.h +++ b/libs/yocto/yocto_shape.h @@ -229,9 +229,6 @@ vector> vertex_to_faces_adjacencies( // ----------------------------------------------------------------------------- namespace yocto { -// Maximum number of primitives per BVH node. -const int bvh_max_prims = 4; - // BVH tree node containing its bounds, indices to the BVH arrays of either // primitives or internal nodes, the node element type, // and the split axis. Leaf and internal nodes are identical, except that diff --git a/libs/yocto/yocto_trace.cpp b/libs/yocto/yocto_trace.cpp index a702d9314..300d9e0fb 100644 --- a/libs/yocto/yocto_trace.cpp +++ b/libs/yocto/yocto_trace.cpp @@ -62,13 +62,6 @@ using namespace std::string_literals; // ----------------------------------------------------------------------------- namespace yocto { -trace_shape::~trace_shape() { - delete bvh; -#ifdef YOCTO_EMBREE - if (embree_bvh) rtcReleaseScene(embree_bvh); -#endif -} - trace_scene::~trace_scene() { for (auto camera : cameras) delete camera; for (auto shape : shapes) delete shape; @@ -77,9 +70,6 @@ trace_scene::~trace_scene() { for (auto texture : textures) delete texture; for (auto environment : environments) delete environment; delete bvh; -#ifdef YOCTO_EMBREE - if (embree_bvh) rtcReleaseScene(embree_bvh); -#endif } trace_lights::~trace_lights() {} @@ -989,6 +979,60 @@ static ray3f sample_camera(const trace_camera* camera, const vec2i& ij, // ----------------------------------------------------------------------------- namespace yocto { +// Build the bvh acceleration structure. +void init_bvh(trace_scene* scene, const trace_params& params, + const progress_callback& progress_cb) { + // cleanup + delete scene->bvh; + scene->bvh = new bvh_scene{}; + + // initialize bvh + for (auto shape : scene->shapes) { + shape->bvh = add_shape(scene->bvh); + shape->bvh->points = shape->points; + shape->bvh->lines = shape->lines; + shape->bvh->triangles = shape->triangles; + shape->bvh->quads = shape->quads; + shape->bvh->positions = shape->positions; + shape->bvh->radius = shape->radius; + } + for (auto instance : scene->instances) { + add_instance(scene->bvh, instance->frame, instance->shape->bvh); + } + + // build + // init_bvh(scene->bvh, progress_cb); + init_bvh(scene->bvh); +} + +// Intersect ray with a bvh returning either the first or any intersection. +trace_intersection intersect_scene_bvh(const trace_scene* scene, + const ray3f& ray, bool find_any, bool non_rigid_frames) { + return intersect_scene_bvh(scene->bvh, ray, find_any, non_rigid_frames); +} +trace_intersection intersect_instance_bvh(const trace_instance* instance, + const ray3f& ray, bool find_any, bool non_rigid_frames) { + auto inv_ray = transform_ray(inverse(instance->frame, non_rigid_frames), ray); + auto sintersection = intersect_shape_bvh( + instance->shape->bvh, inv_ray, find_any); + auto intersection = bvh_scene_intersection{}; + intersection.instance = -1; + intersection.element = sintersection.element; + intersection.uv = sintersection.uv; + intersection.distance = sintersection.distance; + intersection.hit = sintersection.hit; + return intersection; +} + +} // namespace yocto + +#if 0 + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION OF RAY-SCENE INTERSECTION +// ----------------------------------------------------------------------------- +namespace yocto { + #ifdef YOCTO_EMBREE // Get Embree device static atomic embree_memory = 0; @@ -1925,6 +1969,8 @@ trace_intersection intersect_instance_bvh(const trace_instance* instance, } // namespace yocto +#endif + // ----------------------------------------------------------------------------- // IMPLEMENTATION FOR PATH TRACING // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_trace.h b/libs/yocto/yocto_trace.h index f062b8fd4..0b21f1b64 100644 --- a/libs/yocto/yocto_trace.h +++ b/libs/yocto/yocto_trace.h @@ -44,6 +44,7 @@ #include #include +#include "yocto_bvh.h" #include "yocto_image.h" #include "yocto_math.h" #include "yocto_sampling.h" @@ -72,27 +73,6 @@ using std::vector; // ----------------------------------------------------------------------------- namespace yocto { -// BVH tree node containing its bounds, indices to the BVH arrays of either -// primitives or internal nodes, the node element type, -// and the split axis. Leaf and internal nodes are identical, except that -// indices refer to primitives for leaf nodes or other nodes for internal nodes. -struct trace_bvh_node { - bbox3f bbox = invalidb3f; - int32_t start = 0; - int16_t num = 0; - int8_t axis = 0; - bool internal = false; -}; - -// BVH tree stored as a node array with the tree structure is encoded using -// array indices. BVH nodes indices refer to either the node array, -// for internal nodes, or the primitive arrays, for leaf nodes. -// Application data is not stored explicitly. -struct trace_bvh { - vector nodes = {}; - vector primitives = {}; -}; - // Camera based on a simple lens model. The camera is placed using a frame. // Camera projection is described in photographic terms. In particular, // we specify film size (35mm by default), film aspect ration, @@ -194,13 +174,7 @@ struct trace_shape { trace_texture* displacement_tex = nullptr; // computed properties - trace_bvh* bvh = nullptr; -#ifdef YOCTO_EMBREE - RTCScene embree_bvh = nullptr; -#endif - - // cleanup - ~trace_shape(); + bvh_shape* bvh = nullptr; // non-owned reference }; // Object. @@ -234,10 +208,7 @@ struct trace_scene { vector materials = {}; // computed properties - trace_bvh* bvh = nullptr; -#ifdef YOCTO_EMBREE - RTCScene embree_bvh = nullptr; -#endif + bvh_scene* bvh = nullptr; // cleanup ~trace_scene(); @@ -599,13 +570,7 @@ namespace yocto { // Results of intersect functions that include hit flag, the instance id, // the shape element id, the shape element uv and intersection distance. // Results values are set only if hit is true. -struct trace_intersection { - int instance = -1; - int element = -1; - vec2f uv = {0, 0}; - float distance = 0; - bool hit = false; -}; +using trace_intersection = bvh_scene_intersection; // Intersect ray with a bvh returning either the first or any intersection // depending on `find_any`. Returns the ray distance , the instance id, From 69a176facac12a1216664f72e9e542bf96d4e96d Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 07:38:45 +0200 Subject: [PATCH 02/15] updated --- libs/yocto/yocto_bvh.cpp | 966 ++++++++++++++++++++++++++++++++++++- libs/yocto/yocto_trace.cpp | 945 ------------------------------------ 2 files changed, 959 insertions(+), 952 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index bae45225f..a10497a0a 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -560,13 +560,6 @@ static void update_bvh(bvh_tree_& bvh, const vector& bboxes) { } } -} // namespace yocto - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION FOR SHAPE/SCENE BVH -// ----------------------------------------------------------------------------- -namespace yocto { - void init_bvh(bvh_shape* shape, bool embree) { #ifdef YOCTO_EMBREE // call Embree if needed @@ -700,6 +693,13 @@ void update_scene_bvh(bvh_scene* scene, const vector& updated_instances, update_bvh(scene->bvh, bboxes); } +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION FOR BVH INTERSECTION +// ----------------------------------------------------------------------------- +namespace yocto { + // Intersect ray with a bvh. static bool intersect_shape_bvh(const bvh_shape* shape, const ray3f& ray_, int& element, vec2f& uv, float& distance, bool find_any) { @@ -884,6 +884,13 @@ static bool intersect_instance_bvh(const bvh_scene* scene, int instance, instance_->shape, inv_ray, element, uv, distance, find_any); } +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION FOR BVH OVERLAP +// ----------------------------------------------------------------------------- +namespace yocto { + // Intersect ray with a bvh. static bool overlap_shape_bvh(const bvh_shape* shape, const vec3f& pos, float max_distance, int& element, vec2f& uv, float& distance, @@ -1125,3 +1132,948 @@ bvh_scene_intersection overlap_scene_bvh(const bvh_scene* scene, } } // namespace yocto + +#if 0 + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION OF RAY-SCENE INTERSECTION +// ----------------------------------------------------------------------------- +namespace yocto { + +#ifdef YOCTO_EMBREE +// Get Embree device +static atomic embree_memory = 0; +static RTCDevice embree_device() { + static RTCDevice device = nullptr; + if (!device) { + device = rtcNewDevice(""); + rtcSetDeviceErrorFunction( + device, + [](void* ctx, RTCError code, const char* message) { + auto str = string{message}; + switch (code) { + case RTC_ERROR_UNKNOWN: + throw std::runtime_error("RTC_ERROR_UNKNOWN: " + str); + case RTC_ERROR_INVALID_ARGUMENT: + throw std::runtime_error("RTC_ERROR_INVALID_ARGUMENT: " + str); + case RTC_ERROR_INVALID_OPERATION: + throw std::runtime_error("RTC_ERROR_INVALID_OPERATION: " + str); + case RTC_ERROR_OUT_OF_MEMORY: + throw std::runtime_error("RTC_ERROR_OUT_OF_MEMORY: " + str); + case RTC_ERROR_UNSUPPORTED_CPU: + throw std::runtime_error("RTC_ERROR_UNSUPPORTED_CPU: " + str); + case RTC_ERROR_CANCELLED: + throw std::runtime_error("RTC_ERROR_CANCELLED: " + str); + default: throw std::runtime_error("invalid error code"); + } + }, + nullptr); + rtcSetDeviceMemoryMonitorFunction( + device, + [](void* userPtr, ssize_t bytes, bool post) { + embree_memory += bytes; + return true; + }, + nullptr); + } + return device; +} + +// Initialize Embree BVH +static void init_embree_bvh(trace_shape* shape, const trace_params& params) { + auto edevice = embree_device(); + if (shape->embree_bvh) rtcReleaseScene(shape->embree_bvh); + shape->embree_bvh = rtcNewScene(edevice); + auto escene = shape->embree_bvh; + if (params.bvh == trace_bvh_type::embree_compact) + rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); + if (params.bvh == trace_bvh_type::embree_highquality) + rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); + if (!shape->points.empty()) { + throw std::runtime_error("embree does not support points"); + } else if (!shape->lines.empty()) { + auto elines = vector{}; + auto epositions = vector{}; + auto last_index = -1; + for (auto& l : shape->lines) { + if (last_index == l.x) { + elines.push_back((int)epositions.size() - 1); + auto& posy = shape->positions[l.y]; + auto& rady = shape->radius[l.y]; + epositions.push_back({posy.x, posy.y, posy.z, rady}); + } else { + elines.push_back((int)epositions.size()); + auto& posx = shape->positions[l.x]; + auto& radx = shape->radius[l.x]; + epositions.push_back({posx.x, posx.y, posx.z, radx}); + auto& posy = shape->positions[l.y]; + auto& rady = shape->radius[l.y]; + epositions.push_back({posy.x, posy.y, posy.z, rady}); + } + last_index = l.y; + } + auto egeometry = rtcNewGeometry( + edevice, RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE); + rtcSetGeometryVertexAttributeCount(egeometry, 1); + auto embree_positions = rtcSetNewGeometryBuffer(egeometry, + RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT4, 4 * 4, epositions.size()); + auto embree_lines = rtcSetNewGeometryBuffer( + egeometry, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, 4, elines.size()); + memcpy(embree_positions, epositions.data(), epositions.size() * 16); + memcpy(embree_lines, elines.data(), elines.size() * 4); + rtcCommitGeometry(egeometry); + rtcAttachGeometryByID(escene, egeometry, 0); + } else if (!shape->triangles.empty()) { + auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_TRIANGLE); + rtcSetGeometryVertexAttributeCount(egeometry, 1); + if (params.bvh == trace_bvh_type::embree_compact) { + rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_VERTEX, 0, + RTC_FORMAT_FLOAT3, shape->positions.data(), 0, 3 * 4, + shape->positions.size()); + rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_INDEX, 0, + RTC_FORMAT_UINT3, shape->triangles.data(), 0, 3 * 4, + shape->triangles.size()); + } else { + auto embree_positions = rtcSetNewGeometryBuffer(egeometry, + RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * 4, + shape->positions.size()); + auto embree_triangles = rtcSetNewGeometryBuffer(egeometry, + RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3 * 4, + shape->triangles.size()); + memcpy(embree_positions, shape->positions.data(), + shape->positions.size() * 12); + memcpy(embree_triangles, shape->triangles.data(), + shape->triangles.size() * 12); + } + rtcCommitGeometry(egeometry); + rtcAttachGeometryByID(escene, egeometry, 0); + } else if (!shape->quads.empty()) { + auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_QUAD); + rtcSetGeometryVertexAttributeCount(egeometry, 1); + if (params.bvh == trace_bvh_type::embree_compact) { + rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_VERTEX, 0, + RTC_FORMAT_FLOAT3, shape->positions.data(), 0, 3 * 4, + shape->positions.size()); + rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_INDEX, 0, + RTC_FORMAT_UINT4, shape->quads.data(), 0, 4 * 4, shape->quads.size()); + } else { + auto embree_positions = rtcSetNewGeometryBuffer(egeometry, + RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * 4, + shape->positions.size()); + auto embree_quads = rtcSetNewGeometryBuffer(egeometry, + RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT4, 4 * 4, + shape->quads.size()); + memcpy(embree_positions, shape->positions.data(), + shape->positions.size() * 12); + memcpy(embree_quads, shape->quads.data(), shape->quads.size() * 16); + } + rtcCommitGeometry(egeometry); + rtcAttachGeometryByID(escene, egeometry, 0); + } else { + throw std::runtime_error("empty shapes not supported"); + } + rtcCommitScene(escene); +} + +static void init_embree_bvh(trace_scene* scene, const trace_params& params) { + // scene bvh + auto edevice = embree_device(); + if (scene->embree_bvh) rtcReleaseScene(scene->embree_bvh); + scene->embree_bvh = rtcNewScene(edevice); + auto escene = scene->embree_bvh; + if (params.bvh == trace_bvh_type::embree_compact) + rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); + if (params.bvh == trace_bvh_type::embree_highquality) + rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); + auto object_id = 0; + for (auto instance : scene->instances) { + auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_INSTANCE); + rtcSetGeometryInstancedScene(egeometry, instance->shape->embree_bvh); + rtcSetGeometryTransform( + egeometry, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &instance->frame); + rtcCommitGeometry(egeometry); + rtcAttachGeometryByID(escene, egeometry, object_id++); + } + rtcCommitScene(escene); +} + +static void update_embree_bvh(trace_scene* scene, + const vector& updated_objects, + const vector& updated_shapes, const trace_params& params) { + throw std::runtime_error("not implemented yet"); + // // scene bvh + // auto escene = scene->embree_bvh; + // for (auto& [object_id, instance_id] : scene->embree_instances) { + // auto instance = scene->objects[object_id]; + // auto frame = scene->objects[instance_id]->frame * instance->frame; + // auto egeometry = rtcGetGeometry(escene, instance_id); + // rtcSetGeometryInstancedScene(egeometry, instance->shape->embree_bvh); + // rtcSetGeometryTransform( + // egeometry, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &frame); + // rtcCommitGeometry(egeometry); + // } + // rtcCommitScene(escene); +} + +static bool intersect_shape_embree_bvh(trace_shape* shape, const ray3f& ray, + int& element, vec2f& uv, float& distance, bool find_any) { + RTCRayHit embree_ray; + embree_ray.ray.org_x = ray.o.x; + embree_ray.ray.org_y = ray.o.y; + embree_ray.ray.org_z = ray.o.z; + embree_ray.ray.dir_x = ray.d.x; + embree_ray.ray.dir_y = ray.d.y; + embree_ray.ray.dir_z = ray.d.z; + embree_ray.ray.tnear = ray.tmin; + embree_ray.ray.tfar = ray.tmax; + embree_ray.ray.flags = 0; + embree_ray.hit.geomID = RTC_INVALID_GEOMETRY_ID; + embree_ray.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; + RTCIntersectContext embree_ctx; + rtcInitIntersectContext(&embree_ctx); + rtcIntersect1(shape->embree_bvh, &embree_ctx, &embree_ray); + if (embree_ray.hit.geomID == RTC_INVALID_GEOMETRY_ID) return false; + element = (int)embree_ray.hit.primID; + uv = {embree_ray.hit.u, embree_ray.hit.v}; + distance = embree_ray.ray.tfar; + return true; +} + +static bool intersect_scene_embree_bvh(const trace_scene* scene, + const ray3f& ray, int& instance, int& element, vec2f& uv, float& distance, + bool find_any) { + RTCRayHit embree_ray; + embree_ray.ray.org_x = ray.o.x; + embree_ray.ray.org_y = ray.o.y; + embree_ray.ray.org_z = ray.o.z; + embree_ray.ray.dir_x = ray.d.x; + embree_ray.ray.dir_y = ray.d.y; + embree_ray.ray.dir_z = ray.d.z; + embree_ray.ray.tnear = ray.tmin; + embree_ray.ray.tfar = ray.tmax; + embree_ray.ray.flags = 0; + embree_ray.hit.geomID = RTC_INVALID_GEOMETRY_ID; + embree_ray.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; + RTCIntersectContext embree_ctx; + rtcInitIntersectContext(&embree_ctx); + rtcIntersect1(scene->embree_bvh, &embree_ctx, &embree_ray); + if (embree_ray.hit.geomID == RTC_INVALID_GEOMETRY_ID) return false; + instance = (int)embree_ray.hit.instID[0]; + element = (int)embree_ray.hit.primID; + uv = {embree_ray.hit.u, embree_ray.hit.v}; + distance = embree_ray.ray.tfar; + return true; +} +#endif + +// primitive used to sort bvh entries +struct trace_bvh_primitive { + bbox3f bbox = invalidb3f; + vec3f center = zero3f; + int primitive = 0; +}; + +// Splits a BVH node using the SAH heuristic. Returns split position and axis. +static pair split_sah( + vector& primitives, int start, int end) { + // initialize split axis and position + auto split_axis = 0; + auto mid = (start + end) / 2; + + // compute primintive bounds and size + auto cbbox = invalidb3f; + for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); + auto csize = cbbox.max - cbbox.min; + if (csize == zero3f) return {mid, split_axis}; + + // consider N bins, compute their cost and keep the minimum + const int nbins = 16; + auto middle = 0.0f; + auto min_cost = flt_max; + auto area = [](auto& b) { + auto size = b.max - b.min; + return 1e-12f + 2 * size.x * size.y + 2 * size.x * size.z + + 2 * size.y * size.z; + }; + for (auto saxis = 0; saxis < 3; saxis++) { + for (auto b = 1; b < nbins; b++) { + auto split = cbbox.min[saxis] + b * csize[saxis] / nbins; + auto left_bbox = invalidb3f, right_bbox = invalidb3f; + auto left_nprims = 0, right_nprims = 0; + for (auto i = start; i < end; i++) { + if (primitives[i].center[saxis] < split) { + left_bbox = merge(left_bbox, primitives[i].bbox); + left_nprims += 1; + } else { + right_bbox = merge(right_bbox, primitives[i].bbox); + right_nprims += 1; + } + } + auto cost = 1 + left_nprims * area(left_bbox) / area(cbbox) + + right_nprims * area(right_bbox) / area(cbbox); + if (cost < min_cost) { + min_cost = cost; + middle = split; + split_axis = saxis; + } + } + } + // split + mid = (int)(std::partition(primitives.data() + start, primitives.data() + end, + [split_axis, middle](auto& primitive) { + return primitive.center[split_axis] < middle; + }) - + primitives.data()); + + // if we were not able to split, just break the primitives in half + if (mid == start || mid == end) { + split_axis = 0; + mid = (start + end) / 2; + // throw std::runtime_error("bad bvh split"); + } + + return {mid, split_axis}; +} + +// Splits a BVH node using the balance heuristic. Returns split position and +// axis. +static pair split_balanced( + vector& primitives, int start, int end) { + // initialize split axis and position + auto axis = 0; + auto mid = (start + end) / 2; + + // compute primintive bounds and size + auto cbbox = invalidb3f; + for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); + auto csize = cbbox.max - cbbox.min; + if (csize == zero3f) return {mid, axis}; + + // split along largest + if (csize.x >= csize.y && csize.x >= csize.z) axis = 0; + if (csize.y >= csize.x && csize.y >= csize.z) axis = 1; + if (csize.z >= csize.x && csize.z >= csize.y) axis = 2; + + // balanced tree split: find the largest axis of the + // bounding box and split along this one right in the middle + mid = (start + end) / 2; + std::nth_element(primitives.data() + start, primitives.data() + mid, + primitives.data() + end, [axis](auto& primitive_a, auto& primitive_b) { + return primitive_a.center[axis] < primitive_b.center[axis]; + }); + + // if we were not able to split, just break the primitives in half + if (mid == start || mid == end) { + // throw std::runtime_error("bad bvh split"); + mid = (start + end) / 2; + } + + return {mid, axis}; +} + +// Splits a BVH node using the middle heuristic. Returns split position and +// axis. +static pair split_middle( + vector& primitives, int start, int end) { + // initialize split axis and position + auto axis = 0; + auto mid = (start + end) / 2; + + // compute primintive bounds and size + auto cbbox = invalidb3f; + for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); + auto csize = cbbox.max - cbbox.min; + if (csize == zero3f) return {mid, axis}; + + // split along largest + if (csize.x >= csize.y && csize.x >= csize.z) axis = 0; + if (csize.y >= csize.x && csize.y >= csize.z) axis = 1; + if (csize.z >= csize.x && csize.z >= csize.y) axis = 2; + + // split the space in the middle along the largest axis + mid = (int)(std::partition(primitives.data() + start, primitives.data() + end, + [axis, middle = center(cbbox)[axis]](auto& primitive) { + return primitive.center[axis] < middle; + }) - + primitives.data()); + + // if we were not able to split, just break the primitives in half + if (mid == start || mid == end) { + // throw std::runtime_error("bad bvh split"); + mid = (start + end) / 2; + } + + return {mid, axis}; +} + +// Split bvh nodes according to a type +static pair split_nodes(vector& primitives, + int start, int end, trace_bvh_type type) { + switch (type) { + case trace_bvh_type::default_: return split_middle(primitives, start, end); + case trace_bvh_type::highquality: return split_sah(primitives, start, end); + case trace_bvh_type::middle: return split_middle(primitives, start, end); + case trace_bvh_type::balanced: + return split_balanced(primitives, start, end); + default: throw std::runtime_error("should not have gotten here"); + } +} + +// Maximum number of primitives per BVH node. +const int trace_bvh_max_prims = 4; + +// Build BVH nodes +static void build_bvh_serial(vector& nodes, + vector& primitives, trace_bvh_type type) { + // prepare to build nodes + nodes.clear(); + nodes.reserve(primitives.size() * 2); + + // queue up first node + auto queue = deque{{0, 0, (int)primitives.size()}}; + nodes.emplace_back(); + + // create nodes until the queue is empty + while (!queue.empty()) { + // grab node to work on + auto next = queue.front(); + queue.pop_front(); + auto nodeid = next.x, start = next.y, end = next.z; + + // grab node + auto& node = nodes[nodeid]; + + // compute bounds + node.bbox = invalidb3f; + for (auto i = start; i < end; i++) + node.bbox = merge(node.bbox, primitives[i].bbox); + + // split into two children + if (end - start > trace_bvh_max_prims) { + // get split + auto [mid, axis] = split_nodes(primitives, start, end, type); + + // make an internal node + node.internal = true; + node.axis = (uint8_t)axis; + node.num = 2; + node.start = (int)nodes.size(); + nodes.emplace_back(); + nodes.emplace_back(); + queue.push_back({node.start + 0, start, mid}); + queue.push_back({node.start + 1, mid, end}); + } else { + // Make a leaf node + node.internal = false; + node.num = (int16_t)(end - start); + node.start = start; + } + } + + // cleanup + nodes.shrink_to_fit(); +} + +#if 0 + +// Build BVH nodes +static void build_bvh_parallel( + const shared_ptr& bvh, vector& bboxes, bvh_type type) { + // get values + auto& nodes = bvh->nodes; + auto& primitives = bvh->primitives; + + // prepare to build nodes + nodes.clear(); + nodes.reserve(bboxes.size() * 2); + + // prepare primitives + bvh->primitives.resize(bboxes.size()); + for (auto idx = 0; idx < bboxes.size(); idx++) bvh->primitives[idx] = idx; + + // prepare centers + auto centers = vector(bboxes.size()); + for (auto idx = 0; idx < bboxes.size(); idx++) + centers[idx] = center(bboxes[idx]); + + // queue up first node + auto queue = deque{{0, 0, (int)primitives.size()}}; + nodes.emplace_back(); + + // synchronization + atomic num_processed_prims(0); + std::mutex queue_mutex; + vector> futures; + auto nthreads = std::thread::hardware_concurrency(); + + // create nodes until the queue is empty + for (auto thread_id = 0; thread_id < nthreads; thread_id++) { + futures.emplace_back(std::async( + std::launch::async, [&nodes, &primitives, &bboxes, ¢ers, &type, + &num_processed_prims, &queue_mutex, &queue] { + while (true) { + // exit if needed + if (num_processed_prims >= primitives.size()) return; + + // grab node to work on + auto next = zero3i; + { + std::lock_guard lock{queue_mutex}; + if (!queue.empty()) { + next = queue.front(); + queue.pop_front(); + } + } + + // wait a bit if needed + if (next == zero3i) { + std::this_thread::sleep_for(std::chrono::microseconds(10)); + continue; + } + + // grab node + auto nodeid = next.x, start = next.y, end = next.z; + auto& node = nodes[nodeid]; + + // compute bounds + node.bbox = invalidb3f; + for (auto i = start; i < end; i++) + node.bbox = merge(node.bbox, bboxes[primitives[i]]); + + // split into two children + if (end - start > bvh_max_prims) { + // get split + auto [mid, axis] = split_nodes( + primitives, bboxes, centers, start, end, type); + + // make an internal node + { + std::lock_guard lock{queue_mutex}; + node.internal = true; + node.axis = axis; + node.num = 2; + node.start = (int)nodes.size(); + nodes.emplace_back(); + nodes.emplace_back(); + queue.push_back({node.start + 0, start, mid}); + queue.push_back({node.start + 1, mid, end}); + } + } else { + // Make a leaf node + node.internal = false; + node.num = end - start; + node.start = start; + num_processed_prims += node.num; + } + } + })); + } + for (auto& f : futures) f.get(); + + // cleanup + nodes.shrink_to_fit(); +} + +#endif + +// Update bvh +static void update_bvh(trace_bvh* bvh, const vector& bboxes) { + for (auto nodeid = (int)bvh->nodes.size() - 1; nodeid >= 0; nodeid--) { + auto& node = bvh->nodes[nodeid]; + node.bbox = invalidb3f; + if (node.internal) { + for (auto idx = 0; idx < 2; idx++) { + node.bbox = merge(node.bbox, bvh->nodes[node.start + idx].bbox); + } + } else { + for (auto idx = 0; idx < node.num; idx++) { + node.bbox = merge(node.bbox, bboxes[node.start + idx]); + } + } + } +} + +static void init_bvh(trace_shape* shape, const trace_params& params) { +#ifdef YOCTO_EMBREE + // call Embree if needed + if (params.bvh == trace_bvh_type::embree_default || + params.bvh == trace_bvh_type::embree_highquality || + params.bvh == trace_bvh_type::embree_compact) { + return init_embree_bvh(shape, params); + } +#endif + + // build primitives + auto primitives = vector{}; + if (!shape->points.empty()) { + for (auto idx = 0; idx < shape->points.size(); idx++) { + auto& p = shape->points[idx]; + auto& primitive = primitives.emplace_back(); + primitive.bbox = point_bounds(shape->positions[p], shape->radius[p]); + primitive.center = center(primitive.bbox); + primitive.primitive = idx; + } + } else if (!shape->lines.empty()) { + for (auto idx = 0; idx < shape->lines.size(); idx++) { + auto& l = shape->lines[idx]; + auto& primitive = primitives.emplace_back(); + primitive.bbox = line_bounds(shape->positions[l.x], shape->positions[l.y], + shape->radius[l.x], shape->radius[l.y]); + primitive.center = center(primitive.bbox); + primitive.primitive = idx; + } + } else if (!shape->triangles.empty()) { + for (auto idx = 0; idx < shape->triangles.size(); idx++) { + auto& primitive = primitives.emplace_back(); + auto& t = shape->triangles[idx]; + primitive.bbox = triangle_bounds( + shape->positions[t.x], shape->positions[t.y], shape->positions[t.z]); + primitive.center = center(primitive.bbox); + primitive.primitive = idx; + } + } else if (!shape->quads.empty()) { + for (auto idx = 0; idx < shape->quads.size(); idx++) { + auto& q = shape->quads[idx]; + auto& primitive = primitives.emplace_back(); + primitive.bbox = quad_bounds(shape->positions[q.x], shape->positions[q.y], + shape->positions[q.z], shape->positions[q.w]); + primitive.center = center(primitive.bbox); + primitive.primitive = idx; + } + } + + // build nodes + delete shape->bvh; + shape->bvh = new trace_bvh{}; + build_bvh_serial(shape->bvh->nodes, primitives, params.bvh); + + // set bvh primitives + shape->bvh->primitives.reserve(primitives.size()); + for (auto& primitive : primitives) { + shape->bvh->primitives.push_back(primitive.primitive); + } +} + +void init_bvh(trace_scene* scene, const trace_params& params, + const progress_callback& progress_cb) { + // handle progress + auto progress = vec2i{0, 1 + (int)scene->shapes.size()}; + + // shapes + for (auto shape : scene->shapes) { + if (progress_cb) progress_cb("build shape bvh", progress.x++, progress.y); + init_bvh(shape, params); + } + + // embree +#ifdef YOCTO_EMBREE + if (params.bvh == trace_bvh_type::embree_default || + params.bvh == trace_bvh_type::embree_highquality || + params.bvh == trace_bvh_type::embree_compact) { + return init_embree_bvh(scene, params); + } +#endif + + // handle progress + if (progress_cb) progress_cb("build scene bvh", progress.x++, progress.y); + + // instance bboxes + auto primitives = vector{}; + auto object_id = 0; + auto empty_instance_frames = vector{identity3x4f}; + for (auto instance : scene->instances) { + auto& primitive = primitives.emplace_back(); + primitive.bbox = instance->shape->bvh->nodes.empty() + ? invalidb3f + : transform_bbox(instance->frame, + instance->shape->bvh->nodes[0].bbox); + primitive.center = center(primitive.bbox); + primitive.primitive = object_id++; + } + + // build nodes + delete scene->bvh; + scene->bvh = new trace_bvh{}; + build_bvh_serial(scene->bvh->nodes, primitives, params.bvh); + + // set bvh primitives + scene->bvh->primitives.reserve(primitives.size()); + for (auto& primitive : primitives) { + scene->bvh->primitives.push_back(primitive.primitive); + } + + // handle progress + if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); +} + +static void update_bvh(trace_shape* shape, const trace_params& params) { +#ifdef YOCTO_EMBREE + if (shape->embree_bvh) { + throw std::runtime_error("embree shape update not implemented"); + } +#endif + + // build primitives + auto bboxes = vector(shape->bvh->primitives.size()); + if (!shape->points.empty()) { + for (auto idx = 0; idx < bboxes.size(); idx++) { + auto& p = shape->points[shape->bvh->primitives[idx]]; + bboxes[idx] = point_bounds(shape->positions[p], shape->radius[p]); + } + } else if (!shape->lines.empty()) { + bboxes = vector(shape->lines.size()); + for (auto idx = 0; idx < bboxes.size(); idx++) { + auto& l = shape->lines[shape->bvh->primitives[idx]]; + bboxes[idx] = line_bounds(shape->positions[l.x], shape->positions[l.y], + shape->radius[l.x], shape->radius[l.y]); + } + } else if (!shape->triangles.empty()) { + bboxes = vector(shape->triangles.size()); + for (auto idx = 0; idx < bboxes.size(); idx++) { + auto& t = shape->triangles[shape->bvh->primitives[idx]]; + bboxes[idx] = triangle_bounds( + shape->positions[t.x], shape->positions[t.y], shape->positions[t.z]); + } + } else if (!shape->quads.empty()) { + bboxes = vector(shape->quads.size()); + for (auto idx = 0; idx < bboxes.size(); idx++) { + auto& q = shape->quads[shape->bvh->primitives[idx]]; + bboxes[idx] = quad_bounds(shape->positions[q.x], shape->positions[q.y], + shape->positions[q.z], shape->positions[q.w]); + } + } + + // update nodes + update_bvh(shape->bvh, bboxes); +} + +void update_bvh(trace_scene* scene, + const vector& updated_objects, + const vector& updated_shapes, const trace_params& params) { + for (auto shape : updated_shapes) update_bvh(shape, params); + +#ifdef YOCTO_EMBREE + if (scene->embree_bvh) { + update_embree_bvh(scene, updated_objects, updated_shapes, params); + } +#endif + + // build primitives + auto bboxes = vector(scene->bvh->primitives.size()); + for (auto idx = 0; idx < bboxes.size(); idx++) { + auto instance = scene->instances[scene->bvh->primitives[idx]]; + auto sbvh = instance->shape->bvh; + bboxes[idx] = transform_bbox(instance->frame, sbvh->nodes[0].bbox); + } + + // update nodes + update_bvh(scene->bvh, bboxes); +} + +// Intersect ray with a bvh-> +static bool intersect_shape_bvh(trace_shape* shape, const ray3f& ray_, + int& element, vec2f& uv, float& distance, bool find_any) { +#ifdef YOCTO_EMBREE + // call Embree if needed + if (shape->embree_bvh) { + return intersect_shape_embree_bvh( + shape, ray_, element, uv, distance, find_any); + } +#endif + + // get bvh and shape pointers for fast access + auto bvh = shape->bvh; + + // check empty + if (bvh->nodes.empty()) return false; + + // node stack + auto node_stack = array{}; + auto node_cur = 0; + node_stack[node_cur++] = 0; + + // shared variables + auto hit = false; + + // copy ray to modify it + auto ray = ray_; + + // prepare ray for fast queries + auto ray_dinv = vec3f{1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z}; + auto ray_dsign = vec3i{(ray_dinv.x < 0) ? 1 : 0, (ray_dinv.y < 0) ? 1 : 0, + (ray_dinv.z < 0) ? 1 : 0}; + + // walking stack + while (node_cur != 0) { + // grab node + auto& node = bvh->nodes[node_stack[--node_cur]]; + + // intersect bbox + // if (!intersect_bbox(ray, ray_dinv, ray_dsign, node.bbox)) continue; + if (!intersect_bbox(ray, ray_dinv, node.bbox)) continue; + + // intersect node, switching based on node type + // for each type, iterate over the the primitive list + if (node.internal) { + // for internal nodes, attempts to proceed along the + // split axis from smallest to largest nodes + if (ray_dsign[node.axis] != 0) { + node_stack[node_cur++] = node.start + 0; + node_stack[node_cur++] = node.start + 1; + } else { + node_stack[node_cur++] = node.start + 1; + node_stack[node_cur++] = node.start + 0; + } + } else if (!shape->points.empty()) { + for (auto idx = node.start; idx < node.start + node.num; idx++) { + auto& p = shape->points[shape->bvh->primitives[idx]]; + if (intersect_point( + ray, shape->positions[p], shape->radius[p], uv, distance)) { + hit = true; + element = shape->bvh->primitives[idx]; + ray.tmax = distance; + } + } + } else if (!shape->lines.empty()) { + for (auto idx = node.start; idx < node.start + node.num; idx++) { + auto& l = shape->lines[shape->bvh->primitives[idx]]; + if (intersect_line(ray, shape->positions[l.x], shape->positions[l.y], + shape->radius[l.x], shape->radius[l.y], uv, distance)) { + hit = true; + element = shape->bvh->primitives[idx]; + ray.tmax = distance; + } + } + } else if (!shape->triangles.empty()) { + for (auto idx = node.start; idx < node.start + node.num; idx++) { + auto& t = shape->triangles[shape->bvh->primitives[idx]]; + if (intersect_triangle(ray, shape->positions[t.x], + shape->positions[t.y], shape->positions[t.z], uv, distance)) { + hit = true; + element = shape->bvh->primitives[idx]; + ray.tmax = distance; + } + } + } else if (!shape->quads.empty()) { + for (auto idx = node.start; idx < node.start + node.num; idx++) { + auto& q = shape->quads[shape->bvh->primitives[idx]]; + if (intersect_quad(ray, shape->positions[q.x], shape->positions[q.y], + shape->positions[q.z], shape->positions[q.w], uv, distance)) { + hit = true; + element = shape->bvh->primitives[idx]; + ray.tmax = distance; + } + } + } + + // check for early exit + if (find_any && hit) return hit; + } + + return hit; +} + +// Intersect ray with a bvh-> +static bool intersect_scene_bvh(const trace_scene* scene, const ray3f& ray_, + int& instance, int& element, vec2f& uv, float& distance, bool find_any, + bool non_rigid_frames) { +#ifdef YOCTO_EMBREE + // call Embree if needed + if (scene->embree_bvh) { + return intersect_scene_embree_bvh( + scene, ray_, instance, element, uv, distance, find_any); + } +#endif + + // get bvh and scene pointers for fast access + auto bvh = scene->bvh; + + // check empty + if (bvh->nodes.empty()) return false; + + // node stack + auto node_stack = array{}; + auto node_cur = 0; + node_stack[node_cur++] = 0; + + // shared variables + auto hit = false; + + // copy ray to modify it + auto ray = ray_; + + // prepare ray for fast queries + auto ray_dinv = vec3f{1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z}; + auto ray_dsign = vec3i{(ray_dinv.x < 0) ? 1 : 0, (ray_dinv.y < 0) ? 1 : 0, + (ray_dinv.z < 0) ? 1 : 0}; + + // walking stack + while (node_cur != 0) { + // grab node + auto& node = bvh->nodes[node_stack[--node_cur]]; + + // intersect bbox + // if (!intersect_bbox(ray, ray_dinv, ray_dsign, node.bbox)) continue; + if (!intersect_bbox(ray, ray_dinv, node.bbox)) continue; + + // intersect node, switching based on node type + // for each type, iterate over the the primitive list + if (node.internal) { + // for internal nodes, attempts to proceed along the + // split axis from smallest to largest nodes + if (ray_dsign[node.axis] != 0) { + node_stack[node_cur++] = node.start + 0; + node_stack[node_cur++] = node.start + 1; + } else { + node_stack[node_cur++] = node.start + 1; + node_stack[node_cur++] = node.start + 0; + } + } else { + for (auto idx = node.start; idx < node.start + node.num; idx++) { + auto object_ = scene->instances[scene->bvh->primitives[idx]]; + auto inv_ray = transform_ray( + inverse(object_->frame, non_rigid_frames), ray); + if (intersect_shape_bvh( + object_->shape, inv_ray, element, uv, distance, find_any)) { + hit = true; + instance = scene->bvh->primitives[idx]; + ray.tmax = distance; + } + } + } + + // check for early exit + if (find_any && hit) return hit; + } + + return hit; +} + +// Intersect ray with a bvh-> +static bool intersect_instance_bvh(const trace_instance* instance, + const ray3f& ray, int& element, vec2f& uv, float& distance, bool find_any, + bool non_rigid_frames) { + auto inv_ray = transform_ray(inverse(instance->frame, non_rigid_frames), ray); + return intersect_shape_bvh( + instance->shape, inv_ray, element, uv, distance, find_any); +} + +trace_intersection intersect_scene_bvh(const trace_scene* scene, + const ray3f& ray, bool find_any, bool non_rigid_frames) { + auto intersection = trace_intersection{}; + intersection.hit = intersect_scene_bvh(scene, ray, intersection.instance, + intersection.element, intersection.uv, intersection.distance, find_any, + non_rigid_frames); + return intersection; +} +trace_intersection intersect_instance_bvh(const trace_instance* instance, + const ray3f& ray, bool find_any, bool non_rigid_frames) { + auto intersection = trace_intersection{}; + intersection.hit = intersect_instance_bvh(instance, ray, intersection.element, + intersection.uv, intersection.distance, find_any, non_rigid_frames); + return intersection; +} + +} // namespace yocto + +#endif diff --git a/libs/yocto/yocto_trace.cpp b/libs/yocto/yocto_trace.cpp index 300d9e0fb..2fa8b46a5 100644 --- a/libs/yocto/yocto_trace.cpp +++ b/libs/yocto/yocto_trace.cpp @@ -1026,951 +1026,6 @@ trace_intersection intersect_instance_bvh(const trace_instance* instance, } // namespace yocto -#if 0 - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION OF RAY-SCENE INTERSECTION -// ----------------------------------------------------------------------------- -namespace yocto { - -#ifdef YOCTO_EMBREE -// Get Embree device -static atomic embree_memory = 0; -static RTCDevice embree_device() { - static RTCDevice device = nullptr; - if (!device) { - device = rtcNewDevice(""); - rtcSetDeviceErrorFunction( - device, - [](void* ctx, RTCError code, const char* message) { - auto str = string{message}; - switch (code) { - case RTC_ERROR_UNKNOWN: - throw std::runtime_error("RTC_ERROR_UNKNOWN: " + str); - case RTC_ERROR_INVALID_ARGUMENT: - throw std::runtime_error("RTC_ERROR_INVALID_ARGUMENT: " + str); - case RTC_ERROR_INVALID_OPERATION: - throw std::runtime_error("RTC_ERROR_INVALID_OPERATION: " + str); - case RTC_ERROR_OUT_OF_MEMORY: - throw std::runtime_error("RTC_ERROR_OUT_OF_MEMORY: " + str); - case RTC_ERROR_UNSUPPORTED_CPU: - throw std::runtime_error("RTC_ERROR_UNSUPPORTED_CPU: " + str); - case RTC_ERROR_CANCELLED: - throw std::runtime_error("RTC_ERROR_CANCELLED: " + str); - default: throw std::runtime_error("invalid error code"); - } - }, - nullptr); - rtcSetDeviceMemoryMonitorFunction( - device, - [](void* userPtr, ssize_t bytes, bool post) { - embree_memory += bytes; - return true; - }, - nullptr); - } - return device; -} - -// Initialize Embree BVH -static void init_embree_bvh(trace_shape* shape, const trace_params& params) { - auto edevice = embree_device(); - if (shape->embree_bvh) rtcReleaseScene(shape->embree_bvh); - shape->embree_bvh = rtcNewScene(edevice); - auto escene = shape->embree_bvh; - if (params.bvh == trace_bvh_type::embree_compact) - rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); - if (params.bvh == trace_bvh_type::embree_highquality) - rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); - if (!shape->points.empty()) { - throw std::runtime_error("embree does not support points"); - } else if (!shape->lines.empty()) { - auto elines = vector{}; - auto epositions = vector{}; - auto last_index = -1; - for (auto& l : shape->lines) { - if (last_index == l.x) { - elines.push_back((int)epositions.size() - 1); - auto& posy = shape->positions[l.y]; - auto& rady = shape->radius[l.y]; - epositions.push_back({posy.x, posy.y, posy.z, rady}); - } else { - elines.push_back((int)epositions.size()); - auto& posx = shape->positions[l.x]; - auto& radx = shape->radius[l.x]; - epositions.push_back({posx.x, posx.y, posx.z, radx}); - auto& posy = shape->positions[l.y]; - auto& rady = shape->radius[l.y]; - epositions.push_back({posy.x, posy.y, posy.z, rady}); - } - last_index = l.y; - } - auto egeometry = rtcNewGeometry( - edevice, RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE); - rtcSetGeometryVertexAttributeCount(egeometry, 1); - auto embree_positions = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT4, 4 * 4, epositions.size()); - auto embree_lines = rtcSetNewGeometryBuffer( - egeometry, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, 4, elines.size()); - memcpy(embree_positions, epositions.data(), epositions.size() * 16); - memcpy(embree_lines, elines.data(), elines.size() * 4); - rtcCommitGeometry(egeometry); - rtcAttachGeometryByID(escene, egeometry, 0); - } else if (!shape->triangles.empty()) { - auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_TRIANGLE); - rtcSetGeometryVertexAttributeCount(egeometry, 1); - if (params.bvh == trace_bvh_type::embree_compact) { - rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_VERTEX, 0, - RTC_FORMAT_FLOAT3, shape->positions.data(), 0, 3 * 4, - shape->positions.size()); - rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_INDEX, 0, - RTC_FORMAT_UINT3, shape->triangles.data(), 0, 3 * 4, - shape->triangles.size()); - } else { - auto embree_positions = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * 4, - shape->positions.size()); - auto embree_triangles = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3 * 4, - shape->triangles.size()); - memcpy(embree_positions, shape->positions.data(), - shape->positions.size() * 12); - memcpy(embree_triangles, shape->triangles.data(), - shape->triangles.size() * 12); - } - rtcCommitGeometry(egeometry); - rtcAttachGeometryByID(escene, egeometry, 0); - } else if (!shape->quads.empty()) { - auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_QUAD); - rtcSetGeometryVertexAttributeCount(egeometry, 1); - if (params.bvh == trace_bvh_type::embree_compact) { - rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_VERTEX, 0, - RTC_FORMAT_FLOAT3, shape->positions.data(), 0, 3 * 4, - shape->positions.size()); - rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_INDEX, 0, - RTC_FORMAT_UINT4, shape->quads.data(), 0, 4 * 4, shape->quads.size()); - } else { - auto embree_positions = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * 4, - shape->positions.size()); - auto embree_quads = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT4, 4 * 4, - shape->quads.size()); - memcpy(embree_positions, shape->positions.data(), - shape->positions.size() * 12); - memcpy(embree_quads, shape->quads.data(), shape->quads.size() * 16); - } - rtcCommitGeometry(egeometry); - rtcAttachGeometryByID(escene, egeometry, 0); - } else { - throw std::runtime_error("empty shapes not supported"); - } - rtcCommitScene(escene); -} - -static void init_embree_bvh(trace_scene* scene, const trace_params& params) { - // scene bvh - auto edevice = embree_device(); - if (scene->embree_bvh) rtcReleaseScene(scene->embree_bvh); - scene->embree_bvh = rtcNewScene(edevice); - auto escene = scene->embree_bvh; - if (params.bvh == trace_bvh_type::embree_compact) - rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); - if (params.bvh == trace_bvh_type::embree_highquality) - rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); - auto object_id = 0; - for (auto instance : scene->instances) { - auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_INSTANCE); - rtcSetGeometryInstancedScene(egeometry, instance->shape->embree_bvh); - rtcSetGeometryTransform( - egeometry, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &instance->frame); - rtcCommitGeometry(egeometry); - rtcAttachGeometryByID(escene, egeometry, object_id++); - } - rtcCommitScene(escene); -} - -static void update_embree_bvh(trace_scene* scene, - const vector& updated_objects, - const vector& updated_shapes, const trace_params& params) { - throw std::runtime_error("not implemented yet"); - // // scene bvh - // auto escene = scene->embree_bvh; - // for (auto& [object_id, instance_id] : scene->embree_instances) { - // auto instance = scene->objects[object_id]; - // auto frame = scene->objects[instance_id]->frame * instance->frame; - // auto egeometry = rtcGetGeometry(escene, instance_id); - // rtcSetGeometryInstancedScene(egeometry, instance->shape->embree_bvh); - // rtcSetGeometryTransform( - // egeometry, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &frame); - // rtcCommitGeometry(egeometry); - // } - // rtcCommitScene(escene); -} - -static bool intersect_shape_embree_bvh(trace_shape* shape, const ray3f& ray, - int& element, vec2f& uv, float& distance, bool find_any) { - RTCRayHit embree_ray; - embree_ray.ray.org_x = ray.o.x; - embree_ray.ray.org_y = ray.o.y; - embree_ray.ray.org_z = ray.o.z; - embree_ray.ray.dir_x = ray.d.x; - embree_ray.ray.dir_y = ray.d.y; - embree_ray.ray.dir_z = ray.d.z; - embree_ray.ray.tnear = ray.tmin; - embree_ray.ray.tfar = ray.tmax; - embree_ray.ray.flags = 0; - embree_ray.hit.geomID = RTC_INVALID_GEOMETRY_ID; - embree_ray.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; - RTCIntersectContext embree_ctx; - rtcInitIntersectContext(&embree_ctx); - rtcIntersect1(shape->embree_bvh, &embree_ctx, &embree_ray); - if (embree_ray.hit.geomID == RTC_INVALID_GEOMETRY_ID) return false; - element = (int)embree_ray.hit.primID; - uv = {embree_ray.hit.u, embree_ray.hit.v}; - distance = embree_ray.ray.tfar; - return true; -} - -static bool intersect_scene_embree_bvh(const trace_scene* scene, - const ray3f& ray, int& instance, int& element, vec2f& uv, float& distance, - bool find_any) { - RTCRayHit embree_ray; - embree_ray.ray.org_x = ray.o.x; - embree_ray.ray.org_y = ray.o.y; - embree_ray.ray.org_z = ray.o.z; - embree_ray.ray.dir_x = ray.d.x; - embree_ray.ray.dir_y = ray.d.y; - embree_ray.ray.dir_z = ray.d.z; - embree_ray.ray.tnear = ray.tmin; - embree_ray.ray.tfar = ray.tmax; - embree_ray.ray.flags = 0; - embree_ray.hit.geomID = RTC_INVALID_GEOMETRY_ID; - embree_ray.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; - RTCIntersectContext embree_ctx; - rtcInitIntersectContext(&embree_ctx); - rtcIntersect1(scene->embree_bvh, &embree_ctx, &embree_ray); - if (embree_ray.hit.geomID == RTC_INVALID_GEOMETRY_ID) return false; - instance = (int)embree_ray.hit.instID[0]; - element = (int)embree_ray.hit.primID; - uv = {embree_ray.hit.u, embree_ray.hit.v}; - distance = embree_ray.ray.tfar; - return true; -} -#endif - -// primitive used to sort bvh entries -struct trace_bvh_primitive { - bbox3f bbox = invalidb3f; - vec3f center = zero3f; - int primitive = 0; -}; - -// Splits a BVH node using the SAH heuristic. Returns split position and axis. -static pair split_sah( - vector& primitives, int start, int end) { - // initialize split axis and position - auto split_axis = 0; - auto mid = (start + end) / 2; - - // compute primintive bounds and size - auto cbbox = invalidb3f; - for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); - auto csize = cbbox.max - cbbox.min; - if (csize == zero3f) return {mid, split_axis}; - - // consider N bins, compute their cost and keep the minimum - const int nbins = 16; - auto middle = 0.0f; - auto min_cost = flt_max; - auto area = [](auto& b) { - auto size = b.max - b.min; - return 1e-12f + 2 * size.x * size.y + 2 * size.x * size.z + - 2 * size.y * size.z; - }; - for (auto saxis = 0; saxis < 3; saxis++) { - for (auto b = 1; b < nbins; b++) { - auto split = cbbox.min[saxis] + b * csize[saxis] / nbins; - auto left_bbox = invalidb3f, right_bbox = invalidb3f; - auto left_nprims = 0, right_nprims = 0; - for (auto i = start; i < end; i++) { - if (primitives[i].center[saxis] < split) { - left_bbox = merge(left_bbox, primitives[i].bbox); - left_nprims += 1; - } else { - right_bbox = merge(right_bbox, primitives[i].bbox); - right_nprims += 1; - } - } - auto cost = 1 + left_nprims * area(left_bbox) / area(cbbox) + - right_nprims * area(right_bbox) / area(cbbox); - if (cost < min_cost) { - min_cost = cost; - middle = split; - split_axis = saxis; - } - } - } - // split - mid = (int)(std::partition(primitives.data() + start, primitives.data() + end, - [split_axis, middle](auto& primitive) { - return primitive.center[split_axis] < middle; - }) - - primitives.data()); - - // if we were not able to split, just break the primitives in half - if (mid == start || mid == end) { - split_axis = 0; - mid = (start + end) / 2; - // throw std::runtime_error("bad bvh split"); - } - - return {mid, split_axis}; -} - -// Splits a BVH node using the balance heuristic. Returns split position and -// axis. -static pair split_balanced( - vector& primitives, int start, int end) { - // initialize split axis and position - auto axis = 0; - auto mid = (start + end) / 2; - - // compute primintive bounds and size - auto cbbox = invalidb3f; - for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); - auto csize = cbbox.max - cbbox.min; - if (csize == zero3f) return {mid, axis}; - - // split along largest - if (csize.x >= csize.y && csize.x >= csize.z) axis = 0; - if (csize.y >= csize.x && csize.y >= csize.z) axis = 1; - if (csize.z >= csize.x && csize.z >= csize.y) axis = 2; - - // balanced tree split: find the largest axis of the - // bounding box and split along this one right in the middle - mid = (start + end) / 2; - std::nth_element(primitives.data() + start, primitives.data() + mid, - primitives.data() + end, [axis](auto& primitive_a, auto& primitive_b) { - return primitive_a.center[axis] < primitive_b.center[axis]; - }); - - // if we were not able to split, just break the primitives in half - if (mid == start || mid == end) { - // throw std::runtime_error("bad bvh split"); - mid = (start + end) / 2; - } - - return {mid, axis}; -} - -// Splits a BVH node using the middle heuristic. Returns split position and -// axis. -static pair split_middle( - vector& primitives, int start, int end) { - // initialize split axis and position - auto axis = 0; - auto mid = (start + end) / 2; - - // compute primintive bounds and size - auto cbbox = invalidb3f; - for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); - auto csize = cbbox.max - cbbox.min; - if (csize == zero3f) return {mid, axis}; - - // split along largest - if (csize.x >= csize.y && csize.x >= csize.z) axis = 0; - if (csize.y >= csize.x && csize.y >= csize.z) axis = 1; - if (csize.z >= csize.x && csize.z >= csize.y) axis = 2; - - // split the space in the middle along the largest axis - mid = (int)(std::partition(primitives.data() + start, primitives.data() + end, - [axis, middle = center(cbbox)[axis]](auto& primitive) { - return primitive.center[axis] < middle; - }) - - primitives.data()); - - // if we were not able to split, just break the primitives in half - if (mid == start || mid == end) { - // throw std::runtime_error("bad bvh split"); - mid = (start + end) / 2; - } - - return {mid, axis}; -} - -// Split bvh nodes according to a type -static pair split_nodes(vector& primitives, - int start, int end, trace_bvh_type type) { - switch (type) { - case trace_bvh_type::default_: return split_middle(primitives, start, end); - case trace_bvh_type::highquality: return split_sah(primitives, start, end); - case trace_bvh_type::middle: return split_middle(primitives, start, end); - case trace_bvh_type::balanced: - return split_balanced(primitives, start, end); - default: throw std::runtime_error("should not have gotten here"); - } -} - -// Maximum number of primitives per BVH node. -const int trace_bvh_max_prims = 4; - -// Build BVH nodes -static void build_bvh_serial(vector& nodes, - vector& primitives, trace_bvh_type type) { - // prepare to build nodes - nodes.clear(); - nodes.reserve(primitives.size() * 2); - - // queue up first node - auto queue = deque{{0, 0, (int)primitives.size()}}; - nodes.emplace_back(); - - // create nodes until the queue is empty - while (!queue.empty()) { - // grab node to work on - auto next = queue.front(); - queue.pop_front(); - auto nodeid = next.x, start = next.y, end = next.z; - - // grab node - auto& node = nodes[nodeid]; - - // compute bounds - node.bbox = invalidb3f; - for (auto i = start; i < end; i++) - node.bbox = merge(node.bbox, primitives[i].bbox); - - // split into two children - if (end - start > trace_bvh_max_prims) { - // get split - auto [mid, axis] = split_nodes(primitives, start, end, type); - - // make an internal node - node.internal = true; - node.axis = (uint8_t)axis; - node.num = 2; - node.start = (int)nodes.size(); - nodes.emplace_back(); - nodes.emplace_back(); - queue.push_back({node.start + 0, start, mid}); - queue.push_back({node.start + 1, mid, end}); - } else { - // Make a leaf node - node.internal = false; - node.num = (int16_t)(end - start); - node.start = start; - } - } - - // cleanup - nodes.shrink_to_fit(); -} - -#if 0 - -// Build BVH nodes -static void build_bvh_parallel( - const shared_ptr& bvh, vector& bboxes, bvh_type type) { - // get values - auto& nodes = bvh->nodes; - auto& primitives = bvh->primitives; - - // prepare to build nodes - nodes.clear(); - nodes.reserve(bboxes.size() * 2); - - // prepare primitives - bvh->primitives.resize(bboxes.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) bvh->primitives[idx] = idx; - - // prepare centers - auto centers = vector(bboxes.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) - centers[idx] = center(bboxes[idx]); - - // queue up first node - auto queue = deque{{0, 0, (int)primitives.size()}}; - nodes.emplace_back(); - - // synchronization - atomic num_processed_prims(0); - std::mutex queue_mutex; - vector> futures; - auto nthreads = std::thread::hardware_concurrency(); - - // create nodes until the queue is empty - for (auto thread_id = 0; thread_id < nthreads; thread_id++) { - futures.emplace_back(std::async( - std::launch::async, [&nodes, &primitives, &bboxes, ¢ers, &type, - &num_processed_prims, &queue_mutex, &queue] { - while (true) { - // exit if needed - if (num_processed_prims >= primitives.size()) return; - - // grab node to work on - auto next = zero3i; - { - std::lock_guard lock{queue_mutex}; - if (!queue.empty()) { - next = queue.front(); - queue.pop_front(); - } - } - - // wait a bit if needed - if (next == zero3i) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); - continue; - } - - // grab node - auto nodeid = next.x, start = next.y, end = next.z; - auto& node = nodes[nodeid]; - - // compute bounds - node.bbox = invalidb3f; - for (auto i = start; i < end; i++) - node.bbox = merge(node.bbox, bboxes[primitives[i]]); - - // split into two children - if (end - start > bvh_max_prims) { - // get split - auto [mid, axis] = split_nodes( - primitives, bboxes, centers, start, end, type); - - // make an internal node - { - std::lock_guard lock{queue_mutex}; - node.internal = true; - node.axis = axis; - node.num = 2; - node.start = (int)nodes.size(); - nodes.emplace_back(); - nodes.emplace_back(); - queue.push_back({node.start + 0, start, mid}); - queue.push_back({node.start + 1, mid, end}); - } - } else { - // Make a leaf node - node.internal = false; - node.num = end - start; - node.start = start; - num_processed_prims += node.num; - } - } - })); - } - for (auto& f : futures) f.get(); - - // cleanup - nodes.shrink_to_fit(); -} - -#endif - -// Update bvh -static void update_bvh(trace_bvh* bvh, const vector& bboxes) { - for (auto nodeid = (int)bvh->nodes.size() - 1; nodeid >= 0; nodeid--) { - auto& node = bvh->nodes[nodeid]; - node.bbox = invalidb3f; - if (node.internal) { - for (auto idx = 0; idx < 2; idx++) { - node.bbox = merge(node.bbox, bvh->nodes[node.start + idx].bbox); - } - } else { - for (auto idx = 0; idx < node.num; idx++) { - node.bbox = merge(node.bbox, bboxes[node.start + idx]); - } - } - } -} - -static void init_bvh(trace_shape* shape, const trace_params& params) { -#ifdef YOCTO_EMBREE - // call Embree if needed - if (params.bvh == trace_bvh_type::embree_default || - params.bvh == trace_bvh_type::embree_highquality || - params.bvh == trace_bvh_type::embree_compact) { - return init_embree_bvh(shape, params); - } -#endif - - // build primitives - auto primitives = vector{}; - if (!shape->points.empty()) { - for (auto idx = 0; idx < shape->points.size(); idx++) { - auto& p = shape->points[idx]; - auto& primitive = primitives.emplace_back(); - primitive.bbox = point_bounds(shape->positions[p], shape->radius[p]); - primitive.center = center(primitive.bbox); - primitive.primitive = idx; - } - } else if (!shape->lines.empty()) { - for (auto idx = 0; idx < shape->lines.size(); idx++) { - auto& l = shape->lines[idx]; - auto& primitive = primitives.emplace_back(); - primitive.bbox = line_bounds(shape->positions[l.x], shape->positions[l.y], - shape->radius[l.x], shape->radius[l.y]); - primitive.center = center(primitive.bbox); - primitive.primitive = idx; - } - } else if (!shape->triangles.empty()) { - for (auto idx = 0; idx < shape->triangles.size(); idx++) { - auto& primitive = primitives.emplace_back(); - auto& t = shape->triangles[idx]; - primitive.bbox = triangle_bounds( - shape->positions[t.x], shape->positions[t.y], shape->positions[t.z]); - primitive.center = center(primitive.bbox); - primitive.primitive = idx; - } - } else if (!shape->quads.empty()) { - for (auto idx = 0; idx < shape->quads.size(); idx++) { - auto& q = shape->quads[idx]; - auto& primitive = primitives.emplace_back(); - primitive.bbox = quad_bounds(shape->positions[q.x], shape->positions[q.y], - shape->positions[q.z], shape->positions[q.w]); - primitive.center = center(primitive.bbox); - primitive.primitive = idx; - } - } - - // build nodes - delete shape->bvh; - shape->bvh = new trace_bvh{}; - build_bvh_serial(shape->bvh->nodes, primitives, params.bvh); - - // set bvh primitives - shape->bvh->primitives.reserve(primitives.size()); - for (auto& primitive : primitives) { - shape->bvh->primitives.push_back(primitive.primitive); - } -} - -void init_bvh(trace_scene* scene, const trace_params& params, - const progress_callback& progress_cb) { - // handle progress - auto progress = vec2i{0, 1 + (int)scene->shapes.size()}; - - // shapes - for (auto shape : scene->shapes) { - if (progress_cb) progress_cb("build shape bvh", progress.x++, progress.y); - init_bvh(shape, params); - } - - // embree -#ifdef YOCTO_EMBREE - if (params.bvh == trace_bvh_type::embree_default || - params.bvh == trace_bvh_type::embree_highquality || - params.bvh == trace_bvh_type::embree_compact) { - return init_embree_bvh(scene, params); - } -#endif - - // handle progress - if (progress_cb) progress_cb("build scene bvh", progress.x++, progress.y); - - // instance bboxes - auto primitives = vector{}; - auto object_id = 0; - auto empty_instance_frames = vector{identity3x4f}; - for (auto instance : scene->instances) { - auto& primitive = primitives.emplace_back(); - primitive.bbox = instance->shape->bvh->nodes.empty() - ? invalidb3f - : transform_bbox(instance->frame, - instance->shape->bvh->nodes[0].bbox); - primitive.center = center(primitive.bbox); - primitive.primitive = object_id++; - } - - // build nodes - delete scene->bvh; - scene->bvh = new trace_bvh{}; - build_bvh_serial(scene->bvh->nodes, primitives, params.bvh); - - // set bvh primitives - scene->bvh->primitives.reserve(primitives.size()); - for (auto& primitive : primitives) { - scene->bvh->primitives.push_back(primitive.primitive); - } - - // handle progress - if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); -} - -static void update_bvh(trace_shape* shape, const trace_params& params) { -#ifdef YOCTO_EMBREE - if (shape->embree_bvh) { - throw std::runtime_error("embree shape update not implemented"); - } -#endif - - // build primitives - auto bboxes = vector(shape->bvh->primitives.size()); - if (!shape->points.empty()) { - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto& p = shape->points[shape->bvh->primitives[idx]]; - bboxes[idx] = point_bounds(shape->positions[p], shape->radius[p]); - } - } else if (!shape->lines.empty()) { - bboxes = vector(shape->lines.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto& l = shape->lines[shape->bvh->primitives[idx]]; - bboxes[idx] = line_bounds(shape->positions[l.x], shape->positions[l.y], - shape->radius[l.x], shape->radius[l.y]); - } - } else if (!shape->triangles.empty()) { - bboxes = vector(shape->triangles.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto& t = shape->triangles[shape->bvh->primitives[idx]]; - bboxes[idx] = triangle_bounds( - shape->positions[t.x], shape->positions[t.y], shape->positions[t.z]); - } - } else if (!shape->quads.empty()) { - bboxes = vector(shape->quads.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto& q = shape->quads[shape->bvh->primitives[idx]]; - bboxes[idx] = quad_bounds(shape->positions[q.x], shape->positions[q.y], - shape->positions[q.z], shape->positions[q.w]); - } - } - - // update nodes - update_bvh(shape->bvh, bboxes); -} - -void update_bvh(trace_scene* scene, - const vector& updated_objects, - const vector& updated_shapes, const trace_params& params) { - for (auto shape : updated_shapes) update_bvh(shape, params); - -#ifdef YOCTO_EMBREE - if (scene->embree_bvh) { - update_embree_bvh(scene, updated_objects, updated_shapes, params); - } -#endif - - // build primitives - auto bboxes = vector(scene->bvh->primitives.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto instance = scene->instances[scene->bvh->primitives[idx]]; - auto sbvh = instance->shape->bvh; - bboxes[idx] = transform_bbox(instance->frame, sbvh->nodes[0].bbox); - } - - // update nodes - update_bvh(scene->bvh, bboxes); -} - -// Intersect ray with a bvh-> -static bool intersect_shape_bvh(trace_shape* shape, const ray3f& ray_, - int& element, vec2f& uv, float& distance, bool find_any) { -#ifdef YOCTO_EMBREE - // call Embree if needed - if (shape->embree_bvh) { - return intersect_shape_embree_bvh( - shape, ray_, element, uv, distance, find_any); - } -#endif - - // get bvh and shape pointers for fast access - auto bvh = shape->bvh; - - // check empty - if (bvh->nodes.empty()) return false; - - // node stack - auto node_stack = array{}; - auto node_cur = 0; - node_stack[node_cur++] = 0; - - // shared variables - auto hit = false; - - // copy ray to modify it - auto ray = ray_; - - // prepare ray for fast queries - auto ray_dinv = vec3f{1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z}; - auto ray_dsign = vec3i{(ray_dinv.x < 0) ? 1 : 0, (ray_dinv.y < 0) ? 1 : 0, - (ray_dinv.z < 0) ? 1 : 0}; - - // walking stack - while (node_cur != 0) { - // grab node - auto& node = bvh->nodes[node_stack[--node_cur]]; - - // intersect bbox - // if (!intersect_bbox(ray, ray_dinv, ray_dsign, node.bbox)) continue; - if (!intersect_bbox(ray, ray_dinv, node.bbox)) continue; - - // intersect node, switching based on node type - // for each type, iterate over the the primitive list - if (node.internal) { - // for internal nodes, attempts to proceed along the - // split axis from smallest to largest nodes - if (ray_dsign[node.axis] != 0) { - node_stack[node_cur++] = node.start + 0; - node_stack[node_cur++] = node.start + 1; - } else { - node_stack[node_cur++] = node.start + 1; - node_stack[node_cur++] = node.start + 0; - } - } else if (!shape->points.empty()) { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto& p = shape->points[shape->bvh->primitives[idx]]; - if (intersect_point( - ray, shape->positions[p], shape->radius[p], uv, distance)) { - hit = true; - element = shape->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } else if (!shape->lines.empty()) { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto& l = shape->lines[shape->bvh->primitives[idx]]; - if (intersect_line(ray, shape->positions[l.x], shape->positions[l.y], - shape->radius[l.x], shape->radius[l.y], uv, distance)) { - hit = true; - element = shape->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } else if (!shape->triangles.empty()) { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto& t = shape->triangles[shape->bvh->primitives[idx]]; - if (intersect_triangle(ray, shape->positions[t.x], - shape->positions[t.y], shape->positions[t.z], uv, distance)) { - hit = true; - element = shape->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } else if (!shape->quads.empty()) { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto& q = shape->quads[shape->bvh->primitives[idx]]; - if (intersect_quad(ray, shape->positions[q.x], shape->positions[q.y], - shape->positions[q.z], shape->positions[q.w], uv, distance)) { - hit = true; - element = shape->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } - - // check for early exit - if (find_any && hit) return hit; - } - - return hit; -} - -// Intersect ray with a bvh-> -static bool intersect_scene_bvh(const trace_scene* scene, const ray3f& ray_, - int& instance, int& element, vec2f& uv, float& distance, bool find_any, - bool non_rigid_frames) { -#ifdef YOCTO_EMBREE - // call Embree if needed - if (scene->embree_bvh) { - return intersect_scene_embree_bvh( - scene, ray_, instance, element, uv, distance, find_any); - } -#endif - - // get bvh and scene pointers for fast access - auto bvh = scene->bvh; - - // check empty - if (bvh->nodes.empty()) return false; - - // node stack - auto node_stack = array{}; - auto node_cur = 0; - node_stack[node_cur++] = 0; - - // shared variables - auto hit = false; - - // copy ray to modify it - auto ray = ray_; - - // prepare ray for fast queries - auto ray_dinv = vec3f{1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z}; - auto ray_dsign = vec3i{(ray_dinv.x < 0) ? 1 : 0, (ray_dinv.y < 0) ? 1 : 0, - (ray_dinv.z < 0) ? 1 : 0}; - - // walking stack - while (node_cur != 0) { - // grab node - auto& node = bvh->nodes[node_stack[--node_cur]]; - - // intersect bbox - // if (!intersect_bbox(ray, ray_dinv, ray_dsign, node.bbox)) continue; - if (!intersect_bbox(ray, ray_dinv, node.bbox)) continue; - - // intersect node, switching based on node type - // for each type, iterate over the the primitive list - if (node.internal) { - // for internal nodes, attempts to proceed along the - // split axis from smallest to largest nodes - if (ray_dsign[node.axis] != 0) { - node_stack[node_cur++] = node.start + 0; - node_stack[node_cur++] = node.start + 1; - } else { - node_stack[node_cur++] = node.start + 1; - node_stack[node_cur++] = node.start + 0; - } - } else { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto object_ = scene->instances[scene->bvh->primitives[idx]]; - auto inv_ray = transform_ray( - inverse(object_->frame, non_rigid_frames), ray); - if (intersect_shape_bvh( - object_->shape, inv_ray, element, uv, distance, find_any)) { - hit = true; - instance = scene->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } - - // check for early exit - if (find_any && hit) return hit; - } - - return hit; -} - -// Intersect ray with a bvh-> -static bool intersect_instance_bvh(const trace_instance* instance, - const ray3f& ray, int& element, vec2f& uv, float& distance, bool find_any, - bool non_rigid_frames) { - auto inv_ray = transform_ray(inverse(instance->frame, non_rigid_frames), ray); - return intersect_shape_bvh( - instance->shape, inv_ray, element, uv, distance, find_any); -} - -trace_intersection intersect_scene_bvh(const trace_scene* scene, - const ray3f& ray, bool find_any, bool non_rigid_frames) { - auto intersection = trace_intersection{}; - intersection.hit = intersect_scene_bvh(scene, ray, intersection.instance, - intersection.element, intersection.uv, intersection.distance, find_any, - non_rigid_frames); - return intersection; -} -trace_intersection intersect_instance_bvh(const trace_instance* instance, - const ray3f& ray, bool find_any, bool non_rigid_frames) { - auto intersection = trace_intersection{}; - intersection.hit = intersect_instance_bvh(instance, ray, intersection.element, - intersection.uv, intersection.distance, find_any, non_rigid_frames); - return intersection; -} - -} // namespace yocto - -#endif - // ----------------------------------------------------------------------------- // IMPLEMENTATION FOR PATH TRACING // ----------------------------------------------------------------------------- From 238894fccb550dc8262078ce8bdeb25d18d84ce1 Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 07:43:48 +0200 Subject: [PATCH 03/15] updated --- libs/yocto/yocto_bvh.cpp | 6 +++--- libs/yocto/yocto_trace.cpp | 20 ++++++++++++++++++++ libs/yocto/yocto_trace.h | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index a10497a0a..d0bad60b7 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -628,7 +628,7 @@ void init_bvh(bvh_scene* scene, bool embree) { build_bvh(scene->bvh, bboxes); } -void update_shape_bvh(bvh_shape* shape) { +void update_bvh(bvh_shape* shape) { #ifdef YOCTO_EMBREE if (shape->embree_bvh) { throw std::runtime_error("embree shape refit not supported"); @@ -670,10 +670,10 @@ void update_shape_bvh(bvh_shape* shape) { update_bvh(shape->bvh, bboxes); } -void update_scene_bvh(bvh_scene* scene, const vector& updated_instances, +void update_bvh(bvh_scene* scene, const vector& updated_instances, const vector& updated_shapes) { // update shapes - for (auto shape : updated_shapes) update_shape_bvh(scene->shapes[shape]); + for (auto shape : updated_shapes) update_bvh(scene->shapes[shape]); #ifdef YOCTO_EMBREE if (scene->embree_bvh) { diff --git a/libs/yocto/yocto_trace.cpp b/libs/yocto/yocto_trace.cpp index 2fa8b46a5..a368513ec 100644 --- a/libs/yocto/yocto_trace.cpp +++ b/libs/yocto/yocto_trace.cpp @@ -1005,6 +1005,26 @@ void init_bvh(trace_scene* scene, const trace_params& params, init_bvh(scene->bvh); } +// Refit bvh data +void update_bvh(trace_scene* scene, + const vector& updated_instances, + const vector& updated_shapes, const trace_params& params) { + auto updated_instances_ids = vector{}; + auto updated_shapes_ids = vector{}; + for (auto shape : updated_shapes) { + updated_shapes_ids.push_back( + (int)(std::find(scene->shapes.begin(), scene->shapes.end(), shape) - + scene->shapes.begin())); + } + for (auto instance : updated_instances) { + updated_instances_ids.push_back( + (int)(std::find( + scene->instances.begin(), scene->instances.end(), instance) - + scene->instances.begin())); + } + update_bvh(scene->bvh, updated_instances_ids, updated_shapes_ids); +} + // Intersect ray with a bvh returning either the first or any intersection. trace_intersection intersect_scene_bvh(const trace_scene* scene, const ray3f& ray, bool find_any, bool non_rigid_frames) { diff --git a/libs/yocto/yocto_trace.h b/libs/yocto/yocto_trace.h index 0b21f1b64..135dc819d 100644 --- a/libs/yocto/yocto_trace.h +++ b/libs/yocto/yocto_trace.h @@ -526,7 +526,7 @@ void init_bvh(trace_scene* scene, const trace_params& params, // Refit bvh data void update_bvh(trace_scene* scene, - const vector& updated_objects, + const vector& updated_instances, const vector& updated_shapes, const trace_params& params); // Progressively computes an image. From 341b6b12ebb791aaf2c40714f34326cb3bf68113 Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 07:49:44 +0200 Subject: [PATCH 04/15] updated --- libs/yocto/yocto_bvh.cpp | 83 +--------------------------------------- 1 file changed, 1 insertion(+), 82 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index d0bad60b7..19e4cde05 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -677,7 +677,7 @@ void update_bvh(bvh_scene* scene, const vector& updated_instances, #ifdef YOCTO_EMBREE if (scene->embree_bvh) { - update_scene_embree_bvh(scene, updated_instances); + update_embree_bvh(scene, updated_instances); } #endif @@ -1676,23 +1676,6 @@ static void build_bvh_parallel( #endif -// Update bvh -static void update_bvh(trace_bvh* bvh, const vector& bboxes) { - for (auto nodeid = (int)bvh->nodes.size() - 1; nodeid >= 0; nodeid--) { - auto& node = bvh->nodes[nodeid]; - node.bbox = invalidb3f; - if (node.internal) { - for (auto idx = 0; idx < 2; idx++) { - node.bbox = merge(node.bbox, bvh->nodes[node.start + idx].bbox); - } - } else { - for (auto idx = 0; idx < node.num; idx++) { - node.bbox = merge(node.bbox, bboxes[node.start + idx]); - } - } - } -} - static void init_bvh(trace_shape* shape, const trace_params& params) { #ifdef YOCTO_EMBREE // call Embree if needed @@ -1806,70 +1789,6 @@ void init_bvh(trace_scene* scene, const trace_params& params, if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); } -static void update_bvh(trace_shape* shape, const trace_params& params) { -#ifdef YOCTO_EMBREE - if (shape->embree_bvh) { - throw std::runtime_error("embree shape update not implemented"); - } -#endif - - // build primitives - auto bboxes = vector(shape->bvh->primitives.size()); - if (!shape->points.empty()) { - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto& p = shape->points[shape->bvh->primitives[idx]]; - bboxes[idx] = point_bounds(shape->positions[p], shape->radius[p]); - } - } else if (!shape->lines.empty()) { - bboxes = vector(shape->lines.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto& l = shape->lines[shape->bvh->primitives[idx]]; - bboxes[idx] = line_bounds(shape->positions[l.x], shape->positions[l.y], - shape->radius[l.x], shape->radius[l.y]); - } - } else if (!shape->triangles.empty()) { - bboxes = vector(shape->triangles.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto& t = shape->triangles[shape->bvh->primitives[idx]]; - bboxes[idx] = triangle_bounds( - shape->positions[t.x], shape->positions[t.y], shape->positions[t.z]); - } - } else if (!shape->quads.empty()) { - bboxes = vector(shape->quads.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto& q = shape->quads[shape->bvh->primitives[idx]]; - bboxes[idx] = quad_bounds(shape->positions[q.x], shape->positions[q.y], - shape->positions[q.z], shape->positions[q.w]); - } - } - - // update nodes - update_bvh(shape->bvh, bboxes); -} - -void update_bvh(trace_scene* scene, - const vector& updated_objects, - const vector& updated_shapes, const trace_params& params) { - for (auto shape : updated_shapes) update_bvh(shape, params); - -#ifdef YOCTO_EMBREE - if (scene->embree_bvh) { - update_embree_bvh(scene, updated_objects, updated_shapes, params); - } -#endif - - // build primitives - auto bboxes = vector(scene->bvh->primitives.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) { - auto instance = scene->instances[scene->bvh->primitives[idx]]; - auto sbvh = instance->shape->bvh; - bboxes[idx] = transform_bbox(instance->frame, sbvh->nodes[0].bbox); - } - - // update nodes - update_bvh(scene->bvh, bboxes); -} - // Intersect ray with a bvh-> static bool intersect_shape_bvh(trace_shape* shape, const ray3f& ray_, int& element, vec2f& uv, float& distance, bool find_any) { From 24100e683c7af84067cc4e29cd0a19839393f738 Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 08:00:14 +0200 Subject: [PATCH 05/15] updated --- libs/yocto/yocto_bvh.cpp | 179 --------------------------------------- 1 file changed, 179 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index 19e4cde05..fab0f79de 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -1789,185 +1789,6 @@ void init_bvh(trace_scene* scene, const trace_params& params, if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); } -// Intersect ray with a bvh-> -static bool intersect_shape_bvh(trace_shape* shape, const ray3f& ray_, - int& element, vec2f& uv, float& distance, bool find_any) { -#ifdef YOCTO_EMBREE - // call Embree if needed - if (shape->embree_bvh) { - return intersect_shape_embree_bvh( - shape, ray_, element, uv, distance, find_any); - } -#endif - - // get bvh and shape pointers for fast access - auto bvh = shape->bvh; - - // check empty - if (bvh->nodes.empty()) return false; - - // node stack - auto node_stack = array{}; - auto node_cur = 0; - node_stack[node_cur++] = 0; - - // shared variables - auto hit = false; - - // copy ray to modify it - auto ray = ray_; - - // prepare ray for fast queries - auto ray_dinv = vec3f{1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z}; - auto ray_dsign = vec3i{(ray_dinv.x < 0) ? 1 : 0, (ray_dinv.y < 0) ? 1 : 0, - (ray_dinv.z < 0) ? 1 : 0}; - - // walking stack - while (node_cur != 0) { - // grab node - auto& node = bvh->nodes[node_stack[--node_cur]]; - - // intersect bbox - // if (!intersect_bbox(ray, ray_dinv, ray_dsign, node.bbox)) continue; - if (!intersect_bbox(ray, ray_dinv, node.bbox)) continue; - - // intersect node, switching based on node type - // for each type, iterate over the the primitive list - if (node.internal) { - // for internal nodes, attempts to proceed along the - // split axis from smallest to largest nodes - if (ray_dsign[node.axis] != 0) { - node_stack[node_cur++] = node.start + 0; - node_stack[node_cur++] = node.start + 1; - } else { - node_stack[node_cur++] = node.start + 1; - node_stack[node_cur++] = node.start + 0; - } - } else if (!shape->points.empty()) { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto& p = shape->points[shape->bvh->primitives[idx]]; - if (intersect_point( - ray, shape->positions[p], shape->radius[p], uv, distance)) { - hit = true; - element = shape->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } else if (!shape->lines.empty()) { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto& l = shape->lines[shape->bvh->primitives[idx]]; - if (intersect_line(ray, shape->positions[l.x], shape->positions[l.y], - shape->radius[l.x], shape->radius[l.y], uv, distance)) { - hit = true; - element = shape->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } else if (!shape->triangles.empty()) { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto& t = shape->triangles[shape->bvh->primitives[idx]]; - if (intersect_triangle(ray, shape->positions[t.x], - shape->positions[t.y], shape->positions[t.z], uv, distance)) { - hit = true; - element = shape->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } else if (!shape->quads.empty()) { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto& q = shape->quads[shape->bvh->primitives[idx]]; - if (intersect_quad(ray, shape->positions[q.x], shape->positions[q.y], - shape->positions[q.z], shape->positions[q.w], uv, distance)) { - hit = true; - element = shape->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } - - // check for early exit - if (find_any && hit) return hit; - } - - return hit; -} - -// Intersect ray with a bvh-> -static bool intersect_scene_bvh(const trace_scene* scene, const ray3f& ray_, - int& instance, int& element, vec2f& uv, float& distance, bool find_any, - bool non_rigid_frames) { -#ifdef YOCTO_EMBREE - // call Embree if needed - if (scene->embree_bvh) { - return intersect_scene_embree_bvh( - scene, ray_, instance, element, uv, distance, find_any); - } -#endif - - // get bvh and scene pointers for fast access - auto bvh = scene->bvh; - - // check empty - if (bvh->nodes.empty()) return false; - - // node stack - auto node_stack = array{}; - auto node_cur = 0; - node_stack[node_cur++] = 0; - - // shared variables - auto hit = false; - - // copy ray to modify it - auto ray = ray_; - - // prepare ray for fast queries - auto ray_dinv = vec3f{1 / ray.d.x, 1 / ray.d.y, 1 / ray.d.z}; - auto ray_dsign = vec3i{(ray_dinv.x < 0) ? 1 : 0, (ray_dinv.y < 0) ? 1 : 0, - (ray_dinv.z < 0) ? 1 : 0}; - - // walking stack - while (node_cur != 0) { - // grab node - auto& node = bvh->nodes[node_stack[--node_cur]]; - - // intersect bbox - // if (!intersect_bbox(ray, ray_dinv, ray_dsign, node.bbox)) continue; - if (!intersect_bbox(ray, ray_dinv, node.bbox)) continue; - - // intersect node, switching based on node type - // for each type, iterate over the the primitive list - if (node.internal) { - // for internal nodes, attempts to proceed along the - // split axis from smallest to largest nodes - if (ray_dsign[node.axis] != 0) { - node_stack[node_cur++] = node.start + 0; - node_stack[node_cur++] = node.start + 1; - } else { - node_stack[node_cur++] = node.start + 1; - node_stack[node_cur++] = node.start + 0; - } - } else { - for (auto idx = node.start; idx < node.start + node.num; idx++) { - auto object_ = scene->instances[scene->bvh->primitives[idx]]; - auto inv_ray = transform_ray( - inverse(object_->frame, non_rigid_frames), ray); - if (intersect_shape_bvh( - object_->shape, inv_ray, element, uv, distance, find_any)) { - hit = true; - instance = scene->bvh->primitives[idx]; - ray.tmax = distance; - } - } - } - - // check for early exit - if (find_any && hit) return hit; - } - - return hit; -} - // Intersect ray with a bvh-> static bool intersect_instance_bvh(const trace_instance* instance, const ray3f& ray, int& element, vec2f& uv, float& distance, bool find_any, From 5e5fd3b73aa6c66c22704ba6118f74dd371d903a Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 08:01:33 +0200 Subject: [PATCH 06/15] updated --- libs/yocto/yocto_bvh.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index fab0f79de..a62af67c8 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -2,6 +2,30 @@ // Implementation for Yocto/Bvh // +// +// LICENSE: +// +// Copyright (c) 2016 -- 2020 Fabio Pellacini +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + // ----------------------------------------------------------------------------- // INCLUDES // ----------------------------------------------------------------------------- From acdde0c2b0c3ae87e3943264c1c32921139a9d0f Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 08:19:34 +0200 Subject: [PATCH 07/15] updated --- libs/yocto/yocto_bvh.cpp | 164 ++++++++++----------------------------- libs/yocto/yocto_bvh.h | 26 ++++++- 2 files changed, 66 insertions(+), 124 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index a62af67c8..1870e8db4 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -503,6 +503,23 @@ static pair split_middle(vector& primitives, #pragma GCC diagnostic pop #endif +// Split bvh nodes according to a type +static pair split_nodes(vector& primitives, + const vector& bboxes, const vector& centers, int start, + int end, bvh_type type) { + switch (type) { + case bvh_type::default_: + return split_middle(primitives, bboxes, centers, start, end); + case bvh_type::highquality: + return split_sah(primitives, bboxes, centers, start, end); + case bvh_type::middle: + return split_middle(primitives, bboxes, centers, start, end); + case bvh_type::balanced: + return split_balanced(primitives, bboxes, centers, start, end); + default: throw std::runtime_error("should not have gotten here"); + } +} + // Build BVH nodes static void build_bvh(bvh_tree_& bvh, vector& bboxes) { // get values @@ -584,11 +601,12 @@ static void update_bvh(bvh_tree_& bvh, const vector& bboxes) { } } -void init_bvh(bvh_shape* shape, bool embree) { +void init_bvh(bvh_shape* shape, bvh_type type) { #ifdef YOCTO_EMBREE - // call Embree if needed - if (embree) { - return init_shape_embree_bvh(shape); + if (params.bvh == bvh_type::embree_default || + params.bvh == bvh_type::embree_highquality || + params.bvh == bvh_type::embree_compact) { + return init_embree_bvh(shape, type); } #endif @@ -627,17 +645,29 @@ void init_bvh(bvh_shape* shape, bool embree) { build_bvh(shape->bvh, bboxes); } -void init_bvh(bvh_scene* scene, bool embree) { +void init_bvh( + bvh_scene* scene, bvh_type type, const progress_callback& progress_cb) { + // handle progress + auto progress = vec2i{0, 1 + (int)scene->shapes.size()}; + // Make shape bvh - for (auto shape : scene->shapes) init_bvh(shape, embree); + for (auto shape : scene->shapes) { + if (progress_cb) progress_cb("build shape bvh", progress.x++, progress.y); + init_bvh(shape, type); + } - // embree + // embree #ifdef YOCTO_EMBREE - if (embree) { - return init_scene_embree_bvh(scene); + if (params.bvh == bvh_type::embree_default || + params.bvh == bvh_type::embree_highquality || + params.bvh == bvh_type::embree_compact) { + return init_embree_bvh(scene, type); } #endif + // handle progress + if (progress_cb) progress_cb("build scene bvh", progress.x++, progress.y); + // instance bboxes auto bboxes = vector(scene->instances.size()); for (auto idx = 0; idx < bboxes.size(); idx++) { @@ -650,6 +680,9 @@ void init_bvh(bvh_scene* scene, bool embree) { // build nodes build_bvh(scene->bvh, bboxes); + + // handle progress + if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); } void update_bvh(bvh_shape* shape) { @@ -1700,119 +1733,6 @@ static void build_bvh_parallel( #endif -static void init_bvh(trace_shape* shape, const trace_params& params) { -#ifdef YOCTO_EMBREE - // call Embree if needed - if (params.bvh == trace_bvh_type::embree_default || - params.bvh == trace_bvh_type::embree_highquality || - params.bvh == trace_bvh_type::embree_compact) { - return init_embree_bvh(shape, params); - } -#endif - - // build primitives - auto primitives = vector{}; - if (!shape->points.empty()) { - for (auto idx = 0; idx < shape->points.size(); idx++) { - auto& p = shape->points[idx]; - auto& primitive = primitives.emplace_back(); - primitive.bbox = point_bounds(shape->positions[p], shape->radius[p]); - primitive.center = center(primitive.bbox); - primitive.primitive = idx; - } - } else if (!shape->lines.empty()) { - for (auto idx = 0; idx < shape->lines.size(); idx++) { - auto& l = shape->lines[idx]; - auto& primitive = primitives.emplace_back(); - primitive.bbox = line_bounds(shape->positions[l.x], shape->positions[l.y], - shape->radius[l.x], shape->radius[l.y]); - primitive.center = center(primitive.bbox); - primitive.primitive = idx; - } - } else if (!shape->triangles.empty()) { - for (auto idx = 0; idx < shape->triangles.size(); idx++) { - auto& primitive = primitives.emplace_back(); - auto& t = shape->triangles[idx]; - primitive.bbox = triangle_bounds( - shape->positions[t.x], shape->positions[t.y], shape->positions[t.z]); - primitive.center = center(primitive.bbox); - primitive.primitive = idx; - } - } else if (!shape->quads.empty()) { - for (auto idx = 0; idx < shape->quads.size(); idx++) { - auto& q = shape->quads[idx]; - auto& primitive = primitives.emplace_back(); - primitive.bbox = quad_bounds(shape->positions[q.x], shape->positions[q.y], - shape->positions[q.z], shape->positions[q.w]); - primitive.center = center(primitive.bbox); - primitive.primitive = idx; - } - } - - // build nodes - delete shape->bvh; - shape->bvh = new trace_bvh{}; - build_bvh_serial(shape->bvh->nodes, primitives, params.bvh); - - // set bvh primitives - shape->bvh->primitives.reserve(primitives.size()); - for (auto& primitive : primitives) { - shape->bvh->primitives.push_back(primitive.primitive); - } -} - -void init_bvh(trace_scene* scene, const trace_params& params, - const progress_callback& progress_cb) { - // handle progress - auto progress = vec2i{0, 1 + (int)scene->shapes.size()}; - - // shapes - for (auto shape : scene->shapes) { - if (progress_cb) progress_cb("build shape bvh", progress.x++, progress.y); - init_bvh(shape, params); - } - - // embree -#ifdef YOCTO_EMBREE - if (params.bvh == trace_bvh_type::embree_default || - params.bvh == trace_bvh_type::embree_highquality || - params.bvh == trace_bvh_type::embree_compact) { - return init_embree_bvh(scene, params); - } -#endif - - // handle progress - if (progress_cb) progress_cb("build scene bvh", progress.x++, progress.y); - - // instance bboxes - auto primitives = vector{}; - auto object_id = 0; - auto empty_instance_frames = vector{identity3x4f}; - for (auto instance : scene->instances) { - auto& primitive = primitives.emplace_back(); - primitive.bbox = instance->shape->bvh->nodes.empty() - ? invalidb3f - : transform_bbox(instance->frame, - instance->shape->bvh->nodes[0].bbox); - primitive.center = center(primitive.bbox); - primitive.primitive = object_id++; - } - - // build nodes - delete scene->bvh; - scene->bvh = new trace_bvh{}; - build_bvh_serial(scene->bvh->nodes, primitives, params.bvh); - - // set bvh primitives - scene->bvh->primitives.reserve(primitives.size()); - for (auto& primitive : primitives) { - scene->bvh->primitives.push_back(primitive.primitive); - } - - // handle progress - if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); -} - // Intersect ray with a bvh-> static bool intersect_instance_bvh(const trace_instance* instance, const ray3f& ray, int& element, vec2f& uv, float& distance, bool find_any, diff --git a/libs/yocto/yocto_bvh.h b/libs/yocto/yocto_bvh.h index 0916d8183..e5b025ed0 100644 --- a/libs/yocto/yocto_bvh.h +++ b/libs/yocto/yocto_bvh.h @@ -40,6 +40,8 @@ #include #include +#include +#include #include #include "yocto_math.h" @@ -55,6 +57,8 @@ namespace yocto { // using directives using std::array; +using std::function; +using std::string; using std::vector; } // namespace yocto @@ -152,9 +156,27 @@ bvh_shape* add_shape(bvh_scene* bvh, const vector& points, bvh_instance* add_instance( bvh_scene* bvh, const frame3f& frame, bvh_shape* shape); +// Strategy used to build the bvh +enum struct bvh_type { + default_, + highquality, + middle, + balanced, +#ifdef YOCTO_EMBREE + embree_default, + embree_highquality, + embree_compact // only for copy interface +#endif +}; + +// Progress report callback +using progress_callback = + function; + // Build the bvh acceleration structure. -void init_bvh(bvh_shape* bvh, bool embree = false); -void init_bvh(bvh_scene* bvh, bool embree = false); +void init_bvh(bvh_shape* bvh, bvh_type type = bvh_type::default_); +void init_bvh(bvh_scene* bvh, bvh_type type = bvh_type::default_, + const progress_callback& progress_cb = {}); // Refit bvh data void update_bvh(bvh_shape* bvh); From 548bf40fb65084f015eb40ddbf0d6ebc98893f88 Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 08:28:09 +0200 Subject: [PATCH 08/15] updated --- libs/yocto/yocto_bvh.cpp | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index 1870e8db4..2e35e90d3 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -349,15 +349,6 @@ bvh_instance* add_instance( // ----------------------------------------------------------------------------- namespace yocto { -#if !defined(_WIN32) && !defined(_WIN64) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#pragma GCC diagnostic ignored "-Wunused-variable" -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -#endif -#endif - // Splits a BVH node using the SAH heuristic. Returns split position and axis. static pair split_sah(vector& primitives, const vector& bboxes, const vector& centers, int start, @@ -499,10 +490,6 @@ static pair split_middle(vector& primitives, return {mid, axis}; } -#if !defined(_WIN32) && !defined(_WIN64) -#pragma GCC diagnostic pop -#endif - // Split bvh nodes according to a type static pair split_nodes(vector& primitives, const vector& bboxes, const vector& centers, int start, @@ -521,7 +508,7 @@ static pair split_nodes(vector& primitives, } // Build BVH nodes -static void build_bvh(bvh_tree_& bvh, vector& bboxes) { +static void build_bvh(bvh_tree_& bvh, vector& bboxes, bvh_type type) { // get values auto& nodes = bvh.nodes; auto& primitives = bvh.primitives; @@ -561,7 +548,8 @@ static void build_bvh(bvh_tree_& bvh, vector& bboxes) { // split into two children if (end - start > bvh_max_prims) { // get split - auto [mid, axis] = split_middle(primitives, bboxes, centers, start, end); + auto [mid, axis] = split_nodes( + primitives, bboxes, centers, start, end, type); // make an internal node node.internal = true; @@ -642,7 +630,7 @@ void init_bvh(bvh_shape* shape, bvh_type type) { } // build nodes - build_bvh(shape->bvh, bboxes); + build_bvh(shape->bvh, bboxes, type); } void init_bvh( @@ -679,7 +667,7 @@ void init_bvh( } // build nodes - build_bvh(scene->bvh, bboxes); + build_bvh(scene->bvh, bboxes, type); // handle progress if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); From c2ae48dd74defcc45a8429141fd00836feefa89f Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 08:33:04 +0200 Subject: [PATCH 09/15] updated --- libs/yocto/yocto_bvh.cpp | 17 +++++++++-------- libs/yocto/yocto_bvh.h | 10 ++++++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index 2e35e90d3..a6d18dfda 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -508,7 +508,8 @@ static pair split_nodes(vector& primitives, } // Build BVH nodes -static void build_bvh(bvh_tree_& bvh, vector& bboxes, bvh_type type) { +static void build_bvh( + bvh_tree_& bvh, vector& bboxes, const bvh_params& params) { // get values auto& nodes = bvh.nodes; auto& primitives = bvh.primitives; @@ -549,7 +550,7 @@ static void build_bvh(bvh_tree_& bvh, vector& bboxes, bvh_type type) { if (end - start > bvh_max_prims) { // get split auto [mid, axis] = split_nodes( - primitives, bboxes, centers, start, end, type); + primitives, bboxes, centers, start, end, params.bvh); // make an internal node node.internal = true; @@ -589,7 +590,7 @@ static void update_bvh(bvh_tree_& bvh, const vector& bboxes) { } } -void init_bvh(bvh_shape* shape, bvh_type type) { +void init_bvh(bvh_shape* shape, const bvh_params& params) { #ifdef YOCTO_EMBREE if (params.bvh == bvh_type::embree_default || params.bvh == bvh_type::embree_highquality || @@ -630,18 +631,18 @@ void init_bvh(bvh_shape* shape, bvh_type type) { } // build nodes - build_bvh(shape->bvh, bboxes, type); + build_bvh(shape->bvh, bboxes, params); } -void init_bvh( - bvh_scene* scene, bvh_type type, const progress_callback& progress_cb) { +void init_bvh(bvh_scene* scene, const bvh_params& params, + const progress_callback& progress_cb) { // handle progress auto progress = vec2i{0, 1 + (int)scene->shapes.size()}; // Make shape bvh for (auto shape : scene->shapes) { if (progress_cb) progress_cb("build shape bvh", progress.x++, progress.y); - init_bvh(shape, type); + init_bvh(shape, params); } // embree @@ -667,7 +668,7 @@ void init_bvh( } // build nodes - build_bvh(scene->bvh, bboxes, type); + build_bvh(scene->bvh, bboxes, params); // handle progress if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); diff --git a/libs/yocto/yocto_bvh.h b/libs/yocto/yocto_bvh.h index e5b025ed0..532bca36a 100644 --- a/libs/yocto/yocto_bvh.h +++ b/libs/yocto/yocto_bvh.h @@ -169,13 +169,19 @@ enum struct bvh_type { #endif }; +// Bvh parameters +struct bvh_params { + bvh_type bvh = bvh_type::default_; + bool noparallel = false; // only serial momentarily +}; + // Progress report callback using progress_callback = function; // Build the bvh acceleration structure. -void init_bvh(bvh_shape* bvh, bvh_type type = bvh_type::default_); -void init_bvh(bvh_scene* bvh, bvh_type type = bvh_type::default_, +void init_bvh(bvh_shape* bvh, const bvh_params& params); +void init_bvh(bvh_scene* bvh, const bvh_params& params, const progress_callback& progress_cb = {}); // Refit bvh data From 6a51618e493a37850253e10d7eec3ec8a6dec0e1 Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 08:44:56 +0200 Subject: [PATCH 10/15] updated --- libs/yocto/yocto_bvh.cpp | 209 ++++++++++++++++++------------------- libs/yocto/yocto_trace.cpp | 4 +- 2 files changed, 105 insertions(+), 108 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index a6d18dfda..7922c57a0 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -43,6 +43,7 @@ #include #include "yocto_geometry.h" +#include "yocto_parallel.h" #ifdef YOCTO_EMBREE #include @@ -508,8 +509,8 @@ static pair split_nodes(vector& primitives, } // Build BVH nodes -static void build_bvh( - bvh_tree_& bvh, vector& bboxes, const bvh_params& params) { +static void build_bvh_serial( + bvh_tree_& bvh, const vector& bboxes, const bvh_params& params) { // get values auto& nodes = bvh.nodes; auto& primitives = bvh.primitives; @@ -573,6 +574,104 @@ static void build_bvh( nodes.shrink_to_fit(); } +// Build BVH nodes +static void build_bvh_parallel( + bvh_tree_& bvh, const vector& bboxes, bvh_type type) { + // get values + auto& nodes = bvh.nodes; + auto& primitives = bvh.primitives; + + // prepare to build nodes + nodes.clear(); + nodes.reserve(bboxes.size() * 2); + + // prepare primitives + bvh.primitives.resize(bboxes.size()); + for (auto idx = 0; idx < bboxes.size(); idx++) bvh.primitives[idx] = idx; + + // prepare centers + auto centers = vector(bboxes.size()); + for (auto idx = 0; idx < bboxes.size(); idx++) + centers[idx] = center(bboxes[idx]); + + // queue up first node + auto queue = deque{{0, 0, (int)primitives.size()}}; + nodes.emplace_back(); + + // synchronization + atomic num_processed_prims(0); + std::mutex queue_mutex; + vector> futures; + auto nthreads = std::thread::hardware_concurrency(); + + // create nodes until the queue is empty + for (auto thread_id = 0; thread_id < nthreads; thread_id++) { + futures.emplace_back(std::async( + std::launch::async, [&nodes, &primitives, &bboxes, ¢ers, &type, + &num_processed_prims, &queue_mutex, &queue] { + while (true) { + // exit if needed + if (num_processed_prims >= primitives.size()) return; + + // grab node to work on + auto next = zero3i; + { + std::lock_guard lock{queue_mutex}; + if (!queue.empty()) { + next = queue.front(); + queue.pop_front(); + } + } + + // wait a bit if needed + if (next == zero3i) { + std::this_thread::sleep_for(std::chrono::microseconds(10)); + continue; + } + + // grab node + auto nodeid = next.x, start = next.y, end = next.z; + auto& node = nodes[nodeid]; + + // compute bounds + node.bbox = invalidb3f; + for (auto i = start; i < end; i++) + node.bbox = merge(node.bbox, bboxes[primitives[i]]); + + // split into two children + if (end - start > bvh_max_prims) { + // get split + auto [mid, axis] = split_nodes( + primitives, bboxes, centers, start, end, type); + + // make an internal node + { + std::lock_guard lock{queue_mutex}; + node.internal = true; + node.axis = axis; + node.num = 2; + node.start = (int)nodes.size(); + nodes.emplace_back(); + nodes.emplace_back(); + queue.push_back({node.start + 0, start, mid}); + queue.push_back({node.start + 1, mid, end}); + } + } else { + // Make a leaf node + node.internal = false; + node.num = end - start; + node.start = start; + num_processed_prims += node.num; + } + } + })); + } + for (auto& f : futures) f.get(); + + // cleanup + nodes.shrink_to_fit(); +} + // Update bvh static void update_bvh(bvh_tree_& bvh, const vector& bboxes) { for (auto nodeid = (int)bvh.nodes.size() - 1; nodeid >= 0; nodeid--) { @@ -631,7 +730,7 @@ void init_bvh(bvh_shape* shape, const bvh_params& params) { } // build nodes - build_bvh(shape->bvh, bboxes, params); + build_bvh_serial(shape->bvh, bboxes, params); } void init_bvh(bvh_scene* scene, const bvh_params& params, @@ -668,7 +767,7 @@ void init_bvh(bvh_scene* scene, const bvh_params& params, } // build nodes - build_bvh(scene->bvh, bboxes, params); + build_bvh_serial(scene->bvh, bboxes, params); // handle progress if (progress_cb) progress_cb("build bvh", progress.x++, progress.y); @@ -1620,108 +1719,6 @@ static void build_bvh_serial(vector& nodes, nodes.shrink_to_fit(); } -#if 0 - -// Build BVH nodes -static void build_bvh_parallel( - const shared_ptr& bvh, vector& bboxes, bvh_type type) { - // get values - auto& nodes = bvh->nodes; - auto& primitives = bvh->primitives; - - // prepare to build nodes - nodes.clear(); - nodes.reserve(bboxes.size() * 2); - - // prepare primitives - bvh->primitives.resize(bboxes.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) bvh->primitives[idx] = idx; - - // prepare centers - auto centers = vector(bboxes.size()); - for (auto idx = 0; idx < bboxes.size(); idx++) - centers[idx] = center(bboxes[idx]); - - // queue up first node - auto queue = deque{{0, 0, (int)primitives.size()}}; - nodes.emplace_back(); - - // synchronization - atomic num_processed_prims(0); - std::mutex queue_mutex; - vector> futures; - auto nthreads = std::thread::hardware_concurrency(); - - // create nodes until the queue is empty - for (auto thread_id = 0; thread_id < nthreads; thread_id++) { - futures.emplace_back(std::async( - std::launch::async, [&nodes, &primitives, &bboxes, ¢ers, &type, - &num_processed_prims, &queue_mutex, &queue] { - while (true) { - // exit if needed - if (num_processed_prims >= primitives.size()) return; - - // grab node to work on - auto next = zero3i; - { - std::lock_guard lock{queue_mutex}; - if (!queue.empty()) { - next = queue.front(); - queue.pop_front(); - } - } - - // wait a bit if needed - if (next == zero3i) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); - continue; - } - - // grab node - auto nodeid = next.x, start = next.y, end = next.z; - auto& node = nodes[nodeid]; - - // compute bounds - node.bbox = invalidb3f; - for (auto i = start; i < end; i++) - node.bbox = merge(node.bbox, bboxes[primitives[i]]); - - // split into two children - if (end - start > bvh_max_prims) { - // get split - auto [mid, axis] = split_nodes( - primitives, bboxes, centers, start, end, type); - - // make an internal node - { - std::lock_guard lock{queue_mutex}; - node.internal = true; - node.axis = axis; - node.num = 2; - node.start = (int)nodes.size(); - nodes.emplace_back(); - nodes.emplace_back(); - queue.push_back({node.start + 0, start, mid}); - queue.push_back({node.start + 1, mid, end}); - } - } else { - // Make a leaf node - node.internal = false; - node.num = end - start; - node.start = start; - num_processed_prims += node.num; - } - } - })); - } - for (auto& f : futures) f.get(); - - // cleanup - nodes.shrink_to_fit(); -} - -#endif - // Intersect ray with a bvh-> static bool intersect_instance_bvh(const trace_instance* instance, const ray3f& ray, int& element, vec2f& uv, float& distance, bool find_any, diff --git a/libs/yocto/yocto_trace.cpp b/libs/yocto/yocto_trace.cpp index a368513ec..661c929fe 100644 --- a/libs/yocto/yocto_trace.cpp +++ b/libs/yocto/yocto_trace.cpp @@ -1001,8 +1001,8 @@ void init_bvh(trace_scene* scene, const trace_params& params, } // build - // init_bvh(scene->bvh, progress_cb); - init_bvh(scene->bvh); + init_bvh(scene->bvh, bvh_params{(bvh_type)params.bvh, params.noparallel}, + progress_cb); } // Refit bvh data From be6a36e9900f7c95c7b4c8a66f0e4a2b31bc3784 Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 08:46:40 +0200 Subject: [PATCH 11/15] updated --- libs/yocto/yocto_bvh.cpp | 212 +-------------------------------------- 1 file changed, 2 insertions(+), 210 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index 7922c57a0..886cd339f 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -648,7 +648,7 @@ static void build_bvh_parallel( { std::lock_guard lock{queue_mutex}; node.internal = true; - node.axis = axis; + node.axis = (int8_t)axis; node.num = 2; node.start = (int)nodes.size(); nodes.emplace_back(); @@ -659,7 +659,7 @@ static void build_bvh_parallel( } else { // Make a leaf node node.internal = false; - node.num = end - start; + node.num = (int16_t)(end - start); node.start = start; num_processed_prims += node.num; } @@ -1511,214 +1511,6 @@ static bool intersect_scene_embree_bvh(const trace_scene* scene, } #endif -// primitive used to sort bvh entries -struct trace_bvh_primitive { - bbox3f bbox = invalidb3f; - vec3f center = zero3f; - int primitive = 0; -}; - -// Splits a BVH node using the SAH heuristic. Returns split position and axis. -static pair split_sah( - vector& primitives, int start, int end) { - // initialize split axis and position - auto split_axis = 0; - auto mid = (start + end) / 2; - - // compute primintive bounds and size - auto cbbox = invalidb3f; - for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); - auto csize = cbbox.max - cbbox.min; - if (csize == zero3f) return {mid, split_axis}; - - // consider N bins, compute their cost and keep the minimum - const int nbins = 16; - auto middle = 0.0f; - auto min_cost = flt_max; - auto area = [](auto& b) { - auto size = b.max - b.min; - return 1e-12f + 2 * size.x * size.y + 2 * size.x * size.z + - 2 * size.y * size.z; - }; - for (auto saxis = 0; saxis < 3; saxis++) { - for (auto b = 1; b < nbins; b++) { - auto split = cbbox.min[saxis] + b * csize[saxis] / nbins; - auto left_bbox = invalidb3f, right_bbox = invalidb3f; - auto left_nprims = 0, right_nprims = 0; - for (auto i = start; i < end; i++) { - if (primitives[i].center[saxis] < split) { - left_bbox = merge(left_bbox, primitives[i].bbox); - left_nprims += 1; - } else { - right_bbox = merge(right_bbox, primitives[i].bbox); - right_nprims += 1; - } - } - auto cost = 1 + left_nprims * area(left_bbox) / area(cbbox) + - right_nprims * area(right_bbox) / area(cbbox); - if (cost < min_cost) { - min_cost = cost; - middle = split; - split_axis = saxis; - } - } - } - // split - mid = (int)(std::partition(primitives.data() + start, primitives.data() + end, - [split_axis, middle](auto& primitive) { - return primitive.center[split_axis] < middle; - }) - - primitives.data()); - - // if we were not able to split, just break the primitives in half - if (mid == start || mid == end) { - split_axis = 0; - mid = (start + end) / 2; - // throw std::runtime_error("bad bvh split"); - } - - return {mid, split_axis}; -} - -// Splits a BVH node using the balance heuristic. Returns split position and -// axis. -static pair split_balanced( - vector& primitives, int start, int end) { - // initialize split axis and position - auto axis = 0; - auto mid = (start + end) / 2; - - // compute primintive bounds and size - auto cbbox = invalidb3f; - for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); - auto csize = cbbox.max - cbbox.min; - if (csize == zero3f) return {mid, axis}; - - // split along largest - if (csize.x >= csize.y && csize.x >= csize.z) axis = 0; - if (csize.y >= csize.x && csize.y >= csize.z) axis = 1; - if (csize.z >= csize.x && csize.z >= csize.y) axis = 2; - - // balanced tree split: find the largest axis of the - // bounding box and split along this one right in the middle - mid = (start + end) / 2; - std::nth_element(primitives.data() + start, primitives.data() + mid, - primitives.data() + end, [axis](auto& primitive_a, auto& primitive_b) { - return primitive_a.center[axis] < primitive_b.center[axis]; - }); - - // if we were not able to split, just break the primitives in half - if (mid == start || mid == end) { - // throw std::runtime_error("bad bvh split"); - mid = (start + end) / 2; - } - - return {mid, axis}; -} - -// Splits a BVH node using the middle heuristic. Returns split position and -// axis. -static pair split_middle( - vector& primitives, int start, int end) { - // initialize split axis and position - auto axis = 0; - auto mid = (start + end) / 2; - - // compute primintive bounds and size - auto cbbox = invalidb3f; - for (auto i = start; i < end; i++) cbbox = merge(cbbox, primitives[i].center); - auto csize = cbbox.max - cbbox.min; - if (csize == zero3f) return {mid, axis}; - - // split along largest - if (csize.x >= csize.y && csize.x >= csize.z) axis = 0; - if (csize.y >= csize.x && csize.y >= csize.z) axis = 1; - if (csize.z >= csize.x && csize.z >= csize.y) axis = 2; - - // split the space in the middle along the largest axis - mid = (int)(std::partition(primitives.data() + start, primitives.data() + end, - [axis, middle = center(cbbox)[axis]](auto& primitive) { - return primitive.center[axis] < middle; - }) - - primitives.data()); - - // if we were not able to split, just break the primitives in half - if (mid == start || mid == end) { - // throw std::runtime_error("bad bvh split"); - mid = (start + end) / 2; - } - - return {mid, axis}; -} - -// Split bvh nodes according to a type -static pair split_nodes(vector& primitives, - int start, int end, trace_bvh_type type) { - switch (type) { - case trace_bvh_type::default_: return split_middle(primitives, start, end); - case trace_bvh_type::highquality: return split_sah(primitives, start, end); - case trace_bvh_type::middle: return split_middle(primitives, start, end); - case trace_bvh_type::balanced: - return split_balanced(primitives, start, end); - default: throw std::runtime_error("should not have gotten here"); - } -} - -// Maximum number of primitives per BVH node. -const int trace_bvh_max_prims = 4; - -// Build BVH nodes -static void build_bvh_serial(vector& nodes, - vector& primitives, trace_bvh_type type) { - // prepare to build nodes - nodes.clear(); - nodes.reserve(primitives.size() * 2); - - // queue up first node - auto queue = deque{{0, 0, (int)primitives.size()}}; - nodes.emplace_back(); - - // create nodes until the queue is empty - while (!queue.empty()) { - // grab node to work on - auto next = queue.front(); - queue.pop_front(); - auto nodeid = next.x, start = next.y, end = next.z; - - // grab node - auto& node = nodes[nodeid]; - - // compute bounds - node.bbox = invalidb3f; - for (auto i = start; i < end; i++) - node.bbox = merge(node.bbox, primitives[i].bbox); - - // split into two children - if (end - start > trace_bvh_max_prims) { - // get split - auto [mid, axis] = split_nodes(primitives, start, end, type); - - // make an internal node - node.internal = true; - node.axis = (uint8_t)axis; - node.num = 2; - node.start = (int)nodes.size(); - nodes.emplace_back(); - nodes.emplace_back(); - queue.push_back({node.start + 0, start, mid}); - queue.push_back({node.start + 1, mid, end}); - } else { - // Make a leaf node - node.internal = false; - node.num = (int16_t)(end - start); - node.start = start; - } - } - - // cleanup - nodes.shrink_to_fit(); -} - // Intersect ray with a bvh-> static bool intersect_instance_bvh(const trace_instance* instance, const ray3f& ray, int& element, vec2f& uv, float& distance, bool find_any, From beeaa4780d4938c2d94e6a227bc91202f5dc3fef Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 08:48:02 +0200 Subject: [PATCH 12/15] updated --- libs/yocto/yocto_bvh.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index 886cd339f..39cfbdab2 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -574,6 +574,8 @@ static void build_bvh_serial( nodes.shrink_to_fit(); } +#if 0 + // Build BVH nodes static void build_bvh_parallel( bvh_tree_& bvh, const vector& bboxes, bvh_type type) { @@ -672,6 +674,8 @@ static void build_bvh_parallel( nodes.shrink_to_fit(); } +#endif + // Update bvh static void update_bvh(bvh_tree_& bvh, const vector& bboxes) { for (auto nodeid = (int)bvh.nodes.size() - 1; nodeid >= 0; nodeid--) { From 76eb4e2464a5315853342a48f7054f26feb661cb Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 09:07:23 +0200 Subject: [PATCH 13/15] updated --- .vscode/cmake-variants.json | 20 ++- libs/yocto/yocto_bvh.cpp | 296 +++--------------------------------- 2 files changed, 41 insertions(+), 275 deletions(-) diff --git a/.vscode/cmake-variants.json b/.vscode/cmake-variants.json index a3cc41117..609e92c6b 100644 --- a/.vscode/cmake-variants.json +++ b/.vscode/cmake-variants.json @@ -18,5 +18,23 @@ } } } + }, + "useEmbree": { + "default": "yes", + "description": "Build with Embree", + "choices": { + "yes": { + "short": "Embree", + "settings": { + "YOCTO_EMBREE": "yes" + } + }, + "no": { + "short": "NoEmbree", + "settings": { + "YOCTO_EMBREE": "no" + } + } + } } -} \ No newline at end of file +} diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index 39cfbdab2..ca8b4fe98 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -70,6 +70,7 @@ using namespace std::string_literals; namespace yocto { #ifdef YOCTO_EMBREE + // Get Embree device atomic bvh_embree_memory = 0; static RTCDevice bvh_embree_device() { @@ -109,10 +110,14 @@ static RTCDevice bvh_embree_device() { } // Initialize Embree BVH -void init_shape_embree_bvh(bvh_shape* shape) { +static void init_embree_bvh(bvh_shape* shape, const bvh_params& params) { auto edevice = bvh_embree_device(); shape->embree_bvh = rtcNewScene(edevice); auto escene = shape->embree_bvh; + if (params.bvh == bvh_type::embree_compact) + rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); + if (params.bvh == bvh_type::embree_highquality) + rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); if (!shape->points.empty()) { throw std::runtime_error("embree does not support points"); } else if (!shape->lines.empty()) { @@ -180,15 +185,20 @@ void init_shape_embree_bvh(bvh_shape* shape) { } rtcCommitScene(escene); } -void init_scene_embree_bvh(bvh_scene* scene) { + +static void init_embree_bvh(bvh_scene* scene, const bvh_params& params) { // scene bvh auto edevice = bvh_embree_device(); scene->embree_bvh = rtcNewScene(edevice); auto escene = scene->embree_bvh; + if (params.bvh == bvh_type::embree_compact) + rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); + if (params.bvh == bvh_type::embree_highquality) + rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); for (auto instance_id = 0; instance_id < scene->instances.size(); instance_id++) { auto& instance = scene->instances[instance_id]; - auto& shape = scene->shapes[instance->shape]; + auto& shape = instance->shape; auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_INSTANCE); rtcSetGeometryInstancedScene(egeometry, shape->embree_bvh); rtcSetGeometryTransform( @@ -199,13 +209,13 @@ void init_scene_embree_bvh(bvh_scene* scene) { rtcCommitScene(escene); } -void update_scene_embree_bvh( +static void update_embree_bvh( bvh_scene* scene, const vector& updated_instances) { // scene bvh auto escene = scene->embree_bvh; for (auto instance_id : updated_instances) { auto& instance = scene->instances[instance_id]; - auto& shape = scene->shapes[instance->shape]; + auto& shape = instance->shape; auto embree_geom = rtcGetGeometry(escene, instance_id); rtcSetGeometryInstancedScene(embree_geom, shape->embree_bvh); rtcSetGeometryTransform( @@ -215,7 +225,7 @@ void update_scene_embree_bvh( rtcCommitScene(escene); } -bool intersect_shape_embree_bvh(const bvh_shape* shape, const ray3f& ray, +static bool intersect_embree_bvh(const bvh_shape* shape, const ray3f& ray, int& element, vec2f& uv, float& distance, bool find_any) { RTCRayHit embree_ray; embree_ray.ray.org_x = ray.o.x; @@ -238,7 +248,8 @@ bool intersect_shape_embree_bvh(const bvh_shape* shape, const ray3f& ray, distance = embree_ray.ray.tfar; return true; } -bool intersect_scene_embree_bvh(const bvh_scene* scene, const ray3f& ray, + +static bool intersect_embree_bvh(const bvh_scene* scene, const ray3f& ray, int& instance, int& element, vec2f& uv, float& distance, bool find_any) { RTCRayHit embree_ray; embree_ray.ray.org_x = ray.o.x; @@ -698,7 +709,7 @@ void init_bvh(bvh_shape* shape, const bvh_params& params) { if (params.bvh == bvh_type::embree_default || params.bvh == bvh_type::embree_highquality || params.bvh == bvh_type::embree_compact) { - return init_embree_bvh(shape, type); + return init_embree_bvh(shape, params); } #endif @@ -753,7 +764,7 @@ void init_bvh(bvh_scene* scene, const bvh_params& params, if (params.bvh == bvh_type::embree_default || params.bvh == bvh_type::embree_highquality || params.bvh == bvh_type::embree_compact) { - return init_embree_bvh(scene, type); + return init_embree_bvh(scene, params); } #endif @@ -855,8 +866,7 @@ static bool intersect_shape_bvh(const bvh_shape* shape, const ray3f& ray_, #ifdef YOCTO_EMBREE // call Embree if needed if (shape->embree_bvh) { - return intersect_shape_embree_bvh( - shape, ray_, element, uv, distance, find_any); + return intersect_embree_bvh(shape, ray_, element, uv, distance, find_any); } #endif @@ -956,7 +966,7 @@ static bool intersect_scene_bvh(const bvh_scene* scene, const ray3f& ray_, #ifdef YOCTO_EMBREE // call Embree if needed if (scene->embree_bvh) { - return intersect_scene_embree_bvh( + return intersect_embree_bvh( scene, ray_, instance, element, uv, distance, find_any); } #endif @@ -1281,265 +1291,3 @@ bvh_scene_intersection overlap_scene_bvh(const bvh_scene* scene, } } // namespace yocto - -#if 0 - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION OF RAY-SCENE INTERSECTION -// ----------------------------------------------------------------------------- -namespace yocto { - -#ifdef YOCTO_EMBREE -// Get Embree device -static atomic embree_memory = 0; -static RTCDevice embree_device() { - static RTCDevice device = nullptr; - if (!device) { - device = rtcNewDevice(""); - rtcSetDeviceErrorFunction( - device, - [](void* ctx, RTCError code, const char* message) { - auto str = string{message}; - switch (code) { - case RTC_ERROR_UNKNOWN: - throw std::runtime_error("RTC_ERROR_UNKNOWN: " + str); - case RTC_ERROR_INVALID_ARGUMENT: - throw std::runtime_error("RTC_ERROR_INVALID_ARGUMENT: " + str); - case RTC_ERROR_INVALID_OPERATION: - throw std::runtime_error("RTC_ERROR_INVALID_OPERATION: " + str); - case RTC_ERROR_OUT_OF_MEMORY: - throw std::runtime_error("RTC_ERROR_OUT_OF_MEMORY: " + str); - case RTC_ERROR_UNSUPPORTED_CPU: - throw std::runtime_error("RTC_ERROR_UNSUPPORTED_CPU: " + str); - case RTC_ERROR_CANCELLED: - throw std::runtime_error("RTC_ERROR_CANCELLED: " + str); - default: throw std::runtime_error("invalid error code"); - } - }, - nullptr); - rtcSetDeviceMemoryMonitorFunction( - device, - [](void* userPtr, ssize_t bytes, bool post) { - embree_memory += bytes; - return true; - }, - nullptr); - } - return device; -} - -// Initialize Embree BVH -static void init_embree_bvh(trace_shape* shape, const trace_params& params) { - auto edevice = embree_device(); - if (shape->embree_bvh) rtcReleaseScene(shape->embree_bvh); - shape->embree_bvh = rtcNewScene(edevice); - auto escene = shape->embree_bvh; - if (params.bvh == trace_bvh_type::embree_compact) - rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); - if (params.bvh == trace_bvh_type::embree_highquality) - rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); - if (!shape->points.empty()) { - throw std::runtime_error("embree does not support points"); - } else if (!shape->lines.empty()) { - auto elines = vector{}; - auto epositions = vector{}; - auto last_index = -1; - for (auto& l : shape->lines) { - if (last_index == l.x) { - elines.push_back((int)epositions.size() - 1); - auto& posy = shape->positions[l.y]; - auto& rady = shape->radius[l.y]; - epositions.push_back({posy.x, posy.y, posy.z, rady}); - } else { - elines.push_back((int)epositions.size()); - auto& posx = shape->positions[l.x]; - auto& radx = shape->radius[l.x]; - epositions.push_back({posx.x, posx.y, posx.z, radx}); - auto& posy = shape->positions[l.y]; - auto& rady = shape->radius[l.y]; - epositions.push_back({posy.x, posy.y, posy.z, rady}); - } - last_index = l.y; - } - auto egeometry = rtcNewGeometry( - edevice, RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE); - rtcSetGeometryVertexAttributeCount(egeometry, 1); - auto embree_positions = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT4, 4 * 4, epositions.size()); - auto embree_lines = rtcSetNewGeometryBuffer( - egeometry, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, 4, elines.size()); - memcpy(embree_positions, epositions.data(), epositions.size() * 16); - memcpy(embree_lines, elines.data(), elines.size() * 4); - rtcCommitGeometry(egeometry); - rtcAttachGeometryByID(escene, egeometry, 0); - } else if (!shape->triangles.empty()) { - auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_TRIANGLE); - rtcSetGeometryVertexAttributeCount(egeometry, 1); - if (params.bvh == trace_bvh_type::embree_compact) { - rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_VERTEX, 0, - RTC_FORMAT_FLOAT3, shape->positions.data(), 0, 3 * 4, - shape->positions.size()); - rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_INDEX, 0, - RTC_FORMAT_UINT3, shape->triangles.data(), 0, 3 * 4, - shape->triangles.size()); - } else { - auto embree_positions = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * 4, - shape->positions.size()); - auto embree_triangles = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3 * 4, - shape->triangles.size()); - memcpy(embree_positions, shape->positions.data(), - shape->positions.size() * 12); - memcpy(embree_triangles, shape->triangles.data(), - shape->triangles.size() * 12); - } - rtcCommitGeometry(egeometry); - rtcAttachGeometryByID(escene, egeometry, 0); - } else if (!shape->quads.empty()) { - auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_QUAD); - rtcSetGeometryVertexAttributeCount(egeometry, 1); - if (params.bvh == trace_bvh_type::embree_compact) { - rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_VERTEX, 0, - RTC_FORMAT_FLOAT3, shape->positions.data(), 0, 3 * 4, - shape->positions.size()); - rtcSetSharedGeometryBuffer(egeometry, RTC_BUFFER_TYPE_INDEX, 0, - RTC_FORMAT_UINT4, shape->quads.data(), 0, 4 * 4, shape->quads.size()); - } else { - auto embree_positions = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, 3 * 4, - shape->positions.size()); - auto embree_quads = rtcSetNewGeometryBuffer(egeometry, - RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT4, 4 * 4, - shape->quads.size()); - memcpy(embree_positions, shape->positions.data(), - shape->positions.size() * 12); - memcpy(embree_quads, shape->quads.data(), shape->quads.size() * 16); - } - rtcCommitGeometry(egeometry); - rtcAttachGeometryByID(escene, egeometry, 0); - } else { - throw std::runtime_error("empty shapes not supported"); - } - rtcCommitScene(escene); -} - -static void init_embree_bvh(trace_scene* scene, const trace_params& params) { - // scene bvh - auto edevice = embree_device(); - if (scene->embree_bvh) rtcReleaseScene(scene->embree_bvh); - scene->embree_bvh = rtcNewScene(edevice); - auto escene = scene->embree_bvh; - if (params.bvh == trace_bvh_type::embree_compact) - rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); - if (params.bvh == trace_bvh_type::embree_highquality) - rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); - auto object_id = 0; - for (auto instance : scene->instances) { - auto egeometry = rtcNewGeometry(edevice, RTC_GEOMETRY_TYPE_INSTANCE); - rtcSetGeometryInstancedScene(egeometry, instance->shape->embree_bvh); - rtcSetGeometryTransform( - egeometry, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &instance->frame); - rtcCommitGeometry(egeometry); - rtcAttachGeometryByID(escene, egeometry, object_id++); - } - rtcCommitScene(escene); -} - -static void update_embree_bvh(trace_scene* scene, - const vector& updated_objects, - const vector& updated_shapes, const trace_params& params) { - throw std::runtime_error("not implemented yet"); - // // scene bvh - // auto escene = scene->embree_bvh; - // for (auto& [object_id, instance_id] : scene->embree_instances) { - // auto instance = scene->objects[object_id]; - // auto frame = scene->objects[instance_id]->frame * instance->frame; - // auto egeometry = rtcGetGeometry(escene, instance_id); - // rtcSetGeometryInstancedScene(egeometry, instance->shape->embree_bvh); - // rtcSetGeometryTransform( - // egeometry, 0, RTC_FORMAT_FLOAT3X4_COLUMN_MAJOR, &frame); - // rtcCommitGeometry(egeometry); - // } - // rtcCommitScene(escene); -} - -static bool intersect_shape_embree_bvh(trace_shape* shape, const ray3f& ray, - int& element, vec2f& uv, float& distance, bool find_any) { - RTCRayHit embree_ray; - embree_ray.ray.org_x = ray.o.x; - embree_ray.ray.org_y = ray.o.y; - embree_ray.ray.org_z = ray.o.z; - embree_ray.ray.dir_x = ray.d.x; - embree_ray.ray.dir_y = ray.d.y; - embree_ray.ray.dir_z = ray.d.z; - embree_ray.ray.tnear = ray.tmin; - embree_ray.ray.tfar = ray.tmax; - embree_ray.ray.flags = 0; - embree_ray.hit.geomID = RTC_INVALID_GEOMETRY_ID; - embree_ray.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; - RTCIntersectContext embree_ctx; - rtcInitIntersectContext(&embree_ctx); - rtcIntersect1(shape->embree_bvh, &embree_ctx, &embree_ray); - if (embree_ray.hit.geomID == RTC_INVALID_GEOMETRY_ID) return false; - element = (int)embree_ray.hit.primID; - uv = {embree_ray.hit.u, embree_ray.hit.v}; - distance = embree_ray.ray.tfar; - return true; -} - -static bool intersect_scene_embree_bvh(const trace_scene* scene, - const ray3f& ray, int& instance, int& element, vec2f& uv, float& distance, - bool find_any) { - RTCRayHit embree_ray; - embree_ray.ray.org_x = ray.o.x; - embree_ray.ray.org_y = ray.o.y; - embree_ray.ray.org_z = ray.o.z; - embree_ray.ray.dir_x = ray.d.x; - embree_ray.ray.dir_y = ray.d.y; - embree_ray.ray.dir_z = ray.d.z; - embree_ray.ray.tnear = ray.tmin; - embree_ray.ray.tfar = ray.tmax; - embree_ray.ray.flags = 0; - embree_ray.hit.geomID = RTC_INVALID_GEOMETRY_ID; - embree_ray.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; - RTCIntersectContext embree_ctx; - rtcInitIntersectContext(&embree_ctx); - rtcIntersect1(scene->embree_bvh, &embree_ctx, &embree_ray); - if (embree_ray.hit.geomID == RTC_INVALID_GEOMETRY_ID) return false; - instance = (int)embree_ray.hit.instID[0]; - element = (int)embree_ray.hit.primID; - uv = {embree_ray.hit.u, embree_ray.hit.v}; - distance = embree_ray.ray.tfar; - return true; -} -#endif - -// Intersect ray with a bvh-> -static bool intersect_instance_bvh(const trace_instance* instance, - const ray3f& ray, int& element, vec2f& uv, float& distance, bool find_any, - bool non_rigid_frames) { - auto inv_ray = transform_ray(inverse(instance->frame, non_rigid_frames), ray); - return intersect_shape_bvh( - instance->shape, inv_ray, element, uv, distance, find_any); -} - -trace_intersection intersect_scene_bvh(const trace_scene* scene, - const ray3f& ray, bool find_any, bool non_rigid_frames) { - auto intersection = trace_intersection{}; - intersection.hit = intersect_scene_bvh(scene, ray, intersection.instance, - intersection.element, intersection.uv, intersection.distance, find_any, - non_rigid_frames); - return intersection; -} -trace_intersection intersect_instance_bvh(const trace_instance* instance, - const ray3f& ray, bool find_any, bool non_rigid_frames) { - auto intersection = trace_intersection{}; - intersection.hit = intersect_instance_bvh(instance, ray, intersection.element, - intersection.uv, intersection.distance, find_any, non_rigid_frames); - return intersection; -} - -} // namespace yocto - -#endif From 4860217ac19efbe62b8d3ce95aee329d97a8ea2a Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 09:09:55 +0200 Subject: [PATCH 14/15] updated --- libs/yocto/yocto_bvh.cpp | 32 ++++++++++++++++---------------- libs/yocto/yocto_bvh.h | 13 ++++++++++--- libs/yocto/yocto_trace.cpp | 4 ++-- libs/yocto/yocto_trace.h | 4 ---- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/libs/yocto/yocto_bvh.cpp b/libs/yocto/yocto_bvh.cpp index ca8b4fe98..54b378161 100644 --- a/libs/yocto/yocto_bvh.cpp +++ b/libs/yocto/yocto_bvh.cpp @@ -114,9 +114,9 @@ static void init_embree_bvh(bvh_shape* shape, const bvh_params& params) { auto edevice = bvh_embree_device(); shape->embree_bvh = rtcNewScene(edevice); auto escene = shape->embree_bvh; - if (params.bvh == bvh_type::embree_compact) + if (params.bvh == bvh_build_type::embree_compact) rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); - if (params.bvh == bvh_type::embree_highquality) + if (params.bvh == bvh_build_type::embree_highquality) rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); if (!shape->points.empty()) { throw std::runtime_error("embree does not support points"); @@ -191,9 +191,9 @@ static void init_embree_bvh(bvh_scene* scene, const bvh_params& params) { auto edevice = bvh_embree_device(); scene->embree_bvh = rtcNewScene(edevice); auto escene = scene->embree_bvh; - if (params.bvh == bvh_type::embree_compact) + if (params.bvh == bvh_build_type::embree_compact) rtcSetSceneFlags(escene, RTC_SCENE_FLAG_COMPACT); - if (params.bvh == bvh_type::embree_highquality) + if (params.bvh == bvh_build_type::embree_highquality) rtcSetSceneBuildQuality(escene, RTC_BUILD_QUALITY_HIGH); for (auto instance_id = 0; instance_id < scene->instances.size(); instance_id++) { @@ -505,15 +505,15 @@ static pair split_middle(vector& primitives, // Split bvh nodes according to a type static pair split_nodes(vector& primitives, const vector& bboxes, const vector& centers, int start, - int end, bvh_type type) { + int end, bvh_build_type type) { switch (type) { - case bvh_type::default_: + case bvh_build_type::default_: return split_middle(primitives, bboxes, centers, start, end); - case bvh_type::highquality: + case bvh_build_type::highquality: return split_sah(primitives, bboxes, centers, start, end); - case bvh_type::middle: + case bvh_build_type::middle: return split_middle(primitives, bboxes, centers, start, end); - case bvh_type::balanced: + case bvh_build_type::balanced: return split_balanced(primitives, bboxes, centers, start, end); default: throw std::runtime_error("should not have gotten here"); } @@ -589,7 +589,7 @@ static void build_bvh_serial( // Build BVH nodes static void build_bvh_parallel( - bvh_tree_& bvh, const vector& bboxes, bvh_type type) { + bvh_tree_& bvh, const vector& bboxes, bvh_build_type type) { // get values auto& nodes = bvh.nodes; auto& primitives = bvh.primitives; @@ -706,9 +706,9 @@ static void update_bvh(bvh_tree_& bvh, const vector& bboxes) { void init_bvh(bvh_shape* shape, const bvh_params& params) { #ifdef YOCTO_EMBREE - if (params.bvh == bvh_type::embree_default || - params.bvh == bvh_type::embree_highquality || - params.bvh == bvh_type::embree_compact) { + if (params.bvh == bvh_build_type::embree_default || + params.bvh == bvh_build_type::embree_highquality || + params.bvh == bvh_build_type::embree_compact) { return init_embree_bvh(shape, params); } #endif @@ -761,9 +761,9 @@ void init_bvh(bvh_scene* scene, const bvh_params& params, // embree #ifdef YOCTO_EMBREE - if (params.bvh == bvh_type::embree_default || - params.bvh == bvh_type::embree_highquality || - params.bvh == bvh_type::embree_compact) { + if (params.bvh == bvh_build_type::embree_default || + params.bvh == bvh_build_type::embree_highquality || + params.bvh == bvh_build_type::embree_compact) { return init_embree_bvh(scene, params); } #endif diff --git a/libs/yocto/yocto_bvh.h b/libs/yocto/yocto_bvh.h index 532bca36a..10384bee7 100644 --- a/libs/yocto/yocto_bvh.h +++ b/libs/yocto/yocto_bvh.h @@ -157,7 +157,7 @@ bvh_instance* add_instance( bvh_scene* bvh, const frame3f& frame, bvh_shape* shape); // Strategy used to build the bvh -enum struct bvh_type { +enum struct bvh_build_type { default_, highquality, middle, @@ -169,10 +169,17 @@ enum struct bvh_type { #endif }; +const auto bvh_build_names = vector{ + "default", "highquality", "middle", "balanced", +#ifdef YOCTO_EMBREE + "embree-default", "embree-highquality", "embree-compact" +#endif +}; + // Bvh parameters struct bvh_params { - bvh_type bvh = bvh_type::default_; - bool noparallel = false; // only serial momentarily + bvh_build_type bvh = bvh_build_type::default_; + bool noparallel = false; // only serial momentarily }; // Progress report callback diff --git a/libs/yocto/yocto_trace.cpp b/libs/yocto/yocto_trace.cpp index 661c929fe..34018b04d 100644 --- a/libs/yocto/yocto_trace.cpp +++ b/libs/yocto/yocto_trace.cpp @@ -1001,8 +1001,8 @@ void init_bvh(trace_scene* scene, const trace_params& params, } // build - init_bvh(scene->bvh, bvh_params{(bvh_type)params.bvh, params.noparallel}, - progress_cb); + init_bvh(scene->bvh, + bvh_params{(bvh_build_type)params.bvh, params.noparallel}, progress_cb); } // Refit bvh data diff --git a/libs/yocto/yocto_trace.h b/libs/yocto/yocto_trace.h index 135dc819d..14b47aed7 100644 --- a/libs/yocto/yocto_trace.h +++ b/libs/yocto/yocto_trace.h @@ -49,10 +49,6 @@ #include "yocto_math.h" #include "yocto_sampling.h" -#ifdef YOCTO_EMBREE -#include -#endif - // ----------------------------------------------------------------------------- // USING DIRECTIVES // ----------------------------------------------------------------------------- From 43a4b36424565cb5d36a71253a81a2077670435f Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Thu, 3 Sep 2020 09:55:05 +0200 Subject: [PATCH 15/15] updated --- apps/ysceneitrace/ysceneitrace.cpp | 24 ++-- apps/ysceneitraces/ysceneitraces.cpp | 9 +- apps/yscenetrace/yscenetrace.cpp | 14 ++- libs/yocto/yocto_trace.cpp | 174 ++++++++++++++------------- libs/yocto/yocto_trace.h | 35 +++--- 5 files changed, 137 insertions(+), 119 deletions(-) diff --git a/apps/ysceneitrace/ysceneitrace.cpp b/apps/ysceneitrace/ysceneitrace.cpp index fd6aad11b..b24f9a099 100644 --- a/apps/ysceneitrace/ysceneitrace.cpp +++ b/apps/ysceneitrace/ysceneitrace.cpp @@ -58,6 +58,7 @@ struct app_state { // rendering objects trace_lights* lights = new trace_lights{}; + trace_bvh* bvh = new trace_bvh{}; // options trace_params params = {}; @@ -96,8 +97,9 @@ struct app_state { ~app_state() { if (render_state) trace_stop(render_state); delete render_state; - delete scene; + delete bvh; delete lights; + delete scene; delete ioscene; delete glimage; } @@ -247,7 +249,8 @@ void reset_display(app_state* app) { app->status = "render"; app->render_counter = 0; trace_start( - app->render_state, app->scene, app->camera, app->lights, app->params, + app->render_state, app->scene, app->camera, app->bvh, app->lights, + app->params, [app](const string& message, int sample, int nsamples) { app->current = sample; app->total = nsamples; @@ -289,8 +292,8 @@ void load_scene_async(app_states* apps, const string& filename, init_scene( app->scene, app->ioscene, app->camera, app->iocamera, progress_cb); tesselate_shapes(app->scene, progress_cb); - init_bvh(app->scene, app->params); - init_lights(app->lights, app->scene); + init_bvh(app->bvh, app->scene, app->params); + init_lights(app->lights, app->scene, app->params); if (app->lights->lights.empty() && is_sampler_lit(app->params)) { app->params.sampler = trace_sampler_type::eyelight; } @@ -336,7 +339,7 @@ bool draw_widgets( draw_label(win, "ldr", std::to_string(iotexture->ldr.width()) + " x " + std::to_string(iotexture->ldr.height())); - // TODO: load texture + // TODO(fabio): load texture return edited; } @@ -590,7 +593,7 @@ void draw_widgets(gui_window* win, app_states* apps, const gui_input& input) { set_frame(environment, ioenvironment->frame); set_emission(environment, ioenvironment->emission, get_texture(ioenvironment->emission_tex)); - init_lights(app->lights, app->scene); + init_lights(app->lights, app->scene, app->params); reset_display(app); } end_header(win); @@ -609,7 +612,7 @@ void draw_widgets(gui_window* win, app_states* apps, const gui_input& input) { set_material( instance, get_element(ioinstance->material, app->ioscene->materials, app->scene->materials)); - update_bvh(app->scene, {instance}, {}, app->params); + update_bvh(app->bvh, app->scene, {instance}, {}, app->params); reset_display(app); } end_header(win); @@ -632,7 +635,7 @@ void draw_widgets(gui_window* win, app_states* apps, const gui_input& input) { set_colors(shape, ioshape->colors); set_radius(shape, ioshape->radius); set_tangents(shape, ioshape->tangents); - update_bvh(app->scene, {}, {shape}, app->params); + update_bvh(app->bvh, app->scene, {}, {shape}, app->params); reset_display(app); } end_header(win); @@ -666,7 +669,7 @@ void draw_widgets(gui_window* win, app_states* apps, const gui_input& input) { set_normalmap(material, get_texture(iomaterial->normal_tex)); set_scattering(material, iomaterial->scattering, iomaterial->scanisotropy, get_texture(iomaterial->scattering_tex)); - init_lights(app->lights, app->scene); + init_lights(app->lights, app->scene, app->params); reset_display(app); } end_header(win); @@ -818,7 +821,8 @@ int main(int argc, const char* argv[]) { app->camera->lens, app->camera->film, vec2f{ij.x + 0.5f, ij.y + 0.5f} / vec2f{(float)app->render.width(), (float)app->render.height()}); - if (auto isec = intersect_scene_bvh(app->scene, ray); isec.hit) { + if (auto isec = intersect_scene_bvh(app->bvh, app->scene, ray); + isec.hit) { app->selected_instance = app->ioscene->instances[isec.instance]; } } diff --git a/apps/ysceneitraces/ysceneitraces.cpp b/apps/ysceneitraces/ysceneitraces.cpp index 414152056..7b6c22816 100644 --- a/apps/ysceneitraces/ysceneitraces.cpp +++ b/apps/ysceneitraces/ysceneitraces.cpp @@ -55,6 +55,7 @@ struct app_state { // rendering objects trace_lights* lights = new trace_lights{}; + trace_bvh* bvh = new trace_bvh{}; // rendering state image render = {}; @@ -78,6 +79,7 @@ struct app_state { if (render_state) trace_stop(render_state); delete render_state; delete lights; + delete bvh; delete scene; if (glimage) delete glimage; } @@ -90,7 +92,8 @@ void reset_display(app_state* app) { // start render app->render_counter = 0; trace_start( - app->render_state, app->scene, app->camera, app->lights, app->params, + app->render_state, app->scene, app->camera, app->bvh, app->lights, + app->params, [app](const string& message, int sample, int nsamples) { app->current = sample; app->total = nsamples; @@ -280,10 +283,10 @@ int main(int argc, const char* argv[]) { tesselate_shapes(app->scene, print_progress); // build bvh - init_bvh(app->scene, app->params, print_progress); + init_bvh(app->bvh, app->scene, app->params, print_progress); // init renderer - init_lights(app->lights, app->scene, print_progress); + init_lights(app->lights, app->scene, app->params, print_progress); // fix renderer type if no lights if (app->lights->lights.empty() && is_sampler_lit(app->params)) { diff --git a/apps/yscenetrace/yscenetrace.cpp b/apps/yscenetrace/yscenetrace.cpp index 8006d0ff7..75542b1b3 100644 --- a/apps/yscenetrace/yscenetrace.cpp +++ b/apps/yscenetrace/yscenetrace.cpp @@ -211,12 +211,14 @@ int main(int argc, const char* argv[]) { tesselate_shapes(scene, print_progress); // build bvh - init_bvh(scene, params, print_progress); + auto bvh_guard = std::make_unique(); + auto bvh = bvh_guard.get(); + init_bvh(bvh, scene, params, print_progress); // init renderer auto lights_guard = std::make_unique(); auto lights = lights_guard.get(); - init_lights(lights, scene, print_progress); + init_lights(lights, scene, params, print_progress); // fix renderer type if no lights if (lights->lights.empty() && is_sampler_lit(params)) { @@ -225,7 +227,7 @@ int main(int argc, const char* argv[]) { } // render - auto render = trace_image(scene, camera, lights, params, print_progress, + auto render = trace_image(scene, camera, bvh, lights, params, print_progress, [save_batch, imfilename]( const image& render, int sample, int samples) { if (!save_batch) return; @@ -256,7 +258,8 @@ int main(int argc, const char* argv[]) { // render denoise albedo fparams.sampler = trace_sampler_type::albedo; - auto albedo = trace_image(scene, camera, lights, fparams, print_progress); + auto albedo = trace_image( + scene, camera, bvh, lights, fparams, print_progress); auto albedo_filename = replace_extension( imfilename, "-albedo" + feature_ext); @@ -266,7 +269,8 @@ int main(int argc, const char* argv[]) { // render denoise normals fparams.sampler = trace_sampler_type::normal; - auto normal = trace_image(scene, camera, lights, fparams, print_progress); + auto normal = trace_image( + scene, camera, bvh, lights, fparams, print_progress); auto normal_filename = replace_extension( imfilename, "-normal" + feature_ext); diff --git a/libs/yocto/yocto_trace.cpp b/libs/yocto/yocto_trace.cpp index 34018b04d..8d3702b39 100644 --- a/libs/yocto/yocto_trace.cpp +++ b/libs/yocto/yocto_trace.cpp @@ -69,7 +69,6 @@ trace_scene::~trace_scene() { for (auto instance : instances) delete instance; for (auto texture : textures) delete texture; for (auto environment : environments) delete environment; - delete bvh; } trace_lights::~trace_lights() {} @@ -980,33 +979,24 @@ static ray3f sample_camera(const trace_camera* camera, const vec2i& ij, namespace yocto { // Build the bvh acceleration structure. -void init_bvh(trace_scene* scene, const trace_params& params, - const progress_callback& progress_cb) { - // cleanup - delete scene->bvh; - scene->bvh = new bvh_scene{}; - +void init_bvh(trace_bvh* bvh, const trace_scene* scene, + const trace_params& params, const progress_callback& progress_cb) { // initialize bvh for (auto shape : scene->shapes) { - shape->bvh = add_shape(scene->bvh); - shape->bvh->points = shape->points; - shape->bvh->lines = shape->lines; - shape->bvh->triangles = shape->triangles; - shape->bvh->quads = shape->quads; - shape->bvh->positions = shape->positions; - shape->bvh->radius = shape->radius; + shape->bvh = add_shape(bvh, shape->points, shape->lines, shape->triangles, + shape->quads, shape->positions, shape->radius); } for (auto instance : scene->instances) { - add_instance(scene->bvh, instance->frame, instance->shape->bvh); + add_instance(bvh, instance->frame, instance->shape->bvh); } // build - init_bvh(scene->bvh, - bvh_params{(bvh_build_type)params.bvh, params.noparallel}, progress_cb); + init_bvh(bvh, bvh_params{(bvh_build_type)params.bvh, params.noparallel}, + progress_cb); } // Refit bvh data -void update_bvh(trace_scene* scene, +void update_bvh(trace_bvh* bvh, const trace_scene* scene, const vector& updated_instances, const vector& updated_shapes, const trace_params& params) { auto updated_instances_ids = vector{}; @@ -1022,16 +1012,18 @@ void update_bvh(trace_scene* scene, scene->instances.begin(), scene->instances.end(), instance) - scene->instances.begin())); } - update_bvh(scene->bvh, updated_instances_ids, updated_shapes_ids); + update_bvh(bvh, updated_instances_ids, updated_shapes_ids); } // Intersect ray with a bvh returning either the first or any intersection. -trace_intersection intersect_scene_bvh(const trace_scene* scene, - const ray3f& ray, bool find_any, bool non_rigid_frames) { - return intersect_scene_bvh(scene->bvh, ray, find_any, non_rigid_frames); -} -trace_intersection intersect_instance_bvh(const trace_instance* instance, - const ray3f& ray, bool find_any, bool non_rigid_frames) { +trace_intersection intersect_scene_bvh(const trace_bvh* bvh, + const trace_scene* scene, const ray3f& ray, bool find_any, + bool non_rigid_frames) { + return intersect_scene_bvh(bvh, ray, find_any, non_rigid_frames); +} +trace_intersection intersect_instance_bvh(const trace_bvh* bvh, + const trace_instance* instance, const ray3f& ray, bool find_any, + bool non_rigid_frames) { auto inv_ray = transform_ray(inverse(instance->frame, non_rigid_frames), ray); auto sintersection = intersect_shape_bvh( instance->shape->bvh, inv_ray, find_any); @@ -1355,7 +1347,7 @@ static vec3f sample_lights(const trace_scene* scene, const trace_lights* lights, } // Sample lights pdf -static float sample_lights_pdf(const trace_scene* scene, +static float sample_lights_pdf(const trace_scene* scene, const trace_bvh* bvh, const trace_lights* lights, const vec3f& position, const vec3f& direction) { auto pdf = 0.0f; for (auto light : lights->lights) { @@ -1365,7 +1357,7 @@ static float sample_lights_pdf(const trace_scene* scene, auto next_position = position; for (auto bounce = 0; bounce < 100; bounce++) { auto intersection = intersect_instance_bvh( - light->instance, {next_position, direction}); + bvh, light->instance, {next_position, direction}); if (!intersection.hit) break; // accumulate pdf auto lposition = eval_position( @@ -1407,8 +1399,9 @@ static float sample_lights_pdf(const trace_scene* scene, } // Recursive path tracing. -static vec4f trace_path(const trace_scene* scene, const trace_lights* lights, - const ray3f& ray_, rng_state& rng, const trace_params& params) { +static vec4f trace_path(const trace_scene* scene, const trace_bvh* bvh, + const trace_lights* lights, const ray3f& ray_, rng_state& rng, + const trace_params& params) { // initialize auto radiance = zero3f; auto weight = vec3f{1, 1, 1}; @@ -1420,7 +1413,7 @@ static vec4f trace_path(const trace_scene* scene, const trace_lights* lights, // trace path for (auto bounce = 0; bounce < params.bounces; bounce++) { // intersect next point - auto intersection = intersect_scene_bvh(scene, ray); + auto intersection = intersect_scene_bvh(bvh, scene, ray); if (!intersection.hit) { if (bounce > 0 || !params.envhidden) radiance += weight * eval_environment(scene, ray.d); @@ -1480,10 +1473,10 @@ static vec4f trace_path(const trace_scene* scene, const trace_lights* lights, incoming = sample_lights( scene, lights, position, rand1f(rng), rand1f(rng), rand2f(rng)); } - weight *= - eval_bsdfcos(bsdf, normal, outgoing, incoming) / - (0.5f * sample_bsdfcos_pdf(bsdf, normal, outgoing, incoming) + - 0.5f * sample_lights_pdf(scene, lights, position, incoming)); + weight *= eval_bsdfcos(bsdf, normal, outgoing, incoming) / + (0.5f * sample_bsdfcos_pdf(bsdf, normal, outgoing, incoming) + + 0.5f * sample_lights_pdf( + scene, bvh, lights, position, incoming)); } else { incoming = sample_delta(bsdf, normal, outgoing, rand1f(rng)); weight *= eval_delta(bsdf, normal, outgoing, incoming) / @@ -1526,7 +1519,7 @@ static vec4f trace_path(const trace_scene* scene, const trace_lights* lights, weight *= eval_scattering(vsdf, outgoing, incoming) / (0.5f * sample_scattering_pdf(vsdf, outgoing, incoming) + - 0.5f * sample_lights_pdf(scene, lights, position, incoming)); + 0.5f * sample_lights_pdf(scene, bvh, lights, position, incoming)); // setup next iteration ray = {position, incoming}; @@ -1547,8 +1540,9 @@ static vec4f trace_path(const trace_scene* scene, const trace_lights* lights, } // Recursive path tracing. -static vec4f trace_naive(const trace_scene* scene, const trace_lights* lights, - const ray3f& ray_, rng_state& rng, const trace_params& params) { +static vec4f trace_naive(const trace_scene* scene, const trace_bvh* bvh, + const trace_lights* lights, const ray3f& ray_, rng_state& rng, + const trace_params& params) { // initialize auto radiance = zero3f; auto weight = vec3f{1, 1, 1}; @@ -1558,7 +1552,7 @@ static vec4f trace_naive(const trace_scene* scene, const trace_lights* lights, // trace path for (auto bounce = 0; bounce < params.bounces; bounce++) { // intersect next point - auto intersection = intersect_scene_bvh(scene, ray); + auto intersection = intersect_scene_bvh(bvh, scene, ray); if (!intersection.hit) { if (bounce > 0 || !params.envhidden) radiance += weight * eval_environment(scene, ray.d); @@ -1618,7 +1612,7 @@ static vec4f trace_naive(const trace_scene* scene, const trace_lights* lights, } // Eyelight for quick previewing. -static vec4f trace_eyelight(const trace_scene* scene, +static vec4f trace_eyelight(const trace_scene* scene, const trace_bvh* bvh, const trace_lights* lights, const ray3f& ray_, rng_state& rng, const trace_params& params) { // initialize @@ -1630,7 +1624,7 @@ static vec4f trace_eyelight(const trace_scene* scene, // trace path for (auto bounce = 0; bounce < max(params.bounces, 4); bounce++) { // intersect next point - auto intersection = intersect_scene_bvh(scene, ray); + auto intersection = intersect_scene_bvh(bvh, scene, ray); if (!intersection.hit) { if (bounce > 0 || !params.envhidden) radiance += weight * eval_environment(scene, ray.d); @@ -1678,11 +1672,11 @@ static vec4f trace_eyelight(const trace_scene* scene, } // False color rendering -static vec4f trace_falsecolor(const trace_scene* scene, +static vec4f trace_falsecolor(const trace_scene* scene, const trace_bvh* bvh, const trace_lights* lights, const ray3f& ray, rng_state& rng, const trace_params& params) { // intersect next point - auto intersection = intersect_scene_bvh(scene, ray); + auto intersection = intersect_scene_bvh(bvh, scene, ray); if (!intersection.hit) { return {0, 0, 0, 0}; } @@ -1754,9 +1748,10 @@ static vec4f trace_falsecolor(const trace_scene* scene, } } -static vec4f trace_albedo(const trace_scene* scene, const trace_lights* lights, - const ray3f& ray, rng_state& rng, const trace_params& params, int bounce) { - auto intersection = intersect_scene_bvh(scene, ray); +static vec4f trace_albedo(const trace_scene* scene, const trace_bvh* bvh, + const trace_lights* lights, const ray3f& ray, rng_state& rng, + const trace_params& params, int bounce) { + auto intersection = intersect_scene_bvh(bvh, scene, ray); if (!intersection.hit) { auto radiance = eval_environment(scene, ray.d); return {radiance.x, radiance.y, radiance.z, 1}; @@ -1785,7 +1780,7 @@ static vec4f trace_albedo(const trace_scene* scene, const trace_lights* lights, // handle opacity if (opacity < 1.0f) { - auto blend_albedo = trace_albedo(scene, lights, + auto blend_albedo = trace_albedo(scene, bvh, lights, ray3f{position + ray.d * 1e-2f, ray.d}, rng, params, bounce); return lerp(blend_albedo, vec4f{albedo.x, albedo.y, albedo.z, 1}, opacity); } @@ -1793,20 +1788,20 @@ static vec4f trace_albedo(const trace_scene* scene, const trace_lights* lights, if (bsdf.roughness < 0.05 && bounce < 5) { if (bsdf.transmission != zero3f && material->thin) { auto incoming = -outgoing; - auto trans_albedo = trace_albedo( - scene, lights, ray3f{position, incoming}, rng, params, bounce + 1); + auto trans_albedo = trace_albedo(scene, bvh, lights, + ray3f{position, incoming}, rng, params, bounce + 1); incoming = reflect(outgoing, normal); - auto spec_albedo = trace_albedo( - scene, lights, ray3f{position, incoming}, rng, params, bounce + 1); + auto spec_albedo = trace_albedo(scene, bvh, lights, + ray3f{position, incoming}, rng, params, bounce + 1); auto fresnel = fresnel_dielectric(material->ior, outgoing, normal); auto dielectric_albedo = lerp(trans_albedo, spec_albedo, fresnel); return dielectric_albedo * vec4f{albedo.x, albedo.y, albedo.z, 1}; } else if (bsdf.metal != zero3f) { auto incoming = reflect(outgoing, normal); - auto refl_albedo = trace_albedo( - scene, lights, ray3f{position, incoming}, rng, params, bounce + 1); + auto refl_albedo = trace_albedo(scene, bvh, lights, + ray3f{position, incoming}, rng, params, bounce + 1); return refl_albedo * vec4f{albedo.x, albedo.y, albedo.z, 1}; } } @@ -1814,15 +1809,17 @@ static vec4f trace_albedo(const trace_scene* scene, const trace_lights* lights, return {albedo.x, albedo.y, albedo.z, 1}; } -static vec4f trace_albedo(const trace_scene* scene, const trace_lights* lights, - const ray3f& ray, rng_state& rng, const trace_params& params) { - auto albedo = trace_albedo(scene, lights, ray, rng, params, 0); +static vec4f trace_albedo(const trace_scene* scene, const trace_bvh* bvh, + const trace_lights* lights, const ray3f& ray, rng_state& rng, + const trace_params& params) { + auto albedo = trace_albedo(scene, bvh, lights, ray, rng, params, 0); return clamp(albedo, 0.0, 1.0); } -static vec4f trace_normal(const trace_scene* scene, const trace_lights* lights, - const ray3f& ray, rng_state& rng, const trace_params& params, int bounce) { - auto intersection = intersect_scene_bvh(scene, ray); +static vec4f trace_normal(const trace_scene* scene, const trace_bvh* bvh, + const trace_lights* lights, const ray3f& ray, rng_state& rng, + const trace_params& params, int bounce) { + auto intersection = intersect_scene_bvh(bvh, scene, ray); if (!intersection.hit) { return {0, 0, 0, 1}; } @@ -1840,7 +1837,7 @@ static vec4f trace_normal(const trace_scene* scene, const trace_lights* lights, // handle opacity if (opacity < 1.0f) { - auto normal = trace_normal(scene, lights, + auto normal = trace_normal(scene, bvh, lights, ray3f{position + ray.d * 1e-2f, ray.d}, rng, params, bounce); return lerp(normal, normal, opacity); } @@ -1848,32 +1845,33 @@ static vec4f trace_normal(const trace_scene* scene, const trace_lights* lights, if (bsdf.roughness < 0.05f && bounce < 5) { if (bsdf.transmission != zero3f && material->thin) { auto incoming = -outgoing; - auto trans_norm = trace_normal( - scene, lights, ray3f{position, incoming}, rng, params, bounce + 1); + auto trans_norm = trace_normal(scene, bvh, lights, + ray3f{position, incoming}, rng, params, bounce + 1); incoming = reflect(outgoing, normal); - auto spec_norm = trace_normal( - scene, lights, ray3f{position, incoming}, rng, params, bounce + 1); + auto spec_norm = trace_normal(scene, bvh, lights, + ray3f{position, incoming}, rng, params, bounce + 1); auto fresnel = fresnel_dielectric(material->ior, outgoing, normal); return lerp(trans_norm, spec_norm, fresnel); } else if (bsdf.metal != zero3f) { auto incoming = reflect(outgoing, normal); - return trace_normal( - scene, lights, ray3f{position, incoming}, rng, params, bounce + 1); + return trace_normal(scene, bvh, lights, ray3f{position, incoming}, rng, + params, bounce + 1); } } return {normal.x, normal.y, normal.z, 1}; } -static vec4f trace_normal(const trace_scene* scene, const trace_lights* lights, - const ray3f& ray, rng_state& rng, const trace_params& params) { - return trace_normal(scene, lights, ray, rng, params, 0); +static vec4f trace_normal(const trace_scene* scene, const trace_bvh* bvh, + const trace_lights* lights, const ray3f& ray, rng_state& rng, + const trace_params& params) { + return trace_normal(scene, bvh, lights, ray, rng, params, 0); } // Trace a single ray from the camera using the given algorithm. -using sampler_func = vec4f (*)(const trace_scene* scene, +using sampler_func = vec4f (*)(const trace_scene* scene, const trace_bvh* bvh, const trace_lights* lights, const ray3f& ray, rng_state& rng, const trace_params& params); static sampler_func get_trace_sampler_func(const trace_params& params) { @@ -1909,12 +1907,12 @@ bool is_sampler_lit(const trace_params& params) { // Trace a block of samples void trace_sample(trace_state* state, const trace_scene* scene, - const trace_camera* camera, const trace_lights* lights, const vec2i& ij, - const trace_params& params) { + const trace_camera* camera, const trace_bvh* bvh, + const trace_lights* lights, const vec2i& ij, const trace_params& params) { auto sampler = get_trace_sampler_func(params); auto ray = sample_camera(camera, ij, state->render.imsize(), rand2f(state->rngs[ij]), rand2f(state->rngs[ij]), params.tentfilter); - auto sample = sampler(scene, lights, ray, state->rngs[ij], params); + auto sample = sampler(scene, bvh, lights, ray, state->rngs[ij], params); if (!isfinite(xyz(sample))) sample = {0, 0, 0, sample.w}; if (max(sample) > params.clamp) sample = sample * (params.clamp / max(sample)); @@ -1952,7 +1950,7 @@ static trace_light* add_light(trace_lights* lights) { // Init trace lights void init_lights(trace_lights* lights, const trace_scene* scene, - const progress_callback& progress_cb) { + const trace_params& params, const progress_callback& progress_cb) { // handle progress auto progress = vec2i{0, 1}; if (progress_cb) progress_cb("build light", progress.x++, progress.y); @@ -2018,17 +2016,22 @@ void init_lights(trace_lights* lights, const trace_scene* scene, image trace_image(const trace_scene* scene, const trace_camera* camera, const trace_params& params, const progress_callback& progress_cb, const image_callback& image_cb) { + auto bvh_guard = std::make_unique(); + auto bvh = bvh_guard.get(); + init_bvh(bvh, scene, params, progress_cb); + auto lights_guard = std::make_unique(); auto lights = lights_guard.get(); - init_lights(lights, scene, progress_cb); + init_lights(lights, scene, params, progress_cb); - return trace_image(scene, camera, lights, params, progress_cb, image_cb); + return trace_image(scene, camera, bvh, lights, params, progress_cb, image_cb); } // Progressively compute an image by calling trace_samples multiple times. image trace_image(const trace_scene* scene, const trace_camera* camera, - const trace_lights* lights, const trace_params& params, - const progress_callback& progress_cb, const image_callback& image_cb) { + const trace_bvh* bvh, const trace_lights* lights, + const trace_params& params, const progress_callback& progress_cb, + const image_callback& image_cb) { auto state_guard = std::make_unique(); auto state = state_guard.get(); init_state(state, scene, camera, params); @@ -2038,13 +2041,13 @@ image trace_image(const trace_scene* scene, const trace_camera* camera, if (params.noparallel) { for (auto j = 0; j < state->render.height(); j++) { for (auto i = 0; i < state->render.width(); i++) { - trace_sample(state, scene, camera, lights, {i, j}, params); + trace_sample(state, scene, camera, bvh, lights, {i, j}, params); } } } else { parallel_for(state->render.width(), state->render.height(), - [state, scene, camera, lights, ¶ms](int i, int j) { - trace_sample(state, scene, camera, lights, {i, j}, params); + [state, scene, camera, bvh, lights, ¶ms](int i, int j) { + trace_sample(state, scene, camera, bvh, lights, {i, j}, params); }); } if (image_cb) image_cb(state->render, sample + 1, params.samples); @@ -2056,9 +2059,10 @@ image trace_image(const trace_scene* scene, const trace_camera* camera, // [experimental] Asynchronous interface void trace_start(trace_state* state, const trace_scene* scene, - const trace_camera* camera, const trace_lights* lights, - const trace_params& params, const progress_callback& progress_cb, - const image_callback& image_cb, const async_callback& async_cb) { + const trace_camera* camera, const trace_bvh* bvh, + const trace_lights* lights, const trace_params& params, + const progress_callback& progress_cb, const image_callback& image_cb, + const async_callback& async_cb) { init_state(state, scene, camera, params); state->worker = {}; state->stop = false; @@ -2068,7 +2072,7 @@ void trace_start(trace_state* state, const trace_scene* scene, auto pprms = params; pprms.resolution /= params.pratio; pprms.samples = 1; - auto preview = trace_image(scene, camera, lights, pprms); + auto preview = trace_image(scene, camera, bvh, lights, pprms); for (auto j = 0; j < state->render.height(); j++) { for (auto i = 0; i < state->render.width(); i++) { auto pi = clamp(i / params.pratio, 0, preview.width() - 1), @@ -2086,7 +2090,7 @@ void trace_start(trace_state* state, const trace_scene* scene, parallel_for( state->render.width(), state->render.height(), [&](int i, int j) { if (state->stop) return; - trace_sample(state, scene, camera, lights, {i, j}, params); + trace_sample(state, scene, camera, bvh, lights, {i, j}, params); if (async_cb) async_cb(state->render, sample, params.samples, {i, j}); }); diff --git a/libs/yocto/yocto_trace.h b/libs/yocto/yocto_trace.h index 14b47aed7..d8e4b64af 100644 --- a/libs/yocto/yocto_trace.h +++ b/libs/yocto/yocto_trace.h @@ -203,9 +203,6 @@ struct trace_scene { vector textures = {}; vector materials = {}; - // computed properties - bvh_scene* bvh = nullptr; - // cleanup ~trace_scene(); }; @@ -514,22 +511,25 @@ struct trace_lights { // Initialize lights. void init_lights(trace_lights* lights, const trace_scene* scene, - const progress_callback& progress_cb = {}); + const trace_params& params, const progress_callback& progress_cb = {}); + +// Define BVH +using trace_bvh = bvh_scene; // Build the bvh acceleration structure. -void init_bvh(trace_scene* scene, const trace_params& params, - const progress_callback& progress_cb = {}); +void init_bvh(trace_bvh* bvh, const trace_scene* scene, + const trace_params& params, const progress_callback& progress_cb = {}); // Refit bvh data -void update_bvh(trace_scene* scene, +void update_bvh(trace_bvh* bvh, const trace_scene* scene, const vector& updated_instances, const vector& updated_shapes, const trace_params& params); // Progressively computes an image. image trace_image(const trace_scene* scene, const trace_camera* camera, - const trace_lights* lights, const trace_params& params, - const progress_callback& progress_cb = {}, - const image_callback& image_cb = {}); + const trace_bvh* bvh, const trace_lights* lights, + const trace_params& params, const progress_callback& progress_cb = {}, + const image_callback& image_cb = {}); // Check is a sampler requires lights bool is_sampler_lit(const trace_params& params); @@ -551,8 +551,9 @@ using async_callback = function