I have a circular shape object, which I want to rotate like a fan along it's own axis.
I can change the rotation in any direction i.e. dx, dy, dz
using my transformation matrix.
The following it's the code:
Matrix4f matrix = new Matrix4f();
matrix.setIdentity();
Matrix4f.translate(translation, matrix, matrix);
Matrix4f.rotate((float) Math.toRadians(rx), new Vector3f(1,0,0), matrix, matrix);
Matrix4f.rotate((float) Math.toRadians(ry), new Vector3f(0,1,0), matrix, matrix);
Matrix4f.rotate((float) Math.toRadians(rz), new Vector3f(0,0,1), matrix, matrix);
Matrix4f.scale(new Vector3f(scale,scale,scale), matrix, matrix);
My vertex code:
vec4 worldPosition = transformationMatrix * vec4(position,1.0);
vec4 positionRelativeToCam = viewMatrix*worldPosition;
gl_Position = projectionMatrix *positionRelativeToCam;
Main Game Loop:
Object.increaseRotation(dxf,dyf,dzf);
But, it's not rotating along it's own axis. What am I missing here? I want something like this. Please Help
You should Get rid of Euler angles for this.
Object/mesh geometry
You need to be aware of how your object is oriented in its local space. For example let assume this:
So in this case the main rotation is around axis
z
. If your mesh is defined so the rotation axis is not aligned to any of the axises (x,y
orz
) or the center point is not(0,0,0)
than that will cause you problems. The remedy is either change your mesh geometry or create a special constant transform matrixM0
that will transform all vertexes from mesh LCS (local coordinate system) to a different one that is axis aligned and center of rotation has zero in the axis which is also the axis of rotation.In the latter case any operation on object matrix
M
would be done like this:or in reverse or in inverse (depends on your matrix/vertex multiplication and row/column order conventions). If you got your mesh already centered and axis aligned then do just this instead:
The
operation
is transform matrix of the change increment (for example rotation matrix). TheM
is the object current transform matrix from #2 andM'
is its new version after applyingoperation
.Object transform matrix
You need single Transform matrix for each object you got. This will hold the position and orientation of your object LCS so it can be converted to world/scene GCS (global coordinate system) or its parent object LCS
rotating your object around its local axis of rotation
As in the Understanding 4x4 homogenous transform matrices is mentioned for standard OpenGL matrix convetions you need to do this:
Where
M
is current object transform matrix andM'
is the new version of it after rotation. This is the thing you got different. You are using Euler anglesrx,ry,rz
instead of accumulating the rotations incrementally. You can not do this with Euler angles in any sane and robust way! Even if many modern games and apps are still trying hard to do it (and failing for years).So what to do to get rid of Euler angles:
You must have persistent/global/static matrix
M
per objectinstead of local instance per render so you need to init it just once instead of clearing it on per frame basis.
On animation update apply operation you need
so:
Where
angspeed
is in[rad/second]
or[deg/second]
of your fan speed anddt
is time elapsed in[seconds]
. For example if you do this in timer thendt
is the timer interval. For variable times you can measure the time elapsed (it is platform dependent I usually use PerformanceTimers or RDTSC).You can stack more operations on top of itself (for example your fan can also turning back and forward around
y
axis to cover more area.For object direct control (by keyboard,mouse or joystick) just add things like:
Where
keys
is my key map holding on/off state for every key in the keyboard (so I can use more keys at once). This code just control object with arrows. For more info on the subject see related QA:Computer Graphics: Moving in the world
Preserve accuracy
With incremental changes there is a risc of loosing precision due to floating point errors. So add a counter to your matrix class which counts how many times it has been changed (incremental operation applied) and if some constant count hit (for example 128 operations) Normalize your matrix.
To do that you need to ensure orthogonormality of your matrix. So eaxh axis vector
X,Y,Z
must be perpendicular to the other two and its size has to be unit. I do it like this:Z
axis as that is usually my main axis in my meshes (viewing direction, rotation axis etc). so just make this vector unitZ = Z/|Z|
X = (+/-) Z x Y
andY = (+/-) Z x X
and also normalize them tooX = X/|X|
andY = Y/|Y|
. The(+/-)
is there because I do not know your coordinate system conventions and the cross product can produce opposite vector to your original direction so if the direction is opposite change the multiplication order or negate the result (this is done while coding time not in runtime!).Here example in C++ how my orthonormal normalization is done:
Where
axis?_get/set(a)
just get/seta
as axis from/to your matrix. Thevector_one(a,b)
returnsa = b/|b|
andvector_mul(a,b,c)
returna = b x c