LightSampler::LightSampler(const Scene& scene) : m_total_emissive_area(0.0) { RENDERER_LOG_INFO("collecting light emitters..."); // Collect all lights and light-emitting triangles. for (const_each<AssemblyInstanceContainer> i = scene.assembly_instances(); i; ++i) { const AssemblyInstance& assembly_instance = *i; const Assembly& assembly = assembly_instance.get_assembly(); collect_lights(assembly, assembly_instance); if (has_emitting_materials(assembly)) collect_emitting_triangles(assembly, assembly_instance); } // Precompute some values. m_light_count = m_lights.size(); m_rcp_total_emissive_area = 1.0 / m_total_emissive_area; // Prepare the CDFs for sampling. if (m_emitter_cdf.valid()) m_emitter_cdf.prepare(); if (m_emitting_triangle_cdf.valid()) m_emitting_triangle_cdf.prepare(); RENDERER_LOG_INFO( "found %s %s, %s emitting %s.", pretty_int(m_light_count).c_str(), plural(m_light_count, "light").c_str(), pretty_int(m_emitting_triangles.size()).c_str(), plural(m_emitting_triangles.size(), "triangle").c_str()); }
void SPPMParameters::print() const { RENDERER_LOG_INFO( "sppm settings:\n" " dl %s\n" " ibl %s", m_dl_mode == SPPM ? "sppm" : m_dl_mode == RayTraced ? "ray traced" : "off", m_enable_ibl ? "on" : "off"); RENDERER_LOG_INFO( "sppm photon tracing settings:\n" " light photons %s\n" " env. photons %s\n" " max path length %s\n" " rr min path len. %s", pretty_uint(m_light_photon_count).c_str(), pretty_uint(m_env_photon_count).c_str(), m_photon_tracing_max_path_length == ~0 ? "infinite" : pretty_uint(m_photon_tracing_max_path_length).c_str(), m_photon_tracing_rr_min_path_length == ~0 ? "infinite" : pretty_uint(m_photon_tracing_rr_min_path_length).c_str()); RENDERER_LOG_INFO( "sppm path tracing settings:\n" " max path length %s\n" " rr min path len. %s\n" " initial radius %s%%\n" " alpha %s\n" " max photons/est. %s\n" " dl light samples %s", m_path_tracing_max_path_length == ~0 ? "infinite" : pretty_uint(m_path_tracing_max_path_length).c_str(), m_path_tracing_rr_min_path_length == ~0 ? "infinite" : pretty_uint(m_path_tracing_rr_min_path_length).c_str(), pretty_scalar(m_initial_radius_percents, 3).c_str(), pretty_scalar(m_alpha, 1).c_str(), pretty_uint(m_max_photons_per_estimate).c_str(), pretty_scalar(m_dl_light_sample_count).c_str()); }
LightSampler::LightSampler(const Scene& scene) : m_total_emissive_area(0.0) { RENDERER_LOG_INFO("collecting light emitters..."); // Collect all lights and light-emitting triangles. collect_lights(scene); collect_emitting_triangles(scene); // Precompute some values. m_light_count = m_lights.size(); m_rcp_total_emissive_area = 1.0 / m_total_emissive_area; // Prepare the CDFs for sampling. if (m_emitter_cdf.valid()) m_emitter_cdf.prepare(); if (m_emitting_triangle_cdf.valid()) m_emitting_triangle_cdf.prepare(); RENDERER_LOG_INFO( "found %s %s, %s emitting %s.", pretty_int(m_light_count).c_str(), plural(m_light_count, "light").c_str(), pretty_int(m_emitting_triangles.size()).c_str(), plural(m_emitting_triangles.size(), "triangle").c_str()); }
ItemBase* ScenePickingHandler::pick(const QPoint& point) { if (!m_project.has_trace_context()) { RENDERER_LOG_INFO("the scene must be rendering or must have been rendered at least once for picking to be available."); return 0; } const Vector2i pix = m_mouse_tracker.widget_to_pixel(point); const Vector2d ndc = m_mouse_tracker.widget_to_ndc(point); const ScenePicker scene_picker(m_project); const ScenePicker::PickingResult result = scene_picker.pick(ndc); stringstream sstr; sstr << "picking details:" << endl; sstr << " pixel coords " << pix.x << ", " << pix.y << endl; sstr << " ndc coords " << ndc.x << ", " << ndc.y << endl; sstr << " world coords " << result.m_point.x << ", " << result.m_point.y << ", " << result.m_point.z << endl; sstr << " depth " << result.m_distance << endl; sstr << " primitive type " << get_primitive_type_name(result.m_primitive_type) << endl; sstr << print_entity(" camera ", result.m_camera) << endl; sstr << print_entity(" assembly inst. ", result.m_assembly_instance) << endl; sstr << print_entity(" assembly ", result.m_assembly) << endl; sstr << print_entity(" object inst. ", result.m_object_instance) << endl; sstr << print_entity(" object ", result.m_object) << endl; sstr << print_entity(" material ", result.m_material) << endl; sstr << print_entity(" surface shader ", result.m_surface_shader) << endl; sstr << print_entity(" bsdf ", result.m_bsdf) << endl; sstr << print_entity(" bssrdf ", result.m_bssrdf) << endl; sstr << print_entity(" edf ", result.m_edf); RENDERER_LOG_INFO("%s", sstr.str().c_str()); emit signal_entity_picked(result); const QString picking_mode = m_picking_mode_combo->itemData(m_picking_mode_combo->currentIndex()).value<QString>(); const Entity* picked_entity = get_picked_entity(result, picking_mode); ItemBase* item; if (picked_entity) { item = m_project_explorer.select_entity(picked_entity->get_uid()); } else { m_project_explorer.clear_selection(); item = 0; } m_widget->setFocus(); return item; }
void CurveTree::build_bvh( const ParamArray& params, const double time, Statistics& statistics) { // Collect curves for this tree. RENDERER_LOG_INFO( "collecting geometry for curve tree #" FMT_UNIQUE_ID " from assembly \"%s\"...", m_arguments.m_curve_tree_uid, m_arguments.m_assembly.get_path().c_str()); vector<GAABB3> curve_bboxes; collect_curves(curve_bboxes); // Print statistics about the input geometry. RENDERER_LOG_INFO( "building curve tree #" FMT_UNIQUE_ID " (bvh, %s %s)...", m_arguments.m_curve_tree_uid, pretty_uint(m_curve_keys.size()).c_str(), plural(m_curve_keys.size(), "curve").c_str()); // Create the partitioner. typedef bvh::SAHPartitioner<vector<GAABB3> > Partitioner; Partitioner partitioner( curve_bboxes, CurveTreeDefaultMaxLeafSize, CurveTreeDefaultInteriorNodeTraversalCost, CurveTreeDefaultCurveIntersectionCost); // Build the tree. typedef bvh::Builder<CurveTree, Partitioner> Builder; Builder builder; builder.build<DefaultWallclockTimer>( *this, partitioner, m_curves1.size() + m_curves3.size(), CurveTreeDefaultMaxLeafSize); statistics.merge( bvh::TreeStatistics<CurveTree>(*this, m_arguments.m_bbox)); // Reorder the curve keys based on the nodes ordering. if (!m_curves1.empty() || !m_curves3.empty()) { const vector<size_t>& ordering = partitioner.get_item_ordering(); reorder_curve_keys(ordering); reorder_curves(ordering); reorder_curve_keys_in_leaf_nodes(); } }
bool MeshObjectWriter::write( const MeshObject& object, const char* object_name, const char* filename) { assert(filename); Stopwatch<DefaultWallclockTimer> stopwatch; stopwatch.start(); try { GenericMeshFileWriter writer(filename); MeshObjectWalker walker(object, object_name); writer.write(walker); } catch (const exception& e) { RENDERER_LOG_ERROR( "failed to write mesh file %s: %s.", filename, e.what()); return false; } stopwatch.measure(); RENDERER_LOG_INFO( "wrote mesh file %s in %s.", filename, pretty_time(stopwatch.get_seconds()).c_str()); return true; }
bool Frame::write_image( const char* file_path, const Image& image, const ImageAttributes& image_attributes) const { assert(file_path); Image final_image(image); transform_to_output_color_space(final_image); Stopwatch<DefaultWallclockTimer> stopwatch; stopwatch.start(); try { try { GenericImageFileWriter writer; writer.write(file_path, final_image, image_attributes); } catch (const ExceptionUnsupportedFileFormat&) { const string extension = lower_case(filesystem::path(file_path).extension()); RENDERER_LOG_ERROR( "file format '%s' not supported, writing the image in OpenEXR format " "(but keeping the filename unmodified).", extension.c_str()); EXRImageFileWriter writer; writer.write(file_path, final_image, image_attributes); } } catch (const ExceptionIOError&) { RENDERER_LOG_ERROR( "failed to write image file %s: i/o error.", file_path); return false; } catch (const Exception& e) { RENDERER_LOG_ERROR( "failed to write image file %s: %s.", file_path, e.what()); return false; } stopwatch.measure(); RENDERER_LOG_INFO( "wrote image file %s in %s.", file_path, pretty_time(stopwatch.get_seconds()).c_str()); return true; }
void SPPMPassCallback::pre_render( const Frame& frame, JobQueue& job_queue, IAbortSwitch& abort_switch) { if (m_initial_lookup_radius > 0.0f) { RENDERER_LOG_INFO( "sppm lookup radius is %f (%s of initial radius).", m_lookup_radius, pretty_percent(m_lookup_radius, m_initial_lookup_radius, 3).c_str()); } m_stopwatch.start(); // Create a new set of photons. m_photons.clear_keep_memory(); m_photon_tracer.trace_photons( m_photons, hash_uint32(m_pass_number), job_queue, abort_switch); // Stop there if rendering was aborted. if (abort_switch.is_aborted()) return; // Build a new photon map. m_photon_map.reset(new SPPMPhotonMap(m_photons)); }
void Frame::print_settings() { RENDERER_LOG_INFO( "frame settings:\n" " resolution %s x %s\n" " tile size %s x %s\n" " pixel format %s\n" " filter %s\n" " filter size %f\n" " color space %s\n" " premult. alpha %s\n" " clamping %s\n" " gamma correction %f\n" " crop window (%s, %s)-(%s, %s)", pretty_uint(impl->m_frame_width).c_str(), pretty_uint(impl->m_frame_height).c_str(), pretty_uint(impl->m_tile_width).c_str(), pretty_uint(impl->m_tile_height).c_str(), pixel_format_name(impl->m_pixel_format), impl->m_filter_name.c_str(), impl->m_filter_radius, color_space_name(m_color_space), m_is_premultiplied_alpha ? "on" : "off", impl->m_clamp ? "on" : "off", impl->m_target_gamma, pretty_uint(impl->m_crop_window.min[0]).c_str(), pretty_uint(impl->m_crop_window.min[1]).c_str(), pretty_uint(impl->m_crop_window.max[0]).c_str(), pretty_uint(impl->m_crop_window.max[1]).c_str()); }
void FrameRendererBase::print_rendering_thread_count(const size_t thread_count) { RENDERER_LOG_INFO( "using %s %s for rendering.", pretty_int(thread_count).c_str(), plural(thread_count, "thread").c_str()); }
AOVoxelTree::AOVoxelTree( const Scene& scene, const GScalar max_extent_fraction) { assert(max_extent_fraction > GScalar(0.0)); // Print a progress message. RENDERER_LOG_INFO("building ambient occlusion voxel tree..."); // Compute the bounding box of the scene. const GAABB3 scene_bbox = scene.compute_bbox(); // Compute the maximum extent of a leaf, in world space. const GScalar max_extent = max_extent_fraction * max_value(scene_bbox.extent()); // Build the tree. BuilderType builder(m_tree, scene_bbox, max_extent); build(scene, builder); builder.complete(); // Print statistics. TreeStatisticsType tree_stats(m_tree, builder); RENDERER_LOG_DEBUG("ambient occlusion voxel tree statistics:"); tree_stats.print(global_logger()); }
void OIIOErrorHandler::operator()(int errcode, const std::string& msg) { switch (errcode) { case EH_WARNING: RENDERER_LOG_WARNING("%s", msg.c_str()); break; case EH_ERROR: RENDERER_LOG_ERROR("%s", msg.c_str()); break; case EH_SEVERE: RENDERER_LOG_FATAL("%s", msg.c_str()); break; case EH_DEBUG: RENDERER_LOG_DEBUG("%s", msg.c_str()); break; default: RENDERER_LOG_INFO("%s", msg.c_str()); break; } }
void ShaderGroup::report_uses_global(const char* global_name, const bool uses_global) const { if (uses_global) { RENDERER_LOG_INFO( "shader group %s uses the %s global.", get_name(), global_name); } else { RENDERER_LOG_INFO( "shader group %s does not use the %s global.", get_name(), global_name); } }
void ShaderGroup::report_has_closure(const char* closure_name, const bool has_closure) const { if (has_closure) { RENDERER_LOG_INFO( "shader group %s has %s closures.", get_name(), closure_name); } else { RENDERER_LOG_INFO( "shader group %s does not have %s closures.", get_name(), closure_name); } }
void RenderingManager::print_final_rendering_time() { const double rendering_time = m_rendering_timer.get_seconds(); const string rendering_time_string = pretty_time(rendering_time, 3); RENDERER_LOG_INFO("rendering finished in %s.", rendering_time_string.c_str()); m_status_bar.set_text("Rendering finished in " + rendering_time_string); }
void AOVoxelTree::dump_solid_leaves_to_disk(const string& filename) const { RENDERER_LOG_INFO( "writing ambient occlusion voxel tree file %s...", filename.c_str()); if (m_tree.dump_solid_leaves_to_disk(filename)) { RENDERER_LOG_INFO( "wrote ambient occlusion voxel tree file %s.", filename.c_str()); } else { RENDERER_LOG_ERROR( "failed to write ambient occlusion voxel tree file %s: i/o error.", filename.c_str()); } }
RegionTree::~RegionTree() { RENDERER_LOG_INFO( "deleting region tree for assembly #" FMT_UNIQUE_ID "...", m_assembly_uid); // Delete triangle trees. for (each<TriangleTreeContainer> i = m_triangle_trees; i; ++i) delete i->second; }
void RenderingManager::archive_frame_to_disk() { RENDERER_LOG_INFO("archiving frame to disk..."); const filesystem::path autosave_path = filesystem::path(Application::get_root_path()) / "images" / "autosave"; m_project->get_frame()->archive(autosave_path.string().c_str()); }
void AssemblyTree::rebuild_assembly_tree() { // Clear the current tree. clear(); m_assembly_instances.clear(); Statistics statistics; // Collect all assembly instances of the scene. AABBVector assembly_instance_bboxes; collect_assembly_instances(assembly_instance_bboxes); RENDERER_LOG_INFO( "building assembly tree (%s %s)...", pretty_int(m_assembly_instances.size()).c_str(), plural(m_assembly_instances.size(), "assembly instance").c_str()); // Create the partitioner. typedef bvh::SAHPartitioner<AABBVector> Partitioner; Partitioner partitioner( assembly_instance_bboxes, AssemblyTreeMaxLeafSize, AssemblyTreeInteriorNodeTraversalCost, AssemblyTreeTriangleIntersectionCost); // Build the assembly tree. typedef bvh::Builder<AssemblyTree, Partitioner> Builder; Builder builder; builder.build<DefaultWallclockTimer>(*this, partitioner, m_assembly_instances.size(), AssemblyTreeMaxLeafSize); statistics.insert_time("build time", builder.get_build_time()); statistics.merge(bvh::TreeStatistics<AssemblyTree>(*this, AABB3d(m_scene.compute_bbox()))); if (!m_assembly_instances.empty()) { const vector<size_t>& ordering = partitioner.get_item_ordering(); assert(m_assembly_instances.size() == ordering.size()); // Reorder the assembly instances according to the tree ordering. vector<const AssemblyInstance*> temp_assembly_instances(ordering.size()); small_item_reorder( &m_assembly_instances[0], &temp_assembly_instances[0], &ordering[0], ordering.size()); // Store assembly instances in the tree leaves whenever possible. store_assembly_instances_in_leaves(statistics); } // Print assembly tree statistics. RENDERER_LOG_DEBUG("%s", StatisticsVector::make( "assembly tree statistics", statistics).to_string().c_str()); }
void ScenePickingHandler::pick(const QPoint& point) { if (!m_project.has_trace_context()) { RENDERER_LOG_INFO("the scene must be rendering or must have been rendered at least once for picking to be available."); return; } const Vector2i pix = m_mouse_tracker.widget_to_pixel(point); const Vector2d ndc = m_mouse_tracker.widget_to_ndc(point); const ScenePicker scene_picker(m_project.get_trace_context()); const ScenePicker::PickingResult result = scene_picker.pick(ndc); stringstream sstr; sstr << "picking details:" << endl; sstr << " pixel coords " << pix.x << ", " << pix.y << endl; sstr << " ndc coords " << ndc.x << ", " << ndc.y << endl; print_entity(sstr, " camera ", result.m_camera); print_entity(sstr, " assembly inst. ", result.m_assembly_instance); print_entity(sstr, " assembly ", result.m_assembly); print_entity(sstr, " object inst. ", result.m_object_instance); print_entity(sstr, " object ", result.m_object); print_entity(sstr, " material ", result.m_material); print_entity(sstr, " surface shader ", result.m_surface_shader); print_entity(sstr, " bsdf ", result.m_bsdf); print_entity(sstr, " edf ", result.m_edf); RENDERER_LOG_INFO("%s", sstr.str().c_str()); const QString picking_mode = m_picking_mode_combo->itemData(m_picking_mode_combo->currentIndex()).value<QString>(); const Entity* picked_entity = get_picked_entity(result, picking_mode); if (picked_entity) m_project_explorer.highlight_entity(picked_entity->get_uid()); else m_project_explorer.clear_highlighting(); }
void LightPathsWidget::dump_selected_light_path() const { if (m_selected_light_path_index == -1) { if (m_light_paths.empty()) RENDERER_LOG_INFO("no light path to display."); else { RENDERER_LOG_INFO("displaying all %s light path%s.", pretty_uint(m_light_paths.size()).c_str(), m_light_paths.size() > 1 ? "s" : ""); } } else { RENDERER_LOG_INFO("displaying light path %s:", pretty_int(m_selected_light_path_index + 1).c_str()); const auto& light_path_recorder = m_project.get_light_path_recorder(); const auto& path = m_light_paths[m_selected_light_path_index]; for (size_t i = path.m_vertex_begin_index; i < path.m_vertex_end_index; ++i) { LightPathVertex v; light_path_recorder.get_light_path_vertex(i, v); const string entity_name = v.m_entity != nullptr ? foundation::format("\"{0}\"", v.m_entity->get_path().c_str()) : "n/a"; RENDERER_LOG_INFO(" vertex " FMT_SIZE_T ": entity: %s - position: (%f, %f, %f) - radiance: (%f, %f, %f) - total radiance: %f", i - path.m_vertex_begin_index + 1, entity_name.c_str(), v.m_position[0], v.m_position[1], v.m_position[2], v.m_radiance[0], v.m_radiance[1], v.m_radiance[2], v.m_radiance[0] + v.m_radiance[1] + v.m_radiance[2]); } } }
int LibInitialize() { start_memory_tracking(); asr::global_logger().add_target(&g_log_target); std::stringstream sstr; sstr << "appleseed-max "; sstr << wide_to_utf8(PluginVersionString); sstr << " plug-in for "; sstr << asf::Appleseed::get_synthetic_version_string(); sstr << " loaded"; const std::string title = sstr.str(); const std::string sep(title.size(), '='); RENDERER_LOG_INFO("%s", sep.c_str()); RENDERER_LOG_INFO("%s", title.c_str()); RENDERER_LOG_INFO("%s", sep.c_str()); return TRUE; }
void postprocess(const RenderingResult& rendering_result) { Frame* frame = m_project.get_frame(); assert(frame != nullptr); // Nothing to do if there are no post-processing stages. if (frame->post_processing_stages().empty()) return; // Collect post-processing stages. vector<PostProcessingStage*> ordered_stages; ordered_stages.reserve(frame->post_processing_stages().size()); for (auto& stage : frame->post_processing_stages()) ordered_stages.push_back(&stage); // Sort post-processing stages in increasing order. sort( ordered_stages.begin(), ordered_stages.end(), [](PostProcessingStage* lhs, PostProcessingStage* rhs) { return lhs->get_order() < rhs->get_order(); }); // Detect post-processing stages with equal order. size_t previous_stage_index = 0; int previous_order = ordered_stages[0]->get_order(); for (size_t i = 1, e = ordered_stages.size(); i < e; ++i) { const int order = ordered_stages[i]->get_order(); if (order == previous_order) { RENDERER_LOG_WARNING( "post-processing stages \"%s\" and \"%s\" have equal order (%d); results will be unpredictable.", ordered_stages[previous_stage_index]->get_path().c_str(), ordered_stages[i]->get_path().c_str(), order); } } // Execute post-processing stages. for (auto stage : ordered_stages) { RENDERER_LOG_INFO("executing \"%s\" post-processing stage with order %d on frame \"%s\"...", stage->get_path().c_str(), stage->get_order(), frame->get_path().c_str()); stage->execute(*frame); invoke_tile_callbacks(*frame); } }
LightSampler::LightSampler(const Scene& scene, const ParamArray& params) : m_params(params) , m_emitting_triangle_hash_table(m_triangle_key_hasher) { RENDERER_LOG_INFO("collecting light emitters..."); // Collect all non-physical lights. collect_non_physical_lights(scene.assembly_instances(), TransformSequence()); m_non_physical_light_count = m_non_physical_lights.size(); // Collect all light-emitting triangles. collect_emitting_triangles( scene.assembly_instances(), TransformSequence()); // Build the hash table of emitting triangles. build_emitting_triangle_hash_table(); // Prepare the CDFs for sampling. if (m_non_physical_lights_cdf.valid()) m_non_physical_lights_cdf.prepare(); if (m_emitting_triangles_cdf.valid()) m_emitting_triangles_cdf.prepare(); // Store the triangle probability densities into the emitting triangles. const size_t emitting_triangle_count = m_emitting_triangles.size(); for (size_t i = 0; i < emitting_triangle_count; ++i) m_emitting_triangles[i].m_triangle_prob = m_emitting_triangles_cdf[i].second; RENDERER_LOG_INFO( "found %s %s, %s emitting %s.", pretty_int(m_non_physical_light_count).c_str(), plural(m_non_physical_light_count, "non-physical light").c_str(), pretty_int(m_emitting_triangles.size()).c_str(), plural(m_emitting_triangles.size(), "triangle").c_str()); }
AssemblyTree::~AssemblyTree() { // Log a progress message. RENDERER_LOG_INFO("deleting assembly tree..."); // Delete region trees. for (each<RegionTreeContainer> i = m_region_trees; i; ++i) delete i->second; m_region_trees.clear(); // Delete triangle trees. for (each<TriangleTreeContainer> i = m_triangle_trees; i; ++i) delete i->second; m_triangle_trees.clear(); }
bool RenderClipboardHandler::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::KeyPress) { const QKeyEvent* key_event = static_cast<QKeyEvent*>(event); if (key_event->modifiers() == Qt::ControlModifier && key_event->key() == Qt::Key_C) { QApplication::clipboard()->setImage(m_widget->get_image_copy()); RENDERER_LOG_INFO("copied render to clipboard."); return true; } } return QObject::eventFilter(object, event); }
void ExpressionEditorWindow::apply_expression() { const string expression = m_editor->getExpr(); const SeAppleseedExpr expr(expression); if (expr.isValid()) { m_error->hide(); RENDERER_LOG_INFO("expression successfully applied."); emit signal_expression_applied(m_widget_name, QString::fromStdString(expression)); } else { m_error->show(); RENDERER_LOG_ERROR("expression error: %s", expr.parseError().c_str()); } }
void ExpressionEditorWindow::apply_expression() { const string expression = m_editor->getExpr(); const DisneyParamExpression se_expression(expression.c_str()); if (se_expression.is_valid()) { m_error->hide(); RENDERER_LOG_INFO("Expression successfully applied."); const QString q_expression = QString::fromStdString(expression); emit signal_expression_applied(m_widget_name, q_expression); } else { m_error->show(); se_expression.report_error("Expression has errors"); } }
void SPPMPassCallback::post_render( const Frame& frame, JobQueue& job_queue, IAbortSwitch& abort_switch) { // Shrink the lookup radius for the next pass. const float k = (m_pass_number + m_params.m_alpha) / (m_pass_number + 1); assert(k <= 1.0); m_lookup_radius *= sqrt(k); m_stopwatch.measure(); RENDERER_LOG_INFO( "sppm pass %s completed in %s.", pretty_uint(m_pass_number + 1).c_str(), pretty_time(m_stopwatch.get_seconds()).c_str()); ++m_pass_number; }
void AssemblyTree::build_assembly_tree() { // Insert all assembly instances of the scene into the tree. for (const_each<AssemblyInstanceContainer> i = m_scene.assembly_instances(); i; ++i) { // Retrieve the assembly instance. const AssemblyInstance& assembly_instance = *i; // Retrieve the assembly. const Assembly& assembly = assembly_instance.get_assembly(); // Skip empty assemblies. if (assembly.object_instances().empty()) continue; // Insert the assembly instance into the root leaf. insert( assembly_instance.get_uid(), assembly_instance.compute_parent_bbox()); } // Log a progress message. RENDERER_LOG_INFO( "building assembly bvh (%s %s)...", pretty_int(size()).c_str(), plural(size(), "assembly instance").c_str()); // Build the assembly tree. AssemblyTreePartitioner partitioner; AssemblyTreeBuilder builder; builder.build(*this, partitioner); // Collect and print assembly tree statistics. AssemblyTreeStatistics tree_stats(*this, builder); RENDERER_LOG_DEBUG("assembly bvh statistics:"); tree_stats.print(global_logger()); }