# bvh Rotations to C4D HPB?

#1

Hi,
Does anyone know what the magic formula is for converting bvh rotation values to C4D HPB values?
AFAIK bvh rotations are Euler angles expressed in degrees. And HPB are also Euler angles expressed in degrees. But they are definitely not the same values.
Example:
bvh rotation = 0, 0, 0
C4D HPB rotation = 0, 90, 0 <----WTF?

I’ve tried the typical sdk functions for this: HPBToMatrix(), etc… But so far I can’t find the magic formula that will convert the bvh rotations to HPB properly.
Anyone know how to do this?

-ScottA

#2

You have to employ matrix multiplication in order to go from Euler angles to C4D HPB. First, you need to know the rotation order (which is given in BVH). I have old code that I used in InterPoser Pro for loading BVH onto the IPP joints. For HPB, it might be better to multiply rotation matrices (in rotation order). This is typically done in reverse: for instance, if the rotation order is XYZ, you multiply the rotation matrices (based on the rotation angle for each) like this: Matrix HPB = MatrixRotZ() * MatrixRotY() * MatrixRotX(). It may also be necessary to convert from right-handed to left-handed (not sure which BVH is in but C4D uses a left-handed coordinate system). If so, you have to use a similarity matrix to convert the handedness of the coordinate system. See what you get without consideration of handedness. If things look ‘mirrored’, I can provide code to convert from right->left.

#3

Robert! How are you?
You kind of vanished a while ago. It’s good to see you back.

I’m working in Python. But I can switch over to C++ if needed.
In my bvh file I have this: CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
So I’m guessing that means the rotation order = ZXY

But I’m not sure how to get the hpb vector values from the hbpMtx?

``````import c4d,math
def main():

rotMtxY = c4d.utils.MatrixRotY(0)
rotMtxX = c4d.utils.MatrixRotX(0)   #These zero values are just here as simple example values from my bvh file
rotMtxZ = c4d.utils.MatrixRotZ(0)

hpbMtx = rotMtxY * rotMtxX * rotMtxZ   #<---How do I get the hpb vector values from this matrix?
print hpbMtx

#This returns vector(0,0,0)   [i][b]not[/b][/i]  vector(0,90,0) like the C4D importer does
hpb = c4d.utils.MatrixToHPB(hpbMtx, c4d.ROTATIONORDER_HPB) #<---Not sure if this is correct?
print hpb

if __name__=='__main__':
main()
``````

Also,
In my bvh file where the MOTION values are listed. I’m assuming that the values are always listed as XYZ. That never changes regardless of the order listed in the CHANNELS right?
I’m plugging in those values into my rotation matrices code accordingly. But I still can’t get the correct rotation values from them.

-ScottA

#4

The MOTION section values use the same position and rotation orders as those given in the CHANNELS section for the JOINT hierarchy.

MatrixToHPB() will give you a vector using the order (and type) as you specify in the second argument. So, you should be getting the actual HPB values if using ‘c4d.ROTATIONORDER_HPB’.

As for why the Cinema 4D import yields (0,90,0) and you get (0,0,0) may have something to do with the axial system. The 90 degree rotation puts the Z as up instead of the Y. You may need to apply this 90 degree rotation to all rotation matrices (add a MatrixRotY(0.5*PI) as the first multiplication - last matrix multiplication, right to left).

#5

You lost me there.
I can’t quite picture what you’re saying.

Also:
I’ve successfully written my own fbx exporter. And the FBX sdk is also a right handed system like the bvh files are.
In order to convert my fbx rotation values to c4d I needed to swap the X&Y axis values.
In order to to convert the positions I needed to negate the Z axis value.
I’m not sure if I will need to do the same thing when working with bvh files?

-ScottA

#6

I think I have it figured out. But I will need to test it some more before I’m 100% convinced.
This is what I came up with that seems to work:

``````#This is how to convert the rotation values in a .bvh file to HPB values in C4D
#This example uses a .bvh file that has the rotations listed as: ZXY
#    EXAMPLE: CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation
#Change the code if your .bvh file uses a different order

import c4d,math
def main():

#First...we need to convert the bvh file's rotation values to radians
#Why?..I have no frackin' clue!!
vz = 1.45748 * math.pi / 180  #<---NOTE: This value needs to be negated from the Z value in the bvh file
vx = -11.8543 * math.pi / 180
vy = 1.15059 * math.pi / 180

#Create three rotation matrices using the above values
rotMtxZ = c4d.utils.MatrixRotZ(vz)
rotMtxX = c4d.utils.MatrixRotX(vx)
rotMtxY = c4d.utils.MatrixRotY(vy)

hpbMtx = rotMtxY * rotMtxX * rotMtxZ    #The reverse rotation order in the bvh file

#Apply the rotations to your own joint
jnt = doc.SearchObject("myJoint")
jnt.SetAbsRot(c4d.utils.MatrixToHPB(hpbMtx))

"""
At this point your joint's rotations should match the rotation of the bvh file
However...The axis might be pointing in different directions
So we need to Align the joint's axis in the Z direction (as it is in the bvh file)
"""

#Set the Axis option to "Z"
jnt[c4d.ID_CA_JOINT_OBJECT_BONE_AXIS] = 2

#Execute the Align button
c4d.CallButton(jnt,c4d.ID_CA_JOINT_OBJECT_ALIGN_AXIS)

if __name__=='__main__':
main()
``````

-ScottA

#7

Apparently this does not work. Back to the drawing board. :banghead:
I’m still looking for a way to turn bvh rotation values into HPB values.

-ScottA

#8

Since the view count for this thread keeps climbing. And nobody has replied to it. I’m guessing most of you are as lost about the subject as I am.
So I’ll share what I’ve learned with you guys so far. What I’ve discovered is quite surprising.

First of all. What I originally wanted to accomplish is to read the rotations for the joints in a bvh file. And get the C4D versions of them.
I was going to take those values and write a Qt application that could be used to Drag&Drop them onto C4D rigs in various ways.
*The reason I wanted to use Qt is mainly because the UA in C4D is far too primitive. The screen items in Qt are full blown objects, which makes them much easier to animate/D&D/ Rotate/.ect…
But I had no clue how .bvh files worked. And how utterly confusing this was going to be.
After getting nowhere trying to convert the bvh rotations to C4D rotations that matched the ones created by built-in .bvh importer. I decide to write my own bvh importer.
What I discovered from doing this is that the bvh importer that is built into C4D DOES NOT USE THE ROTATION ORDERS in the bvh files!
Changing them in the .bvh file doesn’t have any effect on the axis orientations when imported into C4D.
AFAICT. The importer is using the OFFSET positions of the base of a joint, and it’s child tip joint, to determine what axis orientation to use. It does NOT use the XYZ order listed in the bvh files at all!

The direction a joint is facing determines which Axis Orientation gets assigned to it.
These are the joint’s matrix values the built-in importer assigns to the joint. Depending on what direction the joint is facing:

Up
v1 = c4d.Vector(1, 0, 0)
v2 = c4d.Vector(0, 0, -1)
v3 = c4d.Vector(0, 1, 0)

Down
v1 = c4d.Vector(1, 0, 0)
v2 = c4d.Vector(0, 0, 1)
v3 = c4d.Vector(0, -1, 0)

Left
v1 = c4d.Vector(0, 0, -1)
v2 = c4d.Vector(0, 1, 0)
v3 = c4d.Vector(1, 0, 0)

Right
v1 = c4d.Vector(0, 0, 1)
v2 = c4d.Vector(0, 1, 0)
v3 = c4d.Vector(-1, 0, 0)

Forward -Z
v1 = c4d.Vector(-1, 0, 0)
v2 = c4d.Vector(0, 1, 0)
v3 = c4d.Vector(0, 0, -1)

Backward +Z
v1 = c4d.Vector(1, 0, 0)
v2 = c4d.Vector(0, 1, 0)
v3 = c4d.Vector(0, 0, 1)

So for example:
A bvh imported joint pointing up in C4D will have a default rotation value of: 0, 90, 0
But a bvh imported joint pointing to the Right will have a default rotation value of: 360, 0, 0
These values are the “Default Pose” values. Assuming that the bvh file’s rotations at frame zero are: 0,0,0

AFAICT. The rotation values in the bvh files are not treated as absolute values. They’re treated as ADDITIVE values.
What I mean by that is that the rotation values are added to the current axis values for the joints.
So for example. If you have imported a rig from a bvh file. And at frame zero the hip joint’s axis values are: 0, 90, 0.
And the rotation values for that joint at frame zero are: 5, 5, 5.
Then the actual value in C4D for that joint would be: 0+5, 90+5, 0+5

So what does this all mean?
Well. I’m not exactly sure yet. I’m still trying to wrap my head around it all.
Trying to get the same rotation values directly from the bvh files as the built-in C4D importer probably means that we need to write code the same way that plugin is written.
Meaning that we will need to forget about using the XYZ data in the files. And instead use their locations to get their directions. Then apply the axis orientations to them in the same way the built-in importer does.
Another thing that I’ve been considering it to forget about the built-in importer all together. And write the entire thing from scratch (both creating the rig and the animations).
But I’m not sure if that would collide with how users use .bvh file or not?

Anyway.
I’m not sure what I’m going to do about it yet. Or how this helps (if at all) in trying to read and apply bvh rotations in C4D yet.

-ScottA

#9

Well i can say thanks what do you mean by UA in C4D is far too primitive? UA stands for what?

#10

UA = UserArea.
It’s basically a canvas area where you can draw things like shapes(which can also be used like image buttons) for your C4D plugins.
It’s very old and very outdated in C4D. It’s ok for very basic things. But we have to code everything by hand. And some things like rotating a shape are not supported.
In modern frameworks like Qt and the HTML5 canvas. The shapes are full blown objects with a matrix that can be changed via built-in methods. So it’s very simple to do fancier animations, collisions, parenting, drag&drop, etc. with them. And we don’t need to write all of the code ourselves to do them.

-ScottA

#11

But isn’t C4D revamping the core? shouldn’t include changing that.?
With caveat that i am painful aware as just an user of what a mess have been transitions to QT of Max, and the issues with Maya and even Houdini.

#12

Years ago I spoke to some of the support guys about how C4D partnering up with Qt would solve a lot of their prehistoric GUI limitations. And the general replies I got were that they didn’t like Qt. :sad:
MAXON does not talk openly about such things with it’s users. So I can’t say for sure what’s going to happen with the new core change. But historically they have not cared enough about their ancient GUI tools to warrant updating it.
I guess we’ll see when the new core arrives. But I’ll be surprised if the GUI tools change much. Or even at all for that matter.
At this point it doesn’t really matter that much to me. Because I’ve figured out how to write Qt apps that will work with the C4D SDK without MAXON’s support.

-ScottA

#13

Thanks Scott for the feedback.