www.jpct.net
jPCT  a 3d engine for Java => Support => Topic started by: cyberkilla on February 12, 2007, 05:01:01 pm

Is there a way to rotate around an axis, like this:
rotateAxis
public void rotateAxis(SimpleVector axis,
float angle)
Rotates the object's rotation matrix around an arbitrary axis. The method is more powerfull than the normal rotatearoundanaxis methods, but also a bit slower. The resulting matrix will be orthonormalized to ensure numerical stability.
Parameters:
axis  a directionvector pointing into the axis direction with the object's rotation pivot as position vector
angle  the angle of the rotation
..but for SimpleVector:)
I need to rotate the vertices around an axis, by an angle.
This must mean that I need to rotate around this axis, like in Object3D, but also take into account that it isn't the center.
Something like this?
vertex = vertex.calcSub(axis);
vertex.rotateAxis(axis,angle);
vertex.add(axis);

You may create a matrix for this, do the rotateAxis() with that one and do a matMul() with the SimpleVector. Should work...

vertex = vertex.calcSub(axis); // move vertex into "pivot"space
Matrix matrix = new Matrix();
matrix.rotateAxis(axis, angle);
vertex.matMul(matrix); // Do the rotation
vertex.add(axis); //move back into former space
Something like that?

Exactly.

Brilliant:)
I am moving the vertices into world space, so the skeletons coords match up correctly.
dstVertex.x = srcVertex.x;
dstVertex.matMul(worldTransformation);
rotateVertex(keyframe.rotationAxis,keyframe.rotation,dstVertex);
dstVertex.matMul(worldTransformation.invert3x3());
I believe that I may have to "scale" the skeletal coordinates, if I decide to scale the model itself.
Both the skeleton, and the model, have to be in world space, and around the origin, by the looks of it. Or, at least, moving in sync.
Now, since I need the world transformation matrix, I guess I need to add the object to the world before all of this. I think it will work out ok though, because I separated the "createMeshAnimation" method outside of the loading.

I am still having trouble, but my requirements have changed...
I have a vector, which is the rotational pivot.
I have a vector and angle  axis, and angle.
I need to rotate a point around a pivot, in the direction in the axis, and the amount specified in the angle.
I have this...
//The rotation matrix.
Matrix matrix = new Matrix();
matrix.rotateAxis(new SimpleVector(rotateX,rotateY,rotateZ), rotateAngle);
//Translation vector.
SimpleVector pivot = new SimpleVector(translateX,translateY,translateZ);
SimpleVector myPoint = new SimpleVector(???,???,???);
myPoint.matMul(matrix);
myPoint.translate(pivot);
Should that be working? I fear it does not.
It could be something else, but this is where my confusion lies, so
I would greatly appreciate some opinions before I seek other reasons.
Oh, and the ERROR is:
It seems to ALWAYS rotate around the origin(0,0,0), instead of the pivot vector.

Something like this? (untested):
//The rotation matrix.
Matrix matrix = new Matrix();
matrix.rotateAxis(new SimpleVector(rotateX,rotateY,rotateZ), rotateAngle);
//Translation vector.
SimpleVector pivot = new SimpleVector(translateX,translateY,translateZ);
SimpleVector myPoint = new SimpleVector(???,???,???);
myPoint=myPoint.calcSub(pivot);
myPoint.matMul(matrix);
myPoint.add(pivot);

Thanks egon, it does work;)
How can I rotate the axis of a simplevector, using a matrix?
Is that possible?
My discoveries have brought up some new information.
The bone structure uses normal axis/angle, and vector for translation.
When I apply the matrix to the position vector, it rotates it.
The C++ version im borrowing the implementation from does not do this.
It appears to merely alter the rotation axis of the vector.
By doing this, adding another simplevector to it should move it in the direction of the axis.
Is this possible?
Does this make sense to you?
The only other option that seems to work is...
Root Bone...
translate vector to bone position
Child Bone...
translate vector to child bone position.
matMul the invert3x3 matrix of the parent bones rotation matrix.
add parent vector.
This yields the right position, but I dont know why.
Also, it isn't very easy to modify all of this code ive written.
Is the first way I suggested possible?
I seem to be having no real trouble in any other area:)
I have set it up to support 4 bones per vertex by default,
and vertex weighting is easy.
Its because im mixing matrices with vectors in a way they do not.
The methods they have in matrix are not the same as the ones we have, but they are supported in simplevector.
The rotation of the simplevector's axis appears to be the only problem.
Please forgive my lack of knowledge in these matters.
I am, however, learning quickly. I have source code of a quaternion, which I hear a lot about. Personally, I dont think they are needed, and look similar in operating functionality to matrices.

Well...i...don't...understand... :?: What are trying to achieve? I don't get it. What do you mean with axis of a SimpleVector? A SimpleVector is (depending on the view of things) either a point in space or a vector pointing into a direction...what should its axis be?
But i can comment on quaternions: There are equivalent to matrices but are easier to use in some cases (according to some people...i've never really used them and never saw the need to do so). For doing simple rotations, there are definitely not needed.

You know, I think i simply do not understand the fundamentals here.
I will research some more.
Could you possibly provide an explanation for why invert3x3 seems to work?
Root Bone...
translate vector to bone position
Child Bone...
translate vector to child bone position.
matMul the invert3x3 matrix of the parent bones rotation matrix.
add parent vector.
Consider two bones.
One is the parent, and the other is the child bone.
The child bone has its own relative translation, and rotation.
I need to add the rotation, and translation of the parent bone to the child bone to get the absolute translation/rotation.
The only problem is, the ROTATION of the root bone should not be applied to the root bone's vector. The effect of the root bone's rotation matrix/axis only has an affect on child bones.
When I look at the source of the C++, i see this...
joint.m_relative.setRotationRadians( joint.m_localRotation );
joint.m_relative.setTranslation( joint.m_localTranslation );
void Matrix::setRotationRadians( const float *angles )
{
double cr = cos( angles[0] );
double sr = sin( angles[0] );
double cp = cos( angles[1] );
double sp = sin( angles[1] );
double cy = cos( angles[2] );
double sy = sin( angles[2] );
m_matrix[0] = ( float )( cp*cy );
m_matrix[1] = ( float )( cp*sy );
m_matrix[2] = ( float )( sp );
double srsp = sr*sp;
double crsp = cr*sp;
m_matrix[4] = ( float )( srsp*cycr*sy );
m_matrix[5] = ( float )( srsp*sy+cr*cy );
m_matrix[6] = ( float )( sr*cp );
m_matrix[8] = ( float )( crsp*cy+sr*sy );
m_matrix[9] = ( float )( crsp*sysr*cy );
m_matrix[10] = ( float )( cr*cp );
}
Is that the equivalent of:
bone.matMul(rotationMatrix);
bone.translate(translation);
It does not seem to do the same thing.

Could you possibly provide an explanation for why invert3x3 seems to work?
I'm not sure. It sounds wrong to me. I would expect that it should work without the invert(), not with it. Could be rowcolumnmajor mix problem. In jPCT, all matrices are treated as row major. In other engines, matrices may as well be column major. To get the same result out of a multiplication of 2 column major matrices in a row major manner, you have to invert them. This test program shows what i mean:
package jpcttest;
import com.threed.jpct.*;
public class MatrixTest {
public static void main(String[] args) {
Matrix m1=new Matrix();
m1.rotateX(1);
Matrix m2=new Matrix();
m2.rotateY(2);
Matrix m3=m1.cloneMatrix();
m1.matMul(m2);
m3=flip(m3).invert3x3();
m2=flip(m2).invert3x3();
m3.matMul(m2);
System.out.println(m1);
System.out.println(m3);
}
private static Matrix flip(Matrix m) {
float[] d=m.getDump();
float[] d2=new float[d.length];
for (int y=0; y<4; y++) {
for (int x = 0; x < 4; x++) {
d2[y+x*4] = d[x+y*4];
}
}
m.setDump(d2);
return m;
}
}
Here we have 2 row major matrices m1 and m2. The matMulresult is...something. Then these matrices get flipped, i.e. they become column major. To get the same result out of the operation, you've to invert them.
Maybe this applies here too?

Im not going to pretend to understand exactly what you say, because I honestly dont have a lot of experience with 3d:)
However, all matrices used in my skeletal implementation are from jPCT.
I mix no constructs from the C++ version.
Only the general implementation of the skeletal engine.
Since all of the matrices are from the jPCT classes, I do not believe this could be the problem. I will post some graphics:)
I will be 1 minute. Thanks again for your help! Very much appreciated:).
EDIT:
Ah, you think the behaviour in this c++ opengl source code is different to the jPCT equivalent because it used column major vertices? And the invert seems to give the right result, because of the need to invert in these situations? hmm...
Matrix matrix;
SimpleVector bone1Vector,bone2Vector;
bone1Vector = new SimpleVector();
bone2Vector = new SimpleVector();
matrix = new Matrix();
matrix.rotateAxis(new SimpleVector(0.226520f,0.841552f,0.490387f),1.140979f);
//bone1Vector.matMul(matrix);
bone1Vector.add(new SimpleVector(1.985717f,1.083508f,0.000000f));
bone1Vector.matMul(worldTransformationMatrix);
// matrix = new Matrix();
// matrix.rotateAxis(new SimpleVector(0.772281f,0.325713f,0.545429f),2.895753f);
bone2Vector.add(new SimpleVector(0.000000f,3.701808f,0.000000f));
bone2Vector.matMul(matrix.invert3x3());
bone2Vector.matMul(worldTransformationMatrix);
bone2Vector.add(bone1Vector);
(http://www.rpwar.com/betafiles/files/cyberkilla/bone.png)
As you can see, the child bone is relative to the parent bone(bone 1).
In the xml file...
<bones>
<bone id="0" name="1">
<position x="1.985717" y="1.083508" z="0.000000"/>
<rotation angle="1.140979">
<axis x="0.226520" y="0.841552" z="0.490387"/>
</rotation>
</bone>
<bone id="1" name="2">
<position x="0.000000" y="3.701808" z="0.000000"/>
<rotation angle="2.895753">
<axis x="0.772281" y="0.325713" z="0.545429"/>
</rotation>
</bone>
</bones>
The axis of BONE 1( id=0 ) is applied to bone two, to get its absolute rotation.
Does this make my intentions more clear?
EDIT:
(http://futurerp.net/betafiles/files/cyberkilla/bone2.png)
This is the view from blender

Okay, okay, I dont think ive succeeded in explaining my problem:)
This code does not work.
It DOES interpolate the keyframes. It DOES rotate the bones.
BUT it ROTATES them around (0,0,0):) And not the actual pivot of the bone:(.
If you done understand the source code, thats fine. Ill eventually get it, i hope.
This is the website Im reading from:
http://rsn.gamedev.net/tutorials/ms3danim.asp
public void advanceAnimation()
{
SkeletalAnimation animation = animations[currentAnimation];
double time = timer.getTime();
if (time > animation.length)
{
if (animation.looping)
{
restartAnimation();
time = 0;
}
else
{
time = animation.length;
}
}
//Update the joints:)
for( int i = 0; i < bones.length; i++)
{
SimpleVector transVec = new SimpleVector();
Matrix transform = new Matrix();
int frame;
//Joint *pJoint = &m_pJoints[i];
SimpleBone bone = bones[i];
if (bone.rotationKeyframes[currentAnimation].length == 0 &&
bone.translationKeyframes[currentAnimation].length == 0)
{
bone.matrixFinal = bone.matrixAbsolute.cloneMatrix();
continue;
}
//Start with the keyframes:D
frame = bone.currentTranslationKeyframe;
while(frame < bone.translationKeyframes[currentAnimation].length &&
bone.translationKeyframes[currentAnimation][frame].time < time)
{
frame++;
}
bone.currentTranslationKeyframe = frame;
//System.out.println(time+""+ animation.length+""+currentAnimation+""+frame);
//TranslationKeyframe translationKeyframe = bone.translationKeyframes[currentAnimation][frame];
if (frame == 0)
{
transVec.set(bone.translationKeyframes[currentAnimation][0].translation);
}
else if (frame == bone.translationKeyframes[currentAnimation].length)
{
transVec.set(bone.translationKeyframes[currentAnimation][frame1].translation);
}
else
{
TranslationKeyframe curFrame = bone.translationKeyframes[currentAnimation][frame];
TranslationKeyframe prevFrame = bone.translationKeyframes[currentAnimation][frame1];
float timeDelta = (curFrame.time)(prevFrame.time);
float interpValue = (float)(time(prevFrame.time))/timeDelta;
SimpleVector curTranslation = curFrame.translation;
SimpleVector prevTranslation = prevFrame.translation;
transVec.x = prevTranslation.x + (curTranslation.x  prevTranslation.x) * interpValue;
transVec.y = prevTranslation.y + (curTranslation.y  prevTranslation.y) * interpValue;
transVec.z = prevTranslation.z + (curTranslation.z  prevTranslation.z) * interpValue;
}
frame = bone.currentRotationKeyframe;
while(frame < bone.rotationKeyframes[currentAnimation].length &&
bone.rotationKeyframes[currentAnimation][frame].time < time)
{
frame++;
}
bone.currentRotationKeyframe = frame;
if (frame == 0)
{
transform.matMul(bone.rotationKeyframes[currentAnimation][0].rotationMatrix);
}
else if (frame == bone.rotationKeyframes[currentAnimation].length)
{
transform.matMul(bone.rotationKeyframes[currentAnimation][frame1].rotationMatrix);
}
else
{
RotationKeyframe curFrame = bone.rotationKeyframes[currentAnimation][frame];
RotationKeyframe prevFrame = bone.rotationKeyframes[currentAnimation][frame1];
Matrix curMatrix = curFrame.rotationMatrix;
Matrix prevMatrix = prevFrame.rotationMatrix;
float timeDelta = (curFrame.time)(prevFrame.time);
float interpValue = (float)(time(prevFrame.time))/timeDelta;
Matrix matrix = new Matrix();
matrix.interpolate(prevMatrix,curMatrix, interpValue);
//transform.translate(bone.localTranslation);
transform.matMul(matrix);
// transform.translate(bone.localTranslation);
}
transform.translate(transVec);
Matrix relativeFinal = bone.matrixRelative.cloneMatrix();
relativeFinal.matMul(transform);
if(bone.parent == null)
{
bone.matrixFinal = relativeFinal;
}
else
{
bone.matrixFinal = bone.parent.matrixFinal.cloneMatrix();
bone.matrixFinal.matMul(relativeFinal);
}
}
}

Dooh! This is fooling me every time...well, not every time, but often enough. It's like with the camera and the world: You want to rotate the camera but what you are actually rotating is the world around it. The same applies here: You want to rotate the SimpleVector by an angle x, but you actually rotate the "world" (in this case the matrix) by x. Ehh...this is so damn difficult to explain. It really works best with the cameraworldapproach. So look at this:
(http://www.jpct.net/img/rotation.gif)
The black dot is the camera, btw... :wink:
In your case, you can fix this problem by either inverting the matrix or, the better solution IMHO, just rotate around angle. If you don't fully understand the problem, don't worry. It twists my head every time and i usually stop thinking about it once i understood it. Because if i do think further, i wouldn't understand it any longer... :wink:

Aha! I think I do understand!
In the camera example, your moving the camera down, but what is actually happening is the objects are moving up.
So the application of a matrix add complications:)
Is there a way to split an axis+angle into a vector of x,y,z, where each stores the rotational movement along each axis?
The OrgeXML format stores it as Vector(x,y,z) and an angle in radians.
Im sure you understand what i mean:)
So I can just use SimpleVectors, and apply rotations with rotate(x,y,z), or rotateX/Y/Z.
Finally, im getting somewhere:) Two days ive been on this!:)

Is there a way to split an axis+angle into a vector of x,y,z, where each stores the rotational movement along each axis?
Yes, i think you can derive the angles from a matrix (i've posted some code for this in the past somewhere)...but that would mean that you've to create the matrix first. And if you already have it, why not use it? Just don't use the angle but angle and you should be fine.

Excellent:) Thanks :)
EDIT:
Found this...
public SimpleVector deriveAngles(Matrix mat) {
SimpleVector s=new SimpleVector();
float[] m=mat.getDump();
s.x=(float) Math.atan(m[9]/m[10]);
s.y=(float) Math.asin(m[2]);
s.z=(float) Math.atan(m[4]/m[0]);
return s;
}

I dont believe it!!!! It works!!!!!!!!!!!!!!
Sorry about that:) I still dont believe it:) Im sure Ill discover a better way of doing this, but it looks great for now! Ill screenshot it now.
Ill also document the source code, change it to your xml parser ,and release the code!!!!
Oh, wow!:) You know, there is a lot to be said for trying to do something yourself.
Now we have an essentially working skeletal api:)
Im sure youll be able to go through it, and correct obvious mistakes, or rather, make things more efficient.
The excitement is quite immense. Well worth working until 2:42am for:)