I have been trying to throw together a chess game using a 3dsmax model. At this point, I have been able to import the model, highlight the selected game piece I am interested in moving, and choose a square I want to move to. Here is a screenshot of the current state:
http://img26.imageshack.us/img26/9555/chessk.png
The black circle represents where I clicked, and you can see where the pawn went. I haven't done specific calculations on where it should go. Whenever I click on the board with a selected piece, it always moves in the same direction. It's because just threw in this dummy code to start off:
if ( isObjectSelected && isSquareSelected && moveObject )
{
glPushMatrix();
glTranslatef(0.2f, 0.0f, 0.0f); //PLACEHOLDER-DUMMY CODE
}
glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + pMaterial->startIndex );
if ( isObjectSelected && isSquareSelected )
glPopMatrix();
What I was considering doing was after the model was done loading, is to somehow check which square on the board a game piece occupies. Then, when a piece is selected and a "move to" square is selected, find the x,y,z glTranslate3f to move to that center square.
Is this the best way? It seems as the game progresses, I will need to store the glTranslate of each piece individually. And when a piece goes from it's 2nd to 3rd spot, I should calculate the glTranslate from the original starting point to the 3rd spot, right?
But how would you figure out whether or not a game piece occupies a square, and how would you figure out glTranslate3f(X, Y, Z) between two squares? Here is an example of a square from my .OBJ file
#
# object Square58
#
v -37.1874 18.6313 80.7864
v -67.0955 18.6313 91.4436
v -56.4384 18.6313 121.3513
v -26.5306 18.6313 110.6938
# 4 vertices
vn 0.0000 1.0000 -0.0000
# 1 vertex normals
vt 0.0000 0.0000 0.0000
# 1 texture coords
I am assuming I would need to find the center of each square and say once the app knows this piece is in square1, and you clicked on sqaure4, calculate the translate & go. I am just not sure how to calculate the center of each square, and figure out what the translate coords should be from square1-->square4.
OR how I would determine which pieces occupies which square from the beginning. I can hard code this in during load, but it would help me more in understanding if there was a sound way to accomplish this.
Each square & game piece is a struct GroupObject like:
//A chess piece or square
struct GroupObject
{
std::vector<Material *> materials;
std::string objectName;
std::string groupName;
int index;
std::vector<Vector3 *> vertices;
Vector3 center;
};
And Vector3 looks like:
#ifndef VECTOR3_H
#define VECTOR3_H
#include <math.h>
class Vector3
{
public:
Vector3(float X = 0.0f, float Y = 0.0f, float Z = 0.0f)
{
x = X;
y = Y;
z = Z;
}
Vector3 operator+=(const Vector3 &vec)
{
return (*this = (*this + vec) );
}
Vector3 operator+(const Vector3 &vec)
{
return Vector3(vec.x + x, vec.y + y, vec.z + z);
}
Vector3 operator-=(const Vector3 &vec)
{
return (*this = (*this - vec) );
}
Vector3 operator-(const Vector3 &vec)
{
return Vector3(x - vec.x, y - vec.y, z - vec.z);
}
Vector3 operator*=(float num)
{
return (*this = (*this * num) );
}
Vector3 operator*(float num)
{
return Vector3(x * num, y * num, z * num);
}
Vector3 operator/=(float num)
{
return (*this = (*this / num) );
}
Vector3 operator/(float num)
{
return Vector3(x / num, y / num, z / num);
}
Vector3 operator-(void)
{
//invert direction of vector
return Vector3(-x, -y, -z);
}
float Dot(Vector3 &vec)
{
return (x * vec.x + y * vec.y + z * vec.z);
}
Vector3 operator*(const Vector3 &vec)
{
//cross product
return Vector3( y * vec.z - z * vec.y,
z * vec.x - x * vec.z,
x * vec.y - y * vec.x );
}
float Length(void)
{
//length of vector
return sqrt( x * x + y * y + z * z);
}
Vector3 Normalize(void)
{
float length = Length();
x /= length;
y /= length;
z /= length;
return *this;
}
float Distance(Vector3 &vec)
{
//find distance between two separate vectors
float disX = vec.x - x;
float disY = vec.y - y;
float disZ = vec.z - z;
return sqrt(disX * disX + disY * disY + disZ * disZ);
}
bool operator==(Vector3 &vec)
{
return (vec.x == x && vec.y == y && vec.z == z);
}
bool operator!=(Vector3 &vec)
{
return !(vec == *this);
}
public:
float x, y, z;
};
#endif
I was going to use this to store the center of each square like:
Vector3 square->center = X, Y, Z
Hopefully this would help it determining where to move, and if something occupies that space, but figured I should get some help since I haven't done this before. I am also not sure how I should calculate it, or still completely clear on how to figure out whether a square has a game piece that occupies it at the beginning, or it's empty.
Any and all help is welcome. Thank you.
UPDATE
Right now I had a buddy look in 3ds max and on a square object properties it said dimensions were x: 40.565 y: 40.565
Then I picked two squares, one in front of the other. I calculated the center of each by added all the vertices together and divided by the sum of the vertices. I got this:
#square 1
x: 111.12125
y: 18.631268
z: 78.286982
#square 2
x: 82.276817
y: 17.615297
z: 88.545845
But from my original example, the only way I could get even close to moving to the right spot was moving glTranslatef(0.2f, 0.0f, 0.0f);
The difference between the two centers listed above is much larger. I am doing something incorrect, but not seeing it.
[a-bit-offtopic]
When i was coding my univercity project, i used to draw my 'world' (which has something similar with check-desk: it is endless and have no colour distincts) using simple math formulas for determining each line cross points. When i got those points, i was able to simply determine which particular square cursor is pointing. To be honest, i used simple mathematics to determine those centers in the last ('production') version.
Here and there are my old screens.
So, what's the trouble? When you're doing glPushMatrix(), your matrix resets to its defaults - the position becomes (0, 0, 0) and rotations are reset. If you wanna put your object in the point of intersect {line, plane}, where the line is the ray from camera origin through cursor position (recall camera theory in OpenGL: actually, screen is the plane in front of camera 'position' point) and the plane is something in front of that ray. Note: if you'll not define the plane - you'll be not able to determine intersection point.
Now, when you'll got intersection point, just do glTranslatef(<intersection_point>) and then draw your pawn or whatever.
If you are interested in pure code on how to determine intersection point - just notify me in comments.
Hope this will help.
UPD: here's the intersection point code i've mentioned before. It uses sf::Vector3f structure/class - it is taken from SFML (i'm using it for all my GL projects to handle input events and forget 'bout window creation). You can define it as a simple structure with 3 floats. Function arguments are the mouse cursor' coordinates. Calling this would provide you with intersection point coordinates.
sf::Vector3f ScreenToSpace(int x, int y)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
winX = (float) x;
winY = (float) viewport[3] - (float) y;
glReadPixels(x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
return sf::Vector3f((float) posX, (float) posY, (float) posZ);
}
Here's the example on how you should use this (as far as i've understood your problem):
sf::Vector3f v = ScreenToSpace(App.GetInput().GetMouseX(), App.GetInput().GetMouseY()); // get global coordinates
v = SpaceToDeskCorrection(v.x, v.y, v.z); // align v to the check-desk cell center
glPushMatrix(); // reset matrix state (incl. position and rotation) and save previous one
glTranslatef(v.x, v.y, v.z); // set matrix position to the intersection point
DrawPawn(); // well, you'll understand this line, right? =)
glPopMatrix();
I have recently done a checkers game myself, 2D but the same principles apply. First of all I would suggest you keep an array with board squares, which as you defined in your struct has a centre var.
To know where each piece is, you could for instance add a variable which is a pointer to a given square, or simply a representation like 'E4' or 'A8' (square code). Having this, you would automatically have the x,y coordinates of where the piece is, and once detecting which square you want to move it to, you just substitute the pointer to the new piece and get its centre information, which is where you want to translate the piece to. This also has the added benefit that it makes it easier for you to check later when a piece takes over another one from the adversary, or simply if it can move to a certain square (go through all the pieces, if any of them is laying on the square you want to go to, don't allow the move).
To find out the centre of any square, assuming you don't know it when you create the board (don't you ?), and do an average over all the constituting vertices x and y coordinates (if the board is laying on the xy plane). For instance, Square58 centre = (v1(x)+v2(x)+v3(x)+v4(x) ; v1(y)+v2(y)+v3(y)+v4(y)).
I hope that was clear enough, if not just let me know what you didn't get and I'll try to explain.
EDIT: Sorry, I wrote something wrong. In the Square58 centre.... line, you divide the final sum by 4 (to get the average).. ie:
Square58 centre = ((v1(x)+v2(x)+v3(x)+v4(x))/4 ; (v1(y)+v2(y)+v3(y)+v4(y))/4).
Related
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.)
I Am currently having alot of problems with the camera I am making. The problem occurs with my matrix rotation I am doing as this website says to avoid gimble lock..
One of the first problems you will note is that the order you apply
these rotations matter. As previously stated, a rotation matrix is an
orientation transform. Each transform defines a new coordinate system,
and the next transform is based on an object in the new space. For
example, if we apply the roll first, we have now changed what the axis
for the subsequent yaw is.
And when i perform this for example if I am wanted to pitch around the current x axis the x axis also changes in my axis to rotation method which is obviously wrong. Ive look around alot and cant find any solution. I have tried alot of differenet version of the axis angle rotation matrix..
void FrustumCamera::xAxisRotation(float angle)
{
Vector3<float> x = m_orientation.getXAxis();
Matrix4<float> matrix = m_orientation.axisAngleRotation(x,angle);
m_orientation = matrix*m_orientation;
normalise(m_orientation.getXAxis());
normalise(m_orientation.getYAxis());
normalise(m_orientation.getZAxis());
}
void FrustumCamera::yAxisRotation(float angle)
{
Vector3<float> y = m_orientation.getYAxis();
Matrix4<float> matrix = m_orientation.axisAngleRotation(y,angle);
m_orientation = matrix*m_orientation;
normalise(m_orientation.getXAxis());
normalise(m_orientation.getYAxis());
normalise(m_orientation.getZAxis());
}
Matrix4<Type> Matrix4<Type>::operator*(Matrix4& matrix)
{
Matrix4<Type> temp(m_matrix);
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
Type total = 0;
for(int k=0;k<4;k++)
{
total += m_matrix[i][k]*matrix.getAt(k,j);;
}
temp.setAt(i,j,total);
}
}
return temp;
}
template <class Type>
Matrix4<Type> Matrix4<Type>::axisAngleRotation(Vector3<Type> axis, const Type angle)
{
Type radians = angle * (double)degToRad;
Matrix4<Type> temp;
float c = cosf(radians);
float s = sinf(radians);
float t = 1.0f - c;
float x = axis.x;
float y = axis.y;
float z = axis.z;
temp.setAt(0,0, c+x*x*(t));
temp.setAt(0,1, x*y*(t)-z*s);
temp.setAt(0,2, x*z*(t)+y*s);
temp.setAt(0,3, 0.0f);
temp.setAt(1,0, y*x*(t)+z*s);
temp.setAt(1,1, c+y*y*(t));
temp.setAt(1,2, y*z*(t)-x*s);
temp.setAt(1,3, 0.0f);
temp.setAt(2,0, z*x*(t)-y*s);
temp.setAt(2,1, z*y*(1-c)+x*s);
temp.setAt(2,2, c+z*z*(t));
temp.setAt(2,3, 0.0f);
temp.setAt(3,0, 0.0f);
temp.setAt(3,1, 0.0f);
temp.setAt(3,2, 0.0f);
temp.setAt(3,3, 1.0f);
return temp;
}
void OpenGLRenderer::startDraw(unsigned long mask)
{
//sortBuffer(); // sort draw queue
clearBuffers(mask); // clear buffers
loadIdentity();
glTranslatef(-1*m_frustumCamera->getViewMatrix().getTranslationAxis().x,-1*m_frustumCamera->getViewMatrix().getTranslationAxis().y,-1*m_frustumCamera->getViewMatrix().getTranslationAxis().z);// load identity
glMultMatrixf(m_frustumCamera->getViewMatrix().getMatrix());
glTranslatef(m_frustumCamera->getViewMatrix().getTranslationAxis().x,m_frustumCamera->getViewMatrix().getTranslationAxis().y,m_frustumCamera->getViewMatrix().getTranslationAxis().z);
matrixStackPush();
}
I think order of multiplication can cause the problem, instead of
m_orientation = matrix*m_orientation;
try
m_orientation = m_orientation * matrix;
I have been trying to get a working algorithm that detects intersection between a ray (representing the bullets from a gun) and a sphere around the enemy.... I tried a few found on the net but none seems to work properly, maybe I am doing something wrong...
This is the one I am currently using:
//// Ray-sphere intersection.
// p=(ray origin position - sphere position),
// d=ray direction,
// r=sphere radius,
// Output:
// i1=first intersection distance,
// i2=second intersection distance
// i1<=i2
// i1>=0
// returns true if intersection found,false otherwise.//
bool Player::RaySphereIntersect(const Vector3 &p, const Vector3 &d, double r, double &i1, double &i2){
double det,b;
b = -Vector3::dot(p,d);
det = b*b - Vector3::dot(p,p) + r*r;
if (det<0){
return false;
}
det= sqrt(det);
i1= b - det;
i2= b + det;
// intersecting with ray?
if(i2<0)
return false;
if(i1<0)
i1=0;
return true;
}
Where I use the position of the enemy as sphere position, roughly the position of the player's gun as ray origin and the projected mouse coordinates for ray direction... This is the OpenGL code I am using to project the mouse coords to the the far plane:
Vector3 projectedMouse(float mx, float my){
GLdouble model_view[16];
GLint viewport[4];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble dx, dy, dz, bx, by, bz;
glGetDoublev(GL_MODELVIEW_MATRIX, model_view);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
winX = (float)mx;
winY = (float)viewport[3] - (float)my;
glReadPixels ((int)mx, (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, 1, model_view, projection, viewport, &dx, &dy, &dz);
projectedAim = Vector3(dx, dy, dz);
return projectedAim;
}
Which seems right cos I am drawing a GL Line with it and it looks fine... So maybe it's the intersection code, but nothing seems to work.... I tried this other one that should return the intersection point distance, but for any given enemy position, it still gives me very random results:
double intersectRaySphere(Vector3 rO, Vector3 rV, Vector3 sO, double sR)
Vector3 Q = sO-rO;
double c = Q.magnitude();
double v = Vector3::dot(Q,rV);
double d = sR*sR - (c*c - v*v);
// If there was no intersection, return -1
if (d < 0.0) return (-1.0f);
// Return the distance to the [first] intersecting point
return (v - sqrt(d));
they have both been slightly modified to match the Math function in the library that I am using.... can anyone spot something wrong with them, or suggest another one? this is driving me crazy....
Thank you!
It looks like you are doing something with Cramer's rule to solve for intersection. Consider substitution. The roots of the polynomial will tell you the points of intersection.
Starting with the 2D case what we want is to see if the orthogonal (and thus the minimum) distance of the point P located at the center of circle C has a distance less than the radius R of the circle C.
Basically. We find the minimum distance between the center of the circle and your ray/line. How do we do this? There are a few ways.
We know the shortest distance is a straight line that has the orthogonal slope (in R2 negative recip ) to our ray starting at the middle of the circle. We then find the intersection between our two lines. if we had to go more than length R we are outside and we don't care how far it is.
http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html
Solving the intersection of two lines and seeing it the intersection is further than R may not be the most efficient approach but fortunately wolfram has a better way to do essentially the same thing using higher level math.
Now considering a ray in R3 intersecting a sphere its basically the same thing, but "orthogonal" is harder to please than in R2, so we use double cross products. and solve parametric equations.
http://www.cs.umbc.edu/~olano/435f02/ray-sphere.html
This is a clever way to see if any portion of our ray satisfies the equation of our sphere as a constraint.
float a = Vector3::dot(d,d);
float b = Vector3::dot(d * 2, p);
float c = Vector3::dot(p,p) - r*r
// if the discriminant of the quadratic formula is positive
// we have at least one intersection
return (b*b - 4 * a * c) >= 0
In short. I only found Cramer's rule helpful in differential equations when my columns were functions and its derivitives. Usually when finding the Wronskian.
I'd like to convert a 3d position into 2d screen position. I had a look at a similar question: Projecting a 3D point to a 2D screen coordinate , but I dont understand it completely. I thought in order to calculate the 2d position I would need the projection matrix, but I dont see how it is used, apart from converting a point into the location coordinate space. Besides, is cam.FieldOfView equal to farZ in OpenGL?
Could someone please help me complete this function. Are the parameters sufficient to calculate the 2d position? Pos is already a vector relative to the camera position.
Vector2* convert(Vector3& pos, Matrix4& projectionMatrix, int screenWidth, int screenHeight)
{
float ratio = screenWidth / screenHeight;
...
screenX = screenWidth * ( 1.0f - screenX);
screenY = screenHeight * ( 1.0f - screenY);
return new Vector2(screenX, screenY);
}
Seems to me it would be something like that:
Vector2 Convert(Vector3 pos, const Matrix& viewMatrix, const Matrix& projectionMatrix, int screenWidth, int screenHeight)
{
pos = Vector3::Transform(pos, viewMatrix);
pos = Vector3::Transform(pos, projectionMatrix);
pos.X = screenWidth*(pos.X + 1.0)/2.0;
pos.Y = screenHeight * (1.0 - ((pos.Y + 1.0) / 2.0));
return Vector2(pos.X, pos.Y);
}
What are we doing here is just passing the Vector though the two transformation matrices: the view, then the projection. After the projection you get a vector with Y and X between -1 and 1. We do the appropriate transformation to obtain real pixel coordinates and return a new Vector2. Note that the Z component of 'pos' also store the depth of the point, in the screen space, at the end of the function.
You need the 'view' matrix because it defines where the camera is located and rotated. The projection only defines the way the 3D space is 'flattened' on the 2D space.
A field of view is not the farZ. A projection matrix has some parameters, among them:
the field of view, FOV, that is the horizontal angle of view, in radians;
the far plane, or farZ : this defines the maximum distance a point can be from the camera;
the near plane, nearZ: the minimum distance a point can be from the camera.
Besides the math problem, you may use directly the Vector2 instead of a heap allocation (returning a pointer). Vector2 is a light structure and pointers are very likely to cause headaches in this context (where are you going to delete it, and so on). Also note that I used 'const' references as we do not modify them, except the vector. For this one we want a local copy, this is why it is not a reference at all.
Previous code only work if you do not do any rotations (for eg. GL.Rotate(rotation_x, 1.0, 0.0, 0.0)).
But if you do here is the code:
private Vector2 Convert(Vector3 pos, Matrix4 viewMatrix, Matrix4 projectionMatrix, int screenWidth, int screenHeight)
{
pos = Vector3.Transform(pos, viewMatrix);
pos = Vector3.Transform(pos, projectionMatrix);
pos.X /= pos.Z;
pos.Y /= pos.Z;
pos.X = (pos.X + 1) * screenWidth / 2;
pos.Y = (pos.Y + 1) * screenHeight / 2;
return new Vector2(pos.X, pos.Y);
}
I think what you're looking for is a replacement for gluLookAt. Given a position and orientation it converts the scene geometry into screen coordinates for rendering. As the article says, it relies on a number of deprecated features of OpenGL, but it does provide a code sample you can implement using your vector / matrix library. More detailed information on the projection matrices is available from here.
Once you have the projection matrix you simply apply it to your vectors (post-multiply your scene's vectors by the projection matrix) and then just drop the Z component of the resulting vector ... that is, just use the X and Y components of the resultant vectors.
I'm new to c++ 3D, so I may just be missing something obvious, but how do I convert from 3D to 2D and (for a given z location) from 2D to 3D?
You map 3D to 2D via projection. You map 2D to 3D by inserting the appropriate value in the Z element of the vector.
It is a matter of casting a ray from the screen onto a plane which is parallel to x-y and is at the required z location. You then need to find out where on the plane the ray is colliding.
Here's one example, considering that screen_x and screen_y ranges from [0, 1], where 0 is the left-most or top-most coordinate and 1 is right-most or bottom-most, respectively:
Vector3 point_of_contact(-1.0f, -1.0f, -1.0f);
Matrix4 view_matrix = camera->getViewMatrix();
Matrix4 proj_matrix = camera->getProjectionMatrix();
Matrix4 inv_view_proj_matrix = (proj_matrix * view_matrix).inverse();
float nx = (2.0f * screen_x) - 1.0f;
float ny = 1.0f - (2.0f * screen_y);
Vector3 near_point(nx, ny, -1.0f);
Vector3 mid_point(nx, ny, 0.0f);
// Get ray origin and ray target on near plane in world space
Vector3 ray_origin, ray_target;
ray_origin = inv_view_proj_matrix * near_point;
ray_target = inv_view_proj_matrix * mid_point;
Vector3 ray_direction = ray_target - ray_origin;
ray_direction.normalise();
// Check for collision with the plane
Vector3 plane_normal(0.0f, 0.0f, 1.0f);
float denominator = plane_normal.dotProduct(ray_direction);
if (fabs(denom) >= std::numeric_limits<float>::epsilon())
{
float num = plane_normal.dotProduct(ray.getOrigin()) + Vector3(0, 0, z_pos);
float distance = -(num/denom);
if (distance > 0)
{
point_of_contact = ray_origin + (ray_direction * distance);
}
}
return point_of_contact
Disclaimer Notice: This solution was taken from bits and pieces of Ogre3D graphics library.
The simplest way is to do a divide by z. Therefore ...
screenX = projectionX / projectionZ;
screenY = projectionY / projectionZ;
That does perspective projection based on distance. Thing is it is often better to use homgeneous coordinates as this simplifies matrix transformation (everything becomes a multiply). Equally this is what D3D and OpenGL use. Understanding how to use non-homogeneous coordinates (ie an (x,y,z) coordinate triple) will be very helpful for things like shader optimisations however.
One lame solution:
^ y
|
|
| /z
| /
+/--------->x
Angle is the angle between the Ox and Oz axes (
#include <cmath>
typedef struct {
double x,y,z;
} Point3D;
typedef struct {
double x,y;
} Point2D
const double angle = M_PI/4; //can be changed
Point2D* projection(Point3D& point) {
Point2D* p = new Point2D();
p->x = point.x + point.z * sin(angle);
p->y = point.y + point.z * cos(angle);
return p;
}
However there are lots of tutorials on this on the net... Have you googled for it?