Related
I have a spaceship model that I want to move along a circular path. I want the nose of the ship to always point in the direction it is moving in.
Here is the code I have to move it in a circle right now:
glm::mat4 m = glm::mat4(1.0f);
//time
long value_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::
high_resolution_clock::now())
.time_since_epoch())
.count();
//translate
m = glm::translate(m, translate);
m = glm::translate(m, glm::vec3(-50, 0, -20));
m = glm::scale(m, glm::vec3(0.025f, 0.025f, 0.025f));
m = glm::translate(m, glm::vec3(1800, 0, 3000));
float speed = .002;
float x = 100 * cos(value_ms * speed); // + 1800;
float y = 0;
float z = 100 * sin(value_ms * speed); // + 3000;
m = glm::translate(m, glm::vec3(x, y, z));
How would I move it so the nose always points ahead? I tried doing glm::rotate with the rotation axis set as x or y or z but I cannot get it to work properly.
First see Understanding 4x4 homogenous transform matrices as I am using terminology and stuff from there...
Its usual to use a transform matrix of object for its navigation purposes and not the other way around ... So you should have a transform matrix M for your space ship that represents its position and orientation in [GCS] (global coordinate system). On top of that is sometimes multiplied another matrix M0 that align your space ship mesh to the first matrix (you know some meshes are not centered around (0,0,0) nor axis aligned...)
Now when you are moving your object you just do local transformations on the M so moving forward is just translating M origin position by a multiple of forward axis basis vector. The same goes for sliding to sides (just use different basis vector) resulting in that the object is alway aligned to where it supposed to be (in respect to movement). The same goes for turns. So going in circle is just moving forward and turning at constant speeds per time iteration step (timer).
You are doing this backwards first you compute position and orientation and then you are trying to make operations resulting in matrix that would do the same... In such case is much much easier to construct the matrix M instead of creating transformations that will create it... So what you need is:
origin position
3 perpendicular (most likely unit) basis vectors
So the origin is your x,y,z position. 2 basis vectors can be obtained from the circle so forward is tangent (or position-last_position) and vector towards circle center cen be used as (right or left). The 3th vector can be obtained by cross product so let assume:
+X axis is right
+Y axis is up
+Z axis is forward
you got:
r=100.0
a=speed*t
pos = (r*cos(a),0.0,r*sin(a))
center = (0.0,0.0,0.0)
so:
Z = (cos(a-0.5*M_PI),0.0,sin(a-0.5*M_PI))
X = (cos(a),0.0,sin(a))-ceneter
Y = cross(X,Z)
O = pos
normalize:
X /= length(X)
Y /= length(Y)
Z /= length(Z)
So now just feed your X,Y,Z,O to your matrix (depending on the conventions you use like multiplication order, direct/inverse matrix, row-major or column-major matrices ...)
so for example like this:
double M[16]=
{
X[0],X[1],X[2],0.0,
Y[0],Y[1],Y[2],0.0,
Z[0],Z[1],Z[2],0.0,
O[0],O[1],O[2],1.0,
};
or:
double M[16]=
{
X[0],Y[0],Z[0],O[0],
X[1],Y[1],Z[1],O[1],
X[2],Y[2],Z[2],O[2],
0.0 ,0.0 ,0.0 ,1.0,
};
And that is all ... The matrix might be transposed, inverted etc based on the conventions you use. Sorry I do not use GLM but the syntax should be very siilar ... the matrix feeding might be even simpler if rows or columns are loadable by a vector ...
I have a completely implemented, working engine in OpenGL that supports a projection camera with raycasting. Recently, I implemented an orthogonal camera type, and visually, it's working just fine. For reference, here's how I compute the orthographic matrix:
double l = -viewportSize.x / 2 * zoom_;
double r = -l;
double t = -viewportSize.y / 2 * zoom_;
double b = -t;
double n = getNear();
double f = getFar();
m = Matrix4x4(
2 / (r - l),
0,
0,
-(r + l) / (r - l),
0,
2 / (t - b),
0,
-(t + b) / (t - b),
0,
0,
-2 / (f - n),
-(f + n) / (f - n),
0,
0,
0,
1);
However, my issue now is that raycasting does not work with the orthogonal camera. The issue seems to be that the raycasting engine was coded with projection-type cameras in mind, therefore when using the orthographic matrix instead it stops functioning. For reference, here's a high-level description of how the raycasting is implemented:
Get the world-space origin vector
Get normalized screen coordinate from input screen coordinates
Build mouseVector = (normScreenCoords.x, normScreenCoords.y, 0 if "near" or 1 if "far"
Build view-projection matrix (get view and projection matrices from Camera and multiply them)
Multiply the mouseVector by the inverse of the view-projection matrix.
Get the world-space forward vector
Get mouse world coordinates (far) and subtract them from mouse world coordinates (near)
Send the world-space origin and world-space forward vectors to the raycasting engine, which handles the logic of comparing these vectors to all the visible objects in the scene efficiently by using bounding boxes.
How do I modify this algorithm to work with orthographic cameras?
Your steps are fine and should work as expected with an orthographic camera. There may be a problem with the way you are calculating the origin and direction.
1.) Get the origin vector. First calculate the mouse position in world-space units, ie float rayX = (mouseX - halfResolution) / viewport.width * (r - l) or similar. It should be offset so the center of the screen is (0, 0), and the extreme values the mouse can reach translate to the edges of the viewport l, r, t, b. Then start with the camera position in world space and add two vectors rayX * camera.local.right and rayY * camera.local.up, where right and up are unit vectors in the camera's local co-ordinate system.
2.) The world space forward vector is always the camera forward vector for any mouse position.
3.) This should work without modification as long as you have the correct vectors for 1 and 2.
Let's say I have 2 coordinate systems as it is shown in image attached
How can I align this coordinate systems? I know that I need to translate second coordinate system around X with 180 grads, and then translate it to (0, 0) of the first coordinate system, but I have some troubles with doing it getting wrong results. Will really appreciate any detailed answer.
EDIT: Actually (0, 0) of second coordinate system is in the same point like Y of the first coordinate system.
The important piece of information is where is the second coordinate system's origin - namely (a,b).
Once you know that, all you need is:
QPointF pos1; // original position
QTransform t;
t.scale(1, -1);
t.translate(a, -b+1);
QPointF pos2 = pos1 * t;
You have to find correct values of a,b,c,d,Ox,Oy in:
X = a * x + b * y + Ox
Y = c * x + d * y + Oy
where x,y are the coordinates on the point in one system and X,Y in the other one.
In your case, a = 1, b = 0, c = 0, d = -1.
Ox,Oy is the offset between the Origins.
see https://en.wikipedia.org/wiki/Change_of_basis
Here's the problem with rotating 180 degrees, it not only affects the direction of your Y coordinate but also X.
Y X <--+
^ => |
| v
+--> X Y
What you probably meant to do was translate the point at Y and then invert your Y-coordinate. You can do this like so:
Translate Y to new origin
Scale (1, -1)
Y +--> X
^ => |
| v
+--> X Y
After more thought, I have to wonder, why are you doing this transformation in the first place? Is it because of differences in OpenGL coordinates and the coordinates Qt uses for its window?
If this is the case, you can actually alter your projection matrix... If you're using an orthographic matrix, for instance:
Try glOrtho (0, X, 0, Y, -1, 1); instead of the traditional glOrtho (0, X, Y, 0, -1, 1);
If you opt to do this, you will probably need to change the winding direction of your polygon "front faces". OpenGL defaults to CCW, change it to glFrontFace (GL_CW) and this should prevent weird lighting / culling behavior.
In my hobbyist shader-based (non-FFP) GL (3.2+ core) "engine", everything in world-space and model-space is by design "left-handed" (and to stay that way), so X-axis goes from -1 ("left") to 1 ("right"), Y from -1 ("bottom") to 1 ("top") and Z from -1 ("near") to 1 ("far").
Now, by default in OpenGL the NDC-space works the same but the clip space doesn't, from what I gather, here z extends from 1 ("near") to -1 ("far").
At the same time I want to ideally keep using the "kinda-sorta inofficial quasi-standard" matrix functions for lookat and perspective, currently defined as:
func (me *Mat4) Lookat(eyePos, lookTarget, upVec *Vec3) {
l := lookTarget.Sub(eyePos)
l.Normalize()
s := l.Cross(upVec)
s.Normalize()
u := s.Cross(l)
me[0], me[4], me[8], me[12] = s.X, u.X, -l.X, -eyePos.X
me[1], me[5], me[9], me[13] = s.Y, u.Y, -l.Y, -eyePos.Y
me[2], me[6], me[10], me[14] = s.Z, u.Z, -l.Z, -eyePos.Z
me[3], me[7], me[11], me[15] = 0, 0, 0, 1
}
// a: aspect ratio. n: near-plane. f: far-plane.
func (me *Mat4) Perspective(fovY, a, n, f float64) {
s := 1 / math.Tan(DegToRad(fovY)/2) // scaling
me[0], me[4], me[8], me[12] = s/a, 0, 0, 0
me[1], me[5], me[9], me[13] = 0, s, 0, 0
me[2], me[6], me[10], me[14] = 0, 0, (f+n)/(n-f), (2*f*n)/(n-f)
me[3], me[7], me[11], me[15] = 0, 0, -1, 0
}
So, for the lookat-part to have my world-space camera (positive-Z) work with lookat (negative-Z) as per this pseudocode:
// world-space position:
camPos := cam.Pos
// normalized direction-vector, up/right/forward are 1 not -1:
camTarget := cam.Dir
// lookat-target:
camTarget.Add(&camPos)
// invert both Z:
camPos.Z, camTarget.Z = -camPos.Z, -camTarget.Z
// compute lookat-matrix:
cam.mat.Lookat(&camPos, &camTarget, &Vec3{0, 1, 0})
That works well. Moving the camera in all 6 degrees of freedom produces correct on-screen movement and correct new camera world-space coords.
But geometry is still inverted on the Z-axis. When I position two boxes, A at (-2, 1, -2), to appear near-left and B (2, 1, 2) to appear far-right, then A appears far-left and B appears near-right. Z is still inverted here.
Now, these nodes have their own world-space coords and update from those their own model-to-world matrices. I shouldn't invert posZ there as they form a hierarchy of sub-nodes multiplying with their parents transforms and all that. They're still in model or world space, which as per my decree is to remain left-handed.
Their world-to-camera calculation happens on the CPU at my end, not in a vertex shader which just gets a single final (mvp/clip-space) matrix.
When that happens -- multiplication of world-space-object-matrix with clip-space lookat-and-projection matrix -- at that point I need to somehow invert Z.
What's the best way to do this? Or, more generally speaking, what's a common way that works? Do I have to modify the projection to accept left-handed but output-to-GL right-handed? If so, how? And then wouldn't I also have to modify lookat? Is there a smart way to do all this without having to modify the somewhat-standard lookat/projection matrices while also keeping model-transform-matrices in left-handed coords?
In Perspective, changing me[11] from -1 to 1 should invert the z axis the way you're describing. If that isn't correct, try negating me[10]. Of course, because the z axis is inverted the directions of your rotations will affected as well. If I recall right rotations around the y axis, as well as maybe the x axis will be inverted. If this is the case you should be able to just negate the rotations to counteract it.
I have a scene and an object placed in some coordinates. I can transform the object using
glTranslate(center) and then glRotate...
But how do I rotate an object not using angles but rather directions top and forward?
Thanks
What I'm looking is translation between model coordinate system and global coordinate system.
Say you know 3 axes for your object in object space. For simplicity we'll assume these are the cartesian axes (if it's not the case, the process described below can be applied twice to take care of that):
ox = (1, 0, 0)
oy = (0, 1, 0)
oz = (0, 0, 1)
And say you have 3 other orthogonal and normalized axes in world space, indicating the top, forward and side directions of your object [*]:
wx = (wx.x, wx.y, wx.z)
wy = (wy.x, wy.y, wy.z)
wz = (wz.x, wz.y, wz.z)
Then the following (assuming column vectors) is a rotation matrix taking you from object space to world space:
[ wx.x wx.y wx.z ]
M = [ wy.x wy.y wy.z ]
[ wz.x wz.y wz.z ]
It's a rotation matrix because the determinant is 1 (othogonal and normalized lines). To verify it goes from world space to object space, just note how M*wx = (1, 0, 0) etc.
Now you want the exact opposite: from object space to world space. Just invert the matrix. In that case, the inverse is the same as the transpose, so your final answer is:
objectToWorld = transpose(M)
Two things remain:
1) Loading this matrix in OpenGL. glMultMatrix will do this for you (be careful, glMultMatrix is column major and needs a 4x4 matrix):
double objectToWorld[] = [ wx.x, wy.x, wz.x, 0,
wx.y, wy.y, wz.y, 0,
wx.z, wy.z, wz.z, 0,
0, 0, 0, 1 ];
glMultMatrixd( objectToWorld );
2) Translating. This is done just by following this with a call to glTranslate.
[*] If you have only two of the three, say top and forward you can easily compute the side with a cross product. If they're not normalized, simply normalize them. If they're not orthogonal it all gets trickier.
Since OpenGL works just with matrices there is no concept of top, bottom and so on..
you'll have to find a corrispondence between the rotation you wanna give and the orientation needed. Since glRotates(angle,x,y,z,) wants an angle in degrees you can just use a bidimensional array and store all 16 possibilities (from one of top, bottom, left, right to one of the same.. otherwise you can just see how much 90° steps are needed from actual position to new one and multiply the value by 90..
eg:
from top to bottom = 2 steps = 180°
from right to top = 1 step backward = -90°