Help: loading motion capture files to blender 2.42

Hi all,

I’m trying to load some motion captured files (BVH) into blender 2.42. I’ve got some python code that can construct an Armature object to match the skeleton model of the BVH file, but I’m having trouble generating the actual animation.

The BVH file contains a list of joint values for each joint of the Armature, naturally what I want to do is make each Bone of the Armature go through its corresponding sequence of joint values. I used to be able to sort of do this with the 2.3x version of Blender, but I cannot do this with the current version of Blender.

I’ve read some of the threads on this forum about the use of the Pose/PoseBone module, but they don’t seem to work. For example, I’ve got the following code to generate a 3-segment Armature:


import Blender
import Blender.Armature
import Blender.Mathutils
import Blender.Scene
import Blender.Object
import Blender.Object.Pose
import sys

arm = Blender.Armature.Armature()
arm.makeEditable()

print >> sys.stderr, 'arm =', arm

eb1 = Blender.Armature.Editbone()
eb1.head = Blender.Mathutils.Vector(0, 0, 0)
eb1.tail = Blender.Mathutils.Vector(1, 0, 0)

eb2 = Blender.Armature.Editbone()
eb2.head = Blender.Mathutils.Vector(1, 0, 0)
eb2.tail = Blender.Mathutils.Vector(2, 0, 0)

eb3 = Blender.Armature.Editbone()
eb3.head = Blender.Mathutils.Vector(2, 0, 0)
eb3.tail = Blender.Mathutils.Vector(3, 0, 0)

arm.bones['bottom'] = eb1
arm.bones['middle'] = eb2
arm.bones['top'] = eb3
eb2.parent = eb1
eb3.parent = eb2

scn = Blender.Scene.getCurrent()
armObj = Blender.Object.New('Armature')
armObj.link(arm)
arm.update()

scn.link(armObj)
scn.update()

This code creates 3 bones: bottom->middle->top

Then I have the following code to change the rotation of the top bone:


import Blender
import Blender.Scene
import Blender.Mathutils

scn = Blender.Scene.GetCurrent()
	
armObj = scn.getChildren()[0]

print armObj, dir(armObj), armObj.getData()

arm = armObj.getData()
arm.makeEditable()
print dir(arm)

pos = armObj.getPose()

topBonePos = pos.bones['top']

print
print 'pose matrix'
print topBonePos.poseMatrix
print
print 'local matrix'
print topBonePos.localMatrix
print
print 'local matrix rotation' 
print topBonePos.localMatrix.rotationPart()


topBonePos.localMatrix[0][0] = -0.5
#topBonePos.tail = Blender.Mathutils.Vector(5, 5, 5)
print
print 'after the change, the local matrix' 
print topBonePos.localMatrix
print
print 'local matrix rotation' 
print topBonePos.localMatrix.rotationPart()


pos.update()
arm.update()
scn.update()

print 'after the updates, the local matrix' 
print topBonePos.localMatrix
print
print 'local matrix rotation' 
print topBonePos.localMatrix.rotationPart()

It seems that I can change the local matrix and the rotation part of the local matrix – however, whatever change I make gets lost as soon as I update the pos object.

Could someone please tell me what I’m doing wrong? Is what I’m trying to do actually possible? If so, how can I do it?

By the way, the NLA part of the python API for Blender 2.4.2 seems to be the same as the 2.3x version.

Any help is greatly appreciated.

Thanks a lot.

-D

Try 2.43rc1, it has a working BVH importer,

wow, thanks a lot! So, how do I get 2.43rc1? :confused:

Thanks for that … hey, it seems that I can change the Quat value of the PoseBone and have the change shown on the screen … I guess I can use that for now if I have to, but I’m having trouble with the insertKey function of PoseBone – this function requires the type of the Pose, the documentataion says the three accepted types are:

type - Can be any combination of 3 Module constants:
Pose.LOC
Pose.ROT
Pose.QUAT

But there is no Pose.QUAT in the Pose module … there is only Pose.LOC, Pose.ROT and Pose.SIZE … how do I get Pose.QUAT?

Thanks a lot in advance.

-D

Sorry, but 2.43rc’s BVH importer doesn’t seem to work, here is the screenshot of its Armature model for a walking person:

http://img440.imageshack.us/img440/3875/errorfr5.jpg

As you can see, it’s not possible to tell what’s going on there.

The correct skeleton model should look like this:

http://img141.imageshack.us/img141/1437/goodgj8.jpg

Edit: I’ve just done a bit more mucking around and here’s what I found:

(1) If I just change the Quat using Pose.ROT, I get some animation of the joints – but the movements of the joints are obviously wrong.

(2) If I get the rotation matrix using the Quat and overwrite the localMatrix of the PoseBone – I get no animation at all.

(3) If I don’t use either Quat or the rotation matrix, and only use the location (PoseBone.loc), then the location animation seems to work.

(4) If I combine either Quat or the rotation matrix with the location change – the location animation would just get stuck on its last frame.

Please help me … thanks a lot. :frowning: :confused:

By the way, I can get the loading of bvh files working with Empties, I will be more than happy to share my code/blender file and the actual bvh file – if it can help you figure out the problem with Armature.

Thanks a lot!!

  1. cant help you there, if stuff looks wrong youll need to check your calculations.
  2. setting the local matrix is supported. it looks to set the loc/quat/size properly and then calc the poseMatrix, not sure why it wouldent work.
  3. odd. insertKey accets a list of constants, can you chjeck your using it properly?
  4. I dont understand.

This isnt what you want to here, but from using the Pose API it seems to work fine, albeit very tricky to get what you want done due to armatures being so complex pose/rest, relative matricies etc.

I have got a working BVH importer with 2.43rc1, and had none of the problems you mention, take a look at the source maybe it will help.

If your sure theres a pose bug, make a very small script that reproduces JUST that bug and then the issue can be investigated.

Looks like the problem is your editing posedata after the .makeEditable() commend, that wont work.

That’s fine, you already told me that ROT and QUAT were the same.

Er, what do you mean by “it looks to set the loc/quat/size properly and then calc the poseMatrix”? Here is the different version of code I’ve tried:

(1) This one won’t produce any rotations at all:


e = Blender.Mathutils.Euler(rot['x'], rot['y'], rot['z'])
quat = e.toQuat()
qmat = quat.toMatrix()
for i in range(3):
   for j in range(3):
     pb.localMatrix[i][j] = qmat[i][j]
pb.insertKey(parentObj, i+1, Blender.Object.Pose.ROT )

(2) This one produces the wrong rotations:


e = Blender.Mathutils.Euler(rot['x'], rot['y'], rot['z'])
quat = e.toQuat()
pb.quat = quat
pb.insertKey(parentObj, i+1, Blender.Object.Pose.ROT )

[quote=“ideasman42,post:7,topic:379684"”]

  1. odd. insertKey accets a list of constants, can you chjeck your using it properly?
  2. I dont understand.


What do you mean by "a list of constants"? Do you mean that if I want to combine both rotation and translation, my code should look like:


pb.insertKey(parentObj, i+1, [Blender.Object.Pose.ROT, Blender.Object.Pose.LOC])



I tried to combine LOC and ROT using the following code and it didn't work:


pb.insertKey(parentObj, i+1, Blender.Object.Pose.ROT and Blender.Object.Pose.LOC)



[QUOTE=ideasman42;783913]
This isnt what you want to here, but from using the Pose API it seems to work fine, albeit very tricky to get what you want done due to armatures being so complex pose/rest, relative matricies etc.

[/quote]



I understand and I really appreciate the help. 



[quote="ideasman42,post:7,topic:379684""]


I have got a working BVH importer with 2.43rc1, and had none of the problems you mention, take a look  at the source maybe it will help.

[/quote]



That's just weird, maybe the guy who wrote that BVH importer dealt with a different format of BVH than the ones I have. I doubt that source code could help because it doesn't seem to use Armature -- which is what I want. I've already got the import working with Empties too.



[quote="ideasman42,post:7,topic:379684""]


If your sure theres a pose bug, make a very small script that reproduces JUST that bug and then the issue can be investigated.

[/quote]



I'm about 90% sure it's a bug in the Pose module. I'd be more than happy to report it. Here is the url for my code, the BVH file I've been talking about and the blender file that does the whole thing:

http://www.csse.unimelb.edu.au/~jingy/download/problem.tar.gz

If you are running linux, you can download it and use the following command to unzip it:

tar -zxvf problem.tar.gz

You would get the following files in a directory called "blenderMovieMaker":

Motion.py -- the file that contains all the main code
MotionDrive.py -- a driver file
stringUtils.py -- a string manipulation module for parsing the BVH files
motionTest2.blend -- the blender file
b_confid_wlk.bvh  -- the BVH file

The blender file has already got the BVH file loaded with hierarchical spheres, and you can just use the left and right arrow keys to see the animation.

The version of Motion.py in that tar.gz file loads the BVH file with spheres, to change it to use Armature, change the line 1389 from


createArmature = False



to


createArmature = True



The code for setting up the PoseBone data is in the function _createBlenderActionArmatureUsingPose, between line 976 and line 1027. I know you probably won't have time to look at it, but just in case.


def _createBlenderActionArmatureUsingPose(self, scene, sMotion, parentObj, poseBones, componentMapping):
	"""
	"""
	import Blender
	import Blender.Object
	import Blender.Mathutils

	if componentMapping == None:
		boneName = self['Name']
	else:
		boneName = componentMapping[self['Name']]

	pb = poseBones[boneName]

	print >> sys.stderr, 'Doing bone:', boneName

	for i in range(sMotion['NumOfFrames']):
		scene.getRenderingContext().currentFrame(i+1)
		motion = sMotion.getMotionForBone(boneName, i)
		if motion == None:
			continue

		rot = motion['rot']
		e = Blender.Mathutils.Euler(rot['x'], rot['y'], rot['z'])
		quat = e.toQuat()
		qmat = quat.toMatrix()
		#print >> sys.stderr, 'quat =', quat, 'rot =', rot
		#print >> sys.stderr, 'qmat ='
		#print >> sys.stderr, qmat

		#print >> sys.stderr, 'local mat ='
		#print >> sys.stderr, pb.localMatrix
		#print >> sys.stderr
		#for i in range(3):
		#	for j in range(3):
		#		pb.localMatrix[i][j] = qmat[i][j]
		#pb.quat = quat
		#print >> sys.stderr, 'after local mat ='
		#print >> sys.stderr, pb.localMatrix
		#print >> sys.stderr

		loc = motion['loc']
		pb.loc = Blender.Mathutils.Vector(loc['x'], loc['y'], loc['z'])
		if boneName.lower().rfind('hip') >= 0:
			print pb.loc
		#pb.insertKey(parentObj, i+1, Blender.Object.Pose.ROT and Blender.Object.Pose.LOC)
		pb.insertKey(parentObj, i+1, Blender.Object.Pose.LOC)

	for c in self['CHILDREN']:
		c._createBlenderActionArmatureUsingPose(scene, sMotion, parentObj, poseBones, componentMapping)

	pass


The rendered animation can be seen in this gif file -- it's a bit more than 5MB:

http://www.cs.mu.oz.au:80/~jingy/download/confid_walk0001_0072.gif

![http://www.cs.mu.oz.au:80/~jingy/download/confid_walk0001_0072.gif](http://www.cs.mu.oz.au:80/~jingy/download/confid_walk0001_0072.gif)

Please feel free to load that BVH file in your working BVH loader to see what happens.


> Looks like the problem is your editing posedata after the .makeEditable() commend, that wont work.


What do you mean here? Do you mean I should set the Poses of each bone <b>before</b> calling the makeEditable() function of the Armature object?

Edit: Ok, I understand now that to combine ROT and LOC, I have to put them in a list, it's wrong to just "and" them.

hrm cant repoly to everything atm but 2 things.
Updated docs.
http://members.optusnet.com.au/cjbarton/BPY_API/Armature.Armature-class.html#makeEditable

No pose setting code should be between
arm.makeEditable … and … arm.update()

I see where setting the local matrix wrong.
.localMatrix returns a copy of the matrix, not wrapped

so .localMatrix[0][0] = 0 does nothing

do

mat = bone.localMatrix

change mat

bone.localMatrix = mat

First of all, thanks so much for the prompt reply, I really appreciate it! I’ve triple-checked my code and the PoseBone settings are done outside the arm.makeEditable() and arm.update() calls, so no problem there.

I’ve also tried what you said about bone.localMatrix – it didn’t work and after loading the BVH file, the Armature is nowhere to be found on the rendering screen.

I just had a look at my code which uses Empties/spheres to load the animation, and it turns out that that part of the code uses the IPO module. The Armature part of my code uses the NLA module. There is no setIPO function with the EditBone class or the Bone class, so is it possible to use the IPO module with the Armature objects?

Nope, I think you have to use the pose, but thats easy since the names match.

I was wrong, the pose localMatrix is wrapped when you get it. strange still, I think a lot of this needs to be documented. sometimes quirks cant be avoided but they should be documented.

That’s what I thought too … oh well, never mind. :slight_smile:

I think that’s more like a bug than a quirk. I started trying to import BVH files into blender from version 2.37, and I had the same problems with the old Bone module … it seems that these problems just haven’t been fixed.

By the way, have you had time to see how 2.43rc handles the BVH file in the tar.gz file I posted above? Is there anyone I should talk to/email about the problems I’m having?

Thanks a lot!

tested and setting the pose bones local matrix worked fine.
http://members.optusnet.com.au/cjbarton/arm_test.blend

PS. when doing bug reports this is the kind of test file file that makes bug testing a lot easier.


bname = 'Bone.001'

from Blender import *

scn = Scene.GetCurrent()
ob = scn.objects.active
pose = ob.getPose()

e = Mathutils.Euler(90, 0, 0)
mat = e.toMatrix()

pose_bone = pose.bones[bname]
pose_bone.localMatrix = mat

pose_bone.insertKey(ob, 10, Object.Pose.ROT)

Interesting … I’m starting to think that there is a problem with the Euler module of Blender, or the way I’m using it, here is why:

My code for loading the motions of the BVH file has two parts: one part uses NLA to manipulate the Armature version of the skeleton model, the other part uses IPO curves to manipulate the Emtpies/Spheres version of the skeleton model.

All the angles in the BVH file are in degrees.

For NLA part of the code, what I do is the following:


for each bone:
   for each frame:
        rx,ry,rz = the corresponding angles for the right bone at the right frame
        e = Euler(rx,ry,rz)
        pose_bone.localMatrix = e.toMatrix()
        pose_bone.insertKey(ob, frame, Object.Pose.ROT)

The last line of the previous code is interchangeable with the quat.

For the IPO part of the code, what I do is the following:


for each bone:
   for each frame:
        rx,ry,rz = the corresponding angles for the right bone at the right frame
        ex, ey,ez = eulerRotate.eulerRotate(rx,ry,rz)
        set ex, ey, ez at the right frame for the corresponding IPO curve.

The eulerRotate script is the following:


"""
Name: 'Motion Capture (.bvh)...'
Blender: 232
Group: 'Import'
Tip: 'Import a (.bvh) motion capture file'
"""

__author__ = "Campbell Barton"
__url__ = ("blender", "elysiun")
__version__ = "1.0 03/25/04"

__bpydoc__ = """\
This script imports BVH motion capture data to Blender.

Supported:&lt;br&gt;

Missing:&lt;br&gt;

Known issues:&lt;br&gt;

Notes:&lt;br&gt;

"""

# $Id: bvh_import.py,v 1.4 2004/11/07 16:31:13 ianwill Exp $
#
#===============================================#
# BVH Import script 1.0 by Campbell Barton      #
# 25/03/2004, euler rotation code taken from    #
# Reevan Mckay's BVH import script v1.1         #
# if you have any questions about this script   #
# email me [email protected]               #
#===============================================#

#===============================================#
# TODO:                                         #
# * Create bones when importing                 #
# * Make an IPO jitter removal script           #
# * Work out a better naming system             #
#===============================================#

# -------------------------------------------------------------------------- 
# BVH Import v0.9 by Campbell Barton (AKA Ideasman) 
# -------------------------------------------------------------------------- 
# ***** BEGIN GPL LICENSE BLOCK ***** 
# 
# This program is free software; you can redistribute it and/or 
# modify it under the terms of the GNU General Public License 
# as published by the Free Software Foundation; either version 2 
# of the License, or (at your option) any later version. 
# 
# This program is distributed in the hope that it will be useful, 
# but WITHOUT ANY WARRANTY; without even the implied warranty of 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
# GNU General Public License for more details. 
# 
# You should have received a copy of the GNU General Public License 
# along with this program; if not, write to the Free Software Foundation, 
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
# 
# ***** END GPL LICENCE BLOCK ***** 
# -------------------------------------------------------------------------- 


import string
import math
import Blender
from Blender import Window, Object, Scene, Ipo
from Blender.Scene import Render


# # PSYCO IS CRASHING ON MY SYSTEM
# # Attempt to load psyco, speed things up
# try:
#   print 'using psyco to speed up BVH importing'
#   import psyco
#   psyco.full()
#  
# except:
#   print 'psyco is not present on this system'
 

# Update as we load?
debug = 0

# Global scale facctor # sHOULD BE 1 BY DEFAULT
scale = 1

# Get the current scene.
scn = Scene.GetCurrent()
context = scn.getRenderingContext()

# Here we store the Ipo curves in the order they load.
channelCurves = []

# Object list
# We need this so we can loop through the objects and edit there IPO's 
# Chenging there rotation to EULER rotation
objectList = []

def MAT(m):
	if len(m) == 3:
		return Blender.Mathutils.Matrix(m[0], m[1], m[2])
	elif len(m) == 4:
		return Blender.Mathutils.Matrix(m[0], m[1], m[2], m[3])



#===============================================#
# eulerRotation: converts X, Y, Z rotation      #
# to eular Rotation. This entire function       #
# is copied from Reevan Mckay's BVH script      #
#===============================================#
# Vars used in eular rotation funtcion
DEG_TO_RAD = math.pi/180.0
RAD_TO_DEG = 180.0/math.pi
PI=3.14159

def eulerRotate(x,y,z): 
  #=================================
  def RVMatMult3 (mat1,mat2):
  #=================================
    mat3=[[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]]
    for i in range(3):
      for k in range(3):
        for j in range(3):
          mat3[i][k]=mat3[i][k]+mat1[i][j]*mat2[j][k]
    mat1 = mat2 = i = k = j = None # Save memory
    return mat3
  
  
  #=================================
  def	RVAxisAngleToMat3 (rot4):
  #	Takes a direction vector and
  #	a rotation (in rads) and
  #	returns the rotation matrix.
  #	Graphics Gems I p. 466:
  #=================================
    mat3=[[0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]]
    if math.fabs(rot4[3])&gt;0.01:
      s=math.sin(rot4[3])
      c=math.cos(rot4[3])
      t=1.0-math.cos(rot4[3])
    else:
      s=rot4[3]
      c=1.0
      t=0.0

    x=rot4[0]; y=rot4[1]; z=rot4[2]
    
    mat3[0][0]=t*x*x+c
    mat3[0][1]=t*x*y+s*z
    mat3[0][2]=t*x*z-s*y 
    
    mat3[1][0]=t*x*y-s*z
    mat3[1][1]=t*y*y+c
    mat3[1][2]=t*y*z+s*x
    
    mat3[2][0]=t*x*z+s*y
    mat3[2][1]=t*y*z-s*x
    mat3[2][2]=t*z*z+c
    
    rot4 = s = c = t = x = y = z = None # Save some memory
    return mat3
 
  eul = [x,y,z]
  
  for jj in range(3):
    while eul[jj] &lt; 0:
      eul[jj] = eul[jj] + 360.0
    while eul[jj] &gt;= 360.0:
      eul[jj] = eul[jj] - 360.0

  eul[0] = eul[0]*DEG_TO_RAD
  eul[1] = eul[1]*DEG_TO_RAD
  eul[2] = eul[2]*DEG_TO_RAD
  
  xmat=RVAxisAngleToMat3([1,0,0,eul[0]])
  ymat=RVAxisAngleToMat3([0,1,0,eul[1]])
  zmat=RVAxisAngleToMat3([0,0,1,eul[2]])
  
  mat=[[1.0,0.0,0.0],[0.0,1.0,0.0],[0.0,0.0,1.0]]  
  
  # Standard BVH multiplication order
  mat=RVMatMult3 (zmat,mat)
  mat=RVMatMult3 (xmat,mat)
  mat=RVMatMult3 (ymat,mat)
  
  
  '''
  # Screwy Animation Master BVH multiplcation order
  mat=RVMatMult3 (ymat,mat)
  mat=RVMatMult3 (xmat,mat)
  mat=RVMatMult3 (zmat,mat)
  '''
  mat = MAT(mat)
  
  eul = mat.toEuler()
  x =- eul[0]/-10
  y =- eul[1]/-10
  z =- eul[2]/-10
  

  eul = mat = zmat = xmat = ymat = jj = None
  return x, y, z # Returm euler roration values.


The NLA code produces the wrong animation, the IPO cod produces the right animation. For the IPO code, I have to first transform the original angles to the euler angles, otherwise the animation would be beyond wrong. The only difference I can see here is the Euler code. Maybe that’s the problem?

are you applying the rotations in the order they are set in the bvh?

That’s a great question … in the IPO, I follow the order of x->z->y – which is the order specified by the BVH. How should do this for NLA? Should take rx, ry, rz, and create 3 separate Quats and insert them into the same frame in the order of x->y->z? I will have a go and let you know how it worked out.

Update: Nah, it didn’t work, I used the following code:


ex = Blender.Mathutils.Euler(rx, 0, 0)
exmat = ex.toMatrix()
pb.localMatrix = exmat
pb.insertKey(parentObj, i+1, [Blender.Object.Pose.ROT])

ey = Blender.Mathutils.Euler(0.0, ry, 0.0)
eymat = ey.toMatrix()
pb.localMatrix = eymat
pb.insertKey(parentObj, i+1, [Blender.Object.Pose.ROT])
			
ez = Blender.Mathutils.Euler(0.0, 0.0, rz)
ezmat = ez.toMatrix()
pb.localMatrix = ezmat
pb.insertKey(parentObj, i+1, [Blender.Object.Pose.ROT])

And I tried pretty much all the combinations of those and none of them worked.

the order is set in the file. a bvh can have any order of rotation and they have to be applied in the order set out in that node.

Take a look at my bvh importer, it has a function that applys the rotation in any order, this is important. sure if the BVH file you have is in XYZ order, it will work. but importing another file thats not will mess up.

I think you are right, it looks like I messed up the specification of the order of rotation in the NLA part of my code. I will have to look into it a bit more. So, is my way of specifying the order of rotations in my previous post correct?

Thanks a lot!

P.S. How do I calculate the field of view of the Blender camera?

ao2’s vrm script has some realy nice functions to do this.