Game Development Community

Script-Defined ImageMaps Code

by Charlie Patterson · in Torque Game Builder · 05/08/2012 (1:53 pm) · 7 replies

Note: "blogs" and "resources" will not allow a post over 3500 chars. This is 25K!

On a recent blog, I was asked for the code for what I call "script-defined" image maps. Here it is...


(Also, if you enjoy this resource, please consider "paying forward" with a click on my game's like button, here: facebook.com/RednecksVsRobots. Gonna look for some funding soon and likes will help, I hope!)

----------

How to install the "script-defined" image map.

Check out the "documentation"

Please see the tech demo to get an idea how this works.

Patch, patch, patch! Literally


While there is suprisingly little code to this feature, the code spans almost every components of Torque AND your project. This is because you need the script-defined ability in both the TGB (game editor) and in your game. Further, you need the ability before your main.cs is called. So this code has to be "embedded" early than that. And the TGB editor is written partially in C++ and partially in TorqueScript. You'll need to patch:

* the Torque engine source (and compile for the game builder and your game)
* the TGB script code
* your project's "common" directories (for each project that will use it)
* your project's "managed" directories (for each project that will use it)

Phew!

code patches

I'm going to put this here as a series of patches. This seems to be the best bet, and I've gotten good feedback in the past that GnuWin32 Patch works with these. (Then again, I've had someone have trouble.) Each patch affects multiple files, so you need to patch at the appropriate directory! I'm no patch expert, unfortunately. I'm making these using Tortoise Subversion's "unified diff" feature...

patch for the torque engine's "engine/source" directory

I'm using TGB 1.7.5, but I think newer and probably older will work find:
Index: t2dImageMapDatablock.cc
===================================================================
--- t2dImageMapDatablock.cc	(revision 312)
+++ t2dImageMapDatablock.cc	(revision 337)
@@ -67,6 +67,8 @@
                 { t2dImageMapDatablock::T2D_IMAGEMAP_ERROR_FAIL_SEPERATOR_COLOR,        "Failed to get separator color!" },
                 { t2dImageMapDatablock::T2D_IMAGEMAP_ERROR_NO_FRAMES,                   "No frames have been acquired!" },
                 { t2dImageMapDatablock::T2D_IMAGEMAP_ERROR_INVALID_FRAME_COUNT,         "Number of frames acquired differs from that specified!" },
+                { t2dImageMapDatablock::T2D_IMAGEMAP_ERROR_INVALID_FRAME_DEFINITION,    "At least one frame index missing while building in SCRIPTED mode!" },
+                { t2dImageMapDatablock::T2D_IMAGEMAP_ERROR_NO_DEFINEMAP_METHOD,         "SCRIPTED mode used but method 'onDefineImageMapFrames' not defined!" },
                 { t2dImageMapDatablock::T2D_IMAGEMAP_ERROR_NO_LINKED_IMAGEMAPS,         "No LINK mode image-maps selected!" },
                 { t2dImageMapDatablock::T2D_IMAGEMAP_ERROR_NOT_ENOUGH_LINKED_IMAGEMAPS, "Not enough LINK mode image-maps selected; Must be at least two!" },
                 { t2dImageMapDatablock::T2D_IMAGEMAP_ERROR_INVALID_LINKED_IMAGEMAP,     "Invalid LINK mode image-map selected!" },
@@ -96,10 +98,11 @@
 //-----------------------------------------------------------------------------
 static EnumTable::Enums imageModeLookup[] =
                 {
-                { t2dImageMapDatablock::T2D_IMAGEMODE_FULL, "FULL" },
-                { t2dImageMapDatablock::T2D_IMAGEMODE_KEY,  "KEY" },
-                { t2dImageMapDatablock::T2D_IMAGEMODE_CELL, "CELL" },
-                { t2dImageMapDatablock::T2D_IMAGEMODE_LINK, "LINK" },
+                { t2dImageMapDatablock::T2D_IMAGEMODE_FULL,     "FULL" },
+                { t2dImageMapDatablock::T2D_IMAGEMODE_KEY,      "KEY" },
+                { t2dImageMapDatablock::T2D_IMAGEMODE_CELL,     "CELL" },
+                { t2dImageMapDatablock::T2D_IMAGEMODE_LINK,     "LINK" },
+                { t2dImageMapDatablock::T2D_IMAGEMODE_SCRIPTED, "SCRIPTED" },
                 };
 
 static EnumTable imageModeTable(sizeof(imageModeLookup) / sizeof(EnumTable::Enums), &imageModeLookup[0]);
@@ -249,7 +252,8 @@
                                                 mLockReference(0),
                                                 mTexturesLoaded(false),
 												mImageMapDumpComplete(false),
-												mForce16bit(0)
+												mForce16bit(0),
+                                                mOnDefineFramesScriptRunning(false)
 {
     // Set Vector Associations.
     VECTOR_SET_ASSOCIATION( mVecFrameIn );
@@ -351,9 +355,6 @@
         mLockReference = 1;
     }
 
-    // Compile ImageMap.
-    compileImageMap();
-
     bool valid = getIsValid();
 
     // Call Parent.
@@ -362,6 +363,9 @@
 
     setIsValid(valid);
 
+    // Compile ImageMap.
+    compileImageMap();
+
     // Linked Mode Image-Map?
  	 if ( getImageMapMode() != t2dImageMapDatablock::T2D_IMAGEMODE_LINK )
  	 {
@@ -741,6 +745,13 @@
 
         } break;
 
+        // Defined with Script-Callbacks
+        case T2D_IMAGEMODE_SCRIPTED:
+        {
+            // Calculate Map.
+            CHECK_IMAGEMAP_ERROR( calculateScriptedImageMap() );
+
+        } break;
     };
 
 	//Luma: don't unload the bitmap if we are also preloading... otherwise we end up loading twice (when we load textures for preloading)... SLOW!!!!!!!!!
@@ -1126,7 +1137,42 @@
     return true;
 }
 
+//------------------------------------------------------------------------------
+// Script-Defined ImageMap.
+//
+// Use TorqueScript to define frames.
+//------------------------------------------------------------------------------
+bool t2dImageMapDatablock::calculateScriptedImageMap( void )
+{
+    if ( ! Namespace::global()->lookup(StringTable->insert("onDefineImageMapFrames")) ) {
+        THROW_IMAGEMAP_ERROR( T2D_IMAGEMAP_ERROR_NO_DEFINEMAP_METHOD );
+    }
 
+    // the user script will be called now.
+    // they should call "addFrame(frameIndex, left, top, width, height)" for each frame of this image map before returning from their script
+    mOnDefineFramesScriptRunning = true;
+    Con::executef(3, "onDefineImageMapFrames", getName(), getSrcBitmapName());
+    mOnDefineFramesScriptRunning = false;
+
+    // TODO: need to sort frames? the frame index is a field in each frame, so array order may not matter
+    dQsort( mVecFrameIn.address(), mVecFrameIn.size(), sizeof(cFrameIn), frameInFrameNumberSort );
+
+    for (int i = 0; i < mVecFrameIn.size(); i++) {
+        if (mVecFrameIn[i].mFrame != i) {
+            THROW_IMAGEMAP_ERROR( T2D_IMAGEMAP_ERROR_INVALID_FRAME_DEFINITION );
+        }
+    }
+
+    // Return No Error.
+    return true;
+}
+
+S32 QSORT_CALLBACK t2dImageMapDatablock::frameInFrameNumberSort(const void* a, const void* b)
+{
+    // Ascending frame-number sort.
+    return ((*((cFrameIn*)a)).mFrame) - ((*((cFrameIn*)b)).mFrame);
+}
+
 //------------------------------------------------------------------------------
 // Link ImageMap.
 //
@@ -2452,8 +2498,38 @@
 }
 
 
+//-----------------------------------------------------------------------------
+// Add a Frame in SCRIPTED Mode.
+//-----------------------------------------------------------------------------
+ConsoleMethod(t2dImageMapDatablock, addFrame, void, 7, 7, "(frameIndex, x, y, width, height) Define one frame for an image map.n"
+			  "This may only be called while inside the method 'onDefineImageMapFrames'n"
+			  "@param frameIndex Index of this frame.  Can be in any order but final result should define frames 0..N-1 where N is the number of framesn"
+			  "@param x X value of the top left pixel of this frame within the bitmapn"
+			  "@param y Y value of the top left pixel of this frame within the bitmapn"
+			  "@param width Horizontal count of pixel within this framen"
+			  "@param height Vertical count of pixel within this framen"
+			  "@return No return value.")
+{
+    // only allow calls to this method when we are running from onDefineImageMapFrames user method.
+    if (!object->mOnDefineFramesScriptRunning) {
+        Con::warnf("t2dImageMapDatablock::addFrame() - cannot add frame unless in 'onDefineImageMapFrames' callback!" );
+        return;
+    }
 
+    S32 frame = dAtoi(argv[2]);
+    S32 x = dAtoi(argv[3]);
+    S32 y = dAtoi(argv[4]);
+    S32 width = dAtoi(argv[5]);
+    S32 height = dAtoi(argv[6]);
 
+    t2dImageMapDatablock::cFrameIn frameIn;
+    frameIn.mFrame = frame;
+    frameIn.mOriginalPixelArea.setArea( x, y, width, height );
+
+    object->mVecFrameIn.push_back( frameIn );
+}
+
+
 //------------------------------------------------------------------------------
 
 void t2dImageMapDatablock::packData(BitStream* stream)
Index: t2dImageMapDatablock.h
===================================================================
--- t2dImageMapDatablock.h	(revision 312)
+++ t2dImageMapDatablock.h	(revision 337)
@@ -30,6 +30,7 @@
     // QSorts.
     // --------------------------------------------------------------------------------------
     static S32 QSORT_CALLBACK frameOutFrameNumberSort(const void* a, const void* b);
+    static S32 QSORT_CALLBACK frameInFrameNumberSort(const void* a, const void* b);
     static S32 QSORT_CALLBACK frameInHeightSort(const void* a, const void* b);
 
 
@@ -62,6 +63,8 @@
         T2D_IMAGEMAP_ERROR_NO_LINKED_IMAGEMAPS          = 21,
         T2D_IMAGEMAP_ERROR_NOT_ENOUGH_LINKED_IMAGEMAPS  = 22,
         T2D_IMAGEMAP_ERROR_INVALID_LINKED_IMAGEMAP      = 23,
+        T2D_IMAGEMAP_ERROR_INVALID_FRAME_DEFINITION     = 24,
+        T2D_IMAGEMAP_ERROR_NO_DEFINEMAP_METHOD          = 25,
     };
 
 
@@ -86,6 +89,7 @@
         T2D_IMAGEMODE_KEY,
         T2D_IMAGEMODE_CELL,
         T2D_IMAGEMODE_LINK,
+        T2D_IMAGEMODE_SCRIPTED,
 
         T2D_IMAGEMODE_INVALID,
     };
@@ -253,7 +257,10 @@
 	bool						mForce16bit;				//Luma: control 16bit settings on a per-bitmap basis
 
     /// Frame Packing.
+public:
+    // expose the frame import variable to Console Methods (public)
     static typeFrameIn          mVecFrameIn;
+private:
     typeFrameOut                mVecFrameOut;
     typeTexturePage             mVecTexturePage;
     static typeFrameIn          mVecFrameOverflow;
@@ -263,13 +270,20 @@
     F32                         mTotalTimeTaken;
     U32                         mIterations;
 
+public:
 
+    // private but used from ConsoleMethod
+    bool                        mOnDefineFramesScriptRunning;
+
+private:
+
     /// Calculate Image Map Frames.
     bool calculateFrames( void );
     bool calculateFullImageMap( void );
     bool calculateCellImageMap( void );
     bool calculateKeyImageMap( void );
     bool calculateLinkImageMap( void );
+    bool calculateScriptedImageMap( void );
     bool calculateSrcFrames( void );
     bool validateFrames( void );
     bool calculateDstFrames( void );

patch for Torque's main directory (affects "tgb" and "tools")


Index: imageMapSelect.ed.gui
===================================================================
--- imageMapSelect.ed.gui	(revision 331)
+++ imageMapSelect.ed.gui	(revision 332)
@@ -214,6 +214,10 @@
       if (%filter & $IMAGE_MAP_FILTER_KEY)
          if (%datablock.imageMode $= "KEY")
             %includeThisImageMap = true;
+
+      if (%filter & $IMAGE_MAP_FILTER_SCRIPTED)
+         if (%datablock.imageMode $= "SCRIPTED")
+            %includeThisImageMap = true;
       
       if (%includeThisImageMap)
       {
Index: main.cs
===================================================================
--- main.cs	(revision 331)
+++ main.cs	(revision 332)
@@ -11,10 +11,11 @@
 exec("./gui/AnimationBuilder.ed.gui");
 exec("./gui.ed.cs");
 
-$IMAGE_MAP_FILTER_FULL = BIT(0);
-$IMAGE_MAP_FILTER_CELL = BIT(1);
-$IMAGE_MAP_FILTER_LINK = BIT(2);
-$IMAGE_MAP_FILTER_KEY  = BIT(3);
+$IMAGE_MAP_FILTER_FULL      = BIT(0);
+$IMAGE_MAP_FILTER_CELL      = BIT(1);
+$IMAGE_MAP_FILTER_LINK      = BIT(2);
+$IMAGE_MAP_FILTER_KEY       = BIT(3);
+$IMAGE_MAP_FILTER_SCRIPTED  = BIT(4);
 
 function initializeAnimationEditor()
 {
@@ -64,7 +65,7 @@
 function AnimationBuilder::createAnimation(%this)
 {
    // we want celled and linked image maps
-   %filter = $IMAGE_MAP_FILTER_CELL | $IMAGE_MAP_FILTER_LINK | $IMAGE_MAP_FILTER_KEY;
+   %filter = $IMAGE_MAP_FILTER_CELL | $IMAGE_MAP_FILTER_LINK | $IMAGE_MAP_FILTER_KEY | | $IMAGE_MAP_FILTER_SCRIPTED;
    ImageMapSelectGui.getImageMap("Select Material", %filter, %this @ ".newAnimation");
 }
 
Index: editorSettings.ed.cs
===================================================================
--- editorSettings.ed.cs	(revision 331)
+++ editorSettings.ed.cs	(revision 332)
@@ -32,6 +32,7 @@
       ImageEditorComImageMode.add("FULL", 0);
       ImageEditorComImageMode.add("CELL", 1);
       ImageEditorComImageMode.add("KEY", 3);
+      ImageEditorComImageMode.add("SCRIPTED", 4);
    } else
    {
       ImageEditorComImageMode.add("LINK", 2);
@@ -115,7 +116,19 @@
          %imageMap.allowUnload = %imageAllowUnload;
          %imageMap.force16Bit = %imageForce16Bit;         
          %imageMap.imageMode = %imageMode;
-      
+         
+      case "SCRIPTED":
+         
+         %imageMap.setName(%imageName);
+         %imageMap.imageName = %image;
+         %imageMap.filterMode = %imageFilterMode;
+         %imageMap.filterPad = %imageFilterPad;
+         %imageMap.preferPerf = %imagePreferPerf;
+         %imageMap.preload = %imagePreload;
+         %imageMap.allowUnload = %imageAllowUnload;
+         %imageMap.force16Bit = %imageForce16Bit;         
+         %imageMap.imageMode = %imageMode;
+
       case "CELL":
       
          %cellCountX = ImageEditorCellCountX.getValue();
@@ -307,8 +320,12 @@
    } else if(%imageMode $= "KEY")
    {
       %imageMode = 3;
-   } else
+   } else if (%imageMode $= "SCRIPTED")
    {
+      %imageMode = 4;
+   }
+   else
+   {
       %imageMode = 2;
    }
    
@@ -358,6 +375,10 @@
          ImageEditorCellOptionsPanel.setVisible(false);
          ImageEditorLinkOptionsPanel.setVisible(false);
          
+      case "SCRIPTED":
+         ImageEditorCellOptionsPanel.setVisible(false);
+         ImageEditorLinkOptionsPanel.setVisible(false);
+
       case "CELL":
          loadImageMapCellSettings(ImageEditor.selectedImage);
          
Index: editorPreviewWindow.ed.cs
===================================================================
--- editorPreviewWindow.ed.cs	(revision 331)
+++ editorPreviewWindow.ed.cs	(revision 332)
@@ -264,7 +264,7 @@
          %srcMousePosY = trimAfter(%srcMousePosY, ".");
       
          ImageEditorPreviewSrcPosition.setText("X =" SPC %srcMousePosX SPC "Y =" SPC %srcMousePosY);  
-      } else if(ImageEditor.imageMode $= "CELL" || ImageEditor.imageMode $= "KEY")
+      } else if(ImageEditor.imageMode $= "CELL" || ImageEditor.imageMode $= "KEY" || ImageEditor.imageMode $= "SCRIPTED" )
       {
          // this must be a cell image, we have to take the borders
          // and padding into account to calc where our mouse is
@@ -572,7 +572,7 @@
       {
          %this.clearPreview();
          %cleared = true;
-      } else if(%imageMode $= "CELL" || %imageMode $= "LINK" || %imageMode $= "KEY")
+      } else if(%imageMode $= "CELL" || %imageMode $= "LINK" || %imageMode $= "KEY" || %imageMode $= "SCRIPTED" )
       {
          // if our image mode is the same and we're in cell mode
          // then check if our previous frame count is greater
@@ -625,7 +625,7 @@
       
          %this.cleanUp.add(%this.sprite);
       }
-   } else if(%imageMode $= "CELL" || %imageMode $= "LINK" || %imageMode $= "KEY")
+   } else if(%imageMode $= "CELL" || %imageMode $= "LINK" || %imageMode $= "KEY" || %imageMode $= "SCRIPTED" )
    {
       // set the size text
       ImageEditorPreviewImageSize.setText(getWord(%srcSize, 0) SPC "x" SPC getWord(%srcSize, 1));
Index: sceneView.ed.cs
===================================================================
--- sceneView.ed.cs	(revision 331)
+++ sceneView.ed.cs	(revision 332)
@@ -151,7 +151,7 @@
          case "t2dStaticSprite":
                %object = %control.getSceneObject();
                %imageMode = %datablockName.getImageMapMode();
-               if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") )
+               if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY")|| (%imageMode $= "SCRIPTED") )
                {
                   %currentFrame = %object.getFrame();
                   levelEditorStaticSpriteTool.setImageMap( %datablockName, %currentFrame );   
Index: sideBarCreate.ed.cs
===================================================================
--- sideBarCreate.ed.cs	(revision 331)
+++ sideBarCreate.ed.cs	(revision 332)
@@ -404,7 +404,7 @@
          {
             %object = %this.getSceneObject();
             %imageMode = %datablockName.getImageMapMode();
-            if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") )
+            if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") || (%imageMode $= "SCRIPTED") )
             {
                %currentFrame = %object.getFrame();
                %totalFrames  = %datablockName.getFrameCount();
Index: objectLibraryBaseType.ed.cs
===================================================================
--- objectLibraryBaseType.ed.cs	(revision 331)
+++ objectLibraryBaseType.ed.cs	(revision 332)
@@ -201,7 +201,7 @@
       case "t2dStaticSprite":
          %object = %this.button.getSceneObject();
          %imageMode = %datablockName.getImageMapMode();
-         if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") )
+         if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") || (%imageMode $= "SCRIPTED") )
          {
             %currentFrame = %object.getFrame();
             %totalFrames  = %datablockName.getFrameCount();
@@ -266,7 +266,7 @@
       case "t2dStaticSprite":
          %object = %this.button.getSceneObject();
          %imageMode = %datablockName.getImageMapMode();
-         if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") )
+         if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") || (%imageMode $= "SCRIPTED") )
          {
             %currentFrame = %object.getFrame();
             %totalFrames  = %datablockName.getFrameCount();
@@ -442,7 +442,7 @@
          //
             %object = %this.getSceneObject();
             %imageMode = %datablockName.getImageMapMode();
-            if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") )
+            if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") || (%imageMode $= "SCRIPTED") )
                %currentFrame = %object.getFrame();
             else
                %currentFrame = 0;
Index: staticSprites.ed.cs
===================================================================
--- staticSprites.ed.cs	(revision 331)
+++ staticSprites.ed.cs	(revision 332)
@@ -61,7 +61,7 @@
 
          %caption = "";
          %imageMode = %object.getImageMapMode();
-         if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") )
+         if( (%imageMode $= "CELL") || (%imageMode $= "LINK") || (%imageMode $= "KEY") || (%imageMode $= "SCRIPTED") )
          {
             %totalFrames  = %object.getFrameCount();
             %caption = "1/" @ %totalFrames;
Index: t2dProjectPersistence.ed.cs
===================================================================
--- t2dProjectPersistence.ed.cs	(revision 331)
+++ t2dProjectPersistence.ed.cs	(revision 332)
@@ -223,6 +223,9 @@
 function T2DProject::LoadProjectData( %this )
 {
    // Managed File Paths
+   // YEP - temp
+   %loadersFile = expandFilename("^game/managed/loaders.cs");
+
    %resFile = expandFilename("^game/managed/resources.cs");
    %dbFile = expandFilename("^game/managed/datablocks.cs");
    %persistFile = expandFilename("^game/managed/persistent.cs");
@@ -264,8 +267,15 @@
       
    // Mark Set Valid   
    %this.createValidatorToken( $dependentResourceGroup, "Resources" );
-   
+
+   // YEP temporary
    //---------------------------------------------------------------------------
+   // Loader Methods
+   //---------------------------------------------------------------------------
+   if ( isFile( %loadersFile ) || isFile( %loadersFile @ ".dso" ) )
+      exec( %loadersFile );
+
+   //---------------------------------------------------------------------------
    // Managed Datablocks
    //---------------------------------------------------------------------------
    if ( isFile( %dbFile ) || isFile( %dbFile @ ".dso" ) )

your project's "common" directory

This code finds and executes a managed file of "loaders," before it loads any images, animations, or whatever. The idea is that is that these loaders could help Torque load the rest of the managed files. In our case, they help load the "script-defined" imageMaps.

Index: projectManagement.cs
===================================================================
--- projectManagement.cs	(revision 319)
+++ projectManagement.cs	(revision 336)
@@ -15,6 +15,7 @@
 function _initializeProject()
 {
    // Managed File Paths
+   %loaderFile = expandFilename("game/managed/loaders.cs");
    %dbFile = expandFilename("game/managed/datablocks.cs");
    %persistFile = expandFilename("game/managed/persistent.cs");
    %brushFile = expandFilename("game/managed/brushes.cs");   
@@ -59,6 +60,12 @@
    }
 
    //---------------------------------------------------------------------------
+   // Managed (Datablock) Loaders
+   //---------------------------------------------------------------------------
+   if ( isFile( %loaderFile ) || isFile( %loaderFile @ ".dso" ) )
+      exec( %loaderFile );
+     
+   //---------------------------------------------------------------------------
    // Managed Datablocks
    //---------------------------------------------------------------------------
    if ( isFile( %dbFile ) || isFile( %dbFile @ ".dso" ) )

my personal "loaders.cs" file

This file should go in your managed directory. In my case, it knows how to read XML output in Texture Packer's "generic XML" mode.

// loaders are code blocks that install before managed objects.
// managed objects are loaded by the game, but also by the editor, so this is the place
// to define methods (and global variables etc) that will help load your managed objects.

exec("~/gameScripts/yep-util.cs");

function onDefineImageMapFrames(%imageMap, %bitmapPath)
{
   echo("onDefineImageMapFrames: ", %imageMap, " from ", %bitmapPath);
   $onDefineImageMapFrames::TPFrameNumber = 0;
   loadTexturePackerXMLImageMapAtlas(%imageMap, %bitmapPath);
}

function loadTexturePackerXMLImageMapAtlas(%imageMap, %bitmapPath)
{
   %imageMapAtlasName = filePath(%bitmapPath) @ "/" @ fileBase(%bitmapPath) @ ".xml";
   %xml = new SimXMLDocument();
   if( !%xml.loadFile( %imageMapAtlasName ) ) {
      error("loadTexturePackerXMLImageMapAtlas: could not find atlas '", %imageMapAtlasName, "'.  skipping.");
      return;
   }

   if (! %xml.pushFirstChildElement( "TextureAtlas" )) {
      error("loadTexturePackerXMLImageMapAtlas: did not find 'TextureAtlas' element in '", %imageMapAtlasName, "'.  skipping.");
      return;
   }

   YepUtil::callPerXMLChild(%xml, "sprite", 0, loadTexturePackerXMLSprite, %imageMap);

   %xml.delete();
}

function loadTexturePackerXMLSprite(%xml, %imageMap)
{
   %name = %xml.attribute("n");
   %x = %xml.attribute("x");
   %y = %xml.attribute("y");
   %width = %xml.attribute("w");
   %height = %xml.attribute("h");

   if (%name $= "") {
      error("loadTexturePackerXMLSprite: expected to find attribute for name of image but found '", %name, "'.  skipping.");
      return;
   }

   if (%x $= "" || %y $= "") {
      error("loadTexturePackerXMLSprite: expected to find attributes for top, left of image x and y but found '", %x, "' and '", %y, "'.  skipping.");
      return;
   }

   if (%width $= "" || %height $= "") {
      error("loadTexturePackerXMLSprite: expected to find attributes for width and height of image but found '", %width, "' and '", %height, "'.  skipping.");
      return;
   }

   // we want the frame "names" so store them in the ImageMap.
   // we'll use an (dynamic field) array instead of anything allocated, and then
   // it will automatically be deleted when the ImageMap is.
   %imageMap.frameNames[$onDefineImageMapFrames::TPFrameNumber] = %name;

   %imageMap.addFrame($onDefineImageMapFrames::TPFrameNumber, %x, %y, %width, %height);
   $onDefineImageMapFrames::TPFrameNumber++;
}

// next function patched in from a utility script I create, but it should work here as a one-off:
// at a given level in the xml stack (Torque style), run "%object.callback(%xml, %param1)" for each element
function YepUtil::callPerXMLChild(%xml, %elementName, %object, %callback, %param1, %param2)
{
   %another = %xml.pushFirstChildElement( %elementName );
   %atleastOne = %another;
   while(%another) {
      if (!isObject(%object))
         call(%callback, %xml, %param1, %param2);
      else
         %object.call(%callback, %xml, %param1, %param2);
      %another = %xml.nextSiblingElement( %elementName );
   }
   if (%atleastOne)
      %xml.popElement();
}

#1
05/11/2012 (5:55 pm)
@Charlie, this works a treat thank you. I have adapted it to allow the playing of animations from the sprite sheet and handle the situation where the animation frames are different sizes.
#2
05/11/2012 (5:56 pm)
These are the code changes to support the playing of different sized animation frames from a TexturePacker sprite sheet (only tested with iTorque2D).

Part 1

t2dAnimatedSprite.h

Add the following boolean
bool                            mAutoSize;

Part 2

t2dAnimatedSprite.cc

Change the default constructor:

t2dAnimatedSprite::t2dAnimatedSprite() :    T2D_Stream_HeaderID(makeFourCCTag('2','D','A','S')),
                                            mAnimationCallbackComplete(true),
                                            mFrameChangeCallback(false),
                                            mAutoSize(false)

Part 3

t2dAnimatedSprite.cc

Add a console method to switch on auto sizing.

//-----------------------------------------------------------------------------
// Auto set the size of the current frame based on the original frame size
// in the image map.
//-----------------------------------------------------------------------------
ConsoleMethod(t2dAnimatedSprite, setAutoSize, void, 3, 3, "(bool enabled) - Auto set the size of the current frame based on the original frame size.n"
              "@param enabled Whether or not to switch on auto sizing of the animation frame.n"
              "@return No return value.")
{
   
    object->mAutoSize = dAtob(argv[2]);
}
#3
05/11/2012 (5:57 pm)
Part 4

t2dAnimatedSprite.cc

Find the method integrateObject

Add the auto sizing code after the frame change callback code.

// Are we using frame-change callback and the frame has changed?
        if ( mFrameChangeCallback && frameChanged )
        {
            // Yes ...

            // Argument Buffer.
            static char argBuffer[16];
            // Format Event-Modifier Buffer.
            dSprintf(argBuffer, 16, "%d", mAnimationController.getCurrentFrame() );
            // Call Scripts.
            if( isMethod( "onFrameChange" ) )
               Con::executef(this, 2, "onFrameChange", argBuffer);
        }

        // Are we auto-sizing?
        if (mAutoSize && frameChanged ) {
            // Set the size to the frame.
            const t2dImageMapDatablock::cFramePixelArea& pixelArea =
                        mAnimationController.getCurrentFramePixelArea();
            t2dVector size = t2dVector(pixelArea.mWidth, pixelArea.mHeight);
            setSize(size);
        }
#4
05/11/2012 (5:59 pm)
Part 5

Usage:

1. Use Charlie's resource for scripted image maps.
2. Add the animation changes.
3. Define your image map, animation block etc:

new t2dImageMapDatablock(elviraEmotionImageMap) {
	  	imageName = "~/data/images/scripted/elvira-emotion" @ $retinaIpad;
	  	imageMode = "SCRIPTED";
	  	frameCount = "-1";
	  	filterMode = "SMOOTH";
	  	filterPad = "0";
	  	preferPerf = "1";
	  	cellRowOrder = "1";
	  	cellOffsetX = "0";
	  	cellOffsetY = "0";
	  	cellStrideX = "0";
	  	cellStrideY = "0";
	  	cellCountX = "-1";
	  	cellCountY = "-1";
	  	cellWidth = "2048";
	  	cellHeight = "2048";
	  	preload = "1";
	  	allowUnload = "1";
	  	compressPVR = "0";
	  	optimised = "1";
	  	force16bit = $FORCE_16BIT_MENU_LEVEL;
    };

   new t2dAnimationDatablock(elviraEmotionAnimationDataBlock) {
	  imageMap = "elviraEmotionImageMap";
	  animationFrames = "0 1 2 3 4 5 6 7 8";
	  animationTime = "1";
	  animationCycle = "1";
	  randomStart = "0";
	  startframe = "0";
	  pingPong = "0";
	  playForward = "1";
   };
   new t2dSceneObjectDatablock(elviraEmotionAnimation) {
	  animationName = "elviraEmotionAnimationDataBlock";
   };

4. Create a t2dAnimatedSprite as per usual.

%this.elvira = new t2dAnimatedSprite() {
		config = "elviraEmotionAnimation";
		scenegraph = %this;
		position = "0 0";
		layer = 0;
	};

5. Switch on the auto sizing for the animation.

%this.elvira.setAutoSize(true);

That's it!
#5
05/11/2012 (6:41 pm)
@Charlie - This is an outstanding resource. I have wanted to extend the art pipeline for our 2D tech for a long time, mainly to support more formats and reduce content processing time. Importing packed textures was first on the hit list, so I highly encourage others to follow your lead.

@Scott - As I said in your thread, great addition. It's only natural to get animations working with the system.
#6
05/11/2012 (7:09 pm)
@Scott, that's great! I'm impressed that you survived "the patch gauntlet" on this one. That makes the effort worth it!

And thanks for the update to t2dAnimatedSprite as well!

@Mich, thanks for the recognition! It means a lot!
#7
05/12/2012 (3:00 am)
@Charlie, this really is an excellent resource and I was impressed at how easy to get working! I'm going to be re-packing Cannibal Cookout this weekend using the new resource.

@Mich, Charlie. One thing I wasn't able to do was test out the editor - my editor is still the 1.3 editor and no longer compiles with OSX Lion (yeah I know, need to upgrade). I'm guessing that the only code change is to expose the autosize flag in the editor pallet for animations, I think the rest will work.