I have recently been trying to calculate a 3D point out of a mouse position. So far I have this:
const D3DXMATRIX* pmatProj = g_Camera.GetProjMatrix();
POINT ptCursor;
GetCursorPos( &ptCursor );
ScreenToClient( DXUTGetHWND(), &ptCursor );
// Compute the vector of the pick ray in screen space
D3DXVECTOR3 v;
v.x = ( ( ( 2.0f * ptCursor.x ) / pd3dsdBackBuffer->Width ) - 1 ) / pmatProj->_11;
v.y = -( ( ( 2.0f * ptCursor.y ) / pd3dsdBackBuffer->Height ) - 1 ) / pmatProj->_22;
v.z = 1.0f;
// Get the inverse view matrix
const D3DXMATRIX matView = *g_Camera.GetViewMatrix();
const D3DXMATRIX matWorld = *g_Camera.GetWorldMatrix();
D3DXMATRIX mWorldView = matWorld * matView;
D3DXMATRIX m;
D3DXMatrixInverse( &m, NULL, &mWorldView );
// Transform the screen space pick ray into 3D space
vPickRayDir.x = v.x * m._11 + v.y * m._21 + v.z * m._31;
vPickRayDir.y = v.x * m._12 + v.y * m._22 + v.z * m._32;
vPickRayDir.z = v.x * m._13 + v.y * m._23 + v.z * m._33;
vPickRayOrig.x = m._41;
vPickRayOrig.y = m._42;
vPickRayOrig.z = m._43;
However, as my mathematical skills are lacklustre, I am unsure how to utilise the direction and origin to produce a position. What calculations/formulas do I need to perform to produce the desired results?
It's just like a * x + b, except three times.
For any distance d (positive or negative) from vPickRayOrig:
newPos.x = d * vPickRayDir.x + vPickRayOrig.x;
newPos.y = d * vPickRayDir.y + vPickRayOrig.y;
newPos.z = d * vPickRayDir.z + vPickRayOrig.z;
Hi I'm making a 3D graphics engine for an assignment that is due later tonight, it's going smoothly at the moment except I'm loading a cube model from an .obj file, the positions start at 0.
My transformation matrix works for numbers that don't = 0. I mean if X = 0 and I try to translate it by 10 on the X Axis, it returns 0.
Matrix * Vector:
Vec4 Mat4::operator*(const Vec4& v) const
{
Vec4 tmp(0, 0, 0, 0, 255, 255, 255, 255);
tmp.x = (this->data[0] * v.x) + (this->data[4] * v.y) + (this->data[8] * v.z) + (this->data[12] * v.w);
tmp.y = (this->data[1] * v.x) + (this->data[5] * v.y) + (this->data[9] * v.z) + (this->data[13] * v.w);
tmp.z = (this->data[2] * v.x) + (this->data[6] * v.y) + (this->data[10] * v.z) + (this->data[14] * v.w);
tmp.w = (this->data[3] * v.x) + (this->data[7] * v.y) + (this->data[11] * v.z) + (this->data[15] * v.w);
return tmp;
}
Translate Matrix:
Mat4 Mat4::translate(float x, float y, float z)
{
Mat4 tmp;
tmp.data[12] = x;
tmp.data[13] = y;
tmp.data[14] = z;
return tmp;
}
A Mat4 class by default is an identity matrix.
It is too late now, but... it might be helpful to know the following:
A vector strictly equal to 0.0 (e.g. <0,0,0,0>) cannot be translated using matrix multiplication and technically should not be considered a position in this context. In fact, such a vector is not even representative of a direction because it has 0 length. It is simply zero; there are not a whole lot of uses for a vector that cannot be rotated or translated.
You can rotate vectors with 0.0 for the W coordinate, but the value 0.0 for W prevents translation.
Generally you want a W coordinate of 1.0 for spatial (e.g. position) vectors and 0.0 for directional (e.g. normal).
If you want to understand this better, you need to consider how your 4x4 matrix is setup. The first 3 rows or columns (depending on which convention you use) store rotation, and the 4th stores translation.
Consider how translation is applied when you multiply your matrix and vector:
x = ... + (this->data[12] * v.w);
y = ... + (this->data[13] * v.w);
z = ... + (this->data[14] * v.w);
w = ... + (this->data[15] * v.w);
If v.w is 0.0, then translation evaluates to 0.0 for all coordinates.
I am trying the calculate normal per vertex.
But I do something wrong. When I run the code I see this:
Here Is my code, Note that vertex1 is vertex before the current vertex and vertex2 is vertex after the current vertex.
for (int j = 0; j < meshes[t].face[i].numOfPoints; j++)
{
if (normalSetChange)
{
vector3D vertex1, vertex2;
if ((j < meshes[t].face[i].numOfPoints - 1) && (j > 0))
{
vertex1 = vertexes[meshes[t].face[i].vertex[j + 1]] - vertexes[meshes[t].face[i].vertex[j]];
vertex2 = vertexes[meshes[t].face[i].vertex[j - 1]] - vertexes[meshes[t].face[i].vertex[j]];
}
else if (j < meshes[t].face[i].numOfPoints - 1)
{
vertex1 = vertexes[meshes[t].face[i].vertex[j + 1]] - vertexes[meshes[t].face[i].vertex[j]];
vertex2 = vertexes[meshes[t].face[i].vertex[meshes[t].face[i].numOfPoints - 1]] - vertexes[meshes[t].face[i].vertex[j]];
}
else if (j > 0)
{
vertex1 = vertexes[meshes[t].face[i].vertex[0]] - vertexes[meshes[t].face[i].vertex[j]];
vertex2 = vertexes[meshes[t].face[i].vertex[j - 1]] - vertexes[meshes[t].face[i].vertex[j]];
}
normalSet = vector3D(vertex1.y * vertex2.z - vertex1.z * vertex2.y,
vertex1.z * vertex2.x - vertex1.x * vertex2.z,
vertex1.x * vertex2.y - vertex1.y * vertex2.x);
normalLength = sqrt(normalSet.x * normalSet.x + normalSet.y * normalSet.y + normalSet.z * normalSet.z);
normalSet.x /= normalLength;
normalSet.y /= normalLength;
normalSet.z /= normalLength;
writePolygonLineVCN(PolygonLineVCN(vertexes[meshes[t].face[i].vertex[j]], vertexestexCoordinate[meshes[t].face[i].texCoordinate[j]], normalSet), newFile[workOnCPU]);
}
else
writePolygonLineVCN(PolygonLineVCN(vertexes[meshes[t].face[i].vertex[j]], vertexestexCoordinate[meshes[t].face[i].texCoordinate[j]], vertexesNormals[meshes[t].face[i].normal[j]]), newFile[workOnCPU]);
}
You are computing normals per triangle, not per vertex. In fact you can clearly see "solid" normals in the image you posted.
In order to compute "smooth" normals, you need to assign to each vertex a normal which is an average of the normals of the triangles adjacent to that vertex.
Here's some pseudocode, which computed the weighted average of the normals based on the angle between the two edges adjacent to the vertex. (Maybe someone uses the area of the triangle as weight, I don't know if there is an universally accepted way to do it).
vector3D triangleNormalFromVertex(int face_id, int vertex_id) {
//This assumes that A->B->C is a counter-clockwise ordering
vector3D A = mesh.face[face_id].vertex[vertex_id];
vector3D B = mesh.face[face_id].vertex[(vertex_id+1)%3];
vector3D C = mesh.face[face_id].vertex[(vertex_id+2)%3];
vector3D N = cross(B-A,C-A);
float sin_alpha = length(N) / (length(B-A) * length(C-A) );
return normalize(N) * asin(sin_alpha);
}
void computeNormals() {
for (vertex v in mesh) {
vector3D N (0,0,0);
for (int i = 0;i < NumOfTriangles;++i) {
if (mesh.face[i].contains(v) ) {
int VertexID = index_of_v_in_triangle(i,v); //Can be 0,1 or 2
N = N + triangleNormalFromVertex(i,VertexID);
}
}
N = normalize(N);
add_N_to_normals_for_vertex_v(N,v);
}
}
I have a problem when trying to tesselate a polygon using GLU. The vertex callback always calls back with the last vertex defined by gluTessVertex. It seems as though the values stored in GLdouble v[3] are getting GC'd in each iteration of the for loop. How can I store each GLdouble v[3] so it does not get GC'd?
for(int i = 0; i < vtxcnt; i++)
{
float lon = dbls[i * 2];
float lat = dbls[(i * 2)+1];
GLdouble v[3] = {lon, lat, 0.0f};
gluTessVertex(tess, v, v);
}
* EDIT: This seems to fix the problem... *
GLdouble *vtxs = new GLdouble[vtxcnt * 3];
for(int i = 0; i < vtxcnt; i++)
{
lon = dbls[i * 2];
lat = dbls[(i * 2)+1];
vtxs[(i * 3) + 0] = (double)lon;
vtxs[(i * 3) + 1] = (double)lat;
vtxs[(i * 3) + 2] = (double)0;
gluTessVertex(tess, &vtxs[(i * 3) + 0], &vtxs[(i * 3) + 0]);
}
gluTessVertex only stores the vertex pointer. The pointer must stay valid until the tesselation is performed. This is not the case in your code, so it fails.
I think I understand why calling glRotate(#, 0, 0, 0) results in a divide-by-zero. The rotation vector, a, is normalized: a' = a/|a| = a/0
Is that the only situation glRotate could result in a divide-by-zero? Yes, I know glRotate is deprecated. Yes, I know the matrix is on the OpenGL manual. No, I don't know linear algebra enough to confidently answer the question from the matrix. Yes, I think it would help. Yes, I asked this already in #opengl (can you tell?). And no, I didn't get an answer.
I would say yes. And I would say that you are right about the normalization step as well. The matrix shown in the OpenGL manual only consists of multiplications. And multiplying a vector would result into the same. Of course, it would do strange things if you result in a vector of (0,0,0). OpenGL states in the same manual that |x,y,z|=1 (or OpenGL will normalize).
So IF it wouldn't normalize, you would end up with a very empty matrix of:
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 1
Which will implode your object in the strangest ways. So DON'T call this function with a zero-vector. If you would like to, tell me why.
And I recommend using a library like GLM to do your matrix calculations if it gets too complicated for some simple glRotates.
Why should it divide by zero when you can check for that?:
/**
* Generate a 4x4 transformation matrix from glRotate parameters, and
* post-multiply the input matrix by it.
*
* \author
* This function was contributed by Erich Boleyn (erich#uruk.org).
* Optimizations contributed by Rudolf Opalla (rudi#khm.de).
*/
void
_math_matrix_rotate( GLmatrix *mat,
GLfloat angle, GLfloat x, GLfloat y, GLfloat z )
{
GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c, s, c;
GLfloat m[16];
GLboolean optimized;
s = (GLfloat) sin( angle * DEG2RAD );
c = (GLfloat) cos( angle * DEG2RAD );
memcpy(m, Identity, sizeof(GLfloat)*16);
optimized = GL_FALSE;
#define M(row,col) m[col*4+row]
if (x == 0.0F) {
if (y == 0.0F) {
if (z != 0.0F) {
optimized = GL_TRUE;
/* rotate only around z-axis */
M(0,0) = c;
M(1,1) = c;
if (z < 0.0F) {
M(0,1) = s;
M(1,0) = -s;
}
else {
M(0,1) = -s;
M(1,0) = s;
}
}
}
else if (z == 0.0F) {
optimized = GL_TRUE;
/* rotate only around y-axis */
M(0,0) = c;
M(2,2) = c;
if (y < 0.0F) {
M(0,2) = -s;
M(2,0) = s;
}
else {
M(0,2) = s;
M(2,0) = -s;
}
}
}
else if (y == 0.0F) {
if (z == 0.0F) {
optimized = GL_TRUE;
/* rotate only around x-axis */
M(1,1) = c;
M(2,2) = c;
if (x < 0.0F) {
M(1,2) = s;
M(2,1) = -s;
}
else {
M(1,2) = -s;
M(2,1) = s;
}
}
}
if (!optimized) {
const GLfloat mag = SQRTF(x * x + y * y + z * z);
if (mag <= 1.0e-4) {
/* no rotation, leave mat as-is */
return;
}
x /= mag;
y /= mag;
z /= mag;
/*
* Arbitrary axis rotation matrix.
*
* This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied
* like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation
* (which is about the X-axis), and the two composite transforms
* Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary
* from the arbitrary axis to the X-axis then back. They are
* all elementary rotations.
*
* Rz' is a rotation about the Z-axis, to bring the axis vector
* into the x-z plane. Then Ry' is applied, rotating about the
* Y-axis to bring the axis vector parallel with the X-axis. The
* rotation about the X-axis is then performed. Ry and Rz are
* simply the respective inverse transforms to bring the arbitrary
* axis back to its original orientation. The first transforms
* Rz' and Ry' are considered inverses, since the data from the
* arbitrary axis gives you info on how to get to it, not how
* to get away from it, and an inverse must be applied.
*
* The basic calculation used is to recognize that the arbitrary
* axis vector (x, y, z), since it is of unit length, actually
* represents the sines and cosines of the angles to rotate the
* X-axis to the same orientation, with theta being the angle about
* Z and phi the angle about Y (in the order described above)
* as follows:
*
* cos ( theta ) = x / sqrt ( 1 - z^2 )
* sin ( theta ) = y / sqrt ( 1 - z^2 )
*
* cos ( phi ) = sqrt ( 1 - z^2 )
* sin ( phi ) = z
*
* Note that cos ( phi ) can further be inserted to the above
* formulas:
*
* cos ( theta ) = x / cos ( phi )
* sin ( theta ) = y / sin ( phi )
*
* ...etc. Because of those relations and the standard trigonometric
* relations, it is pssible to reduce the transforms down to what
* is used below. It may be that any primary axis chosen will give the
* same results (modulo a sign convention) using thie method.
*
* Particularly nice is to notice that all divisions that might
* have caused trouble when parallel to certain planes or
* axis go away with care paid to reducing the expressions.
* After checking, it does perform correctly under all cases, since
* in all the cases of division where the denominator would have
* been zero, the numerator would have been zero as well, giving
* the expected result.
*/
xx = x * x;
yy = y * y;
zz = z * z;
xy = x * y;
yz = y * z;
zx = z * x;
xs = x * s;
ys = y * s;
zs = z * s;
one_c = 1.0F - c;
/* We already hold the identity-matrix so we can skip some statements */
M(0,0) = (one_c * xx) + c;
M(0,1) = (one_c * xy) - zs;
M(0,2) = (one_c * zx) + ys;
/* M(0,3) = 0.0F; */
M(1,0) = (one_c * xy) + zs;
M(1,1) = (one_c * yy) + c;
M(1,2) = (one_c * yz) - xs;
/* M(1,3) = 0.0F; */
M(2,0) = (one_c * zx) - ys;
M(2,1) = (one_c * yz) + xs;
M(2,2) = (one_c * zz) + c;
/* M(2,3) = 0.0F; */
/*
M(3,0) = 0.0F;
M(3,1) = 0.0F;
M(3,2) = 0.0F;
M(3,3) = 1.0F;
*/
}
#undef M
matrix_multf( mat, m, MAT_FLAG_ROTATION );
}