blender 绘制离散顶点, SMPL骨架绘制

给定一些点,如何绘制出来,借助 blender 看下效果。纠结于 unity 还是 blender, 最终还是 blender 了。

目前还都不太满意,思路一比较靠谱,但是需要更复杂的计算 思路一,第二版,已完成,吐血制作!

blender 一些快捷键

alt+p 运行脚本
h , alt+h 隐藏与显示

shift+c , / 如果找不到模型,可以通过这两个快捷键,或者 视图->局部视图 来选择。
f3 可以用于查找命令

1 3 7 三视图

blender 急救箱

思路一: 绘制顶点,和边

就是用物体代表顶点和边,并添加各自的材质。 动作通过设置关键帧,其中顶点比较好办就设置一个位置就行,边的话还需要计算旋转,就是旋转到点之间的向量

第一版,只画了点

import numpy as np
import os
import bpy

# short-key alt+p

from 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'] = a
            c['space_data'] = a.spaces.active
            c['region'] = a.regions[-1]
            c['window'] = context.window
            c['screen'] = context.screen
            s = " ".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 console


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],
    [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_relationship24

j3d=np.load("joint3d.npy")[:100] * 10
bpy.context.scene.render.fps = 60

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/


# Must start in object mode

print( 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 = joint


bpy.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 Euler

from 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'] = a
            c['space_data'] = a.spaces.active
            c['region'] = a.regions[-1]
            c['window'] = context.window
            c['screen'] = context.screen
            s = " ".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 console


def 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 then
        c = 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 directions

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_relationship24

cord_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 = 60

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/


# Must start in object mode

print( 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_ob

obj_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_scale
    bpy.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]) / 2
        object.keyframe_insert(data_path="location", frame=rcnt)

#joint = bpy.data.objects.get( f'Joint{0}' )
#bpy.context.scene.objects.active = joint


bpy.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 Euler

from 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'] = a
            c['space_data'] = a.spaces.active
            c['region'] = a.regions[-1]
            c['window'] = context.window
            c['screen'] = context.screen
            s = " ".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 console


def 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 then
        c = 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 directions



def 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_socket
    else:
        color_link=None
    defaults_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_in

    if 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 things
    for node in doomed:
        mat.node_tree.nodes.remove(node)

def get_mat(mat, rgb , type="emission" ):
    r,g,b = rgb
    mat.use_nodes = True
    if mat.node_tree:
        mat.node_tree.links.clear()
        mat.node_tree.nodes.clear()
    
    nodes = mat.node_tree.nodes
    links = mat.node_tree.links
    output = 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 = 1

    elif 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 = 0

    links.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_relationship24

cord_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 mode

print( len( bpy.data.objects) )
if len(bpy.data.objects) > 0:
    try:
        bpy.ops.object.mode_set(mode='OBJECT')
    except:
        pass
    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_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_scale
    bpy.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))
    #break
    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]) / 2
        object.keyframe_insert(data_path="location", frame=rcnt)

#joint = bpy.data.objects.get( f'Joint{0}' )
#bpy.context.scene.objects.active = joint


bpy.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 .png
    r.image_settings.file_format = 'FFMPEG' # set output format to .png
    #r.ffmpeg.format = 'MPEG4'
    r.fps = fps
    r.resolution_x = w
    r.resolution_y = h
    r.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=0
    nm=None
    for line in  readlines(ftemp):
        e = line.split()
        if flag==0 and len(e) == 2 and e[0] in ['JOINT', 'ROOT']:
            flag = 1
            nm=e[1]
            if e[0]=='ROOT':
                nm='m_avg_root'
        elif flag==1 and len(e)==4 and e[0]=='OFFSET':
            flag=0
            mp[ 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) / 10
    np.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==0
        else:
            pnode = mp[ pid ]
            pnode.arr.append( mp[i] )
            
    j3d = np.load(fjoint).reshape(-1, 24, 3)
    FPS=60
    j3d[:, :3] = 0
    N = 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 = ' '*2
        fp.write(f"ROOT J{id}\n")
        fp.write("{\n")
        #OFFSET 5.19116e-05 8.61242 -0.246368
        #    CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
        node = 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*layer
        fp.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 Yrotation
        node = 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 0

def write_joint_V1(fp, mp, id, layer):
    if id == 0:
        blank = ' '*2
        fp.write(f"ROOT J{id}\n")
        fp.write("{\n")
        #OFFSET 5.19116e-05 8.61242 -0.246368
        #    CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
        node = 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*layer
        fp.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 Yrotation
        node = 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 0

class Node:
    def __init__(self, name):
        self.val = name
        self.arr=[]

if __name__=="__main__": main()

在这里插入图片描述

思路三,骨骼驱动

进入编辑模式,然后 alt+p 取消父子关系。

import sys, os, math

import numpy as np
import subprocess
from scipy.spatial.transform import Rotation as sciR
from mathutils import Quaternion, Vector, Matrix

from _chj.comm.pic import *
try:
    import bpy
except:
    pass

from 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'] = a
            c['space_data'] = a.spaces.active
            c['region'] = a.regions[-1]
            c['window'] = context.window
            c['screen'] = context.screen
            s = " ".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 console



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)) )



def main():
    f2_render_bones_video()
    pass

class params: 
    FPS=60
    stride=1
    n_frame=1200 # 负数就是全部
    width=800 #* 3
    height=450 #*3
    ob_scale=None #0.1 #11 #None
    t_pose_scale=100
    kf_step = 1 #5
    pass


j3d=np.load("joint3d.npy").reshape(-1, 24, 3)[1400:1500] #* params.t_pose_scale
j3d -= j3d[:1, :1].copy()

#j3d[:, :] = 0
#j3d[:] = 0.01




fsmpl_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/14

def 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 = pms
    params.parents = np.loadtxt( fparents, delimiter="," ).astype(np.int32)
    params.NJ = 24
    

    del_project()
    bpy.ops.import_scene.fbx( filepath = 'official_smpl.fbx' )


    bpy.context.scene.render.fps = 60
    bpy.context.scene.frame_current = 1
    bpy.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 = ob
    
    
    bpy.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 = arr
    params.bones = bones
    
    set_key_frame( len(j3d) )
    pass


def set_key_frame(nframe):
    NJ = params.NJ
    bones = params.bones
    for 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 = True
            bpy.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+=1
    bpy.context.scene.frame_current = 1


def 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()

在这里插入图片描述

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值