/* * Copyright 2018, Blender Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* This class implemens a ray accelerator for Cycles using Intel's Embree library. * It supports triangles, curves, object and deformation blur and instancing. * Not supported are thick line segments, those have no native equivalent in Embree. * They could be implemented using Embree's thick curves, at the expense of wasted memory. * User defined intersections for Embree could also be an option, but since Embree only uses aligned BVHs * for user geometry, this would come with reduced performance and/or higher memory usage. * * Since Embree allows object to be either curves or triangles but not both, Cycles object IDs are maapped * to Embree IDs by multiplying by two and adding one for curves. * * This implementation shares RTCDevices between Cycles instances. Eventually each instance should get * a separate RTCDevice to correctly keep track of memory usage. * * Vertex and index buffers are duplicated between Cycles device arrays and Embree. These could be merged, * which would requrie changes to intersection refinement, shader setup, mesh light sampling and a few * other places in Cycles where direct access to vertex data is required. */ #ifdef WITH_EMBREE #include #include #include #include "bvh/bvh_embree.h" /* Kernel includes are necessary so that the filter function for Embree can access the packed BVH. */ #include "kernel/bvh/bvh_embree.h" #include "kernel/kernel_compat_cpu.h" #include "kernel/split/kernel_split_data_types.h" #include "kernel/kernel_globals.h" #include "kernel/kernel_random.h" #include "render/mesh.h" #include "render/object.h" #include "util/util_foreach.h" #include "util/util_logging.h" #include "util/util_progress.h" CCL_NAMESPACE_BEGIN #define IS_HAIR(x) (x & 1) /* This gets called by Embree at every valid ray/object intersection. * Things like recording subsurface or shadow hits for later evaluation * as well as filtering for volume objects happen here. * Cycles' own BVH does that directly inside the traversal calls. */ static void rtc_filter_func(const RTCFilterFunctionNArguments *args) { /* Current implementation in Cycles assumes only single-ray intersection queries. */ assert(args->N == 1); const RTCRay *ray = (RTCRay*)args->ray; const RTCHit *hit = (RTCHit*)args->hit; CCLIntersectContext *ctx = ((IntersectContext*)args->context)->userRayExt; KernelGlobals *kg = ctx->kg; /* Check if there is backfacing hair to ignore. */ if(IS_HAIR(hit->geomID) && (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) && !(kernel_data.curve.curveflags & CURVE_KN_BACKFACING) && !(kernel_data.curve.curveflags & CURVE_KN_RIBBONS)) { if(dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { *args->valid = 0; return; } } } static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments* args) { assert(args->N == 1); const RTCRay *ray = (RTCRay*)args->ray; RTCHit *hit = (RTCHit*)args->hit; CCLIntersectContext *ctx = ((IntersectContext*)args->context)->userRayExt; KernelGlobals *kg = ctx->kg; /* For all ray types: Check if there is backfacing hair to ignore */ if(IS_HAIR(hit->geomID) && (kernel_data.curve.curveflags & CURVE_KN_INTERPOLATE) && !(kernel_data.curve.curveflags & CURVE_KN_BACKFACING) && !(kernel_data.curve.curveflags & CURVE_KN_RIBBONS)) { if(dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { *args->valid = 0; return; } } switch(ctx->type) { case CCLIntersectContext::RAY_SHADOW_ALL: { /* Append the intersection to the end of the array. */ if(ctx->num_hits < ctx->max_hits) { Intersection current_isect; kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); for(size_t i = 0; i < ctx->max_hits; ++i) { if(current_isect.object == ctx->isect_s[i].object && current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { /* This intersection was already recorded, skip it. */ *args->valid = 0; break; } } Intersection *isect = &ctx->isect_s[ctx->num_hits]; ++ctx->num_hits; *isect = current_isect; int prim = kernel_tex_fetch(__prim_index, isect->prim); int shader = 0; if(kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) { shader = kernel_tex_fetch(__tri_shader, prim); } else { float4 str = kernel_tex_fetch(__curves, prim); shader = __float_as_int(str.z); } int flag = kernel_tex_fetch(__shaders, shader & SHADER_MASK).flags; /* If no transparent shadows, all light is blocked. */ if(flag & (SD_HAS_TRANSPARENT_SHADOW)) { /* This tells Embree to continue tracing. */ *args->valid = 0; } } else { /* Increase the number of hits beyond ray.max_hits * so that the caller can detect this as opaque. */ ++ctx->num_hits; } break; } case CCLIntersectContext::RAY_SSS: { /* No intersection information requested, just return a hit. */ if(ctx->max_hits == 0) { break; } /* See triangle_intersect_subsurface() for the native equivalent. */ for(int i = min(ctx->max_hits, ctx->ss_isect->num_hits) - 1; i >= 0; --i) { if(ctx->ss_isect->hits[i].t == ray->tfar) { /* This tells Embree to continue tracing. */ *args->valid = 0; break; } } ++ctx->ss_isect->num_hits; int hit_idx; if(ctx->ss_isect->num_hits <= ctx->max_hits) { hit_idx = ctx->ss_isect->num_hits - 1; } else { /* reservoir sampling: if we are at the maximum number of * hits, randomly replace element or skip it */ hit_idx = lcg_step_uint(ctx->lcg_state) % ctx->ss_isect->num_hits; if(hit_idx >= ctx->max_hits) { /* This tells Embree to continue tracing. */ *args->valid = 0; break; } } /* record intersection */ kernel_embree_convert_local_hit(kg, ray, hit, &ctx->ss_isect->hits[hit_idx], ctx->sss_object_id); ctx->ss_isect->Ng[hit_idx].x = hit->Ng_x; ctx->ss_isect->Ng[hit_idx].y = hit->Ng_y; ctx->ss_isect->Ng[hit_idx].z = hit->Ng_z; ctx->ss_isect->Ng[hit_idx] = normalize(ctx->ss_isect->Ng[hit_idx]); /* This tells Embree to continue tracing .*/ *args->valid = 0; break; } case CCLIntersectContext::RAY_VOLUME_ALL: { /* Append the intersection to the end of the array. */ if(ctx->num_hits < ctx->max_hits) { Intersection current_isect; kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); for(size_t i = 0; i < ctx->max_hits; ++i) { if(current_isect.object == ctx->isect_s[i].object && current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { /* This intersection was already recorded, skip it. */ *args->valid = 0; break; } } Intersection *isect = &ctx->isect_s[ctx->num_hits]; ++ctx->num_hits; *isect = current_isect; /* Only primitives from volume object. */ uint tri_object = (isect->object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, isect->prim) : isect->object; int object_flag = kernel_tex_fetch(__object_flag, tri_object); if((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { --ctx->num_hits; } /* This tells Embree to continue tracing. */ *args->valid = 0; break; } } case CCLIntersectContext::RAY_REGULAR: default: /* Nothing to do here. */ break; } } static size_t unaccounted_mem = 0; static bool rtc_memory_monitor_func(void* userPtr, const ssize_t bytes, const bool) { Stats *stats = (Stats*)userPtr; if(stats) { if(bytes > 0) { stats->mem_alloc(bytes); } else { stats->mem_free(-bytes); } } else { /* A stats pointer may not yet be available. Keep track of the memory usage for later. */ if(bytes >= 0) { atomic_add_and_fetch_z(&unaccounted_mem, bytes); } else { atomic_sub_and_fetch_z(&unaccounted_mem, -bytes); } } return true; } static void rtc_error_func(void*, enum RTCError, const char* str) { VLOG(1) << str; } static double progress_start_time = 0.0f; static bool rtc_progress_func(void* user_ptr, const double n) { Progress *progress = (Progress*)user_ptr; if(time_dt() - progress_start_time < 0.25) { return true; } string msg = string_printf("Building BVH %.0f%%", n * 100.0); progress->set_substatus(msg); progress_start_time = time_dt(); return !progress->get_cancel(); } /* This is to have a shared device between all BVH instances. It would be useful to actually to use a separte RTCDevice per Cycles instance. */ RTCDevice BVHEmbree::rtc_shared_device = NULL; int BVHEmbree::rtc_shared_users = 0; thread_mutex BVHEmbree::rtc_shared_mutex; BVHEmbree::BVHEmbree(const BVHParams& params_, const vector& objects_) : BVH(params_, objects_), scene(NULL), mem_used(0), top_level(NULL), stats(NULL), curve_subdivisions(params.curve_subdivisions), build_quality(RTC_BUILD_QUALITY_REFIT), use_curves(params_.curve_flags & CURVE_KN_INTERPOLATE), use_ribbons(params.curve_flags & CURVE_KN_RIBBONS), dynamic_scene(true) { _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); thread_scoped_lock lock(rtc_shared_mutex); if(rtc_shared_users == 0) { rtc_shared_device = rtcNewDevice("verbose=0"); /* Check here if Embree was built with the correct flags. */ ssize_t ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED); if(ret != 1) { assert(0); VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_RAY_MASK_SUPPORTED flag."\ "Ray visiblity will not work."; } ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED); if(ret != 1) { assert(0); VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_FILTER_FUNCTION_SUPPORTED flag."\ "Renders may not look as expected."; } ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED); if(ret != 1) { assert(0); VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_CURVE_GEOMETRY_SUPPORTED flag. "\ "Line primitives will not be rendered."; } ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED); if(ret != 1) { assert(0); VLOG(1) << "Embree is compiled without the RTC_DEVICE_PROPERTY_TRIANGLE_GEOMETRY_SUPPORTED flag. "\ "Triangle primitives will not be rendered."; } ret = rtcGetDeviceProperty (rtc_shared_device,RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED); if(ret != 0) { assert(0); VLOG(1) << "Embree is compiled with the RTC_DEVICE_PROPERTY_BACKFACE_CULLING_ENABLED flag. "\ "Renders may not look as expected."; } } ++rtc_shared_users; rtcSetDeviceErrorFunction(rtc_shared_device, rtc_error_func, NULL); pack.root_index = -1; } BVHEmbree::~BVHEmbree() { if(!params.top_level) { destroy(scene); } } void BVHEmbree::destroy(RTCScene scene) { if(scene) { rtcReleaseScene(scene); scene = NULL; } thread_scoped_lock lock(rtc_shared_mutex); --rtc_shared_users; if(rtc_shared_users == 0) { rtcReleaseDevice (rtc_shared_device); rtc_shared_device = NULL; } } void BVHEmbree::delete_rtcScene() { if(scene) { /* When this BVH is used as an instance in a top level BVH, don't delete now * Let the top_level BVH know that it should delete it later. */ if(top_level) { top_level->add_delayed_delete_scene(scene); } else { rtcReleaseScene(scene); if(delayed_delete_scenes.size()) { foreach(RTCScene s, delayed_delete_scenes) { rtcReleaseScene(s); } } delayed_delete_scenes.clear(); } scene = NULL; } } void BVHEmbree::build(Progress& progress, Stats *stats_) { assert(rtc_shared_device); stats = stats_; rtcSetDeviceMemoryMonitorFunction(rtc_shared_device, rtc_memory_monitor_func, stats); progress.set_substatus("Building BVH"); if(scene) { rtcReleaseScene(scene); scene = NULL; } const bool dynamic = params.bvh_type == SceneParams::BVH_DYNAMIC; scene = rtcNewScene(rtc_shared_device); const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) | RTC_SCENE_FLAG_COMPACT | RTC_SCENE_FLAG_ROBUST; rtcSetSceneFlags(scene, scene_flags); build_quality = dynamic ? RTC_BUILD_QUALITY_LOW : (params.use_spatial_split ? RTC_BUILD_QUALITY_HIGH : RTC_BUILD_QUALITY_MEDIUM); rtcSetSceneBuildQuality(scene, build_quality); int i = 0; pack.object_node.clear(); foreach(Object *ob, objects) { if(params.top_level) { if(!ob->is_traceable()) { ++i; continue; } if(!ob->mesh->is_instanced()) { add_object(ob, i); } else { add_instance(ob, i); } } else { add_object(ob, i); } ++i; if(progress.get_cancel()) return; } if(progress.get_cancel()) { delete_rtcScene(); stats = NULL; return; } rtcSetSceneProgressMonitorFunction(scene, rtc_progress_func, &progress); rtcCommitScene(scene); pack_primitives(); if(progress.get_cancel()) { delete_rtcScene(); stats = NULL; return; } progress.set_substatus("Packing geometry"); pack_nodes(NULL); stats = NULL; } void BVHEmbree::add_object(Object *ob, int i) { Mesh *mesh = ob->mesh; if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE && mesh->num_triangles() > 0) { add_triangles(ob, i); } if(params.primitive_mask & PRIMITIVE_ALL_CURVE && mesh->num_curves() > 0) { add_curves(ob, i); } } void BVHEmbree::add_instance(Object *ob, int i) { if(!ob || !ob->mesh) { assert(0); return; } BVHEmbree *instance_bvh = (BVHEmbree*)(ob->mesh->bvh); if(instance_bvh->top_level != this) { instance_bvh->top_level = this; } const size_t num_motion_steps = ob->use_motion() ? ob->motion.size() : 1; RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, RTC_GEOMETRY_TYPE_INSTANCE); rtcSetGeometryInstancedScene(geom_id, instance_bvh->scene); rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); if(ob->use_motion()) { for(size_t step = 0; step < num_motion_steps; ++step) { rtcSetGeometryTransform(geom_id, step, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float*)&ob->motion[step]); } } else { rtcSetGeometryTransform(geom_id, 0, RTC_FORMAT_FLOAT3X4_ROW_MAJOR, (const float*)&ob->tfm); } pack.prim_index.push_back_slow(-1); pack.prim_object.push_back_slow(i); pack.prim_type.push_back_slow(PRIMITIVE_NONE); pack.prim_tri_index.push_back_slow(-1); rtcSetGeometryUserData(geom_id, (void*) instance_bvh->scene); rtcSetGeometryMask(geom_id, ob->visibility); rtcCommitGeometry(geom_id); rtcAttachGeometryByID(scene, geom_id, i*2); rtcReleaseGeometry(geom_id); } void BVHEmbree::add_triangles(Object *ob, int i) { size_t prim_offset = pack.prim_index.size(); Mesh *mesh = ob->mesh; const Attribute *attr_mP = NULL; size_t num_motion_steps = 1; if(mesh->has_motion_blur()) { attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if(attr_mP) { num_motion_steps = mesh->motion_steps; if(num_motion_steps > RTC_MAX_TIME_STEP_COUNT) { assert(0); num_motion_steps = RTC_MAX_TIME_STEP_COUNT; } } } const size_t num_triangles = mesh->num_triangles(); RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, RTC_GEOMETRY_TYPE_TRIANGLE); rtcSetGeometryBuildQuality(geom_id, build_quality); rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); unsigned *rtc_indices = (unsigned*)rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof (int) * 3, num_triangles); assert(rtc_indices); if(!rtc_indices) { VLOG(1) << "Embree could not create new geometry buffer for mesh " << mesh->name.c_str() << ".\n"; return; } for(size_t j = 0; j < num_triangles; ++j) { Mesh::Triangle t = mesh->get_triangle(j); rtc_indices[j*3] = t.v[0]; rtc_indices[j*3+1] = t.v[1]; rtc_indices[j*3+2] = t.v[2]; } update_tri_vertex_buffer(geom_id, mesh); pack.prim_object.reserve(pack.prim_object.size() + num_triangles); pack.prim_type.reserve(pack.prim_type.size() + num_triangles); pack.prim_index.reserve(pack.prim_index.size() + num_triangles); pack.prim_tri_index.reserve(pack.prim_index.size() + num_triangles); for(size_t j = 0; j < num_triangles; ++j) { pack.prim_object.push_back_reserved(i); pack.prim_type.push_back_reserved(num_motion_steps > 1 ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE); pack.prim_index.push_back_reserved(j); pack.prim_tri_index.push_back_reserved(j); } rtcSetGeometryUserData(geom_id, (void*) prim_offset); rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func); rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); rtcSetGeometryMask(geom_id, ob->visibility); rtcCommitGeometry(geom_id); rtcAttachGeometryByID(scene, geom_id, i*2); rtcReleaseGeometry(geom_id); } void BVHEmbree::update_tri_vertex_buffer(RTCGeometry geom_id, const Mesh* mesh) { const Attribute *attr_mP = NULL; size_t num_motion_steps = 1; int t_mid = 0; if(mesh->has_motion_blur()) { attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if(attr_mP) { num_motion_steps = mesh->motion_steps; t_mid = (num_motion_steps - 1) / 2; if(num_motion_steps > RTC_MAX_TIME_STEP_COUNT) { assert(0); num_motion_steps = RTC_MAX_TIME_STEP_COUNT; } } } const size_t num_verts = mesh->verts.size(); for(int t = 0; t < num_motion_steps; ++t) { const float3 *verts; if(t == t_mid) { verts = &mesh->verts[0]; } else { int t_ = (t > t_mid) ? (t - 1) : t; verts = &attr_mP->data_float3()[t_ * num_verts]; } float *rtc_verts = (float*) rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t, RTC_FORMAT_FLOAT3, sizeof(float) * 3, num_verts + 1); assert(rtc_verts); if(rtc_verts) { for(size_t j = 0; j < num_verts; ++j) { rtc_verts[0] = verts[j].x; rtc_verts[1] = verts[j].y; rtc_verts[2] = verts[j].z; rtc_verts += 3; } } } } void BVHEmbree::update_curve_vertex_buffer(RTCGeometry geom_id, const Mesh* mesh) { const Attribute *attr_mP = NULL; size_t num_motion_steps = 1; if(mesh->has_motion_blur()) { attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if(attr_mP) { num_motion_steps = mesh->motion_steps; } } const size_t num_curves = mesh->num_curves(); size_t num_keys = 0; for(size_t j = 0; j < num_curves; ++j) { const Mesh::Curve c = mesh->get_curve(j); num_keys += c.num_keys; } /* Copy the CV data to Embree */ const int t_mid = (num_motion_steps - 1) / 2; const float *curve_radius = &mesh->curve_radius[0]; for(int t = 0; t < num_motion_steps; ++t) { const float3 *verts; if(t == t_mid || attr_mP == NULL) { verts = &mesh->curve_keys[0]; } else { int t_ = (t > t_mid) ? (t - 1) : t; verts = &attr_mP->data_float3()[t_ * num_keys]; } float4 *rtc_verts = (float4*)rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t, RTC_FORMAT_FLOAT4, sizeof (float) * 4, num_keys); float4 *rtc_tangents = NULL; if(use_curves) { rtc_tangents = (float4*)rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_TANGENT, t, RTC_FORMAT_FLOAT4, sizeof (float) * 4, num_keys); assert(rtc_tangents); } assert(rtc_verts); if(rtc_verts) { if(use_curves && rtc_tangents) { const size_t num_curves = mesh->num_curves(); for(size_t j = 0; j < num_curves; ++j) { Mesh::Curve c = mesh->get_curve(j); int fk = c.first_key; rtc_verts[0] = float3_to_float4(verts[fk]); rtc_verts[0].w = curve_radius[fk]; rtc_tangents[0] = float3_to_float4(verts[fk + 1] - verts[fk]); rtc_tangents[0].w = curve_radius[fk + 1] - curve_radius[fk]; ++fk; int k = 1; for(;k < c.num_segments(); ++k, ++fk) { rtc_verts[k] = float3_to_float4(verts[fk]); rtc_verts[k].w = curve_radius[fk]; rtc_tangents[k] = float3_to_float4((verts[fk + 1] - verts[fk - 1]) * 0.5f); rtc_tangents[k].w = (curve_radius[fk + 1] - curve_radius[fk - 1]) * 0.5f; } rtc_verts[k] = float3_to_float4(verts[fk]); rtc_verts[k].w = curve_radius[fk]; rtc_tangents[k] = float3_to_float4(verts[fk] - verts[fk - 1]); rtc_tangents[k].w = curve_radius[fk] - curve_radius[fk - 1]; rtc_verts += c.num_keys; rtc_tangents += c.num_keys; } } else { for(size_t j = 0; j < num_keys; ++j) { rtc_verts[j] = float3_to_float4(verts[j]); rtc_verts[j].w = curve_radius[j]; } } } } } void BVHEmbree::add_curves(Object *ob, int i) { size_t prim_offset = pack.prim_index.size(); const Mesh *mesh = ob->mesh; const Attribute *attr_mP = NULL; size_t num_motion_steps = 1; if(mesh->has_motion_blur()) { attr_mP = mesh->curve_attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); if(attr_mP) { num_motion_steps = mesh->motion_steps; } } const size_t num_curves = mesh->num_curves(); size_t num_segments = 0; for(size_t j = 0; j < num_curves; ++j) { Mesh::Curve c = mesh->get_curve(j); assert(c.num_segments() > 0); num_segments += c.num_segments(); } /* Make room for Cycles specific data. */ pack.prim_object.reserve(pack.prim_object.size() + num_segments); pack.prim_type.reserve(pack.prim_type.size() + num_segments); pack.prim_index.reserve(pack.prim_index.size() + num_segments); pack.prim_tri_index.reserve(pack.prim_index.size() + num_segments); enum RTCGeometryType type = (!use_curves) ? RTC_GEOMETRY_TYPE_FLAT_LINEAR_CURVE : (use_ribbons ? RTC_GEOMETRY_TYPE_FLAT_HERMITE_CURVE : RTC_GEOMETRY_TYPE_ROUND_HERMITE_CURVE); RTCGeometry geom_id = rtcNewGeometry(rtc_shared_device, type); rtcSetGeometryTessellationRate(geom_id, curve_subdivisions); unsigned *rtc_indices = (unsigned*) rtcSetNewGeometryBuffer(geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT, sizeof (int), num_segments); size_t rtc_index = 0; for(size_t j = 0; j < num_curves; ++j) { Mesh::Curve c = mesh->get_curve(j); for(size_t k = 0; k < c.num_segments(); ++k) { rtc_indices[rtc_index] = c.first_key + k; /* Cycles specific data. */ pack.prim_object.push_back_reserved(i); pack.prim_type.push_back_reserved(PRIMITIVE_PACK_SEGMENT(num_motion_steps > 1 ? PRIMITIVE_MOTION_CURVE : PRIMITIVE_CURVE, k)); pack.prim_index.push_back_reserved(j); pack.prim_tri_index.push_back_reserved(rtc_index); ++rtc_index; } } rtcSetGeometryBuildQuality(geom_id, build_quality); rtcSetGeometryTimeStepCount(geom_id, num_motion_steps); update_curve_vertex_buffer(geom_id, mesh); rtcSetGeometryUserData(geom_id, (void*) prim_offset); rtcSetGeometryIntersectFilterFunction(geom_id, rtc_filter_func); rtcSetGeometryOccludedFilterFunction(geom_id, rtc_filter_occluded_func); rtcSetGeometryMask(geom_id, ob->visibility); rtcCommitGeometry(geom_id); rtcAttachGeometryByID(scene, geom_id, i * 2 + 1); rtcReleaseGeometry(geom_id); } void BVHEmbree::pack_nodes(const BVHNode *) { /* Quite a bit of this code is for compatibility with Cycles' native BVH. */ if(!params.top_level) { return; } for(size_t i = 0; i < pack.prim_index.size(); ++i) { if(pack.prim_index[i] != -1) { if(pack.prim_type[i] & PRIMITIVE_ALL_CURVE) pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->curve_offset; else pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->tri_offset; } } size_t prim_offset = pack.prim_index.size(); /* reserve */ size_t prim_index_size = pack.prim_index.size(); size_t prim_tri_verts_size = pack.prim_tri_verts.size(); size_t pack_prim_index_offset = prim_index_size; size_t pack_prim_tri_verts_offset = prim_tri_verts_size; size_t object_offset = 0; map mesh_map; foreach(Object *ob, objects) { Mesh *mesh = ob->mesh; BVH *bvh = mesh->bvh; if(mesh->need_build_bvh()) { if(mesh_map.find(mesh) == mesh_map.end()) { prim_index_size += bvh->pack.prim_index.size(); prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); mesh_map[mesh] = 1; } } } mesh_map.clear(); pack.prim_index.resize(prim_index_size); pack.prim_type.resize(prim_index_size); pack.prim_object.resize(prim_index_size); pack.prim_visibility.clear(); pack.prim_tri_verts.resize(prim_tri_verts_size); pack.prim_tri_index.resize(prim_index_size); pack.object_node.resize(objects.size()); int *pack_prim_index = (pack.prim_index.size())? &pack.prim_index[0]: NULL; int *pack_prim_type = (pack.prim_type.size())? &pack.prim_type[0]: NULL; int *pack_prim_object = (pack.prim_object.size())? &pack.prim_object[0]: NULL; float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size())? &pack.prim_tri_verts[0]: NULL; uint *pack_prim_tri_index = (pack.prim_tri_index.size())? &pack.prim_tri_index[0]: NULL; /* merge */ foreach(Object *ob, objects) { Mesh *mesh = ob->mesh; /* We assume that if mesh doesn't need own BVH it was already included * into a top-level BVH and no packing here is needed. */ if(!mesh->need_build_bvh()) { pack.object_node[object_offset++] = prim_offset; continue; } /* if mesh already added once, don't add it again, but used set * node offset for this object */ map::iterator it = mesh_map.find(mesh); if(mesh_map.find(mesh) != mesh_map.end()) { int noffset = it->second; pack.object_node[object_offset++] = noffset; continue; } BVHEmbree *bvh = (BVHEmbree*)mesh->bvh; rtc_memory_monitor_func(stats, unaccounted_mem, true); unaccounted_mem = 0; int mesh_tri_offset = mesh->tri_offset; int mesh_curve_offset = mesh->curve_offset; /* fill in node indexes for instances */ pack.object_node[object_offset++] = prim_offset; mesh_map[mesh] = pack.object_node[object_offset-1]; /* merge primitive, object and triangle indexes */ if(bvh->pack.prim_index.size()) { size_t bvh_prim_index_size = bvh->pack.prim_index.size(); int *bvh_prim_index = &bvh->pack.prim_index[0]; int *bvh_prim_type = &bvh->pack.prim_type[0]; uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; for(size_t i = 0; i < bvh_prim_index_size; ++i) { if(bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_curve_offset; pack_prim_tri_index[pack_prim_index_offset] = -1; } else { pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_tri_offset; pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] + pack_prim_tri_verts_offset; } pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i]; pack_prim_object[pack_prim_index_offset] = 0; ++pack_prim_index_offset; } } /* Merge triangle vertices data. */ if(bvh->pack.prim_tri_verts.size()) { const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, &bvh->pack.prim_tri_verts[0], prim_tri_size*sizeof(float4)); pack_prim_tri_verts_offset += prim_tri_size; } prim_offset += bvh->pack.prim_index.size(); } } void BVHEmbree::refit_nodes() { /* Update all vertex buffers, then tell Embree to rebuild/-fit the BVHs. */ unsigned geom_id = 0; foreach(Object *ob, objects) { if(!params.top_level || (ob->is_traceable() && !ob->mesh->is_instanced())) { if(params.primitive_mask & PRIMITIVE_ALL_TRIANGLE && ob->mesh->num_triangles() > 0) { update_tri_vertex_buffer(rtcGetGeometry(scene, geom_id), ob->mesh); rtcCommitGeometry(rtcGetGeometry(scene,geom_id)); } if(params.primitive_mask & PRIMITIVE_ALL_CURVE && ob->mesh->num_curves() > 0) { update_curve_vertex_buffer(rtcGetGeometry(scene, geom_id+1), ob->mesh); rtcCommitGeometry(rtcGetGeometry(scene,geom_id+1)); } } geom_id += 2; } rtcCommitScene(scene); } CCL_NAMESPACE_END #endif /* WITH_EMBREE */