Merge branch 'blender-v4.3-release'
This commit is contained in:
@@ -18,6 +18,8 @@ from bl_ui.properties_view_layer import (
|
||||
ViewLayerLightgroupsPanelHelper,
|
||||
)
|
||||
|
||||
from bl_ui.properties_object import has_geometry_visibility
|
||||
|
||||
|
||||
class CyclesPresetPanel(PresetPanel, Panel):
|
||||
COMPAT_ENGINES = {'CYCLES'}
|
||||
@@ -1255,46 +1257,14 @@ class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel):
|
||||
col.prop(cob, "use_deform_motion", text="Deformation")
|
||||
|
||||
|
||||
def has_geometry_visibility(ob):
|
||||
return ob and (
|
||||
(ob.type in {
|
||||
'MESH',
|
||||
'CURVE',
|
||||
'SURFACE',
|
||||
'FONT',
|
||||
'META',
|
||||
'LIGHT',
|
||||
'VOLUME',
|
||||
'POINTCLOUD',
|
||||
'CURVES',
|
||||
}) or (ob.instance_type == 'COLLECTION' and ob.instance_collection))
|
||||
|
||||
|
||||
class CYCLES_OBJECT_PT_shading(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Shading"
|
||||
bl_context = "object"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not CyclesButtonsPanel.poll(context):
|
||||
return False
|
||||
|
||||
ob = context.object
|
||||
return ob and has_geometry_visibility(ob)
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class CYCLES_OBJECT_PT_shading_shadow_terminator(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Shadow Terminator"
|
||||
bl_parent_id = "CYCLES_OBJECT_PT_shading"
|
||||
bl_parent_id = "OBJECT_PT_shading"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object.type != 'LIGHT'
|
||||
return CyclesButtonsPanel.poll(context) and context.object.type != 'LIGHT'
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -1310,12 +1280,12 @@ class CYCLES_OBJECT_PT_shading_shadow_terminator(CyclesButtonsPanel, Panel):
|
||||
|
||||
class CYCLES_OBJECT_PT_shading_gi_approximation(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Fast GI Approximation"
|
||||
bl_parent_id = "CYCLES_OBJECT_PT_shading"
|
||||
bl_parent_id = "OBJECT_PT_shading"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.object.type != 'LIGHT'
|
||||
return CyclesButtonsPanel.poll(context) and context.object.type != 'LIGHT'
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
@@ -1334,7 +1304,7 @@ class CYCLES_OBJECT_PT_shading_gi_approximation(CyclesButtonsPanel, Panel):
|
||||
|
||||
class CYCLES_OBJECT_PT_shading_caustics(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Caustics"
|
||||
bl_parent_id = "CYCLES_OBJECT_PT_shading"
|
||||
bl_parent_id = "OBJECT_PT_shading"
|
||||
bl_context = "object"
|
||||
|
||||
@classmethod
|
||||
@@ -1356,7 +1326,7 @@ class CYCLES_OBJECT_PT_shading_caustics(CyclesButtonsPanel, Panel):
|
||||
|
||||
class CYCLES_OBJECT_PT_lightgroup(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Light Group"
|
||||
bl_parent_id = "CYCLES_OBJECT_PT_shading"
|
||||
bl_parent_id = "OBJECT_PT_shading"
|
||||
bl_context = "object"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@@ -1379,96 +1349,6 @@ class CYCLES_OBJECT_PT_lightgroup(CyclesButtonsPanel, Panel):
|
||||
sub.operator("scene.view_layer_add_lightgroup", icon='ADD', text="").name = ob.lightgroup
|
||||
|
||||
|
||||
class CYCLES_OBJECT_MT_light_linking_context_menu(Menu):
|
||||
bl_label = "Light Linking Specials"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("object.light_linking_receivers_select")
|
||||
|
||||
|
||||
class CYCLES_OBJECT_MT_shadow_linking_context_menu(Menu):
|
||||
bl_label = "Shadow Linking Specials"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("object.light_linking_blockers_select")
|
||||
|
||||
|
||||
class CYCLES_OBJECT_PT_light_linking(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Light Linking"
|
||||
bl_parent_id = "CYCLES_OBJECT_PT_shading"
|
||||
bl_context = "object"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
object = context.object
|
||||
light_linking = object.light_linking
|
||||
|
||||
col = layout.column()
|
||||
|
||||
col.template_ID(
|
||||
light_linking,
|
||||
"receiver_collection",
|
||||
new="object.light_linking_receiver_collection_new")
|
||||
|
||||
if not light_linking.receiver_collection:
|
||||
return
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.template_light_linking_collection(row, light_linking, "receiver_collection")
|
||||
|
||||
col = row.column()
|
||||
sub = col.column(align=True)
|
||||
prop = sub.operator("object.light_linking_receivers_link", icon='ADD', text="")
|
||||
prop.link_state = 'INCLUDE'
|
||||
sub.operator("object.light_linking_unlink_from_collection", icon='REMOVE', text="")
|
||||
sub = col.column()
|
||||
sub.menu("CYCLES_OBJECT_MT_light_linking_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
|
||||
class CYCLES_OBJECT_PT_shadow_linking(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Shadow Linking"
|
||||
bl_parent_id = "CYCLES_OBJECT_PT_shading"
|
||||
bl_context = "object"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
object = context.object
|
||||
light_linking = object.light_linking
|
||||
|
||||
col = layout.column()
|
||||
|
||||
col.template_ID(
|
||||
light_linking,
|
||||
"blocker_collection",
|
||||
new="object.light_linking_blocker_collection_new")
|
||||
|
||||
if not light_linking.blocker_collection:
|
||||
return
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.template_light_linking_collection(row, light_linking, "blocker_collection")
|
||||
|
||||
col = row.column()
|
||||
sub = col.column(align=True)
|
||||
prop = sub.operator("object.light_linking_blockers_link", icon='ADD', text="")
|
||||
prop.link_state = 'INCLUDE'
|
||||
sub.operator("object.light_linking_unlink_from_collection", icon='REMOVE', text="")
|
||||
sub = col.column()
|
||||
sub.menu("CYCLES_OBJECT_MT_shadow_linking_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
|
||||
class CYCLES_OBJECT_PT_visibility(CyclesButtonsPanel, Panel):
|
||||
bl_label = "Visibility"
|
||||
bl_context = "object"
|
||||
@@ -2517,14 +2397,6 @@ def draw_pause(self, context):
|
||||
layout.prop(cscene, "preview_pause", icon='PLAY' if cscene.preview_pause else 'PAUSE', text="")
|
||||
|
||||
|
||||
def draw_make_links(self, context):
|
||||
if context.engine == "CYCLES":
|
||||
layout = self.layout
|
||||
layout.separator()
|
||||
layout.operator_menu_enum("object.light_linking_receivers_link", "link_state")
|
||||
layout.operator_menu_enum("object.light_linking_blockers_link", "link_state")
|
||||
|
||||
|
||||
def get_panels():
|
||||
exclude_panels = {
|
||||
'DATA_PT_camera_dof',
|
||||
@@ -2608,15 +2480,10 @@ classes = (
|
||||
CYCLES_CAMERA_PT_dof_aperture,
|
||||
CYCLES_PT_context_material,
|
||||
CYCLES_OBJECT_PT_motion_blur,
|
||||
CYCLES_OBJECT_PT_shading,
|
||||
CYCLES_OBJECT_PT_shading_shadow_terminator,
|
||||
CYCLES_OBJECT_PT_shading_gi_approximation,
|
||||
CYCLES_OBJECT_PT_shading_caustics,
|
||||
CYCLES_OBJECT_PT_lightgroup,
|
||||
CYCLES_OBJECT_MT_light_linking_context_menu,
|
||||
CYCLES_OBJECT_PT_light_linking,
|
||||
CYCLES_OBJECT_MT_shadow_linking_context_menu,
|
||||
CYCLES_OBJECT_PT_shadow_linking,
|
||||
CYCLES_OBJECT_PT_visibility,
|
||||
CYCLES_OBJECT_PT_visibility_ray_visibility,
|
||||
CYCLES_OBJECT_PT_visibility_culling,
|
||||
@@ -2663,7 +2530,6 @@ def register():
|
||||
|
||||
bpy.types.RENDER_PT_context.append(draw_device)
|
||||
bpy.types.VIEW3D_HT_header.append(draw_pause)
|
||||
bpy.types.VIEW3D_MT_make_links.append(draw_make_links)
|
||||
|
||||
for panel in get_panels():
|
||||
panel.COMPAT_ENGINES.add('CYCLES')
|
||||
@@ -2677,7 +2543,6 @@ def unregister():
|
||||
|
||||
bpy.types.RENDER_PT_context.remove(draw_device)
|
||||
bpy.types.VIEW3D_HT_header.remove(draw_pause)
|
||||
bpy.types.VIEW3D_MT_make_links.remove(draw_make_links)
|
||||
|
||||
for panel in get_panels():
|
||||
if 'CYCLES' in panel.COMPAT_ENGINES:
|
||||
|
||||
@@ -421,6 +421,128 @@ class OBJECT_PT_visibility(ObjectButtonsPanel, Panel):
|
||||
col.prop(ob, "is_holdout")
|
||||
|
||||
|
||||
def has_geometry_visibility(ob):
|
||||
return ob and (
|
||||
(ob.type in {
|
||||
'MESH',
|
||||
'CURVE',
|
||||
'SURFACE',
|
||||
'FONT',
|
||||
'META',
|
||||
'LIGHT',
|
||||
'VOLUME',
|
||||
'POINTCLOUD',
|
||||
'CURVES',
|
||||
}) or (ob.instance_type == 'COLLECTION' and ob.instance_collection))
|
||||
|
||||
|
||||
class OBJECT_PT_shading(ObjectButtonsPanel, Panel):
|
||||
bl_label = "Shading"
|
||||
bl_context = "object"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.engine not in cls.COMPAT_ENGINES:
|
||||
return False
|
||||
|
||||
return has_geometry_visibility(context.object)
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class OBJECT_MT_light_linking_context_menu(Menu):
|
||||
bl_label = "Light Linking Specials"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("object.light_linking_receivers_select")
|
||||
|
||||
|
||||
class OBJECT_PT_light_linking(ObjectButtonsPanel, Panel):
|
||||
bl_label = "Light Linking"
|
||||
bl_parent_id = "OBJECT_PT_shading"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
object = context.object
|
||||
light_linking = object.light_linking
|
||||
|
||||
col = layout.column()
|
||||
|
||||
col.template_ID(
|
||||
light_linking,
|
||||
"receiver_collection",
|
||||
new="object.light_linking_receiver_collection_new")
|
||||
|
||||
if not light_linking.receiver_collection:
|
||||
return
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.template_light_linking_collection(row, light_linking, "receiver_collection")
|
||||
|
||||
col = row.column()
|
||||
sub = col.column(align=True)
|
||||
prop = sub.operator("object.light_linking_receivers_link", icon='ADD', text="")
|
||||
prop.link_state = 'INCLUDE'
|
||||
sub.operator("object.light_linking_unlink_from_collection", icon='REMOVE', text="")
|
||||
sub = col.column()
|
||||
sub.menu("OBJECT_MT_light_linking_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
|
||||
class OBJECT_MT_shadow_linking_context_menu(Menu):
|
||||
bl_label = "Shadow Linking Specials"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("object.light_linking_blockers_select")
|
||||
|
||||
|
||||
class OBJECT_PT_shadow_linking(ObjectButtonsPanel, Panel):
|
||||
bl_label = "Shadow Linking"
|
||||
bl_parent_id = "OBJECT_PT_shading"
|
||||
bl_context = "object"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
|
||||
object = context.object
|
||||
light_linking = object.light_linking
|
||||
|
||||
col = layout.column()
|
||||
|
||||
col.template_ID(
|
||||
light_linking,
|
||||
"blocker_collection",
|
||||
new="object.light_linking_blocker_collection_new")
|
||||
|
||||
if not light_linking.blocker_collection:
|
||||
return
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.template_light_linking_collection(row, light_linking, "blocker_collection")
|
||||
|
||||
col = row.column()
|
||||
sub = col.column(align=True)
|
||||
prop = sub.operator("object.light_linking_blockers_link", icon='ADD', text="")
|
||||
prop.link_state = 'INCLUDE'
|
||||
sub.operator("object.light_linking_unlink_from_collection", icon='REMOVE', text="")
|
||||
sub = col.column()
|
||||
sub.menu("OBJECT_MT_shadow_linking_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
|
||||
class OBJECT_PT_animation(ObjectButtonsPanel, PropertiesAnimationMixin, PropertyPanel, Panel):
|
||||
_animated_id_context_property = "object"
|
||||
|
||||
@@ -443,6 +565,11 @@ classes = (
|
||||
OBJECT_PT_motion_paths,
|
||||
OBJECT_PT_motion_paths_display,
|
||||
OBJECT_PT_display,
|
||||
OBJECT_PT_shading,
|
||||
OBJECT_MT_light_linking_context_menu,
|
||||
OBJECT_PT_light_linking,
|
||||
OBJECT_MT_shadow_linking_context_menu,
|
||||
OBJECT_PT_shadow_linking,
|
||||
OBJECT_PT_visibility,
|
||||
OBJECT_PT_lineart,
|
||||
OBJECT_PT_animation,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
from bpy.types import Menu
|
||||
from bpy.types import Menu, Panel
|
||||
|
||||
|
||||
class BrushAssetShelf:
|
||||
@@ -18,12 +18,43 @@ class BrushAssetShelf:
|
||||
def poll(cls, context):
|
||||
return hasattr(context, "object") and context.object and context.object.mode == cls.mode
|
||||
|
||||
@classmethod
|
||||
def has_tool_with_brush_type(cls, context, brush_type):
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
space_type = context.space_data.type
|
||||
|
||||
brush_type_items = bpy.types.Brush.bl_rna.properties[cls.tool_prop].enum_items
|
||||
|
||||
tool_helper_cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
|
||||
for item in ToolSelectPanelHelper._tools_flatten(
|
||||
tool_helper_cls.tools_from_context(context, mode=context.mode),
|
||||
):
|
||||
if item is None:
|
||||
continue
|
||||
if item.idname in {
|
||||
"builtin.arc",
|
||||
"builtin.curve",
|
||||
"builtin.line",
|
||||
"builtin.box",
|
||||
"builtin.circle",
|
||||
"builtin.polyline",
|
||||
}:
|
||||
continue
|
||||
if item.options is None or ('USE_BRUSHES' not in item.options):
|
||||
continue
|
||||
if item.brush_type is not None:
|
||||
if brush_type_items[item.brush_type].value == brush_type:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@classmethod
|
||||
def brush_type_poll(cls, context, asset):
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
tool = ToolSelectPanelHelper.tool_active_from_context(context)
|
||||
|
||||
if not tool or tool.brush_type == 'ANY':
|
||||
if not tool:
|
||||
return True
|
||||
if not cls.brush_type_prop or not cls.tool_prop:
|
||||
return True
|
||||
@@ -33,9 +64,14 @@ class BrushAssetShelf:
|
||||
# certain brush type.
|
||||
if asset_brush_type is None:
|
||||
return False
|
||||
brush_type_items = bpy.types.Brush.bl_rna.properties[cls.tool_prop].enum_items
|
||||
|
||||
return brush_type_items[asset_brush_type].identifier == tool.brush_type
|
||||
# For the general brush that supports any brush type, filter out brushes that show up for
|
||||
# other tools already.
|
||||
if tool.brush_type == 'ANY':
|
||||
return not cls.has_tool_with_brush_type(context, asset_brush_type)
|
||||
|
||||
brush_type_items = bpy.types.Brush.bl_rna.properties[cls.tool_prop].enum_items
|
||||
return brush_type_items[tool.brush_type].value == asset_brush_type
|
||||
|
||||
@classmethod
|
||||
def asset_poll(cls, asset):
|
||||
@@ -45,12 +81,13 @@ class BrushAssetShelf:
|
||||
return False
|
||||
|
||||
context = bpy.context
|
||||
prefs = context.preferences
|
||||
|
||||
is_asset_shelf_region = context.region and context.region.type == 'ASSET_SHELF'
|
||||
# Show all brushes in the permanent asset shelf region. Otherwise filter out brushes that
|
||||
# Show all brushes in the popup asset shelves. Otherwise filter out brushes that
|
||||
# are incompatible with the tool.
|
||||
if not is_asset_shelf_region and not cls.brush_type_poll(context, asset):
|
||||
return False
|
||||
if is_asset_shelf_region and prefs.view.use_filter_brushes_by_tool:
|
||||
return cls.brush_type_poll(context, asset)
|
||||
|
||||
return True
|
||||
|
||||
@@ -112,6 +149,25 @@ class BrushAssetShelf:
|
||||
)
|
||||
|
||||
|
||||
class VIEW3D_PT_brush_asset_shelf_filter(Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'HEADER'
|
||||
bl_label = "Filter"
|
||||
bl_parent_id = "ASSETSHELF_PT_display"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.asset_shelf is None:
|
||||
return False
|
||||
return context.asset_shelf.bl_idname == BrushAssetShelf.get_shelf_name_from_context(context)
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
prefs = context.preferences
|
||||
|
||||
layout.prop(prefs.view, "use_filter_brushes_by_tool", text="By Active Tool")
|
||||
|
||||
|
||||
class UnifiedPaintPanel:
|
||||
# subclass must set
|
||||
# bl_space_type = 'IMAGE_EDITOR'
|
||||
@@ -1828,6 +1884,7 @@ def brush_basic_grease_pencil_vertex_settings(layout, context, brush, *, compact
|
||||
|
||||
|
||||
classes = (
|
||||
VIEW3D_PT_brush_asset_shelf_filter,
|
||||
VIEW3D_MT_tools_projectpaint_clone,
|
||||
)
|
||||
|
||||
|
||||
@@ -3651,6 +3651,10 @@ class VIEW3D_MT_make_links(Menu):
|
||||
layout.operator("object.data_transfer")
|
||||
layout.operator("object.datalayout_transfer")
|
||||
|
||||
layout.separator()
|
||||
layout.operator_menu_enum("object.light_linking_receivers_link", "link_state")
|
||||
layout.operator_menu_enum("object.light_linking_blockers_link", "link_state")
|
||||
|
||||
|
||||
class VIEW3D_MT_paint_vertex(Menu):
|
||||
bl_label = "Paint"
|
||||
|
||||
@@ -31,7 +31,7 @@ extern "C" {
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 0
|
||||
#define BLENDER_FILE_SUBVERSION 1
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
||||
@@ -946,7 +946,7 @@ void blo_do_versions_userdef(UserDef *userdef)
|
||||
|
||||
if (!USER_VERSION_ATLEAST(400, 24)) {
|
||||
/* Clear deprecated USER_MENUFIXEDORDER user flag for reuse. */
|
||||
userdef->uiflag &= ~USER_UIFLAG_UNUSED_4;
|
||||
userdef->uiflag &= ~(1 << 23);
|
||||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(400, 26)) {
|
||||
@@ -1064,6 +1064,10 @@ void blo_do_versions_userdef(UserDef *userdef)
|
||||
userdef->sequencer_editor_flag |= USER_SEQ_ED_CONNECT_STRIPS_BY_DEFAULT;
|
||||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(403, 30)) {
|
||||
userdef->uiflag |= USER_FILTER_BRUSHES_BY_TOOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a USER_VERSION_ATLEAST check.
|
||||
|
||||
@@ -496,6 +496,7 @@ set(GLSL_SRC
|
||||
engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_tilemap_rendermap_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_shadow_visibility_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl
|
||||
engines/eevee_next/shaders/eevee_subsurface_convolve_comp.glsl
|
||||
engines/eevee_next/shaders/eevee_subsurface_lib.glsl
|
||||
|
||||
@@ -141,7 +141,7 @@ struct GBuffer {
|
||||
|
||||
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
|
||||
GPU_TEXTURE_USAGE_ATTACHMENT;
|
||||
header_tx.ensure_2d(GPU_R16UI, extent, usage);
|
||||
header_tx.ensure_2d(GPU_R32UI, extent, usage);
|
||||
closure_tx.ensure_2d_array(GPU_RGB10_A2, extent, data_count, usage);
|
||||
normal_tx.ensure_2d_array(GPU_RG16, extent, normal_count, usage);
|
||||
/* Ensure layer view for frame-buffer attachment. */
|
||||
|
||||
@@ -19,6 +19,12 @@
|
||||
|
||||
namespace blender::eevee {
|
||||
|
||||
/* Convert by putting the least significant bits in the first component. */
|
||||
static uint2 uint64_to_uint2(uint64_t data)
|
||||
{
|
||||
return {uint(data), uint(data >> uint64_t(32))};
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name LightData
|
||||
* \{ */
|
||||
@@ -50,6 +56,7 @@ void Light::sync(ShadowModule &shadows,
|
||||
float4x4 object_to_world,
|
||||
char visibility_flag,
|
||||
const ::Light *la,
|
||||
const LightLinking *light_linking /* = nullptr */,
|
||||
float threshold)
|
||||
{
|
||||
using namespace blender::math;
|
||||
@@ -99,6 +106,16 @@ void Light::sync(ShadowModule &shadows,
|
||||
shadow_discard_safe(shadows);
|
||||
}
|
||||
|
||||
if (light_linking) {
|
||||
this->light_set_membership = uint64_to_uint2(light_linking->runtime.light_set_membership);
|
||||
this->shadow_set_membership = uint64_to_uint2(light_linking->runtime.shadow_set_membership);
|
||||
}
|
||||
else {
|
||||
/* Set all bits if light linking is not used. */
|
||||
this->light_set_membership = uint64_to_uint2(~uint64_t(0));
|
||||
this->shadow_set_membership = uint64_to_uint2(~uint64_t(0));
|
||||
}
|
||||
|
||||
this->initialized = true;
|
||||
}
|
||||
|
||||
@@ -365,7 +382,7 @@ void LightModule::begin_sync()
|
||||
|
||||
Light &light = light_map_.lookup_or_add_default(world_sunlight_key);
|
||||
light.used = true;
|
||||
light.sync(inst_.shadows, float4x4::identity(), 0, &la, light_threshold_);
|
||||
light.sync(inst_.shadows, float4x4::identity(), 0, &la, nullptr, light_threshold_);
|
||||
|
||||
sun_lights_len_ += 1;
|
||||
}
|
||||
@@ -388,7 +405,12 @@ void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
|
||||
light.used = true;
|
||||
if (handle.recalc != 0 || !light.initialized) {
|
||||
light.initialized = true;
|
||||
light.sync(inst_.shadows, ob->object_to_world(), ob->visibility_flag, la, light_threshold_);
|
||||
light.sync(inst_.shadows,
|
||||
ob->object_to_world(),
|
||||
ob->visibility_flag,
|
||||
la,
|
||||
ob->light_linking,
|
||||
light_threshold_);
|
||||
}
|
||||
sun_lights_len_ += int(is_sun_light(light.type));
|
||||
local_lights_len_ += int(!is_sun_light(light.type));
|
||||
|
||||
@@ -80,6 +80,7 @@ struct Light : public LightData, NonCopyable {
|
||||
float4x4 object_to_world,
|
||||
char visibility_flag,
|
||||
const ::Light *la,
|
||||
const LightLinking *light_linking,
|
||||
float threshold);
|
||||
|
||||
void shadow_ensure(ShadowModule &shadows);
|
||||
|
||||
@@ -340,6 +340,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
|
||||
return "eevee_shadow_page_tile_store";
|
||||
case SHADOW_TILEMAP_TAG_USAGE_VOLUME:
|
||||
return "eevee_shadow_tag_usage_volume";
|
||||
case SHADOW_VIEW_VISIBILITY:
|
||||
return "eevee_shadow_view_visibility";
|
||||
case SUBSURFACE_CONVOLVE:
|
||||
return "eevee_subsurface_convolve";
|
||||
case SUBSURFACE_SETUP:
|
||||
|
||||
@@ -139,6 +139,7 @@ enum eShaderType {
|
||||
SHADOW_TILEMAP_TAG_USAGE_SURFELS,
|
||||
SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT,
|
||||
SHADOW_TILEMAP_TAG_USAGE_VOLUME,
|
||||
SHADOW_VIEW_VISIBILITY,
|
||||
|
||||
SUBSURFACE_CONVOLVE,
|
||||
SUBSURFACE_SETUP,
|
||||
|
||||
@@ -1008,6 +1008,11 @@ struct LightData {
|
||||
/* True if the light uses jittered soft shadows. */
|
||||
bool32_t shadow_jitter;
|
||||
float _pad2;
|
||||
uint2 light_set_membership;
|
||||
/** Used by shadow sync. */
|
||||
/* TODO(fclem): this should be part of #eevee::Light struct. But for some reason it gets cleared
|
||||
* to zero after each sync cycle. */
|
||||
uint2 shadow_set_membership;
|
||||
|
||||
#if USE_LIGHT_UNION
|
||||
union {
|
||||
@@ -1337,6 +1342,9 @@ struct ShadowTileMapData {
|
||||
float half_size;
|
||||
/** Offset in local space to the tilemap center in world units. Used for directional winmat. */
|
||||
float2 center_offset;
|
||||
/** Shadow set bitmask of the light using this tilemap. */
|
||||
uint2 shadow_set_membership;
|
||||
uint2 _pad3;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowTileMapData, 16)
|
||||
|
||||
@@ -1362,6 +1370,9 @@ struct ShadowRenderView {
|
||||
int tilemap_lod;
|
||||
/** Updated region of the tilemap. */
|
||||
int2 rect_min;
|
||||
/** Shadow set bitmask of the light generating this view. */
|
||||
uint2 shadow_set_membership;
|
||||
uint2 _pad0;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ShadowRenderView, 16)
|
||||
|
||||
@@ -1725,9 +1736,10 @@ struct Surfel {
|
||||
int cluster_id;
|
||||
/** True if the light can bounce or be emitted by the surfel back face. */
|
||||
bool32_t double_sided;
|
||||
/** Surface receiver light set for light linking. */
|
||||
uint receiver_light_set;
|
||||
int _pad0;
|
||||
int _pad1;
|
||||
int _pad2;
|
||||
/** Surface radiance: Emission + Direct Lighting. */
|
||||
SurfelRadiance radiance_direct;
|
||||
/** Surface radiance: Indirect Lighting. Double buffered to avoid race conditions. */
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "GPU_compute.hh"
|
||||
|
||||
#include "eevee_instance.hh"
|
||||
|
||||
@@ -28,14 +29,18 @@ ShadowTechnique ShadowModule::shadow_technique = ShadowTechnique::ATOMIC_RASTER;
|
||||
void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_,
|
||||
int2 origin_offset,
|
||||
int clipmap_level,
|
||||
eShadowProjectionType projection_type_)
|
||||
eShadowProjectionType projection_type_,
|
||||
uint2 shadow_set_membership_)
|
||||
{
|
||||
if ((projection_type != projection_type_) || (level != clipmap_level)) {
|
||||
if ((projection_type != projection_type_) || (level != clipmap_level) ||
|
||||
(shadow_set_membership_ != shadow_set_membership))
|
||||
{
|
||||
set_dirty();
|
||||
}
|
||||
projection_type = projection_type_;
|
||||
level = clipmap_level;
|
||||
light_type = eLightType::LIGHT_SUN;
|
||||
shadow_set_membership = shadow_set_membership_;
|
||||
|
||||
grid_shift = origin_offset - grid_offset;
|
||||
grid_offset = origin_offset;
|
||||
@@ -63,16 +68,23 @@ void ShadowTileMap::sync_orthographic(const float4x4 &object_mat_,
|
||||
1.0f);
|
||||
}
|
||||
|
||||
void ShadowTileMap::sync_cubeface(
|
||||
eLightType light_type_, const float4x4 &object_mat_, float near_, float far_, eCubeFace face)
|
||||
void ShadowTileMap::sync_cubeface(eLightType light_type_,
|
||||
const float4x4 &object_mat_,
|
||||
float near_,
|
||||
float far_,
|
||||
eCubeFace face,
|
||||
uint2 shadow_set_membership_)
|
||||
{
|
||||
if (projection_type != SHADOW_PROJECTION_CUBEFACE || (cubeface != face)) {
|
||||
if (projection_type != SHADOW_PROJECTION_CUBEFACE || (cubeface != face) ||
|
||||
(shadow_set_membership_ != shadow_set_membership))
|
||||
{
|
||||
set_dirty();
|
||||
}
|
||||
projection_type = SHADOW_PROJECTION_CUBEFACE;
|
||||
cubeface = face;
|
||||
grid_offset = int2(0);
|
||||
light_type = light_type_;
|
||||
shadow_set_membership = shadow_set_membership_;
|
||||
|
||||
if ((clip_near != near_) || (clip_far != far_)) {
|
||||
set_dirty();
|
||||
@@ -237,7 +249,8 @@ void ShadowPunctual::end_sync(Light &light)
|
||||
float far = int_as_float(light.clip_far);
|
||||
for (int i : tilemaps_.index_range()) {
|
||||
eCubeFace face = eCubeFace(Z_NEG + i);
|
||||
tilemaps_[face]->sync_cubeface(light.type, object_to_world, near, far, face);
|
||||
tilemaps_[face]->sync_cubeface(
|
||||
light.type, object_to_world, near, far, face, light.shadow_set_membership);
|
||||
}
|
||||
|
||||
light.local.tilemaps_count = tilemaps_needed;
|
||||
@@ -372,7 +385,8 @@ void ShadowDirectional::cascade_tilemaps_distribution(Light &light, const Camera
|
||||
/* Equal spacing between cascades layers since we want uniform shadow density. */
|
||||
int2 level_offset = origin_offset +
|
||||
shadow_cascade_grid_offset(light.sun.clipmap_base_offset_pos, i);
|
||||
tilemap->sync_orthographic(object_mat, level_offset, level, SHADOW_PROJECTION_CASCADE);
|
||||
tilemap->sync_orthographic(
|
||||
object_mat, level_offset, level, SHADOW_PROJECTION_CASCADE, light.shadow_set_membership);
|
||||
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
shadows_.tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
@@ -429,7 +443,8 @@ void ShadowDirectional::clipmap_tilemaps_distribution(Light &light, const Camera
|
||||
float2 light_space_camera_position = camera.position() * float2x3(object_mat.view<2, 3>());
|
||||
int2 level_offset = int2(math::round(light_space_camera_position / tile_size));
|
||||
|
||||
tilemap->sync_orthographic(object_mat, level_offset, level, SHADOW_PROJECTION_CLIPMAP);
|
||||
tilemap->sync_orthographic(
|
||||
object_mat, level_offset, level, SHADOW_PROJECTION_CLIPMAP, light.shadow_set_membership);
|
||||
|
||||
/* Add shadow tile-maps grouped by lights to the GPU buffer. */
|
||||
shadows_.tilemap_pool.tilemaps_data.append(*tilemap);
|
||||
@@ -1215,6 +1230,44 @@ int ShadowModule::max_view_per_tilemap()
|
||||
return max_view_count;
|
||||
}
|
||||
|
||||
/* Special culling pass to take shadow linking into consideration. */
|
||||
void ShadowModule::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
|
||||
ObjectInfosBuf &infos,
|
||||
uint resource_len,
|
||||
bool /*debug_freeze*/)
|
||||
{
|
||||
GPU_debug_group_begin("View.compute_visibility");
|
||||
|
||||
uint word_per_draw = this->visibility_word_per_draw();
|
||||
/* Switch between tightly packed and set of whole word per instance. */
|
||||
uint words_len = (view_len_ == 1) ? divide_ceil_u(resource_len, 32) :
|
||||
resource_len * word_per_draw;
|
||||
words_len = ceil_to_multiple_u(max_ii(1, words_len), 4);
|
||||
/* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */
|
||||
visibility_buf_.resize(words_len);
|
||||
|
||||
const uint32_t data = 0xFFFFFFFFu;
|
||||
GPU_storagebuf_clear(visibility_buf_, data);
|
||||
|
||||
if (do_visibility_) {
|
||||
GPUShader *shader = inst_.shaders.static_shader_get(SHADOW_VIEW_VISIBILITY);
|
||||
GPU_shader_bind(shader);
|
||||
GPU_shader_uniform_1i(shader, "resource_len", resource_len);
|
||||
GPU_shader_uniform_1i(shader, "view_len", view_len_);
|
||||
GPU_shader_uniform_1i(shader, "visibility_word_per_draw", word_per_draw);
|
||||
GPU_storagebuf_bind(bounds, GPU_shader_get_ssbo_binding(shader, "bounds_buf"));
|
||||
GPU_storagebuf_bind(visibility_buf_, GPU_shader_get_ssbo_binding(shader, "visibility_buf"));
|
||||
GPU_storagebuf_bind(render_view_buf_, GPU_shader_get_ssbo_binding(shader, "render_view_buf"));
|
||||
GPU_storagebuf_bind(infos, DRW_OBJ_INFOS_SLOT);
|
||||
GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT);
|
||||
GPU_uniformbuf_bind(culling_, DRW_VIEW_CULLING_UBO_SLOT);
|
||||
GPU_compute_dispatch(shader, divide_ceil_u(resource_len, DRW_VISIBILITY_GROUP_SIZE), 1, 1);
|
||||
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
|
||||
}
|
||||
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
void ShadowModule::set_view(View &view, int2 extent)
|
||||
{
|
||||
if (enabled_ == false) {
|
||||
|
||||
@@ -93,10 +93,15 @@ struct ShadowTileMap : public ShadowTileMapData {
|
||||
void sync_orthographic(const float4x4 &object_mat_,
|
||||
int2 origin_offset,
|
||||
int clipmap_level,
|
||||
eShadowProjectionType projection_type_);
|
||||
eShadowProjectionType projection_type_,
|
||||
uint2 shadow_set_membership_ = ~uint2(0));
|
||||
|
||||
void sync_cubeface(
|
||||
eLightType light_type_, const float4x4 &object_mat, float near, float far, eCubeFace face);
|
||||
void sync_cubeface(eLightType light_type_,
|
||||
const float4x4 &object_mat,
|
||||
float near,
|
||||
float far,
|
||||
eCubeFace face,
|
||||
uint2 shadow_set_membership_ = ~uint2(0));
|
||||
|
||||
void debug_draw() const;
|
||||
|
||||
@@ -278,8 +283,25 @@ class ShadowModule {
|
||||
/** \name Rendering
|
||||
* \{ */
|
||||
|
||||
class ShadowView : public View {
|
||||
Instance &inst_;
|
||||
ShadowRenderViewBuf &render_view_buf_;
|
||||
|
||||
public:
|
||||
ShadowView(const char *name, Instance &inst, ShadowRenderViewBuf &render_view_buf)
|
||||
: View(name, SHADOW_VIEW_MAX, true), inst_(inst), render_view_buf_(render_view_buf)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void compute_visibility(ObjectBoundsBuf &bounds,
|
||||
ObjectInfosBuf &infos,
|
||||
uint resource_len,
|
||||
bool debug_freeze) override;
|
||||
};
|
||||
|
||||
/** Multi-View containing a maximum of 64 view to be rendered with the shadow pipeline. */
|
||||
View shadow_multi_view_ = {"ShadowMultiView", SHADOW_VIEW_MAX, true};
|
||||
ShadowView shadow_multi_view_ = {"ShadowMultiView", inst_, render_view_buf_};
|
||||
/** Framebuffer with the atlas_tx attached. */
|
||||
Framebuffer render_fb_ = {"shadow_write_framebuffer"};
|
||||
|
||||
|
||||
@@ -61,12 +61,13 @@ void main()
|
||||
/* Direct light. */
|
||||
ClosureLightStack stack;
|
||||
stack.cl[0] = closure_light_new(cl, V);
|
||||
light_eval_reflection(stack, P, Ng, V, vPz);
|
||||
uchar receiver_light_set = gbuffer_light_link_receiver_unpack(gbuf.header);
|
||||
light_eval_reflection(stack, P, Ng, V, vPz, receiver_light_set);
|
||||
|
||||
vec3 radiance_front = stack.cl[0].light_shadowed;
|
||||
|
||||
stack.cl[0] = closure_light_new(cl_transmit, V, gbuf.thickness);
|
||||
light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness);
|
||||
light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness, receiver_light_set);
|
||||
|
||||
vec3 radiance_back = stack.cl[0].light_shadowed;
|
||||
|
||||
|
||||
@@ -70,7 +70,8 @@ void main()
|
||||
|
||||
/* TODO(fclem): If transmission (no SSS) is present, we could reduce LIGHT_CLOSURE_EVAL_COUNT
|
||||
* by 1 for this evaluation and skip evaluating the transmission closure twice. */
|
||||
light_eval_reflection(stack, P, Ng, V, vPz);
|
||||
uchar receiver_light_set = gbuffer_light_link_receiver_unpack(gbuf.header);
|
||||
light_eval_reflection(stack, P, Ng, V, vPz, receiver_light_set);
|
||||
|
||||
if (use_transmission) {
|
||||
ClosureUndetermined cl_transmit = gbuffer_closure_get(gbuf, 0);
|
||||
@@ -85,7 +86,7 @@ void main()
|
||||
stack.cl[0] = closure_light_new(cl_transmit, V, gbuf.thickness);
|
||||
|
||||
/* NOTE: Only evaluates `stack.cl[0]`. */
|
||||
light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness);
|
||||
light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness, receiver_light_set);
|
||||
|
||||
#if 1 /* TODO Limit to SSS. */
|
||||
if (cl_transmit.type == CLOSURE_BSSRDF_BURLEY_ID) {
|
||||
|
||||
@@ -56,12 +56,13 @@ void main()
|
||||
/* Direct light. */
|
||||
ClosureLightStack stack;
|
||||
stack.cl[0] = closure_light_new(cl, V);
|
||||
light_eval_reflection(stack, P, Ng, V, vPz);
|
||||
uchar receiver_light_set = gbuffer_light_link_receiver_unpack(gbuf.header);
|
||||
light_eval_reflection(stack, P, Ng, V, vPz, receiver_light_set);
|
||||
|
||||
vec3 radiance_front = stack.cl[0].light_shadowed;
|
||||
|
||||
stack.cl[0] = closure_light_new(cl_transmit, V, gbuf.thickness);
|
||||
light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness);
|
||||
light_eval_transmission(stack, P, Ng, V, vPz, gbuf.thickness, receiver_light_set);
|
||||
|
||||
vec3 radiance_back = stack.cl[0].light_shadowed;
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ void forward_lighting_eval(float thickness, out vec3 radiance, out vec3 transmit
|
||||
|
||||
/* TODO(fclem): If transmission (no SSS) is present, we could reduce LIGHT_CLOSURE_EVAL_COUNT
|
||||
* by 1 for this evaluation and skip evaluating the transmission closure twice. */
|
||||
light_eval_reflection(stack, g_data.P, surface_N, V, vPz);
|
||||
uchar receiver_light_set = receiver_light_set_get(drw_infos[resource_id]);
|
||||
light_eval_reflection(stack, g_data.P, surface_N, V, vPz, receiver_light_set);
|
||||
|
||||
#if defined(MAT_SUBSURFACE) || defined(MAT_REFRACTION) || defined(MAT_TRANSLUCENT)
|
||||
|
||||
@@ -54,7 +55,7 @@ void forward_lighting_eval(float thickness, out vec3 radiance, out vec3 transmit
|
||||
stack.cl[0] = closure_light_new(cl_transmit, V, thickness);
|
||||
|
||||
/* NOTE: Only evaluates `stack.cl[0]`. */
|
||||
light_eval_transmission(stack, g_data.P, surface_N, V, vPz, thickness);
|
||||
light_eval_transmission(stack, g_data.P, surface_N, V, vPz, thickness, receiver_light_set);
|
||||
|
||||
# if defined(MAT_SUBSURFACE)
|
||||
if (cl_transmit.type == CLOSURE_BSSRDF_BURLEY_ID) {
|
||||
|
||||
@@ -44,6 +44,8 @@ struct GBufferData {
|
||||
uint object_id;
|
||||
/* First world normal stored in the gbuffer. Only valid if `has_any_surface` is true. */
|
||||
vec3 surface_N;
|
||||
/* Index into `light_set_membership` bitmask of Lights for light linking. */
|
||||
uchar receiver_light_set;
|
||||
};
|
||||
|
||||
/* Result of Packing the GBuffer. */
|
||||
@@ -310,6 +312,16 @@ bool gbuffer_is_refraction(vec4 gbuffer)
|
||||
return gbuffer.w < 1.0;
|
||||
}
|
||||
|
||||
uint gbuffer_light_link_receiver_pack(uchar receiver_light_set)
|
||||
{
|
||||
return receiver_light_set << 26u;
|
||||
}
|
||||
|
||||
uint gbuffer_light_link_receiver_unpack(uint data)
|
||||
{
|
||||
return data >> 26u;
|
||||
}
|
||||
|
||||
uint gbuffer_header_pack(GBufferMode mode, uint bin)
|
||||
{
|
||||
return (mode << (4u * bin));
|
||||
@@ -807,6 +819,9 @@ GBufferWriter gbuffer_pack(GBufferData data_in)
|
||||
gbuf.data_len = 0;
|
||||
gbuf.normal_len = 0;
|
||||
|
||||
/* Pack light linking data into header. */
|
||||
gbuf.header |= gbuffer_light_link_receiver_pack(data_in.receiver_light_set);
|
||||
|
||||
/* Check special configurations first. */
|
||||
|
||||
bool has_additional_data = false;
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
* - utility_tx
|
||||
*/
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
|
||||
#pragma BLENDER_REQUIRE(eevee_shadow_tracing_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_bxdf_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
|
||||
@@ -86,6 +88,11 @@ float light_power_get(LightData light, LightingType type)
|
||||
return light.power[type & 3u];
|
||||
}
|
||||
|
||||
bool light_linking_affects_receiver(uvec2 light_set_membership, uchar receiver_light_set)
|
||||
{
|
||||
return bitmask64_test(light_set_membership, receiver_light_set);
|
||||
}
|
||||
|
||||
void light_eval_single_closure(LightData light,
|
||||
LightVector lv,
|
||||
inout ClosureLight cl,
|
||||
@@ -112,10 +119,15 @@ void light_eval_single(uint l_idx,
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 V,
|
||||
float thickness)
|
||||
float thickness,
|
||||
uchar receiver_light_set)
|
||||
{
|
||||
LightData light = light_buf[l_idx];
|
||||
|
||||
if (!light_linking_affects_receiver(light.light_set_membership, receiver_light_set)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(SPECIALIZED_SHADOW_PARAMS)
|
||||
int ray_count = shadow_ray_count;
|
||||
int ray_step_count = shadow_ray_step_count;
|
||||
@@ -171,37 +183,43 @@ void light_eval_single(uint l_idx,
|
||||
}
|
||||
}
|
||||
|
||||
void light_eval_transmission(
|
||||
inout ClosureLightStack stack, vec3 P, vec3 Ng, vec3 V, float vPz, float thickness)
|
||||
void light_eval_transmission(inout ClosureLightStack stack,
|
||||
vec3 P,
|
||||
vec3 Ng,
|
||||
vec3 V,
|
||||
float vPz,
|
||||
float thickness,
|
||||
uchar receiver_light_set)
|
||||
{
|
||||
#ifdef SKIP_LIGHT_EVAL
|
||||
return;
|
||||
#endif
|
||||
|
||||
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
|
||||
light_eval_single(l_idx, true, true, stack, P, Ng, V, thickness);
|
||||
light_eval_single(l_idx, true, true, stack, P, Ng, V, thickness, receiver_light_set);
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
|
||||
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, PIXEL, vPz, l_idx) {
|
||||
light_eval_single(l_idx, false, true, stack, P, Ng, V, thickness);
|
||||
light_eval_single(l_idx, false, true, stack, P, Ng, V, thickness, receiver_light_set);
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
}
|
||||
|
||||
void light_eval_reflection(inout ClosureLightStack stack, vec3 P, vec3 Ng, vec3 V, float vPz)
|
||||
void light_eval_reflection(
|
||||
inout ClosureLightStack stack, vec3 P, vec3 Ng, vec3 V, float vPz, uchar receiver_light_set)
|
||||
{
|
||||
#ifdef SKIP_LIGHT_EVAL
|
||||
return;
|
||||
#endif
|
||||
|
||||
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
|
||||
light_eval_single(l_idx, true, false, stack, P, Ng, V, 0.0);
|
||||
light_eval_single(l_idx, true, false, stack, P, Ng, V, 0.0, receiver_light_set);
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
|
||||
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, PIXEL, vPz, l_idx) {
|
||||
light_eval_single(l_idx, false, false, stack, P, Ng, V, 0.0);
|
||||
light_eval_single(l_idx, false, false, stack, P, Ng, V, 0.0, receiver_light_set);
|
||||
}
|
||||
LIGHT_FOREACH_END
|
||||
}
|
||||
|
||||
@@ -135,6 +135,8 @@ void main()
|
||||
render_view_buf[view_index].tilemap_tiles_index = tilemap_data.tiles_index;
|
||||
render_view_buf[view_index].tilemap_lod = lod;
|
||||
render_view_buf[view_index].rect_min = rect_min;
|
||||
/* For shadow linking. */
|
||||
render_view_buf[view_index].shadow_set_membership = tilemap_data.shadow_set_membership;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Compute visibility of each resource bounds for a given shadow view.
|
||||
* Checks for shadow linking properties and issue one draw call for each view.
|
||||
*/
|
||||
/* TODO(fclem): Could reject bounding boxes that are covering only invalid tiles. */
|
||||
|
||||
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
|
||||
|
||||
bool shadow_linking_affects_caster(uint view_id, uint resource_id)
|
||||
{
|
||||
return bitmask64_test(render_view_buf[view_id].shadow_set_membership,
|
||||
blocker_shadow_set_get(drw_infos[resource_id]));
|
||||
}
|
||||
|
||||
void mask_visibility_bit(uint view_id)
|
||||
{
|
||||
if (view_len > 1) {
|
||||
uint index = gl_GlobalInvocationID.x * uint(visibility_word_per_draw) + (view_id / 32u);
|
||||
visibility_buf[index] &= ~(1u << view_id);
|
||||
}
|
||||
else {
|
||||
atomicAnd(visibility_buf[gl_WorkGroupID.x], ~(1u << gl_LocalInvocationID.x));
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if visibility needs to be disabled. */
|
||||
bool non_culling_tests(uint view_id, uint resource_id)
|
||||
{
|
||||
if (shadow_linking_affects_caster(view_id, resource_id) == false) {
|
||||
/* Object doesn't cast shadow from this light. */
|
||||
return true;
|
||||
}
|
||||
else if (drw_view_culling.bound_sphere.w == -1.0) {
|
||||
/* View disabled. */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
if (int(gl_GlobalInvocationID.x) >= resource_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectBounds bounds = bounds_buf[gl_GlobalInvocationID.x];
|
||||
|
||||
if (drw_bounds_are_valid(bounds)) {
|
||||
IsectBox box = isect_box_setup(bounds.bounding_corners[0].xyz,
|
||||
bounds.bounding_corners[1].xyz,
|
||||
bounds.bounding_corners[2].xyz,
|
||||
bounds.bounding_corners[3].xyz);
|
||||
Sphere bounding_sphere = shape_sphere(bounds.bounding_sphere.xyz, bounds.bounding_sphere.w);
|
||||
Sphere inscribed_sphere = shape_sphere(bounds.bounding_sphere.xyz,
|
||||
bounds._inner_sphere_radius);
|
||||
|
||||
for (drw_view_id = 0u; drw_view_id < uint(view_len); drw_view_id++) {
|
||||
if (non_culling_tests(drw_view_id, gl_GlobalInvocationID.x)) {
|
||||
mask_visibility_bit(drw_view_id);
|
||||
}
|
||||
else if (drw_view_culling.bound_sphere.w == -1.0) {
|
||||
/* View disabled. */
|
||||
mask_visibility_bit(drw_view_id);
|
||||
}
|
||||
else if (intersect_view(inscribed_sphere) == true) {
|
||||
/* Visible. */
|
||||
}
|
||||
else if (intersect_view(bounding_sphere) == false) {
|
||||
/* Not visible. */
|
||||
mask_visibility_bit(drw_view_id);
|
||||
}
|
||||
else if (intersect_view(box) == false) {
|
||||
/* Not visible. */
|
||||
mask_visibility_bit(drw_view_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Culling is disabled, but we need to mask the bits for disabled views. */
|
||||
for (drw_view_id = 0u; drw_view_id < uint(view_len); drw_view_id++) {
|
||||
if (non_culling_tests(drw_view_id, gl_GlobalInvocationID.x)) {
|
||||
mask_visibility_bit(drw_view_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,7 @@ void main()
|
||||
surfel_buf[surfel_id].radiance_direct.back.rgb = is_double_sided ? g_emission : vec3(0);
|
||||
surfel_buf[surfel_id].radiance_direct.back.a = 0.0;
|
||||
surfel_buf[surfel_id].double_sided = is_double_sided;
|
||||
surfel_buf[surfel_id].receiver_light_set = receiver_light_set_get(drw_infos[resource_id]);
|
||||
|
||||
if (!capture_info_buf.capture_emission) {
|
||||
surfel_buf[surfel_id].radiance_direct.front.rgb = vec3(0.0);
|
||||
|
||||
@@ -93,6 +93,7 @@ void main()
|
||||
gbuf_data.surface_N = g_data.N;
|
||||
gbuf_data.thickness = thickness;
|
||||
gbuf_data.object_id = resource_id;
|
||||
gbuf_data.receiver_light_set = receiver_light_set_get(drw_infos[resource_id]);
|
||||
|
||||
GBufferWriter gbuf = gbuffer_pack(gbuf_data);
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ void main()
|
||||
gbuf_data.surface_N = g_data.N;
|
||||
gbuf_data.thickness = g_thickness;
|
||||
gbuf_data.object_id = resource_id;
|
||||
gbuf_data.receiver_light_set = receiver_light_set_get(drw_infos[resource_id]);
|
||||
|
||||
GBufferWriter gbuf = gbuffer_pack(gbuf_data);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ void main()
|
||||
cl_reflect.N = surfel.normal;
|
||||
cl_reflect.type = CLOSURE_BSDF_DIFFUSE_ID;
|
||||
stack.cl[0] = closure_light_new(cl_reflect, V);
|
||||
light_eval_reflection(stack, P, Ng, V, 0.0);
|
||||
light_eval_reflection(stack, P, Ng, V, 0.0, surfel.receiver_light_set);
|
||||
|
||||
if (capture_info_buf.capture_indirect) {
|
||||
surfel_buf[index].radiance_direct.front.rgb += stack.cl[0].light_shadowed *
|
||||
@@ -43,7 +43,7 @@ void main()
|
||||
cl_transmit.N = -surfel.normal;
|
||||
cl_transmit.type = CLOSURE_BSDF_DIFFUSE_ID;
|
||||
stack.cl[0] = closure_light_new(cl_transmit, -V);
|
||||
light_eval_reflection(stack, P, -Ng, -V, 0.0);
|
||||
light_eval_reflection(stack, P, -Ng, -V, 0.0, surfel.receiver_light_set);
|
||||
|
||||
if (capture_info_buf.capture_indirect) {
|
||||
surfel_buf[index].radiance_direct.back.rgb += stack.cl[0].light_shadowed * surfel.albedo_back;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "draw_defines.hh"
|
||||
#include "eevee_defines.hh"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
@@ -270,6 +271,22 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_tile_store)
|
||||
.vertex_source("eevee_shadow_page_tile_vert.glsl")
|
||||
.fragment_source("eevee_shadow_page_tile_frag.glsl");
|
||||
|
||||
/* Custom visibility check pass. */
|
||||
GPU_SHADER_CREATE_INFO(eevee_shadow_view_visibility)
|
||||
.do_static_compilation(true)
|
||||
.typedef_source("eevee_defines.hh")
|
||||
.typedef_source("eevee_shader_shared.hh")
|
||||
.local_group_size(DRW_VISIBILITY_GROUP_SIZE)
|
||||
.define("DRW_VIEW_LEN", STRINGIFY(DRW_VIEW_MAX))
|
||||
.storage_buf(0, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
|
||||
.storage_buf(1, Qualifier::READ_WRITE, "uint", "visibility_buf[]")
|
||||
.storage_buf(2, Qualifier::READ, "ShadowRenderView", "render_view_buf[SHADOW_VIEW_MAX]")
|
||||
.push_constant(Type::INT, "resource_len")
|
||||
.push_constant(Type::INT, "view_len")
|
||||
.push_constant(Type::INT, "visibility_word_per_draw")
|
||||
.compute_source("eevee_shadow_visibility_comp.glsl")
|
||||
.additional_info("draw_view", "draw_view_culling", "draw_object_infos_new");
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -412,6 +412,7 @@ class ShadowPass {
|
||||
|
||||
protected:
|
||||
virtual void compute_visibility(ObjectBoundsBuf &bounds,
|
||||
ObjectInfosBuf &infos,
|
||||
uint resource_len,
|
||||
bool debug_freeze) override;
|
||||
virtual VisibilityBuf &get_visibility_buffer() override;
|
||||
|
||||
@@ -208,6 +208,7 @@ void ShadowPass::ShadowView::set_mode(ShadowPass::PassType type)
|
||||
}
|
||||
|
||||
void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
|
||||
ObjectInfosBuf & /*infos*/,
|
||||
uint resource_len,
|
||||
bool /*debug_freeze*/)
|
||||
{
|
||||
|
||||
@@ -185,7 +185,8 @@ void Manager::submit(PassMain &pass, View &view)
|
||||
bool freeze_culling = (U.experimental.use_viewport_debug && DST.draw_ctx.v3d &&
|
||||
(DST.draw_ctx.v3d->debug_flag & V3D_DEBUG_FREEZE_CULLING) != 0);
|
||||
|
||||
view.compute_visibility(bounds_buf.current(), resource_len_, freeze_culling);
|
||||
view.compute_visibility(
|
||||
bounds_buf.current(), infos_buf.current(), resource_len_, freeze_culling);
|
||||
|
||||
command::RecordingState state;
|
||||
state.inverted_view = view.is_inverted();
|
||||
|
||||
@@ -73,6 +73,15 @@ inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active
|
||||
{
|
||||
object_attrs_len = 0;
|
||||
object_attrs_offset = 0;
|
||||
light_and_shadow_set_membership = 0;
|
||||
|
||||
LightLinking *light_linking = (ref.dupli_parent) != nullptr ? ref.dupli_parent->light_linking :
|
||||
ref.object->light_linking;
|
||||
if (light_linking) {
|
||||
light_and_shadow_set_membership |= light_linking->runtime.receiver_light_set;
|
||||
light_and_shadow_set_membership |= light_linking->runtime.blocker_shadow_set << 8;
|
||||
}
|
||||
|
||||
bool is_holdout = (ref.object->base_flag & BASE_HOLDOUT) ||
|
||||
(ref.object->visibility_flag & OB_HOLDOUT);
|
||||
|
||||
|
||||
@@ -173,7 +173,8 @@ struct ObjectInfos {
|
||||
|
||||
float4 ob_color;
|
||||
uint index;
|
||||
uint _pad2;
|
||||
/** Used for Light Linking in EEVEE */
|
||||
uint light_and_shadow_set_membership;
|
||||
float random;
|
||||
eObjectInfoFlag flag;
|
||||
#endif
|
||||
@@ -185,6 +186,24 @@ struct ObjectInfos {
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(ObjectInfos, 16)
|
||||
|
||||
inline uint receiver_light_set_get(ObjectInfos object_infos)
|
||||
{
|
||||
#if defined(GPU_SHADER) && !defined(DRAW_FINALIZE_SHADER)
|
||||
return floatBitsToUint(object_infos.infos.y) & 0xFFu;
|
||||
#else
|
||||
return object_infos.light_and_shadow_set_membership & 0xFFu;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint blocker_shadow_set_get(ObjectInfos object_infos)
|
||||
{
|
||||
#if defined(GPU_SHADER) && !defined(DRAW_FINALIZE_SHADER)
|
||||
return (floatBitsToUint(object_infos.infos.y) >> 8u) & 0xFFu;
|
||||
#else
|
||||
return (object_infos.light_and_shadow_set_membership >> 8u) & 0xFFu;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct ObjectBounds {
|
||||
/**
|
||||
* Uploaded as vertex (0, 4, 3, 1) of the bbox in local space, matching XYZ axis order.
|
||||
|
||||
@@ -244,7 +244,10 @@ void View::compute_procedural_bounds()
|
||||
GPU_debug_group_end();
|
||||
}
|
||||
|
||||
void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze)
|
||||
void View::compute_visibility(ObjectBoundsBuf &bounds,
|
||||
ObjectInfosBuf & /*infos*/,
|
||||
uint resource_len,
|
||||
bool debug_freeze)
|
||||
{
|
||||
if (debug_freeze && frozen_ == false) {
|
||||
data_freeze_[0] = static_cast<ViewMatrices>(data_[0]);
|
||||
|
||||
@@ -28,6 +28,7 @@ class Manager;
|
||||
|
||||
/* TODO: de-duplicate. */
|
||||
using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>;
|
||||
using ObjectInfosBuf = StorageArrayBuffer<ObjectInfos, 128>;
|
||||
using VisibilityBuf = StorageArrayBuffer<uint, 4, true>;
|
||||
|
||||
class View {
|
||||
@@ -173,7 +174,10 @@ class View {
|
||||
protected:
|
||||
/** Called from draw manager. */
|
||||
void bind();
|
||||
virtual void compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze);
|
||||
virtual void compute_visibility(ObjectBoundsBuf &bounds,
|
||||
ObjectInfosBuf &infos,
|
||||
uint resource_len,
|
||||
bool debug_freeze);
|
||||
virtual VisibilityBuf &get_visibility_buffer();
|
||||
|
||||
void update_viewport_size();
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
|
||||
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
|
||||
|
||||
shared uint shared_result;
|
||||
|
||||
void mask_visibility_bit(uint view_id)
|
||||
{
|
||||
if (view_len > 1) {
|
||||
|
||||
@@ -76,6 +76,16 @@ void set_flag_from_test(inout int value, bool test, int flag)
|
||||
/* Keep define to match C++ implementation. */
|
||||
#define SET_FLAG_FROM_TEST(value, test, flag) set_flag_from_test(value, test, flag)
|
||||
|
||||
/**
|
||||
* Return true if the bit inside bitmask at bit_index is set high.
|
||||
* Assume the lower bits are inside first component of bitmask,
|
||||
*/
|
||||
bool bitmask64_test(uvec2 bitmask, uint bit_index)
|
||||
{
|
||||
uint bitmask32 = (bit_index >= 32u) ? bitmask.y : bitmask.x;
|
||||
return flag_test(bitmask32, 1u << (bit_index & 0x1Fu));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pack two 16-bit uint into one 32-bit uint.
|
||||
*/
|
||||
|
||||
@@ -1294,7 +1294,8 @@ typedef enum eUserpref_UI_Flag {
|
||||
USER_ZOOM_TO_MOUSEPOS = (1 << 20),
|
||||
USER_SHOW_FPS = (1 << 21),
|
||||
USER_REGISTER_ALL_USERS = (1 << 22),
|
||||
USER_UIFLAG_UNUSED_4 = (1 << 23), /* Cleared. */
|
||||
/** Actually implemented in .py. */
|
||||
USER_FILTER_BRUSHES_BY_TOOL = (1 << 23),
|
||||
USER_CONTINUOUS_MOUSE = (1 << 24),
|
||||
USER_ZOOM_INVERT = (1 << 25),
|
||||
USER_ZOOM_HORIZ = (1 << 26), /* for CONTINUE and DOLLY zoom */
|
||||
|
||||
@@ -170,6 +170,32 @@ static GreasePencilFrame *rna_Frames_frame_copy(ID *id,
|
||||
return layer.frame_at(to_frame_number);
|
||||
}
|
||||
|
||||
static GreasePencilFrame *rna_Frames_frame_move(ID *id,
|
||||
GreasePencilLayer *layer_in,
|
||||
ReportList *reports,
|
||||
int from_frame_number,
|
||||
int to_frame_number)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
GreasePencil &grease_pencil = *reinterpret_cast<GreasePencil *>(id);
|
||||
Layer &layer = static_cast<GreasePencilLayer *>(layer_in)->wrap();
|
||||
|
||||
if (!layer.frames().contains(from_frame_number)) {
|
||||
BKE_reportf(reports, RPT_ERROR, "Frame doesn't exists on frame number %d", from_frame_number);
|
||||
return nullptr;
|
||||
}
|
||||
if (layer.frames().contains(to_frame_number)) {
|
||||
BKE_reportf(reports, RPT_ERROR, "Frame already exists on frame number %d", to_frame_number);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
grease_pencil.insert_duplicate_frame(layer, from_frame_number, to_frame_number, true);
|
||||
grease_pencil.remove_frames(layer, {from_frame_number});
|
||||
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, &grease_pencil);
|
||||
|
||||
return layer.frame_at(to_frame_number);
|
||||
}
|
||||
|
||||
static GreasePencilFrame *rna_GreasePencilLayer_get_frame_at(GreasePencilLayer *layer,
|
||||
int frame_number)
|
||||
{
|
||||
@@ -520,6 +546,32 @@ void RNA_api_grease_pencil_frames(StructRNA *srna)
|
||||
"Let the copied frame use the same drawing as the source");
|
||||
parm = RNA_def_pointer(func, "copy", "GreasePencilFrame", "", "The newly copied frame");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "move", "rna_Frames_frame_move");
|
||||
RNA_def_function_ui_description(func, "Move a Grease Pencil frame");
|
||||
RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_SELF_ID);
|
||||
parm = RNA_def_int(func,
|
||||
"from_frame_number",
|
||||
1,
|
||||
MINAFRAME,
|
||||
MAXFRAME,
|
||||
"Source Frame Number",
|
||||
"The frame number of the source frame",
|
||||
MINAFRAME,
|
||||
MAXFRAME);
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
parm = RNA_def_int(func,
|
||||
"to_frame_number",
|
||||
2,
|
||||
MINAFRAME,
|
||||
MAXFRAME,
|
||||
"Target Frame Number",
|
||||
"The frame number to move the frame to",
|
||||
MINAFRAME,
|
||||
MAXFRAME);
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
parm = RNA_def_pointer(func, "moved", "GreasePencilFrame", "", "The moved frame");
|
||||
RNA_def_function_return(func, parm);
|
||||
}
|
||||
|
||||
void RNA_api_grease_pencil_layer(StructRNA *srna)
|
||||
|
||||
@@ -5323,6 +5323,14 @@ static void rna_def_userdef_view(BlenderRNA *brna)
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "uiflag", USER_PLAINMENUS);
|
||||
RNA_def_property_ui_text(prop, "Toolbox Column Layout", "Use a column layout for toolbox");
|
||||
|
||||
prop = RNA_def_property(srna, "use_filter_brushes_by_tool", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "uiflag", USER_FILTER_BRUSHES_BY_TOOL);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Filter Brushes by Tool",
|
||||
"Only show brushes applicable for the currently active tool in the "
|
||||
"asset shelf. Stored in the Preferences, which may have to be saved "
|
||||
"manually if Auto-Save Preferences is disabled");
|
||||
|
||||
static const EnumPropertyItem header_align_items[] = {
|
||||
{0, "NONE", 0, "Keep Existing", "Keep existing header alignment"},
|
||||
{USER_HEADER_FROM_PREF, "TOP", 0, "Top", "Top aligned on load"},
|
||||
|
||||
@@ -746,7 +746,7 @@ if(WITH_CYCLES OR WITH_GPU_RENDER_TESTS)
|
||||
|
||||
if(WITH_GPU_RENDER_TESTS)
|
||||
list(APPEND gpu_render_tests ${render_tests})
|
||||
list(FILTER gpu_render_tests EXCLUDE REGEX light_group|light_linking|shadow_catcher|denoise|guiding|reports)
|
||||
list(FILTER gpu_render_tests EXCLUDE REGEX light_group|shadow_catcher|denoise|guiding|reports)
|
||||
|
||||
set(_gpu_render_tests_arguments)
|
||||
if(WITH_GPU_RENDER_TESTS_SILENT)
|
||||
|
||||
Reference in New Issue
Block a user