GPv3: Python API for frame, drawing and drawing attributes #124787

Merged
Falk David merged 22 commits from filedescriptor/blender:gpv3-drawing-python-api into main 2024-07-26 16:30:21 +02:00
95 changed files with 1569 additions and 552 deletions
Showing only changes of commit 4a9e0b2e90 - Show all commits

View File

@ -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()

View File

@ -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);

View File

@ -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);
}

View File

@ -26,7 +26,6 @@ import bpy
from bpy.props import (
BoolProperty,
EnumProperty,
IntProperty,
PointerProperty,
StringProperty,
)

View File

@ -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(),
)
]

View File

@ -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
)

View File

@ -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')

View File

@ -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:

View File

@ -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():

View File

@ -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:

View File

@ -17,7 +17,6 @@ bl_info = {
import bpy
from bpy.props import (
BoolProperty,
IntProperty,
StringProperty,
)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -27,7 +27,6 @@ from bpy.types import (
Action,
AssetRepresentation,
Context,
Event,
Object,
Operator,
)

View File

@ -6,9 +6,6 @@ import bpy
from bpy.types import (
Operator,
Menu,
Panel,
UIList,
PropertyGroup,
)
from bpy.props import (

View File

@ -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')

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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'

View File

@ -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:

View File

@ -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)

View File

@ -35,7 +35,6 @@ from bl_ui.space_toolsystem_common import (
from bpy.app.translations import (
contexts as i18n_contexts,
pgettext_iface as iface_,
)

View File

@ -7,7 +7,6 @@ from bpy.types import (
Header,
Menu,
Panel,
UIList,
)
from bpy.app.translations import (
pgettext_iface as iface_,

View File

@ -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 (

View File

@ -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

View File

@ -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.
*

View File

@ -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.

View File

@ -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);

View File

@ -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. */

View File

@ -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),

View 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

View File

@ -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;

View File

@ -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

View 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;
}
</