Get rotation about y-axis from rotation matrix - matrix

i can't figure out how to extract the rotation around the global y-axis from the model matrix of my object.
I have the current model matrix from my object as an glm::mat4 and i need to cancel out the rotation around the y-axis.
Are there any functions in glm i haven't noticed?

You have to convert the rotation part of the matrix to Euler angles. It is not a trivial operation. I don't know if "glm" have a function for that, but there is a code (other methods may exists) to convert rotation part of a 4x4 matrix to X, Y and Z Euler angles:
function Matrix4ToEuler(OutEuler, InMatrix4)
{
let cy = Math.sqrt(InMatrix4[0] * InMatrix4[0] + InMatrix4[1] * InMatrix4[1]);
if(cy > 0.001) {
OutEuler.x = Math.atan2(InMatrix4[6], InMatrix4[10]);
OutEuler.y = Math.atan2(-InMatrix4[2], cy);
OutEuler.z = Math.atan2(InMatrix4[1], InMatrix4[0]);
} else {
OutEuler.x = Math.atan2(-InMatrix4[9], InMatrix4[5]);
OutEuler.y = Math.atan2(-InMatrix4[2], cy);
OutEuler.z = 0;
}
}

Related

Monogame - Rotate Sprite around centre of screen and itself

I have a problem and although I serached everywhere I couldn't find a solution.
I have a stacked sprite and I'm rotating this sprite around the center of the screen. So I iterate over a list of sprites (stacked) and increase the y-coordinate by 2 every loop (rotation is increased step by step by 0.01f outside of the loop):
foreach(var s in stacked)
{
Vector2 origin = new Vector2(Basic.width / 2, Basic.height / 2);
Rectangle newPosition = new Rectangle(position.X, position.Y - y, position.Width, position.Height);
float angle = 0f;
Matrix transform = Matrix.CreateTranslation(-origin.X, -origin.Y, 0f) *
Matrix.CreateRotationZ(rotation) *
Matrix.CreateTranslation(origin.X, origin.Y, 0f);
Vector2 pos = new Vector2(newPosition.X, newPosition.Y);
pos = Vector2.Transform(pos, transform);
newPosition.X = (int)pos.X;
newPosition.Y = (int)pos.Y;
angle += rotation;
s.Draw(newPosition, origin, angle, Color.White);
y += 2;
}
This works fine. But now my problem. I want not only to rotate the sprite around the center of the screen but also around itself. How to achieve this? I can only set one origin and one rotation per Draw. I would like to rotate the sprite around the origin 'Basic.width / 2, Basic.height / 2' and while it rotates, around 'position.Width / 2, position.Height / 2'. With different rotation speed each. How is this possible?
Thank you in advance!
Just to be clear:
When using SpriteBatch.Draw() with origin and angle, there is only one rotation: the final angle of the sprite.
The other rotations are positional offsets.
The origin in the Draw() call is a translation, rotation, translate back. Your transform matrix shows this quite well:
Matrix transform = Matrix.CreateTranslation(-origin.X, -origin.Y, 0f) *
Matrix.CreateRotationZ(rotation) *
Matrix.CreateTranslation(origin.X, origin.Y, 0f);
//Class level variables:
float ScreenRotation, ScreenRotationSpeed;
float ObjectRotation, ObjectRotationSpeed;
Vector2 ScreenOrigin, SpriteOrigin;
// ...
// In constructor and resize events:
ScreenOrigin = new Vector2(Basic.width <<1, Basic.height <<1);
// shifts are faster for `int` type. If "Basic.width" is `float`:
//ScreenOrigin = new Vector2(Basic.width, Basic.height) * 0.5f;
// In Update():
ScreenRotation += ScreenRotationSpeed; // * gameTime.ElapsedGameTime.Seconds; // for FPS invariant speed where speed = 60 * single frame speed
ObjectRotation+= ObjectRotationSpeed;
//Calculate the screen center rotation once per step
Matrix baseTransform = Matrix.CreateTranslation(-ScreenOrigin.X, -ScreenOrigin.Y, 0f) *
Matrix.CreateRotationZ(ScreenRotation) *
Matrix.CreateTranslation(ScreenOrigin.X, ScreenOrigin.Y, 0f);
// In Draw() at the start of your code snippet posted:
// moved outside of the loop for a translationally invariant vertical y interpretation
// or move it inside the loop and apply -y to position.Y for an elliptical effect
Vector2 ObjectOrigin = new Vector2(position.X, position.Y);
Matrix transform = baseTransform *
Matrix.CreateTranslation(-ObjectOrigin.X, -ObjectOrigin.Y, 0f) *
Matrix.CreateRotationZ(ObjectRotation) *
Matrix.CreateTranslation(ObjectOrigin.X, ObjectOrigin.Y, 0f);
foreach(var s in stacked)
{
Vector2 pos = new Vector2(ObjectOrigin.X, ObjectOrigin.Y - y);
pos = Vector2.Transform(pos, transform);
float DrawAngle = ObjectRotation;
// or float DrawAngle = ScreenRotation;
// or float DrawAngle = ScreenRotation + ObjectRotation;
// or float DrawAngle = 0;
s.Draw(pos, SpriteOrigin, DrawAngle, Color.White);
}
I suggest moving the Draw() parameter away from destinationRectangle and use the Vector2 position directly with scaling. Rotations within square rectangles can differ up to SQRT(2) in aspect ratio, i.e. stretching/squashing. Using Vector2 incurs a cost of higher collision complexity.
I am sorry for the ors, but without complete knowledge of the problem...YMMV
In my 2D projects, I use the vector form of polar coordinates.
The Matrix class requires more calculations than the polar equivalents in 2D. Matrix operates in 3D, wasting cycles calculating Z components.
With normalized direction vectors (cos t,sin t) and a radius(vector length),in many cases I use Vector2.LengthSquared() to avoid the square root when possible.
The only time I have used Matrices in 2D is display projection matrix(entire SpriteBatch) and Mouse and TouchScreen input deprojection(times the inverse of the projection matrix)

How are should openGL matrices be created? Is there something wrong with my multiplication or order of translation/rotation?

So basically I'm trying to make a simple open gl 3D graphics engine using my own linear algebra to make projection and transformation matrices. OpenGL has a class called glUniformMatrix4fv() which I use to pass the matrices as a float[].
Here is my "Matrix" class to construct a float[] for that openGl method:
private float[] m= {
1,0,0,0,
0,1,0,0,
0,0,1,0,
0,0,0,1
};
public Matrix() {}
public Matrix(float[] m) {
this.m=m;
}
//gets value at x,y coords of matrix
public float getValue(int x,int y) {
return m[y*4 + x];
}
//sets value of x,y coord to n
public void setValue(int x,int y,float n) {
m[y*4 + x]=n;
}
To construct a transformation for object translation and rotation I first create translation Matrix (s is for scale). Also a vertex is basically just a size 4 float array I have my Vector/vertex info in:
public Matrix createTranslationMatrix(Vertex pos,float s) {
Matrix m=new Matrix();
m.setValue(0,0,s);
m.setValue(1,1,s);
m.setValue(2,2,s);
m.setValue(3,0,pos.getValue(0));
m.setValue(3,1,pos.getValue(1));
m.setValue(3,2,pos.getValue(2));
return m;
}
Then I create a rotation matrix which is a combo of x, y, and z rotation of object around origin
public Matrix createRotationMatrix(Vertex rot) {
//if rotation is screwed up maybe mess around with order of these :)
Matrix rotX=createRotationMatrixX(rot.getValue(0));
Matrix rotY=createRotationMatrixY(rot.getValue(1));
Matrix rotZ=createRotationMatrixZ(rot.getValue(2));
Matrix returnValue=multiply(rotX,rotY);
returnValue=multiply(returnValue,rotZ);
return returnValue;
}
private Matrix createRotationMatrixX(float num) {
float n=num;
n=(float)Math.toRadians(n);
Matrix rot=new Matrix();
rot.setValue(1, 1, (float)Math.cos(n));
rot.setValue(1, 2, (float)Math.sin(n));
rot.setValue(2, 1, (float)-Math.sin(n));
rot.setValue(2, 2, (float)Math.cos(n));
return rot;
}
//rotation mat Y
private Matrix createRotationMatrixY(float num) {
float n=num;
n=(float)Math.toRadians(n);
Matrix rot=new Matrix();
rot.setValue(0, 0, (float)Math.cos(n));
rot.setValue(0, 2, (float)-Math.sin(n));
rot.setValue(2, 0, (float)Math.sin(n));
rot.setValue(2, 2, (float)Math.cos(n));
return rot;
}
//rotation mat Z
private Matrix createRotationMatrixZ(float num) {
float n=num;
n=(float)Math.toRadians(n);
Matrix rot=new Matrix();
rot.setValue(0, 0, (float)Math.cos(n));
rot.setValue(0, 1, (float)Math.sin(n));
rot.setValue(1, 0, (float)-Math.sin(n));
rot.setValue(1, 1, (float)Math.cos(n));
return rot;
}
I combine the translation and create my objectTransform float[] using a matrix with multiply(rotationMat,translationMat):
public Matrix multiply(Matrix a, Matrix b){
Matrix m=new Matrix();
for(int y=0;y<4;y++) {
for(int x=0;x<4;x++) {
//if this doesn't work maybe try switching x and y around?
m.setValue(x,y,a.getValue(x,0)*b.getValue(0,y) + a.getValue(x,1)*b.getValue(1,y) + a.getValue(x,2)*b.getValue(2,y) + a.getValue(x,3)*b.getValue(3, y));
}
}
return m;
}
And my code for my worldTransorm is defined from by combining a transformation with negative values for position and rotation (so it moves vertex and rotates opposite from camera position and rotation), then combinging rotation and transformation like so multiply(translationMat,rotationMat) , so it theoretically moves opposite camera pos, THEN rotates opposite camera rotation.
then I create my projection using this function:
public Matrix createProjectionMatrix(float fov, float aspectRatio, float near, float far) {
float fovRad=1/(float)Math.tan(Math.toRadians(fov*.5));
Matrix projection=new Matrix(base);
projection.setValue(0,0,aspectRatio*fovRad);
projection.setValue(1,1,fovRad);
projection.setValue(2,2,far/(far-near));
projection.setValue(2,3,(-far*near)/(far-near));
projection.setValue(3,2,1);
projection.setValue(3,3,0);
return projection;
}
I combine my projection , worldTransform, and objectTransform with my Vec3 position (vector with mesh coordinates I import). These are all multiplied together in my openGL shader class like so:
gl_Position=projection * worldTransform * objectTransform * vec4(position,1);
Write now if I back my camera up by 3, rotate it around with hopes of finding the "triangle" mesh I made
float[] verts= {
//top left tri
-.5f,-.5f,0,
0,.5f,0,
.5f,-.5f,0,
};
Then I get a really small pixel moving really fast accross my screen from top to bottom. I also have the object spinning, but that (if my code worked properly) shouldn't be an issue, but if I don't have the object spinning, then I don't see any pixel at all. So my thinking is the object transformation is applying like the world transormation should be working, moving the vertex by "translation" then rotating it, or the triangle is really small and not scaled properly (do I have to offset it somehow?), but then it shouldn't be flying off the screen repeatedly as if its rotating around the camera. I've tried switching multiplication of translation and rotation for both types of transforms, but either the triangle doesn't appear at all or I just see a teensy tiny little pixel, almost orbitting the camera at high speeds (when I should see just the triangle and camera rotating seperately)
I know its a lot to ask but what am I doing wrong? Do I need to transpose something? Is my projection matrix out of wack? I feel like everything should be right :(

Generating a pixel-based spiral gradient

I have a program that creates pixel-based gradients (meaning it calculates the step in the gradient for each pixel, then calculates the colour at that step, then gives the pixel that colour).
I'd like to implement spiral gradients (such as below).
My program can create conic gradients (as below), where each pixel is assigned a step in the gradient according to the angle between it and the midpoint (effectively mapping the midpoint-pixel angle [0...2PI] to [0...1]).
It would seem to me that a spiral gradient is a conic gradient with some additional function applied to it, where the gradient step for a given pixel depends not only on the angle, but on some additional non-linear function applied to the euclidean distance between the midpoint and pixel.
I envisage that a solution would take the original (x, y) pixel coordinate and displace it by some amounts in the x and y axes resulting in a new coordinate (x2, y2). Then, for each pixel, I'd simply calculate the angle between the midPoint and its new displaced coordinate (x2, y2) and use this angle as the gradient step for that pixel. But it's this displacement function that I need help with... of course, there may be other, better ways.
Below is a simple white-to-black conic gradient. I show how I imagine the displacement would work, but its the specifics about this function (the non-linearity), that I'm unable to implement.
My code for conic gradient:
public void conicGradient(Gradient gradient, PVector midPoint, float angle) {
float rise, run;
double t = 0;
for (int y = 0, x; y < imageHeight; ++y) {
rise = midPoint.y - y;
run = midPoint.x;
for (x = 0; x < imageWidth; ++x) {
t = Functions.fastAtan2(rise, run) + Math.PI - angle;
// Ensure a positive value if angle is negative.
t = Functions.floorMod(t, PConstants.TWO_PI);
// Divide by TWO_PI to get value in range 0...1
step = t *= INV_TWO_PI;
pixels[imageWidth * y + x] = gradient.ColorAt(step); // pixels is 1D pixel array
run -= 1;
}
}
}
By eye, it looks like after t = ... fastAtan2..., you just need:
t += PConstants.TWO_PI * Math.sqrt( (rise*rise + run*run) / (imageWidth * imageWidth + imageHeight * imageHeight) )
This just adds the distance from the center to the angle, with appropriate scaling.

Apply matrix transformation to a sphere

I have a Sphere structure that looks like this
struct Sphere {
vec3 _center;
float _radius;
};
How do I apply a 4x4 transformation matrix to that sphere? The matrix may contain a scale factor, a rotation (which will obviously will not affect the sphere) and a translation.
The current approach I'm using contains three length() methods (that have sqrt() in them) which are pretty slow.
glm::vec3 extractTranslation(const glm::mat4 &m)
{
glm::vec3 translation;
// Extract the translation
translation.x = m[3][0];
translation.y = m[3][1];
translation.z = m[3][2];
return translation;
}
glm::vec3 extractScale(const glm::mat4 &m) //should work only if matrix is calculated as M = T * R * S
{
glm::vec3 scale;
scale.x = glm::length( glm::vec3(m[0][0], m[0][1], m[0][2]) );
scale.y = glm::length( glm::vec3(m[1][0], m[1][1], m[1][2]) );
scale.z = glm::length( glm::vec3(m[2][0], m[2][1], m[2][2]) );
return scale;
}
float extractLargestScale(const glm::mat4 &m)
{
glm::vec3 scale = extractScale(m);
return glm::max(scale.x, glm::max(scale.y, scale.z));
}
void Sphere::applyTransformation(const glm::mat4 &transformation)
{
glm::vec4 center = transformation * glm::vec4(_center, 1.0f);
float largestScale = extractLargestScale(transformation);
set(glm::vec3(center)/* / center.w */, _radius * largestScale);
}
I wonder if anyone knows of a more efficient way to do this?
This is a question about efficiency and specifically to avoid doing the square root. One idea would be to defer doing the square root until the last moment. Since length and length squared are increasing functions starting at 0, comparing length squared is the same as comparing length. So you could avoid the three calls to length and make it one.
#include <glm/gtx/norm.hpp>
#include <algorithm>
glm::vec3 extractScale(const glm::mat4 &m)
{
// length2 returns length squared i.e. v·v
// no square root involved
return glm::vec3(glm::length2( glm::vec3(m[0]) ),
glm::length2( glm::vec3(m[1]) ),
glm::length2( glm::vec3(m[2]) ));
}
void Sphere::applyTransformation(const glm::mat4 &transformation)
{
glm::vec4 center = transformation * glm::vec4(_center, 1.0f);
glm::vec3 scalesSq = extractScale(transformation);
float const maxScaleSq = std::max_element(&scalesSq[0], &scalesSq[0] + scalesSq.length()); // length gives the dimension here i.e. 3
// one sqrt when you know the largest of the three
float const largestScale = std::sqrt(maxScaleSq);
set(glm::vec3(center), _radius * largestScale);
}
Aside:
A non-uniform scale means the scaling ratios along the different axes aren't the same. E.g. S1, 2, 4 is non-uniform while S2, 2, 2 is uniform. See this intuitive primer on transformations to understand them better; it has animations to demonstrate such differences.
Can the scale be non-uniform too? From the code it looks like it could. Transforming the radius with the largest scale isn't right. If you'd a non-uniform scale, the sphere would actually become an ellipsoid and hence just scaling the radius isn't correct. You'd have to transform the sphere into an ellipsoid with semi-principle axes of differing lengths.

Rotate scene about Up vector in jsc3d

I'm using jsc3d to load and display some 3d objects on a canvas. The viewer has already a built-in feature that allows to rotate the "view coordinates" (correct me if i'm wrong) about the Y axis by dragging the mouse.
The rotation is performed through a classic rotation matrix, and finally the trasformation matrix is multiplied by this rotation matrix.
The totation about the Y axis is calculated in a way that resembles a circular movement around the whole scene of loaded objects:
JSC3D.Matrix3x4.prototype.rotateAboutYAxis = function(angle) {
if(angle != 0) {
angle *= Math.PI / 180;
var c = Math.cos(angle);
var s = Math.sin(angle);
var m00 = c * this.m00 + s * this.m20;
var m01 = c * this.m01 + s * this.m21;
var m02 = c * this.m02 + s * this.m22;
var m03 = c * this.m03 + s * this.m23;
var m20 = c * this.m20 - s * this.m00;
var m21 = c * this.m21 - s * this.m01;
var m22 = c * this.m22 - s * this.m02;
var m23 = c * this.m23 - s * this.m03;
this.m00 = m00; this.m01 = m01; this.m02 = m02; this.m03 = m03;
this.m20 = m20; this.m21 = m21; this.m22 = m22; this.m23 = m23;
}
};
Now, dragging the mouse will apply this rotation about the Y axis on the whole world, like on the left side in the picture below. Is there a way, to apply a rotation about the Up vector to keep it in the initial position, like it appear on the right side?
I tried something like that:
var rotY = (x - viewer.mouseX) * 360 / viewer.canvas.height;
var rotMat = new JSC3D.Matrix3x4; // identity
rotMat.rotateAboutYAxis(rotY);
viewer.rotMatrix.multiply(rotMat);
but it has no effect.
What operations shall be applied to my rotation matrix to achieve a rotation about the Up vector?
Sample: https://jsfiddle.net/4xzjnnar/1/
This 3D library has already some built-in functions to allow scene rotation about X,Y,and Z axis, so there is no need to implement new matrix operations for that, we can use the existing functions rotateAboutXAyis, rotateAboutYAxis and rotateAboutZAxis, which apply an in-place matrix multiplication of the desired rotation angle in degrees.
The scene in JSC3D is transformed by a 3x4 matrix where the rotation is stored in the first 3 values of each row.
After applying a scene rotation and/or translation, applying a subsequent rotation about the Up vector, is a problem of calculate a rotation about an arbitrary axis.
A very clean and didactic explanation how to solve this problem is described here: http://ami.ektf.hu/uploads/papers/finalpdf/AMI_40_from175to186.pdf
Translate the P 0 (x 0 ,y 0 ,z 0 ) axis point to the origin of the coordinate system.
Perform appropriate rotations to make the axis of rotation coincident with
z-coordinate axis.
Rotate about the z-axis by the angle θ.
Perform the inverse of the combined rotation transformation.
Perform the inverse of the translation.
Now, its easy to write a function for that, because we use the functions already available in JSC3D (translation part is omitted here).
JSC3D.Viewer.prototype.rotateAboutUpVector = function(angle) {
angle %= 360;
/* pitch, counter-clockwise rotation about the Y axis */
var degX = this.rpy[0], degZ = this.rpy[2];
this.rotMatrix.rotateAboutXAxis(-degX);
this.rotMatrix.rotateAboutZAxis(-degZ);
this.rotMatrix.rotateAboutYAxis(angle);
this.rotMatrix.rotateAboutZAxis(degZ);
this.rotMatrix.rotateAboutXAxis(degX);
}
Because all above mentioned functions are using degrees, we need to get back the actual Euler angles from the rotation matrix (simplified):
JSC3D.Viewer.prototype.calcRollPitchYaw = function() {
var m = this.rotMatrix;
var radians = 180 / Math.PI;
var angleX = Math.atan2(-m.m12, m.m22) * radians;
var angleY = Math.asin(m.m01) * radians;
var angleZ = Math.atan2(-m.m01, m.m00) * radians;
this.rpy[0] = angleX;
this.rpy[1] = angleY;
this.rpy[2] = angleZ;
}
The tricky part here, is that we need always to get back the current rotation angles, as they results from the applied rotations, so a separate function must be used to store the current Euler angles every time that a rotation is applied to the scene.
For that, we can use a very simple structure:
JSC3D.Viewer.prototype.rpy = [0, 0, 0];
This will be the final result:

Resources