orphean 122 Report post Posted June 11, 2006 Hi all, I'm making a sort of 'mostly 2d' tile engine but using 3d graphics for the lot of it (nothing new to you guys I'm sure, just textured quads for the most part). For the standard 2d mode I'm just using an orthographic camera and all works as I want. For the 3d stuff I have run into a problem. I want to find a way to lock the camera's view onto a sprite irregardless of the camera's location in the scene. So irregardless of the value of the camera's eye vector I want to rotate the look vector to point at the sprite that's targeted. Vector math is a whole new area for me. I suspect this has something to do with taking the cross product of the sprite's vector and the camera's look vector but I really have no idea. I don't need anyone to spell it out step by step with commented code or anything but a general explanation to do what I'm trying to do would be awesome. ANY help is greatly appreciated. Thanks! Orphean 0 Share this post Link to post Share on other sites
TheC00L1 100 Report post Posted June 11, 2006 Are you using directx, or opengl? Cause there a specific functions that generate the views via transofrmation matrix, unless of course you gnerate thme yourselfEdit: you jus generate a transformation matrix, like i said above. 0 Share this post Link to post Share on other sites
orphean 122 Report post Posted June 11, 2006 I'm using Direct3d.Thanks for the reply I guess I'll go lookup what I can about generating transformation matrices in the api. 0 Share this post Link to post Share on other sites
jyk 2095 Report post Posted June 11, 2006 Quote:Original post by orpheanI'm using Direct3d.It sounds like what you want is a 'look at' matrix; DirectX has functions for this, but it's also fairly straightforward to generate your own. Just ask if you need further details. 0 Share this post Link to post Share on other sites
orphean 122 Report post Posted June 11, 2006 Any details you're willing to part with would be fantastic! :) 0 Share this post Link to post Share on other sites
Driv3MeFar 1080 Report post Posted June 11, 2006 Quote:Original post by orpheanAny details you're willing to part with would be fantastic! :)the first step to constructing the lookat matrix is defining local your coordinate axis wrt your target.to build the matrix, you'll need to provide the worlds "up" vector (typically +Y, in the directX coord system). knowing the "up" vector and the target location, construct your axis like so:(note that after finding each of these vectors, they should be normalized)vector vZ = target position - camera position;vector vX = vZ (cross) up;vY = vX (cross) vZ; <--(no need to normailze this, the magnitude of the cross product of two orthonormal vectors is always 1)the last part of your lookat matrix (it will be a 4x4) comes from taking the inverse of the translation from the world origin to your cameras current position, which, with a little more vector math, gives you the following matrix:(let P = cameras world position)| vX.x, vX.y, vX.z, 0|| vY.x, vY.y, vY.z, 0|| vZ.x, vZ.y, vZ.z, 0||-(vX (dot) P), -(vY (dot) P), -(vZ (dot) P), 1|disclaimer: my vector math is a little rusty, the matrix may be a bit off, but its something like thatedit: another way of solving this problem, which you elluded to in your first post, would be to make a vector from the camera position to the target, take the cross product of that vector with the cameras current look vector to find an axis orthogonal to both, and rotate your camera about that axis by the angle between the two vectors (as can be found with a dot product). this will accomplish the same thing as the lookat matrix, but I believe it would be a bit slower.[Edited by - Driv3MeFar on June 11, 2006 4:57:28 AM] 0 Share this post Link to post Share on other sites
orphean 122 Report post Posted June 13, 2006 "edit: another way of solving this problem, which you elluded to in your first post, would be to make a vector from the camera position to the target, take the cross product of that vector with the cameras current look vector to find an axis orthogonal to both, and rotate your camera about that axis by the angle between the two vectors (as can be found with a dot product). this will accomplish the same thing as the lookat matrix, but I believe it would be a bit slower."This is exactly what I need. The speed-factor isn't a real issue for this application.> would be to make a vector from the camera position to the targetAny ideas on how to accomplish this? I know the sprite's location <0,0,0> we'll say. And then we'll say the camera is at <20,30,500>. How do I make a vector from <20,30,500> to the sprite?And... > find an axis orthogonal to both,How do I determine axis orthogonality? :)That is exactly what I need though, and those are my final two stumbling blocks. Thank you so much for the help everyone so far!!!! :DOrphean 0 Share this post Link to post Share on other sites
Driv3MeFar 1080 Report post Posted June 13, 2006 Quote:Original post by orphean> would be to make a vector from the camera position to the targetAny ideas on how to accomplish this? I know the sprite's location <0,0,0> we'll say. And then we'll say the camera is at <20,30,500>. How do I make a vector from <20,30,500> to the sprite?If you have two points, you can find a vector simply by subtracting the two. Geometrically, this finds you the displacement between the two points, and a vector is really just a displacement (a physics vector at least, if/when you take linear algebra, a vector is simply an element of a vector space, but I digress). So with the sample points you provided:vector v = <0, 0, 0> - <20, 30, 500> = <-20, -30, -500>;you will then want to normalize the vector by dividing it by its magnitude:v /= |v|, where |v| = sqrt((-20)^2 + (-30)^2 + (-500)^2)(Actualy, since your not building a lookAt matrix, I don't think you need to normalize, but if you wanted to, thats how you would.)Quote:Original post by orpheanAnd... > find an axis orthogonal to both,How do I determine axis orthogonality? :)With a cross product. By definition, the cross product of two vectors is a vector perpendicular (orthogonal) to both of the origninal vectors.The cross product of two vectors, u and v, is:u (cross) v = (u.y * v.z - u.z * v.y)i - (u.x * v.z - u.z * v.x)j + (u.x * v.y - u.y * v.x)kWhere i = <1, 0, 0>, j = <0, 1, 0>, and k = <0, 0, 1> 0 Share this post Link to post Share on other sites
Guest Anonymous Poster Report post Posted June 13, 2006 Perfect.Driv3MeFar next time I drive to the Seattle area I owe you a beer ;)Thanks much for the help.orphean 0 Share this post Link to post Share on other sites
Driv3MeFar 1080 Report post Posted June 13, 2006 Quote:Original post by Anonymous PosterPerfect.Driv3MeFar next time I drive to the Seattle area I owe you a beer ;)Thanks much for the help.orpheanDon't thank me yet, wait until you get it working in code.I will, however, take you up on the beer anytime.Glad to help. 0 Share this post Link to post Share on other sites
orphean 122 Report post Posted June 13, 2006 Okay one last thing hopefully.In the result of the cross product I've noticed that only one item is negative at any one time.Is that negative item the orthogonal axis? :) (ie, I test to see which item is negative to find my axis?)Also, the angle seems very large when I take the dot product. Is that normal? It has a tendency to return stuff like -600, etc which doesn't make much sense to me since I would be doing multiple full rotations using that. Or should I just not worry about it.The numbers were all very large (like 80000-100000+) so I normalized the offset vector like you said and they all got toned down.Here's my current code.void ElCamera::Normalize(gobVector3 *v){ float magSq = v->x * v->x + v->y * v->y + v->z * v->z; if(magSq > 0.0f) { float oneOverMag = 1.0f / sqrt(magSq); v->x *= oneOverMag; v->y *= oneOverMag; v->z *= oneOverMag; }}void ElCamera::Cross(gobVector3 *out, gobVector3 *a, gobVector3 *b){ out->x = a->y * b->z - a->z * b->y; out->y = a->z * b->x - a->x * b->z; out->z = a->x * b->y - a->y * b->x;}void ElCamera::LookAt(float x, float y, float z, HBFONT fnt){ gobVector3 spriteVec; gobVector3 offsetVec; gobVector3 camEyeVec; gobVector3 camLookVec; gobVector3 crossVec; float angle; char buf[2048]; spriteVec.x = x; spriteVec.y = y; spriteVec.z = z; iCamera::GetVectorEye(&camEyeVec); iCamera::GetVectorLook(&camLookVec); offsetVec = spriteVec - camEyeVec; Normalize(&offsetVec); Cross(&crossVec, &offsetVec, &camLookVec); angle = offsetVec.Dot(&camLookVec); // Snipped some output stuff here}Here's some sample output:SpriteVec-> X: 582 Y: 128 Z: 48camEyeVec-> X: 366 Y: 538 Z: 150camLookVec-> X: 368 Y: 268 Z: 0OffsetVec-> X: 0.463622 Y: -0.858559 Z: -0.218933CrossVec-> X: 58.6739 Y: -80.1293 Z: 438.483Angle: -60.387That look right?Orphean[Edited by - orphean on June 13, 2006 6:15:23 AM] 0 Share this post Link to post Share on other sites
Driv3MeFar 1080 Report post Posted June 13, 2006 Quote:Original post by orpheanOkay one last thing hopefully.In the result of the cross product I've noticed that only one item is negative at any one time.Is that negative item the orthogonal axis? :) (ie, I test to see which item is negative to find my axis?)Also, the angle seems very large when I take the dot product. Is that normal?The numbers were all very large (like 80000-100000+) so I normalized the offset vector like you said and they all got toned down.The cross product results in a vector, which should be comprised of 3 elements: the x, y, and z components of the vector. Your rotation axis is going to be the vector which contains all three of these components, so you have to use all of them.This requires rotation about an arbitrary axis, which is fairly complex, but here are the basics:The cross product will give you a vector U (which you should normalize; above I said that you might not have to, but I was wrong), which is composed of the elements u1, u2, and u3 (your x, y, and z components, respectively). Using these three components, first construct the matrix: | 0, -u2, u1 |S = | u2, 0, -u3 | |-u1, u3, 0 |Using that matrix, your final rotation matrix for an angle Theta will be (non-trivially):R = I + sin(Theta)(S) + (1 - cos(Theta))(S^2);Where I is the identity matrix, and S is as defined above.As for the dot product results, the dot product formula is u.v = |u||v|cos(Theta), so the cosine of the angle is getting multipied by the magnitude of each vector. If you are dealing with large vectors, this would explain why your results are so huge. Normalizing will fix this problem, as bot |u| and |v| will be 1, so the dot product will result in just the cosine of the angle between the two vectors. In the code you posted, your angle will be:acos(offsetVec.Dot(&camLookVec));Assuming camLookVec is normalized.[Edited by - Driv3MeFar on June 13, 2006 7:36:47 AM] 0 Share this post Link to post Share on other sites
orphean 122 Report post Posted June 13, 2006 Okay. This is beginning to make more sense to me so that's a good sign.First - R = I + sin(Theta)(R) + (1 - cos(Theta))(S^2);Should that be R = I + sin(Theta)(*S*) + (1 - cos(Theta))(S^2); since R isn't defined when I'm calculating it?And then, just to make sure I got my head screwed on straight. I would then multiple the camLookVec by the rotation matrix to get the new LookVec, correct?Thanks for the help here, seriously.Orphean 0 Share this post Link to post Share on other sites
Driv3MeFar 1080 Report post Posted June 13, 2006 Quote:Original post by orpheanFirst - R = I + sin(Theta)(R) + (1 - cos(Theta))(S^2);Should that be R = I + sin(Theta)(*S*) + (1 - cos(Theta))(S^2); since R isn't defined when I'm calculating it?Yes, my mistake.Quote:Original post by orpheanAnd then, just to make sure I got my head screwed on straight. I would then multiple the camLookVec by the rotation matrix to get the new LookVec, correct?Correct. Or, if you wanted to animate the camera moving to that position over time rather than just jumping to it, you could introduce a time variable, 0 <= t <= 1, and every set ammount of time (eg, every game loop, every 10th of a second, or however you wanted to do it), increment t by some small ammount (eg, t += 0.01f), multiply your original Theta value by t, and recalculate the rotation matrix (To do this, you would need to recalculate the orginal rotation matrix, using your original U value, which would then be applied to your original camLookVec. Its important that you do this recalculation every frame of animation, because if you do it incrementaly, floating point errors can accumulate and throw off the animation). 0 Share this post Link to post Share on other sites
orphean 122 Report post Posted June 13, 2006 Man I am so close I can just feel it ;)Using the explained method results in something I cannot adequately put into words. Sort of perpendicular to the z plane. When the sprite moves the camera is moving slightly but not pointing towards the sprite.Here is a picture of the oddball behavior along with the results of the below code.Apologize for the ridiculous nature of the code, I'm trying to be as clear as possible in it while I'm working it out.void ElCamera::Normalize(gobVector3 *v){ float magSq = v->x * v->x + v->y * v->y + v->z * v->z; if(magSq > 0.0f) { float oneOverMag = 1.0f / sqrt(magSq); v->x *= oneOverMag; v->y *= oneOverMag; v->z *= oneOverMag; }}void ElCamera::Cross(gobVector3 *out, gobVector3 *a, gobVector3 *b){ out->x = a->y * b->z - a->z * b->y; out->y = a->z * b->x - a->x * b->z; out->z = a->x * b->y - a->y * b->x;}struct ElMatrix { float _11, _12, _13; float _21, _22, _23; float _31, _32, _33;};void ElCamera::LookAt(float x, float y, float z, HBFONT fnt){ gobVector3 spriteVec; gobVector3 offsetVec; gobVector3 camEyeVec; gobVector3 camLookVec; gobVector3 crossVec; gobVector3 camNewLookVec; ElMatrix identityMatrix; ElMatrix SMatrix; ElMatrix sinMatrix; ElMatrix cosMatrix; ElMatrix rotMatrix; float theta; float dotProduct; char buf[2048]; spriteVec.x = x; spriteVec.y = y; spriteVec.z = z; iCamera::GetVectorEye(&camEyeVec); iCamera::GetVectorLook(&camLookVec); offsetVec = spriteVec - camEyeVec; Normalize(&offsetVec); Normalize(&camLookVec); Cross(&crossVec, &offsetVec, &camLookVec); Normalize(&crossVec); dotProduct = offsetVec.x * camLookVec.x + offsetVec.y * camLookVec.y + offsetVec.z * camLookVec.z; theta = acos(dotProduct); /* | 0, -u2, u1 | u1 = crossVec.x; S = | u2, 0, -u3 | u2 = crossVec.y; |-u1, u3, 0 | u3 = crossVec.z; */ SMatrix._11 = 0, SMatrix._12 = -crossVec.y; SMatrix._13 = crossVec.x; SMatrix._21 = crossVec.y; SMatrix._22 = 0; SMatrix._23 = -crossVec.z; SMatrix._31 = -crossVec.x; SMatrix._32 = crossVec.z; SMatrix._33 = 0; /* | 1, 0, 0, | I = | 0, 1, 0, | | 0, 0, 1 | */ identityMatrix._11 = 1; identityMatrix._12 = 0; identityMatrix._13 = 0; identityMatrix._21 = 0; identityMatrix._22 = 1; identityMatrix._23 = 0; identityMatrix._31 = 0; identityMatrix._32 = 0; identityMatrix._33 = 1; /* sinMatrix = S * sin(theta) */ double rotSin = sin(theta); sinMatrix._11 = SMatrix._11 * rotSin; sinMatrix._12 = SMatrix._12 * rotSin; sinMatrix._13 = SMatrix._13 * rotSin; sinMatrix._21 = SMatrix._21 * rotSin; sinMatrix._22 = SMatrix._22 * rotSin; sinMatrix._23 = SMatrix._23 * rotSin; sinMatrix._31 = SMatrix._31 * rotSin; sinMatrix._32 = SMatrix._32 * rotSin; sinMatrix._33 = SMatrix._33 * rotSin; /* cosMatrix = (1 - cos(theta)) * (S*S) */ double rotCos = (1 - cos(theta)); cosMatrix._11 = rotCos * (SMatrix._11 * SMatrix._11); cosMatrix._12 = rotCos * (SMatrix._12 * SMatrix._12); cosMatrix._13 = rotCos * (SMatrix._13 * SMatrix._13); cosMatrix._21 = rotCos * (SMatrix._21 * SMatrix._21); cosMatrix._22 = rotCos * (SMatrix._22 * SMatrix._22); cosMatrix._23 = rotCos * (SMatrix._23 * SMatrix._23); cosMatrix._31 = rotCos * (SMatrix._31 * SMatrix._31); cosMatrix._32 = rotCos * (SMatrix._32 * SMatrix._32); cosMatrix._33 = rotCos * (SMatrix._33 * SMatrix._33); /* R = I + sin(Theta)(S) + (1 - cos(Theta))(S^2); */ rotMatrix._11 = identityMatrix._11 + sinMatrix._11 + cosMatrix._11; rotMatrix._12 = identityMatrix._12 + sinMatrix._12 + cosMatrix._12; rotMatrix._13 = identityMatrix._13 + sinMatrix._13 + cosMatrix._13; rotMatrix._21 = identityMatrix._21 + sinMatrix._21 + cosMatrix._21; rotMatrix._22 = identityMatrix._22 + sinMatrix._22 + cosMatrix._22; rotMatrix._23 = identityMatrix._23 + sinMatrix._23 + cosMatrix._23; rotMatrix._31 = identityMatrix._31 + sinMatrix._31 + cosMatrix._31; rotMatrix._32 = identityMatrix._32 + sinMatrix._32 + cosMatrix._32; rotMatrix._33 = identityMatrix._33 + sinMatrix._33 + cosMatrix._33; /* Multiply camLookVec by the rotation matrix. */ camNewLookVec.x = camLookVec.x*rotMatrix._11 + camLookVec.y*rotMatrix._12 + camLookVec.z*rotMatrix._13; camNewLookVec.y = camLookVec.x*rotMatrix._21 + camLookVec.y*rotMatrix._22 + camLookVec.z*rotMatrix._23; camNewLookVec.z = camLookVec.x*rotMatrix._31 + camLookVec.y*rotMatrix._32 + camLookVec.z*rotMatrix._33; mLookVec.x = camNewLookVec.x; mLookVec.y = camNewLookVec.y; mLookVec.z = camNewLookVec.z; iCamera::SetVectorLook(&camNewLookVec); // Output code snipped.}What concerns me in particular, is look at the values of the CameraLookVec. X: -1.31294e-006 Y: 0.999999 Z: 0.00116981That seems... incorrect. :DThe only thing I can think of maybe is in this operation:cosMatrix = (1 - cos(theta)) * (S*S)I'm treating the S^2 just like a scalar but maybe I should be multiply the matrix against itself using the matrix multiplication stuff?If anyone could see anything else wrong with what I'm doing that would be great. I have this feeling I'm really close and its killing me :)orphean[Edited by - orphean on June 13, 2006 10:04:22 AM] 0 Share this post Link to post Share on other sites
Zahlman 1682 Report post Posted June 13, 2006 Quote:Original post by orpheanI'm treating the S^2 just like a scalar but maybe I should be multiply the matrix against itself using the matrix multiplication stuff?That would be a problem, yes. "Squaring a matrix" means matrix-multiplying it with itself, not squaring the values element-wise. 0 Share this post Link to post Share on other sites
orphean 122 Report post Posted June 13, 2006 Thank you for the clarification! :)I switched to using proper matrix multiplication for the S^2. The numbers are no longer being expressed in scientific notation so thats a step forward.However I'm getting precisely the same behavior as I explained above.I am just baffled by this. I know I must be doing something wrong but I just can't see it.Orphean. 0 Share this post Link to post Share on other sites
Driv3MeFar 1080 Report post Posted June 13, 2006 edit: I think the results your getting are correct, I just may have provided you with the wrong way of doing things.The rotation method I described is for rotating objects about an arbitrary axis. It is unideal for a camera, because there is usualy (as is the case here) some rotation about the Z axis, which causes the camera's "up" vector to rotate, leading to the tilt you're seeing.To rotate your camera, I would look into the D3D function:HRESULT RotateYawPitchRoll( FLOAT Yaw, FLOAT Pitch, FLOAT Roll);Where Yaw is the rotation about the y-axis, pitch is about the x-axis, and roll is about the z-axis (should be 0 for your camera).To find the Yaw and Pitch values, orthographically project your vectors on the X and Y axis, respectively (that is, set their X or Y values = 0), and use dot products to find your angles.For example:offsetVec = <0, 1/(2 * sqrt(1.25)), 1/sqrt(1.25)>;camEyeVec = <1/sqrt(2), 0, 1/sqrt(2)>;float yawAngle = acos( <0, 1/2sqrt(1.25), 1/sqrt(1.25)> dot <0, 0, 1/sqrt(2)> );float pitchAngle = acos( <0, 0, 1/sqrt(1.25)> dot <1/sqrt(2), 0, 1/sqrt(2)> );float rollAngle = 0;Those two angles should be whats needed to rotate about X and Y to allign your camera with the sprite.Note that this accomplishes pretty much the same thing as the lookAt matrix described above.[Edited by - Driv3MeFar on June 13, 2006 5:31:47 PM] 0 Share this post Link to post Share on other sites