Browse Source

Asset support for named cells/frames

lilligreen 11 years ago
parent
commit
408078f88a

+ 124 - 14
engine/source/2d/assets/AnimationAsset.cc

@@ -79,13 +79,15 @@ IMPLEMENT_CONOBJECT(AnimationAsset);
 
 //------------------------------------------------------------------------------
 
-AnimationAsset::AnimationAsset() :    mAnimationTime(1.0f),
-                                                    mAnimationCycle(true),
-                                                    mRandomStart(false),
-                                                    mAnimationIntegration(0.0f)
+AnimationAsset::AnimationAsset() :  mAnimationTime(1.0f),
+                                    mAnimationCycle(true),
+                                    mRandomStart(false),
+                                    mAnimationIntegration(0.0f),
+                                    mNamedCellsMode(false)
 {
     // Set Vector Associations.
     VECTOR_SET_ASSOCIATION( mAnimationFrames );
+    VECTOR_SET_ASSOCIATION( mNamedAnimationFrames );
     VECTOR_SET_ASSOCIATION( mValidatedFrames );    
 }
 
@@ -104,9 +106,11 @@ void AnimationAsset::initPersistFields()
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, AnimationAsset), &setImage, &defaultProtectedGetFn, &writeImage, "");
     addProtectedField("AnimationFrames", TypeS32Vector, Offset(mAnimationFrames, AnimationAsset), &setAnimationFrames, &defaultProtectedGetFn, &writeAnimationFrames, "");
+    addProtectedField("NamedAnimationFrames", TypeStringTableEntryVector, Offset(mNamedAnimationFrames, AnimationAsset), &setNamedAnimationFrames, &defaultProtectedGetFn, &writeNamedAnimationFrames, "");
     addProtectedField("AnimationTime", TypeF32, Offset(mAnimationTime, AnimationAsset), &setAnimationTime, &defaultProtectedGetFn, &defaultProtectedWriteFn, "");
     addProtectedField("AnimationCycle", TypeBool, Offset(mAnimationCycle, AnimationAsset), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "");
     addProtectedField("RandomStart", TypeBool, Offset(mRandomStart, AnimationAsset), &setRandomStart, &defaultProtectedGetFn, &writeRandomStart, "");
+    addProtectedField("NamedCellsMode", TypeBool, Offset(mNamedCellsMode, AnimationAsset), &setNamedCellsMode, &defaultProtectedGetFn, &writeNamedCellsMode, "");
 }
 
 //------------------------------------------------------------------------------
@@ -156,10 +160,17 @@ void AnimationAsset::copyTo(SimObject* object)
 
     // Copy state.
     pAsset->setImage( getImage().getAssetId() );
-    pAsset->setAnimationFrames( Con::getData( TypeS32Vector, (void*)&getSpecifiedAnimationFrames(), 0 ) );
+
+    // Are we in named cells mode?
+    if ( !pAsset->getNamedCellsMode() )
+        pAsset->setAnimationFrames( Con::getData( TypeS32Vector, (void*)&getSpecifiedAnimationFrames(), 0 ) );
+    else
+        pAsset->setNamedAnimationFrames( Con::getData( TypeStringTableEntryVector, (void*)&getSpecifiedNamedAnimationFrames(), 0 ) );
+
     pAsset->setAnimationTime( getAnimationTime() );
     pAsset->setAnimationCycle( getAnimationCycle() );
     pAsset->setRandomStart( getRandomStart() );
+    pAsset->setNamedCellsMode( getNamedCellsMode() );
 }
 
 //------------------------------------------------------------------------------
@@ -200,6 +211,34 @@ void AnimationAsset::setAnimationFrames( const char* pAnimationFrames )
         mAnimationFrames.push_back( dAtoi( StringUnit::getUnit( pAnimationFrames, frameIndex, " \t\n" ) ) );
     }
 
+    mNamedCellsMode = false;
+
+    // Validate frames.
+    validateFrames();
+
+    // Refresh the asset.
+    refreshAsset();
+}
+
+//------------------------------------------------------------------------------
+
+void AnimationAsset::setNamedAnimationFrames( const char* pAnimationFrames )
+{
+    // Clear any existing frames.
+    mNamedAnimationFrames.clear();
+
+    // Fetch frame count.
+    const U32 frameCount = StringUnit::getUnitCount( pAnimationFrames, " \t\n" );
+
+    // Iterate frames.
+    for( U32 frameIndex = 0; frameIndex < frameCount; ++frameIndex )
+    {
+        // Store frame.
+        mNamedAnimationFrames.push_back( StringTable->insert( StringUnit::getUnit( pAnimationFrames, frameIndex, " \t\n" ) ) );
+    }
+
+    mNamedCellsMode = true;
+
     // Validate frames.
     validateFrames();
 
@@ -254,18 +293,26 @@ void AnimationAsset::setRandomStart( const bool randomStart )
 
 //------------------------------------------------------------------------------
 
-void AnimationAsset::validateFrames( void )
+void AnimationAsset::setNamedCellsMode( const bool namedCellsMode )
 {
-    // Debug Profiling.
-    PROFILE_SCOPE(AnimationAsset_ValidateFrames);
+    // Ignore no change.
+    if ( mNamedCellsMode == mNamedCellsMode)
+        return;
+
+    // Update.
+    mNamedCellsMode = namedCellsMode;
+
+    // Refresh the asset.
+    refreshAsset();
+}
 
+//------------------------------------------------------------------------------
+
+void AnimationAsset::validateNumericalFrames( void )
+{
     // Clear validated frames.
     mValidatedFrames.clear();
 
-    // Finish if we don't have a valid image asset.
-    if ( mImageAsset.isNull() )
-        return;
-
     // Fetch Animation Frame Count.
     const U32 animationFrameCount = (U32)mAnimationFrames.size();
 
@@ -290,12 +337,13 @@ void AnimationAsset::validateFrames( void )
         if ( frame < 0 || frame >= imageAssetFrameCount )
         {
             // No, warn.
-            Con::warnf( "AnimationAsset::validateFrames() - Animation asset '%s' specifies an out-of-bound frame of '%d' (key-index:'%d') against image asset Id '%s'.",
+            Con::warnf( "AnimationAsset::validateFrames() - Animation asset '%s' specifies an out-of-bound frame of '%d' (key-index:'%d') against image asset Id '%s'.", 
                 getAssetName(),
                 frame,
                 frameIndex,
                 mImageAsset.getAssetId() );
 
+            // Set the frame to a valid one.
             if ( frame < 0 )
                 frame = 0;
             else if ( frame >= imageAssetFrameCount )
@@ -309,9 +357,71 @@ void AnimationAsset::validateFrames( void )
 
 //------------------------------------------------------------------------------
 
+void AnimationAsset::validateNamedFrames( void )
+{
+    mValidatedNameFrames.clear();
+
+    // Fetch Animation Frame Count.
+    const U32 animationFrameCount = (U32)mNamedAnimationFrames.size();
+
+    // Finish if no animation frames are specified.
+    if ( animationFrameCount == 0 )
+        return;
+
+    // Fetch image asset frame count.
+    const S32 imageAssetFrameCount = (S32)mImageAsset->getFrameCount();
+
+    // Finish if the image has no frames.
+    if ( imageAssetFrameCount == 0 )
+        return;
+
+    // Validate each specified frame.
+    for ( U32 frameIndex = 0; frameIndex < animationFrameCount; ++frameIndex )
+    {
+        // Fetch frame.
+        const char* frame = mNamedAnimationFrames[frameIndex];
+
+        // Valid Frame?
+        if ( frame ==  StringTable->EmptyString || !mImageAsset->containsFrame(frame) )
+        {
+            // No, warn.
+            Con::warnf( "AnimationAsset::validateNamedFrames() - Animation asset '%s' specifies a bad frame '%s' against image asset Id '%s'.",
+                getAssetName(),
+                frame,
+                mImageAsset.getAssetId() );
+        }
+
+        // Use frame.
+        mValidatedNameFrames.push_back( StringTable->insert(frame) );
+    }
+}
+
+//------------------------------------------------------------------------------
+
+void AnimationAsset::validateFrames( void )
+{
+    // Debug Profiling.
+    PROFILE_SCOPE(AnimationAsset_ValidateFrames);
+
+    // Finish if we don't have a valid image asset.
+    if ( mImageAsset.isNull() )
+        return;
+
+    if (mNamedCellsMode)
+    {
+        validateNamedFrames();
+    }
+    else
+    {
+        validateNumericalFrames();
+    }
+}
+
+//------------------------------------------------------------------------------
+
 bool AnimationAsset::isAssetValid( void ) const
 {
-    return mImageAsset.notNull() && mImageAsset->isAssetValid() && mValidatedFrames.size() > 0;
+    return mImageAsset.notNull() && mImageAsset->isAssetValid() && (mValidatedFrames.size() > 0 || mValidatedNameFrames.size() > 0);
 }
 
 //------------------------------------------------------------------------------

+ 32 - 16
engine/source/2d/assets/AnimationAsset.h

@@ -42,14 +42,18 @@ class AnimationAsset : public AssetBase
 private:
     typedef AssetBase  Parent;
 
-    AssetPtr<ImageAsset>    mImageAsset;
-    Vector<S32>             mAnimationFrames;
-    Vector<S32>             mValidatedFrames;
-    F32                     mAnimationTime;
-    bool                    mAnimationCycle;
-    bool                    mRandomStart;
+    AssetPtr<ImageAsset>     mImageAsset;
+    Vector<S32>              mAnimationFrames;
+    Vector<StringTableEntry> mNamedAnimationFrames;
+    Vector<S32>              mValidatedFrames;
+    Vector<StringTableEntry> mValidatedNameFrames;
+    F32                      mAnimationTime;
+    bool                     mAnimationCycle;
+    bool                     mRandomStart;
 
-    F32                     mAnimationIntegration;
+    F32                      mAnimationIntegration;
+
+    bool                     mNamedCellsMode;
 
 public:
     AnimationAsset();
@@ -67,15 +71,23 @@ public:
     inline const Vector<S32>& getSpecifiedAnimationFrames( void ) const { return mAnimationFrames; }
     inline const Vector<S32>& getValidatedAnimationFrames( void ) const { return mValidatedFrames; }
 
+    void            setNamedAnimationFrames( const char* pAnimationFrames );
+    inline const Vector<StringTableEntry>& getSpecifiedNamedAnimationFrames( void ) const { return mNamedAnimationFrames; }
+    inline const Vector<StringTableEntry>& getValidatedNamedAnimationFrames( void ) const { return mValidatedNameFrames; }
+
     void            setAnimationTime( const F32 animationTime );
     inline F32      getAnimationTime( void ) const                      { return mAnimationTime; }
     void            setAnimationCycle( const bool animationCycle );
     inline bool     getAnimationCycle( void ) const                     { return mAnimationCycle; }
     void            setRandomStart( const bool randomStart );
     inline bool     getRandomStart( void ) const                        { return mRandomStart; }
+    void            setNamedCellsMode( const bool namedCellsMode );
+    inline bool     getNamedCellsMode( void ) const                     { return mNamedCellsMode; }
 
     // Frame validation.
     void            validateFrames( void );
+    void            validateNumericalFrames( void );
+    void            validateNamedFrames( void );
 
     // Asset validation.
     virtual bool    isAssetValid( void ) const;
@@ -88,15 +100,19 @@ protected:
     virtual void onAssetRefresh( void );
 
 protected:
-    static bool setImage( void* obj, const char* data )                         { static_cast<AnimationAsset*>(obj)->setImage( data ); return false; }
-    static bool writeImage( void* obj, StringTableEntry pFieldName )            { return static_cast<AnimationAsset*>(obj)->mImageAsset.notNull(); }
-    static bool setAnimationFrames( void* obj, const char* data )               { static_cast<AnimationAsset*>(obj)->setAnimationFrames( data ); return false; }    
-    static bool writeAnimationFrames( void* obj, StringTableEntry pFieldName )  { return static_cast<AnimationAsset*>(obj)->mAnimationFrames.size() > 0; }
-    static bool setAnimationTime( void* obj, const char* data )                 { static_cast<AnimationAsset*>(obj)->setAnimationTime( dAtof(data) ); return false; }
-    static bool setAnimationCycle( void* obj, const char* data )                { static_cast<AnimationAsset*>(obj)->setAnimationCycle( dAtob(data) ); return false; }
-    static bool writeAnimationCycle( void* obj, StringTableEntry pFieldName )   { return static_cast<AnimationAsset*>(obj)->getAnimationCycle() == false; }
-    static bool setRandomStart( void* obj, const char* data )                   { static_cast<AnimationAsset*>(obj)->setRandomStart( dAtob(data) ); return false; }
-    static bool writeRandomStart( void* obj, StringTableEntry pFieldName )      { return static_cast<AnimationAsset*>(obj)->getRandomStart() == true; }
+    static bool setImage( void* obj, const char* data )                             { static_cast<AnimationAsset*>(obj)->setImage( data ); return false; }
+    static bool writeImage( void* obj, StringTableEntry pFieldName )                { return static_cast<AnimationAsset*>(obj)->mImageAsset.notNull(); }
+    static bool setAnimationFrames( void* obj, const char* data )                   { static_cast<AnimationAsset*>(obj)->setAnimationFrames( data ); return false; }
+    static bool writeAnimationFrames( void* obj, StringTableEntry pFieldName )      { return static_cast<AnimationAsset*>(obj)->mAnimationFrames.size() > 0; }
+    static bool setNamedAnimationFrames( void* obj, const char* data )              { static_cast<AnimationAsset*>(obj)->setNamedAnimationFrames( data ); return false; }    
+    static bool writeNamedAnimationFrames( void* obj, StringTableEntry pFieldName ) { return static_cast<AnimationAsset*>(obj)->mNamedAnimationFrames.size() > 0; }
+    static bool setAnimationTime( void* obj, const char* data )                     { static_cast<AnimationAsset*>(obj)->setAnimationTime( dAtof(data) ); return false; }
+    static bool setAnimationCycle( void* obj, const char* data )                    { static_cast<AnimationAsset*>(obj)->setAnimationCycle( dAtob(data) ); return false; }
+    static bool writeAnimationCycle( void* obj, StringTableEntry pFieldName )       { return static_cast<AnimationAsset*>(obj)->getAnimationCycle() == false; }
+    static bool setRandomStart( void* obj, const char* data )                       { static_cast<AnimationAsset*>(obj)->setRandomStart( dAtob(data) ); return false; }
+    static bool writeRandomStart( void* obj, StringTableEntry pFieldName )          { return static_cast<AnimationAsset*>(obj)->getRandomStart() == true; }
+    static bool setNamedCellsMode( void* obj, const char* data )                    { static_cast<AnimationAsset*>(obj)->setNamedCellsMode( dAtob(data) ); return false; }
+    static bool writeNamedCellsMode( void* obj, StringTableEntry pFieldName )       { return static_cast<AnimationAsset*>(obj)->getNamedCellsMode() == true; }
 };
 
 #endif // _ANIMATION_ASSET_H_

+ 131 - 0
engine/source/2d/assets/AnimationAsset_ScriptBinding.h

@@ -48,6 +48,14 @@ ConsoleMethodWithDocs(AnimationAsset, getImage, ConsoleString, 2, 2, ())
 */
 ConsoleMethodWithDocs(AnimationAsset, setAnimationFrames, ConsoleVoid, 3, 3, (animationFrames))
 {
+    // Are we in named cells mode?
+    if ( object->getNamedCellsMode() )
+    {
+        // Yes, so warn.
+        Con::warnf( "AnimationAsset::setAnimationFrames() - Method invalid, in named cells mode." );
+        return;
+    }
+
     object->setAnimationFrames( argv[2] );
 }
 
@@ -59,6 +67,14 @@ ConsoleMethodWithDocs(AnimationAsset, setAnimationFrames, ConsoleVoid, 3, 3, (an
 */
 ConsoleMethodWithDocs(AnimationAsset, getAnimationFrames, ConsoleString, 2, 3, ([bool validatedFrames]))
 {
+    // Are we in named cells mode?
+    if ( object->getNamedCellsMode() )
+    {
+        // Yes, so warn.
+        Con::warnf( "AnimationAsset::getAnimationFrames() - Method invalid, in named cells mode." );
+        return StringTable->EmptyString;
+    }
+
     // Fetch a return buffer.
     S32 bufferSize = 4096;
     char* pBuffer = Con::getReturnBuffer( bufferSize );
@@ -93,6 +109,14 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationFrames, ConsoleString, 2, 3, (
 */
 ConsoleMethodWithDocs(AnimationAsset, getAnimationFrameCount, ConsoleInt, 2, 3, ([bool validatedFrames]))
 {
+    // Are we in named cells mode?
+    if ( object->getNamedCellsMode() )
+    {
+        // Yes, so warn.
+        Con::warnf( "AnimationAsset::getAnimationFrameCount() - Method invalid, in named cells mode." );
+        return -1;
+    }
+
     // Fetch validated frames flag.
     const bool validatedFrames = argc >= 3 ? dAtob( argv[2] ) : false;
 
@@ -104,6 +128,92 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationFrameCount, ConsoleInt, 2, 3,
 
 //-----------------------------------------------------------------------------
 
+/*! Sets the named image frames that compose the animation.
+    @param animationFrames A set of named image frames that compose the animation.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(AnimationAsset, setNamedAnimationFrames, ConsoleVoid, 3, 3, (animationFrames))
+{
+    // Are we in named cells mode?
+    if ( !object->getNamedCellsMode() )
+    {
+        // No, so warn.
+        Con::warnf( "AnimationAsset::setNamedAnimationFrames() - Method invalid, not in named cells mode." );
+        return;
+    }
+
+    object->setNamedAnimationFrames( argv[2] );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the named frames that compose the animation or optionally only the ones validated against the image asset.
+    @param validatedFrames - Whether to return only the validated frames or not.  Optional: Default is false.
+    @return The named image frames that compose the animation or optionally only the ones validated against the image asset.
+*/
+ConsoleMethodWithDocs(AnimationAsset, getNamedAnimationFrames, ConsoleString, 2, 3, ([bool validatedFrames]))
+{
+    // Are we in named cells mode?
+    if ( !object->getNamedCellsMode() )
+    {
+        // No, so warn.
+        Con::warnf( "AnimationAsset::getNamedAnimationFrames() - Method invalid, not in named cells mode." );
+        return StringTable->EmptyString;
+    }
+
+    // Fetch a return buffer.
+    S32 bufferSize = 4096;
+    char* pBuffer = Con::getReturnBuffer( bufferSize );
+    char* pReturnBuffer = pBuffer;    
+
+    // Fetch validated frames flag.
+    const bool validatedFrames = argc >= 3 ? dAtob( argv[2] ) : false;
+
+    // Fetch specified frames.
+    const Vector<StringTableEntry>& frames = validatedFrames ? object->getValidatedNamedAnimationFrames() : object->getSpecifiedNamedAnimationFrames();
+
+    // Fetch frame count.
+    const U32 frameCount = (U32)frames.size();
+
+    // Format frames.
+    for ( U32 frameIndex = 0; frameIndex < frameCount; ++frameIndex )
+    {
+        const S32 offset = dSprintf( pBuffer, bufferSize, "%d ", frames[frameIndex] );
+        pBuffer += offset;
+        bufferSize -= offset;
+    }
+
+    // Return frames.
+    return pReturnBuffer;
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the count of named frames that compose the animation or optionally only the ones validated against the image asset.
+    @param validatedFrames - Whether to return only the validated frames or not.  Optional: Default is false.
+    @return The named image frames that compose the animation or optionally only the ones validated against the image asset.
+*/
+ConsoleMethodWithDocs(AnimationAsset, getNamedAnimationFrameCount, ConsoleInt, 2, 3, ([bool validatedFrames]))
+{
+    // Are we in named cells mode?
+    if ( !object->getNamedCellsMode() )
+    {
+        // No, so warn.
+        Con::warnf( "AnimationAsset::getNamedAnimationFrameCount() - Method invalid, not in named cells mode." );
+        return -1;
+    }
+
+    // Fetch validated frames flag.
+    const bool validatedFrames = argc >= 3 ? dAtob( argv[2] ) : false;
+
+    // Fetch specified frames.
+    const Vector<StringTableEntry>& frames = validatedFrames ? object->getValidatedNamedAnimationFrames() : object->getSpecifiedNamedAnimationFrames();
+
+    return frames.size();
+}
+
+//-----------------------------------------------------------------------------
+
 /*! Sets the total time to cycle through all animation frames.
     @param animationTime The total time to cycle through all animation frames.
     @return No return value.
@@ -145,4 +255,25 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationCycle, ConsoleBool, 2, 2, ())
     return object->getAnimationCycle();
 }
 
+//-----------------------------------------------------------------------------
+
+/*! Sets whether the animation uses names for cells, instead of numerical index.
+    @param namedCellsMode True if it should be using named cells.
+    @return No return value.
+*/
+ConsoleMethodWithDocs(AnimationAsset, setNamedCellsMode, ConsoleVoid, 3, 3, ())
+{
+    object->setNamedCellsMode( dAtob(argv[2] ) );
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets whether the animation is using names for its cells.
+    @return True if the animation is using named cells.
+*/
+ConsoleMethodWithDocs(AnimationAsset, getNamedCellsMode, ConsoleBool, 2, 2, ())
+{
+    return object->getNamedCellsMode();
+}
+
 ConsoleMethodGroupEndWithDocs(AnimationAsset)

+ 290 - 140
engine/source/2d/assets/ImageAsset.cc

@@ -107,11 +107,15 @@ ConsoleSetType( TypeImageAssetPtr )
 
 //------------------------------------------------------------------------------
 
-static StringTableEntry cellCustomNodeName          = StringTable->insert( "Cells" );
+static StringTableEntry cellCustomNodeCellsName     = StringTable->insert( "Cells" );
 static StringTableEntry cellNodeName                = StringTable->insert( "Cell" );
+static StringTableEntry cellRegionName              = StringTable->insert( "RegionName" );
 static StringTableEntry cellOffsetName              = StringTable->insert( "Offset" );
+static StringTableEntry cellOffsetXName             = StringTable->insert( "OffsetX" );
+static StringTableEntry cellOffsetYName             = StringTable->insert( "OffsetY" );
 static StringTableEntry cellWidthName               = StringTable->insert( "Width" );
 static StringTableEntry cellHeightName              = StringTable->insert( "Height" );
+static StringTableEntry cellNameEntryName           = StringTable->insert( "Name" );
 
 //------------------------------------------------------------------------------
 
@@ -133,7 +137,7 @@ ImageAsset::TextureFilterMode ImageAsset::getFilterModeEnum(const char* label)
             return((ImageAsset::TextureFilterMode)textureFilterLookup[i].index);
 
     // Warn.
-    Con::warnf("ImageAsset::getFilterModeEnum() - Invalid filter-mode '%s'", label );
+    Con::warnf( "ImageAsset::getFilterModeEnum() - Invalid filter-mode '%s'", label );
 
     return ImageAsset::FILTER_INVALID;
 }
@@ -148,7 +152,7 @@ const char* ImageAsset::getFilterModeDescription( ImageAsset::TextureFilterMode
             return textureFilterLookup[i].label;
 
     // Warn.
-    Con::warnf("ImageAsset::getFilterModeDescription() - Invalid filter-mode." );
+    Con::warnf( "ImageAsset::getFilterModeDescription() - Invalid filter-mode." );
 
     return StringTable->EmptyString;
 }
@@ -159,7 +163,6 @@ ImageAsset::ImageAsset() :  mImageFile(StringTable->EmptyString),
                             mForce16Bit(false),
                             mLocalFilterMode(FILTER_INVALID),
                             mExplicitMode(false),
-
                             mCellRowOrder(true),
                             mCellOffsetX(0),
                             mCellOffsetY(0),
@@ -294,7 +297,7 @@ void ImageAsset::copyTo(SimObject* object)
         const FrameArea::PixelArea& pixelArea = getImageFrameArea( index ).mPixelArea;
 
         // Add the explicit cell.
-        pAsset->addExplicitCell( pixelArea.mPixelOffset.x, pixelArea.mPixelOffset.y, pixelArea.mPixelWidth, pixelArea.mPixelHeight );
+        pAsset->addExplicitCell( pixelArea.mPixelOffset.x, pixelArea.mPixelOffset.y, pixelArea.mPixelWidth, pixelArea.mPixelHeight, pixelArea.mRegionName );
     }
 }
 
@@ -550,6 +553,8 @@ void ImageAsset::setCellHeight( const S32 cellheight )
     refreshAsset();
 }
 
+//------------------------------------------------------------------------------
+
 S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 {
 	if ( !getExplicitMode() )
@@ -564,6 +569,8 @@ S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 
 }
 
+//------------------------------------------------------------------------------
+
 S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
 {
 	if ( !getExplicitMode() )
@@ -577,6 +584,27 @@ S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
     return(thisCell.mPixelHeight);
 
 }
+
+//------------------------------------------------------------------------------
+
+bool ImageAsset::containsNamedRegion(const char* regionName)
+{
+    for( typeFrameAreaVector::iterator frameItr = mFrames.begin(); frameItr != mFrames.end(); ++frameItr )
+    {
+        // Grab the current pixelArea
+        const FrameArea::PixelArea& pixelArea = frameItr->mPixelArea;
+            
+        // Check to see if the name matches the argument
+        if (!dStrcmp(pixelArea.mRegionName, regionName))
+        {
+            // Found it, so erase it and return success
+            return true;
+        }
+    }
+    
+    return false;
+}
+
 //------------------------------------------------------------------------------
 
 bool ImageAsset::clearExplicitCells( void )
@@ -600,33 +628,42 @@ bool ImageAsset::clearExplicitCells( void )
 
 //------------------------------------------------------------------------------
 
-bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight )
+bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName )
 {
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     {
         // No, so warn.
-        Con::warnf( "ImageAsset::addCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        Con::warnf( "ImageAsset::addExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
         return false;
     }
 
     // Fetch the original image dimensions.
     const S32 imageWidth = getImageWidth();
     const S32 imageHeight = getImageHeight();
+    
+    // The region name cannot be empty
+    if ( regionName == StringTable->EmptyString )
+    {
+        Con::warnf( "ImageAsset::addExplicitCell() - Cell name of '%s' is invalid or was not set.", regionName );
+        U32 currentIndex = mExplicitFrames.size();
+        Con::warnf( "- Setting to the next index in the frame list: '%i'", currentIndex );
+        dSscanf(regionName, "%i", currentIndex);
+    }
 
     // The Cell Offset X needs to be within the image.
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
     }
 
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
     }
 
@@ -634,7 +671,7 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
     }
 
@@ -642,12 +679,12 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
     }
 
     // Store frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
     mExplicitFrames.push_back( pixelArea );
 
     // Refresh the asset.
@@ -658,13 +695,13 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
 
 //------------------------------------------------------------------------------
 
-bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight )
+bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName )
 {
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     {
         // No, so warn.
-        Con::warnf( "ImageAsset::insertCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
         return false;
     }
 
@@ -674,12 +711,20 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
 
     // Fetch the explicit frame count.
     const S32 explicitFramelCount = mExplicitFrames.size();
+    
+    // Region cannot be empty
+    if ( regionName == StringTable->EmptyString )
+    {
+        Con::warnf( "ImageAsset::insertExplicitCell() - Cell name of '%s' is invalid or was not set.", regionName );
+        Con::warnf( "- Setting to the next index in the frame list: '%i'", explicitFramelCount );
+        dSscanf(regionName, "%i", explicitFramelCount);
+    }
 
     // The cell index needs to be in range.
     if ( cellIndex < 0 )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
     }
 
@@ -687,15 +732,15 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
     }
 
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
     }
 
@@ -703,7 +748,7 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
     }
 
@@ -711,12 +756,12 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
     }
 
     // Configure frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
 
     // Insert frame appropriately.
     if ( cellIndex >= explicitFramelCount )
@@ -737,13 +782,13 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
 
 //------------------------------------------------------------------------------
 
-bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight )
+bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName )
 {
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     {
         // No, so warn.
-        Con::warnf( "ImageAsset::setCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        Con::warnf( "ImageAsset::setExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
         return false;
     }
 
@@ -753,12 +798,20 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
 
     // Fetch the explicit frame count.
     const S32 explicitFrameCount = mExplicitFrames.size();
+    
+    // Region cannot be empty
+    if ( regionName == StringTable->EmptyString )
+    {
+        Con::warnf( "ImageAsset::setExplicitCell() - Cell name of '%s' is invalid or was not set.", regionName );
+        Con::warnf( "- Setting to the next index in the frame list: '%i'", explicitFrameCount );
+        dSscanf(regionName, "%i", explicitFrameCount);
+    }
 
     // The cell index needs to be in range.
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
     }
 
@@ -766,15 +819,15 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
     }
 
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
     }
 
@@ -782,7 +835,7 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
     }
 
@@ -790,12 +843,12 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
     }
 
     // Configure frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
 
     // Set cell.
     mExplicitFrames[cellIndex] = pixelArea;
@@ -814,7 +867,7 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
     if ( !getExplicitMode() )
     {
         // No, so warn.
-        Con::warnf( "ImageAsset::removeCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        Con::warnf( "ImageAsset::removeExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
         return false;
     }
 
@@ -825,7 +878,7 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     {
         // Warn.
-        Con::warnf("ImageAsset::removeCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::removeExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
     }
 
@@ -840,6 +893,69 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
 
 //------------------------------------------------------------------------------
 
+bool ImageAsset::removeExplicitCell( const char* regionName )
+{
+    // Are we in explicit mode?
+    if ( !getExplicitMode() )
+    {
+        // No, so warn.
+        Con::warnf( "ImageAsset::removeExplicitCell() - Cannot perform explicit cell operation when not in explicit mode." );
+        return false;
+    }
+
+    // Interate through the vector
+    for( typeExplicitFrameAreaVector::iterator frameItr = mExplicitFrames.begin(); frameItr != mExplicitFrames.end(); ++frameItr )
+    {
+        // Grab the current pixelArea
+        const FrameArea::PixelArea& pixelArea = *frameItr;
+        
+        // Check to see if the name matches the argument
+        if (pixelArea.mRegionName == regionName)
+        {
+            // Found it, so erase it and return success
+            mExplicitFrames.erase(frameItr);
+            return true;
+        }
+    }
+
+    // Didn't find it, so warn
+    Con::warnf( "ImageAsset::removeExplicitCell() - Cannot find %s cell to remove.", regionName );
+    return false;
+}
+
+//------------------------------------------------------------------------------
+
+ImageAsset::FrameArea& ImageAsset::getCellByName( const char* cellName)
+{
+    // If the cellName was empty
+    if (cellName == StringTable->EmptyString)
+    {
+        // Warn and return a bad frame
+        Con::warnf( "ImageAsset::getCellByName() - Empty cell name was passed." );
+        return BadFrameArea;
+    }
+    
+    // Interate through the vector
+    for( typeFrameAreaVector::iterator frameItr = mFrames.begin(); frameItr != mFrames.end(); ++frameItr )
+    {
+        // Grab the current pixelArea
+        const FrameArea::PixelArea& pixelArea = frameItr->mPixelArea;
+        
+        // Check to see if the name matches the argument
+        if (pixelArea.mRegionName == cellName)
+        {
+            // Found it, so erase it and return success
+            return *frameItr;
+        }
+    }
+
+    // Didn't find it, so warn and return a bad frame
+    Con::warnf( "ImageAsset::getCellByName() - Cannot find %s cell.", cellName );
+    return BadFrameArea;
+}
+
+//------------------------------------------------------------------------------
+
 void ImageAsset::setTextureFilter( const TextureFilterMode filterMode )
 {
     // Finish if no texture.
@@ -1021,7 +1137,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellWidth < 1 || mCellWidth > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell Width of %d.", mCellWidth );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell Width of %d.", mCellWidth );
         return;
     }
 
@@ -1029,7 +1145,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellHeight < 1 || mCellHeight > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell Height of %d.", mCellHeight );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell Height of %d.", mCellHeight );
         return;
     }
 
@@ -1037,7 +1153,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellOffsetX < 0 || mCellOffsetX >= imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetX of %d.", mCellOffsetX );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetX of %d.", mCellOffsetX );
         return;
     }
 
@@ -1045,11 +1161,10 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellOffsetY < 0 || mCellOffsetY >= imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetY of %d.", mCellOffsetY );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetY of %d.", mCellOffsetY );
         return;
     }
 
-
     // Are we using Cell-StrideX?
     S32 cellStepX;
     if ( mCellStrideX != 0 )
@@ -1082,14 +1197,14 @@ void ImageAsset::calculateImplicitMode( void )
     if ( cellFinalPositionX < 0 )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetX(%d)/Width(%d)/CountX(%d); off image left-hand-side.", mCellOffsetX, mCellWidth, mCellCountX );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetX(%d)/Width(%d)/CountX(%d); off image left-hand-side.", mCellOffsetX, mCellWidth, mCellCountX );
         return;
     }
     // Off Right?
     else if ( cellFinalPositionX > imageWidth )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetX(%d)/Width(%d)/CountX(%d); off image right-hand-side.", mCellOffsetX, mCellWidth, mCellCountX );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetX(%d)/Width(%d)/CountX(%d); off image right-hand-side.", mCellOffsetX, mCellWidth, mCellCountX );
         return;
     }
 
@@ -1099,14 +1214,14 @@ void ImageAsset::calculateImplicitMode( void )
     if ( cellFinalPositionY < 0 )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetY(%d)/Height(%d)/CountY(%d); off image top-side.", mCellOffsetY, mCellHeight, mCellCountY );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetY(%d)/Height(%d)/CountY(%d); off image top-side.", mCellOffsetY, mCellHeight, mCellCountY );
         return;
     }
     // Off Bottom?
     else if ( cellFinalPositionY > imageHeight )
     {
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetY(%d)/Height(%d)/CountY(%d); off image bottom-side.", mCellOffsetY, mCellHeight, mCellCountY );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetY(%d)/Height(%d)/CountY(%d); off image bottom-side.", mCellOffsetY, mCellHeight, mCellCountY );
         return;
     }
 
@@ -1187,7 +1302,7 @@ void ImageAsset::calculateExplicitMode( void )
         const FrameArea::PixelArea& pixelArea = *frameItr;
 
         // Set frame area.
-        FrameArea frameArea( pixelArea.mPixelOffset.x, pixelArea.mPixelOffset.y, pixelArea.mPixelWidth, pixelArea.mPixelHeight, texelWidthScale, texelHeightScale );
+        FrameArea frameArea( pixelArea.mPixelOffset.x, pixelArea.mPixelOffset.y, pixelArea.mPixelWidth, pixelArea.mPixelHeight, texelWidthScale, texelHeightScale, pixelArea.mRegionName );
 
         // Store frame.
         mFrames.push_back( frameArea );
@@ -1216,22 +1331,26 @@ void ImageAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
     if ( !mExplicitMode )
         return;
 
-    // Add cell custom node.
-    TamlCustomNode* pCustomCellNodes = customNodes.addNode( cellCustomNodeName );
-
-    // Iterate explicit frames.
-    for( typeExplicitFrameAreaVector::iterator frameItr = mExplicitFrames.begin(); frameItr != mExplicitFrames.end(); ++frameItr )
+    if (mExplicitFrames.size() > 0)
     {
-        // Fetch pixel area.
-        const FrameArea::PixelArea& pixelArea = *frameItr;
+        // Add cell custom node.
+        TamlCustomNode* pCustomCellNodes = customNodes.addNode( cellCustomNodeCellsName );
+
+        // Iterate explicit frames.
+        for( typeExplicitFrameAreaVector::iterator frameItr = mExplicitFrames.begin(); frameItr != mExplicitFrames.end(); ++frameItr )
+        {
+            // Fetch pixel area.
+            const FrameArea::PixelArea& pixelArea = *frameItr;
 
-        // Add cell alias.
-        TamlCustomNode* pCellNode = pCustomCellNodes->addNode( cellNodeName );
+            // Add cell alias.
+            TamlCustomNode* pCellNode = pCustomCellNodes->addNode( cellNodeName );
 
-        // Add cell properties.
-        pCellNode->addField( cellOffsetName, pixelArea.mPixelOffset );
-        pCellNode->addField( cellWidthName, pixelArea.mPixelWidth );
-        pCellNode->addField( cellHeightName, pixelArea.mPixelHeight );
+            // Add cell properties.
+            pCellNode->addField( cellRegionName, pixelArea.mRegionName );
+            pCellNode->addField( cellOffsetName, pixelArea.mPixelOffset );
+            pCellNode->addField( cellWidthName, pixelArea.mPixelWidth );
+            pCellNode->addField( cellHeightName, pixelArea.mPixelHeight );
+        }
     }
 }
 
@@ -1241,104 +1360,129 @@ void ImageAsset::onTamlCustomRead( const TamlCustomNodes& customNodes )
 {
     // Debug Profiling.
     PROFILE_SCOPE(ImageAsset_OnTamlCustomRead);
-
+    
     // Call parent.
     Parent::onTamlCustomRead( customNodes );
-
+    
     // Find cell custom node.
-    const TamlCustomNode* pCustomCellNodes = customNodes.findNode( cellCustomNodeName );
-
-    // Finish if we don't have explicit cells.
-    if ( pCustomCellNodes == NULL )
-        return;
-
-    // Set explicit mode.
-    mExplicitMode = true;
-
-    // Fetch children cell nodes.
-    const TamlCustomNodeVector& cellNodes = pCustomCellNodes->getChildren();
-
-    // Iterate cells.
-    for( TamlCustomNodeVector::const_iterator cellNodeItr = cellNodes.begin(); cellNodeItr != cellNodes.end(); ++cellNodeItr )
+    const TamlCustomNode* pCustomCellNodes = customNodes.findNode( cellCustomNodeCellsName );
+    
+    // Continue if we have explicit cells.
+    if ( pCustomCellNodes != NULL )
     {
-        // Fetch cell node.
-        TamlCustomNode* pCellNode = *cellNodeItr;
-
-        // Fetch node name.
-        StringTableEntry nodeName = pCellNode->getNodeName();
-
-        // Is this a valid alias?
-        if ( nodeName != cellNodeName )
-        {
-            // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom name of '%s'.  Only '%s' is valid.", nodeName, cellNodeName );
-            continue;
-        }
-
-        Point2I cellOffset(-1, -1);
-        S32 cellWidth = 0;
-        S32 cellHeight = 0;
-
-        // Fetch fields.
-        const TamlCustomFieldVector& fields = pCellNode->getFields();
-
-        // Iterate property fields.
-        for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
+        // Set explicit mode.
+        mExplicitMode = true;
+        
+        // Fetch children cell nodes.
+        const TamlCustomNodeVector& cellNodes = pCustomCellNodes->getChildren();
+        
+        // Iterate cells.
+        for( TamlCustomNodeVector::const_iterator cellNodeItr = cellNodes.begin(); cellNodeItr != cellNodes.end(); ++cellNodeItr )
         {
-            // Fetch field.
-            const TamlCustomField* pField = *fieldItr;
-
-            // Fetch field name.
-            StringTableEntry fieldName = pField->getFieldName();
-
-            // Check common fields.
-            if ( fieldName == cellOffsetName )
+            // Fetch cell node.
+            TamlCustomNode* pCellNode = *cellNodeItr;
+            
+            // Fetch node name.
+            StringTableEntry nodeName = pCellNode->getNodeName();
+            
+            // Is this a valid alias?
+            if ( nodeName != cellNodeName )
             {
-                pField->getFieldValue( cellOffset );
+                // No, so warn.
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom name of '%s'.  Only '%s' is valid.", nodeName, cellNodeName );
+                continue;
             }
-            else if ( fieldName == cellWidthName )
+            
+            Point2I cellOffset(-1, -1);
+            S32 cellWidth = 0;
+            S32 cellHeight = 0;
+            const char* regionName = StringTable->EmptyString;
+            
+            // Fetch fields.
+            const TamlCustomFieldVector& fields = pCellNode->getFields();
+            
+            // Iterate property fields.
+            for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
             {
-                pField->getFieldValue( cellWidth );
+                // Fetch field.
+                const TamlCustomField* pField = *fieldItr;
+                
+                // Fetch field name.
+                StringTableEntry fieldName = pField->getFieldName();
+                
+                // Check common fields.
+                if ( fieldName == cellRegionName )
+                {
+                    regionName = pField->getFieldValue();
+                }
+                else if ( fieldName == cellOffsetName )
+                {
+                    pField->getFieldValue( cellOffset );
+                }
+                else if ( fieldName == cellOffsetXName )
+                {
+                    pField->getFieldValue( cellOffset.x );
+                }
+                else if ( fieldName == cellOffsetYName )
+                {
+                    pField->getFieldValue( cellOffset.y );
+                }
+                else if ( fieldName == cellWidthName )
+                {
+                    pField->getFieldValue( cellWidth );
+                }
+                else if ( fieldName == cellHeightName )
+                {
+                    pField->getFieldValue( cellHeight );
+                }
+                else
+                {
+                    // Unknown name so warn.
+                    Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom field name of '%s'.", fieldName );
+                    continue;
+                }
             }
-            else if ( fieldName == cellHeightName )
+            
+            // Does the region have a name
+            if ( regionName == StringTable->EmptyString )
             {
-                pField->getFieldValue( cellHeight );
+                // No, so warn and set it to the next index
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Cell name of '%s' is invalid or was not set.", regionName );
+                
+                U32 currentIndex = mExplicitFrames.size();
+                Con::warnf( "- Setting to the next index in the frame list: '%i'", currentIndex );
+                
+                dSscanf(regionName, "%i", currentIndex);
             }
-            else
+            
+            // Is cell offset valid?
+            if ( cellOffset.x < 0 || cellOffset.y < 0 )
             {
-                // Unknown name so warn.
-                Con::warnf( "ImageAsset::onTamlCustomRead() - Encountered an unknown custom field name of '%s'.", fieldName );
+                // No, so warn.
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Cell offset of '(%d,%d)' is invalid or was not set.", cellOffset.x, cellOffset.y );
                 continue;
             }
+            
+            // Is cell width valid?
+            if ( cellWidth <= 0 )
+            {
+                // No, so warn.
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Cell width of '%d' is invalid or was not set.", cellWidth );
+                continue;
+            }
+            
+            // Is cell height valid?
+            if ( cellHeight <= 0 )
+            {
+                // No, so warn.
+                Con::warnf( "ImageAsset::onTamlCustomRead() - Cell height of '%d' is invalid or was not set.", cellHeight );
+                continue;
+            }
+            
+            // Add explicit frame.
+            FrameArea::PixelArea pixelArea( cellOffset.x, cellOffset.y, cellWidth, cellHeight, regionName );
+            mExplicitFrames.push_back( pixelArea );
         }
-
-        // Is cell offset valid?
-        if ( cellOffset.x < 0 || cellOffset.y < 0 )
-        {
-            // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Cell offset of '(%d,%d)' is invalid or was not set.", cellOffset.x, cellOffset.y );
-            continue;
-        }
-
-        // Is cell width valid?
-        if ( cellWidth <= 0 )
-        {
-            // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Cell width of '%d' is invalid or was not set.", cellWidth );
-            continue;
-        }
-
-        // Is cell height valid?
-        if ( cellHeight <= 0 )
-        {
-            // No, so warn.
-            Con::warnf( "ImageAsset::onTamlCustomRead() - Cell height of '%d' is invalid or was not set.", cellHeight );
-            continue;
-        }
-
-        // Add explicit frame.
-        FrameArea::PixelArea pixelArea( cellOffset.x, cellOffset.y, cellWidth, cellHeight );
-        mExplicitFrames.push_back( pixelArea );
     }
 }
 
@@ -1354,7 +1498,7 @@ static void WriteCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlEleme
 
     // Create ImageAsset node element.
     TiXmlElement* pImageAssetNodeElement = new TiXmlElement( "xs:element" );
-    dSprintf( buffer, sizeof(buffer), "%s.%s", pClassRep->getClassName(), cellCustomNodeName );
+    dSprintf( buffer, sizeof(buffer), "%s.%s", pClassRep->getClassName(), cellCustomNodeCellsName );
     pImageAssetNodeElement->SetAttribute( "name", buffer );
     pImageAssetNodeElement->SetAttribute( "minOccurs", 0 );
     pImageAssetNodeElement->SetAttribute( "maxOccurs", 1 );
@@ -1381,6 +1525,12 @@ static void WriteCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlEleme
     TiXmlElement* pImageAssetComplexTypeElement = new TiXmlElement( "xs:complexType" );
     pImageAssetElement->LinkEndChild( pImageAssetComplexTypeElement );
 
+    // Create "RegionName" attribute.
+    TiXmlElement* pImageRegionName = new TiXmlElement( "xs:attribute" );
+    pImageRegionName->SetAttribute( "name", cellRegionName );
+    pImageRegionName->SetAttribute( "type", "xs:string" );
+    pImageAssetComplexTypeElement->LinkEndChild( pImageRegionName );
+    
     // Create "Offset" attribute.
     TiXmlElement* pImageAssetOffset = new TiXmlElement( "xs:attribute" );
     pImageAssetOffset->SetAttribute( "name", cellOffsetName );

+ 38 - 5
engine/source/2d/assets/ImageAsset.h

@@ -73,17 +73,28 @@ public:
             {
                 setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
             }
-
+            PixelArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const char* regionName )
+            {
+                setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, regionName );
+            }
             inline void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight )
             {
                 mPixelOffset.set( pixelFrameOffsetX, pixelFrameOffsetY );
                 mPixelWidth = pixelFrameWidth;
                 mPixelHeight = pixelFrameHeight;
             };
+            inline void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const char* regionName )
+            {
+                mPixelOffset.set( pixelFrameOffsetX, pixelFrameOffsetY );
+                mPixelWidth = pixelFrameWidth;
+                mPixelHeight = pixelFrameHeight;
+                mRegionName = StringTable->insert(regionName);
+            };
 
             Point2I mPixelOffset;
             U32 mPixelWidth;
             U32 mPixelHeight;
+            StringTableEntry mRegionName;
         };
 
 
@@ -122,12 +133,25 @@ public:
         {
             setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, texelWidthScale, texelHeightScale );
         }
+        FrameArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const F32 texelWidthScale, const F32 texelHeightScale, const char* regionName )
+        {
+            setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, texelWidthScale, texelHeightScale, regionName);
+        }
+        FrameArea()
+        {
+            setArea(0, 0, 0, 0, 0.0f, 0.0f);
+        }
 
         void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const F32 texelWidthScale, const F32 texelHeightScale )
         {
             mPixelArea.setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
             mTexelArea.setArea( mPixelArea, texelWidthScale, texelHeightScale );
         }
+        void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight, const F32 texelWidthScale, const F32 texelHeightScale, const char* regionName )
+        {
+            mPixelArea.setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, regionName );
+            mTexelArea.setArea( mPixelArea, texelWidthScale, texelHeightScale );
+        }
 
         PixelArea mPixelArea;
         TexelArea mTexelArea;
@@ -207,27 +231,36 @@ public:
     void                    setCellHeight( const S32 cellheight );
     S32                     getCellHeight( void) const						{ return mCellHeight; }
     S32                     getExplicitCellHeight(const S32 cellIndex);
+    
+    bool                    containsNamedRegion(const char* regionName);
 
     inline TextureHandle&   getImageTexture( void )                         { return mImageTextureHandle; }
     inline S32              getImageWidth( void ) const                     { return mImageTextureHandle.getWidth(); }
     inline S32              getImageHeight( void ) const                    { return mImageTextureHandle.getHeight(); }
     inline U32              getFrameCount( void ) const                     { return (U32)mFrames.size(); };
-
+    inline bool             containsFrame( const char* namedFrame )         { return containsNamedRegion(namedFrame); };
+    
+    FrameArea&              getCellByName(const char* cellName);
+    
     inline const FrameArea& getImageFrameArea( U32 frame ) const            { clampFrame(frame); return mFrames[frame]; };
+    inline const FrameArea& getImageFrameArea( const char* namedFrame)      { return getCellByName(namedFrame); };
     inline const void       bindImageTexture( void)                         { glBindTexture( GL_TEXTURE_2D, getImageTexture().getGLName() ); };
     
     virtual bool            isAssetValid( void ) const                      { return !mImageTextureHandle.IsNull(); }
 
     /// Explicit cell control.
     bool                    clearExplicitCells( void );
-    bool                    addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight );
-    bool                    insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight );
+    bool                    addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName );
+    bool                    insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName );
     bool                    removeExplicitCell( const S32 cellIndex );
-    bool                    setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight );
+    bool                    removeExplicitCell( const char* regionName );
+    bool                    setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, const S32 cellOffsetY, const S32 cellWidth, const S32 cellHeight, const char* regionName );
     inline S32              getExplicitCellCount( void ) const              { return mExplicitFrames.size(); }
     
     static TextureFilterMode getFilterModeEnum(const char* label);
     static const char* getFilterModeDescription( TextureFilterMode filterMode );
+    
+    inline void forceCalculation( void ) { calculateImage(); }
 
     /// Declare Console Object.
     DECLARE_CONOBJECT(ImageAsset);

+ 24 - 13
engine/source/2d/assets/ImageAsset_ScriptBinding.h

@@ -253,24 +253,32 @@ ConsoleMethodWithDocs(ImageAsset, getCellWidth, ConsoleInt, 2, 2, ())
     return object->getCellWidth();
 }
 
-ConsoleMethod(ImageAsset, getExplicitCellWidth, S32, 3,3, "(CellIndex) Gets the CELL width in Explicit Mode.\n"
-                                                                        "@return the specified CELL width.")
+//-----------------------------------------------------------------------------
+
+/*! Gets the CELL width in Explicit Mode.
+    @return the specified CELL width.
+*/
+ConsoleMethodWithDocs(ImageAsset, getExplicitCellWidth, ConsoleInt, 3,3, (CellIndex))
 {
-	    // Fetch cell index.
+    // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
 
     return(object->getExplicitCellWidth(cellIndex));
 }
 
-ConsoleMethod(ImageAsset, getExplicitCellHeight, S32, 3,3, "(CellIndex) Gets the CELL height in Explicit Mode.\n"
-                                                                        "@return the specified CELL height.")
+//-----------------------------------------------------------------------------
+
+/*! Gets the CELL height in Explicit Mode.
+    @return the specified CELL height.
+*/
+ConsoleMethodWithDocs(ImageAsset, getExplicitCellHeight, ConsoleInt, 3,3, (CellIndex))
 {
-	    // Fetch cell index.
+    // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
 
     return(object->getExplicitCellHeight(cellIndex));
-
 }
+
 //-----------------------------------------------------------------------------
 
 /*! Sets the CELL height.
@@ -399,10 +407,11 @@ ConsoleMethodWithDocs(ImageAsset, clearExplicitCells, ConsoleBool, 2, 2, ())
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height of the cell.
+    @param cellName The name of the cell's region.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
 */
-ConsoleMethodWithDocs(ImageAsset, addExplicitCell, ConsoleBool, 6, 6, (int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight))
+ConsoleMethodWithDocs(ImageAsset, addExplicitCell, ConsoleBool, 7, 7, (int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight, string cellName))
 {
     // Fetch offsets.
     const S32 cellOffsetX = dAtoi( argv[2] );
@@ -412,7 +421,7 @@ ConsoleMethodWithDocs(ImageAsset, addExplicitCell, ConsoleBool, 6, 6, (int cellO
     const S32 cellWidth = dAtoi( argv[4] );
     const S32 cellHeight = dAtoi (argv[5] );
 
-    return object->addExplicitCell( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    return object->addExplicitCell( cellOffsetX, cellOffsetY, cellWidth, cellHeight, argv[6] );
 }
 
 //-----------------------------------------------------------------------------
@@ -423,10 +432,11 @@ ConsoleMethodWithDocs(ImageAsset, addExplicitCell, ConsoleBool, 6, 6, (int cellO
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height of the cell.
+    @param cellName The name of the cell's region.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
 */
-ConsoleMethodWithDocs(ImageAsset, insertExplicitCell, ConsoleBool, 7, 7, (int cellIndex, int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight))
+ConsoleMethodWithDocs(ImageAsset, insertExplicitCell, ConsoleBool, 8, 8, (int cellIndex, int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight, string cellName))
 {
     // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
@@ -439,7 +449,7 @@ ConsoleMethodWithDocs(ImageAsset, insertExplicitCell, ConsoleBool, 7, 7, (int ce
     const S32 cellWidth = dAtoi( argv[5] );
     const S32 cellHeight = dAtoi (argv[6] );
 
-    return object->insertExplicitCell( cellIndex, cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    return object->insertExplicitCell( cellIndex, cellOffsetX, cellOffsetY, cellWidth, cellHeight, argv[7] );
 }
 
 
@@ -465,10 +475,11 @@ ConsoleMethodWithDocs(ImageAsset, removeExplicitCell, ConsoleBool, 7, 7, (int ce
     @param cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height of the cell.
+    @param cellName The name of the cell's region.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
 */
-ConsoleMethodWithDocs(ImageAsset, setExplicitCell, ConsoleBool, 7, 7, (int cellIndex, int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight))
+ConsoleMethodWithDocs(ImageAsset, setExplicitCell, ConsoleBool, 8, 8, (int cellIndex, int cellOffsetX, int cellOffsetY, int cellWidth, int cellHeight, string cellName))
 {
     // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
@@ -481,7 +492,7 @@ ConsoleMethodWithDocs(ImageAsset, setExplicitCell, ConsoleBool, 7, 7, (int cellI
     const S32 cellWidth = dAtoi( argv[5] );
     const S32 cellHeight = dAtoi (argv[6] );
 
-    return object->setExplicitCell( cellIndex, cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    return object->setExplicitCell( cellIndex, cellOffsetX, cellOffsetY, cellWidth, cellHeight, argv[7] );
 }
 
 //-----------------------------------------------------------------------------

+ 100 - 2
engine/source/2d/assets/ParticleAssetEmitter.cc

@@ -203,6 +203,8 @@ ParticleAssetEmitter::ParticleAssetEmitter() :
     // Register for refresh notifications.
     mImageAsset.registerRefreshNotify( this );
     mAnimationAsset.registerRefreshNotify( this );
+    
+    mImageFrameName = "";
 }
 
 //------------------------------------------------------------------------------
@@ -246,6 +248,7 @@ void ParticleAssetEmitter::initPersistFields()
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, ParticleAssetEmitter), &setImage, &getImage, &writeImage, "");
     addProtectedField("Frame", TypeS32, Offset(mImageFrame, ParticleAssetEmitter), &setImageFrame, &defaultProtectedGetFn, &writeImageFrame, "");
+    addProtectedField("FrameName", TypeString, Offset(mImageFrameName, ParticleAssetEmitter), &setImageFrameName, &defaultProtectedGetFn, &writeImageFrameName, "");
     addProtectedField("RandomImageFrame", TypeBool, Offset(mRandomImageFrame, ParticleAssetEmitter), &setRandomImageFrame, &defaultProtectedGetFn, &writeRandomImageFrame, "");
     addProtectedField("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, ParticleAssetEmitter), &setAnimation, &getAnimation, &writeAnimation, "");
 }
@@ -291,10 +294,15 @@ void ParticleAssetEmitter::copyTo(SimObject* object)
    pParticleAssetEmitter->setAlphaTest( getAlphaTest() );
 
    pParticleAssetEmitter->setRandomImageFrame( getRandomImageFrame() );
+
+   // Static provider?
    if ( pParticleAssetEmitter->isStaticFrameProvider() )
    {
-       pParticleAssetEmitter->setImage( getImage() );
-       pParticleAssetEmitter->setImageFrame( getImageFrame() );
+        // Named image frame?
+        if ( pParticleAssetEmitter->isUsingNamedImageFrame() )
+            pParticleAssetEmitter->setImage( getImage(), getImageFrameName() );
+        else
+            pParticleAssetEmitter->setImage( getImage(), getImageFrame() );
    }
    else
    {
@@ -400,6 +408,9 @@ bool ParticleAssetEmitter::setImage( const char* pAssetId, U32 frame )
         mImageFrame = 0;
     }
 
+    // Using a numerical frame index
+    mUsingFrameName = false;
+
     // Refresh the asset.
     refreshAsset();
 
@@ -409,6 +420,53 @@ bool ParticleAssetEmitter::setImage( const char* pAssetId, U32 frame )
 
 //------------------------------------------------------------------------------
 
+bool ParticleAssetEmitter::setImage( const char* pAssetId, const char* frameName )
+{
+    // Sanity!
+    AssertFatal( pAssetId != NULL, "ParticleAssetEmitter::setImage() - Cannot use a NULL asset Id." );
+    
+    // Set static mode.
+    mStaticMode = true;
+    
+    // Clear animation asset.
+    mAnimationAsset.clear();
+    
+    // Set asset Id.
+    mImageAsset = pAssetId;
+    
+    // Is there an asset?
+    if ( mImageAsset.notNull() )
+    {
+        // Yes, so is the frame valid?
+        if ( !mImageAsset->containsFrame(frameName) )
+        {
+            // No, so warn.
+            Con::warnf( "ParticleAssetEmitter::setImage() - Invalid frame '%s' for ImageAsset '%s'.", frameName, mImageAsset.getAssetId() );
+        }
+        else
+        {
+        // Yes, so set the frame.
+        mImageFrameName = StringTable->insert(frameName);
+        }
+    }
+    else
+    {
+        // No, so reset the image frame.
+        mImageFrameName = StringTable->insert(StringTable->EmptyString);
+    }
+
+    // Using a named frame index
+    mUsingFrameName = true;
+    
+    // Refresh the asset.
+    refreshAsset();
+    
+    // Return Okay.
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
 bool ParticleAssetEmitter::setImageFrame( const U32 frame )
 {
     // Check Existing Image.
@@ -433,6 +491,9 @@ bool ParticleAssetEmitter::setImageFrame( const U32 frame )
     // Set Frame.
     mImageFrame = frame;
 
+    // Using a numerical frame index.
+    mUsingFrameName = false;
+
     // Refresh the asset.
     refreshAsset();
 
@@ -442,6 +503,43 @@ bool ParticleAssetEmitter::setImageFrame( const U32 frame )
 
 //------------------------------------------------------------------------------
 
+bool ParticleAssetEmitter::setImageFrameName( const char* nameFrame )
+{
+    // Check Existing Image.
+    if ( mImageAsset.isNull() )
+    {
+        // Warn.
+        Con::warnf("ParticleAssetEmitter::setImageNameFrame() - Cannot set Frame without existing asset Id.");
+        
+        // Return Here.
+        return false;
+    }
+    
+    // Check Frame Validity.
+    if ( !mImageAsset->containsFrame(nameFrame) )
+    {
+        // Warn.
+        Con::warnf( "ParticleAssetEmitter::setImageFrameName() - Invalid Frame %s for asset Id '%s'.", nameFrame, mImageAsset.getAssetId() );
+        
+        // Return Here.
+        return false;
+    }
+    
+    // Set frame.
+    mImageFrameName = StringTable->insert(nameFrame);
+
+    // Using a named frame index
+    mUsingFrameName = true;
+    
+    // Refresh the asset.
+    refreshAsset();
+    
+    // Return Okay.
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
 bool ParticleAssetEmitter::setAnimation( const char* pAnimationAssetId )
 {
     // Sanity!

+ 8 - 0
engine/source/2d/assets/ParticleAssetEmitter.h

@@ -107,8 +107,10 @@ private:
     bool                                    mStaticMode;
     AssetPtr<ImageAsset>                    mImageAsset;
     U32                                     mImageFrame;
+    StringTableEntry                        mImageFrameName;
     bool                                    mRandomImageFrame;
     AssetPtr<AnimationAsset>                mAnimationAsset;
+    bool                                    mUsingFrameName;
 
     /// Particle fields.
     ParticleAssetFieldCollection            mParticleFields;
@@ -182,10 +184,14 @@ public:
     inline bool getOldestInFront( void ) const { return mOldestInFront; }
    
     inline bool isStaticFrameProvider( void ) const { return mStaticMode; }
+    inline bool isUsingNamedImageFrame( void ) const { return mUsingFrameName; }
     bool setImage( const char* pAssetId, const U32 frame = 0 );
+    bool setImage( const char* pAssetId, const char* frameName);
     inline StringTableEntry getImage( void ) const { return mImageAsset.getAssetId(); }
     bool setImageFrame( const U32 frame );
     inline U32 getImageFrame( void ) const { return mImageFrame; }
+    bool setImageFrameName( const char* frameName);
+    inline const char* getImageFrameName( void ) const { return mImageFrameName; }
     inline void setRandomImageFrame( const bool randomImageFrame ) { mRandomImageFrame = randomImageFrame; }
     inline bool getRandomImageFrame( void ) const { return mRandomImageFrame; }
     bool setAnimation( const char* animationName );
@@ -304,6 +310,8 @@ protected:
     static bool     writeImage( void* obj, StringTableEntry pFieldName )                { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
     static bool     setImageFrame(void* obj, const char* data)                          { static_cast<ParticleAssetEmitter*>(obj)->setImageFrame(dAtoi(data)); return false; };
     static bool     writeImageFrame( void* obj, StringTableEntry pFieldName )           { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull() && !pCastObject->getRandomImageFrame(); }
+    static bool     setImageFrameName(void* obj, const char* data)                      { static_cast<ParticleAssetEmitter*>(obj)->setImageFrameName(data); return false; };
+    static bool     writeImageFrameName( void* obj, StringTableEntry pFieldName )       { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull() && !pCastObject->getRandomImageFrame(); }
     static bool     setRandomImageFrame(void* obj, const char* data)                    { static_cast<ParticleAssetEmitter*>(obj)->setRandomImageFrame(dAtob(data)); return false; };
     static bool     writeRandomImageFrame( void* obj, StringTableEntry pFieldName )     { ParticleAssetEmitter* pCastObject = static_cast<ParticleAssetEmitter*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->getRandomImageFrame(); }
     static bool     setAnimation(void* obj, const char* data)                           { static_cast<ParticleAssetEmitter*>(obj)->setAnimation(data); return false; };

+ 43 - 4
engine/source/2d/assets/ParticleAssetEmitter_ScriptBinding.h

@@ -517,10 +517,28 @@ ConsoleMethodWithDocs(ParticleAssetEmitter, getOldestInFront, ConsoleBool, 2, 2,
 */
 ConsoleMethodWithDocs(ParticleAssetEmitter, setImage, ConsoleBool, 3, 4, (imageAssetId, [frame]))
 {
-    // Fetch the frame.
-    const U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
-
-    return object->setImage( argv[2], frame );
+    // Was a frame specified?
+    if (argc >= 4)
+    {
+        // Was it a number or a string?
+        if (!dIsalpha(*argv[3]))
+        {
+            // Fetch the numerical frame and set the image
+            const U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
+            return object->setImage(argv[2], frame);
+        }
+        else
+        {
+            // Set the image and pass the named frame string
+            return object->setImage(argv[2], argv[3]);
+        }
+    }
+    else
+    {
+        // Frame was not specified, use default 0 and set the image
+        const U32 frame = 0;
+        return object->setImage( argv[2], frame);
+    }
 }
 
 //------------------------------------------------------------------------------
@@ -577,6 +595,27 @@ ConsoleMethodWithDocs(ParticleAssetEmitter, getRandomImageFrame, ConsoleBool, 2,
 
 //------------------------------------------------------------------------------
 
+/*! Sets the emitter to use the specified image frame by name.
+    @param frame String containing the name of the frame in the image to use.
+    @return Whether the operation was successful or not.
+*/
+ConsoleMethodWithDocs(ParticleAssetEmitter, setImageFrameName, ConsoleBool, 3, 3,  (frame))
+{
+    return object->setImageFrameName( argv[2] );
+}
+
+//------------------------------------------------------------------------------
+
+/*! Gets the asset Id of the image asset assigned to the emitter.
+    @return The asset Id of the image asset assigned to the emitter or nothing if no image is assigned.
+*/
+ConsoleMethodWithDocs(ParticleAssetEmitter, getImageFrameName, ConsoleString, 2, 2, ())
+{
+    return object->getImageFrameName();
+}
+
+//------------------------------------------------------------------------------
+
 /*! Sets the emitter to use the specified animation asset Id.
     @param animationAssetId The animation asset Id to use.
     @return Whether the operation was successful or not.

+ 169 - 33
engine/source/2d/core/ImageFrameProviderCore.cc

@@ -114,7 +114,13 @@ void ImageFrameProviderCore::copyTo( ImageFrameProviderCore* pImageFrameProvider
     {
         // Yes, so use the image/frame if we have an asset.
         if ( mpImageAsset->notNull() )
-            pImageFrameProviderCore->setImage( getImage(), getImageFrame() );
+        {
+            // Named image frame?
+            if ( !isUsingNamedImageFrame() )
+                pImageFrameProviderCore->setImage( getImage(), getImageFrame() );
+            else
+                pImageFrameProviderCore->setImage( getImage(), getImageFrameByName() );
+        }
     }
     else if ( mpAnimationAsset->notNull() )
     {
@@ -178,7 +184,10 @@ bool ImageFrameProviderCore::validRender( void ) const
     if ( isStaticFrameProvider() )
     {
         // Yes, so we must have an image asset and the frame must be in bounds.
-        return mpImageAsset->notNull() && ( getImageFrame() < (*mpImageAsset)->getFrameCount() );
+        if (!isUsingNamedImageFrame())
+            return mpImageAsset->notNull() && ( getImageFrame() < (*mpImageAsset)->getFrameCount() );
+        else
+            return mpImageAsset->notNull() && getImageFrameByName() != StringTable->EmptyString && ( (*mpImageAsset)->containsFrame(getImageFrameByName()) );
     }
 
     // No, so if the animation must be valid.
@@ -187,6 +196,27 @@ bool ImageFrameProviderCore::validRender( void ) const
 
 //------------------------------------------------------------------------------
 
+const ImageAsset::FrameArea& ImageFrameProviderCore::getProviderImageFrameArea( void ) const
+{
+    // If this does not have a valid render state, return a bad frame
+    if (!validRender())
+        return BadFrameArea;
+    
+    // If it is a static frame and it's not using named frames, get the image area based mImageFrame
+    // If it is a static frame and it's using named frames, get the image area based on mImageNameFrame
+    // Otherwise, get the current animation frame
+    if (isStaticFrameProvider())
+        return !isUsingNamedImageFrame() ? (*mpImageAsset)->getImageFrameArea(mImageFrame) : (*mpImageAsset)->getImageFrameArea(mImageNameFrame);
+    else
+        return !(*mpAnimationAsset)->getNamedCellsMode() ? (*mpAnimationAsset)->getImage()->getImageFrameArea(getCurrentAnimationFrame()) :
+                                                           (*mpAnimationAsset)->getImage()->getImageFrameArea(getCurrentAnimationFrameName());
+    
+    // If we got here for some reason, that's bad. So return a bad area frame
+    return BadFrameArea;
+}
+
+//------------------------------------------------------------------------------
+
 void ImageFrameProviderCore::render(
     const bool flipX,
     const bool flipY,
@@ -254,7 +284,7 @@ void ImageFrameProviderCore::renderGui( GuiControl& owner, Point2I offset, const
         RectI destinationRegion(offset, owner.mBounds.extent);
 
         // Render image.
-		dglSetBitmapModulation( owner.mProfile->mFillColor );
+        dglSetBitmapModulation( owner.mProfile->mFillColor );
         dglDrawBitmapStretchSR( getProviderTexture(), destinationRegion, sourceRegion );
         dglClearBitmapModulation();
     }
@@ -283,6 +313,9 @@ bool ImageFrameProviderCore::setImage( const char* pImageAssetId, const U32 fram
 
     // Set as static provider.
     mStaticProvider = true;
+    
+    // Using a numerical frame index.
+    mUsingNameFrame = false;
 
     // Turn-off tick processing.
     setProcessTicks( false );
@@ -293,6 +326,37 @@ bool ImageFrameProviderCore::setImage( const char* pImageAssetId, const U32 fram
 
 //------------------------------------------------------------------------------
 
+bool ImageFrameProviderCore::setImage( const char* pImageAssetId, const char* pNameFrame )
+{
+    // Finish if invalid image asset.
+    if ( pImageAssetId == NULL )
+        return false;
+    
+    // Set asset.
+    mpImageAsset->setAssetId( pImageAssetId );
+    
+    // Set the image frame if the image asset was set.
+    if ( mpImageAsset->notNull() )
+        setImageFrameByName( pNameFrame );
+    
+    // Set Frame.
+    mImageNameFrame = StringTable->insert(pNameFrame);
+    
+    // Set as static provider.
+    mStaticProvider = true;
+    
+    // Using a named frame index.
+    mUsingNameFrame = true;
+    
+    // Turn-off tick processing.
+    setProcessTicks( false );
+    
+    // Return Okay.
+    return true;
+}
+
+//------------------------------------------------------------------------------
+
 bool ImageFrameProviderCore::setImageFrame( const U32 frame )
 {
     // Check Existing Image.
@@ -309,7 +373,7 @@ bool ImageFrameProviderCore::setImageFrame( const U32 frame )
     if ( frame >= (*mpImageAsset)->getFrameCount() )
     {
         // Warn.
-        Con::warnf( "ImageFrameProviderCore::setImageFrame() - Invalid Frame #%d for asset Id '%s'.", frame, mpImageAsset->getAssetId() );
+        Con::warnf("ImageFrameProviderCore::setImageFrame() - Invalid Frame #%d for asset Id '%s'.", frame, mpImageAsset->getAssetId());
         // Return Here.
         return false;
     }
@@ -317,6 +381,40 @@ bool ImageFrameProviderCore::setImageFrame( const U32 frame )
     // Set Frame.
     mImageFrame = frame;
 
+    // Using a numerical frame index.
+    mUsingNameFrame = false;
+
+    // Return Okay.
+    return true;
+}
+
+//-----------------------------------------------------------------------------
+
+bool ImageFrameProviderCore::setImageFrameByName(const char* frame)
+{
+    // Check Existing Image.
+    if ( mpImageAsset->isNull() )
+    {
+        // Warn.
+        Con::warnf("ImageFrameProviderCore::setImageNameFrame() - Cannot set Frame without existing asset Id.");
+        
+        // Return Here.
+        return false;
+    }
+    
+    // Check Frame Validity.
+    if ( frame == StringTable->EmptyString )
+    {
+        // Warn.
+        Con::warnf( "ImageFrameProviderCore::setImageNameFrame() - Invalid Frame %s for asset Id '%s'.", frame, mpImageAsset->getAssetId() );
+        // Return Here.
+        return false;
+    }
+    
+    // Set Frame.
+    mImageNameFrame = StringTable->insert(frame);
+    mUsingNameFrame = true;
+    
     // Return Okay.
     return true;
 }
@@ -339,17 +437,37 @@ const U32 ImageFrameProviderCore::getCurrentAnimationFrame( void ) const
 
 //-----------------------------------------------------------------------------
 
+const char* ImageFrameProviderCore::getCurrentAnimationFrameName( void ) const
+{
+    // Sanity!
+    AssertFatal( mpAnimationAsset->notNull(), "Animation controller requested current image frame but no animation asset assigned." );
+
+    // Fetch validated frames.
+    const Vector<StringTableEntry>& validatedFrames = (*mpAnimationAsset)->getValidatedNamedAnimationFrames();
+
+    // Sanity!
+    AssertFatal( mCurrentFrameIndex < validatedFrames.size(), "Animation controller requested the current frame but it is out of bounds of the validated frames." );
+
+    return validatedFrames[mCurrentFrameIndex];
+}
+
+//-----------------------------------------------------------------------------
+
 bool ImageFrameProviderCore::isAnimationValid( void ) const
 {
     // Not valid if no animation asset.
     if ( mpAnimationAsset->isNull() )
         return false;
 
-    // Fetch validated frames.
-    const Vector<S32>& validatedFrames = (*mpAnimationAsset)->getValidatedAnimationFrames();
+    S32 validatedFrameSize = 0;
+
+    if ((*mpAnimationAsset)->getNamedCellsMode())
+        validatedFrameSize = (*mpAnimationAsset)->getValidatedNamedAnimationFrames().size();
+    else
+        validatedFrameSize = (*mpAnimationAsset)->getValidatedAnimationFrames().size();
 
     // Not valid if current frame index is out of bounds of the validated frames.
-    if ( mCurrentFrameIndex >= validatedFrames.size() )
+    if ( mCurrentFrameIndex >= validatedFrameSize )
         return false;
 
     // Fetch image asset.
@@ -359,12 +477,23 @@ bool ImageFrameProviderCore::isAnimationValid( void ) const
     if ( imageAsset.isNull() )
         return false;
 
-    // Fetch current frame.
-    const U32 currentFrame = getCurrentAnimationFrame();
+    if (!(*mpAnimationAsset)->getNamedCellsMode())
+    {
+        // Fetch current frame.
+        const U32 currentFrame = getCurrentAnimationFrame();
 
-    // Not valid if current frame is out of bounds of the image asset.
-    if ( currentFrame >= imageAsset->getFrameCount() )
-        return false;
+        // Not valid if current frame is out of bounds of the image asset.
+        if ( currentFrame >= imageAsset->getFrameCount() )
+            return false;
+    }
+    else
+    {
+        // Fetch the current name frame.
+        const char* frameName = getCurrentAnimationFrameName();
+
+        if (!imageAsset->containsFrame(frameName))
+            return false;
+    }
 
     // Valid.
     return true;
@@ -419,10 +548,15 @@ bool ImageFrameProviderCore::playAnimation( const AssetPtr<AnimationAsset>& anim
     mStaticProvider = false;
 
     // Fetch validated frames.
-    const Vector<S32>& validatedFrames = animationAsset->getValidatedAnimationFrames();
+    U32 validatedFrameSize = 0;
+
+    if (animationAsset->getNamedCellsMode())
+        validatedFrameSize = animationAsset->getValidatedNamedAnimationFrames().size();
+    else
+        validatedFrameSize = animationAsset->getValidatedAnimationFrames().size();
 
     // Check we've got some frames.
-    if ( validatedFrames.size() == 0 )
+    if ( validatedFrameSize == 0 )
     {
         Con::warnf( "ImageFrameProviderCore::playAnimation() - Cannot play AnimationAsset '%s' - Animation has no validated frames!", mpAnimationAsset->getAssetId() );
         return false;
@@ -432,13 +566,13 @@ bool ImageFrameProviderCore::playAnimation( const AssetPtr<AnimationAsset>& anim
     mpAnimationAsset->setAssetId( animationAsset.getAssetId() );
 
     // Set Maximum Frame Index.
-    mMaxFrameIndex = validatedFrames.size()-1;
+    mMaxFrameIndex = validatedFrameSize-1;
 
     // Calculate Total Integration Time.
     mTotalIntegrationTime = (*mpAnimationAsset)->getAnimationTime();
 
     // Calculate Frame Integration Time.
-    mFrameIntegrationTime = mTotalIntegrationTime / validatedFrames.size();
+    mFrameIntegrationTime = mTotalIntegrationTime / validatedFrameSize;
 
     // No, so random Start?
     if ( (*mpAnimationAsset)->getRandomStart() )
@@ -477,12 +611,25 @@ bool ImageFrameProviderCore::updateAnimation( const F32 elapsedTime )
     if ( mAnimationFinished )
         return false;
 
-    // Fetch validated frames.
-    const Vector<S32>& validatedFrames = (*mpAnimationAsset)->getValidatedAnimationFrames();
+    // Check for validity in the different frame lists
+    if ((*mpAnimationAsset)->getNamedCellsMode())
+    {
+        // Fetch the validated name frames.
+        const Vector<StringTableEntry>& validatedFrames = (*mpAnimationAsset)->getValidatedNamedAnimationFrames();
 
-    // Finish if there are no validated frames.
-    if ( validatedFrames.size() == 0 )
-        return false;
+        // Finish if there are no validated frames.
+        if ( validatedFrames.size() == 0 )
+            return false;
+    }
+    else
+    {
+        // Fetch validated frames.
+        const Vector<S32>& validatedFrames = (*mpAnimationAsset)->getValidatedAnimationFrames();
+
+        // Finish if there are no validated frames.
+        if ( validatedFrames.size() == 0 )
+            return false;
+    }
 
     // Calculate scaled time.
     const F32 scaledTime = elapsedTime * mAnimationTimeScale;
@@ -506,18 +653,6 @@ bool ImageFrameProviderCore::updateAnimation( const F32 elapsedTime )
     // Calculate Current Frame.
     mCurrentFrameIndex = (S32)(mCurrentModTime / mFrameIntegrationTime);
 
-    // Fetch frame.
-    S32 frame = validatedFrames[mCurrentFrameIndex];
-
-    // Fetch image frame count.
-    const S32 imageFrameCount = (*mpAnimationAsset)->getImage()->getFrameCount();
-
-    // Clamp frames.
-    if ( frame < 0 )
-        frame = 0;
-    else if (frame >= imageFrameCount )
-        frame = imageFrameCount-1;
-
     // Calculate if frame has changed.
     bool frameChanged = (mCurrentFrameIndex != mLastFrameIndex);
 
@@ -569,6 +704,7 @@ void ImageFrameProviderCore::clearAssets( void )
 
     // Reset remaining state.
     mImageFrame = 0;
+    mImageNameFrame = StringTable->EmptyString;
     mStaticProvider = true;
     setProcessTicks( false );
 }

+ 9 - 2
engine/source/2d/core/ImageFrameProviderCore.h

@@ -62,8 +62,11 @@ protected:
     bool                                    mSelfTick;
 
     bool                                    mStaticProvider;
+    
+    bool                                    mUsingNameFrame;
 
     U32                                     mImageFrame;
+    StringTableEntry                        mImageNameFrame;
     AssetPtr<ImageAsset>*                   mpImageAsset;
     AssetPtr<AnimationAsset>*               mpAnimationAsset;
 
@@ -112,9 +115,12 @@ public:
     /// Static-Image Frame.
     inline bool setImage( const char* pImageAssetId ) { return setImage( pImageAssetId, mImageFrame ); }
     virtual bool setImage( const char* pImageAssetId, const U32 frame );
+    virtual bool setImage( const char* pImageAssetId, const char* pNameFrame );
     inline StringTableEntry getImage( void ) const{ return mpImageAsset->getAssetId(); }
     virtual bool setImageFrame( const U32 frame );
     inline U32 getImageFrame( void ) const { return mImageFrame; }
+    virtual bool setImageFrameByName( const char* frame );
+    inline StringTableEntry getImageFrameByName( void ) const { return mImageNameFrame; }
 
     /// Animated-Image Frame.
     virtual bool setAnimation( const char* pAnimationAssetId );
@@ -132,12 +138,13 @@ public:
 
     /// Frame provision.
     inline bool isStaticFrameProvider( void ) const { return mStaticProvider; }
+    inline bool isUsingNamedImageFrame( void ) const { return mUsingNameFrame; }
     inline TextureHandle& getProviderTexture( void ) const { return !validRender() ? BadTextureHandle : isStaticFrameProvider() ? (*mpImageAsset)->getImageTexture() : (*mpAnimationAsset)->getImage()->getImageTexture(); };
-    inline const ImageAsset::FrameArea& getProviderImageFrameArea( void ) const  { return !validRender() ? BadFrameArea : isStaticFrameProvider() ? (*mpImageAsset)->getImageFrameArea(mImageFrame) : (*mpAnimationAsset)->getImage()->getImageFrameArea(getCurrentAnimationFrame()); };
-
+    const ImageAsset::FrameArea& getProviderImageFrameArea( void ) const;
     inline const AnimationAsset* getCurrentAnimation( void ) const { return mpAnimationAsset->notNull() ? *mpAnimationAsset : NULL; };
     inline const StringTableEntry getCurrentAnimationAssetId( void ) const { return mpAnimationAsset->getAssetId(); };
     const U32 getCurrentAnimationFrame( void ) const;
+    const char* getCurrentAnimationFrameName( void ) const;
     inline const F32 getCurrentAnimationTime( void ) const { return mCurrentTime; };
 
     void clearAssets( void );

+ 1 - 0
engine/source/2d/core/SpriteBase.cc

@@ -56,6 +56,7 @@ void SpriteBase::initPersistFields()
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, SpriteBase), &setImage, &getImage, &writeImage, "");
     addProtectedField("Frame", TypeS32, Offset(mImageFrame, SpriteBase), &setImageFrame, &defaultProtectedGetFn, &writeImageFrame, "");
+    addProtectedField("FrameName", TypeString, Offset(mImageFrame, SpriteBase), &setImageNameFrame, &defaultProtectedGetFn, &writeImageNameFrame, "");
     addProtectedField("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, SpriteBase), &setAnimation, &getAnimation, &writeAnimation, "");
 }
 

+ 10 - 8
engine/source/2d/core/SpriteBase.h

@@ -57,14 +57,16 @@ protected:
     virtual void onAnimationEnd( void );
 
 protected:
-    static bool setImage(void* obj, const char* data)                       { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImage(data); return false; };
-    static const char* getImage(void* obj, const char* data)                { return DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->getImage(); }
-    static bool writeImage( void* obj, StringTableEntry pFieldName )        { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
-    static bool setImageFrame(void* obj, const char* data)                  { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImageFrame(dAtoi(data)); return false; };
-    static bool writeImageFrame( void* obj, StringTableEntry pFieldName )   { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
-    static bool setAnimation(void* obj, const char* data)                   { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setAnimation(data); return false; };
-    static const char* getAnimation(void* obj, const char* data)            { return DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->getAnimation(); }
-    static bool writeAnimation( void* obj, StringTableEntry pFieldName )    { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mAnimationAsset.notNull(); }
+    static bool setImage(void* obj, const char* data)                           { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImage(data); return false; };
+    static const char* getImage(void* obj, const char* data)                    { return DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->getImage(); }
+    static bool writeImage( void* obj, StringTableEntry pFieldName )            { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
+    static bool setImageFrame(void* obj, const char* data)                      { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImageFrame(dAtoi(data)); return false; };
+    static bool setImageNameFrame(void* obj, const char* data)                  { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setImageFrameByName(data); return false; };
+    static bool writeImageFrame( void* obj, StringTableEntry pFieldName )       { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mImageAsset.notNull(); }
+    static bool writeImageNameFrame( void* obj, StringTableEntry pFieldName )   { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( !pCastObject->isUsingNamedImageFrame() ) return false; return pCastObject->mImageAsset.notNull(); }
+    static bool setAnimation(void* obj, const char* data)                       { DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->setAnimation(data); return false; };
+    static const char* getAnimation(void* obj, const char* data)                { return DYNAMIC_VOID_CAST_TO(SpriteBase, ImageFrameProvider, obj)->getAnimation(); }
+    static bool writeAnimation( void* obj, StringTableEntry pFieldName )        { SpriteBase* pCastObject = static_cast<SpriteBase*>(obj); if ( pCastObject->isStaticFrameProvider() ) return false; return pCastObject->mAnimationAsset.notNull(); }
 };
 
 #endif // _SPRITE_BASE_H_

+ 63 - 7
engine/source/2d/core/SpriteBase_ScriptBinding.h

@@ -34,16 +34,33 @@ ConsoleMethodWithDocs(SpriteBase, isStaticFrameProvider, ConsoleBool, 2, 2, ())
 
 /*! Sets the sprite image and optionally frame.
     @param imageAssetId The image asset Id to display
-    @param frame The frame of the image to display
+    @param frame The numerical or named frame of the image to display
     @return Returns true on success.
 */
-ConsoleMethodWithDocs(SpriteBase, setImage, ConsoleBool, 3, 4, (string imageAssetId, [int frame]))
+ConsoleMethodWithDocs(SpriteBase, setImage, ConsoleBool, 3, 4, (imageAssetId, [frame]))
 {
-    // Calculate Frame.
-    U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
-
-    // Set image.
-    return static_cast<ImageFrameProvider*>(object)->setImage( argv[2], frame );
+    // Was a frame specified?
+    if (argc >= 4)
+    {
+        // Was it a number or a string?
+        if (!dIsalpha(*argv[3]))
+        {
+            // Fetch the numerical frame and set the image
+            const U32 frame = argc >= 4 ? dAtoi(argv[3]) : 0;
+            return static_cast<ImageFrameProvider*>(object)->setImage( argv[2], frame );
+        }
+        else
+        {
+            // Set the image and pass the named frame string
+            return static_cast<ImageFrameProvider*>(object)->setImage( argv[2], argv[3] );
+        }
+    }
+    else
+    {
+        // Frame was not specified, use default 0 and set the image
+        const U32 frame = 0;
+        return static_cast<ImageFrameProvider*>(object)->setImage( argv[2], frame );
+    }
 }
 
 //------------------------------------------------------------------------------
@@ -106,6 +123,45 @@ ConsoleMethodWithDocs(SpriteBase, getImageFrame, ConsoleInt, 2, 2, ())
 
 //------------------------------------------------------------------------------
 
+/*! Sets the image frame using a string.
+    @param frame - The name of the frame.
+    @return True on success.
+*/
+ConsoleMethodWithDocs(SpriteBase, setImageFrameName, ConsoleBool, 3, 3,  (frame))
+{
+    // Are we in static mode?
+    if ( !object->isStaticFrameProvider() )
+    {
+        // No, so warn.
+        Con::warnf( "SpriteBase::setImageFrameName() - Method invalid, not in static mode." );
+        return false;
+    }
+    
+    // Set image Frame.
+    return static_cast<ImageFrameProvider*>(object)->setImageFrameByName( argv[2] );
+}
+
+//------------------------------------------------------------------------------
+
+/*! Gets the current image frame name.
+    @return The current image frame name.
+*/
+ConsoleMethodWithDocs(SpriteBase, getImageFrameName, ConsoleString, 2, 2, ())
+{
+    // Are we in static mode?
+    if ( !object->isStaticFrameProvider() )
+    {
+        // No, so warn.
+        Con::warnf( "SpriteBase::getImageFrameName() - Method invalid, not in static mode." );
+        return false;
+    }
+    
+    // Get image Frame.
+    return static_cast<ImageFrameProvider*>(object)->getImageFrameByName();
+}
+
+//------------------------------------------------------------------------------
+
 /*! Plays an animation.
     @param animationAssetId The animation asset Id to play.
     @return Returns true on success.

+ 1 - 1
engine/source/2d/gui/guiSpriteCtrl.cc

@@ -125,7 +125,7 @@ bool GuiSpriteCtrl::setImage( const char* pImageAssetId )
 		return true;
 
 	// Call parent.
-	if ( !ImageFrameProvider::setImage( pImageAssetId, 0 ) )
+	if ( !ImageFrameProvider::setImage( pImageAssetId, mImageFrame ) )
 		return false;
 
     // Update control.

+ 6 - 2
engine/source/2d/sceneobject/ParticlePlayer.cc

@@ -794,6 +794,7 @@ bool ParticlePlayer::play( const bool resetParticles )
         // Fetch the emitter node.
         EmitterNode* pEmitterNode = *emitterItr;
 		pEmitterNode->setPaused(false);
+        
         // Reset the time since last generation.
         pEmitterNode->setTimeSinceLastGeneration( 0.0f );
     }
@@ -1272,7 +1273,10 @@ void ParticlePlayer::configureParticle( EmitterNode* pEmitterNode, ParticleSyste
         else
         {
             // No, so set the emitter image frame.
-            frameProvider.setImageFrame( pParticleAssetEmitter->getImageFrame() );
+            if (pParticleAssetEmitter->isUsingNamedImageFrame())
+                frameProvider.setImageFrameByName( pParticleAssetEmitter->getImageFrameName() );
+            else
+                frameProvider.setImageFrame( pParticleAssetEmitter->getImageFrame() );
         }
     }
     else
@@ -1534,7 +1538,7 @@ void ParticlePlayer::initializeParticleAsset( void )
 
         // Skip if the emitter does not have a valid assigned asset to render.
         if (( pParticleAssetEmitter->isStaticFrameProvider() && (imageAsset.isNull() || imageAsset->getFrameCount() == 0 ) ) ||
-            ( !pParticleAssetEmitter->isStaticFrameProvider() && (animationAsset.isNull() || animationAsset->getValidatedAnimationFrames().size() == 0 ) ) )
+            ( !pParticleAssetEmitter->isStaticFrameProvider() && (animationAsset.isNull() || (animationAsset->getValidatedAnimationFrames().size() == 0 && animationAsset->getValidatedNamedAnimationFrames().size() == 0)) ) )
             continue;
 
         // Create a new emitter node.