Jump to content

  • Log In with Google      Sign In   
  • Create Account

Awesome job so far everyone! Please give us your feedback on how our article efforts are going. We still need more finished articles for our May contest theme: Remake the Classics

haegarr

Member Since 10 Oct 2005
Offline Last Active Yesterday, 10:45 PM
*****

#5050886 How to re-orthogonalise vectors

Posted by haegarr on 07 April 2013 - 10:03 AM

Let aside whether using quaternions makes sense in your use case, IMO your code snippet shows a construction flaw:

 

...
D3DXVec3TransformCoord(&forward, &forward, &matYaw); D3DXVec3TransformCoord(&right, &right, &matYaw); //Apply the yaw transformation to the relevant vectors
D3DXVec3TransformCoord(&forward, &forward, &matPitch); D3DXVec3TransformCoord(&up, &up, &matPitch); //Apply the pitch transformation to the relevant vectors
D3DXVec3TransformCoord(&right, &right, &matRoll); D3DXVec3TransformCoord(&up, &up, &matRoll); //Apply the roll transformation to the relevant vectors

OrientationMatrix *= matPitch*matYaw*matRoll; //Adjust the orientation matrix

 

The 3 row vectors of a rotation (row) matrix already define the side, up, and forward vectors of the local frame w.r.t. the parental frame. That means that your vectors right, forward, and up are logically part of your OrientationMatrix.

 

Now, you compute the new OrientationMatrix as:

  OrientationMatrixn+1 = OrientationMatrixn * matPitch matYaw matRoll

 

But you compute the new vectors as:

  OrientationVectorsn+1 = OrientationVectorsn * matYaw * matPitch * matRoll

 

That is obviously not the same when remembering that matrix multiplications isn't commutative. Hence the both orientation representations you use will be different, although logically they should be the same.




#5050822 How to re-orthogonalise vectors

Posted by haegarr on 07 April 2013 - 04:20 AM

I've read a bit about them and it seems like they hold 4 values only - an axis and an angle.

Yes: Quaternions are defined by 4 scalars. No: They don't hold an axis and an angle. There is another rotation representation that is called "axis/angle representation" and that actually hold, well, an axis and an angle. IMHO it is best not to try to interpret some geometric meaning into quaternions; look at them as being a mathematical construct in your toolbox.

In fact, each representation has some degree of freedom: In 3D, a rotation matrix has 9, a quaternion has 4, an axis/angle has 4. Because rotation only has 3, you need to enforce some constraints on the representations, or else your representation doesn't mean a pure rotation.

In the case of rotation matrices the column/row vectors are pairwise orthogonal and each one is a unit vector. Hence the process of "re-ortho-normalization", which carries both orthogonalization as well as normalization in its name.

In the case of quaternions they need to be of unit length. Hence, to be precise, one has to talk about "unit-quaternions" and not just quaternions when referring to pure rotation.

Because of this, quaternions are not less prone to numerical errors per se, but they are more stable in that numerical imprecision has less effect on still representing a useable rotation. So re-normalisating quaternions needs to be done less often, and ensuring that single constraint is less work than ensuring the 6 constraints on rotation matrices.

That said, using quaternions and re-normalization (from time to time) and on-the-fly to-matrix conversion would be sufficient for sure.

 

What I don't understand is how I can use a quaternion to hold the entire orientation of my object?

The problem of lacking a geometrical interpretation suitable for humans, doesn't mean that unit-quaternions don't work. A unit-quaternions "holds the entire orientation" by its nature. Use conversions from or into, resp., other rotation representations where meaningful.




#5041584 Rotating about local axes?

Posted by haegarr on 10 March 2013 - 01:37 PM

Sorry for confusion. A position is an absolute co-ordinate tuple, with respect to a reference point, while a translation is a relative one, just with a direction and distance. Similarly, an orientation matrix is absolute, while a rotation matrix is relative. Computationally there is no difference between an absolute or relative matrix. It is just a question of interpretation.

 

So, start with the identity matrix as the orientation. This means "rotate to, err, ... as is". Then multiply that with a rotation matrix. This means "rotate by, e.g., 10° yaw". You yield in a new orientation matrix. Multiply that by with another rotation matrix. This means "rotate by, e.g., 5° pitch". You yield in a new orientation matrix. And so on.




#5038711 Structuring a scene

Posted by haegarr on 03 March 2013 - 06:23 AM

In the traditional sense of "scene graph" we are speaking of a "one-for-all" structure, where each and everything is pressed into this one structure. You may gather from this wording already that I'm not a fan of scene graphs ;)

An "entity" is even less well defined. An entity on the sense of the nowadays somewhat popular "component based entity system" (CES for short) is just a placeholder or identifier or sometimes a container of components, perhaps with message distribution functionality. The is no standard, and a best practice does not exist IMHO.

So, if you mean those both concepts, you are already speaking of very different things. Furthermore, in a traditional scene graph a mesh would be an own node inside the graph. When you say "storing the mesh anywhere outside the graph but in the scene and letting them refer to the scene node" it sounds to me that this is just another kind of concept. I.e. it sounds that what you call the scene graph is just a tree of FK ("parenting") dependencies, while the meshes may be organized in another way, perhaps in a spatial structure.

Being confused of what you are actually doing, I recommend to not follow the traditional scene graph but to use a couple of specialized structures to organize the scene. I personally prefer CES as one of the aforementioned specializations when it comes to structuring game entities. I further prefer the entity not being explicit, and the components being managed in sub-systems. However, it makes no sense to program a (highly) complex solution for a game with low scene complexity; so you still have to decide...


#5038112 Adding a storyline

Posted by haegarr on 01 March 2013 - 01:31 PM

A story is a sequence of events happening in the view (to be understood in a wider sense) of the player. To ensure that the player notices the events, they are usually not running detached but being enabled by requirements on the world state, and perhaps being triggered by the player. During happening and at its end, an event often alters the world state, so being able to enable or disable other (not yet happened) events. This way a sequence or even branches can be defined.

 

The world state is the entirety of variables describing, err, the state in which the game world is. E.g. a boolean flag for the life/death of an opponent belonging to the story, a counter for how often the player has hit a target, a flag whether or not a specific NPC has told the player about the hidden secret, ... whatever.

 
Events are enabled when their requirements on the world state are satisfied. E.g. if the flag meaning "opponent A is shot to death" is cleared and the counter for "rings of the bell" has reached 12 and the flag for "colliding with the proximity volume close to opponent A" is set, then let opponent A throw an offense against the player.
 

Triggers can be any action a player is able to perform. Colliding with a collision volume can be used as a location based trigger, or the death by shot of an NPC can be a trigger. On the other hand, the expiration of a timer can be a trigger, too, e.g. allowing for deferred events. Triggers can be implemented as a mechanism which alters the world state.

 

The player usually needs be hinted at how to trigger enabled events, or else s/he may not realize what to do next. How to do this depends on the type and style of the game. Briefings, books, journals, gossip, talks, ... by NPCs, a companion, the HUD, ... and so on. This can also be implemented as events in the way described above.

 

There isn't necessarily the need to explicitly track the current chapter in the story. The world state already reflects this.




#5036062 Animation blending matrix math

Posted by haegarr on 24 February 2013 - 06:05 AM

As Tasche already explained, is a pure rotation matrix required to be ortho-normal. It is possible to interpolate rotations without using quaternions, but following Dave's argumentation (in this PDF located on its famous Geometric Tools site), performancewise it is a slightly less effective way.

 

Well, looking a the interpolation methods slerp and nlerp, both have their advantages as well as disadvantages. nlerp is faster to compute (always good) and is commutative (less problematic when arranging animation tracks). But it lacks linearity in the interpolated angle, or, in other words, constant angular velocity. In the extreme case the resulting quaternion will vanish. That are the reasons why nlerp should be used with relatively small difference angles only.

 

Also quaternions have a requirement to represent a pure rotation: They need to be of unit length (i.e. a unit-quaternion). In general quaternions a less prone to de-normalization due to numerical imprecision than matrices are, so they are often preferred in this sense, too. However, the normalization step in nlerp exists just for this reason, because the intrinsic interpolation in nlerp doesn't keep the length.




#5017691 Logic help...

Posted by haegarr on 05 January 2013 - 03:59 AM

The OP describes IMHO: From a total of 30 different models a subset is to be picked during the run of level 1. This subset is to be re-used "as-is" during level 2 and perhaps following levels.

So the task is to pick n <= 30 (probably n < 30) models from the original set of 30 models, i.e. selection without putting back.

A solution would be to hold all IDs of still available models in an indexable storage (e.g. the array mentioned in the OP), generate a random integral number in the range [0, length(storage)-1], use that as index into the storage to read out the appropriate ID, remove it from the storage (i.e. in the case of an array by shifting all IDs with greater indices one index before and decrease the length of the storage by 1), and use the picked ID further for remembering and instantiation.


#5017115 Getting data from multiple vertex data

Posted by haegarr on 03 January 2013 - 08:40 AM

I want 2 VBOs but how? If I bind one buffer and then the next will the first buffer not be unbound? Or is it enough to bind the buffer, set the vertex attrib pointers and then unbind it again before the draw call?
There is perhaps still a misunderstanding. The vertex positions are the same for the faces, edges, and point handles. You need only 1 VBO with the position, and that is the VBO I've mentioned as "shared". However, you need to transport the edge color as well, and that attribute is unique to edge rendering. Hence create another VBO for the edge color (but notice that it is still given for each vertex; but that isn't a problem with your kind of coloring the edges).

Then do as follows (schematic):
1. bind (shared) VBO with positions
2. set attribute pointer for positions
3. bind VBO with colors
4. set attribute pointer for colors
5. bind index buffer for edges
6. draw edges
7. clean up

If the shared VBO contains colors, too, because you deal with it in face rendering ... it doesn't matter, because you ignore it (i.e. you don't use it with the attribute pointer setting) during edge rendering.


#5016712 Problems with creating view matrix

Posted by haegarr on 02 January 2013 - 09:41 AM

Well, err ... forgotten that OpenGL uses a camera that looks along the negative z axis. So although the (unnormalized) forward vector is

   forward := target - pos

it doesn't point in the same direction as rowZ from the matrix, because rowZ is (per definition) along the positive z axis.

 

Also

rowY = crossProd(&rowX, &rowZ); // new rowY

should probably better be

rowY = crossProd(&rowZ, &rowX); // new rowY

to match the other cross-product.

 

 

With a little bit of clean-up, what's about the hopefully-now-considering-all-caveats-solution:

 

Matrix4x4 createView( Vector3* pos, Vector3* target, Vector3* up )
{
   Matrix4x4 cameraMat;
   Matrix4x4 view;

   Vector3 vX;
   Vector3 vY;
   Vector3 vZ;

   // computing vZ as the unit vector to look along
   vZ = sub(pos, target);
   normalise(&vZ);

   // computing vX being orthogonal on the plane of up and vZ; considering
   // its place in the ortho-normal basis later, this means up x vZ
   vX = crossProd(up, &vZ);
   normalise(&vX);

   // computing vY being orthogonal on the plane of vZ and vX; considering
   // its place in the ortho-normal basis later, this means vZ x vX
   vY = crossProd(&vZ, &vX);
   normalise(&vY);

   // building basis ...
   setColv(&cameraMat, 0, &vX, 0.0f);
   setColv(&cameraMat, 1, &vY, 0.0f);
   setColv(&cameraMat, 2, &vZ, 0.0f);
   // ... and injecting position
   setColv(&cameraMat, 3, pos, 1.0f);

   // inverting camera matrix to yield in the view matrix
   view = inverseMat4(&cameraMat);

   return view;
}

 

Please notice that looking along the negative axis also has an implication on the code when pressing W a.k.a. "go forward". In fact, you need to go in direction of the negative 3 column vector then.




#5016625 Problems with creating view matrix

Posted by haegarr on 02 January 2013 - 03:53 AM

First I have to apologize that I've made a mistake. The correct computation for column-major indices is
   index := col * 4 + row
so that e.g. col == 1 skips the first 4 elements (i.e. column vector 0) and addresses m1[4] up to m1[7] (i.e. the 2nd column) in dependence on row.

The second problem is with the order of inverseMat4 and setting the position vector. The position is an inherent part of the transformation. As such the camera matrix is a composition of
M := T * R
and its inverse is

M-1 = ( T * R )-1 = R-1 * T-1
Your current computation gives
T * R-1
what is obviously different in 2 ways: The translation is not inverted, and the translation is applies on the wrong side.

So please invoke inverseMat4 as last step after setting all 4 columns and just before returning the view matrix result.


BTW: Notice please that setRowv is no longer for setting a row but for setting a column (assuming that you actually use column-major layout). It should hence look like

void setColumnV( Matrix4x4* m, int colNum, Vector3* v, float w )
{
   int index = colNum * 4 + 0;
   m->m.m1[index] = v->x;
   m->m.m1[++index] = v->y;
   m->m.m1[++index] = v->z;
   m->m.m1[++index] = w;
}
to avoid misguidance.


#5016345 Problems with creating view matrix

Posted by haegarr on 01 January 2013 - 05:53 AM

1. Thanks, I will rename it to "target"
2. I guess so
Okay.

3. Because i'm using C I can't overload operators.
No problem. And the implementation of sub(...) works as expected.

4. It should be fine, here's the code:
The implementation is okay. However, the invocation is still questionable. Remember that the cross-product is not commutative, because
a x b = - ( b x a )
Hence the wrong order will yield in the reverse direction vector. IMHO you need to compute forward vector cross up vector to yield in the side vector, but you compute up vector cross forward vector and yield in the negative side vector. Please check this.

5. A little further down in the function I call setEqual which initialises the left parameter to the right parameter; In this cals rowY gets set to the up vector.
Nonetheless, what happens "a little further down" doesn't interest the code above. Normalizing a vector means to keep its direction and ensure a length of 1. Now rowY is [0,0,0] at the moment of normalization, and hence has no non-vanishing direction and cannot be enlarged to have a length of 1. So normalize(&rowY) must die with an error like "division by zero". That is the point I made.

6. I'll need to change this because my rowX is currently the cross of up and rowZ.
Not sure if I understand you answer here, so I explain in detail what I mean. The orientation matrix you want to compute has the requirement that each row/column has a length of 1 and each pair of them is orthogonal. Your forward vector and up vectors are not necessarily orthogonal when createView is invoked, but the cross product of those 2 vectors will be orthogonal to both. So after the first cross product only 2 of the 3 required orthogonalities are guaranteed. Hence you need to compute a second cross-product to guarantee that the up vector is orthogonal to the other vectors, too. In summary:
  side := forward x up
  new_up := side x forward

7. I think I might be treating rows as columns because opengl is column-major.
First, "column-major" is a term that describes how the elements of the 2D matrix construct are arranged linearly in 1D memory. That is not the topic I meant, although you have to consider it in your routines, of course.

What I meant is whether you use column-vectors, so that you do a matrix vector product like so
p' := M * p
or else row-vectors, so that you compute the same matrix vector product like so
q' := q * N
because there is a mathematical correspondence named the "transpose"
p := qt
M := Nt
between them. Looking at a pure orientation matrix gives
Rt = R-1
so that confusing row and column vectors gives the inverse rotation. Confusing them when dealing with the position sub-matrix is even worse.

When you're using column-vectors (what is usual in OpenGL) then side, up, forward, and location vectors have to be set as columns of the matrix.

Now coming to column-major (what is also usual in OpenGL): Make sure that the linear index is computed as
   index = row * 4 + column (edited)
   index = column * 4 + row
and it should work.

8. Brain exploded! So it should just be called the view matrix then?
Yes, it is the view matrix.


#5016330 Problems with creating view matrix

Posted by haegarr on 01 January 2013 - 04:24 AM

There are several minor and major issues with your code as well as some lack of information to verify the code. In the order they came to my attention:
 
1. The 2nd argument of createView is named "dir". That connotes a direction. But it is set and interpreted as a position. The usual name for that parameter is "lookAt" or something similar.
 
2. Initializing mat and invView to be the identity matrix is needless.
 
3. How does "sub(dir, pos)" work? It should be "dir minus pos".
 
4. How does "crossProd(up, &rowZ)" work, and do you use a LHS or RHS? In other words, is the order of up and rowZ correct? Edit: I would expect it to be "rowZ cross up"...
 
5. Normalizing rowY after setting it to a constant is needless, not to say that it must not work at all because rowY has the length of zero at that moment!
 
6. Setting "up" as-is as rowY of the matrix is wrong. Instead, rowY needs to be computed by a cross product using the computed rowX and rowZ. Otherwise you don't respect pitching and the rotation matrix will be corrupted.
 
7. Do you use column or row vectors? Calling "setRowv" hints at row vectors. Correct?
 
8. Naming the result "invView" is misleading. In fact, you store the camera's local-to-global frame matrix in "mat", so that computing its inverse yields in what is usually called the view matrix: In other words: The camera matrix is the inverse view matrix, and the inverse camera matrix is the (standard) view matrix.



#5015764 Getting data from multiple vertex data

Posted by haegarr on 30 December 2012 - 09:48 AM

If you mean you want 2 buffers, the vertices, and lets say colors to tell you the color of the vertex (you only need a boolean or something like that but i thought it would be nice to be able to use different colors etc. for other things) you can probably do it using a VAO

Indeed is it possible to pass the geometric vertex attributes in one VBO (still sticking to VBOs here because the OP mentioned them explicitly) and the color attribute in another one, assembling them on the GPU to yield in the full featured vertex. This is more a question of how the selection is encoded when sighted from the GPU side. For example, the selection can be processed on the CPU in a way that writes the color in dependence to the selection state into the 2nd VBO. This would allow to load just the color to the graphics card instead of the entire vertex data. However, whether this makes sense depends on the purpose of the selection: In an editor it is probable that the geometry is changed on the selected vertices. Hence loading the geometry as well may be done anyway, so that splitting into 2 VBOs may introduce needless work.




#5015706 APi inconsistencies

Posted by haegarr on 30 December 2012 - 04:20 AM

glGetAttribLocation uses a result of -1 to denote a problem. Cited from one of the links in the OP:

... If the named attribute variable is not an active attribute in the specified program object or if name starts with the reserved prefix "gl_", a value of -1 is returned.
-1 is noticeably different from the "valid" values, and it doesn't make sense to pass such an index into glEnableVertexAttribArray. Doing this requires a signed return type for glGetAttribLocation, but for the glEnableVertexAttribArray index an unsigned parameter type is still appropriate. (Whether this is a solution one is happy with is another question.)


#5015375 I dont understand localspace for a particle?

Posted by haegarr on 29 December 2012 - 03:26 AM

A local space is one that is used as a reference for a given set of co-ordinate tuples, so to say. Whether the given set is a mesh, particle system, single particle, skeleton, bone, parented entity frame, ... is irrelevant for this purpose.

To re-interpret the co-ordinates given in a more-local space to a less-local (a.k.a. more-global) space, you multiply the co-ordinates with the transformation build from the placement of the co-ordinate frame of the more-local space with respect to the more-global space (in the following written for row vectors):
pn-1 := pn * Tn-1
This can be done a step further to the next more-global space
pn-2 := pn-1 * Tn-2 = pn * Tn-1 * Tn-2
up until stopping at a space that is in this sense super-ordinated to all the local spaces; then this one is called the global space (or "world").

This can be done in the other direction, too: You can re-interpret co-ordinates given in some more-global space in a more-local space. This is done by using the inverse transformation, because you just need to invert the equations given above. E.g.
pn-1 = pn * Tn-1 <==>    pn-1 * Tn-1-1 = pn * Tn-1 * Tn-1-1
so that
pn = pn-1 * Tn-1-1

Something like that is done in the code snippet shown in the OP: You apply the inverse transformation to the camera's position. Assuming that the camera's position was given in global space before, you re-interpret it now in a local space. From the comments I see that the original transformation is the one that places the particle system in the world. So at the end you have transformed the world position of the camera so that is given now w.r.t. the local space of the particle system.

BTW: Notice please that I've written "re-interpret" above. This is because the "world" doesn't really change due to those transformations. You just alter the way you look at it.




PARTNERS