Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release')
m---------release/scripts/addons0
-rw-r--r--release/scripts/modules/addon_utils.py4
-rw-r--r--release/scripts/modules/animsys_refactor.py8
-rw-r--r--release/scripts/modules/bl_rna_utils/__init__.py0
-rw-r--r--release/scripts/modules/bl_rna_utils/data_path.py91
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py1
-rw-r--r--release/scripts/startup/bl_operators/anim.py16
-rw-r--r--release/scripts/startup/bl_operators/wm.py190
-rw-r--r--release/scripts/startup/bl_ui/properties_collection.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_armature.py9
-rw-r--r--release/scripts/startup/bl_ui/properties_material.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py1
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_common.py4
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py2
-rw-r--r--release/scripts/startup/bl_ui/space_graph.py5
-rw-r--r--release/scripts/startup/bl_ui/space_node.py8
-rw-r--r--release/scripts/startup/bl_ui/space_topbar.py9
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py12
-rw-r--r--release/scripts/startup/nodeitems_builtins.py32
-rw-r--r--release/scripts/templates_osl/basic_shader.osl10
-rw-r--r--release/scripts/templates_py/image_processing.py35
22 files changed, 352 insertions, 88 deletions
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject bcd08a9506d33bdd7358201031b04d041ef22d9
+Subproject 63492d3d0334e1827f611f8fe5a931f3ccbddfc
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index 83bed69d8d2..387691f9f05 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -349,6 +349,10 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
# 1) try import
try:
mod = __import__(module_name)
+ if mod.__file__ is None:
+ # This can happen when the addon has been removed but there are
+ # residual `.pyc` files left behind.
+ raise ImportError(name=module_name)
mod.__time__ = os.path.getmtime(mod.__file__)
mod.__addon_enabled__ = False
except Exception as ex:
diff --git a/release/scripts/modules/animsys_refactor.py b/release/scripts/modules/animsys_refactor.py
index 97e8a8dd144..fd4952e2a53 100644
--- a/release/scripts/modules/animsys_refactor.py
+++ b/release/scripts/modules/animsys_refactor.py
@@ -32,12 +32,6 @@ import bpy
IS_TESTING = False
-def drepr(string):
- # is there a less crappy way to do this in python?, re.escape also escapes
- # single quotes strings so can't use it.
- return '"%s"' % repr(string)[1:-1].replace("\"", "\\\"").replace("\\'", "'")
-
-
def classes_recursive(base_type, clss=None):
if clss is None:
clss = [base_type]
@@ -66,7 +60,7 @@ class DataPathBuilder:
if type(key) is int:
str_value = '[%d]' % key
elif type(key) is str:
- str_value = '[%s]' % drepr(key)
+ str_value = '["%s"]' % bpy.utils.escape_identifier(key)
else:
raise Exception("unsupported accessor %r of type %r (internal error)" % (key, type(key)))
return DataPathBuilder(self.data_path + (str_value, ))
diff --git a/release/scripts/modules/bl_rna_utils/__init__.py b/release/scripts/modules/bl_rna_utils/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/release/scripts/modules/bl_rna_utils/__init__.py
diff --git a/release/scripts/modules/bl_rna_utils/data_path.py b/release/scripts/modules/bl_rna_utils/data_path.py
new file mode 100644
index 00000000000..42942b7a295
--- /dev/null
+++ b/release/scripts/modules/bl_rna_utils/data_path.py
@@ -0,0 +1,91 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+__all__ = (
+ "property_definition_from_data_path",
+ "decompose_data_path",
+)
+
+class _TokenizeDataPath:
+ """
+ Class to split up tokens of a data-path.
+
+ Note that almost all access generates new objects with additional paths,
+ with the exception of iteration which is the intended way to access the resulting data."""
+ __slots__ = (
+ "data_path",
+ )
+
+ def __init__(self, attrs):
+ self.data_path = attrs
+
+ def __getattr__(self, attr):
+ return _TokenizeDataPath(self.data_path + ((".%s" % attr),))
+
+ def __getitem__(self, key):
+ return _TokenizeDataPath(self.data_path + (("[%r]" % (key,)),))
+
+ def __call__(self, *args, **kw):
+ value_str = ", ".join([
+ val for val in (
+ ", ".join(repr(value) for value in args),
+ ", ".join(["%s=%r" % (key, value) for key, value in kw.items()]),
+ ) if val])
+ return _TokenizeDataPath(self.data_path + ('(%s)' % value_str, ))
+
+ def __iter__(self):
+ return iter(self.data_path)
+
+
+def decompose_data_path(data_path):
+ """
+ Return the components of a data path split into a list.
+ """
+ ns = {"base": _TokenizeDataPath(())}
+ return list(eval("base" + data_path, ns, ns))
+
+
+def property_definition_from_data_path(base, data_path):
+ """
+ Return an RNA property definition from an object and a data path.
+
+ In Blender this is often used with ``context`` as the base and a
+ path that it references, for example ``.space_data.lock_camera``.
+ """
+ data = decompose_data_path(data_path)
+ while data and (not data[-1].startswith(".")):
+ data.pop()
+
+ if (not data) or (not data[-1].startswith(".")) or (len(data) < 2):
+ return None
+
+ data_path_head = "".join(data[:-1])
+ data_path_tail = data[-1]
+
+ value_head = eval("base" + data_path_head)
+ value_head_rna = getattr(value_head, "bl_rna", None)
+ if value_head_rna is None:
+ return None
+
+ value_tail = value_head.bl_rna.properties.get(data_path_tail[1:])
+ if not value_tail:
+ return None
+
+ return value_tail
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 2fb0e9a0bea..68e273f2244 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -1694,6 +1694,7 @@ def km_image(params):
("image.view_all", {"type": 'HOME', "value": 'PRESS', "shift": True},
{"properties": [("fit_view", True)]}),
("image.view_selected", {"type": 'NUMPAD_PERIOD', "value": 'PRESS'}, None),
+ ("image.view_cursor_center", {"type": 'C', "value": 'PRESS', "shift": True}, None),
("image.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("image.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
("image.view_pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py
index 8ec9bececc5..81a28964389 100644
--- a/release/scripts/startup/bl_operators/anim.py
+++ b/release/scripts/startup/bl_operators/anim.py
@@ -355,7 +355,8 @@ class UpdateAnimatedTransformConstraint(Operator):
use_convert_to_radians: BoolProperty(
name="Convert to Radians",
- description="Convert fcurves/drivers affecting rotations to radians (Warning: use this only once!)",
+ description="Convert f-curves/drivers affecting rotations to radians.\n"
+ "Warning: Use this only once",
default=True,
)
@@ -430,22 +431,9 @@ class UpdateAnimatedTransformConstraint(Operator):
return {'FINISHED'}
-class ANIM_OT_show_group_colors_deprecated(Operator):
- """This option moved to Preferences > Animation"""
-
- bl_idname = "anim.show_group_colors_deprecated"
- bl_label = "Show Group Colors"
- bl_options = {'REGISTER'}
-
- @classmethod
- def poll(cls, _context):
- return False
-
-
classes = (
ANIM_OT_keying_set_export,
NLA_OT_bake,
ClearUselessActions,
UpdateAnimatedTransformConstraint,
- ANIM_OT_show_group_colors_deprecated,
)
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 2f97942faa4..35826cea860 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -95,6 +95,76 @@ def context_path_validate(context, data_path):
return value
+def context_path_to_rna_property(context, data_path):
+ from bl_rna_utils.data_path import property_definition_from_data_path
+ rna_prop = property_definition_from_data_path(context, "." + data_path)
+ if rna_prop is not None:
+ return rna_prop
+ return None
+
+
+def context_path_decompose(data_path):
+ # Decompose a data_path into 3 components:
+ # base_path, prop_attr, prop_item, where:
+ # `"foo.bar["baz"].fiz().bob.buz[10][2]"`, returns...
+ # `("foo.bar["baz"].fiz().bob", "buz", "[10][2]")`
+ #
+ # This is useful as we often want the base and the property, ignoring any item access.
+ # Note that item access includes function calls since these aren't properties.
+ #
+ # Note that the `.` is removed from the start of the first and second values,
+ # this is done because `.attr` isn't convenient to use as an argument,
+ # also the convention is not to include this within the data paths or the operator logic for `bpy.ops.wm.*`.
+ from bl_rna_utils.data_path import decompose_data_path
+ path_split = decompose_data_path("." + data_path)
+
+ # Find the last property that isn't a function call.
+ value_prev = ""
+ i = len(path_split)
+ while (i := i - 1) >= 0:
+ value = path_split[i]
+ if value.startswith("."):
+ if not value_prev.startswith("("):
+ break
+ value_prev = value
+
+ if i != -1:
+ base_path = "".join(path_split[:i])
+ prop_attr = path_split[i]
+ prop_item = "".join(path_split[i + 1:])
+
+ if base_path:
+ assert(base_path.startswith("."))
+ base_path= base_path[1:]
+ if prop_attr:
+ assert(prop_attr.startswith("."))
+ prop_attr = prop_attr[1:]
+ else:
+ # If there are no properties, everything is an item.
+ # Note that should not happen in practice with values which are added onto `context`,
+ # include since it's correct to account for this case and not doing so will create a confusing exception.
+ base_path = ""
+ prop_attr = ""
+ prop_item = "".join(path_split)
+
+ return (base_path, prop_attr, prop_item)
+
+
+def description_from_data_path(base, data_path, *, prefix, value=Ellipsis):
+ if context_path_validate(base, data_path) is Ellipsis:
+ return None
+
+ if (
+ (rna_prop := context_path_to_rna_property(base, data_path)) and
+ (description := rna_prop.description)
+ ):
+ description = "%s: %s" % (prefix, description)
+ if value != Ellipsis:
+ description = "%s\n%s: %s" % (description, iface_("Value"), str(value))
+ return description
+ return None
+
+
def operator_value_is_undo(value):
if value in {None, Ellipsis}:
return False
@@ -120,12 +190,9 @@ def operator_value_is_undo(value):
def operator_path_is_undo(context, data_path):
- # note that if we have data paths that use strings this could fail
- # luckily we don't do this!
- #
- # When we can't find the data owner assume no undo is needed.
- data_path_head = data_path.rpartition(".")[0]
+ data_path_head, _, _ = context_path_decompose(data_path)
+ # When we can't find the data owner assume no undo is needed.
if not data_path_head:
return False
@@ -168,6 +235,10 @@ class WM_OT_context_set_boolean(Operator):
default=True,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
+
execute = execute_context_assign
@@ -185,6 +256,10 @@ class WM_OT_context_set_int(Operator): # same as enum
)
relative: rna_relative_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value)
+
execute = execute_context_assign
@@ -201,6 +276,10 @@ class WM_OT_context_scale_float(Operator):
default=1.0,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value)
+
def execute(self, context):
data_path = self.data_path
if context_path_validate(context, data_path) is Ellipsis:
@@ -235,6 +314,10 @@ class WM_OT_context_scale_int(Operator):
options={'SKIP_SAVE'},
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value)
+
def execute(self, context):
data_path = self.data_path
if context_path_validate(context, data_path) is Ellipsis:
@@ -274,6 +357,10 @@ class WM_OT_context_set_float(Operator): # same as enum
)
relative: rna_relative_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value)
+
execute = execute_context_assign
@@ -290,6 +377,10 @@ class WM_OT_context_set_string(Operator): # same as enum
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
+
execute = execute_context_assign
@@ -306,6 +397,10 @@ class WM_OT_context_set_enum(Operator):
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
+
execute = execute_context_assign
@@ -322,6 +417,10 @@ class WM_OT_context_set_value(Operator):
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
+
def execute(self, context):
data_path = self.data_path
if context_path_validate(context, data_path) is Ellipsis:
@@ -339,6 +438,13 @@ class WM_OT_context_toggle(Operator):
data_path: rna_path_prop
module: rna_module_prop
+ @classmethod
+ def description(cls, context, props):
+ # Currently unsupported, it might be possible to extract this.
+ if props.module:
+ return None
+ return description_from_data_path(context, props.data_path, prefix=iface_("Toggle"))
+
def execute(self, context):
data_path = self.data_path
@@ -375,6 +481,11 @@ class WM_OT_context_toggle_enum(Operator):
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ value = "(%r, %r)" % (props.value_1, props.value_2)
+ return description_from_data_path(context, props.data_path, prefix=iface_("Toggle"), value=value)
+
def execute(self, context):
data_path = self.data_path
@@ -406,6 +517,10 @@ class WM_OT_context_cycle_int(Operator):
reverse: rna_reverse_prop
wrap: rna_wrap_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
+
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
@@ -442,6 +557,10 @@ class WM_OT_context_cycle_enum(Operator):
reverse: rna_reverse_prop
wrap: rna_wrap_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
+
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
@@ -450,22 +569,11 @@ class WM_OT_context_cycle_enum(Operator):
orig_value = value
- # Have to get rna enum values
- rna_struct_str, rna_prop_str = data_path.rsplit('.', 1)
- i = rna_prop_str.find('[')
-
- # just in case we get "context.foo.bar[0]"
- if i != -1:
- rna_prop_str = rna_prop_str[0:i]
-
- rna_struct = eval("context.%s.rna_type" % rna_struct_str)
-
- rna_prop = rna_struct.properties[rna_prop_str]
-
+ rna_prop = context_path_to_rna_property(context, data_path)
if type(rna_prop) != bpy.types.EnumProperty:
raise Exception("expected an enum property")
- enums = rna_struct.properties[rna_prop_str].enum_items.keys()
+ enums = rna_prop.enum_items.keys()
orig_index = enums.index(orig_value)
# Have the info we need, advance to the next item.
@@ -498,6 +606,10 @@ class WM_OT_context_cycle_array(Operator):
data_path: rna_path_prop
reverse: rna_reverse_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
+
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
@@ -523,6 +635,10 @@ class WM_OT_context_menu_enum(Operator):
data_path: rna_path_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Menu"))
+
def execute(self, context):
data_path = self.data_path
value = context_path_validate(context, data_path)
@@ -530,15 +646,15 @@ class WM_OT_context_menu_enum(Operator):
if value is Ellipsis:
return {'PASS_THROUGH'}
- base_path, prop_string = data_path.rsplit(".", 1)
+ base_path, prop_attr, _ = context_path_decompose(data_path)
value_base = context_path_validate(context, base_path)
- prop = value_base.bl_rna.properties[prop_string]
+ rna_prop = context_path_to_rna_property(context, data_path)
def draw_cb(self, context):
layout = self.layout
- layout.prop(value_base, prop_string, expand=True)
+ layout.prop(value_base, prop_attr, expand=True)
- context.window_manager.popup_menu(draw_func=draw_cb, title=prop.name, icon=prop.icon)
+ context.window_manager.popup_menu(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon)
return {'FINISHED'}
@@ -550,6 +666,10 @@ class WM_OT_context_pie_enum(Operator):
data_path: rna_path_prop
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu"))
+
def invoke(self, context, event):
wm = context.window_manager
data_path = self.data_path
@@ -558,15 +678,15 @@ class WM_OT_context_pie_enum(Operator):
if value is Ellipsis:
return {'PASS_THROUGH'}
- base_path, prop_string = data_path.rsplit(".", 1)
+ base_path, prop_attr, _ = context_path_decompose(data_path)
value_base = context_path_validate(context, base_path)
- prop = value_base.bl_rna.properties[prop_string]
+ rna_prop = context_path_to_rna_property(context, data_path)
def draw_cb(self, context):
layout = self.layout
- layout.prop(value_base, prop_string, expand=True)
+ layout.prop(value_base, prop_attr, expand=True)
- wm.popup_menu_pie(draw_func=draw_cb, title=prop.name, icon=prop.icon, event=event)
+ wm.popup_menu_pie(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon, event=event)
return {'FINISHED'}
@@ -587,11 +707,15 @@ class WM_OT_operator_pie_enum(Operator):
maxlen=1024,
)
+ @classmethod
+ def description(cls, context, props):
+ return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu"))
+
def invoke(self, context, event):
wm = context.window_manager
data_path = self.data_path
- prop_string = self.prop_string
+ prop_attr = self.prop_string
# same as eval("bpy.ops." + data_path)
op_mod_str, ob_id_str = data_path.split(".", 1)
@@ -607,7 +731,7 @@ class WM_OT_operator_pie_enum(Operator):
def draw_cb(self, context):
layout = self.layout
pie = layout.menu_pie()
- pie.operator_enum(data_path, prop_string)
+ pie.operator_enum(data_path, prop_attr)
wm.popup_menu_pie(draw_func=draw_cb, title=op_rna.name, event=event)
@@ -631,17 +755,17 @@ class WM_OT_context_set_id(Operator):
value = self.value
data_path = self.data_path
- # match the pointer type from the target property to bpy.data.*
+ # Match the pointer type from the target property to `bpy.data.*`
# so we lookup the correct list.
- data_path_base, data_path_prop = data_path.rsplit(".", 1)
- data_prop_rna = eval("context.%s" % data_path_base).rna_type.properties[data_path_prop]
- data_prop_rna_type = data_prop_rna.fixed_type
+
+ rna_prop = context_path_to_rna_property(context, data_path)
+ rna_prop_fixed_type = rna_prop.fixed_type
id_iter = None
for prop in bpy.data.rna_type.properties:
if prop.rna_type.identifier == "CollectionProperty":
- if prop.fixed_type == data_prop_rna_type:
+ if prop.fixed_type == rna_prop_fixed_type:
id_iter = prop.identifier
break
diff --git a/release/scripts/startup/bl_ui/properties_collection.py b/release/scripts/startup/bl_ui/properties_collection.py
index 186314f9591..c5c35121135 100644
--- a/release/scripts/startup/bl_ui/properties_collection.py
+++ b/release/scripts/startup/bl_ui/properties_collection.py
@@ -77,6 +77,7 @@ class COLLECTION_PT_instancing(CollectionButtonsPanel, Panel):
class COLLECTION_PT_lineart_collection(CollectionButtonsPanel, Panel):
bl_label = "Line Art"
+ bl_order = 10
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py
index 4cdcab45926..87572fcd438 100644
--- a/release/scripts/startup/bl_ui/properties_data_armature.py
+++ b/release/scripts/startup/bl_ui/properties_data_armature.py
@@ -86,12 +86,19 @@ class DATA_PT_display(ArmatureButtonsPanel, Panel):
col = layout.column(heading="Show")
col.prop(arm, "show_names", text="Names")
- col.prop(arm, "show_axes", text="Axes")
col.prop(arm, "show_bone_custom_shapes", text="Shapes")
col.prop(arm, "show_group_colors", text="Group Colors")
+
if ob:
col.prop(ob, "show_in_front", text="In Front")
+ col = layout.column(align=False, heading="Axes")
+ row = col.row(align=True)
+ row.prop(arm, "show_axes", text="")
+ sub = row.row(align=True)
+ sub.active = arm.show_axes
+ sub.prop(arm, "axes_position", text="Position")
+
class DATA_MT_bone_group_context_menu(Menu):
bl_label = "Bone Group Specials"
diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py
index d85078d4ec2..aca7ba3c5ad 100644
--- a/release/scripts/startup/bl_ui/properties_material.py
+++ b/release/scripts/startup/bl_ui/properties_material.py
@@ -277,6 +277,7 @@ class MATERIAL_PT_viewport(MaterialButtonsPanel, Panel):
class MATERIAL_PT_lineart(MaterialButtonsPanel, Panel):
bl_label = "Line Art"
bl_options = {'DEFAULT_CLOSED'}
+ bl_order = 10
@classmethod
def poll(cls, context):
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index b74100aa570..033e6196323 100644
--- a/release/scripts/startup/bl_ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -311,6 +311,7 @@ class OBJECT_PT_instancing_size(ObjectButtonsPanel, Panel):
class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
bl_label = "Line Art"
bl_options = {'DEFAULT_CLOSED'}
+ bl_order = 10
@classmethod
def poll(cls, context):
diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py
index 82f43790d72..4ddb4953fbd 100644
--- a/release/scripts/startup/bl_ui/properties_physics_common.py
+++ b/release/scripts/startup/bl_ui/properties_physics_common.py
@@ -74,10 +74,6 @@ class PHYSICS_PT_add(PhysicButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
- row = layout.row(align=True)
- row.alignment = 'LEFT'
- row.label(text="Enable physics for:")
-
flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=True)
obj = context.object
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index 84f83559da6..e7893b8c448 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -348,8 +348,6 @@ class DOPESHEET_MT_view(Menu):
col.active = context.space_data.mode != 'SHAPEKEY'
col.prop(st, "show_sliders")
- if bpy.app.version < (2, 93):
- layout.operator("anim.show_group_colors_deprecated", icon='CHECKBOX_HLT')
layout.prop(st, "show_interpolation")
layout.prop(st, "show_extremes")
layout.prop(st, "use_auto_merge_keyframes")
diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py
index 9ba3bd8f8cc..f8521592dd9 100644
--- a/release/scripts/startup/bl_ui/space_graph.py
+++ b/release/scripts/startup/bl_ui/space_graph.py
@@ -18,7 +18,6 @@
# <pep8 compliant>
-import bpy
from bpy.types import Header, Menu, Panel
from bl_ui.space_dopesheet import (
DopesheetFilterPopoverBase,
@@ -120,10 +119,6 @@ class GRAPH_MT_view(Menu):
layout.prop(st, "use_realtime_update")
layout.prop(st, "show_cursor")
layout.prop(st, "show_sliders")
-
- if bpy.app.version < (2, 93):
- layout.operator("anim.show_group_colors_deprecated", icon='CHECKBOX_HLT')
-
layout.prop(st, "use_auto_merge_keyframes")
if st.mode != 'DRIVERS':
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index a85dd70e8f4..d8cfa9dcc82 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -481,7 +481,7 @@ class NODE_MT_context_menu(Menu):
class NODE_PT_active_node_generic(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
- bl_category = "Item"
+ bl_category = "Node"
bl_label = "Node"
@classmethod
@@ -499,7 +499,7 @@ class NODE_PT_active_node_generic(Panel):
class NODE_PT_active_node_color(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
- bl_category = "Item"
+ bl_category = "Node"
bl_label = "Color"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = 'NODE_PT_active_node_generic'
@@ -529,7 +529,7 @@ class NODE_PT_active_node_color(Panel):
class NODE_PT_active_node_properties(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
- bl_category = "Item"
+ bl_category = "Node"
bl_label = "Properties"
bl_options = {'DEFAULT_CLOSED'}
@@ -570,7 +570,7 @@ class NODE_PT_active_node_properties(Panel):
class NODE_PT_texture_mapping(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
- bl_category = "Item"
+ bl_category = "Node"
bl_label = "Texture Mapping"
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py
index 7219922c379..adab0b0c88a 100644
--- a/release/scripts/startup/bl_ui/space_topbar.py
+++ b/release/scripts/startup/bl_ui/space_topbar.py
@@ -469,6 +469,8 @@ class TOPBAR_MT_file_import(Menu):
if bpy.app.build_options.alembic:
self.layout.operator("wm.alembic_import", text="Alembic (.abc)")
+ self.layout.operator("wm.gpencil_import_svg", text="SVG as Grease Pencil")
+
class TOPBAR_MT_file_export(Menu):
bl_idname = "TOPBAR_MT_file_export"
@@ -485,6 +487,13 @@ class TOPBAR_MT_file_export(Menu):
self.layout.operator(
"wm.usd_export", text="Universal Scene Description (.usd, .usdc, .usda)")
+ # Pugixml lib dependency
+ if bpy.app.build_options.pugixml:
+ self.layout.operator("wm.gpencil_export_svg", text="Grease Pencil as SVG")
+ # Haru lib dependency
+ if bpy.app.build_options.haru:
+ self.layout.operator("wm.gpencil_export_pdf", text="Grease Pencil as PDF")
+
class TOPBAR_MT_file_external_data(Menu):
bl_label = "External Data"
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 96695ff1be5..f44cf23fb58 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -2244,6 +2244,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
({"property": "use_switch_object_operator"}, "T80402"),
({"property": "use_sculpt_tools_tilt"}, "T82877"),
({"property": "use_asset_browser"}, ("project/profile/124/", "Milestone 1")),
+ ({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
),
)
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 72baa5331bf..831fc06eda5 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -622,9 +622,15 @@ class VIEW3D_PT_tools_brush_texture(Panel, View3DPaintPanel):
@classmethod
def poll(cls, context):
- settings = cls.paint_settings(context)
- return (settings and settings.brush and
- (context.sculpt_object or context.image_paint_object or context.vertex_paint_object))
+ if (
+ (settings := cls.paint_settings(context)) and
+ (brush := settings.brush)
+ ):
+ if context.sculpt_object or context.vertex_paint_object:
+ return True
+ elif context.image_paint_object:
+ return (brush.image_tool == 'DRAW')
+ return False
def draw(self, context):
layout = self.layout
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index febb31af188..7e887caf3f2 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -368,6 +368,7 @@ compositor_node_categories = [
NodeItem("CompositorNodePixelate"),
NodeItem("CompositorNodeSunBeams"),
NodeItem("CompositorNodeDenoise"),
+ NodeItem("CompositorNodeAntiAliasing"),
]),
CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items=[
NodeItem("CompositorNodeNormal"),
@@ -484,6 +485,7 @@ geometry_node_categories = [
GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[
NodeItem("GeometryNodeAttributeRandomize"),
NodeItem("GeometryNodeAttributeMath"),
+ NodeItem("GeometryNodeAttributeClamp"),
NodeItem("GeometryNodeAttributeCompare"),
NodeItem("GeometryNodeAttributeConvert"),
NodeItem("GeometryNodeAttributeFill"),
@@ -495,6 +497,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeAttributeCombineXYZ"),
NodeItem("GeometryNodeAttributeSeparateXYZ"),
NodeItem("GeometryNodeAttributeRemove"),
+ NodeItem("GeometryNodeAttributeMapRange"),
]),
GeometryNodeCategory("GEO_COLOR", "Color", items=[
NodeItem("ShaderNodeValToRGB"),
@@ -520,7 +523,16 @@ geometry_node_categories = [
NodeItem("GeometryNodeEdgeSplit"),
NodeItem("GeometryNodeSubdivisionSurface"),
NodeItem("GeometryNodeSubdivide"),
-
+ ]),
+ GeometryNodeCategory("GEO_PRIMITIVES", "Mesh Primitives", items=[
+ NodeItem("GeometryNodeMeshCircle"),
+ NodeItem("GeometryNodeMeshCone"),
+ NodeItem("GeometryNodeMeshCube"),
+ NodeItem("GeometryNodeMeshCylinder"),
+ NodeItem("GeometryNodeMeshGrid"),
+ NodeItem("GeometryNodeMeshIcoSphere"),
+ NodeItem("GeometryNodeMeshLine"),
+ NodeItem("GeometryNodeMeshUVSphere"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
NodeItem("GeometryNodePointDistribute"),
@@ -531,20 +543,6 @@ geometry_node_categories = [
NodeItem("GeometryNodeRotatePoints"),
NodeItem("GeometryNodeAlignRotationToVector"),
]),
- GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
- NodeItem("GeometryNodePointsToVolume"),
- NodeItem("GeometryNodeVolumeToMesh"),
- ]),
- GeometryNodeCategory("GEO_PRIMITIVES", "Mesh Primitives", items=[
- NodeItem("GeometryNodeMeshCube"),
- NodeItem("GeometryNodeMeshCircle"),
- NodeItem("GeometryNodeMeshUVSphere"),
- NodeItem("GeometryNodeMeshIcoSphere"),
- NodeItem("GeometryNodeMeshCylinder"),
- NodeItem("GeometryNodeMeshCone"),
- NodeItem("GeometryNodeMeshLine"),
- NodeItem("GeometryNodeMeshPlane"),
- ]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
NodeItem("ShaderNodeClamp"),
@@ -558,6 +556,10 @@ geometry_node_categories = [
NodeItem("ShaderNodeVectorMath"),
NodeItem("ShaderNodeVectorRotate"),
]),
+ GeometryNodeCategory("GEO_VOLUME", "Volume", items=[
+ NodeItem("GeometryNodePointsToVolume"),
+ NodeItem("GeometryNodeVolumeToMesh"),
+ ]),
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
diff --git a/release/scripts/templates_osl/basic_shader.osl b/release/scripts/templates_osl/basic_shader.osl
new file mode 100644
index 00000000000..3c5240a1bbd
--- /dev/null
+++ b/release/scripts/templates_osl/basic_shader.osl
@@ -0,0 +1,10 @@
+shader basic_shader(
+ float in_float = 1.0,
+ color in_color = color(1.0, 1.0, 1.0),
+ output float out_float = 0.0,
+ output color out_color = color(0.0, 0.0, 0.0)
+ )
+{
+ out_float = in_float * 2.0;
+ out_color = in_color * 2.0;
+}
diff --git a/release/scripts/templates_py/image_processing.py b/release/scripts/templates_py/image_processing.py
new file mode 100644
index 00000000000..2392faf440c
--- /dev/null
+++ b/release/scripts/templates_py/image_processing.py
@@ -0,0 +1,35 @@
+# This sample shows the an efficient way of doing image processing
+# over Blender's images using Python.
+
+import bpy
+import numpy as np
+
+
+input_image_name = "Image"
+output_image_name = "NewImage"
+
+# Retrieve input image.
+input_image = bpy.data.images[input_image_name]
+w, h = input_image.size
+
+# Allocate a numpy array to manipulate pixel data.
+pixel_data = np.zeros((w, h, 4), 'f')
+
+# Fast copy of pixel data from bpy.data to numpy array.
+input_image.pixels.foreach_get(pixel_data.ravel())
+
+# Do whatever image processing you want using numpy here:
+# Example 1: Inverse red green and blue channels.
+pixel_data[:,:,:3] = 1.0 - pixel_data[:,:,:3]
+# Example 2: Change gamma on the red channel.
+pixel_data[:,:,0] = np.power(pixel_data[:,:,0], 1.5)
+
+# Create output image.
+if output_image_name in bpy.data.images:
+ output_image = bpy.data.images[output_image_name]
+else:
+ output_image = bpy.data.images.new(output_image_name, width=w, height=h)
+
+# Copy of pixel data from numpy array back to the output image.
+output_image.pixels.foreach_set(pixel_data.ravel())
+output_image.update()