Browse Source

Complex Colors

This adds a features which I've named Complex Colors. Essentially it allows a separate blend color to be applied to each corner of a sprite. This allows much more complex blending scenarios that can mimic lighting. Complex colors can also be used with the individual sprites of a Composite Sprite.
Peter Robinson 1 year ago
parent
commit
3461e66b62

+ 1 - 0
engine/compilers/VisualStudio 2022/Torque 2D.vcxproj

@@ -1087,6 +1087,7 @@
     <ClInclude Include="..\..\source\game\gameInterface.h" />
     <ClInclude Include="..\..\source\game\gameInterface_ScriptBinding.h" />
     <ClInclude Include="..\..\source\game\version_ScriptBinding.h" />
+    <ClInclude Include="..\..\source\graphics\FadeToColor.h" />
     <ClInclude Include="..\..\source\graphics\gColor.h" />
     <ClInclude Include="..\..\source\graphics\gColor_ScriptBinding.h" />
     <ClInclude Include="..\..\source\graphics\dgl.h" />

+ 3 - 0
engine/compilers/VisualStudio 2022/Torque 2D.vcxproj.filters

@@ -3091,6 +3091,9 @@
     <ClInclude Include="..\..\source\gui\guiColorPopupCtrl_ScriptBinding.h">
       <Filter>gui</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\source\graphics\FadeToColor.h">
+      <Filter>graphics</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Torque 2D.rc" />

+ 25 - 9
engine/source/2d/core/BatchRender.cc

@@ -208,7 +208,10 @@ void BatchRender::SubmitQuad(
         const Vector2& texturePos2,
         const Vector2& texturePos3,
         TextureHandle& texture,
-        const ColorF& color )
+		const ColorF& color0,
+		const ColorF& color1,
+		const ColorF& color2,
+		const ColorF& color3)
 {
     // Sanity!
     AssertFatal( mpDebugStats != NULL, "Debug stats have not been configured." );
@@ -229,13 +232,13 @@ void BatchRender::SubmitQuad(
         if ( mColorCount == 0 )
         {
             // No, so flush if color is specified.
-            if ( color != NoColor  )
+            if ( color0 != NoColor  )
                 flush( mpDebugStats->batchColorStateFlush );
         }
         else
         {
             // Yes, so flush if color is not specified.
-            if ( color == NoColor  )
+            if ( color0 == NoColor  )
                 flush( mpDebugStats->batchColorStateFlush );
         }
     }
@@ -268,13 +271,26 @@ void BatchRender::SubmitQuad(
     }
 
     // Is a color specified?
-    if ( color != NoColor )
+    if ( color0 != NoColor )
     {
-        // Yes, so add colors.
-        mColorBuffer[mColorCount++] = color;
-        mColorBuffer[mColorCount++] = color;
-        mColorBuffer[mColorCount++] = color;
-        mColorBuffer[mColorCount++] = color;
+		// Yes, all or one. Do we have four colors?
+		if (color1 != NoColor && color2 != NoColor && color3 != NoColor)
+		{
+			// We have four colors!
+			// NOTE: We swap #2/#3 here.
+			mColorBuffer[mColorCount++] = color0;
+			mColorBuffer[mColorCount++] = color1;
+			mColorBuffer[mColorCount++] = color3;
+			mColorBuffer[mColorCount++] = color2;
+		}
+		else
+		{
+			// No, we only have one color.
+			mColorBuffer[mColorCount++] = color0;
+			mColorBuffer[mColorCount++] = color0;
+			mColorBuffer[mColorCount++] = color0;
+			mColorBuffer[mColorCount++] = color0;
+		}
     }
 
     // Add textured vertices.

+ 4 - 1
engine/source/2d/core/BatchRender.h

@@ -250,7 +250,10 @@ public:
             const Vector2& texturePos2,
             const Vector2& texturePos3,
             TextureHandle& texture,
-            const ColorF& color = ColorF(-1.0f, -1.0f, -1.0f) );
+			const ColorF& color0 = ColorF(-1.0f, -1.0f, -1.0f),
+			const ColorF& color1 = ColorF(-1.0f, -1.0f, -1.0f),
+			const ColorF& color2 = ColorF(-1.0f, -1.0f, -1.0f),
+			const ColorF& color3 = ColorF(-1.0f, -1.0f, -1.0f));
 
     /// Render a quad immediately without affecting current batch.
     /// All render state should be set beforehand directly.

+ 46 - 0
engine/source/2d/core/ImageFrameProviderCore.cc

@@ -307,6 +307,52 @@ void ImageFrameProviderCore::render(
 		getProviderTexture());
 }
 
+//------------------------------------------------------------------------------
+
+void ImageFrameProviderCore::renderComplex(
+	const bool flipX,
+	const bool flipY,
+	const Vector2& vertexPos0,
+	const Vector2& vertexPos1,
+	const Vector2& vertexPos2,
+	const Vector2& vertexPos3,
+	BatchRender* pBatchRenderer,
+	const ColorF& colorTL,
+	const ColorF& colorTR,
+	const ColorF& colorBR,
+	const ColorF& colorBL) const
+{
+	// Finish if we can't render.
+	if (!validRender())
+		return;
+
+	// Fetch texel area.
+	ImageAsset::FrameArea::TexelArea texelArea = getProviderImageFrameArea().mTexelArea;
+
+	// Flip texture coordinates appropriately.
+	texelArea.setFlip(flipX, flipY);
+
+	// Fetch lower/upper texture coordinates.
+	const Vector2& texLower = texelArea.mTexelLower;
+	const Vector2& texUpper = texelArea.mTexelUpper;
+
+	// Submit batched quad.
+	pBatchRenderer->SubmitQuad(
+		vertexPos0,
+		vertexPos1,
+		vertexPos2,
+		vertexPos3,
+		Vector2(texLower.x, texUpper.y),
+		Vector2(texUpper.x, texUpper.y),
+		Vector2(texUpper.x, texLower.y),
+		Vector2(texLower.x, texLower.y),
+		getProviderTexture(),
+		colorBL,
+		colorBR,
+		colorTR,
+		colorTL);
+}
+
 //------------------------------------------------------------------------------
 void ImageFrameProviderCore::renderGui( GuiControl& owner, Point2I offset, const RectI &updateRect ) const
 {

+ 13 - 0
engine/source/2d/core/ImageFrameProviderCore.h

@@ -132,6 +132,19 @@ public:
 		const ColorF *colorArray,
 		BatchRender* pBatchRenderer) const;
 
+	void renderComplex(
+		const bool flipX,
+		const bool flipY,
+		const Vector2& vertexPos0,
+		const Vector2& vertexPos1,
+		const Vector2& vertexPos2,
+		const Vector2& vertexPos3,
+		BatchRender* pBatchRenderer,
+		const ColorF& colorTL,
+		const ColorF& colorTR,
+		const ColorF& colorBR,
+		const ColorF& colorBL) const;
+
 	void renderGui( GuiControl& owner, Point2I offset, const RectI &updateRect ) const;
 
     /// Static-Image Frame.

+ 48 - 0
engine/source/2d/core/SpriteBatch.cc

@@ -1014,6 +1014,54 @@ F32 SpriteBatch::getSpriteAlphaTest( void ) const
 
 //------------------------------------------------------------------------------
 
+void SpriteBatch::setSpriteUseComplexColor(const bool complexColor)
+{
+	// Finish if a sprite is not selected.
+	if (!checkSpriteSelected())
+		return;
+
+	// Set complex color.
+	mSelectedSprite->setUseComplexColor(complexColor);
+}
+
+//------------------------------------------------------------------------------
+
+bool SpriteBatch::getSpriteUseComplexColor(void) const
+{
+	// Finish if a sprite is not selected.
+	if (!checkSpriteSelected())
+		return true;
+
+	// Get complex color.
+	return mSelectedSprite->getUseComplexColor();
+}
+
+//------------------------------------------------------------------------------
+
+void SpriteBatch::setSpriteComplexColor(const ColorF& blendColor0, const ColorF& blendColor1, const ColorF& blendColor2, const ColorF& blendColor3)
+{
+	// Finish if a sprite is not selected.
+	if (!checkSpriteSelected())
+		return;
+
+	// Set blend color.
+	mSelectedSprite->setComplexColor(blendColor0, blendColor1, blendColor2, blendColor3);
+}
+
+//------------------------------------------------------------------------------
+
+const ColorF& SpriteBatch::getSpriteComplexColor(const S8 cornerID) const
+{
+	// Finish if a sprite is not selected.
+	if (!checkSpriteSelected())
+		return ColorF::StockColor("White");
+
+	// Get blend color.
+	return mSelectedSprite->getComplexColor(cornerID);
+}
+
+//------------------------------------------------------------------------------
+
 void SpriteBatch::setSpriteDataObject( SimObject* pDataObject )
 {
     // Finish if a sprite is not selected.

+ 6 - 0
engine/source/2d/core/SpriteBatch.h

@@ -176,6 +176,12 @@ public:
     const ColorF& getSpriteBlendColor( void ) const;
     void setSpriteBlendAlpha( const F32 alpha );
     F32  getSpriteBlendAlpha( void ) const;
+
+	void setSpriteUseComplexColor(const bool complexColor);
+	bool getSpriteUseComplexColor(void) const;
+
+	void setSpriteComplexColor(const ColorF& blendColor0, const ColorF& blendColor1, const ColorF& blendColor2, const ColorF& blendColor3);
+	const ColorF& getSpriteComplexColor(const S8 cornerID) const;
     
     void setSpriteAlphaTest( const F32 alphaTestMode );
     F32 getSpriteAlphaTest( void ) const;

+ 44 - 0
engine/source/2d/core/SpriteBatchItem.cc

@@ -119,6 +119,12 @@ void SpriteBatchItem::resetState( void )
     mBlendColor = ColorF(1.0f,1.0f,1.0f,1.0f);
     mAlphaTest = -1.0f;
 
+	mUseComplexColor = false;
+	mComplexColor0 = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+	mComplexColor1 = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+	mComplexColor2 = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+	mComplexColor3 = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+
     mDataObject = NULL;
 
     mLocalTransformDirty = true;
@@ -183,6 +189,32 @@ void SpriteBatchItem::copyTo( SpriteBatchItem* pSpriteBatchItem ) const
 
 //------------------------------------------------------------------------------
 
+const ColorF& SpriteBatchItem::getComplexColor(const S8 cornerID)
+{
+	if (cornerID == 1)
+	{
+		return mComplexColor0;
+	}
+	else if (cornerID == 2)
+	{
+		return mComplexColor1;
+	}
+	else if (cornerID == 3)
+	{
+		return mComplexColor2;
+	}
+	else if (cornerID == 4)
+	{
+		return mComplexColor3;
+	}
+	else
+	{
+		return ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+	}
+}
+
+//------------------------------------------------------------------------------
+
 void SpriteBatchItem::prepareRender( SceneRenderRequest* pSceneRenderRequest, const U32 batchTransformId )
 {
     // Debug Profiling.
@@ -244,6 +276,18 @@ void SpriteBatchItem::render( BatchRender* pBatchRenderer, const SceneRenderRequ
 			mExplicitUVs[3],
 			pBatchRenderer);
 	}
+	else if (mUseComplexColor) {
+		Parent::renderComplex(mFlipX, mFlipY,
+			mRenderOOBB[0],
+			mRenderOOBB[1],
+			mRenderOOBB[2],
+			mRenderOOBB[3],
+			pBatchRenderer,
+			mComplexColor0,
+			mComplexColor1,
+			mComplexColor2,
+			mComplexColor3);
+	}
 	else {
 		Parent::render(mFlipX, mFlipY,
 			mRenderOOBB[0],

+ 11 - 0
engine/source/2d/core/SpriteBatchItem.h

@@ -237,6 +237,12 @@ protected:
     ColorF              mBlendColor;
     F32                 mAlphaTest;
 
+	bool				mUseComplexColor;
+	ColorF				mComplexColor0;
+	ColorF				mComplexColor1;
+	ColorF				mComplexColor2;
+	ColorF				mComplexColor3;
+
     SimObjectPtr<SimObject> mDataObject;
 
     Vector2             mLocalOOBB[4];
@@ -314,6 +320,11 @@ public:
     inline void setBlendAlpha( const F32 alpha ) { mBlendColor.alpha = alpha; }
     inline F32 getBlendAlpha( void ) const { return mBlendColor.alpha; }
 
+	inline void setUseComplexColor(const bool complexColor) { mUseComplexColor = complexColor; }
+	inline bool getUseComplexColor(void) const { return mUseComplexColor; }
+	inline void setComplexColor(const ColorF& blendColor0, const ColorF& blendColor1, const ColorF& blendColor2, const ColorF& blendColor3) { mComplexColor0 = blendColor0; mComplexColor1 = blendColor1; mComplexColor2 = blendColor2; mComplexColor3 = blendColor3; }
+	const ColorF& getComplexColor(const S8 cornerID);
+
     inline void setAlphaTest( const F32 alphaTest ) { mAlphaTest = alphaTest; }
     inline F32 getAlphaTest( void ) const { return mAlphaTest; }
 

+ 137 - 0
engine/source/2d/sceneobject/CompositeSprite_ScriptBinding.h

@@ -872,6 +872,30 @@ ConsoleMethodWithDocs(CompositeSprite, getSpriteDstBlendFactor, ConsoleString, 2
 
 //-----------------------------------------------------------------------------
 
+/*! Sets whether sprite uses complex colors or not.
+	@complexColor Whether sprite complex color is on or not.
+	@return No return Value.
+*/
+ConsoleMethodWithDocs(CompositeSprite, setSpriteUseComplexColor, ConsoleVoid, 3, 3, (bool complexColor))
+{
+	// Fetch blend mode.
+	const bool complex = dAtob(argv[2]);
+
+	object->setSpriteUseComplexColor(complex);
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets whether sprite uses complex colors or not.
+	@return (bool complexColor) Whether sprite complex color is on or not.
+*/
+ConsoleMethodWithDocs(CompositeSprite, getSpriteUseComplexColor, ConsoleBool, 2, 2, ())
+{
+	return object->getSpriteUseComplexColor();
+}
+
+//-----------------------------------------------------------------------------
+
 /*! or ( stockColorName ) - Sets the sprite blend color.
     @param red The red value.
     @param green The green value.
@@ -979,6 +1003,119 @@ ConsoleMethodWithDocs(CompositeSprite, getSpriteBlendColor, ConsoleString, 2, 3,
 
 //-----------------------------------------------------------------------------
 
+/*! Sets the sprite complex color.
+	@param topLeft The topLeft color space separated.
+	@param topLeft The topRight color space separated.
+	@param topLeft The bottomRight color space separated.
+	@param topLeft The bottomLeft color space separated.
+	@return No return Value.
+*/
+ConsoleMethodWithDocs(CompositeSprite, setSpriteComplexColor, ConsoleVoid, 6, 6, (topLeft, topRight, bottomRight, bottomLeft))
+{
+	// The corners.
+	ColorF corner0;
+	ColorF corner1;
+	ColorF corner2;
+	ColorF corner3;
+
+	// Space separated.
+	if (argc == 6)
+	{
+		// Grab the element count.
+		const U32 elementCount0 = Utility::mGetStringElementCount(argv[2]);
+		if (elementCount0 == 3)
+		{
+			corner0 = ColorF(dAtof(Utility::mGetStringElement(argv[2], 0)), dAtof(Utility::mGetStringElement(argv[2], 1)), dAtof(Utility::mGetStringElement(argv[2], 2)));
+		}
+		else if (elementCount0 == 4)
+		{
+			corner0 = ColorF(dAtof(Utility::mGetStringElement(argv[2], 0)), dAtof(Utility::mGetStringElement(argv[2], 1)), dAtof(Utility::mGetStringElement(argv[2], 2)), dAtof(Utility::mGetStringElement(argv[2], 3)));
+		}
+		else
+		{
+			Con::warnf("CompositeSprite::setSpriteComplexColor() - Top Left Corner has Invalid Number of arguments!");
+			return;
+		}
+
+		// Grab the element count.
+		const U32 elementCount1 = Utility::mGetStringElementCount(argv[3]);
+		if (elementCount1 == 3)
+		{
+			corner1 = ColorF(dAtof(Utility::mGetStringElement(argv[3], 0)), dAtof(Utility::mGetStringElement(argv[3], 1)), dAtof(Utility::mGetStringElement(argv[3], 2)));
+		}
+		else if (elementCount1 == 4)
+		{
+			corner1 = ColorF(dAtof(Utility::mGetStringElement(argv[3], 0)), dAtof(Utility::mGetStringElement(argv[3], 1)), dAtof(Utility::mGetStringElement(argv[3], 2)), dAtof(Utility::mGetStringElement(argv[3], 3)));
+		}
+		else
+		{
+			Con::warnf("CompositeSprite::setSpriteComplexColor() - Top Right Corner has Invalid Number of arguments!");
+			return;
+		}
+
+		// Grab the element count.
+		const U32 elementCount2 = Utility::mGetStringElementCount(argv[4]);
+		if (elementCount2 == 3)
+		{
+			corner2 = ColorF(dAtof(Utility::mGetStringElement(argv[4], 0)), dAtof(Utility::mGetStringElement(argv[4], 1)), dAtof(Utility::mGetStringElement(argv[4], 2)));
+		}
+		else if (elementCount2 == 4)
+		{
+			corner2 = ColorF(dAtof(Utility::mGetStringElement(argv[4], 0)), dAtof(Utility::mGetStringElement(argv[4], 1)), dAtof(Utility::mGetStringElement(argv[4], 2)), dAtof(Utility::mGetStringElement(argv[4], 3)));
+		}
+		else
+		{
+			Con::warnf("CompositeSprite::setSpriteComplexColor() - Bottom Right Corner has Invalid Number of arguments!");
+			return;
+		}
+
+		// Grab the element count.
+		const U32 elementCount3 = Utility::mGetStringElementCount(argv[5]);
+		if (elementCount3 == 3)
+		{
+			corner3 = ColorF(dAtof(Utility::mGetStringElement(argv[5], 0)), dAtof(Utility::mGetStringElement(argv[5], 1)), dAtof(Utility::mGetStringElement(argv[5], 2)));
+		}
+		else if (elementCount3 == 4)
+		{
+			corner3 = ColorF(dAtof(Utility::mGetStringElement(argv[5], 0)), dAtof(Utility::mGetStringElement(argv[5], 1)), dAtof(Utility::mGetStringElement(argv[5], 2)), dAtof(Utility::mGetStringElement(argv[5], 3)));
+		}
+		else
+		{
+			Con::warnf("CompositeSprite::setSpriteComplexColor() - Bottom Left Corner has Invalid Number of arguments!");
+			return;
+		}
+
+		// Set blend color.
+		object->setSpriteComplexColor(corner0, corner1, corner2, corner3);
+	}
+
+	// Invalid.
+	else
+	{
+		Con::warnf("CompositeSprite::setSpriteComplexColor() - Invalid Number of parameters!");
+		return;
+	}
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the sprite complex color
+	@param cornerID The cornerID whose color will be returned (1 - 4).
+	@return (float red / float green / float blue / float alpha) The sprite corner blend color.
+*/
+ConsoleMethodWithDocs(CompositeSprite, getSpriteComplexColor, ConsoleString, 3, 3, (cornerID))
+{
+	S8 cornerID = dAtoi(argv[2]);
+
+	// Get Blend color.
+	ColorF blendColor = object->getSpriteComplexColor(cornerID);
+
+	// No, so fetch the raw color values.
+	return blendColor.scriptThis();
+}
+
+//-----------------------------------------------------------------------------
+
 /*! Sets the sprite color alpha (transparency).
     The alpha value specifies directly the transparency of the image. A value of 1.0 will not affect the object and a value of 0.0 will make the object completely transparent.
     @param alpha The alpha value.

+ 28 - 22
engine/source/2d/sceneobject/SceneObject.cc

@@ -148,7 +148,7 @@ SceneObject::SceneObject() :
     mGatherContacts(false),
     mpCurrentContacts(NULL),
 
-    /// Render visibility.                                        
+    /// Render visibility.
     mVisible(true),
 
     /// Render blending.
@@ -160,11 +160,7 @@ SceneObject::SceneObject() :
 
 	// Fading.
 	mFadeActive(false),
-	mTargetColor(ColorF(1.0f, 1.0f, 1.0f, 1.0f)),
-	mDeltaRed(1.0f),
-	mDeltaGreen(1.0f),
-	mDeltaBlue(1.0f),
-	mDeltaAlpha(1.0f),
+	mFadeToColor(),
 
     /// Render sorting.
     mSortPoint(0.0f,0.0f),
@@ -699,13 +695,7 @@ void SceneObject::postIntegrate(const F32 totalTime, const F32 elapsedTime, Debu
     }
 
 	// Check to see if we're done fading.
-	if ( mFadeActive && mBlendColor == mTargetColor )
-	{
-		mFadeActive = false;
-
-		PROFILE_SCOPE(SceneObject_onFadeToComplete);
-		Con::executef(this, 1, "onFadeToComplete");
-	}
+	checkFadeComplete();
 
 	//Check to see if we're done growing.
 	if (mGrowActive && mSize == mTargetSize)
@@ -1790,11 +1780,7 @@ bool SceneObject::fadeTo(const ColorF& targetColor, const F32 deltaRed, const F3
 	if (targetColor != mBlendColor)
 	{
 		mFadeActive = true;
-		mTargetColor = targetColor;
-		mDeltaRed = deltaRed;
-		mDeltaGreen = deltaGreen;
-		mDeltaBlue = deltaBlue;
-		mDeltaAlpha = deltaAlpha;
+		mFadeToColor.set(targetColor, deltaRed, deltaGreen, deltaBlue, deltaAlpha);
 	}
 
 	return true;
@@ -4346,10 +4332,7 @@ const char* SceneObject::getDstBlendFactorDescription(const GLenum factor)
 void SceneObject::updateBlendColor(const F32 elapsedTime)
 {
 	// Apply the color deltas to the blendColor to move it toward the targetColor.
-	mBlendColor.red = processEffect(mBlendColor.red, mTargetColor.red, mDeltaRed * elapsedTime);
-	mBlendColor.green = processEffect(mBlendColor.green, mTargetColor.green, mDeltaGreen * elapsedTime);
-	mBlendColor.blue = processEffect(mBlendColor.blue, mTargetColor.blue, mDeltaBlue * elapsedTime);
-	mBlendColor.alpha = processEffect(mBlendColor.alpha, mTargetColor.alpha, mDeltaAlpha * elapsedTime);
+	updateFadeColor(mBlendColor, mFadeToColor, elapsedTime);
 }
 
 //-----------------------------------------------------------------------------
@@ -4466,6 +4449,29 @@ F32 SceneObject::processEffect(const F32 current, const F32 target, const F32 ra
 
 //-----------------------------------------------------------------------------
 
+void SceneObject::updateFadeColor(ColorF& color, FadeToColor& target, const F32 elapsedTime)
+{
+	color.red = processEffect(color.red, target.TargetColor.red, target.DeltaRed * elapsedTime);
+	color.green = processEffect(color.green, target.TargetColor.green, target.DeltaGreen * elapsedTime);
+	color.blue = processEffect(color.blue, target.TargetColor.blue, target.DeltaBlue * elapsedTime);
+	color.alpha = processEffect(color.alpha, target.TargetColor.alpha, target.DeltaAlpha * elapsedTime);
+}
+
+//-----------------------------------------------------------------------------
+
+void SceneObject::checkFadeComplete()
+{
+	if (mFadeActive && mBlendColor == mFadeToColor.TargetColor)
+	{
+		mFadeActive = false;
+
+		PROFILE_SCOPE(SceneObject_onFadeToComplete);
+		Con::executef(this, 1, "onFadeToComplete");
+	}
+}
+
+//-----------------------------------------------------------------------------
+
 static void WriteCircleCustomTamlSchema( const AbstractClassRep* pClassRep, TiXmlElement* pParentElement )
 {
     // Sanity!

+ 8 - 6
engine/source/2d/sceneobject/SceneObject.h

@@ -67,6 +67,10 @@
 #include "component/behaviors/behaviorInstance.h"
 #endif
 
+#ifndef _FADETOCOLOR_H_
+#include "graphics/FadeToColor.h"
+#endif
+
 //-----------------------------------------------------------------------------
 
 struct tDestroyNotification
@@ -205,11 +209,7 @@ protected:
 
 	// Fading
 	bool					mFadeActive;
-	ColorF					mTargetColor;
-	F32						mDeltaRed;
-	F32						mDeltaGreen;
-	F32						mDeltaBlue;
-	F32						mDeltaAlpha;
+	FadeToColor				mFadeToColor;
 
     /// Render sorting.
     Vector2                 mSortPoint;
@@ -269,6 +269,7 @@ protected:
     virtual void            onTamlCustomRead( const TamlCustomNodes& customNodes );
 
 	// Effect Processing.
+	void				updateFadeColor(ColorF& color, FadeToColor& target, const F32 elapsedTime);
 	F32					processEffect( const F32 current, const F32 target, const F32 rate );
 
 public:
@@ -434,7 +435,8 @@ public:
 	bool					fadeTo( const ColorF& targetColor, const F32 deltaRed, const F32 deltaGreen, const F32 deltaBlue, const F32 deltaAlpha );
 	inline void				cancelFadeTo( void )						{ mFadeActive = false; }
 	inline bool				isFadeToComplete( void ) const				{ return !mFadeActive; }
-	void					updateBlendColor( const F32 elapsedTime );
+	virtual void			updateBlendColor( const F32 elapsedTime );
+	virtual void			checkFadeComplete();
 
 	// Grow to
 	bool					growTo( const Vector2& targetSize, const Vector2& deltaSize );

+ 23 - 22
engine/source/2d/sceneobject/SceneObject_ScriptBinding.h

@@ -1770,7 +1770,7 @@ ConsoleMethodWithDocs(SceneObject, getLinearVelocityFromWorldPoint, ConsoleStrin
     // Invalid
     else
     {
-        Con::warnf("Scene::getLinearVelocityFromWorldPoint() - Invalid number of parameters!");
+        Con::warnf("SceneObject::getLinearVelocityFromWorldPoint() - Invalid number of parameters!");
         return NULL;
     }
 
@@ -1804,7 +1804,7 @@ ConsoleMethodWithDocs(SceneObject, getLinearVelocityFromLocalPoint, ConsoleStrin
     // Invalid
     else
     {
-        Con::warnf("Scene::getLinearVelocityFromLocalPoint() - Invalid number of parameters!");
+        Con::warnf("SceneObject::getLinearVelocityFromLocalPoint() - Invalid number of parameters!");
         return NULL;
     }
 
@@ -1915,7 +1915,7 @@ ConsoleMethodWithDocs(SceneObject, moveTo, ConsoleBool, 4, 7, (worldPoint X/Y, s
     // Invalid
     else
     {
-        Con::warnf("Scene::moveTo() - Invalid number of parameters!");
+        Con::warnf("SceneObject::moveTo() - Invalid number of parameters!");
         return false;
     }
 
@@ -2001,21 +2001,21 @@ ConsoleMethodWithDocs(SceneObject, fadeTo, ConsoleBool, 4, 4, (targetColor red /
 {
 	if (argc < 3)
 	{
-		Con::warnf("Scene::fadeTo() - Invalid number of parameters!");
+		Con::warnf("SceneObject::fadeTo() - Invalid number of parameters!");
 		return false;
 	}
 
 	const U32 colorCount = Utility::mGetStringElementCount(argv[2]);
 	if (colorCount != 4)
 	{
-		Con::warnf("Scene::fadeTo() - Invalid color! Colors require four values (red / green / blue / alpha)!");
+		Con::warnf("SceneObject::fadeTo() - Invalid color! Colors require four values (red / green / blue / alpha)!");
 		return false;
 	}
 
 	F32 rate = dAtof(argv[3]);
 	if (rate <= 0.0f)
 	{
-		Con::warnf("Scene::fadeTo() - Rate must be greater than zero!");
+		Con::warnf("SceneObject::fadeTo() - Rate must be greater than zero!");
 		return false;
 	}
 
@@ -2040,21 +2040,21 @@ ConsoleMethodWithDocs(SceneObject, fadeToTime, ConsoleBool, 4, 4, (targetColor r
 {
 	if (argc < 3)
 	{
-		Con::warnf("Scene::fadeToTime() - Invalid number of parameters!");
+		Con::warnf("SceneObject::fadeToTime() - Invalid number of parameters!");
 		return false;
 	}
 
 	const U32 colorCount = Utility::mGetStringElementCount(argv[2]);
 	if (colorCount != 4)
 	{
-		Con::warnf("Scene::fadeToTime() - Invalid color! Colors require four values (red / green / blue / alpha)!");
+		Con::warnf("SceneObject::fadeToTime() - Invalid color! Colors require four values (red / green / blue / alpha)!");
 		return false;
 	}
 
 	F32 time = dAtof(argv[3]);
 	if (time <= 0.0f)
 	{
-		Con::warnf("Scene::fadeToTime() - Time must be greater than zero!");
+		Con::warnf("SceneObject::fadeToTime() - Time must be greater than zero!");
 		return false;
 	}
 
@@ -2087,14 +2087,14 @@ ConsoleMethodWithDocs(SceneObject, growTo, ConsoleBool, 4, 4, (targetSize width
 {
 	if (argc < 3)
 	{
-		Con::warnf("Scene::growTo() - Invalid number of parameters!");
+		Con::warnf("SceneObject::growTo() - Invalid number of parameters!");
 		return false;
 	}
 
 	const U32 targetCount = Utility::mGetStringElementCount(argv[2]);
 	if (targetCount != 2)
 	{
-		Con::warnf("Scene::growTo() - Invalid size! Target size requires two values (width / height)!");
+		Con::warnf("SceneObject::growTo() - Invalid size! Target size requires two values (width / height)!");
 		return false;
 	}
 
@@ -2112,13 +2112,13 @@ ConsoleMethodWithDocs(SceneObject, growTo, ConsoleBool, 4, 4, (targetSize width
 	}
 	else
 	{
-		Con::warnf("Scene::growTo() - Invalid size! Target size requires two values (width / height)!");
+		Con::warnf("SceneObject::growTo() - Invalid size! Target size requires two values (width / height)!");
 		return false;
 	}
 
 	if (rate.x <= 0.0f || rate.y <= 0.0f)
 	{
-		Con::warnf("Scene::growTo() - Rate must be greater than zero!");
+		Con::warnf("SceneObject::growTo() - Rate must be greater than zero!");
 		return false;
 	}
 
@@ -2141,21 +2141,21 @@ ConsoleMethodWithDocs(SceneObject, growToTime, ConsoleBool, 4, 4, (targetSize wi
 {
 	if (argc < 3)
 	{
-		Con::warnf("Scene::growToTime() - Invalid number of parameters!");
+		Con::warnf("SceneObject::growToTime() - Invalid number of parameters!");
 		return false;
 	}
 
 	const U32 targetCount = Utility::mGetStringElementCount(argv[2]);
 	if (targetCount != 2)
 	{
-		Con::warnf("Scene::growToTime() - Invalid size! Target size requires two values (width / height)!");
+		Con::warnf("SceneObject::growToTime() - Invalid size! Target size requires two values (width / height)!");
 		return false;
 	}
 
 	F32 time = dAtof(argv[3]);
 	if (time <= 0.0f)
 	{
-		Con::warnf("Scene::growToTime() - Time must be greater than zero!");
+		Con::warnf("SceneObject::growToTime() - Time must be greater than zero!");
 		return false;
 	}
 
@@ -2297,7 +2297,7 @@ ConsoleMethodWithDocs(SceneObject, applyForce, ConsoleVoid, 4, 6, (worldForce X/
     // Invalid
     else
     {
-        Con::warnf("Scene::applyForce() - Invalid number of parameters!");
+        Con::warnf("SceneObject::applyForce() - Invalid number of parameters!");
         return;
     }
 
@@ -2325,7 +2325,7 @@ ConsoleMethodWithDocs(SceneObject, applyForce, ConsoleVoid, 4, 6, (worldForce X/
     // Invalid
     else
     {
-        Con::warnf("Scene::applyForce() - Invalid number of parameters!");
+        Con::warnf("SceneObject::applyForce() - Invalid number of parameters!");
         return;
     }
 
@@ -2377,7 +2377,7 @@ ConsoleMethodWithDocs(SceneObject, applyLinearImpulse, ConsoleVoid, 4, 6, (world
     // Invalid
     else
     {
-        Con::warnf("Scene::applyLinearImpulse() - Invalid number of parameters!");
+        Con::warnf("SceneObject::applyLinearImpulse() - Invalid number of parameters!");
         return;
     }
 
@@ -2405,7 +2405,7 @@ ConsoleMethodWithDocs(SceneObject, applyLinearImpulse, ConsoleVoid, 4, 6, (world
     // Invalid
     else
     {
-        Con::warnf("Scene::applyLinearImpulse() - Invalid number of parameters!");
+        Con::warnf("SceneObject::applyLinearImpulse() - Invalid number of parameters!");
         return;
     }
 
@@ -3967,12 +3967,13 @@ ConsoleMethodWithDocs(SceneObject, setAlphaTest, ConsoleVoid, 3, 3, (float alpha
 
 //-----------------------------------------------------------------------------
 
+
 /*! Gets the render alpha test threshold.
-    @return The render alpha test threshold in the range of 0.0f to 1.0.  Less than zero represents disabled alpha testing.
+	@return The render alpha test threshold in the range of 0.0f to 1.0.  Less than zero represents disabled alpha testing.
 */
 ConsoleMethodWithDocs(SceneObject, getAlphaTest, ConsoleFloat, 2, 2, ())
 {
-    return object->getAlphaTest();
+	return object->getAlphaTest();
 }
 
 //-----------------------------------------------------------------------------

+ 128 - 8
engine/source/2d/sceneobject/Sprite.cc

@@ -45,6 +45,11 @@ Sprite::Sprite() :
     mFlipX(false),
     mFlipY(false)
 {
+	mUseComplexColor = false;
+	mComplexColor0 = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+	mComplexColor1 = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+	mComplexColor2 = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+	mComplexColor3 = ColorF(1.0f, 1.0f, 1.0f, 1.0f);
 }
 
 //------------------------------------------------------------------------------
@@ -80,20 +85,135 @@ void Sprite::initPersistFields()
     /// Render flipping.
     addField("FlipX", TypeBool, Offset(mFlipX, Sprite), &writeFlipX, "");
     addField("FlipY", TypeBool, Offset(mFlipY, Sprite), &writeFlipY, "");
+
+	addField("useComplexColor", TypeBool, Offset(mUseComplexColor, Sprite), &writeUseComplexColor, "");
 }
 
 //------------------------------------------------------------------------------
 
 void Sprite::sceneRender( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer )
 {
-    // Let the parent render.
-    ImageFrameProvider::render(
-        getFlipX(), getFlipY(),
-        mRenderOOBB[0],
-        mRenderOOBB[1],
-        mRenderOOBB[2],
-        mRenderOOBB[3],
-        pBatchRenderer );
+	if (mUseComplexColor)
+	{
+		ImageFrameProviderCore::renderComplex(
+			getFlipX(), getFlipY(),
+			mRenderOOBB[0],
+			mRenderOOBB[1],
+			mRenderOOBB[2],
+			mRenderOOBB[3],
+			pBatchRenderer,
+			mComplexColor0,
+			mComplexColor1,
+			mComplexColor2,
+			mComplexColor3);
+	}
+	else 
+	{
+		ImageFrameProvider::render(
+			getFlipX(), getFlipY(),
+			mRenderOOBB[0],
+			mRenderOOBB[1],
+			mRenderOOBB[2],
+			mRenderOOBB[3],
+			pBatchRenderer );
+	}
+}
+
+//------------------------------------------------------------------------------
+
+const ColorF& Sprite::getComplexColor(const S8 cornerID)
+{
+	if (cornerID == 1)
+	{
+		return mComplexColor0;
+	}
+	else if (cornerID == 2)
+	{
+		return mComplexColor1;
+	}
+	else if (cornerID == 3)
+	{
+		return mComplexColor2;
+	}
+	else if (cornerID == 4)
+	{
+		return mComplexColor3;
+	}
+	else
+	{
+		return ColorF(1.0f, 1.0f, 1.0f, 1.0f);
+	}
+}
+
+//-----------------------------------------------------------------------------
+
+void Sprite::updateBlendColor(const F32 elapsedTime)
+{
+	if (!mUseComplexColor)
+	{
+		SceneObject::updateBlendColor(elapsedTime);
+	}
+	else
+	{
+		updateFadeColor(mComplexColor0, mFadeToColorTL, elapsedTime);
+		updateFadeColor(mComplexColor1, mFadeToColorTR, elapsedTime);
+		updateFadeColor(mComplexColor2, mFadeToColorBR, elapsedTime);
+		updateFadeColor(mComplexColor3, mFadeToColorBL, elapsedTime);
+	}
 }
 
+//-----------------------------------------------------------------------------
+
+void Sprite::checkFadeComplete()
+{
+	if (mFadeActive && 
+		((mUseComplexColor && 
+		mComplexColor0 == mFadeToColorTL.TargetColor &&
+		mComplexColor1 == mFadeToColorTR.TargetColor &&
+		mComplexColor2 == mFadeToColorBR.TargetColor &&
+		mComplexColor3 == mFadeToColorBL.TargetColor) ||
+		(!mUseComplexColor &&
+		mBlendColor == mFadeToColor.TargetColor)))
+	{
+		mFadeActive = false;
+
+		PROFILE_SCOPE(Sprite_onFadeToComplete);
+		Con::executef(this, 1, "onFadeToComplete");
+	}
+}
 
+bool Sprite::fadeToComplex(const S8 cornerID, const ColorF& targetColor, const F32 deltaRed, const F32 deltaGreen, const F32 deltaBlue, const F32 deltaAlpha)
+{
+	// Check in a scene.
+	if (!getScene())
+	{
+		Con::warnf("Sprite::fadeToComplex() - Cannot fade object (%d) to a color as it is not in a scene.", getId());
+		return false;
+	}
+
+	// Check targetColor.
+	if (!targetColor.isValidColor())
+	{
+		Con::warnf("Sprite::fadeToComplex() - Cannot fade object (%d) because the color is invalid.", getId());
+		return false;
+	}
+
+	// Check that the sprite is using complex colors
+	if (!mUseComplexColor)
+	{
+		Con::warnf("Sprite::fadeToComplex() - Cannot fade object (%d) because the sprite is not using complex colors.", getId());
+		return false;
+	}
+
+	// Only set fading active if the target color is not the blending color.
+	if (targetColor != getComplexColor(cornerID))
+	{
+		mFadeActive = true;
+		if (cornerID == 1) { mFadeToColorTL.set(targetColor, deltaRed, deltaGreen, deltaBlue, deltaAlpha); }
+		else if (cornerID == 2) { mFadeToColorTR.set(targetColor, deltaRed, deltaGreen, deltaBlue, deltaAlpha); }
+		else if (cornerID == 3) { mFadeToColorBL.set(targetColor, deltaRed, deltaGreen, deltaBlue, deltaAlpha); }
+		else if (cornerID == 4) { mFadeToColorBR.set(targetColor, deltaRed, deltaGreen, deltaBlue, deltaAlpha); }
+	}
+
+	return true;
+}

+ 29 - 0
engine/source/2d/sceneobject/Sprite.h

@@ -27,6 +27,10 @@
 #include "2d/core/SpriteBase.h"
 #endif
 
+#ifndef _FADETOCOLOR_H_
+#include "graphics/FadeToColor.h"
+#endif
+
 //------------------------------------------------------------------------------
 
 class Sprite : public SpriteBase
@@ -38,6 +42,11 @@ private:
     bool mFlipX;
     bool mFlipY;
 
+	FadeToColor				mFadeToColorTL;
+	FadeToColor				mFadeToColorTR;
+	FadeToColor				mFadeToColorBL;
+	FadeToColor				mFadeToColorBR;
+
 public:
     Sprite();
     virtual ~Sprite();
@@ -54,12 +63,32 @@ public:
 
     virtual void sceneRender( const SceneRenderState* pSceneRenderState, const SceneRenderRequest* pSceneRenderRequest, BatchRender* pBatchRenderer );
 
+	// Complex Colors (Using 4 blend colors, one for each corner)
+	inline void setUseComplexColor(const bool complexColor) { mUseComplexColor = complexColor; }
+	inline bool getUseComplexColor(void) const { return mUseComplexColor; }
+	inline void setComplexColor(const ColorF& blendColor0, const ColorF& blendColor1, const ColorF& blendColor2, const ColorF& blendColor3) { mComplexColor0 = blendColor0; mComplexColor1 = blendColor1; mComplexColor2 = blendColor2; mComplexColor3 = blendColor3; }
+	const ColorF& getComplexColor(const S8 cornerID);
+	static Corner getCornerEnum(const char* label);
+
+	// FadeTo for Complex Colors
+	bool					fadeToComplex(const S8 cornerID, const ColorF& targetColor, const F32 deltaRed, const F32 deltaGreen, const F32 deltaBlue, const F32 deltaAlpha);
+	void					updateBlendColor(const F32 elapsedTime);
+	void					checkFadeComplete();
+
     /// Declare Console Object.
     DECLARE_CONOBJECT( Sprite );
 
 protected:
     static bool writeFlipX( void* obj, StringTableEntry pFieldName )        { return static_cast<Sprite*>(obj)->getFlipX() == true; }
     static bool writeFlipY( void* obj, StringTableEntry pFieldName )        { return static_cast<Sprite*>(obj)->getFlipY() == true; }
+
+	static bool writeUseComplexColor( void* obj, StringTableEntry pFieldName ) {return static_cast<Sprite*>(obj)->getUseComplexColor() == true; }
+
+	bool					mUseComplexColor;
+	ColorF					mComplexColor0;
+	ColorF					mComplexColor1;
+	ColorF					mComplexColor2;
+	ColorF					mComplexColor3;
 };
 
 #endif // _SPRITE_H_

+ 229 - 0
engine/source/2d/sceneobject/Sprite_ScriptBinding.h

@@ -94,4 +94,233 @@ ConsoleMethodWithDocs(Sprite, getFlipY, ConsoleBool, 2, 2, ())
    return object->getFlipY();
 }
 
+//-----------------------------------------------------------------------------
+
+/*! Sets whether the sprite uses complex colors or not.
+	@complexColor Whether complex color is on or not.
+	@return No return Value.
+*/
+ConsoleMethodWithDocs(Sprite, setUseComplexColor, ConsoleVoid, 3, 3, (bool complexColor))
+{
+	// Fetch blend mode.
+	const bool complex = dAtob(argv[2]);
+
+	object->setUseComplexColor(complex);
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets whether the sprite uses complex colors or not.
+	@return (bool complexColor) Whether complex color is on or not.
+*/
+ConsoleMethodWithDocs(Sprite, getUseComplexColor, ConsoleBool, 2, 2, ())
+{
+	return object->getUseComplexColor();
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Sets the complex color.
+	@param topLeft The topLeft color space separated.
+	@param topLeft The topRight color space separated.
+	@param topLeft The bottomRight color space separated.
+	@param topLeft The bottomLeft color space separated.
+	@return No return Value.
+*/
+ConsoleMethodWithDocs(Sprite, setComplexColor, ConsoleVoid, 6, 6, (topLeft, topRight, bottomRight, bottomLeft))
+{
+	// The corners.
+	ColorF corner0;
+	ColorF corner1;
+	ColorF corner2;
+	ColorF corner3;
+
+	// Space separated.
+	if (argc == 6)
+	{
+		// Grab the element count.
+		const U32 elementCount0 = Utility::mGetStringElementCount(argv[2]);
+		if (elementCount0 == 3)
+		{
+			corner0 = ColorF(dAtof(Utility::mGetStringElement(argv[2], 0)), dAtof(Utility::mGetStringElement(argv[2], 1)), dAtof(Utility::mGetStringElement(argv[2], 2)));
+		}
+		else if (elementCount0 == 4)
+		{
+			corner0 = ColorF(dAtof(Utility::mGetStringElement(argv[2], 0)), dAtof(Utility::mGetStringElement(argv[2], 1)), dAtof(Utility::mGetStringElement(argv[2], 2)), dAtof(Utility::mGetStringElement(argv[2], 3)));
+		}
+		else
+		{
+			Con::warnf("Sprite::setComplexColor() - Top Left Corner has Invalid Number of arguments!");
+			return;
+		}
+
+		// Grab the element count.
+		const U32 elementCount1 = Utility::mGetStringElementCount(argv[3]);
+		if (elementCount1 == 3)
+		{
+			corner1 = ColorF(dAtof(Utility::mGetStringElement(argv[3], 0)), dAtof(Utility::mGetStringElement(argv[3], 1)), dAtof(Utility::mGetStringElement(argv[3], 2)));
+		}
+		else if (elementCount1 == 4)
+		{
+			corner1 = ColorF(dAtof(Utility::mGetStringElement(argv[3], 0)), dAtof(Utility::mGetStringElement(argv[3], 1)), dAtof(Utility::mGetStringElement(argv[3], 2)), dAtof(Utility::mGetStringElement(argv[3], 3)));
+		}
+		else
+		{
+			Con::warnf("Sprite::setComplexColor() - Top Right Corner has Invalid Number of arguments!");
+			return;
+		}
+
+		// Grab the element count.
+		const U32 elementCount2 = Utility::mGetStringElementCount(argv[4]);
+		if (elementCount2 == 3)
+		{
+			corner2 = ColorF(dAtof(Utility::mGetStringElement(argv[4], 0)), dAtof(Utility::mGetStringElement(argv[4], 1)), dAtof(Utility::mGetStringElement(argv[4], 2)));
+		}
+		else if (elementCount2 == 4)
+		{
+			corner2 = ColorF(dAtof(Utility::mGetStringElement(argv[4], 0)), dAtof(Utility::mGetStringElement(argv[4], 1)), dAtof(Utility::mGetStringElement(argv[4], 2)), dAtof(Utility::mGetStringElement(argv[4], 3)));
+		}
+		else
+		{
+			Con::warnf("Sprite::setComplexColor() - Bottom Right Corner has Invalid Number of arguments!");
+			return;
+		}
+
+		// Grab the element count.
+		const U32 elementCount3 = Utility::mGetStringElementCount(argv[5]);
+		if (elementCount3 == 3)
+		{
+			corner3 = ColorF(dAtof(Utility::mGetStringElement(argv[5], 0)), dAtof(Utility::mGetStringElement(argv[5], 1)), dAtof(Utility::mGetStringElement(argv[5], 2)));
+		}
+		else if (elementCount3 == 4)
+		{
+			corner3 = ColorF(dAtof(Utility::mGetStringElement(argv[5], 0)), dAtof(Utility::mGetStringElement(argv[5], 1)), dAtof(Utility::mGetStringElement(argv[5], 2)), dAtof(Utility::mGetStringElement(argv[5], 3)));
+		}
+		else
+		{
+			Con::warnf("Sprite::setComplexColor() - Bottom Left Corner has Invalid Number of arguments!");
+			return;
+		}
+
+		// Set blend color.
+		object->setComplexColor(corner0, corner1, corner2, corner3);
+	}
+
+	// Invalid.
+	else
+	{
+		Con::warnf("Sprite::setComplexColor() - Invalid Number of parameters!");
+		return;
+	}
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Gets the complex color
+	@param cornerID The cornerID whose color will be returned (1 - 4).
+	@return (float red / float green / float blue / float alpha) The corner blend color.
+*/
+ConsoleMethodWithDocs(Sprite, getComplexColor, ConsoleString, 3, 3, (cornerID))
+{
+	S8 cornerID = dAtoi(argv[2]);
+
+	// Get Blend color.
+	ColorF blendColor = object->getComplexColor(cornerID);
+
+	// No, so fetch the raw color values.
+	return blendColor.scriptThis();
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Fades one corner of the sprite to the target color.
+	The current color of the object will continue to change until it arrives at the target color or the fade is cancelled.
+	The change will continue even if the blendColor is set directly.
+	@param cornerID The cornerID whose color will be returned (1 - 4).
+	@param (red / green / blue / alpha) The target color to fade the corner of the sprite to.
+	@param rate The rate per second to change each color value. Must be a number greater than zero.
+	@return Whether the fade started or not.
+*/
+ConsoleMethodWithDocs(Sprite, fadeToComplex, ConsoleBool, 5, 5, (cornerID, targetColor red / green / blue / alpha, rate))
+{
+	if (argc < 5)
+	{
+		Con::warnf("Sprite::fadeToComplex() - Invalid number of parameters!");
+		return false;
+	}
+
+	const S8 cornerID = dAtoi(argv[2]);
+
+	const U32 colorCount = Utility::mGetStringElementCount(argv[3]);
+	if (colorCount != 4)
+	{
+		Con::warnf("Sprite::fadeToComplex() - Invalid color! Colors require four values (red / green / blue / alpha)!");
+		return false;
+	}
+
+	F32 rate = dAtof(argv[4]);
+	if (rate <= 0.0f)
+	{
+		Con::warnf("Sprite::fadeToComplex() - Rate must be greater than zero!");
+		return false;
+	}
+
+	return object->fadeToComplex(cornerID, ColorF(dAtof(Utility::mGetStringElement(argv[3], 0)),
+		dAtof(Utility::mGetStringElement(argv[3], 1)),
+		dAtof(Utility::mGetStringElement(argv[3], 2)),
+		dAtof(Utility::mGetStringElement(argv[3], 3))),
+		rate, rate, rate, rate);
+}
+
+//-----------------------------------------------------------------------------
+
+/*! Fades one corner of the sprite to the target color over a period of time.
+	The current color of the object will continue to change until it arrives at the target color or the fade is cancelled.
+	The change will continue even if the complexColors is set directly which will change the amount of time it takes.
+	Unhindered, each of the color values will arrive at the target in approximately the target time.
+	@param cornerID The cornerID whose color will be returned (1 - 4).
+	@param (red / green / blue / alpha) The target color to fade the corner of the sprite to.
+	@param time The amount of time in milliseconds that each color value will take to reach the target. Must be a number greater than zero.
+	@return Whether the fade started or not.
+*/
+ConsoleMethodWithDocs(Sprite, fadeToTimeComplex, ConsoleBool, 5, 5, (cornerID, targetColor red / green / blue / alpha, time))
+{
+	if (argc < 5)
+	{
+		Con::warnf("Sprite::fadeToTimeComplex() - Invalid number of parameters!");
+		return false;
+	}
+
+	const S8 cornerID = dAtoi(argv[2]);
+
+	const U32 colorCount = Utility::mGetStringElementCount(argv[3]);
+	if (colorCount != 4)
+	{
+		Con::warnf("Sprite::fadeToTimeComplex() - Invalid color! Colors require four values (red / green / blue / alpha)!");
+		return false;
+	}
+
+	F32 time = dAtof(argv[4]);
+	if (time <= 0.0f)
+	{
+		Con::warnf("Sprite::fadeToTimeComplex() - Time must be greater than zero!");
+		return false;
+	}
+
+	// Get the target color values.
+	const F32 tRed = dAtof(Utility::mGetStringElement(argv[3], 0));
+	const F32 tGreen = dAtof(Utility::mGetStringElement(argv[3], 1));
+	const F32 tBlue = dAtof(Utility::mGetStringElement(argv[3], 2));
+	const F32 tAlpha = dAtof(Utility::mGetStringElement(argv[3], 3));
+
+	// Get the rate to change each value. The rate will be change per second.
+	const ColorF currentColor = object->getComplexColor(cornerID);
+	F32 rRed = (1000.0f * fabs(tRed - currentColor.red)) / time;
+	F32 rGreen = (1000.0f * fabs(tGreen - currentColor.green)) / time;
+	F32 rBlue = (1000.0f * fabs(tBlue - currentColor.blue)) / time;
+	F32 rAlpha = (1000.0f * fabs(tAlpha - currentColor.alpha)) / time;
+
+	return object->fadeToComplex(cornerID, ColorF(tRed, tGreen, tBlue, tAlpha), rRed, rGreen, rBlue, rAlpha);
+}
+
 ConsoleMethodGroupEndWithDocs(Sprite)

+ 34 - 0
engine/source/graphics/FadeToColor.h

@@ -0,0 +1,34 @@
+#ifndef _FADETOCOLOR_H_
+#define _FADETOCOLOR_H_
+
+#ifndef _UTILITY_H_
+#include "2d/core/Utility.h"
+#endif
+
+#ifndef _COLOR_H_
+#include "graphics/gColor.h"
+#endif
+
+class FadeToColor
+{
+public:
+	FadeToColor() : TargetColor(1.0f, 1.0f, 1.0f, 1.0f), DeltaRed(0.0f), DeltaGreen(0.0f), DeltaBlue(0.0f), DeltaAlpha(0.0f) { }
+	~FadeToColor() { }
+
+	ColorF					TargetColor;
+	F32						DeltaRed;
+	F32						DeltaGreen;
+	F32						DeltaBlue;
+	F32						DeltaAlpha;
+
+	void set(ColorF targetColor, F32 deltaRed, F32 deltaGreen, F32 deltaBlue, F32 deltaAlpha)
+	{
+		TargetColor = targetColor;
+		DeltaRed = deltaRed;
+		DeltaGreen = deltaGreen;
+		DeltaBlue = deltaBlue;
+		DeltaAlpha = deltaAlpha;
+	}
+};
+
+#endif //_FADETOCOLOR_H_

+ 8 - 0
engine/source/gui/guiTypes.h

@@ -102,6 +102,14 @@ enum class GuiDirection
 	Center
 };
 
+enum Corner
+{
+	TopLeft,
+	TopRight,
+	BottomLeft,
+	BottomRight
+};
+
 enum AlignmentType
 {
 	LeftAlign,