forked from bartvdbraak/blender
added bvh export operator & menu item, now user accessible.
This commit is contained in:
parent
631745ab9b
commit
f932371d1e
@ -27,18 +27,18 @@ if "bpy" in locals():
|
||||
|
||||
import bpy
|
||||
from bpy.props import *
|
||||
from io_utils import ImportHelper
|
||||
from io_utils import ImportHelper, ExportHelper
|
||||
|
||||
|
||||
class BvhImporter(bpy.types.Operator, ImportHelper):
|
||||
'''Load a OBJ Motion Capture File'''
|
||||
'''Load a BVH motion capture file'''
|
||||
bl_idname = "import_anim.bvh"
|
||||
bl_label = "Import BVH"
|
||||
|
||||
filename_ext = ".bvh"
|
||||
filter_glob = StringProperty(default="*.bvh", options={'HIDDEN'})
|
||||
|
||||
scale = FloatProperty(name="Scale", description="Scale the BVH by this value", min=0.0001, max=1000000.0, soft_min=0.001, soft_max=100.0, default=0.1)
|
||||
global_scale = FloatProperty(name="Scale", description="Scale the BVH by this value", min=0.0001, max=1000000.0, soft_min=0.001, soft_max=100.0, default=1.0)
|
||||
frame_start = IntProperty(name="Start Frame", description="Starting frame for the animation", default=1)
|
||||
use_cyclic = BoolProperty(name="Loop", description="Loop the animation playback", default=False)
|
||||
rotate_mode = EnumProperty(items=(
|
||||
@ -53,23 +53,52 @@ class BvhImporter(bpy.types.Operator, ImportHelper):
|
||||
),
|
||||
name="Rotation",
|
||||
description="Rotation conversion.",
|
||||
default='NATIVE')
|
||||
default='QUATERNION') # XXX, eulers are broken!
|
||||
|
||||
def execute(self, context):
|
||||
from . import import_bvh
|
||||
return import_bvh.load(self, context, **self.as_keywords(ignore=("filter_glob",)))
|
||||
|
||||
|
||||
def menu_func(self, context):
|
||||
class BvhExporter(bpy.types.Operator, ExportHelper):
|
||||
'''Save a BVH motion capture file from an armature'''
|
||||
bl_idname = "export_anim.bvh"
|
||||
bl_label = "Export BVH"
|
||||
|
||||
filename_ext = ".bvh"
|
||||
filter_glob = StringProperty(default="*.bvh", options={'HIDDEN'})
|
||||
|
||||
global_scale = FloatProperty(name="Scale", description="Scale the BVH by this value", min=0.0001, max=1000000.0, soft_min=0.001, soft_max=100.0, default=1.0)
|
||||
frame_start = IntProperty(name="Start Frame", description="Starting frame to export")
|
||||
frame_end = IntProperty(name="End Frame", description="End frame to export")
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.frame_start = context.scene.frame_start
|
||||
self.frame_end = context.scene.frame_end
|
||||
|
||||
return super().invoke(context, event)
|
||||
|
||||
def execute(self, context):
|
||||
from . import export_bvh
|
||||
return export_bvh.save(self, context, **self.as_keywords(ignore=("check_existing", "filter_glob")))
|
||||
|
||||
|
||||
def menu_func_import(self, context):
|
||||
self.layout.operator(BvhImporter.bl_idname, text="Motion Capture (.bvh)")
|
||||
|
||||
|
||||
def menu_func_export(self, context):
|
||||
self.layout.operator(BvhExporter.bl_idname, text="Motion Capture (.bvh)")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.types.INFO_MT_file_import.append(menu_func)
|
||||
bpy.types.INFO_MT_file_import.append(menu_func_import)
|
||||
bpy.types.INFO_MT_file_export.append(menu_func_export)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.types.INFO_MT_file_import.remove(menu_func)
|
||||
bpy.types.INFO_MT_file_import.remove(menu_func_import)
|
||||
bpy.types.INFO_MT_file_export.remove(menu_func_export)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
|
@ -22,44 +22,32 @@
|
||||
# fixes from Andrea Rugliancich
|
||||
|
||||
import bpy
|
||||
from mathutils import Matrix, Vector
|
||||
from math import degrees
|
||||
|
||||
def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
def _read(context, filepath, frame_start, frame_end, global_scale=1.0):
|
||||
|
||||
# Window.EditMode(0)
|
||||
from mathutils import Matrix, Vector
|
||||
from math import degrees
|
||||
|
||||
file = open(filepath, "w")
|
||||
|
||||
# bvh_nodes = {}
|
||||
arm_data = obj.data
|
||||
bones = arm_data.bones.values()
|
||||
obj = context.object
|
||||
arm = obj.data
|
||||
|
||||
# Build a dictionary of bone children.
|
||||
# None is for parentless bones
|
||||
bone_children = {None: []}
|
||||
|
||||
# initialize with blank lists
|
||||
for bone in bones:
|
||||
for bone in arm.bones:
|
||||
bone_children[bone.name] = []
|
||||
|
||||
for bone in bones:
|
||||
parent = bone.parent
|
||||
bone_name = bone.name
|
||||
if parent:
|
||||
bone_children[parent.name].append(bone_name)
|
||||
else: # root node
|
||||
bone_children[None].append(bone_name)
|
||||
for bone in arm.bones:
|
||||
bone_children[getattr(bone.parent, "name", None)].append(bone.name)
|
||||
|
||||
# sort the children
|
||||
for children_list in bone_children.values():
|
||||
children_list.sort()
|
||||
|
||||
# build a (name:bone) mapping dict
|
||||
bone_dict = {}
|
||||
for bone in bones:
|
||||
bone_dict[bone.name] = bone
|
||||
|
||||
# bone name list in the order that the bones are written
|
||||
bones_serialized_names = []
|
||||
|
||||
@ -72,7 +60,7 @@ def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
|
||||
indent_str = "\t" * indent
|
||||
|
||||
bone = bone_dict[bone_name]
|
||||
bone = arm.bones[bone_name]
|
||||
loc = bone.head_local
|
||||
bone_locs[bone_name] = loc
|
||||
|
||||
@ -86,7 +74,7 @@ def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
file.write("%sROOT %s\n" % (indent_str, bone_name))
|
||||
|
||||
file.write("%s{\n" % indent_str)
|
||||
file.write("%s\tOFFSET %.6f %.6f %.6f\n" % (indent_str, loc.x * pref_scale, loc.y * pref_scale, loc.z * pref_scale))
|
||||
file.write("%s\tOFFSET %.6f %.6f %.6f\n" % (indent_str, loc.x * global_scale, loc.y * global_scale, loc.z * global_scale))
|
||||
file.write("%s\tCHANNELS 6 Xposition Yposition Zposition Xrotation Yrotation Zrotation\n" % indent_str)
|
||||
|
||||
if my_bone_children:
|
||||
@ -103,7 +91,7 @@ def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
file.write("%s\tEnd Site\n" % indent_str)
|
||||
file.write("%s\t{\n" % indent_str)
|
||||
loc = bone.tail_local - bone_locs[bone_name]
|
||||
file.write("%s\t\tOFFSET %.6f %.6f %.6f\n" % (indent_str, loc.x * pref_scale, loc.y * pref_scale, loc.z * pref_scale))
|
||||
file.write("%s\t\tOFFSET %.6f %.6f %.6f\n" % (indent_str, loc.x * global_scale, loc.y * global_scale, loc.z * global_scale))
|
||||
file.write("%s\t}\n" % indent_str)
|
||||
|
||||
file.write("%s}\n" % indent_str)
|
||||
@ -129,9 +117,7 @@ def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
file.write("}\n")
|
||||
|
||||
# redefine bones as sorted by bones_serialized_names
|
||||
# se we can write motion
|
||||
pose_dict = obj.pose.bones
|
||||
#pose_bones = [(pose_dict[bone_name], bone_dict[bone_name].matrix_local.copy().invert()) for bone_name in bones_serialized_names]
|
||||
# so we can write motion
|
||||
|
||||
class decorated_bone(object):
|
||||
__slots__ = (\
|
||||
@ -148,8 +134,8 @@ def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
|
||||
def __init__(self, bone_name):
|
||||
self.name = bone_name
|
||||
self.rest_bone = bone_dict[bone_name]
|
||||
self.pose_bone = pose_dict[bone_name]
|
||||
self.rest_bone = arm.bones[bone_name]
|
||||
self.pose_bone = obj.pose.bones[bone_name]
|
||||
|
||||
self.pose_mat = self.pose_bone.matrix
|
||||
|
||||
@ -189,13 +175,13 @@ def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
# finish assigning parents
|
||||
|
||||
file.write("MOTION\n")
|
||||
file.write("Frames: %d\n" % (pref_endframe - pref_startframe + 1))
|
||||
file.write("Frames: %d\n" % (frame_end - frame_start + 1))
|
||||
file.write("Frame Time: %.6f\n" % 0.03)
|
||||
|
||||
scene = bpy.context.scene
|
||||
|
||||
triple = "%.6f %.6f %.6f "
|
||||
for frame in range(pref_startframe, pref_endframe + 1):
|
||||
for frame in range(frame_start, frame_end + 1):
|
||||
scene.frame_set(frame)
|
||||
obj.update(scene, 1,1,1)
|
||||
scene.update()
|
||||
@ -205,9 +191,6 @@ def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
if dbone.parent:
|
||||
trans = Matrix.Translation(dbone.rest_bone.head_local)
|
||||
itrans = Matrix.Translation(-dbone.rest_bone.head_local)
|
||||
|
||||
# mat2 = dbone.rest_arm_imat * dbone.pose_mat * dbone.parent.pose_imat * dbone.parent.rest_arm_mat
|
||||
# mat2 = trans * mat2 * itrans
|
||||
mat2 = dbone.parent.rest_arm_mat * dbone.parent.pose_imat * dbone.pose_mat * dbone.rest_arm_imat
|
||||
mat2 = itrans * mat2 * trans
|
||||
|
||||
@ -217,63 +200,37 @@ def bvh_export(filepath, obj, pref_startframe, pref_endframe, pref_scale=1.0):
|
||||
trans = Matrix.Translation(dbone.rest_bone.head_local)
|
||||
itrans = Matrix.Translation(-dbone.rest_bone.head_local)
|
||||
|
||||
# mat2 = dbone.rest_arm_imat * dbone.pose_mat
|
||||
# mat2 = trans * mat2 * itrans
|
||||
mat2 = dbone.pose_mat * dbone.rest_arm_imat
|
||||
mat2 = itrans * mat2 * trans
|
||||
|
||||
myloc = mat2.translation_part() + dbone.rest_bone.head_local
|
||||
rot = mat2.copy().transpose().to_euler()
|
||||
|
||||
file.write(triple % (myloc[0] * pref_scale, myloc[1] * pref_scale, myloc[2] * pref_scale))
|
||||
file.write(triple % (myloc[0] * global_scale, myloc[1] * global_scale, myloc[2] * global_scale))
|
||||
file.write(triple % (-degrees(rot[0]), -degrees(rot[1]), -degrees(rot[2])))
|
||||
|
||||
file.write("\n")
|
||||
|
||||
numframes = pref_endframe - pref_startframe + 1
|
||||
file.close()
|
||||
|
||||
print("BVH Exported: %s frames:%d\n" % (filepath, numframes))
|
||||
print("BVH Exported: %s frames:%d\n" % (filepath, frame_end - frame_start + 1))
|
||||
|
||||
bvh_export("/foo.bvh", bpy.context.object, 1, 190, 1.0)
|
||||
|
||||
'''
|
||||
def bvh_export_ui(filepath):
|
||||
# Dont overwrite
|
||||
if not BPyMessages.Warning_SaveOver(filepath):
|
||||
return
|
||||
def save(operator, context, filepath="",
|
||||
frame_start=-1,
|
||||
frame_end=-1,
|
||||
global_scale=1.0,
|
||||
):
|
||||
|
||||
scn = Scene.GetCurrent()
|
||||
ob_act = scn.objects.active
|
||||
if not ob_act or ob_act.type != 'Armature':
|
||||
BPyMessages.Error_NoArmatureActive()
|
||||
_read(context, filepath,
|
||||
frame_start=frame_start,
|
||||
frame_end=frame_end,
|
||||
global_scale=global_scale,
|
||||
)
|
||||
|
||||
arm_ob = scn.objects.active
|
||||
return {'FINISHED'}
|
||||
|
||||
if not arm_ob or arm_ob.type != 'Armature':
|
||||
Blender.Draw.PupMenu('No Armature object selected.')
|
||||
return
|
||||
|
||||
ctx = scn.getRenderingContext()
|
||||
orig_frame = Blender.Get('curframe')
|
||||
pref_startframe = Blender.Draw.Create(int(ctx.startFrame()))
|
||||
pref_endframe = Blender.Draw.Create(int(ctx.endFrame()))
|
||||
|
||||
block = [\
|
||||
("Start Frame: ", pref_startframe, 1, 30000, "Start Bake from what frame?: Default 1"),\
|
||||
("End Frame: ", pref_endframe, 1, 30000, "End Bake on what Frame?"),\
|
||||
]
|
||||
|
||||
if not Blender.Draw.PupBlock("Export MDD", block):
|
||||
return
|
||||
|
||||
pref_startframe, pref_endframe = \
|
||||
min(pref_startframe.val, pref_endframe.val),\
|
||||
max(pref_startframe.val, pref_endframe.val)
|
||||
|
||||
bvh_export(filepath, ob_act, pref_startframe, pref_endframe)
|
||||
Blender.Set('curframe', orig_frame)
|
||||
|
||||
if __name__ == '__main__':
|
||||
Blender.Window.FileSelector(bvh_export_ui, 'EXPORT BVH', sys.makename(ext='.bvh'))
|
||||
'''
|
||||
if __name__ == "__main__":
|
||||
scene = bpy.context.scene
|
||||
_read(bpy.data.filepath.rstrip(".blend") + ".bvh", bpy.context.object, scene.frame_start, scene.frame_end, 1.0)
|
||||
|
@ -90,7 +90,7 @@ def eulerRotate(x, y, z, rot_order):
|
||||
'''
|
||||
|
||||
|
||||
def read_bvh(context, file_path, ROT_MODE='XYZ', GLOBAL_SCALE=1.0):
|
||||
def read_bvh(context, file_path, rotate_mode='XYZ', global_scale=1.0):
|
||||
# File loading stuff
|
||||
# Open the file for importing
|
||||
file = open(file_path, 'rU')
|
||||
@ -134,7 +134,7 @@ def read_bvh(context, file_path, ROT_MODE='XYZ', GLOBAL_SCALE=1.0):
|
||||
#print '%snode: %s, parent: %s' % (len(bvh_nodes_serial) * ' ', name, bvh_nodes_serial[-1])
|
||||
|
||||
lineIdx += 2 # Incriment to the next line (Offset)
|
||||
rest_head_local = Vector((float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3]))) * GLOBAL_SCALE
|
||||
rest_head_local = Vector((float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3]))) * global_scale
|
||||
lineIdx += 1 # Incriment to the next line (Channels)
|
||||
|
||||
# newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation]
|
||||
@ -185,7 +185,7 @@ def read_bvh(context, file_path, ROT_MODE='XYZ', GLOBAL_SCALE=1.0):
|
||||
# Account for an end node
|
||||
if file_lines[lineIdx][0].lower() == 'end' and file_lines[lineIdx][1].lower() == 'site': # There is somtimes a name after 'End Site' but we will ignore it.
|
||||
lineIdx += 2 # Incriment to the next line (Offset)
|
||||
rest_tail = Vector((float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3]))) * GLOBAL_SCALE
|
||||
rest_tail = Vector((float(file_lines[lineIdx][1]), float(file_lines[lineIdx][2]), float(file_lines[lineIdx][3]))) * global_scale
|
||||
|
||||
bvh_nodes_serial[-1].rest_tail_world = bvh_nodes_serial[-1].rest_head_world + rest_tail
|
||||
bvh_nodes_serial[-1].rest_tail_local = bvh_nodes_serial[-1].rest_head_local + rest_tail
|
||||
@ -220,18 +220,18 @@ def read_bvh(context, file_path, ROT_MODE='XYZ', GLOBAL_SCALE=1.0):
|
||||
channels = bvh_node.channels
|
||||
anim_data = bvh_node.anim_data
|
||||
if channels[0] != -1:
|
||||
lx = GLOBAL_SCALE * float(line[channels[0]])
|
||||
lx = global_scale * float(line[channels[0]])
|
||||
|
||||
if channels[1] != -1:
|
||||
ly = GLOBAL_SCALE * float(line[channels[1]])
|
||||
ly = global_scale * float(line[channels[1]])
|
||||
|
||||
if channels[2] != -1:
|
||||
lz = GLOBAL_SCALE * float(line[channels[2]])
|
||||
lz = global_scale * float(line[channels[2]])
|
||||
|
||||
if channels[3] != -1 or channels[4] != -1 or channels[5] != -1:
|
||||
rx, ry, rz = float(line[channels[3]]), float(line[channels[4]]), float(line[channels[5]])
|
||||
|
||||
if ROT_MODE != 'NATIVE':
|
||||
if rotate_mode != 'NATIVE':
|
||||
rx, ry, rz = eulerRotate(radians(rx), radians(ry), radians(rz), bvh_node.rot_order)
|
||||
else:
|
||||
rx, ry, rz = radians(rx), radians(ry), radians(rz)
|
||||
@ -273,9 +273,9 @@ def read_bvh(context, file_path, ROT_MODE='XYZ', GLOBAL_SCALE=1.0):
|
||||
bvh_node.rest_tail_local = rest_tail_local * (1.0 / len(bvh_node.children))
|
||||
|
||||
# Make sure tail isnt the same location as the head.
|
||||
if (bvh_node.rest_tail_local - bvh_node.rest_head_local).length <= 0.001 * GLOBAL_SCALE:
|
||||
bvh_node.rest_tail_local.y = bvh_node.rest_tail_local.y + GLOBAL_SCALE / 10
|
||||
bvh_node.rest_tail_world.y = bvh_node.rest_tail_world.y + GLOBAL_SCALE / 10
|
||||
if (bvh_node.rest_tail_local - bvh_node.rest_head_local).length <= 0.001 * global_scale:
|
||||
bvh_node.rest_tail_local.y = bvh_node.rest_tail_local.y + global_scale / 10
|
||||
bvh_node.rest_tail_world.y = bvh_node.rest_tail_world.y + global_scale / 10
|
||||
|
||||
return bvh_nodes
|
||||
|
||||
@ -334,7 +334,7 @@ def bvh_node_dict2objects(context, bvh_nodes, IMPORT_START_FRAME=1, IMPORT_LOOP=
|
||||
return objects
|
||||
|
||||
|
||||
def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAME=1, IMPORT_LOOP=False):
|
||||
def bvh_node_dict2armature(context, bvh_nodes, rotate_mode='XYZ', IMPORT_START_FRAME=1, IMPORT_LOOP=False):
|
||||
|
||||
if IMPORT_START_FRAME < 1:
|
||||
IMPORT_START_FRAME = 1
|
||||
@ -430,7 +430,7 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM
|
||||
pose = arm_ob.pose
|
||||
pose_bones = pose.bones
|
||||
|
||||
if ROT_MODE == 'NATIVE':
|
||||
if rotate_mode == 'NATIVE':
|
||||
eul_order_lookup = {\
|
||||
(0, 1, 2): 'XYZ',
|
||||
(0, 2, 1): 'XZY',
|
||||
@ -444,9 +444,9 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM
|
||||
pose_bone = pose_bones[bone_name]
|
||||
pose_bone.rotation_mode = eul_order_lookup[tuple(bvh_node.rot_order)]
|
||||
|
||||
elif ROT_MODE != 'QUATERNION':
|
||||
elif rotate_mode != 'QUATERNION':
|
||||
for pose_bone in pose_bones:
|
||||
pose_bone.rotation_mode = ROT_MODE
|
||||
pose_bone.rotation_mode = rotate_mode
|
||||
else:
|
||||
# Quats default
|
||||
pass
|
||||
@ -488,7 +488,7 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM
|
||||
# KEYFRAME METHOD, SLOW, USE IPOS DIRECT
|
||||
# TODO: use f-point samples instead (Aligorith)
|
||||
|
||||
if ROT_MODE != 'QUATERNION':
|
||||
if rotate_mode != 'QUATERNION':
|
||||
prev_euler = [Euler() for i in range(len(bvh_nodes))]
|
||||
|
||||
# Animate the data, the last used bvh_node will do since they all have the same number of frames
|
||||
@ -507,7 +507,7 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM
|
||||
bone_rotation_matrix = Euler((rx, ry, rz)).to_matrix().resize4x4()
|
||||
bone_rotation_matrix = bone_rest_matrix_inv * bone_rotation_matrix * bone_rest_matrix
|
||||
|
||||
if ROT_MODE == 'QUATERNION':
|
||||
if rotate_mode == 'QUATERNION':
|
||||
pose_bone.rotation_quaternion = bone_rotation_matrix.to_quat()
|
||||
else:
|
||||
euler = bone_rotation_matrix.to_euler(pose_bone.rotation_mode, prev_euler[i])
|
||||
@ -520,7 +520,7 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM
|
||||
if bvh_node.has_loc:
|
||||
pose_bone.keyframe_insert("location")
|
||||
if bvh_node.has_rot:
|
||||
if ROT_MODE == 'QUATERNION':
|
||||
if rotate_mode == 'QUATERNION':
|
||||
pose_bone.keyframe_insert("rotation_quaternion")
|
||||
else:
|
||||
pose_bone.keyframe_insert("rotation_euler")
|
||||
@ -539,21 +539,21 @@ def bvh_node_dict2armature(context, bvh_nodes, ROT_MODE='XYZ', IMPORT_START_FRAM
|
||||
return arm_ob
|
||||
|
||||
|
||||
def load(operator, context, filepath="", rotate_mode='NATIVE', scale=1.0, use_cyclic=False, frame_start=1):
|
||||
def load(operator, context, filepath="", rotate_mode='NATIVE', global_scale=1.0, use_cyclic=False, frame_start=1):
|
||||
import time
|
||||
t1 = time.time()
|
||||
print('\tparsing bvh %r...' % filepath, end="")
|
||||
|
||||
bvh_nodes = read_bvh(context, filepath,
|
||||
ROT_MODE=rotate_mode,
|
||||
GLOBAL_SCALE=scale)
|
||||
rotate_mode=rotate_mode,
|
||||
global_scale=global_scale)
|
||||
|
||||
print('%.4f' % (time.time() - t1))
|
||||
t1 = time.time()
|
||||
print('\timporting to blender...', end="")
|
||||
|
||||
bvh_node_dict2armature(context, bvh_nodes,
|
||||
ROT_MODE=rotate_mode,
|
||||
rotate_mode=rotate_mode,
|
||||
IMPORT_START_FRAME=frame_start,
|
||||
IMPORT_LOOP=use_cyclic)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user