SHARE
TWEET

Untitled

a guest Feb 20th, 2019 59 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. These are a pair of gui objects.
  2. guiCopyContainer is a container that can render its contents to a named texture for use in other objects.
  3. 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.
  4.  
  5. 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.
  6. -a) Add this as public: (important being public:) in "class FontRenderBatcher" of source\gfx\gfxFontRenderBatcher.h-
  7.  
  8. [code2=BBCODE40_CPLUSPLUS]
  9.    GFXStateBlockDesc getFontStateBlockDesc();
  10.    
  11.    void externalSetupFontStateBlock(GFXStateBlockDesc fontSBD);
  12. [/code2]
  13.  
  14. -b) Add this in source\gfx\gfxFontRenderBatcher.cpp-
  15.  
  16. [code2=BBCODE40_CPLUSPLUS]GFXStateBlockDesc FontRenderBatcher::getFontStateBlockDesc()
  17. {
  18.    return mFontSB->getDesc();
  19. }
  20.  
  21. void FontRenderBatcher::externalSetupFontStateBlock(GFXStateBlockDesc fontSBD)
  22. {
  23.    fontSBD.cullDefined = true;
  24.    fontSBD.cullMode = GFXCullNone;
  25.    
  26.    fontSBD.samplersDefined = true;
  27.    fontSBD.samplers[0].alphaOp = GFXTOPModulate;
  28.    fontSBD.samplers[0].magFilter = GFXTextureFilterPoint;
  29.    fontSBD.samplers[0].minFilter = GFXTextureFilterPoint;
  30.    fontSBD.samplers[0].addressModeU = GFXAddressClamp;
  31.    fontSBD.samplers[0].addressModeV = GFXAddressClamp;
  32.    fontSBD.samplers[0].alphaArg1 = GFXTATexture;
  33.    fontSBD.samplers[0].alphaArg2 = GFXTADiffuse;
  34.    // This is an add operation because in D3D, when a texture of format D3DFMT_A8
  35.    // is used, the RGB channels are all set to 0.  Therefore a modulate would
  36.    // result in the text always being black.  This may not be the case in OpenGL
  37.    // so it may have to change.  -bramage
  38.    fontSBD.samplers[0].textureColorOp = GFXTOPAdd;
  39.  
  40.    mFontSB = GFX->createStateBlock(fontSBD);
  41. }
  42. [/code2]
  43.  
  44. -c) Add this as public: (important being public:) in "class GFXDrawUtil" of source\gfx\gfxDrawUtil.h-
  45.  
  46. [code2=BBCODE40_CPLUSPLUS]   GFXStateBlockDesc getBitmapStateBlockDesc();
  47.    GFXStateBlockDesc getFontStateBlockDesc();
  48.    void externalSetupBitmapStateBlock(GFXStateBlockDesc bitmapSBD);
  49.    void externalSetupFontStateBlock(GFXStateBlockDesc fontSBD);
  50.    [/code2]
  51.    
  52.    
  53. -d) Finally, add this in source\gfx\gfxDrawUtil.cpp-
  54.  
  55. [code2=BBCODE40_CPLUSPLUS]GFXStateBlockDesc GFXDrawUtil::getBitmapStateBlockDesc()
  56. {
  57.    return mBitmapStretchWrapLinearSB->getDesc();
  58. }
  59.  
  60. GFXStateBlockDesc GFXDrawUtil::getFontStateBlockDesc()
  61. {
  62.    return mFontRenderBatcher->getFontStateBlockDesc();
  63. }
  64.  
  65. void GFXDrawUtil::externalSetupBitmapStateBlock(GFXStateBlockDesc bitmapSBD)
  66. {
  67.    // Linear: Create wrap SB
  68.    mBitmapStretchWrapLinearSB = mDevice->createStateBlock(bitmapSBD);
  69.  
  70.    // Linear: Create clamp SB
  71.    bitmapSBD.samplers[0] = GFXSamplerStateDesc::getClampLinear();
  72.    mBitmapStretchLinearSB = mDevice->createStateBlock(bitmapSBD);
  73.  
  74.    // Point:
  75.    bitmapSBD.samplers[0].minFilter = GFXTextureFilterPoint;
  76.    bitmapSBD.samplers[0].mipFilter = GFXTextureFilterPoint;
  77.    bitmapSBD.samplers[0].magFilter = GFXTextureFilterPoint;
  78.  
  79.    // Point: Create clamp SB, last created clamped so no work required here
  80.    mBitmapStretchSB = mDevice->createStateBlock(bitmapSBD);
  81.  
  82.    // Point: Create wrap SB, have to do this manually because getWrapLinear doesn't
  83.    bitmapSBD.samplers[0].addressModeU = GFXAddressWrap;
  84.    bitmapSBD.samplers[0].addressModeV = GFXAddressWrap;
  85.    bitmapSBD.samplers[0].addressModeW = GFXAddressWrap;
  86.    mBitmapStretchWrapSB = mDevice->createStateBlock(bitmapSBD);
  87.  
  88.    //Note: Not sure about rectFill. But it seems to work fine without changes.
  89.    //GFXStateBlockDesc rectFill;
  90.    //rectFill.setCullMode(GFXCullNone);
  91.    //rectFill.setZReadWrite(false);
  92.    //rectFill.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
  93.    //mRectFillSB = mDevice->createStateBlock(rectFill);
  94. }
  95.  
  96. void GFXDrawUtil::externalSetupFontStateBlock(GFXStateBlockDesc fontSBD)
  97. {
  98.    //font StateBlock
  99.    mFontRenderBatcher->externalSetupFontStateBlock(fontSBD);
  100. }
  101. [/code2]
  102.  
  103.  
  104. //--------------------------------------------------------------------------------------------------
  105.  
  106. Now add these c++ files to the source:
  107.  
  108. guiCopyContainer.h
  109. [code2=BBCODE40_CPLUSPLUS]#ifndef _GUICOPYCONTAINER_H_
  110. #define _GUICOPYCONTAINER_H_
  111.  
  112. #ifndef _GUICONTAINER_H_
  113. #include "gui\containers\guiContainer.h"
  114. #endif
  115.  
  116. #ifndef _MATTEXTURETARGET_H_
  117. #include "materials/matTextureTarget.h"
  118. #endif
  119.  
  120. class GFXStateBlockData;
  121.  
  122. class GuiCopyContainer : public GuiContainer
  123. {
  124. private:
  125.    typedef GuiContainer Parent;
  126.  
  127. public:
  128.    DECLARE_CONOBJECT(GuiCopyContainer);
  129.    DECLARE_CATEGORY( "Gui Containers" );
  130.    DECLARE_DESCRIPTION( "A container that can copy its children renders into a texture.");
  131.  
  132.    // Constructor/Destructor/ConObject Declaration
  133.    GuiCopyContainer();
  134.    
  135.    static void initPersistFields();
  136.    
  137.    bool onAdd();
  138.    void onRemove();
  139.  
  140.    void onRender(Point2I offset, const RectI &updateRect);
  141.    
  142.    void _setUniqueTextureName( const char *inName );
  143.    void _onTextureEvent( GFXTexCallbackCode code );
  144.    
  145.    void _setupTargets();
  146.    void _teardownTargets();
  147.    
  148.    void copyContainer();
  149.    
  150. protected:
  151.    static bool setTextureName( void *object, const char *index, const char *data );
  152.  
  153.    //Set this to true to have the GuiCopyContainer become invisible but active
  154.    bool mBlockNormalRender;
  155.    bool mAutoCopy;
  156.  
  157.    GFXTextureTargetRef mTarget;
  158.    GFXTexHandle mTargetTexture;
  159.    NamedTexTarget mNamedTarget;
  160.    
  161.    GFXFormat mTargetFormat;
  162.    StringTableEntry mTargetName;
  163.    
  164.    GFXStateBlockData *mStateBlockData;
  165.    GFXStateBlockData *mOldStateBlockData;
  166.    GFXStateBlockRef mStateBlock;
  167. };
  168. /// @}
  169.  
  170. #endif // _GUICOPYCONTAINER_H_[/code2]
  171.  
  172. guiCopyContainer.cpp
  173. [code2=BBCODE40_CPLUSPLUS]#include "guiCopyContainer.h"
  174. #include "gui/core/guiDefaultControlRender.h"
  175.  
  176. #include "console/console.h"
  177. #include "console/consoleTypes.h"
  178. #include "console/engineAPI.h"
  179.  
  180. #include "gfx/gfxDrawUtil.h"
  181. #include "gfx/gfxTextureManager.h"
  182. #include "gfx/gfxAPI.h"
  183.  
  184. #include "gfx/sim/gfxStateBlockData.h"
  185. #include "gfx/gfxTransformSaver.h"
  186.  
  187. IMPLEMENT_CONOBJECT(GuiCopyContainer);
  188.  
  189. ConsoleDocClass( GuiCopyContainer,
  190.    "@brief A gui container that copies its content into a named texture.\n\n"
  191.    
  192.    "@ingroup GuiContainers"
  193. );
  194.  
  195. GuiCopyContainer::GuiCopyContainer()
  196. {
  197.    mBlockNormalRender = false;
  198.    mAutoCopy = false;
  199.    mTargetFormat = GFXFormatR8G8B8A8;
  200.    //default mTargetName will be  "copiedcontainer" + n  defined inside onAdd
  201.    mTargetName = NULL;
  202.    
  203.    mStateBlockData = NULL;
  204.    mStateBlock = NULL;
  205.    mOldStateBlockData = NULL;
  206. }
  207.  
  208. bool GuiCopyContainer::onAdd()
  209. {
  210.    if(!Parent::onAdd())
  211.       return false;
  212.    
  213.    _setUniqueTextureName(mTargetName);
  214.    //Inside _setUniqueTextureName we have:
  215.    //mNamedTarget.registerWithName( mTargetName );
  216.  
  217.    _setupTargets();
  218.    GFXTextureManager::addEventDelegate( this, &GuiCopyContainer::_onTextureEvent );
  219.    return true;
  220. }
  221.  
  222. void GuiCopyContainer::onRemove()
  223. {
  224.    mNamedTarget.unregister();
  225.    GFXTextureManager::removeEventDelegate( this, &GuiCopyContainer::_onTextureEvent );
  226.  
  227.    _teardownTargets();
  228.  
  229.    //Needed to avoid
  230.    //   Exception thrown : read access violation.
  231.    //   **GFXDevice::get**(...) returned nullptr.
  232.    //when closing the program while running:
  233.    if(mBlockNormalRender && mAutoCopy)
  234.       GFX->clear(GFXClearTarget, LinearColorF(0, 0, 0, 0), 1.0f, 0);
  235.  
  236.    mTarget = NULL;
  237.    mTargetTexture = NULL;
  238.    Parent::onRemove();
  239. }
  240.  
  241. void GuiCopyContainer::initPersistFields()
  242. {
  243.    addGroup( "CopyContainer" );
  244.    
  245.       addField( "blockNormalRender", TypeBool, Offset( mBlockNormalRender, GuiCopyContainer ),
  246.          "True to disable render of its children even with the visible flag enabled.");
  247.       addField( "autoCopy", TypeBool, Offset( mAutoCopy, GuiCopyContainer ),
  248.          "If true it calls copyContainer() itself each frame.");
  249.       addProtectedField( "textureName", TypeString, Offset( mTargetName, GuiCopyContainer ), &setTextureName, &defaultProtectedGetFn,
  250.          "Name of the texture.");
  251.       addField( "targetFormat", TypeGFXFormat, Offset( mTargetFormat, GuiCopyContainer ),
  252.          "Format of the texture. It should be GFXFormatR8G8B8A8.");
  253.       addField( "stateBlock", TYPEID<GFXStateBlockData>(), Offset( mStateBlockData,  GuiCopyContainer ),
  254.          "Optional name of a GFXStateBlockData to replace default values." );
  255.      
  256.    endGroup( "CopyContainer" );
  257.  
  258.    Parent::initPersistFields();
  259. }
  260.  
  261. bool GuiCopyContainer::setTextureName( void *object, const char *index, const char *data )
  262. {
  263.    static_cast<GuiCopyContainer *>( object )->_setUniqueTextureName(data);
  264.    // Return false because the _setUniqueTextureName method will assign 'mTargetName' to the
  265.    // argument we are specifying in the call.
  266.    return false;
  267. }
  268.  
  269. void GuiCopyContainer::_setupTargets()
  270. {
  271.    _teardownTargets();
  272.  
  273.    if (!mTarget.isValid())
  274.    {
  275.       mTarget = GFX->allocRenderToTextureTarget();
  276.    }
  277.  
  278.    // Update color
  279.    Point2I targetSize = getExtent();
  280.    if (!mTargetTexture.isValid() || targetSize != mTargetTexture.getWidthHeight())
  281.    {
  282.       mTargetTexture.set( targetSize.x, targetSize.y, mTargetFormat, &GFXRenderTargetSRGBProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ), 1, 0 );
  283.    }
  284.    
  285.    mTarget->attachTexture( GFXTextureTarget::RenderSlot(GFXTextureTarget::Color0), mTargetTexture );
  286.    mNamedTarget.setTexture(0, mTargetTexture);
  287.  
  288. }
  289.  
  290. void GuiCopyContainer::_teardownTargets()
  291. {
  292.    mNamedTarget.release();
  293.    mTargetTexture = NULL;
  294. }
  295.  
  296.  
  297. void GuiCopyContainer::_setUniqueTextureName( const char *inName )
  298. {
  299.    String outName( inName );
  300.  
  301.    if ( outName.isEmpty() )
  302.       outName = "copiedcontainer";
  303.  
  304.    if ( mNamedTarget.registerWithName( outName ) )
  305.    {
  306.       mTargetName = StringTable->EmptyString();
  307.       mTargetName = StringTable->insert(outName);
  308.       return;
  309.    }
  310.  
  311.    S32 suffixNumb = -1;
  312.    String nameStr( String::GetTrailingNumber( outName, suffixNumb ) );
  313.    suffixNumb = mAbs( suffixNumb ) + 1;
  314.  
  315.    #define MAX_TRIES 100
  316.  
  317.    for ( U32 i = 0; i < MAX_TRIES; i++ )
  318.    {  
  319.        outName = String::ToString("%s%d", nameStr.c_str(), suffixNumb);
  320.  
  321.       if ( mNamedTarget.registerWithName( outName ) )
  322.       {
  323.          mTargetName = StringTable->EmptyString();
  324.          mTargetName = StringTable->insert(outName);
  325.          return;
  326.       }
  327.  
  328.       suffixNumb++;
  329.    }
  330.  
  331.    Con::errorf( "_setUniqueTextureName - failed after %d attempts", inName, MAX_TRIES );
  332.    mTargetName = NULL;
  333.    return;
  334. }
  335.  
  336.  
  337. void GuiCopyContainer::_onTextureEvent( GFXTexCallbackCode code )
  338. {
  339.    switch(code)
  340.    {
  341.       case GFXZombify:
  342.          _teardownTargets();
  343.          break;
  344.  
  345.       case GFXResurrect:
  346.          _setupTargets();
  347.          break;
  348.    }
  349. }
  350.  
  351. void GuiCopyContainer::onRender(Point2I offset, const RectI &updateRect)
  352. {
  353.    if (mAutoCopy)
  354.       copyContainer();
  355.  
  356.    if(!mBlockNormalRender)
  357.    {
  358.       RectI ctrlRect(offset, getExtent());
  359.  
  360.       //if opaque, fill the update rect with the fill color
  361.       if ( mProfile->mOpaque )
  362.          GFX->getDrawUtil()->drawRectFill(ctrlRect, mProfile->mFillColor);
  363.      
  364.       //if there's a border, draw the border
  365.       if ( mProfile->mBorder )
  366.          renderBorder(ctrlRect, mProfile);
  367.      
  368.       renderChildControls(offset, updateRect);
  369.    }
  370. }
  371.  
  372.  
  373. void GuiCopyContainer::copyContainer()
  374. {
  375.    if( dStrIsEmpty(mTargetName) )
  376.    {
  377.        Con::errorf("Name of the texture couldn't be set properly.");
  378.        return;
  379.    }
  380.    
  381.    Point2I size = getExtent();
  382.    if(size.x == 0 || size.y == 0)
  383.    {
  384.       return;
  385.    }
  386.    
  387.    // Make sure we have a clean matrix state
  388.    // before we start rendering anything!
  389.    GFXTransformSaver saver;
  390.    //GFX->setWorldMatrix( MatrixF::Identity );
  391.    //GFX->setViewMatrix( MatrixF::Identity );
  392.    //GFX->setProjectionMatrix( MatrixF::Identity );
  393.  
  394.    bool tempAwake=false;
  395.    if (!isAwake())
  396.    {
  397.       tempAwake=true;
  398.       awaken();
  399.    }
  400.    
  401.    if (mTarget->getSize() != size)
  402.    {
  403.       _setupTargets();
  404.       mNamedTarget.setViewport( RectI( Point2I::Zero, size ) );
  405.    }
  406.    
  407.    RectI screenRect(0, 0, size.x, size.y);
  408.    
  409.    // Set active target
  410.    GFX->pushActiveRenderTarget();
  411.    GFX->setActiveRenderTarget(mTarget);
  412.  
  413.    // Clear the current viewport area
  414.    GFX->setViewport(screenRect);
  415.    GFX->clear(GFXClearTarget, LinearColorF(0, 0, 0, 0), 1.0f, 0);
  416.    
  417.    if ( mStateBlockData == NULL )
  418.    {
  419.       if( mStateBlockData != mOldStateBlockData )
  420.          mOldStateBlockData = NULL;
  421.       renderChildControls(Point2I(0,0),RectI(Point2I(0,0), getExtent()));
  422.    }
  423.    else
  424.    {
  425.       GFXStateBlockDesc prev_bitmapDefaultGuiDesc( GFX->getDrawUtil()->getBitmapStateBlockDesc() );
  426.       GFXStateBlockDesc prev_fontDefaultGuiDesc( GFX->getDrawUtil()->getFontStateBlockDesc() );
  427.       GFXStateBlockDesc prev_mDefaultGuiDesc( mDefaultGuiSB->getDesc() );
  428.      
  429.       GFXStateBlockDesc desc;
  430.       if( mStateBlockData != mOldStateBlockData )
  431.       {
  432.          mOldStateBlockData = mStateBlockData;
  433.          desc = mStateBlockData->getState();
  434.          mStateBlock = GFX->createStateBlock(desc);
  435.       }
  436.       else
  437.       {
  438.          desc = mStateBlock->getDesc();
  439.       }
  440.          
  441.       mDefaultGuiSB = mStateBlock;
  442.       GFX->setStateBlock( mStateBlock );
  443.      
  444.       GFX->getDrawUtil()->externalSetupBitmapStateBlock(desc);
  445.       GFX->getDrawUtil()->externalSetupFontStateBlock(desc);
  446.      
  447.       renderChildControls(Point2I(0,0),RectI(Point2I(0,0), getExtent()));
  448.      
  449.       mDefaultGuiSB = GFX->createStateBlock( prev_mDefaultGuiDesc );
  450.       GFX->setStateBlock(mDefaultGuiSB);  
  451.      
  452.       GFX->getDrawUtil()->externalSetupBitmapStateBlock(prev_bitmapDefaultGuiDesc);
  453.       GFX->getDrawUtil()->externalSetupFontStateBlock(prev_fontDefaultGuiDesc);
  454.    }
  455.    
  456.    mTarget->resolve();
  457.    GFX->popActiveRenderTarget();
  458.    
  459.    if(tempAwake)
  460.       sleep();
  461.    
  462.    //Test. It copies the named texture into a file
  463.    /*
  464.    GFXTexHandle theTex;
  465.    NamedTexTarget *namedTarget = NULL;
  466.    namedTarget = NamedTexTarget::find(mTargetName);
  467.    if ( namedTarget )
  468.    {
  469.       theTex = namedTarget->getTexture( 0 );
  470.    }
  471.    
  472.    if ( theTex.isValid() )
  473.    {
  474.       theTex->dumpToDisk("png", "./testXX.png");
  475.    }  
  476.    */
  477. }
  478.  
  479. DefineEngineMethod( GuiCopyContainer, copyContainer, void,(),,
  480.    "@brief Copy its contents into the named texture.")
  481. {
  482.    object->copyContainer();
  483. }
  484. [/code2]
  485.  
  486. guiBitmapShader.h
  487. [code2=BBCODE40_CPLUSPLUS]#ifndef _GUIBITMAPSHADER_H_
  488. #define _GUIBITMAPSHADER_H_
  489.  
  490. #ifndef _GUIBITMAPCTRL_H_
  491. #include "gui/controls/guiBitmapCtrl.h"
  492. #endif
  493.  
  494. class GFXStateBlockData;
  495. class ShaderData;
  496.  
  497. /// Renders a bitmap that can use a Shader.
  498. class GuiBitmapShader : public GuiBitmapCtrl
  499. {
  500. public:
  501.    typedef GuiBitmapCtrl Parent;
  502.  
  503.    enum { NumTextures = 8 };
  504.  
  505. protected:
  506.    Point2F mPosVertex[4]; //It needs to be float to avoid aproximation errors when rotating
  507.    Point2F mUVValues[4];
  508.    bool mAutoResizeBitmap;
  509.    Point2I mOldGuiExtent;
  510.  
  511.    FileName mTexFilename[NumTextures];
  512.    FileName mOldTexFilename[NumTextures];
  513.    GFXTexHandle mTextures[NumTextures];
  514.  
  515.    ShaderData *mShaderData;
  516.    ShaderData *mOldShaderData;
  517.    GFXStateBlockData *mStateBlockData;
  518.    GFXStateBlockData *mOldStateBlockData;
  519.    
  520.    GFXStateBlockRef mStateBlock;
  521.    GFXShaderRef mShader;
  522.    GFXShaderConstBufferRef mShaderConsts;
  523.    
  524.    //
  525.    GFXShaderConstHandle *mModelViewProjSC;
  526.    GFXShaderConstHandle *mGuiSizeSC;
  527.    GFXShaderConstHandle *mTexSizeSC[NumTextures];
  528.    GFXShaderConstHandle *mGuiOffsetSC;
  529.    GFXShaderConstHandle *mAccumTimeSC;
  530.    GFXShaderConstHandle *mDeltaTimeSC;
  531.    //
  532.  
  533.    void _setupStateBlock();
  534.    bool _setupShader();
  535.    void _setupConstants();
  536.    
  537.    bool _setupTexture( U8 slot );
  538.    void setTexture( U8 index, const String &texFilePath );
  539.    
  540.    void _doAutoResizeBitmap(Point2I extent, Point2I oldGuiExtent);
  541.    
  542.    //
  543.    U32 mShaderReloadKey;
  544.  
  545.    class EffectConst
  546.    {
  547.    public:
  548.  
  549.       EffectConst( const String &name, const String &val )
  550.          : mName( name ),
  551.            mHandle( NULL ),
  552.            mDirty( true )
  553.       {
  554.          set( val );
  555.       }
  556.  
  557.       void set( const String &newVal );
  558.  
  559.       void setToBuffer( GFXShaderConstBufferRef buff );
  560.  
  561.       String mName;
  562.  
  563.       GFXShaderConstHandle *mHandle;
  564.  
  565.       String mStringVal;
  566.  
  567.       bool mDirty;
  568.    };
  569.  
  570.    typedef HashTable<StringCase,EffectConst*> EffectConstTable;
  571.  
  572.    EffectConstTable mEffectConsts;
  573.    
  574. public:
  575.    DECLARE_CONOBJECT( GuiBitmapShader );
  576.    DECLARE_CATEGORY( "Gui Images" );
  577.    DECLARE_DESCRIPTION( "A control that displays an image like GuiBitmapCtrl.\n"
  578.                         "But the image can use a Shader.");
  579.  
  580.    GuiBitmapShader();
  581.    ~GuiBitmapShader();
  582.    
  583.    static void initPersistFields();
  584.    
  585.    DECLARE_CALLBACK( void, setShaderConsts, () );
  586.  
  587.    void onRender(Point2I offset, const RectI &updateRect);
  588.    
  589.    bool drawBitmapShader(Point2I offset);
  590.    void setShaderConst( const String &name, const String &val );
  591.    
  592.    Point2F getVCenter();
  593.    //vertex: 0,1,2 or 3
  594.    Point2F getVertexPosition(U8 vertex);
  595.    void setVertexPosition(Point2F vertexPos, U8 vertex);
  596.    void moveVertex(Point2I distance, U8 vertex );
  597.    void rotateVertex(F32 angleDeg, Point2I pivot, U8 vertex);
  598.    void scaleVertex(F32 scale, Point2I pivot, U8 vertex);
  599.    F32 getBitmapProportions();
  600.    Point4F getBitmapBounds();
  601.    void setOldGuiExtent(Point2I value) { mOldGuiExtent = value; }
  602. };
  603.  
  604. #endif //_GUIBITMAPTSHADER_H_
  605. [/code2]
  606.  
  607. guiBitmapShader.cpp
  608. [code2=BBCODE40_CPLUSPLUS]#include "platform/platform.h"
  609. #include "guiBitmapShader.h"
  610.  
  611. #include "console/console.h"
  612. #include "console/consoleTypes.h"
  613. #include "console/engineAPI.h"
  614. #include "gfx/gfxDevice.h"
  615. #include "gfx/gfxDrawUtil.h"
  616.  
  617. #include "materials/shaderData.h"
  618. #include "gfx/sim/gfxStateBlockData.h"
  619.  
  620. #include "materials/materialManager.h"
  621. #include "materials/matTextureTarget.h"
  622. #include "core/strings/stringUnit.h"
  623. #include "math/mMathFn.h"
  624. //#include "postFx/postEffectManager.h" //it was here for getBackBufferTex()
  625.  
  626. IMPLEMENT_CONOBJECT(GuiBitmapShader);
  627.  
  628. ConsoleDocClass( GuiBitmapShader,
  629.    "@brief A gui control that is used to display an image with shader.\n\n"
  630.    
  631.    "@ingroup GuiControls"
  632. );
  633.  
  634. IMPLEMENT_CALLBACK( GuiBitmapShader, setShaderConsts, void, (), (),
  635.    "Called immediate before processing this effect. This is the user's chance "
  636.    "to set the value of shader uniforms (constants).\n"
  637.    "@see setShaderConst"
  638. );
  639.  
  640. //---------------------------------
  641. void GuiBitmapShader::EffectConst::set( const String &newVal )
  642. {
  643.    if ( mStringVal == newVal )
  644.       return;
  645.  
  646.    mStringVal = newVal;
  647.    mDirty = true;
  648. }
  649.  
  650. void GuiBitmapShader::EffectConst::setToBuffer( GFXShaderConstBufferRef buff )
  651. {
  652.    // Nothing to do if the value hasn't changed.
  653.    if ( !mDirty )
  654.       return;
  655.    mDirty = false;
  656.  
  657.    // If we don't have a handle... get it now.
  658.    if ( !mHandle )
  659.       mHandle = buff->getShader()->getShaderConstHandle( mName );
  660.  
  661.    // If the handle isn't valid then we're done.
  662.    if ( !mHandle->isValid() )
  663.       return;
  664.  
  665.    const GFXShaderConstType type = mHandle->getType();
  666.  
  667.    // For now, we're only going
  668.    // to support float4 arrays.
  669.    // Expand to other types as necessary.
  670.    U32 arraySize = mHandle->getArraySize();
  671.  
  672.    const char *strVal = mStringVal.c_str();
  673.  
  674.    if ( type == GFXSCT_Int )
  675.    {
  676.       S32 val;
  677.       Con::setData( TypeS32, &val, 0, 1, &strVal );
  678.       buff->set( mHandle, val );
  679.    }
  680.    else if ( type == GFXSCT_Float )
  681.    {
  682.       F32 val;
  683.       Con::setData( TypeF32, &val, 0, 1, &strVal );
  684.       buff->set( mHandle, val );
  685.    }
  686.    else if ( type == GFXSCT_Float2 )
  687.    {
  688.       Point2F val;
  689.       Con::setData( TypePoint2F, &val, 0, 1, &strVal );
  690.       buff->set( mHandle, val );
  691.    }
  692.    else if ( type == GFXSCT_Float3 )
  693.    {
  694.       Point3F val;
  695.       Con::setData( TypePoint3F, &val, 0, 1, &strVal );
  696.       buff->set( mHandle, val );
  697.    }
  698.    else if ( type == GFXSCT_Float4 )
  699.    {
  700.       Point4F val;
  701.  
  702.       if ( arraySize > 1 )
  703.       {
  704.          // Do array setup!
  705.          //U32 unitCount = StringUnit::getUnitCount( strVal, "\t" );
  706.          //AssertFatal( unitCount == arraySize, "" );
  707.  
  708.          String tmpString;
  709.          Vector<Point4F> valArray;
  710.  
  711.          for ( U32 i = 0; i < arraySize; i++ )
  712.          {
  713.             tmpString = StringUnit::getUnit( strVal, i, "\t" );
  714.             valArray.increment();
  715.             const char *tmpCStr = tmpString.c_str();
  716.  
  717.             Con::setData( TypePoint4F, &valArray.last(), 0, 1, &tmpCStr );
  718.          }
  719.  
  720.          AlignedArray<Point4F> rectData( valArray.size(), sizeof( Point4F ), (U8*)valArray.address(), false );
  721.          buff->set( mHandle, rectData );
  722.       }
  723.       else
  724.       {
  725.          // Do regular setup.
  726.          Con::setData( TypePoint4F, &val, 0, 1, &strVal );
  727.          buff->set( mHandle, val );
  728.       }
  729.    }
  730.    else
  731.    {
  732. #if TORQUE_DEBUG
  733.       const char* err = avar("GuiBitmapShader::EffectConst::setToBuffer $s type is not implemented", mName.c_str());
  734.       Con::errorf(err);
  735.       GFXAssertFatal(0,err);
  736. #endif
  737.    }
  738. }
  739. //---------------------------------
  740.  
  741. GuiBitmapShader::GuiBitmapShader()
  742. {
  743.    mPosVertex[0] = Point2F(0.0,0.0);
  744.    mPosVertex[1] = Point2F(64.0,0.0);
  745.    mPosVertex[2] = Point2F(0.0,64.0);
  746.    mPosVertex[3] = Point2F(64.0,64.0);
  747.    mUVValues[0] = Point2F(0.0,0.0);
  748.    mUVValues[1] = Point2F(1.0,0.0);
  749.    mUVValues[2] = Point2F(0.0,1.0);
  750.    mUVValues[3] = Point2F(1.0,1.0);
  751.    mAutoResizeBitmap = false;
  752.    mOldGuiExtent = Point2I(0,0);
  753.    
  754.    mTexFilename[0] = "$inTex";
  755.    mOldTexFilename[0] = "$inTex";
  756.    mTexSizeSC[0] = NULL; //another SC
  757.    for( U8 i = 1; i < NumTextures; i++ )
  758.    {
  759.       mTexFilename[ i ] = String::EmptyString;
  760.       mOldTexFilename[ i ] = String::EmptyString;
  761.       mTexSizeSC[ i ] = NULL; //another SC
  762.    }
  763.  
  764.    mShaderData = NULL;
  765.    mShader = NULL;
  766.    mStateBlockData = NULL;
  767.    mStateBlock = NULL;
  768.    mShaderConsts = NULL;
  769.    
  770.    mOldShaderData = NULL;
  771.    mOldStateBlockData = NULL;
  772.    
  773.    //SC
  774.    mModelViewProjSC = NULL;
  775.    mGuiSizeSC = NULL;
  776.    mGuiOffsetSC = NULL;
  777.    mAccumTimeSC = NULL;
  778.    mDeltaTimeSC = NULL;
  779.    
  780.    mShaderReloadKey = 0;
  781. }
  782.  
  783. GuiBitmapShader::~GuiBitmapShader()
  784. {
  785.    for ( U8 i = 0; i < NumTextures; i++ )
  786.    {
  787.       mTextures[ i ].free();
  788.       mTextures[ i ] = NULL;
  789.    }
  790.    
  791.    EffectConstTable::Iterator iter = mEffectConsts.begin();
  792.    for ( ; iter != mEffectConsts.end(); iter++ )
  793.       delete iter->value;
  794. }
  795.  
  796. void GuiBitmapShader::initPersistFields()
  797. {
  798.    ///I add this here so in the editor the order keeps correct
  799.    addGroup( "Bitmap" );
  800.       ///Parent will add bitmap here
  801.    endGroup( "Bitmap" );
  802.    
  803.    addGroup( "Bitmap transform" );
  804.       addField( "pointTL", TypePoint2F, Offset( mPosVertex[0], GuiBitmapShader), "Location of the top left vertex." );
  805.       addField( "pointTR", TypePoint2F, Offset( mPosVertex[1], GuiBitmapShader), "Location of the top right vertex." );
  806.       addField( "pointBL", TypePoint2F, Offset( mPosVertex[2], GuiBitmapShader), "Location of the bottom left vertex." );
  807.       addField( "pointBR", TypePoint2F, Offset( mPosVertex[3], GuiBitmapShader), "Location of the bottom right vertex." );
  808.       addField( "uvTL", TypePoint2F, Offset( mUVValues[0], GuiBitmapShader), "UV values of the top left vertex." );
  809.       addField( "uvTR", TypePoint2F, Offset( mUVValues[1], GuiBitmapShader), "UV values of the top right vertex." );
  810.       addField( "uvBL", TypePoint2F, Offset( mUVValues[2], GuiBitmapShader), "UV values of the bottom left vertex." );
  811.       addField( "uvBR", TypePoint2F, Offset( mUVValues[3], GuiBitmapShader), "UV values of the bottom right vertex." );
  812.       addField( "autoResize", TypeBool, Offset( mAutoResizeBitmap, GuiBitmapShader ), "Bitmap resizes if the gui changes size." );
  813.    endGroup( "Bitmap transform" );
  814.    
  815.    addGroup( "Bitmap shader" );
  816.       addField( "shader", TYPEID<ShaderData>(), Offset( mShaderData, GuiBitmapShader ),
  817.          "Name of a ShaderData for this effect." );
  818.       addField( "stateBlock", TYPEID<GFXStateBlockData>(), Offset( mStateBlockData,  GuiBitmapShader ),
  819.          "Name of a GFXStateBlockData for this effect." );
  820.       addField( "texture", TypeImageFilename, Offset( mTexFilename, GuiBitmapShader ), NumTextures,
  821.          "Input textures to this shader ( samplers )." );
  822.    endGroup( "Bitmap shader" );
  823.  
  824.    Parent::initPersistFields();
  825.    removeField( "wrap" ); //It comes from Parent::initPersistFields(). Not used in this gui.
  826. }
  827.  
  828. void GuiBitmapShader::onRender(Point2I offset, const RectI &updateRect)
  829. {
  830.    Point2I extent = getExtent();
  831.    if (mAutoResizeBitmap)
  832.    {
  833.       if (mOldGuiExtent.x != extent.x || mOldGuiExtent.y != extent.y)
  834.       {
  835.          _doAutoResizeBitmap(extent, mOldGuiExtent);
  836.          mOldGuiExtent = extent;
  837.       }
  838.    }
  839.    
  840.    bool drawingSuccess = drawBitmapShader(offset);
  841.    if (mProfile->mBorder || !drawingSuccess )
  842.    {
  843.       RectI rect(offset.x, offset.y, extent.x, extent.y);
  844.       GFX->getDrawUtil()->drawRect(rect, mProfile->mBorderColor);
  845.    }
  846.    
  847.    renderChildControls(offset, updateRect);
  848. }
  849.  
  850. bool GuiBitmapShader::drawBitmapShader(Point2I offset)
  851. {
  852.    _setupStateBlock();
  853.    bool customShader = true;
  854.    customShader = _setupShader();
  855.    
  856.    //Shader textures
  857.    // Set the textures.
  858.    bool thereIsTexture = false;
  859.    for ( U8 i = 0; i < NumTextures; i++ )
  860.    {
  861.       thereIsTexture = _setupTexture( i );
  862.       if(i==0 && !customShader && !thereIsTexture)
  863.          return false;
  864.    }
  865.    
  866.    const F32 fillConv = GFX->getFillConventionOffset();
  867.    GFXVertexBufferHandle<GFXVertexPCT> verts(GFX, 4, GFXBufferTypeVolatile );
  868.    verts.lock();
  869.    Point2F vertexLocation;
  870.    for(U8 v = 0; v<4; v++)
  871.    {
  872.       vertexLocation = mPosVertex[v] + Point2F((F32)offset.x,(F32)offset.y);
  873.       verts[v].point.set(vertexLocation.x - fillConv, vertexLocation.y - fillConv, 0.f );
  874.       verts[v].texCoord.set( mUVValues[v].x, mUVValues[v].y );
  875.       verts[v].color = mColor;
  876.    }
  877.    verts.unlock();
  878.    
  879.    GFX->setVertexBuffer( verts );
  880.    GFX->drawPrimitive( GFXTriangleStrip, 0, 2 );
  881.    return true;
  882. }
  883.  
  884. bool GuiBitmapShader::_setupTexture( U8 stage )
  885. {
  886.    const String &texFilename = mTexFilename[ stage ];
  887.    
  888.    if(texFilename.compare( mOldTexFilename[ stage ], 0, String::NoCase ) != 0 )
  889.    {
  890.       setTexture(stage, texFilename);
  891.    }
  892.  
  893.    GFXTexHandle theTex;
  894.    NamedTexTarget *namedTarget = NULL;
  895.  
  896.    if ( texFilename.compare( "$inTex", 0, String::NoCase ) == 0 )
  897.    {
  898.       theTex = mTextureObject;
  899.    }
  900.    else if ( texFilename.compare( "$backBuffer", 0, String::NoCase ) == 0 )
  901.    {
  902.       //Here I wanted "theTex = PFXMGR->getBackBufferTex();" like PostFX but it throws some texture leak
  903.       //A copy of the base of the code of getBackBufferTex() seems to work
  904.       GFXTarget *target = GFX->getActiveRenderTarget();
  905.      
  906.       const Point2I &targetSize = target->getSize();
  907.       GFXFormat targetFormat = target->getFormat();
  908.  
  909.       theTex.set( targetSize.x, targetSize.y,
  910.                               targetFormat,
  911.                               &GFXRenderTargetProfile, "theTex" );
  912.  
  913.       target->resolveTo( theTex );
  914.    }
  915.    else if ( texFilename.isNotEmpty() && texFilename[0] == '#' )
  916.    {
  917.       namedTarget = NamedTexTarget::find( texFilename.c_str() + 1 );
  918.       if ( namedTarget )
  919.       {
  920.          theTex = namedTarget->getTexture( 0 );
  921.       }
  922.    }
  923.    else
  924.    {
  925.       theTex = mTextures[ stage ];
  926.    }
  927.  
  928.    if ( theTex.isValid() )
  929.    {
  930.       if (mShaderConsts)
  931.       {
  932.          if (mTexSizeSC[stage]->isValid())
  933.          {
  934.             Point2F texSizeConst;
  935.             texSizeConst.x = (F32)theTex->getWidth();
  936.             texSizeConst.y = (F32)theTex->getHeight();
  937.             mShaderConsts->set(mTexSizeSC[stage], texSizeConst);
  938.          }
  939.       }
  940.  
  941.       GFX->setTexture( stage, theTex );
  942.       return true;
  943.    }
  944.    return false; //theTex is not valid
  945. }
  946.  
  947. void GuiBitmapShader::setTexture( U8 index, const String &texFilePath )
  948. {
  949.     // Set the new texture name.
  950.     mTexFilename[index] = texFilePath;
  951.    mOldTexFilename[index] = texFilePath;
  952.    mTextures[index].free();
  953.    mTextures[index] = NULL;
  954.  
  955.    // Skip empty stages or ones with variable or target names.
  956.    if ( texFilePath.isEmpty() || texFilePath[0] == '$' || texFilePath[0] == '#' )
  957.       return;
  958.  
  959.    // Try to load the texture.
  960.    mTextures[index].set( texFilePath, &GFXTexturePersistentProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) );
  961. }
  962.  
  963. void GuiBitmapShader::_setupStateBlock()
  964. {
  965.    if ( mStateBlock.isNull() || mStateBlockData != mOldStateBlockData )
  966.    {
  967.       mOldStateBlockData = mStateBlockData;
  968.       GFXStateBlockDesc desc;
  969.       if ( mStateBlockData )
  970.          desc = mStateBlockData->getState();
  971.       else
  972.          desc = GFX->getDrawUtil()->getBitmapStateBlockDesc(); //default values from drawUtil
  973.      
  974.       mStateBlock = GFX->createStateBlock( desc );
  975.    }
  976.  
  977.    GFX->setStateBlock( mStateBlock );
  978. }
  979.  
  980. bool GuiBitmapShader::_setupShader()
  981. {
  982.    if ( mShaderData == NULL || mShaderData != mOldShaderData )
  983.    {
  984.       if(mOldShaderData)
  985.       {
  986.          //Maybe use this to clean mShaderConsts because the "allocConstBuffer"?
  987.          //delete[] mShaderConsts;
  988.          
  989.          mShaderConsts = NULL;
  990.          mShader = NULL;
  991.  
  992.          //kinda cheat.
  993.          mShaderReloadKey--;
  994.          EffectConstTable::Iterator iter = mEffectConsts.begin();
  995.          for ( ; iter != mEffectConsts.end(); iter++ )
  996.             delete iter->value;
  997.          mEffectConsts = EffectConstTable();
  998.       }
  999.      
  1000.       mOldShaderData = mShaderData;
  1001.      
  1002.       if( mShaderData )
  1003.       {
  1004.          if ( mShaderData->getPixVersion() <= GFX->getPixelShaderVersion() )
  1005.             mShader = mShaderData->getShader();
  1006.       }
  1007.    }
  1008.    
  1009.    if ( !mShader ) //mShaderData failed
  1010.    {
  1011.       GFX->setupGenericShaders( GFXDevice::GSModColorTexture );
  1012.       return false;
  1013.    }
  1014.    else
  1015.    {
  1016.       _setupConstants();
  1017.       GFX->setShader( mShader );
  1018.  
  1019.       GFX->setShaderConstBuffer( mShaderConsts );
  1020.       return true;
  1021.    }
  1022. }
  1023.  
  1024. void GuiBitmapShader::_setupConstants()
  1025. {
  1026.    // Alloc the const buffer.
  1027.    if ( mShaderConsts.isNull() )
  1028.    {
  1029.       mShaderConsts = mShader->allocConstBuffer();
  1030.       mModelViewProjSC = mShader->getShaderConstHandle( "$modelView" );
  1031.       mGuiSizeSC = mShader->getShaderConstHandle( "$guiSize" );
  1032.      
  1033.       mTexSizeSC[0] = mShader->getShaderConstHandle( "$texSize0" );
  1034.       mTexSizeSC[1] = mShader->getShaderConstHandle( "$texSize1" );
  1035.       mTexSizeSC[2] = mShader->getShaderConstHandle( "$texSize2" );
  1036.       mTexSizeSC[3] = mShader->getShaderConstHandle( "$texSize3" );
  1037.       mTexSizeSC[4] = mShader->getShaderConstHandle( "$texSize4" );
  1038.       mTexSizeSC[5] = mShader->getShaderConstHandle( "$texSize5" );
  1039.       mTexSizeSC[6] = mShader->getShaderConstHandle( "$texSize6" );
  1040.       mTexSizeSC[7] = mShader->getShaderConstHandle( "$texSize7" );
  1041.      
  1042.       mGuiOffsetSC = mShader->getShaderConstHandle( "$guiOffset" );
  1043.      
  1044.       mAccumTimeSC = mShader->getShaderConstHandle( "$accumTime" );
  1045.       mDeltaTimeSC = mShader->getShaderConstHandle( "$deltaTime" );
  1046.    }
  1047.    
  1048.    MatrixF xform(GFX->getProjectionMatrix());
  1049.    xform *= GFX->getViewMatrix();
  1050.    xform *= GFX->getWorldMatrix();
  1051.    mShaderConsts->setSafe( mModelViewProjSC, xform );
  1052.    
  1053.    if ( mGuiSizeSC->isValid() )
  1054.    {
  1055.       Point2F guiSize = Point2F((F32)getExtent().x,(F32)getExtent().y);
  1056.       mShaderConsts->set( mGuiSizeSC, guiSize );
  1057.    }
  1058.    
  1059.    if ( mGuiOffsetSC->isValid() )
  1060.    {
  1061.       Point2F guiOffset = Point2F((F32)getPosition().x,(F32)getPosition().y);
  1062.       mShaderConsts->set( mGuiOffsetSC, guiOffset );
  1063.    }
  1064.    
  1065.    mShaderConsts->setSafe( mAccumTimeSC, MATMGR->getTotalTime() );
  1066.    mShaderConsts->setSafe( mDeltaTimeSC, MATMGR->getDeltaTime() );
  1067.    
  1068.    ///
  1069.    // Set EffectConsts - specified from script
  1070.  
  1071.    // If our shader has reloaded since last frame we must mark all
  1072.    // EffectConsts dirty so they will be reset.
  1073.    if ( mShader->getReloadKey() != mShaderReloadKey )
  1074.    {
  1075.       mShaderReloadKey = mShader->getReloadKey();
  1076.  
  1077.       EffectConstTable::Iterator iter = mEffectConsts.begin();
  1078.       for ( ; iter != mEffectConsts.end(); iter++ )
  1079.       {
  1080.          iter->value->mDirty = true;
  1081.          iter->value->mHandle = NULL;
  1082.       }
  1083.    }
  1084.    
  1085.    setShaderConsts_callback();
  1086.    
  1087.    EffectConstTable::Iterator iter = mEffectConsts.begin();
  1088.    for ( ; iter != mEffectConsts.end(); iter++ )
  1089.       iter->value->setToBuffer( mShaderConsts );
  1090. }
  1091.  
  1092. void GuiBitmapShader::setShaderConst( const String &name, const String &val )
  1093. {
  1094.    PROFILE_SCOPE( GuiBitmapShader_SetShaderConst );
  1095.  
  1096.    EffectConstTable::Iterator iter = mEffectConsts.find( name );
  1097.    if ( iter == mEffectConsts.end() )
  1098.    {
  1099.       EffectConst *newConst = new EffectConst( name, val );
  1100.       iter = mEffectConsts.insertUnique( name, newConst );
  1101.    }
  1102.  
  1103.    iter->value->set( val );
  1104. }
  1105.  
  1106. DefineEngineMethod( GuiBitmapShader, setShaderConst, void, ( const char* name, const char* value ),,
  1107.    "Sets the value of a uniform defined in the shader. This will usually "
  1108.    "be called within the setShaderConsts callback. Array type constants are "
  1109.    "not supported.\n"    
  1110.    "@param name Name of the constanst, prefixed with '$'.\n"
  1111.    "@param value Value to set, space seperate values with more than one element.\n"
  1112.    "@tsexample\n"
  1113.    "function MyGBTS::setShaderConsts( %this )\n"
  1114.    "{\n"
  1115.    "   // example float4 uniform\n"
  1116.    "   %this.setShaderConst( \"$colorMod\", \"1.0 0.9 1.0 1.0\" );\n"
  1117.    "   // example float1 uniform\n"
  1118.    "   %this.setShaderConst( \"$strength\", \"3.0\" );\n"
  1119.    "   // example integer uniform\n"
  1120.    "   %this.setShaderConst( \"$loops\", \"5\" );"
  1121.    "}\n"
  1122.    "@endtsexample" )  
  1123. {
  1124.    object->setShaderConst( name, value );
  1125. }
  1126.  
  1127. //------------------------------------------------------------------------------
  1128. void GuiBitmapShader::_doAutoResizeBitmap(Point2I extent, Point2I oldGuiExtent)
  1129. {
  1130.    if(oldGuiExtent.x != 0)
  1131.    {
  1132.       F32 factor = (F32)extent.x / (F32)oldGuiExtent.x;
  1133.       mPosVertex[0].x *= factor;
  1134.       mPosVertex[1].x *= factor;
  1135.       mPosVertex[2].x *= factor;
  1136.       mPosVertex[3].x *= factor;
  1137.    }
  1138.    if(oldGuiExtent.y != 0)
  1139.    {
  1140.       F32 factor = (F32)extent.y / (F32)oldGuiExtent.y;
  1141.       mPosVertex[0].y *= factor;
  1142.       mPosVertex[1].y *= factor;
  1143.       mPosVertex[2].y *= factor;
  1144.       mPosVertex[3].y *= factor;
  1145.    }
  1146. }
  1147.  
  1148. Point2F GuiBitmapShader::getVCenter()
  1149. {
  1150.    return Point2F((mPosVertex[0].x+mPosVertex[1].x+mPosVertex[2].x+mPosVertex[3].x)/4.0,
  1151.       (mPosVertex[0].y+mPosVertex[1].y+mPosVertex[2].y+mPosVertex[3].y)/4.0);
  1152. }
  1153.  
  1154. Point2F GuiBitmapShader::getVertexPosition(U8 vertex)
  1155. {
  1156.    if (vertex > 3)
  1157.       return Point2F(-9999.9,-9999.9);
  1158.    
  1159.    return mPosVertex[vertex];
  1160. }
  1161.  
  1162. void GuiBitmapShader::setVertexPosition(Point2F vertexPos, U8 vertex)
  1163. {
  1164.    if (vertex > 3)
  1165.       return;
  1166.    
  1167.    mPosVertex[vertex] = vertexPos;
  1168. }
  1169.  
  1170. void GuiBitmapShader::moveVertex(Point2I distance, U8 vertex )
  1171. {
  1172.    if (vertex > 3)
  1173.       return;
  1174.    
  1175.    mPosVertex[vertex] += Point2F((F32)distance.x,(F32)distance.y);
  1176. }
  1177.  
  1178. void GuiBitmapShader::rotateVertex(F32 angleDeg, Point2I pivot, U8 vertex)
  1179. {
  1180.    if (vertex > 3)
  1181.       return;
  1182.    
  1183.    if(mPosVertex[vertex].x == (F32)pivot.x && mPosVertex[vertex].y == (F32)pivot.y)
  1184.       return;
  1185.    
  1186.    Point2F vector = Point2F(mPosVertex[vertex].x - (F32)pivot.x, mPosVertex[vertex].y - (F32)pivot.y);
  1187.    
  1188.    F32 dist = mSqrt((vector.x * vector.x) + (vector.y * vector.y));
  1189.    F32 angl = mAtan2( vector.x , vector.y );
  1190.    
  1191.    mPosVertex[vertex].x = (dist*mSin(angl - mDegToRad(angleDeg))) + pivot.x;
  1192.    mPosVertex[vertex].y = (dist*mCos(angl - mDegToRad(angleDeg))) + pivot.y;
  1193. }
  1194.  
  1195. void GuiBitmapShader::scaleVertex(F32 scale, Point2I pivot, U8 vertex)
  1196. {
  1197.    if (vertex > 3)
  1198.       return;
  1199.    
  1200.    if(mPosVertex[vertex].x == pivot.x && mPosVertex[vertex].y == pivot.y)
  1201.       return;
  1202.    
  1203.    Point2F vector = Point2F(mPosVertex[vertex].x - (F32)pivot.x, mPosVertex[vertex].y - (F32)pivot.y);
  1204.    
  1205.    vector.x *= scale;
  1206.    vector.y *= scale;
  1207.    
  1208.    mPosVertex[vertex] = Point2F(vector.x + (F32)pivot.x, vector.y + (F32)pivot.y);
  1209. }
  1210.  
  1211. F32 GuiBitmapShader::getBitmapProportions()
  1212. {
  1213.    if ( mTextureObject )
  1214.    {
  1215.       return (F32)mTextureObject->getWidth() / (F32)mTextureObject->getHeight();
  1216.    }
  1217.    else
  1218.       return 0.0;
  1219. }
  1220.  
  1221. Point4F GuiBitmapShader::getBitmapBounds()
  1222. {
  1223.    Point4F bounds = Point4F(mPosVertex[0].x,mPosVertex[0].y,mPosVertex[0].x,mPosVertex[0].y);
  1224.    for(U8 i = 1; i<4; i++)
  1225.    {
  1226.       if(mPosVertex[ i ].x<bounds.x)
  1227.          bounds.x = mPosVertex[ i ].x;
  1228.       if(mPosVertex[ i ].x>bounds.z)
  1229.          bounds.z = mPosVertex[ i ].x;
  1230.      
  1231.       if(mPosVertex[ i ].y<bounds.y)
  1232.          bounds.y = mPosVertex[ i ].y;
  1233.       if(mPosVertex[ i ].y>bounds.w)
  1234.          bounds.w = mPosVertex[ i ].y;
  1235.    }
  1236.    return bounds;
  1237. }
  1238.  
  1239. //-----------------------------------------------------------------------------
  1240.  
  1241. DefineEngineMethod(GuiBitmapShader, getVCenter, Point2F, (),,
  1242.    "@brief Get the center of mass of the bitmap.\n\n"
  1243.    "@return A point2F. You may want to convert it into a Point2I." )
  1244. {
  1245.    return object->getVCenter();
  1246. }
  1247.  
  1248. DefineEngineMethod(GuiBitmapShader, moveBitmap, void, (Point2I distance),,
  1249.    "@brief Move the bitmap.\n\n"
  1250.    "@param distance Pixels (x,y) to move.\n"
  1251.    "+x: right, -x: left, +y: down, -y: up")
  1252. {
  1253.    object->moveVertex(distance,0);
  1254.    object->moveVertex(distance,1);
  1255.    object->moveVertex(distance,2);
  1256.    object->moveVertex(distance,3);
  1257. }
  1258.  
  1259. DefineEngineMethod(GuiBitmapShader, rotateBitmap, void, (F32 angleDeg, Point2I pivot),,
  1260.    "@brief Rotate the bitmap.\n\n"
  1261.    "@param angleDeg Angle in degrees to rotate (positive clockwise).\n"
  1262.    "@param pivot Point around vertices rotate.\n\"0 0\" is top left of the gui.")
  1263. {
  1264.    object->rotateVertex(angleDeg,pivot,0);
  1265.    object->rotateVertex(angleDeg,pivot,1);
  1266.    object->rotateVertex(angleDeg,pivot,2);
  1267.    object->rotateVertex(angleDeg,pivot,3);
  1268. }
  1269.  
  1270. DefineEngineMethod(GuiBitmapShader, scaleBitmap, void, (F32 scale, Point2I pivot),,
  1271.    "@brief Resize the bitmap.\n\n"
  1272.    "@param scale Number to scale. 1.0 keeps size.\n"
  1273.    "@param pivot Point around vertices scale the distance.\n\"0 0\" is top left of the gui.")
  1274. {
  1275.    object->scaleVertex(scale,pivot,0);
  1276.    object->scaleVertex(scale,pivot,1);
  1277.    object->scaleVertex(scale,pivot,2);
  1278.    object->scaleVertex(scale,pivot,3);
  1279. }
  1280.  
  1281. DefineEngineMethod(GuiBitmapShader, fillExpand, void, (bool keepBitmapRatio),,
  1282.    "@brief Expand the bitmap to fill the gui.\n\n"
  1283.    "@param keepBitmapRatio If true the bitmap keeps proportions of $inTex.\n")
  1284. {
  1285.    F32 ratio = object->getBitmapProportions();
  1286.    
  1287.    //ratio == 0.0 means there is no bitmap asigned. Just set vertices to fill.
  1288.    if (!keepBitmapRatio || ratio == 0.0)
  1289.    {
  1290.       object->setVertexPosition(Point2F(0.0,0.0),0);
  1291.       object->setVertexPosition(Point2F(object->getExtent().x,0.0),1);
  1292.       object->setVertexPosition(Point2F(0.0, object->getExtent().y),2);
  1293.       object->setVertexPosition(Point2F(object->getExtent().x, object->getExtent().y),3);
  1294.    }
  1295.    else
  1296.    {
  1297.       //ratio = Width / Height
  1298.       F32 guiRatio = (F32)object->getExtent().x / (F32)object->getExtent().y;
  1299.      
  1300.       if(guiRatio < ratio)
  1301.       {
  1302.          F32 xExtent = (F32)object->getExtent().x;
  1303.          F32 yExtent = xExtent / ratio;
  1304.          F32 topPosition = 0.5 * (F32)object->getExtent().y - 0.5 * yExtent;
  1305.          F32 bottomPosition = topPosition + yExtent;
  1306.          
  1307.          object->setVertexPosition(Point2F(0.0,topPosition),0);
  1308.          object->setVertexPosition(Point2F(xExtent,topPosition),1);
  1309.          object->setVertexPosition(Point2F(0.0,bottomPosition),2);
  1310.          object->setVertexPosition(Point2F(xExtent,bottomPosition),3);
  1311.       }
  1312.       else
  1313.       {
  1314.          F32 yExtent = (F32)object->getExtent().y;
  1315.          F32 xExtent = yExtent * ratio;
  1316.          F32 leftPosition = 0.5 * (F32)object->getExtent().x - 0.5 * xExtent;
  1317.          F32 rightPosition = leftPosition + xExtent;
  1318.          
  1319.          object->setVertexPosition(Point2F(leftPosition,0.0),0);
  1320.          object->setVertexPosition(Point2F(rightPosition,0.0),1);
  1321.          object->setVertexPosition(Point2F(leftPosition,yExtent),2);
  1322.          object->setVertexPosition(Point2F(rightPosition,yExtent),3);
  1323.       }
  1324.    }
  1325. }
  1326.  
  1327. DefineEngineMethod(GuiBitmapShader, getBitmapBounds, Point4F, (),,
  1328.    "@brief Get the bounds of the bitmap.\n\n"
  1329.    "@return Point4F. First pair is top left and last is bottom right." )
  1330. {
  1331.    return object->getBitmapBounds();
  1332. }
  1333.  
  1334. DefineEngineMethod(GuiBitmapShader, doCenterBitmap, void, (),,
  1335.    "@brief Move the bitmap to be centered in the gui.\n")
  1336. {
  1337.    Point2F bitmapCenter = object->getVCenter();
  1338.    Point2F guiCenter = Point2F(object->getExtent().x/2.0,object->getExtent().x/2.0);
  1339.    
  1340.    Point2I distance = Point2I(mRound(guiCenter.x-bitmapCenter.x),mRound(guiCenter.y-bitmapCenter.y));
  1341.    
  1342.    object->moveVertex(distance,0);
  1343.    object->moveVertex(distance,1);
  1344.    object->moveVertex(distance,2);
  1345.    object->moveVertex(distance,3);
  1346. }
  1347.  
  1348. DefineEngineMethod(GuiBitmapShader, setBitmapBounds, void, (Point4F newBounds),,
  1349.    "@brief Set the bounds of the bitmap.\n\n"
  1350.    "@param newBounds First pair is top left and last is bottom right." )
  1351. {  
  1352.    Point4F oldBounds = object->getBitmapBounds();
  1353.    
  1354.    F32 horRatio = 0.0;
  1355.    F32 verRatio = 0.0;
  1356.    
  1357.    if (oldBounds.z-oldBounds.x != 0.0)
  1358.       horRatio = (newBounds.z-newBounds.x) / (oldBounds.z-oldBounds.x);
  1359.    if (oldBounds.w-oldBounds.y != 0.0)
  1360.       verRatio = (newBounds.w-newBounds.y) / (oldBounds.w-oldBounds.y);
  1361.    
  1362.    for(U8 i = 0; i<4; i++)
  1363.    {
  1364.       Point2F ver = object->getVertexPosition(i);
  1365.       object->setVertexPosition( Point2F((horRatio*(ver.x-oldBounds.x))+newBounds.x,(verRatio*(ver.y-oldBounds.y))+newBounds.y), i);
  1366.    }
  1367. }
  1368.    
  1369. DefineEngineMethod(GuiBitmapShader, autoResizeGui, void, (),,
  1370.    "@brief Move and resize the gui to show all the bitmap.\n")
  1371. {
  1372.    Point4F bounds = object->getBitmapBounds();
  1373.    Point2I newExtent = Point2I(mRound(bounds.z - bounds.x), mRound(bounds.w - bounds.y));
  1374.    if (newExtent.x != 0 && newExtent.y != 0)
  1375.    {
  1376.       object->setExtent(newExtent);
  1377.       object->setOldGuiExtent(newExtent);
  1378.  
  1379.       Point2I newPosition = object->getPosition() + Point2I(mRound(bounds.x), mRound(bounds.y));
  1380.       object->setPosition(newPosition.x,newPosition.y);
  1381.  
  1382.       Point2I distance = Point2I(-1 * mRound(bounds.x), -1 * mRound(bounds.y));
  1383.       object->moveVertex(distance, 0);
  1384.       object->moveVertex(distance, 1);
  1385.       object->moveVertex(distance, 2);
  1386.       object->moveVertex(distance, 3);
  1387.    }
  1388. }[/code2]
  1389.  
  1390. //------------------------------------------------------------------
  1391.  
  1392. -Usage-
  1393. GuiCopyContainer
  1394.  
  1395. In the gui editor you can find it in Lybrary/Containers/GuiCopyConatiner .(or make it in script).
  1396. Then just like a normal gui container add any gui inside you want.
  1397. 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):
  1398.  
  1399. [code2=BBCODE40_CPLUSPLUS]singleton GFXStateBlockData( BasicStateBlock )
  1400. {  
  1401.    blendDefined = true;
  1402.    blendEnable = true;
  1403.    blendSrc = GFXBlendSrcAlpha;
  1404.    blendDest = GFXBlendInvSrcAlpha;
  1405.    blendOp = GFXBlendOpAdd;
  1406.    
  1407.    separateAlphaBlendDefined = true;
  1408.    separateAlphaBlendEnable = true;
  1409.    separateAlphaBlendSrc = GFXBlendOne;
  1410.    separateAlphaBlendDest = GFXBlendOne;
  1411.    separateAlphaBlendOp = GFXBlendOpAdd;
  1412.    
  1413.    samplersDefined = true;
  1414.    samplerStates[0] = SamplerClampLinear;
  1415. };
  1416. [/code2]
  1417.  
  1418. 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.
  1419.  
  1420. Other variables are:
  1421. -textureName: this is the name of the texture you can use in places where named textures are used.
  1422. -targetFormat: the format of the texture. GFXFormatR8G8B8A8 is good enough.
  1423.  
  1424. -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.
  1425. -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.
  1426.  
  1427. The method is:
  1428. GuiCopyContainer::copyContainer()
  1429. It just does the copy of the container to a texture manually. It's used if autoCopy is disabled.
  1430.  
  1431. //------------------------------------------------------------------
  1432.  
  1433. -Usage-
  1434. GuiBitmapShader
  1435.  
  1436. In the gui editor you can find it in Lybrary/Images/GuiBitmapShader .(or make it in script).
  1437. Then just like a normal gui bitmap control it'll display an image related to how shader is defined and the textures detailed.
  1438.  
  1439. 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.
  1440.  
  1441. Its variables are:
  1442. -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).
  1443. 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).
  1444. 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.
  1445. -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.
  1446. -autoResize: if the gui changes size, the vertices are modified to resize the image bounds with the gui.
  1447.  
  1448. -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.
  1449. -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.
  1450.  
  1451. -texture[0-7]: The used textures.
  1452. Use "$inTex" to use the image from the bitmap variable.
  1453. 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 #.
  1454. Use "#texture" to use a named texture with name "texture" (note the # before the name).
  1455. Use the location of the image to use the texture like "data/splash.png".
  1456.  
  1457. Then in the hlsl or glsl shader you can use the textures like
  1458. TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0);
  1459. TORQUE_UNIFORM_SAMPLER2D(basealpha, 1);
  1460. TORQUE_UNIFORM_SAMPLER2D(diffuseMapB, 2);
  1461.  
  1462.  
  1463. Like PostEffect you can use the setShaderConsts callback to add constants to the shader with setShaderConst like:
  1464. [code2=BBCODE40_CPLUSPLUS]$colorVar1 = "0.0 1.0 0.0 1.0";
  1465. $colorVar2 = "1.0 0.0 0.0 1.0";
  1466. function GuiBitmapShaderName::setShaderConsts( %this )
  1467. {
  1468.    %this.setShaderConst( "$colorMod", $colorVar1 );
  1469.    %this.setShaderConst( "$colorModB", $colorVar2 );
  1470. }
  1471. [/code2]
  1472.  
  1473. The gui has some engine uniforms defined you can use:
  1474. modelView : transform for vertex drawing
  1475. guiSize : float2 with the pixel extent of the gui
  1476. texSize0-7 : float2 with the size of the texture[0-7]
  1477. guiOffset : the getPosition() of the gui (so it can be located in combination with guiSize in screen space)
  1478. accumTime : the getTime() milliseconds variable.
  1479. deltaTime : the same as accumTime but only for the current frame
  1480.  
  1481. Other methods:
  1482. -Point2F XXX.getVCenter(): returns the center of the four vertices. Use it to rotate better the image, specially if the gui changes size.
  1483. -XXX.moveBitmap(Point2I distance): moves the four vertices around that distance.
  1484. -XXX.rotateBitmap(F32 angle, Point2I pivot): rotate the four vertices around the pivot.
  1485. -XXX.scaleBitmap(F32 scale, Point2I pivot): scale uniformly the four vertices around the pivot.
  1486. -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.
  1487. -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.
  1488. -XXX.doCenterBitmap(): moves the vertices so its center is the center of the gui.
  1489. -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.
  1490. -XXX.autoResizeGui(): scales and moves the gui itself so it'll display all the vertices (the vertices coordinates will change, then).
  1491.  
  1492. [img]https://i.imgur.com/0FdS9Me.png[/img]
  1493. 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.
  1494. 2) GuiCopyContainer with a bitmap control and a text.
  1495. 3) bitmap control using setNamedTexture to display 2)
  1496.  
  1497. Example of shader (note: only using directx). You may need to change the location link of the files:
  1498. cs file:
  1499. [code2=BBCODE40_CPLUSPLUS]
  1500. singleton ShaderData( TestSOne )
  1501. {
  1502.    DXVertexShaderFile = "./fileV.hlsl";
  1503.    DXPixelShaderFile  = "./fileP.hlsl";
  1504.    
  1505.    pixVersion = 1.0;
  1506. };
  1507.  
  1508. singleton GFXStateBlockData( BaseStateBlock )
  1509. {  
  1510.    blendDefined = true;
  1511.    blendEnable = true;
  1512.    blendSrc = GFXBlendSrcAlpha;
  1513.    blendDest = GFXBlendInvSrcAlpha;
  1514.    blendOp = GFXBlendOpAdd;
  1515.    
  1516.    separateAlphaBlendDefined = true;
  1517.    separateAlphaBlendEnable = true;
  1518.    separateAlphaBlendSrc = GFXBlendOne;
  1519.    separateAlphaBlendDest = GFXBlendOne;
  1520.    separateAlphaBlendOp = GFXBlendOpAdd;
  1521.    
  1522.    samplersDefined = true;
  1523.    samplerStates[0] = SamplerClampLinear;
  1524. };
  1525.  
  1526. $colorVar1 = "0.0 1.0 0.0 1.0";
  1527. $colorVar2 = "1.0 0.0 0.0 1.0";
  1528. function TestA::setShaderConsts( %this )
  1529. {
  1530.    %this.setShaderConst( "$colorMod", $colorVar1 );
  1531.    %this.setShaderConst( "$colorModB", $colorVar2 );
  1532. }
  1533.  
  1534. //type est in the consolo to add the gui in the mainmenu
  1535. function est()
  1536. {
  1537.    MainMenuGui.add(TestA);
  1538.    TestA.fillExpand(false);
  1539. }
  1540.  
  1541. if(!isObject(TestA))
  1542. {
  1543.    
  1544.    new GuiBitmapShader(TestA)
  1545.    {
  1546.       shader = "TestSOne";
  1547.       stateBlock = BaseStateBlock;
  1548.       texture[0] = "data/splash.png";
  1549.       texture[1] = "$inTex";
  1550.       texture[2] = "#copiedcontainer";
  1551.       //uvTL = "0.01 0.01";
  1552.       //uvTR = "0.99 0.01";
  1553.       //uvBL = "0.01 0.99";
  1554.       //uvBR = "0.99 0.99";
  1555.       bitmap = "data/customdata/scripts/base_img.png";
  1556.       color = "255 255 255 255";
  1557.       position = "50 50";
  1558.       extent = "384 384";
  1559.       minExtent = "8 2";
  1560.       horizSizing = "right";
  1561.       vertSizing = "bottom";
  1562.       profile = "GuiDefaultProfile";
  1563.       visible = "1";
  1564.       active = "1";
  1565.       tooltipProfile = "GuiToolTipProfile";
  1566.       hovertime = "1000";
  1567.       isContainer = "0";
  1568.       canSave = "1";
  1569.       canSaveDynamicFields = "0";
  1570.    };
  1571. }
  1572. [/code2]
  1573.  
  1574. fileV.hlsl:
  1575. [code2=BBCODE40_CPLUSPLUS]#include "../../../core/rendering/shaders/shaderModel.hlsl"
  1576.  
  1577. struct Appdata
  1578. {
  1579.     float3 position   : POSITION;
  1580.     float4 color      : COLOR;
  1581.     float2 texCoord   : TEXCOORD0;
  1582. };
  1583.  
  1584. struct Conn
  1585. {
  1586.    float4 HPOS             : TORQUE_POSITION;
  1587.    float4 color            : COLOR;
  1588.    float2 texCoord         : TEXCOORD0;
  1589. };
  1590.  
  1591. uniform float4x4 modelview;
  1592.  
  1593. Conn main( Appdata In )
  1594. {
  1595.    Conn Out;
  1596.    Out.HPOS = mul(modelview, float4(In.position,1.0));
  1597.    Out.color = In.color;
  1598.    Out.texCoord = In.texCoord;
  1599.    return Out;
  1600. }[/code2]
  1601.  
  1602. fileP.hlsl:
  1603. [code2=BBCODE40_CPLUSPLUS]#include "../../../core/rendering/shaders/shaderModel.hlsl"
  1604.  
  1605. struct datainput {
  1606.    float4 HPOS             : TORQUE_POSITION;
  1607.    float4 color            : COLOR;
  1608.    float2 texCoord         : TEXCOORD0;
  1609. };
  1610.  
  1611. TORQUE_UNIFORM_SAMPLER2D(diffuseMap, 0);
  1612. TORQUE_UNIFORM_SAMPLER2D(basealpha, 1);
  1613. TORQUE_UNIFORM_SAMPLER2D(diffuseMapB, 2);
  1614.  
  1615. uniform float2 guiSize;
  1616. uniform float accumTime;
  1617. uniform float4 colorMod;
  1618. uniform float4 colorModB;
  1619.  
  1620. float4 main( datainput IN ) : SV_Target0
  1621. {
  1622.    IN.texCoord += 0.5*cos(accumTime);
  1623.    float4 col = TORQUE_TEX2D(diffuseMap, IN.texCoord);
  1624.    col = colorModB;
  1625.    if(IN.texCoord.x*guiSize.x > 20 && IN.texCoord.y*guiSize.y > 20)
  1626.       col = TORQUE_TEX2D(diffuseMapB, IN.texCoord);
  1627.      
  1628.    float4 alphy = TORQUE_TEX2D(basealpha, IN.texCoord);
  1629.    
  1630.    if(alphy.a > -1)
  1631.       col = float4(col.xyz,alphy.a);
  1632.    else
  1633.       col = colorMod;
  1634.  
  1635.    //here you can modify colourexit
  1636.  
  1637.    return col;
  1638. }
  1639.  
  1640. [/code2]
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top