简体   繁体   中英

In which order are Biovision Hierarchy transforms applied?

I'm trying to display Biovision Hierarchy mocap file with pygame. For now, in the orthographic projection, the perspective would be z division. I seem to have bones imported, but I cannot choose the right axes even in still armature (XY and XZ are not working).

The most funny thing happens when I'm trying to apply the animation. It looks like a contortion of the android from Blade Runner.

Show me how to fix it. I'm using the 3dsMax version of CMU mocap.

Here's my code:

import numpy as np

class Cell(list):
    def getname(self):
        if hasattr(self, "name"):
            return self.name
        return ""
    def setname(self, name):
        self.name=name
    def __repr__(self):
        if hasattr(self, "name"):
            if self:
                return self.name+" "+list.__repr__(self)
            return self.name
        return list.__repr__(self)

class Node(object):
    def __init__(self, name, parent, offset, channels):
        self.name=name
        self.parent=parent
        self.children=[]
        self.offset=offset
        self.channels=channels
    def add_child(self, child):
        self.children.append(child)
    def get_bones(self):
        bones=[]
        if self.parent is not None:
            bones.append((self.parent.abspos(),self.abspos()))
        for child in self.children:
            bones.extend(child.get_bones())
        return bones
    def abspos(self):
        if self.parent is None:
            return np.dot(self.get_matrix(),np.hstack((self.offset, 1)))[:3]#also need rotation
        return self.parent.abspos()+np.dot(self.get_matrix(),np.hstack((self.offset, 1)))[:3]#also need rotation
    def __repr__(self):
        return "<"+self.name+" ["+", ".join([repr(child) for child in self.children])+"]>"
    def feed_transform(self, transform):
        obj=transform
        matrix=np.zeros((4,4))
        matrix[0,0]=1
        matrix[1,1]=1
        matrix[2,2]=1
        matrix[3,3]=1
        for channel in self.channels:
            if channel=="Xposition":
                mod=np.zeros((4,4))
                mod[0,0]=1
                mod[1,1]=1
                mod[2,2]=1
                mod[3,3]=1
                mod[3,0]=obj[0]
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
            elif channel=="Yposition":
                mod=np.zeros((4,4))
                mod[0,0]=1
                mod[1,1]=1
                mod[2,2]=1
                mod[3,3]=1
                mod[3,1]=obj[0]
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
            elif channel=="Zposition":
                mod=np.zeros((4,4))
                mod[0,0]=1
                mod[1,1]=1
                mod[2,2]=1
                mod[3,3]=1
                mod[3,2]=obj[0]
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
            elif channel=="Xrotation":
                mod=np.zeros((4,4))
                mod[0,0]=1
                mod[1,1]=np.cos(obj[0])
                mod[2,2]=np.cos(obj[0])
                mod[1,2]=np.sin(obj[0])
                mod[2,1]=-np.sin(obj[0])
                mod[3,3]=1
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
            elif channel=="Yrotation":
                mod=np.zeros((4,4))
                mod[1,1]=1
                mod[0,0]=np.cos(obj[0])
                mod[2,2]=np.cos(obj[0])
                mod[2,0]=np.sin(obj[0])
                mod[0,2]=-np.sin(obj[0])
                mod[3,3]=1
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
            elif channel=="Zrotation":
                mod=np.zeros((4,4))
                mod[2,2]=1
                mod[0,0]=np.cos(obj[0])
                mod[1,1]=np.cos(obj[0])
                mod[0,1]=np.sin(obj[0])
                mod[1,0]=-np.sin(obj[0])
                mod[3,3]=1
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
        self.matrix=matrix
        for child in self.children:
            obj=child.feed_transform(obj)
        return obj
    def get_matrix(self):
        if self.parent is None:
            return self.matrix
        return np.dot(self.matrix,self.parent.get_matrix())
def parse(obj, parent=None):
    if obj is None:
        return None
    tp, nm=obj.getname().split(" ")
    offset=None
    channels=None
    joints=[]
    if tp not in ("ROOT", "JOINT"):
        print("warning", tp)
    for line in obj:
        if line.getname().startswith("OFFSET"):
            offset=np.array([float(x) for x in line.getname().split(" ")[1:4]])
        elif line.getname().startswith("CHANNELS"):
            channels=tuple(line.getname().split(" ")[2:])
        elif line.getname().startswith("JOINT"):
            joints.append(line)
    res=Node(nm, parent, offset, channels)
    for joint in joints:
        res.add_child(parse(joint, parent=res))
    return res

tree=Cell()

depth=0
with open("/media/stuntj/ESD-ISO/cmu/01/01_01.bvh","r") as f:
    for line in f:
        ls=line.strip(" \t\r\n")
        obj=tree
        for i in range(depth):
            obj=obj[-1]
        if not ls:
            pass
        elif ls=="{":
            depth+=1
        elif ls=="}":
            depth-=1
        else:
            obj.append(Cell())
            obj[-1].setname(ls)

#print(tree)
hier=False
moti=False
parsed=[]
frames=[]
fps=30
for line in tree:
    if line.getname() == "HIERARCHY":
        hier=True
        moti=False
    elif line.getname() == "MOTION":
        hier=False
        moti=True
    elif hier:
        tp, nm=line.getname().split(" ")
        if tp == "ROOT":
            parsed.append(parse(line))
    elif moti:
        if line.getname().startswith("Frame Time:"):
            fps=int(1.0/float(line.getname()[11:].strip(" \t\r\n"))+.5)
        elif line.getname()[0:1] in "-.0123456789":
            frames.append([float(x) for x in line.getname().split(" ")])
#print(parsed)
#print(frames)
#print(fps)
#print(parsed[0].get_bones())import pygame, sys
import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((800, 800))
pygame.display.set_caption('Yet Another BVH Lexer')
fpsClock = pygame.time.Clock()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
for frame in frames:
    obj=frame
    for ps in parsed:
        obj=ps.feed_transform(obj)
        DISPLAYSURF.fill(WHITE)
        for bone in ps.get_bones():
            pygame.draw.line(DISPLAYSURF, BLACK, (bone[0][0]+400,bone[0][2]+400), (bone[1][0]*8+400,bone[1][2]*-8+400),1)
        pygame.display.update()
    if obj:
        print("Malformed data")
    fpsClock.tick(fps)
pygame.quit()
sys.exit()

It's fixed. The key problem was rotation in BVH was specified in degrees, not the radians. Then some brute forse on the matrices, and it's done.

import numpy as np

class Node(object):
    def __init__(self, name, parent, offset, channels):
        self.name=name
        self.parent=parent
        self.children=[]
        self.offset=offset
        self.channels=channels
        matrix=np.zeros((4,4))
        matrix[0,0]=1
        matrix[1,1]=1
        matrix[2,2]=1
        matrix[3,3]=1
        self.matrix=matrix
    def add_child(self, child):
        self.children.append(child)
    def get_bones(self):
        bones=[]
        if self.parent is not None and self.parent.parent is not None:
            bones.append((self.parent.abspos(),self.abspos()))
        for child in self.children:
            bones.extend(child.get_bones())
        return bones
    def abspos(self):
        if self.parent is None:
            return np.dot(self.get_matrix(),np.hstack((self.offset, 1)))[:3]
        return self.parent.abspos()+np.dot(self.get_matrix(),np.hstack((self.offset, 1)))[:3]+self.parent.trans
    def __repr__(self):
        return "<"+self.name+" ["+", ".join([repr(child) for child in self.children])+"]>"
    def feed_transform(self, transform):
        obj=transform
        trans=np.zeros(3)
        matrix=np.zeros((4,4))
        matrix[0,0]=1
        matrix[1,1]=1
        matrix[2,2]=1
        matrix[3,3]=1
        for channel in self.channels:
            if channel=="Xposition":
                trans[0]+=obj[0]
                obj=obj[1:]
            elif channel=="Yposition":
                trans[1]+=obj[0]
                obj=obj[1:]
            elif channel=="Zposition":
                trans[2]+=obj[0]
                obj=obj[1:]
            elif channel=="Xrotation":
                rad=obj[0]*(np.pi/180.0)
                mod=np.zeros((4,4))
                mod[0,0]=1
                mod[1,1]=np.cos(rad)
                mod[2,2]=np.cos(rad)
                mod[2,1]=np.sin(rad)
                mod[1,2]=-np.sin(rad)
                mod[3,3]=1
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
            elif channel=="Yrotation":
                rad=obj[0]*(np.pi/180.0)
                mod=np.zeros((4,4))
                mod[1,1]=1
                mod[0,0]=np.cos(rad)
                mod[2,2]=np.cos(rad)
                mod[0,2]=np.sin(rad)
                mod[2,0]=-np.sin(rad)
                mod[3,3]=1
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
            elif channel=="Zrotation":
                rad=obj[0]*(np.pi/180.0)
                mod=np.zeros((4,4))
                mod[2,2]=1
                mod[0,0]=np.cos(rad)
                mod[1,1]=np.cos(rad)
                mod[0,1]=-np.sin(rad)
                mod[1,0]=np.sin(rad)
                mod[3,3]=1
                obj=obj[1:]
                matrix=np.dot(matrix, mod)
        self.trans=trans
        self.matrix=matrix
        for child in self.children:
            obj=child.feed_transform(obj)
        return obj
    def get_matrix(self):
        if self.parent is None:
            matrix=np.zeros((4,4))
            matrix[0,0]=1
            matrix[1,1]=1
            matrix[2,2]=1
            matrix[3,3]=1
            return matrix
        return np.dot(self.parent.get_matrix(), self.parent.matrix)
def parse(obj, parent=None):
    if obj[0]=="End Site":
        tp="End Site"
        nm=""
    else:
        tp, nm=obj[0].split(" ")
    offset=None
    channels=tuple()
    joints=[]
    if tp not in ("ROOT", "JOINT", "End Site"):
        print("warning", tp)
    for line in obj[1:]:
        if line[0].startswith("OFFSET"):
            offset=np.array([float(x) for x in line[0].split(" ")[1:4]])
        elif line[0].startswith("CHANNELS"):
            channels=tuple(line[0].split(" ")[2:])
        elif line[0].startswith("JOINT"):
            joints.append(line)
        elif line[0].startswith("End Site"):
            joints.append(line)
    res=Node(nm, parent, offset, channels)
    for joint in joints:
        res.add_child(parse(joint, parent=res))
    return res

tree=[""]

depth=0
with open("/media/stuntj/ESD-ISO/cmu/01/01_01.bvh","r") as f:
    for line in f:
        ls=line.strip(" \t\r\n")
        obj=tree
        for i in range(depth):
            obj=obj[-1]
        if not ls:
            pass
        elif ls=="{":
            depth+=1
        elif ls=="}":
            depth-=1
        else:
            obj.append([ls])

hier=False
moti=False
parsed=[]
frames=[]
fps=30
for line in tree[1:]:
    if line[0] == "HIERARCHY":
        hier=True
        moti=False
    elif line[0] == "MOTION":
        hier=False
        moti=True
    elif hier:
        tp, nm=line[0].split(" ")
        if tp == "ROOT":
            parsed.append(parse(line))
    elif moti:
        if line[0].startswith("Frame Time:"):
            fps=int(1.0/float(line[0][11:].strip(" \t\r\n"))+.5)
        elif line[0][0:1] in "-.0123456789":
            frames.append([float(x) for x in line[0].split(" ")])

import pygame, sys
from pygame.locals import *
pygame.init()
DISPLAYSURF = pygame.display.set_mode((800, 800))
pygame.display.set_caption('Yet Another BVH Lexer')
fpsClock = pygame.time.Clock()
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
for frame in frames:
    obj=frame
    for ps in parsed:
        obj=ps.feed_transform(obj)
        DISPLAYSURF.fill(WHITE)
        for bone in ps.get_bones():
            if bone[0][2]<-.1 and bone[1][2]<-.1:
                pygame.draw.line(DISPLAYSURF, BLACK, (bone[0][0]/bone[0][2]*-64+400,bone[0][1]/bone[0][2]*64+400), (bone[1][0]/bone[1][2]*-64+400,bone[1][1]/bone[1][2]*64+400),1)
        pygame.display.update()
    if obj:
        print("Malformed data")
    fpsClock.tick(fps)
pygame.quit()
sys.exit()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM