Update for blender 5+ with retro-compatibility
- Added version check to keep addon compatible with previous version - Some feature are still broken (e.g: realign, broken since 4.3's gpv3) - note: DIR_PATH string property do not accept relative path since Blender 5.0. preferences brush_path,output_path and playblast_path had "//" by default. those props are turned to stnadard string for now
This commit is contained in:
parent
9fd3b4af83
commit
9d2644eb9f
@ -1,6 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
|
||||||
|
5.0.0
|
||||||
|
|
||||||
|
- added: Update for blender 5+. With retro-compatibility
|
||||||
|
|
||||||
4.2.0
|
4.2.0
|
||||||
|
|
||||||
- added: Delete Grease pencil strokes or points view bound (selection outside of viewport region is untouched)
|
- added: Delete Grease pencil strokes or points view bound (selection outside of viewport region is untouched)
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import bpy
|
|||||||
import re
|
import re
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from math import radians, degrees
|
from math import radians, degrees
|
||||||
|
from . import utils
|
||||||
|
|
||||||
# exemple for future improve: https://justinsbarrett.com/tweenmachine/
|
# exemple for future improve: https://justinsbarrett.com/tweenmachine/
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ def get_surrounding_points(fc, frame):
|
|||||||
|
|
||||||
return p_pt, n_pt
|
return p_pt, n_pt
|
||||||
|
|
||||||
## unused direct breackdown func
|
## unused direct breakdown func
|
||||||
def breakdown_keys(percentage=50, channels=('location', 'rotation_euler', 'scale'), axe=(0,1,2)):
|
def breakdown_keys(percentage=50, channels=('location', 'rotation_euler', 'scale'), axe=(0,1,2)):
|
||||||
cf = bpy.context.scene.frame_current# use operator context (may be unsynced timeline)
|
cf = bpy.context.scene.frame_current# use operator context (may be unsynced timeline)
|
||||||
axes_name = ('x', 'y', 'z')
|
axes_name = ('x', 'y', 'z')
|
||||||
@ -42,7 +43,7 @@ def breakdown_keys(percentage=50, channels=('location', 'rotation_euler', 'scale
|
|||||||
|
|
||||||
skipping = []
|
skipping = []
|
||||||
|
|
||||||
for fc in action.fcurves:
|
for fc in utils.get_fcurves(obj):
|
||||||
# if fc.data_path.split('"')[1] in bone_names_filter:# bones
|
# if fc.data_path.split('"')[1] in bone_names_filter:# bones
|
||||||
# if fc.data_path.split('.')[-1] in channels and fc.array_index in axe:# bones
|
# if fc.data_path.split('.')[-1] in channels and fc.array_index in axe:# bones
|
||||||
if fc.data_path in channels and fc.array_index in axe:# .split('.')[-1]
|
if fc.data_path in channels and fc.array_index in axe:# .split('.')[-1]
|
||||||
@ -252,7 +253,7 @@ class OBJ_OT_breakdown_obj_anim(bpy.types.Operator):
|
|||||||
|
|
||||||
## TODO for ob in context.selected objects, need to reduce list with upper filters...
|
## TODO for ob in context.selected objects, need to reduce list with upper filters...
|
||||||
|
|
||||||
for fc in action.fcurves:
|
for fc in utils.get_fcurves(obj):
|
||||||
# if fc.data_path.split('"')[1] in bone_names_filter:# bones
|
# if fc.data_path.split('"')[1] in bone_names_filter:# bones
|
||||||
# if fc.data_path.split('.')[-1] in channels and fc.array_index in axe:# bones
|
# if fc.data_path.split('.')[-1] in channels and fc.array_index in axe:# bones
|
||||||
if fc.data_path in self.channels:# .split('.')[-1]# and fc.array_index in axe
|
if fc.data_path in self.channels:# .split('.')[-1]# and fc.array_index in axe
|
||||||
|
|||||||
@ -180,10 +180,14 @@ def selection_changed():
|
|||||||
## Note: Same owner as layer manager (will be removed as well)
|
## Note: Same owner as layer manager (will be removed as well)
|
||||||
def subscribe_object_change():
|
def subscribe_object_change():
|
||||||
subscribe_to = (bpy.types.LayerObjects, 'active')
|
subscribe_to = (bpy.types.LayerObjects, 'active')
|
||||||
|
if bpy.app.version >= (5, 0, 0):
|
||||||
|
owner = bpy.types.GreasePencil
|
||||||
|
else:
|
||||||
|
owner = bpy.types.GreasePencilv3
|
||||||
bpy.msgbus.subscribe_rna(
|
bpy.msgbus.subscribe_rna(
|
||||||
key=subscribe_to,
|
key=subscribe_to,
|
||||||
# owner of msgbus subcribe (for clearing later)
|
# owner of msgbus subcribe (for clearing later)
|
||||||
owner=bpy.types.GreasePencilv3, # <-- attach to ID during it's lifetime.
|
owner=owner, # <-- attach to ID during it's lifetime.
|
||||||
args=(),
|
args=(),
|
||||||
notify=selection_changed,
|
notify=selection_changed,
|
||||||
options={'PERSISTENT'},
|
options={'PERSISTENT'},
|
||||||
@ -222,4 +226,4 @@ def unregister():
|
|||||||
if cursor_follow.__name__ in [hand.__name__ for hand in bpy.app.handlers.frame_change_post]:
|
if cursor_follow.__name__ in [hand.__name__ for hand in bpy.app.handlers.frame_change_post]:
|
||||||
bpy.app.handlers.frame_change_post.remove(cursor_follow)
|
bpy.app.handlers.frame_change_post.remove(cursor_follow)
|
||||||
|
|
||||||
bpy.msgbus.clear_by_owner(bpy.types.GreasePencilv3)
|
bpy.msgbus.clear_by_owner(bpy.types.GreasePencil)
|
||||||
@ -20,7 +20,8 @@ def remove_stroke_exact_duplications(apply=True, verbose=True, select=False):
|
|||||||
ct = 0
|
ct = 0
|
||||||
if verbose:
|
if verbose:
|
||||||
print('\nRemove redundant strokes in GP frames...')
|
print('\nRemove redundant strokes in GP frames...')
|
||||||
gp_datas = [gp for gp in bpy.data.grease_pencils_v3]
|
gp_source = bpy.data.grease_pencils if bpy.app.version >= (5,0,0) else bpy.data.grease_pencils_v3
|
||||||
|
gp_datas = [gp for gp in gp_source]
|
||||||
for gp in gp_datas:
|
for gp in gp_datas:
|
||||||
for l in gp.layers:
|
for l in gp.layers:
|
||||||
for f in l.frames:
|
for f in l.frames:
|
||||||
@ -221,17 +222,19 @@ class GPTB_OT_file_checker(bpy.types.Operator):
|
|||||||
o.use_grease_pencil_lights = False
|
o.use_grease_pencil_lights = False
|
||||||
|
|
||||||
## Disabled animation
|
## Disabled animation
|
||||||
if fix.list_disabled_anim:
|
# TODO : fix for Blender 5.0
|
||||||
fcu_ct = 0
|
if bpy.app.version < (5,0,0):
|
||||||
for act in bpy.data.actions:
|
if fix.list_disabled_anim:
|
||||||
if not act.users:
|
fcu_ct = 0
|
||||||
continue
|
for act in bpy.data.actions:
|
||||||
for fcu in act.fcurves:
|
if not act.users:
|
||||||
if fcu.mute:
|
continue
|
||||||
fcu_ct += 1
|
for fcu in act.fcurves:
|
||||||
print(f"muted: {act.name} > {fcu.data_path}")
|
if fcu.mute:
|
||||||
if fcu_ct:
|
fcu_ct += 1
|
||||||
problems.append(f'{fcu_ct} anim channel disabled (details in console)')
|
print(f"muted: {act.name} > {fcu.data_path}")
|
||||||
|
if fcu_ct:
|
||||||
|
problems.append(f'{fcu_ct} anim channel disabled (details in console)')
|
||||||
|
|
||||||
## Object visibility conflict
|
## Object visibility conflict
|
||||||
if fix.list_obj_vis_conflict:
|
if fix.list_obj_vis_conflict:
|
||||||
@ -330,7 +333,7 @@ class GPTB_OT_file_checker(bpy.types.Operator):
|
|||||||
|
|
||||||
# ## Set onion skin filter to 'All type'
|
# ## Set onion skin filter to 'All type'
|
||||||
# fix_kf_type = 0
|
# fix_kf_type = 0
|
||||||
# for gp in bpy.data.grease_pencils_v3:#from data
|
# for gp in bpy.data.grease_pencils: #from data
|
||||||
# if not gp.is_annotation:
|
# if not gp.is_annotation:
|
||||||
# if gp.onion_keyframe_type != 'ALL':
|
# if gp.onion_keyframe_type != 'ALL':
|
||||||
# gp.onion_keyframe_type = 'ALL'
|
# gp.onion_keyframe_type = 'ALL'
|
||||||
|
|||||||
@ -421,12 +421,12 @@ class GPTB_OT_toggle_mute_animation(Operator):
|
|||||||
self.selection = event.shift
|
self.selection = event.shift
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
|
|
||||||
def set_action_mute(self, act):
|
def set_action_mute(self, bag):
|
||||||
for i, fcu in enumerate(act.fcurves):
|
for i, fcu in enumerate(bag.fcurves):
|
||||||
print(i, fcu.data_path, fcu.array_index)
|
print(i, fcu.data_path, fcu.array_index)
|
||||||
# fcu.group don't have mute attribute in api.
|
# fcu.group don't have mute attribute in api.
|
||||||
fcu.mute = self.mute
|
fcu.mute = self.mute
|
||||||
for g in act.groups:
|
for g in bag.groups:
|
||||||
g.mute = self.mute
|
g.mute = self.mute
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
@ -443,22 +443,15 @@ class GPTB_OT_toggle_mute_animation(Operator):
|
|||||||
if self.mode == 'CAMERA' and o.type != 'CAMERA':
|
if self.mode == 'CAMERA' and o.type != 'CAMERA':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# mute attribute animation for GP and cameras
|
## Mute attribute animation for GP and cameras
|
||||||
if o.type in ('GREASEPENCIL', 'CAMERA') and o.data.animation_data:
|
if o.type in ('GREASEPENCIL', 'CAMERA') and o.data.animation_data:
|
||||||
gp_act = o.data.animation_data.action
|
if data_channelbag := utils.get_active_channelbag(o.data):
|
||||||
if gp_act:
|
|
||||||
print(f'\n---{o.name} data:')
|
print(f'\n---{o.name} data:')
|
||||||
self.set_action_mute(gp_act)
|
self.set_action_mute(data_channelbag)
|
||||||
|
|
||||||
if not o.animation_data:
|
if object_channelbag := utils.get_active_channelbag(o):
|
||||||
continue
|
print(f'\n---{o.name}:')
|
||||||
act = o.animation_data.action
|
self.set_action_mute(object_channelbag)
|
||||||
if not act:
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f'\n---{o.name}:')
|
|
||||||
self.set_action_mute(act)
|
|
||||||
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@ -495,7 +488,7 @@ class GPTB_OT_toggle_hide_gp_modifier(Operator):
|
|||||||
class GPTB_OT_list_disabled_anims(Operator):
|
class GPTB_OT_list_disabled_anims(Operator):
|
||||||
bl_idname = "gp.list_disabled_anims"
|
bl_idname = "gp.list_disabled_anims"
|
||||||
bl_label = "List Disabled Anims"
|
bl_label = "List Disabled Anims"
|
||||||
bl_description = "List disabled animations channels in scene. (shit+clic to list only on seleciton)"
|
bl_description = "List disabled animations channels in scene. (shit+clic to list only on selection)"
|
||||||
bl_options = {"REGISTER"}
|
bl_options = {"REGISTER"}
|
||||||
|
|
||||||
mute : bpy.props.BoolProperty(default=False)
|
mute : bpy.props.BoolProperty(default=False)
|
||||||
@ -521,27 +514,22 @@ class GPTB_OT_list_disabled_anims(Operator):
|
|||||||
# continue
|
# continue
|
||||||
|
|
||||||
if o.type == 'GREASEPENCIL':
|
if o.type == 'GREASEPENCIL':
|
||||||
if o.data.animation_data:
|
if fcurves := utils.get_fcurves(o.data):
|
||||||
gp_act = o.data.animation_data.action
|
for i, fcu in enumerate(fcurves):
|
||||||
if gp_act:
|
if fcu.mute:
|
||||||
for i, fcu in enumerate(gp_act.fcurves):
|
if o not in oblist:
|
||||||
if fcu.mute:
|
oblist.append(o)
|
||||||
if o not in oblist:
|
li.append(f'{o.name}:')
|
||||||
oblist.append(o)
|
li.append(f' - {fcu.data_path} {fcu.array_index}')
|
||||||
li.append(f'{o.name}:')
|
|
||||||
li.append(f' - {fcu.data_path} {fcu.array_index}')
|
|
||||||
|
|
||||||
if not o.animation_data:
|
|
||||||
continue
|
|
||||||
act = o.animation_data.action
|
|
||||||
if not act:
|
|
||||||
continue
|
|
||||||
|
|
||||||
for g in act.groups:
|
bag = utils.get_active_channelbag(o)
|
||||||
|
if not bag:
|
||||||
|
continue
|
||||||
|
for g in bag.groups:
|
||||||
if g.mute:
|
if g.mute:
|
||||||
li.append(f'{o.name} - group: {g.name}')
|
li.append(f'{o.name} - group: {g.name}')
|
||||||
|
|
||||||
for i, fcu in enumerate(act.fcurves):
|
for i, fcu in enumerate(bag.fcurves):
|
||||||
# print(i, fcu.data_path, fcu.array_index)
|
# print(i, fcu.data_path, fcu.array_index)
|
||||||
if fcu.mute:
|
if fcu.mute:
|
||||||
if o not in oblist:
|
if o not in oblist:
|
||||||
|
|||||||
@ -624,11 +624,15 @@ def obj_layer_name_callback():
|
|||||||
|
|
||||||
def subscribe_layer_change():
|
def subscribe_layer_change():
|
||||||
subscribe_to = (bpy.types.GreasePencilv3Layers, "active")
|
subscribe_to = (bpy.types.GreasePencilv3Layers, "active")
|
||||||
|
if bpy.app.version >= (5, 0, 0):
|
||||||
|
owner = bpy.types.GreasePencil
|
||||||
|
else:
|
||||||
|
owner = bpy.types.GreasePencilv3
|
||||||
bpy.msgbus.subscribe_rna(
|
bpy.msgbus.subscribe_rna(
|
||||||
key=subscribe_to,
|
key=subscribe_to,
|
||||||
# owner of msgbus subcribe (for clearing later)
|
# owner of msgbus subcribe (for clearing later)
|
||||||
# owner=handle,
|
# owner=handle,
|
||||||
owner=bpy.types.GreasePencilv3, # <-- can attach to an ID during all it's lifetime...
|
owner=owner, # <-- can attach to an ID during all it's lifetime...
|
||||||
# Args passed to callback function (tuple)
|
# Args passed to callback function (tuple)
|
||||||
args=(),
|
args=(),
|
||||||
# Callback function for property update
|
# Callback function for property update
|
||||||
@ -783,4 +787,4 @@ def unregister():
|
|||||||
|
|
||||||
# Delete layer index trigger
|
# Delete layer index trigger
|
||||||
# /!\ can remove msgbus made for other functions or other addons using same owner
|
# /!\ can remove msgbus made for other functions or other addons using same owner
|
||||||
bpy.msgbus.clear_by_owner(bpy.types.GreasePencilv3)
|
bpy.msgbus.clear_by_owner(bpy.types.GreasePencil)
|
||||||
@ -165,9 +165,9 @@ class GPTB_OT_import_obj_palette(Operator):
|
|||||||
# self.report({'WARNING'}, 'All materials are already in other selected object')
|
# self.report({'WARNING'}, 'All materials are already in other selected object')
|
||||||
|
|
||||||
# unlink objects and their gp data
|
# unlink objects and their gp data
|
||||||
|
data_source = bpy.data.grease_pencils if bpy.app.version >= (5, 0, 0) else bpy.data.grease_pencils_v3
|
||||||
for src_ob in linked_objs:
|
for src_ob in linked_objs:
|
||||||
bpy.data.grease_pencils_v3.remove(src_ob.data)
|
data_source.remove(src_ob.data)
|
||||||
|
|
||||||
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|||||||
@ -158,10 +158,10 @@ def align_all_frames(reproject=True, ref=None, all_strokes=True):
|
|||||||
chanel = 'rotation_quaternion' if o.rotation_mode == 'QUATERNION' else 'rotation_euler'
|
chanel = 'rotation_quaternion' if o.rotation_mode == 'QUATERNION' else 'rotation_euler'
|
||||||
|
|
||||||
## double list keys
|
## double list keys
|
||||||
rot_keys = [int(k.co.x) for fcu in o.animation_data.action.fcurves for k in fcu.keyframe_points if fcu.data_path == chanel]
|
rot_keys = [int(k.co.x) for fcu in utils.get_fcurves(o) for k in fcu.keyframe_points if fcu.data_path == chanel]
|
||||||
|
|
||||||
## normal iter
|
## normal iter
|
||||||
# for fcu in o.animation_data.action.fcurves:
|
# for fcu in utils.get_fcurves(o):
|
||||||
# if fcu.data_path != chanel :
|
# if fcu.data_path != chanel :
|
||||||
# continue
|
# continue
|
||||||
# for k in fcu.keyframe_points():
|
# for k in fcu.keyframe_points():
|
||||||
@ -268,12 +268,12 @@ class GPTB_OT_realign(bpy.types.Operator):
|
|||||||
|
|
||||||
self.alert = ''
|
self.alert = ''
|
||||||
o = context.object
|
o = context.object
|
||||||
if o.animation_data and o.animation_data.action:
|
fcurves = utils.get_fcurves(o)
|
||||||
act = o.animation_data.action
|
if fcurves:
|
||||||
for chan in ('rotation_euler', 'rotation_quaternion'):
|
for chan in ('rotation_euler', 'rotation_quaternion'):
|
||||||
if act.fcurves.find(chan):
|
if fcurves.find(chan):
|
||||||
self.alert = 'Animated Rotation (CONSTANT interpolation)'
|
self.alert = 'Animated Rotation (CONSTANT interpolation)'
|
||||||
interpos = [p for fcu in act.fcurves if fcu.data_path == chan for p in fcu.keyframe_points if p.interpolation != 'CONSTANT']
|
interpos = [p for fcu in fcurves if fcu.data_path == chan for p in fcu.keyframe_points if p.interpolation != 'CONSTANT']
|
||||||
if interpos:
|
if interpos:
|
||||||
self.alert = f'Animated Rotation ! ({len(interpos)} key not constant)'
|
self.alert = f'Animated Rotation ! ({len(interpos)} key not constant)'
|
||||||
break
|
break
|
||||||
@ -323,8 +323,8 @@ class GPTB_OT_realign(bpy.types.Operator):
|
|||||||
oframe = context.scene.frame_current
|
oframe = context.scene.frame_current
|
||||||
|
|
||||||
o = bpy.context.object
|
o = bpy.context.object
|
||||||
if o.animation_data and o.animation_data.action:
|
if fcurves := utils.get_fcurves(o):
|
||||||
if o.animation_data.action.fcurves.find('rotation_euler') or o.animation_data.action.fcurves.find('rotation_quaternion'):
|
if fcurves.find('rotation_euler') or fcurves.find('rotation_quaternion'):
|
||||||
align_all_frames(reproject=self.reproject)
|
align_all_frames(reproject=self.reproject)
|
||||||
print(f'\nAnim realign ({time()-t0:.2f}s)')
|
print(f'\nAnim realign ({time()-t0:.2f}s)')
|
||||||
self.exit(context, oframe)
|
self.exit(context, oframe)
|
||||||
|
|||||||
19
UI_tools.py
19
UI_tools.py
@ -771,9 +771,15 @@ GPTB_PT_palettes_list_ui, # subpanels
|
|||||||
def register():
|
def register():
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
|
|
||||||
|
if bpy.app.version >= (5,0,0):
|
||||||
|
bpy.types.GREASE_PENCIL_MT_material_context_menu.append(palette_manager_menu)
|
||||||
|
# bpy.types.GREASE_PENCIL_MT_material_context_menu.append(palette_manager_menu)
|
||||||
|
else:
|
||||||
|
bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
|
||||||
|
# bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
|
||||||
|
|
||||||
bpy.types.DOPESHEET_PT_grease_pencil_mode.append(expose_use_channel_color_pref)
|
bpy.types.DOPESHEET_PT_grease_pencil_mode.append(expose_use_channel_color_pref)
|
||||||
# bpy.types.GPENCIL_MT_material_context_menu.append(palette_manager_menu)
|
|
||||||
# bpy.types.DOPESHEET_PT_gpencil_layer_display.append(expose_use_channel_color_pref)
|
# bpy.types.DOPESHEET_PT_gpencil_layer_display.append(expose_use_channel_color_pref)
|
||||||
|
|
||||||
# bpy.types.VIEW3D_HT_header.append(interpolate_header_ui) # WIP
|
# bpy.types.VIEW3D_HT_header.append(interpolate_header_ui) # WIP
|
||||||
@ -786,9 +792,14 @@ def unregister():
|
|||||||
# bpy.types.VIEW3D_HT_header.remove(interpolate_header_ui) # WIP
|
# bpy.types.VIEW3D_HT_header.remove(interpolate_header_ui) # WIP
|
||||||
|
|
||||||
bpy.types.DOPESHEET_PT_grease_pencil_mode.remove(expose_use_channel_color_pref)
|
bpy.types.DOPESHEET_PT_grease_pencil_mode.remove(expose_use_channel_color_pref)
|
||||||
bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
|
if bpy.app.version >= (5,0,0):
|
||||||
|
bpy.types.GREASE_PENCIL_MT_material_context_menu.remove(palette_manager_menu)
|
||||||
|
# bpy.types.GREASE_PENCIL_MT_material_context_menu.remove(palette_manager_menu)
|
||||||
|
else:
|
||||||
|
bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
|
||||||
|
# bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
|
||||||
|
|
||||||
# bpy.types.DOPESHEET_PT_gpencil_layer_display.remove(expose_use_channel_color_pref)
|
# bpy.types.DOPESHEET_PT_gpencil_layer_display.remove(expose_use_channel_color_pref)
|
||||||
# bpy.types.GPENCIL_MT_material_context_menu.remove(palette_manager_menu)
|
|
||||||
|
|
||||||
|
|
||||||
# if bpy.app.version >= (3,0,0):
|
# if bpy.app.version >= (3,0,0):
|
||||||
|
|||||||
72
__init__.py
72
__init__.py
@ -4,7 +4,7 @@ bl_info = {
|
|||||||
"name": "GP toolbox",
|
"name": "GP toolbox",
|
||||||
"description": "Tool set for Grease Pencil in animation production",
|
"description": "Tool set for Grease Pencil in animation production",
|
||||||
"author": "Samuel Bernou, Christophe Seux",
|
"author": "Samuel Bernou, Christophe Seux",
|
||||||
"version": (4, 2, 0),
|
"version": (5, 0, 0),
|
||||||
"blender": (4, 3, 0),
|
"blender": (4, 3, 0),
|
||||||
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
"location": "Sidebar (N menu) > Gpencil > Toolbox / Gpencil properties",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
@ -160,23 +160,6 @@ class GPTB_prefs(bpy.types.AddonPreferences):
|
|||||||
)
|
)
|
||||||
|
|
||||||
## output settings for automated renders
|
## output settings for automated renders
|
||||||
output_parent_level = IntProperty(
|
|
||||||
name='Parent level',
|
|
||||||
description="Go up in folder to define a render path relative to the file in upper directotys",
|
|
||||||
default=0,
|
|
||||||
min=0,
|
|
||||||
max=20
|
|
||||||
)
|
|
||||||
|
|
||||||
output_path : StringProperty(
|
|
||||||
name="Output path",
|
|
||||||
description="Path relative to blend to place render",
|
|
||||||
default="//render", maxlen=0, subtype='DIR_PATH')
|
|
||||||
|
|
||||||
playblast_path : StringProperty(
|
|
||||||
name="Playblast Path",
|
|
||||||
description="Path to folder for playblasts output",
|
|
||||||
default="//playblast", maxlen=0, subtype='DIR_PATH')
|
|
||||||
|
|
||||||
use_env_palettes : BoolProperty(
|
use_env_palettes : BoolProperty(
|
||||||
name="Use Project Palettes",
|
name="Use Project Palettes",
|
||||||
@ -206,10 +189,39 @@ class GPTB_prefs(bpy.types.AddonPreferences):
|
|||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
brush_path : StringProperty(
|
if bpy.app.version >= (5, 0, 0):
|
||||||
name="Brushes directory",
|
# DIR_PATH type do not accept relative ("//") since blender 5
|
||||||
description="Path to brushes containing the blends holding the brushes",
|
## using standard string
|
||||||
default="//", maxlen=0, subtype='DIR_PATH')#, update = set_palette_path
|
brush_path : StringProperty(
|
||||||
|
name="Brushes directory",
|
||||||
|
description="Path to brushes containing the blends holding the brushes",
|
||||||
|
default="//", maxlen=0)
|
||||||
|
|
||||||
|
output_path : StringProperty(
|
||||||
|
name="Output path",
|
||||||
|
description="Path relative to blend to place render",
|
||||||
|
default="//render", maxlen=0)
|
||||||
|
|
||||||
|
playblast_path : StringProperty(
|
||||||
|
name="Playblast Path",
|
||||||
|
description="Path to folder for playblasts output",
|
||||||
|
default="//playblast", maxlen=0)
|
||||||
|
|
||||||
|
else:
|
||||||
|
brush_path : StringProperty(
|
||||||
|
name="Brushes directory",
|
||||||
|
description="Path to brushes containing the blends holding the brushes",
|
||||||
|
default="//", maxlen=0, subtype='DIR_PATH')#, update = set_palette_path
|
||||||
|
|
||||||
|
output_path : StringProperty(
|
||||||
|
name="Output path",
|
||||||
|
description="Path relative to blend to place render",
|
||||||
|
default="//render", maxlen=0, subtype='DIR_PATH')
|
||||||
|
|
||||||
|
playblast_path : StringProperty(
|
||||||
|
name="Playblast Path",
|
||||||
|
description="Path to folder for playblasts output",
|
||||||
|
default="//playblast", maxlen=0, subtype='DIR_PATH')
|
||||||
|
|
||||||
## namespace
|
## namespace
|
||||||
separator : StringProperty(
|
separator : StringProperty(
|
||||||
@ -279,7 +291,7 @@ class GPTB_prefs(bpy.types.AddonPreferences):
|
|||||||
)
|
)
|
||||||
|
|
||||||
## KF jumper
|
## KF jumper
|
||||||
kfj_use_shortcut: BoolProperty(
|
kfj_use_shortcut : BoolProperty(
|
||||||
name = "Use Keyframe Jump Shortcut",
|
name = "Use Keyframe Jump Shortcut",
|
||||||
description = "Auto bind shotcut for keyframe jump (else you can bind manually using 'screen.gp_keyframe_jump' id_name)",
|
description = "Auto bind shotcut for keyframe jump (else you can bind manually using 'screen.gp_keyframe_jump' id_name)",
|
||||||
default = True)
|
default = True)
|
||||||
@ -289,17 +301,17 @@ class GPTB_prefs(bpy.types.AddonPreferences):
|
|||||||
description="Shortcut to trigger previous keyframe jump",
|
description="Shortcut to trigger previous keyframe jump",
|
||||||
default="F5")
|
default="F5")
|
||||||
|
|
||||||
kfj_prev_shift: BoolProperty(
|
kfj_prev_shift : BoolProperty(
|
||||||
name = "Shift",
|
name = "Shift",
|
||||||
description = "add shift",
|
description = "add shift",
|
||||||
default = False)
|
default = False)
|
||||||
|
|
||||||
kfj_prev_alt: BoolProperty(
|
kfj_prev_alt : BoolProperty(
|
||||||
name = "Alt",
|
name = "Alt",
|
||||||
description = "add alt",
|
description = "add alt",
|
||||||
default = False)
|
default = False)
|
||||||
|
|
||||||
kfj_prev_ctrl: BoolProperty(
|
kfj_prev_ctrl : BoolProperty(
|
||||||
name = "combine with ctrl",
|
name = "combine with ctrl",
|
||||||
description = "add ctrl",
|
description = "add ctrl",
|
||||||
default = False)
|
default = False)
|
||||||
@ -309,17 +321,17 @@ class GPTB_prefs(bpy.types.AddonPreferences):
|
|||||||
description="Shortcut to trigger keyframe jump",
|
description="Shortcut to trigger keyframe jump",
|
||||||
default="F6")
|
default="F6")
|
||||||
|
|
||||||
kfj_next_shift: BoolProperty(
|
kfj_next_shift : BoolProperty(
|
||||||
name = "Shift",
|
name = "Shift",
|
||||||
description = "add shift",
|
description = "add shift",
|
||||||
default = False)
|
default = False)
|
||||||
|
|
||||||
kfj_next_alt: BoolProperty(
|
kfj_next_alt : BoolProperty(
|
||||||
name = "Alt",
|
name = "Alt",
|
||||||
description = "add alt",
|
description = "add alt",
|
||||||
default = False)
|
default = False)
|
||||||
|
|
||||||
kfj_next_ctrl: BoolProperty(
|
kfj_next_ctrl : BoolProperty(
|
||||||
name = "combine with ctrl",
|
name = "combine with ctrl",
|
||||||
description = "add ctrl",
|
description = "add ctrl",
|
||||||
default = False)
|
default = False)
|
||||||
@ -704,8 +716,6 @@ def set_namespace_env(name_env, prop_group):
|
|||||||
n.is_project = n.tag in project_pfix
|
n.is_project = n.tag in project_pfix
|
||||||
|
|
||||||
def set_env_properties():
|
def set_env_properties():
|
||||||
|
|
||||||
|
|
||||||
prefs = get_addon_prefs()
|
prefs = get_addon_prefs()
|
||||||
|
|
||||||
fps = os.getenv('FPS')
|
fps = os.getenv('FPS')
|
||||||
|
|||||||
37
utils.py
37
utils.py
@ -1485,6 +1485,30 @@ def has_fully_enabled_anim(o):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_fcurves(obj):
|
||||||
|
'''return f-curves of object's active action'''
|
||||||
|
if bpy.app.version >= (5, 0, 0):
|
||||||
|
# action slot
|
||||||
|
if obj.animation_data and obj.animation_data.action_slot:
|
||||||
|
active_slot = obj.animation_data.action_slot
|
||||||
|
return active_slot.id_data.layers[0].strips[0].channelbag(active_slot).fcurves
|
||||||
|
else:
|
||||||
|
if obj.animation_data and obj.animation_data.action:
|
||||||
|
return obj.animation_data.action.fcurves
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_active_channelbag(obj):
|
||||||
|
'''return active channelbag in blender 5, else action'''
|
||||||
|
if bpy.app.version >= (5, 0, 0):
|
||||||
|
# action slot
|
||||||
|
if obj.animation_data and obj.animation_data.action_slot:
|
||||||
|
active_slot = obj.animation_data.action_slot
|
||||||
|
return active_slot.id_data.layers[0].strips[0].channelbag(active_slot)
|
||||||
|
else:
|
||||||
|
if obj.animation_data and obj.animation_data.action:
|
||||||
|
return obj.animation_data.action
|
||||||
|
|
||||||
|
|
||||||
def anim_status(objects) -> tuple((str, str)):
|
def anim_status(objects) -> tuple((str, str)):
|
||||||
'''Return a tutple of icon string status in ('ALL_ON', 'MIXED', 'ALL_OFF', 'NONE')'''
|
'''Return a tutple of icon string status in ('ALL_ON', 'MIXED', 'ALL_OFF', 'NONE')'''
|
||||||
|
|
||||||
@ -1504,8 +1528,9 @@ def anim_status(objects) -> tuple((str, str)):
|
|||||||
# off_count += 1
|
# off_count += 1
|
||||||
|
|
||||||
### Consider All channels individually
|
### Consider All channels individually
|
||||||
if o.animation_data and o.animation_data.action:
|
|
||||||
for grp in o.animation_data.action.groups:
|
if channelbag := get_active_channelbag(o):
|
||||||
|
for grp in channelbag.groups:
|
||||||
## Check if groups are muted
|
## Check if groups are muted
|
||||||
if grp.mute:
|
if grp.mute:
|
||||||
off_count += 1
|
off_count += 1
|
||||||
@ -1514,7 +1539,7 @@ def anim_status(objects) -> tuple((str, str)):
|
|||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
|
|
||||||
for fcu in o.animation_data.action.fcurves:
|
for fcu in channelbag.fcurves:
|
||||||
## Check if fcurves are muted
|
## Check if fcurves are muted
|
||||||
if fcu.mute:
|
if fcu.mute:
|
||||||
off_count += 1
|
off_count += 1
|
||||||
@ -1524,12 +1549,8 @@ def anim_status(objects) -> tuple((str, str)):
|
|||||||
|
|
||||||
if o.type in ('GREASEPENCIL', 'CAMERA'):
|
if o.type in ('GREASEPENCIL', 'CAMERA'):
|
||||||
datablock = o.data
|
datablock = o.data
|
||||||
if datablock.animation_data is None:
|
|
||||||
continue
|
|
||||||
if not datablock.animation_data.action:
|
|
||||||
continue
|
|
||||||
## Check if object data attributes fcurves are muted
|
## Check if object data attributes fcurves are muted
|
||||||
for fcu in datablock.animation_data.action.fcurves:
|
for fcu in get_fcurves(datablock):
|
||||||
if fcu.mute:
|
if fcu.mute:
|
||||||
off_count += 1
|
off_count += 1
|
||||||
else:
|
else:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user