C4D中将关键帧动画导出到文件并还原的python工具1.0
可以将骨骼动画的位置、旋转、多边形变换标签(记录角色表情的标签)的关键帧导出并在任意位置还原
目前在R26版本可以运行,R20与之气那的旧版本c4d把变量类型说明语句删掉就行。因为之前的c4d内置了python,而且版本很旧,不支持新语法。
自己做unity游戏的时候需要更新模型结构,于是写了一个简单小工具,感觉只是自己用的话就浪费了,所以就发出来。
没啥技术含量,就是照着开发文档来写的,求大佬千万别批评…
关键帧动画导出到文件的python代码:
使用方法:
1、在c4d里面选中要导出的动画的根对象(重要,不然不能导出)
2、在代码中修改一下json文件的保存位置
3、运行脚本
注意事项:
1、目前能导出自定义的数据,但还不能读取,准备接下来再完善功能
2、同一层级的对象的名称不能相同。因为工具是按照层级来对对象进行标识的
代码:
from typing import Optional
import c4d
import sys
import json
import time
doc: c4d.documents.BaseDocument # The active document
op: Optional[c4d.BaseObject] # The active object, None if unselected
#root_obj = doc.SearchObject('root')
root_obj = doc.GetActiveObject()#用来获取骨骼根对象
if root_obj is None:
print("未选择对象")
sys.exit(0)
# --------------------全局函数---------------------------
#递归器
#参数layer:数组方式记录的物体层级信息
#参数obj,当前递归得到的对象
#参数func,可以自定义的递归时调用的函数
def Process_Node(layer,obj,func):
chlidren_list = obj.GetChildren()
layer_current = []
for str in layer:
layer_current.append(str)
layer_current.append(obj.GetName())
pass
func(obj,layer_current)
pass
for child in chlidren_list:
Process_Node(layer_current,child,func)
def Get_CKey_Info(ckey):
pass
key_info ={}
key_info["frame"] = ckey.GetTime().GetFrame(30)
key_info["value"] = ckey.GetValue()
key_info["val_L"] = ckey.GetValueLeft()
key_info["val_R"] = ckey.GetValueRight()
key_info["time_L"] = ckey.GetTimeLeft().GetFrame(30)
key_info["time_R"] = ckey.GetTimeRight().GetFrame(30)
return key_info
# ------------------------------------------------------
Anim_Record = {}
Anim_Record["bone"] = []
Anim_Record["morph"] = []
#读取骨骼关键帧的函数
def Read_Bone_Anim(obj,layer_current):
pass
obj_anim={}
obj_anim["layer"] = layer_current
obj_anim["tracks"] = []
tracks = obj.GetCTracks()
if len(tracks)==0:
return
for track in tracks:
pass
track_info = {}
track_info["name"] = track.GetName()
track_info["keys"] = []
curve = track.GetCurve()
key_count = curve.GetKeyCount()
for i in range(key_count):
pass
key = curve.GetKey(i)
key_info = Get_CKey_Info(key)
track_info["keys"].append(key_info)
obj_anim["tracks"].append(track_info)
Anim_Record["bone"].append(obj_anim)
Process_Node([],root_obj,Read_Bone_Anim)#传入函数,调用递归器
#读取对象上的表情标签的关键帧的函数
def Read_Morph_Anim(obj,layer_current):
pass
morph_tag = obj.GetTag(c4d.Tposemorph)
if morph_tag is None:
return
tag_tracks = morph_tag.GetCTracks()
if len(tag_tracks) == 0:
return
obj_anim={}
obj_anim["layer"] = layer_current
obj_anim["tracks"] = []
for track in tag_tracks:
curve = track.GetCurve()
key_count = curve.GetKeyCount()
track_info = {}
track_info["name"] = track.GetName()
track_info["keys"] = []
for i in range(key_count):
pass
key = curve.GetKey(i)
key_info = Get_CKey_Info(key)
track_info["keys"].append(key_info)
obj_anim["tracks"].append(track_info)
Anim_Record["morph"].append(obj_anim)
Process_Node([],root_obj,Read_Morph_Anim)
file = open("C:/Users/0204/AppData/Roaming/MAXON/Cinema 4D R20_4FA5020E/library/scripts/anim.json", mode='w+')#json文件保存位置,请先更改这个目录
file.write(json.dumps(Anim_Record))#对象转json字符串
file.close()
从文件中还原关键帧的python代码:
使用方法:
1、修改还原脚本中的文件读取路径。注意是上一步生成的文件
2、在c4d中选中要附着关键帧的模型的根对象。 注意这个新模型的带有关键帧的骨骼层级必须与上一阶段生成关键帧的模型相同
3、在c4d的文档里面设置时间指针,时间指针指到哪里就把关键帧移动复制到哪里。可以直接把时间指针拉到0帧位置
注意事项:
1、新的模型的带有关键帧的层级结构必须与原来的相同,否则将忽略这个骨骼
2、目前还不能还原自定义的数据
代码:
from typing import Optional
import c4d
import sys
import json
doc: c4d.documents.BaseDocument # The active document
op: Optional[c4d.BaseObject] # The active object, None if unselected
# root_obj = doc.SearchObject('root')
root_obj = doc.GetActiveObject()#获取骨骼根对象
if root_obj is None:
print("未选择对象")
sys.exit(0)
file = open("C:/Users/0204/AppData/Roaming/MAXON/Cinema 4D R20_4FA5020E/library/scripts/anim.json", mode='r')#json文件读取位置
str = file.read()
file.close()
current_doc_frame = doc.GetTime().GetFrame(30)#记录目前时间指针位置,假定文档设置为30帧/秒
dict_for_json = json.loads(str)#json字符串转对象
############################################
#定义的使用名称获取对象的子对象的函数
def Get_Child(obj, name):
pass
if obj is None:
return
for c in obj.GetChildren():
pass
if c.GetName() == name:
return c
#使用层级信息获取到对象的函数
def Get_Obj_By_Layer(root_obj, layer):
pass
obj_tmp = root_obj
for name in layer[1:]:
pass
if obj_tmp is None:
return
obj_tmp = Get_Child(obj_tmp, name)
return obj_tmp
#使用轨道名称生成DESCID的函数
def Get_PR_DescID_By_Name(name):
pass
if name == "位置 . X":
return c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))
if name == "位置 . Y":
return c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))
if name == "位置 . Z":
return c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0))
if name == "旋转 . H":
return c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))
if name == "旋转 . P":
return c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))
if name == "旋转 . B":
return c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_REL_ROTATION, c4d.DTYPE_VECTOR, 0), c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0))
#还原骨骼的位置和旋转关键帧
def Track_Name_Process(obj, key_list, track_name):
pass
Did = Get_PR_DescID_By_Name(track_name)
if not (Did is None):
pass
track = obj.FindCTrack(Did)
if track is None:
track = c4d.CTrack(obj, Did)
obj.InsertTrackSorted(track)
curve = track.GetCurve()
for key_info in key_list:
pass
Set_Key_To_Curve(curve, key_info)
else:
pass
#使用表情名称获取表情标签的轨道
def Get_Morph_Track_By_Name(tag, name):
pass
tag_tracks = tag.GetCTracks()
for track in tag_tracks:
if track.GetName() == name:
return track
#通过记录的关键帧信息将关键帧插入轨道
def Set_Key_To_Curve(curve, key_info):
pass
result = curve.AddKey(c4d.BaseTime(
key_info["frame"]+current_doc_frame, 30))
key: c4d.CKey
key = result["key"]
key.SetValue(curve, key_info["value"])
key.SetValueLeft(curve, key_info["val_L"])
key.SetValueRight(curve, key_info["val_R"])
key.SetTimeLeft(curve, c4d.BaseTime(key_info["time_L"], 30))
key.SetTimeRight(curve, c4d.BaseTime(key_info["time_R"], 30))
############################################
#还原骨骼的位置、旋转的代码
for info in dict_for_json["bone"]:
pass
# print(info)
obj = Get_Obj_By_Layer(root_obj, info['layer'])
if obj is None:
print("找不到对象:",info['layer'],"\n将忽略此对象")
continue
for track_info in info["tracks"]:
pass
Track_Name_Process(obj, track_info["keys"], track_info["name"])#主要的循环体
doc.SetTime(c4d.BaseTime(0, 30))#把帧位置移动到0
#还原表情标签的关键帧的代码
for info in dict_for_json["morph"]:
pass
obj = Get_Obj_By_Layer(root_obj, info['layer'])
tag = obj.GetTag(c4d.Tposemorph)
c4d.CallButton(tag, c4d.ID_CA_POSE_RESET_SLIDER)#点击表情标签的复位按钮
c4d.CallButton(tag, c4d.ID_CA_POSE_RECORD_SLIDER)#点击记录按钮,用来产生动画的track
for track_info in info["tracks"]:
pass
track = Get_Morph_Track_By_Name(tag, track_info["name"])
if track is None:
continue
curve = track.GetCurve()
for key_info in track_info["keys"]:
pass
Set_Key_To_Curve(curve, key_info)
doc.SetTime(c4d.BaseTime(current_doc_frame,30))#把时间指针复位