diff options
author | Brecht Van Lommel <brecht@blender.org> | 2021-10-24 15:19:19 +0300 |
---|---|---|
committer | Brecht Van Lommel <brecht@blender.org> | 2021-10-26 16:36:39 +0300 |
commit | d7d40745fa09061a3117bd3669c5a46bbf611eae (patch) | |
tree | 8dbaca086ecbb09aad62c25e9ece66332fe79af3 /intern/cycles/scene/scene.cpp | |
parent | b698fe1e047e56e8ed67ba47464c0017d9c50eea (diff) |
Cycles: changes to source code folders structure
* Split render/ into scene/ and session/. The scene/ folder now contains the
scene and its nodes. The session/ folder contains the render session and
associated data structures like drivers and render buffers.
* Move top level kernel headers into new folders kernel/camera/, kernel/film/,
kernel/light/, kernel/sample/, kernel/util/
* Move integrator related kernel headers into kernel/integrator/
* Move OSL shaders from kernel/shaders/ to kernel/osl/shaders/
For patches and branches, git merge and rebase should be able to detect the
renames and move over code to the right file.
Diffstat (limited to 'intern/cycles/scene/scene.cpp')
-rw-r--r-- | intern/cycles/scene/scene.cpp | 961 |
1 files changed, 961 insertions, 0 deletions
diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp new file mode 100644 index 00000000000..bc5737ec126 --- /dev/null +++ b/intern/cycles/scene/scene.cpp @@ -0,0 +1,961 @@ +/* + * Copyright 2011-2013 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. + */ + +#include <stdlib.h> + +#include "bvh/bvh.h" +#include "device/device.h" +#include "scene/alembic.h" +#include "scene/background.h" +#include "scene/bake.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/osl.h" +#include "scene/particles.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/svm.h" +#include "scene/tables.h" +#include "scene/volume.h" +#include "session/session.h" + +#include "util/util_foreach.h" +#include "util/util_guarded_allocator.h" +#include "util/util_logging.h" +#include "util/util_progress.h" + +CCL_NAMESPACE_BEGIN + +DeviceScene::DeviceScene(Device *device) + : bvh_nodes(device, "__bvh_nodes", MEM_GLOBAL), + bvh_leaf_nodes(device, "__bvh_leaf_nodes", MEM_GLOBAL), + object_node(device, "__object_node", MEM_GLOBAL), + prim_type(device, "__prim_type", MEM_GLOBAL), + prim_visibility(device, "__prim_visibility", MEM_GLOBAL), + prim_index(device, "__prim_index", MEM_GLOBAL), + prim_object(device, "__prim_object", MEM_GLOBAL), + prim_time(device, "__prim_time", MEM_GLOBAL), + tri_verts(device, "__tri_verts", MEM_GLOBAL), + tri_shader(device, "__tri_shader", MEM_GLOBAL), + tri_vnormal(device, "__tri_vnormal", MEM_GLOBAL), + tri_vindex(device, "__tri_vindex", MEM_GLOBAL), + tri_patch(device, "__tri_patch", MEM_GLOBAL), + tri_patch_uv(device, "__tri_patch_uv", MEM_GLOBAL), + curves(device, "__curves", MEM_GLOBAL), + curve_keys(device, "__curve_keys", MEM_GLOBAL), + curve_segments(device, "__curve_segments", MEM_GLOBAL), + patches(device, "__patches", MEM_GLOBAL), + objects(device, "__objects", MEM_GLOBAL), + object_motion_pass(device, "__object_motion_pass", MEM_GLOBAL), + object_motion(device, "__object_motion", MEM_GLOBAL), + object_flag(device, "__object_flag", MEM_GLOBAL), + object_volume_step(device, "__object_volume_step", MEM_GLOBAL), + camera_motion(device, "__camera_motion", MEM_GLOBAL), + attributes_map(device, "__attributes_map", MEM_GLOBAL), + attributes_float(device, "__attributes_float", MEM_GLOBAL), + attributes_float2(device, "__attributes_float2", MEM_GLOBAL), + attributes_float3(device, "__attributes_float3", MEM_GLOBAL), + attributes_uchar4(device, "__attributes_uchar4", MEM_GLOBAL), + light_distribution(device, "__light_distribution", MEM_GLOBAL), + lights(device, "__lights", MEM_GLOBAL), + light_background_marginal_cdf(device, "__light_background_marginal_cdf", MEM_GLOBAL), + light_background_conditional_cdf(device, "__light_background_conditional_cdf", MEM_GLOBAL), + particles(device, "__particles", MEM_GLOBAL), + svm_nodes(device, "__svm_nodes", MEM_GLOBAL), + shaders(device, "__shaders", MEM_GLOBAL), + lookup_table(device, "__lookup_table", MEM_GLOBAL), + sample_pattern_lut(device, "__sample_pattern_lut", MEM_GLOBAL), + ies_lights(device, "__ies", MEM_GLOBAL) +{ + memset((void *)&data, 0, sizeof(data)); +} + +Scene::Scene(const SceneParams ¶ms_, Device *device) + : name("Scene"), + bvh(NULL), + default_surface(NULL), + default_volume(NULL), + default_light(NULL), + default_background(NULL), + default_empty(NULL), + device(device), + dscene(device), + params(params_), + update_stats(NULL), + kernels_loaded(false), + /* TODO(sergey): Check if it's indeed optimal value for the split kernel. */ + max_closure_global(1) +{ + memset((void *)&dscene.data, 0, sizeof(dscene.data)); + + /* OSL only works on the CPU */ + if (device->info.has_osl) + shader_manager = ShaderManager::create(params.shadingsystem); + else + shader_manager = ShaderManager::create(SHADINGSYSTEM_SVM); + + light_manager = new LightManager(); + geometry_manager = new GeometryManager(); + object_manager = new ObjectManager(); + image_manager = new ImageManager(device->info); + particle_system_manager = new ParticleSystemManager(); + bake_manager = new BakeManager(); + procedural_manager = new ProceduralManager(); + + /* Create nodes after managers, since create_node() can tag the managers. */ + camera = create_node<Camera>(); + dicing_camera = create_node<Camera>(); + lookup_tables = new LookupTables(); + film = create_node<Film>(); + background = create_node<Background>(); + integrator = create_node<Integrator>(); + + film->add_default(this); + shader_manager->add_default(this); +} + +Scene::~Scene() +{ + free_memory(true); +} + +void Scene::free_memory(bool final) +{ + delete bvh; + bvh = NULL; + + /* The order of deletion is important to make sure data is freed based on possible dependencies + * as the Nodes' reference counts are decremented in the destructors: + * + * - Procedurals can create and hold pointers to any other types. + * - Objects can hold pointers to Geometries and ParticleSystems + * - Lights and Geometries can hold pointers to Shaders. + * + * Similarly, we first delete all nodes and their associated device data, and then the managers + * and their associated device data. + */ + foreach (Procedural *p, procedurals) + delete p; + foreach (Object *o, objects) + delete o; + foreach (Geometry *g, geometry) + delete g; + foreach (ParticleSystem *p, particle_systems) + delete p; + foreach (Light *l, lights) + delete l; + foreach (Pass *p, passes) + delete p; + + geometry.clear(); + objects.clear(); + lights.clear(); + particle_systems.clear(); + procedurals.clear(); + passes.clear(); + + if (device) { + camera->device_free(device, &dscene, this); + film->device_free(device, &dscene, this); + background->device_free(device, &dscene); + integrator->device_free(device, &dscene, true); + } + + if (final) { + delete camera; + delete dicing_camera; + delete film; + delete background; + delete integrator; + } + + /* Delete Shaders after every other nodes to ensure that we do not try to decrement the reference + * count on some dangling pointer. */ + foreach (Shader *s, shaders) + delete s; + + shaders.clear(); + + /* Now that all nodes have been deleted, we can safely delete managers and device data. */ + if (device) { + object_manager->device_free(device, &dscene, true); + geometry_manager->device_free(device, &dscene, true); + shader_manager->device_free(device, &dscene, this); + light_manager->device_free(device, &dscene); + + particle_system_manager->device_free(device, &dscene); + + bake_manager->device_free(device, &dscene); + + if (final) + image_manager->device_free(device); + else + image_manager->device_free_builtin(device); + + lookup_tables->device_free(device, &dscene); + } + + if (final) { + delete lookup_tables; + delete object_manager; + delete geometry_manager; + delete shader_manager; + delete light_manager; + delete particle_system_manager; + delete image_manager; + delete bake_manager; + delete update_stats; + delete procedural_manager; + } +} + +void Scene::device_update(Device *device_, Progress &progress) +{ + if (!device) + device = device_; + + bool print_stats = need_data_update(); + + if (update_stats) { + update_stats->clear(); + } + + scoped_callback_timer timer([this, print_stats](double time) { + if (update_stats) { + update_stats->scene.times.add_entry({"device_update", time}); + + if (print_stats) { + printf("Update statistics:\n%s\n", update_stats->full_report().c_str()); + } + } + }); + + /* The order of updates is important, because there's dependencies between + * the different managers, using data computed by previous managers. + * + * - Image manager uploads images used by shaders. + * - Camera may be used for adaptive subdivision. + * - Displacement shader must have all shader data available. + * - Light manager needs lookup tables and final mesh data to compute emission CDF. + * - Lookup tables are done a second time to handle film tables + */ + + progress.set_status("Updating Shaders"); + shader_manager->device_update(device, &dscene, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + procedural_manager->update(this, progress); + + if (progress.get_cancel()) + return; + + progress.set_status("Updating Background"); + background->device_update(device, &dscene, this); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Camera"); + camera->device_update(device, &dscene, this); + + if (progress.get_cancel() || device->have_error()) + return; + + geometry_manager->device_update_preprocess(device, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Objects"); + object_manager->device_update(device, &dscene, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Particle Systems"); + particle_system_manager->device_update(device, &dscene, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Meshes"); + geometry_manager->device_update(device, &dscene, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Objects Flags"); + object_manager->device_update_flags(device, &dscene, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Images"); + image_manager->device_update(device, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Camera Volume"); + camera->device_update_volume(device, &dscene, this); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Lookup Tables"); + lookup_tables->device_update(device, &dscene, this); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Lights"); + light_manager->device_update(device, &dscene, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Integrator"); + integrator->device_update(device, &dscene, this); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Film"); + film->device_update(device, &dscene, this); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Lookup Tables"); + lookup_tables->device_update(device, &dscene, this); + + if (progress.get_cancel() || device->have_error()) + return; + + progress.set_status("Updating Baking"); + bake_manager->device_update(device, &dscene, this, progress); + + if (progress.get_cancel() || device->have_error()) + return; + + if (device->have_error() == false) { + dscene.data.volume_stack_size = get_volume_stack_size(); + + progress.set_status("Updating Device", "Writing constant memory"); + device->const_copy_to("__data", &dscene.data, sizeof(dscene.data)); + } + + if (print_stats) { + size_t mem_used = util_guarded_get_mem_used(); + size_t mem_peak = util_guarded_get_mem_peak(); + + VLOG(1) << "System memory statistics after full device sync:\n" + << " Usage: " << string_human_readable_number(mem_used) << " (" + << string_human_readable_size(mem_used) << ")\n" + << " Peak: " << string_human_readable_number(mem_peak) << " (" + << string_human_readable_size(mem_peak) << ")"; + } +} + +Scene::MotionType Scene::need_motion() +{ + if (integrator->get_motion_blur()) + return MOTION_BLUR; + else if (Pass::contains(passes, PASS_MOTION)) + return MOTION_PASS; + else + return MOTION_NONE; +} + +float Scene::motion_shutter_time() +{ + if (need_motion() == Scene::MOTION_PASS) + return 2.0f; + else + return camera->get_shuttertime(); +} + +bool Scene::need_global_attribute(AttributeStandard std) +{ + if (std == ATTR_STD_UV) + return Pass::contains(passes, PASS_UV); + else if (std == ATTR_STD_MOTION_VERTEX_POSITION) + return need_motion() != MOTION_NONE; + else if (std == ATTR_STD_MOTION_VERTEX_NORMAL) + return need_motion() == MOTION_BLUR; + + return false; +} + +void Scene::need_global_attributes(AttributeRequestSet &attributes) +{ + for (int std = ATTR_STD_NONE; std < ATTR_STD_NUM; std++) + if (need_global_attribute((AttributeStandard)std)) + attributes.add((AttributeStandard)std); +} + +bool Scene::need_update() +{ + return (need_reset() || film->is_modified()); +} + +bool Scene::need_data_update() +{ + return (background->is_modified() || image_manager->need_update() || + object_manager->need_update() || geometry_manager->need_update() || + light_manager->need_update() || lookup_tables->need_update() || + integrator->is_modified() || shader_manager->need_update() || + particle_system_manager->need_update() || bake_manager->need_update() || + film->is_modified() || procedural_manager->need_update()); +} + +bool Scene::need_reset() +{ + return need_data_update() || camera->is_modified(); +} + +void Scene::reset() +{ + shader_manager->reset(this); + shader_manager->add_default(this); + + /* ensure all objects are updated */ + camera->tag_modified(); + dicing_camera->tag_modified(); + film->tag_modified(); + background->tag_modified(); + + background->tag_update(this); + integrator->tag_update(this, Integrator::UPDATE_ALL); + object_manager->tag_update(this, ObjectManager::UPDATE_ALL); + geometry_manager->tag_update(this, GeometryManager::UPDATE_ALL); + light_manager->tag_update(this, LightManager::UPDATE_ALL); + particle_system_manager->tag_update(this); + procedural_manager->tag_update(); +} + +void Scene::device_free() +{ + free_memory(false); +} + +void Scene::collect_statistics(RenderStats *stats) +{ + geometry_manager->collect_statistics(this, stats); + image_manager->collect_statistics(stats); +} + +void Scene::enable_update_stats() +{ + if (!update_stats) { + update_stats = new SceneUpdateStats(); + } +} + +void Scene::update_kernel_features() +{ + if (!need_update()) { + return; + } + + /* These features are not being tweaked as often as shaders, + * so could be done selective magic for the viewport as well. */ + uint kernel_features = shader_manager->get_kernel_features(this); + + bool use_motion = need_motion() == Scene::MotionType::MOTION_BLUR; + kernel_features |= KERNEL_FEATURE_PATH_TRACING; + if (params.hair_shape == CURVE_THICK) { + kernel_features |= KERNEL_FEATURE_HAIR_THICK; + } + if (use_motion && camera->use_motion()) { + kernel_features |= KERNEL_FEATURE_CAMERA_MOTION; + } + foreach (Object *object, objects) { + Geometry *geom = object->get_geometry(); + if (use_motion) { + if (object->use_motion() || geom->get_use_motion_blur()) { + kernel_features |= KERNEL_FEATURE_OBJECT_MOTION; + } + if (geom->get_use_motion_blur()) { + kernel_features |= KERNEL_FEATURE_CAMERA_MOTION; + } + } + if (object->get_is_shadow_catcher()) { + kernel_features |= KERNEL_FEATURE_SHADOW_CATCHER; + } + if (geom->is_mesh()) { + Mesh *mesh = static_cast<Mesh *>(geom); +#ifdef WITH_OPENSUBDIV + if (mesh->get_subdivision_type() != Mesh::SUBDIVISION_NONE) { + kernel_features |= KERNEL_FEATURE_PATCH_EVALUATION; + } +#endif + } + else if (geom->is_hair()) { + kernel_features |= KERNEL_FEATURE_HAIR; + } + } + + if (bake_manager->get_baking()) { + kernel_features |= KERNEL_FEATURE_BAKING; + } + + kernel_features |= film->get_kernel_features(this); + kernel_features |= integrator->get_kernel_features(); + + dscene.data.kernel_features = kernel_features; + + /* Currently viewport render is faster with higher max_closures, needs investigating. */ + const uint max_closures = (params.background) ? get_max_closure_count() : MAX_CLOSURE; + dscene.data.max_closures = max_closures; + dscene.data.max_shaders = shaders.size(); +} + +bool Scene::update(Progress &progress) +{ + if (!need_update()) { + return false; + } + + /* Load render kernels, before device update where we upload data to the GPU. */ + load_kernels(progress, false); + + /* Upload scene data to the GPU. */ + progress.set_status("Updating Scene"); + MEM_GUARDED_CALL(&progress, device_update, device, progress); + + return true; +} + +static void log_kernel_features(const uint features) +{ + VLOG(2) << "Requested features:\n"; + VLOG(2) << "Use BSDF " << string_from_bool(features & KERNEL_FEATURE_NODE_BSDF) << "\n"; + VLOG(2) << "Use Principled BSDF " << string_from_bool(features & KERNEL_FEATURE_PRINCIPLED) + << "\n"; + VLOG(2) << "Use Emission " << string_from_bool(features & KERNEL_FEATURE_NODE_EMISSION) << "\n"; + VLOG(2) << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_NODE_VOLUME) << "\n"; + VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_NODE_HAIR) << "\n"; + VLOG(2) << "Use Bump " << string_from_bool(features & KERNEL_FEATURE_NODE_BUMP) << "\n"; + VLOG(2) << "Use Voronoi " << string_from_bool(features & KERNEL_FEATURE_NODE_VORONOI_EXTRA) + << "\n"; + VLOG(2) << "Use Shader Raytrace " << string_from_bool(features & KERNEL_FEATURE_NODE_RAYTRACE) + << "\n"; + VLOG(2) << "Use Transparent " << string_from_bool(features & KERNEL_FEATURE_TRANSPARENT) << "\n"; + VLOG(2) << "Use Denoising " << string_from_bool(features & KERNEL_FEATURE_DENOISING) << "\n"; + VLOG(2) << "Use Path Tracing " << string_from_bool(features & KERNEL_FEATURE_PATH_TRACING) + << "\n"; + VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_HAIR) << "\n"; + VLOG(2) << "Use Object Motion " << string_from_bool(features & KERNEL_FEATURE_OBJECT_MOTION) + << "\n"; + VLOG(2) << "Use Camera Motion " << string_from_bool(features & KERNEL_FEATURE_CAMERA_MOTION) + << "\n"; + VLOG(2) << "Use Baking " << string_from_bool(features & KERNEL_FEATURE_BAKING) << "\n"; + VLOG(2) << "Use Subsurface " << string_from_bool(features & KERNEL_FEATURE_SUBSURFACE) << "\n"; + VLOG(2) << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_VOLUME) << "\n"; + VLOG(2) << "Use Patch Evaluation " + << string_from_bool(features & KERNEL_FEATURE_PATCH_EVALUATION) << "\n"; + VLOG(2) << "Use Shadow Catcher " << string_from_bool(features & KERNEL_FEATURE_SHADOW_CATCHER) + << "\n"; +} + +bool Scene::load_kernels(Progress &progress, bool lock_scene) +{ + thread_scoped_lock scene_lock; + if (lock_scene) { + scene_lock = thread_scoped_lock(mutex); + } + + update_kernel_features(); + + const uint kernel_features = dscene.data.kernel_features; + + if (!kernels_loaded || loaded_kernel_features != kernel_features) { + progress.set_status("Loading render kernels (may take a few minutes the first time)"); + + scoped_timer timer; + + log_kernel_features(kernel_features); + if (!device->load_kernels(kernel_features)) { + string message = device->error_message(); + if (message.empty()) + message = "Failed loading render kernel, see console for errors"; + + progress.set_error(message); + progress.set_status(message); + progress.set_update(); + return false; + } + + kernels_loaded = true; + loaded_kernel_features = kernel_features; + return true; + } + return false; +} + +int Scene::get_max_closure_count() +{ + if (shader_manager->use_osl()) { + /* OSL always needs the maximum as we can't predict the + * number of closures a shader might generate. */ + return MAX_CLOSURE; + } + + int max_closures = 0; + for (int i = 0; i < shaders.size(); i++) { + Shader *shader = shaders[i]; + if (shader->reference_count()) { + int num_closures = shader->graph->get_num_closures(); + max_closures = max(max_closures, num_closures); + } + } + max_closure_global = max(max_closure_global, max_closures); + + if (max_closure_global > MAX_CLOSURE) { + /* This is usually harmless as more complex shader tend to get many + * closures discarded due to mixing or low weights. We need to limit + * to MAX_CLOSURE as this is hardcoded in CPU/mega kernels, and it + * avoids excessive memory usage for split kernels. */ + VLOG(2) << "Maximum number of closures exceeded: " << max_closure_global << " > " + << MAX_CLOSURE; + + max_closure_global = MAX_CLOSURE; + } + + return max_closure_global; +} + +int Scene::get_volume_stack_size() const +{ + int volume_stack_size = 0; + + /* Space for background volume and terminator. + * Don't do optional here because camera ray initialization expects that there is space for + * at least those elements (avoiding extra condition to check if there is actual volume or not). + */ + volume_stack_size += 2; + + /* Quick non-expensive check. Can over-estimate maximum possible nested level, but does not + * require expensive calculation during pre-processing. */ + bool has_volume_object = false; + for (const Object *object : objects) { + if (!object->get_geometry()->has_volume) { + continue; + } + + if (object->intersects_volume) { + /* Object intersects another volume, assume it's possible to go deeper in the stack. */ + /* TODO(sergey): This might count nesting twice (A intersects B and B intersects A), but + * can't think of a computationally cheap algorithm. Dividing my 2 doesn't work because of + * Venn diagram example with 3 circles. */ + ++volume_stack_size; + } + else if (!has_volume_object) { + /* Allocate space for at least one volume object. */ + ++volume_stack_size; + } + + has_volume_object = true; + + if (volume_stack_size == MAX_VOLUME_STACK_SIZE) { + break; + } + } + + volume_stack_size = min(volume_stack_size, MAX_VOLUME_STACK_SIZE); + + VLOG(3) << "Detected required volume stack size " << volume_stack_size; + + return volume_stack_size; +} + +bool Scene::has_shadow_catcher() +{ + if (shadow_catcher_modified_) { + has_shadow_catcher_ = false; + for (Object *object : objects) { + if (object->get_is_shadow_catcher()) { + has_shadow_catcher_ = true; + break; + } + } + + shadow_catcher_modified_ = false; + } + + return has_shadow_catcher_; +} + +void Scene::tag_shadow_catcher_modified() +{ + shadow_catcher_modified_ = true; +} + +template<> Light *Scene::create_node<Light>() +{ + Light *node = new Light(); + node->set_owner(this); + lights.push_back(node); + light_manager->tag_update(this, LightManager::LIGHT_ADDED); + return node; +} + +template<> Mesh *Scene::create_node<Mesh>() +{ + Mesh *node = new Mesh(); + node->set_owner(this); + geometry.push_back(node); + geometry_manager->tag_update(this, GeometryManager::MESH_ADDED); + return node; +} + +template<> Hair *Scene::create_node<Hair>() +{ + Hair *node = new Hair(); + node->set_owner(this); + geometry.push_back(node); + geometry_manager->tag_update(this, GeometryManager::HAIR_ADDED); + return node; +} + +template<> Volume *Scene::create_node<Volume>() +{ + Volume *node = new Volume(); + node->set_owner(this); + geometry.push_back(node); + geometry_manager->tag_update(this, GeometryManager::MESH_ADDED); + return node; +} + +template<> Object *Scene::create_node<Object>() +{ + Object *node = new Object(); + node->set_owner(this); + objects.push_back(node); + object_manager->tag_update(this, ObjectManager::OBJECT_ADDED); + return node; +} + +template<> ParticleSystem *Scene::create_node<ParticleSystem>() +{ + ParticleSystem *node = new ParticleSystem(); + node->set_owner(this); + particle_systems.push_back(node); + particle_system_manager->tag_update(this); + return node; +} + +template<> Shader *Scene::create_node<Shader>() +{ + Shader *node = new Shader(); + node->set_owner(this); + shaders.push_back(node); + shader_manager->tag_update(this, ShaderManager::SHADER_ADDED); + return node; +} + +template<> AlembicProcedural *Scene::create_node<AlembicProcedural>() +{ +#ifdef WITH_ALEMBIC + AlembicProcedural *node = new AlembicProcedural(); + node->set_owner(this); + procedurals.push_back(node); + procedural_manager->tag_update(); + return node; +#else + return nullptr; +#endif +} + +template<> Pass *Scene::create_node<Pass>() +{ + Pass *node = new Pass(); + node->set_owner(this); + passes.push_back(node); + film->tag_modified(); + return node; +} + +template<typename T> void delete_node_from_array(vector<T> &nodes, T node) +{ + for (size_t i = 0; i < nodes.size(); ++i) { + if (nodes[i] == node) { + std::swap(nodes[i], nodes[nodes.size() - 1]); + break; + } + } + + nodes.resize(nodes.size() - 1); + + delete node; +} + +template<> void Scene::delete_node_impl(Light *node) +{ + delete_node_from_array(lights, node); + light_manager->tag_update(this, LightManager::LIGHT_REMOVED); +} + +template<> void Scene::delete_node_impl(Mesh *node) +{ + delete_node_from_array(geometry, static_cast<Geometry *>(node)); + geometry_manager->tag_update(this, GeometryManager::MESH_REMOVED); +} + +template<> void Scene::delete_node_impl(Hair *node) +{ + delete_node_from_array(geometry, static_cast<Geometry *>(node)); + geometry_manager->tag_update(this, GeometryManager::HAIR_REMOVED); +} + +template<> void Scene::delete_node_impl(Volume *node) +{ + delete_node_from_array(geometry, static_cast<Geometry *>(node)); + geometry_manager->tag_update(this, GeometryManager::MESH_REMOVED); +} + +template<> void Scene::delete_node_impl(Geometry *node) +{ + uint flag; + if (node->is_hair()) { + flag = GeometryManager::HAIR_REMOVED; + } + else { + flag = GeometryManager::MESH_REMOVED; + } + + delete_node_from_array(geometry, node); + geometry_manager->tag_update(this, flag); +} + +template<> void Scene::delete_node_impl(Object *node) +{ + delete_node_from_array(objects, node); + object_manager->tag_update(this, ObjectManager::OBJECT_REMOVED); +} + +template<> void Scene::delete_node_impl(ParticleSystem *node) +{ + delete_node_from_array(particle_systems, node); + particle_system_manager->tag_update(this); +} + +template<> void Scene::delete_node_impl(Shader *shader) +{ + /* don't delete unused shaders, not supported */ + shader->clear_reference_count(); +} + +template<> void Scene::delete_node_impl(Procedural *node) +{ + delete_node_from_array(procedurals, node); + procedural_manager->tag_update(); +} + +template<> void Scene::delete_node_impl(AlembicProcedural *node) +{ +#ifdef WITH_ALEMBIC + delete_node_impl(static_cast<Procedural *>(node)); +#else + (void)node; +#endif +} + +template<> void Scene::delete_node_impl(Pass *node) +{ + delete_node_from_array(passes, node); + film->tag_modified(); +} + +template<typename T> +static void remove_nodes_in_set(const set<T *> &nodes_set, + vector<T *> &nodes_array, + const NodeOwner *owner) +{ + size_t new_size = nodes_array.size(); + + for (size_t i = 0; i < new_size; ++i) { + T *node = nodes_array[i]; + + if (nodes_set.find(node) != nodes_set.end()) { + std::swap(nodes_array[i], nodes_array[new_size - 1]); + + assert(node->get_owner() == owner); + delete node; + + i -= 1; + new_size -= 1; + } + } + + nodes_array.resize(new_size); + (void)owner; +} + +template<> void Scene::delete_nodes(const set<Light *> &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, lights, owner); + light_manager->tag_update(this, LightManager::LIGHT_REMOVED); +} + +template<> void Scene::delete_nodes(const set<Geometry *> &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, geometry, owner); + geometry_manager->tag_update(this, GeometryManager::GEOMETRY_REMOVED); +} + +template<> void Scene::delete_nodes(const set<Object *> &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, objects, owner); + object_manager->tag_update(this, ObjectManager::OBJECT_REMOVED); +} + +template<> void Scene::delete_nodes(const set<ParticleSystem *> &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, particle_systems, owner); + particle_system_manager->tag_update(this); +} + +template<> void Scene::delete_nodes(const set<Shader *> &nodes, const NodeOwner * /*owner*/) +{ + /* don't delete unused shaders, not supported */ + for (Shader *shader : nodes) { + shader->clear_reference_count(); + } +} + +template<> void Scene::delete_nodes(const set<Procedural *> &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, procedurals, owner); + procedural_manager->tag_update(); +} + +template<> void Scene::delete_nodes(const set<Pass *> &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, passes, owner); + film->tag_modified(); +} + +CCL_NAMESPACE_END |