C4D中将关键帧动画导出到文件并还原的python工具1.0

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))#把时间指针复位
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值