Game Development Community

SetTransform: only z rotation?!

by Mr. Kristen Overmyer · in Torque Game Engine · 01/22/2005 (4:51 pm) · 30 replies

Been working through the Finney book example on "Programmed Rotation". On page 113 he suggests playing with the parameters for %shape.setTransform(...). This I have been doing. First a reality check on my understanding of the transform arguments. They appear to be:

lx the x position
ly the y position
lz the z position
rx the x unit vector for the rotation axis
ry the y unit vector for the rotation axis
rz the z unit vector for the rotation axis
rd the rotation (in radians) about the axis

I am only able to get the item to rotate for rx, ry, rz values of 0, 0, 1. When I try 1, 0, 0, or 0, 1, 0, nothing happens as evidenced by neither the object moving or the subsequent getTransform showing a change in value.

Is my understanding of the arguments correct?
Is there a bug here?

I have literally spent hours trying to track down any usefull information on either setTransform or the Transform argument itself. I can find stuff for the engine equivalent SceneObject::setTransform and matrices, but nothing to document the argument meanings and behaviors as used in Torque Script.

On a side note, the Finney book talks about Torque using a right-handed system. This appears to be the case when I translate objects in the x, y, and z directions using script commands. However, the one z rotation I have managed appears to be a left-handed rotation about the positive z axis. Seems to be a mixed bag. Is the true?

Any and all help gratefully appreciated.

Kristen.
Page «Previous 1 2
#1
01/22/2005 (5:27 pm)
A more appropriate forum for this question would be:

Torque Game Engine Public Area...
...Book: 3D Game Programming All-in-One (book)

By posting there, you are likely to run into other readers of the same book who are struggling with the same problem or who have already solved it. By skimming through that forum, you might even find a thread that has already covered this topic (definitely the fastest way to get an answer since you don't have to wait for other people).

The forum you posted in, Torque SDK Private Forum, is for topics that involve the c++ source code of Torque. Your question is purely script, so it is out of place here.
#2
01/23/2005 (9:56 am)
Thanks for the re-direct. I am still getting use to the categories.
#3
01/23/2005 (10:33 am)
@Eric: Yes and no...as far as I am aware, Ken didn't change anything regarding setTransform, so the question is actually appropriate here as well!

@Kristen: There is a bit of a breakdown to be honest in the various ways things can be rotated in TGE--basically, you have methods for just about any type of rotation from Axis Angle to Euler to Quat.

From script however, IIRC, you mostly use the techinque you described (I think that's Axis Angle?), but instead of radians use degrees...it depends on the object's over-ridden implementation of setTransform (if there is one), but I think player objects do the conversion from deg to rad in the console function.

You may want to experiment with the World Editor (whichever one allows you to load objects into your mission), and use the rotation GUI blanks to see how it works. Again, IIRC, that uses a script call to setTransform that should mimic the normal use of the concept from script.
#4
01/23/2005 (11:36 am)
@Stephen:
No, there's nothing particularly wrong with posting this topic here, but the book forum is a much better place. The reason is that anything which is discussed in the book is also discussed over and over in that forum (usually labeled according to chapter or page). Below is a recent example that Kristen has already found.

www.garagegames.com/mg/forums/result.thread.php?qt=24950
#5
01/23/2005 (3:17 pm)
I'm suggesting at this point that this is actually a bug (my original behavior description that begins this thread). I would consider any discrepancy between actual and documented behavior to be such. In so far as the Finney text is put foward as a Torque reference, any Torque descrepancies with it would be viewed as such.

The "Programmed Rotation" example on page 113 makes the statement, "Things of interest to explore are the variables %rx, %ry, %rz, and %rd in the TurnShape() function. Try making changes to each of these and observing the effects your changes have on the item." As described at the top of this thread, only z rotations (0 0 1 %rd) have any effect. Given that "Item" is a generic sounding name and no hint is given in the book that Items should only rotate about the z axis, one starts searching their own code for problems. However, (as Zepp got me to checking), the Item::setTransform override appears as follows:

void Item::setTransform(const MatrixF& mat)
{
   Point3F pos;
   mat.getColumn(3,&pos);
   MatrixF tmat;
   if (!mRotate) {
      // Forces all rotation to be around the z axis
      VectorF vec;
      mat.getColumn(1,&vec);
      tmat.set(EulerF(0,0,-mAtan(-vec.x,vec.y)));
   }
   else
      tmat.identity();
   tmat.setColumn(3,pos);
   Parent::setTransform(tmat);
   if (!mStatic)
   {
      mAtRest = false;
      mAtRestCounter = 0;
   }
   setMaskBits(RotationMask | PositionMask | NoWarpMask);
}

Well .... no wonder :) The pack and unpackUpdate methods limit to z as well. Is this really as it should be? If one wants a generic item them can rotate about other axes, what should they use?
#6
01/23/2005 (3:20 pm)
(continued) The sample heart item dropped into the scene can not even be rotated about x and y axies using the GUI editor (as Stephen suggested trying). However, I was able to get some heart objects already in the scene (the ch3 example) to respond to the rotate shape cs code and GUI. I'm off to see what is different about them. Don't feel like I have this completely understood just yet.

@Stephen. Thanks for the suggestions. They were of help.

Kristen.
#7
01/23/2005 (4:50 pm)
These rotatable hearts, are they items? Many things besides items can be rotated with the settransform method, and some of the other shapes might not have the same override behavior.
#8
01/23/2005 (5:11 pm)
Yes, at least the one in the example script is an Item.

Are you suggesting "Player" or "StaticShape"? The other ShapeBase derivatives seem pretty specific (e.g., Vehicle, Camera, ...)

Also, is it intended that one can instantiate a "ShapeBase" object into a scene? Or is this just an abstract class? The reaon I ask is that I tried, but was not able to see it in the scene. The onAdd code lacks a call to addToScene.

Thanks,

Kristen.
#9
01/23/2005 (6:10 pm)
>The sample heart item dropped into the scene can not even be rotated about x
>and y axies using the GUI editor (as Stephen suggested trying). However, I was
>able to get some heart objects already in the scene (the ch3 example) to respond
>to the rotate shape cs code and GUI. I'm off to see what is different about them.

You said that some of the hearts can rotate normally, but not the one that you were trying to rotate at first. I am suggesting that these other hearts might not be items. Staticshape would be my first guess, yes, but just looking and checking is easier than guessing.

I have never seen shapebase instantiated, and I tend to think of it as an abstract class.

Settransform is a method of sceneobject. One branch of the inheritance diagram, going from sceneobject to item, is...
sceneobject > gamebase > shapebase > item

Apparently someone thought that items should rotate only about the z axis, so the item::settransform method was created to overwrite the sceneobject::settransform method. This is probably due to the common behavior of having some collectable item (such as ammo) rotating in place while waiting for a player to pick it up.
#10
01/24/2005 (9:56 am)
As Eric mentioned, one of the artifacts of TGE is the fact that in some areas (ok, many areas), you are using code that is written to accomplish specific things, and may not be as appropriate as you would think for a more generic situation. Personally, I think he's right on with his description of why the Item class was written the way it was--it works great for ammo/health kits that get picked up in an FPS type of game.

That's one of the beautiful benefits of TGE as well however--you aren't limited by someone "thinking of everything" prior to releasing a binary for you to make your game from--you have the source code, and if you don't find a specific implementation available that meets your requirements, you can simply change the implementation!

GarageGames has done a pretty excellent job overall of making the best generic assumptions possible in the creation of TGE from the old Tribes engine, but you will occasionally run into things like this--and it's great to bring them to the boards for review, and possible inclusion into future releases. This is one for sure that I think is an invalid assumption: just because a specific use of Item::setTransform wants to only allow rotation in the Z axis, the method itself isn't the place to force that limitation--that should be handled by the calling method.

So, long story short: if the behaviour of the Item class doesn't fit your needs, there really isn't any reason you cannot change it at all, especially with the amount of dev experience you have. That, and don't always assume what appears to be available in TGE is the best choice for you--just like any other class heirarchy, use what you can, derive what you need!
#11
01/24/2005 (10:36 am)
@Stephen. Good advice. Still absorbing the "flavor" and "nuances" of TGE. I would agree GG has done a good job. I see real potential here and that is why I am sticking with it.

In that vain, would you say that "class extension" is the prefered method to providing new behaviors (plugins) to the TGE dev environment as opposed to through script. I'm invisioning a wide range of new motion behavioral types for TGE and have been investigating the best mechanism(s) within the TGE dev environment to make them accessable to other game developers. Possible approaches include:

1) through Torque Script alone; new data block types (actually instances) and data block scoped script functions. This might be the simplest for most users.

2) modifying existing class implementations within TGE; I don't like this one; it creates branched flavors of TGE and requires a user to create a non-standard build

3) extending existing TGE classes with new classes (having their own datablocks, initPersist, consoleMethods, etc.); still accessable through script; user still must rebuild, but from an existing standard source with additions; their existing stuff should still work.

What's the best approach from GG's perspective? Your thoughts greatly appreciated.

Kristen.
#12
01/24/2005 (11:07 am)
Keep in mind I'm just a community member like you...I don't work for GG, so take my input from that perspective!

All of your points are good ones, and therefore none of them really should hold more weight as a general rule. Instead, do what seems right for your particular implementation goals, and then present it as a resource that can be implemented with the least amount of difficulty.

Some specific examples:

The "Item" class that you're working with currently might simply be a "code snippet" that you could release to provide extensions of base behaviour. Now that you've pointed it out, I do agree that the rotatation being limited only around the Z-axis is a pretty arbitrary/assumption specific implementation, and making it more generic is a good idea, but in this case I myself would simply post the changes I would have made to extend the class, instead of worrying about trying it in just script, or deriving a new class...it just makes sense that you aren't really doing anything new, you are modifying existing functionality that needs modification.

A different example would be something like the resources you see here about the advanced Camera, FPS gui huds, or other "add on" modules that give new functionality. For these, do it just like you would for your project--derive a class, provide access methods, script calls (console methods), the works. This is probably the "easiest" for community members to use of course, since it would have the least impact on "core" code.

I've done some pretty hefty resource work, and I've found that if you simply make it how you would want to receive the resource, it works out the best--limit core code changes when you can, but don't destroy your functionality or ease of implementation simply due to an arbitary self-imposed restriction either!
#13
01/24/2005 (11:44 am)
It might be how you are calling the setTransform() be I can't be cerain w/o looking at the code it should be something like

%tran = %obj.getTransform();
//disassemble
%x = getWord(%tran, 0);
%y = getWord(%tran, 1); 
%z = getWord(%tran, 2);
%rx = getWord(%tran, 3);
%ry = getWord(%tran, 4);
%rz = getWord(%tran, 5);
%rRad = getWord(%tran, 6);
//reassemble
%newTrans = %x SPC %y SPC %z SPC %rx SPC %ry SPC %rz SPC %rRad;
//reinitalize
%obj.setTransform(%newTrans);
#14
01/26/2005 (8:10 am)
I think I've found the problem in the Item::setTransform engine code highlighted 9 posts up. It uses the y axis unit vector to factor out any rotation other that z.
#15
01/31/2005 (3:58 am)
Did anyone actually fix this? I've just spent 4 hours trying to figuire out why my thrown items wouldn't rotate :( This sould probably be reported as a bug in the sdk forum too.
I need to be able to rotate my items round all 3 axes, has anyone changed the above routine to enable this?
#16
01/31/2005 (4:44 pm)
Actually, I think it was coded that way intentionally. Someone made the decision that Item objects can only rotate about their z axes (see Zepp's remarks above). However, I share your :( The Finney book as well, seems to imply that they should rotate about all axes. As far as posting it as a bug, I think it's more a documentation issue, although the term Item does sound quite generic.

As far as creating an object that can rotate in any fashion, one could derive a class from Item and provide alternative implementations for the virtual functions setTransform, updatePack, and updateUnpack (there may be others but these are the ones I see right off). But first it might be a good idea to ask or look around for an existing object type that might fit the bill.

Hope this helps,

Kristen.
#17
01/31/2005 (6:04 pm)
@Kristen,

You can rotate "items" in all axis. But the fact is that the heart provided in the book has a special flag in the Datablock called "rotate". This flag makes the heart rotate about the Z-axis continuosly.

Items are a special class derived from ShapeBase class. They are designed to be used with the player Inventory Manager, hence, they have special methods called OnPickup, OnDrop, etc. that controls how the inventory will use them.

If you just want to have an object that doesn't have the Item functionality, all you got to do is use a ShapeBase object.
#18
02/27/2005 (4:27 pm)
I've gotten the same problem as Kristen. I need item functionality (adding to the inventory), but I need the setTransform() behavior of StaticShape. I wouldn't expect my artist to make the items in the axes that I want. I'm ignoring the last post because it is wrong. Neither setTranfrom() nor Alt-click dragging on the x or y axes within the mission editor produce the desired results, regardless of %item.rotate.

It's been a month since this post was updated. Has anyone produced modified item::setTransform(), item::pack() and item::unpack() functions that fix the problem?
#19
03/15/2005 (10:17 pm)
After combing the forums, hunting down various people, giving up, then redoubling my efforts, I've made it work.

From item.cc
void Item::setTransform(const MatrixF& mat)
{
   Parent::setTransform(mat);
   setMaskBits(PositionMask | NoWarpMask);
}

.
.
.

U32 Item::packUpdate(NetConnection *connection, U32 mask, BitStream *stream)
{
   U32 retMask = Parent::packUpdate(connection,mask,stream);

   if (stream->writeFlag(mask & InitialUpdateMask)) {
      stream->writeFlag(mRotate);
      stream->writeFlag(mStatic);
      stream->writeFlag(mCollideable);
      if (stream->writeFlag(getScale() != Point3F(1, 1, 1)))
         mathWrite(*stream, getScale());
   }
   if (mask & ThrowSrcMask && mCollisionObject) {
      S32 gIndex = connection->getGhostIndex(mCollisionObject);
      if (stream->writeFlag(gIndex != -1))
         stream->writeInt(gIndex,NetConnection::GhostIdBitSize);
   }
   else
      stream->writeFlag(false);
   if (stream->writeFlag(mask & PositionMask)) {
      stream->writeAffineTransform(mObjToWorld);
      mathWrite(*stream, mObjScale);

      Point3F pos;
      mObjToWorld.getColumn(3,&pos);
      mathWrite(*stream, pos);
      if (!stream->writeFlag(mAtRest)) {
         mathWrite(*stream, mVelocity);
      }
      stream->writeFlag(!(mask & NoWarpMask));
	}
   return retMask;
}

void Item::unpackUpdate(NetConnection *connection, BitStream *stream)
{
   .
   .
   .
   if (stream->readFlag()) {
      S32 gIndex = stream->readInt(10);
      setCollisionTimeout(static_cast<ShapeBase*>(connection->resolveGhost(gIndex)));
   }
   if (stream->readFlag()) {
		// From StaticShape
      MatrixF mat;
      stream->readAffineTransform(&mat);

      VectorF scale;
      mathRead(*stream, &scale);
      setScale(scale);

		// Origional Code
      Point3F pos;
      mathRead(*stream, &pos);
      F32 speed = mVelocity.len();
      if ((mAtRest = stream->readFlag()) == true)
         mVelocity.set(0,0,0);
      else
         mathRead(*stream, &mVelocity);

      if (stream->readFlag() && isProperlyAdded()) {
         // Determin number of ticks to warp based on the average
         // of the client and server velocities.
         delta.warpOffset = pos - delta.pos;
         F32 as = (speed + mVelocity.len()) * 0.5 * TickSec;
         F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks;
         delta.warpTicks = (S32)((dt > sMinWarpTicks)? getMax(mFloor(dt + 0.5), 1.0f): 0.0f);

         if (delta.warpTicks) {
            // Setup the warp to start on the next tick, only the
            // object's position is warped.
            if (delta.warpTicks > sMaxWarpTicks)
               delta.warpTicks = sMaxWarpTicks;
            delta.warpOffset /= delta.warpTicks;
         }
         else {
            // Going to skip the warp, server and client are real close.
            // Adjust the frame interpolation to move smoothly to the
            // new position within the current tick.
            Point3F cp = delta.pos + delta.posVec * delta.dt;
            VectorF vec = delta.pos - cp;
            F32 vl = vec.len();
            if (vl) {
               F32 s = delta.posVec.len() / vl;
               delta.posVec = (cp - pos) * s;
            }
            delta.pos = pos;
            mat.setColumn(3,pos);
         }
      }
      else {
         // Set the item to the server position
         delta.warpTicks = 0;
         delta.posVec.set(0,0,0);
         delta.pos = pos;
         delta.dt = 0;
         mat.setColumn(3,pos);
      }
      Parent::setTransform(mat);
      Parent::setRenderTransform(mat);
	}
}

I'm not quite sure exactally what the warp ticks or warp mask are about, but without it, the code didn't work. It seems to play a part in the subtle "settling" of an item into a new position when its transform changes.
#20
04/08/2005 (2:04 pm)
I've moved this topic to this resource.
Page «Previous 1 2