Calculate rotation by vector - opengl

I have a sphere with a center in (0,0,0) and radius = 1, with three marked points on it's surface, forming a triangle:
x = (1,0,0)
y = (0,1,0)
z = (0,0,1)
Now, I am translating y by a vector v_y to get y'
v_y = [1, 2, 3]
y' = (1, 3, 3)
I want to calculate v_x and v_z the way, both x' and z' will move in the same manner, so calculated points will form simillar triangle (same side to side ratios)
I know it can be achieved by rotating, and scaling this sphere, but I have no idea how to calculate this just by knowing this vector.
I need two angles for rotation, and one scalar for scaling.
Maybe I am wrong, and I can just calculate those vectors without calculating a rotation.

I think I get what you are trying to do here. You are adding v_y to y to form y' and you want the x and z (perpendicular vectors) to stay perpendicular and relatively increase in length to reach the circumference of the sphere which is now scaled.
I will do this in two parts, scale and rotate. note that y2 in code is your y'. I use glm here but you can replace with your own math library.
//compute y2
glm::vec3 y2 = y + v_y;
//compute the scale
float scale = glm::length(y2) / glm::length(y);
//compute the scaled x and z
glm::vec3 x2 = x * scale;
glm::vec3 z2 = z * scale;
//find the normal and angle and arc as a quaternion
glm::vec3 ynorm = cross(y, glm::normalize(y2));
float angle = glm::angle(y, glm::normalize(y2));
glm::quat arcQuat = glm::angleAxis(angle, ynorm);
//now rotate x2 and z2 with the above arc
x2 = glm::gtx::quaternion::rotate(arcQuat, x2);
z2 = glm::gtx::quaternion::rotate(arcQuat, z2);
//now you have your v_x and v_z
glm::vec3 v_x = x2 - x;
glm::vec3 v_z = z2 - z;
If you scale your sphere by scale, x2, y2 and z2 will form a similar triangle that you want.

It's impossible to know how to create the other vertices without knowing what transformation you think you actually applied, and hence how you expect the other verts to move.
Based on a single original point and a single new point, you only describe the radius and one axis of the sphere's orientation (the vector between origin and y'). However, it's ambiguous about the rotation of the second axis you need to concretely define a precise transform. You can rotate the new sphere 360 degrees around the vector between the origin and the y', and any of those values on a 360 degree arc would describe valid values of x' and z'.

Related

How do you find the Y position of a point between four vertices? HLSL

Let's say there is a grid terrain for a game composed of tiles made of two triangles - made from four vertices. How would we find the Y (up) position of a point between the four vertices?
I have tried this:
float diffZ1 = lerp(heights[0], heights[2], zOffset);
float diffZ2 = lerp(heights[1], heights[3], zOffset);
float yPosition = lerp(diffZ1, diffZ2, xOffset);
Where z/yOffset is the z/y offset from the first vertex of the tile in percent / 100. This works for flat surfaces but not so well on bumpy terrain.
I expect this has something to do with the terrain being made from triangles where the above may work on flat planes. I'm not sure, but does anybody know what's going wrong?
This may better explain what's going on here:
In the code above "heights[]" is an array of the Y coordinate of surrounding vertices v0-3.
Triangle 1 is made of vertex 0, 2 and 1.
Triangle 2 is made of vertex 1, 2 and 3.
I wish to find coordinate Y of p1 when its x,y coordinates lay between v0-3.
So I have tried determining which triangle the point is between through this function:
bool PointInTriangle(float3 pt, float3 pa, float3 pb, float3 pc)
{
// Compute vectors
float2 v0 = pc.xz - pa.xz;
float2 v1 = pb.xz - pa.xz;
float2 v2 = pt.xz - pa.xz;
// Compute dot products
float dot00 = dot(v0, v0);
float dot01 = dot(v0, v1);
float dot02 = dot(v0, v2);
float dot11 = dot(v1, v1);
float dot12 = dot(v1, v2);
// Compute barycentric coordinates
float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01);
float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
float v = (dot00 * dot12 - dot01 * dot02) * invDenom;
// Check if point is in triangle
return (u >= 0.0f) && (v >= 0.0f) && (u + v <= 1.0f);
}
This isn't giving me the results I expected
I am then trying to find the y coordinate of point p1 inside each triangle:
// Position of point p1
float3 pos = input[0].PosI;
// Calculate point and normal for triangles
float3 p1 = tile[0];
float3 n1 = (tile[2] - p1) * (tile[1] - p1); // <-- Error, cross needed
// = cross(tile[2] - p1, tile[1] - p1);
float3 p2 = tile[3];
float3 n2 = (tile[2] - p2) * (tile[1] - p2); // <-- Error
// = cross(tile[2] - p2, tile[1] - p2);
float newY = 0.0f;
// Determine triangle & get y coordinate inside correct triangle
if(PointInTriangle(pos, tile[0], tile[1], tile[2]))
{
newY = p1.y - ((pos.x - p1.x) * n1.x + (pos.z - p1.z) * n1.z) / n1.y;
}
else if(PointInTriangle(input[0].PosI, tile[3], tile[2], tile[1]))
{
newY = p2.y - ((pos.x - p2.x) * n2.x + (pos.z - p2.z) * n2.z) / n2.y;
}
Using the following to find the correct triangle:
if((1.0f - xOffset) <= zOffset)
inTri1 = true;
And correcting the code above to use the correct cross function seems to have solved the problem.
Because your 4 vertices may not be on a plane, you should consider each triangle separately. First find the triangle that the point resides in, and then use the following StackOverflow discussion to solve for the Z value (note the different naming of the axes). I personally like DanielKO's answer much better, but the accepted answer should work too:
Linear interpolation of three 3D points in 3D space
EDIT: For the 2nd part of your problem (finding the triangle that the point is in):
Because the projection of your tiles onto the xz plane (as you define your coordinates) are perfect squares, finding the triangle that the point resides in is a very simple operation. Here I'll use the terms left-right to refer to the x axis (from lower to higher values of x) and bottom-top to refer to the z axis (from lower to higher values of z).
Each tile can only be split in one of two ways. Either (A) via a diagonal line from the bottom-left corner to the top-right corner, or (B) via a diagonal line from the bottom-right corner to the top-left corner.
For any tile that's split as A:
Check if x' > z', where x' is the distance from the left edge of the tile to the point, and z' is the distance from the bottom edge of the tile to the point. If x' > z' then your point is in the bottom-right triangle; otherwise it's in the upper-left triangle.
For any tile that's split as B: Check if x" > z', where x" is the distance from the right edge of your tile to the point, and z' is the distance from the bottom edge of the tile to the point. If x" > z' then your point is in the lower-left triangle; otherwise it's in the upper-right triangle.
(Minor note: Above I assume your tiles aren't rotated in the xz plane; i.e. that they are aligned with the axes. If that's not correct, simply rotate them to align them with the axes before doing the above checks.)

Rotating a Group of Vectors

I am trying to rotate a group of vectors I sampled to the normal of a triangle
If this was correct, the randomly sampled hemisphere would line up with the triangle.
Currently I generate it on the Z-axis and am attempting to rotate all the samples to the normal of the triangle.
but it seems to be "just off"
glm::quat getQuat(glm::vec3 v1, glm::vec3 v2)
{
glm::quat myQuat;
float dot = glm::dot(v1, v2);
if (dot != 1)
{
glm::vec3 aa = glm::normalize(glm::cross(v1, v2));
float w = sqrt(glm::length(v1)*glm::length(v1) * glm::length(v2)*glm::length(v2)) + dot;
myQuat.x = aa.x;
myQuat.y = aa.y;
myQuat.z = aa.z;
myQuat.w = w;
}
return myQuat;
}
Which I pulled from the bottom of this page : http://lolengine.net/blog/2013/09/18/beautiful-maths-quaternion-from-vectors
Then I :
glm::vec3 zaxis = glm::normalize( glm::vec3(0, 0, 1) ); // hardcoded but test orginal axis
glm::vec3 n1 = glm::normalize( glm::cross((p2 - p1), (p3 - p1)) ); //normal
glm::quat myQuat = glm::normalize(getQuat(zaxis, n1));
glm::mat4 rotmat = glm::toMat4(myQuat); //make a rotation matrix
glm::vec4 n3 = rotmat * glm::vec4(n2,1); // current vector I am trying to rotate
Construct 4x4 transform matrix instead of Quaternions.
Do not forget that OpenGL has column wise matrix
so for double m[16];
is X axis vector in m[ 0],m[ 1],m[ 2]
is Y axis vector in m[ 4],m[ 5],m[ 6]
is Z axis vector in m[ 8],m[ 9],m[10]
and position is in m[12],m[13],m[14]
The LCS mean local coordinate system (your triangle or object or whatever) and GCS mean global coordinate system (world or whatever).
All the X,Y,Z vectors should be normalized to unit vectors otherwise scaling will occur.
construction
set Z-axis vector to your triangle normal
set position (LCS origin) to mid point of your triangle (or average point form its vertexes)
now you just need X and Y axises which is easy
let X = any triangle vertex - triangle midpoint
or X = substraction of any 2 vertexes of triangle
The only condition that must be met for X is that it must lie on triangle plane.
Now let Y = X x Z the cross product will create vector perpendicular to X and Z (which also lies in triangle plane).
now put all this inside matrix and load it to OpenGL as ModelView matrix or what ever.

Rotate geometry to align to a direction vector

I've been trying to get my generated geometry to align with a direction vector. To illustrate what my current problem is:
A = Correctly aligned geometry ( just a triangle for testing )
B = Incorrectly aligned geometry
My current solution in code for this triangle example (This code is run for all the nodes you see on screen starting at the split, I am using the GLM math library):
glm::vec3 v1, v2, v3;
v1.x = -0.25f;
v1.z = -0.25f;
v2.x = 0.25f;
v2.z = -0.25f;
v3.x = 0.0f;
v3.z = 0.25f;
v1.y = 0.0f;
v2.y = 0.0f;
v3.y = 0.0f;
glm::mat4x4 translate = glm::translate(glm::mat4x4(1.0f), sp.position);
glm::mat4x4 rotate = glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), sp.direction, glm::vec3(0.0f, 1.0f, 0.0f));
v1 = glm::vec4(translate * rotate * glm::vec4(v1, 1.0f)).swizzle(glm::comp::X, glm::comp::Y, glm::comp::Z);
v2 = glm::vec4(translate * rotate * glm::vec4(v2, 1.0f)).swizzle(glm::comp::X, glm::comp::Y, glm::comp::Z);
v3 = glm::vec4(translate * rotate * glm::vec4(v3, 1.0f)).swizzle(glm::comp::X, glm::comp::Y, glm::comp::Z);
The direction vector values for point A:
x 0.000000000 float
y 0.788205445 float
z 0.615412235 float
The direction vector values for point B:
x 0.0543831661 float
y 0.788205445 float
z -0.613004684 float
Edit 1 (24/11/2013 # 20:36):
A and B do not have any relation, both are generated separately. When generating A or B only a position and direction is known.
I've been looking at solutions posted here:
Quaternions, rotate a model and align with a direction
Direct3D
Rotation Matrix from Vector and vice-versa
Direction Vector To
Rotation Matrix
But I haven't been able to successfully rotate my geometry to align with my direction vector. I feel like I'm doing something rather basic wrong.
Any help would be greatly appreciated!
If A and B are unit vectors and you want a rotation matrix R that transforms B so that it aligns with A, then start by computing C = B x A (the cross-product of B and A). C is the axis of rotation, and arcsin(|C|) is the necessary rotation angle.
From these you can build the required rotation matrix. It looks like glm has support for this, so I won't explain further.
NB if you are doing many, many of these in performance-critical code, you can gain a bit of speed by noting |C| = sin(theta), sqrt(1 - |C|^2) = cos(theta) and computing the matrix yourself with these known values of sin(theta) and cos(theta). For this see for example this discussion. The glm routine will take your angle arcsin(|C|) and proceed immediately to compute its sin and cos, a small waste since you already knew these and the operations are relatively expensive.
If the rotation is about some point p other than the origin, then let T be a translation that takes p to the origin, and find X = T^-1 R T. This X will be the transformation you want.

Orbiting object around orbiting object

How do I get to orbit green circle around orange and blue around green ?
I found many solutions which works fine with rotating around static point(int this case orange circle) but didn't find any good maths equation which would work for both static and moving points.
angle += sunRot;
if(angle > 360.0f)
{
angle = 0.0f;
}
float radian = glm::radians(angle);
float radius = glm::distance(position, rotCenter);
float x = rotCenter.x + (radius * cosf(radian));
float z = rotCenter.z + (radius * sinf(radian));
glm::vec3 newPos = glm::vec3(x, 0, z);
setPosition(newPos);
Here is what I'm trying to achieve (Thanks to #George Profenza for sharing link)
Base all your calculations on the radius and angle of the current object where possible and store the radius and angle with the object.
In particular, do not calculate the radius based on the x/y coordinates in every iteration: If the base object has moved between steps, your calculated radius will be slightly off and the error will accumulate.
You should be able to nest coordinate spaces using opengl using glPushMatrix(), glPopMatrix() calls. Here's a basic example(press mouse to see coordinate spaces).
The syntax isn't c++, but it's easy to see what I mean.
You can do this multiple ways:
polar coordinate formula
manually multiplying transformation matrices
simply using push/pop matrix calls (along with translate/rotate where needed), which does the matrix multiplication for you behind the scenes.
Just in case you want to try the polar coordinate formula:
x = cos(angle) * radius
y = sin(angle) * radius
Where angle is the current rotation of a circle and the radius is it's distance from the centre of rotation.

How to rotate a vector to a place that is aligned with Z axis?

I want to rotate a vector to the Z axis and its direction is Z-axis backward. So if the vector is (1,1,1), my result should be (0,0,-sqrt(3)).
My idea is two steps. The first step is to rotate my vector around X axis to the XZ plane. The second step is to rotate the vector in the XZ plane around Y axis to Z axis.
Here is my code:
GLfloat p[4] = {1,1,1,0}; //my vector, in homogeneous coordinates
GLfloat r[4]; //result vector to test
float theta1 = ((double)180/PI)*asin(p[1]/sqrt(p[0]*p[0]+p[1]*p[1]+p[2]*p[2]));
//angle theta1 between the vector and XZ plane, is this right ??? I doubt it !!!
float theta2 = ((double)180/PI)*atan(p[0]/p[2]);
//angle theta2 between the vector's projection in XZ plane and Z axis
GLfloat m[16];
glMatrixMode(GL_MODELVIEW); // get the rotation matrix in model-view matrix
glPushMatrix();
glLoadIdentity();
glRotatef(theta1, 1,0,0); //rotate to the XZ plane
glRotatef(180-theta2,0,1,0); //rotate to the Z axis
glGetFloatv(GL_MODELVIEW_MATRIX, m); // m is column-major.
glPopMatrix();
// use the matrix multiply my vector and get the result vector r[4]
//my expectation is (0,0,-sqrt(3))
r[0] = p[0]*m[0]+p[1]*m[4]+p[2]*m[8]+p[3]*m[12];
r[1] = p[0]*m[1]+p[1]*m[5]+p[2]*m[9]+p[3]*m[13];
r[2] = p[0]*m[2]+p[1]*m[6]+p[2]*m[10]+p[3]*m[14];
r[3] = p[0]*m[3]+p[1]*m[7]+p[2]*m[11]+p[3]*m[15];
However, the result r[4] is not my expectation. So I think I made some mistakes in some places above. Could anyone give me a hint about that ?
To rotate one vector so it faces another:
normalise it
take their dot product to get the cosine of the rotation angle
take their cross product to find an orthogonal rotation vector
rotate around that new vector by the angle found in #2
Step 2 can be omitted if you remember that | A x B | = sin(theta) if A and B are both normalised.