Game Development Community

Plastic Gem #4: Animated Guis

by Anthony Rosenbaum · 06/12/2008 (6:43 am) · 38 comments

Download Code File

i936.photobucket.com/albums/ad202/vincismurf/banner.jpg

Plastic Gem #4: Animated GUI Controls

Difficulty: Hard

For a list of gems see the Gem A Day page.

Hello again from Plastic Games. This resource will allow you to animate your GUI s in a variety of ways. You can animate a GUI's position, extent, clipping area, alpha and even color! However the alpha and color aspects have to be incorporated into the children GUI you wish you use. Lucky for you we decided to include our GuiPlasticBitmapCtrl. The GuiPlasticBitmapCtrl will also allow you to run bitmap animation. Essentially you will have the ability to make a nice 'flashy' interface without the hassle of incorporating Flash.

You must follow the instructions in Gem #3: Ease to add the Ease class before you can add the GUI changes. You can elect to follow the instructions in Gem #2: Animation Threads first. If you do then you can follow all instructions from Gem #3. If you do not care about animation threads in 3D objects you can skip that part and simply ignore any mention of threads when you follow the Gem #3 on ease.

To get you psyched check out this YouTube Video

'

All the code presented here was developed by Paul Dana, Lead programmer at Plastic Games.

This code can be implemented in TGEA 1.7 and TGE 1.5.2

1)We need to change mBounds from a private to a protected variable

At top of GuiControl Class change this
private:
   typedef SimGroup Parent;

   StringTableEntry        mScriptFile; ///< The script file this GUI was created from
   SimGroup               *mAddGroup;   ///< The internal name of a SimGroup child of the global GuiGroup in which to organize this gui on creation
RectI                   mBounds;     ///< The internal bounds of this control

To this
private:
   typedef SimGroup Parent;

   StringTableEntry        mScriptFile; ///< The script file this GUI was created from
   SimGroup               *mAddGroup;   ///< The internal name of a SimGroup child of the global GuiGroup in which to organize this gui on creation
   	// > pg animation
protected:   
   RectI                   mBounds;     ///< The internal bounds of this control
// < animation

2) Provide a lot of variables for animation.

Guicontrol.h after
static GuiControl *smCurResponder;
// > pg animation
    bool mPassThru; // pass mouse events through this control...

    Point2F mCenter; // only updated when asked for it...always call getCenter()

    // NOTE: ADDED Robert Brenner's very fine EASE code to mMath.h as mEase.h
    struct animation
    {
       const static S32 maxDims = 4; // max # of dims allowed...
	    bool  enabled;
       EaseF ease;
	    F32   startTime; // careful! this IS a F32 but it represents MILLISECONDS here
	    F32   duration;
	    F32   currValue[maxDims];
	    F32   startValue[maxDims];
	    F32   deltaValue[maxDims];

       // when doing a simple animation interpStart would always be 0.0
       // and interpEnd would always be 1.0...however when doing a Key Point List
       // these values will represent which SEGMENT of the overal KPL is equivalent
       // to the current interpolation underway
       // TODO: remove this?
       F32   interpStart;
       F32   interpEnd;

       bool  kplEnabled;   // true if we are interpolating along a list of keypoints
       F32   kplStartTime; // start time of entire Key Point List (in milliseconds) 
       F32   kplDuration;  // DURATION in milliseconds for entire animation
       EaseF kplEase;      // ease to use for kpl
       // the input from user in the KPL is seconds...we normalize from that to 0..1
       Vector<Point4F>keyPointList; // the KPL: list of key points (values are x,y,z, and w = normalized time, ie last w is always 1.0)
       S32   kplIndex;     // current INDEX in a KPL playback...
       S32   kplNumDimensions; // how many dimensions in input...set in kplReadInput()
       void kplReadInput(SimObject *input, S32 numDims);
       void kplSetup(SimObject *input, S32 numDims, F32 startDelay, F32 duration, EaseF ease); // -1 duration means get from input.duration
       bool kplSetupNext(); // setup next sgement in KPL, return false if there is no next...

       animation();
       ~animation();

       void cancel() { enabled= false; }
	    void setup(F32 startDelay, F32 duration, EaseF ease);
       void setup(F32 startDelay, F32 dur, EaseF ease, F32 start, F32 end);
       void setup(F32 dur, F32 start, F32 end);
	    void setupF(F32 oldVal, F32 newVal, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
	    void setup2I(S32 oldX, S32 oldY, S32 newX, S32 newY, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
	    void setup2F(F32 oldX, F32 oldY, F32 newX, F32 newY, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
	    void setup3F(F32 oldX, F32 oldY, F32 oldZ, F32 newX, F32 newY, F32 newZ, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);
	    void setup4F(F32 oldX, F32 oldY, F32 oldZ, F32 oldW, F32 newX, F32 newY, F32 newZ, F32 newW, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end);

       // start end values actaully currentl not used
       // TODO: remove start/end?
	    void setupF(F32 oldVal, F32 newVal, F32 startDelay, F32 duration, EaseF ease)
       {
          setupF(oldVal,newVal,startDelay,duration,ease,0.0f,1.0f);
       }
	    void setup2I(S32 oldX, S32 oldY, S32 newX, S32 newY, F32 startDelay, F32 duration, EaseF ease)
       {
          setup2I(oldX,oldY,newX,newY,startDelay,duration,ease,0.0f,1.0f);
       }
	    void setup2F(F32 oldX, F32 oldY, F32 newX, F32 newY, F32 startDelay, F32 duration, EaseF ease)
       {
          setup2F(oldX,oldY,newX,newY,startDelay,duration,ease,0.0f,1.0f);
       }
	    void setup3F(F32 oldX, F32 oldY, F32 oldZ, F32 newX, F32 newY, F32 newZ, F32 startDelay, F32 duration, EaseF ease)
       {
          setup3F(oldX,oldY,oldZ,newX,newY,newZ,startDelay,duration,ease,0.0f,1.0f);
       }
	    void setup4F(F32 oldX, F32 oldY, F32 oldZ, F32 oldW, F32 newX, F32 newY, F32 newZ, F32 newW, F32 startDelay, F32 duration, EaseF ease)
       {
          setup4F(oldX,oldY,oldZ,oldW,newX,newY,newZ,newW,startDelay,duration,ease,0.0f,1.0f);
       }
	    bool interp(int numDims);

	    void getFloat(F32 &f)
	    {
		    f = currValue[0];
	    }

	    void getColor(ColorI &c)
	    {
		    c.red = (S32)currValue[0];
		    c.green = (S32)currValue[1];
		    c.blue = (S32)currValue[2];
	    }

       // FIX THESE...g
	    void getRectF(RectF &r)
	    {
		    r.point.x  = currValue[0];
		    r.point.y  = currValue[1];
		    r.extent.x = currValue[2];
          r.extent.y = currValue[3];
	    }

	    void getRectI(RectI &r)
	    {
		    r.point.x  = (S32)currValue[0];
		    r.point.y  = (S32)currValue[1];
		    r.extent.x = (S32)currValue[2];
          r.extent.y = (S32)currValue[3];
	    }

       void getPoint4F(Point4F &p)
	    {
		    p.x = currValue[0];
		    p.y = currValue[1];
		    p.z = currValue[2];
		    p.w = currValue[3];
	    }

       void getPoint3F(Point3F &p)
	    {
		    p.x = currValue[0];
		    p.y = currValue[1];
		    p.z = currValue[1];
	    }

       void getPoint2F(Point2F &p)
	    {
		    p.x = currValue[0];
		    p.y = currValue[1];
	    }

	    void getPoint2I(Point2I &p)
	    {
		    p.x = (S32)currValue[0];
		    p.y = (S32)currValue[1];
	    }
    };
    // < pg animation

after
enum vertSizingOptions
{
vertResizeBottom = 0, ///< fixed on the top and in height
vertResizeHeight, ///< fixed on the top and bottom
vertResizeTop, ///< fixed in height and on the bottom
vertResizeCenter,
vertResizeRelative ///< resize relative
};
protected:
// > pg guicolor
    F32 mAlpha;
    ColorI mColor;
    RectF mUnitClip; // clip rect in unit space...ie 0 0 1 1 means draw all
    bool mAnimating; // track when any animation is taking place...
    animation mAlphaAnim;
    animation mColorAnim;
    animation mUnitClipAnim;
    animation mPositionAnim; // EITHER use position & extent...
    animation mExtentAnim;
    animation mBoundsAnim;   // ... OR use bounds
    // < pg guicolor

3) Declare an interface to animate with.
after
F32 getFloatVariable(); ///< Returns value of control's bound variable as a float


public:
   // > pg animation
    void setPassThru(bool passThru);
    bool getPassThru() { return mPassThru; }

    const Point2F& getCenter() {
      mCenter.x = ((F32)mBounds.extent.x) / 2.0F + mBounds.point.x;
       mCenter.y = ((F32)mBounds.extent.y) / 2.0F + mBounds.point.y;
      return mCenter;
    }
    const F32 getHalfWidth() { return (F32)mBounds.extent.x/2.0F; }
    const F32 getHalfHeight() { return (F32)mBounds.extent.y/2.0F; }
    void setCenter(const Point2F &center);
    void setCenterAndSize(const Point2F &center, const Point2I &extent);

    void setAlpha(F32 a);
    F32 getAlpha() {return mAlpha;}

    void setColor(ColorI color);
    ColorI getColor() {return mColor;}

    void setUnitClip(RectF unitClip);
    RectF getUnitClip() {return mUnitClip;}

    void updateAnimationState();
    void cancelAllAnimations();
    void updateAnimation();

    // position now cets the controls CENTER...
    void animatePositionKeyPointList(SimObject *input, F32 startDelay, F32 duration, EaseF ease);
    void animatePosition(Point2F newPos, F32 startDelay, F32 duration, EaseF ease);
    void animateExtent(Point2I newExt, F32 startDelay, F32 duration, EaseF ease);
    void animateAlpha(F32 newAlpha, F32 startDelay, F32 duration, EaseF ease);
    void animateColor(ColorI newColor, F32 startDelay, F32 duration, EaseF ease);
    void animateUnitClip(RectF newClip, F32 startDelay, F32 duration, EaseF ease);
    void animateBounds(RectF newBounds, F32 startDelay, F32 duration, EaseF ease);
    void updateColorAnimation();
    void updatePositionAnimation();
    void updateExtentAnimation();
    void updateAlphaAnimation();
    void updateUnitClipAnimation();
    void updateBoundsAnimation();

    bool isAnimating() { return mAnimating;}
// < pg animation
4) Let's initialize all those new variables.
Now edit the file guicontrol.cc

GuiControl::GuiControl()
After
mTipHoverTime = 1000;
(or mCanSaveFieldDictionary = false; in TGEA)
// > pg animation
   mPassThru = false;
	mAlpha = 1.0;
   mColor.set(255,255,255);
   mUnitClip.point.x = mUnitClip.point.y = 0;
   mUnitClip.extent.x = mUnitClip.extent.y = 1;
   mAnimating = false;
	mPositionAnim.enabled = false;
	mExtentAnim.enabled = false;
	mBoundsAnim.enabled = false;
	mAlphaAnim.enabled = false;
   mColorAnim.enabled = false;
   mUnitClipAnim.enabled = false;
   // < pg animation

5) Time to expose a new group and fields to the editor.

in void GuiControl::initPersistFields()
after endGroup("ToolTip");
// > pg Animation
   addGroup("Animation");
   addField("alpha", TypeF32, Offset(mAlpha, GuiControl));
   addField("color", TypeColorI, Offset(mColor, GuiControl));
   addField("unitClip", TypeRectF, Offset(mUnitClip, GuiControl));
   endGroup("Animation");
   // < pg Animation


6) Here is some support functions for the clip animation.
after renderTooltip()

// > pg animation
// re express a rect relative to current & unit
static RectI getRealClip(const RectF &unitClip, const Point2I &point, const Point2I &extent)
{
    RectI clip(point.x+unitClip.point.x*extent.x,
               point.y+unitClip.point.y*extent.y,
        unitClip.extent.x*extent.x,
        unitClip.extent.y*extent.y);
 
    return clip;
}
// < pg animation


7) This next function allows us to incorporate animation into child controls so replace the existing function with this one.
replace
void GuiControl::renderChildControls()

with TGE VERSION ONLY ( TGEA BELOW)

void GuiControl::renderChildControls(Point2I offset, const RectI &updateRect)
{
   // > pg animation
   // offset is the upper-left corner of this control in screen coordinates
   // updateRect is the intersection rectangle in screen coords of the control
   // hierarchy.  This can be set as the clip rectangle in most cases.
   // move this into loop...
   //RectI clipRect = updateRect;

   iterator i;
   for(i = begin(); i != end(); i++)
   {
      GuiControl *ctrl = static_cast<GuiControl *>(*i);
      if (ctrl->mVisible)
      {
         ctrl->updateAnimation();

         Point2I childPosition;
         RectI childClip;
         RectI clipRect;
         bool draw;

         childPosition = offset + ctrl->getPosition();
         if (ctrl->mUnitClip.point.x == 0 && ctrl->mUnitClip.point.y == 0 &&
             mUnitClip.extent.x == 1 && ctrl->mUnitClip.extent.y == 1)
         {
            // no unit clipping...
            draw = true;
            clipRect = updateRect;
         }
         else
         {
            
            // unit clipping...
            RectI realClip = getRealClip(ctrl->mUnitClip,childPosition,ctrl->getExtent());
            clipRect = updateRect;
            draw = clipRect.intersect(realClip);
         }

         childClip.point = childPosition;
         childClip.extent = ctrl->getExtent();

         if (draw && childClip.intersect(clipRect))
         {
            dglSetClipRect(childClip);
            glDisable(GL_CULL_FACE);
            ctrl->onRender(childPosition, childClip);
         }
      }
   }
   // < pg animation
}
THIS IS TGEA VERSION
void GuiControl::renderChildControls(Point2I offset, const RectI &updateRect)
{
   // > pg animation
   // offset is the upper-left corner of this control in screen coordinates
   // updateRect is the intersection rectangle in screen coords of the control
   // hierarchy.  This can be set as the clip rectangle in most cases.
   // move this into loop...
   //RectI clipRect = updateRect;

   iterator i;
   for(i = begin(); i != end(); i++)
   {
      GuiControl *ctrl = static_cast<GuiControl *>(*i);
      if (ctrl->mVisible)
      {
         ctrl->updateAnimation();

         Point2I childPosition;
         RectI childClip;
         RectI clipRect;
         bool draw;

         childPosition = offset + ctrl->getPosition();
         if (ctrl->mUnitClip.point.x == 0 && ctrl->mUnitClip.point.y == 0 &&
             mUnitClip.extent.x == 1 && ctrl->mUnitClip.extent.y == 1)
         {
            // no unit clipping...
            draw = true;
            clipRect = updateRect;
         }
         else
         {
            
            // unit clipping...
            RectI realClip = getRealClip(ctrl->mUnitClip,childPosition,ctrl->getExtent());
            clipRect = updateRect;
            draw = clipRect.intersect(realClip);
         }

         childClip.point = childPosition;
         childClip.extent = ctrl->getExtent();

         if (draw && childClip.intersect(clipRect))
         {
			GFX->setClipRect( childClip );
             GFX->setStateBlock(mDefaultGuiSB); // <-----NEW
            ctrl->onRender(childPosition, childClip);
         }
      }
   }

   // < pg animation
}

8) We are going to comment out the old getCenter() Interface for the GUI control, you can delete it if you like but I am of the opinion to keep the old until you know for sure you'll never use it again, your call. We will be providing a new getCenter() in a little bit.

comment out
ConsoleMethod( GuiControl, getCenter,
Like this
// > pg animation
/*
ConsoleMethod( GuiControl, getCenter, const char*, 2, 2, " returns center of control, as space seperated ints")
{
   char *retBuffer = Con::getReturnBuffer(64);
   const Point2I pos = object->getPosition();
   const Point2I ext = object->getExtent();
   Point2I center(pos.x + ext.x/2, pos.y + ext.y/2);
   dSprintf(retBuffer, 64, "%d %d", center.x, center.y);
   return retBuffer;
}
*/
// < pg animation
9) Now let's define all those interfaces we declared.

at the end of the file

// > pg Animation
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //

void GuiControl::cancelAllAnimations()
{
   mColorAnim.cancel();
   mPositionAnim.cancel();
   mExtentAnim.cancel();
   mBoundsAnim.cancel();
   mAlphaAnim.cancel();
   mUnitClipAnim.cancel();

   updateAnimationState();
}

void GuiControl::updateAnimation()
{
	updatePositionAnimation();
	updateExtentAnimation();
   updateBoundsAnimation();
	updateAlphaAnimation();
	updateColorAnimation();
   updateUnitClipAnimation();

   updateAnimationState();

   // test if we can infact stop causing updates if not animating...
   //if (mAnimating)
      setUpdate();
}

void GuiControl::updateAnimationState()
{
   bool actuallyAnimating = 
      mColorAnim.enabled ||
      mPositionAnim.enabled ||
      mExtentAnim.enabled ||
      mUnitClipAnim.enabled ||
      mBoundsAnim.enabled ||
      mAlphaAnim.enabled ;

   // if we have changed state...do callback
   if (actuallyAnimating != mAnimating)
   {
      if (actuallyAnimating)
         Con::executef(this,2,"onAnimationStarted"); // TGE
		// Con::executef(this,"onAnimationStarted"); // TGEA

      else
         Con::executef(this,2,"onAnimationStopped"); //TGE
//// Con::executef(this," onAnimationStopped "); // TGEA

      mAnimating = actuallyAnimating;
   }
}

// CTOR
GuiControl::animation::animation()
{
	enabled = false;
   kplEnabled = false;
}

// DTOR
GuiControl::animation::~animation()
{
}

// Setup a key point list...
void GuiControl::animation::kplSetup(SimObject *input, S32 numDims, F32 startDelay, F32 duration, EaseF ease)
{
   // read input into keypoint list and set kplDuration
   kplReadInput(input,numDims);

   // override duration?
   if (duration < -1)
       kplDuration = duration; // already in milliseconds...

   // ok we MUST have at least two keypoints or there is nothing to do...
   // and we only handle dimensions 1..3
   if (keyPointList.size() < 2 || kplNumDimensions < 0 || kplNumDimensions > 3)
   {
      keyPointList.clear();
      kplEnabled = false;
      return;
   }

   // OK apply the ease to the time values themselves...
   // NOT supporing any extrapolation, so we clamp
   int i;
   for (i=0; i<keyPointList.size(); i++)
       keyPointList[i].w = ease.getUnitValue(keyPointList[i].w,true);

   // but no matter what...first timeoint is zero and last is one...
   // this should be true anyway after the above, but let's force it
   keyPointList[0].w = 0.0f;
   keyPointList[keyPointList.size()-1].w = 1.0f;

   // ok setup for first segment....
   kplEnabled = true;
   kplEase = ease;

   // ok setup the first segment...
   kplIndex = 0; // we always interp from kplIndex-1 to kplIndex, start at
                 // zero and the "next" call below will advance...
	kplStartTime = Platform::getVirtualMilliseconds() + startDelay;

   // ok setup next, meaning first...
   kplSetupNext();
}

bool GuiControl::animation::kplSetupNext()
{
   // goto next segment...
   kplIndex++;

   // are we out of segments?  return false if so
   if (kplIndex >= keyPointList.size())
   {
       kplEnabled = false;
   }
   else
   {
       // figure out the duration and start/end of first bit...
       F32 start = keyPointList[kplIndex-1].w;
       F32 end   = keyPointList[kplIndex].w;
       F32 duration = (end - start) * kplDuration;
       F32 startDelay = 0.0; // start time is corrected below...

       // setup data for first segment..
       if (kplNumDimensions == 1)
	        setupF(keyPointList[kplIndex-1].x, keyPointList[kplIndex].x,startDelay,duration,kplEase,start,end);
       else if (kplNumDimensions == 2)
	        setup2F(keyPointList[kplIndex-1].x, keyPointList[kplIndex-1].y, keyPointList[kplIndex].x,keyPointList[kplIndex].y,startDelay,duration,kplEase,start,end);
       else 
	        setup3F(keyPointList[kplIndex-1].x, keyPointList[kplIndex-1].y, keyPointList[kplIndex-1].z, keyPointList[kplIndex].x,keyPointList[kplIndex].y,keyPointList[kplIndex].z,startDelay,duration,kplEase,start,end);

       // correct start time to be more accurate and to reflect the start delay...
       startTime = kplStartTime + (start * kplDuration);
   }

   return kplEnabled;
}

void GuiControl::animation::kplReadInput(SimObject *input, S32 numDims)
{
   kplNumDimensions = numDims;
   keyPointList.clear();

   AbstractClassRep::FieldList list = input->getFieldList();

   // keypoint list is in seconds but we renormalize...

   // assume a duration of 1.0 unless specified in time[size-1] or as input.duration
   kplDuration = 1.0f;

   // let's see if there is a .duration override
   F32 overrideDuration = -1.0f;
   const char *durValue = input->getDataField(StringTable->insert("duration"),NULL);
   if (durValue != NULL && durValue[0] != '')
      overrideDuration = dAtof(durValue);

   // should we be reading time values (or generating them?)
   const char *firstTime = input->getDataField("time","0");
   bool doReadTime = (firstTime != NULL && firstTime[0] != '');

   // read the times and keypoints from the given input object...
   int i;
   for (i=0; i<10000; i++)
   {
      // read values till we hit the end...
      static char indexBuf[128];
      dSprintf(indexBuf,128,"%d",i);

      const char *keyValue = input->getDataField("value",indexBuf);
      if (keyValue == NULL || keyValue[0] == '')
         break;

      Point4F value(0,0,0,0);
      if (kplNumDimensions == 1)
         dSscanf(keyValue,"%g",&value.x);   
      else if (kplNumDimensions == 2)
         dSscanf(keyValue,"%g %g",&value.x,&value.y);   
      else
         dSscanf(keyValue,"%g %g %g",&value.x,&value.y,&value.z);

      if (doReadTime)
      {
          const char *keyTime = input->getDataField("time",indexBuf);
          if (keyTime == NULL || keyTime[0] == '')
          {
             // if we ever get a missing piece...we drop back to
             // as if we had none...
             Con::errorf("GuiControl::animation::kplReadInput(): missing time[%d], ignoring all time[] values",i);
             doReadTime = false;
          }
          else
          {
             value.w = dAtof(keyTime);
          } 
      }

      keyPointList.push_back(value);
   }

   // we need 2 or more
   S32 size = keyPointList.size();
   if (size < 2)
   {
       Con::errorf("GuiControl::animation::kplReadInput(): less than 2 points in KeyPointList");
       keyPointList.clear();
       return;
   }

   // if we read given times...then we must renormalize based on that...
   if (doReadTime)
   {
       F32 max = keyPointList[size-1].w;
       for (i=0; i<size; i++)
           keyPointList[i].w /= max;

       // duration comes from time[size-1]
       kplDuration = max;
   }
   else
   {
       // if we did not read time then fill in default values...
       F32 max = size-1;
       for (i=0; i<size; i++)
           keyPointList[i].w = (F32)(i) / max;

       // duration stays at default of 1.0...unless overridden
   }

   // did we have an override?  ok then use that...
   if (overrideDuration > 0)
      kplDuration = overrideDuration;

   // ok we want this in milliseconds, not seconds
   kplDuration *= 1000;
}

// NOTE: in the re-worked animation system, we animate by "center" (not by "position" meaning upper left)
void GuiControl::animation::setup(F32 startDelay, F32 dur, EaseF easeData, F32 start, F32 end)
{
	enabled = true;
   ease = easeData;
	startTime = Platform::getVirtualMilliseconds() + startDelay;
	duration = dur;
   interpStart = start;
   interpEnd = end;
}

void GuiControl::animation::setup(F32 dur, F32 start, F32 end)
{
	startTime = Platform::getVirtualMilliseconds();
	duration = dur;
   interpStart = start;
   interpEnd = end;
}

void GuiControl::animation::setup(F32 startDelay, F32 dur, EaseF easeData)
{
   setup(startDelay,dur,easeData,0.0f,1.0f);
}

void GuiControl::animation::setupF(F32 oldVal, F32 newVal, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
	setup(startDelay, duration, ease,start,end);
	currValue[0] = startValue[0] = oldVal;
	deltaValue[0] = newVal - oldVal;
}

void GuiControl::animation::setup2F(F32 oldX, F32 oldY, F32 newX, F32 newY, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
	setup(startDelay, duration, ease,start,end);
	currValue[0] = startValue[0] = oldX;
	currValue[1] = startValue[1] = oldY;
	deltaValue[0] = newX - oldX;
	deltaValue[1] = newY - oldY;
}

void GuiControl::animation::setup2I(S32 oldX, S32 oldY, S32 newX, S32 newY, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
	setup(startDelay, duration, ease, start,end);
	currValue[0] = startValue[0] = (F32)oldX;
	currValue[1] = startValue[1] = (F32)oldY;
	deltaValue[0] = (F32)newX - oldX;
	deltaValue[1] = (F32)newY - oldY;
}

void GuiControl::animation::setup3F(F32 oldX, F32 oldY, F32 oldZ, F32 newX, F32 newY, F32 newZ, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
	setup(startDelay, duration, ease,start, end);
	currValue[0] = startValue[0] = oldX;
	currValue[1] = startValue[1] = oldY;
	currValue[2] = startValue[2] = oldZ;
	deltaValue[0] = newX - oldX;
	deltaValue[1] = newY - oldY;
	deltaValue[2] = newZ - oldZ;
}

void GuiControl::animation::setup4F(F32 oldX, F32 oldY, F32 oldZ, F32 oldW, F32 newX, F32 newY, F32 newZ, F32 newW, F32 startDelay, F32 duration, EaseF ease, F32 start, F32 end)
{
	setup(startDelay, duration, ease,start, end);
	currValue[0] = startValue[0] = oldX;
	currValue[1] = startValue[1] = oldY;
	currValue[2] = startValue[2] = oldZ;
	currValue[3] = startValue[3] = oldW;
	deltaValue[0] = newX - oldX;
	deltaValue[1] = newY - oldY;
	deltaValue[2] = newZ - oldZ;
	deltaValue[3] = newW - oldW;
}

bool GuiControl::animation::interp(int numDims)
{
	if (!enabled)
		return false;

	// interp the given # of dimensions...
	U32 now = Platform::getVirtualMilliseconds();

	// wait for scheduled start time
	if(now < startTime)
		return false;

   // was kpl enabled when we came in
   bool doKPL = kplEnabled;

	// calculate elapsed time and distance traveled
	F32 elapsedTime = now - startTime;

	// watch for end of animation
	if(elapsedTime >= duration)
	{
      if (kplEnabled)
      {
          // ok we need to move to the next segment
          if (kplSetupNext())
          {
              // ok we have setup the next segment...move on...
              // we do this recursively incase we have to even skip
              // past a few segments to find the right one...
              return interp(numDims);
          }
          else
          {
              // there is no next...kpl is already disabled just
              // finish out this last segment by dropping through...
              enabled = false;
              elapsedTime = duration;
          }
      }
      else
      {
		   enabled = false;
		   elapsedTime = duration;
      }
	}
    
   // do the interpolation
   if (doKPL)
   {
       // for each individual segment...we always do linear interpolation
       // the ease has been calculated accross the whole segment...
       int i;
       for (i=0; i<numDims; i++)
	       currValue[i] = mLinearTween(elapsedTime,startValue[i], deltaValue[i], duration);
   }
   else
   {
       int i;
       for (i=0; i<numDims; i++)
	       currValue[i] = ease.getValue(elapsedTime,startValue[i], deltaValue[i], duration);
   }

	// we DID interp something...
	return true;
}

void GuiControl::setCenter(const Point2F &center)
{
	Point2I newPos((S32)(center.x - getHalfWidth()),(S32)(center.y - getHalfHeight()));
	resize(newPos, mBounds.extent);
}

void GuiControl::setCenterAndSize(const Point2F &center, const Point2I &extent)
{
	Point2I newPos((S32)(center.x - (F32)extent.x/2.0f),(S32)(center.y - (F32)extent.y/2.0f));
	resize(newPos, extent);
}

void GuiControl::animatePosition(Point2F newPos, F32 startDelay, F32 duration, EaseF ease)
{
	Point2F center = getCenter();
	mPositionAnim.setup2F(center.x, center.y, newPos.x,newPos.y,startDelay,duration,ease);
}

void GuiControl::animatePositionKeyPointList(SimObject *input, F32 startDelay, F32 duration, EaseF ease )
{
   mPositionAnim.kplSetup(input,2,startDelay,duration,ease); 
}

void GuiControl::animateExtent(Point2I newExt, F32 startDelay, F32 duration, EaseF ease)
{
	mExtentAnim.setup2I(mBounds.extent.x, mBounds.extent.y, newExt.x,newExt.y,startDelay,duration,ease);
}

void GuiControl::setAlpha(F32 newAlpha)
{
	mAlpha = newAlpha;
	setUpdate();
	mAlphaAnim.enabled = false;

   // setting alpha AUTOMATICALLY set's alpha of children so that
   // setting alpha is analogous to setting position in that it
   // propogates down to the children.
	iterator i;
	for(i = begin(); i != end(); i++)
	{
		GuiControl *ctrl = static_cast<GuiControl *>(*i);
		ctrl->setAlpha(newAlpha);
	}
}

void GuiControl::setColor(ColorI color)
{
   // setting the color simply sets the color...it does not affect
   // the children's color, but it might cancel any animation in progress...
	mColor = color;
	setUpdate();
	mColorAnim.enabled = false;
}

void GuiControl::animateAlpha(F32 newAlpha, F32 startDelay, F32 duration, EaseF ease)
{
	mAlphaAnim.setupF(mAlpha, newAlpha, startDelay,duration,ease);

	// update children
	iterator i;
	for(i = begin(); i != end(); i++)
	{
		GuiControl *ctrl = static_cast<GuiControl *>(*i);
		ctrl->animateAlpha(newAlpha, startDelay, duration,ease);
	}
}

void GuiControl::animateColor(ColorI newColor, F32 startDelay, F32 duration, EaseF ease)
{
	mColorAnim.setup3F(mColor.red,mColor.green,mColor.blue,newColor.red,newColor.green,newColor.blue,startDelay,duration,ease);
}

void GuiControl::updateColorAnimation()
{
	if (!mColorAnim.interp(3))
		return;

	ColorI newColor;
	mColorAnim.getColor(newColor);

	// apply changes
	mColor = newColor;
}

void GuiControl::animateUnitClip(RectF newClip, F32 startDelay, F32 duration, EaseF ease)
{
	mUnitClipAnim.setup4F(
       mUnitClip.point.x,mUnitClip.point.y,mUnitClip.extent.x,mUnitClip.extent.y,
       newClip.point.x,newClip.point.y,newClip.extent.x,newClip.extent.y,
       startDelay,duration,ease);
}


void GuiControl::updateUnitClipAnimation()
{
	if (!mUnitClipAnim.interp(4))
		return;

	RectF newClip;
	mUnitClipAnim.getRectF(newClip);

	// apply changes
	mUnitClip = newClip;
}

void GuiControl::setUnitClip(RectF clip)
{
   // setting the clip simply sets the clip...it does not affect
   // the children's clip, but it might cancel any animation in progress...
	mUnitClip = clip;
	setUpdate();
	mUnitClipAnim.enabled = false;
}

void GuiControl::animateBounds(RectF newBounds, F32 startDelay, F32 duration, EaseF ease)
{
	mBoundsAnim.setup4F(
       mBounds.point.x,mBounds.point.y,mBounds.extent.x,mBounds.extent.y,
       newBounds.point.x,newBounds.point.y,newBounds.extent.x,newBounds.extent.y,
       startDelay,duration,ease);
}


void GuiControl::updateBoundsAnimation()
{
	if (!mBoundsAnim.interp(4))
		return;

	RectF newBounds;
	mBoundsAnim.getRectF(newBounds);

	// apply changes
   Point2I pos(newBounds.point.x,newBounds.point.y);
   Point2I extent(newBounds.extent.x,newBounds.extent.y);
	resize(pos, extent);
}


void GuiControl::updatePositionAnimation()
{
	if (!mPositionAnim.interp(2))
		return;

	Point2F newCenter;
	mPositionAnim.getPoint2F(newCenter);
	setCenter(newCenter);
}


void GuiControl::updateExtentAnimation()
{
	if (!mExtentAnim.interp(2))
		return;

	Point2I newExt;
	mExtentAnim.getPoint2I(newExt);

	// apply changes
	setCenterAndSize(getCenter(), newExt);
}


void GuiControl::updateAlphaAnimation()
{
	if (!mAlphaAnim.interp(1))
		return;

	F32 newAlpha;
	mAlphaAnim.getFloat(newAlpha);

	// apply changes
	mAlpha = newAlpha;
}

void GuiControl::setPassThru(bool passThru)
{
    mPassThru = passThru;
}

// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- //

ConsoleMethod(GuiControl, getPassThru, bool, 2, 2, "ctrl.getPassThru()")
{
   GuiControl *ctrl = static_cast<GuiControl*>(object);
   return ctrl->getPassThru();
}

ConsoleMethod(GuiControl, setPassThru, void, 3, 3, "ctrl.setPassThru(passThru)")
{
	argc;
	GuiControl* ctrl = static_cast<GuiControl*>(object);
	ctrl->setPassThru(dAtob(argv[2]));
}

ConsoleMethod(GuiControl, getCenter, const char *, 2, 2, "ctrl.getCenter()")
{
   GuiControl *ctrl = static_cast<GuiControl*>(object);
   char *retBuffer = Con::getReturnBuffer(64);
   const Point2F &pos = ctrl->getCenter();
   dSprintf(retBuffer, 64, "%f %f", pos.x, pos.y);
   return retBuffer;
}

ConsoleMethod(GuiControl, setColor, void, 5, 5, "ctrl.setColor(r,g,b)")
{
	argc;
	GuiControl* ctrl = static_cast<GuiControl*>(object);
   ColorI color(dAtof(argv[2]),dAtof(argv[3]),dAtof(argv[4]));
	ctrl->setColor(color);
}

ConsoleMethod(GuiControl, setUnitClip, void, 6, 6, "ctrl.setUnitClip(x,y,w,h)")
{
	argc;
	GuiControl* ctrl = static_cast<GuiControl*>(object);
   RectF clip(dAtof(argv[2]),dAtof(argv[3]),dAtof(argv[4]),dAtof(argv[5]));
	ctrl->setUnitClip(clip);
}


ConsoleMethod(GuiControl, setAlpha, void, 3, 3, "ctrl.setAlpha(value)")
{
	argc;
	GuiControl* ctrl = static_cast<GuiControl*>(object);
	ctrl->setAlpha(dAtof(argv[2]));
}

ConsoleMethod(GuiControl, getAlpha, F32, 2, 2, "ctrl.getAlpha()")
{
	argc;
	GuiControl* ctrl = static_cast<GuiControl*>(object);
	return ctrl->getAlpha();
}

ConsoleMethod(GuiControl, isAnimating, bool, 2, 2, "ctrl.isAnimating()")
{
	argc;
	GuiControl* ctrl = static_cast<GuiControl*>(object);
	return ctrl->isAnimating();
}

// internally this animation refers to "position", but logically what is
// happening is we are animating the CENTER...therefore it is renamed
// when exposed to script
ConsoleMethod(GuiControl, animateCenter, void, 6,7,"ctrl.animateCenter(x, y, delay, duration [,ease])")
{
	GuiControl *ctrl = static_cast<GuiControl*>(object);
	Point2F newPos(dAtof(argv[2]), dAtof(argv[3]));
   EaseF ease;
	if (argc > 6)
       ease.set(argv[6]);
	ctrl->animatePosition(newPos, dAtof(argv[4]), dAtof(argv[5]), ease);
}

ConsoleMethod(GuiControl, animateExtent, void, 6,7,"ctrl.animateExtent(w, h, delay, duration [,ease]))")
{
	GuiControl *ctrl = static_cast<GuiControl*>(object);
	Point2I newExt(dAtoi(argv[2]), dAtoi(argv[3]));
   EaseF ease;
	if (argc > 6)
       ease.set(argv[6]);
	ctrl->animateExtent(newExt, dAtof(argv[4]), dAtof(argv[5]), ease);
}


ConsoleMethod(GuiControl, animateAlpha, void, 5,6,"ctrl.animateAlpha(alpha, delay, duration [,ease]))")
{
	GuiControl *ctrl = static_cast<GuiControl*>(object);
   EaseF ease;
	if (argc > 5)
       ease.set(argv[5]);
	ctrl->animateAlpha(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]), ease);
}

ConsoleMethod(GuiControl, animateColor, void, 7,8,"ctrl.animateColor(r, g, b, delay, duration [,ease]))")
{
	GuiControl *ctrl = static_cast<GuiControl*>(object);
	ColorI newColor(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]));
   EaseF ease;
	if (argc > 7)
       ease.set(argv[7]);
	ctrl->animateColor(newColor, dAtof(argv[5]), dAtof(argv[6]), ease);
}

ConsoleMethod(GuiControl, animateUnitClipping, void, 8,9,"ctrl.animateUnitClipping(x, y, w, h, delay, duration [,ease]))")
{
	GuiControl *ctrl = static_cast<GuiControl*>(object);
	RectF newClip(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]), dAtof(argv[5]));
   EaseF ease;
	if (argc > 8)
       ease.set(argv[8]);
	ctrl->animateUnitClip(newClip, dAtof(argv[6]), dAtof(argv[7]), ease);
}

ConsoleMethod(GuiControl, animateBounds, void, 8,9,"ctrl.animateBounds(x, y, w, h, delay, duration [,ease]))")
{
	GuiControl *ctrl = static_cast<GuiControl*>(object);
	RectF newBounds(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]), dAtof(argv[5]));
   EaseF ease;
	if (argc > 8)
       ease.set(argv[8]);
	ctrl->animateBounds(newBounds, dAtof(argv[6]), dAtof(argv[7]), ease);
}

ConsoleMethod(GuiControl, cancelAllAnimations, void, 2,2,"ctrl.cancelAllAnimations()")
{
	GuiControl *ctrl = static_cast<GuiControl*>(object);
	ctrl->cancelAllAnimations();
}

// new experimental method to animate the center along a keypoint list...
ConsoleMethod(GuiControl, animateCenterKeyPointList, void, 4,6,"ctrl.animateCenterKeyPointList(keyobj, delay [,duration] [,ease])")
{
	GuiControl *ctrl = static_cast<GuiControl*>(object);
   F32 delay = dAtof(argv[3]);
   EaseF ease;
   F32 duration = -1.0f;
	if (argc > 4)
       duration = dAtof(argv[4]);
	if (argc > 5)
       ease.set(argv[5]);

   SimObject *obj;
   if (!Sim::findObject(argv[2],obj))
   {
       Con::errorf("GuiControl.animateCenterKeyPointList() - cannot find key object: %s",argv[2]);
       return;
   }
	ctrl->animatePositionKeyPointList(obj, delay, duration, ease);
}


ConsoleMethod( GuiControl, getRoot, S32, 2, 2, "()"){   
	GuiCanvas* canvas = object->getRoot();   
	return canvas ? canvas->getId() : -1;
}
// < pg Animation
NOTE watch out for the Con::execute functions the TGE version take an additional parameter. I have commented out a TGEA equivalent below the TGE version
Specifically
Con::executef(this,2,"onAnimationStarted"); // TGE
// Con::executef(this,"onAnimationStarted"); // TGEA

ALSO Comment out the old ConsoleMethod GetRoot


There you have it now you can do some amazing things with GUI. Here is the necessary script code to get it working.
%ctrl.animateColor(%r, %g, %b, %delay, %duration, %ease); // animate to a new color %ctrl.animateAlpha(%alpha, %delay, %duration, %ease); // animate to new alpha
%ctrl.animateExtent(%w, %h, %delay, %duration, %ease); // animate to new extent while maintaining top/left position
%ctrl.animateCenter(%x, %y, %delay, %duration, %ease); // animate center of GUI to a new position
%ctrl.animateBounds(%x, %y, %w, %h, %delay, %duration, %ease); // animates to a new recatangle ( equivallent of animateCenter + animateExtent)
%ctrl.animateUnitClipping(%x, %y, %w, %h, %delay, %duration, %ease); // animates what clip area gets rendered, great for wipes.
%ctrl.cancelAllAnimations(); // stop all animations
%ctrl.getCenter();
%ctrl.getGlobalCenter(); // get center related to root GUI
%ctrl.setAlpha(%alpha);
%ctrl.getAlpha();
%ctrl.setColor(%r, %g, %b);
%ctrl.getColor();
%ctrl.getRoot();
%ctrl.setUnitClip(x,y,w,h); // default 0, 0, 1, 1
%ctrl.isAnimating(); // determines if an animation is in process
%ctrl.getPassThru();
%ctrl.setPassThru( %bool ); // allow mouse click to pass to parent
%ctrl.animateCenterKeyPointList(%keyobj, %delay [,%duration] [,%ease]);
// Takes a script object which has value[] and a time[] arrays to be used for animating . Duration and Ease can be added to the scriptObject or passed in optionally as additional parameters. If time[0] is not specified it will animate each value equally based on the duration.

new ScriptObject(TestKeyPoints)
{
   duration = "5.0"; // seconds
   ease = "1 3  "; // TypeEaseF ease for whole path...
   value[0] = "999 100"; //x y center ot GUI
   value[1] = "950 135"; 
   value[2] = "900 195"; 
   value[3] = "998 248"; 
   value[4] = "945 320"; 
   value[5] = "880 417"; 
   value[6] = "821 501"; 
   value[7] = "920 540"; 
   value[8] = "1004 588"; 
   time[0] = "0"; //in seconds from start of animation
   time [1] = "2"; 
   time [2] = "3"; 
   time [3] = "7"; 
   time [4] = "8"; 
   time [5] = "13"; 
   time [6] = "15"; 
   time [7] = "16"; 
   time [8] = "20"; 
};
%ctrl.animateCenterKeyPointList(TestKeyPoints,0);


EXAMPLE APPLICATION

10) Here is an example of how you might incorporate the alpha and color into an existing control, the GuiMLTextColor.
guiMLTextCtrl.cc
TGE ONLY

in void GuiMLTextCtrl::drawAtomText(
after
dglDrawTextN(font, drawPoint + atom->style->shadowOffset, tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL);
}

// > pg guicolor
      color.alpha = color.alpha * mAlpha;
      F32 red = F32(color.red) * mColor.red / 255.0f;
      F32 green = F32(color.green) * mColor.green / 255.0f;
      F32 blue = F32(color.blue) * mColor.blue / 255.0f;
      ColorI blendedColor(red,green,blue,color.alpha);
      dglSetBitmapModulation(blendedColor);
// < pg guicolor

TGEA ONLY

in void GuiMLTextCtrl::drawAtomText(
after
if(atom->style->shadowOffset.x || atom->style->shadowOffset.y)
{
ColorI shadowColor = atom->style->shadowColor;
shadowColor.alpha = (S32)(mAlpha * shadowColor.alpha);
drawer->setBitmapModulation(shadowColor);
drawer->drawTextN(font, drawPoint + atom->style->shadowOffset,
tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL);
}
// > pg guicolor
      color.alpha = color.alpha * mAlpha;
      F32 red = F32(color.red) * mColor.red / 255.0f;
      F32 green = F32(color.green) * mColor.green / 255.0f;
      F32 blue = F32(color.blue) * mColor.blue / 255.0f;
      ColorI blendedColor(red,green,blue,color.alpha);
      drawer->setBitmapModulation(blendedColor);
      // < pg guicolor


11) Finally add the GuiPlasticBitmapCtrl.h and GuiPlasticBitmapCtrl.cc into the gui/shiney folder. And Compile

This control can load single images or a dml then play thu the dml as an animation.
%ctrl.setDml(%dml); // pass a dml for animation or a filename for a single bitmap
%ctrl.setFrame(%num); // sets the current frame of the animation
%ctrl.setFPS(F32); // sets the interval between frames 
%ctrl.setValue(xAxis, yAxis); // set X/Y wrap offset
%ctrl.play([stopAtEnd]); // plays animation, bool can be set to play once thru then stop
%ctrl.stop(); // stop animation
%ctrl.rewind(); // rewind animation
%ctrl.isPlaying();// determines if dml animation is playing
%ctrl.getFrames(); // gets current frame 
%ctrl.getTotalFrames();// get total frames per animation you can have up to 50 frames.

The GuiPlasticBitmapCtrl also has callbacks for mouse and animation events.

%ctrl.onStoppedAtEnd(); // called at end of animation if bool is set in .play()
%ctrl.onMouseLeave(); 
%ctrl.onMouseEnter();
%ctrl.onMouseDragged();
%ctrl.onMouseMove();
%ctrl.onMouseDown();
%ctrl.onMouseUp();

You'll also find 2 additional fields in the GuiPlasticBitmapCtrl.
%ctrl.rotation 
%ctrl.delta rotation
Rotation will allow you rotate the GUI element X degrees from the normal. DeltaRotation will automatically update the rotation of the GUI element by X degrees/second.

Another great feature with the GuiPlasticBitmapCtrl is that if you make your images colored white you can blend and animate color with the animateColor and setColor functions.

PlasticBitmap Example TGE. This will make a PlasticBitmap on the current GUI name pg. I put some functionality into its mouseCallbacks but I also included some code so you can see how sweet this stuff can be.


12) place the star folder within you game's folder as a sibling of the data/server/client

13) In console (~) exec("YOURGAMENAME/star/star.cs");

14) Press the Star to start the PlasticBitmap Spinning and animating color.

15) pg.animateAlpha(0, 0, 500); watch the bitmap disappear;

16) pg.animateAlpha(1, 0, 500); watch the bitmap reappear

17) pg.animateUnitClipping(1, 1, 1, 1, 500, 500); wipes texture to bottom right

18) pg.animateUnitClipping(1, 1, 1, 1, 500, 500); restore clip area

19) pg.animateCenter(500, 200 , 0, 1500, "0 2"); //notice the ease in out on the move

20) pg.animateCenter(500,500 , 0, 1500, "2 10"); //notice the bounce



The Next Gem

That's all for the animated GUI resource. This resource is so large there really wasn't enough time or space to fully cover all the applications of this code. Later gems will give more applications for GUI resources.

The next gem will cover the isJugg variable and the AudioManager which allow us to have one script code base that works between TGE and TGEA. It also introduces the Plastic folder, a folder for useful code that you might share between games.
Page«First 1 2 Next»
#21
01/05/2009 (10:36 pm)
I've got a question about using this gem for animated gui elements.

I've put together a 2d game in TGEA using the animated gui elements gem. I've noticed a decent delay as the ui loads all of the animated images. Do you know of a way to precache or preload the animated gui images to prevent the load delay?
#22
01/07/2009 (7:29 am)
Vihn - good question. I do not know of a way right off the top of my head, but I bet that is is possible. I don't think GUI elements have a "preload" flag like datablocks for 3D objects do, but it should be possible to alter the C++ code to allow it.
#23
01/11/2009 (11:27 am)
I know this is probably terrible, but for now I've found that if you add a hidden plastic gui element that references the DML on a previous UI screen then there is no load time if it is referenced again. Therefore, I've added hidden references at startup for now...
#24
03/07/2009 (6:41 am)
I found a coupel thing while intergrating this into 1.8.1

1)remove the existing console method for Getroot()
// > pg animation
/*
ConsoleMethod( GuiControl, getRoot, S32, 2, 2, "returns the Id of the parent canvas.")
{
   GuiCanvas *root = object->getRoot();
   if(root)
      return root->getId();
   return 0;
}
*/
// < pg animation


2) in Step 3 don't forget to add the public: keyword before the new code

3) In renderchildcontrols change
GFX->setCullMode( GFXCullNone );
to the new
GFX->setStateBlock(mDefaultGuiSB);

4) Finally GG has rewritten the GFX layer since this intial release, so Plasticbitmapgui won't compile as is. I have changed our version and once I get some time to test it I will post the new file. Fixing on your own is easy thought look at sky for DML changes, bitmapgui for setbitmap changes and gfxdrawutil for drawBitmapStretchRotated() changes


As of March 7 09 this resource is 1.8.1 compatable
#25
08/08/2009 (11:13 pm)
Download Gone forever? or Possible to get it back? Do the gui's resize correctly?
#26
08/12/2009 (9:42 am)
I've been trying to port the guiPlasticBitmapCtrl.h and guiPlasticBitmapCtrl.cc. I've got it compiling but I think there is a problem with the OnRender Function when the control actually has something to show. It is crashing out with:

> Stronghold_DEBUG.exe!GFXVertexBufferHandleBase::set(GFXDevice * theDevice=0xcdcdcdcd, unsigned int numVerts=4, unsigned int flags=322, unsigned int vertexSize=24, GFXBufferType type=GFXBufferTypeVolatile) Line 15 + 0x13 bytes C++
Stronghold_DEBUG.exe!GFXVertexBufferHandle<GFXVertexPCT>::set(GFXDevice * theDevice=0xcdcdcdcd, unsigned int numVerts=4, GFXBufferType t=GFXBufferTypeVolatile) Line 97 C++
Stronghold_DEBUG.exe!GFXVertexBufferHandle<GFXVertexPCT>::GFXVertexBufferHandle<GFXVertexPCT>(GFXDevice * theDevice=0xcdcdcdcd, unsigned int numVerts=4, GFXBufferType bufferType=GFXBufferTypeVolatile) Line 93 C++
Stronghold_DEBUG.exe!GuiPlasticBitmapCtrl::drawBitmapStretchRotated(GFXTextureObject * texture=0x09e1ff40, const RectI & dstRect={...}, const RectI & srcRect={...}, const ColorI & color={...}, float degrees=0.00000000) Line 466 + 0x16 bytes C++
Stronghold_DEBUG.exe!GuiPlasticBitmapCtrl::onRender(Point2I offset={...}, const RectI & updateRect={...}) Line 428 C++
Stronghold_DEBUG.exe!GuiControl::renderChildControls(Point2I offset={...}, const RectI & updateRect={...}) Line 189 C++
Stronghold_DEBUG.exe!GuiChunkedBitmapCtrl::onRender(Point2I offset={...}, const RectI & updateRect={...}) Line 144 C++
Stronghold_DEBUG.exe!GuiControl::renderChildControls(Point2I offset={...}, const RectI & updateRect={...}) Line 189 C++
#27
08/12/2009 (4:46 pm)
Ok, finished it!
// > pg 
//-----------------------------------------------------------------------------
// GuiPlasticBitmapCtrl.h
// Original GuiAnimatedBitmapCtrl Made by Robert Brower
// Modified by Jeff Bakke for C3 command www.c3command.com
// Modified by Paul Dana for Flash Bios
// Modified again by Paul Dana for Plastic Gems for Torque 1.5 + codebases
// Modified by Jesse Lord for compatibilty to version Torque Game Engine Advance 1.8.1
//-----------------------------------------------------------------------------
#ifndef _GuiPlasticBitmapCtrl_H_
#define _GuiPlasticBitmapCtrl_H_

#ifndef _GUIBITMAPCTRL_H_
#include "gui/controls/guiBitmapCtrl.h"
#endif
// > pg N/a in Jugg

#ifndef _MATERIALLIST_H_
#include "materials/materialList.h"
#endif

// < pg N/a in Jugg

class GuiPlasticBitmapCtrl : public GuiBitmapCtrl
{
public:
   enum
   {
      MaxFrames = 200,
   };

private:
   typedef GuiBitmapCtrl Parent;

protected:
   bool mIs3d;
   S32       mInterval;
   S32       mLastTime;
   S32      mIndex;
   S32      mMaxBitmaps;
   MaterialList   mMaterialList;
   StringTableEntry mDmlFilename;
   StringTableEntry mBitmapNames[MaxFrames];
   GFXTexHandle mTextureHandles[MaxFrames];
   bool mIsPlaying;      
   bool mStopAtEnd;

   F32 mRotation; // degrees counterclockwise from nomrmal
   F32 mDeltaRotation; // if non zero automatically update rotation by this degreee/sec
   S32 mLastDeltaRotationTime; // if non negative then time of last delta rotation

public:
   //creation methods
   DECLARE_CONOBJECT(GuiPlasticBitmapCtrl);
   GuiPlasticBitmapCtrl();
   static void initPersistFields();
   static void consoleInit();

   //Parental methods
   bool onWake();
   void onSleep();
   void loadDml();
   bool isImageFileName(const char *filename);
   void setDML(const char *dmlFileName);
   void setBitmap(S32 index, const char *name);
   void setIs3d(bool temp);
   void setInterval(F32 temp);
   void onPreRender();
   void onRender(Point2I offset, const RectI &updateRect); 

   virtual void onMouseUp(const GuiEvent &event);
   virtual void onMouseDown(const GuiEvent &event);

	void play(bool stopAtEnd);
	void stop();
	void rewind();
	bool isPlaying() {return mIsPlaying;}
	S32  getFrame() {return mIndex;}
	S32 setFrame(S32 index);
	S32  getTotalFrames() {return mMaxBitmaps;}
};

#endif 
// < pg
#28
08/12/2009 (4:47 pm)
// > pg 

#include "console/console.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDevice.h"
#include "gui/shiny/guiPlasticBitmapCtrl.h"
#include "core/stream/fileStream.h"

IMPLEMENT_CONOBJECT(GuiPlasticBitmapCtrl);

GuiPlasticBitmapCtrl::GuiPlasticBitmapCtrl(void)
{
   mBitmapName = StringTable->insert("");
   mDmlFilename = StringTable->insert("");

   for (int i=0; i<50; i++)
   {
      mBitmapNames[i] = StringTable->insert("");
      mTextureHandles[i] = NULL;
   }
   
   startPoint.set(0,0);
   mWrap = false;
   mIs3d = false;
   mInterval = 0;
   mLastTime = 0;
   mMaxBitmaps = 0;
   mIsPlaying = false;
   mStopAtEnd = false;

   mRotation = 0.0f;
   mDeltaRotation = 0.0f;
   mLastDeltaRotationTime =-1; // set when deltaRotation becomes non zero
}

void GuiPlasticBitmapCtrl::setDML(const char *dml)
{
   mMaxBitmaps = 0;
   mDmlFilename = dml;
   if (dStrlen(mDmlFilename))
      loadDml();
}

void GuiPlasticBitmapCtrl::initPersistFields()
{
   Parent::initPersistFields();
   addField("is3d", TypeBool, Offset(mIs3d,GuiPlasticBitmapCtrl));
   //
   removeField("Bitmap");
   addGroup("Plastic");
   addField("Interval", TypeS32, Offset(mInterval,GuiPlasticBitmapCtrl));
   addField("DML", TypeFilename, Offset(mDmlFilename,GuiPlasticBitmapCtrl));
   addField("autoPlay", TypeBool, Offset(mIsPlaying, GuiPlasticBitmapCtrl));
   //
   addField("rotation", TypeF32, Offset(mRotation,GuiPlasticBitmapCtrl));
   addField("deltaRotation", TypeF32, Offset(mDeltaRotation,GuiPlasticBitmapCtrl));
   endGroup("Plastic");
}

static void cAnimatedBitmapSetDML(SimObject *obj, S32, const char **argv)
{
   GuiPlasticBitmapCtrl *ctrl = static_cast<GuiPlasticBitmapCtrl*>(obj);
   ctrl->setDML(argv[2]);
}

static void cAnimatedBitmapSetValue(SimObject *obj, S32, const char **argv)
{
   GuiPlasticBitmapCtrl *ctrl = static_cast<GuiPlasticBitmapCtrl*>(obj);
   ctrl->setValue(dAtoi(argv[2]), dAtoi(argv[3]));
}

static void cAnimatedBitmapSetIs3d(SimObject *obj, S32, const char **argv)
{
   GuiPlasticBitmapCtrl *ctrl = static_cast<GuiPlasticBitmapCtrl*>(obj);
   ctrl->setIs3d(dAtob(argv[2]));
}

static void cAnimatedBitmapSetFPS(SimObject *obj, S32, const char **argv)
{
   GuiPlasticBitmapCtrl *ctrl = static_cast<GuiPlasticBitmapCtrl*>(obj);
   ctrl->setInterval(dAtof(argv[2]));
}


void cGuiPlasticBitmapCtrlPlay(SimObject *obj, S32 argc, const char **argv)
{
	GuiPlasticBitmapCtrl* control = (GuiPlasticBitmapCtrl*)obj;
   bool stopAtEnd = false;
   if (argc >2)
      stopAtEnd = dAtob(argv[2]);
	control->play(stopAtEnd); 
}


void cGuiPlasticBitmapCtrlStop(SimObject *obj, S32 argc, const char **argv)
{
	GuiPlasticBitmapCtrl* control = (GuiPlasticBitmapCtrl*)obj;
	control->stop(); 
}


void cGuiPlasticBitmapCtrlRewind(SimObject *obj, S32 argc, const char **argv)
{
	GuiPlasticBitmapCtrl* control = (GuiPlasticBitmapCtrl*)obj;
	control->rewind(); 
}


bool cGuiPlasticBitmapCtrlIsPlaying(SimObject *obj, S32 argc, const char **argv)
{
	GuiPlasticBitmapCtrl* control = (GuiPlasticBitmapCtrl*)obj;
	return control->isPlaying(); 
}


S32 cGuiPlasticBitmapCtrlGetFrame(SimObject *obj, S32 argc, const char **argv)
{
	GuiPlasticBitmapCtrl* control = (GuiPlasticBitmapCtrl*)obj;
	return control->getFrame(); 
}

S32 cGuiPlasticBitmapCtrlSetFrame(SimObject *obj, S32 argc, const char **argv)
{
	GuiPlasticBitmapCtrl* control = (GuiPlasticBitmapCtrl*)obj;
	return control->setFrame(dAtoi(argv[2])); 
}


S32 cGuiPlasticBitmapCtrlGetTotalFrames(SimObject *obj, S32 argc, const char **argv)
{
	GuiPlasticBitmapCtrl* control = (GuiPlasticBitmapCtrl*)obj;
	return control->getTotalFrames(); 
}
#29
08/12/2009 (4:49 pm)
void GuiPlasticBitmapCtrl::consoleInit()
{
   Con::addCommand("GuiPlasticBitmapCtrl", "setIs3d", cAnimatedBitmapSetIs3d, "GuiPlasticBitmapCtrl.setIs3d(bool)", 3, 3);
   Con::addCommand("GuiPlasticBitmapCtrl", "setFPS", cAnimatedBitmapSetFPS, "GuiPlasticBitmapCtrl.setFPS(F32)", 3, 3);

   Con::addCommand("GuiPlasticBitmapCtrl", "setDML", cAnimatedBitmapSetDML, "GuiPlasticBitmapCtrl.setDML(dmlFilename|"")", 3, 3);

    Con::addCommand("GuiPlasticBitmapCtrl", "setValue",  cAnimatedBitmapSetValue,   "GuiPlasticBitmapCtrl.setValue(xAxis, yAxis)", 4, 4);// > pg	
    Con::addCommand("GuiPlasticBitmapCtrl", "play", cGuiPlasticBitmapCtrlPlay, "control.play([stopAtEnd])", 2, 3);
	Con::addCommand("GuiPlasticBitmapCtrl", "stop", cGuiPlasticBitmapCtrlStop, "control.stop()", 2, 2);
	Con::addCommand("GuiPlasticBitmapCtrl", "rewind", cGuiPlasticBitmapCtrlRewind, "control.rewind()", 2, 2);
	Con::addCommand("GuiPlasticBitmapCtrl", "isPlaying", cGuiPlasticBitmapCtrlIsPlaying, "control.isPlaying()", 2, 2);
	Con::addCommand("GuiPlasticBitmapCtrl", "getFrame", cGuiPlasticBitmapCtrlGetFrame, "control.getFrames()", 2, 2);
	Con::addCommand("GuiPlasticBitmapCtrl", "getTotalFrames", cGuiPlasticBitmapCtrlGetTotalFrames, "control.getTotalFrames()", 2, 2);
	Con::addCommand("GuiPlasticBitmapCtrl", "setFrame", cGuiPlasticBitmapCtrlSetFrame, "control.setFrame(frameIndex)", 3, 3);
}

void GuiPlasticBitmapCtrl::setIs3d(bool temp)
{
   mIs3d = temp;
}

void GuiPlasticBitmapCtrl::setInterval(F32 temp)
{
   mInterval = temp;
}


void GuiPlasticBitmapCtrl::setBitmap(S32 index, const char *name)
{
   if (index < 0 || index >= 50)
      return;
   
   mBitmapNames[index] = StringTable->insert(name);
   if (*mBitmapNames[index])
   {
      mTextureHandles[index].set(mBitmapNames[index], &GFXDefaultGUIProfile, avar("%s() - mTextureHandles[index] (line %d)", __FUNCTION__, __LINE__) );
   }
   else
      mTextureHandles[index] = NULL;
   setUpdate();
}

bool GuiPlasticBitmapCtrl::onWake()
{   
   if (! Parent::onWake())
      return false;
   setActive(true);

   if (dStrlen(mDmlFilename))
      loadDml();
   
   mIndex = 0;
   Parent::setBitmap(mTextureHandles[mIndex], false);
   mLastTime = Sim::getCurrentTime();
   
   return true;
}
void GuiPlasticBitmapCtrl::onSleep()
{

   for(S32 i = 0; i < mMaxBitmaps;i++)
   {
		mTextureHandles[i] = NULL;
   }
   mMaxBitmaps = 0;
   Parent::onSleep();
}

bool GuiPlasticBitmapCtrl::isImageFileName(const char *filename)
{
   char buffer[1025];
   dStrncpy(buffer,filename,1024);
   dStrlwr(buffer);
   const char *p = buffer;

   return  dStrstr( p, ".jpg" ) ||
           dStrstr( p , ".bmp" ) ||
           dStrstr( p, ".png" ) ||
           dStrstr( p, ".gif" ) ;
}
#30
08/12/2009 (4:50 pm)
void GuiPlasticBitmapCtrl::loadDml()
{
   mMaxBitmaps = 0;

   FileStream  *stream = FileStream::createAndOpen( mDmlFilename, Torque::FS::File::Read );
   
   if (stream == NULL)
   {
      return;
   }

   if(isImageFileName(mDmlFilename))
   {
      mMaxBitmaps = 1;
      setBitmap(0, mDmlFilename);
   }
   else
   {
	   mMaterialList.read(*stream);
	   stream->close();
	   
	   delete stream;

	   const Torque::Path  thePath( mDmlFilename);

	   if(!mMaterialList.load(thePath.getPath()))
	   {
		  return;
	   }

	   S32 x;

	   for(x = 0; x < mMaterialList.size(); ++x, ++mMaxBitmaps)
	   {
		  if (x >= 50) break;
		  mTextureHandles[x] = mMaterialList.getMaterial(x);
	   }
   }
}

void GuiPlasticBitmapCtrl::onPreRender() 
{
   S32 thisTime = Sim::getCurrentTime();
   S32 timeDelta = thisTime - mLastTime;
   if (timeDelta > mInterval)
   {
      setUpdate();
   }
}

void GuiPlasticBitmapCtrl::onRender(Point2I offset, const RectI &updateRect) 
{
   S32 thisTime = Sim::getCurrentTime();
   S32 timeDelta = thisTime - mLastTime;
   if (timeDelta > mInterval && mIsPlaying)
   {
      mIndex ++;
      if (mIndex >= mMaxBitmaps)
      {
         if (mStopAtEnd)
         {
            mIndex--;  // stay at end and stop playing...
            stop();
            Con::executef(this,  "onStoppedAtEnd");
         }
         else
         {
            mIndex = 0;         
         }
      }
      mLastTime = thisTime;
   }
   if (mTextureHandles[mIndex])
       Parent::setBitmap(mTextureHandles[mIndex]);

 	 S32 a = (S32)(255 * mAlpha);
    ColorI alphaColor(mColor.red, mColor.green, mColor.blue, a);

   // update rotation?
   if (mDeltaRotation != 0)
   {
       // how much time?
       S32 now = Sim::getCurrentTime();
       F32 factor = 0.0f;
       if (mLastDeltaRotationTime >= 0)
           factor = F32(now - mLastDeltaRotationTime) / 1000.0f;
       mLastDeltaRotationTime = now;
       
       mRotation += mDeltaRotation * factor;
   }

   if (mTextureHandles[mIndex]) 
   { 
      GFX->getDrawUtil()->clearBitmapModulation();
      GFX->getDrawUtil()->setBitmapModulation(alphaColor);

		if(mWrap)
		{
 			GFXTextureObject* texture = mTextureHandles[mIndex];
			RectI srcRegion;
			RectI dstRegion;
			float xdone = ((float)getExtent().x/(float)texture->mBitmapSize.x)+1;
			float ydone = ((float)getExtent().y/(float)texture->mBitmapSize.y)+1;

			int xshift = startPoint.x%texture->mBitmapSize.x;
			int yshift = startPoint.y%texture->mBitmapSize.y;
			for(int y = 0; y < ydone; ++y)
				for(int x = 0; x < xdone; ++x)
				{
		 			srcRegion.set(0,0,texture->mBitmapSize.x,texture->mBitmapSize.y);
  					dstRegion.set( ((texture->mBitmapSize.x*x)+offset.x)-xshift,
								      ((texture->mBitmapSize.y*y)+offset.y)-yshift,
								      texture->mBitmapSize.x,
								      texture->mBitmapSize.y);
        	   GFX->getDrawUtil()->drawBitmapStretchRotated(texture, dstRegion, srcRegion, alphaColor, mRotation);
            }

	}
	else
      {
         RectI rect(offset, getExtent());
         GFXTextureObject* texture = mTextureHandles[mIndex];
         RectI subRegion( 0, 0, texture->mBitmapSize.x, texture->mBitmapSize.y );
		 GFX->getDrawUtil()->drawBitmapStretchRotated(texture, rect, subRegion, alphaColor, mRotation);
      }
   } 
   else 
   { 
 	   // if we have NO bitmap..then just draw a white squre in the current color...
      RectI rect(offset.x, offset.y, getExtent().x, getExtent().y);
      GFX->getDrawUtil()->drawRectFill(rect,mColor);
   } 

   // if we are "playing" then rendering causes the next rendering...
   if(mIsPlaying)
		setUpdate();

	//render childern
	renderChildControls(offset, updateRect);
}
#31
08/12/2009 (4:52 pm)
void GuiPlasticBitmapCtrl::play(bool stopAtEnd)
{
	mIsPlaying = true;
   mStopAtEnd = stopAtEnd;
	mLastTime = Platform::getVirtualMilliseconds();
	setUpdate();
}


S32 GuiPlasticBitmapCtrl::setFrame(S32 index)
{
	S32 ret = mIndex;
	if (index >= 0 && index < mMaxBitmaps)
	{
	   mIndex = index;
	   setUpdate();
	}

	return ret;
}

void GuiPlasticBitmapCtrl::stop()
{
	mIsPlaying = false;
}


void GuiPlasticBitmapCtrl::rewind()
{
	mIndex = 0;
	if(mIsPlaying)
		mLastTime = Platform::getVirtualMilliseconds();
	setUpdate();
}

void GuiPlasticBitmapCtrl::onMouseUp(const GuiEvent &event)
{
   //if this control is a dead end, make sure the event stops here
   if ( !mVisible || !mAwake || mAlpha <= 0.0)
      return;

    UTF8 *pointStr = Con::getArgBuffer(128);
    dSprintf(pointStr,128,"%d %d",event.mousePoint.x,event.mousePoint.y);

    Con::executef(this,"onMouseUp",pointStr);
}

void GuiPlasticBitmapCtrl::onMouseDown(const GuiEvent &event)
{
   //if this control is a dead end, make sure the event stops here
   if ( !mVisible || !mAwake || mAlpha <= 0.0)
      return;

    UTF8 *pointStr = Con::getArgBuffer(128);
    dSprintf(pointStr,128,"%d %d",event.mousePoint.x,event.mousePoint.y);

    Con::executef(this,"onMouseDown",pointStr);
}


// < pg
#32
08/15/2009 (10:51 am)
I went to port it to T3D. If all I ported was the Ease code and the Animated Gui it works great. The thread control code they provided is a different story. Since the code no longer has a bool forward it makes it difficult to call some of the commands. So that would need to be figured out to provide ease support to threading but other then that it works great!
#33
08/15/2009 (11:40 am)
The same function as bool forward is there in Torque3D Beta 5, except its an F32 IIRC. So its possible to replace all occurrences of forward. I'm not on my dev box, and can't check what the variable name is, but if you diff Beta 4 and Beta 5, two new vars were added, one of them being the one you can use instead of forward. There are also checks for forward / backward animation in various places to give you an idea about how to use the value for such a check.
#34
08/21/2009 (9:04 pm)
naa looks like taking out bool forward happened before beta 4. Not sure how far back I would have to go yet since I only diff'ed Beta 5 and 4.
#35
10/08/2009 (9:21 pm)
Would someone from Plastic Game mind if I put up a resource for the T3D on this? Just asking permission before I do it.
#36
02/18/2010 (2:24 pm)
Download i still available at http://www.torquepowered.com/static/pg/resource/14865.pg04_gui.zip
#37
09/22/2011 (3:58 am)
..any chance there is a T3D 1.1 resource on this ?
#38
03/29/2012 (11:38 am)
Implementing in T3D 1.2

errors

1>f:plastic_gamesshowofftorqueenginesourceguicoreguicontrol.cpp(3063): error C2137: empty character constant
1>f:plastic_gamesshowofftorqueenginesourceguicoreguicontrol.cpp(3068): error C2137: empty character constant
1>f:plastic_gamesshowofftorqueenginesourceguicoreguicontrol.cpp(3079): error C2137: empty character constant
1>f:plastic_gamesshowofftorqueenginesourceguicoreguicontrol.cpp(3093): error C2137: empty character constant

for expressions like
durValue[0] != ''

Empty string are not allowed in VS 2010,
do durValue[0] != '�'

To test for the null char


In the Plastic gui the variable startpoint must be renamed mStartPoint
Page«First 1 2 Next»