View Full Version : Getting to grips with HPB Matrices for rotations... ARGH!

04-03-2011, 12:22 PM
Hi all,

I'm trying to put together an accelerometer script so that I can measure the acceleration of a target object and apply it to a null for use in my animation work. One of these applications is in the automated assignment of thrust vectors on a spacecraft for example.

I've already worked out how to get the acceleration values of the target object from comparisons between frames, and if all I wanted to do was focus on just the world coordinates, my job would be pretty much done, but I'm trying to implement a facing vector adjustment to the results. This is where I've run into problems.

I found no way to transfer the motion vectors around a set of rotation axes using built in commands, so I've had to look up matrices online, and have concocted the following code as a result.

// Get values for object position and rotation. fvec is in RADIANS.
tmp1=srcobj.param(ROTATION, time);
fvec = rad(tmp1); // fvec = -fvec;
wvec=(srcobj.param(POSITION, time));

// Get current velocity from comparison between previous and next frames.
locvel=wvec-srcobj.param(POSITION, time-(1/sceneObj.fps));
nlocvel=srcobj.param(POSITION, time+(1/sceneObj.fps))-wvec;

// Get Acceleration Values for World Velocity

// Extract acceleration coordinates to separate variables.
// This is so we can perform a sin/cos calculation later.
ax = accelworld.x;
ay = accelworld.y;
az = accelworld.z;

// Build Matrices
// Matrix for Heading
a[1][1] = ax * cos(fvec.h); a[1][2] = 0; a[1][3] = az * sin(fvec.h);
b[1][1] = 0; b[1][2] = ay; b[1][3] = 0;
c[1][1] = ax * -sin(fvec.h); c[1][2] = 0; c[1][3] = az * cos(fvec.h);

// Matrix for Pitch
a[2][1] = ax; a[2][2] = 0; a[2][3] = 0;
b[2][1] = 0; b[2][2] = ay * cos(fvec.p); b[2][3] = az * sin(fvec.p);
c[2][1] = 0; c[2][2] = ay * sin(fvec.p); c[2][3] = az * cos(fvec.p);

// Matrix for Bank
a[3][1] = ax * cos(fvec.b); a[3][2] = ay * -sin(fvec.b); a[3][3] = 0;
b[3][1] = ax * sin(fvec.b); b[3][2] = ay * cos(fvec.b); b[3][3] = 0;
c[3][1] = 0; c[3][2] = 0; c[3][3] = az;

// Multiply cells
/* Matrix format: X* Y* Z*
H A1 A2 A3
P B1 B2 B3
B C1 C2 C3 */

for (i=1;i<=3;i++)
m[1][i] = a[1][i] * a[2][i] * a[3][i];
m[2][i] = b[1][i] * b[2][i] * b[3][i];
m[3][i] = c[1][i] * c[2][i] * c[3][i];

// Add the matrix cells together to get all rotations.

matx = m[1][1] + m[1][2] + m[1][3];
maty = m[2][1] + m[2][2] + m[2][3];
matz = m[3][1] + m[3][2] + m[3][3];

// Adjust vectors

fvaccx = matx;
fvaccy = maty;
fvaccz = matz;

// Feed this back as a vector to output to our positional object.
fveclvel = <fvaccx, fvaccy, fvaccz>;

// Apply to our positional object
ma.set(POSITION, fveclvel);

The problem is that I have not gotten the desired results. On testing, I applied some forward motion on my target object so that I had some Z acceleration. What should happen is if the object rotates to face at right angles on either the heading or banking angles, the object this script is applied to should rotate counter to it, so that the null position maintains a constant world coordinate acceleration in relation to the facing vector (such that if it's parented to the control object it doesn't change direction when looking at it in the perspective view), but this doesn't happen. The result is that it seems to only take information on one axis.

I suspect it's my matrix construction at fault, which I tried to build based on the information I found at mikegreen.name, but I think I took a wrong turn somewhere.

Any math geniuses want to see where I've gone wrong?


maxim capra
04-04-2011, 01:55 AM
hey, i just had a quick look and some things seem weird to me. first of all, HPB corresponds to a rotation around XY and Z in that order, doesn't it? but as far as i know, you have switched the sin/cos calculations for X and Y rotation (so P and H is switched in your example)?

04-04-2011, 05:59 AM
Maxim: This is based off of the stuff I found on mikegreen's website that covers LScript matrix transformations. Unfortunately, I found that I was doing the multiplications wrong. I'e corrected the code accordingly, but it still doesn't take all motions into account...

But I found a far easier way to do it. I can just write the accelerometer code and apply it to world coordinates, then use follower to pick up said world coordinates and apply them to another object. Having the accelerometer's output piped to a null parented to another null that picks up the target object's rotation values should give me the results I've been looking for.

I'll give it a try.

04-15-2011, 08:43 AM
OK so I've been trying to get to grips with this issue and it's causing me a headache. I'm not sure if it's a problem with LW's rotation order or if I'm just not doing the math properly, but I've managed to implement an adjustment via channels and graph editor calculations (I've done this as for some reason I can't pipe the LScript output to another channel, it's as if it only takes the original keyframed or interpolated data).

I set up a simple scene to test out my previous idea (outlined in my last post). One object (a very simple polygonal Glider), a null to mimic the HPB values of the Glider using expressions (in this case [Glider.Rotation.H] etc), another null at 1 metre Z distance (for testing purposes) and a fourth null to take the world position of the third null (which is parented to the second null at the world origin). This forth null takes the position information using wpos(Time) LScript expressions in the Graph Editor and works fine for the purposes of my rig, putting the null at the correct coordinates relative to the keyframed position I gave it.

What happens is if I adjust either the heading, pitch or bank for the target object, the values are properly piped through to the null, allowing me to use those values in my calculations. I can even combine heading and pitch correctly.

Combining Bank with either Heading or Pitch gives me weird results. I'll try and post an example scene later as well as a video with the results when I can get to my data again, but it's looking difficult to crack.

So I guess I have two questions:

1) Is there a way to correct for this using expressions only?

2) Is there a way to pipe the channel data output by LScript motion channel scripts to other channels in the same way as expressions work?

Again, I'll update with a video and scene later.


04-18-2011, 02:32 AM
I managed to find a solution in the end. It's not an elegant one and involves setting up a lot of nulls, but it works and I'm implementing it in one of my scenes today.

The next problem I need solving is how to automate the process. I imagine that I could simplify the process by eliminating the need for nulls, but I can't use the getForward() etc functions in a graph channel, and an LScript doesn't (easily at least) allow its output to be read by other envelopes (I tried to read the resulting output in another object's motion channel and it didn't work)... So I'm thinking of setting up an LScript that will create the needed heirarchy and assign it to a selected object, then set up everything automatically through the use of CS commands.

Unless anyone knows how I can get a channel to reference data output by LScript motion modifiers... Then I can avoid the need to set up an LSCript master.

04-25-2011, 12:23 AM
Here's my next problem:

Does anyone know how to create (via LScript) a channel group within a mesh or light object agent? I need this to set up some envelopes on the lights my script creates.