Welcome Guest [Log In] [Register]
We hope you enjoy your visit.


You're currently viewing the Ultimate 3D Community as a guest. This means that you can only read posts, but can not create posts or topics by yourself. To be able to post you need to register. Then you can participate in the community active and use many member-only features such as customizing your profile, sending personal messages, and voting in polls. Registration is simple, fast, and completely free.

Join our community!

If you are already a member please log in to your account to access all of our features:

Username:   Password:
Add Reply
Matrix Transformations; need to rotate around any given axis
Topic Started: Oct 1 2008, 06:42 AM (462 Views)
EbTech
Newbie
[ * ]
Hello, I'm new to this forum and to Ultimate 3D in general.

Anyway I need some help with a particular matrix transformation. Basically, I want a way to replicate the effect of Game Maker's built-in d3d_transform_set_rotation_axis(xa,ya,za,angle) function, which rotates an object about a custom vector (i.e. axis) by a given angle.

I'm only starting to learn basic linear algebra. Fortunately, I managed to Google up a formula for the general 4X4 matrix which should do the required transformation. My only problem is that I can't seem to find a function in U3D's documentation that would let me create this transformation matrix. :dunno:

In case anyone's wondering, I'm trying to make a realistic lightsaber duelling game. The incomplete version (without U3D) is posted here: Cybersword Duels

By the way, do the default rotx, roty and rotz variables refer to Euler angles? And is there a way to rotate backgrounds (sky spheres) to give, for example, the illusion of wind?
Edited by EbTech, Oct 1 2008, 06:44 AM.
Offline Profile Quote Post Goto Top
 
MysteriXYZ
Member Avatar
Master Matrix Masher
[ *  *  *  *  *  * ]
EbTech
Oct 1 2008, 06:42 AM
Hello, I'm new to this forum and to Ultimate 3D in general.
Hi EbTech, and welcome to the Ultimate 3D Community :) .

EbTech
Oct 1 2008, 06:42 AM
Anyway I need some help with a particular matrix transformation. Basically, I want a way to replicate the effect of Game Maker's built-in d3d_transform_set_rotation_axis(xa,ya,za,angle) function, which rotates an object about a custom vector (i.e. axis) by a given angle.

I'm only starting to learn basic linear algebra. Fortunately, I managed to Google up a formula for the general 4X4 matrix which should do the required transformation. My only problem is that I can't seem to find a function in U3D's documentation that would let me create this transformation matrix. :dunno:
To get that transformation matrix, you need to create a couple of others first and transform them with each other.

Here's a script that returns a vector whose components you need to assign respectively to rotx/y/z of your object:
Code:
 
//CustomAxisRotate(RotAnglesVect,AxisVectX,AxisVectY,AxisVectZ,Angle)
//Computes the world-space rotation angles corresponding to the rotation
//around the custom axis, given by the AxisVectX, AxisVectY and AxisVectZ
//vector components, by the given Angle.
//Returns the corresponding Euler angles through the given RotAnglesVect
//vector. If RotAnglesVect is negative, a new vector is returned.
var AxisVect,Longi,Lati,AxisMatr,InvAxisMatr,RotMatr,AngleVect;

//create the vector that points in the direction of the custom axis of
//rotation, using the given vector components
AxisVect=CreateVector(-1,argument1,argument2,argument3);
//retrieve the direction of the custom axis from that vector, given by
//a longitude...
Longi=CalculateVectorLongitude(AxisVect);
//...and a latitude...
Lati=CalculateVectorLatitude(AxisVect);
ReleaseVector(AxisVect);
//...and create a rotation matrix that describes the axis direction;
//this is the "axis-to-world" matrix
AxisMatr=CreateRotationMatrix(-1,Longi,Lati,0);
//now create another rotation matrix to describe the rotation around the
//custom axis (relative to that axis);
//this is the "axisrotz" matrix:
//since the direction of the custom axis is measured relative to the positive
//Y-axis and rotating around this Y-axis is done using rotz, the given Angle
//has to be interpreted as a rotz rotation relative to the custom axis
RotMatr=CreateRotationMatrix(-1,0,0,argument4);
//to rotate around the custom axis as if it were the world Y-axis, the custom
//axis direction has to be inverted;
//this is the "world-to-axis" matrix
InvAxisMatr=InvertMatrix(-1,AxisMatr);
//transform the world-to-axis matrix with the axisrotz matrix to
//obtain the rotation-to-axis matrix
TransformMatrix(RotMatr,InvAxisMatr,RotMatr);
ReleaseMatrix(InvAxisMatr);
//to get the needed rotation-to-world matrix, transform the rotation-to-axis
//matrix with the axis-to-world matrix
TransformMatrix(RotMatr,RotMatr,AxisMatr);
ReleaseMatrix(AxisMatr);
//retrieve from that matrix the Euler angles that correspond to the rotation
//described by it
AngleVect=ComputeMatrixRotationAngles(argument0,RotMatr);
ReleaseMatrix(RotMatr);

return AngleVect;


EbTech
Oct 1 2008, 06:42 AM
By the way, do the default rotx, roty and rotz variables refer to Euler angles?
Yes, rotx (longitude) is the rotation angle in degrees around the world X-axis, roty (latitude) is the Euler angle around the Z-axis and rotz is the rotation performed around the Y-axis.
The order in which the rotations are applied is as follows:
1) rotz
2) rotx
3) roty

Also keep in mind that U3D uses a left-handed coordinate system, so to easily remember the positive sense of a rotation, just imagine that your left thumb points in the direction of the axis of rotation and curling your left fingers will mimic a positive rotation around that axis.

Note also that the Y-axis is mirrored compared to the one in GM's Room Editor, which you can therefore interpret as a bottom-up view instead of a top-down one.

EbTech
Oct 1 2008, 06:42 AM
And is there a way to rotate backgrounds (sky spheres) to give, for example, the illusion of wind?
You can use model objects to create custom backgrounds, which can be manipulated any way you want (see the end of this chapter of the documentation).
U3D is like candy; after extensive consumption, it's Best to brush.
Offline Profile Quote Post Goto Top
 
EbTech
Newbie
[ * ]
Wow, thank you so much! Ironically I have to prepare for my first linear algebra test on Friday, but after that I should finally be able to continue working on my game!

My problem with Euler angles was that they lock up at the poles. I don't know whether this is related to gimbal lock, since I was only rotating around two of the axis, but either way it was making the lightsaber controls feel very unintuitive. Your script fixed this problem. :D

Quote:
 
You can use model objects to create custom backgrounds, which can be manipulated any way you want (see the end of this chapter of the documentation).

I've never used models before, but I'll have to give it a try eventually. Do you know of any good tutorials that explain how to make simple polygonal shapes? By that I mean, how would one draw polygons or ellipsoids out of triangles? I'm already familiar with the U3D functions that are used to set individual vertices and triangles after calling CreateEmptyMesh().

By the way, it doesn't seem like you ever used the RotAnglesVect parameter in your example. Was that intentionally left for me to implement? :think:
Edited by EbTech, Oct 2 2008, 08:08 AM.
Offline Profile Quote Post Goto Top
 
MysteriXYZ
Member Avatar
Master Matrix Masher
[ *  *  *  *  *  * ]
EbTech
Oct 2 2008, 08:01 AM
Wow, thank you so much! Ironically I have to prepare for my first linear algebra test on Friday, but after that I should finally be able to continue working on my game!

My problem with Euler angles was that they lock up at the poles. I don't know whether this is related to gimbal lock, since I was only rotating around two of the axis, but either way it was making the lightsaber controls feel very unintuitive. Your script fixed this problem. :D
Glad to hear it ^_^ .
EbTech
Oct 2 2008, 08:01 AM
I've never used models before, but I'll have to give it a try eventually. Do you know of any good tutorials that explain how to make simple polygonal shapes? By that I mean, how would one draw polygons or ellipsoids out of triangles? I'm already familiar with the U3D functions that are used to set individual vertices and triangles after calling CreateEmptyMesh().
Although it's possible to create models from scratch with U3D, I wouldn't recommend it. Even something as simple as a sphere already requires a lot of thought (as you will see in the script below), and the most difficult part is setting the texture coordinates. If you wanted to project the texture in specific ways, this would become very complicated. Worst of all is that texture seams (where you want part of a texture to meet another part of a texture) require additional ("double") vertices. This is because one vertex can only have one pair of UV texture coordinates.

So I would urge you to use a dedicated modelling program that you feel comfortable with, and export to one of the formats that U3D supports.

Anyway, here's your ellipsoid, created entirely in U3D ^_^ :
Code:
 
//CreateEllipsoid(RadiusH,RadiusV,Resolution)
//Creates an ellipsoidal mesh with a horizontal radius given by RadiusH and
//a vertical radius given by RadiusV. The given Resolution determines how
//smooth the ellipsoid will be; it should be an even number, not smaller than
//4.
var RadiusH,RadiusV,Resolution;

RadiusH=argument0;
RadiusV=argument1;
Resolution=argument2;
material_count=1;
//the ellipsoid consists of a number of vertex rings and 2 poles, which are
//actually collapsed rings;
//each ring is made up of a number of vertices equal to the given Resolution,
//while the number of rings equals Resolution/2+1;
//however, an additional row of Resolution/2+1 vertices is needed to allow
//a texture to be wrapped around the model (the UV-coordinates of the left
//and right texture map edges cannot be assigned to the same row of vertices)
vertex_count=(Resolution+1)*(Resolution/2+1);
triangle_count=(Resolution/2-1)*Resolution*2;
CreateEmptyMesh();

var Angle,RingRadius,RingHeight,i,j,X,Y,U,V,Index;

Angle=2*pi/Resolution;
LockMesh(0,0,0,0);
Index=0;
//set the top pole vertices
for (i=0; i < Resolution+1; i+=1)
{
U=i/Resolution;
SetLockedMeshVertex(0,Index,0,0);
SetLockedMeshVertex(0,Index,2,0);
SetLockedMeshVertex(0,Index,1,RadiusV);
SetLockedMeshVertex(0,Index,6,U);
SetLockedMeshVertex(0,Index,7,0);
Index+=1;
}
//set the vertex rings
for (i=1; i < Resolution/2; i+=1)
{
RingRadius=sin(Angle*i)*RadiusH;
RingHeight=cos(Angle*i)*RadiusV;
V=2*i/Resolution;
for (j=0; j < Resolution+1; j+=1)
{
X=cos(Angle*j)*RingRadius;
Y=sin(Angle*j)*RingRadius;
U=j/Resolution;
SetLockedMeshVertex(0,Index,0,X);
SetLockedMeshVertex(0,Index,2,Y);
SetLockedMeshVertex(0,Index,1,RingHeight);
SetLockedMeshVertex(0,Index,6,U);
SetLockedMeshVertex(0,Index,7,V);
Index+=1;
}
}
//set the bottom pole vertices
for (i=0; i < Resolution+1; i+=1)
{
U=i/Resolution;
SetLockedMeshVertex(0,Index,0,0);
SetLockedMeshVertex(0,Index,2,0);
SetLockedMeshVertex(0,Index,1,-RadiusV);
SetLockedMeshVertex(0,Index,6,U);
SetLockedMeshVertex(0,Index,7,1);
Index+=1;
}

//The triangles should be defined by passing the vertex indexes in clockwise
//order, as seen from a specific point of view, to make them visible from
//that viewpoint (otherwise they will be invisible due to backface culling).
var V1,V2,V3;

Index=0;
//define the triangles at the top pole
for (i=0; i < Resolution; i+=1)
{
V1=i;
V2=V1+Resolution+2;
V3=V1+Resolution+1;
SetLockedMeshTriangle(0,Index,V1,V2,V3);
Index+=1;
}
//define the main triangles
for (i=1; i < Resolution/2-1; i+=1)
{
for (j=0; j < Resolution; j+=1)
{
V1=i*(Resolution+1)+j;
V2=V1+1;
V3=V1+Resolution+1;
SetLockedMeshTriangle(0,Index,V1,V2,V3);
Index+=1;
V1=V3+1;
SetLockedMeshTriangle(0,Index,V3,V2,V1);
Index+=1;
}
}
//define the triangles at the bottom pole
for (i=vertex_count-Resolution; i < vertex_count; i+=1)
{
V1=i;
V2=V1-Resolution-2;
V3=V1-Resolution-1;
SetLockedMeshTriangle(0,Index,V1,V2,V3);
Index+=1;
}

//finalize the ellipsoid mesh
UnlockMesh(0);
RecalculateNormals();

After calling this script in the Create Event of an object, you can set its texture like this:
Code:
 
SetMaterialStageTexture(0,0,TexID);
where TexID is the index you assigned to the texture when loading it with LoadTexture(<TextureFile>,TexID).

EbTech
Oct 2 2008, 08:01 AM
By the way, it doesn't seem like you ever used the RotAnglesVect parameter in your example. Was that intentionally left for me to implement? :think:
It is used, as "argument0" in the following line near the end of the script:
Code:
 
AngleVect=ComputeMatrixRotationAngles(argument0,RotMatr);
So if that parameter is the ID of an already previously created vector, that vector will contain the 3 angles needed to rotate your object. If the parameter is a negative number like -1, a new vector will be created and its ID returned by ComputeMatrixRotationAngles(...), which is then assigned to AngleVect, which is then returned by my script.

So if you want to use an existing vector to retrieve the rotation angles, call my script as follows:
Code:
 
CustomAxisRotate(RotAnglesVect,AxisVectX,AxisVectY,AxisVectZ,Angle);

If you want to create a new vector for that, call it like this:
Code:
 
RotAnglesVect=CustomAxisRotate(-1,AxisVectX,AxisVectY,AxisVectZ,Angle);
U3D is like candy; after extensive consumption, it's Best to brush.
Offline Profile Quote Post Goto Top
 
EbTech
Newbie
[ * ]
MysteriXYZ
Oct 2 2008, 07:28 PM
It is used, as "argument0" in the following line near the end of the script:
Code:
 
AngleVect=ComputeMatrixRotationAngles(argument0,RotMatr);
So if that parameter is the ID of an already previously created vector, that vector will contain the 3 angles needed to rotate your object. If the parameter is a negative number like -1, a new vector will be created and its ID returned by ComputeMatrixRotationAngles(...), which is then assigned to AngleVect, which is then returned by my script.

So if you want to use an existing vector to retrieve the rotation angles, call my script as follows:
Code:
 
CustomAxisRotate(RotAnglesVect,AxisVectX,AxisVectY,AxisVectZ,Angle);

If you want to create a new vector for that, call it like this:
Code:
 
RotAnglesVect=CustomAxisRotate(-1,AxisVectX,AxisVectY,AxisVectZ,Angle);
Oh okay, that makes sense.

Ouch! You're right, the ellipsoid script is pretty complicated! Still, I can definitely use this to make a simple wind effect (by rotating a cloudy sky sphere) and for thunder (by creating a partially transparent white sphere beneath the sky sphere). I also managed to build cylinders in U3D without too much trouble, which when combined with your rotation script and my own collision script (which works by calculating the distance between two line segments) end up making very realistic lightsabers! Okay well, I guess that's everything I needed for now. :)

I might try Anim8or. My artistic skills are terrible, so I probably won't be making anything complicated.

I might also add a two-player online mode to this game after it's done. GM's built-in mplay functions seem to be rather problematic, requiring port forwarding and updating shared data far too slowly for a high-speed action game. 39dll seems to be the most popular choice for online games... maybe I'll go learn that.

In case you'd like to see how this game progresses, you can check the YoYoGames link in a few months. ;)

Edit: Do you know if Game Maker uses a similar algorithm to draw ellipsoids? I notice that yours only uses 2 radii (not that I want more, just curious.)
Edited by EbTech, Oct 3 2008, 03:27 AM.
Offline Profile Quote Post Goto Top
 
MysteriXYZ
Member Avatar
Master Matrix Masher
[ *  *  *  *  *  * ]
EbTech
Oct 2 2008, 09:33 PM
Do you know if Game Maker uses a similar algorithm to draw ellipsoids? I notice that yours only uses 2 radii (not that I want more, just curious.)
To be honest, I never really checked out GM's d3d functions, as it was obvious to me that it would be very hard to do anything worthwile with them.

To add a third radius to my script is quite easy; replace RadiusH and RadiusV with RadiusX, RadiusY and RadiusZ which you then use like this:
Code:
 
var RadiusX,RadiusY,RadiusZ,Resolution;

RadiusX=argument0;
RadiusY=argument1;
RadiusZ=argument2;
Resolution=argument3;
...
//set the vertex rings
for (i=1; i < Resolution/2; i+=1)
{
RingRadiusX=sin(Angle*i)*RadiusX;
RingRadiusY=sin(Angle*i)*RadiusY;
RingHeight=cos(Angle*i)*RadiusZ;
V=2*i/Resolution;
for (j=0; j < Resolution+1; j+=1)
{
X=cos(Angle*j)*RingRadiusX;
Y=sin(Angle*j)*RingRadiusY;
...

Also replace both occurrences of "RadiusV" with "RadiusZ" and you're set.

EbTech
Oct 2 2008, 09:33 PM
In case you'd like to see how this game progresses, you can check the YoYoGames link in a few months. ;)

I'll be sure to check it out :) . Good luck :thumb_up: !
U3D is like candy; after extensive consumption, it's Best to brush.
Offline Profile Quote Post Goto Top
 
« Previous Topic · Questions about Ultimate 3D · Next Topic »
Add Reply