GPv3: Python API for frame, drawing and drawing attributes #124787
@ -10,7 +10,6 @@ import bpy
|
||||
import gpu
|
||||
from mathutils import Matrix
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
import array
|
||||
import time
|
||||
|
||||
start_time = time.time()
|
||||
|
@ -186,13 +186,24 @@ LightTreeEmitter::LightTreeEmitter(Scene *scene,
|
||||
else if (type == LIGHT_SPOT) {
|
||||
measure.bcone.theta_o = 0;
|
||||
|
||||
const float unscaled_theta_e = lamp->get_spot_angle() * 0.5f;
|
||||
float theta_e = min(lamp->get_spot_angle() * 0.5f, M_PI_2_F);
|
||||
const float len_u = len(lamp->get_axisu());
|
||||
const float len_v = len(lamp->get_axisv());
|
||||
const float len_w = len(lamp->get_dir());
|
||||
|
||||
measure.bcone.theta_e = fast_atanf(fast_tanf(unscaled_theta_e) * fmaxf(len_u, len_v) /
|
||||
len_w);
|
||||
/* As `theta_e` approaches `pi/2`, the behaviour of `atan(tan(theta_e))` can become quite
|
||||
* unpredicatable as `tan(x)` has an asymptote at `x = pi/2`. To avoid this, we skip the back
|
||||
* and forward conversion.
|
||||
* The conversion is required to deal with scaled lights, but near `pi/2` the scaling does
|
||||
* not make a big difference in the angle, so we can skip the conversion without worrying
|
||||
* about overestimation. */
|
||||
if (fabsf(M_PI_2_F - theta_e) < 1e-6f) {
|
||||
theta_e = M_PI_2_F;
|
||||
}
|
||||
else {
|
||||
theta_e = fast_atanf(fast_tanf(theta_e) * fmaxf(len_u, len_v) / len_w);
|
||||
}
|
||||
measure.bcone.theta_e = theta_e;
|
||||
|
||||
/* Point and spot lights can emit light from any point within its radius. */
|
||||
const float3 radius = make_float3(size);
|
||||
|
@ -118,13 +118,13 @@ report_error_on_address(const void *vmemh, const char *message, ...)
|
||||
return;
|
||||
}
|
||||
|
||||
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
|
||||
size_t len = MEMHEAD_LEN(memh);
|
||||
const MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
|
||||
const size_t len = MEMHEAD_LEN(memh);
|
||||
|
||||
void *address = memh;
|
||||
const void *address = memh;
|
||||
size_t size = len + sizeof(*memh);
|
||||
if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
|
||||
MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
|
||||
const MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
|
||||
address = MEMHEAD_REAL_PTR(memh_aligned);
|
||||
size = len + sizeof(*memh_aligned) + MEMHEAD_ALIGN_PADDING(memh_aligned->alignment);
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
EnumProperty,
|
||||
IntProperty,
|
||||
PointerProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
@ -3046,9 +3046,9 @@ class EXTENSIONS_OT_package_install(Operator, _ExtCmdMixIn):
|
||||
"Repository found:",
|
||||
"\"{:s}\"".format(repo_from_url_name),
|
||||
lambda layout: layout.separator(),
|
||||
"The extension dropped may be incompatible.",
|
||||
"Check for an extension compatible with:",
|
||||
"Blender v{:s} on \"{:s}\".".format(
|
||||
"The extension dropped was not found in the remote repository.",
|
||||
"Check this is part of the repository and compatible with:",
|
||||
"Blender version {:s} on \"{:s}\".".format(
|
||||
".".join(str(v) for v in bpy.app.version), platform_from_this_system(),
|
||||
)
|
||||
]
|
||||
|
@ -1578,6 +1578,32 @@ def pkg_manifest_validate_field_type(value: str, strict: bool) -> Optional[str]:
|
||||
return None
|
||||
|
||||
|
||||
def pkg_manifest_validate_field_blender_version(
|
||||
value: str,
|
||||
strict: bool,
|
||||
) -> Optional[str]:
|
||||
if (error := pkg_manifest_validate_field_any_version_primitive(value, strict)) is not None:
|
||||
return error
|
||||
|
||||
if strict:
|
||||
# NOTE: Blender's extension support allows `X`, `X.X`, `X.X.X`,
|
||||
# Blender's own extensions site doesn't, so require this for validation.
|
||||
if value.count(".") != 2:
|
||||
return "expected 3 numbers separated by \".\", found \"{:s}\"".format(value)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def pkg_manifest_validate_field_blender_version_or_empty(
|
||||
value: str,
|
||||
strict: bool,
|
||||
) -> Optional[str]:
|
||||
if value:
|
||||
return pkg_manifest_validate_field_blender_version(value, strict)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def pkg_manifest_validate_field_tagline(value: str, strict: bool) -> Optional[str]:
|
||||
if strict:
|
||||
return pkg_manifest_validate_terse_description_or_error(value)
|
||||
@ -1790,10 +1816,10 @@ pkg_manifest_known_keys_and_types: Tuple[
|
||||
("type", str, pkg_manifest_validate_field_type),
|
||||
("maintainer", str, pkg_manifest_validate_field_any_non_empty_string_stripped_no_control_chars),
|
||||
("license", list, pkg_manifest_validate_field_any_non_empty_list_of_non_empty_strings),
|
||||
("blender_version_min", str, pkg_manifest_validate_field_any_version_primitive),
|
||||
("blender_version_min", str, pkg_manifest_validate_field_blender_version),
|
||||
|
||||
# Optional.
|
||||
("blender_version_max", str, pkg_manifest_validate_field_any_version_primitive_or_empty),
|
||||
("blender_version_max", str, pkg_manifest_validate_field_blender_version_or_empty),
|
||||
("website", str, pkg_manifest_validate_field_any_non_empty_string_stripped_no_control_chars),
|
||||
("copyright", list, pkg_manifest_validate_field_copyright),
|
||||
# Type should be `dict` eventually, some existing packages will have a list of strings instead.
|
||||
@ -2181,7 +2207,7 @@ def blender_version_parse_or_error(version: str) -> Union[Tuple[int, int, int],
|
||||
# `mypy` can't detect that this is guaranteed to be 3 items.
|
||||
return (
|
||||
version_tuple if (len(version_tuple) == 3) else
|
||||
(*version_tuple, (0, 0))[:3] # type: ignore
|
||||
(*version_tuple, 0, 0)[:3] # type: ignore
|
||||
)
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
bl_info = {
|
||||
'name': 'glTF 2.0 format',
|
||||
'author': 'Julien Duroure, Scurest, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
|
||||
"version": (4, 3, 9),
|
||||
"version": (4, 3, 12),
|
||||
'blender': (4, 2, 0),
|
||||
'location': 'File > Import-Export',
|
||||
'description': 'Import-Export as glTF 2.0',
|
||||
@ -651,7 +651,8 @@ class ExportGLTF2_Base(ConvertGLTF2_Base):
|
||||
|
||||
export_pointer_animation: BoolProperty(
|
||||
name='Export Animation Pointer (Experimental)',
|
||||
description='Export material, Light & Camera animation as Animation Pointer',
|
||||
description='Export material, Light & Camera animation as Animation Pointer. '
|
||||
'Available only for baked animation mode \'NLA Tracks\' and \'Scene\'',
|
||||
default=False
|
||||
)
|
||||
|
||||
@ -1650,9 +1651,8 @@ def export_panel_animation_pointer(layout, operator):
|
||||
header.prop(operator, "export_pointer_animation", text="")
|
||||
header.label(text="Animation Pointer (Experimental)")
|
||||
if body:
|
||||
|
||||
row = body.row()
|
||||
row.active = operator.export_pointer_animation
|
||||
row.active = header.active and operator.export_pointer_animation
|
||||
row.prop(operator, 'export_convert_animation_pointer')
|
||||
|
||||
|
||||
|
@ -32,6 +32,10 @@ def gather_actions_animations(export_settings):
|
||||
vtree = export_settings['vtree']
|
||||
for obj_uuid in vtree.get_all_objects():
|
||||
|
||||
# Do not manage real collections (if case of full hierarchy export)
|
||||
if vtree.nodes[obj_uuid].blender_type == VExportNode.COLLECTION:
|
||||
continue
|
||||
|
||||
# Do not manage not exported objects
|
||||
if vtree.nodes[obj_uuid].node is None:
|
||||
if export_settings["gltf_armature_object_remove"] is True:
|
||||
@ -69,6 +73,7 @@ def prepare_actions_range(export_settings):
|
||||
vtree = export_settings['vtree']
|
||||
for obj_uuid in vtree.get_all_objects():
|
||||
|
||||
# Do not manage real collections (if case of full hierarchy export)
|
||||
if vtree.nodes[obj_uuid].blender_type == VExportNode.COLLECTION:
|
||||
continue
|
||||
|
||||
@ -186,6 +191,10 @@ def prepare_actions_range(export_settings):
|
||||
# Need to store animation slides
|
||||
for obj_uuid in vtree.get_all_objects():
|
||||
|
||||
# Do not manage real collections (if case of full hierarchy export)
|
||||
if vtree.nodes[obj_uuid].blender_type == VExportNode.COLLECTION:
|
||||
continue
|
||||
|
||||
# Do not manage not exported objects
|
||||
if vtree.nodes[obj_uuid].node is None:
|
||||
if export_settings['gltf_armature_object_remove'] is True:
|
||||
|
@ -379,6 +379,11 @@ def armature_caching(data, obj_uuid, blender_obj, action_name, frame, export_set
|
||||
|
||||
def object_caching(data, obj_uuids, current_instance, action_name, frame, depsgraph, export_settings):
|
||||
for obj_uuid in obj_uuids:
|
||||
|
||||
# Do not cache real collection
|
||||
if export_settings['vtree'].nodes[obj_uuid].blender_type == VExportNode.COLLECTION:
|
||||
continue
|
||||
|
||||
blender_obj = export_settings['vtree'].nodes[obj_uuid].blender_object
|
||||
if blender_obj is None: # GN instance
|
||||
if export_settings['vtree'].nodes[obj_uuid].parent_uuid not in current_instance.keys():
|
||||
|
@ -682,13 +682,19 @@ class PrimitiveCreator:
|
||||
for u in range(u_tiles):
|
||||
for v in range(v_tiles):
|
||||
|
||||
if u != u_tiles - 1 and v != v_tiles - 1:
|
||||
# Check if this tile exists
|
||||
if int("10" + str(v) + str(u + 1)) not in tiles:
|
||||
continue
|
||||
|
||||
# Manage tile limits (inclusive or not), avoiding to have the same vertex
|
||||
# in two tiles, if the vertex is on the limit
|
||||
if int("10" + str(v) + str(u + 1 + 1)) in tiles and int("10" + str(v + 1) + str(u + 1)) in tiles:
|
||||
indices = np.where((self.dots[uvmap_name + '0'] >= u) & (self.dots[uvmap_name + '0'] < (u + 1)) & (
|
||||
self.dots[uvmap_name + '1'] <= (1 - v)) & (self.dots[uvmap_name + '1'] > 1 - (v + 1)))[0]
|
||||
elif u == u_tiles - 1 and v != v_tiles - 1:
|
||||
elif int("10" + str(v) + str(u + 1 + 1)) not in tiles and int("10" + str(v + 1) + str(u + 1)) in tiles:
|
||||
indices = np.where((self.dots[uvmap_name + '0'] >= u) & (self.dots[uvmap_name + '0'] <= (u + 1)) & (
|
||||
self.dots[uvmap_name + '1'] <= (1 - v)) & (self.dots[uvmap_name + '1'] > 1 - (v + 1)))[0]
|
||||
elif u != u_tiles - 1 and v == v_tiles - 1:
|
||||
elif int("10" + str(v) + str(u + 1 + 1)) in tiles and int("10" + str(v + 1) + str(u + 1)) not in tiles:
|
||||
indices = np.where((self.dots[uvmap_name + '0'] >= u) & (self.dots[uvmap_name + '0'] < (u + 1)) & (
|
||||
self.dots[uvmap_name + '1'] <= (1 - v)) & (self.dots[uvmap_name + '1'] >= 1 - (v + 1)))[0]
|
||||
else:
|
||||
|
@ -17,7 +17,6 @@ bl_info = {
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
StringProperty,
|
||||
)
|
||||
|
@ -6,7 +6,6 @@ import bpy
|
||||
from bpy.types import Panel, Menu
|
||||
from bpy.props import StringProperty
|
||||
from bpy.app.translations import contexts as i18n_contexts
|
||||
from nodeitems_utils import node_categories_iter, NodeItemCustom
|
||||
|
||||
from . import operators
|
||||
|
||||
|
@ -33,7 +33,7 @@ from .utils.constants import blend_types, geo_combine_operations, operations, na
|
||||
from .utils.draw import draw_callback_nodeoutline
|
||||
from .utils.paths import match_files_to_socket_names, split_into_components
|
||||
from .utils.nodes import (node_mid_pt, autolink, node_at_pos, get_nodes_links,
|
||||
get_group_output_node, get_output_location, force_update, get_internal_socket, nw_check,
|
||||
force_update, nw_check,
|
||||
nw_check_not_empty, nw_check_selected, nw_check_active, nw_check_space_type,
|
||||
nw_check_node_type, nw_check_visible_outputs, nw_check_viewer_node, NWBase,
|
||||
get_first_enabled_output, is_visible_socket)
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
import bpy
|
||||
from bpy.props import EnumProperty, BoolProperty, StringProperty
|
||||
from nodeitems_utils import node_categories_iter
|
||||
|
||||
from . import operators
|
||||
from . import interface
|
||||
|
@ -8,15 +8,12 @@ Pose Library - GUI definition.
|
||||
|
||||
import bpy
|
||||
from bpy.types import (
|
||||
AssetHandle,
|
||||
AssetRepresentation,
|
||||
Context,
|
||||
Menu,
|
||||
Panel,
|
||||
UILayout,
|
||||
UIList,
|
||||
WindowManager,
|
||||
WorkSpace,
|
||||
)
|
||||
from bl_ui_utils.layout import operator_context
|
||||
|
||||
|
@ -27,7 +27,6 @@ from bpy.types import (
|
||||
Action,
|
||||
AssetRepresentation,
|
||||
Context,
|
||||
Event,
|
||||
Object,
|
||||
Operator,
|
||||
)
|
||||
|
@ -6,9 +6,6 @@ import bpy
|
||||
|
||||
from bpy.types import (
|
||||
Operator,
|
||||
Menu,
|
||||
Panel,
|
||||
UIList,
|
||||
PropertyGroup,
|
||||
)
|
||||
from bpy.props import (
|
||||
|
@ -4,14 +4,10 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import IntProperty, BoolProperty
|
||||
from bpy.props import BoolProperty
|
||||
|
||||
from bpy.app.translations import pgettext_data as data_
|
||||
|
||||
from bpy.props import (
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
|
||||
def add_empty_geometry_node_group(name):
|
||||
group = bpy.data.node_groups.new(name, 'GeometryNodeTree')
|
||||
|
@ -22,7 +22,6 @@ from bpy.props import (
|
||||
|
||||
from bpy.app.translations import (
|
||||
pgettext_tip as tip_,
|
||||
contexts as i18n_contexts,
|
||||
)
|
||||
from mathutils import Vector
|
||||
|
||||
|
@ -24,7 +24,6 @@ from mathutils import (
|
||||
from bpy.app.translations import (
|
||||
pgettext_tip as tip_,
|
||||
pgettext_rpt as rpt_,
|
||||
pgettext_data as data_,
|
||||
)
|
||||
|
||||
from nodeitems_builtins import node_tree_group_type
|
||||
|
@ -24,7 +24,7 @@ class LayerDataButtonsPanel:
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
grease_pencil = context.grease_pencil
|
||||
return grease_pencil and grease_pencil.layers.active_layer
|
||||
return grease_pencil and grease_pencil.layers.active
|
||||
|
||||
|
||||
class GREASE_PENCIL_UL_masks(UIList):
|
||||
@ -44,7 +44,7 @@ class GreasePencil_LayerMaskPanel:
|
||||
def draw_header(self, context):
|
||||
ob = context.object
|
||||
grease_pencil = ob.data
|
||||
layer = grease_pencil.layers.active_layer
|
||||
layer = grease_pencil.layers.active
|
||||
|
||||
self.layout.prop(layer, "use_masks", text="", toggle=0)
|
||||
|
||||
@ -52,7 +52,7 @@ class GreasePencil_LayerMaskPanel:
|
||||
layout = self.layout
|
||||
ob = context.object
|
||||
grease_pencil = ob.data
|
||||
layer = grease_pencil.layers.active_layer
|
||||
layer = grease_pencil.layers.active
|
||||
|
||||
layout = self.layout
|
||||
layout.enabled = layer.use_masks
|
||||
@ -86,7 +86,7 @@ class GreasePencil_LayerTransformPanel:
|
||||
|
||||
ob = context.object
|
||||
grease_pencil = ob.data
|
||||
layer = grease_pencil.layers.active_layer
|
||||
layer = grease_pencil.layers.active
|
||||
layout.active = not layer.lock
|
||||
|
||||
row = layout.row(align=True)
|
||||
@ -106,7 +106,7 @@ class GreasPencil_LayerRelationsPanel:
|
||||
|
||||
ob = context.object
|
||||
grease_pencil = ob.data
|
||||
layer = grease_pencil.layers.active_layer
|
||||
layer = grease_pencil.layers.active
|
||||
layout.active = not layer.lock
|
||||
|
||||
row = layout.row(align=True)
|
||||
@ -137,7 +137,7 @@ class GREASE_PENCIL_MT_layer_mask_add(Menu):
|
||||
layout = self.layout
|
||||
|
||||
grease_pencil = context.grease_pencil
|
||||
active_layer = grease_pencil.layers.active_layer
|
||||
active_layer = grease_pencil.layers.active
|
||||
found = False
|
||||
for layer in grease_pencil.layers:
|
||||
if layer == active_layer or layer.name in active_layer.mask_layers:
|
||||
@ -174,7 +174,7 @@ class GREASE_PENCIL_MT_grease_pencil_add_layer_extra(Menu):
|
||||
layout = self.layout
|
||||
ob = context.object
|
||||
grease_pencil = ob.data
|
||||
layer = grease_pencil.layers.active_layer
|
||||
layer = grease_pencil.layers.active
|
||||
space = context.space_data
|
||||
|
||||
if space.type == 'PROPERTIES':
|
||||
@ -223,7 +223,7 @@ class DATA_PT_grease_pencil_layers(DataButtonsPanel, Panel):
|
||||
layout = self.layout
|
||||
|
||||
grease_pencil = context.grease_pencil
|
||||
layer = grease_pencil.layers.active_layer
|
||||
layer = grease_pencil.layers.active
|
||||
|
||||
row = layout.row()
|
||||
row.template_grease_pencil_layer_tree()
|
||||
|
@ -291,7 +291,7 @@ class GREASE_PENCIL_MT_move_to_layer(Menu):
|
||||
|
||||
for i in range(len(grease_pencil.layers) - 1, -1, -1):
|
||||
layer = grease_pencil.layers[i]
|
||||
if layer == grease_pencil.layers.active_layer:
|
||||
if layer == grease_pencil.layers.active:
|
||||
icon = 'GREASEPENCIL'
|
||||
else:
|
||||
icon = 'NONE'
|
||||
@ -316,7 +316,7 @@ class GREASE_PENCIL_MT_layer_active(Menu):
|
||||
|
||||
for i in range(len(obd.layers) - 1, -1, -1):
|
||||
layer = obd.layers[i]
|
||||
if layer == obd.layers.active_layer:
|
||||
if layer == obd.layers.active:
|
||||
icon = 'GREASEPENCIL'
|
||||
else:
|
||||
icon = 'NONE'
|
||||
|
@ -10,7 +10,6 @@ from bl_ui.space_view3d import (
|
||||
VIEW3D_PT_shading_options,
|
||||
)
|
||||
from bl_ui.utils import PresetPanel
|
||||
from bpy.app.translations import pgettext_rpt as rpt_
|
||||
|
||||
|
||||
class RenderButtonsPanel:
|
||||
|
@ -870,7 +870,7 @@ class GreasePencilLayersDopeSheetPanel:
|
||||
return False
|
||||
|
||||
grease_pencil = ob.data
|
||||
active_layer = grease_pencil.layers.active_layer
|
||||
active_layer = grease_pencil.layers.active
|
||||
if active_layer:
|
||||
return True
|
||||
|
||||
@ -942,7 +942,7 @@ class DOPESHEET_PT_grease_pencil_mode(GreasePencilLayersDopeSheetPanel, Panel):
|
||||
|
||||
ob = context.object
|
||||
grease_pencil = ob.data
|
||||
active_layer = grease_pencil.layers.active_layer
|
||||
active_layer = grease_pencil.layers.active
|
||||
|
||||
if active_layer:
|
||||
row = layout.row(align=True)
|
||||
|
@ -35,7 +35,6 @@ from bl_ui.space_toolsystem_common import (
|
||||
|
||||
from bpy.app.translations import (
|
||||
contexts as i18n_contexts,
|
||||
pgettext_iface as iface_,
|
||||
)
|
||||
|
||||
|
||||
|
@ -7,7 +7,6 @@ from bpy.types import (
|
||||
Header,
|
||||
Menu,
|
||||
Panel,
|
||||
UIList,
|
||||
)
|
||||
from bpy.app.translations import (
|
||||
pgettext_iface as iface_,
|
||||
|
@ -10,7 +10,6 @@ from bpy.types import (
|
||||
)
|
||||
from bpy.app.translations import (
|
||||
contexts as i18n_contexts,
|
||||
pgettext_iface as iface_,
|
||||
pgettext_rpt as rpt_,
|
||||
)
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
|
@ -29,7 +29,6 @@ from bl_ui.properties_paint_common import (
|
||||
brush_settings,
|
||||
brush_settings_advanced,
|
||||
draw_color_settings,
|
||||
BrushAssetShelf,
|
||||
)
|
||||
from bl_ui.utils import PresetPanel
|
||||
|
||||
|
@ -675,7 +675,8 @@ class KeyframeStrip : public ::KeyframeActionStrip {
|
||||
/** Return the channelbag's index, or -1 if there is none for this slot handle. */
|
||||
int64_t find_channelbag_index(const ChannelBag &channelbag) const;
|
||||
|
||||
SingleKeyingResult keyframe_insert(const Slot &slot,
|
||||
SingleKeyingResult keyframe_insert(Main *bmain,
|
||||
const Slot &slot,
|
||||
FCurveDescriptor fcurve_descriptor,
|
||||
float2 time_value,
|
||||
const KeyframeSettings &settings,
|
||||
@ -713,15 +714,27 @@ class ChannelBag : public ::ActionChannelBag {
|
||||
/**
|
||||
* Find an FCurve matching the fcurve descriptor, or create one if it doesn't
|
||||
* exist.
|
||||
*
|
||||
* \param bmain Used to tag the dependency graph(s) for relationship
|
||||
* rebuilding. This is necessary when adding a new F-Curve, as a
|
||||
* previously-unanimated depsgraph component may become animated now. Can be
|
||||
* nullptr, in which case the tagging is skipped and is left as the
|
||||
* responsibility of the caller.
|
||||
*/
|
||||
FCurve &fcurve_ensure(FCurveDescriptor fcurve_descriptor);
|
||||
FCurve &fcurve_ensure(Main *bmain, FCurveDescriptor fcurve_descriptor);
|
||||
|
||||
/**
|
||||
* Create an F-Curve, but only if it doesn't exist yet in this ChannelBag.
|
||||
*
|
||||
* \return the F-Curve it it was created, or nullptr if it already existed.
|
||||
*
|
||||
* \param bmain Used to tag the dependency graph(s) for relationship
|
||||
* rebuilding. This is necessary when adding a new F-Curve, as a
|
||||
* previously-unanimated depsgraph component may become animated now. Can be
|
||||
* nullptr, in which case the tagging is skipped and is left as the
|
||||
* responsibility of the caller.
|
||||
*/
|
||||
FCurve *fcurve_create_unique(FCurveDescriptor fcurve_descriptor);
|
||||
FCurve *fcurve_create_unique(Main *bmain, FCurveDescriptor fcurve_descriptor);
|
||||
|
||||
/**
|
||||
* Remove an F-Curve from the ChannelBag.
|
||||
@ -742,9 +755,16 @@ class ChannelBag : public ::ActionChannelBag {
|
||||
/**
|
||||
* Create an F-Curve.
|
||||
*
|
||||
* Assumes that there is no such F-Curve yet on this ChannelBag.
|
||||
* Assumes that there is no such F-Curve yet on this ChannelBag. If it is
|
||||
* uncertain whether this is the case, use `fcurve_create_unique()` instead.
|
||||
*
|
||||
* \param bmain Used to tag the dependency graph(s) for relationship
|
||||
* rebuilding. This is necessary when adding a new F-Curve, as a
|
||||
* previously-unanimated depsgraph component may become animated now. Can be
|
||||
* nullptr, in which case the tagging is skipped and is left as the
|
||||
* responsibility of the caller.
|
||||
*/
|
||||
FCurve &fcurve_create(FCurveDescriptor fcurve_descriptor);
|
||||
FCurve &fcurve_create(Main *bmain, FCurveDescriptor fcurve_descriptor);
|
||||
};
|
||||
static_assert(sizeof(ChannelBag) == sizeof(::ActionChannelBag),
|
||||
"DNA struct and its C++ wrapper must have the same size");
|
||||
@ -848,6 +868,10 @@ Vector<FCurve *> fcurves_all(Action &action);
|
||||
* nullptr for layered actions. See the comments in the implementation for more
|
||||
* details.
|
||||
*
|
||||
* \note This function also ensures that dependency graph relationships are
|
||||
* rebuilt. This is necessary when adding a new F-Curve, as a
|
||||
* previously-unanimated depsgraph component may become animated now.
|
||||
*
|
||||
* \param ptr: RNA pointer for the struct the fcurve is being looked up/created
|
||||
* for. For legacy actions this is optional and may be null.
|
||||
*
|
||||
|
@ -1124,23 +1124,23 @@ FCurve *ChannelBag::fcurve_find(const FCurveDescriptor fcurve_descriptor)
|
||||
return animrig::fcurve_find(fcurves, fcurve_descriptor);
|
||||
}
|
||||
|
||||
FCurve &ChannelBag::fcurve_ensure(const FCurveDescriptor fcurve_descriptor)
|
||||
FCurve &ChannelBag::fcurve_ensure(Main *bmain, const FCurveDescriptor fcurve_descriptor)
|
||||
{
|
||||
if (FCurve *existing_fcurve = this->fcurve_find(fcurve_descriptor)) {
|
||||
return *existing_fcurve;
|
||||
}
|
||||
return this->fcurve_create(fcurve_descriptor);
|
||||
return this->fcurve_create(bmain, fcurve_descriptor);
|
||||
}
|
||||
|
||||
FCurve *ChannelBag::fcurve_create_unique(FCurveDescriptor fcurve_descriptor)
|
||||
FCurve *ChannelBag::fcurve_create_unique(Main *bmain, FCurveDescriptor fcurve_descriptor)
|
||||
{
|
||||
if (this->fcurve_find(fcurve_descriptor)) {
|
||||
return nullptr;
|
||||
}
|
||||
return &this->fcurve_create(fcurve_descriptor);
|
||||
return &this->fcurve_create(bmain, fcurve_descriptor);
|
||||
}
|
||||
|
||||
FCurve &ChannelBag::fcurve_create(FCurveDescriptor fcurve_descriptor)
|
||||
FCurve &ChannelBag::fcurve_create(Main *bmain, FCurveDescriptor fcurve_descriptor)
|
||||
{
|
||||
FCurve *new_fcurve = create_fcurve_for_channel(fcurve_descriptor);
|
||||
|
||||
@ -1149,6 +1149,11 @@ FCurve &ChannelBag::fcurve_create(FCurveDescriptor fcurve_descriptor)
|
||||
}
|
||||
|
||||
grow_array_and_append(&this->fcurve_array, &this->fcurve_array_num, new_fcurve);
|
||||
|
||||
if (bmain) {
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
return *new_fcurve;
|
||||
}
|
||||
|
||||
@ -1167,6 +1172,10 @@ bool ChannelBag::fcurve_remove(FCurve &fcurve_to_remove)
|
||||
dna::array::remove_index(
|
||||
&this->fcurve_array, &this->fcurve_array_num, nullptr, fcurve_index, fcurve_ptr_destructor);
|
||||
|
||||
/* As an optimisation, this function could call `DEG_relations_tag_update(bmain)` to prune any
|
||||
* relationships that are now no longer necessary. This is not needed for correctness of the
|
||||
* depsgraph evaluation results though. */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1175,7 +1184,8 @@ void ChannelBag::fcurves_clear()
|
||||
dna::array::clear(&this->fcurve_array, &this->fcurve_array_num, nullptr, fcurve_ptr_destructor);
|
||||
}
|
||||
|
||||
SingleKeyingResult KeyframeStrip::keyframe_insert(const Slot &slot,
|
||||
SingleKeyingResult KeyframeStrip::keyframe_insert(Main *bmain,
|
||||
const Slot &slot,
|
||||
const FCurveDescriptor fcurve_descriptor,
|
||||
const float2 time_value,
|
||||
const KeyframeSettings &settings,
|
||||
@ -1185,7 +1195,7 @@ SingleKeyingResult KeyframeStrip::keyframe_insert(const Slot &slot,
|
||||
* allow. */
|
||||
FCurve *fcurve = nullptr;
|
||||
if (key_insertion_may_create_fcurve(insert_key_flags)) {
|
||||
fcurve = &this->channelbag_for_slot_ensure(slot).fcurve_ensure(fcurve_descriptor);
|
||||
fcurve = &this->channelbag_for_slot_ensure(slot).fcurve_ensure(bmain, fcurve_descriptor);
|
||||
}
|
||||
else {
|
||||
ChannelBag *channels = this->channelbag_for_slot(slot);
|
||||
@ -1438,7 +1448,7 @@ FCurve *action_fcurve_ensure(Main *bmain,
|
||||
assert_baklava_phase_1_invariants(action);
|
||||
KeyframeStrip &strip = action.layer(0)->strip(0)->as<KeyframeStrip>();
|
||||
|
||||
return &strip.channelbag_for_slot_ensure(slot).fcurve_ensure(fcurve_descriptor);
|
||||
return &strip.channelbag_for_slot_ensure(slot).fcurve_ensure(bmain, fcurve_descriptor);
|
||||
}
|
||||
|
||||
/* Try to find f-curve matching for this setting.
|
||||
|
@ -144,9 +144,9 @@ TEST_F(ActionLayersTest, add_strip)
|
||||
/* Add some keys to check that also the strip data is freed correctly. */
|
||||
const KeyframeSettings settings = get_keyframe_settings(false);
|
||||
Slot &slot = action->slot_add();
|
||||
strip.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
strip.as<KeyframeStrip>().keyframe_insert(bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
another_strip.as<KeyframeStrip>().keyframe_insert(
|
||||
slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
}
|
||||
|
||||
TEST_F(ActionLayersTest, remove_strip)
|
||||
@ -159,9 +159,12 @@ TEST_F(ActionLayersTest, remove_strip)
|
||||
/* Add some keys to check that also the strip data is freed correctly. */
|
||||
const KeyframeSettings settings = get_keyframe_settings(false);
|
||||
Slot &slot = action->slot_add();
|
||||
strip0.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
strip1.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
strip2.as<KeyframeStrip>().keyframe_insert(slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
strip0.as<KeyframeStrip>().keyframe_insert(
|
||||
bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
strip1.as<KeyframeStrip>().keyframe_insert(
|
||||
bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
strip2.as<KeyframeStrip>().keyframe_insert(
|
||||
bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
|
||||
EXPECT_TRUE(layer.strip_remove(strip1));
|
||||
EXPECT_EQ(2, layer.strips().size());
|
||||
@ -555,7 +558,7 @@ TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
|
||||
|
||||
const KeyframeSettings settings = get_keyframe_settings(false);
|
||||
SingleKeyingResult result_loc_a = key_strip.keyframe_insert(
|
||||
slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
bmain, slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
ASSERT_EQ(SingleKeyingResult::SUCCESS, result_loc_a)
|
||||
<< "Expected keyframe insertion to be successful";
|
||||
|
||||
@ -566,7 +569,7 @@ TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
|
||||
|
||||
/* Insert a second key, should insert into the same FCurve as before. */
|
||||
SingleKeyingResult result_loc_b = key_strip.keyframe_insert(
|
||||
slot, {"location", 0}, {5.0f, 47.1f}, settings);
|
||||
bmain, slot, {"location", 0}, {5.0f, 47.1f}, settings);
|
||||
EXPECT_EQ(SingleKeyingResult::SUCCESS, result_loc_b);
|
||||
ASSERT_EQ(1, channels->fcurves().size()) << "Expect insertion with the same (slot/rna "
|
||||
"path/array index) tuple to go into the same FCurve";
|
||||
@ -579,7 +582,7 @@ TEST_F(ActionLayersTest, KeyframeStrip__keyframe_insert)
|
||||
|
||||
/* Insert another key for another property, should create another FCurve. */
|
||||
SingleKeyingResult result_rot = key_strip.keyframe_insert(
|
||||
slot, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings);
|
||||
bmain, slot, {"rotation_quaternion", 0}, {1.0f, 0.25f}, settings);
|
||||
EXPECT_EQ(SingleKeyingResult::SUCCESS, result_rot);
|
||||
ASSERT_EQ(2, channels->fcurves().size()) << "Expected a second FCurve to be created.";
|
||||
EXPECT_EQ(2, channels->fcurves()[0]->totvert);
|
||||
|
@ -149,10 +149,10 @@ TEST_F(AnimationEvaluationTest, evaluate_layer__keyframes)
|
||||
KeyframeStrip &key_strip = strip.as<KeyframeStrip>();
|
||||
|
||||
/* Set some keys. */
|
||||
key_strip.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.1f}, settings);
|
||||
key_strip.keyframe_insert(*slot, {"location", 0}, {5.0f, 47.5f}, settings);
|
||||
key_strip.keyframe_insert(*slot, {"rotation_euler", 1}, {1.0f, 0.0f}, settings);
|
||||
key_strip.keyframe_insert(*slot, {"rotation_euler", 1}, {5.0f, 3.14f}, settings);
|
||||
key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.1f}, settings);
|
||||
key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 47.5f}, settings);
|
||||
key_strip.keyframe_insert(bmain, *slot, {"rotation_euler", 1}, {1.0f, 0.0f}, settings);
|
||||
key_strip.keyframe_insert(bmain, *slot, {"rotation_euler", 1}, {5.0f, 3.14f}, settings);
|
||||
|
||||
/* Set the animated properties to some values. These should not be overwritten
|
||||
* by the evaluation itself. */
|
||||
@ -189,9 +189,9 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__single_strip)
|
||||
|
||||
/* Set some keys. */
|
||||
KeyframeStrip &key_strip = strip.as<KeyframeStrip>();
|
||||
key_strip.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
|
||||
/* Evaluate the layer to see how it handles the boundaries + something in between. */
|
||||
EXPECT_TRUE(test_evaluate_layer("location", 0, {1.0f, 47.0f}));
|
||||
@ -213,15 +213,15 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__nonoverlapping)
|
||||
/* Set some keys. */
|
||||
{
|
||||
KeyframeStrip &key_strip1 = strip1.as<KeyframeStrip>();
|
||||
key_strip1.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip1.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip1.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
}
|
||||
{
|
||||
KeyframeStrip &key_strip2 = strip2.as<KeyframeStrip>();
|
||||
key_strip2.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip2.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip2.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
}
|
||||
|
||||
/* Check Strip 1. */
|
||||
@ -253,15 +253,15 @@ TEST_F(AnimationEvaluationTest, strip_boundaries__overlapping_edge)
|
||||
/* Set some keys. */
|
||||
{
|
||||
KeyframeStrip &key_strip1 = strip1.as<KeyframeStrip>();
|
||||
key_strip1.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip1.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip1.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip1.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
}
|
||||
{
|
||||
KeyframeStrip &key_strip2 = strip2.as<KeyframeStrip>();
|
||||
key_strip2.keyframe_insert(*slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip2.keyframe_insert(*slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip2.keyframe_insert(*slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {1.0f, 47.0f}, settings);
|
||||
key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {5.0f, 327.0f}, settings);
|
||||
key_strip2.keyframe_insert(bmain, *slot, {"location", 0}, {10.0f, 48.0f}, settings);
|
||||
}
|
||||
|
||||
/* Check Strip 1. */
|
||||
|
@ -877,7 +877,8 @@ struct KeyInsertData {
|
||||
int array_index;
|
||||
};
|
||||
|
||||
static SingleKeyingResult insert_key_layer(Layer &layer,
|
||||
static SingleKeyingResult insert_key_layer(Main *bmain,
|
||||
Layer &layer,
|
||||
const Slot &slot,
|
||||
const std::string &rna_path,
|
||||
const std::optional<PropertySubType> prop_subtype,
|
||||
@ -889,14 +890,16 @@ static SingleKeyingResult insert_key_layer(Layer &layer,
|
||||
BLI_assert(layer.strips().size() == 1);
|
||||
|
||||
Strip *strip = layer.strip(0);
|
||||
return strip->as<KeyframeStrip>().keyframe_insert(slot,
|
||||
return strip->as<KeyframeStrip>().keyframe_insert(bmain,
|
||||
slot,
|
||||
{rna_path, key_data.array_index, prop_subtype},
|
||||
key_data.position,
|
||||
key_settings,
|
||||
insert_key_flags);
|
||||
}
|
||||
|
||||
static CombinedKeyingResult insert_key_layered_action(Action &action,
|
||||
static CombinedKeyingResult insert_key_layered_action(Main *bmain,
|
||||
Action &action,
|
||||
PointerRNA *rna_pointer,
|
||||
const blender::Span<RNAPath> rna_paths,
|
||||
const float scene_frame,
|
||||
@ -955,7 +958,8 @@ static CombinedKeyingResult insert_key_layered_action(Action &action,
|
||||
}
|
||||
|
||||
const KeyInsertData key_data = {{scene_frame, rna_values[property_index]}, property_index};
|
||||
const SingleKeyingResult result = insert_key_layer(*layer,
|
||||
const SingleKeyingResult result = insert_key_layer(bmain,
|
||||
*layer,
|
||||
slot,
|
||||
*rna_path_id_to_prop,
|
||||
prop_subtype,
|
||||
@ -1002,7 +1006,8 @@ CombinedKeyingResult insert_keyframes(Main *bmain,
|
||||
KeyframeSettings key_settings = get_keyframe_settings(
|
||||
(insert_key_flags & INSERTKEY_NO_USERPREF) == 0);
|
||||
key_settings.keyframe_type = key_type;
|
||||
return insert_key_layered_action(action,
|
||||
return insert_key_layered_action(bmain,
|
||||
action,
|
||||
struct_pointer,
|
||||
rna_paths,
|
||||
scene_frame.value_or(anim_eval_context.eval_time),
|
||||
|
20
source/blender/blenkernel/BKE_compositor.hh
Normal file
20
source/blender/blenkernel/BKE_compositor.hh
Normal file
@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_set.hh"
|
||||
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
|
||||
namespace blender::bke::compositor {
|
||||
|
||||
/* Get the set of all passes used by the compositor for the given view layer, identified by their
|
||||
* pass names. */
|
||||
Set<std::string> get_used_passes(const Scene &scene, const ViewLayer *view_layer);
|
||||
|
||||
} // namespace blender::bke::compositor
|
@ -880,7 +880,8 @@ class GreasePencilRuntime {
|
||||
/**
|
||||
* Temporarily enable the eraser. Used by the draw tool.
|
||||
*/
|
||||
bool use_eraser_temp = false;
|
||||
bool temp_use_eraser = false;
|
||||
float temp_eraser_size = 0.0f;
|
||||
|
||||
std::unique_ptr<bake::BakeMaterialsList> bake_materials;
|
||||
|
||||
|
@ -94,6 +94,7 @@ set(SRC
|
||||
intern/collision.cc
|
||||
intern/colorband.cc
|
||||
intern/colortools.cc
|
||||
intern/compositor.cc
|
||||
intern/compute_contexts.cc
|
||||
intern/constraint.cc
|
||||
intern/context.cc
|
||||
@ -363,6 +364,7 @@ set(SRC
|
||||
BKE_collision.h
|
||||
BKE_colorband.hh
|
||||
BKE_colortools.hh
|
||||
BKE_compositor.hh
|
||||
BKE_compute_contexts.hh
|
||||
BKE_constraint.h
|
||||
BKE_context.hh
|
||||
|
154
source/blender/blenkernel/intern/compositor.cc
Normal file
154
source/blender/blenkernel/intern/compositor.cc
Normal file
@ -0,0 +1,154 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_compositor.hh"
|
||||
#include "BKE_cryptomatte.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
namespace blender::bke::compositor {
|
||||
|
||||
/* Adds the pass names of the passes used by the given Render Layer node to the given used passes.
|
||||
* This essentially adds the identifiers of the outputs that are logically linked, their the
|
||||
* identifiers are the names of the passes. Note however that the Image output is actually the
|
||||
* Combined pass, but named as Image for compatibility reasons. */
|
||||
static void add_passes_used_by_render_layer_node(const bNode *node, Set<std::string> &used_passes)
|
||||
{
|
||||
for (const bNodeSocket *output : node->output_sockets()) {
|
||||
if (output->is_logically_linked()) {
|
||||
if (StringRef(output->identifier) == "Image") {
|
||||
used_passes.add(RE_PASSNAME_COMBINED);
|
||||
}
|
||||
else {
|
||||
used_passes.add(output->identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Adds the pass names of all Cryptomatte layers needed by the given node to the given used passes.
|
||||
* Only passes in the given viewer layers are added. */
|
||||
static void add_passes_used_by_cryptomatte_node(const bNode *node,
|
||||
const ViewLayer *view_layer,
|
||||
Set<std::string> &used_passes)
|
||||
{
|
||||
if (node->custom1 != CMP_NODE_CRYPTOMATTE_SOURCE_RENDER) {
|
||||
return;
|
||||
}
|
||||
|
||||
Scene *scene = reinterpret_cast<Scene *>(node->id);
|
||||
if (!scene) {
|
||||
return;
|
||||
}
|
||||
|
||||