Cycles: Add support for light groups
Light groups are a type of pass that only contains lighting from a subset of light sources. They are created in the View layer, and light sources (lamps, objects with emissive materials and/or the environment) can be assigned to a group. Currently, each light group ends up generating its own version of the Combined pass. In the future, additional types of passes (e.g. shadowcatcher) might be getting their own per-lightgroup versions. The lightgroup creation and assignment is not Cycles-specific, so Eevee or external render engines could make use of it in the future. Note that Lightgroups are identified by their name - therefore, the name of the Lightgroup in the View Layer and the name that's set in an object's settings must match for it to be included. Currently, changing a Lightgroup's name does not update objects - this is planned for the future, along with other features such as denoising for light groups and viewing them in preview renders. Original patch by Alex Fuller (@mistaed), with some polishing by Lukas Stockner (@lukasstockner97). Differential Revision: https://developer.blender.org/D12871
This commit is contained in:
parent
5387d33e5f
commit
ad35453cd1
Notes:
blender-bot
2023-02-14 06:19:41 +01:00
Referenced by issue #78008, Cycles: Light Group render passes improvements
|
@ -228,6 +228,10 @@ def list_render_passes(scene, srl):
|
|||
else:
|
||||
yield (aov.name, "RGB", 'COLOR')
|
||||
|
||||
# Light groups.
|
||||
for lightgroup in srl.lightgroups:
|
||||
yield ("Combined_%s" % lightgroup.name, "RGB", 'COLOR')
|
||||
|
||||
|
||||
def register_passes(engine, scene, view_layer):
|
||||
for name, channelids, channeltype in list_render_passes(scene, view_layer):
|
||||
|
|
|
@ -12,7 +12,7 @@ from bpy.types import Panel
|
|||
|
||||
from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel
|
||||
from bl_ui.properties_render import draw_hair_settings
|
||||
from bl_ui.properties_view_layer import ViewLayerCryptomattePanel, ViewLayerAOVPanel
|
||||
from bl_ui.properties_view_layer import ViewLayerCryptomattePanel, ViewLayerAOVPanel, ViewLayerLightgroupsPanel
|
||||
|
||||
class CyclesPresetPanel(PresetPanel, Panel):
|
||||
COMPAT_ENGINES = {'CYCLES'}
|
||||
|
@ -883,6 +883,12 @@ class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, ViewLayerAOVPanel):
|
|||
bl_parent_id = "CYCLES_RENDER_PT_passes"
|
||||
|
||||
|
||||
class CYCLES_RENDER_PT_passes_lightgroups(CyclesButtonsPanel, ViewLayerLightgroupsPanel):
|
||||
bl_label = "Light Groups"
|
||||
bl_context = "view_layer"
|
||||
bl_parent_id = "CYCLES_RENDER_PT_passes"
|
||||
|
||||
|
||||
class CYCLES_PT_post_processing(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Post Processing"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
@ -1147,6 +1153,23 @@ class CYCLES_OBJECT_PT_shading_caustics(CyclesButtonsPanel, Panel):
|
|||
col.prop(cob, "is_caustics_receiver")
|
||||
|
||||
|
||||
class CYCLES_OBJECT_PT_lightgroup(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Light Group"
|
||||
bl_parent_id = "CYCLES_OBJECT_PT_shading"
|
||||
bl_context = "object"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
ob = context.object
|
||||
|
||||
view_layer = context.view_layer
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop_search(ob, "lightgroup", view_layer, "lightgroups", text="Light Group")
|
||||
|
||||
|
||||
class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Visibility"
|
||||
bl_context = "object"
|
||||
|
@ -1399,10 +1422,14 @@ class CYCLES_WORLD_PT_surface(CyclesButtonsPanel, Panel):
|
|||
layout.use_property_split = True
|
||||
|
||||
world = context.world
|
||||
view_layer = context.view_layer
|
||||
|
||||
if not panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Surface'):
|
||||
layout.prop(world, "color")
|
||||
|
||||
col = layout.column(align=True)
|
||||
col.prop_search(world, "lightgroup", view_layer, "lightgroups", text="Light Group")
|
||||
|
||||
|
||||
class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Volume"
|
||||
|
@ -2209,6 +2236,7 @@ classes = (
|
|||
CYCLES_RENDER_PT_passes_light,
|
||||
CYCLES_RENDER_PT_passes_crypto,
|
||||
CYCLES_RENDER_PT_passes_aov,
|
||||
CYCLES_RENDER_PT_passes_lightgroups,
|
||||
CYCLES_RENDER_PT_filter,
|
||||
CYCLES_RENDER_PT_override,
|
||||
CYCLES_PT_post_processing,
|
||||
|
@ -2220,6 +2248,7 @@ classes = (
|
|||
CYCLES_OBJECT_PT_shading_shadow_terminator,
|
||||
CYCLES_OBJECT_PT_shading_gi_approximation,
|
||||
CYCLES_OBJECT_PT_shading_caustics,
|
||||
CYCLES_OBJECT_PT_lightgroup,
|
||||
CYCLES_OBJECT_PT_visibility,
|
||||
CYCLES_OBJECT_PT_visibility_ray_visibility,
|
||||
CYCLES_OBJECT_PT_visibility_culling,
|
||||
|
|
|
@ -143,6 +143,9 @@ 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());
|
||||
|
||||
/* lightgroup */
|
||||
light->set_lightgroup(ustring(b_ob_info.real_object.lightgroup()));
|
||||
|
||||
/* tag */
|
||||
light->tag_update(scene);
|
||||
}
|
||||
|
|
|
@ -343,6 +343,9 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph,
|
|||
object->set_random_id(hash_uint2(hash_string(object->name.c_str()), 0));
|
||||
}
|
||||
|
||||
/* lightgroup */
|
||||
object->set_lightgroup(ustring(b_ob.lightgroup()));
|
||||
|
||||
object->tag_update(scene);
|
||||
}
|
||||
|
||||
|
|
|
@ -1532,6 +1532,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d,
|
|||
background->set_use_shader(view_layer.use_background_shader ||
|
||||
viewport_parameters.use_custom_shader());
|
||||
|
||||
background->set_lightgroup(ustring(b_world ? b_world.lightgroup() : ""));
|
||||
|
||||
background->tag_update(scene);
|
||||
}
|
||||
|
||||
|
|
|
@ -745,6 +745,20 @@ void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_v
|
|||
}
|
||||
}
|
||||
|
||||
/* Light Group passes. */
|
||||
BL::ViewLayer::lightgroups_iterator b_lightgroup_iter;
|
||||
for (b_view_layer.lightgroups.begin(b_lightgroup_iter);
|
||||
b_lightgroup_iter != b_view_layer.lightgroups.end();
|
||||
++b_lightgroup_iter) {
|
||||
BL::Lightgroup b_lightgroup(*b_lightgroup_iter);
|
||||
|
||||
string name = string_printf("Combined_%s", b_lightgroup.name().c_str());
|
||||
|
||||
b_engine.add_pass(name.c_str(), 3, "RGB", b_view_layer.name().c_str());
|
||||
Pass *pass = pass_add(scene, PASS_COMBINED, name.c_str(), PassMode::NOISY);
|
||||
pass->set_lightgroup(ustring(b_lightgroup.name()));
|
||||
}
|
||||
|
||||
scene->film->set_pass_alpha_threshold(b_view_layer.pass_alpha_threshold());
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,11 @@ CCL_NAMESPACE_BEGIN
|
|||
*/
|
||||
|
||||
PassAccessor::PassAccessInfo::PassAccessInfo(const BufferPass &pass)
|
||||
: type(pass.type), mode(pass.mode), include_albedo(pass.include_albedo), offset(pass.offset)
|
||||
: type(pass.type),
|
||||
mode(pass.mode),
|
||||
include_albedo(pass.include_albedo),
|
||||
is_lightgroup(!pass.lightgroup.empty()),
|
||||
offset(pass.offset)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -127,7 +131,8 @@ bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers,
|
|||
|
||||
const PassType type = pass_access_info_.type;
|
||||
const PassMode mode = pass_access_info_.mode;
|
||||
const PassInfo pass_info = Pass::get_info(type, pass_access_info_.include_albedo);
|
||||
const PassInfo pass_info = Pass::get_info(
|
||||
type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup);
|
||||
int num_written_components = pass_info.num_components;
|
||||
|
||||
if (pass_info.num_components == 1) {
|
||||
|
@ -215,8 +220,8 @@ void PassAccessor::init_kernel_film_convert(KernelFilmConvert *kfilm_convert,
|
|||
const Destination &destination) const
|
||||
{
|
||||
const PassMode mode = pass_access_info_.mode;
|
||||
const PassInfo &pass_info = Pass::get_info(pass_access_info_.type,
|
||||
pass_access_info_.include_albedo);
|
||||
const PassInfo &pass_info = Pass::get_info(
|
||||
pass_access_info_.type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup);
|
||||
|
||||
kfilm_convert->pass_offset = pass_access_info_.offset;
|
||||
kfilm_convert->pass_stride = buffer_params.pass_stride;
|
||||
|
@ -279,8 +284,8 @@ bool PassAccessor::set_render_tile_pixels(RenderBuffers *render_buffers, const S
|
|||
return false;
|
||||
}
|
||||
|
||||
const PassInfo pass_info = Pass::get_info(pass_access_info_.type,
|
||||
pass_access_info_.include_albedo);
|
||||
const PassInfo pass_info = Pass::get_info(
|
||||
pass_access_info_.type, pass_access_info_.include_albedo, pass_access_info_.is_lightgroup);
|
||||
|
||||
const BufferParams &buffer_params = render_buffers->params;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ class PassAccessor {
|
|||
PassType type = PASS_NONE;
|
||||
PassMode mode = PassMode::NOISY;
|
||||
bool include_albedo = false;
|
||||
bool is_lightgroup = false;
|
||||
int offset = -1;
|
||||
|
||||
/* For the shadow catcher matte pass: whether to approximate shadow catcher pass into its
|
||||
|
|
|
@ -320,12 +320,13 @@ ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg,
|
|||
}
|
||||
|
||||
/* Write background or emission to appropriate pass. */
|
||||
ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
float3 contribution,
|
||||
ccl_global float *ccl_restrict
|
||||
buffer,
|
||||
const int pass)
|
||||
ccl_device_inline void kernel_accum_emission_or_background_pass(
|
||||
KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
float3 contribution,
|
||||
ccl_global float *ccl_restrict buffer,
|
||||
const int pass,
|
||||
const int lightgroup = LIGHTGROUP_NONE)
|
||||
{
|
||||
if (!(kernel_data.film.light_pass_flag & PASS_ANY)) {
|
||||
return;
|
||||
|
@ -351,52 +352,59 @@ ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg
|
|||
/* Directly visible, write to emission or background pass. */
|
||||
pass_offset = pass;
|
||||
}
|
||||
else if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
else {
|
||||
/* Don't write any light passes for shadow catcher, for easier
|
||||
* compositing back together of the combined pass. */
|
||||
if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (path_flag & PATH_RAY_SURFACE_PASS) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const float3 diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
||||
const float3 glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight);
|
||||
|
||||
/* Glossy */
|
||||
const int glossy_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_glossy_direct :
|
||||
kernel_data.film.pass_glossy_indirect);
|
||||
if (glossy_pass_offset != PASS_UNUSED) {
|
||||
kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_weight * contribution);
|
||||
}
|
||||
|
||||
/* Transmission */
|
||||
const int transmission_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_transmission_direct :
|
||||
kernel_data.film.pass_transmission_indirect);
|
||||
|
||||
if (transmission_pass_offset != PASS_UNUSED) {
|
||||
/* Transmission is what remains if not diffuse and glossy, not stored explicitly to save
|
||||
* GPU memory. */
|
||||
const float3 transmission_weight = one_float3() - diffuse_weight - glossy_weight;
|
||||
kernel_write_pass_float3(buffer + transmission_pass_offset,
|
||||
transmission_weight * contribution);
|
||||
}
|
||||
|
||||
/* Reconstruct diffuse subset of throughput. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_diffuse_direct :
|
||||
kernel_data.film.pass_diffuse_indirect;
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
contribution *= diffuse_weight;
|
||||
}
|
||||
if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) {
|
||||
kernel_write_pass_float3(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup,
|
||||
contribution);
|
||||
}
|
||||
else if (path_flag & PATH_RAY_VOLUME_PASS) {
|
||||
/* Indirectly visible through volume. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_volume_direct :
|
||||
kernel_data.film.pass_volume_indirect;
|
||||
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
if (path_flag & PATH_RAY_SURFACE_PASS) {
|
||||
/* Indirectly visible through reflection. */
|
||||
const float3 diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight);
|
||||
const float3 glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight);
|
||||
|
||||
/* Glossy */
|
||||
const int glossy_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_glossy_direct :
|
||||
kernel_data.film.pass_glossy_indirect);
|
||||
if (glossy_pass_offset != PASS_UNUSED) {
|
||||
kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_weight * contribution);
|
||||
}
|
||||
|
||||
/* Transmission */
|
||||
const int transmission_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_transmission_direct :
|
||||
kernel_data.film.pass_transmission_indirect);
|
||||
|
||||
if (transmission_pass_offset != PASS_UNUSED) {
|
||||
/* Transmission is what remains if not diffuse and glossy, not stored explicitly to save
|
||||
* GPU memory. */
|
||||
const float3 transmission_weight = one_float3() - diffuse_weight - glossy_weight;
|
||||
kernel_write_pass_float3(buffer + transmission_pass_offset,
|
||||
transmission_weight * contribution);
|
||||
}
|
||||
|
||||
/* Reconstruct diffuse subset of throughput. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_diffuse_direct :
|
||||
kernel_data.film.pass_diffuse_indirect;
|
||||
if (pass_offset != PASS_UNUSED) {
|
||||
contribution *= diffuse_weight;
|
||||
}
|
||||
}
|
||||
else if (path_flag & PATH_RAY_VOLUME_PASS) {
|
||||
/* Indirectly visible through volume. */
|
||||
pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ?
|
||||
kernel_data.film.pass_volume_direct :
|
||||
kernel_data.film.pass_volume_indirect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -449,6 +457,13 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg,
|
|||
return;
|
||||
}
|
||||
|
||||
/* Write lightgroup pass. LIGHTGROUP_NONE is ~0 so decode from unsigned to signed */
|
||||
const int lightgroup = (int)(INTEGRATOR_STATE(state, shadow_path, lightgroup)) - 1;
|
||||
if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) {
|
||||
kernel_write_pass_float3(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup,
|
||||
contribution);
|
||||
}
|
||||
|
||||
if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) {
|
||||
int pass_offset = PASS_UNUSED;
|
||||
|
||||
|
@ -566,15 +581,20 @@ ccl_device_inline void kernel_accum_background(KernelGlobals kg,
|
|||
kernel_accum_combined_transparent_pass(
|
||||
kg, path_flag, sample, contribution, transparent, buffer);
|
||||
}
|
||||
kernel_accum_emission_or_background_pass(
|
||||
kg, state, contribution, buffer, kernel_data.film.pass_background);
|
||||
kernel_accum_emission_or_background_pass(kg,
|
||||
state,
|
||||
contribution,
|
||||
buffer,
|
||||
kernel_data.film.pass_background,
|
||||
kernel_data.background.lightgroup);
|
||||
}
|
||||
|
||||
/* Write emission to render buffer. */
|
||||
ccl_device_inline void kernel_accum_emission(KernelGlobals kg,
|
||||
ConstIntegratorState state,
|
||||
const float3 L,
|
||||
ccl_global float *ccl_restrict render_buffer)
|
||||
ccl_global float *ccl_restrict render_buffer,
|
||||
const int lightgroup = LIGHTGROUP_NONE)
|
||||
{
|
||||
float3 contribution = L;
|
||||
kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1);
|
||||
|
@ -585,7 +605,7 @@ ccl_device_inline void kernel_accum_emission(KernelGlobals kg,
|
|||
|
||||
kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer);
|
||||
kernel_accum_emission_or_background_pass(
|
||||
kg, state, contribution, buffer, kernel_data.film.pass_emission);
|
||||
kg, state, contribution, buffer, kernel_data.film.pass_emission, lightgroup);
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
|
|
@ -283,6 +283,26 @@ ccl_device_inline float object_pass_id(KernelGlobals kg, int object)
|
|||
return kernel_tex_fetch(__objects, object).pass_id;
|
||||
}
|
||||
|
||||
/* Lightgroup of lamp */
|
||||
|
||||
ccl_device_inline int lamp_lightgroup(KernelGlobals kg, int lamp)
|
||||
{
|
||||
if (lamp == LAMP_NONE)
|
||||
return LIGHTGROUP_NONE;
|
||||
|
||||
return kernel_tex_fetch(__lights, lamp).lightgroup;
|
||||
}
|
||||
|
||||
/* Lightgroup of object */
|
||||
|
||||
ccl_device_inline int object_lightgroup(KernelGlobals kg, int object)
|
||||
{
|
||||
if (object == OBJECT_NONE)
|
||||
return LIGHTGROUP_NONE;
|
||||
|
||||
return kernel_tex_fetch(__objects, object).lightgroup;
|
||||
}
|
||||
|
||||
/* Per lamp random number for shader variation */
|
||||
|
||||
ccl_device_inline float lamp_random_number(KernelGlobals kg, int lamp)
|
||||
|
|
|
@ -186,7 +186,8 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg,
|
|||
|
||||
/* Write to render buffer. */
|
||||
const float3 throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
kernel_accum_emission(kg, state, throughput * light_eval, render_buffer);
|
||||
kernel_accum_emission(
|
||||
kg, state, throughput * light_eval, render_buffer, kernel_data.background.lightgroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg,
|
|||
|
||||
/* Write to render buffer. */
|
||||
const float3 throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
kernel_accum_emission(kg, state, throughput * light_eval, render_buffer);
|
||||
kernel_accum_emission(kg, state, throughput * light_eval, render_buffer, ls.group);
|
||||
}
|
||||
|
||||
ccl_device void integrator_shade_light(KernelGlobals kg,
|
||||
|
|
|
@ -87,7 +87,8 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg,
|
|||
}
|
||||
|
||||
const float3 throughput = INTEGRATOR_STATE(state, path, throughput);
|
||||
kernel_accum_emission(kg, state, throughput * L, render_buffer);
|
||||
kernel_accum_emission(
|
||||
kg, state, throughput * L, render_buffer, object_lightgroup(kg, sd->object));
|
||||
}
|
||||
#endif /* __EMISSION__ */
|
||||
|
||||
|
@ -258,6 +259,12 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
|
|||
if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) {
|
||||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = throughput;
|
||||
}
|
||||
|
||||
/* Write Lightgroup, +1 as lightgroup is int but we need to encode into a uint8_t. */
|
||||
INTEGRATOR_STATE_WRITE(
|
||||
shadow_state, shadow_path, lightgroup) = (ls.type != LIGHT_BACKGROUND) ?
|
||||
ls.group + 1 :
|
||||
kernel_data.background.lightgroup + 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -653,7 +653,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
|
|||
|
||||
/* Write accumulated emission. */
|
||||
if (!is_zero(accum_emission)) {
|
||||
kernel_accum_emission(kg, state, accum_emission, render_buffer);
|
||||
kernel_accum_emission(
|
||||
kg, state, accum_emission, render_buffer, object_lightgroup(kg, sd->object));
|
||||
}
|
||||
|
||||
# ifdef __DENOISING_FEATURES__
|
||||
|
@ -833,6 +834,12 @@ ccl_device_forceinline void integrate_volume_direct_light(
|
|||
INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = throughput;
|
||||
}
|
||||
|
||||
/* Write Lightgroup, +1 as lightgroup is int but we need to encode into a uint8_t. */
|
||||
INTEGRATOR_STATE_WRITE(
|
||||
shadow_state, shadow_path, lightgroup) = (ls->type != LIGHT_BACKGROUND) ?
|
||||
ls->group + 1 :
|
||||
kernel_data.background.lightgroup + 1;
|
||||
|
||||
integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state);
|
||||
}
|
||||
# endif
|
||||
|
|
|
@ -38,6 +38,8 @@ KERNEL_STRUCT_MEMBER(shadow_path, packed_float3, pass_diffuse_weight, KERNEL_FEA
|
|||
KERNEL_STRUCT_MEMBER(shadow_path, packed_float3, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES)
|
||||
/* Number of intersections found by ray-tracing. */
|
||||
KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING)
|
||||
/* Light group. */
|
||||
KERNEL_STRUCT_MEMBER(shadow_path, uint8_t, lightgroup, KERNEL_FEATURE_PATH_TRACING)
|
||||
KERNEL_STRUCT_END(shadow_path)
|
||||
|
||||
/********************************** Shadow Ray *******************************/
|
||||
|
|
|
@ -23,6 +23,7 @@ typedef struct LightSample {
|
|||
int prim; /* primitive id for triangle/curve lights */
|
||||
int shader; /* shader id */
|
||||
int lamp; /* lamp id */
|
||||
int group; /* lightgroup */
|
||||
LightType type; /* type of light */
|
||||
} LightSample;
|
||||
|
||||
|
@ -52,6 +53,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
|
|||
ls->lamp = lamp;
|
||||
ls->u = randu;
|
||||
ls->v = randv;
|
||||
ls->group = lamp_lightgroup(kg, lamp);
|
||||
|
||||
if (in_volume_segment && (type == LIGHT_DISTANT || type == LIGHT_BACKGROUND)) {
|
||||
/* Distant lights in a volume get a dummy sample, position will not actually
|
||||
|
@ -413,6 +415,7 @@ ccl_device bool light_sample_from_distant_ray(KernelGlobals kg,
|
|||
ls->P = -ray_D;
|
||||
ls->Ng = -ray_D;
|
||||
ls->D = ray_D;
|
||||
ls->group = lamp_lightgroup(kg, lamp);
|
||||
|
||||
/* compute pdf */
|
||||
float invarea = klight->distant.invarea;
|
||||
|
@ -441,6 +444,7 @@ ccl_device bool light_sample_from_intersection(KernelGlobals kg,
|
|||
ls->t = isect->t;
|
||||
ls->P = ray_P + ray_D * ls->t;
|
||||
ls->D = ray_D;
|
||||
ls->group = lamp_lightgroup(kg, lamp);
|
||||
|
||||
if (type == LIGHT_SPOT) {
|
||||
const float3 center = make_float3(klight->co[0], klight->co[1], klight->co[2]);
|
||||
|
@ -706,6 +710,7 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals kg,
|
|||
ls->lamp = LAMP_NONE;
|
||||
ls->shader |= SHADER_USE_MIS;
|
||||
ls->type = LIGHT_TRIANGLE;
|
||||
ls->group = object_lightgroup(kg, object);
|
||||
|
||||
float distance_to_plane = fabsf(dot(N0, V[0] - P) / dot(N0, N0));
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ CCL_NAMESPACE_BEGIN
|
|||
#define LAMP_NONE (~0)
|
||||
#define ID_NONE (0.0f)
|
||||
#define PASS_UNUSED (~0)
|
||||
#define LIGHTGROUP_NONE (~0)
|
||||
|
||||
#define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024U
|
||||
#define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4U
|
||||
|
@ -1108,6 +1109,7 @@ typedef struct KernelFilm {
|
|||
|
||||
int pass_aov_color;
|
||||
int pass_aov_value;
|
||||
int pass_lightgroup;
|
||||
|
||||
/* XYZ to rendering color space transform. float4 instead of float3 to
|
||||
* ensure consistent padding/alignment across devices. */
|
||||
|
@ -1192,8 +1194,10 @@ typedef struct KernelBackground {
|
|||
|
||||
int use_mis;
|
||||
|
||||
int lightgroup;
|
||||
|
||||
/* Padding */
|
||||
int pad1, pad2, pad3;
|
||||
int pad1, pad2;
|
||||
} KernelBackground;
|
||||
static_assert_align(KernelBackground, 16);
|
||||
|
||||
|
@ -1372,9 +1376,12 @@ typedef struct KernelObject {
|
|||
|
||||
float ao_distance;
|
||||
|
||||
int lightgroup;
|
||||
|
||||
uint visibility;
|
||||
int primitive_type;
|
||||
int pad[2];
|
||||
|
||||
int pad1;
|
||||
} KernelObject;
|
||||
static_assert_align(KernelObject, 16);
|
||||
|
||||
|
@ -1427,7 +1434,7 @@ typedef struct KernelLight {
|
|||
float random;
|
||||
float strength[3];
|
||||
int use_caustics;
|
||||
float pad1;
|
||||
int lightgroup;
|
||||
Transform tfm;
|
||||
Transform itfm;
|
||||
union {
|
||||
|
|
|
@ -32,6 +32,8 @@ NODE_DEFINE(Background)
|
|||
|
||||
SOCKET_NODE(shader, "Shader", Shader::get_node_type());
|
||||
|
||||
SOCKET_STRING(lightgroup, "Light Group", ustring());
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -101,6 +103,15 @@ void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene
|
|||
kbackground->surface_shader |= SHADER_EXCLUDE_CAMERA;
|
||||
}
|
||||
|
||||
/* Light group. */
|
||||
auto it = scene->lightgroups.find(lightgroup);
|
||||
if (it != scene->lightgroups.end()) {
|
||||
kbackground->lightgroup = it->second;
|
||||
}
|
||||
else {
|
||||
kbackground->lightgroup = LIGHTGROUP_NONE;
|
||||
}
|
||||
|
||||
clear_modified();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ class Background : public Node {
|
|||
|
||||
NODE_SOCKET_API(float, volume_step_size)
|
||||
|
||||
NODE_SOCKET_API(ustring, lightgroup)
|
||||
|
||||
Background();
|
||||
~Background();
|
||||
|
||||
|
|
|
@ -175,6 +175,7 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
|
|||
kfilm->pass_volume_direct = PASS_UNUSED;
|
||||
kfilm->pass_volume_indirect = PASS_UNUSED;
|
||||
kfilm->pass_shadow = PASS_UNUSED;
|
||||
kfilm->pass_lightgroup = PASS_UNUSED;
|
||||
|
||||
/* Mark passes as unused so that the kernel knows the pass is inaccessible. */
|
||||
kfilm->pass_denoising_normal = PASS_UNUSED;
|
||||
|
@ -189,6 +190,7 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
|
|||
bool have_cryptomatte = false;
|
||||
bool have_aov_color = false;
|
||||
bool have_aov_value = false;
|
||||
bool have_lightgroup = false;
|
||||
|
||||
for (size_t i = 0; i < scene->passes.size(); i++) {
|
||||
const Pass *pass = scene->passes[i];
|
||||
|
@ -223,6 +225,15 @@ void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene)
|
|||
assert(pass->get_type() <= PASS_CATEGORY_BAKE_END);
|
||||
}
|
||||
|
||||
if (pass->get_lightgroup() != ustring()) {
|
||||
if (!have_lightgroup) {
|
||||
kfilm->pass_lightgroup = kfilm->pass_stride;
|
||||
have_lightgroup = true;
|
||||
}
|
||||
kfilm->pass_stride += pass->get_info().num_components;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (pass->get_type()) {
|
||||
case PASS_COMBINED:
|
||||
kfilm->pass_combined = kfilm->pass_stride;
|
||||
|
@ -414,6 +425,26 @@ int Film::get_aov_offset(Scene *scene, string name, bool &is_color)
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool Film::update_lightgroups(Scene *scene)
|
||||
{
|
||||
map<ustring, int> lightgroups;
|
||||
int i = 0;
|
||||
foreach (const Pass *pass, scene->passes) {
|
||||
ustring lightgroup = pass->get_lightgroup();
|
||||
if (!lightgroup.empty()) {
|
||||
if (!lightgroups.count(lightgroup)) {
|
||||
lightgroups[lightgroup] = i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (scene->lightgroups != lightgroups) {
|
||||
scene->lightgroups = lightgroups;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Film::update_passes(Scene *scene, bool add_sample_count_pass)
|
||||
{
|
||||
const Background *background = scene->background;
|
||||
|
@ -580,11 +611,19 @@ void Film::remove_auto_passes(Scene *scene)
|
|||
|
||||
static bool compare_pass_order(const Pass *a, const Pass *b)
|
||||
{
|
||||
/* On the highest level, sort by number of components.
|
||||
* Within passes of the same component count, sort so that all non-lightgroup passes come first.
|
||||
* Within that group, sort by type. */
|
||||
const int num_components_a = a->get_info().num_components;
|
||||
const int num_components_b = b->get_info().num_components;
|
||||
|
||||
if (num_components_a == num_components_b) {
|
||||
return (a->get_type() < b->get_type());
|
||||
const int is_lightgroup_a = !a->get_lightgroup().empty();
|
||||
const int is_lightgroup_b = !b->get_lightgroup().empty();
|
||||
if (is_lightgroup_a == is_lightgroup_b) {
|
||||
return (a->get_type() < b->get_type());
|
||||
}
|
||||
return is_lightgroup_b;
|
||||
}
|
||||
|
||||
return num_components_a > num_components_b;
|
||||
|
|
|
@ -68,6 +68,8 @@ class Film : public Node {
|
|||
|
||||
int get_aov_offset(Scene *scene, string name, bool &is_color);
|
||||
|
||||
bool update_lightgroups(Scene *scene);
|
||||
|
||||
/* Update passes so that they contain all passes required for the configured functionality.
|
||||
*
|
||||
* If `add_sample_count_pass` is true then the SAMPLE_COUNT pass is ensured to be added. */
|
||||
|
|
|
@ -134,6 +134,8 @@ NODE_DEFINE(Light)
|
|||
|
||||
SOCKET_NODE(shader, "Shader", Shader::get_node_type());
|
||||
|
||||
SOCKET_STRING(lightgroup, "Light Group", ustring());
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -902,6 +904,14 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc
|
|||
klights[light_index].tfm = light->tfm;
|
||||
klights[light_index].itfm = transform_inverse(light->tfm);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
light_index++;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,8 @@ class Light : public Node {
|
|||
NODE_SOCKET_API(int, max_bounces)
|
||||
NODE_SOCKET_API(uint, random_id)
|
||||
|
||||
NODE_SOCKET_API(ustring, lightgroup)
|
||||
|
||||
void tag_update(Scene *scene);
|
||||
|
||||
/* Check whether the light has contribution the scene. */
|
||||
|
|
|
@ -98,6 +98,8 @@ NODE_DEFINE(Object)
|
|||
|
||||
SOCKET_FLOAT(ao_distance, "AO Distance", 0.0f);
|
||||
|
||||
SOCKET_STRING(lightgroup, "Light Group", ustring());
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -393,7 +395,8 @@ static float object_volume_density(const Transform &tfm, Geometry *geom)
|
|||
|
||||
void ObjectManager::device_update_object_transform(UpdateObjectTransformState *state,
|
||||
Object *ob,
|
||||
bool update_all)
|
||||
bool update_all,
|
||||
const Scene *scene)
|
||||
{
|
||||
KernelObject &kobject = state->objects[ob->index];
|
||||
Transform *object_motion_pass = state->object_motion_pass;
|
||||
|
@ -532,6 +535,15 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s
|
|||
if (geom->geometry_type == Geometry::HAIR) {
|
||||
state->have_curves = true;
|
||||
}
|
||||
|
||||
/* Light group. */
|
||||
auto it = scene->lightgroups.find(ob->lightgroup);
|
||||
if (it != scene->lightgroups.end()) {
|
||||
kobject.lightgroup = it->second;
|
||||
}
|
||||
else {
|
||||
kobject.lightgroup = LIGHTGROUP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectManager::device_update_prim_offsets(Device *device, DeviceScene *dscene, Scene *scene)
|
||||
|
@ -618,7 +630,7 @@ void ObjectManager::device_update_transforms(DeviceScene *dscene, Scene *scene,
|
|||
[&](const blocked_range<size_t> &r) {
|
||||
for (size_t i = r.begin(); i != r.end(); i++) {
|
||||
Object *ob = state.scene->objects[i];
|
||||
device_update_object_transform(&state, ob, update_all);
|
||||
device_update_object_transform(&state, ob, update_all, scene);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -66,6 +66,8 @@ class Object : public Node {
|
|||
|
||||
NODE_SOCKET_API(float, ao_distance)
|
||||
|
||||
NODE_SOCKET_API(ustring, lightgroup)
|
||||
|
||||
/* Set during device update. */
|
||||
bool intersects_volume;
|
||||
|
||||
|
@ -169,7 +171,8 @@ class ObjectManager {
|
|||
protected:
|
||||
void device_update_object_transform(UpdateObjectTransformState *state,
|
||||
Object *ob,
|
||||
bool update_all);
|
||||
bool update_all,
|
||||
const Scene *scene);
|
||||
void device_update_object_transform_task(UpdateObjectTransformState *state);
|
||||
bool device_update_object_transform_pop_work(UpdateObjectTransformState *state,
|
||||
int *start_index,
|
||||
|
|
|
@ -124,6 +124,7 @@ NODE_DEFINE(Pass)
|
|||
SOCKET_ENUM(mode, "Mode", *pass_mode_enum, static_cast<int>(PassMode::DENOISED));
|
||||
SOCKET_STRING(name, "Name", ustring());
|
||||
SOCKET_BOOLEAN(include_albedo, "Include Albedo", false);
|
||||
SOCKET_STRING(lightgroup, "Light Group", ustring());
|
||||
|
||||
return type;
|
||||
}
|
||||
|
@ -134,7 +135,7 @@ Pass::Pass() : Node(get_node_type()), is_auto_(false)
|
|||
|
||||
PassInfo Pass::get_info() const
|
||||
{
|
||||
return get_info(type, include_albedo);
|
||||
return get_info(type, include_albedo, !lightgroup.empty());
|
||||
}
|
||||
|
||||
bool Pass::is_written() const
|
||||
|
@ -142,7 +143,7 @@ bool Pass::is_written() const
|
|||
return get_info().is_written;
|
||||
}
|
||||
|
||||
PassInfo Pass::get_info(const PassType type, const bool include_albedo)
|
||||
PassInfo Pass::get_info(const PassType type, const bool include_albedo, const bool is_lightgroup)
|
||||
{
|
||||
PassInfo pass_info;
|
||||
|
||||
|
@ -157,9 +158,9 @@ PassInfo Pass::get_info(const PassType type, const bool include_albedo)
|
|||
pass_info.num_components = 0;
|
||||
break;
|
||||
case PASS_COMBINED:
|
||||
pass_info.num_components = 4;
|
||||
pass_info.num_components = is_lightgroup ? 3 : 4;
|
||||
pass_info.use_exposure = true;
|
||||
pass_info.support_denoise = true;
|
||||
pass_info.support_denoise = !is_lightgroup;
|
||||
break;
|
||||
case PASS_DEPTH:
|
||||
pass_info.num_components = 1;
|
||||
|
@ -369,13 +370,16 @@ const Pass *Pass::find(const vector<Pass *> &passes, const string &name)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const Pass *Pass::find(const vector<Pass *> &passes, PassType type, PassMode mode)
|
||||
const Pass *Pass::find(const vector<Pass *> &passes,
|
||||
PassType type,
|
||||
PassMode mode,
|
||||
const ustring &lightgroup)
|
||||
{
|
||||
for (const Pass *pass : passes) {
|
||||
if (pass->get_type() != type || pass->get_mode() != mode) {
|
||||
if (pass->get_type() != type || pass->get_mode() != mode ||
|
||||
pass->get_lightgroup() != lightgroup) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ class Pass : public Node {
|
|||
NODE_SOCKET_API(PassMode, mode)
|
||||
NODE_SOCKET_API(ustring, name)
|
||||
NODE_SOCKET_API(bool, include_albedo)
|
||||
NODE_SOCKET_API(ustring, lightgroup)
|
||||
|
||||
Pass();
|
||||
|
||||
|
@ -72,7 +73,9 @@ class Pass : public Node {
|
|||
static const NodeEnum *get_type_enum();
|
||||
static const NodeEnum *get_mode_enum();
|
||||
|
||||
static PassInfo get_info(PassType type, const bool include_albedo = false);
|
||||
static PassInfo get_info(PassType type,
|
||||
const bool include_albedo = false,
|
||||
const bool is_lightgroup = false);
|
||||
|
||||
static bool contains(const vector<Pass *> &passes, PassType type);
|
||||
|
||||
|
@ -80,7 +83,8 @@ class Pass : public Node {
|
|||
static const Pass *find(const vector<Pass *> &passes, const string &name);
|
||||
static const Pass *find(const vector<Pass *> &passes,
|
||||
PassType type,
|
||||
PassMode mode = PassMode::NOISY);
|
||||
PassMode mode = PassMode::NOISY,
|
||||
const ustring &lightgroup = ustring());
|
||||
|
||||
/* Returns PASS_UNUSED if there is no corresponding pass. */
|
||||
static int get_offset(const vector<Pass *> &passes, const Pass *pass);
|
||||
|
|
|
@ -251,6 +251,11 @@ void Scene::device_update(Device *device_, Progress &progress)
|
|||
* - Lookup tables are done a second time to handle film tables
|
||||
*/
|
||||
|
||||
if (film->update_lightgroups(this)) {
|
||||
light_manager->tag_update(this, ccl::LightManager::LIGHT_MODIFIED);
|
||||
object_manager->tag_update(this, ccl::ObjectManager::OBJECT_MODIFIED);
|
||||
}
|
||||
|
||||
progress.set_status("Updating Shaders");
|
||||
shader_manager->device_update(device, &dscene, this, progress);
|
||||
|
||||
|
|
|
@ -197,6 +197,9 @@ class Scene : public NodeOwner {
|
|||
/* Optional name. Is used for logging and reporting. */
|
||||
string name;
|
||||
|
||||
/* Maps from Light group names to their pass ID. */
|
||||
map<ustring, int> lightgroups;
|
||||
|
||||
/* data */
|
||||
BVH *bvh;
|
||||
Camera *camera;
|
||||
|
|
|
@ -49,6 +49,7 @@ NODE_DEFINE(BufferPass)
|
|||
SOCKET_ENUM(mode, "Mode", *pass_mode_enum, static_cast<int>(PassMode::DENOISED));
|
||||
SOCKET_STRING(name, "Name", ustring());
|
||||
SOCKET_BOOLEAN(include_albedo, "Include Albedo", false);
|
||||
SOCKET_STRING(lightgroup, "Light Group", ustring());
|
||||
|
||||
SOCKET_INT(offset, "Offset", -1);
|
||||
|
||||
|
@ -64,13 +65,14 @@ BufferPass::BufferPass(const Pass *scene_pass)
|
|||
type(scene_pass->get_type()),
|
||||
mode(scene_pass->get_mode()),
|
||||
name(scene_pass->get_name()),
|
||||
include_albedo(scene_pass->get_include_albedo())
|
||||
include_albedo(scene_pass->get_include_albedo()),
|
||||
lightgroup(scene_pass->get_lightgroup())
|
||||
{
|
||||
}
|
||||
|
||||
PassInfo BufferPass::get_info() const
|
||||
{
|
||||
return Pass::get_info(type, include_albedo);
|
||||
return Pass::get_info(type, include_albedo, !lightgroup.empty());
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
|
|
|
@ -30,6 +30,7 @@ class BufferPass : public Node {
|
|||
PassMode mode = PassMode::NOISY;
|
||||
ustring name;
|
||||
bool include_albedo = false;
|
||||
ustring lightgroup;
|
||||
|
||||
int offset = -1;
|
||||
|
||||
|
@ -49,7 +50,8 @@ class BufferPass : public Node {
|
|||
inline bool operator==(const BufferPass &other) const
|
||||
{
|
||||
return type == other.type && mode == other.mode && name == other.name &&
|
||||
include_albedo == other.include_albedo && offset == other.offset;
|
||||
include_albedo == other.include_albedo && lightgroup == other.lightgroup &&
|
||||
offset == other.offset;
|
||||
}
|
||||
inline bool operator!=(const BufferPass &other) const
|
||||
{
|
||||
|
|
|
@ -187,6 +187,33 @@ class VIEWLAYER_PT_layer_passes_cryptomatte(ViewLayerCryptomattePanel, Panel):
|
|||
COMPAT_ENGINES = {'BLENDER_EEVEE'}
|
||||
|
||||
|
||||
class ViewLayerLightgroupsPanel(ViewLayerButtonsPanel, Panel):
|
||||
bl_label = "Light Groups"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
view_layer = context.view_layer
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.template_list("UI_UL_list", "lightgroups", view_layer,
|
||||
"lightgroups", view_layer, "active_lightgroup_index", rows=2)
|
||||
|
||||
col = row.column()
|
||||
sub = col.column(align=True)
|
||||
sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="")
|
||||
sub.operator("scene.view_layer_remove_lightgroup", icon='REMOVE', text="")
|
||||
|
||||
|
||||
class VIEWLAYER_PT_layer_passes_lightgroups(ViewLayerLightgroupsPanel):
|
||||
bl_parent_id = "VIEWLAYER_PT_layer_passes"
|
||||
COMPAT_ENGINES = {'CYCLES'}
|
||||
|
||||
|
||||
classes = (
|
||||
VIEWLAYER_PT_layer,
|
||||
VIEWLAYER_PT_layer_passes,
|
||||
|
@ -195,6 +222,7 @@ classes = (
|
|||
VIEWLAYER_PT_eevee_layer_passes_effects,
|
||||
VIEWLAYER_PT_layer_passes_cryptomatte,
|
||||
VIEWLAYER_PT_layer_passes_aov,
|
||||
VIEWLAYER_PT_layer_passes_lightgroups,
|
||||
VIEWLAYER_UL_aov,
|
||||
)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "BKE_collection.h"
|
||||
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -581,6 +582,21 @@ bool BKE_view_layer_has_valid_aov(struct ViewLayer *view_layer);
|
|||
struct ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene,
|
||||
struct ViewLayerAOV *view_layer_aov);
|
||||
|
||||
struct ViewLayerLightgroup *BKE_view_layer_add_lightgroup(struct ViewLayer *view_layer);
|
||||
void BKE_view_layer_remove_lightgroup(struct ViewLayer *view_layer,
|
||||
struct ViewLayerLightgroup *lightgroup);
|
||||
void BKE_view_layer_set_active_lightgroup(struct ViewLayer *view_layer,
|
||||
struct ViewLayerLightgroup *lightgroup);
|
||||
struct ViewLayer *BKE_view_layer_find_with_lightgroup(
|
||||
struct Scene *scene, struct ViewLayerLightgroup *view_layer_lightgroup);
|
||||
void BKE_view_layer_rename_lightgroup(ViewLayer *view_layer,
|
||||
ViewLayerLightgroup *lightgroup,
|
||||
const char *name);
|
||||
|
||||
void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *value);
|
||||
int BKE_lightgroup_membership_length(struct LightgroupMembership *lgm);
|
||||
void BKE_lightgroup_membership_set(struct LightgroupMembership **lgm, const char *value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -266,6 +266,8 @@ void BKE_view_layer_free_ex(ViewLayer *view_layer, const bool do_id_user)
|
|||
BLI_freelistN(&view_layer->drawdata);
|
||||
BLI_freelistN(&view_layer->aovs);
|
||||
view_layer->active_aov = NULL;
|
||||
BLI_freelistN(&view_layer->lightgroups);
|
||||
view_layer->active_lightgroup = NULL;
|
||||
|
||||
MEM_SAFE_FREE(view_layer->stats);
|
||||
|
||||
|
@ -428,6 +430,29 @@ static void layer_aov_copy_data(ViewLayer *view_layer_dst,
|
|||
}
|
||||
}
|
||||
|
||||
static void layer_lightgroup_copy_data(ViewLayer *view_layer_dst,
|
||||
const ViewLayer *view_layer_src,
|
||||
ListBase *lightgroups_dst,
|
||||
const ListBase *lightgroups_src)
|
||||
{
|
||||
if (lightgroups_src != NULL) {
|
||||
BLI_duplicatelist(lightgroups_dst, lightgroups_src);
|
||||
}
|
||||
|
||||
ViewLayerLightgroup *lightgroup_dst = lightgroups_dst->first;
|
||||
const ViewLayerLightgroup *lightgroup_src = lightgroups_src->first;
|
||||
|
||||
while (lightgroup_dst != NULL) {
|
||||
BLI_assert(lightgroup_src);
|
||||
if (lightgroup_src == view_layer_src->active_lightgroup) {
|
||||
view_layer_dst->active_lightgroup = lightgroup_dst;
|
||||
}
|
||||
|
||||
lightgroup_dst = lightgroup_dst->next;
|
||||
lightgroup_src = lightgroup_src->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void layer_collections_copy_data(ViewLayer *view_layer_dst,
|
||||
const ViewLayer *view_layer_src,
|
||||
ListBase *layer_collections_dst,
|
||||
|
@ -496,6 +521,10 @@ void BKE_view_layer_copy_data(Scene *scene_dst,
|
|||
layer_aov_copy_data(
|
||||
view_layer_dst, view_layer_src, &view_layer_dst->aovs, &view_layer_src->aovs);
|
||||
|
||||
BLI_listbase_clear(&view_layer_dst->lightgroups);
|
||||
layer_lightgroup_copy_data(
|
||||
view_layer_dst, view_layer_src, &view_layer_dst->lightgroups, &view_layer_src->lightgroups);
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
||||
id_us_plus((ID *)view_layer_dst->mat_override);
|
||||
}
|
||||
|
@ -2256,6 +2285,9 @@ void BKE_view_layer_blend_write(BlendWriter *writer, ViewLayer *view_layer)
|
|||
LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
|
||||
BLO_write_struct(writer, ViewLayerAOV, aov);
|
||||
}
|
||||
LISTBASE_FOREACH (ViewLayerLightgroup *, lightgroup, &view_layer->lightgroups) {
|
||||
BLO_write_struct(writer, ViewLayerLightgroup, lightgroup);
|
||||
}
|
||||
write_layer_collections(writer, &view_layer->layer_collections);
|
||||
}
|
||||
|
||||
|
@ -2294,6 +2326,9 @@ void BKE_view_layer_blend_read_data(BlendDataReader *reader, ViewLayer *view_lay
|
|||
BLO_read_list(reader, &view_layer->aovs);
|
||||
BLO_read_data_address(reader, &view_layer->active_aov);
|
||||
|
||||
BLO_read_list(reader, &view_layer->lightgroups);
|
||||
BLO_read_data_address(reader, &view_layer->active_lightgroup);
|
||||
|
||||
BLI_listbase_clear(&view_layer->drawdata);
|
||||
view_layer->object_bases_array = NULL;
|
||||
view_layer->object_bases_hash = NULL;
|
||||
|
@ -2471,4 +2506,117 @@ ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene, struct ViewLayerAOV
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Groups
|
||||
* \{ */
|
||||
|
||||
static void viewlayer_lightgroup_make_name_unique(ViewLayer *view_layer,
|
||||
ViewLayerLightgroup *lightgroup)
|
||||
{
|
||||
/* Don't allow dots, it's incompatible with OpenEXR convention to store channels
|
||||
* as "layer.pass.channel". */
|
||||
BLI_str_replace_char(lightgroup->name, '.', '_');
|
||||
BLI_uniquename(&view_layer->lightgroups,
|
||||
lightgroup,
|
||||
DATA_("Lightgroup"),
|
||||
'_',
|
||||
offsetof(ViewLayerLightgroup, name),
|
||||
sizeof(lightgroup->name));
|
||||
}
|
||||
|
||||
static void viewlayer_lightgroup_active_set(ViewLayer *view_layer, ViewLayerLightgroup *lightgroup)
|
||||
{
|
||||
if (lightgroup != NULL) {
|
||||
BLI_assert(BLI_findindex(&view_layer->lightgroups, lightgroup) != -1);
|
||||
view_layer->active_lightgroup = lightgroup;
|
||||
}
|
||||
else {
|
||||
view_layer->active_lightgroup = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct ViewLayerLightgroup *BKE_view_layer_add_lightgroup(struct ViewLayer *view_layer)
|
||||
{
|
||||
ViewLayerLightgroup *lightgroup;
|
||||
lightgroup = MEM_callocN(sizeof(ViewLayerLightgroup), __func__);
|
||||
BLI_strncpy(lightgroup->name, DATA_("Lightgroup"), sizeof(lightgroup->name));
|
||||
BLI_addtail(&view_layer->lightgroups, lightgroup);
|
||||
viewlayer_lightgroup_active_set(view_layer, lightgroup);
|
||||
viewlayer_lightgroup_make_name_unique(view_layer, lightgroup);
|
||||
return lightgroup;
|
||||
}
|
||||
|
||||
void BKE_view_layer_remove_lightgroup(ViewLayer *view_layer, ViewLayerLightgroup *lightgroup)
|
||||
{
|
||||
BLI_assert(BLI_findindex(&view_layer->lightgroups, lightgroup) != -1);
|
||||
BLI_assert(lightgroup != NULL);
|
||||
if (view_layer->active_lightgroup == lightgroup) {
|
||||
if (lightgroup->next) {
|
||||
viewlayer_lightgroup_active_set(view_layer, lightgroup->next);
|
||||
}
|
||||
else {
|
||||
viewlayer_lightgroup_active_set(view_layer, lightgroup->prev);
|
||||
}
|
||||
}
|
||||
BLI_freelinkN(&view_layer->lightgroups, lightgroup);
|
||||
}
|
||||
|
||||
void BKE_view_layer_set_active_lightgroup(ViewLayer *view_layer, ViewLayerLightgroup *lightgroup)
|
||||
{
|
||||
viewlayer_lightgroup_active_set(view_layer, lightgroup);
|
||||
}
|
||||
|
||||
ViewLayer *BKE_view_layer_find_with_lightgroup(struct Scene *scene,
|
||||
struct ViewLayerLightgroup *lightgroup)
|
||||
{
|
||||
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
|
||||
if (BLI_findindex(&view_layer->lightgroups, lightgroup) != -1) {
|
||||
return view_layer;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void BKE_view_layer_rename_lightgroup(ViewLayer *view_layer,
|
||||
ViewLayerLightgroup *lightgroup,
|
||||
const char *name)
|
||||
{
|
||||
BLI_strncpy_utf8(lightgroup->name, name, sizeof(lightgroup->name));
|
||||
viewlayer_lightgroup_make_name_unique(view_layer, lightgroup);
|
||||
}
|
||||
|
||||
void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *name)
|
||||
{
|
||||
if (lgm != NULL) {
|
||||
BLI_strncpy(name, lgm->name, sizeof(lgm->name));
|
||||
}
|
||||
else {
|
||||
name[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
int BKE_lightgroup_membership_length(struct LightgroupMembership *lgm)
|
||||
{
|
||||
if (lgm != NULL) {
|
||||
return strlen(lgm->name);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BKE_lightgroup_membership_set(struct LightgroupMembership **lgm, const char *name)
|
||||
{
|
||||
if (name[0] != '\0') {
|
||||
if (*lgm == NULL) {
|
||||
*lgm = MEM_callocN(sizeof(LightgroupMembership), __func__);
|
||||
}
|
||||
BLI_strncpy((*lgm)->name, name, sizeof((*lgm)->name));
|
||||
}
|
||||
else {
|
||||
if (*lgm != NULL) {
|
||||
MEM_freeN(*lgm);
|
||||
*lgm = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -260,6 +260,10 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in
|
|||
else {
|
||||
ob_dst->preview = nullptr;
|
||||
}
|
||||
|
||||
if (ob_src->lightgroup) {
|
||||
ob_dst->lightgroup = (LightgroupMembership *)MEM_dupallocN(ob_src->lightgroup);
|
||||
}
|
||||
}
|
||||
|
||||
static void object_free_data(ID *id)
|
||||
|
@ -310,6 +314,8 @@ static void object_free_data(ID *id)
|
|||
}
|
||||
|
||||
BKE_previewimg_free(&ob->preview);
|
||||
|
||||
MEM_SAFE_FREE(ob->lightgroup);
|
||||
}
|
||||
|
||||
static void library_foreach_modifiersForeachIDLink(void *user_data,
|
||||
|
@ -584,6 +590,10 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre
|
|||
BLO_write_struct_list(writer, LinkData, &ob->pc_ids);
|
||||
|
||||
BKE_previewimg_blend_write(writer, ob->preview);
|
||||
|
||||
if (ob->lightgroup) {
|
||||
BLO_write_struct(writer, LightgroupMembership, ob->lightgroup);
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX deprecated - old animation system */
|
||||
|
@ -800,6 +810,8 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id)
|
|||
|
||||
BLO_read_data_address(reader, &ob->preview);
|
||||
BKE_previewimg_blend_read(reader, ob->preview);
|
||||
|
||||
BLO_read_data_address(reader, &ob->lightgroup);
|
||||
}
|
||||
|
||||
/* XXX deprecated - old animation system */
|
||||
|
|
|
@ -59,6 +59,8 @@ static void world_free_data(ID *id)
|
|||
|
||||
BKE_icon_id_delete((struct ID *)wrld);
|
||||
BKE_previewimg_free(&wrld->preview);
|
||||
|
||||
MEM_SAFE_FREE(wrld->lightgroup);
|
||||
}
|
||||
|
||||
static void world_init_data(ID *id)
|
||||
|
@ -107,6 +109,10 @@ static void world_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
|
|||
else {
|
||||
wrld_dst->preview = NULL;
|
||||
}
|
||||
|
||||
if (wrld_src->lightgroup) {
|
||||
wrld_dst->lightgroup = (LightgroupMembership *)MEM_dupallocN(wrld_src->lightgroup);
|
||||
}
|
||||
}
|
||||
|
||||
static void world_foreach_id(ID *id, LibraryForeachIDData *data)
|
||||
|
@ -142,6 +148,10 @@ static void world_blend_write(BlendWriter *writer, ID *id, const void *id_addres
|
|||
}
|
||||
|
||||
BKE_previewimg_blend_write(writer, wrld->preview);
|
||||
|
||||
if (wrld->lightgroup) {
|
||||
BLO_write_struct(writer, LightgroupMembership, wrld->lightgroup);
|
||||
}
|
||||
}
|
||||
|
||||
static void world_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
|
@ -153,6 +163,8 @@ static void world_blend_read_data(BlendDataReader *reader, ID *id)
|
|||
BLO_read_data_address(reader, &wrld->preview);
|
||||
BKE_previewimg_blend_read(reader, wrld->preview);
|
||||
BLI_listbase_clear(&wrld->gpumaterial);
|
||||
|
||||
BLO_read_data_address(reader, &wrld->lightgroup);
|
||||
}
|
||||
|
||||
static void world_blend_read_lib(BlendLibReader *reader, ID *id)
|
||||
|
|
|
@ -33,6 +33,8 @@ void SCENE_OT_view_layer_add(struct wmOperatorType *ot);
|
|||
void SCENE_OT_view_layer_remove(struct wmOperatorType *ot);
|
||||
void SCENE_OT_view_layer_add_aov(struct wmOperatorType *ot);
|
||||
void SCENE_OT_view_layer_remove_aov(struct wmOperatorType *ot);
|
||||
void SCENE_OT_view_layer_add_lightgroup(struct wmOperatorType *ot);
|
||||
void SCENE_OT_view_layer_remove_lightgroup(struct wmOperatorType *ot);
|
||||
|
||||
void SCENE_OT_light_cache_bake(struct wmOperatorType *ot);
|
||||
void SCENE_OT_light_cache_free(struct wmOperatorType *ot);
|
||||
|
|
|
@ -39,6 +39,8 @@ void ED_operatortypes_render()
|
|||
WM_operatortype_append(SCENE_OT_view_layer_remove);
|
||||
WM_operatortype_append(SCENE_OT_view_layer_add_aov);
|
||||
WM_operatortype_append(SCENE_OT_view_layer_remove_aov);
|
||||
WM_operatortype_append(SCENE_OT_view_layer_add_lightgroup);
|
||||
WM_operatortype_append(SCENE_OT_view_layer_remove_lightgroup);
|
||||
|
||||
WM_operatortype_append(SCENE_OT_render_view_add);
|
||||
WM_operatortype_append(SCENE_OT_render_view_remove);
|
||||
|
|
|
@ -1114,6 +1114,86 @@ void SCENE_OT_view_layer_remove_aov(wmOperatorType *ot)
|
|||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name View Layer Add Lightgroup Operator
|
||||
* \{ */
|
||||
|
||||
static int view_layer_add_lightgroup_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
BKE_view_layer_add_lightgroup(view_layer);
|
||||
|
||||
if (scene->nodetree) {
|
||||
ntreeCompositUpdateRLayers(scene->nodetree);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&scene->id, 0);
|
||||
DEG_relations_tag_update(CTX_data_main(C));
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void SCENE_OT_view_layer_add_lightgroup(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Lightgroup";
|
||||
ot->idname = "SCENE_OT_view_layer_add_lightgroup";
|
||||
ot->description = "Add a Light Group";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = view_layer_add_lightgroup_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name View Layer Remove Lightgroup Operator
|
||||
* \{ */
|
||||
|
||||
static int view_layer_remove_lightgroup_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
if (view_layer->active_lightgroup == nullptr) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
BKE_view_layer_remove_lightgroup(view_layer, view_layer->active_lightgroup);
|
||||
|
||||
if (scene->nodetree) {
|
||||
ntreeCompositUpdateRLayers(scene->nodetree);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&scene->id, 0);
|
||||
DEG_relations_tag_update(CTX_data_main(C));
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void SCENE_OT_view_layer_remove_lightgroup(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Lightgroup";
|
||||
ot->idname = "SCENE_OT_view_layer_remove_lightgroup";
|
||||
ot->description = "Remove Active Lightgroup";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = view_layer_remove_lightgroup_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Light Cache Bake Operator
|
||||
* \{ */
|
||||
|
|
|
@ -124,6 +124,21 @@ typedef struct ViewLayerAOV {
|
|||
* matches `eViewLayerAOVType` */
|
||||
int type;
|
||||
} ViewLayerAOV;
|
||||
|
||||
/* Lightgroup Renderpass definition. */
|
||||
typedef struct ViewLayerLightgroup {
|
||||
struct ViewLayerLightgroup *next, *prev;
|
||||
|
||||
/* Name of the Lightgroup */
|
||||
char name[64];
|
||||
} ViewLayerLightgroup;
|
||||
|
||||
/* Lightgroup membership information. */
|
||||
typedef struct LightgroupMembership {
|
||||
/* Name of the Lightgroup */
|
||||
char name[64];
|
||||
} LightgroupMembership;
|
||||
|
||||
typedef struct ViewLayer {
|
||||
struct ViewLayer *next, *prev;
|
||||
/** MAX_NAME. */
|
||||
|
@ -164,6 +179,10 @@ typedef struct ViewLayer {
|
|||
ListBase aovs;
|
||||
ViewLayerAOV *active_aov;
|
||||
|
||||
/* List containing the 'ViewLayerLightgroup`s */
|
||||
ListBase lightgroups;
|
||||
ViewLayerLightgroup *active_lightgroup;
|
||||
|
||||
/* Runtime data */
|
||||
/** ViewLayerEngineData. */
|
||||
ListBase drawdata;
|
||||
|
|
|
@ -31,6 +31,7 @@ struct Curve;
|
|||
struct FluidsimSettings;
|
||||
struct GeometrySet;
|
||||
struct Ipo;
|
||||
struct LightgroupMembership;
|
||||
struct Material;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
|
@ -434,8 +435,10 @@ typedef struct Object {
|
|||
|
||||
ObjectLineArt lineart;
|
||||
|
||||
/** Lightgroup membership information. */
|
||||
struct LightgroupMembership *lightgroup;
|
||||
|
||||
/** Runtime evaluation data (keep last). */
|
||||
void *_pad9;
|
||||
Object_Runtime runtime;
|
||||
} Object;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ extern "C" {
|
|||
|
||||
struct AnimData;
|
||||
struct Ipo;
|
||||
struct LightgroupMembership;
|
||||
struct bNodeTree;
|
||||
|
||||
#ifndef MAX_MTEX
|
||||
|
@ -70,6 +71,9 @@ typedef struct World {
|
|||
/* nodes */
|
||||
struct bNodeTree *nodetree;
|
||||
|
||||
/* Lightgroup membership information. */
|
||||
struct LightgroupMembership *lightgroup;
|
||||
|
||||
/** Runtime. */
|
||||
ListBase gpumaterial;
|
||||
} World;
|
||||
|
|
|
@ -355,6 +355,10 @@ void rna_ViewLayer_active_aov_index_range(
|
|||
PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax);
|
||||
int rna_ViewLayer_active_aov_index_get(PointerRNA *ptr);
|
||||
void rna_ViewLayer_active_aov_index_set(PointerRNA *ptr, int value);
|
||||
void rna_ViewLayer_active_lightgroup_index_range(
|
||||
PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax);
|
||||
int rna_ViewLayer_active_lightgroup_index_get(PointerRNA *ptr);
|
||||
void rna_ViewLayer_active_lightgroup_index_set(PointerRNA *ptr, int value);
|
||||
/**
|
||||
* Set `r_rna_path` with the base view-layer path.
|
||||
* `rna_path_buffer_size` should be at least `sizeof(ViewLayer.name) * 3`.
|
||||
|
|
|
@ -2292,6 +2292,21 @@ static int rna_Object_mesh_symmetry_yz_editable(PointerRNA *ptr, const char **UN
|
|||
return PROP_EDITABLE;
|
||||
}
|
||||
|
||||
void rna_Object_lightgroup_get(PointerRNA *ptr, char *value)
|
||||
{
|
||||
BKE_lightgroup_membership_get(((Object *)ptr->owner_id)->lightgroup, value);
|
||||
}
|
||||
|
||||
int rna_Object_lightgroup_length(PointerRNA *ptr)
|
||||
{
|
||||
return BKE_lightgroup_membership_length(((Object *)ptr->owner_id)->lightgroup);
|
||||
}
|
||||
|
||||
void rna_Object_lightgroup_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
BKE_lightgroup_membership_set(&((Object *)ptr->owner_id)->lightgroup, value);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void rna_def_vertex_group(BlenderRNA *brna)
|
||||
|
@ -3775,6 +3790,15 @@ static void rna_def_object(BlenderRNA *brna)
|
|||
RNA_def_property_editable_func(prop, "rna_Object_mesh_symmetry_yz_editable");
|
||||
RNA_def_property_ui_text(prop, "Z", "Enable mesh symmetry in the Z axis");
|
||||
|
||||
/* Lightgroup Membership */
|
||||
prop = RNA_def_property(srna, "lightgroup", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_funcs(prop,
|
||||
"rna_Object_lightgroup_get",
|
||||
"rna_Object_lightgroup_length",
|
||||
"rna_Object_lightgroup_set");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Lightgroup", "Lightgroup that the object belongs to");
|
||||
|
||||
RNA_define_lib_overridable(false);
|
||||
|
||||
/* anim */
|
||||
|
|
|
@ -1767,6 +1767,10 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr)
|
|||
ViewLayerAOV *aov = (ViewLayerAOV *)ptr->data;
|
||||
view_layer = BKE_view_layer_find_with_aov(scene, aov);
|
||||
}
|
||||
else if (ptr->type == &RNA_Lightgroup) {
|
||||
ViewLayerLightgroup *lightgroup = (ViewLayerLightgroup *)ptr->data;
|
||||
view_layer = BKE_view_layer_find_with_lightgroup(scene, lightgroup);
|
||||
}
|
||||
|
||||
if (view_layer) {
|
||||
RenderEngineType *engine_type = RE_engines_find(scene->r.engine);
|
||||
|
@ -2447,6 +2451,49 @@ void rna_ViewLayer_active_aov_index_set(PointerRNA *ptr, int value)
|
|||
view_layer->active_aov = aov;
|
||||
}
|
||||
|
||||
void rna_ViewLayer_active_lightgroup_index_range(
|
||||
PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax))
|
||||
{
|
||||
ViewLayer *view_layer = (ViewLayer *)ptr->data;
|
||||
|
||||
*min = 0;
|
||||
*max = max_ii(0, BLI_listbase_count(&view_layer->lightgroups) - 1);
|
||||
}
|
||||
|
||||
int rna_ViewLayer_active_lightgroup_index_get(PointerRNA *ptr)
|
||||
{
|
||||
ViewLayer *view_layer = (ViewLayer *)ptr->data;
|
||||
return BLI_findindex(&view_layer->lightgroups, view_layer->active_lightgroup);
|
||||
}
|
||||
|
||||
void rna_ViewLayer_active_lightgroup_index_set(PointerRNA *ptr, int value)
|
||||
{
|
||||
ViewLayer *view_layer = (ViewLayer *)ptr->data;
|
||||
ViewLayerLightgroup *lightgroup = BLI_findlink(&view_layer->lightgroups, value);
|
||||
view_layer->active_lightgroup = lightgroup;
|
||||
}
|
||||
|
||||
static void rna_ViewLayerLightgroup_name_get(PointerRNA *ptr, char *value)
|
||||
{
|
||||
ViewLayerLightgroup *lightgroup = (ViewLayerLightgroup *)ptr->data;
|
||||
BLI_strncpy(value, lightgroup->name, sizeof(lightgroup->name));
|
||||
}
|
||||
|
||||
static int rna_ViewLayerLightgroup_name_length(PointerRNA *ptr)
|
||||
{
|
||||
ViewLayerLightgroup *lightgroup = (ViewLayerLightgroup *)ptr->data;
|
||||
return strlen(lightgroup->name);
|
||||
}
|
||||
|
||||
static void rna_ViewLayerLightgroup_name_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
ViewLayerLightgroup *lightgroup = (ViewLayerLightgroup *)ptr->data;
|
||||
Scene *scene = (Scene *)ptr->owner_id;
|
||||
ViewLayer *view_layer = BKE_view_layer_find_with_lightgroup(scene, lightgroup);
|
||||
|
||||
BKE_view_layer_rename_lightgroup(view_layer, lightgroup, value);
|
||||
}
|
||||
|
||||
/* Fake value, used internally (not saved to DNA). */
|
||||
# define V3D_ORIENT_DEFAULT -1
|
||||
|
||||
|
@ -4156,6 +4203,43 @@ static void rna_def_view_layer_aov(BlenderRNA *brna)
|
|||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
|
||||
}
|
||||
|
||||
static void rna_def_view_layer_lightgroups(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
{
|
||||
StructRNA *srna;
|
||||
/* PropertyRNA *prop; */
|
||||
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
RNA_def_property_srna(cprop, "Lightgroups");
|
||||
srna = RNA_def_struct(brna, "Lightgroups", NULL);
|
||||
RNA_def_struct_sdna(srna, "ViewLayer");
|
||||
RNA_def_struct_ui_text(srna, "List of Lightgroups", "Collection of Lightgroups");
|
||||
|
||||
func = RNA_def_function(srna, "add", "BKE_view_layer_add_lightgroup");
|
||||
parm = RNA_def_pointer(func, "lightgroup", "Lightgroup", "", "Newly created Lightgroup");
|
||||
RNA_def_function_return(func, parm);
|
||||
}
|
||||
|
||||
static void rna_def_view_layer_lightgroup(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
srna = RNA_def_struct(brna, "Lightgroup", NULL);
|
||||
RNA_def_struct_sdna(srna, "ViewLayerLightgroup");
|
||||
RNA_def_struct_ui_text(srna, "Light Group", "");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_string_funcs(prop,
|
||||
"rna_ViewLayerLightgroup_name_get",
|
||||
"rna_ViewLayerLightgroup_name_length",
|
||||
"rna_ViewLayerLightgroup_name_set");
|
||||
RNA_def_property_ui_text(prop, "Name", "Name of the Lightgroup");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
}
|
||||
|
||||
void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool scene)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
@ -4226,6 +4310,25 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce
|
|||
RNA_def_property_ui_text(prop, "Active AOV Index", "Index of active aov");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "lightgroups", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, NULL, "lightgroups", NULL);
|
||||
RNA_def_property_struct_type(prop, "Lightgroup");
|
||||
RNA_def_property_ui_text(prop, "Light Groups", "");
|
||||
rna_def_view_layer_lightgroups(brna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "active_lightgroup", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "Lightgroup");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Light Groups", "Active Lightgroup");
|
||||
|
||||
prop = RNA_def_property(srna, "active_lightgroup_index", PROP_INT, PROP_UNSIGNED);
|
||||
RNA_def_property_int_funcs(prop,
|
||||
"rna_ViewLayer_active_lightgroup_index_get",
|
||||
"rna_ViewLayer_active_lightgroup_index_set",
|
||||
"rna_ViewLayer_active_lightgroup_index_range");
|
||||
RNA_def_property_ui_text(prop, "Active Lightgroup Index", "Index of active lightgroup");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
|
||||
|
||||
prop = RNA_def_property(srna, "use_pass_cryptomatte_object", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "cryptomatte_flag", VIEW_LAYER_CRYPTOMATTE_OBJECT);
|
||||
RNA_def_property_ui_text(
|
||||
|
@ -8088,6 +8191,7 @@ void RNA_def_scene(BlenderRNA *brna)
|
|||
rna_def_scene_display(brna);
|
||||
rna_def_scene_eevee(brna);
|
||||
rna_def_view_layer_aov(brna);
|
||||
rna_def_view_layer_lightgroup(brna);
|
||||
rna_def_view_layer_eevee(brna);
|
||||
rna_def_scene_gpencil(brna);
|
||||
RNA_define_animate_sdna(true);
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
# include "MEM_guardedalloc.h"
|
||||
|
||||
# include "BKE_context.h"
|
||||
# include "BKE_layer.h"
|
||||
# include "BKE_main.h"
|
||||
# include "BKE_texture.h"
|
||||
|
||||
|
@ -84,6 +85,21 @@ static void rna_World_use_nodes_update(bContext *C, PointerRNA *ptr)
|
|||
rna_World_draw_update(bmain, scene, ptr);
|
||||
}
|
||||
|
||||
void rna_World_lightgroup_get(PointerRNA *ptr, char *value)
|
||||
{
|
||||
BKE_lightgroup_membership_get(((World *)ptr->owner_id)->lightgroup, value);
|
||||
}
|
||||
|
||||
int rna_World_lightgroup_length(PointerRNA *ptr)
|
||||
{
|
||||
return BKE_lightgroup_membership_length(((World *)ptr->owner_id)->lightgroup);
|
||||
}
|
||||
|
||||
void rna_World_lightgroup_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
BKE_lightgroup_membership_set(&((World *)ptr->owner_id)->lightgroup, value);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void rna_def_lighting(BlenderRNA *brna)
|
||||
|
@ -234,6 +250,13 @@ void RNA_def_world(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Use Nodes", "Use shader nodes to render the world");
|
||||
RNA_def_property_update(prop, 0, "rna_World_use_nodes_update");
|
||||
|
||||
/* Lightgroup Membership */
|
||||
prop = RNA_def_property(srna, "lightgroup", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_funcs(
|
||||
prop, "rna_World_lightgroup_get", "rna_World_lightgroup_length", "rna_World_lightgroup_set");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Lightgroup", "Lightgroup that the world belongs to");
|
||||
|
||||
rna_def_lighting(brna);
|
||||
rna_def_world_mist(brna);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue