Game Development Community

Improved TGEA 1.8.1 ObjectView

by Nathan Bowhay (ESAL) · 03/27/2009 (1:18 pm) · 2 comments

Just added a couple improvements to the object view like animation speed control, a list of all animations to select from, reversing animation direction...

Engine


guiObjectView.h
On line 58 add:
bool onMouseWheelUp(const GuiEvent &event);
   bool onMouseWheelDown(const GuiEvent &event);

On line 62 change:
void setObjectAnimation(S32 index);
to
void setObjectAnimation(S32 index, F32 time = 1);
   void setObjectAnimation(const String & animationName, F32 time = 1);
   void setObjectAnimationSpeed(F32 time);

On line 67 add:
const char* getObjectAnimationName(S32 index);
   S32 getObjectAnimationCount();

guiObjectView.cpp
On line 125 add (feel free to change the deltas for the scroll wheel to whatever you want or open it to script):
bool GuiObjectView::onMouseWheelUp(const GuiEvent &event)
{
	if (mMouseState == Zooming)
   {
      return false;
   }

   S32 delta = 5;

   mOrbitDist += (delta * 0.01f);

	return true;
}

bool GuiObjectView::onMouseWheelDown(const GuiEvent &event)
{
	if (mMouseState == Zooming)
   {
      return false;
   }

   S32 delta = -5;

   mOrbitDist += (delta * 0.01f);

	return true;
}

void GuiObjectView::setObjectAnimationSpeed(F32 time)
{
	if(mModel && runThread)
	{
		// If you found the sequence add the thread then scale the thread (speed it up or down)
		mModel->setTimeScale( runThread, time );
	}
	else
		Con::printf("Error: The model hasn't been loaded yet."); 
}

change line 165 from:
void GuiObjectView::setObjectAnimation(S32 index)
to
void GuiObjectView::setObjectAnimation(S32 index, F32 time)

on line 174 add:
if(mModel && index < mModel->getShape()->getSequenceCount())
	{
		if (runThread)
		{
			mModel->destroyThread(runThread);
		}

		// If you found the sequence add the thread and set sequence and scale
		runThread = mModel->addThread();
		lastRenderTime = Platform::getVirtualMilliseconds();
		mModel->setPos( runThread, 0 );
		mModel->setTimeScale( runThread, time );
		mModel->setSequence( runThread, mAnimationSeq, 0 );
		Con::printf("Loading sequence %s", mAnimationSeq);
	}
	else
		Con::printf("Error: no such sequence index %s or the model hasn't been loaded yet.", mAnimationSeq); 
}

void GuiObjectView::setObjectAnimation(const String & animationName, F32 time)
{
	if(mModel)
	{
		S32 sequence = mModel->getShape()->findSequence(animationName);

		if( sequence != -1 )
		{
			setObjectAnimation(sequence, time);
		}
		else
		{
			Con::printf("Error: Could not locate sequence %s", animationName);
		}
	}
	else
	{
		Con::printf("Error: No model set");
	}
}

S32 GuiObjectView::getObjectAnimationCount()
{
	if(mModel)
		return mModel->getShape()->getSequenceCount();
	else
		return -1;
}

const char* GuiObjectView::getObjectAnimationName(S32 index)
{
	if(mModel && index < mModel->getShape()->sequences.size())
	{
		const String & s = mModel->getShape()->getSequenceName(index);
		return s.c_str();
	}
	else
		return NULL;

uncomment line 362:
//S32 dt = time - lastRenderTime;

change line 379 from:
// animate and render in a run pose
      //F32 fdt = dt;
      // mModel->advanceTime( fdt/1000.f, runThread );
      // mModel->animate();
to
if(runThread)
		{
			mModel->advanceTime( F32(dt)/1000.f, runThread );
			mModel->animate();
		}

change line 450 from:
ConsoleMethod(GuiObjectView, setSeq, void, 3, 3,
              "(int index)\n"
              "Sets the animation to play for the viewed object.\n\n"
              "\param index The index of the animation to play.")
{
   argc;
   object->setObjectAnimation(dAtoi(argv[2]));
}
to
ConsoleMethod(GuiObjectView, setSeq, void, 3, 4,
              "(int index, float time = 1)\n"
              "Sets the animation to play for the viewed object.\n\n"
              "\param index The index of the animation to play."
				  "\param time The time scale to play, 1 normal, -1 reverse, 0 stopped.")
{
	if(argc == 4)
		object->setObjectAnimation(dAtoi(argv[2]),dAtof(argv[3]));
	else
		object->setObjectAnimation(dAtoi(argv[2]));
}

on line 480 add:
ConsoleMethod(GuiObjectView, getSeqName, const char *, 3, 3, "(int index) get the name of the sequence/animation with the given index for the current dts.")
{
   argc; argv;
	const char *ret = object->getObjectAnimationName(dAtoi(argv[2]));
   return ret ? ret : "";
}

ConsoleMethod(GuiObjectView, getSeqCount, S32, 2, 2, "() get the number of sequences/animations for current dts.")
{
   argc; argv;
	return object->getObjectAnimationCount();
}

ConsoleMethod(GuiObjectView, setSeqSpeed, void, 3, 3,
              "(float time)\n"
              "Sets the animation speed for the viewed object.\n\n"
				  "\param time The time scale to play, 1 normal, -1 reverse, 0 stopped.")
{
		object->setObjectAnimationSpeed(dAtof(argv[2]));
}

tsShape.h
on line 390 add:
S32 getSequenceCount() const;
	const String & getSequenceName(S32 index) const;

tsShape.cpp
on line 153 add:
S32 TSShape::getSequenceCount() const
{
	return sequences.size();
}

const String &TSShape::getSequenceName(S32 index) const
{
	AssertFatal(index>=0 && index<sequences.size(),"TSShape::getSequenceName");
	return getName(sequences[index].nameIndex);
}

Script

My build is based off of the T3D build so I will explain the changes according to that build.

objectViewExample.gui
There may be some additional fields that are not needed as I have changed some other gui source, I will try and remove as many as possible, but may miss some.

//--- OBJECT WRITE BEGIN ---
%guiContent = new GuiControl(ObjectViewGui) {
   canSaveDynamicFields = "0";
   Enabled = "1";
   isContainer = "1";
   Profile = "GuiDefaultProfile";
   HorizSizing = "right";
   VertSizing = "bottom";
   Position = "0 0";
   Extent = "1024 768";
   MinExtent = "8 8";
   canSave = "1";
   Visible = "1";
   hovertime = "1000";

   new GuiWindowCtrl(ObjectViewWindow) {
      canSaveDynamicFields = "1";
      Enabled = "1";
      isContainer = "1";
      Profile = "GuiWindowProfile";
      HorizSizing = "center";
      VertSizing = "center";
      Position = "212 164";
      Extent = "600 440";
      MinExtent = "8 8";
      canSave = "1";
      Visible = "1";
      hovertime = "1000";
      Margin = "0 0 0 0";
      Padding = "0 0 0 0";
      AnchorTop = "1";
      AnchorBottom = "0";
      AnchorLeft = "1";
      AnchorRight = "0";
      resizeWidth = "0";
      resizeHeight = "0";
      canMove = "1";
      canClose = "1";
      canMinimize = "0";
      canMaximize = "0";
      minSize = "50 50";
      closeCommand = "Canvas.popDialog(ObjectViewGui);";
      EdgeSnap = "1";
      text = "ObjectViewExample";
         speed = "1";
         animation = "0";
         model = " ";

      new GuiBitmapCtrl() {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         Profile = "GuiDefaultProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "188 30";
         Extent = "400 400";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         bitmap = "scriptsAndAssets/client/ui/images/hudfill.png";
         wrap = "0";

         new GuiObjectView(view) {
            canSaveDynamicFields = "0";
            Enabled = "1";
            isContainer = "0";
            Profile = "GuiDefaultProfile";
            HorizSizing = "relative";
            VertSizing = "relative";
            Position = "0 0";
            Extent = "400 400";
            MinExtent = "8 2";
            canSave = "1";
            Visible = "1";
            hovertime = "1000";
            Margin = "0 0 0 0";
            Padding = "0 0 0 0";
            AnchorTop = "1";
            AnchorBottom = "0";
            AnchorLeft = "1";
            AnchorRight = "0";
            cameraZRot = "0";
            forceFOV = "0";
         };
      };
      new GuiButtonCtrl(ObjectViewExitButton) {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         Profile = "GuiButtonProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "25 400";
         Extent = "140 30";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         Command = "Canvas.popDialog(ObjectViewGui);";
         hovertime = "1000";
         text = "Exit";
         groupNum = "-1";
         buttonType = "PushButton";
         useMouseEvents = "0";
      };
      new GuiPopUpMenuCtrl(ObjectViewSelectAnimation) {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "23 68";
         Extent = "144 21";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         Margin = "0 0 0 0";
         Padding = "0 0 0 0";
         AnchorTop = "1";
         AnchorBottom = "0";
         AnchorLeft = "1";
         AnchorRight = "0";
         maxLength = "1024";
         maxPopupHeight = "200";
         sbUsesNAColor = "0";
         reverseTextList = "0";
         bitmapBounds = "16 16";
      };
      new GuiTextCtrl() {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "23 43";
         Extent = "47 18";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         Margin = "0 0 0 0";
         Padding = "0 0 0 0";
         AnchorTop = "1";
         AnchorBottom = "0";
         AnchorLeft = "1";
         AnchorRight = "0";
         text = "Animation";
         maxLength = "1024";
      };
      new GuiButtonCtrl(ObjectViewForward) {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         Profile = "GuiButtonProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "137 96";
         Extent = "30 15";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         text = ">";
         groupNum = "-1";
         buttonType = "ToggleButton";
         useMouseEvents = "0";
      };
      new GuiButtonCtrl(ObjectViewBackward) {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         Profile = "GuiButtonProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "23 96";
         Extent = "30 15";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         text = "<";
         groupNum = "-1";
         buttonType = "ToggleButton";
         useMouseEvents = "0";
      };
      new GuiSliderCtrl(ObjectViewSpeedSlider) {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "23 137";
         Extent = "145 28";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         range = "0 5";
         ticks = "20";
         value = "0";
      };
      new GuiTextCtrl() {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "73 95";
         Extent = "42 18";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         Margin = "0 0 0 0";
         Padding = "0 0 0 0";
         AnchorTop = "1";
         AnchorBottom = "0";
         AnchorLeft = "1";
         AnchorRight = "0";
         text = "Direction";
         maxLength = "1024";
      };
      new GuiTextCtrl(ObjectViewSpeedText) {
         canSaveDynamicFields = "0";
         Enabled = "1";
         isContainer = "0";
         HorizSizing = "right";
         VertSizing = "bottom";
         Position = "28 116";
         Extent = "58 18";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         Margin = "0 0 0 0";
         Padding = "0 0 0 0";
         AnchorTop = "1";
         AnchorBottom = "0";
         AnchorLeft = "1";
         AnchorRight = "0";
         text = "Speed:";
         maxLength = "1024";
      };
   };
};
//--- OBJECT WRITE END ---

objectViewExample.cs

function ObjectViewWindow::onWake(%this)
{   
   if(isFile(%this.model))
   {
      // Set main object
      view.setModel(%this.model);
      
      //Update the animation drop down
      ObjectViewSelectAnimation.update();
      
      //Convert the animation name to an index (if need be)
      if(!isInt(%this.animation))
         %this.animation = ObjectViewSelectAnimation.findText(%this.animation);

      // Set animation and dropdown
      ObjectViewSelectAnimation.setSelected(%this.animation);
      
      //Set the speed slider to the correct position
      %value = %this.speed < 0 ? %this.speed * -1 : %this.speed;
      ObjectViewSpeedSlider.setValue(%value);
      
      //Set the text so the user can see the value of the speed
      ObjectViewSpeedText.setText("Speed:" SPC %value);
      
      //Set the direction buttons
      if(%this.speed < 0)
      {
         ObjectViewBackward.setStateOn(true);
         ObjectViewForward.setStateOn(false);
      }
      else
      {
         ObjectViewForward.setStateOn(true);
         ObjectViewBackward.setStateOn(false);
      }
   }
}

///Open the model viewer and view the model %model
///%animation the animation to play (index or name)
///%speed is the speed for the animation
function ObjectViewWindow::viewModel(%this, %model, %animation, %speed)
{
   if(isFile(%model))
   {
      %this.model = %model;
      
      if(%animation $= "")
         %animation = 0;
         
      if(%speed $= "")
         %speed = 1;
         
      %this.animation = %animation;
      %this.speed = %speed;
         
      Canvas.pushDialog(ObjectViewGui);
   }
}

function ObjectViewSelectAnimation::update(%this)
{
   //Clear whatever was there before
   %this.clear();
   
   //If there is a model then populate drop down
   //with all the animations that dts has
   if(isFile(ObjectViewWindow.model))
   {
      for(%i = 0; %i < view.getSeqCount(); %i++)
      {
         %text = view.getSeqName(%i);
         %this.add(%text,%i);
      }
   }
}

function ObjectViewSelectAnimation::onSelect(%this, %id, %text)
{
   //Animate
   view.setSeq(%id,ObjectViewWindow.speed);
   
   //Set the animation var
   %this.animation = %id;
}

function ObjectViewBackward::onClick(%this)
{
   ObjectViewForward.setStateOn(false);
   
   if(ObjectViewWindow.speed >= 0)
   {
      ObjectViewWindow.speed *= -1;
      
      view.setSeqSpeed(ObjectViewWindow.speed);
   }
}

function ObjectViewForward::onClick(%this)
{
   ObjectViewBackward.setStateOn(false);
   
   if(ObjectViewWindow.speed < 0)
   {
      ObjectViewWindow.speed *= -1;
      
      view.setSeqSpeed(ObjectViewWindow.speed);
   }
}

function ObjectViewSpeedSlider::onMouseDragged(%this)
{
   //Get the speed
   %value = %this.getValue();
   %sign = ObjectViewWindow.speed < 0 ?  -1 : 1;
   ObjectViewWindow.speed = %value * %sign;
   
   //Set the animation speed
   view.setSeqSpeed(ObjectViewWindow.speed);
   
   //Set the text so the user can see the value of the speed
   ObjectViewSpeedText.setText("Speed:" SPC %value);
}

And that is it. If you want to open it just call:
ObjectViewWindow.viewModel(%dtsPath, %animationToPlay, %animationSpeed);

%dtsPath should be the path to the dts, like "scriptsAndAssets/data/shapes/players/ForgeSoldier/ForgeSoldier.dts"
%animationToPlay if not passed in or "" will be sequence 0 else it should be the index of the animation or the animation name.
animationSpeed if not passed in or "" will be 1. 1 is the normal speed with the animation moving forward, -1 is normal speed backwards, 0 is stopped and any speed greater than 1 or less than -1 is faster in that direction, any value in between 0 and 1 or 0 and -1 is slower than normal.

Feel free to add mounting buttons back in with some extra functionality if need be (I just didn't need them and if I did I wouldn't make them set a static mount like previously in this ctrl).

I may add to this resource in the future when we begin to use it more at work. The thing I really wanted to add, but didn't get around to doing is allowing you to pass setSeq function the index or the name of the animation by doing data type verification on the first param. If anyone does this please post it as it would make it a much more complete resource.

Also feel free to comment and any additions you add to post them.

#1
06/22/2009 (1:38 am)
Some bug fix, change the "%s" in these lines:

Con::printf("Loading sequence %s", mAnimationSeq);
...
Con::printf("Error: no such sequence index %s or the model hasn't been loaded yet.", mAnimationSeq);

to "%d":

Con::printf("Loading sequence %d", mAnimationSeq);
...
Con::printf("Error: no such sequence index %d or the model hasn't been loaded yet.", mAnimationSeq);
#2
07/08/2009 (2:33 pm)
c1xx : fatal error C1083: Cannot open source file: '..\..\..\..\..\engine\source\T3D\gameTSCtrl.cpp': No such file or directory

What went wrong here, I didn't change any includes...