Game Development Community

Manually Rotating Nodes doesn't Move connected Nodes

by John "Mythic" Henry · in Torque 3D Professional · 05/31/2011 (3:33 pm) · 4 replies

Pretty simple idea, but became kind of complex for some reason...
The short version is this:

I have a Dts object with no animations, a Skeleton and want to Rotate 2 of the axis's...
The Nodes (Bones) connected down the chain from the Modified Node Doesn't rotate when I do this...
This is the first time in years I dug this far into the animation/nodes and I'm real rusty...

Here is the nitty gritty:
It started in this thread,
www.garagegames.com/community/forums/viewthread/126020
but this particular part can be for anything...

so heres the basic structure of my Dts object:

(eP)
[-------------------](mP)------------(M)--(mzP)
[-------E-----------]----------------------MzP
[-------R-----------]
|
[---------------|-----------------------------]
[---------------Root--------------------------]
[---------------|-----------------------------]
|
/ (Hubs Below)

=============================================
(Tank Shape Bones) (TreadVehicleData -- Vehicle Class)
(these all start from Root Bone)

R == turretRotate ( Turret Vertices are assigned to this bone )
E == mount2 ( Doubles as Mount Point for Barrel and Elevate Node )
MzP == muzzleFlash ( Used as a Test MuzzleFlash Point )
Root ( All other Vertices except Tracks are assigned here)
Hubs
===============================================
(Barrel Bones) (ShapeBaseImage -- Weapon Class)

Mounted to mount2 on Tank Shape

mP == mountPoint ( vertices assigned from Barrel end )
m == muzzle (Animates Fire Sequence -- Barrel tip vertices assigned here)
mzP == muzzlePoint (For Flash / Vector/Position firing)
eP == ejectPoint (Shell ejection not connected to other bones)

Now most is working just fine..
I use the following to Rotate the (R Node) and Elevate the {E Node)
TSShape* turretShape = mShapeInstance->getShape();
	if( turretShape != NULL )
	{
      MatrixF matWorld;
		MatrixF * mat;
		Point3F defaultPos;

		// Rotate Turret ...
		if( sTurretNode != -1 )
		{
         turretShape->getNodeWorldTransform( sTurretNode, &matWorld );
         defaultPos = matWorld.getPosition();

			mat = &mShapeInstance->mNodeTransforms[sTurretNode];
			mat->set(EulerF(0,0,mRot.x));
			mat->setPosition(defaultPos);
		}
		// Elevate/Rotate Barrel ... 
		if( sBarrelNode != -1 )
		{
         turretShape->getNodeWorldTransform( sBarrelNode, &matWorld );
         defaultPos = matWorld.getPosition();

			mat = &mShapeInstance->mNodeTransforms[sBarrelNode];
			mat->set(EulerF(mRot.z,0,mRot.x));
			mat->setPosition(defaultPos);
		}
		if(isServerObject())
		{
			mShapeInstance->setDirty(TSShapeInstance::AllDirtyMask);	
			//mShapeInstance->setDirty(TSShapeInstance::TransformDirty);
			mShapeInstance->animateNodeSubtrees(true);
			//mShapeInstance->animate();
		}
	}
This does work, with the following side effects..
I had to add the Rotate (mRot.x) to the Barrel Elevation code so it follows
the Turret Rotation, otherwise the Barrel stayed in Place and Elevated
While the Turret rotated away merrily leaving the barrel in the same place.
I attached a basic Box to the (MzP) Node and that Node goes nowhere, just hangs in
space :(

I am not quite sure what steps I missed to force the connected Nodes to move with the Modified Nodes...
Aka: Rotate (R) which has children nodes (E) & (MzP) which Should rotate with (R) but don't..

#1
05/31/2011 (3:43 pm)
Addendum:
The E / MzP Nodes are just Bones, no Meshes connected..
The Barrel is laid on top of them with the E node as a mountPoint.
This was due to the fact that the Weapon Class State system
would reset my animations/nodes when it fired/changed state..
So I hacked around it :)

Basically, the Connected Meshes are moving fine, but the Chained
Nodes are not...
#2
06/02/2011 (8:59 am)
I've shelved this for the moment unless someone can point me in the
right direction. I've gone back to a modified Weapon image continaing
the Complete Turret/Barrel instead.

By Dropping the Animation sequences handled by the Weapon System states
I am now able to manual setPos( [thread], pos) without any issues now.
So I added a second animation for elevation as Rotating a node in the
Shape Image with a Fire Animation was not feasable. As I had to
seperate out the Vetices for the barrel from the turret. If I didn't
do that, it incorrectly elevates the Whole turret of course :)

I ran into a couple of issues testing this out. Rotating the image
node would be reset if the Barrel Fire animation was activated
wether blended or not. If I masked out the rotating node, that didnt
work unless I did a HandsOff mask, which then when I set the default
position as I had been doing previously it went UP too far :(

Which is why I've gone back to a full weapon image with no Sequences
for the State system to do. I now manually run the Sequences myself
in the TreadVehicle system (C++). I call the Fire Sequence from script
during the ::onFire() call which is now working. I use the position
for elevation to set the Thread Pos in the elevate sequence.

This setup is now working except for the actual node that it fires
from. I am now digging in to figure out why it is either grabbing
the wrong node for calculating the Firing or if its not being
changed with the current setup.
#3
06/02/2011 (9:04 am)
Heres some of the Code I'm using now to elevate/rotate:

void TreadVehicle::updateTurretAnimation()
{
	//Con::printf("TreadVehicle::updateTurretAnimation() ... [ %s ]", isServerObject()?"ServerCall":"ClientCall");
	
	//Turret / Weapon Movement
	//---------------------------------
	TSShape* turretShape = mShapeInstance->getShape();
	if( turretShape != NULL )
	{
      MatrixF matWorld;
		MatrixF * mat;
		Point3F defaultPos;

		// Rotate Turret ...
		if( sTurretNode != -1 )
		{
         turretShape->getNodeWorldTransform( sTurretNode, &matWorld );
         defaultPos = matWorld.getPosition();

			mat = &mShapeInstance->mNodeTransforms[sTurretNode];
			mat->set(EulerF(0,0,mRot.z));
			mat->setPosition(defaultPos);
			if(isServerObject())
			{
				mShapeInstance->animateNodeSubtrees(true);
				mShapeInstance->setDirty(TSShapeInstance::AllDirtyMask);	
				//mShapeInstance->setDirty(TSShapeInstance::TransformDirty);
				//mShapeInstance->animate();
			}
		}
		// Elevate/Rotate Barrel ... 
		// As it is Mounted AFTER onNewDataBlock
		if( sBarrelNode == -1 )
		{
			//Barrel Weapon Image Node
			TSShapeInstance* tmpShapeInst = getImageShapeInstance( mDataBlock->mStartWepMountPoint );
			if( tmpShapeInst != NULL )
			{
				TSShape* barrelShape = tmpShapeInst->getShape();
				if( barrelShape != NULL )
				{
					sBarrelNode  = barrelShape->findNode("barrelPivot");
					S32 tempSeq = barrelShape->findSequence("fire");
					if( tempSeq != -1 )
					{
						mFireThread = tmpShapeInst->addThread();
						tmpShapeInst->setSequence(mFireThread,tempSeq,0);
						tmpShapeInst->setBlendEnabled(mFireThread,true);
					}
					tempSeq = barrelShape->findSequence("elevate");
					if( tempSeq != -1 )
					{
						mElevateThread = tmpShapeInst->addThread();
						tmpShapeInst->setSequence(mElevateThread, tempSeq, 0.38);
						tmpShapeInst->setBlendEnabled(mElevateThread,true);
						tmpShapeInst->setTimeScale(mElevateThread, 0.0f);
					}
					//tmpShapeInst->setNodeAnimationState(sBarrelNode,TSShapeInstance::MaskNodeHandsOff);
					//barrelShape->getNodeWorldTransform( sBarrelNode, &matWorld );
					//sBarrelPos = matWorld.getPosition();
				}
			}
		}
/*
		if( sBarrelNode != -1 )
		{
			TSShapeInstance* tmpShapeInst = getImageShapeInstance( mDataBlock->mStartWepMountPoint );
			TSShape* barrelShape = tmpShapeInst->getShape();
			if( tmpShapeInst != NULL && barrelShape != NULL )
			{
				//barrelShape->getNodeWorldTransform( sBarrelNode, &matWorld );
				defaultPos = matWorld.getPosition();

				mat = &tmpShapeInst->mNodeTransforms[sBarrelNode];
				mat->set(EulerF(mRot.x,0,0));
				mat->setPosition(defaultPos);  // was using defaultPos but this gets screwed up
				//mat->setPosition(sBarrelPos);  // was using defaultPos but this gets screwed up

				if(isServerObject())
				{
					tmpShapeInst->animateNodeSubtrees(true);
					tmpShapeInst->setDirty(TSShapeInstance::AllDirtyMask);
				}
			}
		}
*/
		if (mElevateThread)
		{
			//Barrel Weapon Elevate Sequence
			TSShapeInstance* tmpShapeInst = getImageShapeInstance( mDataBlock->mStartWepMountPoint );
			if( tmpShapeInst != NULL )
			{
				//F32 tmpDur = tmpShapeInst->getDuration(mElevateThread);
				if( mRot.x == 0.0f )	//Center Barrel
					tmpShapeInst->setPos(mElevateThread, 0.38f);	//Center of Thread
				else if( mRot.x < 0.0f )	//Down
				{
					F32 rotate = abs(mRot.x * (180.0f/M_PI_F));	//Rotation in Degrees
					rotate = mClamp( rotate / 10, 1, 10) * 0.035f;
					tmpShapeInst->setPos(mElevateThread, rotate );
				}
				else								//Up
				{
					F32 rotate = abs(mRot.x * (180.0f/M_PI_F));	//Rotation in Degrees
					rotate = mClamp( rotate / 10, 1, 10) * 0.035f;
					tmpShapeInst->setPos(mElevateThread, 0.38f + rotate );
				}

				if(isServerObject())
				{
					tmpShapeInst->animate();
					tmpShapeInst->setDirty(TSShapeInstance::AllDirtyMask);
				}
			}
		}
	}
}

I do the check for no Current Elevate Thread as the Turret is not
in the tank system until AFTER the onNewDataBlock call.
#4
06/03/2011 (2:15 am)
A minor change forgot to put into post:

rotate = mClamp( rotate / 10, 1, 10) * 0.035f;

change to

rotate = mClampF( rotate / 10.0f, 1.0f, 10.0f) * 0.035f;

As were dealing with all F32's here :)