First Commit

This commit is contained in:
“christopheseux”
2022-12-24 15:30:32 +01:00
parent 3c7f133a85
commit e3d24aa4e3
49 changed files with 8466 additions and 4 deletions
+30
View File
@@ -0,0 +1,30 @@
from asset_library.action import (
gui,
keymaps,
clear_asset,
concat_preview,
operators,
properties,
rename_pose,
render_preview,)
if 'bpy' in locals():
import importlib
importlib.reload(gui)
importlib.reload(keymaps)
importlib.reload(clear_asset)
importlib.reload(concat_preview)
importlib.reload(operators)
importlib.reload(properties)
importlib.reload(rename_pose)
importlib.reload(render_preview)
def register():
operators.register()
keymaps.register()
def unregister():
operators.unregister()
keymaps.unregister()
+48
View File
@@ -0,0 +1,48 @@
import argparse
import bpy
import json
import sys
from pathlib import Path
sys.path.append(Path(__file__).parents[3])
from asset_library.common.bl_utils import (
get_preview,
)
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.')
bpy.ops.wm.quit_blender()
action.asset_clear()
if use_fake_user:
action.use_fake_user = True
else:
preview = get_preview(asset_path=bpy.data.filepath, asset_name=action_name)
if preview:
preview.unlink()
bpy.data.actions.remove(action)
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)
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:]]
args = parser.parse_args()
clear_asset(**vars(args))
+146
View File
@@ -0,0 +1,146 @@
import bpy
import math
import numpy as np
from pathlib import Path
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))
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)
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))
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)
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)
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)
#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:
continue
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,
):
img_dict = format_files(files, catalog_data)
for cat, files_list in img_dict.items():
if not files_list:
continue
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}'
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'
if auto_calculate:
rows = int(math.sqrt(len(img_list)))
columns = math.ceil(len(img_list)/rows)
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
##It not, create empty array
h_stack = []
total_len = rows*columns
if len(img_pixels) < total_len:
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]))
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)
if resize_output != 100:
w, h = combined_img.size
combined_img.scale(w*(resize_output*.01), h*(resize_output*.01))
combined_img.filepath_raw = '/'.join([output_dir, output_name])
combined_img.file_format = 'JPEG'
combined_img.save()
print(f"""
Image saved: {combined_img.filepath_raw}
""")
+207
View File
@@ -0,0 +1,207 @@
# SPDX-License-Identifier: GPL-2.0-or-later
"""
Functions related to anim and pose.
"""
from collections.abc import Collection
from typing import Optional, FrozenSet, Set, Union, Iterable, cast
import dataclasses
import functools
import re
import bpy
from bpy.types import (
Action,
Bone,
Context,
FCurve,
Keyframe,
Object,
TimelineMarker
)
from asset_library.common.bl_utils import active_catalog_id, split_path
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:
return False
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)])
keyframes = []
for f in action.fcurves:
bone, prop = split_path(f.data_path)
for k in f.keyframe_points:
if bone not in includes:
continue
if not k.select_control_point:
continue
if not get_bone_visibility(f.data_path):
continue
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])
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)
def reset_bone(bone, transform=True, custom_props=True):
if transform:
bone.location = (0, 0, 0)
if bone.rotation_mode == "QUATERNION":
bone.rotation_quaternion = (0, 0, 0, 0)
elif bone.rotation_mode == "AXIS_ANGLE":
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']
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')
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
if len(fc.keyframe_points) > 1:
action.asset_data['is_single_frame'] = False
break
if action.asset_data['is_single_frame']:
action.asset_data.tags.new('pose')
else:
action.asset_data.tags.new('anim')
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)
# !! Mush Mush dependent. Need to be fix
if bone in excludes or bone not in includes:
action.fcurves.remove(fc)
continue
# Add Keyframe At Start/End Range
for fr in (frame_start, frame_end):
fc_val = fc.evaluate(fr)
fc.keyframe_points.insert(frame=fr, value=fc_val)
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):
fc.keyframe_points.remove(k)
fc.update()
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
scn = bpy.context.scene
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')
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'
)
for fc in action_lib.fcurves:
bone_name, prop_name = split_path(fc.data_path)
if bones and bone_name not in bones:
continue
action_fc = action.fcurves.find(fc.data_path, index=fc.array_index)
if not action_fc:
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]
)
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]
)
for attr in key_attr:
src_val = getattr(kf_lib, 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()
+49
View File
@@ -0,0 +1,49 @@
import bpy
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.play_preview", text="Play Preview")
layout.separator()
layout.operator_context = 'INVOKE_DEFAULT'
#layout.operator("assetlib.rename_asset", text="Rename Action")
layout.operator("assetlib.clear_asset", text="Remove Asset")
layout.operator("assetlib.edit_data", text="Edit Asset data")
#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.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.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'''
layout.separator()
layout.operator("actionlib.store_anim_pose", text='Add Action', icon='FILE_NEW')
+49
View File
@@ -0,0 +1,49 @@
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.properties.flipped = False
addon_keymaps.append((km, kmi))
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.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.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'
addon_keymaps.append((km, kmi))
kmi = km.keymap_items.new("poselib.pose_asset_select_bones", "S", "PRESS", alt=True)
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'
addon_keymaps.append((km, kmi))
def unregister():
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
+1075
View File
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
import bpy
from bpy.types import PropertyGroup
from bpy.props import PointerProperty, StringProperty, BoolProperty
class ACTIONLIB_PG_scene(PropertyGroup):
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)
classes = (
ACTIONLIB_PG_scene,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.actionlib = PointerProperty(type=ACTIONLIB_PG_scene)
def unregister():
try:
del bpy.types.Scene.actionlib
except AttributeError:
pass
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
+44
View File
@@ -0,0 +1,44 @@
import argparse
import bpy
import json
import re
import sys
from pathlib import Path
sys.path.append(Path(__file__).parents[3])
from asset_library.common.bl_utils import (
get_preview,
)
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.')
bpy.ops.wm.quit_blender()
action.name = dst_name
preview = get_preview(asset_path=bpy.data.filepath, asset_name=src_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
)
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')
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))
+292
View File
@@ -0,0 +1,292 @@
import sys
from pathlib import Path
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
from asset_library.action.functions import reset_bone, get_keyframes
from asset_library.common.functions import read_catalog
import bpy
import argparse
import json
import re
import shutil
import subprocess
from tempfile import gettempdir
def rm_tree(pth):
pth = Path(pth)
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):
scn = bpy.context.scene
rnd = bpy.context.scene.render
rnd.resolution_x = rnd.resolution_y = 512
report = []
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.mkdir(exist_ok=True, parents=True)
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'):
Path(preview_render_dir / i).mkdir(exist_ok=True, parents=True)
for f in preview_render_dir.rglob('*'):
if f.is_dir():
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.')
continue
if not any(f.stem.endswith(a) for a in publish_actions):
print(f'{str(f)} not in publish actions anymore. Removing...')
f.unlink()
# Set Scene
# ----------
# Scene Setting
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.use_simplify = False
rnd.use_stamp_date = True
rnd.use_stamp_time = True
rnd.use_stamp_render_time = False
rnd.use_stamp_frame = True
rnd.use_stamp_frame_range = False
rnd.use_stamp_memory = False
rnd.use_stamp_hostname = False
rnd.use_stamp_camera = True
rnd.use_stamp_lens = False
rnd.use_stamp_scene = False
rnd.use_stamp_marker = False
rnd.use_stamp_filename = False
rnd.use_stamp_sequencer_strip = False
rnd.use_stamp_note = True
rnd.use_stamp = True
rnd.stamp_font_size = 16
rnd.use_stamp_labels = False
rnd.image_settings.file_format = 'JPEG'
# Viewport Look
# ----------
"""
# Eevee
for screen in bpy.data.screens:
for area in screen.areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.overlay.show_overlays = False
space.shading.type = 'RENDERED'
space.shading.use_scene_lights_render = False
space.shading.use_scene_world_render = False
space.region_3d.view_perspective = 'CAMERA'
"""
# Cycles Mat Shading
for a in bpy.context.screen.areas:
if a.type == 'VIEW_3D':
a.spaces[0].overlay.show_overlays = False
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_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.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[:])
]
for o in objects:
subsurf = False
for m in o.modifiers:
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.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)
# 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.')
continue
print(f"-- Current --: {action.name}")
rnd.stamp_note_text = '{type} : {pose_name}'
action_data = action.asset_data
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'
rig.animation_data.action = None
bpy.context.view_layer.update()
for b in rig.pose.bones:
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'])
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 action_cam:
scn.camera = action_cam
# Is Anim
if not action_data['is_single_frame'] or 'anim' in action_data.tags.keys():
keyframes = get_keyframes(action)
if not keyframes:
continue
anim_start = keyframes[0]
anim_end = keyframes[-1]
if anim_start < scn.frame_start:
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',
pose_name=pose_name,
)
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')),
]
subprocess.call(ffmpeg_cmd)
# Is Pose
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',
pose_name=pose_name,
)
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))
shutil.rmtree(anim_render_dir)
# Report
# ------
if report:
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))
result = report_file
else:
result = preview_render_dir
open_file(result)
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
)
bpy.ops.wm.quit_blender()
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')
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))