2026-03-16

This commit is contained in:
2026-03-17 15:39:39 -06:00
parent 67782275d5
commit 330fed4231
29 changed files with 532 additions and 199 deletions
+5 -5
View File
@@ -10,13 +10,13 @@ D:\Work\9 iClone\Amazon\
D:\Amazon\00_external-files\ D:\Amazon\00_external-files\
N:\1. CHARACTERS\remapping\ N:\1. CHARACTERS\remapping\
[Recent] [Recent]
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Deliverable\
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\
A:\1 Amazon_Active_Projects\260127_Pick-and-Stage_2026_edits\Blends\stills\
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\
C:\Program Files (x86)\Steam\steamapps\common\Blender\5.0\scripts\startup\
P:\260217_Jarvis-Defense\Blends\animations\ P:\260217_Jarvis-Defense\Blends\animations\
T:\1 BlenderAssets\ T:\1 BlenderAssets\
A:\1 Amazon_Active_Projects\1 BlenderAssets\ A:\1 Amazon_Active_Projects\1 BlenderAssets\
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\
F:\renders\ F:\renders\
P:\260217_Jarvis-Defense\Assets\Blends\Props\ P:\260217_Jarvis-Defense\Assets\Blends\Props\
C:\Users\Nathan\Downloads\Shot5\
P:\260217_Jarvis-Defense\Assets\Blends\textures\
P:\260217_Jarvis-Defense\Assets\Blends\textures\3075 Decade Dr\common\
P:\260217_Jarvis-Defense\Assets\
+51 -2
View File
@@ -1,11 +1,60 @@
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_18h.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_18g.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_3d.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_18f.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_18e.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_18c.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_18d.blend
A:\1 Amazon_Active_Projects\260127_Pick-and-Stage_2026_edits\Blends\stills\iP&S_2026_10.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_18a.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\stills\iP&S_BD2_18b.blend
A:\1 Amazon_Active_Projects\260127_Pick-and-Stage_2026_edits\Blends\stills\iP&S_2026_3a-b.blend
A:\1 Amazon_Active_Projects\260127_Pick-and-Stage_2026_edits\Blends\stills\iP&S_2026_1a.blend
A:\1 Amazon_Active_Projects\260127_Pick-and-Stage_2026_edits\Blends\stills\iP&S_2026_1c.blend
A:\1 Amazon_Active_Projects\260127_Pick-and-Stage_2026_edits\Blends\stills\iP&S_2026_1d.blend
C:\Users\Nathan\Downloads\P&S_BD2_animation 6d.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 6d.blend
C:\Users\Nathan\Downloads\PnS_BD2_animation 6d\UNC_NEXUS_amazon\1 Amazon_Active_Projects\1 BlenderAssets\Amazon\Char\Cartoon3\Bebe_v1.1.blend
C:\Users\Nathan\Downloads\PnS_BD2_animation 6d\UNC_NEXUS_amazon\1 Amazon_Active_Projects\1 BlenderAssets\Amazon\Char\Cartoon1\Kirk_v4.4.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 6a.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 6c.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 6b.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 8c_part2.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 11a.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 9b.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 7c.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 5b.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 4d.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 3a_part2.blend
F:\jobs\P&S_BD2_animation 8c\P&S_BD2_animation 8c.flamenco.blend
F:\jobs\P&S_BD2_animation 9e\P&S_BD2_animation 9e.flamenco.blend
F:\jobs\P&S_BD2_animation 8b\P&S_BD2_animation 8b.flamenco.blend
F:\jobs\P&S_BD2_animation 8c_part2\P&S_BD2_animation 8c_part2.flamenco.blend
F:\jobs\P&S_BD2_animation 8a\P&S_BD2_animation 8a.flamenco.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 8a.blend
T:\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 8a.blend
T:\260311_PnS-Beyond-Day-2\Blends\stills\P&S_BD2_animation 8a.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 8c.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 9e.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 8b.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 9c.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 9a.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 12b_part2.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 12b.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 12a.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 11b.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 10b_part3.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 10b_part2.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 10a.blend
A:\1 Amazon_Active_Projects\260311_PnS-Beyond-Day-2\Blends\animations\P&S_BD2_animation 10b_part1.blend
P:\260217_Jarvis-Defense\Blends\animations\Shot_4.blend
P:\260217_Jarvis-Defense\Blends\animations\Shot_3_retimed.blend
F:\jobs\Shot_4c\Shot_4.flamenco.blend F:\jobs\Shot_4c\Shot_4.flamenco.blend
F:\jobs\Shot_4e\Shot_4.flamenco.blend F:\jobs\Shot_4e\Shot_4.flamenco.blend
F:\jobs\Shot_4a\Shot_4.flamenco.blend F:\jobs\Shot_4a\Shot_4.flamenco.blend
F:\jobs\Shot_4g\Shot_4.flamenco.blend F:\jobs\Shot_4g\Shot_4.flamenco.blend
P:\260217_Jarvis-Defense\Blends\animations\Shot_4.blend
F:\jobs\Shot_1a-tqpj\Shot_1.flamenco.blend F:\jobs\Shot_1a-tqpj\Shot_1.flamenco.blend
F:\jobs\Shot_6-4dkh\closeup.flamenco.blend F:\jobs\Shot_6-4dkh\closeup.flamenco.blend
P:\260217_Jarvis-Defense\Blends\animations\Shot_3_retimed.blend
F:\jobs\Shot_0_5a\Shot_0_5.flamenco.blend F:\jobs\Shot_0_5a\Shot_0_5.flamenco.blend
P:\260217_Jarvis-Defense\Blends\animations\closeup.blend P:\260217_Jarvis-Defense\Blends\animations\closeup.blend
F:\jobs\Shot5c\Shot_Q.flamenco.blend F:\jobs\Shot5c\Shot_Q.flamenco.blend
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
schema_version = "1.0.0" schema_version = "1.0.0"
id = "brushstroke_tools" id = "brushstroke_tools"
version = "1.1.2" version = "1.2.1"
name = "Brushstroke Tools" name = "Brushstroke Tools"
tagline = "Brushstroke painting tools by the Blender Studio" tagline = "Brushstroke painting tools by the Blender Studio"
maintainer = "Simon Thommes <simon@blender.org>" maintainer = "Simon Thommes <simon@blender.org>"
@@ -94,6 +94,12 @@ class BSBST_OT_pre_process_brushstroke(bpy.types.Operator):
for prop in props_list: for prop in props_list:
setattr(context.tool_settings.curve_paint_settings, prop, getattr(tool_settings, prop)) setattr(context.tool_settings.curve_paint_settings, prop, getattr(tool_settings, prop))
if bpy.app.version >= (5,1):
utils.ensure_resources()
ng_process = bpy.data.node_groups['.brushstroke_tools.draw_processing']
ng_process.asset_mark() # workaround to let Blender register the node tool as an operator with the automatically generated id
ng_process.asset_clear()
return {'FINISHED'} return {'FINISHED'}
class BSBST_OT_post_process_brushstroke(bpy.types.Operator): class BSBST_OT_post_process_brushstroke(bpy.types.Operator):
@@ -119,7 +125,11 @@ class BSBST_OT_post_process_brushstroke(bpy.types.Operator):
if 'BSBST_surface_object' in context.object.keys(): if 'BSBST_surface_object' in context.object.keys():
if context.object['BSBST_surface_object']: if context.object['BSBST_surface_object']:
self.ng_process.nodes['surface_object'].inputs[0].default_value = context.object['BSBST_surface_object'] self.ng_process.nodes['surface_object'].inputs[0].default_value = context.object['BSBST_surface_object']
if bpy.app.version < (5,1):
bpy.ops.geometry.execute_node_group(name="set_brush_stroke_color", session_uid=self.ng_process.session_uid) bpy.ops.geometry.execute_node_group(name="set_brush_stroke_color", session_uid=self.ng_process.session_uid)
else:
bpy.ops.geometry._brushstroke_tools_draw_processing()
preserve_draw_settings(context, restore=True) preserve_draw_settings(context, restore=True)
return {'FINISHED'} return {'FINISHED'}
+41 -34
View File
@@ -30,13 +30,10 @@ class BSBST_OT_new_brushstrokes(bpy.types.Operator):
settings.brushstroke_method = self.method settings.brushstroke_method = self.method
if settings.curve_mode == 'GP': if settings.curve_mode == 'GP':
bpy.ops.object.grease_pencil_add(type='EMPTY') brushstrokes_data = bpy.data.grease_pencils.new(name)
context.object.name = name bs_layer = brushstrokes_data.layers.new('Brushstrokes')
context.object.data.name = name bs_layer.frames.new(1)
brushstrokes_object = context.object elif settings.curve_mode == 'CURVE':
context.collection.objects.unlink(brushstrokes_object)
else:
if settings.curve_mode == 'CURVE':
brushstrokes_data = bpy.data.curves.new(name, type='CURVE') brushstrokes_data = bpy.data.curves.new(name, type='CURVE')
brushstrokes_data.dimensions = '3D' brushstrokes_data.dimensions = '3D'
elif settings.curve_mode == 'CURVES': elif settings.curve_mode == 'CURVES':
@@ -44,7 +41,7 @@ class BSBST_OT_new_brushstrokes(bpy.types.Operator):
brushstrokes_object = bpy.data.objects.new(name, brushstrokes_data) brushstrokes_object = bpy.data.objects.new(name, brushstrokes_data)
# link to surface object's collections (fall back to active collection if all are linked data) # link to surface object's collections (fall back to active collection if all are linked data)
utils.link_to_collections_by_ref(brushstrokes_object, surface_object) utils.link_to_collections_by_ref(brushstrokes_object, surface_object, unlink=False)
brushstrokes_object.visible_shadow = False brushstrokes_object.visible_shadow = False
brushstrokes_object['BSBST_version'] = utils.addon_version brushstrokes_object['BSBST_version'] = utils.addon_version
@@ -55,13 +52,10 @@ class BSBST_OT_new_brushstrokes(bpy.types.Operator):
def new_flow_object(self, context, name, surface_object): def new_flow_object(self, context, name, surface_object):
settings = context.scene.BSBST_settings settings = context.scene.BSBST_settings
if settings.curve_mode == 'GP': if settings.curve_mode == 'GP':
bpy.ops.object.grease_pencil_add(type='EMPTY') flow_data = bpy.data.grease_pencils.new(name)
context.object.name = name fl_layer = flow_data.layers.new('Flow')
context.object.data.name = name fl_layer.frames.new(1)
flow_object = context.object elif settings.curve_mode == 'CURVE':
context.collection.objects.unlink(flow_object)
else:
if settings.curve_mode == 'CURVE':
flow_data = bpy.data.curves.new(name, type='CURVE') flow_data = bpy.data.curves.new(name, type='CURVE')
flow_data.dimensions = '3D' flow_data.dimensions = '3D'
elif settings.curve_mode == 'CURVES': elif settings.curve_mode == 'CURVES':
@@ -69,7 +63,7 @@ class BSBST_OT_new_brushstrokes(bpy.types.Operator):
flow_object = bpy.data.objects.new(name, flow_data) flow_object = bpy.data.objects.new(name, flow_data)
# link to surface object's collections (fall back to active collection if all are linked data) # link to surface object's collections (fall back to active collection if all are linked data)
utils.link_to_collections_by_ref(flow_object, surface_object) utils.link_to_collections_by_ref(flow_object, surface_object, unlink=False)
visibility_options = [ visibility_options = [
'visible_camera', 'visible_camera',
@@ -112,7 +106,7 @@ class BSBST_OT_new_brushstrokes(bpy.types.Operator):
self.report({"ERROR"}, "Surface Object needs an available UV Map") self.report({"ERROR"}, "Surface Object needs an available UV Map")
return {"CANCELLED"} return {"CANCELLED"}
name = f'{surface_object.name} - Brushstrokes' name = utils.bs_name(surface_object.name)
brushstrokes_object = self.new_brushstrokes_object(context, name, surface_object) brushstrokes_object = self.new_brushstrokes_object(context, name, surface_object)
flow_is_required = settings.brushstroke_method == 'SURFACE_FILL' flow_is_required = settings.brushstroke_method == 'SURFACE_FILL'
if flow_is_required: if flow_is_required:
@@ -151,7 +145,9 @@ class BSBST_OT_new_brushstrokes(bpy.types.Operator):
with context.temp_override(**override): with context.temp_override(**override):
bpy.ops.object.material_slot_add() bpy.ops.object.material_slot_add()
brushstrokes_object.material_slots[0].material = preset_material brushstrokes_object.material_slots[0].material = preset_material
settings.silent_switch = True
settings.context_material = preset_material settings.context_material = preset_material
settings.silent_switch = False
brushstrokes_object['BSBST_material'] = settings.context_material brushstrokes_object['BSBST_material'] = settings.context_material
# transfer preset modifiers to new brushstrokes TODO: refactor to deduplicate # transfer preset modifiers to new brushstrokes TODO: refactor to deduplicate
@@ -238,7 +234,7 @@ class BSBST_OT_new_brushstrokes(bpy.types.Operator):
mod.show_group_selector = False mod.show_group_selector = False
# update brushstroke context # update brushstroke context
utils.find_context_brushstrokes(None) utils.find_context_brushstrokes(context.scene, context.view_layer.depsgraph)
for i, name in enumerate([bs.name for bs in settings.context_brushstrokes]): for i, name in enumerate([bs.name for bs in settings.context_brushstrokes]):
if name == brushstrokes_object.name: if name == brushstrokes_object.name:
settings.active_context_brushstrokes_index = i settings.active_context_brushstrokes_index = i
@@ -297,7 +293,7 @@ class BSBST_OT_delete_brushstrokes(bpy.types.Operator):
edit_toggle = settings.edit_toggle edit_toggle = settings.edit_toggle
settings.edit_toggle = False settings.edit_toggle = False
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
if not bs_ob: if not bs_ob:
settings.edit_toggle = edit_toggle settings.edit_toggle = edit_toggle
return {"CANCELLED"} return {"CANCELLED"}
@@ -313,7 +309,7 @@ class BSBST_OT_delete_brushstrokes(bpy.types.Operator):
if surface_object: if surface_object:
context.view_layer.objects.active = surface_object context.view_layer.objects.active = surface_object
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
if bs_ob: if bs_ob:
context.view_layer.objects.active = bs_ob context.view_layer.objects.active = bs_ob
bs_ob.select_set(True) bs_ob.select_set(True)
@@ -346,7 +342,7 @@ class BSBST_OT_duplicate_brushstrokes(bpy.types.Operator):
def execute(self, context): def execute(self, context):
settings = context.scene.BSBST_settings settings = context.scene.BSBST_settings
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
if not bs_ob: if not bs_ob:
return {"CANCELLED"} return {"CANCELLED"}
@@ -410,7 +406,7 @@ class BSBST_OT_copy_brushstrokes(bpy.types.Operator):
def execute(self, context): def execute(self, context):
settings = context.scene.BSBST_settings settings = context.scene.BSBST_settings
active_surface_object = utils.get_surface_object(utils.get_active_context_brushstrokes_object(context)) active_surface_object = utils.get_surface_object(utils.get_active_context_brushstrokes_object(context.scene))
surface_objects = [ob for ob in context.selected_objects surface_objects = [ob for ob in context.selected_objects
if ob.type=='MESH' if ob.type=='MESH'
@@ -423,7 +419,7 @@ class BSBST_OT_copy_brushstrokes(bpy.types.Operator):
bs_objects = [bpy.data.objects.get(bs.name) for bs in settings.context_brushstrokes] bs_objects = [bpy.data.objects.get(bs.name) for bs in settings.context_brushstrokes]
bs_objects = [bs for bs in bs_objects if bs] bs_objects = [bs for bs in bs_objects if bs]
else: else:
bs_objects = [utils.get_active_context_brushstrokes_object(context)] bs_objects = [utils.get_active_context_brushstrokes_object(context.scene)]
if not bs_objects: if not bs_objects:
return {"CANCELLED"} return {"CANCELLED"}
@@ -450,6 +446,17 @@ class BSBST_OT_copy_brushstrokes(bpy.types.Operator):
# remap surface pointers and context linked data TODO: refactor to deduplicate # remap surface pointers and context linked data TODO: refactor to deduplicate
for ob in new_bs: for ob in new_bs:
utils.link_to_collections_by_ref(ob, surface_object)
# if it's still using the default name initialize names again
if utils.split_id_name(ob.name)[0] == utils.bs_name(active_surface_object.name):
if utils.is_flow_object(ob):
ob.name = utils.flow_name(utils.bs_name(surface_object.name))
else:
ob.name = utils.bs_name(surface_object.name)
elif ob.name.startswith(active_surface_object.name):
ob.name = f"{surface_object.name}{ob.name[len(active_surface_object.name):]}"
ob.parent = surface_object ob.parent = surface_object
utils.set_surface_object(ob, surface_object) utils.set_surface_object(ob, surface_object)
@@ -504,16 +511,16 @@ class BSBST_OT_select_surface(bpy.types.Operator):
return bool(settings.context_brushstrokes) return bool(settings.context_brushstrokes)
def execute(self, context): def execute(self, context):
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
if not bs_ob: if not bs_ob:
return {"CANCELLED"} return {"CANCELLED"}
surface_object = getattr(bs_ob, '["BSBST_surface_object"]', None) surface_object = getattr(bs_ob, '["BSBST_surface_object"]', None)
if not surface_object: if not surface_object:
return {"CANCELLED"} return {"CANCELLED"}
bpy.context.view_layer.objects.active = surface_object
if not context.mode == 'OBJECT': if not context.mode == 'OBJECT':
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
bpy.context.view_layer.objects.active = surface_object
for ob in bpy.data.objects: for ob in bpy.data.objects:
ob.select_set(False) ob.select_set(False)
surface_object.select_set(True) surface_object.select_set(True)
@@ -537,7 +544,7 @@ class BSBST_OT_assign_surface(bpy.types.Operator):
return bool(settings.context_brushstrokes) return bool(settings.context_brushstrokes)
def execute(self, context): def execute(self, context):
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
if not bs_ob: if not bs_ob:
return {"CANCELLED"} return {"CANCELLED"}
@@ -555,7 +562,7 @@ class BSBST_OT_assign_surface(bpy.types.Operator):
layout.prop_search(self, 'surface_object', bpy.data, 'objects') layout.prop_search(self, 'surface_object', bpy.data, 'objects')
def invoke(self, context, event): def invoke(self, context, event):
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
surf_ob = utils.get_surface_object(bs_ob) surf_ob = utils.get_surface_object(bs_ob)
if surf_ob: if surf_ob:
self.surface_object = surf_ob.name self.surface_object = surf_ob.name
@@ -632,7 +639,7 @@ class BSBST_OT_copy_flow(bpy.types.Operator):
return bool(settings.context_brushstrokes) return bool(settings.context_brushstrokes)
def execute(self, context): def execute(self, context):
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
flow_ob_old = utils.get_flow_object(bs_ob) flow_ob_old = utils.get_flow_object(bs_ob)
if not bs_ob: if not bs_ob:
return {"CANCELLED"} return {"CANCELLED"}
@@ -670,7 +677,7 @@ class BSBST_OT_copy_flow(bpy.types.Operator):
def invoke(self, context, event): def invoke(self, context, event):
settings = context.scene.BSBST_settings settings = context.scene.BSBST_settings
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
for i in range(len(self.bs_list)): for i in range(len(self.bs_list)):
self.bs_list.remove(0) self.bs_list.remove(0)
@@ -721,7 +728,7 @@ class BSBST_OT_switch_deformable(bpy.types.Operator):
bs_objects = [bpy.data.objects.get(bs.name) for bs in settings.context_brushstrokes] bs_objects = [bpy.data.objects.get(bs.name) for bs in settings.context_brushstrokes]
bs_objects = [bs for bs in bs_objects if bs] bs_objects = [bs for bs in bs_objects if bs]
else: else:
bs_objects = [utils.get_active_context_brushstrokes_object(context)] bs_objects = [utils.get_active_context_brushstrokes_object(context.scene)]
if not bs_objects: if not bs_objects:
return {"CANCELLED"} return {"CANCELLED"}
@@ -762,7 +769,7 @@ class BSBST_OT_switch_animated(bpy.types.Operator):
bs_objects = [bpy.data.objects.get(bs.name) for bs in settings.context_brushstrokes] bs_objects = [bpy.data.objects.get(bs.name) for bs in settings.context_brushstrokes]
bs_objects = [bs for bs in bs_objects if bs] bs_objects = [bs for bs in bs_objects if bs]
else: else:
bs_objects = [utils.get_active_context_brushstrokes_object(context)] bs_objects = [utils.get_active_context_brushstrokes_object(context.scene)]
if not bs_objects: if not bs_objects:
return {"CANCELLED"} return {"CANCELLED"}
@@ -963,7 +970,7 @@ class BSBST_OT_make_preset(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return bool(utils.get_active_context_brushstrokes_object(context)) return bool(utils.get_active_context_brushstrokes_object(context.scene))
def execute(self, context): def execute(self, context):
@@ -977,7 +984,7 @@ class BSBST_OT_make_preset(bpy.types.Operator):
settings.preset_object.modifiers.remove(mod) settings.preset_object.modifiers.remove(mod)
# transfer brushstrokes modifiers to preset # transfer brushstrokes modifiers to preset
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
if not bs_ob: if not bs_ob:
return {"CANCELLED"} return {"CANCELLED"}
for mod in bs_ob.modifiers: for mod in bs_ob.modifiers:
@@ -1098,7 +1105,7 @@ class BSBST_OT_brushstrokes_toggle_attribute(bpy.types.Operator):
edit_toggle = settings.edit_toggle edit_toggle = settings.edit_toggle
settings.edit_toggle = False settings.edit_toggle = False
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
if not bs_ob: if not bs_ob:
settings.edit_toggle = edit_toggle settings.edit_toggle = edit_toggle
return {"CANCELLED"} return {"CANCELLED"}
@@ -25,7 +25,7 @@ def update_brushstroke_method(self, context):
preset_object = bpy.data.objects.get(preset_name) preset_object = bpy.data.objects.get(preset_name)
settings.preset_object = preset_object settings.preset_object = preset_object
style_object = utils.get_active_context_brushstrokes_object(context) style_object = utils.get_active_context_brushstrokes_object(context.scene)
if not style_object: if not style_object:
style_object = preset_object style_object = preset_object
@@ -45,7 +45,7 @@ def update_context_material(self, context):
if settings.silent_switch: if settings.silent_switch:
return return
style_object = utils.get_active_context_brushstrokes_object(context) style_object = utils.get_active_context_brushstrokes_object(context.scene)
if not style_object: if not style_object:
style_object = settings.preset_object style_object = settings.preset_object
if not style_object: if not style_object:
@@ -119,7 +119,14 @@ def get_active_context_brushstrokes_index(self):
return self["active_context_brushstrokes_index"] return self["active_context_brushstrokes_index"]
def set_active_context_brushstrokes_index(self, value): def set_active_context_brushstrokes_index(self, value):
settings = bpy.context.scene.BSBST_settings scene = self.id_data
settings = scene.BSBST_settings
for window in bpy.context.window_manager.windows:
if window.scene == scene:
view_layer = window.view_layer
active_object = view_layer.objects.active
if not settings.context_brushstrokes: if not settings.context_brushstrokes:
if not settings.preset_object: if not settings.preset_object:
return return
@@ -136,12 +143,11 @@ def set_active_context_brushstrokes_index(self, value):
return return
if not bs_ob: if not bs_ob:
return return
if not bpy.context.object: if not active_object:
return return
view_layer = bpy.context.view_layer if active_object.visible_get(view_layer = view_layer):
if bpy.context.object.visible_get(view_layer = view_layer):
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
bpy.context.view_layer.objects.active = bs_ob view_layer.objects.active = bs_ob
if bs_ob.visible_get(view_layer = view_layer): if bs_ob.visible_get(view_layer = view_layer):
bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='OBJECT')
for ob in bpy.data.objects: for ob in bpy.data.objects:
@@ -257,7 +263,7 @@ class BSBST_Settings(bpy.types.PropertyGroup):
gpv3 = bpy.context.preferences.experimental.use_grease_pencil_version3 gpv3 = bpy.context.preferences.experimental.use_grease_pencil_version3
except: except:
v0, v1, v3 = bpy.app.version v0, v1, v3 = bpy.app.version
gpv3 = v0 >= 4 and v1 >= 3 gpv3 = v0 >= 4 or (v0 == 4 and v1 >= 3)
curve_mode: bpy.props.EnumProperty(default='CURVES', curve_mode: bpy.props.EnumProperty(default='CURVES',
items= [('CURVE', 'Legacy', 'Use legacy curve type (Limited Support)', 'CURVE_DATA', 0),\ items= [('CURVE', 'Legacy', 'Use legacy curve type (Limited Support)', 'CURVE_DATA', 0),\
('CURVES', 'Curves', 'Use hair curves (Fully supported)', 'CURVES_DATA', 1), ('CURVES', 'Curves', 'Use hair curves (Fully supported)', 'CURVES_DATA', 1),
+12 -9
View File
@@ -536,7 +536,7 @@ class BSBST_PT_brushstroke_tools_panel(bpy.types.Panel):
row = style_panel.row() row = style_panel.row()
row_edit = row.row(align=True) row_edit = row.row(align=True)
row_edit.operator('brushstroke_tools.select_surface', icon='OUTLINER_OB_SURFACE', text='') row_edit.operator('brushstroke_tools.select_surface', icon='OUTLINER_OB_SURFACE', text='')
bs_ob = utils.get_active_context_brushstrokes_object(context) bs_ob = utils.get_active_context_brushstrokes_object(context.scene)
text = 'Edit Flow' if getattr(bs_ob, '["BSBST_method"]', None)=='SURFACE_FILL' else 'Edit Brushstrokes' text = 'Edit Flow' if getattr(bs_ob, '["BSBST_method"]', None)=='SURFACE_FILL' else 'Edit Brushstrokes'
row_edit.operator('brushstroke_tools.edit_brushstrokes', icon='GREASEPENCIL', text = text) row_edit.operator('brushstroke_tools.edit_brushstrokes', icon='GREASEPENCIL', text = text)
row_edit.prop(settings, 'edit_toggle', icon='RESTRICT_SELECT_OFF' if settings.edit_toggle else 'RESTRICT_SELECT_ON', icon_only=True) row_edit.prop(settings, 'edit_toggle', icon='RESTRICT_SELECT_OFF' if settings.edit_toggle else 'RESTRICT_SELECT_ON', icon_only=True)
@@ -549,15 +549,15 @@ class BSBST_PT_brushstroke_tools_panel(bpy.types.Panel):
class BSBST_MT_PIE_brushstroke_data_marking(bpy.types.Menu): class BSBST_MT_PIE_brushstroke_data_marking(bpy.types.Menu):
bl_idname= "BSBST_MT_PIE_brushstroke_data_marking" bl_idname= "BSBST_MT_PIE_brushstroke_data_marking"
bl_label = "Mark Brushstroke Flow" bl_label = "Mark Brushstroke Data"
items = { items = {
"Brush Flow - Mark": ['FORCE_WIND'], "Brush Flow - Mark": ['geometry.brush_flow_mark','FORCE_WIND'],
"Brush Flow - Clear": ['NONE'], "Brush Flow - Clear": ['geometry.brush_flow_clear','NONE'],
"Brush Break - Mark": ['MOD_PHYSICS'], "Brush Break - Mark": ['geometry.brush_break_mark','MOD_PHYSICS'],
"Brush Break - Clear": ['NONE'], "Brush Break - Clear": ['geometry.brush_break_clear','NONE'],
"Brush Ignore - Mark": ['X'], "Brush Ignore - Mark": ['geometry.brush_ignore_mark','X'],
"Brush Ignore - Clear": ['NONE'], "Brush Ignore - Clear": ['geometry.brush_ignore_clear','NONE'],
} }
def draw(self, context): def draw(self, context):
@@ -567,10 +567,13 @@ class BSBST_MT_PIE_brushstroke_data_marking(bpy.types.Menu):
for name, info in self.items.items(): for name, info in self.items.items():
pie.alert=True pie.alert=True
op = pie.operator("geometry.execute_node_group", text=name, icon=info[0]) if bpy.app.version < (5,1):
op = pie.operator("geometry.execute_node_group", text=name, icon=info[1])
op.asset_library_type='CUSTOM' op.asset_library_type='CUSTOM'
op.asset_library_identifier=utils.asset_lib_name op.asset_library_identifier=utils.asset_lib_name
op.relative_asset_identifier=f"core/brushstroke_tools-resources.blend/NodeTree/{name}" op.relative_asset_identifier=f"core/brushstroke_tools-resources.blend/NodeTree/{name}"
else:
op = pie.operator(info[0], text=name, icon=info[1])
class BSBST_OT_brushstroke_data_marking(bpy.types.Operator): class BSBST_OT_brushstroke_data_marking(bpy.types.Operator):
""" """
@@ -37,9 +37,8 @@ linkable_sockets = [
asset_lib_name = 'Brushstroke Tools Library' asset_lib_name = 'Brushstroke Tools Library'
@persistent @persistent
def find_context_brushstrokes(dummy): def find_context_brushstrokes(scene, depsgraph):
context = bpy.context settings = scene.BSBST_settings
settings = context.scene.BSBST_settings
edit_toggle = settings.edit_toggle edit_toggle = settings.edit_toggle
settings.edit_toggle = False settings.edit_toggle = False
@@ -49,7 +48,7 @@ def find_context_brushstrokes(dummy):
# identify context brushstrokes # identify context brushstrokes
for el in range(len(settings.context_brushstrokes)): for el in range(len(settings.context_brushstrokes)):
settings.context_brushstrokes.remove(0) settings.context_brushstrokes.remove(0)
context_object = context.object context_object = depsgraph.view_layer.objects.active
if not is_brushstrokes_object(context_object): if not is_brushstrokes_object(context_object):
bs_ob = is_flow_object(context_object) bs_ob = is_flow_object(context_object)
if bs_ob: if bs_ob:
@@ -97,12 +96,11 @@ def find_context_brushstrokes(dummy):
settings.edit_toggle = edit_toggle settings.edit_toggle = edit_toggle
@persistent @persistent
def refresh_preset(dummy): def refresh_preset(scene, depsgraph):
context = bpy.context settings = scene.BSBST_settings
settings = context.scene.BSBST_settings
if not settings: if not settings:
return return
for ob in [settings.preset_object, get_active_context_brushstrokes_object(context)]: for ob in [settings.preset_object, get_active_context_brushstrokes_object(scene)]:
if not ob: if not ob:
continue continue
for mod in ob.modifiers: for mod in ob.modifiers:
@@ -313,6 +311,25 @@ def write_lib_version(dir: Path = None):
with open(dir.joinpath(".version"), "w") as file: with open(dir.joinpath(".version"), "w") as file:
file.write(str(addon_version)) file.write(str(addon_version))
def get_file_blend_version(path: Path):
with open(path, "rb") as f:
prefix = f.read(4)
f.seek(0)
if prefix.startswith(b"BLENDER"):
header = f.read(12)
elif prefix == b"\x28\xb5\x2f\xfd": # zstd
dctx = zstd.ZstdDecompressor()
with dctx.stream_reader(f) as reader:
header = reader.read(12)
else:
raise ValueError("Unknown blend format")
version = header[9:12].decode()
return int(version[0]), int(version[1:])
def copy_resources_to_dir(tgt_dir = ''): def copy_resources_to_dir(tgt_dir = ''):
source_dir = get_addon_directory().joinpath('assets') source_dir = get_addon_directory().joinpath('assets')
if not tgt_dir: if not tgt_dir:
@@ -545,8 +562,21 @@ def find_brush_style_by_name(name: str):
return brush_style return brush_style
return None return None
def link_to_collections_by_ref(obj, ref_obj): def link_to_collections_by_ref(obj, ref_obj, unlink=True):
col_list = [] col_list = []
if unlink:
for col in obj.users_collection:
if col.library:
continue
col_list += [col]
if col_list:
for col in col_list:
col.objects.unlink(obj)
col_list = []
for col in ref_obj.users_collection: for col in ref_obj.users_collection:
if col.library: if col.library:
continue continue
@@ -754,8 +784,8 @@ def context_brushstrokes(context):
settings = context.scene.BSBST_settings settings = context.scene.BSBST_settings
return settings.context_brushstrokes return settings.context_brushstrokes
def get_active_context_brushstrokes_object(context): def get_active_context_brushstrokes_object(scene):
settings = context.scene.BSBST_settings settings = scene.BSBST_settings
if not settings.context_brushstrokes: if not settings.context_brushstrokes:
return None return None
bs = settings.context_brushstrokes[settings.active_context_brushstrokes_index] bs = settings.context_brushstrokes[settings.active_context_brushstrokes_index]
@@ -765,19 +795,22 @@ def get_active_context_brushstrokes_object(context):
def get_active_context_surface_object(context): def get_active_context_surface_object(context):
if not context.object: if not context.object:
return None return None
bs_ob = get_active_context_brushstrokes_object(context) bs_ob = get_active_context_brushstrokes_object(context.scene)
if bs_ob: if bs_ob:
return get_surface_object(bs_ob) return get_surface_object(bs_ob)
if context.object.type == 'MESH': if context.object.type == 'MESH':
return context.object return context.object
def flow_name(name): def bs_name(surf_name: str) -> str:
return f'{name}-FLOW' return f'{surf_name} - Brushstrokes'
def flow_name(bs_name: str) -> str:
return f'{bs_name}-FLOW'
def edit_active_brushstrokes(context): def edit_active_brushstrokes(context):
context.view_layer.depsgraph.update() context.view_layer.depsgraph.update()
bs_ob = get_active_context_brushstrokes_object(context) bs_ob = get_active_context_brushstrokes_object(context.scene)
if not bs_ob: if not bs_ob:
return {"CANCELLED"} return {"CANCELLED"}
@@ -33,7 +33,7 @@
"id": "rainclouds_bulk_scene_tools", "id": "rainclouds_bulk_scene_tools",
"name": "Raincloud's Bulk Scene Tools", "name": "Raincloud's Bulk Scene Tools",
"tagline": "Bulk utilities for optimizing scene data", "tagline": "Bulk utilities for optimizing scene data",
"version": "0.16.0", "version": "0.17.0",
"type": "add-on", "type": "add-on",
"maintainer": "RaincloudTheDragon <raincloudthedragon@gmail.com>", "maintainer": "RaincloudTheDragon <raincloudthedragon@gmail.com>",
"license": [ "license": [
@@ -49,9 +49,9 @@
"Workflow", "Workflow",
"Materials" "Materials"
], ],
"archive_url": "https://github.com/RaincloudTheDragon/Rainys-Bulk-Scene-Tools/releases/download/v0.16.0/Rainys_Bulk_Scene_Tools.v0.16.0.zip", "archive_url": "https://github.com/RaincloudTheDragon/Rainys-Bulk-Scene-Tools/releases/download/v0.17.0/Rainys_Bulk_Scene_Tools.v0.17.0.zip",
"archive_size": 80251, "archive_size": 80981,
"archive_hash": "sha256:3e6fafe11caa39e48b94288c12b2a88e521c928955a854ffdd1bd0936e6bc70a" "archive_hash": "sha256:419433069465b45ea903bd7bb46d89aa28b9c96c541d587d5f3be651a762811f"
}, },
{ {
"schema_version": "1.0.0", "schema_version": "1.0.0",
@@ -0,0 +1,100 @@
{
"version": "v1",
"blocklist": [],
"data": [
{
"schema_version": "1.0.0",
"id": "basedplayblast",
"name": "BasedPlayblast",
"tagline": "Easily create playblasts from Blender and Flamenco",
"version": "2.6.3",
"type": "add-on",
"maintainer": "RaincloudTheDragon <raincloudthedragon@gmail.com>",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"website": "https://github.com/RaincloudTheDragon/BasedPlayblast",
"permissions": {
"files": "Import/export files and data"
},
"tags": [
"Animation",
"Render",
"Workflow",
"Video"
],
"archive_url": "https://github.com/RaincloudTheDragon/BasedPlayblast/releases/download/v2.6.3/BasedPlayblast.v2.6.3.zip",
"archive_size": 49732,
"archive_hash": "sha256:078b406105ce6f4802e75233569841e2f73d082e09cd1d954696681ebf72b627"
},
{
"schema_version": "1.0.0",
"id": "rainclouds_bulk_scene_tools",
"name": "Raincloud's Bulk Scene Tools",
"tagline": "Bulk utilities for optimizing scene data",
"version": "0.17.0",
"type": "add-on",
"maintainer": "RaincloudTheDragon <raincloudthedragon@gmail.com>",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"website": "https://github.com/RaincloudTheDragon/Rainys-Bulk-Scene-Tools",
"permissions": {
"files": "Read and write external resources referenced by scenes"
},
"tags": [
"Scene",
"Workflow",
"Materials"
],
"archive_url": "https://github.com/RaincloudTheDragon/Rainys-Bulk-Scene-Tools/releases/download/v0.17.0/Rainys_Bulk_Scene_Tools.v0.17.0.zip",
"archive_size": 80981,
"archive_hash": "sha256:419433069465b45ea903bd7bb46d89aa28b9c96c541d587d5f3be651a762811f"
},
{
"schema_version": "1.0.0",
"id": "atomic_data_manager",
"name": "Atomic Data Manager",
"tagline": "Smart cleanup and inspection of Blender data-blocks",
"version": "2.5.0",
"type": "add-on",
"maintainer": "RaincloudTheDragon",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "4.2.0",
"tags": [
"utility",
"management",
"cleanup"
],
"archive_url": "https://github.com/RaincloudTheDragon/atomic-data-manager/releases/download/v2.5.0/Atomic_Data_Manager.v2.5.0.zip",
"archive_size": 114674,
"archive_hash": "sha256:4b4834ed3910a428d4cb01f1891247ad80089b6c5324fc27c6862b09e81ff1c1"
},
{
"schema_version": "1.0.0",
"id": "sheepit_project_submitter",
"name": "SheepIt Project Submitter",
"tagline": "Submit projects to SheepIt render farm",
"version": "0.0.7",
"type": "add-on",
"maintainer": "RaincloudTheDragon",
"license": [
"GPL-3.0-or-later"
],
"blender_version_min": "3.0.0",
"tags": [
"render",
"farm",
"submission",
"utility"
],
"archive_url": "https://github.com/RaincloudTheDragon/sheepit_project_submitter/releases/download/v0.0.7/SheepIt_Project_Submitter.v0.0.7.zip",
"archive_size": 47250,
"archive_hash": "sha256:cb8dee48c45cc51dd8237981f4ab96d97d476b547c8c640606e9bbfd0390a055"
}
]
}
@@ -3,7 +3,7 @@ schema_version = "1.0.0"
id = "rainclouds_bulk_scene_tools" id = "rainclouds_bulk_scene_tools"
name = "Raincloud's Bulk Scene Tools" name = "Raincloud's Bulk Scene Tools"
tagline = "Bulk utilities for optimizing scene data" tagline = "Bulk utilities for optimizing scene data"
version = "0.16.0" version = "0.17.0"
type = "add-on" type = "add-on"
maintainer = "RaincloudTheDragon <raincloudthedragon@gmail.com>" maintainer = "RaincloudTheDragon <raincloudthedragon@gmail.com>"
@@ -1,3 +1,6 @@
# v0.17.0 2026-03-13
- Remove Action FU: new operator in Animation Data section—clears fake users from all actions so only used animations are kept
# v0.16.0 2026-01-15 # v0.16.0 2026-01-15
- Delete Single Keyframe Actions: Blender 5.0 compatible (action layers/strips/channelbags); skip actions from linked libraries - Delete Single Keyframe Actions: Blender 5.0 compatible (action layers/strips/channelbags); skip actions from linked libraries
- Find Material Users: removed (issue #14; use Atomic or DBU) - Find Material Users: removed (issue #14; use Atomic or DBU)
@@ -0,0 +1,24 @@
import bpy
class RemoveActionFakeUsers(bpy.types.Operator):
"""Remove fake users from all actions so only used animations are kept on save"""
bl_idname = "bst.remove_action_fake_users"
bl_label = "Remove Action FU"
bl_description = "Remove fake users from all actions so only used animations are kept"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
actions = bpy.data.actions
cleared_count = 0
for action in actions:
if getattr(action, "library", None) is not None:
continue
if action.use_fake_user:
action.use_fake_user = False
cleared_count += 1
print(f"Removed fake user from action '{action.name}'")
self.report({'INFO'}, f"Removed fake users from {cleared_count} actions")
return {'FINISHED'}
@@ -6,6 +6,7 @@ from ..ops.spawn_scene_structure import SpawnSceneStructure
from ..ops.delete_single_keyframe_actions import DeleteSingleKeyframeActions from ..ops.delete_single_keyframe_actions import DeleteSingleKeyframeActions
from ..ops.remove_unused_material_slots import RemoveUnusedMaterialSlots from ..ops.remove_unused_material_slots import RemoveUnusedMaterialSlots
from ..ops.convert_relations_to_constraint import ConvertRelationsToConstraint from ..ops.convert_relations_to_constraint import ConvertRelationsToConstraint
from ..ops.remove_action_fake_users import RemoveActionFakeUsers
from ..ops.white_world import WhiteWorld from ..ops.white_world import WhiteWorld
from ..utils import compat from ..utils import compat
@@ -56,6 +57,8 @@ class RBST_SceneGen_PT_BulkSceneGeneral(bpy.types.Panel):
box = layout.box() box = layout.box()
box.label(text="Animation Data") box.label(text="Animation Data")
row = box.row(align=True) row = box.row(align=True)
row.operator("bst.remove_action_fake_users", text="Remove Action FU", icon='FAKE_USER_OFF')
row = box.row(align=True)
row.operator("bst.delete_single_keyframe_actions", text="Delete Single Keyframe Actions", icon='ANIM_DATA') row.operator("bst.delete_single_keyframe_actions", text="Delete Single Keyframe Actions", icon='ANIM_DATA')
row = box.row(align=True) row = box.row(align=True)
row.operator("bst.convert_relations_to_constraint", text="Convert Relations to Constraint", icon_value=405) row.operator("bst.convert_relations_to_constraint", text="Convert Relations to Constraint", icon_value=405)
@@ -71,6 +74,7 @@ classes = (
DeleteSingleKeyframeActions, DeleteSingleKeyframeActions,
RemoveUnusedMaterialSlots, RemoveUnusedMaterialSlots,
ConvertRelationsToConstraint, ConvertRelationsToConstraint,
RemoveActionFakeUsers,
) )
# Registration # Registration
@@ -1,5 +1,5 @@
{ {
"last_check": "2026-03-06 10:02:06.473451", "last_check": "2026-03-16 11:19:55.639825",
"backup_date": "January-12-2026", "backup_date": "January-12-2026",
"update_ready": false, "update_ready": false,
"ignore": false, "ignore": false,
@@ -3,7 +3,7 @@ schema_version = "1.0.0"
id = "dynamiclinkmanager" id = "dynamiclinkmanager"
name = "Dynamic Link Manager" name = "Dynamic Link Manager"
tagline = "Character migrator and linked library tools" tagline = "Character migrator and linked library tools"
version = "0.2.0" version = "0.3.0"
type = "add-on" type = "add-on"
# Optional: Semantic Versioning # Optional: Semantic Versioning
@@ -482,14 +482,36 @@ def run_retarg_relatives(orig, rep, rep_descendants, orig_to_rep):
for ob in candidates: for ob in candidates:
if ob.parent == orig: if ob.parent == orig:
ob.parent = rep ob.parent = rep
for c in getattr(ob, "constraints", []):
if getattr(c, "target", None) == orig:
c.target = rep
if ob.type == "MESH" and ob.modifiers: if ob.type == "MESH" and ob.modifiers:
for m in ob.modifiers: for m in ob.modifiers:
if getattr(m, "object", None) == orig: if getattr(m, "object", None) == orig:
m.object = rep m.object = rep
# Retarget constraints on ALL objects (including orig hierarchy like eyes)
for ob in bpy.data.objects:
for c in getattr(ob, "constraints", []):
if getattr(c, "target", None) == orig:
c.target = rep
# Retarget bone constraints on ALL armatures (other characters, etc.)
for ob in bpy.data.objects:
if ob.type != "ARMATURE" or not ob.pose:
continue
for pbone in ob.pose.bones:
for c in pbone.constraints:
if getattr(c, "target", None) == orig:
c.target = rep
# Camera DOF: retarget focus_object from orig to rep
for ob in bpy.data.objects:
if ob.type != 'CAMERA':
continue
camera = ob.data
if not camera.dof:
continue
if camera.dof.focus_object == orig:
camera.dof.focus_object = rep
def _base_body_name_match(ob): def _base_body_name_match(ob):
"""True if object looks like the base body mesh (MESH, name has body+base).""" """True if object looks like the base body mesh (MESH, name has body+base)."""
@@ -3,7 +3,7 @@
# the Free Software Foundation; either version 3 of the License, or # the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version. # (at your option) any later version.
"""Tweak tools: add/remove/bake COPY_TRANSFORMS on Rigify arm/leg tweak bones.""" """Tweak tools: add/remove/bake COPY_TRANSFORMS on Rigify tweak bones (arm, leg, or all)."""
import bpy import bpy
@@ -22,7 +22,7 @@ TWEAK_CONSTRAINT_NAME = "Copy from Original"
def get_tweak_bones(armature, limb): def get_tweak_bones(armature, limb):
"""Return list of tweak bone names that exist on armature. limb in 'arm', 'leg', 'both'.""" """Return list of tweak bone names that exist on armature. limb in 'arm', 'leg', 'body', 'both'."""
if not armature or armature.type != "ARMATURE" or not armature.pose: if not armature or armature.type != "ARMATURE" or not armature.pose:
return [] return []
bones = armature.pose.bones bones = armature.pose.bones
@@ -30,8 +30,20 @@ def get_tweak_bones(armature, limb):
names = ARM_TWEAK_BONES names = ARM_TWEAK_BONES
elif limb == "leg": elif limb == "leg":
names = LEG_TWEAK_BONES names = LEG_TWEAK_BONES
elif limb == "body":
# Body/torso tweaks: tweak_spine, spine_fk, etc. (no arm/leg)
arm_leg_names = set(ARM_TWEAK_BONES + LEG_TWEAK_BONES)
names = [
b.name for b in bones
if ("tweak" in b.name.lower() or "spine_fk" in b.name.lower())
and b.name not in arm_leg_names
]
elif limb == "both": elif limb == "both":
names = ARM_TWEAK_BONES + LEG_TWEAK_BONES # ALL tweak bones: any bone with "tweak" or "spine_fk" in the name
names = [
b.name for b in bones
if "tweak" in b.name.lower() or "spine_fk" in b.name.lower()
]
else: else:
return [] return []
return [n for n in names if n in bones] return [n for n in names if n in bones]
@@ -101,7 +113,7 @@ def bake_tweak_constraints(context, orig, rep, limb, track_name, post_clean):
# Select only tweak bones on rep # Select only tweak bones on rep
bpy.ops.pose.select_all(action="DESELECT") bpy.ops.pose.select_all(action="DESELECT")
for name in names: for name in names:
rep.pose.bones[name].bone.select = True rep.pose.bones[name].select = True
# Bake # Bake
try: try:
@@ -735,10 +735,72 @@ class DLM_OT_tweak_bake_leg(Operator):
return {"CANCELLED"} return {"CANCELLED"}
class DLM_OT_tweak_add_body(Operator):
bl_idname = "dlm.tweak_add_body"
bl_label = "Add Body Tweaks"
bl_description = "Add tweak bone constraints to body/torso bones (spine, no arm/leg)"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return _tweak_poll(context)
def execute(self, context):
orig, rep = _get_migrator_pair(context)
from ..ops import tweak_tools
tweak_tools.add_tweak_constraints(orig, rep, "body")
self.report({"INFO"}, "Body tweak constraints added.")
return {"FINISHED"}
class DLM_OT_tweak_remove_body(Operator):
bl_idname = "dlm.tweak_remove_body"
bl_label = "Remove Body Tweaks"
bl_description = "Remove body/torso tweak constraints from the replacement character"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return _tweak_poll(context)
def execute(self, context):
orig, rep = _get_migrator_pair(context)
from ..ops import tweak_tools
n = tweak_tools.remove_tweak_constraints(orig, rep, "body")
self.report({"INFO"}, f"Removed {n} body tweak constraints.")
return {"FINISHED"}
class DLM_OT_tweak_bake_body(Operator):
bl_idname = "dlm.tweak_bake_body"
bl_label = "Bake Body Tweaks"
bl_description = "Bake body/torso tweak constraints to keyframes and optionally remove constraints"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return _tweak_poll(context)
def execute(self, context):
orig, rep = _get_migrator_pair(context)
props = context.scene.dynamic_link_manager
from ..ops import tweak_tools
ok, msg = tweak_tools.bake_tweak_constraints(
context, orig, rep, "body",
getattr(props, "tweak_nla_track_name", "") or "",
getattr(props, "tweak_bake_post_clean", False),
)
if ok:
self.report({"INFO"}, msg)
return {"FINISHED"}
self.report({"ERROR"}, msg)
return {"CANCELLED"}
class DLM_OT_tweak_add_both(Operator): class DLM_OT_tweak_add_both(Operator):
bl_idname = "dlm.tweak_add_both" bl_idname = "dlm.tweak_add_both"
bl_label = "Add Arm & Leg Tweaks" bl_label = "Add All Tweaks"
bl_description = "Add tweak bone constraints to both arm and leg bones" bl_description = "Add tweak bone constraints to all tweak bones (arm, leg, body)"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod @classmethod
@@ -749,14 +811,14 @@ class DLM_OT_tweak_add_both(Operator):
orig, rep = _get_migrator_pair(context) orig, rep = _get_migrator_pair(context)
from ..ops import tweak_tools from ..ops import tweak_tools
tweak_tools.add_tweak_constraints(orig, rep, "both") tweak_tools.add_tweak_constraints(orig, rep, "both")
self.report({"INFO"}, "Arm & leg tweak constraints added.") self.report({"INFO"}, "All tweak constraints added.")
return {"FINISHED"} return {"FINISHED"}
class DLM_OT_tweak_remove_both(Operator): class DLM_OT_tweak_remove_both(Operator):
bl_idname = "dlm.tweak_remove_both" bl_idname = "dlm.tweak_remove_both"
bl_label = "Remove Arm & Leg Tweaks" bl_label = "Remove All Tweaks"
bl_description = "Remove all arm and leg tweak constraints from the replacement character" bl_description = "Remove all tweak constraints from the replacement character"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod @classmethod
@@ -773,8 +835,8 @@ class DLM_OT_tweak_remove_both(Operator):
class DLM_OT_tweak_bake_both(Operator): class DLM_OT_tweak_bake_both(Operator):
bl_idname = "dlm.tweak_bake_both" bl_idname = "dlm.tweak_bake_both"
bl_label = "Bake Arm & Leg Tweaks" bl_label = "Bake All Tweaks"
bl_description = "Bake all arm and leg tweak constraints to keyframes and optionally remove constraints" bl_description = "Bake all tweak constraints (arm, leg, body) to keyframes and optionally remove constraints"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod @classmethod
@@ -825,6 +887,9 @@ OPERATOR_CLASSES = [
DLM_OT_tweak_add_leg, DLM_OT_tweak_add_leg,
DLM_OT_tweak_remove_leg, DLM_OT_tweak_remove_leg,
DLM_OT_tweak_bake_leg, DLM_OT_tweak_bake_leg,
DLM_OT_tweak_add_body,
DLM_OT_tweak_remove_body,
DLM_OT_tweak_bake_body,
DLM_OT_tweak_add_both, DLM_OT_tweak_add_both,
DLM_OT_tweak_remove_both, DLM_OT_tweak_remove_both,
DLM_OT_tweak_bake_both, DLM_OT_tweak_bake_both,
@@ -119,9 +119,13 @@ class DLM_PT_main_panel(Panel):
row.operator("dlm.tweak_remove_leg", text="Remove Leg", icon="X") row.operator("dlm.tweak_remove_leg", text="Remove Leg", icon="X")
row.operator("dlm.tweak_bake_leg", text="Bake Leg", icon="KEYFRAME") row.operator("dlm.tweak_bake_leg", text="Bake Leg", icon="KEYFRAME")
row = tweak_box.row(align=True) row = tweak_box.row(align=True)
row.operator("dlm.tweak_add_both", text="Add Both", icon="CONSTRAINT_BONE") row.operator("dlm.tweak_add_body", text="Add Body", icon="CONSTRAINT_BONE")
row.operator("dlm.tweak_remove_both", text="Remove Both", icon="X") row.operator("dlm.tweak_remove_body", text="Remove Body", icon="X")
row.operator("dlm.tweak_bake_both", text="Bake Both", icon="KEYFRAME") row.operator("dlm.tweak_bake_body", text="Bake Body", icon="KEYFRAME")
row = tweak_box.row(align=True)
row.operator("dlm.tweak_add_both", text="Add All", icon="CONSTRAINT_BONE")
row.operator("dlm.tweak_remove_both", text="Remove All", icon="X")
row.operator("dlm.tweak_bake_both", text="Bake All", icon="KEYFRAME")
row = tweak_box.row() row = tweak_box.row()
row.prop(props, "tweak_nla_track_name", text="NLA track") row.prop(props, "tweak_nla_track_name", text="NLA track")
row = tweak_box.row() row = tweak_box.row()
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+64 -74
View File
@@ -1,4 +1,4 @@
keyconfig_version = (4, 3, 29) keyconfig_version = (5, 0, 118)
keyconfig_data = \ keyconfig_data = \
[("3D View", [("3D View",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
@@ -1519,11 +1519,6 @@ keyconfig_data = \
), ),
("anim.change_frame", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None), ("anim.change_frame", {"type": 'RIGHTMOUSE', "value": 'PRESS'}, None),
("action.duplicate_move", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None), ("action.duplicate_move", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, None),
("action.keymove",
{"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True},
{ "active":False,
},
),
("action.keymove", ("action.keymove",
{"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True}, {"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True},
{ "active":False, { "active":False,
@@ -1591,11 +1586,7 @@ keyconfig_data = \
], ],
}, },
), ),
("screen.animation_play", ("screen.animation_play", {"type": 'V', "value": 'PRESS', "alt": True, "repeat": True}, None),
{"type": 'SPACE', "value": 'PRESS', "repeat": True},
{ "active":False,
},
),
("screen.animation_play", ("screen.animation_play",
{"type": 'SPACE', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, {"type": 'SPACE', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True},
{"properties": {"properties":
@@ -1956,38 +1947,10 @@ keyconfig_data = \
("Grease Pencil Stroke Paint (Draw brush)", ("Grease Pencil Stroke Paint (Draw brush)",
{"space_type": 'EMPTY', "region_type": 'WINDOW'}, {"space_type": 'EMPTY', "region_type": 'WINDOW'},
{"items": {"items":
[("gpencil.draw", [("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
{"type": 'LEFTMOUSE', "value": 'PRESS'}, ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, None),
{"properties": ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True}, None),
[("mode", 'DRAW'), ("gpencil.draw", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True}, None),
("wait_for_input", False),
],
},
),
("gpencil.draw",
{"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties":
[("mode", 'DRAW'),
("wait_for_input", False),
],
},
),
("gpencil.draw",
{"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True, "alt": True},
{"properties":
[("mode", 'DRAW_STRAIGHT'),
("wait_for_input", False),
],
},
),
("gpencil.draw",
{"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties":
[("mode", 'ERASER'),
("wait_for_input", False),
],
},
),
("gpencil.draw", {"type": 'O', "value": 'PRESS'}, None), ("gpencil.draw", {"type": 'O', "value": 'PRESS'}, None),
("gpencil.draw", {"type": 'J', "value": 'PRESS'}, None), ("gpencil.draw", {"type": 'J', "value": 'PRESS'}, None),
("gpencil.draw", {"type": 'J', "value": 'PRESS', "alt": True}, None), ("gpencil.draw", {"type": 'J', "value": 'PRESS', "alt": True}, None),
@@ -2002,14 +1965,7 @@ keyconfig_data = \
("gpencil.draw", {"type": 'M', "value": 'PRESS'}, None), ("gpencil.draw", {"type": 'M', "value": 'PRESS'}, None),
("gpencil.draw", {"type": 'C', "value": 'PRESS'}, None), ("gpencil.draw", {"type": 'C', "value": 'PRESS'}, None),
("gpencil.draw", {"type": 'C', "value": 'PRESS', "alt": True}, None), ("gpencil.draw", {"type": 'C', "value": 'PRESS', "alt": True}, None),
("gpencil.draw", ("gpencil.draw", {"type": 'ERASER', "value": 'PRESS'}, None),
{"type": 'ERASER', "value": 'PRESS'},
{"properties":
[("mode", 'ERASER'),
("wait_for_input", False),
],
},
),
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None), ("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
("gpencil.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None), ("gpencil.select_lasso", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
], ],
@@ -2838,11 +2794,9 @@ keyconfig_data = \
{ "active":False, { "active":False,
}, },
), ),
("wm.call_menu", ("object.swapmode",
{"type": 'DEL', "value": 'PRESS', "repeat": True}, {"type": 'MIDDLEMOUSE', "value": 'DOUBLE_CLICK'},
{"properties": { "active":False,
[("name", 'VIEW3D_MT_edit_mesh_delete'),
],
}, },
), ),
("object.swapmode", ("object.swapmode",
@@ -2850,6 +2804,23 @@ keyconfig_data = \
{ "active":False, { "active":False,
}, },
), ),
("object.swapmode",
{"type": 'MIDDLEMOUSE', "value": 'DOUBLE_CLICK'},
{ "active":False,
},
),
("object.swapmode",
{"type": 'MIDDLEMOUSE', "value": 'DOUBLE_CLICK'},
{ "active":False,
},
),
("wm.call_menu",
{"type": 'DEL', "value": 'PRESS', "repeat": True},
{"properties":
[("name", 'VIEW3D_MT_edit_mesh_delete'),
],
},
),
], ],
}, },
), ),
@@ -3014,6 +2985,11 @@ keyconfig_data = \
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS', "repeat": True}, None), ("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS', "repeat": True}, None),
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True, "repeat": True}, None), ("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True, "repeat": True}, None),
("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True, "repeat": True}, None), ("anim.keying_set_active_set", {"type": 'I', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True, "repeat": True}, None),
("collection.create",
{"type": 'G', "value": 'PRESS', "ctrl": True, "repeat": True},
{ "active":False,
},
),
("collection.objects_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True, "repeat": True}, None), ("collection.objects_remove", {"type": 'G', "value": 'PRESS', "ctrl": True, "alt": True, "repeat": True}, None),
("collection.objects_remove_all", {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True, "repeat": True}, None), ("collection.objects_remove_all", {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True, "repeat": True}, None),
("collection.objects_add_active", {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, None), ("collection.objects_add_active", {"type": 'G', "value": 'PRESS', "shift": True, "ctrl": True, "repeat": True}, None),
@@ -3211,7 +3187,7 @@ keyconfig_data = \
], ],
}, },
), ),
("object.pivotlock", {"type": 'V', "value": 'PRESS', "repeat": True}, None), ("object.pivotlock", {"type": 'V', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("object.hideorigin", {"type": 'D', "value": 'PRESS', "repeat": True}, None), ("object.hideorigin", {"type": 'D', "value": 'PRESS', "repeat": True}, None),
("theme.double", {"type": 'B', "value": 'PRESS', "alt": True, "repeat": True}, None), ("theme.double", {"type": 'B', "value": 'PRESS', "alt": True, "repeat": True}, None),
("object.keygridsnap", {"type": 'G', "value": 'PRESS', "key_modifier": 'D', "repeat": True}, None), ("object.keygridsnap", {"type": 'G', "value": 'PRESS', "key_modifier": 'D', "repeat": True}, None),
@@ -3413,7 +3389,6 @@ keyconfig_data = \
("outliner.drivers_add_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "repeat": True}, None), ("outliner.drivers_add_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "repeat": True}, None),
("outliner.drivers_delete_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True, "repeat": True}, None), ("outliner.drivers_delete_selected", {"type": 'D', "value": 'PRESS', "ctrl": True, "alt": True, "repeat": True}, None),
("outliner.collection_new", {"type": 'C', "value": 'PRESS', "repeat": True}, None), ("outliner.collection_new", {"type": 'C', "value": 'PRESS', "repeat": True}, None),
("outliner.collection_delete", {"type": 'X', "value": 'PRESS', "repeat": True}, None),
("outliner.delete", {"type": 'DEL', "value": 'PRESS', "repeat": True}, None), ("outliner.delete", {"type": 'DEL', "value": 'PRESS', "repeat": True}, None),
("object.move_to_collection", {"type": 'M', "value": 'PRESS', "repeat": True}, None), ("object.move_to_collection", {"type": 'M', "value": 'PRESS', "repeat": True}, None),
("object.link_to_collection", {"type": 'M', "value": 'PRESS', "shift": True, "repeat": True}, None), ("object.link_to_collection", {"type": 'M', "value": 'PRESS', "shift": True, "repeat": True}, None),
@@ -3528,6 +3503,7 @@ keyconfig_data = \
("outliner.show_active", {"type": 'F', "value": 'PRESS', "repeat": True}, None), ("outliner.show_active", {"type": 'F', "value": 'PRESS', "repeat": True}, None),
("outliner.item_rename", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None), ("outliner.item_rename", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None), ("object.move_to_collection", {"type": 'G', "value": 'PRESS', "ctrl": True}, None),
("outliner.delete", {"type": 'X', "value": 'PRESS'}, None),
], ],
}, },
), ),
@@ -4306,6 +4282,7 @@ keyconfig_data = \
{"properties": {"properties":
[("extend", False), [("extend", False),
], ],
"active":False,
}, },
), ),
("uv.select", ("uv.select",
@@ -4503,14 +4480,13 @@ keyconfig_data = \
}, },
), ),
("wm.call_menu", ("wm.call_menu",
{"type": 'E', "value": 'PRESS', "repeat": True}, {"type": 'RIGHTMOUSE', "value": 'PRESS'},
{"properties": {"properties":
[("name", 'IMAGE_MT_uvs_specials'), [("name", 'IMAGE_MT_uvs_context_menu'),
], ],
"active":False,
}, },
), ),
("uv.cursor_set", {"type": 'LEFTMOUSE', "value": 'CLICK'}, None), ("uv.cursor_set", {"type": 'LEFTMOUSE', "value": 'CLICK', "ctrl": True}, None),
("wm.tool_set_by_name", {"type": 'Q', "value": 'PRESS', "repeat": True}, None), ("wm.tool_set_by_name", {"type": 'Q', "value": 'PRESS', "repeat": True}, None),
("wm.tool_set_by_name", {"type": 'W', "value": 'PRESS', "repeat": True}, None), ("wm.tool_set_by_name", {"type": 'W', "value": 'PRESS', "repeat": True}, None),
("wm.tool_set_by_id", ("wm.tool_set_by_id",
@@ -4541,6 +4517,7 @@ keyconfig_data = \
], ],
}, },
), ),
("uv.select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
], ],
}, },
), ),
@@ -4595,6 +4572,24 @@ keyconfig_data = \
("ui.list_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ("ui.list_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
("ui.view_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None), ("ui.view_start_filter", {"type": 'F', "value": 'PRESS', "ctrl": True}, None),
("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'}, None), ("anim.keyframe_insert_by_name", {"type": 'S', "value": 'PRESS'}, None),
("ui.view_item_select", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("ui.view_item_select",
{"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
{"properties":
[("extend", True),
],
},
),
("ui.view_item_select",
{"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True},
{"properties":
[("range_select", True),
],
},
),
("ui.view_scroll", {"type": 'WHEELUPMOUSE', "value": 'ANY'}, None),
("ui.view_scroll", {"type": 'WHEELDOWNMOUSE', "value": 'ANY'}, None),
("ui.view_scroll", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
], ],
}, },
), ),
@@ -4603,12 +4598,7 @@ keyconfig_data = \
{"items": {"items":
[("view2d.scroller_activate", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None), [("view2d.scroller_activate", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("view2d.scroller_activate", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None), ("view2d.scroller_activate", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
("view2d.pan", ("view2d.pan", {"type": 'MIDDLEMOUSE', "value": 'PRESS'}, None),
{"type": 'MIDDLEMOUSE', "value": 'PRESS', "shift": True},
{ "active":False,
},
),
("view2d.pan", {"type": 'TRACKPADPAN', "value": 'ANY'}, None),
("view2d.scroll_right", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True}, None), ("view2d.scroll_right", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "ctrl": True}, None),
("view2d.scroll_left", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "ctrl": True}, None), ("view2d.scroll_left", {"type": 'WHEELUPMOUSE', "value": 'PRESS', "ctrl": True}, None),
("view2d.scroll_down", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None), ("view2d.scroll_down", {"type": 'WHEELDOWNMOUSE', "value": 'PRESS', "shift": True}, None),
@@ -4670,8 +4660,8 @@ keyconfig_data = \
{"type": 'F', "value": 'PRESS', "repeat": True}, {"type": 'F', "value": 'PRESS', "repeat": True},
{"properties": {"properties":
[("data_path_primary", 'tool_settings.weight_paint.brush.size'), [("data_path_primary", 'tool_settings.weight_paint.brush.size'),
("data_path_secondary", 'tool_settings.unified_paint_settings.size'), ("data_path_secondary", 'tool_settings.weight_paint.unified_paint_settings.size'),
("use_secondary", 'tool_settings.unified_paint_settings.use_unified_size'), ("use_secondary", 'tool_settings.weight_paint.unified_paint_settings.use_unified_size'),
("rotation_path", 'tool_settings.weight_paint.brush.texture_slot.angle'), ("rotation_path", 'tool_settings.weight_paint.brush.texture_slot.angle'),
("color_path", 'tool_settings.weight_paint.brush.cursor_color_add'), ("color_path", 'tool_settings.weight_paint.brush.cursor_color_add'),
("fill_color_path", ''), ("fill_color_path", ''),
@@ -4687,8 +4677,8 @@ keyconfig_data = \
{"type": 'F', "value": 'PRESS', "shift": True, "repeat": True}, {"type": 'F', "value": 'PRESS', "shift": True, "repeat": True},
{"properties": {"properties":
[("data_path_primary", 'tool_settings.weight_paint.brush.strength'), [("data_path_primary", 'tool_settings.weight_paint.brush.strength'),
("data_path_secondary", 'tool_settings.unified_paint_settings.strength'), ("data_path_secondary", 'tool_settings.weight_paint.unified_paint_settings.strength'),
("use_secondary", 'tool_settings.unified_paint_settings.use_unified_strength'), ("use_secondary", 'tool_settings.weight_paint.unified_paint_settings.use_unified_strength'),
("rotation_path", 'tool_settings.weight_paint.brush.texture_slot.angle'), ("rotation_path", 'tool_settings.weight_paint.brush.texture_slot.angle'),
("color_path", 'tool_settings.weight_paint.brush.cursor_color_add'), ("color_path", 'tool_settings.weight_paint.brush.cursor_color_add'),
("fill_color_path", ''), ("fill_color_path", ''),
@@ -4704,8 +4694,8 @@ keyconfig_data = \
{"type": 'F', "value": 'PRESS', "ctrl": True, "repeat": True}, {"type": 'F', "value": 'PRESS', "ctrl": True, "repeat": True},
{"properties": {"properties":
[("data_path_primary", 'tool_settings.weight_paint.brush.weight'), [("data_path_primary", 'tool_settings.weight_paint.brush.weight'),
("data_path_secondary", 'tool_settings.unified_paint_settings.weight'), ("data_path_secondary", 'tool_settings.weight_paint.unified_paint_settings.weight'),
("use_secondary", 'tool_settings.unified_paint_settings.use_unified_weight'), ("use_secondary", 'tool_settings.weight_paint.unified_paint_settings.use_unified_weight'),
("rotation_path", 'tool_settings.weight_paint.brush.texture_slot.angle'), ("rotation_path", 'tool_settings.weight_paint.brush.texture_slot.angle'),
("color_path", 'tool_settings.weight_paint.brush.cursor_color_add'), ("color_path", 'tool_settings.weight_paint.brush.cursor_color_add'),
("fill_color_path", ''), ("fill_color_path", ''),