Motion Data Formtting (Organic Motion XML)

For some cases below, we reference the example motion XML file from the SDK PunchCombo_120fps.xml. (This file can be found attached or in the example motions folder in C:\Program Files\Organic Motion\OpenStage Client 2.x.x.xxxx)
Matrix 44
Matrix44 is a standard 4x4 3D homogenous transform matrix stored in row-major order with basis vectors layed out contiguously. Rotation is the upper left 3x3. Translation is the last row. Such a matrix can be post-multipled to any homogenous row vector representing a 3D point to transform that point.

Looking at the punch combo, the first transform we see is the initial segment for the layout of Actor 0 at line 23. 

As a matrix, that would be the following indicating an identity rotation and a translation of (-0.03, -3.47e-18, 0.87):

[[ 1        0          0        0 ]
 [ 0        1          0        0 ]
 [ 0        0          1        0 ]
 [ -0.03    -3.47e-18  0.87     1]]
Thus, if we define the origin, O, as the row vector (0,0,0,1) and that matrix as M, we can perform the multiply O*M = (-0.03, -3.47e-18, 0.87, 1). i.e., It will translate the origin to the origin of that segment.

If you aren't familiar with transform matrices, there are a number of resources online. We like to recommend Essential Math for Games Programmers.

They offer a number of great online resources as well as a printed book by Van Verth and Bishop that's quite useful in that it's accessible and technical.

Do you store the pose in world coordinates?
Yes. However, the SDK has the capability to give you the joints in absolute or relative coordinates through the utility functions if you're pulling data that way, ConvertSkeletonToJointTreeAbsolute and ConvertSkeletonToJointTreeRelative, or through the IActorViewJoint class. Take a look at simple_sdk_client in the SDK samples. It's less than 100 lines of code in main.cpp. It outputs the absolute joint positions, and you can quickly have it output relative by modifying lines 75 and 76. 

If you want to use that code in real time to work with an xml file, you can just have the simserver stream it. "omsdk2_simserver.exe -motionFiles myMove.xml"

Forward Kinematics
Effectively yes, but that's a bit of a misnomer. Since we store the absolute joint position and orientation, we're not really using foward kinematics in the traditional sense. If you want the rotation of the clavicle, you can just read it. You don't have to rotate the Sacrum to rotate Lumbar to rotate the Thorax to rotate the ... you get the picture.

However, you could construct a scene graph representing the segments hierarchically and apply the relative transforms from the SDK to drive each bone in a method similar to forward kinematics if that's necessary or useful to your application.

File Format
We don't have a formal description of our file format, but it's fairly straightforward XML once you know a few things:
  • Distances are in meters.
  • The typical layout is a header followed by and actor layout list followed by a frame list.
  • The actor layout list defines each actor in the file hierarchically. i.e., If you parse the XML using a standard XML parser, the child SegmentNode nodes are the child bones.
  • Each Segment in the actor layout has a length and initial orientation defining the bind pose skeleton.
    • Segments are defined to be at the origin pointing up the Z axis prior to bind pose.
    • Thus, we would expect the UpperArm segments to have a height around 5 feet/1.5m. If you look at lines 115 or 139 of the punch combo, you see the Z translation is 1.441m.
    • Similarly, we'd expect a rotation matrix for the upper arm that rotates Z onto the Y-axis. i.e., Components 8,9,10, and 11 should be 0, 1, 0, 0 or the inverse of that. At lines 115 and 139, we see that. UpperArm_Left points down -Y and UpperArm_Right points down Y.
  • The frame list has a list of sequential frames with time, event markers, and a list of actors.
  • The actor information is just an ID and a Pose. The pose is a list of matrices in the same order as the segments appeared in the actor layout list.

(Then as a response to more questions)

Length and Segments

Both statements about length for segments are true. You can certainly just use the position information and connect the dots as you say. That will give you a rendering of a skeleton that humans can process, but it will lose rotation information particularly twist where we derive it.

The length is the distance from the origin of a bone down its current Z axis to its end and, generally speaking, any child bones. Let's look at the bind pose for UpperArm_Leff and LowerArm_Left from the punch combo file. The upper arm has a Z-axis/3rd row of (0, -1, 0) as noted in our previous email, and its length is .313097m. Thus, we would expect that the origin of LowerArm_Left is displaced by that much. The UpperArm_Left origin is (-.03, -.160297, 1.441). Our expectation is that the end of the upper arm segment and hence LowerArm_Left is at (-.03, -.160297 - .313097, 1.441) which computes to (-.03, -.473394, 1.441). This matches the numbers we see in our file. 

The same should hold true for the contents of the Pose entries. If we take the matrices for the left upper and lower arm from lines 188 and 189, we can see the same relationship holds true,


The Z-axis for the first matrix, the upper arm is (-0.157206,-0.987519,0.00966871) which makes sense. It's the first frame tracked, so we have not deviated much from the bind pose orientation. If we scale that axis by .313097, we get (-.04922, -.30919, .003027). That offset added to the current origin of the upper arm gives us the origin of the lower arm.

(1.16093,-0.0525544,1.4778) + (-.04922, -.30919, .003027) = (1.112, -.362, 1.481)

This matches up with the origin of the lower arm barring rounding errors and possibly some minor loss of precision when we serialized the values to file since there are only 6 sig figs in the XML.

There's one notable exception to this, and that may be the root of your confusion. The length offset from the Sacrum defines its length, but with respect to the origin of its children, it only applies to Lumbar_0. Pelvis_Left and Pelvis_Right are collocated with the Sacrum. Your example matrices above are off by about 1cm because it's the first frame after bind pose. It takes a few frames to coalesce. If you look at later frames, they should be within a millimeter or two of each other. I haven't gone digging through the code at this point to see why theXML is constructed that way rather than having a root bone with length 0 that parents the Sacrum and Pelvis bones which would in theory remove the special case here.
Matrices in the Pose Section and DOFs

The matrices in the ActorLayout section define the bind pose and hierarchy. The matrices in the frame list Pose sections define the current world matrix of the segment. In game engine parlance, think of this as the bone to world matrix. These matrices encode the current rotation of the bones, so they can be used to position or render a segment independently. Any information about which DOFs a bone has and how much rotation in each DOF occurred is abstracted out at this point. That could be approximated by computing the delta rotation from bind pose for each bone and then performing an Euler angle decomposition.

Vectors and Transforms

It's really up to you and your use case what you transform using the bone information.

If you just want to draw the current joint positions, you have the case that we previously discussed. Just multiply (0,0,0,1) by any bone's matrix to get its origin in world space or extract the values directly from the last row of the matrix. You can see a simple example of this at line 49 of the actor_render_opengl sample. Similarly, you could find a bone's endpoint using (0,0,length,1) multiplied by the matrix. 

If you want to know things like the X and Y axes, it's just a multiply of (1,0,0,0) or (0,1,0,0) against the matrix (although in practice and code it's just easier to extract those components from the matrix.) This could be extended to any arbitrary vector defined in the space of the bone although I don't have a use case for that off the top of my head.
Have more questions? Submit a request


Please sign in to leave a comment.
Powered by Zendesk