Mesh: Replace auto smooth with node group #108014

Merged
Hans Goudey merged 149 commits from HooglyBoogly/blender:refactor-mesh-corner-normals-lazy into main 2023-10-20 16:54:20 +02:00
59 changed files with 1018 additions and 482 deletions
Showing only changes of commit 9b591c4a57 - Show all commits

View File

@ -991,6 +991,8 @@ endif()
if(WITH_MATERIALX)
include("${LIBDIR}/MaterialX/lib/cmake/MaterialX/MaterialXTargets.cmake")
set_target_properties(MaterialXCore PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE)
set_target_properties(MaterialXFormat PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO RELEASE)
endif()
if(WINDOWS_PYTHON_DEBUG)

View File

@ -47,9 +47,31 @@
* twice, once here, and once by LSAN.
*/
#if defined(_MSC_VER)
# define DEBUG_BACKTRACE
# ifdef WITH_ASAN
# define DEBUG_BACKTRACE
# endif
#else
//#define DEBUG_BACKTRACE
/* Un-comment to report back-traces with leaks, uses ASAN when enabled.
* NOTE: The default linking options cause the stack traces only to include addresses.
* Use `addr2line` to expand into file, line & function identifiers,
* see: `tools/utils/addr2line_backtrace.py` convenience utility. */
// # define DEBUG_BACKTRACE
#endif
#ifdef DEBUG_BACKTRACE
# ifdef WITH_ASAN
/* Rely on address sanitizer. */
# else
# if defined(__linux__) || defined(__APPLE__)
# define DEBUG_BACKTRACE_EXECINFO
# else
# error "DEBUG_BACKTRACE: not supported for this platform!"
# endif
# endif
#endif
#ifdef DEBUG_BACKTRACE_EXECINFO
# define BACKTRACE_SIZE 100
#endif
#ifdef DEBUG_MEMCOUNTER
@ -94,6 +116,12 @@ typedef struct MemHead {
#ifdef DEBUG_MEMDUPLINAME
int need_free_name, pad;
#endif
#ifdef DEBUG_BACKTRACE_EXECINFO
void *backtrace[BACKTRACE_SIZE];
int backtrace_size;
#endif
} MemHead;
typedef MemHead MemHeadAligned;
@ -102,6 +130,10 @@ typedef struct MemTail {
int tag3, pad;
} MemTail;
#ifdef DEBUG_BACKTRACE_EXECINFO
# include <execinfo.h>
#endif
/* --------------------------------------------------------------------- */
/* local functions */
/* --------------------------------------------------------------------- */
@ -354,6 +386,26 @@ void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
return newp;
}
#ifdef DEBUG_BACKTRACE_EXECINFO
static void make_memhead_backtrace(MemHead *memh)
{
memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
}
static void print_memhead_backtrace(MemHead *memh)
{
char **strings;
int i;
strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
for (i = 0; i < memh->backtrace_size; i++) {
print_error(" %s\n", strings[i]);
}
free(strings);
}
#endif /* DEBUG_BACKTRACE_EXECINFO */
static void make_memhead_header(MemHead *memh, size_t len, const char *str)
{
MemTail *memt;
@ -370,6 +422,10 @@ static void make_memhead_header(MemHead *memh, size_t len, const char *str)
memh->need_free_name = 0;
#endif
#ifdef DEBUG_BACKTRACE_EXECINFO
make_memhead_backtrace(memh);
#endif
memt = (MemTail *)(((char *)memh) + sizeof(MemHead) + len);
memt->tag3 = MEMTAG3;
@ -720,10 +776,11 @@ static void MEM_guarded_printmemlist_internal(int pydict)
SIZET_ARG(membl->len),
(void *)(membl + 1));
#endif
#ifdef DEBUG_BACKTRACE
# ifdef WITH_ASAN
#ifdef DEBUG_BACKTRACE_EXECINFO
print_memhead_backtrace(membl);
#elif defined(DEBUG_BACKTRACE) && defined(WITH_ASAN)
__asan_describe_address(membl);
# endif
#endif
}
if (membl->next) {

View File

@ -12,6 +12,7 @@ __all__ = (
import bpy
from bpy.types import Action
from dataclasses import dataclass
from typing import (
List,
@ -31,11 +32,35 @@ FCurveKey = Tuple[
ListKeyframes = List[float]
@dataclass
class BakeOptions:
only_selected: bool
"""Only bake selected bones."""
do_pose: bool
"""Bake pose channels"""
do_object: bool
"""Bake objects."""
do_visual_keying: bool
"""Use the final transformations for baking ('visual keying')."""
do_constraint_clear: bool
"""Remove constraints after baking."""
do_parents_clear: bool
"""Unparent after baking objects."""
do_clean: bool
"""Remove redundant keyframes after baking."""
def bake_action(
obj,
*,
action, frames,
**kwargs
bake_options: BakeOptions
):
"""
:arg obj: Object to bake.
@ -49,13 +74,13 @@ def bake_action(
:return: an action or None
:rtype: :class:`bpy.types.Action`
"""
if not (kwargs.get("do_pose") or kwargs.get("do_object")):
if not (bake_options.do_pose or bake_options.do_object):
return None
action, = bake_action_objects(
[(obj, action)],
frames=frames,
**kwargs,
bake_options=bake_options
)
return action
@ -64,7 +89,7 @@ def bake_action_objects(
object_action_pairs,
*,
frames,
**kwargs
bake_options: BakeOptions
):
"""
A version of :func:`bake_action_objects_iter` that takes frames and returns the output.
@ -75,7 +100,7 @@ def bake_action_objects(
:return: A sequence of Action or None types (aligned with `object_action_pairs`)
:rtype: sequence of :class:`bpy.types.Action`
"""
iter = bake_action_objects_iter(object_action_pairs, **kwargs)
iter = bake_action_objects_iter(object_action_pairs, bake_options=bake_options)
iter.send(None)
for frame in frames:
iter.send(frame)
@ -84,7 +109,7 @@ def bake_action_objects(
def bake_action_objects_iter(
object_action_pairs,
**kwargs
bake_options: BakeOptions
):
"""
An coroutine that bakes actions for multiple objects.
@ -96,7 +121,7 @@ def bake_action_objects_iter(
scene = bpy.context.scene
frame_back = scene.frame_current
iter_all = tuple(
bake_action_iter(obj, action=action, **kwargs)
bake_action_iter(obj, action=action, bake_options=bake_options)
for (obj, action) in object_action_pairs
)
for iter in iter_all:
@ -118,13 +143,7 @@ def bake_action_iter(
obj,
*,
action,
only_selected=False,
do_pose=True,
do_object=True,
do_visual_keying=True,
do_constraint_clear=False,
do_parents_clear=False,
do_clean=False
bake_options: BakeOptions
):
"""
An coroutine that bakes action for a single object.
@ -134,20 +153,8 @@ def bake_action_iter(
:arg action: An action to bake the data into, or None for a new action
to be created.
:type action: :class:`bpy.types.Action` or None
:arg only_selected: Only bake selected bones.
:type only_selected: bool
:arg do_pose: Bake pose channels.
:type do_pose: bool
:arg do_object: Bake objects.
:type do_object: bool
:arg do_visual_keying: Use the final transformations for baking ('visual keying')
:type do_visual_keying: bool
:arg do_constraint_clear: Remove constraints after baking.
:type do_constraint_clear: bool
:arg do_parents_clear: Unparent after baking objects.
:type do_parents_clear: bool
:arg do_clean: Remove redundant keyframes after baking.
:type do_clean: bool
:arg bake_options: Boolean options of what to include into the action bake.
:type bake_options: :class: `anim_utils.BakeOptions`
:return: an action or None
:rtype: :class:`bpy.types.Action`
@ -180,7 +187,7 @@ def bake_action_iter(
matrix = {}
bbones = {}
for name, pbone in obj.pose.bones.items():
if do_visual_keying:
if bake_options.do_visual_keying:
# Get the final transform of the bone in its own local space...
matrix[name] = obj.convert_space(pose_bone=pbone, matrix=pbone.matrix,
from_space='POSE', to_space='LOCAL')
@ -192,8 +199,8 @@ def bake_action_iter(
bbones[name] = {bb_prop: getattr(pbone, bb_prop) for bb_prop in BBONE_PROPS}
return matrix, bbones
if do_parents_clear:
if do_visual_keying:
if bake_options.do_parents_clear:
if bake_options.do_visual_keying:
def obj_frame_info(obj):
return obj.matrix_world.copy()
else:
@ -205,7 +212,7 @@ def bake_action_iter(
else:
return matrix.copy()
else:
if do_visual_keying:
if bake_options.do_visual_keying:
def obj_frame_info(obj):
parent = obj.parent
matrix = obj.matrix_world
@ -221,9 +228,9 @@ def bake_action_iter(
# Setup the Context
if obj.pose is None:
do_pose = False
bake_options.do_pose = False
if not (do_pose or do_object):
if not (bake_options.do_pose or bake_options.do_object):
raise Exception("Pose and object baking is disabled, no action needed")
pose_info = []
@ -240,14 +247,14 @@ def bake_action_iter(
if frame is None:
break
if do_pose:
if bake_options.do_pose:
pose_info.append((frame, *pose_frame_info(obj)))
if do_object:
if bake_options.do_object:
obj_info.append((frame, obj_frame_info(obj)))
# -------------------------------------------------------------------------
# Clean (store initial data)
if do_clean and action is not None:
if bake_options.do_clean and action is not None:
clean_orig_data = {fcu: {p.co[1] for p in fcu.keyframe_points} for fcu in action.fcurves}
else:
clean_orig_data = {}
@ -278,12 +285,12 @@ def bake_action_iter(
# pose
lookup_fcurves = {(fcurve.data_path, fcurve.array_index): fcurve for fcurve in action.fcurves}
if do_pose:
if bake_options.do_pose:
for name, pbone in obj.pose.bones.items():
if only_selected and not pbone.bone.select:
if bake_options.only_selected and not pbone.bone.select:
continue
if do_constraint_clear:
if bake_options.do_constraint_clear:
while pbone.constraints:
pbone.constraints.remove(pbone.constraints[0])
@ -359,8 +366,8 @@ def bake_action_iter(
keyframes.insert_keyframes_into_existing_action(lookup_fcurves, total_new_keys, action, name)
# object. TODO. multiple objects
if do_object:
if do_constraint_clear:
if bake_options.do_object:
if bake_options.do_constraint_clear:
while obj.constraints:
obj.constraints.remove(obj.constraints[0])
@ -415,13 +422,13 @@ def bake_action_iter(
else:
keyframes.insert_keyframes_into_existing_action(lookup_fcurves, total_new_keys, action, name)
if do_parents_clear:
if bake_options.do_parents_clear:
obj.parent = None
# -------------------------------------------------------------------------
# Clean
if do_clean:
if bake_options.do_clean:
for fcu in action.fcurves:
fcu_orig_data = clean_orig_data.get(fcu, set())

View File

@ -587,8 +587,6 @@ def _template_items_tool_select(
{"properties": [("deselect_all", True)]}),
(operator, {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties": [("deselect_all", False), ("toggle", True)]}),
("transform.translate", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("release_confirm", True)]}),
]
if params.select_mouse == 'LEFTMOUSE':
@ -5431,6 +5429,27 @@ def km_sculpt(params):
op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}),
op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}),
*_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event),
# Tools
("paint.brush_select", {"type": 'V', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'DRAW')]}),
("paint.brush_select", {"type": 'S', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'SMOOTH')]}),
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'PINCH')]}),
("paint.brush_select", {"type": 'I', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'INFLATE')]}),
("paint.brush_select", {"type": 'G', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'GRAB')]}),
("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True},
{"properties": [("sculpt_tool", 'SCRAPE')]}),
("paint.brush_select", {"type": 'C', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'CLAY_STRIPS')]}),
("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True},
{"properties": [("sculpt_tool", 'CREASE')]}),
("paint.brush_select", {"type": 'K', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'SNAKE_HOOK')]}),
("paint.brush_select", {"type": 'M', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}),
])
# Lasso Masking.

View File

@ -586,7 +586,7 @@ def km_uv_editor(params):
{"properties": [("extend", True)]}),
("uv.select_loop", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'},
{"properties": [("extend", False)]}),
("uv.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
("uv.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
("uv.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, None),
("uv.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, None),
("uv.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
@ -631,6 +631,8 @@ def km_view3d_generic(_params):
{"properties": [("data_path", 'space_data.show_region_toolbar')]}),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
("wm.context_toggle", {"type": 'SPACE', "value": 'PRESS', "shift": True},
{"properties": [("data_path", 'space_data.show_region_asset_shelf')]}),
])
return keymap
@ -895,6 +897,10 @@ def km_graph_editor(params):
items.extend([
("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None),
("wm.context_toggle", {"type": 'LEFT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_channels')]}),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
*_template_items_animation(),
("graph.cursor_set", {"type": 'RIGHTMOUSE', "value": 'PRESS', "ctrl": True}, None),
("graph.clickselect", {"type": 'LEFTMOUSE', "value": 'PRESS'},
@ -931,7 +937,7 @@ def km_graph_editor(params):
{"properties": [("tweak", True), ("axis_range", False), ("mode", 'SUB')]}),
("graph.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, None),
("graph.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, None),
("graph.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
("graph.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
op_menu("GRAPH_MT_delete", {"type": 'BACK_SPACE', "value": 'PRESS'}),
("graph.delete", {"type": 'DEL', "value": 'PRESS'}, {"properties": [("confirm", False)]}),
*_template_items_context_menu("GRAPH_MT_context_menu", {"type": 'RIGHTMOUSE', "value": 'PRESS'}),
@ -975,6 +981,10 @@ def km_image_generic(params):
items.extend([
op_panel("TOPBAR_PT_name", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]),
("wm.context_toggle", {"type": 'LEFT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_toolbar')]}),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None),
("image.new", {"type": 'N', "value": 'PRESS', "alt": True}, None),
("image.open", {"type": 'O', "value": 'PRESS', "alt": True}, None),
@ -1064,6 +1074,10 @@ def km_node_generic(_params):
items.extend([
op_panel("TOPBAR_PT_name", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]),
("wm.context_toggle", {"type": 'LEFT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_toolbar')]}),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
])
return keymap
@ -1201,6 +1215,8 @@ def km_file_browser(params):
)
items.extend([
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_tool_props')]}),
("file.parent", {"type": 'UP_ARROW', "value": 'PRESS', "alt": True}, None),
("file.parent", {"type": 'UP_ARROW', "value": 'PRESS', "ctrl": True}, None),
("file.previous", {"type": 'LEFT_ARROW', "value": 'PRESS', "alt": True}, None),
@ -1344,6 +1360,8 @@ def km_dopesheet_generic(params):
items.extend([
op_panel("TOPBAR_PT_name", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]),
("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None),
("wm.context_toggle", {"type": 'LEFT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_channels')]}),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
])
@ -1403,7 +1421,7 @@ def km_dopesheet(params):
{"properties": [("mode", 'MARKERS_BETWEEN')]}),
("action.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("action.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("action.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS'}, None),
("action.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
("action.frame_jump", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
("wm.context_menu_enum", {"type": 'X', "value": 'PRESS'},
{"properties": [("data_path", 'space_data.auto_snap')]}),
@ -1456,6 +1474,10 @@ def km_nla_generic(params):
items.extend([
op_panel("TOPBAR_PT_name", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]),
("wm.context_toggle", {"type": 'LEFT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_channels')]}),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None),
*_template_items_animation(),
("nla.tweakmode_enter", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
@ -1723,6 +1745,8 @@ def km_sequencercommon(_params):
items.extend([
op_panel("TOPBAR_PT_name", {"type": 'RET', "value": 'PRESS'}, [("keep_open", False)]),
("wm.context_toggle", {"type": 'LEFT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_channels')]}),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
])
@ -1814,11 +1838,11 @@ def km_sequencer(params):
{"properties": [("extend", True), ("side_of_frame", True), ("linked_time", True)]}),
("sequencer.select_more", {"type": 'UP_ARROW', "value": 'PRESS', "repeat": True}, None),
("sequencer.select_less", {"type": 'DOWN_ARROW', "value": 'PRESS', "repeat": True}, None),
("sequencer.select_linked_pick", {"type": 'RIGHT_BRACKET', "value": 'PRESS'},
("sequencer.select_linked_pick", {"type": 'L', "value": 'PRESS', "ctrl": True},
{"properties": [("extend", False)]}),
("sequencer.select_linked_pick", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "shift": True},
{"properties": [("extend", True)]}),
("sequencer.select_linked", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True}, None),
("sequencer.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
("sequencer.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG'},
{"properties": [("tweak", True), ("mode", 'SET')]}),
("sequencer.select_box", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "shift": True},
@ -1999,6 +2023,10 @@ def km_clip_editor(params):
)
items.extend([
("wm.context_toggle", {"type": 'LEFT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_toolbar')]}),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
("wm.search_menu", {"type": 'TAB', "value": 'PRESS'}, None),
("clip.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("clip.view_pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
@ -2179,6 +2207,8 @@ def km_spreadsheet_generic(_params):
*_template_space_region_type_toggle(
channels_key={"type": 'T', "value": 'PRESS'},
),
("wm.context_toggle", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "ctrl": True},
{"properties": [("data_path", 'space_data.show_region_ui')]}),
])
return keymap
@ -3108,7 +3138,7 @@ def km_weight_paint_vertex_selection(params):
("paint.vert_select_hide", {"type": 'H', "value": 'PRESS', "shift": True},
{"properties": [("unselected", True)]}),
("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
("paint.vert_select_linked", {"type": 'L', "value": 'PRESS', "alt": True}, None),
("paint.vert_select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
("paint.vert_select_linked_pick", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "alt": True},
{"properties": [("select", True)]}),
("paint.vert_select_linked_pick", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK', "ctrl": True, "alt": True},
@ -3156,7 +3186,6 @@ def km_pose(params):
("pose.select_hierarchy", {"type": 'DOWN_ARROW', "value": 'PRESS', "shift": True, "repeat": True},
{"properties": [("direction", 'CHILD'), ("extend", True)]}),
("pose.select_linked", {"type": 'L', "value": 'PRESS', "ctrl": True}, None),
("pose.bone_layers", {"type": 'G', "value": 'PRESS'}, None),
("anim.keyframe_insert_menu", {"type": 'S', "value": 'PRESS', "shift": True}, None),
("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'},
{"properties": [("type", 'LocRotScale')]}),
@ -3730,6 +3759,8 @@ def km_mesh(params):
("mesh.shortest_path_pick", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True},
{"properties": [("use_fill", False)]}),
("mesh.shortest_path_pick", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True},
{"properties": [("use_fill", True)]}),
("mesh.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True}, {"properties": [("action", 'SELECT')]}),
("mesh.select_all", {"type": 'A', "value": 'PRESS', "ctrl": True,
@ -4122,7 +4153,8 @@ def km_object_non_modal(params):
items.extend([
("object.transfer_mode", {"type": 'ACCENT_GRAVE', "value": 'PRESS'}, None),
op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'FOUR', "value": 'PRESS'}),
("object.mode_set", {"type": 'FOUR', "value": 'PRESS'}, {"properties": [("mode", 'OBJECT')]}),
op_menu_pie("VIEW3D_MT_object_mode_pie", {"type": 'FIVE', "value": 'PRESS'}),
])
return keymap

View File

@ -255,16 +255,24 @@ class NLA_OT_bake(Operator):
def execute(self, context):
from bpy_extras import anim_utils
do_pose = 'POSE' in self.bake_types
do_object = 'OBJECT' in self.bake_types
if do_pose and self.only_selected:
bake_options = anim_utils.BakeOptions(
only_selected=self.only_selected,
do_pose='POSE' in self.bake_types,
do_object='OBJECT' in self.bake_types,
do_visual_keying=self.visual_keying,
do_constraint_clear=self.clear_constraints,
do_parents_clear=self.clear_parents,
do_clean=self.clean_curves
)
if bake_options.do_pose and self.only_selected:
pose_bones = context.selected_pose_bones or []
armatures = {pose_bone.id_data for pose_bone in pose_bones}
objects = list(armatures)
else:
objects = context.selected_editable_objects
if do_pose and not do_object:
if bake_options.do_pose and not bake_options.do_object:
objects = [obj for obj in objects if obj.pose is not None]
object_action_pairs = (
@ -276,13 +284,7 @@ class NLA_OT_bake(Operator):
actions = anim_utils.bake_action_objects(
object_action_pairs,
frames=range(self.frame_start, self.frame_end + 1, self.step),
only_selected=self.only_selected,
do_pose=do_pose,
do_object=do_object,
do_visual_keying=self.visual_keying,
do_constraint_clear=self.clear_constraints,
do_parents_clear=self.clear_parents,
do_clean=self.clean_curves,
bake_options=bake_options
)
if not any(actions):
@ -429,9 +431,125 @@ class UpdateAnimatedTransformConstraint(Operator):
return {'FINISHED'}
class ARMATURE_OT_sync_bone_color_to_selected(Operator):
"""Copy the bone color of the active bone to all selected bones"""
bl_idname = "armature.sync_bone_color_to_selected"
bl_label = "Sync to Selected"
bl_options = {'REGISTER', 'UNDO'}
_bone_type_enum = [
('EDIT', 'Edit Bone', 'Copy Edit Bone colors from the active bone to all selected bones'),
('POSE', 'Pose Bone', 'Copy Pose Bone colors from the active bone to all selected bones'),
]
bone_type: EnumProperty(
name="Type",
items=_bone_type_enum)
@classmethod
def poll(cls, context):
return context.mode in {'EDIT_ARMATURE', 'POSE'}
def execute(self, context):
match (self.bone_type, context.mode):
# Armature in edit mode:
case ('POSE', 'EDIT_ARMATURE'):
self.report({'ERROR'}, "Go to pose mode to copy pose bone colors")
return {'OPERATOR_CANCELLED'}
case ('EDIT', 'EDIT_ARMATURE'):
bone_source = context.active_bone
bones_dest = context.selected_bones
pose_bones_to_check = []
# Armature in pose mode:
case ('POSE', 'POSE'):
bone_source = context.active_pose_bone
bones_dest = context.selected_pose_bones
pose_bones_to_check = []
case ('EDIT', 'POSE'):
bone_source = context.active_bone
pose_bones_to_check = context.selected_pose_bones
bones_dest = [posebone.bone for posebone in pose_bones_to_check]
# Anything else:
case _:
self.report({'ERROR'}, "Cannot do anything in mode %r" % context.mode)
return {'CANCELLED'}
if not bone_source:
self.report({'ERROR'}, "No active bone to copy from.")
return {'CANCELLED'}
if not bones_dest:
self.report({'ERROR'}, "No selected bones to copy to.")
return {'CANCELLED'}
num_pose_color_overrides = 0
for index, bone_dest in enumerate(bones_dest):
bone_dest.color.palette = bone_source.color.palette
for custom_field in ('normal', 'select', 'active'):
color = getattr(bone_source.color.custom, custom_field)
setattr(bone_dest.color.custom, custom_field, color)
if self.bone_type == 'EDIT' and pose_bones_to_check:
pose_bone = pose_bones_to_check[index]
if pose_bone.color.palette != 'DEFAULT':
# A pose color has been set, and we're now syncing edit bone
# colors. This means that the synced color will not be
# visible. Better to let the user know about this.
num_pose_color_overrides += 1
if num_pose_color_overrides:
self.report(
{'INFO'},
"Bone colors were synced; for %d bones this will not be visible due to pose bone color overrides" %
num_pose_color_overrides)
return {'FINISHED'}
class ARMATURE_OT_collection_solo_visibility(Operator):
"""Hide all other bone collections and show the active one"""
bl_idname = "armature.collection_solo_visibility"
bl_label = "Solo Visibility"
bl_options = {'REGISTER', 'UNDO'}
name: StringProperty(name='Bone Collection')
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE' and context.object.data
def execute(self, context):
arm = context.object.data
for bcoll in arm.collections:
bcoll.is_visible = bcoll.name == self.name
return {'FINISHED'}
class ARMATURE_OT_collection_show_all(Operator):
"""Show all bone collections"""
bl_idname = "armature.collection_show_all"
bl_label = "Show All"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.object and context.object.type == 'ARMATURE' and context.object.data
def execute(self, context):
arm = context.object.data
for bcoll in arm.collections:
bcoll.is_visible = True
return {'FINISHED'}
classes = (
ANIM_OT_keying_set_export,
NLA_OT_bake,
ClearUselessActions,
UpdateAnimatedTransformConstraint,
ARMATURE_OT_sync_bone_color_to_selected,
ARMATURE_OT_collection_solo_visibility,
ARMATURE_OT_collection_show_all,
)

View File

@ -3183,14 +3183,37 @@ class WM_MT_splash_quick_setup(Menu):
layout = self.layout
layout.operator_context = 'EXEC_DEFAULT'
layout.label(text="Quick Setup")
old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version()
can_import = bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version
split = layout.split(factor=0.14) # Left margin.
if can_import:
layout.label(text="Import Existing Settings")
split = layout.split(factor=0.20) # Left margin.
split.label()
split = split.split(factor=0.73) # Content width.
col = split.column()
col.operator(
"preferences.copy_prev",
text=iface_(
"Load Blender %d.%d Settings",
"Operator") %
old_version, icon='DUPLICATE',
translate=False)
col.operator(
"wm.url_open", text="See What's New...", icon='URL'
).url = "https://wiki.blender.org/wiki/Reference/Release_Notes/4.0"
col.separator(factor=2.0)
if can_import:
layout.label(text="Create New Settings")
else:
layout.label(text="Quick Setup")
split = layout.split(factor=0.20) # Left margin.
split.label()
split = split.split(factor=0.73) # Content width.
col = split.column()
col.use_property_split = True
col.use_property_decorate = False
@ -3219,42 +3242,22 @@ class WM_MT_splash_quick_setup(Menu):
if has_spacebar_action:
col.row().prop(kc_prefs, "spacebar_action", text="Spacebar")
col.separator()
# Themes.
col.separator()
sub = col.column(heading="Theme")
label = bpy.types.USERPREF_MT_interface_theme_presets.bl_label
if label == "Presets":
label = "Blender Dark"
sub.menu("USERPREF_MT_interface_theme_presets", text=label)
# Keep height constant.
if not has_select_mouse:
col.label()
if not has_spacebar_action:
col.label()
layout.separator(factor=2.0)
# Save settings buttons.
sub = layout.row()
old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version()
if bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version:
sub.operator(
"preferences.copy_prev",
text=iface_(
"Load %d.%d Settings",
"Operator") %
old_version,
translate=False)
sub.operator("wm.save_userpref", text="Save New Settings")
if can_import:
sub.label()
sub.operator("wm.save_userpref", text="Save New Settings", icon='CHECKMARK')
else:
sub.label()
sub.label()
sub.operator("wm.save_userpref", text="Next")
sub.operator("wm.save_userpref", text="Continue")
layout.separator(factor=2.4)
layout.separator(factor=2.0)
class WM_MT_splash(Menu):

View File

@ -39,8 +39,8 @@ class DATA_PT_context_arm(ArmatureButtonsPanel, Panel):
layout.template_ID(space, "pin_id")
class DATA_PT_skeleton(ArmatureButtonsPanel, Panel):
bl_label = "Skeleton"
class DATA_PT_pose(ArmatureButtonsPanel, Panel):
bl_label = "Pose"
def draw(self, context):
layout = self.layout
@ -135,6 +135,9 @@ class DATA_PT_bone_collections(ArmatureButtonsPanel, Panel):
col.separator()
col.operator("armature.collection_move", icon='TRIA_UP', text="").direction = 'UP'
col.operator("armature.collection_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
col.separator()
col.menu("ARMATURE_MT_collection_context_menu", icon='DOWNARROW_HLT', text="")
row = layout.row()
@ -147,6 +150,20 @@ class DATA_PT_bone_collections(ArmatureButtonsPanel, Panel):
sub.operator("armature.collection_deselect", text="Deselect")
class ARMATURE_MT_collection_context_menu(Menu):
bl_label = "Bone Collection Specials"
def draw(self, context):
layout = self.layout
arm = context.armature
active_bcoll = arm.collections.active
props = layout.operator("armature.collection_solo_visibility")
props.name = active_bcoll.name if active_bcoll else ""
layout.operator("armature.collection_show_all")
class DATA_PT_iksolver_itasc(ArmatureButtonsPanel, Panel):
bl_label = "Inverse Kinematics"
bl_options = {'DEFAULT_CLOSED'}
@ -270,9 +287,10 @@ class DATA_PT_custom_props_bcoll(ArmatureButtonsPanel, PropertyPanel, Panel):
classes = (
DATA_PT_context_arm,
DATA_PT_skeleton,
DATA_PT_pose,
DATA_PT_bone_collections,
DATA_UL_bone_collections,
ARMATURE_MT_collection_context_menu,
DATA_PT_motion_paths,
DATA_PT_motion_paths_display,
DATA_PT_display,

View File

@ -247,6 +247,44 @@ class BONE_PT_relations(BoneButtonsPanel, Panel):
sub.prop(bone, "inherit_scale")
class BONE_PT_collections(BoneButtonsPanel, Panel):
bl_label = "Bone Collections"
bl_parent_id = "BONE_PT_relations"
@classmethod
def poll(cls, context):
return context.bone or context.edit_bone
def draw(self, context):
layout = self.layout
layout.use_property_split = False
bone = context.bone or context.edit_bone
if not bone.collections:
layout.active = False
layout.label(text="Not assigned to any bone collection.")
return
box = layout.box()
sub = box.column(align=True)
for bcoll in bone.collections:
bcoll_row = sub.row()
bcoll_row.emboss = 'NONE'
# Name & visibility of bcoll. Safe things, so aligned together.
row = bcoll_row.row(align=True)
row.label(text=bcoll.name)
row.prop(bcoll, "is_visible", text="",
icon='HIDE_OFF' if bcoll.is_visible else 'HIDE_ON')
# Unassignment operator, less safe so with a bit of spacing.
props = bcoll_row.operator("armature.collection_unassign_named",
text="", icon='X')
props.name = bcoll.name
props.bone_name = bone.name
class BONE_PT_display(BoneButtonsPanel, Panel):
bl_label = "Viewport Display"
bl_options = {'DEFAULT_CLOSED'}
@ -278,9 +316,19 @@ class BONE_PT_display(BoneButtonsPanel, Panel):
return
pose_bone = ob.pose.bones[bone.name]
layout.prop(bone.color, 'palette', text='Edit Bone Color')
# Allow the layout to use the space normally occupied by the 'set a key' diamond.
layout.use_property_decorate = False
row = layout.row(align=True)
row.prop(bone.color, 'palette', text='Edit Bone Color')
props = row.operator("armature.sync_bone_color_to_selected", text="", icon='UV_SYNC_SELECT')
props.bone_type = 'EDIT'
self.draw_bone_color_ui(layout, bone.color)
layout.prop(pose_bone.color, 'palette', text='Pose Bone Color')
row = layout.row(align=True)
row.prop(pose_bone.color, 'palette', text='Pose Bone Color')
props = row.operator("armature.sync_bone_color_to_selected", text="", icon='UV_SYNC_SELECT')
props.bone_type = 'POSE'
self.draw_bone_color_ui(layout, pose_bone.color)
def draw_edit_bone(self, context, layout):
@ -297,8 +345,7 @@ class BONE_PT_display(BoneButtonsPanel, Panel):
if not bone_color.is_custom:
return
layout.use_property_split = False
split = layout.split(factor=0.4)
split = layout.split(factor=0.401)
col = split.column()
row = col.row()
@ -307,6 +354,7 @@ class BONE_PT_display(BoneButtonsPanel, Panel):
col = split.column(align=True)
row = col.row(align=True)
row.use_property_split = False
row.prop(bone_color.custom, "normal", text="")
row.prop(bone_color.custom, "select", text="")
row.prop(bone_color.custom, "active", text="")
@ -504,6 +552,7 @@ classes = (
BONE_PT_transform,
BONE_PT_curved,
BONE_PT_relations,
BONE_PT_collections,
BONE_PT_inverse_kinematics,
BONE_PT_deform,
BONE_PT_display,

View File

@ -547,6 +547,9 @@ def draw_attribute_warnings(context, layout):
ob = context.object
mesh = context.mesh
if not mesh:
return
unique_names = set()
colliding_names = []
for collection in (

View File

@ -3902,12 +3902,6 @@ class VIEW3D_MT_pose(Menu):
layout.separator()
layout.operator_context = 'INVOKE_AREA'
layout.operator("armature.armature_layers", text="Change Armature Layers...")
layout.operator("pose.bone_layers", text="Change Bone Layers...")
layout.separator()
layout.menu("VIEW3D_MT_pose_showhide")
layout.menu("VIEW3D_MT_bone_options_toggle", text="Bone Settings")
@ -3997,8 +3991,12 @@ class VIEW3D_MT_bone_collections(Menu):
layout.label(text="- select bones to operate on first -")
return
layout.operator("armature.collection_show_all")
layout.separator()
arm = context.object.data
bone = context.active_bone
found_editable_bcoll = False
for bcoll in arm.collections:
if not bcoll.is_editable:

View File

@ -915,4 +915,9 @@ inline bool bNodePanelState::is_parent_collapsed() const
return flag & NODE_PANEL_PARENT_COLLAPSED;
}
inline bool bNodePanelState::has_visible_content() const
{
return flag & NODE_PANEL_CONTENT_VISIBLE;
}
/** \} */

View File

@ -533,7 +533,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi.width = vi.gridsize; \
vi.height = vi.gridsize; \
vi.index = vi.vertex.i = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \
vi.grid = vi.grids[vi.grid_indices[vi.g]]; \
vi.grid = CCG_elem_offset(&vi.key, vi.grids[vi.grid_indices[vi.g]], -1); \
if (mode == PBVH_ITER_UNIQUE) { \
vi.gh = vi.grid_hidden[vi.grid_indices[vi.g]]; \
} \
@ -546,10 +546,10 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
for (vi.gy = 0; vi.gy < vi.height; vi.gy++) { \
for (vi.gx = 0; vi.gx < vi.width; vi.gx++, vi.i++) { \
if (vi.grid) { \
vi.grid = CCG_elem_next(&vi.key, vi.grid); \
vi.co = CCG_elem_co(&vi.key, vi.grid); \
vi.fno = CCG_elem_no(&vi.key, vi.grid); \
vi.mask = vi.key.has_mask ? *CCG_elem_mask(&vi.key, vi.grid) : 0.0f; \
vi.grid = CCG_elem_next(&vi.key, vi.grid); \
vi.index++; \
vi.vertex.i++; \
vi.visible = true; \

View File

@ -5427,11 +5427,7 @@ void BKE_curve_material_remap(Curve *cu, const uint *remap, uint remap_len)
}
for (i = 0; i <= charinfo_len; i++) {
if (strinfo[i].mat_nr > 0) {
strinfo[i].mat_nr -= 1;
MAT_NR_REMAP(strinfo[i].mat_nr);
strinfo[i].mat_nr += 1;
}
MAT_NR_REMAP(strinfo[i].mat_nr);
}
}
else {

View File

@ -448,8 +448,8 @@ static void build_underline(Curve *cu,
nu2->bezt = nullptr;
nu2->knotsu = nu2->knotsv = nullptr;
nu2->charidx = charidx + 1000;
if (mat_nr > 0) {
nu2->mat_nr = mat_nr - 1;
if (mat_nr >= 0) {
nu2->mat_nr = mat_nr;
}
nu2->pntsu = 4;
nu2->pntsv = 1;
@ -538,7 +538,7 @@ void BKE_vfont_build_char(Curve *cu,
nu2->flag = CU_SMOOTH;
nu2->charidx = charidx;
if (info->mat_nr > 0) {
nu2->mat_nr = info->mat_nr - 1;
nu2->mat_nr = info->mat_nr;
}
else {
nu2->mat_nr = 0;

View File

@ -17,6 +17,7 @@
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_curve_types.h"
#include "DNA_defaults.h"
#include "DNA_light_types.h"
#include "DNA_lightprobe_types.h"
@ -42,6 +43,7 @@
#include "BKE_armature.h"
#include "BKE_attribute.h"
#include "BKE_curve.h"
#include "BKE_effect.h"
#include "BKE_grease_pencil.hh"
#include "BKE_idprop.hh"
@ -1614,6 +1616,20 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 31)) {
LISTBASE_FOREACH (Curve *, curve, &bmain->curves) {
const int curvetype = BKE_curve_type_get(curve);
if (curvetype == OB_FONT) {
CharInfo *info = curve->strinfo;
for (int i = curve->len_char32 - 1; i >= 0; i--, info++) {
if (info->mat_nr > 0) {
/** CharInfo mat_nr used to start at 1, unlike mesh & nurbs, now zero-based. */
info->mat_nr--;
}
}
}
}
}
/**
* Versioning code until next subversion bump goes here.
*

View File

@ -77,6 +77,7 @@ void ARMATURE_OT_collection_remove(struct wmOperatorType *ot);
void ARMATURE_OT_collection_move(struct wmOperatorType *ot);
void ARMATURE_OT_collection_assign(struct wmOperatorType *ot);
void ARMATURE_OT_collection_unassign(struct wmOperatorType *ot);
void ARMATURE_OT_collection_unassign_named(struct wmOperatorType *ot);
void ARMATURE_OT_collection_select(struct wmOperatorType *ot);
void ARMATURE_OT_collection_deselect(struct wmOperatorType *ot);
@ -134,8 +135,6 @@ void POSE_OT_rotation_mode_set(struct wmOperatorType *ot);
void POSE_OT_quaternions_flip(struct wmOperatorType *ot);
void POSE_OT_bone_layers(struct wmOperatorType *ot);
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -66,6 +66,7 @@ void ED_operatortypes_armature()
WM_operatortype_append(ARMATURE_OT_collection_move);
WM_operatortype_append(ARMATURE_OT_collection_assign);
WM_operatortype_append(ARMATURE_OT_collection_unassign);
WM_operatortype_append(ARMATURE_OT_collection_unassign_named);
WM_operatortype_append(ARMATURE_OT_collection_select);
WM_operatortype_append(ARMATURE_OT_collection_deselect);
@ -119,8 +120,6 @@ void ED_operatortypes_armature()
WM_operatortype_append(POSE_OT_quaternions_flip);
WM_operatortype_append(POSE_OT_bone_layers);
WM_operatortype_append(POSE_OT_propagate);
/* POSELIB */

View File

@ -14,6 +14,7 @@
#include "DNA_ID.h"
#include "DNA_object_types.h"
#include "BKE_action.h"
#include "BKE_context.h"
#include "BKE_layer.h"
#include "BKE_report.h"
@ -290,7 +291,11 @@ static void bone_collection_assign_editbones(bContext *C,
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
}
/* Returns whether the current mode is actually supported. */
/**
* Assign or unassign all selected bones to/from the given bone collection.
*
* \return whether the current mode is actually supported.
*/
static bool bone_collection_assign_mode_specific(bContext *C,
Object *ob,
BoneCollection *bcoll,
@ -327,6 +332,58 @@ static bool bone_collection_assign_mode_specific(bContext *C,
}
}
/**
* Assign or unassign the named bone to/from the given bone collection.
*
* \return whether the current mode is actually supported.
*/
static bool bone_collection_assign_named_mode_specific(bContext *C,
Object *ob,
BoneCollection *bcoll,
const char *bone_name,
assign_bone_func assign_bone_func,
assign_ebone_func assign_ebone_func,
bool *made_any_changes,
bool *had_bones_to_assign)
{
bArmature *arm = static_cast<bArmature *>(ob->data);
switch (CTX_data_mode_enum(C)) {
case CTX_MODE_POSE: {
bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, bone_name);
if (!pchan) {
return true;
}
*had_bones_to_assign = true;
*made_any_changes |= assign_bone_func(bcoll, pchan->bone);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_COLLECTION, ob);
DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); /* Recreate the draw buffers. */
return true;
}
case CTX_MODE_EDIT_ARMATURE: {
EditBone *ebone = ED_armature_ebone_find_name(arm->edbo, bone_name);
if (!ebone) {
return true;
}
*had_bones_to_assign = true;
*made_any_changes |= assign_ebone_func(bcoll, ebone);
ED_armature_edit_sync_selection(arm->edbo);
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_COLLECTION, ob);
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
return true;
}
default:
return false;
}
}
static bool bone_collection_assign_poll(bContext *C)
{
Object *ob = ED_object_context(C);
@ -484,6 +541,82 @@ void ARMATURE_OT_collection_unassign(wmOperatorType *ot)
"the active bone collection");
}
static int bone_collection_unassign_named_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
if (ob == nullptr) {
return OPERATOR_CANCELLED;
}
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob, FAIL_IF_MISSING);
if (bcoll == nullptr) {
return OPERATOR_CANCELLED;
}
char bone_name[MAX_NAME];
RNA_string_get(op->ptr, "bone_name", bone_name);
if (!bone_name[0]) {
WM_report(RPT_ERROR, "Missing bone name");
return OPERATOR_CANCELLED;
}
bool made_any_changes = false;
bool had_bones_to_unassign = false;
const bool mode_is_supported = bone_collection_assign_named_mode_specific(
C,
ob,
bcoll,
bone_name,
ANIM_armature_bonecoll_unassign,
ANIM_armature_bonecoll_unassign_editbone,
&made_any_changes,
&had_bones_to_unassign);
if (!mode_is_supported) {
WM_report(RPT_ERROR, "This operator only works in pose mode and armature edit mode");
return OPERATOR_CANCELLED;
}
if (!had_bones_to_unassign) {
WM_reportf(RPT_WARNING, "Could not find bone '%s'", bone_name);
return OPERATOR_CANCELLED;
}
if (!made_any_changes) {
WM_reportf(
RPT_WARNING, "Bone '%s' was not assigned to collection '%s'", bone_name, bcoll->name);
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
void ARMATURE_OT_collection_unassign_named(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Bone from Bone collections";
ot->idname = "ARMATURE_OT_collection_unassign_named";
ot->description = "Unassign the bone from this bone collection";
/* api callbacks */
ot->exec = bone_collection_unassign_named_exec;
ot->poll = bone_collection_assign_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_string(ot->srna,
"name",
nullptr,
MAX_NAME,
"Bone Collection",
"Name of the bone collection to unassign this bone from; empty to unassign from "
"the active bone collection");
RNA_def_string(ot->srna,
"bone_name",
nullptr,
MAX_NAME,
"Bone Name",
"Name of the bone to unassign from the collection; empty to use the active bone");
}
static bool editbone_is_member(const EditBone *ebone, const BoneCollection *bcoll)
{
LISTBASE_FOREACH (BoneCollectionReference *, ref, &ebone->bone_collections) {

View File

@ -746,95 +746,6 @@ void ARMATURE_OT_layers_show_all(wmOperatorType *ot)
ot->srna, "all", true, "All Layers", "Enable all layers or just the first 16 (top row)");
}
/* ------------------- */
/* Present a popup to get the layers that should be used */
static int pose_bone_layers_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
/* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
bool layers[32] = {false};
/* get layers that are active already */
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) {
short bit;
/* loop over the bits for this pchan's layers, adding layers where they're needed */
for (bit = 0; bit < 32; bit++) {
layers[bit] = (pchan->bone->layer & (1u << bit)) != 0;
}
}
CTX_DATA_END;
/* copy layers to operator */
RNA_boolean_set_array(op->ptr, "layers", layers);
/* part to sync with other similar operators... */
return WM_operator_props_popup(C, op, event);
}
/* Set the visible layers for the active armature (edit and pose modes) */
static int pose_bone_layers_exec(bContext *C, wmOperator *op)
{
/* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
bool layers[32];
/* get the values set in the operator properties */
RNA_boolean_get_array(op->ptr, "layers", layers);
Object *prev_ob = nullptr;
/* Make sure that the pose bone data is up to date.
* (May not always be the case after undo/redo e.g.).
*/
Main *bmain = CTX_data_main(C);
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be nullptr in a lot of cases. */
const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
bArmature *arm = static_cast<bArmature *>(ob_iter->data);
BKE_pose_ensure(bmain, ob_iter, arm, true);
}
FOREACH_OBJECT_IN_MODE_END;
/* set layers of pchans based on the values set in the operator props */
CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, selected_pose_bones, Object *, ob) {
/* get pointer for pchan, and write flags this way */
PointerRNA ptr = RNA_pointer_create((ID *)ob->data, &RNA_Bone, pchan->bone);
RNA_boolean_set_array(&ptr, "layers", layers);
if (prev_ob != ob) {
/* NOTE: notifier might evolve. */
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
DEG_id_tag_update((ID *)ob->data, ID_RECALC_COPY_ON_WRITE);
prev_ob = ob;
}
}
CTX_DATA_END;
return OPERATOR_FINISHED;
}
void POSE_OT_bone_layers(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Change Bone Layers";
ot->idname = "POSE_OT_bone_layers";
ot->description = "Change the layers that the selected bones belong to";
/* callbacks */
ot->invoke = pose_bone_layers_invoke;
ot->exec = pose_bone_layers_exec;
ot->poll = ED_operator_posemode_exclusive;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_boolean_layer_member(
ot->srna, "layers", 32, nullptr, "Layer", "Armature layers that bone belongs to");
}
/* ********************************************** */
/* Show/Hide Bones */

View File

@ -385,7 +385,7 @@ static int insert_into_textbuf(Object *obedit, uintptr_t c)
ef->textbuf[ef->pos] = c;
ef->textbufinfo[ef->pos] = cu->curinfo;
ef->textbufinfo[ef->pos].kern = 0.0f;
ef->textbufinfo[ef->pos].mat_nr = obedit->actcol;
ef->textbufinfo[ef->pos].mat_nr = obedit->actcol - 1;
ef->pos++;
ef->len++;
@ -418,10 +418,7 @@ static void text_update_edited(bContext *C, Object *obedit, const eEditFontMode
cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0];
if (obedit->totcol > 0) {
obedit->actcol = cu->curinfo.mat_nr;
/* since this array is calloc'd, it can be 0 even though we try ensure
* (mat_nr > 0) almost everywhere */
obedit->actcol = cu->curinfo.mat_nr + 1;
if (obedit->actcol < 1) {
obedit->actcol = 1;
}
@ -1851,7 +1848,7 @@ static void font_cursor_set_apply(bContext *C, const wmEvent *event)
cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0];
if (ob->totcol > 0) {
ob->actcol = cu->curinfo.mat_nr;
ob->actcol = cu->curinfo.mat_nr + 1;
if (ob->actcol < 1) {
ob->actcol = 1;
}

View File

@ -1253,8 +1253,9 @@ static void ui_apply_but_TEX(bContext *C, uiBut *but, uiHandleButtonData *data)
if ((but->func_arg2 == nullptr) && (but->type == UI_BTYPE_SEARCH_MENU)) {
uiButSearch *search_but = (uiButSearch *)but;
but->func_arg2 = search_but->item_active;
blender::ui::string_search::add_recent_search(search_but->item_active_str);
if ((U.flag & USER_FLAG_RECENT_SEARCHES_DISABLE) == 0) {
blender::ui::string_search::add_recent_search(search_but->item_active_str);
}
}
ui_apply_but_func(C, but);

View File

@ -29,6 +29,7 @@ struct RecentCacheStorage {
static RecentCacheStorage &get_recent_cache_storage()
{
BLI_assert((U.flag & USER_FLAG_RECENT_SEARCHES_DISABLE) == 0);
static RecentCacheStorage storage;
return storage;
}
@ -62,6 +63,10 @@ static std::optional<std::string> get_recent_searches_file_path()
void write_recent_searches_file()
{
if (U.flag & USER_FLAG_RECENT_SEARCHES_DISABLE) {
return;
}
const std::optional<std::string> path = get_recent_searches_file_path();
if (!path) {
return;
@ -83,6 +88,10 @@ void write_recent_searches_file()
void read_recent_searches_file()
{
if (U.flag & USER_FLAG_RECENT_SEARCHES_DISABLE) {
return;
}
const std::optional<std::string> path = get_recent_searches_file_path();
if (!path) {
return;

View File

@ -131,12 +131,8 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode)
case OB_FONT:
case OB_MBALL:
case OB_POINTCLOUD:
if (mode & OB_MODE_EDIT) {
return true;
}
break;
case OB_LATTICE:
if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) {
if (mode & OB_MODE_EDIT) {
return true;
}
break;

View File

@ -347,7 +347,7 @@ static int material_slot_assign_exec(bContext *C, wmOperator * /*op*/)
if (ef && BKE_vfont_select_get(ob, &selstart, &selend)) {
for (i = selstart; i <= selend; i++) {
changed = true;
ef->textbufinfo[i].mat_nr = mat_nr_active + 1;
ef->textbufinfo[i].mat_nr = mat_nr_active;
}
}
}

View File

@ -631,8 +631,10 @@ struct VisibilityUpdateState {
/* Recursive function to determine visibility of items before drawing. */
static void node_update_panel_items_visibility_recursive(int num_items,
const bool is_parent_collapsed,
bNodePanelState &parent_state,
VisibilityUpdateState &state)
{
parent_state.flag &= ~NODE_PANEL_CONTENT_VISIBLE;
while (state.item_iter != state.item_end) {
/* Stop after adding the expected number of items.
* Root panel consumes all remaining items (num_items == -1). */
@ -651,14 +653,24 @@ static void node_update_panel_items_visibility_recursive(int num_items,
const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
node_update_panel_items_visibility_recursive(
item.panel_decl->num_child_decls, is_collapsed, state);
item.panel_decl->num_child_decls, is_collapsed, *item.state, state);
if (item.state->flag & NODE_PANEL_CONTENT_VISIBLE) {
/* If child panel is visible so is the parent panel. */
parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
}
}
else if (item.is_valid_socket()) {
if (item.input) {
SET_FLAG_FROM_TEST(item.input->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED);
if (item.input->is_visible()) {
parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
}
}
if (item.output) {
SET_FLAG_FROM_TEST(item.output->flag, is_parent_collapsed, SOCK_PANEL_COLLAPSED);
if (item.output->is_visible()) {
parent_state.flag |= NODE_PANEL_CONTENT_VISIBLE;
}
}
}
else {
@ -719,36 +731,41 @@ static void add_panel_items_recursive(const bContext &C,
C, ntree, node, node.typeinfo->draw_buttons, block, locy);
}
if (!is_parent_collapsed) {
locy -= NODE_DY;
state.is_first = false;
}
/* Panel visible if any content is visible. */
if (item.state->has_visible_content()) {
if (!is_parent_collapsed) {
locy -= NODE_DY;
state.is_first = false;
}
/* New top panel is collapsed if self or parent is collapsed. */
const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
/* New top panel is collapsed if self or parent is collapsed. */
const bool is_collapsed = is_parent_collapsed || item.state->is_collapsed();
/* Round the socket location to stop it from jiggling. */
item.runtime->location_y = round(locy + NODE_DYS);
if (!is_collapsed) {
locy -= NODE_ITEM_SPACING_Y / 2; /* Space at bottom of panel header. */
}
item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
if (!is_collapsed) {
locy -= NODE_ITEM_SPACING_Y; /* Space at top of panel contents. */
node_update_basis_buttons(C, ntree, node, item.panel_decl->draw_buttons, block, locy);
}
/* Round the socket location to stop it from jiggling. */
item.runtime->location_y = round(locy + NODE_DYS);
if (is_collapsed) {
item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
}
else {
locy -= NODE_ITEM_SPACING_Y / 2; /* Space at bottom of panel header. */
item.runtime->max_content_y = item.runtime->min_content_y = round(locy);
locy -= NODE_ITEM_SPACING_Y; /* Space at top of panel contents. */
add_panel_items_recursive(C,
ntree,
node,
block,
locx,
locy,
item.panel_decl->num_child_decls,
is_collapsed,
item.panel_decl->name.c_str(),
item.runtime,
state);
node_update_basis_buttons(C, ntree, node, item.panel_decl->draw_buttons, block, locy);
}
add_panel_items_recursive(C,
ntree,
node,
block,
locx,
locy,
item.panel_decl->num_child_decls,
is_collapsed,
item.panel_decl->name.c_str(),
item.runtime,
state);
}
}
else if (item.is_valid_socket()) {
if (item.input) {
@ -804,7 +821,7 @@ static void add_panel_items_recursive(const bContext &C,
}
locy -= NODE_ITEM_SPACING_Y / 2; /* Space at top of next panel header. */
}
};
}
/* Advanced drawing with panels and arbitrary input/output ordering. */
static void node_update_basis_from_declaration(
@ -819,7 +836,9 @@ static void node_update_basis_from_declaration(
/* Update item visibility flags first. */
VisibilityUpdateState visibility_state(item_data);
node_update_panel_items_visibility_recursive(-1, false, visibility_state);
/* Dummy state item to write into, unused. */
bNodePanelState root_panel_state;
node_update_panel_items_visibility_recursive(-1, false, root_panel_state, visibility_state);
/* Space at the top. */
locy -= NODE_DYS / 2;
@ -2015,10 +2034,11 @@ static void node_draw_panels_background(const bNode &node, uiBlock &block)
const bke::bNodePanelRuntime &runtime = node.runtime->panels[panel_i];
/* Don't draw hidden or collapsed panels. */
const bool is_visible = !(state.is_collapsed() || state.is_parent_collapsed());
is_last_panel_visible = is_visible;
const bool is_background_visible = state.has_visible_content() &&
!(state.is_collapsed() || state.is_parent_collapsed());
is_last_panel_visible = is_background_visible;
last_panel_content_y = runtime.max_content_y;
if (!is_visible) {
if (!is_background_visible) {
++panel_i;
continue;
}
@ -2068,7 +2088,8 @@ static void node_draw_panels(bNodeTree &ntree, const bNode &node, uiBlock &block
const bNodePanelState &state = node.panel_states()[panel_i];
/* Don't draw hidden panels. */
if (state.is_parent_collapsed()) {
const bool is_header_visible = state.has_visible_content() && !state.is_parent_collapsed();
if (!is_header_visible) {
++panel_i;
continue;
}

View File

@ -35,9 +35,9 @@ static const char *outliner_context_dir[] = {
nullptr,
};
int /*eContextResult*/ outliner_context(const bContext *C,
const char *member,
bContextDataResult *result)
int /*eContextResult*/ outliner_main_region_context(const bContext *C,
const char *member,
bContextDataResult *result)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);

View File

@ -674,7 +674,9 @@ void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner);
/* `outliner_context.cc` */
int outliner_context(const bContext *C, const char *member, bContextDataResult *result);
int outliner_main_region_context(const bContext *C,
const char *member,
bContextDataResult *result);
/**
* Helper to safely "cast" a #TreeElement to its new C++ #AbstractTreeElement, if possible.

View File

@ -618,7 +618,6 @@ void ED_spacetype_outliner()
st->id_remap = outliner_id_remap;
st->foreach_id = outliner_foreach_id;
st->deactivate = outliner_deactivate;
st->context = outliner_context;
st->blend_read_data = outliner_space_blend_read_data;
st->blend_read_after_liblink = outliner_space_blend_read_after_liblink;
st->blend_write = outliner_space_blend_write;
@ -633,6 +632,7 @@ void ED_spacetype_outliner()
art->free = outliner_main_region_free;
art->listener = outliner_main_region_listener;
art->message_subscribe = outliner_main_region_message_subscribe;
art->context = outliner_main_region_context;
BLI_addhead(&st->regiontypes, art);
/* regions: header */

View File

@ -153,6 +153,6 @@ static void recalcData_sequencer_retiming(TransInfo *t)
TransConvertTypeInfo TransConvertType_SequencerRetiming = {
/*flags*/ (T_POINTS | T_2D_EDIT),
/*createTransData*/ createTransSeqRetimingData,
/*recalcData*/ recalcData_sequencer_retiming,
/*create_trans_data*/ createTransSeqRetimingData,
/*recalc_data*/ recalcData_sequencer_retiming,
};

View File

@ -55,6 +55,19 @@ typedef enum eGPUSupportLevel {
GPU_SUPPORT_LEVEL_UNSUPPORTED,
} eGPUSupportLevel;
typedef enum GPUArchitectureType {
/* Immediate Mode Renderer (IMR).
* Typically, an IMR architecture will execute GPU work in sequence, rasterizing primitives in
* order. */
GPU_ARCHITECTURE_IMR = 0,
/* Tile-Based-Deferred-Renderer (TBDR).
* A TBDR architecture will typically execute the vertex stage up-front for all primitives,
* binning geometry into distinct tiled regions. Fragments will then be rasterized within
* the bounds of one tile at a time. */
GPU_ARCHITECTURE_TBDR = 1,
} GPUArchitectureType;
#ifdef __cplusplus
extern "C" {
#endif
@ -74,6 +87,7 @@ const char *GPU_platform_renderer(void);
const char *GPU_platform_version(void);
const char *GPU_platform_support_level_key(void);
const char *GPU_platform_gpu_name(void);
GPUArchitectureType GPU_platform_architecture(void);
#ifdef __cplusplus
}

View File

@ -30,7 +30,8 @@ class DummyBackend : public GPUBackend {
GPU_BACKEND_NONE,
"Unknown",
"",
"");
"",
GPU_ARCHITECTURE_IMR);
}
void delete_resources() override {}
void samplers_update() override {}

View File

@ -205,13 +205,17 @@ void GPU_render_end()
{
GPUBackend *backend = GPUBackend::get();
BLI_assert(backend);
backend->render_end();
if (backend) {
backend->render_end();
}
}
void GPU_render_step()
{
GPUBackend *backend = GPUBackend::get();
BLI_assert(backend);
backend->render_step();
if (backend) {
backend->render_step();
}
}
/** \} */

View File

@ -70,7 +70,8 @@ void GPUPlatformGlobal::init(eGPUDeviceType gpu_device,
eGPUBackendType backend,
const char *vendor_str,
const char *renderer_str,
const char *version_str)
const char *version_str,
GPUArchitectureType arch_type)
{
this->clear();
@ -91,6 +92,7 @@ void GPUPlatformGlobal::init(eGPUDeviceType gpu_device,
this->support_key = create_key(gpu_support_level, vendor, renderer, version);
this->gpu_name = create_gpu_name(vendor, renderer, version);
this->backend = backend;
this->architecture_type = arch_type;
}
void GPUPlatformGlobal::clear()
@ -149,6 +151,12 @@ const char *GPU_platform_gpu_name()
return GPG.gpu_name;
}
GPUArchitectureType GPU_platform_architecture()
{
BLI_assert(GPG.initialized);
return GPG.architecture_type;
}
bool GPU_type_matches(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver)
{
return GPU_type_matches_ex(device, os, driver, GPU_BACKEND_ANY);

View File

@ -25,6 +25,7 @@ class GPUPlatformGlobal {
char *support_key = nullptr;
char *gpu_name = nullptr;
eGPUBackendType backend = GPU_BACKEND_NONE;
GPUArchitectureType architecture_type = GPU_ARCHITECTURE_IMR;
public:
void init(eGPUDeviceType gpu_device,
@ -34,7 +35,8 @@ class GPUPlatformGlobal {
eGPUBackendType backend,
const char *vendor_str,
const char *renderer_str,
const char *version_str);
const char *version_str,
GPUArchitectureType arch_type);
void clear();
};

View File

@ -194,6 +194,8 @@ void MTLBackend::platform_init(MTLContext *ctx)
if (G.debug & G_DEBUG_GPU) {
printf("METAL API - DETECTED GPU: %s\n", vendor);
}
GPUArchitectureType architecture_type = (mtl_device.hasUnifiedMemory) ? GPU_ARCHITECTURE_TBDR :
GPU_ARCHITECTURE_IMR;
/* macOS is the only supported platform, but check to ensure we are not building with Metal
* enablement on another platform. */
@ -240,7 +242,15 @@ void MTLBackend::platform_init(MTLContext *ctx)
printf("Renderer: %s\n", renderer);
}
GPG.init(device, os, driver, support_level, GPU_BACKEND_METAL, vendor, renderer, version);
GPG.init(device,
os,
driver,
support_level,
GPU_BACKEND_METAL,
vendor,
renderer,
version,
architecture_type);
}
void MTLBackend::platform_exit()

View File

@ -163,7 +163,15 @@ void GLBackend::platform_init()
}
}
GPG.init(device, os, driver, support_level, GPU_BACKEND_OPENGL, vendor, renderer, version);
GPG.init(device,
os,
driver,
support_level,
GPU_BACKEND_OPENGL,
vendor,
renderer,
version,
GPU_ARCHITECTURE_IMR);
}
void GLBackend::platform_exit()

View File

@ -50,7 +50,8 @@ void VKBackend::platform_init()
GPU_BACKEND_VULKAN,
"",
"",
"");
"",
GPU_ARCHITECTURE_IMR);
}
void VKBackend::platform_init(const VKDevice &device)
@ -72,7 +73,8 @@ void VKBackend::platform_init(const VKDevice &device)
GPU_BACKEND_VULKAN,
vendor_name.c_str(),
properties.deviceName,
driver_version.c_str());
driver_version.c_str(),
GPU_ARCHITECTURE_IMR);
}
void VKBackend::detect_workarounds(VKDevice &device)

View File

@ -933,28 +933,6 @@ void convert_host_to_device(void *dst_buffer,
convert_buffer(dst_buffer, src_buffer, buffer_size, device_format, conversion_type);
}
void convert_host_to_device(void *dst_buffer,
const void *src_buffer,
uint2 src_size,
uint src_row_length,
eGPUDataFormat host_format,
eGPUTextureFormat device_format)
{
const uint8_t *src = static_cast<const uint8_t *>(src_buffer);
uint8_t *dst = static_cast<uint8_t *>(dst_buffer);
ConversionType conversion_type = host_to_device(host_format, device_format);
size_t src_row_len = src_row_length * to_bytesize(device_format, host_format);
size_t dst_row_len = src_size.x * to_bytesize(device_format);
for (uint row : IndexRange(src_size.y)) {
convert_buffer(&dst[dst_row_len * row],
&src[src_row_len * row],
src_size.x,
device_format,
conversion_type);
}
}
void convert_device_to_host(void *dst_buffer,
const void *src_buffer,
size_t buffer_size,

View File

@ -33,27 +33,6 @@ void convert_host_to_device(void *dst_buffer,
eGPUDataFormat host_format,
eGPUTextureFormat device_format);
/**
* Convert host buffer to device buffer with row length.
*
* \param dst_buffer: device buffer.
* \param src_buffer: host buffer.
* \param src_size: size of the host buffer.
* \param src_row_length: Length of a single row of the buffer (in pixels).
* \param host_format: format of the host buffer.
* \param device_format: format of the device buffer.
*
* \note Will assert when the host_format/device_format combination isn't valid
* (#validate_data_format) or supported. Some combinations aren't supported in Vulkan due to
* platform incompatibility.
*/
void convert_host_to_device(void *dst_buffer,
const void *src_buffer,
uint2 src_size,
uint src_row_length,
eGPUDataFormat host_format,
eGPUTextureFormat device_format);
/**
* Convert device buffer to host buffer.
*

View File

@ -181,7 +181,6 @@ void VKDescriptorSetTracker::update(VKContext &context)
}
/* TODO: Based on the actual usage we should use
* VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL/VK_IMAGE_LAYOUT_GENERAL. */
binding.texture->ensure_allocated();
binding.texture->layout_ensure(context, VK_IMAGE_LAYOUT_GENERAL);
VkDescriptorImageInfo image_info = {};
image_info.sampler = binding.vk_sampler;

View File

@ -167,7 +167,6 @@ void VKFrameBuffer::clear(const eGPUFrameBufferBits buffers,
<< "PERFORMANCE: impact clearing depth texture in render pass that doesn't allow "
"depth writes.\n";
}
depth_texture->ensure_allocated();
depth_attachment_layout_ensure(context, VK_IMAGE_LAYOUT_GENERAL);
depth_texture->clear_depth_stencil(buffers, clear_depth, clear_stencil);
}
@ -225,14 +224,17 @@ void VKFrameBuffer::read(eGPUFrameBufferBits plane,
int slot,
void *r_data)
{
VKContext &context = *VKContext::get();
VKTexture *texture = nullptr;
switch (plane) {
case GPU_COLOR_BIT:
texture = unwrap(unwrap(attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex));
color_attachment_layout_ensure(context, slot, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
texture = unwrap(unwrap(color_tex(slot)));
break;
case GPU_DEPTH_BIT:
texture = unwrap(unwrap(attachments_[GPU_FB_DEPTH_ATTACHMENT].tex));
depth_attachment_layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
texture = unwrap(unwrap(depth_tex()));
break;
default:
@ -252,44 +254,26 @@ void VKFrameBuffer::read(eGPUFrameBufferBits plane,
/** \name Blit operations
* \{ */
void VKFrameBuffer::blit_to(eGPUFrameBufferBits planes,
int src_slot,
FrameBuffer *dst,
int dst_slot,
int dst_offset_x,
int dst_offset_y)
static void blit_aspect(VKCommandBuffer &command_buffer,
VKTexture &dst_texture,
VKTexture &src_texture,
int dst_offset_x,
int dst_offset_y,
VkImageAspectFlagBits image_aspect)
{
BLI_assert(dst);
BLI_assert(planes == GPU_COLOR_BIT);
UNUSED_VARS_NDEBUG(planes);
VKContext &context = *VKContext::get();
if (!context.has_active_framebuffer()) {
BLI_assert_unreachable();
/* Prefer texture copy, as some platforms don't support using D32_SFLOAT_S8_UINT to be used as
* a blit destination. */
if (dst_offset_x == 0 && dst_offset_y == 0 &&
dst_texture.format_get() == src_texture.format_get() &&
src_texture.width_get() == dst_texture.width_get() &&
src_texture.height_get() == dst_texture.height_get())
{
src_texture.copy_to(dst_texture, image_aspect);
return;
}
/* Retrieve source texture. */
const GPUAttachment &src_attachment = attachments_[GPU_FB_COLOR_ATTACHMENT0 + src_slot];
if (src_attachment.tex == nullptr) {
return;
}
color_attachment_layout_ensure(context, src_slot, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
VKTexture &src_texture = *unwrap(unwrap(src_attachment.tex));
/* Retrieve destination texture. */
VKFrameBuffer &dst_framebuffer = *unwrap(dst);
dst_framebuffer.color_attachment_layout_ensure(
context, dst_slot, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
const GPUAttachment &dst_attachment =
dst_framebuffer.attachments_[GPU_FB_COLOR_ATTACHMENT0 + dst_slot];
if (dst_attachment.tex == nullptr) {
return;
}
VKTexture &dst_texture = *unwrap(unwrap(dst_attachment.tex));
VkImageBlit image_blit = {};
image_blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_blit.srcSubresource.aspectMask = image_aspect;
image_blit.srcSubresource.mipLevel = 0;
image_blit.srcSubresource.baseArrayLayer = 0;
image_blit.srcSubresource.layerCount = 1;
@ -300,7 +284,7 @@ void VKFrameBuffer::blit_to(eGPUFrameBufferBits planes,
image_blit.srcOffsets[1].y = src_texture.height_get();
image_blit.srcOffsets[1].z = 1;
image_blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_blit.dstSubresource.aspectMask = image_aspect;
image_blit.dstSubresource.mipLevel = 0;
image_blit.dstSubresource.baseArrayLayer = 0;
image_blit.dstSubresource.layerCount = 1;
@ -311,7 +295,74 @@ void VKFrameBuffer::blit_to(eGPUFrameBufferBits planes,
image_blit.dstOffsets[1].y = dst_offset_y + src_texture.height_get();
image_blit.dstOffsets[1].z = 1;
context.command_buffer_get().blit(dst_texture, src_texture, Span<VkImageBlit>(&image_blit, 1));
command_buffer.blit(dst_texture, src_texture, Span<VkImageBlit>(&image_blit, 1));
}
void VKFrameBuffer::blit_to(eGPUFrameBufferBits planes,
int src_slot,
FrameBuffer *dst,
int dst_slot,
int dst_offset_x,
int dst_offset_y)
{
BLI_assert(dst);
BLI_assert_msg(ELEM(planes, GPU_COLOR_BIT, GPU_DEPTH_BIT),
"VKFrameBuffer::blit_to only supports a single color or depth aspect.");
UNUSED_VARS_NDEBUG(planes);
VKContext &context = *VKContext::get();
VKCommandBuffer &command_buffer = context.command_buffer_get();
if (!context.has_active_framebuffer()) {
BLI_assert_unreachable();
return;
}
VKFrameBuffer &dst_framebuffer = *unwrap(dst);
if (planes & GPU_COLOR_BIT) {
const GPUAttachment &src_attachment = attachments_[GPU_FB_COLOR_ATTACHMENT0 + src_slot];
const GPUAttachment &dst_attachment =
dst_framebuffer.attachments_[GPU_FB_COLOR_ATTACHMENT0 + dst_slot];
if (src_attachment.tex && dst_attachment.tex) {
VKTexture &src_texture = *unwrap(unwrap(src_attachment.tex));
VKTexture &dst_texture = *unwrap(unwrap(dst_attachment.tex));
color_attachment_layout_ensure(context, src_slot, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
dst_framebuffer.color_attachment_layout_ensure(
context, dst_slot, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
blit_aspect(command_buffer,
dst_texture,
src_texture,
dst_offset_x,
dst_offset_y,
VK_IMAGE_ASPECT_COLOR_BIT);
}
}
if (planes & GPU_DEPTH_BIT) {
/* Retrieve source texture. */
const GPUAttachment &src_attachment = attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex ?
attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT] :
attachments_[GPU_FB_DEPTH_ATTACHMENT];
const GPUAttachment &dst_attachment =
dst_framebuffer.attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex ?
dst_framebuffer.attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT] :
dst_framebuffer.attachments_[GPU_FB_DEPTH_ATTACHMENT];
if (src_attachment.tex && dst_attachment.tex) {
VKTexture &src_texture = *unwrap(unwrap(src_attachment.tex));
VKTexture &dst_texture = *unwrap(unwrap(dst_attachment.tex));
depth_attachment_layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
dst_framebuffer.depth_attachment_layout_ensure(context,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
blit_aspect(command_buffer,
dst_texture,
src_texture,
dst_offset_x,
dst_offset_y,
VK_IMAGE_ASPECT_DEPTH_BIT);
}
}
command_buffer.submit();
}
/** \} */
@ -375,7 +426,6 @@ void VKFrameBuffer::render_pass_create()
if (attachment.tex) {
/* Ensure texture is allocated to ensure the image view. */
VKTexture &texture = *static_cast<VKTexture *>(unwrap(attachment.tex));
texture.ensure_allocated();
image_views_.append(VKImageView(texture,
eImageViewUsage::Attachment,
IndexRange(max_ii(attachment.layer, 0), 1),
@ -493,7 +543,6 @@ void VKFrameBuffer::color_attachment_layout_ensure(VKContext &context,
return;
}
color_texture->ensure_allocated();
color_texture->layout_ensure(context, requested_layout);
dirty_attachments_ = true;
}

View File

@ -23,6 +23,11 @@ class VKPixelBuffer : public PixelBuffer {
void unmap() override;
int64_t get_native_handle() override;
size_t get_size() override;
VKBuffer &buffer_get()
{
return buffer_;
}
};
} // namespace blender::gpu

View File

@ -25,7 +25,7 @@ namespace blender::gpu {
VKTexture::~VKTexture()
{
if (is_allocated() && !is_texture_view()) {
if (vk_image_ != VK_NULL_HANDLE && allocation_ != VK_NULL_HANDLE) {
VKDevice &device = VKBackend::get().device_get();
device.discard_image(vk_image_, allocation_);
@ -48,8 +48,6 @@ void VKTexture::generate_mipmap()
return;
}
ensure_allocated();
VKContext &context = *VKContext::get();
VKCommandBuffer &command_buffer = context.command_buffer_get();
layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
@ -112,6 +110,26 @@ void VKTexture::generate_mipmap()
current_layout_set(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
}
void VKTexture::copy_to(VKTexture &dst_texture, VkImageAspectFlagBits vk_image_aspect)
{
VKContext &context = *VKContext::get();
layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
dst_texture.layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkImageCopy region = {};
region.srcSubresource.aspectMask = vk_image_aspect;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.layerCount = vk_layer_count(1);
region.dstSubresource.aspectMask = vk_image_aspect;
region.dstSubresource.mipLevel = 0;
region.dstSubresource.layerCount = vk_layer_count(1);
region.extent = vk_extent_3d(0);
VKCommandBuffer &command_buffer = context.command_buffer_get();
command_buffer.copy(dst_texture, *this, Span<VkImageCopy>(&region, 1));
command_buffer.submit();
}
void VKTexture::copy_to(Texture *tex)
{
VKTexture *dst = unwrap(tex);
@ -122,32 +140,12 @@ void VKTexture::copy_to(Texture *tex)
BLI_assert(!is_texture_view());
UNUSED_VARS_NDEBUG(src);
VKContext &context = *VKContext::get();
ensure_allocated();
layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
dst->ensure_allocated();
dst->layout_ensure(context, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkImageCopy region = {};
region.srcSubresource.aspectMask = to_vk_image_aspect_flag_bits(format_);
region.srcSubresource.mipLevel = 0;
region.srcSubresource.layerCount = vk_layer_count(1);
region.dstSubresource.aspectMask = to_vk_image_aspect_flag_bits(format_);
region.dstSubresource.mipLevel = 0;
region.dstSubresource.layerCount = vk_layer_count(1);
region.extent = vk_extent_3d(0);
VKCommandBuffer &command_buffer = context.command_buffer_get();
command_buffer.copy(*dst, *this, Span<VkImageCopy>(&region, 1));
command_buffer.submit();
copy_to(*dst, to_vk_image_aspect_flag_bits(format_));
}
void VKTexture::clear(eGPUDataFormat format, const void *data)
{
BLI_assert(!is_texture_view());
if (!is_allocated()) {
allocate();
}
VKContext &context = *VKContext::get();
VKCommandBuffer &command_buffer = context.command_buffer_get();
@ -168,9 +166,6 @@ void VKTexture::clear_depth_stencil(const eGPUFrameBufferBits buffers,
{
BLI_assert(buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT));
if (!is_allocated()) {
allocate();
}
VKContext &context = *VKContext::get();
VKCommandBuffer &command_buffer = context.command_buffer_get();
VkClearDepthStencilValue clear_depth_stencil;
@ -255,13 +250,9 @@ void VKTexture::update_sub(
int mip, int offset[3], int extent_[3], eGPUDataFormat format, const void *data)
{
BLI_assert(!is_texture_view());
if (!is_allocated()) {
allocate();
}
/* Vulkan images cannot be directly mapped to host memory and requires a staging buffer. */
VKContext &context = *VKContext::get();
VKBuffer staging_buffer;
int layers = vk_layer_count(1);
int3 extent = int3(extent_[0], max_ii(extent_[1], 1), max_ii(extent_[2], 1));
size_t sample_len = extent.x * extent.y * extent.z;
@ -275,26 +266,15 @@ void VKTexture::update_sub(
extent.z = 1;
}
VKBuffer staging_buffer;
staging_buffer.create(device_memory_size, GPU_USAGE_DYNAMIC, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
uint buffer_row_length = context.state_manager_get().texture_unpack_row_length_get();
if (buffer_row_length) {
/* Use custom row length #GPU_texture_unpack_row_length */
convert_host_to_device(staging_buffer.mapped_memory_get(),
data,
uint2(extent),
buffer_row_length,
format,
format_);
}
else {
convert_host_to_device(staging_buffer.mapped_memory_get(), data, sample_len, format, format_);
}
convert_host_to_device(staging_buffer.mapped_memory_get(), data, sample_len, format, format_);
VkBufferImageCopy region = {};
region.imageExtent.width = extent.x;
region.imageExtent.height = extent.y;
region.imageExtent.depth = extent.z;
region.bufferRowLength = context.state_manager_get().texture_unpack_row_length_get();
region.imageOffset.x = offset[0];
region.imageOffset.y = offset[1];
region.imageOffset.z = offset[2];
@ -325,10 +305,6 @@ uint VKTexture::gl_bindcode_get() const
bool VKTexture::init_internal()
{
/* Initialization can only happen after the usage is known. By the current API this isn't set
* at this moment, so we cannot initialize here. The initialization is postponed until the
* allocation of the texture on the device. */
const VKDevice &device = VKBackend::get().device_get();
const VKWorkarounds &workarounds = device.workarounds_get();
if (format_ == GPU_DEPTH_COMPONENT24 && workarounds.not_aligned_pixel_formats) {
@ -338,7 +314,10 @@ bool VKTexture::init_internal()
format_ = GPU_DEPTH32F_STENCIL8;
}
/* TODO: return false when texture format isn't supported. */
if (!allocate()) {
return false;
}
return true;
}
@ -388,23 +367,6 @@ bool VKTexture::is_texture_view() const
return source_texture_ != nullptr;
}
void VKTexture::ensure_allocated()
{
if (is_texture_view()) {
source_texture_->ensure_allocated();
return;
}
if (!is_allocated()) {
allocate();
}
}
bool VKTexture::is_allocated() const
{
return (vk_image_ != VK_NULL_HANDLE && allocation_ != VK_NULL_HANDLE) || is_texture_view();
}
static VkImageUsageFlagBits to_vk_image_usage(const eGPUTextureUsage usage,
const eGPUTextureFormatFlag format_flag)
{
@ -463,7 +425,6 @@ static VkImageCreateFlagBits to_vk_image_create(const eGPUTextureType texture_ty
bool VKTexture::allocate()
{
BLI_assert(vk_image_ == VK_NULL_HANDLE);
BLI_assert(!is_allocated());
BLI_assert(!is_texture_view());
VKContext &context = *VKContext::get();
@ -524,9 +485,6 @@ bool VKTexture::allocate()
void VKTexture::bind(int binding, shader::ShaderCreateInfo::Resource::BindType bind_type)
{
if (!is_allocated()) {
allocate();
}
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get();

View File

@ -56,6 +56,7 @@ class VKTexture : public Texture, public VKBindableResource {
void generate_mipmap() override;
void copy_to(Texture *tex) override;
void copy_to(VKTexture &dst_texture, VkImageAspectFlagBits vk_image_aspect);
void clear(eGPUDataFormat format, const void *data) override;
void clear_depth_stencil(const eGPUFrameBufferBits buffer,
float clear_depth,
@ -85,8 +86,6 @@ class VKTexture : public Texture, public VKBindableResource {
return vk_image_;
}
void ensure_allocated();
protected:
bool init_internal() override;
bool init_internal(GPUVertBuf *vbo) override;
@ -96,9 +95,6 @@ class VKTexture : public Texture, public VKBindableResource {
/** Is this texture a view of another texture. */
bool is_texture_view() const;
/** Is this texture already allocated on device. */
bool is_allocated() const;
/**
* Allocate the texture of the device. Result is `true` when texture is successfully allocated
* on the device.

View File

@ -174,7 +174,7 @@ void load_plydata(PlyData &plyData, Depsgraph *depsgraph, const PLYExportParams
BKE_object_get_pre_modified_mesh(&export_object_eval_);
bool force_triangulation = false;
const OffsetIndices faces = mesh->faces();
OffsetIndices faces = mesh->faces();
for (const int i : faces.index_range()) {
if (faces[i].size() > 255) {
force_triangulation = true;
@ -186,6 +186,7 @@ void load_plydata(PlyData &plyData, Depsgraph *depsgraph, const PLYExportParams
bool manually_free_mesh = false;
if (export_params.export_triangulated_mesh || force_triangulation) {
mesh = do_triangulation(mesh, export_params.export_triangulated_mesh);
faces = mesh->faces();
manually_free_mesh = true;
}

View File

@ -157,7 +157,6 @@ typedef struct Nurb {
typedef struct CharInfo {
float kern;
/** Index start at 1, unlike mesh & nurbs. */
short mat_nr;
char flag;
char _pad[1];

View File

@ -315,6 +315,8 @@ typedef enum eNodePanelFlag {
NODE_PANEL_COLLAPSED = (1 << 0),
/* The parent panel is collapsed. */
NODE_PANEL_PARENT_COLLAPSED = (1 << 1),
/* The panel has visible content. */
NODE_PANEL_CONTENT_VISIBLE = (1 << 2),
} eNodePanelFlag;
typedef struct bNodePanelState {
@ -327,6 +329,7 @@ typedef struct bNodePanelState {
#ifdef __cplusplus
bool is_collapsed() const;
bool is_parent_collapsed() const;
bool has_visible_content() const;
#endif
} bNodePanelState;

View File

@ -1635,10 +1635,9 @@ typedef struct ToolSettings {
char transform_pivot_point;
char transform_flag;
/** Snap elements (per space-type), #eSnapMode. */
char _pad1[1];
short snap_mode;
char snap_node_mode;
char snap_uv_mode;
short snap_mode;
short snap_uv_mode;
short snap_anim_mode;
/** Generic flags (per space-type), #eSnapFlag. */
short snap_flag;

View File

@ -1083,9 +1083,9 @@ typedef enum eUserPref_SpaceData_Flag {
typedef enum eUserPref_Flag {
USER_AUTOSAVE = (1 << 0),
USER_FLAG_NUMINPUT_ADVANCED = (1 << 1),
USER_FLAG_RECENT_SEARCHES_DISABLE = (1 << 2), /* cleared */
USER_FLAG_UNUSED_3 = (1 << 3), /* cleared */
USER_FLAG_UNUSED_4 = (1 << 4), /* cleared */
USER_FLAG_RECENT_SEARCHES_DISABLE = (1 << 2),
USER_FLAG_UNUSED_3 = (1 << 3), /* cleared */
USER_FLAG_UNUSED_4 = (1 << 4), /* cleared */
USER_TRACKBALL = (1 << 5),
USER_FLAG_UNUSED_6 = (1 << 6), /* cleared */
USER_FLAG_UNUSED_7 = (1 << 7), /* cleared */

View File

@ -724,7 +724,6 @@ void RNA_def_collections(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SCENE, nullptr);
prop = RNA_def_property(srna, "lineart_intersection_priority", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 0, 255);
RNA_def_property_ui_text(prop,
"Intersection Priority",
"The intersection line will be included into the object with the "

View File

@ -318,13 +318,13 @@ static void rna_Curve_material_index_range(
static int rna_ChariInfo_material_index_get(PointerRNA *ptr)
{
CharInfo *info = static_cast<CharInfo *>(ptr->data);
return info->mat_nr ? info->mat_nr - 1 : 0;
return info->mat_nr ? info->mat_nr : 0;
}
static void rna_ChariInfo_material_index_set(PointerRNA *ptr, int value)
{
CharInfo *info = static_cast<CharInfo *>(ptr->data);
info->mat_nr = value + 1;
info->mat_nr = value;
}
static void rna_Curve_active_textbox_index_range(

View File

@ -673,6 +673,32 @@ void RNA_identifier_sanitize(char *identifier, int property)
}
}
static bool rna_range_from_int_type(const char *dnatype, int r_range[2])
{
/* Type `char` is unsigned too. */
if (STREQ(dnatype, "char") || STREQ(dnatype, "uchar")) {
r_range[0] = CHAR_MIN;
r_range[1] = CHAR_MAX;
return true;
}
if (STREQ(dnatype, "short")) {
r_range[0] = SHRT_MIN;
r_range[1] = SHRT_MAX;
return true;
}
if (STREQ(dnatype, "int")) {
r_range[0] = INT_MIN;
r_range[1] = INT_MAX;
return true;
}
if (STREQ(dnatype, "int8_t")) {
r_range[0] = INT8_MIN;
r_range[1] = INT8_MAX;
return true;
}
return false;
}
/* Blender Data Definition */
BlenderRNA *RNA_create()
@ -1887,6 +1913,36 @@ void RNA_def_property_enum_items(PropertyRNA *prop, const EnumPropertyItem *item
switch (prop->type) {
case PROP_ENUM: {
/* Access DNA size & range (for additional sanity checks). */
int enum_dna_size = -1;
int enum_dna_range[2];
if (DefRNA.preprocess) {
/* If this is larger, this is likely a string which can sometimes store enums. */
if (PropertyDefRNA *dp = rna_find_struct_property_def(srna, prop)) {
if (dp->dnatype == nullptr || dp->dnatype[0] == '\0') {
/* Unfortunately this happens when #PropertyDefRNA::dnastructname is for e.g.
* `type->region_type` there isn't a convenient way to access the int size. */
}
else if (dp->dnaarraylength > 1) {
/* When an array this is likely a string using get/set functions for enum access. */
}
else if (dp->dnasize == 0) {
/* Some cases function callbacks are used, the DNA size isn't known. */
}
else {
enum_dna_size = dp->dnasize;
if (!rna_range_from_int_type(dp->dnatype, enum_dna_range)) {
CLOG_ERROR(&LOG,
"\"%s.%s\", enum type \"%s\" size is not known.",
srna->identifier,
prop->identifier,
dp->dnatype);
}
}
}
}
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
eprop->item = (EnumPropertyItem *)item;
eprop->totitem = 0;
@ -1903,6 +1959,47 @@ void RNA_def_property_enum_items(PropertyRNA *prop, const EnumPropertyItem *item
DefRNA.error = true;
break;
}
/* When the integer size is known, check the flag wont fit. */
if (enum_dna_size != -1) {
if (prop->flag & PROP_ENUM_FLAG) {
uint32_t enum_type_mask = 0;
if (enum_dna_size == 1) {
enum_type_mask = 0xff;
}
else if (enum_dna_size == 2) {
enum_type_mask = 0xffff;
}
if (enum_type_mask != 0) {
if (uint32_t(item[i].value) != (uint32_t(item[i].value) & enum_type_mask)) {
CLOG_ERROR(&LOG,
"\"%s.%s\", enum value for '%s' does not fit into %d byte(s).",
srna->identifier,
prop->identifier,
item[i].identifier,
enum_dna_size);
DefRNA.error = true;
break;
}
}
}
else {
if (ELEM(enum_dna_size, 1, 2)) {
if ((item[i].value < enum_dna_range[0]) || (item[i].value > enum_dna_range[1])) {
CLOG_ERROR(&LOG,
"\"%s.%s\", enum value for '%s' is outside of range [%d - %d].",
srna->identifier,
prop->identifier,
item[i].identifier,
enum_dna_range[0],
enum_dna_range[1]);
DefRNA.error = true;
break;
}
}
}
}
if (item[i].value == eprop->defaultvalue) {
defaultfound = 1;
}
@ -2385,24 +2482,26 @@ void RNA_def_property_int_sdna(PropertyRNA *prop, const char *structname, const
}
/* SDNA doesn't pass us unsigned unfortunately. */
if (dp->dnatype && STREQ(dp->dnatype, "char")) {
iprop->hardmin = iprop->softmin = CHAR_MIN;
iprop->hardmax = iprop->softmax = CHAR_MAX;
}
else if (dp->dnatype && STREQ(dp->dnatype, "short")) {
iprop->hardmin = iprop->softmin = SHRT_MIN;
iprop->hardmax = iprop->softmax = SHRT_MAX;
}
else if (dp->dnatype && STREQ(dp->dnatype, "int")) {
iprop->hardmin = INT_MIN;
iprop->hardmax = INT_MAX;
if (dp->dnatype != nullptr && (dp->dnatype[0] != '\0')) {
int range[2];
if (rna_range_from_int_type(dp->dnatype, range)) {
iprop->hardmin = iprop->softmin = range[0];
iprop->hardmax = iprop->softmax = range[1];
}
else {
CLOG_ERROR(&LOG,
"\"%s.%s\", type \"%s\" range not known.",
srna->identifier,
prop->identifier,
dp->dnatype);
DefRNA.error = true;
}
iprop->softmin = -10000; /* rather arbitrary. */
iprop->softmax = 10000;
}
else if (dp->dnatype && STREQ(dp->dnatype, "int8_t")) {
iprop->hardmin = iprop->softmin = INT8_MIN;
iprop->hardmax = iprop->softmax = INT8_MAX;
/* Rather arbitrary that this is only done for one type. */
if (STREQ(dp->dnatype, "int")) {
iprop->softmin = -10000;
iprop->softmax = 10000;
}
}
if (ELEM(prop->subtype, PROP_UNSIGNED, PROP_PERCENTAGE, PROP_FACTOR)) {

View File

@ -34,6 +34,7 @@
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_mesh.hh"
#include "BKE_mesh_wrapper.hh"
#include "BKE_modifier.h"
#include "BKE_object_deform.h"
#include "BKE_screen.hh"
@ -380,6 +381,15 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
{
using namespace blender;
if (mesh->totvert == 0) {
/* Output just the start cap even if the mesh is empty. */
Object *start_cap_ob = amd->start_cap;
if (start_cap_ob && start_cap_ob != ctx->object) {
Mesh *start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob);
if (start_cap_mesh) {
BKE_mesh_wrapper_ensure_mdata(start_cap_mesh);
return BKE_mesh_copy_for_eval(start_cap_mesh);
}
}
return mesh;
}
@ -428,6 +438,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob);
if (start_cap_mesh) {
BKE_mesh_wrapper_ensure_mdata(start_cap_mesh);
start_cap_nverts = start_cap_mesh->totvert;
start_cap_nedges = start_cap_mesh->totedge;
start_cap_nloops = start_cap_mesh->totloop;
@ -443,6 +454,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
end_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(end_cap_ob);
if (end_cap_mesh) {
BKE_mesh_wrapper_ensure_mdata(end_cap_mesh);
end_cap_nverts = end_cap_mesh->totvert;
end_cap_nedges = end_cap_mesh->totedge;
end_cap_nloops = end_cap_mesh->totloop;

View File

@ -2,11 +2,11 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <MaterialXFormat/XmlIo.h>
#include "material.h"
#include "node_parser.h"
#include <MaterialXFormat/XmlIo.h>
#include "DEG_depsgraph.hh"
#include "DNA_material_types.h"

View File

@ -2459,7 +2459,10 @@ void RE_RenderAnim(Render *re,
void RE_PreviewRender(Render *re, Main *bmain, Scene *sce)
{
/* Ensure within GPU render boundary. */
GPU_render_begin();
const bool use_gpu = GPU_backend_get_type() != GPU_BACKEND_NONE;
if (use_gpu) {
GPU_render_begin();
}
Object *camera;
int winx, winy;
@ -2483,7 +2486,9 @@ void RE_PreviewRender(Render *re, Main *bmain, Scene *sce)
}
/* Close GPU render boundary. */
GPU_render_end();
if (use_gpu) {
GPU_render_end();
}
}
/* NOTE: repeated win/disprect calc... solve that nicer, also in compo. */

View File

@ -692,6 +692,8 @@ static void wm_file_read_post(bContext *C,
CTX_wm_window_set(C, static_cast<wmWindow *>(wm->windows.first));
}
WM_cursor_wait(true);
#ifdef WITH_PYTHON
if (is_startup_file) {
/* On startup (by default), Python won't have been initialized.
@ -818,6 +820,8 @@ static void wm_file_read_post(bContext *C,
WM_toolsystem_init(C);
}
}
WM_cursor_wait(false);
}
static void wm_read_callback_pre_wrapper(bContext *C, const char *filepath)
@ -1067,6 +1071,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
bf_reports.duration.whole = PIL_check_seconds_timer() - bf_reports.duration.whole;
file_read_reports_finalize(&bf_reports);
WM_cursor_wait(true);
success = true;
}
}
@ -1171,6 +1176,8 @@ void wm_homefile_read_ex(bContext *C,
char filepath_startup[FILE_MAX];
char filepath_userdef[FILE_MAX];
WM_cursor_wait(true);
/* When 'app_template' is set:
* '{BLENDER_USER_CONFIG}/{app_template}' */
char app_template_system[FILE_MAX];
@ -1488,6 +1495,8 @@ void wm_homefile_read_ex(bContext *C,
CTX_wm_window_set(C, nullptr);
}
}
WM_cursor_wait(false);
}
void wm_homefile_read(bContext *C,
@ -1871,13 +1880,6 @@ static bool wm_file_write_check_with_report_on_failure(Main *bmain,
return false;
}
/* Check if file write permission is ok */
if (BLI_exists(filepath) && !BLI_file_is_writable(filepath)) {
BKE_reportf(
reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is not writable", filepath);
return false;
}
LISTBASE_FOREACH (Library *, li, &bmain->libraries) {
if (BLI_path_cmp(li->filepath_abs, filepath) == 0) {
BKE_reportf(reports, RPT_ERROR, "Cannot overwrite used library '%.240s'", filepath);
@ -1916,6 +1918,16 @@ static bool wm_file_write(bContext *C,
/* NOTE: either #BKE_CB_EVT_SAVE_POST or #BKE_CB_EVT_SAVE_POST_FAIL must run.
* Runs at the end of this function, don't return beforehand. */
BKE_callback_exec_string(bmain, BKE_CB_EVT_SAVE_PRE, filepath);
/* Check if file write permission is OK. */
if (BLI_exists(filepath) && !BLI_file_is_writable(filepath)) {
BKE_reportf(
reports, RPT_ERROR, "Cannot save blend file, path \"%s\" is not writable", filepath);
BKE_callback_exec_string(bmain, BKE_CB_EVT_SAVE_POST_FAIL, filepath);
return false;
}
ED_assets_pre_save(bmain);
/* Enforce full override check/generation on file save. */

View File

@ -359,7 +359,10 @@ void WM_init(bContext *C, int argc, const char **argv)
ED_render_clear_mtex_copybuf();
wm_history_file_read();
blender::ui::string_search::read_recent_searches_file();
if (!G.background) {
blender::ui::string_search::read_recent_searches_file();
}
STRNCPY(G.lib, BKE_main_blendfile_path_from_global());

View File

@ -96,7 +96,7 @@ def argparse_create() -> argparse.ArgumentParser:
sort_by_choices = tuple(sorted(SORT_BY_FN.keys()))
# When `--help` or no arguments are given, print this help.
epilog = "This is typically used from the output of a stack-trace on Linux/Unix."
epilog = "Use to automate loading many blend files in a single Blender instance."
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,