guiCopyContainer and guiBitmapShader

Expanding and utilizing the engine via C++.
1 post Page 1 of 1
irei1as
Posts: 78
Joined: Fri Feb 27, 2015 5:13 pm
by irei1as » Tue Feb 12, 2019 8:19 pm
These are a pair of gui objects.
guiCopyContainer is a container that can render its contents to a named texture for use in other objects.
guiBitmapShader is a child of bitmap control that lets use up to 8 textures (8 seems to work for most modern graphic cards) with a shader similar to how PostEffect goes.

First we need to make a few changes to the engine in order to read and write a pair of private variables related to gui rendering.
-a) Add this as public: (important being public:) in "class FontRenderBatcher" of source\gfx\gfxFontRenderBatcher.h-
   GFXStateBlockDesc getFontStateBlockDesc();
   
   void externalSetupFontStateBlock(GFXStateBlockDesc fontSBD);
-b) Add this in source\gfx\gfxFontRenderBatcher.cpp-
GFXStateBlockDesc FontRenderBatcher::getFontStateBlockDesc()
{
   return mFontSB->getDesc();
}

void FontRenderBatcher::externalSetupFontStateBlock(GFXStateBlockDesc fontSBD)
{
   fontSBD.cullDefined = true;
   fontSBD.cullMode = GFXCullNone;
   
   fontSBD.samplersDefined = true;
   fontSBD.samplers[0].alphaOp = GFXTOPModulate;
   fontSBD.samplers[0].magFilter = GFXTextureFilterPoint;
   fontSBD.samplers[0].minFilter = GFXTextureFilterPoint;
   fontSBD.samplers[0].addressModeU = GFXAddressClamp;
   fontSBD.samplers[0].addressModeV = GFXAddressClamp;
   fontSBD.samplers[0].alphaArg1 = GFXTATexture;
   fontSBD.samplers[0].alphaArg2 = GFXTADiffuse;
   // This is an add operation because in D3D, when a texture of format D3DFMT_A8
   // is used, the RGB channels are all set to 0.  Therefore a modulate would 
   // result in the text always being black.  This may not be the case in OpenGL
   // so it may have to change.  -bramage
   fontSBD.samplers[0].textureColorOp = GFXTOPAdd;

   mFontSB = GFX->createStateBlock(fontSBD);
}
-c) Add this as public: (important being public:) in "class GFXDrawUtil" of source\gfx\gfxDrawUtil.h-
   GFXStateBlockDesc getBitmapStateBlockDesc();
   GFXStateBlockDesc getFontStateBlockDesc();
   void externalSetupBitmapStateBlock(GFXStateBlockDesc bitmapSBD);
   void externalSetupFontStateBlock(GFXStateBlockDesc fontSBD);
   


-d) Finally, add this in source\gfx\gfxDrawUtil.cpp-
GFXStateBlockDesc GFXDrawUtil::getBitmapStateBlockDesc()
{
   return mBitmapStretchWrapLinearSB->getDesc();
}

GFXStateBlockDesc GFXDrawUtil::getFontStateBlockDesc()
{
   return mFontRenderBatcher->getFontStateBlockDesc();
}

void GFXDrawUtil::externalSetupBitmapStateBlock(GFXStateBlockDesc bitmapSBD)
{
   // Linear: Create wrap SB
   mBitmapStretchWrapLinearSB = mDevice->createStateBlock(bitmapSBD);

   // Linear: Create clamp SB
   bitmapSBD.samplers[0] = GFXSamplerStateDesc::getClampLinear();
   mBitmapStretchLinearSB = mDevice->createStateBlock(bitmapSBD);

   // Point:
   bitmapSBD.samplers[0].minFilter = GFXTextureFilterPoint;
   bitmapSBD.samplers[0].mipFilter = GFXTextureFilterPoint;
   bitmapSBD.samplers[0].magFilter = GFXTextureFilterPoint;

   // Point: Create clamp SB, last created clamped so no work required here
   mBitmapStretchSB = mDevice->createStateBlock(bitmapSBD);

   // Point: Create wrap SB, have to do this manually because getWrapLinear doesn't
   bitmapSBD.samplers[0].addressModeU = GFXAddressWrap;
   bitmapSBD.samplers[0].addressModeV = GFXAddressWrap;
   bitmapSBD.samplers[0].addressModeW = GFXAddressWrap;
   mBitmapStretchWrapSB = mDevice->createStateBlock(bitmapSBD);

   //Note: Not sure about rectFill. But it seems to work fine without changes.
   //GFXStateBlockDesc rectFill;
   //rectFill.setCullMode(GFXCullNone);
   //rectFill.setZReadWrite(false);
   //rectFill.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
   //mRectFillSB = mDevice->createStateBlock(rectFill);
}

void GFXDrawUtil::externalSetupFontStateBlock(GFXStateBlockDesc fontSBD)
{
   //font StateBlock
   mFontRenderBatcher->externalSetupFontStateBlock(fontSBD);
}

//--------------------------------------------------------------------------------------------------

Now add these c++ files to the source:

guiCopyContainer.h
#ifndef _GUICOPYCONTAINER_H_
#define _GUICOPYCONTAINER_H_

#ifndef _GUICONTAINER_H_
#include "gui\containers\guiContainer.h"
#endif

#ifndef _MATTEXTURETARGET_H_
#include "materials/matTextureTarget.h"
#endif

class GFXStateBlockData;

class GuiCopyContainer : public GuiContainer
{
private:
   typedef GuiContainer Parent;

public:
   DECLARE_CONOBJECT(GuiCopyContainer);
   DECLARE_CATEGORY( "Gui Containers" );
   DECLARE_DESCRIPTION( "A container that can copy its children renders into a texture.");

   // Constructor/Destructor/ConObject Declaration
   GuiCopyContainer();
   
   static void initPersistFields();
   
   bool onAdd();
   void onRemove();

   void onRender(Point2I offset, const RectI &updateRect);
   
   void _setUniqueTextureName( const char *inName );
   void _onTextureEvent( GFXTexCallbackCode code );
   
   void _setupTargets();
   void _teardownTargets();
   
   void copyContainer();
   
protected:
   static bool setTextureName( void *object, const char *index, const char *data );

   //Set this to true to have the GuiCopyContainer become invisible but active
   bool mBlockNormalRender;
   bool mAutoCopy;

   GFXTextureTargetRef mTarget;
   GFXTexHandle mTargetTexture;
   NamedTexTarget mNamedTarget;
   
   GFXFormat mTargetFormat;
   StringTableEntry mTargetName;
   
   GFXStateBlockData *mStateBlockData;
   GFXStateBlockData *mOldStateBlockData;
   GFXStateBlockRef mStateBlock;
};
/// @}

#endif // _GUICOPYCONTAINER_H_
guiCopyContainer.cpp
#include "guiCopyContainer.h"
#include "gui/core/guiDefaultControlRender.h"

#include "console/console.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"

#include "gfx/gfxDrawUtil.h"
#include "gfx/gfxTextureManager.h"
#include "gfx/gfxAPI.h"

#include "gfx/sim/gfxStateBlockData.h"
#include "gfx/gfxTransformSaver.h"

IMPLEMENT_CONOBJECT(GuiCopyContainer);

ConsoleDocClass( GuiCopyContainer,
   "@brief A gui container that copies its content into a named texture.\n\n"
   
   "@ingroup GuiContainers"
);

GuiCopyContainer::GuiCopyContainer()
{
   mBlockNormalRender = false;
   mAutoCopy = false;
   mTargetFormat = GFXFormatR8G8B8A8;
   //default mTargetName will be  "copiedcontainer" + n  defined inside onAdd
   mTargetName = NULL;
   
   mStateBlockData = NULL;
   mStateBlock = NULL;
   mOldStateBlockData = NULL;
}

bool GuiCopyContainer::onAdd()
{
   if(!Parent::onAdd())
      return false;
   
   _setUniqueTextureName(mTargetName);
   //Inside _setUniqueTextureName we have:
   //mNamedTarget.registerWithName( mTargetName );

   _setupTargets();
   GFXTextureManager::addEventDelegate( this, &GuiCopyContainer::_onTextureEvent );
   return true;
}

void GuiCopyContainer::onRemove()
{
   mNamedTarget.unregister();
   GFXTextureManager::removeEventDelegate( this, &GuiCopyContainer::_onTextureEvent );

   _teardownTargets();

   //Needed to avoid
   //   Exception thrown : read access violation.
   //   **GFXDevice::get**(...) returned nullptr.
   //when closing the program while running:
   if(mBlockNormalRender && mAutoCopy)
      GFX->clear(GFXClearTarget, LinearColorF(0, 0, 0, 0), 1.0f, 0);

   mTarget = NULL;
   mTargetTexture = NULL;
   Parent::onRemove();
}

void GuiCopyContainer::initPersistFields()
{
   addGroup( "CopyContainer" );
   
      addField( "blockNormalRender", TypeBool, Offset( mBlockNormalRender, GuiCopyContainer ),
         "True to disable render of its children even with the visible flag enabled.");
      addField( "autoCopy", TypeBool, Offset( mAutoCopy, GuiCopyContainer ),
         "If true it calls copyContainer() itself each frame.");
      addProtectedField( "textureName", TypeString, Offset( mTargetName, GuiCopyContainer ), &setTextureName, &defaultProtectedGetFn,
         "Name of the texture.");
      addField( "targetFormat", TypeGFXFormat, Offset( mTargetFormat, GuiCopyContainer ),
         "Format of the texture. It should be GFXFormatR8G8B8A8.");
      addField( "stateBlock", TYPEID<GFXStateBlockData>(), Offset( mStateBlockData,  GuiCopyContainer ),
         "Optional name of a GFXStateBlockData to replace default values." );
      
   endGroup( "CopyContainer" );

   Parent::initPersistFields();
}

bool GuiCopyContainer::setTextureName( void *object, const char *index, const char *data )
{
   static_cast<GuiCopyContainer *>( object )->_setUniqueTextureName(data);
   // Return false because the _setUniqueTextureName method will assign 'mTargetName' to the
   // argument we are specifying in the call.
   return false;
}

void GuiCopyContainer::_setupTargets()
{
   _teardownTargets();

   if (!mTarget.isValid())
   {
      mTarget = GFX->allocRenderToTextureTarget();
   }

   // Update color
   Point2I targetSize = getExtent();
   if (!mTargetTexture.isValid() || targetSize != mTargetTexture.getWidthHeight())
   {
      mTargetTexture.set( targetSize.x, targetSize.y, mTargetFormat, &GFXRenderTargetSRGBProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ), 1, 0 );
   }
   
   mTarget->attachTexture( GFXTextureTarget::RenderSlot(GFXTextureTarget::Color0), mTargetTexture );
   mNamedTarget.setTexture(0, mTargetTexture);

}

void GuiCopyContainer::_teardownTargets()
{
   mNamedTarget.release();
   mTargetTexture = NULL;
}


void GuiCopyContainer::_setUniqueTextureName( const char *inName )
{
   String outName( inName );

   if ( outName.isEmpty() )
      outName = "copiedcontainer";

   if ( mNamedTarget.registerWithName( outName ) )
   {
      mTargetName = StringTable->EmptyString();
      mTargetName = StringTable->insert(outName);
      return;
   }

   S32 suffixNumb = -1;
   String nameStr( String::GetTrailingNumber( outName, suffixNumb ) );
   suffixNumb = mAbs( suffixNumb ) + 1;

   #define MAX_TRIES 100

   for ( U32 i = 0; i < MAX_TRIES; i++ )
   {   
	   outName = String::ToString("%s%d", nameStr.c_str(), suffixNumb);

      if ( mNamedTarget.registerWithName( outName ) )
      {
         mTargetName = StringTable->EmptyString();
         mTargetName = StringTable->insert(outName);
         return;
      }

      suffixNumb++;
   }

   Con::errorf( "_setUniqueTextureName - failed after %d attempts", inName, MAX_TRIES );
   mTargetName = NULL;
   return;
}


void GuiCopyContainer::_onTextureEvent( GFXTexCallbackCode code )
{
   switch(code)
   {
      case GFXZombify:
         _teardownTargets();
         break;

      case GFXResurrect:
         _setupTargets();
         break;
   }
}

void GuiCopyContainer::onRender(Point2I offset, const RectI &updateRect)
{
   if (mAutoCopy)
      copyContainer();

   if(!mBlockNormalRender)
   {
      RectI ctrlRect(offset, getExtent());

      //if opaque, fill the update rect with the fill color
      if ( mProfile->mOpaque )
         GFX->getDrawUtil()->drawRectFill(ctrlRect, mProfile->mFillColor);
      
      //if there's a border, draw the border
      if ( mProfile->mBorder )
         renderBorder(ctrlRect, mProfile);
      
      renderChildControls(offset, updateRect);
   }
}


void GuiCopyContainer::copyContainer()
{
   if( dStrIsEmpty(mTargetName) )
   {
       Con::errorf("Name of the texture couldn't be set properly.");
       return;
   }
   
   Point2I size = getExtent();
   if(size.x == 0 || size.y == 0)
   {
      return;
   }
   
   // Make sure we have a clean matrix state 
   // before we start rendering anything!
   GFXTransformSaver saver;
   //GFX->setWorldMatrix( MatrixF::Identity );
   //GFX->setViewMatrix( MatrixF::Identity );
   //GFX->setProjectionMatrix( MatrixF::Identity );

   bool tempAwake=false;
   if (!isAwake())
   {
      tempAwake=true;
      awaken();
   }
   
   if (mTarget->getSize() != size)
   {
      _setupTargets();
      mNamedTarget.setViewport( RectI( Point2I::Zero, size ) );
   }
   
   RectI screenRect(0, 0, size.x, size.y);
   
   // Set active target
   GFX->pushActiveRenderTarget();
   GFX->setActiveRenderTarget(mTarget);

   // Clear the current viewport area
   GFX->setViewport(screenRect);
   GFX->clear(GFXClearTarget, LinearColorF(0, 0, 0, 0), 1.0f, 0);
   
   if ( mStateBlockData == NULL )
   {
      if( mStateBlockData != mOldStateBlockData )
         mOldStateBlockData = NULL;
      renderChildControls(Point2I(0,0),RectI(Point2I(0,0), getExtent()));
   }
   else
   {
      GFXStateBlockDesc prev_bitmapDefaultGuiDesc( GFX->getDrawUtil()->getBitmapStateBlockDesc() );
      GFXStateBlockDesc prev_fontDefaultGuiDesc( GFX->getDrawUtil()->getFontStateBlockDesc() );
      GFXStateBlockDesc prev_mDefaultGuiDesc( mDefaultGuiSB->getDesc() );
      
      GFXStateBlockDesc desc;
      if( mStateBlockData != mOldStateBlockData )
      {
         mOldStateBlockData = mStateBlockData;
         desc = mStateBlockData->getState();
         mStateBlock = GFX->createStateBlock(desc);
      }
      else
      {
         desc = mStateBlock->getDesc();
      }
         
      mDefaultGuiSB = mStateBlock;
      GFX->setStateBlock( mStateBlock );
      
      GFX->getDrawUtil()->externalSetupBitmapStateBlock(desc);
      GFX->getDrawUtil()->externalSetupFontStateBlock(desc);
      
      renderChildControls(Point2I(0,0),RectI(Point2I(0,0), getExtent()));
      
      mDefaultGuiSB = GFX->createStateBlock( prev_mDefaultGuiDesc );
      GFX->setStateBlock(mDefaultGuiSB);   
      
      GFX->getDrawUtil()->externalSetupBitmapStateBlock(prev_bitmapDefaultGuiDesc);
      GFX->getDrawUtil()->externalSetupFontStateBlock(prev_fontDefaultGuiDesc);
   }
   
   mTarget->resolve();
   GFX->popActiveRenderTarget();
   
   if(tempAwake)
      sleep();
   
   //Test. It copies the named texture into a file
   /*
   GFXTexHandle theTex;
   NamedTexTarget *namedTarget = NULL;
   namedTarget = NamedTexTarget::find(mTargetName);
   if ( namedTarget )
   {
      theTex = namedTarget->getTexture( 0 );
   }
   
   if ( theTex.isValid() )
   {
      theTex->dumpToDisk("png", "./testXX.png");
   }  
   */
}

DefineEngineMethod( GuiCopyContainer, copyContainer, void,(),,
   "@brief Copy its contents into the named texture.")
{
   object->copyContainer();
}
guiBitmapShader.h
#ifndef _GUIBITMAPSHADER_H_
#define _GUIBITMAPSHADER_H_

#ifndef _GUIBITMAPCTRL_H_
#include "gui/controls/guiBitmapCtrl.h"
#endif

class GFXStateBlockData;
class ShaderData;

/// Renders a bitmap that can use a Shader.
class GuiBitmapShader : public GuiBitmapCtrl
{
public:
   typedef GuiBitmapCtrl Parent;

   enum { NumTextures = 8 };

protected:
   Point2F mPosVertex[4]; //It needs to be float to avoid aproximation errors when rotating
   Point2F mUVValues[4];
   bool mAutoResizeBitmap;
   Point2I mOldGuiExtent;

   FileName mTexFilename[NumTextures];
   FileName mOldTexFilename[NumTextures];
   GFXTexHandle mTextures[NumTextures];

   ShaderData *mShaderData;
   ShaderData *mOldShaderData;
   GFXStateBlockData *mStateBlockData;
   GFXStateBlockData *mOldStateBlockData;
   
   GFXStateBlockRef mStateBlock;
   GFXShaderRef mShader;
   GFXShaderConstBufferRef mShaderConsts;
   
   //
   GFXShaderConstHandle *mModelViewProjSC;
   GFXShaderConstHandle *mGuiSizeSC;
   GFXShaderConstHandle *mTexSizeSC[NumTextures];
   GFXShaderConstHandle *mGuiOffsetSC;
   GFXShaderConstHandle *mAccumTimeSC;
   GFXShaderConstHandle *mDeltaTimeSC;
   //

   void _setupStateBlock();
   bool _setupShader();
   void _setupConstants();
   
   bool _setupTexture( U8 slot );
   void setTexture( U8 index, const String &texFilePath );
   
   void _doAutoResizeBitmap(Point2I extent, Point2I oldGuiExtent);
   
   //
   U32 mShaderReloadKey;

   class EffectConst
   {
   public:

      EffectConst( const String &name, const String &val )
         : mName( name ), 
           mHandle( NULL ),
           mDirty( true )
      {
         set( val );
      }

      void set( const String &newVal );

      void setToBuffer( GFXShaderConstBufferRef buff );

      String mName;

      GFXShaderConstHandle *mHandle;

      String mStringVal;

      bool mDirty;
   };

   typedef HashTable<StringCase,EffectConst*> EffectConstTable;

   EffectConstTable mEffectConsts;
   
public:
   DECLARE_CONOBJECT( GuiBitmapShader );
   DECLARE_CATEGORY( "Gui Images" );
   DECLARE_DESCRIPTION( "A control that displays an image like GuiBitmapCtrl.\n"
                        "But the image can use a Shader.");

   GuiBitmapShader();
   ~GuiBitmapShader();
   
   static void initPersistFields();
   
   DECLARE_CALLBACK( void, setShaderConsts, () );

   void onRender(Point2I offset, const RectI &updateRect);
   
   bool drawBitmapShader(Point2I offset);
   void setShaderConst( const String &name, const String &val );
   
   Point2F getCenter();
   //vertex: 0,1,2 or 3
   Point2F getVertexPosition(U8 vertex);
   void setVertexPosition(Point2F vertexPos, U8 vertex);
   void moveVertex(Point2I distance, U8 vertex );
   void rotateVertex(F32 angleDeg, Point2I pivot, U8 vertex);
   void scaleVertex(F32 scale, Point2I pivot, U8 vertex);
   F32 getBitmapProportions();
   Point4F getBitmapBounds();
   void setOldGuiExtent(Point2I value) { mOldGuiExtent = value; }
};

#endif //_GUIBITMAPTSHADER_H_
guiBitmapShader.cpp
#include "platform/platform.h"
#include "guiBitmapShader.h"

#include "console/console.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "gfx/gfxDevice.h"
#include "gfx/gfxDrawUtil.h"

#include "materials/shaderData.h"
#include "gfx/sim/gfxStateBlockData.h"

#include "materials/materialManager.h"
#include "materials/matTextureTarget.h"
#include "core/strings/stringUnit.h"
#include "math/mMathFn.h"
//#include "postFx/postEffectManager.h" //it was here for getBackBufferTex()

IMPLEMENT_CONOBJECT(GuiBitmapShader);

ConsoleDocClass( GuiBitmapShader,
   "@brief A gui control that is used to display an image with shader.\n\n"
   
   "@ingroup GuiControls"
);

IMPLEMENT_CALLBACK( GuiBitmapShader, setShaderConsts, void, (), (),
   "Called immediate before processing this effect. This is the user's chance "
   "to set the value of shader uniforms (constants).\n"
   "@see setShaderConst"
);

//---------------------------------
void GuiBitmapShader::EffectConst::set( const String &newVal )
{
   if ( mStringVal == newVal )
      return;

   mStringVal = newVal;
   mDirty = true;
}

void GuiBitmapShader::EffectConst::setToBuffer( GFXShaderConstBufferRef buff )
{
   // Nothing to do if the value hasn't changed.
   if ( !mDirty )
      return;
   mDirty = false;

   // If we don't have a handle... get it now.
   if ( !mHandle )
      mHandle = buff->getShader()->getShaderConstHandle( mName );

   // If the handle isn't valid then we're done.
   if ( !mHandle->isValid() )
      return;

   const GFXShaderConstType type = mHandle->getType();

   // For now, we're only going
   // to support float4 arrays.
   // Expand to other types as necessary.
   U32 arraySize = mHandle->getArraySize();

   const char *strVal = mStringVal.c_str();

   if ( type == GFXSCT_Int )
   {
      S32 val;
      Con::setData( TypeS32, &val, 0, 1, &strVal );
      buff->set( mHandle, val );
   }
   else if ( type == GFXSCT_Float )
   {
      F32 val;
      Con::setData( TypeF32, &val, 0, 1, &strVal );
      buff->set( mHandle, val );
   }
   else if ( type == GFXSCT_Float2 )
   {
      Point2F val;
      Con::setData( TypePoint2F, &val, 0, 1, &strVal );
      buff->set( mHandle, val );
   }
   else if ( type == GFXSCT_Float3 )
   {
      Point3F val;
      Con::setData( TypePoint3F, &val, 0, 1, &strVal );
      buff->set( mHandle, val );
   }
   else if ( type == GFXSCT_Float4 )
   {
      Point4F val;

      if ( arraySize > 1 )
      {
         // Do array setup!
         //U32 unitCount = StringUnit::getUnitCount( strVal, "\t" );
         //AssertFatal( unitCount == arraySize, "" );

         String tmpString;
         Vector<Point4F> valArray;

         for ( U32 i = 0; i < arraySize; i++ )
         {
            tmpString = StringUnit::getUnit( strVal, i, "\t" );
            valArray.increment();
            const char *tmpCStr = tmpString.c_str();

            Con::setData( TypePoint4F, &valArray.last(), 0, 1, &tmpCStr );
         }

         AlignedArray<Point4F> rectData( valArray.size(), sizeof( Point4F ), (U8*)valArray.address(), false );
         buff->set( mHandle, rectData );
      }
      else
      {
         // Do regular setup.
         Con::setData( TypePoint4F, &val, 0, 1, &strVal );
         buff->set( mHandle, val );
      }
   }
   else
   {
#if TORQUE_DEBUG
      const char* err = avar("GuiBitmapShader::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
      Con::errorf(err);
      GFXAssertFatal(0,err);
#endif
   }
}
//---------------------------------

GuiBitmapShader::GuiBitmapShader()
{
   mPosVertex[0] = Point2F(0.0,0.0);
   mPosVertex[1] = Point2F(64.0,0.0);
   mPosVertex[2] = Point2F(0.0,64.0);
   mPosVertex[3] = Point2F(64.0,64.0);
   mUVValues[0] = Point2F(0.0,0.0);
   mUVValues[1] = Point2F(1.0,0.0);
   mUVValues[2] = Point2F(0.0,1.0);
   mUVValues[3] = Point2F(1.0,1.0);
   mAutoResizeBitmap = false;
   mOldGuiExtent = Point2I(0,0);
   
   mTexFilename[0] = "$inTex";
   mOldTexFilename[0] = "$inTex";
   mTexSizeSC[0] = NULL; //another SC
   for( U8 i = 1; i < NumTextures; i++ )
   {
      mTexFilename[ i ] = String::EmptyString;
      mOldTexFilename[ i ] = String::EmptyString;
      mTexSizeSC[ i ] = NULL; //another SC
   }

   mShaderData = NULL;
   mShader = NULL;
   mStateBlockData = NULL;
   mStateBlock = NULL;
   mShaderConsts = NULL;
   
   mOldShaderData = NULL;
   mOldStateBlockData = NULL;
   
   //SC
   mModelViewProjSC = NULL;
   mGuiSizeSC = NULL;
   mGuiOffsetSC = NULL;
   mAccumTimeSC = NULL;
   mDeltaTimeSC = NULL;
   
   mShaderReloadKey = 0;
}

GuiBitmapShader::~GuiBitmapShader()
{
   for ( U8 i = 0; i < NumTextures; i++ )
   {
      mTextures[ i ].free();
      mTextures[ i ] = NULL;
   }
   
   EffectConstTable::Iterator iter = mEffectConsts.begin();
   for ( ; iter != mEffectConsts.end(); iter++ )
      delete iter->value;
}

void GuiBitmapShader::initPersistFields()
{
   ///I add this here so in the editor the order keeps correct
   addGroup( "Bitmap" );
      ///Parent will add bitmap here
   endGroup( "Bitmap" );
   
   addGroup( "Bitmap transform" );
      addField( "pointTL", TypePoint2F, Offset( mPosVertex[0], GuiBitmapShader), "Location of the top left vertex." );
      addField( "pointTR", TypePoint2F, Offset( mPosVertex[1], GuiBitmapShader), "Location of the top right vertex." );
      addField( "pointBL", TypePoint2F, Offset( mPosVertex[2], GuiBitmapShader), "Location of the bottom left vertex." );
      addField( "pointBR", TypePoint2F, Offset( mPosVertex[3], GuiBitmapShader), "Location of the bottom right vertex." );
      addField( "uvTL", TypePoint2F, Offset( mUVValues[0], GuiBitmapShader), "UV values of the top left vertex." );
      addField( "uvTR", TypePoint2F, Offset( mUVValues[1], GuiBitmapShader), "UV values of the top right vertex." );
      addField( "uvBL", TypePoint2F, Offset( mUVValues[2], GuiBitmapShader), "UV values of the bottom left vertex." );
      addField( "uvBR", TypePoint2F, Offset( mUVValues[3], GuiBitmapShader), "UV values of the bottom right vertex." );
      addField( "autoResize", TypeBool, Offset( mAutoResizeBitmap, GuiBitmapShader ), "Bitmap resizes if the gui changes size." );
   endGroup( "Bitmap transform" );
   
   addGroup( "Bitmap shader" );
      addField( "shader", TYPEID<ShaderData>(), Offset( mShaderData, GuiBitmapShader ),
         "Name of a ShaderData for this effect." );
      addField( "stateBlock", TYPEID<GFXStateBlockData>(), Offset( mStateBlockData,  GuiBitmapShader ),
         "Name of a GFXStateBlockData for this effect." );
      addField( "texture", TypeImageFilename, Offset( mTexFilename, GuiBitmapShader ), NumTextures,
         "Input textures to this shader ( samplers )." );
   endGroup( "Bitmap shader" );

   Parent::initPersistFields();
   removeField( "wrap" ); //It comes from Parent::initPersistFields(). Not used in this gui.
}

void GuiBitmapShader::onRender(Point2I offset, const RectI &updateRect)
{
   Point2I extent = getExtent();
   if (mAutoResizeBitmap)
   {
      if (mOldGuiExtent.x != extent.x || mOldGuiExtent.y != extent.y)
      {
         _doAutoResizeBitmap(extent, mOldGuiExtent);
         mOldGuiExtent = extent;
      }
   }
   
   bool drawingSuccess = drawBitmapShader(offset);
   if (mProfile->mBorder || !drawingSuccess )
   {
      RectI rect(offset.x, offset.y, extent.x, extent.y);
      GFX->getDrawUtil()->drawRect(rect, mProfile->mBorderColor);
   }
   
   renderChildControls(offset, updateRect);
}

bool GuiBitmapShader::drawBitmapShader(Point2I offset)
{
   _setupStateBlock();
   bool customShader = true;
   customShader = _setupShader();
   
   //Shader textures
   // Set the textures.
   bool thereIsTexture = false;
   for ( U8 i = 0; i < NumTextures; i++ )
   {
      thereIsTexture = _setupTexture( i );
      if(i==0 && !customShader && !thereIsTexture)
         return false;
   }
   
   const F32 fillConv = GFX->getFillConventionOffset();
   GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, 4, GFXBufferTypeVolatile );
   verts.lock();
   Point2F vertexLocation;
   for(U8 v = 0; v<4; v++)
   {
      vertexLocation = mPosVertex[v] + Point2F((F32)offset.x,(F32)offset.y);
      verts[v].point.set(vertexLocation.x - fillConv, vertexLocation.y - fillConv, 0.f );
      verts[v].texCoord.set( mUVValues[v].x, mUVValues[v].y );
      verts[v].color = mColor;
   }
   verts.unlock();
   
   GFX->setVertexBuffer( verts );
   GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
   return true;
}

bool GuiBitmapShader::_setupTexture( U8 stage )
{
   const String &texFilename = mTexFilename[ stage ];
   
   if(texFilename.compare( mOldTexFilename[ stage ], 0, String::NoCase ) != 0 )
   {
      setTexture(stage, texFilename);
   }

   GFXTexHandle theTex;
   NamedTexTarget *namedTarget = NULL;

   if ( texFilename.compare( "$inTex", 0, String::NoCase ) == 0 )
   {
      theTex = mTextureObject;
   }
   else if ( texFilename.compare( "$backBuffer", 0, String::NoCase ) == 0 )
   {
      //Here I wanted "theTex = PFXMGR->getBackBufferTex();" like PostFX but it throws some texture leak
      //A copy of the base of the code of getBackBufferTex() seems to work
      GFXTarget *target = GFX->getActiveRenderTarget();
      
      const Point2I &targetSize = target->getSize();
      GFXFormat targetFormat = target->getFormat();

      theTex.set( targetSize.x, targetSize.y, 
                              targetFormat, 
                              &GFXRenderTargetProfile, "theTex" );

      target->resolveTo( theTex );
   }
   else if ( texFilename.isNotEmpty() && texFilename[0] == '#' )
   {
      namedTarget = NamedTexTarget::find( texFilename.c_str() + 1 );
      if ( namedTarget )
      {
         theTex = namedTarget->getTexture( 0 );
      }
   }
   else
   {
      theTex = mTextures[ stage ];
   }

   if ( theTex.isValid() )
   {
      if (mShaderConsts)
      {
         if (mTexSizeSC[stage]->isValid())
         {
            Point2F texSizeConst;
            texSizeConst.x = (F32)theTex->getWidth();
            texSizeConst.y = (F32)theTex->getHeight();
            mShaderConsts->set(mTexSizeSC[stage], texSizeConst);
         }
      }

      GFX->setTexture( stage, theTex );
      return true;
   }
   return false; //theTex is not valid
}

void GuiBitmapShader::setTexture( U8 index, const String &texFilePath )
{
	// Set the new texture name.
	mTexFilename[index] = texFilePath;
   mOldTexFilename[index] = texFilePath;
   mTextures[index].free();
   mTextures[index] = NULL;

   // Skip empty stages or ones with variable or target names.
   if (	texFilePath.isEmpty() || texFilePath[0] == '$' || texFilePath[0] == '#' )
      return;

   // Try to load the texture.
   mTextures[index].set( texFilePath, &GFXTexturePersistentProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
}

void GuiBitmapShader::_setupStateBlock()
{
   if ( mStateBlock.isNull() || mStateBlockData != mOldStateBlockData )
   {
      mOldStateBlockData = mStateBlockData;
      GFXStateBlockDesc desc;
      if ( mStateBlockData )
         desc = mStateBlockData->getState();
      else
         desc = GFX->getDrawUtil()->getBitmapStateBlockDesc(); //default values from drawUtil
      
      mStateBlock = GFX->createStateBlock( desc );
   }

   GFX->setStateBlock( mStateBlock );
}

bool GuiBitmapShader::_setupShader()
{
   if ( mShaderData == NULL || mShaderData != mOldShaderData )
   {
      if(mOldShaderData)
      {
         //Maybe use this to clean mShaderConsts because the "allocConstBuffer"?
         //delete[] mShaderConsts;
         
         mShaderConsts = NULL;
         mShader = NULL;

         //kinda cheat.
         mShaderReloadKey--;
         EffectConstTable::Iterator iter = mEffectConsts.begin();
         for ( ; iter != mEffectConsts.end(); iter++ )
            delete iter->value;
         mEffectConsts = EffectConstTable();
      }
      
      mOldShaderData = mShaderData;
      
      if( mShaderData )
      {
         if ( mShaderData->getPixVersion() <= GFX->getPixelShaderVersion() )
            mShader = mShaderData->getShader();
      }
   }
   
   if ( !mShader ) //mShaderData failed
   {
      GFX->setupGenericShaders( GFXDevice::GSModColorTexture );
      return false;
   }
   else
   {
      _setupConstants();
      GFX->setShader( mShader );

      GFX->setShaderConstBuffer( mShaderConsts );
      return true;
   }
}

void GuiBitmapShader::_setupConstants()
{
   // Alloc the const buffer.
   if ( mShaderConsts.isNull() )
   {
      mShaderConsts = mShader->allocConstBuffer();
      mModelViewProjSC = mShader->getShaderConstHandle( "$modelView" );
      mGuiSizeSC = mShader->getShaderConstHandle( "$guiSize" );
      
      mTexSizeSC[0] = mShader->getShaderConstHandle( "$texSize0" );
      mTexSizeSC[1] = mShader->getShaderConstHandle( "$texSize1" );
      mTexSizeSC[2] = mShader->getShaderConstHandle( "$texSize2" );
      mTexSizeSC[3] = mShader->getShaderConstHandle( "$texSize3" );
      mTexSizeSC[4] = mShader->getShaderConstHandle( "$texSize4" );
      mTexSizeSC[5] = mShader->getShaderConstHandle( "$texSize5" );
      mTexSizeSC[6] = mShader->getShaderConstHandle( "$texSize6" );
      mTexSizeSC[7] = mShader->getShaderConstHandle( "$texSize7" );
      
      mGuiOffsetSC = mShader->getShaderConstHandle( "$guiOffset" );
      
      mAccumTimeSC = mShader->getShaderConstHandle( "$accumTime" );
      mDeltaTimeSC = mShader->getShaderConstHandle( "$deltaTime" );
   }
   
   MatrixF xform(GFX->getProjectionMatrix());
   xform *= GFX->getViewMatrix();
   xform *= GFX->getWorldMatrix();
   mShaderConsts->setSafe( mModelViewProjSC, xform );
   
   if ( mGuiSizeSC->isValid() )
   {
      Point2F guiSize = Point2F((F32)getExtent().x,(F32)getExtent().y);
      mShaderConsts->set( mGuiSizeSC, guiSize );
   }
   
   if ( mGuiOffsetSC->isValid() )
   {
      Point2F guiOffset = Point2F((F32)getPosition().x,(F32)getPosition().y);
      mShaderConsts->set( mGuiOffsetSC, guiOffset );
   }
   
   mShaderConsts->setSafe( mAccumTimeSC, MATMGR->getTotalTime() );
   mShaderConsts->setSafe( mDeltaTimeSC, MATMGR->getDeltaTime() );
   
   ///
   // Set EffectConsts - specified from script

   // If our shader has reloaded since last frame we must mark all
   // EffectConsts dirty so they will be reset.
   if ( mShader->getReloadKey() != mShaderReloadKey )
   {
      mShaderReloadKey = mShader->getReloadKey();

      EffectConstTable::Iterator iter = mEffectConsts.begin();
      for ( ; iter != mEffectConsts.end(); iter++ )
      {
         iter->value->mDirty = true;
         iter->value->mHandle = NULL;
      }
   }
   
   setShaderConsts_callback();
   
   EffectConstTable::Iterator iter = mEffectConsts.begin();
   for ( ; iter != mEffectConsts.end(); iter++ )
      iter->value->setToBuffer( mShaderConsts );
}

void GuiBitmapShader::setShaderConst( const String &name, const String &val )
{
   PROFILE_SCOPE( GuiBitmapShader_SetShaderConst );

   EffectConstTable::Iterator iter = mEffectConsts.find( name );
   if ( iter == mEffectConsts.end() )
   {
      EffectConst *newConst = new EffectConst( name, val );
      iter = mEffectConsts.insertUnique( name, newConst );
   }

   iter->value->set( val );
}

DefineEngineMethod( GuiBitmapShader, setShaderConst, void, ( const char* name, const char* value ),,
   "Sets the value of a uniform defined in the shader. This will usually "
   "be called within the setShaderConsts callback. Array type constants are "
   "not supported.\n"    
   "@param name Name of the constanst, prefixed with '$'.\n" 
   "@param value Value to set, space seperate values with more than one element.\n"
   "@tsexample\n"
   "function MyGBTS::setShaderConsts( %this )\n"
   "{\n"
   "   // example float4 uniform\n"
   "   %this.setShaderConst( \"$colorMod\", \"1.0 0.9 1.0 1.0\" );\n"
   "   // example float1 uniform\n"
   "   %this.setShaderConst( \"$strength\", \"3.0\" );\n"
   "   // example integer uniform\n"
   "   %this.setShaderConst( \"$loops\", \"5\" );"
   "}\n"
   "@endtsexample" )   
{
   object->setShaderConst( name, value );
}

//------------------------------------------------------------------------------
void GuiBitmapShader::_doAutoResizeBitmap(Point2I extent, Point2I oldGuiExtent)
{
   if(oldGuiExtent.x != 0)
   {
      F32 factor = (F32)extent.x / (F32)oldGuiExtent.x;
      mPosVertex[0].x *= factor;
      mPosVertex[1].x *= factor;
      mPosVertex[2].x *= factor;
      mPosVertex[3].x *= factor;
   }
   if(oldGuiExtent.y != 0)
   {
      F32 factor = (F32)extent.y / (F32)oldGuiExtent.y;
      mPosVertex[0].y *= factor;
      mPosVertex[1].y *= factor;
      mPosVertex[2].y *= factor;
      mPosVertex[3].y *= factor;
   }
}

Point2F GuiBitmapShader::getCenter()
{
   return Point2F((mPosVertex[0].x+mPosVertex[1].x+mPosVertex[2].x+mPosVertex[3].x)/4.0,
      (mPosVertex[0].y+mPosVertex[1].y+mPosVertex[2].y+mPosVertex[3].y)/4.0);
}

Point2F GuiBitmapShader::getVertexPosition(U8 vertex)
{
   if (vertex > 3)
      return Point2F(-9999.9,-9999.9);
   
   return mPosVertex[vertex];
}

void GuiBitmapShader::setVertexPosition(Point2F vertexPos, U8 vertex)
{
   if (vertex > 3)
      return;
   
   mPosVertex[vertex] = vertexPos;
}

void GuiBitmapShader::moveVertex(Point2I distance, U8 vertex )
{
   if (vertex > 3)
      return;
   
   mPosVertex[vertex] += Point2F((F32)distance.x,(F32)distance.y);
}

void GuiBitmapShader::rotateVertex(F32 angleDeg, Point2I pivot, U8 vertex)
{
   if (vertex > 3)
      return;
   
   if(mPosVertex[vertex].x == (F32)pivot.x && mPosVertex[vertex].y == (F32)pivot.y)
      return;
   
   Point2F vector = Point2F(mPosVertex[vertex].x - (F32)pivot.x, mPosVertex[vertex].y - (F32)pivot.y);
   
   F32 dist = mSqrt((vector.x * vector.x) + (vector.y * vector.y));
   F32 angl = mAtan2( vector.x , vector.y );
   
   mPosVertex[vertex].x = (dist*mSin(angl - mDegToRad(angleDeg))) + pivot.x;
   mPosVertex[vertex].y = (dist*mCos(angl - mDegToRad(angleDeg))) + pivot.y;
}

void GuiBitmapShader::scaleVertex(F32 scale, Point2I pivot, U8 vertex)
{
   if (vertex > 3)
      return;
   
   if(mPosVertex[vertex].x == pivot.x && mPosVertex[vertex].y == pivot.y)
      return;
   
   Point2F vector = Point2F(mPosVertex[vertex].x - (F32)pivot.x, mPosVertex[vertex].y - (F32)pivot.y);
   
   vector.x *= scale;
   vector.y *= scale;
   
   mPosVertex[vertex] = Point2F(vector.x + (F32)pivot.x, vector.y + (F32)pivot.y);
}

F32 GuiBitmapShader::getBitmapProportions()
{
   if ( mTextureObject )
   {
      return (F32)mTextureObject->getWidth() / (F32)mTextureObject->getHeight();
   }
   else
      return 0.0;
}

Point4F GuiBitmapShader::getBitmapBounds()
{
   Point4F bounds = Point4F(mPosVertex[0].x,mPosVertex[0].y,mPosVertex[0].x,mPosVertex[0].y);
   for(U8 i = 1; i<4; i++)
   {
      if(mPosVertex[ i ].x<bounds.x)
         bounds.x = mPosVertex[ i ].x;
      if(mPosVertex[ i ].x>bounds.z)
         bounds.z = mPosVertex[ i ].x;
      
      if(mPosVertex[ i ].y<bounds.y)
         bounds.y = mPosVertex[ i ].y;
      if(mPosVertex[ i ].y>bounds.w)
         bounds.w = mPosVertex[ i ].y;
   }
   return bounds;
}

//-----------------------------------------------------------------------------

DefineEngineMethod(GuiBitmapShader, getCenter, Point2F, (),,
   "@brief Get the center of mass of the bitmap.\n\n"
   "@return A point2F. You may want to convert it into a Point2I." )
{
   return object->getCenter();
}

DefineEngineMethod(GuiBitmapShader, moveBitmap, void, (Point2I distance),,
   "@brief Move the bitmap.\n\n"
   "@param distance Pixels (x,y) to move.\n"
   "+x: right, -x: left, +y: down, -y: up")
{
   object->moveVertex(distance,0);
   object->moveVertex(distance,1);
   object->moveVertex(distance,2);
   object->moveVertex(distance,3);
}

DefineEngineMethod(GuiBitmapShader, rotateBitmap, void, (F32 angleDeg, Point2I pivot),,
   "@brief Rotate the bitmap.\n\n"
   "@param angleDeg Angle in degrees to rotate (positive clockwise).\n"
   "@param pivot Point around vertices rotate.\n\"0 0\" is top left of the gui.")
{
   object->rotateVertex(angleDeg,pivot,0);
   object->rotateVertex(angleDeg,pivot,1);
   object->rotateVertex(angleDeg,pivot,2);
   object->rotateVertex(angleDeg,pivot,3);
}

DefineEngineMethod(GuiBitmapShader, scaleBitmap, void, (F32 scale, Point2I pivot),,
   "@brief Resize the bitmap.\n\n"
   "@param scale Number to scale. 1.0 keeps size.\n"
   "@param pivot Point around vertices scale the distance.\n\"0 0\" is top left of the gui.")
{
   object->scaleVertex(scale,pivot,0);
   object->scaleVertex(scale,pivot,1);
   object->scaleVertex(scale,pivot,2);
   object->scaleVertex(scale,pivot,3);
}

DefineEngineMethod(GuiBitmapShader, fillExpand, void, (bool keepBitmapRatio),,
   "@brief Expand the bitmap to fill the gui.\n\n"
   "@param keepBitmapRatio If true the bitmap keeps proportions of $inTex.\n")
{
   F32 ratio = object->getBitmapProportions();
   
   //ratio == 0.0 means there is no bitmap asigned. Just set vertices to fill.
   if (!keepBitmapRatio || ratio == 0.0)
   {
      object->setVertexPosition(Point2F(0.0,0.0),0);
      object->setVertexPosition(Point2F(object->getExtent().x,0.0),1);
      object->setVertexPosition(Point2F(0.0, object->getExtent().y),2);
      object->setVertexPosition(Point2F(object->getExtent().x, object->getExtent().y),3);
   }
   else
   {
      //ratio = Width / Height
      F32 guiRatio = (F32)object->getExtent().x / (F32)object->getExtent().y;
      
      if(guiRatio < ratio)
      {
         F32 xExtent = (F32)object->getExtent().x;
         F32 yExtent = xExtent / ratio;
         F32 topPosition = 0.5 * (F32)object->getExtent().y - 0.5 * yExtent;
         F32 bottomPosition = topPosition + yExtent;
         
         object->setVertexPosition(Point2F(0.0,topPosition),0);
         object->setVertexPosition(Point2F(xExtent,topPosition),1);
         object->setVertexPosition(Point2F(0.0,bottomPosition),2);
         object->setVertexPosition(Point2F(xExtent,bottomPosition),3);
      }
      else
      {
         F32 yExtent = (F32)object->getExtent().y;
         F32 xExtent = yExtent * ratio;
         F32 leftPosition = 0.5 * (F32)object->getExtent().x - 0.5 * xExtent;
         F32 rightPosition = leftPosition + xExtent;
         
         object->setVertexPosition(Point2F(leftPosition,0.0),0);
         object->setVertexPosition(Point2F(rightPosition,0.0),1);
         object->setVertexPosition(Point2F(leftPosition,yExtent),2);
         object->setVertexPosition(Point2F(rightPosition,yExtent),3);
      }
   }
}

DefineEngineMethod(GuiBitmapShader, getBitmapBounds, Point4F, (),,
   "@brief Get the bounds of the bitmap.\n\n"
   "@return Point4F. First pair is top left and last is bottom right." )
{
   return object->getBitmapBounds();
}

DefineEngineMethod(GuiBitmapShader, doCenterBitmap, void, (),,
   "@brief Move the bitmap to be centered in the gui.\n")
{
   Point2F bitmapCenter = object->getCenter();
   Point2F guiCenter = Point2F(object->getExtent().x/2.0,object->getExtent().x/2.0);
   
   Point2I distance = Point2I(mRound(guiCenter.x-bitmapCenter.x),mRound(guiCenter.y-bitmapCenter.y));
   
   object->moveVertex(distance,0);
   object->moveVertex(distance,1);
   object->moveVertex(distance,2);
   object->moveVertex(distance,3);
}

DefineEngineMethod(GuiBitmapShader, setBitmapBounds, void, (Point4F newBounds),,
   "@brief Set the bounds of the bitmap.\n\n"
   "@param newBounds First pair is top left and last is bottom right." )
{   
   Point4F oldBounds = object->getBitmapBounds();
   
   F32 horRatio = 0.0;
   F32 verRatio = 0.0;
   
   if (oldBounds.z-oldBounds.x != 0.0)
      horRatio = (newBounds.z-newBounds.x) / (oldBounds.z-oldBounds.x);
   if (oldBounds.w-oldBounds.y != 0.0)
      verRatio = (newBounds.w-newBounds.y) / (oldBounds.w-oldBounds.y);
   
   for(U8 i = 0; i<4; i++)
   {
      Point2F ver = object->getVertexPosition(i);
      object->setVertexPosition( Point2F((horRatio*(ver.x-oldBounds.x))+newBounds.x,(verRatio*(ver.y-oldBounds.y))+newBounds.y), i);
   }
}
   
DefineEngineMethod(GuiBitmapShader, autoResizeGui, void, (),,
   "@brief Move and resize the gui to show all the bitmap.\n")
{
   Point4F bounds = object->getBitmapBounds();
   Point2I newExtent = Point2I(mRound(bounds.z - bounds.x), mRound(bounds.w - bounds.y));
   if (newExtent.x != 0 && newExtent.y != 0)
   {
      object->setExtent(newExtent);
      object->setOldGuiExtent(newExtent);

      Point2I newPosition = object->getPosition() + Point2I(mRound(bounds.x), mRound(bounds.y));
      object->setPosition(newPosition.x,newPosition.y);

      Point2I distance = Point2I(-1 * mRound(bounds.x), -1 * mRound(bounds.y));
      object->moveVertex(distance, 0);
      object->moveVertex(distance, 1);
      object->moveVertex(distance, 2);
      object->moveVertex(distance, 3);
   }
}
//------------------------------------------------------------------

-Usage-
GuiCopyContainer

In the gui editor you can find it in Lybrary/Containers/GuiCopyConatiner .(or make it in script).
Then just like a normal gui container add any gui inside you want.
You need to add a stateBlock for it to render to texture correctly. This type seems to work the best (add it in some .cs file):
singleton GFXStateBlockData( BasicStateBlock )
{   
   blendDefined = true;
   blendEnable = true; 
   blendSrc = GFXBlendSrcAlpha;
   blendDest = GFXBlendInvSrcAlpha;
   blendOp = GFXBlendOpAdd;
   
   separateAlphaBlendDefined = true;
   separateAlphaBlendEnable = true;
   separateAlphaBlendSrc = GFXBlendOne;
   separateAlphaBlendDest = GFXBlendOne;
   separateAlphaBlendOp = GFXBlendOpAdd;
   
   samplersDefined = true;
   samplerStates[0] = SamplerClampLinear;
};
If you're saving the container in the gui you will need to load that before the container is introduced like it's done in mainmenu.gui.

Other variables are:
-textureName: this is the name of the texture you can use in places where named textures are used.
-targetFormat: the format of the texture. GFXFormatR8G8B8A8 is good enough.

-blockNormalRender: if true then the contents of the container are active but not rendered. It's used if you want to have the container in an active gui but you only want to use its named texture.
-autoCopy: if true the gui copies the texture each time onRender is called. If false then the container is passive and you have to use the method manually. Similar on how you can disable PostEffect and make it render one frame.

The method is:
GuiCopyContainer::copyContainer()
It just does the copy of the container to a texture manually. It's used if autoCopy is disabled.

//------------------------------------------------------------------

-Usage-
GuiBitmapShader

In the gui editor you can find it in Lybrary/Images/GuiBitmapShader .(or make it in script).
Then just like a normal gui bitmap control it'll display an image related to how shader is defined and the textures detailed.

If you're saving the gui you will need to load the shader and the stateblock before the gui is loaded like it's done in mainmenu.gui.

Its variables are:
-pointTL, pointTR, pointBL and pointBR: those are Point2F in the coordinates inside the gui ("0 0" being the top left of the gui) that defines the four vertices of the image. There is a method to fill the gui but if you set it manually you can force the 4 faces polygon to have any shape (like diamond or a trapeze).
T top, B bottom, L left and R right but you can rotate it if you want so it's only a way to remember the order. If the order of the polygon is changed it may become backfaced triangles and not render (culling thing).
The stretch of the texture comes from interpolation on the triangles so irregular shapes may not show good results. Use the shader code for that, instead.
-uvTL, uvTR, uvBL and uv BR: those are the uv coordinates in the shader of the previous vertices. If you want to mirror the image you can change the order of these.
-autoResize: if the gui changes size, the vertices are modified to resize the image bounds with the gui.

-shader: the ShaderData used. It can be left blank and the gui will just display the texture[0] with the points and uv values used.
-stateBlock: a GFXStateBlockData that defines how it's rendered. You may leave it blank and it'll use the default one from DrawUtil so you get the options correctly when combining with GuiCopyContainer.

-texture[0-7]: The used textures.
Use "$inTex" to use the image from the bitmap variable.
Use "$backbuffer" to try to use the backbuffer... but it's a bit hack. Better to use a PostEffect rendering it to named texture and use the #.
Use "#texture" to use a named texture with name "texture" (note the # before the name).
Use the location of the image to use the texture like "data/splash.png".

Then in the hlsl or glsl shader you can use the textures like
TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0);
TORQUE_UNIFORM_SAMPLER2D(basealpha, 1);
TORQUE_UNIFORM_SAMPLER2D(diffuseMapB, 2);


Like PostEffect you can use the setShaderConsts callback to add constants to the shader with setShaderConst like:
$colorVar1 = "0.0 1.0 0.0 1.0";
$colorVar2 = "1.0 0.0 0.0 1.0";
function GuiBitmapShaderName::setShaderConsts( %this )
{
   %this.setShaderConst( "$colorMod", $colorVar1 );
   %this.setShaderConst( "$colorModB", $colorVar2 );
}
The gui has some engine uniforms defined you can use:
modelView : transform for vertex drawing
guiSize : float2 with the pixel extent of the gui
texSize0-7 : float2 with the size of the texture[0-7]
guiOffset : the getPosition() of the gui (so it can be located in combination with guiSize in screen space)
accumTime : the getTime() milliseconds variable.
deltaTime : the same as accumTime but only for the current frame

Other methods:
-Point2F XXX.getCenter(): returns the center of the four vertices. Use it to rotate better the image, specially if the gui changes size.
-XXX.moveBitmap(Point2I distance): moves the four vertices around that distance.
-XXX.rotateBitmap(F32 angle, Point2I pivot): rotate the four vertices around the pivot.
-XXX.scaleBitmap(F32 scale, Point2I pivot): scale uniformly the four vertices around the pivot.
-XXX.fillExpand(bool keepBitmapRatio): expands the four vertices to fill the gui. If keepBitmapRatio is true then it will not break the proportions of $inTex.
-Point4F XXX.getBitmapBounds(): returns a Point4F. It represents the rectangle ortogonal rectangle that contains the image. Fist pair is top left and second pair is bottom right coordinates inside the gui.
-XXX.doCenterBitmap(): moves the vertices so its center is the center of the gui.
-XXX.setBitmapBounds(Point4F bounds): scales and moves the vertices so it's located inside the rectangle defined in blounds (first pair is top left and second pair is bottom right coordinates). You can use it to scale the gui with different values for x and y.
-XXX.autoResizeGui(): scales and moves the gui itself so it'll display all the vertices (the vertices coordinates will change, then).

Image
1) GuiBitmapShader. It's using the named texture from 2), a conditional to show red if the uv is lower than a value, it's moving with cos(accumTime) and a file texture to use the alpha as mask.
2) GuiCopyContainer with a bitmap control and a text.
3) bitmap control using setNamedTexture to display 2)

Example of shader (note: only using directx). You may need to change the location link of the files:
cs file:
singleton ShaderData( TestSOne )
{
   DXVertexShaderFile = "./fileV.hlsl";
   DXPixelShaderFile  = "./fileP.hlsl";
   
   pixVersion = 1.0;
};

singleton GFXStateBlockData( BaseStateBlock )
{   
   blendDefined = true;
   blendEnable = true; 
   blendSrc = GFXBlendSrcAlpha;
   blendDest = GFXBlendInvSrcAlpha;
   blendOp = GFXBlendOpAdd;
   
   separateAlphaBlendDefined = true;
   separateAlphaBlendEnable = true;
   separateAlphaBlendSrc = GFXBlendOne;
   separateAlphaBlendDest = GFXBlendOne;
   separateAlphaBlendOp = GFXBlendOpAdd;
   
   samplersDefined = true;
   samplerStates[0] = SamplerClampLinear;
};

$colorVar1 = "0.0 1.0 0.0 1.0";
$colorVar2 = "1.0 0.0 0.0 1.0";
function TestA::setShaderConsts( %this )
{
   %this.setShaderConst( "$colorMod", $colorVar1 );
   %this.setShaderConst( "$colorModB", $colorVar2 );
}

//type est in the consolo to add the gui in the mainmenu
function est()
{
   MainMenuGui.add(TestA);
   TestA.fillExpand(false);
}

if(!isObject(TestA))
{
   
   new GuiBitmapShader(TestA)
   {
      shader = "TestSOne";
      stateBlock = BaseStateBlock;
      texture[0] = "data/splash.png";
      texture[1] = "$inTex";
      texture[2] = "#copiedcontainer";
      //uvTL = "0.01 0.01";
      //uvTR = "0.99 0.01";
      //uvBL = "0.01 0.99";
      //uvBR = "0.99 0.99";
      bitmap = "data/customdata/scripts/base_img.png";
      color = "255 255 255 255";
      position = "50 50";
      extent = "384 384";
      minExtent = "8 2";
      horizSizing = "right";
      vertSizing = "bottom";
      profile = "GuiDefaultProfile";
      visible = "1";
      active = "1";
      tooltipProfile = "GuiToolTipProfile";
      hovertime = "1000";
      isContainer = "0";
      canSave = "1";
      canSaveDynamicFields = "0";
   };
}
fileV.hlsl:
#include "../../../core/rendering/shaders/shaderModel.hlsl"

struct Appdata
{
	float3 position   : POSITION;
	float4 color      : COLOR;
	float2 texCoord   : TEXCOORD0;
};

struct Conn
{
   float4 HPOS             : TORQUE_POSITION;
   float4 color            : COLOR;
   float2 texCoord         : TEXCOORD0;
};

uniform float4x4 modelview;

Conn main( Appdata In )
{
   Conn Out;
   Out.HPOS = mul(modelview, float4(In.position,1.0));
   Out.color = In.color;
   Out.texCoord = In.texCoord;
   return Out;
}
fileP.hlsl:
#include "../../../core/rendering/shaders/shaderModel.hlsl"

struct datainput {
   float4 HPOS             : TORQUE_POSITION;
   float4 color            : COLOR;
   float2 texCoord         : TEXCOORD0;
};

TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0);
TORQUE_UNIFORM_SAMPLER2D(basealpha, 1);
TORQUE_UNIFORM_SAMPLER2D(diffuseMapB, 2);

uniform float2 guiSize;
uniform float accumTime;
uniform float4 colorMod;
uniform float4 colorModB;

float4 main( datainput IN ) : SV_Target0
{
   IN.texCoord += 0.5*cos(accumTime);
   float4 col = TORQUE_TEX2D(diffuseMap, IN.texCoord);
   col = colorModB;
   if(IN.texCoord.x*guiSize.x > 20 && IN.texCoord.y*guiSize.y > 20)
      col = TORQUE_TEX2D(diffuseMapB, IN.texCoord);
      
   float4 alphy = TORQUE_TEX2D(basealpha, IN.texCoord);
   
   if(alphy.a > -1)
      col = float4(col.xyz,alphy.a);
   else
      col = colorMod;

   //here you can modify colourexit

   return col;
}

Text with line breaks: https://pastebin.com/ce64NY7Y
1 post Page 1 of 1

Who is online

Users browsing this forum: No registered users and 4 guests