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.
     // Set Vector Associations.
     VECTOR_SET_ASSOCIATION( mAnimationFrames );
     VECTOR_SET_ASSOCIATION( mAnimationFrames );
+    VECTOR_SET_ASSOCIATION( mNamedAnimationFrames );
     VECTOR_SET_ASSOCIATION( mValidatedFrames );    
     VECTOR_SET_ASSOCIATION( mValidatedFrames );    
 }
 }
 
 
@@ -104,9 +106,11 @@ void AnimationAsset::initPersistFields()
 
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, AnimationAsset), &setImage, &defaultProtectedGetFn, &writeImage, "");
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, AnimationAsset), &setImage, &defaultProtectedGetFn, &writeImage, "");
     addProtectedField("AnimationFrames", TypeS32Vector, Offset(mAnimationFrames, AnimationAsset), &setAnimationFrames, &defaultProtectedGetFn, &writeAnimationFrames, "");
     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("AnimationTime", TypeF32, Offset(mAnimationTime, AnimationAsset), &setAnimationTime, &defaultProtectedGetFn, &defaultProtectedWriteFn, "");
     addProtectedField("AnimationCycle", TypeBool, Offset(mAnimationCycle, AnimationAsset), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "");
     addProtectedField("AnimationCycle", TypeBool, Offset(mAnimationCycle, AnimationAsset), &setAnimationCycle, &defaultProtectedGetFn, &writeAnimationCycle, "");
     addProtectedField("RandomStart", TypeBool, Offset(mRandomStart, AnimationAsset), &setRandomStart, &defaultProtectedGetFn, &writeRandomStart, "");
     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.
     // Copy state.
     pAsset->setImage( getImage().getAssetId() );
     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->setAnimationTime( getAnimationTime() );
     pAsset->setAnimationCycle( getAnimationCycle() );
     pAsset->setAnimationCycle( getAnimationCycle() );
     pAsset->setRandomStart( getRandomStart() );
     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" ) ) );
         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.
     // Validate frames.
     validateFrames();
     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.
     // Clear validated frames.
     mValidatedFrames.clear();
     mValidatedFrames.clear();
 
 
-    // Finish if we don't have a valid image asset.
-    if ( mImageAsset.isNull() )
-        return;
-
     // Fetch Animation Frame Count.
     // Fetch Animation Frame Count.
     const U32 animationFrameCount = (U32)mAnimationFrames.size();
     const U32 animationFrameCount = (U32)mAnimationFrames.size();
 
 
@@ -290,12 +337,13 @@ void AnimationAsset::validateFrames( void )
         if ( frame < 0 || frame >= imageAssetFrameCount )
         if ( frame < 0 || frame >= imageAssetFrameCount )
         {
         {
             // No, warn.
             // 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(),
                 getAssetName(),
                 frame,
                 frame,
                 frameIndex,
                 frameIndex,
                 mImageAsset.getAssetId() );
                 mImageAsset.getAssetId() );
 
 
+            // Set the frame to a valid one.
             if ( frame < 0 )
             if ( frame < 0 )
                 frame = 0;
                 frame = 0;
             else if ( frame >= imageAssetFrameCount )
             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
 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:
 private:
     typedef AssetBase  Parent;
     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:
 public:
     AnimationAsset();
     AnimationAsset();
@@ -67,15 +71,23 @@ public:
     inline const Vector<S32>& getSpecifiedAnimationFrames( void ) const { return mAnimationFrames; }
     inline const Vector<S32>& getSpecifiedAnimationFrames( void ) const { return mAnimationFrames; }
     inline const Vector<S32>& getValidatedAnimationFrames( void ) const { return mValidatedFrames; }
     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 );
     void            setAnimationTime( const F32 animationTime );
     inline F32      getAnimationTime( void ) const                      { return mAnimationTime; }
     inline F32      getAnimationTime( void ) const                      { return mAnimationTime; }
     void            setAnimationCycle( const bool animationCycle );
     void            setAnimationCycle( const bool animationCycle );
     inline bool     getAnimationCycle( void ) const                     { return mAnimationCycle; }
     inline bool     getAnimationCycle( void ) const                     { return mAnimationCycle; }
     void            setRandomStart( const bool randomStart );
     void            setRandomStart( const bool randomStart );
     inline bool     getRandomStart( void ) const                        { return mRandomStart; }
     inline bool     getRandomStart( void ) const                        { return mRandomStart; }
+    void            setNamedCellsMode( const bool namedCellsMode );
+    inline bool     getNamedCellsMode( void ) const                     { return mNamedCellsMode; }
 
 
     // Frame validation.
     // Frame validation.
     void            validateFrames( void );
     void            validateFrames( void );
+    void            validateNumericalFrames( void );
+    void            validateNamedFrames( void );
 
 
     // Asset validation.
     // Asset validation.
     virtual bool    isAssetValid( void ) const;
     virtual bool    isAssetValid( void ) const;
@@ -88,15 +100,19 @@ protected:
     virtual void onAssetRefresh( void );
     virtual void onAssetRefresh( void );
 
 
 protected:
 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_
 #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))
 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] );
     object->setAnimationFrames( argv[2] );
 }
 }
 
 
@@ -59,6 +67,14 @@ ConsoleMethodWithDocs(AnimationAsset, setAnimationFrames, ConsoleVoid, 3, 3, (an
 */
 */
 ConsoleMethodWithDocs(AnimationAsset, getAnimationFrames, ConsoleString, 2, 3, ([bool validatedFrames]))
 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.
     // Fetch a return buffer.
     S32 bufferSize = 4096;
     S32 bufferSize = 4096;
     char* pBuffer = Con::getReturnBuffer( bufferSize );
     char* pBuffer = Con::getReturnBuffer( bufferSize );
@@ -93,6 +109,14 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationFrames, ConsoleString, 2, 3, (
 */
 */
 ConsoleMethodWithDocs(AnimationAsset, getAnimationFrameCount, ConsoleInt, 2, 3, ([bool validatedFrames]))
 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.
     // Fetch validated frames flag.
     const bool validatedFrames = argc >= 3 ? dAtob( argv[2] ) : false;
     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.
 /*! Sets the total time to cycle through all animation frames.
     @param animationTime The total time to cycle through all animation frames.
     @param animationTime The total time to cycle through all animation frames.
     @return No return value.
     @return No return value.
@@ -145,4 +255,25 @@ ConsoleMethodWithDocs(AnimationAsset, getAnimationCycle, ConsoleBool, 2, 2, ())
     return object->getAnimationCycle();
     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)
 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 cellNodeName                = StringTable->insert( "Cell" );
+static StringTableEntry cellRegionName              = StringTable->insert( "RegionName" );
 static StringTableEntry cellOffsetName              = StringTable->insert( "Offset" );
 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 cellWidthName               = StringTable->insert( "Width" );
 static StringTableEntry cellHeightName              = StringTable->insert( "Height" );
 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);
             return((ImageAsset::TextureFilterMode)textureFilterLookup[i].index);
 
 
     // Warn.
     // Warn.
-    Con::warnf("ImageAsset::getFilterModeEnum() - Invalid filter-mode '%s'", label );
+    Con::warnf( "ImageAsset::getFilterModeEnum() - Invalid filter-mode '%s'", label );
 
 
     return ImageAsset::FILTER_INVALID;
     return ImageAsset::FILTER_INVALID;
 }
 }
@@ -148,7 +152,7 @@ const char* ImageAsset::getFilterModeDescription( ImageAsset::TextureFilterMode
             return textureFilterLookup[i].label;
             return textureFilterLookup[i].label;
 
 
     // Warn.
     // Warn.
-    Con::warnf("ImageAsset::getFilterModeDescription() - Invalid filter-mode." );
+    Con::warnf( "ImageAsset::getFilterModeDescription() - Invalid filter-mode." );
 
 
     return StringTable->EmptyString;
     return StringTable->EmptyString;
 }
 }
@@ -159,7 +163,6 @@ ImageAsset::ImageAsset() :  mImageFile(StringTable->EmptyString),
                             mForce16Bit(false),
                             mForce16Bit(false),
                             mLocalFilterMode(FILTER_INVALID),
                             mLocalFilterMode(FILTER_INVALID),
                             mExplicitMode(false),
                             mExplicitMode(false),
-
                             mCellRowOrder(true),
                             mCellRowOrder(true),
                             mCellOffsetX(0),
                             mCellOffsetX(0),
                             mCellOffsetY(0),
                             mCellOffsetY(0),
@@ -294,7 +297,7 @@ void ImageAsset::copyTo(SimObject* object)
         const FrameArea::PixelArea& pixelArea = getImageFrameArea( index ).mPixelArea;
         const FrameArea::PixelArea& pixelArea = getImageFrameArea( index ).mPixelArea;
 
 
         // Add the explicit cell.
         // 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();
     refreshAsset();
 }
 }
 
 
+//------------------------------------------------------------------------------
+
 S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 {
 {
 	if ( !getExplicitMode() )
 	if ( !getExplicitMode() )
@@ -564,6 +569,8 @@ S32 ImageAsset::getExplicitCellWidth(const S32 cellIndex)
 
 
 }
 }
 
 
+//------------------------------------------------------------------------------
+
 S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
 S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
 {
 {
 	if ( !getExplicitMode() )
 	if ( !getExplicitMode() )
@@ -577,6 +584,27 @@ S32 ImageAsset::getExplicitCellHeight(const S32 cellIndex)
     return(thisCell.mPixelHeight);
     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 )
 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?
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     if ( !getExplicitMode() )
     {
     {
         // No, so warn.
         // 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;
         return false;
     }
     }
 
 
     // Fetch the original image dimensions.
     // Fetch the original image dimensions.
     const S32 imageWidth = getImageWidth();
     const S32 imageWidth = getImageWidth();
     const S32 imageHeight = getImageHeight();
     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.
     // The Cell Offset X needs to be within the image.
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
         return false;
     }
     }
 
 
     // The Cell Offset Y needs to be within the image.
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
         return false;
     }
     }
 
 
@@ -634,7 +671,7 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
         return false;
     }
     }
 
 
@@ -642,12 +679,12 @@ bool ImageAsset::addExplicitCell( const S32 cellOffsetX, const S32 cellOffsetY,
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::addCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::addExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
         return false;
     }
     }
 
 
     // Store frame.
     // Store frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
     mExplicitFrames.push_back( pixelArea );
     mExplicitFrames.push_back( pixelArea );
 
 
     // Refresh the asset.
     // 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?
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     if ( !getExplicitMode() )
     {
     {
         // No, so warn.
         // 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;
         return false;
     }
     }
 
 
@@ -674,12 +711,20 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
 
 
     // Fetch the explicit frame count.
     // Fetch the explicit frame count.
     const S32 explicitFramelCount = mExplicitFrames.size();
     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.
     // The cell index needs to be in range.
     if ( cellIndex < 0 )
     if ( cellIndex < 0 )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
         return false;
     }
     }
 
 
@@ -687,15 +732,15 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
         return false;
     }
     }
 
 
     // The Cell Offset Y needs to be within the image.
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
         return false;
     }
     }
 
 
@@ -703,7 +748,7 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
         return false;
     }
     }
 
 
@@ -711,12 +756,12 @@ bool ImageAsset::insertExplicitCell( const S32 cellIndex, const S32 cellOffsetX,
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::insertCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::insertExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
         return false;
     }
     }
 
 
     // Configure frame.
     // Configure frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
 
 
     // Insert frame appropriately.
     // Insert frame appropriately.
     if ( cellIndex >= explicitFramelCount )
     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?
     // Are we in explicit mode?
     if ( !getExplicitMode() )
     if ( !getExplicitMode() )
     {
     {
         // No, so warn.
         // 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;
         return false;
     }
     }
 
 
@@ -753,12 +798,20 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
 
 
     // Fetch the explicit frame count.
     // Fetch the explicit frame count.
     const S32 explicitFrameCount = mExplicitFrames.size();
     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.
     // The cell index needs to be in range.
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
         return false;
     }
     }
 
 
@@ -766,15 +819,15 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     if ( cellOffsetX < 0 || cellOffsetX >= imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell OffsetX of %d.", cellOffsetX );
         return false;
         return false;
     }
     }
 
 
     // The Cell Offset Y needs to be within the image.
     // The Cell Offset Y needs to be within the image.
-    if ( cellOffsetY < 0 || cellOffsetY >= imageWidth )
+    if ( cellOffsetY < 0 || cellOffsetY >= imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell OffsetY of %d.", cellOffsetY );
         return false;
         return false;
     }
     }
 
 
@@ -782,7 +835,7 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     if ( cellWidth <= 0 || (cellOffsetX+cellWidth) > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Width of %d.", cellWidth );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Width of %d.", cellWidth );
         return false;
         return false;
     }
     }
 
 
@@ -790,12 +843,12 @@ bool ImageAsset::setExplicitCell( const S32 cellIndex, const S32 cellOffsetX, co
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     if ( cellHeight <= 0 || (cellOffsetY+cellHeight) > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::setCell() - Invalid Cell Width of %d.", cellHeight );
+        Con::warnf( "ImageAsset::setExplicitCell() - Invalid Cell Width of %d.", cellHeight );
         return false;
         return false;
     }
     }
 
 
     // Configure frame.
     // Configure frame.
-    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight );
+    FrameArea::PixelArea pixelArea( cellOffsetX, cellOffsetY, cellWidth, cellHeight, regionName );
 
 
     // Set cell.
     // Set cell.
     mExplicitFrames[cellIndex] = pixelArea;
     mExplicitFrames[cellIndex] = pixelArea;
@@ -814,7 +867,7 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
     if ( !getExplicitMode() )
     if ( !getExplicitMode() )
     {
     {
         // No, so warn.
         // 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;
         return false;
     }
     }
 
 
@@ -825,7 +878,7 @@ bool ImageAsset::removeExplicitCell( const S32 cellIndex )
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     if ( cellIndex < 0 || cellIndex >= explicitFrameCount )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::removeCell() - Invalid Cell Index of %d.", cellIndex );
+        Con::warnf( "ImageAsset::removeExplicitCell() - Invalid Cell Index of %d.", cellIndex );
         return false;
         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 )
 void ImageAsset::setTextureFilter( const TextureFilterMode filterMode )
 {
 {
     // Finish if no texture.
     // Finish if no texture.
@@ -1021,7 +1137,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellWidth < 1 || mCellWidth > imageWidth )
     if ( mCellWidth < 1 || mCellWidth > imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell Width of %d.", mCellWidth );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell Width of %d.", mCellWidth );
         return;
         return;
     }
     }
 
 
@@ -1029,7 +1145,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellHeight < 1 || mCellHeight > imageHeight )
     if ( mCellHeight < 1 || mCellHeight > imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell Height of %d.", mCellHeight );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell Height of %d.", mCellHeight );
         return;
         return;
     }
     }
 
 
@@ -1037,7 +1153,7 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellOffsetX < 0 || mCellOffsetX >= imageWidth )
     if ( mCellOffsetX < 0 || mCellOffsetX >= imageWidth )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetX of %d.", mCellOffsetX );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetX of %d.", mCellOffsetX );
         return;
         return;
     }
     }
 
 
@@ -1045,11 +1161,10 @@ void ImageAsset::calculateImplicitMode( void )
     if ( mCellOffsetY < 0 || mCellOffsetY >= imageHeight )
     if ( mCellOffsetY < 0 || mCellOffsetY >= imageHeight )
     {
     {
         // Warn.
         // Warn.
-        Con::warnf("ImageAsset::calculateImage() - Invalid Cell OffsetY of %d.", mCellOffsetY );
+        Con::warnf( "ImageAsset::calculateImage() - Invalid Cell OffsetY of %d.", mCellOffsetY );
         return;
         return;
     }
     }
 
 
-
     // Are we using Cell-StrideX?
     // Are we using Cell-StrideX?
     S32 cellStepX;
     S32 cellStepX;
     if ( mCellStrideX != 0 )
     if ( mCellStrideX != 0 )
@@ -1082,14 +1197,14 @@ void ImageAsset::calculateImplicitMode( void )
     if ( cellFinalPositionX < 0 )
     if ( cellFinalPositionX < 0 )
     {
     {
         // Warn.
         // 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;
         return;
     }
     }
     // Off Right?
     // Off Right?
     else if ( cellFinalPositionX > imageWidth )
     else if ( cellFinalPositionX > imageWidth )
     {
     {
         // Warn.
         // 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;
         return;
     }
     }
 
 
@@ -1099,14 +1214,14 @@ void ImageAsset::calculateImplicitMode( void )
     if ( cellFinalPositionY < 0 )
     if ( cellFinalPositionY < 0 )
     {
     {
         // Warn.
         // 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;
         return;
     }
     }
     // Off Bottom?
     // Off Bottom?
     else if ( cellFinalPositionY > imageHeight )
     else if ( cellFinalPositionY > imageHeight )
     {
     {
         // Warn.
         // 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;
         return;
     }
     }
 
 
@@ -1187,7 +1302,7 @@ void ImageAsset::calculateExplicitMode( void )
         const FrameArea::PixelArea& pixelArea = *frameItr;
         const FrameArea::PixelArea& pixelArea = *frameItr;
 
 
         // Set frame area.
         // 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.
         // Store frame.
         mFrames.push_back( frameArea );
         mFrames.push_back( frameArea );
@@ -1216,22 +1331,26 @@ void ImageAsset::onTamlCustomWrite( TamlCustomNodes& customNodes )
     if ( !mExplicitMode )
     if ( !mExplicitMode )
         return;
         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.
     // Debug Profiling.
     PROFILE_SCOPE(ImageAsset_OnTamlCustomRead);
     PROFILE_SCOPE(ImageAsset_OnTamlCustomRead);
-
+    
     // Call parent.
     // Call parent.
     Parent::onTamlCustomRead( customNodes );
     Parent::onTamlCustomRead( customNodes );
-
+    
     // Find cell custom node.
     // 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;
                 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.
     // Create ImageAsset node element.
     TiXmlElement* pImageAssetNodeElement = new TiXmlElement( "xs: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( "name", buffer );
     pImageAssetNodeElement->SetAttribute( "minOccurs", 0 );
     pImageAssetNodeElement->SetAttribute( "minOccurs", 0 );
     pImageAssetNodeElement->SetAttribute( "maxOccurs", 1 );
     pImageAssetNodeElement->SetAttribute( "maxOccurs", 1 );
@@ -1381,6 +1525,12 @@ static void WriteCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlEleme
     TiXmlElement* pImageAssetComplexTypeElement = new TiXmlElement( "xs:complexType" );
     TiXmlElement* pImageAssetComplexTypeElement = new TiXmlElement( "xs:complexType" );
     pImageAssetElement->LinkEndChild( pImageAssetComplexTypeElement );
     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.
     // Create "Offset" attribute.
     TiXmlElement* pImageAssetOffset = new TiXmlElement( "xs:attribute" );
     TiXmlElement* pImageAssetOffset = new TiXmlElement( "xs:attribute" );
     pImageAssetOffset->SetAttribute( "name", cellOffsetName );
     pImageAssetOffset->SetAttribute( "name", cellOffsetName );

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

@@ -73,17 +73,28 @@ public:
             {
             {
                 setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
                 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 )
             inline void setArea( const S32 pixelFrameOffsetX, const S32 pixelFrameOffsetY, const U32 pixelFrameWidth, const U32 pixelFrameHeight )
             {
             {
                 mPixelOffset.set( pixelFrameOffsetX, pixelFrameOffsetY );
                 mPixelOffset.set( pixelFrameOffsetX, pixelFrameOffsetY );
                 mPixelWidth = pixelFrameWidth;
                 mPixelWidth = pixelFrameWidth;
                 mPixelHeight = pixelFrameHeight;
                 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;
             Point2I mPixelOffset;
             U32 mPixelWidth;
             U32 mPixelWidth;
             U32 mPixelHeight;
             U32 mPixelHeight;
+            StringTableEntry mRegionName;
         };
         };
 
 
 
 
@@ -122,12 +133,25 @@ public:
         {
         {
             setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight, texelWidthScale, texelHeightScale );
             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 )
         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 );
             mPixelArea.setArea( pixelFrameOffsetX, pixelFrameOffsetY, pixelFrameWidth, pixelFrameHeight );
             mTexelArea.setArea( mPixelArea, texelWidthScale, texelHeightScale );
             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;
         PixelArea mPixelArea;
         TexelArea mTexelArea;
         TexelArea mTexelArea;
@@ -207,27 +231,36 @@ public:
     void                    setCellHeight( const S32 cellheight );
     void                    setCellHeight( const S32 cellheight );
     S32                     getCellHeight( void) const						{ return mCellHeight; }
     S32                     getCellHeight( void) const						{ return mCellHeight; }
     S32                     getExplicitCellHeight(const S32 cellIndex);
     S32                     getExplicitCellHeight(const S32 cellIndex);
+    
+    bool                    containsNamedRegion(const char* regionName);
 
 
     inline TextureHandle&   getImageTexture( void )                         { return mImageTextureHandle; }
     inline TextureHandle&   getImageTexture( void )                         { return mImageTextureHandle; }
     inline S32              getImageWidth( void ) const                     { return mImageTextureHandle.getWidth(); }
     inline S32              getImageWidth( void ) const                     { return mImageTextureHandle.getWidth(); }
     inline S32              getImageHeight( void ) const                    { return mImageTextureHandle.getHeight(); }
     inline S32              getImageHeight( void ) const                    { return mImageTextureHandle.getHeight(); }
     inline U32              getFrameCount( void ) const                     { return (U32)mFrames.size(); };
     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( 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() ); };
     inline const void       bindImageTexture( void)                         { glBindTexture( GL_TEXTURE_2D, getImageTexture().getGLName() ); };
     
     
     virtual bool            isAssetValid( void ) const                      { return !mImageTextureHandle.IsNull(); }
     virtual bool            isAssetValid( void ) const                      { return !mImageTextureHandle.IsNull(); }
 
 
     /// Explicit cell control.
     /// Explicit cell control.
     bool                    clearExplicitCells( void );
     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                    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(); }
     inline S32              getExplicitCellCount( void ) const              { return mExplicitFrames.size(); }
     
     
     static TextureFilterMode getFilterModeEnum(const char* label);
     static TextureFilterMode getFilterModeEnum(const char* label);
     static const char* getFilterModeDescription( TextureFilterMode filterMode );
     static const char* getFilterModeDescription( TextureFilterMode filterMode );
+    
+    inline void forceCalculation( void ) { calculateImage(); }
 
 
     /// Declare Console Object.
     /// Declare Console Object.
     DECLARE_CONOBJECT(ImageAsset);
     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();
     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] );
     const S32 cellIndex = dAtoi( argv[2] );
 
 
     return(object->getExplicitCellWidth(cellIndex));
     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] );
     const S32 cellIndex = dAtoi( argv[2] );
 
 
     return(object->getExplicitCellHeight(cellIndex));
     return(object->getExplicitCellHeight(cellIndex));
-
 }
 }
+
 //-----------------------------------------------------------------------------
 //-----------------------------------------------------------------------------
 
 
 /*! Sets the CELL height.
 /*! 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 cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height 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.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
     @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.
     // Fetch offsets.
     const S32 cellOffsetX = dAtoi( argv[2] );
     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 cellWidth = dAtoi( argv[4] );
     const S32 cellHeight = dAtoi (argv[5] );
     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 cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height 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.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
     @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.
     // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
     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 cellWidth = dAtoi( argv[5] );
     const S32 cellHeight = dAtoi (argv[6] );
     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 cellOffsetY The offset in the Y axis to the top-left of the cell.
     @param cellWidth The width of the cell.
     @param cellWidth The width of the cell.
     @param cellHeight The height 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.
     The image asset must be in explicit mode for this operation to succeed.
     @return Whether the operation was successful or not.
     @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.
     // Fetch cell index.
     const S32 cellIndex = dAtoi( argv[2] );
     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 cellWidth = dAtoi( argv[5] );
     const S32 cellHeight = dAtoi (argv[6] );
     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.
     // Register for refresh notifications.
     mImageAsset.registerRefreshNotify( this );
     mImageAsset.registerRefreshNotify( this );
     mAnimationAsset.registerRefreshNotify( this );
     mAnimationAsset.registerRefreshNotify( this );
+    
+    mImageFrameName = "";
 }
 }
 
 
 //------------------------------------------------------------------------------
 //------------------------------------------------------------------------------
@@ -246,6 +248,7 @@ void ParticleAssetEmitter::initPersistFields()
 
 
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, ParticleAssetEmitter), &setImage, &getImage, &writeImage, "");
     addProtectedField("Image", TypeImageAssetPtr, Offset(mImageAsset, ParticleAssetEmitter), &setImage, &getImage, &writeImage, "");
     addProtectedField("Frame", TypeS32, Offset(mImageFrame, ParticleAssetEmitter), &setImageFrame, &defaultProtectedGetFn, &writeImageFrame, "");
     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("RandomImageFrame", TypeBool, Offset(mRandomImageFrame, ParticleAssetEmitter), &setRandomImageFrame, &defaultProtectedGetFn, &writeRandomImageFrame, "");
     addProtectedField("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, ParticleAssetEmitter), &setAnimation, &getAnimation, &writeAnimation, "");
     addProtectedField("Animation", TypeAnimationAssetPtr, Offset(mAnimationAsset, ParticleAssetEmitter), &setAnimation, &getAnimation, &writeAnimation, "");
 }
 }
@@ -291,10 +294,15 @@ void ParticleAssetEmitter::copyTo(SimObject* object)
    pParticleAssetEmitter->setAlphaTest( getAlphaTest() );
    pParticleAssetEmitter->setAlphaTest( getAlphaTest() );
 
 
    pParticleAssetEmitter->setRandomImageFrame( getRandomImageFrame() );
    pParticleAssetEmitter->setRandomImageFrame( getRandomImageFrame() );
+
+   // Static provider?
    if ( pParticleAssetEmitter->isStaticFrameProvider() )
    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
    else
    {
    {
@@ -400,6 +408,9 @@ bool ParticleAssetEmitter::setImage( const char* pAssetId, U32 frame )
         mImageFrame = 0;
         mImageFrame = 0;
     }
     }
 
 
+    // Using a numerical frame index
+    mUsingFrameName = false;
+
     // Refresh the asset.
     // Refresh the asset.
     refreshAsset();
     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 )
 bool ParticleAssetEmitter::setImageFrame( const U32 frame )
 {
 {
     // Check Existing Image.
     // Check Existing Image.
@@ -433,6 +491,9 @@ bool ParticleAssetEmitter::setImageFrame( const U32 frame )
     // Set Frame.
     // Set Frame.
     mImageFrame = frame;
     mImageFrame = frame;
 
 
+    // Using a numerical frame index.
+    mUsingFrameName = false;
+
     // Refresh the asset.
     // Refresh the asset.
     refreshAsset();
     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 )
 bool ParticleAssetEmitter::setAnimation( const char* pAnimationAssetId )
 {
 {
     // Sanity!
     // Sanity!

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

@@ -107,8 +107,10 @@ private:
     bool                                    mStaticMode;
     bool                                    mStaticMode;
     AssetPtr<ImageAsset>                    mImageAsset;
     AssetPtr<ImageAsset>                    mImageAsset;
     U32                                     mImageFrame;
     U32                                     mImageFrame;
+    StringTableEntry                        mImageFrameName;
     bool                                    mRandomImageFrame;
     bool                                    mRandomImageFrame;
     AssetPtr<AnimationAsset>                mAnimationAsset;
     AssetPtr<AnimationAsset>                mAnimationAsset;
+    bool                                    mUsingFrameName;
 
 
     /// Particle fields.
     /// Particle fields.
     ParticleAssetFieldCollection            mParticleFields;
     ParticleAssetFieldCollection            mParticleFields;
@@ -182,10 +184,14 @@ public:
     inline bool getOldestInFront( void ) const { return mOldestInFront; }
     inline bool getOldestInFront( void ) const { return mOldestInFront; }
    
    
     inline bool isStaticFrameProvider( void ) const { return mStaticMode; }
     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 U32 frame = 0 );
+    bool setImage( const char* pAssetId, const char* frameName);
     inline StringTableEntry getImage( void ) const { return mImageAsset.getAssetId(); }
     inline StringTableEntry getImage( void ) const { return mImageAsset.getAssetId(); }
     bool setImageFrame( const U32 frame );
     bool setImageFrame( const U32 frame );
     inline U32 getImageFrame( void ) const { return mImageFrame; }
     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 void setRandomImageFrame( const bool randomImageFrame ) { mRandomImageFrame = randomImageFrame; }
     inline bool getRandomImageFrame( void ) const { return mRandomImageFrame; }
     inline bool getRandomImageFrame( void ) const { return mRandomImageFrame; }
     bool setAnimation( const char* animationName );
     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     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     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     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     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     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; };
     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]))
 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.
 /*! Sets the emitter to use the specified animation asset Id.
     @param animationAssetId The animation asset Id to use.
     @param animationAssetId The animation asset Id to use.
     @return Whether the operation was successful or not.
     @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.
         // Yes, so use the image/frame if we have an asset.
         if ( mpImageAsset->notNull() )
         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() )
     else if ( mpAnimationAsset->notNull() )
     {
     {
@@ -178,7 +184,10 @@ bool ImageFrameProviderCore::validRender( void ) const
     if ( isStaticFrameProvider() )
     if ( isStaticFrameProvider() )
     {
     {
         // Yes, so we must have an image asset and the frame must be in bounds.
         // 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.
     // 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(
 void ImageFrameProviderCore::render(
     const bool flipX,
     const bool flipX,
     const bool flipY,
     const bool flipY,
@@ -254,7 +284,7 @@ void ImageFrameProviderCore::renderGui( GuiControl& owner, Point2I offset, const
         RectI destinationRegion(offset, owner.mBounds.extent);
         RectI destinationRegion(offset, owner.mBounds.extent);
 
 
         // Render image.
         // Render image.
-		dglSetBitmapModulation( owner.mProfile->mFillColor );
+        dglSetBitmapModulation( owner.mProfile->mFillColor );
         dglDrawBitmapStretchSR( getProviderTexture(), destinationRegion, sourceRegion );
         dglDrawBitmapStretchSR( getProviderTexture(), destinationRegion, sourceRegion );
         dglClearBitmapModulation();
         dglClearBitmapModulation();
     }
     }
@@ -283,6 +313,9 @@ bool ImageFrameProviderCore::setImage( const char* pImageAssetId, const U32 fram
 
 
     // Set as static provider.
     // Set as static provider.
     mStaticProvider = true;
     mStaticProvider = true;
+    
+    // Using a numerical frame index.
+    mUsingNameFrame = false;
 
 
     // Turn-off tick processing.
     // Turn-off tick processing.
     setProcessTicks( false );
     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 )
 bool ImageFrameProviderCore::setImageFrame( const U32 frame )
 {
 {
     // Check Existing Image.
     // Check Existing Image.
@@ -309,7 +373,7 @@ bool ImageFrameProviderCore::setImageFrame( const U32 frame )
     if ( frame >= (*mpImageAsset)->getFrameCount() )
     if ( frame >= (*mpImageAsset)->getFrameCount() )
     {
     {
         // Warn.
         // 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 Here.
         return false;
         return false;
     }
     }
@@ -317,6 +381,40 @@ bool ImageFrameProviderCore::setImageFrame( const U32 frame )
     // Set Frame.
     // Set Frame.
     mImageFrame = 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 Okay.
     return true;
     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
 bool ImageFrameProviderCore::isAnimationValid( void ) const
 {
 {
     // Not valid if no animation asset.
     // Not valid if no animation asset.
     if ( mpAnimationAsset->isNull() )
     if ( mpAnimationAsset->isNull() )
         return false;
         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.
     // Not valid if current frame index is out of bounds of the validated frames.
-    if ( mCurrentFrameIndex >= validatedFrames.size() )
+    if ( mCurrentFrameIndex >= validatedFrameSize )
         return false;
         return false;
 
 
     // Fetch image asset.
     // Fetch image asset.
@@ -359,12 +477,23 @@ bool ImageFrameProviderCore::isAnimationValid( void ) const
     if ( imageAsset.isNull() )
     if ( imageAsset.isNull() )
         return false;
         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.
     // Valid.
     return true;
     return true;
@@ -419,10 +548,15 @@ bool ImageFrameProviderCore::playAnimation( const AssetPtr<AnimationAsset>& anim
     mStaticProvider = false;
     mStaticProvider = false;
 
 
     // Fetch validated frames.
     // 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.
     // 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() );
         Con::warnf( "ImageFrameProviderCore::playAnimation() - Cannot play AnimationAsset '%s' - Animation has no validated frames!", mpAnimationAsset->getAssetId() );
         return false;
         return false;
@@ -432,13 +566,13 @@ bool ImageFrameProviderCore::playAnimation( const AssetPtr<AnimationAsset>& anim
     mpAnimationAsset->setAssetId( animationAsset.getAssetId() );
     mpAnimationAsset->setAssetId( animationAsset.getAssetId() );
 
 
     // Set Maximum Frame Index.
     // Set Maximum Frame Index.
-    mMaxFrameIndex = validatedFrames.size()-1;
+    mMaxFrameIndex = validatedFrameSize-1;
 
 
     // Calculate Total Integration Time.
     // Calculate Total Integration Time.
     mTotalIntegrationTime = (*mpAnimationAsset)->getAnimationTime();
     mTotalIntegrationTime = (*mpAnimationAsset)->getAnimationTime();
 
 
     // Calculate Frame Integration Time.
     // Calculate Frame Integration Time.
-    mFrameIntegrationTime = mTotalIntegrationTime / validatedFrames.size();
+    mFrameIntegrationTime = mTotalIntegrationTime / validatedFrameSize;
 
 
     // No, so random Start?
     // No, so random Start?
     if ( (*mpAnimationAsset)->getRandomStart() )
     if ( (*mpAnimationAsset)->getRandomStart() )
@@ -477,12 +611,25 @@ bool ImageFrameProviderCore::updateAnimation( const F32 elapsedTime )
     if ( mAnimationFinished )
     if ( mAnimationFinished )
         return false;
         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.
     // Calculate scaled time.
     const F32 scaledTime = elapsedTime * mAnimationTimeScale;
     const F32 scaledTime = elapsedTime * mAnimationTimeScale;
@@ -506,18 +653,6 @@ bool ImageFrameProviderCore::updateAnimation( const F32 elapsedTime )
     // Calculate Current Frame.
     // Calculate Current Frame.
     mCurrentFrameIndex = (S32)(mCurrentModTime / mFrameIntegrationTime);
     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.
     // Calculate if frame has changed.
     bool frameChanged = (mCurrentFrameIndex != mLastFrameIndex);
     bool frameChanged = (mCurrentFrameIndex != mLastFrameIndex);
 
 
@@ -569,6 +704,7 @@ void ImageFrameProviderCore::clearAssets( void )
 
 
     // Reset remaining state.
     // Reset remaining state.
     mImageFrame = 0;
     mImageFrame = 0;
+    mImageNameFrame = StringTable->EmptyString;
     mStaticProvider = true;
     mStaticProvider = true;
     setProcessTicks( false );
     setProcessTicks( false );
 }
 }

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

@@ -62,8 +62,11 @@ protected:
     bool                                    mSelfTick;
     bool                                    mSelfTick;
 
 
     bool                                    mStaticProvider;
     bool                                    mStaticProvider;
+    
+    bool                                    mUsingNameFrame;
 
 
     U32                                     mImageFrame;
     U32                                     mImageFrame;
+    StringTableEntry                        mImageNameFrame;
     AssetPtr<ImageAsset>*                   mpImageAsset;
     AssetPtr<ImageAsset>*                   mpImageAsset;
     AssetPtr<AnimationAsset>*               mpAnimationAsset;
     AssetPtr<AnimationAsset>*               mpAnimationAsset;
 
 
@@ -112,9 +115,12 @@ public:
     /// Static-Image Frame.
     /// Static-Image Frame.
     inline bool setImage( const char* pImageAssetId ) { return setImage( pImageAssetId, mImageFrame ); }
     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 U32 frame );
+    virtual bool setImage( const char* pImageAssetId, const char* pNameFrame );
     inline StringTableEntry getImage( void ) const{ return mpImageAsset->getAssetId(); }
     inline StringTableEntry getImage( void ) const{ return mpImageAsset->getAssetId(); }
     virtual bool setImageFrame( const U32 frame );
     virtual bool setImageFrame( const U32 frame );
     inline U32 getImageFrame( void ) const { return mImageFrame; }
     inline U32 getImageFrame( void ) const { return mImageFrame; }
+    virtual bool setImageFrameByName( const char* frame );
+    inline StringTableEntry getImageFrameByName( void ) const { return mImageNameFrame; }
 
 
     /// Animated-Image Frame.
     /// Animated-Image Frame.
     virtual bool setAnimation( const char* pAnimationAssetId );
     virtual bool setAnimation( const char* pAnimationAssetId );
@@ -132,12 +138,13 @@ public:
 
 
     /// Frame provision.
     /// Frame provision.
     inline bool isStaticFrameProvider( void ) const { return mStaticProvider; }
     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 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 AnimationAsset* getCurrentAnimation( void ) const { return mpAnimationAsset->notNull() ? *mpAnimationAsset : NULL; };
     inline const StringTableEntry getCurrentAnimationAssetId( void ) const { return mpAnimationAsset->getAssetId(); };
     inline const StringTableEntry getCurrentAnimationAssetId( void ) const { return mpAnimationAsset->getAssetId(); };
     const U32 getCurrentAnimationFrame( void ) const;
     const U32 getCurrentAnimationFrame( void ) const;
+    const char* getCurrentAnimationFrameName( void ) const;
     inline const F32 getCurrentAnimationTime( void ) const { return mCurrentTime; };
     inline const F32 getCurrentAnimationTime( void ) const { return mCurrentTime; };
 
 
     void clearAssets( void );
     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("Image", TypeImageAssetPtr, Offset(mImageAsset, SpriteBase), &setImage, &getImage, &writeImage, "");
     addProtectedField("Frame", TypeS32, Offset(mImageFrame, SpriteBase), &setImageFrame, &defaultProtectedGetFn, &writeImageFrame, "");
     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, "");
     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 );
     virtual void onAnimationEnd( void );
 
 
 protected:
 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_
 #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.
 /*! Sets the sprite image and optionally frame.
     @param imageAssetId The image asset Id to display
     @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.
     @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.
 /*! Plays an animation.
     @param animationAssetId The animation asset Id to play.
     @param animationAssetId The animation asset Id to play.
     @return Returns true on success.
     @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;
 		return true;
 
 
 	// Call parent.
 	// Call parent.
-	if ( !ImageFrameProvider::setImage( pImageAssetId, 0 ) )
+	if ( !ImageFrameProvider::setImage( pImageAssetId, mImageFrame ) )
 		return false;
 		return false;
 
 
     // Update control.
     // Update control.

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

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