From e813e46327dca72c367f5259173fb35c4fe14723 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 24 Feb 2025 23:44:14 +0100 Subject: [PATCH] Cycles: Refactor lights to be objects This is an intermediate steps towards making lights actual geometry. Light is now a subclass of Geometry, which simplifies some code. The geometry is not added to the BVH yet, which would be the next step and improve light intersection performance with many lights. This makes object attributes work on lights. Co-authored-by: Lukas Stockner Pull Request: https://projects.blender.org/blender/blender/pulls/134846 --- intern/cycles/blender/geometry.cpp | 35 ++- intern/cycles/blender/light.cpp | 95 +++----- intern/cycles/blender/object.cpp | 45 +--- intern/cycles/blender/shader.cpp | 4 +- intern/cycles/blender/sync.cpp | 5 +- intern/cycles/blender/sync.h | 14 +- intern/cycles/hydra/light.cpp | 24 +- intern/cycles/hydra/light.h | 1 + intern/cycles/hydra/session.cpp | 13 +- intern/cycles/kernel/bvh/util.h | 4 - intern/cycles/kernel/geom/curve.h | 5 +- intern/cycles/kernel/geom/object.h | 49 +--- intern/cycles/kernel/geom/primitive.h | 11 + intern/cycles/kernel/geom/shader_data.h | 16 +- intern/cycles/kernel/geom/subd_triangle.h | 10 - intern/cycles/kernel/geom/triangle.h | 5 +- .../kernel/integrator/intersect_closest.h | 1 - .../integrator/intersect_dedicated_light.h | 1 - .../integrator/intersect_volume_stack.h | 2 - intern/cycles/kernel/integrator/mnee.h | 7 +- .../kernel/integrator/shade_background.h | 8 +- .../kernel/integrator/shade_dedicated_light.h | 12 +- .../cycles/kernel/integrator/shade_shadow.h | 1 - .../cycles/kernel/integrator/shade_surface.h | 6 +- .../cycles/kernel/integrator/shade_volume.h | 6 +- .../kernel/integrator/shadow_state_template.h | 1 - intern/cycles/kernel/integrator/state_util.h | 8 - .../kernel/integrator/subsurface_disk.h | 1 - .../integrator/subsurface_random_walk.h | 1 - .../cycles/kernel/integrator/volume_shader.h | 1 - intern/cycles/kernel/light/common.h | 3 +- intern/cycles/kernel/light/distant.h | 19 +- intern/cycles/kernel/light/light.h | 67 +++--- intern/cycles/kernel/light/point.h | 17 +- intern/cycles/kernel/light/sample.h | 16 +- intern/cycles/kernel/light/spot.h | 28 ++- intern/cycles/kernel/light/tree.h | 6 +- intern/cycles/kernel/light/triangle.h | 1 - intern/cycles/kernel/osl/closures.cpp | 2 +- intern/cycles/kernel/osl/services.cpp | 30 --- intern/cycles/kernel/osl/services.h | 1 - intern/cycles/kernel/osl/services_gpu.h | 17 -- .../kernel/osl/shaders/node_object_info.osl | 10 +- intern/cycles/kernel/svm/ao.h | 1 - intern/cycles/kernel/svm/bevel.h | 1 - intern/cycles/kernel/svm/geometry.h | 7 +- intern/cycles/kernel/svm/vector_transform.h | 2 +- intern/cycles/kernel/types.h | 23 +- intern/cycles/scene/geometry.h | 6 + intern/cycles/scene/light.cpp | 218 ++++++++---------- intern/cycles/scene/light.h | 29 +-- intern/cycles/scene/light_tree.cpp | 80 +++---- intern/cycles/scene/light_tree_debug.cpp | 16 +- intern/cycles/scene/object.cpp | 4 + intern/cycles/scene/object.h | 8 +- intern/cycles/scene/scene.cpp | 32 +-- intern/cycles/scene/scene.h | 2 - 57 files changed, 425 insertions(+), 613 deletions(-) diff --git a/intern/cycles/blender/geometry.cpp b/intern/cycles/blender/geometry.cpp index 8426eb7d0a8..bff90432e9d 100644 --- a/intern/cycles/blender/geometry.cpp +++ b/intern/cycles/blender/geometry.cpp @@ -4,6 +4,7 @@ #include "scene/curves.h" #include "scene/hair.h" +#include "scene/light.h" #include "scene/mesh.h" #include "scene/object.h" #include "scene/pointcloud.h" @@ -18,6 +19,10 @@ CCL_NAMESPACE_BEGIN static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_particle_hair) { + if (b_ob_info.object_data.is_a(&RNA_Light)) { + return Geometry::LIGHT; + } + if (b_ob_info.object_data.is_a(&RNA_Curves) || use_particle_hair) { return Geometry::HAIR; } @@ -38,12 +43,17 @@ static Geometry::Type determine_geom_type(BObjectInfo &b_ob_info, bool use_parti array BlenderSync::find_used_shaders(BL::Object &b_ob) { + array used_shaders; + + if (b_ob.type() == BL::Object::type_LIGHT) { + find_shader(b_ob.data(), used_shaders, scene->default_light); + return used_shaders; + } + BL::Material material_override = view_layer.material_override; Shader *default_shader = (b_ob.type() == BL::Object::type_VOLUME) ? scene->default_volume : scene->default_surface; - array used_shaders; - for (BL::MaterialSlot &b_slot : b_ob.material_slots) { if (material_override) { find_shader(material_override, used_shaders, default_shader); @@ -95,7 +105,10 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, bool sync = true; if (geom == nullptr) { /* Add new geometry if it did not exist yet. */ - if (geom_type == Geometry::HAIR) { + if (geom_type == Geometry::LIGHT) { + geom = scene->create_node(); + } + else if (geom_type == Geometry::HAIR) { geom = scene->create_node(); } else if (geom_type == Geometry::VOLUME) { @@ -115,6 +128,11 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, } if (!sync) { + /* Need to determine this every sync. */ + if (geom->is_light() && static_cast(geom)->get_is_portal()) { + world_use_portal = true; + } + /* If transform was applied to geometry, need full update. */ if (object_updated && geom->transform_applied) { ; @@ -156,7 +174,11 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, progress.set_sync_status("Synchronizing object", b_ob_info.real_object.name()); - if (geom_type == Geometry::HAIR) { + if (geom_type == Geometry::LIGHT) { + Light *light = static_cast(geom); + sync_light(b_depsgraph, b_ob_info, light); + } + else if (geom_type == Geometry::HAIR) { Hair *hair = static_cast(geom); sync_hair(b_depsgraph, b_ob_info, hair); } @@ -209,6 +231,11 @@ void BlenderSync::sync_geometry_motion(BL::Depsgraph &b_depsgraph, return; } + /* Nothing to do for lights. */ + if (geom->is_light()) { + return; + } + /* If the geometry already has motion blur from a velocity attribute, don't * set the geometry motion steps again. * diff --git a/intern/cycles/blender/light.cpp b/intern/cycles/blender/light.cpp index 2ab51147af1..7fe863a35d9 100644 --- a/intern/cycles/blender/light.cpp +++ b/intern/cycles/blender/light.cpp @@ -4,42 +4,16 @@ #include "scene/light.h" -#include "blender/light_linking.h" #include "blender/sync.h" #include "blender/util.h" - -#include "util/hash.h" +#include "scene/object.h" CCL_NAMESPACE_BEGIN -void BlenderSync::sync_light(BL::Object &b_parent, - int persistent_id[OBJECT_PERSISTENT_ID_SIZE], - BObjectInfo &b_ob_info, - const int random_id, - Transform &tfm, - bool *use_portal) +void BlenderSync::sync_light(BL::Depsgraph /*b_depsgraph*/, BObjectInfo &b_ob_info, Light *light) { - /* test if we need to sync */ - const ObjectKey key(b_parent, persistent_id, b_ob_info.real_object, false); BL::Light b_light(b_ob_info.object_data); - Light *light = light_map.find(key); - - /* Check if the transform was modified, in case a linked collection is moved we do not get a - * specific depsgraph update (#88515). This also mimics the behavior for Objects. */ - const bool tfm_updated = (light && light->get_tfm() != tfm); - - /* Update if either object or light data changed. */ - if (!light_map.add_or_update(&light, b_ob_info.real_object, b_parent, key) && !tfm_updated) { - Shader *shader; - if (!shader_map.add_or_update(&shader, b_light)) { - if (light->get_is_portal()) { - *use_portal = true; - } - return; - } - } - light->name = b_light.name().c_str(); /* type */ @@ -104,14 +78,6 @@ void BlenderSync::sync_light(BL::Object &b_parent, const float3 strength = get_float3(b_light.color()) * BL::PointLight(b_light).energy(); light->set_strength(strength); - /* location and (inverted!) direction */ - light->set_tfm(tfm); - - /* shader */ - array used_shaders; - find_shader(b_light, used_shaders, scene->default_light); - light->set_shader(static_cast(used_shaders[0])); - /* shadow */ PointerRNA clight = RNA_pointer_get(&b_light.ptr, "cycles"); light->set_cast_shadow(b_light.use_shadow()); @@ -122,13 +88,6 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_max_bounces(get_int(clight, "max_bounces")); - if (b_ob_info.real_object != b_ob_info.iter_object) { - light->set_random_id(random_id); - } - else { - light->set_random_id(hash_uint2(hash_string(b_ob_info.real_object.name().c_str()), 0)); - } - if (light->get_light_type() == LIGHT_AREA) { light->set_is_portal(get_boolean(clight, "is_portal")); } @@ -137,7 +96,7 @@ void BlenderSync::sync_light(BL::Object &b_parent, } if (light->get_is_portal()) { - *use_portal = true; + world_use_portal = true; } /* visibility */ @@ -149,22 +108,11 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_use_scatter((visibility & PATH_RAY_VOLUME_SCATTER) != 0); light->set_is_shadow_catcher(b_ob_info.real_object.is_shadow_catcher()); - /* Light group and linking. */ - string lightgroup = b_ob_info.real_object.lightgroup(); - if (lightgroup.empty()) { - lightgroup = b_parent.lightgroup(); - } - light->set_lightgroup(ustring(lightgroup)); - light->set_light_set_membership( - BlenderLightLink::get_light_set_membership(PointerRNA_NULL, b_ob_info.real_object)); - light->set_shadow_set_membership( - BlenderLightLink::get_shadow_set_membership(PointerRNA_NULL, b_ob_info.real_object)); - /* tag */ light->tag_update(scene); } -void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) +void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d) { BL::World b_world = view_layer.world_override ? view_layer.world_override : b_scene.world(); @@ -176,14 +124,29 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) cworld, "sampling_method", SAMPLING_NUM, SAMPLING_AUTOMATIC); const bool sample_as_light = (sampling_method != SAMPLING_NONE); - if (sample_as_light || use_portal) { - /* test if we need to sync */ - Light *light; - const ObjectKey key(b_world, nullptr, b_world, false); + if (sample_as_light || world_use_portal) { + /* Create object. */ + Object *object; + const ObjectKey object_key(b_world, nullptr, b_world, false); + bool update = object_map.add_or_update(&object, b_world, b_world, object_key); + + /* Create geometry. */ + const GeometryKey geom_key{b_world.ptr.data, Geometry::LIGHT}; + Geometry *geom = geometry_map.find(geom_key); + if (geom) { + update |= geometry_map.update(geom, b_world); + } + else { + geom = scene->create_node(); + geometry_map.add(geom_key, geom); + object->set_geometry(geom); + update = true; + } + + if (update || world_recalc || b_world.ptr.data != world_map) { + /* Initialize light geometry. */ + Light *light = static_cast(geom); - if (light_map.add_or_update(&light, b_world, b_world, key) || world_recalc || - b_world.ptr.data != world_map) - { light->set_light_type(LIGHT_BACKGROUND); if (sampling_method == SAMPLING_MANUAL) { light->set_map_resolution(get_int(cworld, "sample_map_resolution")); @@ -191,7 +154,9 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) else { light->set_map_resolution(0); } - light->set_shader(scene->default_background); + array used_shaders; + used_shaders.push_back_slow(scene->default_background); + light->set_used_shaders(used_shaders); light->set_use_mis(sample_as_light); light->set_max_bounces(get_int(cworld, "max_bounces")); @@ -202,7 +167,7 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) light->set_use_caustics(get_boolean(cworld, "is_caustics_light")); light->tag_update(scene); - light_map.set_recalc(b_world); + geometry_map.set_recalc(b_world); } } } diff --git a/intern/cycles/blender/object.cpp b/intern/cycles/blender/object.cpp index efa9cd0aea2..ed9ea98a16e 100644 --- a/intern/cycles/blender/object.cpp +++ b/intern/cycles/blender/object.cpp @@ -62,9 +62,9 @@ bool BlenderSync::object_is_geometry(BObjectInfo &b_ob_info) const BL::Object::type_enum type = b_ob_info.iter_object.type(); if (type == BL::Object::type_VOLUME || type == BL::Object::type_CURVES || - type == BL::Object::type_POINTCLOUD) + type == BL::Object::type_POINTCLOUD || type == BL::Object::type_LIGHT) { - /* Will be exported attached to mesh. */ + /* Will be exported as geometry. */ return true; } @@ -152,7 +152,6 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, bool use_particle_hair, bool show_lights, BlenderObjectCulling &culling, - bool *use_portal, TaskPool *geom_task_pool) { const bool is_instance = b_instance.is_instance(); @@ -173,36 +172,18 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, } } - /* light is handled separately */ - if (!motion && object_is_light(b_ob)) { - if (!show_lights) { - return nullptr; - } - - /* TODO: don't use lights for excluded layers used as mask layer, - * when dynamic overrides are back. */ -#if 0 - if (!((layer_flag & view_layer.holdout_layer) && (layer_flag & view_layer.exclude_layer))) -#endif - { - sync_light(b_parent, - persistent_id, - b_ob_info, - is_instance ? b_instance.random_id() : 0, - tfm, - use_portal); - } - - return nullptr; - } - /* only interested in object that we can create geometry from */ if (!object_is_geometry(b_ob_info)) { return nullptr; } /* Perform object culling. */ - if (culling.test(scene, b_ob, tfm)) { + if (object_is_light(b_ob)) { + if (!show_lights) { + return nullptr; + } + } + else if (culling.test(scene, b_ob, tfm)) { return nullptr; } @@ -544,7 +525,6 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, if (!motion) { /* prepare for sync */ - light_map.pre_sync(); geometry_map.pre_sync(); object_map.pre_sync(); procedural_map.pre_sync(); @@ -555,6 +535,8 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, geometry_motion_synced.clear(); } + world_use_portal = false; + if (!motion) { /* Object to geometry instance mapping is built for the reference time, as other * times just look up the corresponding geometry. */ @@ -566,7 +548,6 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, /* object loop */ bool cancel = false; - bool use_portal = false; const bool show_lights = BlenderViewportParameters(b_v3d, use_developer_ui).use_scene_lights; BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); @@ -623,7 +604,6 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, false, show_lights, culling, - &use_portal, sync_hair ? nullptr : &geom_task_pool); } } @@ -637,7 +617,6 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, true, show_lights, culling, - &use_portal, &geom_task_pool); } @@ -649,12 +628,12 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, progress.set_sync_status(""); if (!cancel && !motion) { - sync_background_light(b_v3d, use_portal); + /* After object for world_use_portal. */ + sync_background_light(b_v3d); /* Handle removed data and modified pointers, as this may free memory, delete Nodes in the * right order to ensure that dependent data is freed after their users. Objects should be * freed before particle systems and geometries. */ - light_map.post_sync(); object_map.post_sync(); geometry_map.post_sync(); particle_system_map.post_sync(); diff --git a/intern/cycles/blender/shader.cpp b/intern/cycles/blender/shader.cpp index 04c363a1a76..2d85dc19bf1 100644 --- a/intern/cycles/blender/shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -30,7 +30,9 @@ using ProxyMap = map; /* Find */ -void BlenderSync::find_shader(BL::ID &id, array &used_shaders, Shader *default_shader) +void BlenderSync::find_shader(const BL::ID &id, + array &used_shaders, + Shader *default_shader) { Shader *synced_shader = (id) ? shader_map.find(id) : nullptr; Shader *shader = (synced_shader) ? synced_shader : default_shader; diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index c3d7620d47d..43b05113bab 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -53,7 +53,6 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine, object_map(scene), procedural_map(scene), geometry_map(scene), - light_map(scene), particle_system_map(scene), world_map(nullptr), world_recalc(false), @@ -206,11 +205,11 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d else if (is_light) { if (b_update.is_updated_transform() || b_update.is_updated_shading()) { object_map.set_recalc(b_ob); - light_map.set_recalc(b_ob); + geometry_map.set_recalc(b_ob); } if (updated_geometry) { - light_map.set_recalc(b_ob); + geometry_map.set_recalc(b_ob); } } } diff --git a/intern/cycles/blender/sync.h b/intern/cycles/blender/sync.h index 2a3a5b9d666..2ba340fe414 100644 --- a/intern/cycles/blender/sync.h +++ b/intern/cycles/blender/sync.h @@ -144,7 +144,6 @@ class BlenderSync { bool use_particle_hair, bool show_lights, BlenderObjectCulling &culling, - bool *use_portal, TaskPool *geom_task_pool); void sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob, Object *object); @@ -206,13 +205,8 @@ class BlenderSync { TaskPool *task_pool); /* Light */ - void sync_light(BL::Object &b_parent, - int persistent_id[OBJECT_PERSISTENT_ID_SIZE], - BObjectInfo &b_ob_info, - const int random_id, - Transform &tfm, - bool *use_portal); - void sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal); + void sync_light(BL::Depsgraph b_depsgraph, BObjectInfo &b_ob_info, Light *light); + void sync_background_light(BL::SpaceView3D &b_v3d); /* Particles */ bool sync_dupli_particle(BL::Object &b_ob, @@ -223,7 +217,7 @@ class BlenderSync { void sync_images(); /* util */ - void find_shader(BL::ID &id, array &used_shaders, Shader *default_shader); + void find_shader(const BL::ID &id, array &used_shaders, Shader *default_shader); bool BKE_object_is_modified(BL::Object &b_ob); bool object_is_geometry(BObjectInfo &b_ob_info); bool object_can_have_geometry(BL::Object &b_ob); @@ -242,7 +236,6 @@ class BlenderSync { id_map object_map; id_map procedural_map; id_map geometry_map; - id_map light_map; id_map particle_system_map; set geometry_synced; set geometry_motion_synced; @@ -252,6 +245,7 @@ class BlenderSync { set motion_times; void *world_map; bool world_recalc; + bool world_use_portal = false; BlenderViewportParameters viewport_parameters; Scene *scene; diff --git a/intern/cycles/hydra/light.cpp b/intern/cycles/hydra/light.cpp index cb36d4b8f59..7b5dd0def84 100644 --- a/intern/cycles/hydra/light.cpp +++ b/intern/cycles/hydra/light.cpp @@ -6,11 +6,13 @@ #include "hydra/light.h" #include "hydra/session.h" #include "scene/light.h" +#include "scene/object.h" #include "scene/scene.h" #include "scene/shader.h" #include "scene/shader_graph.h" #include "scene/shader_nodes.h" #include "util/hash.h" +#include "util/transform.h" #include #include @@ -64,7 +66,7 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate, sceneDelegate->GetLightParamValue(id, HdTokens->transform) .Get()); #endif - _light->set_tfm(tfm); + _object->set_tfm(tfm); } if (*dirtyBits & DirtyBits::DirtyParams) { @@ -175,8 +177,8 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate, PopulateShaderGraph(sceneDelegate); } // Need to update shader graph when transform changes in case transform was baked into it - else if (_light->tfm_is_modified() && (_lightType == HdPrimTypeTokens->domeLight || - _light->get_shader()->has_surface_spatial_varying)) + else if (_object->tfm_is_modified() && (_lightType == HdPrimTypeTokens->domeLight || + _light->get_shader()->has_surface_spatial_varying)) { PopulateShaderGraph(sceneDelegate); } @@ -266,7 +268,7 @@ void HdCyclesLight::PopulateShaderGraph(HdSceneDelegate *sceneDelegate) } TextureCoordinateNode *coordNode = graph->create_node(); - coordNode->set_ob_tfm(_light->get_tfm()); + coordNode->set_ob_tfm(_object->get_tfm()); coordNode->set_use_transform(true); IESLightNode *iesNode = graph->create_node(); @@ -287,7 +289,7 @@ void HdCyclesLight::PopulateShaderGraph(HdSceneDelegate *sceneDelegate) ImageSlotTextureNode *textureNode = nullptr; if (_lightType == HdPrimTypeTokens->domeLight) { - Transform tfm = _light->get_tfm(); + Transform tfm = _object->get_tfm(); transform_set_column(&tfm, 3, zero_float3()); // Remove translation TextureCoordinateNode *coordNode = graph->create_node(); @@ -352,9 +354,11 @@ void HdCyclesLight::Finalize(HdRenderParam *renderParam) if (!keep_nodes) { lock.scene->delete_node(_light); + lock.scene->delete_node(_object); } _light = nullptr; + _object = nullptr; } void HdCyclesLight::Initialize(HdRenderParam *renderParam) @@ -365,10 +369,14 @@ void HdCyclesLight::Initialize(HdRenderParam *renderParam) const SceneLock lock(renderParam); + _object = lock.scene->create_node(); + _object->name = GetId().GetString(); + _light = lock.scene->create_node(); _light->name = GetId().GetString(); + _object->set_geometry(_light); - _light->set_random_id(hash_uint2(hash_string(_light->name.c_str()), 0)); + _object->set_random_id(hash_uint2(hash_string(_light->name.c_str()), 0)); if (_lightType == HdPrimTypeTokens->domeLight) { _light->set_light_type(LIGHT_BACKGROUND); @@ -395,7 +403,9 @@ void HdCyclesLight::Initialize(HdRenderParam *renderParam) _light->set_use_camera(false); Shader *const shader = lock.scene->create_node(); - _light->set_shader(shader); + array used_shaders; + used_shaders.push_back_slow(shader); + _light->set_used_shaders(used_shaders); // Create default shader graph PopulateShaderGraph(nullptr); diff --git a/intern/cycles/hydra/light.h b/intern/cycles/hydra/light.h index 381f1c45939..4ed50e7c5a5 100644 --- a/intern/cycles/hydra/light.h +++ b/intern/cycles/hydra/light.h @@ -29,6 +29,7 @@ class HdCyclesLight final : public PXR_NS::HdLight { void PopulateShaderGraph(PXR_NS::HdSceneDelegate *sceneDelegate); + CCL_NS::Object *_object = nullptr; CCL_NS::Light *_light = nullptr; PXR_NS::TfToken _lightType; }; diff --git a/intern/cycles/hydra/session.cpp b/intern/cycles/hydra/session.cpp index abe5368cb31..602ef2b63a7 100644 --- a/intern/cycles/hydra/session.cpp +++ b/intern/cycles/hydra/session.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ #include "hydra/session.h" +#include "scene/object.h" #include "scene/shader.h" // Have to include shader.h before background.h so that 'set_shader' uses the correct 'set' // overload taking a 'Node *', rather than the one taking a 'bool' @@ -99,7 +100,15 @@ void HdCyclesSession::UpdateScene() // Update background depending on presence of a background light if (scene->light_manager->need_update()) { Light *background_light = nullptr; - for (Light *light : scene->lights) { + bool have_lights = false; + for (Object *object : scene->objects) { + if (!object->get_geometry()->is_light()) { + continue; + } + + have_lights = true; + + Light *light = static_cast(object->get_geometry()); if (light->get_light_type() == LIGHT_BACKGROUND) { background_light = light; break; @@ -115,7 +124,7 @@ void HdCyclesSession::UpdateScene() for (ShaderNode *node : scene->default_background->graph->nodes) { if (node->is_a(BackgroundNode::get_node_type())) { BackgroundNode *bgNode = static_cast(node); - bgNode->set_color((scene->lights.empty()) ? make_float3(0.5f) : zero_float3()); + bgNode->set_color((have_lights) ? zero_float3() : make_float3(0.5f)); } } } diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h index 6e41c91ebce..09b6c201e20 100644 --- a/intern/cycles/kernel/bvh/util.h +++ b/intern/cycles/kernel/bvh/util.h @@ -260,10 +260,6 @@ ccl_device_inline bool intersection_skip_self_local(const ccl_ray_data RaySelfPr ccl_device_inline uint64_t ray_get_shadow_set_membership(KernelGlobals kg, const ccl_ray_data RaySelfPrimitives &self) { - if (self.light != LAMP_NONE) { - return kernel_data_fetch(lights, self.light).shadow_set_membership; - } - if (self.light_object != OBJECT_NONE) { return kernel_data_fetch(objects, self.light_object).shadow_set_membership; } diff --git a/intern/cycles/kernel/geom/curve.h b/intern/cycles/kernel/geom/curve.h index 33a8c626b3d..44a932ed117 100644 --- a/intern/cycles/kernel/geom/curve.h +++ b/intern/cycles/kernel/geom/curve.h @@ -85,9 +85,8 @@ ccl_device T curve_attribute(KernelGlobals kg, } # endif - if (desc.element & (ATTR_ELEMENT_CURVE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) { - const int offset = (desc.element == ATTR_ELEMENT_CURVE) ? desc.offset + sd->prim : desc.offset; - return attribute_data_fetch(kg, offset); + if (desc.element == ATTR_ELEMENT_CURVE) { + return attribute_data_fetch(kg, desc.offset + sd->prim); } return make_zero(); } diff --git a/intern/cycles/kernel/geom/object.h b/intern/cycles/kernel/geom/object.h index e93b8968d89..3d0fb72a4d8 100644 --- a/intern/cycles/kernel/geom/object.h +++ b/intern/cycles/kernel/geom/object.h @@ -40,16 +40,6 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals kg, return kernel_data_fetch(objects, object).tfm; } -/* Lamp to world space transformation */ - -ccl_device_inline Transform lamp_fetch_transform(KernelGlobals kg, const int lamp, bool inverse) -{ - if (inverse) { - return kernel_data_fetch(lights, lamp).itfm; - } - return kernel_data_fetch(lights, lamp).tfm; -} - /* Object to world space transformation for motion vectors */ ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals kg, @@ -131,6 +121,13 @@ ccl_device_inline Transform object_get_inverse_transform(KernelGlobals kg, return object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); #endif } + +ccl_device_inline Transform lamp_get_inverse_transform(KernelGlobals kg, + const ccl_global KernelLight *klight) +{ + return object_fetch_transform(kg, klight->object_id, OBJECT_INVERSE_TRANSFORM); +} + /* Transform position from object to world space */ ccl_device_inline void object_position_transform(KernelGlobals kg, @@ -173,7 +170,7 @@ ccl_device_inline void object_inverse_normal_transform(KernelGlobals kg, { #ifdef __OBJECT_MOTION__ if (sd->object_flag & SD_OBJECT_MOTION) { - if ((sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP)) { + if (sd->object != OBJECT_NONE) { *N = safe_normalize(transform_direction_transposed_auto(&sd->ob_tfm_motion, *N)); } return; @@ -184,10 +181,6 @@ ccl_device_inline void object_inverse_normal_transform(KernelGlobals kg, const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); *N = safe_normalize(transform_direction_transposed(&tfm, *N)); } - else if (sd->type == PRIMITIVE_LAMP) { - const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); - *N = safe_normalize(transform_direction_transposed(&tfm, *N)); - } } /* Transform normal from object to world space */ @@ -207,10 +200,6 @@ ccl_device_inline void object_normal_transform(KernelGlobals kg, const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); *N = normalize(transform_direction_transposed(&tfm, *N)); } - else if (sd->type == PRIMITIVE_LAMP) { - const Transform tfm = lamp_fetch_transform(kg, sd->lamp, true); - *N = normalize(transform_direction_transposed(&tfm, *N)); - } } ccl_device_inline bool object_negative_scale_applied(const int object_flag) @@ -304,17 +293,6 @@ ccl_device_inline float object_pass_id(KernelGlobals kg, const int object) return kernel_data_fetch(objects, object).pass_id; } -/* Light-group of lamp. */ - -ccl_device_inline int lamp_lightgroup(KernelGlobals kg, const int lamp) -{ - if (lamp == LAMP_NONE) { - return LIGHTGROUP_NONE; - } - - return kernel_data_fetch(lights, lamp).lightgroup; -} - /* Light-group of object. */ ccl_device_inline int object_lightgroup(KernelGlobals kg, const int object) @@ -326,17 +304,6 @@ ccl_device_inline int object_lightgroup(KernelGlobals kg, const int object) return kernel_data_fetch(objects, object).lightgroup; } -/* Per lamp random number for shader variation */ - -ccl_device_inline float lamp_random_number(KernelGlobals kg, const int lamp) -{ - if (lamp == LAMP_NONE) { - return 0.0f; - } - - return kernel_data_fetch(lights, lamp).random; -} - /* Per object random number for shader variation */ ccl_device_inline float object_random_number(KernelGlobals kg, const int object) diff --git a/intern/cycles/kernel/geom/primitive.h b/intern/cycles/kernel/geom/primitive.h index 0aca36040ae..95656315bbb 100644 --- a/intern/cycles/kernel/geom/primitive.h +++ b/intern/cycles/kernel/geom/primitive.h @@ -36,6 +36,17 @@ ccl_device_forceinline T primitive_surface_attribute(KernelGlobals kg, ccl_private T *dfdx, ccl_private T *dfdy) { + if (desc.element & (ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) { + if (dfdx) { + *dfdx = make_zero(); + } + if (dfdy) { + *dfdy = make_zero(); + } + + return attribute_data_fetch(kg, desc.offset); + } + if (sd->type & PRIMITIVE_TRIANGLE) { if (subd_triangle_patch(kg, sd->prim) == ~0) { return triangle_attribute(kg, sd, desc, dfdx, dfdy); diff --git a/intern/cycles/kernel/geom/shader_data.h b/intern/cycles/kernel/geom/shader_data.h index d2f76c0b1c4..a8cdaf46f29 100644 --- a/intern/cycles/kernel/geom/shader_data.h +++ b/intern/cycles/kernel/geom/shader_data.h @@ -53,7 +53,6 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg, sd->object = isect->object; sd->object_flag = kernel_data_fetch(object_flag, sd->object); sd->prim = isect->prim; - sd->lamp = LAMP_NONE; sd->flag = 0; /* Read matrices and time. */ @@ -139,8 +138,8 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg, const float v, const float t, const float time, - bool object_space, - const int lamp) + const bool object_space, + const bool is_lamp) { /* vectors */ sd->P = P; @@ -148,7 +147,7 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg, sd->Ng = Ng; sd->wi = I; sd->shader = shader; - if (lamp != LAMP_NONE) { + if (is_lamp) { sd->type = PRIMITIVE_LAMP; } else if (prim != PRIM_NONE) { @@ -160,7 +159,6 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg, /* primitive */ sd->object = object; - sd->lamp = LAMP_NONE; /* Currently no access to bvh prim index for strand sd->prim. */ sd->prim = prim; sd->u = u; @@ -213,9 +211,6 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg, } } else { - if (lamp != LAMP_NONE) { - sd->lamp = lamp; - } #ifdef __DPDU__ sd->dPdu = zero_float3(); sd->dPdv = zero_float3(); @@ -278,7 +273,7 @@ ccl_device void shader_setup_from_displace(KernelGlobals kg, 0.0f, 0.5f, !(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), - LAMP_NONE); + false); } /* ShaderData setup for point on curve. */ @@ -293,7 +288,6 @@ ccl_device void shader_setup_from_curve(KernelGlobals kg, { /* Primitive */ sd->type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE_THICK, segment); - sd->lamp = LAMP_NONE; sd->prim = prim; sd->u = u; sd->v = 0.0f; @@ -381,7 +375,6 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals kg, sd->ray_length = FLT_MAX; sd->object = OBJECT_NONE; - sd->lamp = LAMP_NONE; sd->prim = PRIM_NONE; sd->type = PRIMITIVE_NONE; sd->u = 0.0f; @@ -424,7 +417,6 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg, /* TODO: fill relevant fields for texture coordinates. */ sd->object = object; - sd->lamp = LAMP_NONE; sd->prim = PRIM_NONE; sd->type = PRIMITIVE_VOLUME; diff --git a/intern/cycles/kernel/geom/subd_triangle.h b/intern/cycles/kernel/geom/subd_triangle.h index 46aae6bfc29..bf9fe4d5645 100644 --- a/intern/cycles/kernel/geom/subd_triangle.h +++ b/intern/cycles/kernel/geom/subd_triangle.h @@ -253,16 +253,6 @@ ccl_device_noinline T subd_triangle_attribute(KernelGlobals kg, return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a; } - if (desc.element == ATTR_ELEMENT_OBJECT || desc.element == ATTR_ELEMENT_MESH) { - if (dfdx) { - *dfdx = make_zero(); - } - if (dfdy) { - *dfdy = make_zero(); - } - - return attribute_data_fetch(kg, desc.offset); - } if (dfdx) { *dfdx = make_zero(); diff --git a/intern/cycles/kernel/geom/triangle.h b/intern/cycles/kernel/geom/triangle.h index 30fbf8d2008..5237b34dc69 100644 --- a/intern/cycles/kernel/geom/triangle.h +++ b/intern/cycles/kernel/geom/triangle.h @@ -275,9 +275,8 @@ ccl_device T triangle_attribute(KernelGlobals kg, } #endif - if (desc.element & (ATTR_ELEMENT_FACE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) { - const int offset = (desc.element == ATTR_ELEMENT_FACE) ? desc.offset + sd->prim : desc.offset; - return attribute_data_fetch(kg, offset); + if (desc.element == ATTR_ELEMENT_FACE) { + return attribute_data_fetch(kg, desc.offset + sd->prim); } return make_zero(); } diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h index ea432ea3bb8..d078dd655f6 100644 --- a/intern/cycles/kernel/integrator/intersect_closest.h +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -374,7 +374,6 @@ ccl_device void integrator_intersect_closest(KernelGlobals kg, ray.self.prim = last_isect_prim; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; bool hit = scene_intersect(kg, &ray, visibility, &isect); /* TODO: remove this and do it in the various intersection functions instead. */ diff --git a/intern/cycles/kernel/integrator/intersect_dedicated_light.h b/intern/cycles/kernel/integrator/intersect_dedicated_light.h index 606fbf70c4e..dd055e6a3bc 100644 --- a/intern/cycles/kernel/integrator/intersect_dedicated_light.h +++ b/intern/cycles/kernel/integrator/intersect_dedicated_light.h @@ -189,7 +189,6 @@ ccl_device bool shadow_linking_intersect(KernelGlobals kg, IntegratorState state ray.self.object = INTEGRATOR_STATE(state, isect, object); ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; Intersection isect ccl_optional_struct_init; if (!shadow_linking_pick_light_intersection(kg, state, &ray, &isect)) { diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h index d751ad7022f..d539a558b2e 100644 --- a/intern/cycles/kernel/integrator/intersect_volume_stack.h +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -34,7 +34,6 @@ ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, volume_ray.self.prim = INTEGRATOR_STATE(state, isect, prim); volume_ray.self.light_object = OBJECT_NONE; volume_ray.self.light_prim = PRIM_NONE; - volume_ray.self.light = LAMP_NONE; /* Store to avoid global fetches on every intersection step. */ const uint volume_stack_size = kernel_data.volume_stack_size; @@ -98,7 +97,6 @@ ccl_device void integrator_volume_stack_init(KernelGlobals kg, IntegratorState s volume_ray.self.prim = PRIM_NONE; volume_ray.self.light_object = OBJECT_NONE; volume_ray.self.light_prim = PRIM_NONE; - volume_ray.self.light = LAMP_NONE; int stack_index = 0; int enclosed_index = 0; diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index f58623d7da4..81ac77994e5 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -422,7 +422,6 @@ ccl_device_forceinline bool mnee_newton_solver(KernelGlobals kg, Ray projection_ray; projection_ray.self.light_object = OBJECT_NONE; projection_ray.self.light_prim = PRIM_NONE; - projection_ray.self.light = LAMP_NONE; projection_ray.dP = differential_make_compact(sd->dP); projection_ray.dD = differential_zero_compact(); projection_ray.tmin = 0.0f; @@ -866,7 +865,6 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, Ray probe_ray; probe_ray.self.light_object = ls->object; probe_ray.self.light_prim = ls->prim; - probe_ray.self.light = ls->lamp; probe_ray.tmin = 0.0f; probe_ray.dP = differential_make_compact(sd->dP); probe_ray.dD = differential_zero_compact(); @@ -915,7 +913,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, wi_len, sd->time, false, - LAMP_NONE); + false); /* Set bounce info in case a light path node is used in the refractive interface * shader graph. */ @@ -968,7 +966,6 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, probe_ray.self.prim = sd->prim; probe_ray.self.light_object = ls->object; probe_ray.self.light_prim = ls->prim; - probe_ray.self.light = ls->lamp; probe_ray.P = sd->P; probe_ray.tmin = 0.0f; if (ls->t == FLT_MAX) { @@ -1100,7 +1097,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg, /* Distant or environment light. */ bool light_fixed_direction = (ls->t == FLT_MAX); if (ls->type == LIGHT_AREA) { - const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); + const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim); if (klight->area.tan_half_spread == 0.0f) { /* Area light with zero spread also has fixed direction. */ light_fixed_direction = true; diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h index f2dcc22a35f..99dc0a7d384 100644 --- a/intern/cycles/kernel/integrator/shade_background.h +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -138,15 +138,18 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg, } #endif + const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); #ifdef __LIGHT_LINKING__ - if (!light_link_light_match(kg, light_link_receiver_forward(kg, state), lamp) && + if (!light_link_light_match(kg, light_link_receiver_forward(kg, state), klight->object_id) && !(path_flag & PATH_RAY_CAMERA)) { continue; } #endif #ifdef __SHADOW_LINKING__ - if (kernel_data_fetch(lights, lamp).shadow_set_membership != LIGHT_LINK_MASK_ALL) { + if (kernel_data_fetch(objects, klight->object_id).shadow_set_membership != + LIGHT_LINK_MASK_ALL) + { continue; } #endif @@ -155,7 +158,6 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg, if (INTEGRATOR_STATE(state, path, mnee) & PATH_MNEE_CULL_LIGHT_CONNECTION) { /* This path should have been resolved with mnee, it will * generate a firefly for small lights since it is improbable. */ - const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); if (klight->use_caustics) { continue; } diff --git a/intern/cycles/kernel/integrator/shade_dedicated_light.h b/intern/cycles/kernel/integrator/shade_dedicated_light.h index fc678fb2ff1..05752a26aff 100644 --- a/intern/cycles/kernel/integrator/shade_dedicated_light.h +++ b/intern/cycles/kernel/integrator/shade_dedicated_light.h @@ -67,16 +67,8 @@ ccl_device void shadow_linking_setup_ray_from_intersection( ray->self.object = INTEGRATOR_STATE(state, shadow_link, last_isect_object); ray->self.prim = INTEGRATOR_STATE(state, shadow_link, last_isect_prim); - if (isect->type == PRIMITIVE_LAMP) { - ray->self.light_object = OBJECT_NONE; - ray->self.light_prim = PRIM_NONE; - ray->self.light = isect->prim; - } - else { - ray->self.light_object = isect->object; - ray->self.light_prim = isect->prim; - ray->self.light = LAMP_NONE; - } + ray->self.light_object = isect->object; + ray->self.light_prim = isect->prim; } ccl_device bool shadow_linking_shade_light(KernelGlobals kg, diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h index f4039785d72..ea3a4e755dd 100644 --- a/intern/cycles/kernel/integrator/shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -83,7 +83,6 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, ray.self.prim = PRIM_NONE; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; /* Modify ray position and length to match current segment. */ ray.tmin = (hit == 0) ? ray.tmin : INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t); ray.tmax = (hit < num_recorded_hits) ? INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) : diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 3e3ca72c3c6..a8571deb353 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -23,6 +23,7 @@ #include "kernel/integrator/subsurface.h" #include "kernel/integrator/volume_stack.h" +#include "kernel/types.h" #include "util/math_intersect.h" CCL_NAMESPACE_BEGIN @@ -348,9 +349,9 @@ ccl_device #ifdef __MNEE__ IF_KERNEL_FEATURE(MNEE) { - if (ls.lamp != LAMP_NONE) { + if (ls.type != LIGHT_TRIANGLE) { /* Is this a caustic light? */ - const bool use_caustics = kernel_data_fetch(lights, ls.lamp).use_caustics; + const bool use_caustics = kernel_data_fetch(lights, ls.prim).use_caustics; if (use_caustics) { /* Are we on a caustic caster? */ if (is_transmission && (sd->object_flag & SD_OBJECT_CAUSTICS_CASTER)) { @@ -647,7 +648,6 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, ray.self.prim = (skip_self) ? sd->prim : PRIM_NONE; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index 50aa60d5fcd..bbadaaab2b7 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -337,11 +337,11 @@ ccl_device_inline bool volume_equiangular_valid_ray_segment(KernelGlobals kg, const ccl_private LightSample *ls) { if (ls->type == LIGHT_SPOT) { - const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); - return spot_light_valid_ray_segment(klight, ray_P, ray_D, t_range); + const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim); + return spot_light_valid_ray_segment(kg, klight, ray_P, ray_D, t_range); } if (ls->type == LIGHT_AREA) { - const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); + const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim); return area_light_valid_ray_segment(&klight->area, ray_P - klight->co, ray_D, t_range); } if (ls->type == LIGHT_TRIANGLE) { diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h index 8bb577d0cfa..02770b97099 100644 --- a/intern/cycles/kernel/integrator/shadow_state_template.h +++ b/intern/cycles/kernel/integrator/shadow_state_template.h @@ -63,7 +63,6 @@ KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, float, tmin, KERNEL_FEATURE_PATH_TRACING KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, float, tmax, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING) KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING) -KERNEL_STRUCT_MEMBER_PACKED(shadow_ray, int, self_light, KERNEL_FEATURE_SHADOW_LINKING) KERNEL_STRUCT_END(shadow_ray) /*********************** Shadow Intersection result **************************/ diff --git a/intern/cycles/kernel/integrator/state_util.h b/intern/cycles/kernel/integrator/state_util.h index 2a5c7c636d8..3d773021bdb 100644 --- a/intern/cycles/kernel/integrator/state_util.h +++ b/intern/cycles/kernel/integrator/state_util.h @@ -98,10 +98,6 @@ ccl_device_forceinline void integrator_state_read_shadow_ray(ConstIntegratorShad ccl_device_forceinline void integrator_state_write_shadow_ray_self( KernelGlobals kg, IntegratorShadowState state, const ccl_private Ray *ccl_restrict ray) { - if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) { - INTEGRATOR_STATE_WRITE(state, shadow_ray, self_light) = ray->self.light; - } - /* Save memory by storing the light and object indices in the shadow_isect. */ /* TODO(sergey): This optimization does not work on GPU where multiple iterations of intersection * is needed if there are more than 4 transparent intersections. The indices starts to conflict @@ -115,10 +111,6 @@ ccl_device_forceinline void integrator_state_write_shadow_ray_self( ccl_device_forceinline void integrator_state_read_shadow_ray_self( KernelGlobals kg, ConstIntegratorShadowState state, ccl_private Ray *ccl_restrict ray) { - if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) { - ray->self.light = INTEGRATOR_STATE(state, shadow_ray, self_light); - } - ray->self.object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, object); ray->self.prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, prim); ray->self.light_object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 1, object); diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h index a4388ed96f4..c5c0df70436 100644 --- a/intern/cycles/kernel/integrator/subsurface_disk.h +++ b/intern/cycles/kernel/integrator/subsurface_disk.h @@ -111,7 +111,6 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, ray.self.prim = PRIM_NONE; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; /* Intersect with the same object. if multiple intersections are found it * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */ diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h index 2d3db49dd87..3b6cd5c63e4 100644 --- a/intern/cycles/kernel/integrator/subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h @@ -199,7 +199,6 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, ray.self.prim = prim; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; /* Convert subsurface to volume coefficients. * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ diff --git a/intern/cycles/kernel/integrator/volume_shader.h b/intern/cycles/kernel/integrator/volume_shader.h index f3762e04db8..e5df58abe65 100644 --- a/intern/cycles/kernel/integrator/volume_shader.h +++ b/intern/cycles/kernel/integrator/volume_shader.h @@ -439,7 +439,6 @@ ccl_device_inline bool volume_shader_eval_entry(KernelGlobals kg, /* Setup shader-data from stack. It's mostly setup already in shader_setup_from_volume, this * switching should be quick. */ sd->object = entry.object; - sd->lamp = LAMP_NONE; sd->shader = entry.shader; sd->flag &= ~SD_SHADER_FLAGS; diff --git a/intern/cycles/kernel/light/common.h b/intern/cycles/kernel/light/common.h index 84d6623852a..697b0a2afc8 100644 --- a/intern/cycles/kernel/light/common.h +++ b/intern/cycles/kernel/light/common.h @@ -22,9 +22,8 @@ struct LightSample { float pdf_selection; /* pdf for selecting light */ float eval_fac; /* intensity multiplier */ int object; /* object id for triangle/curve lights */ - int prim; /* primitive id for triangle/curve lights */ + int prim; /* lamp id for lights, primitive id for triangle/curve lights */ int shader; /* shader id */ - int lamp; /* lamp id */ int group; /* lightgroup */ LightType type; /* type of light */ int emitter_id; /* index in the emitter array */ diff --git a/intern/cycles/kernel/light/distant.h b/intern/cycles/kernel/light/distant.h index 61e08946b36..b553a59f0b7 100644 --- a/intern/cycles/kernel/light/distant.h +++ b/intern/cycles/kernel/light/distant.h @@ -12,7 +12,8 @@ CCL_NAMESPACE_BEGIN -ccl_device_inline void distant_light_uv(const ccl_global KernelLight *klight, +ccl_device_inline void distant_light_uv(KernelGlobals kg, + const ccl_global KernelLight *klight, const float3 D, ccl_private float *u, ccl_private float *v) @@ -24,7 +25,7 @@ ccl_device_inline void distant_light_uv(const ccl_global KernelLight *klight, const float fac = klight->distant.half_inv_sin_half_angle / len(D - klight->co); /* Get u axis and v axis. */ - const Transform itfm = klight->itfm; + const Transform itfm = lamp_get_inverse_transform(kg, klight); const float u_ = dot(D, make_float3(itfm.x)) * fac; const float v_ = dot(D, make_float3(itfm.y)) * fac; @@ -33,7 +34,8 @@ ccl_device_inline void distant_light_uv(const ccl_global KernelLight *klight, *v = -u_ - v_; } -ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight, +ccl_device_inline bool distant_light_sample(KernelGlobals kg, + const ccl_global KernelLight *klight, const float2 rand, ccl_private LightSample *ls) { @@ -47,7 +49,7 @@ ccl_device_inline bool distant_light_sample(const ccl_global KernelLight *klight ls->eval_fac = klight->distant.eval_fac; - distant_light_uv(klight, ls->D, &ls->u, &ls->v); + distant_light_uv(kg, klight, ls->D, &ls->u, &ls->v); return true; } @@ -114,19 +116,18 @@ ccl_device bool distant_light_sample_from_intersection(KernelGlobals kg, #ifndef __HIP__ ls->shader = klight->shader_id; #endif - ls->object = PRIM_NONE; - ls->prim = PRIM_NONE; - ls->lamp = lamp; + ls->object = klight->object_id; + ls->prim = lamp; ls->t = FLT_MAX; ls->P = -ray_D; ls->Ng = -ray_D; ls->D = ray_D; - ls->group = lamp_lightgroup(kg, lamp); + ls->group = object_lightgroup(kg, ls->object); ls->pdf = klight->distant.pdf; ls->eval_fac = klight->distant.eval_fac; - distant_light_uv(klight, ray_D, &ls->u, &ls->v); + distant_light_uv(kg, klight, ray_D, &ls->u, &ls->v); return true; } diff --git a/intern/cycles/kernel/light/light.h b/intern/cycles/kernel/light/light.h index bc5653c7276..9045818498c 100644 --- a/intern/cycles/kernel/light/light.h +++ b/intern/cycles/kernel/light/light.h @@ -4,6 +4,7 @@ #pragma once +#include "kernel/geom/object.h" #include "kernel/globals.h" #include "kernel/integrator/state.h" @@ -57,14 +58,14 @@ ccl_device_inline int light_link_receiver_forward(KernelGlobals kg, IntegratorSt ccl_device_inline bool light_link_light_match(KernelGlobals kg, const int object_receiver, - const int light_emitter) + const int object_emitter) { #ifdef __LIGHT_LINKING__ if (!(kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_LINKING)) { return true; } - const uint64_t set_membership = kernel_data_fetch(lights, light_emitter).light_set_membership; + const uint64_t set_membership = kernel_data_fetch(objects, object_emitter).light_set_membership; const uint receiver_set = (object_receiver != OBJECT_NONE) ? kernel_data_fetch(objects, object_receiver).receiver_light_set : 0; @@ -122,12 +123,11 @@ ccl_device_inline bool light_sample(KernelGlobals kg, const LightType type = (LightType)klight->type; ls->type = type; ls->shader = klight->shader_id; - ls->object = PRIM_NONE; - ls->prim = PRIM_NONE; - ls->lamp = lamp; + ls->object = klight->object_id; + ls->prim = lamp; ls->u = rand.x; ls->v = rand.y; - ls->group = lamp_lightgroup(kg, lamp); + ls->group = object_lightgroup(kg, ls->object); if (in_volume_segment && (type == LIGHT_DISTANT || type == LIGHT_BACKGROUND)) { /* Distant lights in a volume get a dummy sample, position will not actually @@ -143,7 +143,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg, } if (type == LIGHT_DISTANT) { - if (!distant_light_sample(klight, rand, ls)) { + if (!distant_light_sample(kg, klight, rand, ls)) { return false; } } @@ -158,12 +158,12 @@ ccl_device_inline bool light_sample(KernelGlobals kg, ls->eval_fac = 1.0f; } else if (type == LIGHT_SPOT) { - if (!spot_light_sample(klight, rand, P, N, shader_flags, ls)) { + if (!spot_light_sample(kg, klight, rand, P, N, shader_flags, ls)) { return false; } } else if (type == LIGHT_POINT) { - if (!point_light_sample(klight, rand, P, N, shader_flags, ls)) { + if (!point_light_sample(kg, klight, rand, P, N, shader_flags, ls)) { return false; } } @@ -196,14 +196,15 @@ ccl_device_noinline bool light_sample(KernelGlobals kg, const float2 rand = make_float2(rand_light); int prim; - MeshLight mesh_light; + int shader_flag; + int object_id; #ifdef __LIGHT_TREE__ if (kernel_data.integrator.use_light_tree) { const ccl_global KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters, ls->emitter_id); prim = kemitter->light.id; - mesh_light.shader_flag = kemitter->mesh_light.shader_flag; - mesh_light.object_id = ls->object; + shader_flag = kemitter->shader_flag; + object_id = (prim >= 0) ? ls->object : kemitter->object_id; } else #endif @@ -211,26 +212,25 @@ ccl_device_noinline bool light_sample(KernelGlobals kg, const ccl_global KernelLightDistribution *kdistribution = &kernel_data_fetch( light_distribution, ls->emitter_id); prim = kdistribution->prim; - mesh_light = kdistribution->mesh_light; + object_id = kdistribution->object_id; + shader_flag = 0; + } + + if (!light_link_object_match(kg, object_receiver, object_id)) { + return false; } if (prim >= 0) { /* Mesh light. */ - const int object = mesh_light.object_id; - - if (!light_link_object_match(kg, object_receiver, object)) { - return false; - } /* Exclude synthetic meshes from shadow catcher pass. */ if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) && - !(kernel_data_fetch(object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) + !(kernel_data_fetch(object_flag, object_id) & SD_OBJECT_SHADOW_CATCHER)) { return false; } - const int shader_flag = mesh_light.shader_flag; - if (!triangle_light_sample(kg, prim, object, rand, time, ls, P)) { + if (!triangle_light_sample(kg, prim, object_id, rand, time, ls, P)) { return false; } ls->shader |= shader_flag; @@ -238,10 +238,6 @@ ccl_device_noinline bool light_sample(KernelGlobals kg, else { const int light = ~prim; - if (!light_link_light_match(kg, object_receiver, light)) { - return false; - } - if (UNLIKELY(light_select_reached_max_bounces(kg, light, bounce))) { return false; } @@ -278,6 +274,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg, for (int lamp = 0; lamp < kernel_data.integrator.num_lights; lamp++) { const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); + const int object = klight->object_id; if (path_flag & PATH_RAY_CAMERA) { if (klight->shader_id & SHADER_EXCLUDE_CAMERA) { @@ -312,12 +309,12 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg, if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_LINKING) { if (is_main_path) { if (is_indirect_ray && - kernel_data_fetch(lights, lamp).shadow_set_membership != LIGHT_LINK_MASK_ALL) + kernel_data_fetch(objects, object).shadow_set_membership != LIGHT_LINK_MASK_ALL) { continue; } } - else if (kernel_data_fetch(lights, lamp).shadow_set_membership == LIGHT_LINK_MASK_ALL) { + else if (kernel_data_fetch(objects, object).shadow_set_membership == LIGHT_LINK_MASK_ALL) { continue; } } @@ -325,7 +322,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg, #ifdef __LIGHT_LINKING__ /* Light linking. */ - if (!light_link_light_match(kg, receiver_forward, lamp) && !(path_flag & PATH_RAY_CAMERA)) { + if (!light_link_light_match(kg, receiver_forward, object) && !(path_flag & PATH_RAY_CAMERA)) { continue; } #endif @@ -363,7 +360,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg, } /* Avoid self-intersections. */ - if (last_prim == lamp && last_object == OBJECT_NONE && last_type == PRIMITIVE_LAMP) { + if (last_prim == lamp && last_object == object && last_type == PRIMITIVE_LAMP) { continue; } @@ -391,7 +388,7 @@ ccl_device_forceinline int lights_intersect_impl(KernelGlobals kg, isect->v = v; isect->type = PRIMITIVE_LAMP; isect->prim = lamp; - isect->object = OBJECT_NONE; + isect->object = object; } return num_hits; @@ -465,26 +462,24 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg, const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls) { - const int lamp = isect->prim; - const ccl_global KernelLight *klight = &kernel_data_fetch(lights, lamp); + const ccl_global KernelLight *klight = &kernel_data_fetch(lights, isect->prim); const LightType type = (LightType)klight->type; ls->type = type; ls->shader = klight->shader_id; ls->object = isect->object; ls->prim = isect->prim; - ls->lamp = lamp; ls->t = isect->t; ls->P = ray_P + ray_D * ls->t; ls->D = ray_D; - ls->group = lamp_lightgroup(kg, lamp); + ls->group = object_lightgroup(kg, ls->object); if (type == LIGHT_SPOT) { - if (!spot_light_sample_from_intersection(klight, ray_P, ray_D, N, path_flag, ls)) { + if (!spot_light_sample_from_intersection(kg, klight, ray_P, ray_D, N, path_flag, ls)) { return false; } } else if (type == LIGHT_POINT) { - if (!point_light_sample_from_intersection(klight, ray_P, ray_D, N, path_flag, ls)) { + if (!point_light_sample_from_intersection(kg, klight, ray_P, ray_D, N, path_flag, ls)) { return false; } } diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h index 1f9a80f447c..8346351a920 100644 --- a/intern/cycles/kernel/light/point.h +++ b/intern/cycles/kernel/light/point.h @@ -4,13 +4,16 @@ #pragma once +#include "kernel/globals.h" + #include "kernel/light/common.h" #include "util/math_intersect.h" CCL_NAMESPACE_BEGIN -ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, +ccl_device_inline bool point_light_sample(KernelGlobals kg, + const ccl_global KernelLight *klight, const float2 rand, const float3 P, const float3 N, @@ -72,7 +75,7 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, } /* Texture coordinates. */ - const Transform itfm = klight->itfm; + const Transform itfm = lamp_get_inverse_transform(kg, klight); const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ ls->u = uv.y; @@ -92,7 +95,8 @@ ccl_device_forceinline float sphere_light_pdf( return has_transmission ? M_1_2PI_F * 0.5f : pdf_cos_hemisphere(N, D); } -ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global KernelLight *klight, +ccl_device_forceinline void point_light_mnee_sample_update(KernelGlobals kg, + const ccl_global KernelLight *klight, ccl_private LightSample *ls, const float3 P, const float3 N, @@ -122,7 +126,7 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern } /* Texture coordinates. */ - const Transform itfm = klight->itfm; + const Transform itfm = lamp_get_inverse_transform(kg, klight); const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ ls->u = uv.y; @@ -149,7 +153,8 @@ ccl_device_inline bool point_light_intersect(const ccl_global KernelLight *kligh ray->P, ray->D, ray->tmin, ray->tmax, klight->co, diskN, radius, &P, t); } -ccl_device_inline bool point_light_sample_from_intersection(const ccl_global KernelLight *klight, +ccl_device_inline bool point_light_sample_from_intersection(KernelGlobals kg, + const ccl_global KernelLight *klight, const float3 ray_P, const float3 ray_D, const float3 N, @@ -179,7 +184,7 @@ ccl_device_inline bool point_light_sample_from_intersection(const ccl_global Ker } /* Texture coordinates. */ - const Transform itfm = klight->itfm; + const Transform itfm = lamp_get_inverse_transform(kg, klight); const float2 uv = map_to_sphere(transform_direction(&itfm, ls->Ng)); /* NOTE: Return barycentric coordinates in the same notation as Embree and OptiX. */ ls->u = uv.y; diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index fe55b60bea2..7e2434b72dd 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -8,6 +8,7 @@ #include "kernel/light/distribution.h" #include "kernel/light/light.h" +#include "kernel/types.h" #ifdef __LIGHT_TREE__ # include "kernel/light/tree.h" @@ -56,7 +57,7 @@ light_sample_shader_eval(KernelGlobals kg, ls->t, time, false, - ls->lamp); + ls->type != LIGHT_TRIANGLE); ls->Ng = emission_sd->Ng; } @@ -80,8 +81,8 @@ light_sample_shader_eval(KernelGlobals kg, eval *= ls->eval_fac; - if (ls->lamp != LAMP_NONE) { - const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); + if (ls->type != LIGHT_TRIANGLE) { + const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim); eval *= rgb_to_spectrum( make_float3(klight->strength[0], klight->strength[1], klight->strength[2])); } @@ -251,7 +252,6 @@ ccl_device_inline void shadow_ray_setup(const ccl_private ShaderData *ccl_restri ray->self.prim = (skip_self) ? sd->prim : PRIM_NONE; ray->self.light_object = ls->object; ray->self.light_prim = ls->prim; - ray->self.light = ls->lamp; } /* Create shadow ray towards light sample. */ @@ -392,13 +392,13 @@ ccl_device_forceinline void light_sample_update(KernelGlobals kg, const float3 N, const uint32_t path_flag) { - const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); + const ccl_global KernelLight *klight = &kernel_data_fetch(lights, ls->prim); if (ls->type == LIGHT_POINT) { - point_light_mnee_sample_update(klight, ls, P, N, path_flag); + point_light_mnee_sample_update(kg, klight, ls, P, N, path_flag); } else if (ls->type == LIGHT_SPOT) { - spot_light_mnee_sample_update(klight, ls, P, N, path_flag); + spot_light_mnee_sample_update(kg, klight, ls, P, N, path_flag); } else if (ls->type == LIGHT_AREA) { area_light_mnee_sample_update(klight, ls, P); @@ -487,7 +487,7 @@ ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg, dt, path_flag, 0, - kernel_data_fetch(light_to_tree, ls->lamp), + kernel_data_fetch(light_to_tree, ls->prim), light_link_receiver_forward(kg, state)); } else diff --git a/intern/cycles/kernel/light/spot.h b/intern/cycles/kernel/light/spot.h index 5c3340eba0e..9fb4f1498df 100644 --- a/intern/cycles/kernel/light/spot.h +++ b/intern/cycles/kernel/light/spot.h @@ -13,9 +13,11 @@ CCL_NAMESPACE_BEGIN /* Transform vector to spot light's local coordinate system. */ -ccl_device float3 spot_light_to_local(const ccl_global KernelLight *klight, const float3 ray) +ccl_device float3 spot_light_to_local(KernelGlobals kg, + const ccl_global KernelLight *klight, + const float3 ray) { - const Transform itfm = klight->itfm; + const Transform itfm = lamp_get_inverse_transform(kg, klight); float3 transformed_ray = safe_normalize(transform_direction(&itfm, ray)); transformed_ray.z = -transformed_ray.z; @@ -42,7 +44,8 @@ ccl_device void spot_light_uv(const float3 ray, } template -ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, +ccl_device_inline bool spot_light_sample(KernelGlobals kg, + const ccl_global KernelLight *klight, const float2 rand, const float3 P, const float3 N, @@ -98,7 +101,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, } /* Attenuation. */ - const float3 local_ray = spot_light_to_local(klight, -ls->D); + const float3 local_ray = spot_light_to_local(kg, klight, -ls->D); if (d_sq > r_sq) { ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); } @@ -134,7 +137,7 @@ ccl_device_inline bool spot_light_sample(const ccl_global KernelLight *klight, ls->Ng = -ls->D; /* Attenuation. */ - const float3 local_ray = spot_light_to_local(klight, -ls->D); + const float3 local_ray = spot_light_to_local(kg, klight, -ls->D); ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); if (!in_volume_segment && ls->eval_fac == 0.0f) { return false; @@ -167,7 +170,8 @@ ccl_device_forceinline float spot_light_pdf(const ccl_global KernelSpotLight *sp return has_transmission ? M_1_2PI_F * 0.5f : pdf_cos_hemisphere(N, D); } -ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global KernelLight *klight, +ccl_device_forceinline void spot_light_mnee_sample_update(KernelGlobals kg, + const ccl_global KernelLight *klight, ccl_private LightSample *ls, const float3 P, const float3 N, @@ -203,7 +207,7 @@ ccl_device_forceinline void spot_light_mnee_sample_update(const ccl_global Kerne } /* Attenuation. */ - const float3 local_ray = spot_light_to_local(klight, -ls->D); + const float3 local_ray = spot_light_to_local(kg, klight, -ls->D); if (use_attenuation) { ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); } @@ -224,7 +228,8 @@ ccl_device_inline bool spot_light_intersect(const ccl_global KernelLight *klight return point_light_intersect(klight, ray, t); } -ccl_device_inline bool spot_light_sample_from_intersection(const ccl_global KernelLight *klight, +ccl_device_inline bool spot_light_sample_from_intersection(KernelGlobals kg, + const ccl_global KernelLight *klight, const float3 ray_P, const float3 ray_D, const float3 N, @@ -254,7 +259,7 @@ ccl_device_inline bool spot_light_sample_from_intersection(const ccl_global Kern } /* Attenuation. */ - const float3 local_ray = spot_light_to_local(klight, -ray_D); + const float3 local_ray = spot_light_to_local(kg, klight, -ray_D); if (!klight->spot.is_sphere || d_sq > r_sq) { ls->eval_fac *= spot_light_attenuation(&klight->spot, local_ray); } @@ -269,13 +274,14 @@ ccl_device_inline bool spot_light_sample_from_intersection(const ccl_global Kern } /* Find the ray segment lit by the spot light. */ -ccl_device_inline bool spot_light_valid_ray_segment(const ccl_global KernelLight *klight, +ccl_device_inline bool spot_light_valid_ray_segment(KernelGlobals kg, + const ccl_global KernelLight *klight, const float3 P, const float3 D, ccl_private Interval *t_range) { /* Convert to local space of the spot light. */ - const Transform itfm = klight->itfm; + const Transform itfm = lamp_get_inverse_transform(kg, klight); float3 local_P = P + klight->spot.dir * klight->spot.ray_segment_dp; local_P = transform_point(&itfm, local_P); const float3 local_D = transform_direction(&itfm, D); diff --git a/intern/cycles/kernel/light/tree.h b/intern/cycles/kernel/light/tree.h index 54b2113d77f..98b76033edf 100644 --- a/intern/cycles/kernel/light/tree.h +++ b/intern/cycles/kernel/light/tree.h @@ -73,12 +73,12 @@ ccl_device_inline bool is_light(const ccl_global KernelLightTreeEmitter *kemitte ccl_device_inline bool is_mesh(const ccl_global KernelLightTreeEmitter *kemitter) { - return !is_light(kemitter) && kemitter->mesh_light.object_id == OBJECT_NONE; + return !is_light(kemitter) && kemitter->object_id == OBJECT_NONE; } ccl_device_inline bool is_triangle(const ccl_global KernelLightTreeEmitter *kemitter) { - return !is_light(kemitter) && kemitter->mesh_light.object_id != OBJECT_NONE; + return !is_light(kemitter) && kemitter->object_id != OBJECT_NONE; } ccl_device_inline bool is_leaf(const ccl_global KernelLightTreeNode *knode) @@ -274,7 +274,7 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg, } else { kernel_assert(is_triangle(kemitter)); - const int object = kemitter->mesh_light.object_id; + const int object = kemitter->object_id; float3 vertices[3]; triangle_vertices(kg, kemitter->triangle.id, vertices); centroid = (vertices[0] + vertices[1] + vertices[2]) / 3.0f; diff --git a/intern/cycles/kernel/light/triangle.h b/intern/cycles/kernel/light/triangle.h index f690882bd24..6621a5267c0 100644 --- a/intern/cycles/kernel/light/triangle.h +++ b/intern/cycles/kernel/light/triangle.h @@ -168,7 +168,6 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg, ls->eval_fac = 1.0f; ls->object = object; ls->prim = prim; - ls->lamp = LAMP_NONE; ls->shader |= SHADER_USE_MIS; ls->type = LIGHT_TRIANGLE; ls->group = object_lightgroup(kg, object); diff --git a/intern/cycles/kernel/osl/closures.cpp b/intern/cycles/kernel/osl/closures.cpp index e01effe1f91..4f542151224 100644 --- a/intern/cycles/kernel/osl/closures.cpp +++ b/intern/cycles/kernel/osl/closures.cpp @@ -104,7 +104,7 @@ void osl_eval_nodes(const ThreadKernelGlobalsCPU *kg, OSL::ShadingContext *octx = kg->osl.context; const int shader = sd->shader & SHADER_MASK; - if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) { + if (sd->object == OBJECT_NONE) { /* background */ if (kg->osl.globals->background_state) { ss->execute(*octx, diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 468caaf3034..766c1cad44a 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -75,7 +75,6 @@ ustring OSLRenderServices::u_geom_dupli_generated("geom:dupli_generated"); ustring OSLRenderServices::u_geom_dupli_uv("geom:dupli_uv"); ustring OSLRenderServices::u_material_index("material:index"); ustring OSLRenderServices::u_object_random("object:random"); -ustring OSLRenderServices::u_light_random("light:random"); ustring OSLRenderServices::u_particle_index("particle:index"); ustring OSLRenderServices::u_particle_random("particle:random"); ustring OSLRenderServices::u_particle_age("particle:age"); @@ -177,12 +176,6 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, return true; } - if (sd->type == PRIMITIVE_LAMP) { - const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); - copy_matrix(result, tfm); - - return true; - } return false; } @@ -221,12 +214,6 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, return true; } - if (sd->type == PRIMITIVE_LAMP) { - const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true); - copy_matrix(result, itfm); - - return true; - } return false; } @@ -317,12 +304,6 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, return true; } - if (sd->type == PRIMITIVE_LAMP) { - const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); - copy_matrix(result, tfm); - - return true; - } return false; } @@ -349,12 +330,6 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, return true; } - if (sd->type == PRIMITIVE_LAMP) { - const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true); - copy_matrix(result, itfm); - - return true; - } return false; } @@ -754,10 +729,6 @@ bool OSLRenderServices::get_object_standard_attribute( const float f = object_random_number(kg, sd->object); return set_attribute(f, type, derivatives, val); } - if (name == u_light_random) { - const float f = lamp_random_number(kg, sd->lamp); - return set_attribute(f, type, derivatives, val); - } /* Particle Attributes */ if (name == u_particle_index) { @@ -1571,7 +1542,6 @@ bool OSLRenderServices::trace(TraceOpt &options, ray.self.prim = PRIM_NONE; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; if (options.mindist == 0.0f) { /* avoid self-intersections */ diff --git a/intern/cycles/kernel/osl/services.h b/intern/cycles/kernel/osl/services.h index acbdcea2c8f..621be16713c 100644 --- a/intern/cycles/kernel/osl/services.h +++ b/intern/cycles/kernel/osl/services.h @@ -275,7 +275,6 @@ class OSLRenderServices : public OSL::RendererServices { static ustring u_geom_dupli_uv; static ustring u_material_index; static ustring u_object_random; - static ustring u_light_random; static ustring u_particle_index; static ustring u_particle_random; static ustring u_particle_age; diff --git a/intern/cycles/kernel/osl/services_gpu.h b/intern/cycles/kernel/osl/services_gpu.h index 1698f66f6bc..8237aeae993 100644 --- a/intern/cycles/kernel/osl/services_gpu.h +++ b/intern/cycles/kernel/osl/services_gpu.h @@ -80,8 +80,6 @@ ccl_device_constant DeviceString u_geom_dupli_uv = 1294253317490155849ull; ccl_device_constant DeviceString u_material_index = 741770758159634623ull; /* "object:random" */ ccl_device_constant DeviceString u_object_random = 15789063994977955884ull; -/* "light:random" */ -ccl_device_constant DeviceString u_light_random = 1743557801140685447ull; /* "particle:index" */ ccl_device_constant DeviceString u_particle_index = 9489711748229903784ull; /* "particle:random" */ @@ -628,11 +626,6 @@ ccl_device_extern bool osl_get_matrix(ccl_private ShaderGlobals *sg, copy_matrix(res, tfm); return true; } - else if (sd->type == PRIMITIVE_LAMP) { - const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); - copy_matrix(res, tfm); - return true; - } } else if (from == DeviceStrings::u_ndc) { copy_matrix(res, kernel_data.cam.ndctoworld); @@ -672,11 +665,6 @@ ccl_device_extern bool osl_get_inverse_matrix(ccl_private ShaderGlobals *sg, copy_matrix(res, itfm); return true; } - else if (sd->type == PRIMITIVE_LAMP) { - const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true); - copy_matrix(res, itfm); - return true; - } } else if (to == DeviceStrings::u_ndc) { copy_matrix(res, kernel_data.cam.worldtondc); @@ -1245,11 +1233,6 @@ ccl_device_inline bool get_object_standard_attribute(KernelGlobals kg, return set_attribute(f, type, derivatives, val); } - else if (name == DeviceStrings::u_light_random) { - const float f = lamp_random_number(kg, sd->lamp); - - return set_attribute(f, type, derivatives, val); - } /* Particle attributes */ else if (name == DeviceStrings::u_particle_index) { diff --git a/intern/cycles/kernel/osl/shaders/node_object_info.osl b/intern/cycles/kernel/osl/shaders/node_object_info.osl index 916b491e714..72e31c50f80 100644 --- a/intern/cycles/kernel/osl/shaders/node_object_info.osl +++ b/intern/cycles/kernel/osl/shaders/node_object_info.osl @@ -16,13 +16,5 @@ shader node_object_info(output point Location = point(0.0, 0.0, 0.0), getattribute("object:alpha", Alpha); getattribute("object:index", ObjectIndex); getattribute("material:index", MaterialIndex); - - float is_light; - getattribute("object:is_light", is_light); - if (is_light) { - getattribute("light:random", Random); - } - else { - getattribute("object:random", Random); - } + getattribute("object:random", Random); } diff --git a/intern/cycles/kernel/svm/ao.h b/intern/cycles/kernel/svm/ao.h index 483ccb75916..eb45e0f3702 100644 --- a/intern/cycles/kernel/svm/ao.h +++ b/intern/cycles/kernel/svm/ao.h @@ -76,7 +76,6 @@ ccl_device float svm_ao( ray.self.prim = sd->prim; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; ray.dP = differential_zero_compact(); ray.dD = differential_zero_compact(); diff --git a/intern/cycles/kernel/svm/bevel.h b/intern/cycles/kernel/svm/bevel.h index f82937cfa3a..3cc4fef4049 100644 --- a/intern/cycles/kernel/svm/bevel.h +++ b/intern/cycles/kernel/svm/bevel.h @@ -201,7 +201,6 @@ ccl_device float3 svm_bevel( ray.self.prim = PRIM_NONE; ray.self.light_object = OBJECT_NONE; ray.self.light_prim = PRIM_NONE; - ray.self.light = LAMP_NONE; /* Intersect with the same object. if multiple intersections are found it * will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */ diff --git a/intern/cycles/kernel/svm/geometry.h b/intern/cycles/kernel/svm/geometry.h index 7e54279c56e..12a9f989793 100644 --- a/intern/cycles/kernel/svm/geometry.h +++ b/intern/cycles/kernel/svm/geometry.h @@ -141,12 +141,7 @@ ccl_device_noinline void svm_node_object_info(KernelGlobals kg, data = shader_pass_id(kg, sd); break; case NODE_INFO_OB_RANDOM: { - if (sd->lamp != LAMP_NONE) { - data = lamp_random_number(kg, sd->lamp); - } - else { - data = object_random_number(kg, sd->object); - } + data = object_random_number(kg, sd->object); break; } default: diff --git a/intern/cycles/kernel/svm/vector_transform.h b/intern/cycles/kernel/svm/vector_transform.h index ec8039a51c2..ba4b7100308 100644 --- a/intern/cycles/kernel/svm/vector_transform.h +++ b/intern/cycles/kernel/svm/vector_transform.h @@ -32,7 +32,7 @@ ccl_device_noinline void svm_node_vector_transform(KernelGlobals kg, const NodeVectorTransformConvertSpace to = (NodeVectorTransformConvertSpace)ito; Transform tfm; - const bool is_object = (sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP); + const bool is_object = (sd->object != OBJECT_NONE); const bool is_normal = (type == NODE_VECTOR_TRANSFORM_TYPE_NORMAL); const bool is_direction = (type == NODE_VECTOR_TRANSFORM_TYPE_VECTOR); diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index fb1de5507a7..4eb4f497b65 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -724,7 +724,6 @@ struct RaySelfPrimitives { int object; /* Instance prim is a part of */ int light_prim; /* Light primitive */ int light_object; /* Light object */ - int light; /* Light ID (the light the shadow ray is traced towards to) */ }; struct Ray { @@ -1151,8 +1150,6 @@ struct ccl_align(16) ShaderData float v; /* object id if there is one, ~0 otherwise */ int object; - /* lamp id if there is one, ~0 otherwise */ - int lamp; /* motion blur sample time */ float time; @@ -1606,32 +1603,24 @@ struct KernelLight { int type; packed_float3 co; int shader_id; + int object_id; float max_bounces; - float random; float strength[3]; int use_caustics; - int lightgroup; - Transform tfm; - Transform itfm; + int pad; union { KernelSpotLight spot; KernelAreaLight area; KernelDistantLight distant; }; - uint64_t light_set_membership; - uint64_t shadow_set_membership; }; static_assert_align(KernelLight, 16); -struct MeshLight { - int shader_flag; - int object_id; -}; - struct KernelLightDistribution { float totarea; int prim; - MeshLight mesh_light; + int shader_flag; + int object_id; }; static_assert_align(KernelLightDistribution, 16); @@ -1718,7 +1707,9 @@ struct KernelLightTreeEmitter { } mesh; }; - MeshLight mesh_light; + /* Object and shader. */ + int object_id; + int shader_flag; /* Bit trail from root node to leaf node containing emitter. */ int bit_trail; diff --git a/intern/cycles/scene/geometry.h b/intern/cycles/scene/geometry.h index 6c9598bbb8f..6f09c7374ea 100644 --- a/intern/cycles/scene/geometry.h +++ b/intern/cycles/scene/geometry.h @@ -75,6 +75,7 @@ class Geometry : public Node { HAIR, VOLUME, POINTCLOUD, + LIGHT, }; Type geometry_type; @@ -185,6 +186,11 @@ class Geometry : public Node { return geometry_type == VOLUME; } + bool is_light() const + { + return geometry_type == LIGHT; + } + /* Updates */ void tag_update(Scene *scene, bool rebuild); }; diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 899ec6466d2..49ff6aa7d53 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -84,7 +84,7 @@ static void shade_background_pixels(Device *device, NODE_DEFINE(Light) { - NodeType *type = NodeType::add("light", create); + NodeType *type = NodeType::add("light", create, NodeType::NONE, Geometry::get_node_base_type()); static NodeEnum type_enum; type_enum.insert("point", LIGHT_POINT); @@ -112,8 +112,6 @@ NODE_DEFINE(Light) SOCKET_FLOAT(spot_angle, "Spot Angle", M_PI_4_F); SOCKET_FLOAT(spot_smooth, "Spot Smooth", 0.0f); - SOCKET_TRANSFORM(tfm, "Transform", transform_identity()); - SOCKET_BOOLEAN(cast_shadow, "Cast Shadow", true); SOCKET_BOOLEAN(use_mis, "Use Mis", false); SOCKET_BOOLEAN(use_camera, "Use Camera", true); @@ -124,24 +122,17 @@ NODE_DEFINE(Light) SOCKET_BOOLEAN(use_caustics, "Shadow Caustics", false); SOCKET_INT(max_bounces, "Max Bounces", 1024); - SOCKET_UINT(random_id, "Random ID", 0); SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", true); SOCKET_BOOLEAN(is_portal, "Is Portal", false); SOCKET_BOOLEAN(is_enabled, "Is Enabled", true); - SOCKET_NODE(shader, "Shader", Shader::get_node_type()); - - SOCKET_STRING(lightgroup, "Light Group", ustring()); - SOCKET_UINT64(light_set_membership, "Light Set Membership", LIGHT_LINK_MASK_ALL); - SOCKET_UINT64(shadow_set_membership, "Shadow Set Membership", LIGHT_LINK_MASK_ALL); - SOCKET_BOOLEAN(normalize, "Normalize", true); return type; } -Light::Light() : Node(get_node_type()) +Light::Light() : Geometry(get_node_type(), Geometry::LIGHT) { dereference_all_used_nodes(); } @@ -165,46 +156,33 @@ bool Light::has_contribution(Scene *scene) return true; } - const Shader *effective_shader = (shader) ? shader : scene->default_light; + const Shader *effective_shader = (get_shader()) ? get_shader() : scene->default_light; return !is_zero(effective_shader->emission_estimate); } -bool Light::has_light_linking() const +Shader *Light::get_shader() const { - if (get_light_set_membership() != LIGHT_LINK_MASK_ALL) { - return true; - } - - return false; + return (used_shaders.empty()) ? nullptr : static_cast(used_shaders[0]); } -bool Light::has_shadow_linking() const +void Light::compute_bounds() { - if (get_shadow_set_membership() != LIGHT_LINK_MASK_ALL) { - return true; - } - - return false; + /* To be implemented when this becomes actual geometry. */ } -float3 Light::get_co() const +void Light::apply_transform(const Transform & /*tfm*/, const bool /*apply_to_motion*/) { - return transform_get_column(&tfm, 3); + /* To be implemented when this becomes actual geometry. */ } -float3 Light::get_dir() const +void Light::get_uv_tiles(ustring /*map*/, unordered_set & /*tiles*/) { - return -transform_get_column(&tfm, 2); + /* To be implemented when this becomes actual geometry. */ } -float3 Light::get_axisu() const +PrimitiveType Light::primitive_type() const { - return transform_get_column(&tfm, 0); -} - -float3 Light::get_axisv() const -{ - return transform_get_column(&tfm, 1); + return PRIMITIVE_LAMP; } /* Light Manager */ @@ -219,7 +197,12 @@ LightManager::LightManager() bool LightManager::has_background_light(Scene *scene) { - for (Light *light : scene->lights) { + for (Object *object : scene->objects) { + if (!object->get_geometry()->is_light()) { + continue; + } + + Light *light = static_cast(object->get_geometry()); if (light->light_type == LIGHT_BACKGROUND && light->is_enabled) { return true; } @@ -233,18 +216,31 @@ void LightManager::test_enabled_lights(Scene *scene) * needed for finer-tuning of settings (for example, check whether we've * got portals or not). */ + vector background_lights; + size_t num_lights = 0; bool has_portal = false; - bool has_background = false; - for (Light *light : scene->lights) { + for (Object *object : scene->objects) { + if (!object->get_geometry()->is_light()) { + continue; + } + + Light *light = static_cast(object->get_geometry()); light->is_enabled = light->has_contribution(scene); has_portal |= light->is_portal; - has_background |= light->light_type == LIGHT_BACKGROUND; + + if (light->light_type == LIGHT_BACKGROUND) { + background_lights.push_back(light); + } + + num_lights++; } + VLOG_INFO << "Total " << num_lights << " lights."; + bool background_enabled = false; int background_resolution = 0; - if (has_background) { + if (!background_lights.empty()) { /* Ignore background light if: * - If unsupported on a device * - If we don't need it (no HDRs etc.) @@ -254,12 +250,10 @@ void LightManager::test_enabled_lights(Scene *scene) if (disable_mis) { VLOG_INFO << "Background MIS has been disabled.\n"; } - for (Light *light : scene->lights) { - if (light->light_type == LIGHT_BACKGROUND) { - light->is_enabled = !disable_mis; - background_enabled = !disable_mis; - background_resolution = light->map_resolution; - } + for (Light *light : background_lights) { + light->is_enabled = !disable_mis; + background_enabled = !disable_mis; + background_resolution = light->map_resolution; } } @@ -335,7 +329,6 @@ void LightManager::device_update_distribution(Device * /*unused*/, /* Triangles. */ size_t offset = 0; - int j = 0; for (Object *object : scene->objects) { if (progress.get_cancel()) { @@ -343,14 +336,12 @@ void LightManager::device_update_distribution(Device * /*unused*/, } if (!object->usable_as_light()) { - j++; continue; } /* Sum area. */ Mesh *mesh = static_cast(object->get_geometry()); const bool transform_applied = mesh->transform_applied; const Transform tfm = object->get_tfm(); - const int object_id = j; int shader_flag = 0; if (!(object->get_visibility() & PATH_RAY_CAMERA)) { @@ -382,8 +373,8 @@ void LightManager::device_update_distribution(Device * /*unused*/, if (shader->emission_sampling != EMISSION_SAMPLING_NONE) { distribution[offset].totarea = totarea; distribution[offset].prim = i + mesh->prim_offset; - distribution[offset].mesh_light.shader_flag = shader_flag; - distribution[offset].mesh_light.object_id = object_id; + distribution[offset].shader_flag = shader_flag; + distribution[offset].object_id = object->index; offset++; const Mesh::Triangle t = mesh->get_triangle(i); @@ -403,8 +394,6 @@ void LightManager::device_update_distribution(Device * /*unused*/, totarea += triangle_area(p1, p2, p3); } } - - j++; } const float trianglearea = totarea; @@ -414,15 +403,20 @@ void LightManager::device_update_distribution(Device * /*unused*/, if (num_lights > 0) { const float lightarea = (totarea > 0.0f) ? totarea / num_lights : 1.0f; - for (Light *light : scene->lights) { + for (Object *object : scene->objects) { + if (!object->get_geometry()->is_light()) { + continue; + } + + Light *light = static_cast(object->get_geometry()); if (!light->is_enabled) { continue; } distribution[offset].totarea = totarea; distribution[offset].prim = ~light_index; - distribution[offset].mesh_light.object_id = OBJECT_NONE; - distribution[offset].mesh_light.shader_flag = 0; + distribution[offset].object_id = object->index; + distribution[offset].shader_flag = 0; totarea += lightarea; light_index++; @@ -433,8 +427,8 @@ void LightManager::device_update_distribution(Device * /*unused*/, /* normalize cumulative distribution functions */ distribution[num_distribution].totarea = totarea; distribution[num_distribution].prim = 0; - distribution[num_distribution].mesh_light.object_id = OBJECT_NONE; - distribution[num_distribution].mesh_light.shader_flag = 0; + distribution[num_distribution].object_id = OBJECT_NONE; + distribution[num_distribution].shader_flag = 0; if (totarea > 0.0f) { for (size_t i = 0; i < num_distribution; i++) { @@ -566,8 +560,8 @@ static void light_tree_leaf_emitters_copy_and_flatten(LightTreeFlatten &flatten, } kemitter.triangle.id = emitter.prim_id + mesh->prim_offset; - kemitter.mesh_light.shader_flag = shader_flag; - kemitter.mesh_light.object_id = emitter.object_id; + kemitter.shader_flag = shader_flag; + kemitter.object_id = emitter.object_id; kemitter.triangle.emission_sampling = shader->emission_sampling; flatten.triangle_array[emitter.prim_id + flatten.object_lookup_offset[emitter.object_id]] = emitter_index; @@ -575,16 +569,16 @@ static void light_tree_leaf_emitters_copy_and_flatten(LightTreeFlatten &flatten, else if (emitter.is_light()) { /* Light object. */ kemitter.light.id = emitter.light_id; - kemitter.mesh_light.shader_flag = 0; - kemitter.mesh_light.object_id = OBJECT_NONE; + kemitter.shader_flag = 0; + kemitter.object_id = emitter.object_id; flatten.light_array[~emitter.light_id] = emitter_index; } else { /* Mesh instance. */ assert(emitter.is_mesh()); kemitter.mesh.object_id = emitter.object_id; - kemitter.mesh_light.shader_flag = 0; - kemitter.mesh_light.object_id = OBJECT_NONE; + kemitter.shader_flag = 0; + kemitter.object_id = OBJECT_NONE; flatten.mesh_array[emitter.object_id] = emitter_index; /* Create instance node. One instance node will be the same as the @@ -595,7 +589,8 @@ static void light_tree_leaf_emitters_copy_and_flatten(LightTreeFlatten &flatten, auto map_it = flatten.instances.find(reference_node); if (map_it == flatten.instances.end()) { if (instance_node != reference_node) { - /* Flatten the node with the subtree first so the subsequent instances know the index. */ + /* Flatten the node with the subtree first so the subsequent instances know the index. + */ std::swap(instance_node->type, reference_node->type); std::swap(instance_node->variant_type, reference_node->variant_type); } @@ -727,8 +722,8 @@ static std::pair light_tree_specialize_nodes_flatten( assert(first_emitter != -1); - /* Preserve the type of the node, so that the kernel can do proper decision when sampling node - * with multiple distant lights in it. */ + /* Preserve the type of the node, so that the kernel can do proper decision when sampling + * node with multiple distant lights in it. */ if (node->is_leaf()) { new_node.make_leaf(first_emitter, num_emitters); } @@ -951,7 +946,12 @@ void LightManager::device_update_background(Device *device, bool background_mis = false; /* find background light */ - for (Light *light : scene->lights) { + for (Object *object : scene->objects) { + if (!object->get_geometry()->is_light()) { + continue; + } + + Light *light = static_cast(object->get_geometry()); if (light->light_type == LIGHT_BACKGROUND && light->is_enabled) { background_light = light; background_mis |= light->use_mis; @@ -1125,7 +1125,12 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) size_t num_distant_lights = 0; bool use_light_mis = false; - for (Light *light : scene->lights) { + for (Object *object : scene->objects) { + if (!object->get_geometry()->is_light()) { + continue; + } + + Light *light = static_cast(object->get_geometry()); if (light->is_enabled) { num_lights++; @@ -1165,14 +1170,24 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) int light_index = 0; int portal_index = num_lights; - for (Light *light : scene->lights) { + for (Object *object : scene->objects) { + if (!object->get_geometry()->is_light()) { + continue; + } + + Light *light = static_cast(object->get_geometry()); + const float3 axisu = transform_get_column(&object->get_tfm(), 0); + const float3 axisv = transform_get_column(&object->get_tfm(), 1); + const float3 dir = -transform_get_column(&object->get_tfm(), 2); + const float3 co = transform_get_column(&object->get_tfm(), 3); + /* Consider moving portals update to their own function * keeping this one more manageable. */ if (light->is_portal) { assert(light->light_type == LIGHT_AREA); - const float3 extentu = light->get_axisu() * (light->sizeu * light->size); - const float3 extentv = light->get_axisv() * (light->sizev * light->size); + const float3 extentu = axisu * (light->sizeu * light->size); + const float3 extentv = axisv * (light->sizev * light->size); float len_u; float len_v; @@ -1188,17 +1203,14 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) invarea = -invarea; } - const float3 dir = safe_normalize(light->get_dir()); - - klights[portal_index].co = light->get_co(); + klights[portal_index].co = co; klights[portal_index].area.axis_u = axis_u; klights[portal_index].area.len_u = len_u; klights[portal_index].area.axis_v = axis_v; klights[portal_index].area.len_v = len_v; klights[portal_index].area.invarea = invarea; - klights[portal_index].area.dir = dir; - klights[portal_index].tfm = light->tfm; - klights[portal_index].itfm = transform_inverse(light->tfm); + klights[portal_index].area.dir = safe_normalize(dir); + klights[portal_index].object_id = object->index; portal_index++; continue; @@ -1208,9 +1220,8 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) continue; } - Shader *shader = (light->shader) ? light->shader : scene->default_light; + Shader *shader = (light->get_shader()) ? light->get_shader() : scene->default_light; int shader_id = scene->shader_manager->get_shader_id(shader); - const float random = (float)light->random_id * (1.0f / (float)0xFFFFFFFF); if (!light->cast_shadow) { shader_id &= ~SHADER_CAST_SHADOW; @@ -1255,7 +1266,7 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) shader_id |= SHADER_USE_MIS; } - klights[light_index].co = light->get_co(); + klights[light_index].co = co; klights[light_index].spot.radius = radius; klights[light_index].spot.eval_fac = eval_fac; klights[light_index].spot.is_sphere = light->get_is_sphere() && radius != 0.0f; @@ -1263,7 +1274,6 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) else if (light->light_type == LIGHT_DISTANT) { shader_id &= ~SHADER_AREA_LIGHT; - const float3 dir = safe_normalize(light->get_dir()); const float angle = light->angle / 2.0f; if (light->use_mis && angle > 0.0f) { @@ -1273,7 +1283,7 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) const float one_minus_cosangle = 2.0f * sqr(sinf(0.5f * angle)); const float pdf = (angle > 0.0f) ? (M_1_2PI_F / one_minus_cosangle) : 1.0f; - klights[light_index].co = dir; + klights[light_index].co = safe_normalize(dir); klights[light_index].distant.angle = angle; klights[light_index].distant.one_minus_cosangle = one_minus_cosangle; klights[light_index].distant.pdf = pdf; @@ -1307,8 +1317,8 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) } else if (light->light_type == LIGHT_AREA) { const float light_size = light->size; - const float3 extentu = light->get_axisu() * (light->sizeu * light_size); - const float3 extentv = light->get_axisv() * (light->sizev * light_size); + const float3 extentu = axisu * (light->sizeu * light_size); + const float3 extentv = axisv * (light->sizev * light_size); float len_u; float len_v; @@ -1336,19 +1346,17 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) 3.0f / powf(half_spread, 3.0f)) : FLT_MAX; - const float3 dir = safe_normalize(light->get_dir()); - if (light->use_mis && area != 0.0f && light->spread > 0.0f) { shader_id |= SHADER_USE_MIS; } - klights[light_index].co = light->get_co(); + klights[light_index].co = co; klights[light_index].area.axis_u = axis_u; klights[light_index].area.len_u = len_u; klights[light_index].area.axis_v = axis_v; klights[light_index].area.len_v = len_v; klights[light_index].area.invarea = invarea; - klights[light_index].area.dir = dir; + klights[light_index].area.dir = safe_normalize(dir); klights[light_index].area.tan_half_spread = tan_half_spread; klights[light_index].area.normalize_spread = normalize_spread; } @@ -1357,12 +1365,12 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) const float spot_smooth = 1.0f / ((1.0f - cos_half_spot_angle) * light->spot_smooth); const float tan_half_spot_angle = tanf(light->spot_angle * 0.5f); - const float len_w_sq = len_squared(light->get_dir()); - const float len_u_sq = len_squared(light->get_axisu()); - const float len_v_sq = len_squared(light->get_axisv()); + const float len_w_sq = len_squared(dir); + const float len_u_sq = len_squared(axisu); + const float len_v_sq = len_squared(axisv); const float tan_sq = sqr(tan_half_spot_angle); - klights[light_index].spot.dir = safe_normalize(light->get_dir()); + klights[light_index].spot.dir = safe_normalize(dir); klights[light_index].spot.cos_half_spot_angle = cos_half_spot_angle; klights[light_index].spot.half_cot_half_spot_angle = 0.5f / tan_half_spot_angle; klights[light_index].spot.spot_smooth = spot_smooth; @@ -1375,31 +1383,11 @@ void LightManager::device_update_lights(DeviceScene *dscene, Scene *scene) } klights[light_index].shader_id = shader_id; + klights[light_index].object_id = object->index; klights[light_index].max_bounces = light->max_bounces; - klights[light_index].random = random; klights[light_index].use_caustics = light->use_caustics; - klights[light_index].tfm = light->tfm; - klights[light_index].itfm = transform_inverse(light->tfm); - - /* Light group. */ - if (light->light_type == LIGHT_BACKGROUND) { - klights[light_index].lightgroup = dscene->data.background.lightgroup; - } - else { - auto it = scene->lightgroups.find(light->lightgroup); - if (it != scene->lightgroups.end()) { - klights[light_index].lightgroup = it->second; - } - else { - klights[light_index].lightgroup = LIGHTGROUP_NONE; - } - } - - klights[light_index].light_set_membership = light->light_set_membership; - klights[light_index].shadow_set_membership = light->shadow_set_membership; - light_index++; } @@ -1423,8 +1411,6 @@ void LightManager::device_update(Device *device, } }); - VLOG_INFO << "Total " << scene->lights.size() << " lights."; - /* Detect which lights are enabled, also determines if we need to update the background. */ test_enabled_lights(scene); diff --git a/intern/cycles/scene/light.h b/intern/cycles/scene/light.h index b70b22f507d..3a37a83fa05 100644 --- a/intern/cycles/scene/light.h +++ b/intern/cycles/scene/light.h @@ -8,9 +8,7 @@ #include "graph/node.h" -/* included as Light::set_shader defined through NODE_SOCKET_API does not select - * the right Node::set overload as it does not know that Shader is a Node */ -#include "scene/shader.h" +#include "scene/geometry.h" #include "util/ies.h" #include "util/thread.h" @@ -26,7 +24,7 @@ class Progress; class Scene; class Shader; -class Light : public Node { +class Light : public Geometry { public: NODE_DECLARE; @@ -43,8 +41,6 @@ class Light : public Node { NODE_SOCKET_API(bool, ellipse) NODE_SOCKET_API(float, spread) - NODE_SOCKET_API(Transform, tfm) - NODE_SOCKET_API(int, map_resolution) NODE_SOCKET_API(float, average_radiance) @@ -66,13 +62,7 @@ class Light : public Node { NODE_SOCKET_API(bool, is_portal) NODE_SOCKET_API(bool, is_enabled) - NODE_SOCKET_API(Shader *, shader) NODE_SOCKET_API(int, max_bounces) - NODE_SOCKET_API(uint, random_id) - - NODE_SOCKET_API(ustring, lightgroup) - NODE_SOCKET_API(uint64_t, light_set_membership); - NODE_SOCKET_API(uint64_t, shadow_set_membership); /* Normalize power by the surface area of the light. */ NODE_SOCKET_API(bool, normalize) @@ -82,15 +72,14 @@ class Light : public Node { /* Check whether the light has contribution the scene. */ bool has_contribution(Scene *scene); - /* Check whether this light participates in light or shadow linking. */ - bool has_light_linking() const; - bool has_shadow_linking() const; + /* Shader */ + Shader *get_shader() const; - /* Convenience access to transform. */ - float3 get_co() const; - float3 get_dir() const; - float3 get_axisu() const; - float3 get_axisv() const; + /* Geometry */ + void compute_bounds() override; + void apply_transform(const Transform &tfm, const bool apply_to_motion) override; + void get_uv_tiles(ustring map, unordered_set &tiles) override; + PrimitiveType primitive_type() const override; friend class LightManager; friend class LightTree; diff --git a/intern/cycles/scene/light_tree.cpp b/intern/cycles/scene/light_tree.cpp index 8d920a460ff..b3ff8e36dae 100644 --- a/intern/cycles/scene/light_tree.cpp +++ b/intern/cycles/scene/light_tree.cpp @@ -88,9 +88,10 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, bool need_transformation) : prim_id(prim_id), object_id(object_id) { + Object *object = scene->objects[object_id]; + if (is_triangle()) { float3 vertices[3]; - Object *object = scene->objects[object_id]; Mesh *mesh = static_cast(object->get_geometry()); const Mesh::Triangle triangle = mesh->get_triangle(prim_id); Shader *shader = static_cast(mesh->get_used_shaders()[mesh->get_shader()[prim_id]]); @@ -148,13 +149,13 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, } else { assert(is_light()); - Light *lamp = scene->lights[object_id]; + Light *lamp = static_cast(object->get_geometry()); const LightType type = lamp->get_light_type(); const float size = lamp->get_size(); float3 strength = lamp->get_strength(); - centroid = lamp->get_co(); - measure.bcone.axis = safe_normalize(lamp->get_dir()); + centroid = transform_get_column(&object->get_tfm(), 3); + measure.bcone.axis = -safe_normalize(transform_get_column(&object->get_tfm(), 2)); if (type == LIGHT_AREA) { measure.bcone.theta_o = 0; @@ -163,8 +164,10 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, /* For an area light, sizeu and sizev determine the 2 dimensions of the area light, * while axisu and axisv determine the orientation of the 2 dimensions. * We want to add all 4 corners to our bounding box. */ - const float3 half_extentu = 0.5f * lamp->get_sizeu() * lamp->get_axisu() * size; - const float3 half_extentv = 0.5f * lamp->get_sizev() * lamp->get_axisv() * size; + const float3 axisu = transform_get_column(&object->get_tfm(), 0); + const float3 axisv = transform_get_column(&object->get_tfm(), 1); + const float3 half_extentu = 0.5f * lamp->get_sizeu() * axisu * size; + const float3 half_extentv = 0.5f * lamp->get_sizev() * axisv * size; measure.bbox.grow(centroid + half_extentu + half_extentv); measure.bbox.grow(centroid + half_extentu - half_extentv); measure.bbox.grow(centroid - half_extentu + half_extentv); @@ -188,9 +191,9 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, measure.bcone.theta_o = 0; float theta_e = min(lamp->get_spot_angle() * 0.5f, M_PI_2_F); - const float len_u = len(lamp->get_axisu()); - const float len_v = len(lamp->get_axisv()); - const float len_w = len(lamp->get_dir()); + const float len_u = len(transform_get_column(&object->get_tfm(), 0)); + const float len_v = len(transform_get_column(&object->get_tfm(), 1)); + const float len_w = len(transform_get_column(&object->get_tfm(), 2)); /* As `theta_e` approaches `pi/2`, the behavior of `atan(tan(theta_e))` can become quite * unpredictable as `tan(x)` has an asymptote at `x = pi/2`. To avoid this, we skip the back @@ -236,7 +239,7 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene, * light tree. */ measure.energy = average(fabs(strength)); - light_set_membership = lamp->get_light_set_membership(); + light_set_membership = object->get_light_set_membership(); } } @@ -289,45 +292,42 @@ LightTree::LightTree(Scene *scene, * Therefore, we want to keep track of the light's index on the device. * However, we also need the light's index in the scene when we're constructing the tree. */ int device_light_index = 0; - int scene_light_index = 0; - for (Light *light : scene->lights) { - if (light->is_enabled) { - if (light->light_type == LIGHT_BACKGROUND || light->light_type == LIGHT_DISTANT) { - distant_lights_.emplace_back(scene, ~device_light_index, scene_light_index); - } - else { - local_lights_.emplace_back(scene, ~device_light_index, scene_light_index); - } - - device_light_index++; - } - - scene_light_index++; - } - - /* Similarly, we also want to keep track of the index of triangles of emissive objects. */ - int object_id = 0; for (Object *object : scene->objects) { if (progress_.get_cancel()) { return; } - light_link_receiver_used |= (uint64_t(1) << object->get_receiver_light_set()); + if (object->get_geometry()->is_light()) { + /* Regular lights. */ + Light *light = static_cast(object->get_geometry()); + if (light->is_enabled) { + if (light->light_type == LIGHT_BACKGROUND || light->light_type == LIGHT_DISTANT) { + distant_lights_.emplace_back(scene, ~device_light_index, object->index); + } + else { + local_lights_.emplace_back(scene, ~device_light_index, object->index); + } - if (!object->usable_as_light()) { - object_id++; - continue; + device_light_index++; + } } + else { + /* Emissive triangles. */ + light_link_receiver_used |= (uint64_t(1) << object->get_receiver_light_set()); - mesh_lights_.emplace_back(object, object_id); - object_id++; + if (!object->usable_as_light()) { + continue; + } - /* Only count unique meshes. */ - Mesh *mesh = static_cast(object->get_geometry()); - auto map_it = offset_map_.find(mesh); - if (map_it == offset_map_.end()) { - offset_map_[mesh] = num_triangles; - num_triangles += mesh->num_triangles(); + mesh_lights_.emplace_back(object, object->index); + + /* Only count unique meshes. */ + Mesh *mesh = static_cast(object->get_geometry()); + auto map_it = offset_map_.find(mesh); + if (map_it == offset_map_.end()) { + offset_map_[mesh] = num_triangles; + num_triangles += mesh->num_triangles(); + } } } } diff --git a/intern/cycles/scene/light_tree_debug.cpp b/intern/cycles/scene/light_tree_debug.cpp index e5bde5ceb12..ffebe627122 100644 --- a/intern/cycles/scene/light_tree_debug.cpp +++ b/intern/cycles/scene/light_tree_debug.cpp @@ -75,8 +75,8 @@ static void recursive_print_node(FILE *file, const LightTreeNode &node) fprintf(file, "];\n"); if (node.is_inner()) { - const LightTreeNode &left_node = *node.get_inner().children[LightTree::left].get(); - const LightTreeNode &right_node = *node.get_inner().children[LightTree::right].get(); + const LightTreeNode &left_node = *node.get_inner().children[LightTree::left]; + const LightTreeNode &right_node = *node.get_inner().children[LightTree::right]; recursive_print_node(file, left_node); recursive_print_node(file, right_node); @@ -95,21 +95,17 @@ static void print_emitters(FILE *file, const Scene &scene, const LightTree &tree string label = string_printf(" emitter %s", field++, std::to_string(i).c_str()); /* Emitter details (type, object or light name). */ + const Object &object = *scene.objects[emitter.object_id]; if (emitter.is_light()) { - const Light &lamp = *scene.lights[emitter.object_id]; label += string_printf("| light", field++); - label += string_printf("| %s", field++, lamp.name.c_str()); } else if (emitter.is_triangle()) { - const Object &object = *scene.objects[emitter.object_id]; label += string_printf("| triangle", field++); - label += string_printf("| %s", field++, object.name.c_str()); } else if (emitter.is_mesh()) { - const Object &object = *scene.objects[emitter.object_id]; label += string_printf("| mesh", field++); - label += string_printf("| %s", field++, object.name.c_str()); } + label += string_printf("| %s", field++, object.name.c_str()); /* Light linking. */ if (emitter.light_set_membership == ~uint64_t(0)) { @@ -176,8 +172,8 @@ static void recursive_print_node_relations(FILE *file, return; } - const LightTreeNode &left_node = *node.get_inner().children[LightTree::left].get(); - const LightTreeNode &right_node = *node.get_inner().children[LightTree::right].get(); + const LightTreeNode &left_node = *node.get_inner().children[LightTree::left]; + const LightTreeNode &right_node = *node.get_inner().children[LightTree::right]; const string left_node_id = get_node_id(left_node); const string right_node_id = get_node_id(right_node); diff --git a/intern/cycles/scene/object.cpp b/intern/cycles/scene/object.cpp index caa3f12281d..f491719238d 100644 --- a/intern/cycles/scene/object.cpp +++ b/intern/cycles/scene/object.cpp @@ -272,6 +272,10 @@ int Object::motion_step(const float time) const bool Object::is_traceable() const { + /* Not supported for lights yet. */ + if (geometry->is_light()) { + return false; + } /* Mesh itself can be empty,can skip all such objects. */ if (!bounds.valid() || bounds.size() == zero_float3()) { return false; diff --git a/intern/cycles/scene/object.h b/intern/cycles/scene/object.h index ae0648d0904..026bc8dbcc2 100644 --- a/intern/cycles/scene/object.h +++ b/intern/cycles/scene/object.h @@ -77,6 +77,10 @@ class Object : public Node { /* Set during device update. */ bool intersects_volume; + /* Specifies the position of the object in scene->objects and + * in the device vectors. Gets set in device_update. */ + int index; + Object(); ~Object() override; @@ -120,10 +124,6 @@ class Object : public Node { bool has_shadow_linking() const; protected: - /* Specifies the position of the object in scene->objects and - * in the device vectors. Gets set in device_update. */ - int index; - /* Reference to the attribute map with object attributes, * or 0 if none. Set in update_svm_attributes. */ size_t attr_map_offset; diff --git a/intern/cycles/scene/scene.cpp b/intern/cycles/scene/scene.cpp index 2b66123d5f3..7dbd053ee9b 100644 --- a/intern/cycles/scene/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -99,7 +99,6 @@ void Scene::free_memory(bool final) procedurals.clear(); objects.clear(); geometry.clear(); - lights.clear(); particle_systems.clear(); passes.clear(); @@ -511,6 +510,12 @@ void Scene::update_kernel_features() else if (geom->is_pointcloud()) { kernel_features |= KERNEL_FEATURE_POINTCLOUD; } + else if (geom->is_light()) { + const Light *light = static_cast(object->get_geometry()); + if (light->get_use_caustics()) { + has_caustics_light = true; + } + } if (object->has_light_linking()) { kernel_features |= KERNEL_FEATURE_LIGHT_LINKING; } @@ -519,19 +524,6 @@ void Scene::update_kernel_features() } } - for (Light *light : lights) { - if (light->get_use_caustics()) { - has_caustics_light = true; - } - - if (light->has_light_linking()) { - kernel_features |= KERNEL_FEATURE_LIGHT_LINKING; - } - if (light->has_shadow_linking()) { - kernel_features |= KERNEL_FEATURE_SHADOW_LINKING; - } - } - dscene.data.integrator.use_caustics = false; if (device->info.has_mnee && has_caustics_caster && has_caustics_receiver && has_caustics_light) { @@ -741,7 +733,7 @@ template<> Light *Scene::create_node() unique_ptr node = make_unique(); Light *node_ptr = node.get(); node->set_owner(this); - lights.push_back(std::move(node)); + geometry.push_back(std::move(node)); light_manager->tag_update(this, LightManager::LIGHT_ADDED); return node_ptr; } @@ -879,7 +871,7 @@ template<> Film *Scene::create_node() template<> void Scene::delete_node(Light *node) { assert(node->get_owner() == this); - lights.erase_by_swap(node); + geometry.erase_by_swap(node); light_manager->tag_update(this, LightManager::LIGHT_REMOVED); } @@ -983,18 +975,12 @@ template static void assert_same_owner(const set &nodes, const #endif } -template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner) -{ - assert_same_owner(nodes, owner); - lights.erase_in_set(nodes); - light_manager->tag_update(this, LightManager::LIGHT_REMOVED); -} - template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner) { assert_same_owner(nodes, owner); geometry.erase_in_set(nodes); geometry_manager->tag_update(this, GeometryManager::GEOMETRY_REMOVED); + light_manager->tag_update(this, LightManager::LIGHT_REMOVED); } template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner) diff --git a/intern/cycles/scene/scene.h b/intern/cycles/scene/scene.h index 2777949cae8..4b231270d86 100644 --- a/intern/cycles/scene/scene.h +++ b/intern/cycles/scene/scene.h @@ -136,7 +136,6 @@ class Scene : public NodeOwner { unique_ptr_vector shaders; unique_ptr_vector passes; unique_ptr_vector particle_systems; - unique_ptr_vector lights; unique_ptr_vector geometry; unique_ptr_vector objects; unique_ptr_vector procedurals; @@ -281,7 +280,6 @@ template<> void Scene::delete_node(Procedural *node); template<> void Scene::delete_node(AlembicProcedural *node); template<> void Scene::delete_node(Pass *node); -template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner);