black format

This commit is contained in:
2026-01-07 16:05:47 +01:00
parent f7c125ae7b
commit 3d65bb6e4d
49 changed files with 2817 additions and 2280 deletions
+7 -6
View File
@@ -1,4 +1,3 @@
from asset_library.action import (
gui,
keymaps,
@@ -7,10 +6,10 @@ from asset_library.action import (
operators,
properties,
rename_pose,
#render_preview
)
# render_preview
)
if 'bpy' in locals():
if "bpy" in locals():
import importlib
importlib.reload(gui)
@@ -20,14 +19,16 @@ if 'bpy' in locals():
importlib.reload(operators)
importlib.reload(properties)
importlib.reload(rename_pose)
#importlib.reload(render_preview)
# importlib.reload(render_preview)
import bpy
def register():
operators.register()
keymaps.register()
def unregister():
operators.unregister()
keymaps.unregister()
keymaps.unregister()
+125 -92
View File
@@ -1,8 +1,7 @@
import sys
from pathlib import Path
#sys.path.append(str(Path(__file__).parents[3]))
# sys.path.append(str(Path(__file__).parents[3]))
from asset_library.action.concat_preview import mosaic_export
from asset_library.common.file_utils import open_file
@@ -19,17 +18,19 @@ import subprocess
from tempfile import gettempdir
def rm_tree(pth):
pth = Path(pth)
for child in pth.glob('*'):
for child in pth.glob("*"):
if child.is_file():
child.unlink()
else:
rm_tree(child)
pth.rmdir()
def render_preview(directory, asset_catalog, render_actions, publish_actions, remove_folder):
def render_preview(
directory, asset_catalog, render_actions, publish_actions, remove_folder
):
scn = bpy.context.scene
rnd = bpy.context.scene.render
@@ -39,27 +40,32 @@ def render_preview(directory, asset_catalog, render_actions, publish_actions, re
blendfile = Path(bpy.data.filepath)
asset_catalog_data = read_catalog(asset_catalog)
anim_render_dir = Path(gettempdir()) / 'actionlib_render' #/tmp/actionlib_render. Removed at the end
anim_render_dir = (
Path(gettempdir()) / "actionlib_render"
) # /tmp/actionlib_render. Removed at the end
anim_render_dir.mkdir(exist_ok=True, parents=True)
preview_render_dir = Path(directory) / 'preview'
preview_render_dir = Path(directory) / "preview"
if preview_render_dir.exists() and remove_folder:
rm_tree(preview_render_dir)
preview_render_dir.mkdir(exist_ok=True, parents=True)
for i in ('anim', 'pose'):
for i in ("anim", "pose"):
Path(preview_render_dir / i).mkdir(exist_ok=True, parents=True)
for f in preview_render_dir.rglob('*'):
for f in preview_render_dir.rglob("*"):
if f.is_dir():
print(f'{f} is dir. Skipped.')
print(f"{f} is dir. Skipped.")
continue
if all(i not in f.parts for i in ('anim', 'pose')) and f.parent.parts[-1] != 'preview':
print(f'{f} is out of pipe. Approved or Rtk pictures. Skipped.')
if (
all(i not in f.parts for i in ("anim", "pose"))
and f.parent.parts[-1] != "preview"
):
print(f"{f} is out of pipe. Approved or Rtk pictures. Skipped.")
continue
if not any(f.stem.endswith(a) for a in publish_actions):
print(f'{str(f)} not in publish actions anymore. Removing...')
print(f"{str(f)} not in publish actions anymore. Removing...")
f.unlink()
# Set Scene
@@ -68,9 +74,9 @@ def render_preview(directory, asset_catalog, render_actions, publish_actions, re
scn.use_preview_range = True
scn.eevee.use_gtao = True
scn.tool_settings.use_keyframe_insert_auto = False
# Render Setting
rnd.engine = 'BLENDER_EEVEE'
rnd.engine = "BLENDER_EEVEE"
rnd.use_simplify = False
rnd.use_stamp_date = True
rnd.use_stamp_time = True
@@ -89,7 +95,7 @@ def render_preview(directory, asset_catalog, render_actions, publish_actions, re
rnd.use_stamp = True
rnd.stamp_font_size = 16
rnd.use_stamp_labels = False
rnd.image_settings.file_format = 'JPEG'
rnd.image_settings.file_format = "JPEG"
# Viewport Look
# ----------
@@ -108,94 +114,104 @@ def render_preview(directory, asset_catalog, render_actions, publish_actions, re
"""
# Cycles Mat Shading
for a in bpy.context.screen.areas:
if a.type == 'VIEW_3D':
if a.type == "VIEW_3D":
a.spaces[0].overlay.show_overlays = False
a.spaces[0].region_3d.view_perspective = 'CAMERA'
a.spaces[0].region_3d.view_perspective = "CAMERA"
a.spaces[0].shading.show_cavity = True
a.spaces[0].shading.cavity_type = 'WORLD'
a.spaces[0].shading.cavity_type = "WORLD"
a.spaces[0].shading.cavity_ridge_factor = 0.75
a.spaces[0].shading.cavity_valley_factor = 1.0
# Add Subsurf
# -----------
deform_ob = [m.object for o in scn.objects \
for m in o.modifiers if m.type == 'MESH_DEFORM'
deform_ob = [
m.object for o in scn.objects for m in o.modifiers if m.type == "MESH_DEFORM"
]
deform_ob += [m.target for o in scn.objects \
for m in o.modifiers if m.type == 'SURFACE_DEFORM'
deform_ob += [
m.target for o in scn.objects for m in o.modifiers if m.type == "SURFACE_DEFORM"
]
objects = [o for o in bpy.context.scene.objects if (o.type == 'MESH'
and o not in deform_ob and o not in bpy.context.scene.collection.objects[:])
objects = [
o
for o in bpy.context.scene.objects
if (
o.type == "MESH"
and o not in deform_ob
and o not in bpy.context.scene.collection.objects[:]
)
]
for o in objects:
subsurf = False
for m in o.modifiers:
if m.type == 'SUBSURF':
if m.type == "SUBSURF":
m.show_viewport = m.show_render
m.levels = m.render_levels
subsurf = True
break
if not subsurf:
subsurf = o.modifiers.new('', 'SUBSURF')
subsurf = o.modifiers.new("", "SUBSURF")
subsurf.show_viewport = subsurf.show_render
subsurf.levels = subsurf.render_levels
# Loop through action and render
# ------------------------------
rig = next((o for o in scn.objects if o.type == 'ARMATURE'), None)
rig = next((o for o in scn.objects if o.type == "ARMATURE"), None)
# actions = [a for a in bpy.data.actions if a.asset_data]
rig.animation_data_create()
for action_name in render_actions:
action = bpy.data.actions.get(action_name)
if not action:
print(f'\'{action_name}\' not found.')
print(f"'{action_name}' not found.")
continue
print(f"-- Current --: {action.name}")
rnd.stamp_note_text = '{type} : {pose_name}'
rnd.stamp_note_text = "{type} : {pose_name}"
action_data = action.asset_data
if 'camera' not in action_data.keys():
if "camera" not in action_data.keys():
report.append(f"'{action.name}' has no CameraData.")
continue
catalog_name = next((v['name'] for v in asset_catalog_data.values() if action_data.catalog_id == v['id']), None)
pose_name = '/'.join([*catalog_name.split('-'), action.name])
filename = bpy.path.clean_name(f'{catalog_name}_{action.name}')
ext = 'jpg'
catalog_name = next(
(
v["name"]
for v in asset_catalog_data.values()
if action_data.catalog_id == v["id"]
),
None,
)
pose_name = "/".join([*catalog_name.split("-"), action.name])
filename = bpy.path.clean_name(f"{catalog_name}_{action.name}")
ext = "jpg"
rig.animation_data.action = None
bpy.context.view_layer.update()
for b in rig.pose.bones:
if re.match('^[A-Z]+\.', b.name):
if re.match("^[A-Z]+\.", b.name):
continue
reset_bone(b)
rest_pose = None
if isinstance(action.asset_data.get('rest_pose'), str):
rest_pose = bpy.data.actions.get(action.asset_data['rest_pose'])
if isinstance(action.asset_data.get("rest_pose"), str):
rest_pose = bpy.data.actions.get(action.asset_data["rest_pose"])
rig.animation_data.action = rest_pose
bpy.context.view_layer.update()
rig.animation_data.action = action
if 'camera' in action.asset_data.keys():
action_cam = bpy.data.objects.get(action.asset_data['camera'], '')
if "camera" in action.asset_data.keys():
action_cam = bpy.data.objects.get(action.asset_data["camera"], "")
if action_cam:
scn.camera = action_cam
# Is Anim
if not action_data['is_single_frame'] or 'anim' in action_data.tags.keys():
if not action_data["is_single_frame"] or "anim" in action_data.tags.keys():
keyframes = get_keyframes(action)
if not keyframes:
continue
@@ -203,58 +219,66 @@ def render_preview(directory, asset_catalog, render_actions, publish_actions, re
anim_end = keyframes[-1]
if anim_start < scn.frame_start:
report.append(f"Issue found for '{action.name}'. Has keyframes before 'Start Frame'.")
report.append(
f"Issue found for '{action.name}'. Has keyframes before 'Start Frame'."
)
continue
scn.frame_preview_start = anim_start
scn.frame_preview_end = anim_end
rnd.stamp_note_text = rnd.stamp_note_text.format(
type='ANIM',
type="ANIM",
pose_name=pose_name,
)
rnd.filepath = f'{str(anim_render_dir)}/{filename}_####.{ext}'
rnd.filepath = f"{str(anim_render_dir)}/{filename}_####.{ext}"
bpy.ops.render.opengl(animation=True)
ffmpeg_cmd = [
'ffmpeg', '-y',
'-start_number', f'{anim_start:04d}',
'-i', rnd.filepath.replace('####', '%04d'),
'-c:v', 'libx264',
str((preview_render_dir/'anim'/filename).with_suffix('.mov')),
"ffmpeg",
"-y",
"-start_number",
f"{anim_start:04d}",
"-i",
rnd.filepath.replace("####", "%04d"),
"-c:v",
"libx264",
str((preview_render_dir / "anim" / filename).with_suffix(".mov")),
]
subprocess.call(ffmpeg_cmd)
# Is Pose
elif action_data['is_single_frame'] or 'pose' in action_data.tags.keys():
elif action_data["is_single_frame"] or "pose" in action_data.tags.keys():
scn.frame_preview_start = scn.frame_preview_end = scn.frame_start
rnd.stamp_note_text = rnd.stamp_note_text.format(
type='POSE',
type="POSE",
pose_name=pose_name,
)
rnd.filepath = f'{str(preview_render_dir)}/pose/{filename}_####.{ext}'
rnd.filepath = f"{str(preview_render_dir)}/pose/{filename}_####.{ext}"
bpy.ops.render.opengl(animation=True)
filename = rnd.filepath.replace('####', f'{scn.frame_preview_end:04d}')
Path(filename).rename(re.sub('_[0-9]{4}.', '.', filename))
filename = rnd.filepath.replace("####", f"{scn.frame_preview_end:04d}")
Path(filename).rename(re.sub("_[0-9]{4}.", ".", filename))
shutil.rmtree(anim_render_dir)
# Report
# ------
if report:
report_file = blendfile.parent / Path(f'{blendfile.stem}report').with_suffix('.txt')
report_file = blendfile.parent / Path(f"{blendfile.stem}report").with_suffix(
".txt"
)
if not report_file.exists():
report_file.touch(exist_ok=False)
report_file.write_text('-')
report_file.write_text('\n'.join(report))
report_file.write_text("-")
report_file.write_text("\n".join(report))
result = report_file
else:
@@ -262,31 +286,40 @@ def render_preview(directory, asset_catalog, render_actions, publish_actions, re
open_file(result)
files = [str(f) for f in sorted((preview_render_dir/'pose').glob('*.jpg'))]
files = [str(f) for f in sorted((preview_render_dir / "pose").glob("*.jpg"))]
mosaic_export(
files=files, catalog_data=asset_catalog_data,
row=2, columns=2, auto_calculate=True,
bg_color=(0.18, 0.18, 0.18,), resize_output=100
files=files,
catalog_data=asset_catalog_data,
row=2,
columns=2,
auto_calculate=True,
bg_color=(
0.18,
0.18,
0.18,
),
resize_output=100,
)
bpy.ops.wm.quit_blender()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Add Comment To the tracker",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
if __name__ == '__main__' :
parser = argparse.ArgumentParser(description='Add Comment To the tracker',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--directory")
parser.add_argument("--asset-catalog")
parser.add_argument("--render-actions", nargs="+")
parser.add_argument("--publish-actions", nargs="+")
parser.add_argument("--remove-folder", type=json.loads, default="false")
parser.add_argument('--directory')
parser.add_argument('--asset-catalog')
parser.add_argument('--render-actions', nargs='+')
parser.add_argument('--publish-actions', nargs='+')
parser.add_argument('--remove-folder', type=json.loads, default='false')
if '--' in sys.argv :
index = sys.argv.index('--')
sys.argv = [sys.argv[index-1], *sys.argv[index+1:]]
if "--" in sys.argv:
index = sys.argv.index("--")
sys.argv = [sys.argv[index - 1], *sys.argv[index + 1 :]]
args = parser.parse_args()
render_preview(**vars(args))
+20 -19
View File
@@ -1,25 +1,26 @@
import argparse
import bpy
import json
import sys
from pathlib import Path
#sys.path.append(str(Path(__file__).parents[3]))
# sys.path.append(str(Path(__file__).parents[3]))
from asset_library.common.bl_utils import (
get_preview,
)
def clear_asset(action_name='', use_fake_user=False):
def clear_asset(action_name="", use_fake_user=False):
scn = bpy.context.scene
action = bpy.data.actions.get(action_name)
if not action:
print(f'No {action_name} not found.')
print(f"No {action_name} not found.")
bpy.ops.wm.quit_blender()
action.asset_clear()
action.asset_clear()
if use_fake_user:
action.use_fake_user = True
else:
@@ -27,22 +28,22 @@ def clear_asset(action_name='', use_fake_user=False):
if preview:
preview.unlink()
bpy.data.actions.remove(action)
bpy.ops.wm.save_mainfile(
filepath=bpy.data.filepath, compress=True, exit=True
bpy.ops.wm.save_mainfile(filepath=bpy.data.filepath, compress=True, exit=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Add Comment To the tracker",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
if __name__ == '__main__' :
parser = argparse.ArgumentParser(description='Add Comment To the tracker',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--action-name')
parser.add_argument('--use-fake-user', type=json.loads, default='false')
parser.add_argument("--action-name")
parser.add_argument("--use-fake-user", type=json.loads, default="false")
if '--' in sys.argv :
index = sys.argv.index('--')
sys.argv = [sys.argv[index-1], *sys.argv[index+1:]]
if "--" in sys.argv:
index = sys.argv.index("--")
sys.argv = [sys.argv[index - 1], *sys.argv[index + 1 :]]
args = parser.parse_args()
clear_asset(**vars(args))
+61 -44
View File
@@ -1,4 +1,3 @@
import bpy
import math
import numpy as np
@@ -10,75 +9,90 @@ def alpha_to_color(pixels_data, color):
"""Convert Alpha to WhiteBG"""
new_pixels_data = []
for i in pixels_data:
height, width, array_d = i.shape
mask = i[:,:,3:]
background = np.array([color[0], color[1], color[2] ,1], dtype=np.float32)
background = np.tile(background, (height*width))
background = np.reshape(background, (height,width,4))
height, width, array_d = i.shape
mask = i[:, :, 3:]
background = np.array([color[0], color[1], color[2], 1], dtype=np.float32)
background = np.tile(background, (height * width))
background = np.reshape(background, (height, width, 4))
new_pixels_data.append(i * mask + background * (1 - mask))
# print(new_pixels_data)#Dbg
return new_pixels_data
def create_array(height, width):
return np.zeros((height*width*4), dtype=np.float32)
return np.zeros((height * width * 4), dtype=np.float32)
def read_pixels_data(img, source_height, source_width):
img_w, img_h = img.size
if img_w != source_width :
scale = abs(img_w/source_width)
img.scale(int(img_w/scale), int(img_h/scale))
if img_w != source_width:
scale = abs(img_w / source_width)
img.scale(int(img_w / scale), int(img_h / scale))
img_w, img_h = img.size
array = create_array(img_h, img_w)
img.pixels.foreach_get(array)
array = array.reshape(img_h, img_w, 4)
if array.shape[0] != source_height:
#print('ARRAY SHAPE', array.shape[:], source_height)
missing_height = int(abs(source_height-img_h)/2)
# print('ARRAY SHAPE', array.shape[:], source_height)
missing_height = int(abs(source_height - img_h) / 2)
empty_array = create_array(missing_height, source_width)
empty_array = empty_array.reshape(missing_height, source_width, 4)
array = np.vstack((empty_array, array, empty_array))
return array.reshape(source_height, source_width, 4)
def create_final(output_name, pixels_data, final_height, final_width):
#print('output_name: ', output_name)
# print('output_name: ', output_name)
new_img = bpy.data.images.get(output_name)
if new_img:
bpy.data.images.remove(new_img)
new_img = bpy.data.images.new(output_name, final_width, final_height)
new_img.generated_color=(0,0,0,0)
new_img.generated_color = (0, 0, 0, 0)
#print('pixels_data: ', pixels_data)
# print('pixels_data: ', pixels_data)
new_img.pixels.foreach_set(pixels_data)
return new_img
def guess_input_format(img_list):
for i in img_list:
if i.size[0] == i.size[1]:
return i.size
def format_files(files, catalog_data):
img_dict = {}
for k, v in catalog_data.items():
if '/' not in k:
if "/" not in k:
continue
img_dict[v['name']] = [f for f in files if v['name'] in f]
img_dict[v["name"]] = [f for f in files if v["name"] in f]
return img_dict
def mosaic_export(
files, catalog_data, row=2, columns=2, auto_calculate=True,
bg_color=(0.18, 0.18, 0.18,), resize_output=100,
files,
catalog_data,
row=2,
columns=2,
auto_calculate=True,
bg_color=(
0.18,
0.18,
0.18,
),
resize_output=100,
):
img_dict = format_files(files, catalog_data)
for cat, files_list in img_dict.items():
if not files_list:
@@ -86,61 +100,64 @@ def mosaic_export(
for i in bpy.data.images:
bpy.data.images.remove(i)
img_list = []
chars = Path(files_list[0]).parts[-4]
output_dir = str(Path(files_list[0]).parent.parent)
ext = 'jpg'
output_name = f'{chars}_{cat}.{ext}'
ext = "jpg"
output_name = f"{chars}_{cat}.{ext}"
for img in files_list:
img_list.append(bpy.data.images.load(img, check_existing=True))
for i in img_list:
i.colorspace_settings.name = 'Raw'
i.colorspace_settings.name = "Raw"
if auto_calculate:
rows = int(math.sqrt(len(img_list)))
columns = math.ceil(len(img_list)/rows)
columns = math.ceil(len(img_list) / rows)
if rows * columns < len(img_list):
raise AttributeError("Grid too small for number of images")
if rows*columns < len(img_list):
raise AttributeError('Grid too small for number of images')
src_w, src_h = img_list[0].size
final_w = src_w * columns
final_h = src_h * rows
img_pixels = [read_pixels_data(img, src_h, src_w) for img in img_list]
#Check if there is enough "data" to create an horizontal stack
# Check if there is enough "data" to create an horizontal stack
##It not, create empty array
h_stack = []
total_len = rows*columns
total_len = rows * columns
if len(img_pixels) < total_len:
for i in range(total_len-len(img_pixels)):
for i in range(total_len - len(img_pixels)):
img_pixels.append(create_array(src_h, src_w).reshape(src_h, src_w, 4))
img_pixels = alpha_to_color(img_pixels, bg_color)
for i in range(0,len(img_pixels),columns):
h_stack.append(np.hstack(img_pixels[i:i+columns]))
for i in range(0, len(img_pixels), columns):
h_stack.append(np.hstack(img_pixels[i : i + columns]))
if rows > 1:
combined_stack = np.vstack(h_stack[::-1])
else:
combined_stack = np.hstack((h_stack[:]))
combined_img = create_final(output_name, combined_stack.flatten(), final_h, final_w)
combined_img = create_final(
output_name, combined_stack.flatten(), final_h, final_w
)
if resize_output != 100:
w, h = combined_img.size
combined_img.scale(w*(resize_output*.01), h*(resize_output*.01))
combined_img.scale(w * (resize_output * 0.01), h * (resize_output * 0.01))
combined_img.filepath_raw = '/'.join([output_dir, output_name])
combined_img.file_format = 'JPEG'
combined_img.filepath_raw = "/".join([output_dir, output_name])
combined_img.file_format = "JPEG"
combined_img.save()
print(f"""
print(
f"""
Image saved: {combined_img.filepath_raw}
""")
"""
)
+58 -43
View File
@@ -13,15 +13,7 @@ import functools
import re
import bpy
from bpy.types import (
Action,
Bone,
Context,
FCurve,
Keyframe,
Object,
TimelineMarker
)
from bpy.types import Action, Bone, Context, FCurve, Keyframe, Object, TimelineMarker
from asset_library.common.bl_utils import active_catalog_id, split_path
@@ -30,6 +22,7 @@ FCurveValue = Union[float, int]
pose_bone_re = re.compile(r'pose.bones\["([^"]+)"\]')
"""RegExp for matching FCurve data paths."""
def is_pose(action):
for fc in action.fcurves:
if len(fc.keyframe_points) > 1:
@@ -37,16 +30,18 @@ def is_pose(action):
return True
def get_bone_visibility(data_path):
bone, prop = split_path(data_path)
ob = bpy.context.object
b_layers = [i for i, val in enumerate(ob.pose.bones[bone].bone.layers) if val]
rig_layers = [(i, val) for i, val in enumerate(ob.data.layers)]
return ob.data.layers[b_layers[0]]
def get_keyframes(action, selected=False, includes=[]):
if selected:
# keyframes = sorted([int(k.co[0]) for f in action.fcurves for k in f.keyframe_points if k.select_control_point and get_bone_visibility(f.data_path)])
@@ -60,19 +55,26 @@ def get_keyframes(action, selected=False, includes=[]):
continue
if not get_bone_visibility(f.data_path):
continue
keyframes += [int(k.co[0])]
keyframes += [int(k.co[0])]
if len(keyframes) <= 1:
keyframes = [bpy.context.scene.frame_current]
else:
keyframes = sorted([int(k.co[0]) for f in action.fcurves for k in f.keyframe_points])
keyframes = sorted(
[int(k.co[0]) for f in action.fcurves for k in f.keyframe_points]
)
return keyframes
def get_marker(action):
if action.pose_markers:
markers = action.pose_markers
return next((m.name for m in markers if m.frame == bpy.context.scene.frame_current), None)
return next(
(m.name for m in markers if m.frame == bpy.context.scene.frame_current),
None,
)
def reset_bone(bone, transform=True, custom_props=True):
if transform:
@@ -83,41 +85,44 @@ def reset_bone(bone, transform=True, custom_props=True):
bone.rotation_axis_angle = (0, 0, 0, 0)
else:
bone.rotation_euler = (0, 0, 0)
bone.scale = (1, 1, 1)
if custom_props:
for key, value in bone.items():
try:
id_prop = bone.id_properties_ui(key)
except TypeError:
continue
if not isinstance(value, (int, float)) or not id_prop:
continue
bone[key] = id_prop.as_dict()['default']
bone[key] = id_prop.as_dict()["default"]
def is_asset_action(action):
return action.asset_data and action.asset_data.catalog_id != str(uuid.UUID(int=0))
def conform_action(action):
tags = ('pose', 'anim')
tags = ("pose", "anim")
if any(tag in action.asset_data.tags.keys() for tag in tags):
return
for fc in action.fcurves:
action.asset_data['is_single_frame'] = True
action.asset_data["is_single_frame"] = True
if len(fc.keyframe_points) > 1:
action.asset_data['is_single_frame'] = False
action.asset_data["is_single_frame"] = False
break
if action.asset_data['is_single_frame']:
action.asset_data.tags.new('pose')
if action.asset_data["is_single_frame"]:
action.asset_data.tags.new("pose")
else:
action.asset_data.tags.new('anim')
action.asset_data.tags.new("anim")
def clean_action(action='', frame_start=0, frame_end=0, excludes=[], includes=[]):
def clean_action(action="", frame_start=0, frame_end=0, excludes=[], includes=[]):
## Clean Keyframe Before/After Range
for fc in action.fcurves:
bone, prop = split_path(fc.data_path)
@@ -133,19 +138,21 @@ def clean_action(action='', frame_start=0, frame_end=0, excludes=[], includes=[]
fc.update()
# Remove Keyframe out of range
for k in reversed(fc.keyframe_points):
if int(k.co[0]) not in range(frame_start, frame_end+1):
for k in reversed(fc.keyframe_points):
if int(k.co[0]) not in range(frame_start, frame_end + 1):
fc.keyframe_points.remove(k)
fc.update()
def append_action(action_path='', action_name=''):
print(f'Loading {action_name} from: {action_path}')
def append_action(action_path="", action_name=""):
print(f"Loading {action_name} from: {action_path}")
with bpy.data.libraries.load(str(action_path), link=False) as (data_from, data_to):
data_to.actions = [action_name]
return data_to.actions[0]
def apply_anim(action_lib, ob, bones=[]):
from mathutils import Vector
@@ -153,23 +160,32 @@ def apply_anim(action_lib, ob, bones=[]):
if not ob.animation_data:
ob.animation_data_create()
action = ob.animation_data.action
if not action:
action = bpy.data.actions.new(ob.name)
ob.animation_data.action = action
keys = sorted([k.co[0] for f in action_lib.fcurves for k in f.keyframe_points])
if not keys:
print(f'The action {action_lib.name} has no keyframes')
print(f"The action {action_lib.name} has no keyframes")
return
first_key = keys[0]
key_offset = scn.frame_current - first_key
key_attr = ('type', 'interpolation', 'handle_left_type', 'handle_right_type',
'amplitude', 'back', 'easing', 'period', 'handle_right', 'handle_left'
key_attr = (
"type",
"interpolation",
"handle_left_type",
"handle_right_type",
"amplitude",
"back",
"easing",
"period",
"handle_right",
"handle_left",
)
for fc in action_lib.fcurves:
bone_name, prop_name = split_path(fc.data_path)
@@ -182,26 +198,25 @@ def apply_anim(action_lib, ob, bones=[]):
action_fc = action.fcurves.new(
fc.data_path,
index=fc.array_index,
action_group=fc.group.name if fc.group else fc.data_path.split('"')[1]
action_group=fc.group.name if fc.group else fc.data_path.split('"')[1],
)
for kf_lib in fc.keyframe_points:
kf = action_fc.keyframe_points.insert(
frame=kf_lib.co[0] + key_offset,
value=kf_lib.co[1]
frame=kf_lib.co[0] + key_offset, value=kf_lib.co[1]
)
for attr in key_attr:
src_val = getattr(kf_lib, attr)
if attr.startswith('handle') and 'type' not in attr:
if attr.startswith("handle") and "type" not in attr:
src_val += Vector((key_offset, 0))
setattr(kf, attr, src_val)
fc.update()
# redraw graph areas
for window in bpy.context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'GRAPH_EDITOR':
area.tag_redraw()
if area.type == "GRAPH_EDITOR":
area.tag_redraw()
+36 -22
View File
@@ -1,4 +1,3 @@
import bpy
@@ -6,44 +5,59 @@ def draw_context_menu(layout):
params = bpy.context.space_data.params
asset = bpy.context.asset_file_handle
layout.operator("assetlib.open_blend", text="Open blend file")#.asset = asset.name
layout.operator(
"assetlib.open_blend", text="Open blend file"
) # .asset = asset.name
layout.operator("assetlib.play_preview", text="Play Preview")
layout.separator()
layout.operator_context = 'INVOKE_DEFAULT'
layout.operator_context = "INVOKE_DEFAULT"
#layout.operator("assetlib.rename_asset", text="Rename Action")
# layout.operator("assetlib.rename_asset", text="Rename Action")
layout.operator("assetlib.remove_assets", text="Remove Assets")
layout.operator("assetlib.edit_data", text="Edit Asset data")
#layout.operator("actionlib.clear_asset", text="Clear Asset (Fake User)").use_fake_user = True
# layout.operator("actionlib.clear_asset", text="Clear Asset (Fake User)").use_fake_user = True
layout.separator()
layout.operator("actionlib.apply_selected_action", text="Apply Pose").flipped = False
layout.operator("actionlib.apply_selected_action", text="Apply Pose (Flipped)").flipped = True
layout.separator()
layout.operator("poselib.blend_pose_asset_for_keymap", text="Blend Pose").flipped = False
layout.operator("poselib.blend_pose_asset_for_keymap", text="Blend Pose (Flipped)").flipped = True
layout.operator("actionlib.apply_selected_action", text="Apply Pose").flipped = (
False
)
layout.operator(
"actionlib.apply_selected_action", text="Apply Pose (Flipped)"
).flipped = True
layout.separator()
layout.operator("poselib.pose_asset_select_bones", text="Select Bones").selected_side = 'CURRENT'
layout.operator("poselib.pose_asset_select_bones", text="Select Bones (Flipped)").selected_side = 'FLIPPED'
layout.operator("poselib.pose_asset_select_bones", text="Select Bones (Both)").selected_side = 'BOTH'
layout.operator(
"poselib.blend_pose_asset_for_keymap", text="Blend Pose"
).flipped = False
layout.operator(
"poselib.blend_pose_asset_for_keymap", text="Blend Pose (Flipped)"
).flipped = True
layout.separator()
#layout.operator("asset.library_refresh")
if params.display_type == 'THUMBNAIL':
layout.operator(
"poselib.pose_asset_select_bones", text="Select Bones"
).selected_side = "CURRENT"
layout.operator(
"poselib.pose_asset_select_bones", text="Select Bones (Flipped)"
).selected_side = "FLIPPED"
layout.operator(
"poselib.pose_asset_select_bones", text="Select Bones (Both)"
).selected_side = "BOTH"
layout.separator()
# layout.operator("asset.library_refresh")
if params.display_type == "THUMBNAIL":
layout.prop_menu_enum(params, "display_size")
def draw_header(layout):
'''Draw the header of the Asset Browser Window'''
"""Draw the header of the Asset Browser Window"""
layout.separator()
layout.operator("actionlib.store_anim_pose", text='Add Action', icon='FILE_NEW')
layout.operator("actionlib.store_anim_pose", text="Add Action", icon="FILE_NEW")
+30 -16
View File
@@ -1,49 +1,63 @@
from typing import List, Tuple
import bpy
addon_keymaps: List[Tuple[bpy.types.KeyMap, bpy.types.KeyMapItem]] = []
def register():
wm = bpy.context.window_manager
addon = wm.keyconfigs.addon
if not addon:
return
km = addon.keymaps.new(name="File Browser Main", space_type="FILE_BROWSER")
# DblClick to apply pose.
kmi = km.keymap_items.new("actionlib.apply_selected_action", "LEFTMOUSE", "DOUBLE_CLICK")
kmi = km.keymap_items.new(
"actionlib.apply_selected_action", "LEFTMOUSE", "DOUBLE_CLICK"
)
kmi.properties.flipped = False
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("actionlib.apply_selected_action", "LEFTMOUSE", "DOUBLE_CLICK", alt=True)
kmi = km.keymap_items.new(
"actionlib.apply_selected_action", "LEFTMOUSE", "DOUBLE_CLICK", alt=True
)
kmi.properties.flipped = True
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.blend_pose_asset_for_keymap", "LEFTMOUSE", "DOUBLE_CLICK", shift=True)
kmi = km.keymap_items.new(
"poselib.blend_pose_asset_for_keymap", "LEFTMOUSE", "DOUBLE_CLICK", shift=True
)
kmi.properties.flipped = False
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.blend_pose_asset_for_keymap", "LEFTMOUSE", "DOUBLE_CLICK", alt=True, shift=True)
kmi = km.keymap_items.new(
"poselib.blend_pose_asset_for_keymap",
"LEFTMOUSE",
"DOUBLE_CLICK",
alt=True,
shift=True,
)
kmi.properties.flipped = True
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.pose_asset_select_bones", "S", "PRESS")
kmi.properties.selected_side = 'CURRENT'
kmi.properties.selected_side = "CURRENT"
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.pose_asset_select_bones", "S", "PRESS", alt=True)
kmi.properties.selected_side = 'FLIPPED'
kmi.properties.selected_side = "FLIPPED"
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.pose_asset_select_bones", "S", "PRESS", alt=True, ctrl=True)
kmi.properties.selected_side = 'BOTH'
kmi = km.keymap_items.new(
"poselib.pose_asset_select_bones", "S", "PRESS", alt=True, ctrl=True
)
kmi.properties.selected_side = "BOTH"
addon_keymaps.append((km, kmi))
def unregister():
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
addon_keymaps.clear()
+286 -238
View File
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -2,20 +2,19 @@ import bpy
from bpy.types import PropertyGroup
from bpy.props import PointerProperty, StringProperty, BoolProperty
class ACTIONLIB_PG_scene(PropertyGroup):
flipped : BoolProperty(
flipped: BoolProperty(
name="Flip Pose",
default=False,
)
previous_action : PointerProperty(type=bpy.types.Action)
publish_path : StringProperty(subtype='FILE_PATH')
camera : PointerProperty(type=bpy.types.Object, poll=lambda s, o: o.type == 'CAMERA')
rest_pose : PointerProperty(type=bpy.types.Action, poll=lambda s, a: a.asset_data)
previous_action: PointerProperty(type=bpy.types.Action)
publish_path: StringProperty(subtype="FILE_PATH")
camera: PointerProperty(type=bpy.types.Object, poll=lambda s, o: o.type == "CAMERA")
rest_pose: PointerProperty(type=bpy.types.Action, poll=lambda s, a: a.asset_data)
classes = (
ACTIONLIB_PG_scene,
)
classes = (ACTIONLIB_PG_scene,)
def register():
@@ -24,6 +23,7 @@ def register():
bpy.types.Scene.actionlib = PointerProperty(type=ACTIONLIB_PG_scene)
def unregister():
try:
del bpy.types.Scene.actionlib
@@ -31,4 +31,4 @@ def unregister():
pass
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
bpy.utils.unregister_class(cls)
+18 -17
View File
@@ -1,4 +1,3 @@
import argparse
import bpy
import json
@@ -6,17 +5,19 @@ import re
import sys
from pathlib import Path
#sys.path.append(str(Path(__file__).parents[3]))
# sys.path.append(str(Path(__file__).parents[3]))
from asset_library.common.bl_utils import (
get_preview,
)
def rename_pose(src_name='', dst_name=''):
def rename_pose(src_name="", dst_name=""):
scn = bpy.context.scene
action = bpy.data.actions.get(src_name)
if not action:
print(f'No {src_name} not found.')
print(f"No {src_name} not found.")
bpy.ops.wm.quit_blender()
action.name = dst_name
@@ -24,21 +25,21 @@ def rename_pose(src_name='', dst_name=''):
if preview:
preview.rename(re.sub(src_name, dst_name, str(preview)))
bpy.ops.wm.save_mainfile(
filepath=bpy.data.filepath, compress=True, exit=True
bpy.ops.wm.save_mainfile(filepath=bpy.data.filepath, compress=True, exit=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Add Comment To the tracker",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
if __name__ == '__main__' :
parser = argparse.ArgumentParser(description='Add Comment To the tracker',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--src-name")
parser.add_argument("--dst-name")
parser.add_argument('--src-name')
parser.add_argument('--dst-name')
if '--' in sys.argv :
index = sys.argv.index('--')
sys.argv = [sys.argv[index-1], *sys.argv[index+1:]]
if "--" in sys.argv:
index = sys.argv.index("--")
sys.argv = [sys.argv[index - 1], *sys.argv[index + 1 :]]
args = parser.parse_args()
rename_pose(**vars(args))