Billboard(Z) not really acting like a billboard
by Edward F. Maurina III · in Torque Game Engine · 05/27/2003 (8:38 pm) · 31 replies
First, I know that there have been a few threads on this already. I'm starting another one because to date, the issue is still not working.
In Torque, the attributes Billboard and BillboardZ cause a mesh to behave like a billboard. For the prior, the billboard rotates about 0,0,0. For the later, only about Z.
By beef is that the rotation is a reflection of camera rotation, when it should be a reflection of camera location. Billboards are meant to face the camera at all times, but what I see happening is that the billboard seems to be an inverse of the camera orientation.
My questions are:
1. Does anyone else care to see this fixed?
2. If it does get fixed is it likely to be accepted as a patch?
Regardless, I'm going to look at it.
...adding to the pile... :)
Pls reply with your opinions. I'm quite curious.
In Torque, the attributes Billboard and BillboardZ cause a mesh to behave like a billboard. For the prior, the billboard rotates about 0,0,0. For the later, only about Z.
By beef is that the rotation is a reflection of camera rotation, when it should be a reflection of camera location. Billboards are meant to face the camera at all times, but what I see happening is that the billboard seems to be an inverse of the camera orientation.
My questions are:
1. Does anyone else care to see this fixed?
2. If it does get fixed is it likely to be accepted as a patch?
Regardless, I'm going to look at it.
...adding to the pile... :)
Pls reply with your opinions. I'm quite curious.
About the author
Recent Threads
#22
edit: Whoops. This is Wrong!
06/29/2005 (4:28 pm)
BTW, You won't have to normalize each vector, since the cross product of two unit length vectors will always result in another unit length vector. Just make sure you normalize the camVec and the rest should come out ok.edit: Whoops. This is Wrong!
#23
So, you'll want to normalize camVec, and then the result of the first cross product. The result of the second cross product operation is the only one you can skip normalizing.
I'm going to chage the code above to reflect this.
06/29/2005 (4:34 pm)
Ignore that last post. Heh. That's wrong, my bad. The cross procude of two unit length vectors will result in a unit length vector only if the two vectors being crossed are perpendicular. I'm always forgetting about that little detail. :PSo, you'll want to normalize camVec, and then the result of the first cross product. The result of the second cross product operation is the only one you can skip normalizing.
I'm going to chage the code above to reflect this.
#24
1) It did work to only normalize Camvec, to an extent. When you approach the bb'd object on the preserved axis, it appears to shrink. This shrinking does not show up if you normalize all rotation axis. Maybe that's overkill, as I didn't test any permutations (and I'm just sort of beating this code into place right now because I'm sick of thinking about it and want to move on :))
2) I wasn't sure how to approach full billboarding. I tried using an axis method with crossing the original axis with the last crossed and setting it, but that appeared to do nothing. Any insight?
Here's my code so far:
Thanks again
06/29/2005 (4:40 pm)
2 Things:1) It did work to only normalize Camvec, to an extent. When you approach the bb'd object on the preserved axis, it appears to shrink. This shrinking does not show up if you normalize all rotation axis. Maybe that's overkill, as I didn't test any permutations (and I'm just sort of beating this code into place right now because I'm sick of thinking about it and want to move on :))
2) I wasn't sure how to approach full billboarding. I tried using an axis method with crossing the original axis with the last crossed and setting it, but that appeared to do nothing. Any insight?
Here's my code so far:
Point3F camVec = cameraPos - getRenderPosition();
camVec.normalize();
// Get our current render transform matrix.
MatrixF renderMat = getRenderTransform();
// 3 vectors we'll use to hold our x y and z rotation vectors.
VectorF axisX, axisY, axisZ;
switch(bbType) {
case BillboardAll:
//not sure how to approach this
break;
case BillboardXAxis:
renderMat.getColumn(0, &axisX);
axisX.normalize();
axisY = mCross(camVec, axisX);
axisY.normalize();
axisZ = mCross(axisX, axisY);
axisZ.normalize();
renderMat.setColumn(1, axisY);
renderMat.setColumn(2, axisZ);
break;
case BillboardYAxis:
renderMat.getColumn(1, &axisY);
axisY.normalize();
axisZ = mCross(camVec, axisY);
axisZ.normalize();
axisX = mCross(axisY, axisZ);
axisX.normalize();
renderMat.setColumn(0, axisX);
renderMat.setColumn(2, axisZ);
break;
case BillboardZAxis:
// Start by pulling the Z axis from our current render transform.
// This is the axis we don't wish to change.
renderMat.getColumn(2, &axisZ);
axisZ.normalize();
// Now we'll determine the X axis as a cross between camVec and Z.
// This will give us an X which is perpendicular to both the camera and Z.
// Note that the Right Hand Rule determines the order of the cross.
axisX = mCross(camVec, axisZ);
axisX.normalize();
// Now the object's "right" (X) should be perpendicular to the camera, and
// facing "left" from the camera's point of view.
// Finally, we need a Y which is perpendicular to both the other rotation axes.
// And again, the order is important.
axisY = mCross(axisZ, axisX);
axisY.normalize();
// Y or "forward" should now face in the direction of the camera, but still be
// perpendicular to X and Z.
// That leaves us with 3 perpendicular vectors. Z is unchanged, so the object
// is still "upright", but Y now faces toward the camera.
// All that's left is to put the new axis into the rotation matrix, like so:
renderMat.setColumn(0, axisX);
renderMat.setColumn(1, axisY);
// Column 2 (the third column) is axisZ. This we didn't change, so there's
// no need to set it again.
break;
}
dglMultMatrix(&renderMat); // and we're done.Thanks again
#25
New code in italics. That's an important step and I forgot it! My bad!
06/29/2005 (4:41 pm)
New code here// Get our current render transform matrix.
MatrixF renderMat = getRenderTransform();
// 3 vectors we'll use to hold our x y and z rotation vectors.
VectorF axisX, axisY, axisZ;
// Start by pulling the Z axis from our current render transform.
// This is the axis we don't wish to change.
renderMat.getColumn(2, &axisZ);
// Now we'll determine the X axis as a cross between camVec and Z.
// This will give us an X which is perpendicular to both the camera and Z.
// Note that the Right Hand Rule determines the order of the cross.
axisX = mCross(camVec, axisZ);
[i]// Must normalize axisX
// Also, we should check for a zero length vector here. If camVec is
// parallel to axisZ, axisX will be of zero length. And if we normalize
// a zero length vector, we'll crash the game with a divide by zero.
if (!axisX.isZero()) {
axisX.normalize();
} else {
axisX.set(1,0,0);
}[/i]
// Now the object's "right" (X) should be perpendicular to the camera, and
// facing "left" from the camera's point of view.
// Finally, we need a Y which is perpendicular to both the other rotation axes.
// And again, the order is important.
axisY = mCross(axisZ, axisX);
// Y or "forward" should now face in the direction of the camera, but still be
// perpendicular to X and Z.
// That leaves us with 3 perpendicular vectors. Z is unchanged, so the object
// is still "upright", but Y now faces toward the camera.
// All that's left is to put the new axis into the rotation matrix, like so:
renderMat.setColumn(0, axisX);
renderMat.setColumn(1, axisY);
// Column 2 (the third column) is axisZ. This we didn't change, so there's
// no need to set it again.
dlgMultMatrix(&renderMat); // and we're done.New code in italics. That's an important step and I forgot it! My bad!
#26
06/29/2005 (4:47 pm)
How about the full billboarding?
#27
06/29/2005 (4:48 pm)
For full billboarding, it's a question of where you want to get an "up" vector from. I talked about this earlier. I'd suggested taking "up" from the camera's up. Unfortunately, I just checked sceneGraph.h and I don't see any way to get the camera rotation from a SceneState! Doesn't look like the SceneState class even retains that information. Bummer.
#28
06/29/2005 (4:50 pm)
I'm looking at SceneState's mModelview right now.. Hopefully that will have the information necessary.
#29
Scott, you've saved me a seriously huge headache here. I'll put together a resource on this when I get some time. Thanks again
06/29/2005 (4:52 pm)
Well, maybe I'll just use createOrientFromDir code to do that. It's not perfect, but it's good enough for most cases.Scott, you've saved me a seriously huge headache here. I'll put together a resource on this when I get some time. Thanks again
#30
I think mModelview might work, but I'm not certain. Unfotrunately, it looks like tracing the code path to determine exactly what it's supposed to do isn't going to be easy. I'm thinking state->mModelview.getColumn(2, &axisZ) might work.. but you'd have to test it.
createOrientFromDir will use World Up, which is as you say, good enough for most cases.
06/29/2005 (5:21 pm)
You're welcome. Glad I could help. I think mModelview might work, but I'm not certain. Unfotrunately, it looks like tracing the code path to determine exactly what it's supposed to do isn't going to be easy. I'm thinking state->mModelview.getColumn(2, &axisZ) might work.. but you'd have to test it.
createOrientFromDir will use World Up, which is as you say, good enough for most cases.
#31
For anyone who's interested, here's the final method:
06/29/2005 (10:20 pm)
Ok Scott, I tested everything out and have incorporated all that you told me.For anyone who's interested, here's the final method:
void SceneObject::billboardModelView(Point3F cameraPos, S32 bbType)
{
if (bbType > 0) {
Point3F camVec = cameraPos - getRenderPosition();
camVec.normalize();
// Get our current render transform matrix
MatrixF renderMat = getRenderTransform();
// 3 vectors we'll use to hold our x y and z rotation vectors.
VectorF axisX, axisY, axisZ;
switch(bbType) {
case BillboardAll:
//this forces a positive z orientation, which is ok for most situations
renderMat = MathUtils::createOrientFromDir(camVec);
renderMat.setPosition(getRenderPosition());
break;
case BillboardXAxis:
renderMat.getColumn(0, &axisX);
axisY = mCross(camVec, axisX);
if (!axisY.isZero()) {
axisY.normalize();
} else {
axisY.set(0,1,0);
}
axisZ = mCross(axisX, axisY);
renderMat.setColumn(1, axisY);
renderMat.setColumn(2, axisZ);
break;
case BillboardYAxis:
renderMat.getColumn(1, &axisY);
axisZ = mCross(camVec, axisY);
if (!axisZ.isZero()) {
axisZ.normalize();
} else {
axisZ.set(0,0,1);
}
axisX = mCross(axisY, axisZ);
renderMat.setColumn(0, axisX);
renderMat.setColumn(2, axisZ);
break;
case BillboardZAxis:
// Start by pulling the Z axis from our current render transform.
// This is the axis we don't wish to change.
renderMat.getColumn(2, &axisZ);
// Now we'll determine the X axis as a cross between camVec and Z.
// This will give us an X which is perpendicular to both the camera and Z.
// Note that the Right Hand Rule determines the order of the cross.
axisX = mCross(camVec, axisZ);
// Must normalize axisX
// Also, we should check for a zero length vector here. If camVec is
// parallel to axisZ, axisX will be of zero length. And if we normalize
// a zero length vector, we'll crash the game with a divide by zero.
if (!axisX.isZero()) {
axisX.normalize();
} else {
axisX.set(1,0,0);
}
// Now the object's "right" (X) should be perpendicular to the camera, and
// facing "left" from the camera's point of view.
// Finally, we need a Y which is perpendicular to both the other rotation axes.
// And again, the order is important.
axisY = mCross(axisZ, axisX);
// Y or "forward" should now face in the direction of the camera, but still be
// perpendicular to X and Z.
// That leaves us with 3 perpendicular vectors. Z is unchanged, so the object
// is still "upright", but Y now faces toward the camera.
// All that's left is to put the new axis into the rotation matrix, like so:
renderMat.setColumn(0, axisX);
renderMat.setColumn(1, axisY);
// Column 2 (the third column) is axisZ. This we didn't change, so there's
// no need to set it again.
break;
}
dglMultMatrix(&renderMat); // and we're done.
} else {
//if non-billboarded, just use the normal transform
dglMultMatrix( &getRenderTransform() );
}
}
Torque 3D Owner Scott Richards
camVec should be = cameraPos - getRenderPosition(), but don't forget to camVec.normalize().
Sorry, I should have made that more clear. All the vectors of the rotation matrix should have a length of 1, unless you want to scale the object.
It is in fact an unnecessary number of points for rotation alone. What I did not mention is that this type of matrix can also be used to express the object's scale. A longer Z will scale the object along Z, making it taller. A longer X would make it wider, etc. That's why your object grew as it moved away from the camera.