给定一些点,如何绘制出来,借助 blender 看下效果。纠结于 unity 还是 blender, 最终还是 blender 了。
目前还都不太满意,思路一比较靠谱,但是需要更复杂的计算 思路一,第二版,已完成,吐血制作!
文章目录
- blender 一些快捷键
- 思路一: 绘制顶点,和边
- 第二版,最终版
- 带有背景和光照的
- 思路二: 创建bvh 文件
- 思路三,骨骼驱动
blender 一些快捷键
alt+p
运行脚本
h , alt+h
隐藏与显示
shift+c
, /
如果找不到模型,可以通过这两个快捷键,或者 视图->局部视图 来选择。
f3
可以用于查找命令
1 3 7
三视图
blender 急救箱
思路一: 绘制顶点,和边
就是用物体代表顶点和边,并添加各自的材质。 动作通过设置关键帧,其中顶点比较好办就设置一个位置就行,边的话还需要计算旋转,就是旋转到点之间的向量
第一版,只画了点
import numpy as np
import os
import bpy# short-key alt+pfrom bpy import context
import builtins as __builtin__def console_print(*args, **kwargs):for a in context.screen.areas:if a.type == 'CONSOLE':c = {}c['area'] = ac['space_data'] = a.spaces.activec['region'] = a.regions[-1]c['window'] = context.windowc['screen'] = context.screens = " ".join([str(arg) for arg in args])for line in s.split("\n"):bpy.ops.console.scrollback_append(c, text=line)def print(*args, **kwargs):"""Console print() function."""console_print(*args, **kwargs) # to py consoles__builtin__.print(*args, **kwargs) # to system consolethis_file="D:/myprog/2021-10-25-dance/7.show_points/tmp/draw_by_blender.py"os.chdir( os.path.dirname(os.path.realpath(this_file)) )#basic info about limb composition
joint_relationship18 = [[1, 2], [1, 5], [2, 3], [3, 4], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10],[1, 11], [11, 12], [12, 13], [1, 0], [0, 14], [14, 16], [0, 15], [15, 17],[2, 16], [5, 17]]
joint_relationship24 = [[0,1],[1,4],[4,7],[7,10],[0,2],[2,5],[5,8],[8,11],[0,3],[3,6],[6,9],[9,12],[12,15],[13,16],[16,18],[18,20],[20,22],[9,14],[14,17],[17,19],[19,21],[21,23]
]#for plot usage
colors_18 = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0],[85, 255, 0], [0, 255, 0], [0, 255, 85], [0, 255, 170], [0, 255, 255],[0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], [170, 0, 255],[255, 0, 255], [255, 0, 170], [255, 0, 85]]joint_relationship = joint_relationship24j3d=np.load("joint3d.npy")[:100] * 10
bpy.context.scene.render.fps = 60print(j3d.shape)# https://github.com/Apress/blender-python-api/blob/master/ch03_code/3-4.py
# https://blender.stackexchange.com/questions/134826/how-do-you-specify-the-name-of-a-primitive-created-via-the-python-api
# https://danielhnyk.cz/creating-animation-blender-using-python/# Must start in object modeprint( len( bpy.data.objects) )
if len(bpy.data.objects) > 0:#bpy.ops.object.mode_set(mode='OBJECT')bpy.ops.object.select_all(action='SELECT')bpy.ops.object.delete()#bpy.ops.wm.open_mainfile(filepath=bpy.data.filepath)#for obj in bpy.context.scene.objects:
# if obj.type == 'MESH':
# obj.select = True
# else:
# obj.select = False
#bpy.ops.object.delete()#print(111)
for m in bpy.data.materials:bpy.data.materials.remove(m)obj_scale = 0.5
for i in range(24):bpy.ops.mesh.primitive_cube_add(location=(i,i,i), size=obj_scale )bpy.context.active_object.name = f'Joint{i}'object = bpy.data.objects.get( f'Joint{i}' )mat = bpy.data.materials.new(f'Jmat{i}')r, g, b = colors_18[ i%18 ][::-1]mat.diffuse_color = ( r/255, g/255, b/255, 1 )#bpy.data.materials[f'Jmat{i}'].node_tree.nodes["Principled BSDF"].inputs[0].default_value=(r/255, g/255, b/255, 1)object.data.materials.append(mat)# drop
'''
for i, rel in enumerate( joint_relationship ):bpy.ops.mesh.primitive_cylinder_add(radius=1, depth=2, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(obj_scale, obj_scale, obj_scale))bpy.context.active_object.name = f'JR{i}'object = bpy.data.objects.get( f'JR{i}' )mat = bpy.data.materials.new(f'JRmat{i}')r, g, b = colors_18[ i%18 ][::-1]mat.diffuse_color = ( r/255, g/255, b/255, 1 )object.data.materials.append(mat)
''' FPS=60
for rcnt, frame in enumerate( j3d ):frame=frame.reshape(24, 3)for i in range(24):joint = bpy.data.objects.get( f'Joint{i}' )joint.location = frame[i]joint.keyframe_insert(data_path="location", frame=rcnt)#joint = bpy.data.objects.get( f'Joint{0}' )
#bpy.context.scene.objects.active = jointbpy.context.scene.frame_current = 1
bpy.context.scene.frame_end = len(j3d)
效果
第二版,最终版
#from _chj.comm.pic import *
import numpy as np
import os
import bpy
from scipy.spatial.transform import Rotation as sciR
# short-key alt+p
from mathutils import Eulerfrom bpy import context
import builtins as __builtin__def console_print(*args, **kwargs):for a in context.screen.areas:if a.type == 'CONSOLE':c = {}c['area'] = ac['space_data'] = a.spaces.activec['region'] = a.regions[-1]c['window'] = context.windowc['screen'] = context.screens = " ".join([str(arg) for arg in args])for line in s.split("\n"):bpy.ops.console.scrollback_append(c, text=line)def print(*args, **kwargs):"""Console print() function."""console_print(*args, **kwargs) # to py consoles__builtin__.print(*args, **kwargs) # to system consoledef rotation_matrix_from_vectors(vec1, vec2):""" Find the rotation matrix that aligns vec1 to vec2:param vec1: A 3d "source" vector:param vec2: A 3d "destination" vector:return mat: A transform matrix (3x3) which when applied to vec1, aligns it with vec2."""a, b = (vec1 / np.linalg.norm(vec1)).reshape(3), (vec2 / np.linalg.norm(vec2)).reshape(3)v = np.cross(a, b)if any(v): # if not all zeros thenc = np.dot(a, b)s = np.linalg.norm(v)kmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])return np.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2))else:return np.eye(3) # cross of all zeros only occurs on identical directionsthis_file="D:/myprog/2021-10-25-dance/7.show_points/tmp/draw_by_blender.py"os.chdir( os.path.dirname(os.path.realpath(this_file)) )#basic info about limb composition
joint_relationship18 = [[1, 2], [1, 5], [2, 3], [3, 4], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10],[1, 11], [11, 12], [12, 13], [1, 0], [0, 14], [14, 16], [0, 15], [15, 17],[2, 16], [5, 17]]
joint_relationship24 = [[0,1],[1,4],[4,7],[7,10],[0,2],[2,5],[5,8],[8,11],[0,3],[3,6],[6,9],[9,12],[12,15],[9,13],[13,16],[16,18],[18,20],[20,22],[9,14],[14,17],[17,19],[19,21],[21,23]
]#for plot usage
colors_18 = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0],[85, 255, 0], [0, 255, 0], [0, 255, 85], [0, 255, 170], [0, 255, 255],[0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], [170, 0, 255],[255, 0, 255], [255, 0, 170], [255, 0, 85]]joint_relationship = joint_relationship24cord_cvt = sciR.from_rotvec( np.array([1, 0, 0])*np.deg2rad(90) ).as_matrix()
j3d=np.load("joint3d.npy")[1400:1500] * 5
j3d = np.einsum( "cd,bjd->bjc", cord_cvt, j3d.reshape(-1, 24, 3) )
j3d -= j3d[:1, :1].copy()bpy.context.scene.render.fps = 60print(j3d.shape)# https://github.com/Apress/blender-python-api/blob/master/ch03_code/3-4.py
# https://blender.stackexchange.com/questions/134826/how-do-you-specify-the-name-of-a-primitive-created-via-the-python-api
# https://danielhnyk.cz/creating-animation-blender-using-python/# Must start in object modeprint( len( bpy.data.objects) )
if len(bpy.data.objects) > 0:bpy.ops.object.mode_set(mode='OBJECT')bpy.ops.object.select_all(action='SELECT')bpy.ops.object.delete()for m in bpy.data.materials:bpy.data.materials.remove(m)# cam https://blender.stackexchange.com/questions/12318/get-position-of-focus-point-of-camera
cam_data = bpy.data.cameras.new(name="cam")
cam_ob = bpy.data.objects.new(name="Kamerka", object_data=cam_data)
bpy.context.collection.objects.link(cam_ob)cam_ob.data.lens = 15
cam_ob.location = ( 0, -10, 0 )
cam_ob.rotation_euler = Euler( ( 90/180*np.pi, 0, 0 ) , 'XYZ')bpy.context.scene.camera = cam_obobj_scale = 0.15
for i in range(24):#bpy.ops.mesh.primitive_cube_add(location=(i,i,i), size=obj_scale )bpy.ops.mesh.primitive_uv_sphere_add(radius=obj_scale/2, location=(i,i,i))bpy.context.active_object.name = f'Joint{i}'object = bpy.data.objects.get( f'Joint{i}' )mat = bpy.data.materials.new(f'Jmat{i}')r, g, b = colors_18[ i%18 ][::-1]#mat.diffuse_color = ( r/255, g/255, b/255, 1 )mat.diffuse_color = ( 0, 0, 0, 1 )#bpy.data.materials[f'Jmat{i}'].node_tree.nodes["Principled BSDF"].inputs[0].default_value=(r/255, g/255, b/255, 1)object.data.materials.append(mat)first_pos = j3d[0]
for i, rel in enumerate( joint_relationship ):jlen = first_pos[ rel ]depth = np.linalg.norm( jlen[0] - jlen[1] ) / obj_scalebpy.ops.mesh.primitive_cylinder_add(radius=obj_scale, depth=depth, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(obj_scale, obj_scale, obj_scale))bpy.context.active_object.name = f'JR{i}'object = bpy.data.objects.get( f'JR{i}' )object.rotation_mode='XYZ'mat = bpy.data.materials.new(f'JRmat{i}')r, g, b = colors_18[ i%18 ][::-1]mat.diffuse_color = ( r/255, g/255, b/255, 1 )object.data.materials.append(mat)FPS=60
for rcnt, frame in enumerate( j3d ):frame=frame.reshape(24, 3)for i in range(24):joint = bpy.data.objects.get( f'Joint{i}' )joint.location = frame[i]joint.keyframe_insert(data_path="location", frame=rcnt)for i, rel in enumerate( joint_relationship ):object = bpy.data.objects.get( f'JR{i}' )jlen = frame[ rel ]matR = rotation_matrix_from_vectors( np.array([0,0,1]), jlen[1] - jlen[0] )eulr= sciR.from_matrix( matR ).as_euler('xyz', degrees=False)object.rotation_euler = Euler(tuple(eulr.tolist()), 'XYZ')#object.rotation_euler = Euler( ( 30/180*np.pi, 0, 0 ), 'XYZ') # object.keyframe_insert(data_path="rotation_euler", frame=rcnt)object.location = (jlen[0] + jlen[1]) / 2object.keyframe_insert(data_path="location", frame=rcnt)#joint = bpy.data.objects.get( f'Joint{0}' )
#bpy.context.scene.objects.active = jointbpy.context.scene.frame_current = 1
bpy.context.scene.frame_end = len(j3d)
带有背景和光照的
#from _chj.comm.pic import *
import numpy as np
import os
import bpy
from scipy.spatial.transform import Rotation as sciR
# short-key alt+p
from mathutils import Eulerfrom bpy import context
import builtins as __builtin__def console_print(*args, **kwargs):for a in context.screen.areas:if a.type == 'CONSOLE':c = {}c['area'] = ac['space_data'] = a.spaces.activec['region'] = a.regions[-1]c['window'] = context.windowc['screen'] = context.screens = " ".join([str(arg) for arg in args])for line in s.split("\n"):bpy.ops.console.scrollback_append(c, text=line)def print(*args, **kwargs):"""Console print() function."""console_print(*args, **kwargs) # to py consoles__builtin__.print(*args, **kwargs) # to system consoledef rotation_matrix_from_vectors(vec1, vec2):""" Find the rotation matrix that aligns vec1 to vec2:param vec1: A 3d "source" vector:param vec2: A 3d "destination" vector:return mat: A transform matrix (3x3) which when applied to vec1, aligns it with vec2."""a, b = (vec1 / np.linalg.norm(vec1)).reshape(3), (vec2 / np.linalg.norm(vec2)).reshape(3)v = np.cross(a, b)if any(v): # if not all zeros thenc = np.dot(a, b)s = np.linalg.norm(v)kmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])return np.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2))else:return np.eye(3) # cross of all zeros only occurs on identical directionsdef replace_with_emission(node, node_tree):new_node = node_tree.nodes.new('ShaderNodeEmission')connected_sockets_out = []sock = node.inputs[0]if len(sock.links)>0:color_link = sock.links[0].from_socketelse:color_link=Nonedefaults_in = sock.default_value[:]for sock in node.outputs:if len(sock.links)>0:connected_sockets_out.append( sock.links[0].to_socket)else:connected_sockets_out.append(None)#print( defaults_in )new_node.location = (node.location.x, node.location.y)if color_link is not None:node_tree.links.new(new_node.inputs[0], color_link)new_node.inputs[0].default_value = defaults_inif connected_sockets_out[0] is not None:node_tree.links.new(connected_sockets_out[0], new_node.outputs[0])def material_diffuse_to_emission(mat):doomed=[]for node in mat.node_tree.nodes:if node.type=='BSDF_DIFFUSE':replace_with_emission(node, mat.node_tree)doomed.append(node)# wait until we are done iterating and adding before we start wrecking thingsfor node in doomed:mat.node_tree.nodes.remove(node)def get_mat(mat, rgb , type="emission" ):r,g,b = rgbmat.use_nodes = Trueif mat.node_tree:mat.node_tree.links.clear()mat.node_tree.nodes.clear()nodes = mat.node_tree.nodeslinks = mat.node_tree.linksoutput = nodes.new(type='ShaderNodeOutputMaterial')if type == "diffuse":shader = nodes.new(type='ShaderNodeBsdfDiffuse')nodes["Diffuse BSDF"].inputs[0].default_value = (r, g, b, 1)elif type == "emission":shader = nodes.new(type='ShaderNodeEmission')shader.name='Emission'#object.data.materials.append(mat)nodes["Emission"].inputs[0].default_value = (r, g, b, 1)nodes["Emission"].inputs[1].default_value = 1elif type == "glossy":shader = nodes.new(type='ShaderNodeBsdfGlossy')nodes["Glossy BSDF"].inputs[0].default_value = (r, g, b, 1)nodes["Glossy BSDF"].inputs[1].default_value = 0links.new(shader.outputs[0], output.inputs[0])this_file="D:/myprog/2021-10-25-dance/7.show_points/tmp/draw_by_blender.py"os.chdir( os.path.dirname(os.path.realpath(this_file)) )#basic info about limb composition
joint_relationship18 = [[1, 2], [1, 5], [2, 3], [3, 4], [5, 6], [6, 7], [1, 8], [8, 9], [9, 10],[1, 11], [11, 12], [12, 13], [1, 0], [0, 14], [14, 16], [0, 15], [15, 17],[2, 16], [5, 17]]
joint_relationship24 = [[0,1],[1,4],[4,7],[7,10],[0,2],[2,5],[5,8],[8,11],[0,3],[3,6],[6,9],[9,12],[12,15],[9,13],[13,16],[16,18],[18,20],[20,22],[9,14],[14,17],[17,19],[19,21],[21,23]
]#for plot usage
colors_18 = [[255, 0, 0], [255, 85, 0], [255, 170, 0], [255, 255, 0], [170, 255, 0],[85, 255, 0], [0, 255, 0], [0, 255, 85], [0, 255, 170], [0, 255, 255],[0, 170, 255], [0, 85, 255], [0, 0, 255], [85, 0, 255], [170, 0, 255],[255, 0, 255], [255, 0, 170], [255, 0, 85]]joint_relationship = joint_relationship24cord_cvt = sciR.from_rotvec( np.array([1, 0, 0])*np.deg2rad(90) ).as_matrix()
j3d=np.load("joint3d.npy")[1400:1500] * 5
j3d = np.einsum( "cd,bjd->bjc", cord_cvt, j3d.reshape(-1, 24, 3) )
j3d -= j3d[:1, :1].copy()fv="D:/myprog/2021-10-25-dance/7.show_points/tmp/res.mp4"
FPS=60
fv=None
print(j3d.shape)# https://github.com/Apress/blender-python-api/blob/master/ch03_code/3-4.py
# https://blender.stackexchange.com/questions/134826/how-do-you-specify-the-name-of-a-primitive-created-via-the-python-api
# https://danielhnyk.cz/creating-animation-blender-using-python/
# https://blender.stackexchange.com/questions/91108/using-python-to-create-cuboid-with-a-hole-in-the-centre# Must start in object modeprint( len( bpy.data.objects) )
if len(bpy.data.objects) > 0:try:bpy.ops.object.mode_set(mode='OBJECT')except:passbpy.ops.object.select_all(action='SELECT')bpy.ops.object.delete()for m in bpy.data.materials:bpy.data.materials.remove(m)# cam https://blender.stackexchange.com/questions/12318/get-position-of-focus-point-of-camera
cam_data = bpy.data.cameras.new(name="cam")
cam_ob = bpy.data.objects.new(name="Kamerka", object_data=cam_data)
bpy.context.collection.objects.link(cam_ob)cam_ob.data.lens = 15
cam_ob.location = ( 0, -10, 0 )
cam_ob.rotation_euler = Euler( ( 90/180*np.pi, 0, 0 ) , 'XYZ')bpy.context.scene.camera = cam_ob##### light
bpy.ops.object.light_add(type='AREA', radius=10, location=(0, -30, 20), rotation=(60/180*np.pi, 0, 0))
light_ob = bpy.context.object
light_ob.name = 'light_area'
light_ob = bpy.data.objects['light_area']
light_ob.data.energy = 500
#bpy.context.active_object.name = 'light_area' # both ok#### background
bpy.ops.mesh.primitive_plane_add(size=100, location=(0, 30, 0), rotation=(np.pi/2, 0,0))
bpy.context.active_object.name = 'background'
object = bpy.data.objects.get( 'background' )
mat = bpy.data.materials.new(f'back_mat')
#mat.diffuse_color = ( 1, 1, 1, 1 ) # buliang2
get_mat(mat, (0.85,0.85,0.85))
object.data.materials.append(mat)obj_scale = 0.2
for i in range(24):#bpy.ops.mesh.primitive_cube_add(location=(i,i,i), size=obj_scale )bpy.ops.mesh.primitive_uv_sphere_add(radius=obj_scale/2, location=(i,i,i))bpy.context.active_object.name = f'Joint{i}'object = bpy.data.objects.get( f'Joint{i}' )mat = bpy.data.materials.new(f'Jmat{i}')r, g, b = colors_18[ i%18 ][::-1]#mat.diffuse_color = ( r/255, g/255, b/255, 1 )mat.diffuse_color = ( 0, 0, 0, 1 )#bpy.data.materials[f'Jmat{i}'].node_tree.nodes["Principled BSDF"].inputs[0].default_value=(r/255, g/255, b/255, 1)object.data.materials.append(mat)first_pos = j3d[0]
for i, rel in enumerate( joint_relationship ):jlen = first_pos[ rel ]depth = np.linalg.norm( jlen[0] - jlen[1] ) / obj_scalebpy.ops.mesh.primitive_cylinder_add(radius=obj_scale, depth=depth, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(obj_scale, obj_scale, obj_scale))bpy.context.active_object.name = f'JR{i}'object = bpy.data.objects.get( f'JR{i}' )object.rotation_mode='XYZ'mat = bpy.data.materials.new(f'JRmat{i}')r, g, b = colors_18[ i%18 ][::-1]r, g, b = r/255, g/255, b/255#mat.diffuse_color = ( r, g, b, 1 )get_mat(mat, (r,g,b))#breakobject.data.materials.append(mat)FPS=60
for rcnt, frame in enumerate( j3d ):frame=frame.reshape(24, 3)for i in range(24):joint = bpy.data.objects.get( f'Joint{i}' )joint.location = frame[i]joint.keyframe_insert(data_path="location", frame=rcnt)for i, rel in enumerate( joint_relationship ):object = bpy.data.objects.get( f'JR{i}' )jlen = frame[ rel ]matR = rotation_matrix_from_vectors( np.array([0,0,1]), jlen[1] - jlen[0] )eulr= sciR.from_matrix( matR ).as_euler('xyz', degrees=False)object.rotation_euler = Euler(tuple(eulr.tolist()), 'XYZ')#object.rotation_euler = Euler( ( 30/180*np.pi, 0, 0 ), 'XYZ') # object.keyframe_insert(data_path="rotation_euler", frame=rcnt)object.location = (jlen[0] + jlen[1]) / 2object.keyframe_insert(data_path="location", frame=rcnt)#joint = bpy.data.objects.get( f'Joint{0}' )
#bpy.context.scene.objects.active = jointbpy.context.scene.frame_current = 1
bpy.context.scene.frame_end = len(j3d)def set_render_for_video(fv, fps=30, w=640, h=360 ):r = bpy.context.scene.render#r.alpha_mode = 'SKY'r.filepath = fv#scene.render.image_settings.file_format = 'PNG' # set output format to .pngr.image_settings.file_format = 'FFMPEG' # set output format to .png#r.ffmpeg.format = 'MPEG4'r.fps = fpsr.resolution_x = wr.resolution_y = hr.ffmpeg.format = 'AVI'r.ffmpeg.audio_codec = 'MP3'#bpy.ops.screen.animation_play()bpy.ops.render.render(animation=True)if fv:set_render_for_video(fv, fps=FPS, w=640, h=360 )print("render finished")
思路二: 创建bvh 文件
这个主要是骨骼朝向不好看, 感觉对 bvh 不了解,下面的write_bvh_v1 和 write_bvh 都没有太大区别。 也不清楚 bvh 为什么有 channel position X Y Z
from _chj.comm.pic import *
os.chdir( os.path.dirname(os.path.realpath(__file__)) )def main():#f2_save_offset()f1_make_bvh()def f2_save_offset():fbones="../../4.unity/debug_render/Assets/data/bones2smpl_official.txt"#ftemp="../../3.hd_lyg/5.baiye/data/smpl.bvh.template"ftemp="temp1.bvh"mp={}arr=readlines(fbones)#print(arr)flag=0nm=Nonefor line in readlines(ftemp):e = line.split()if flag==0 and len(e) == 2 and e[0] in ['JOINT', 'ROOT']:flag = 1nm=e[1]if e[0]=='ROOT':nm='m_avg_root'elif flag==1 and len(e)==4 and e[0]=='OFFSET':flag=0mp[ nm ] = [ float(x) for x in e[1:] ]print(mp)offsets = []for e in arr:offsets.append( mp[e] )arr=np.array(offsets).astype(np.float32) / 10np.save("smpl_offsets.npy", arr)def f1_make_bvh():fpids="parents.txt"fjoint="joint3d.npy"fout="a.bvh"foffsets="smpl_offsets.npy"pids=np.loadtxt(fpids, delimiter=',')offsets=np.load(foffsets).astype(str).tolist()print( pids )mp={}for i, pid in enumerate(pids):mp[i] = Node(i)mp[i].offset = offsets[i]if pid==-1: assert i==0else:pnode = mp[ pid ]pnode.arr.append( mp[i] )j3d = np.load(fjoint).reshape(-1, 24, 3)FPS=60j3d[:, :3] = 0N = len(j3d)with open(fout, "w") as fp:fp.write('HIERARCHY\n')write_joint_V1(fp, mp, 0, 0)fp.write(f"MOTION\nFrames: {N}\nFrame Time: {1/FPS:.8f}\n")for j in j3d:a= " ".join(j.reshape(-1).astype(str).tolist())fp.write(a+"\n")def write_joint(fp, mp, id, layer):if id == 0:blank = ' '*2fp.write(f"ROOT J{id}\n")fp.write("{\n")#OFFSET 5.19116e-05 8.61242 -0.246368# CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotationnode = mp[id]fp.write(blank+f"OFFSET {' '.join(node.offset)}\n")fp.write(blank+f"CHANNELS 3 Xposition Yposition Zposition\n")fp.write("}\n")for e in node.arr:write_joint(fp, mp, e.val, layer)else:blank = ' '*2*layerfp.write(blank+f"JOINT J{id}\n")fp.write(blank+"{\n")#OFFSET 5.19116e-05 8.61242 -0.246368# CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotationnode = mp[id]fp.write(blank+f"OFFSET {' '.join(node.offset)}\n")fp.write(blank+' '+f"CHANNELS 3 Xposition Yposition Zposition\n")fp.write(blank+"}\n")for e in node.arr:write_joint(fp, mp, e.val, layer)return 0def write_joint_V1(fp, mp, id, layer):if id == 0:blank = ' '*2fp.write(f"ROOT J{id}\n")fp.write("{\n")#OFFSET 5.19116e-05 8.61242 -0.246368# CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotationnode = mp[id]fp.write(blank+f"OFFSET {' '.join(node.offset)}\n")fp.write(blank+f"CHANNELS 3 Xposition Yposition Zposition\n")for e in node.arr:write_joint(fp, mp, e.val, layer+1)fp.write("}\n")else:blank = ' '*2*layerfp.write(blank+f"JOINT J{id}\n")fp.write(blank+"{\n")#OFFSET 5.19116e-05 8.61242 -0.246368# CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotationnode = mp[id]fp.write(blank+f"OFFSET {' '.join(node.offset)}\n")fp.write(blank+' '+f"CHANNELS 3 Xposition Yposition Zposition\n")for e in node.arr:write_joint(fp, mp, e.val, layer+1)fp.write(blank+"}\n")return 0class Node:def __init__(self, name):self.val = nameself.arr=[]if __name__=="__main__": main()
思路三,骨骼驱动
进入编辑模式,然后 alt+p
取消父子关系。
import sys, os, mathimport numpy as np
import subprocess
from scipy.spatial.transform import Rotation as sciR
from mathutils import Quaternion, Vector, Matrixfrom _chj.comm.pic import *
try:import bpy
except:passfrom bpy import context
import builtins as __builtin__def console_print(*args, **kwargs):for a in context.screen.areas:if a.type == 'CONSOLE':c = {}c['area'] = ac['space_data'] = a.spaces.activec['region'] = a.regions[-1]c['window'] = context.windowc['screen'] = context.screens = " ".join([str(arg) for arg in args])for line in s.split("\n"):bpy.ops.console.scrollback_append(c, text=line)def print(*args, **kwargs):"""Console print() function."""console_print(*args, **kwargs) # to py consoles__builtin__.print(*args, **kwargs) # to system consolethis_file="D:/myprog/2021-10-25-dance/7.show_points/tmp/draw_by_blender.py"
os.chdir( os.path.dirname(os.path.realpath(this_file)) )def main():f2_render_bones_video()passclass params: FPS=60stride=1n_frame=1200 # 负数就是全部width=800 #* 3height=450 #*3ob_scale=None #0.1 #11 #Nonet_pose_scale=100kf_step = 1 #5passj3d=np.load("joint3d.npy").reshape(-1, 24, 3)[1400:1500] #* params.t_pose_scale
j3d -= j3d[:1, :1].copy()#j3d[:, :] = 0
#j3d[:] = 0.01fsmpl_rig_info="bones2smpl_official_v2.txt"
fparents="parents.txt"fbx_ske_nm = "Armature" # smpl_skeleton# https://github.com/boycehbz/HumanRegistration#
# https://blenderartists.org/t/using-bone-translate-to-move-a-bone/511333/14def f2_render_bones_video():cord_cvt = sciR.from_rotvec( np.array([1, 0, 0])*np.deg2rad(90) ).as_matrix()pms = np.einsum( "cd,bjd->bjc", cord_cvt, j3d.reshape(-1, 24, 3) )params.pms = pmsparams.parents = np.loadtxt( fparents, delimiter="," ).astype(np.int32)params.NJ = 24del_project()bpy.ops.import_scene.fbx( filepath = 'official_smpl.fbx' )bpy.context.scene.render.fps = 60bpy.context.scene.frame_current = 1bpy.context.scene.frame_end = len(j3d)#bpy.ops.wm.save_mainfile( filepath = fblend ); exit()#bpy.ops.object.select_all(action='DESELECT')bpy.context.view_layer.objects.active = bpy.data.objects['f_avg']bpy.data.objects['f_avg'].select_set(False)#bpy.ops.object.mode_set(mode='OBJECT')ob = bpy.data.objects[fbx_ske_nm]ob.select_set(True)bpy.context.view_layer.objects.active = ob#a = ob.pose.bones['m_avg_root']#a.bone.select = True#bpy.context.object.data.bones.active = a#ob.data.bones['m_avg_root'].select = True#bpy.context.object.hide_viewport = True#bpy.ops.object.hide_view_set(unselected=False)bpy.context.view_layer.objects.active = obbpy.ops.object.mode_set(mode='EDIT')bpy.ops.armature.parent_clear(type='CLEAR')#bpy.ops.object.hide_view_set() bpy.app.handlers.frame_change_pre.clear()#bpy.app.handlers.frame_change_pre.append(frame_change_pre)bpy.context.view_layer.objects.active = ob #相当于鼠标左键选中bpy.ops.object.mode_set(mode='POSE') #切换为pose更改模式arr = [] bones = []arr_nm_pid = readlines( fsmpl_rig_info )for i in range( len(arr_nm_pid) ):bnm = arr_nm_pid[ i ]bone=ob.pose.bones[bnm] bone.rotation_mode = 'AXIS_ANGLE' #'XYZ'bones.append( bone )arr.append( bone.location.copy() )arr.append( bone.rotation_axis_angle ) # tuple arr#print( [ e[:] for e in arr ] )params.T_pose = arrparams.bones = bonesset_key_frame( len(j3d) )passdef set_key_frame(nframe):NJ = params.NJbones = params.bonesfor rcnt in range(1, nframe+1, params.kf_step ):pm = params.pms[rcnt-1]for i in range(NJ-1, -1, -1):bones[i].location = params.T_pose[i*2]#a = 3+i*4#bones[i].rotation_axis_angle = tuple(pm[a:a+4].tolist())#bones[i].keyframe_insert(data_path="rotation_axis_angle", frame=rcnt)a = pm[i]#bones[i].matrix_world.translation = Vector((a[0], a[1], a[2]))bones[i].bone.select = Truebpy.ops.transform.translate(value=tuple(a.tolist()), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)bones[i].keyframe_insert(data_path="location", frame=rcnt)rcnt+=1bpy.context.scene.frame_current = 1def del_project():if len(bpy.data.objects):bpy.ops.object.mode_set(mode='OBJECT')#bpy.ops.object.select_all(action='SELECT')#bpy.ops.object.delete()for e in bpy.data.objects:print(e)e.select_set(True)bpy.ops.object.delete() for block in bpy.data.meshes:if block.users == 0:bpy.data.meshes.remove(block)for block in bpy.data.materials:if block.users == 0:bpy.data.materials.remove(block)for block in bpy.data.textures:if block.users == 0:bpy.data.textures.remove(block)for block in bpy.data.images:if block.users == 0:bpy.data.images.remove(block)if __name__=="__main__": main()