Advertisement
Guest User

decalManager.cpp

a guest
Jul 27th, 2016
220
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 57.80 KB | None | 0 0
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22.  
  23. #include "platform/platform.h"
  24. #include "T3D/decal/decalManager.h"
  25.  
  26. #include "scene/sceneManager.h"
  27. #include "scene/sceneRenderState.h"
  28. #include "ts/tsShapeInstance.h"
  29. #include "console/console.h"
  30. #include "console/dynamicTypes.h"
  31. #include "gfx/primBuilder.h"
  32. #include "console/consoleTypes.h"
  33. #include "platform/profiler.h"
  34. #include "gfx/gfxTransformSaver.h"
  35. #include "lighting/lightManager.h"
  36. #include "lighting/lightInfo.h"
  37. #include "gfx/gfxDrawUtil.h"
  38. #include "gfx/sim/gfxStateBlockData.h"
  39. #include "materials/shaderData.h"
  40. #include "materials/matInstance.h"
  41. #include "renderInstance/renderPassManager.h"
  42. #include "core/resourceManager.h"
  43. #include "core/stream/fileStream.h"
  44. #include "gfx/gfxDebugEvent.h"
  45. #include "math/util/quadTransforms.h"
  46. #include "math/mathUtils.h"
  47. #include "core/volume.h"
  48. #include "core/module.h"
  49. #include "T3D/decal/decalData.h"
  50. #include "console/engineAPI.h"
  51.  
  52.  
  53. extern bool gEditingMission;
  54.  
  55.  
  56. MODULE_BEGIN( DecalManager )
  57.  
  58.    MODULE_INIT_AFTER( Scene )
  59.    MODULE_SHUTDOWN_BEFORE( Scene )
  60.    
  61.    MODULE_INIT
  62.    {
  63.       gDecalManager = new DecalManager;
  64.       gClientSceneGraph->addObjectToScene( gDecalManager );
  65.    }
  66.    
  67.    MODULE_SHUTDOWN
  68.    {
  69.       gClientSceneGraph->removeObjectFromScene( gDecalManager );
  70.       SAFE_DELETE( gDecalManager );
  71.    }
  72.  
  73. MODULE_END;
  74.  
  75.  
  76. /// A bias applied to the nearPlane for Decal and DecalRoad rendering.
  77. /// Is set by by LevelInfo.
  78. F32 gDecalBias = 0.0015f;
  79.  
  80. bool      DecalManager::smDecalsOn = true;
  81. bool      DecalManager::smDebugRender = false;
  82. F32       DecalManager::smDecalLifeTimeScale = 1.0f;
  83. bool      DecalManager::smPoolBuffers = true;
  84. const U32 DecalManager::smMaxVerts = 6000;
  85. const U32 DecalManager::smMaxIndices = 10000;
  86.  
  87. DecalManager *gDecalManager = NULL;
  88.  
  89. IMPLEMENT_CONOBJECT(DecalManager);
  90.  
  91. ConsoleDoc(
  92.    "@defgroup Decals\n"
  93.    "@brief Decals are non-SimObject derived objects that are stored and loaded "
  94.    "separately from the normal mission file.\n\n"
  95.  
  96.    "The DecalManager handles all aspects of decal management including loading, "
  97.    "creation, saving, and automatically deleting decals that have exceeded their "
  98.    "lifeSpan.\n\n"
  99.  
  100.    "The static decals associated with a mission are normally loaded immediately "
  101.    "after the mission itself has loaded as shown below.\n"
  102.  
  103.    "@tsexample\n"
  104.    "// Load the static mission decals.\n"
  105.    "decalManagerLoad( %missionName @ \".decals\" );\n"
  106.    "@endtsexample\n"
  107.  
  108.    "@ingroup FX\n"
  109. );
  110.  
  111. ConsoleDocClass( DecalManager,
  112.    "@brief The object that manages all of the decals in the active mission.\n\n"
  113.    "@see Decals\n"
  114.    "@ingroup Decals\n"
  115.    "@ingroup FX\n"
  116. );
  117.  
  118. namespace {
  119.  
  120. S32 QSORT_CALLBACK cmpDecalInstance(const void* p1, const void* p2)
  121. {
  122.    const DecalInstance** pd1 = (const DecalInstance**)p1;
  123.    const DecalInstance** pd2 = (const DecalInstance**)p2;
  124.  
  125.    return int(((char *)(*pd1)->mDataBlock) - ((char *)(*pd2)->mDataBlock));
  126. }
  127.  
  128. S32 QSORT_CALLBACK cmpPointsXY( const void *p1, const void *p2 )
  129. {
  130.    const Point3F *pnt1 = (const Point3F*)p1;
  131.    const Point3F *pnt2 = (const Point3F*)p2;
  132.  
  133.    if ( pnt1->x < pnt2->x )
  134.       return -1;
  135.    else if ( pnt1->x > pnt2->x )
  136.       return 1;
  137.    else if ( pnt1->y < pnt2->y )
  138.       return -1;
  139.    else if ( pnt1->y > pnt2->y )
  140.       return 1;
  141.    else
  142.       return 0;
  143. }
  144.  
  145. S32 QSORT_CALLBACK cmpQuadPointTheta( const void *p1, const void *p2 )
  146. {
  147.    const Point4F *pnt1 = (const Point4F*)p1;
  148.    const Point4F *pnt2 = (const Point4F*)p2;
  149.  
  150.    if ( mFabs( pnt1->w ) > mFabs( pnt2->w ) )
  151.       return 1;
  152.    else if ( mFabs( pnt1->w ) < mFabs( pnt2->w ) )
  153.       return -1;
  154.    else
  155.       return 0;
  156. }
  157.  
  158. static Point3F gSortPoint;
  159.  
  160. S32 QSORT_CALLBACK cmpDecalDistance( const void *p1, const void *p2 )
  161. {
  162.    const DecalInstance** pd1 = (const DecalInstance**)p1;
  163.    const DecalInstance** pd2 = (const DecalInstance**)p2;
  164.  
  165.    F32 dist1 = ( (*pd1)->mPosition - gSortPoint ).lenSquared();
  166.    F32 dist2 = ( (*pd2)->mPosition - gSortPoint ).lenSquared();
  167.  
  168.    return mSign( dist1 - dist2 );
  169. }
  170.  
  171. S32 QSORT_CALLBACK cmpDecalRenderOrder( const void *p1, const void *p2 )
  172. {  
  173.    const DecalInstance** pd1 = (const DecalInstance**)p1;
  174.    const DecalInstance** pd2 = (const DecalInstance**)p2;
  175.  
  176.    if ( ( (*pd2)->mFlags & SaveDecal ) && !( (*pd1)->mFlags & SaveDecal ) )
  177.       return -1;
  178.    else if ( !( (*pd2)->mFlags & SaveDecal ) && ( (*pd1)->mFlags & SaveDecal ) )
  179.       return 1;
  180.    else
  181.    {
  182.       S32 priority = (*pd1)->getRenderPriority() - (*pd2)->getRenderPriority();
  183.  
  184.       if ( priority != 0 )
  185.          return priority;
  186.  
  187.       if ( (*pd2)->mFlags & SaveDecal )
  188.       {
  189.          S32 id = ( (*pd1)->mDataBlock->getMaterial()->getId() - (*pd2)->mDataBlock->getMaterial()->getId() );      
  190.          if ( id != 0 )
  191.             return id;
  192.  
  193.          return (*pd1)->mCreateTime - (*pd2)->mCreateTime;
  194.       }
  195.       else
  196.          return (*pd1)->mCreateTime - (*pd2)->mCreateTime;
  197.    }
  198. }
  199.  
  200. } // namespace {}
  201.  
  202. // These numbers should be tweaked to get as many dynamically placed decals
  203. // as possible to allocate buffer arrays with the FreeListChunker.
  204. enum
  205. {
  206.    SIZE_CLASS_0 = 256,
  207.    SIZE_CLASS_1 = 512,
  208.    SIZE_CLASS_2 = 1024,
  209.    
  210.    NUM_SIZE_CLASSES = 3
  211. };
  212.  
  213. //-------------------------------------------------------------------------
  214. // DecalManager
  215. //-------------------------------------------------------------------------
  216. DecalManager::DecalManager()
  217. {
  218.    #ifdef DECALMANAGER_DEBUG
  219.    VECTOR_SET_ASSOCIATION( mDebugPlanes );
  220.    #endif
  221.  
  222.    setGlobalBounds();
  223.  
  224.    mDataFileName = NULL;
  225.  
  226.    mTypeMask |= EnvironmentObjectType;
  227.  
  228.    mDirty = false;
  229.  
  230.    mChunkers[0] = new FreeListChunkerUntyped( SIZE_CLASS_0 * sizeof( U8 ) );
  231.    mChunkers[1] = new FreeListChunkerUntyped( SIZE_CLASS_1 * sizeof( U8 ) );
  232.    mChunkers[2] = new FreeListChunkerUntyped( SIZE_CLASS_2 * sizeof( U8 ) );
  233.  
  234.    GFXDevice::getDeviceEventSignal().notify(this, &DecalManager::_handleGFXEvent);
  235. }
  236.  
  237. DecalManager::~DecalManager()
  238. {
  239.    GFXDevice::getDeviceEventSignal().remove(this, &DecalManager::_handleGFXEvent);
  240.  
  241.    clearData();
  242.  
  243.    for( U32 i = 0; i < NUM_SIZE_CLASSES; ++ i )
  244.       delete mChunkers[ i ];
  245. }
  246.  
  247. void DecalManager::consoleInit()
  248. {
  249.    Con::addVariable( "$pref::Decals::enabled", TypeBool, &smDecalsOn,
  250.       "Controls whether decals are rendered.\n"
  251.       "@ingroup Decals" );
  252.  
  253.    Con::addVariable( "$pref::Decals::lifeTimeScale", TypeF32, &smDecalLifeTimeScale,
  254.       "@brief Lifetime that decals will last after being created in the world.\n"
  255.       "Deprecated. Use DecalData::lifeSpan instead.\n"
  256.       "@ingroup Decals" );
  257.  
  258.    Con::addVariable( "$Decals::poolBuffers", TypeBool, &smPoolBuffers,
  259.       "If true, will merge all PrimitiveBuffers and VertexBuffers into a pair "
  260.       "of pools before clearing them at the end of a frame.\n"
  261.       "If false, will just clear them at the end of a frame.\n"
  262.       "@ingroup Decals" );
  263.  
  264.    Con::addVariable( "$Decals::debugRender", TypeBool, &smDebugRender,
  265.       "If true, the decal spheres will be visualized when in the editor.\n\n"
  266.       "@ingroup Decals" );
  267.  
  268.    Con::addVariable( "$Decals::sphereDistanceTolerance", TypeF32, &DecalSphere::smDistanceTolerance,
  269.       "The distance at which the decal system will start breaking up decal "
  270.       "spheres when adding new decals.\n\n"
  271.       "@ingroup Decals" );
  272.  
  273.    Con::addVariable( "$Decals::sphereRadiusTolerance", TypeF32, &DecalSphere::smRadiusTolerance,
  274.       "The radius beyond which the decal system will start breaking up decal "
  275.       "spheres when adding new decals.\n\n"
  276.       "@ingroup Decals" );
  277. }
  278.  
  279. bool DecalManager::_handleGFXEvent(GFXDevice::GFXDeviceEventType event)
  280. {
  281.    switch(event)
  282.    {
  283.    case GFXDevice::deEndOfFrame:
  284.  
  285.       // Return PrimitiveBuffers and VertexBuffers used this frame to the pool.
  286.  
  287.       if ( smPoolBuffers )
  288.       {
  289.          mPBPool.merge( mPBs );
  290.          mPBs.clear();
  291.  
  292.          mVBPool.merge( mVBs );
  293.          mVBs.clear();
  294.       }
  295.       else
  296.       {
  297.          _freePools();
  298.       }
  299.  
  300.       break;
  301.      
  302.    default: ;
  303.    }
  304.  
  305.    return true;
  306. }
  307.  
  308. bool DecalManager::clipDecal( DecalInstance *decal, Vector<Point3F> *edgeVerts, const Point2F *clipDepth )
  309. {
  310.    PROFILE_SCOPE( DecalManager_clipDecal );
  311.  
  312.    // Free old verts and indices.
  313.    _freeBuffers( decal );
  314.  
  315.    F32 halfSize = decal->mSize * 0.5f;
  316.    
  317.    // Ugly hack for ProjectedShadow!
  318.    F32 halfSizeZ = clipDepth ? clipDepth->x : halfSize;
  319.    F32 negHalfSize = clipDepth ? clipDepth->y : halfSize;
  320.    Point3F decalHalfSize( halfSize, halfSize, halfSize );
  321.    Point3F decalHalfSizeZ( halfSizeZ, halfSizeZ, halfSizeZ );
  322.  
  323.    MatrixF projMat( true );
  324.    decal->getWorldMatrix( &projMat );
  325.  
  326.    const VectorF &crossVec = decal->mNormal;
  327.    const Point3F &decalPos = decal->mPosition;
  328.  
  329.    VectorF newFwd, newRight;
  330.    projMat.getColumn( 0, &newRight );
  331.    projMat.getColumn( 1, &newFwd );  
  332.  
  333.    VectorF objRight( 1.0f, 0, 0 );
  334.    VectorF objFwd( 0, 1.0f, 0 );
  335.    VectorF objUp( 0, 0, 1.0f );
  336.  
  337.    // See above re: decalHalfSizeZ hack.
  338.    mClipper.clear();
  339.    mClipper.mPlaneList.setSize(6);
  340.    mClipper.mPlaneList[0].set( ( decalPos + ( -newRight * halfSize ) ), -newRight );
  341.    mClipper.mPlaneList[1].set( ( decalPos + ( -newFwd * halfSize ) ), -newFwd );
  342.    mClipper.mPlaneList[2].set( ( decalPos + ( -crossVec * decalHalfSizeZ ) ), -crossVec );
  343.    mClipper.mPlaneList[3].set( ( decalPos + ( newRight * halfSize ) ), newRight );
  344.    mClipper.mPlaneList[4].set( ( decalPos + ( newFwd * halfSize ) ), newFwd );
  345.    mClipper.mPlaneList[5].set( ( decalPos + ( crossVec * negHalfSize ) ), crossVec );
  346.  
  347.    mClipper.mNormal = decal->mNormal;
  348.  
  349.    const DecalData *decalData = decal->mDataBlock;
  350.  
  351.    mClipper.mNormalTolCosineRadians = mCos( mDegToRad( decalData->clippingAngle ) );
  352.  
  353.    Box3F box( -decalHalfSizeZ, decalHalfSizeZ );
  354.  
  355.    projMat.mul( box );
  356.  
  357.    PROFILE_START( DecalManager_clipDecal_buildPolyList );
  358.    getContainer()->buildPolyList( PLC_Decal, box, decalData->clippingMasks, &mClipper );  
  359.    PROFILE_END();
  360.  
  361.    mClipper.cullUnusedVerts();
  362.    mClipper.triangulate();
  363.    
  364.    const U32 numVerts = mClipper.mVertexList.size();
  365.    const U32 numIndices = mClipper.mIndexList.size();
  366.  
  367.    if ( !numVerts || !numIndices )
  368.       return false;
  369.  
  370.    // Fail if either of the buffer metrics exceeds our limits
  371.    // on dynamic geometry buffers.
  372.    if ( numVerts > smMaxVerts ||
  373.         numIndices > smMaxIndices )
  374.       return false;
  375.  
  376.    if ( !decalData->skipVertexNormals )
  377.       mClipper.generateNormals();
  378.    
  379. #ifdef DECALMANAGER_DEBUG
  380.    mDebugPlanes.clear();
  381.    mDebugPlanes.merge( mClipper.mPlaneList );
  382. #endif
  383.  
  384.    decal->mVertCount = numVerts;
  385.    decal->mIndxCount = numIndices;
  386.    
  387.    Vector<Point3F> tmpPoints;
  388.  
  389.    tmpPoints.push_back(( objFwd * decalHalfSize ) + ( objRight * decalHalfSize ));
  390.    tmpPoints.push_back(( objFwd * decalHalfSize ) + ( -objRight * decalHalfSize ));
  391.    tmpPoints.push_back(( -objFwd * decalHalfSize ) + ( -objRight * decalHalfSize ));
  392.    
  393.    Point3F lowerLeft(( -objFwd * decalHalfSize ) + ( objRight * decalHalfSize ));
  394.  
  395.    projMat.inverse();
  396.  
  397.    _generateWindingOrder( lowerLeft, &tmpPoints );
  398.  
  399.    BiQuadToSqr quadToSquare( Point2F( lowerLeft.x, lowerLeft.y ),
  400.                              Point2F( tmpPoints[0].x, tmpPoints[0].y ),
  401.                              Point2F( tmpPoints[1].x, tmpPoints[1].y ),
  402.                              Point2F( tmpPoints[2].x, tmpPoints[2].y ) );  
  403.  
  404.    Point2F uv( 0, 0 );
  405.    Point3F vecX(0.0f, 0.0f, 0.0f);
  406.  
  407.    // Allocate memory for vert and index arrays
  408.    _allocBuffers( decal );  
  409.  
  410.    // Mark this so that the color will be assigned on these verts the next
  411.    // time it renders, since we just threw away the previous verts.
  412.    decal->mLastAlpha = -1;
  413.    
  414.    Point3F vertPoint( 0, 0, 0 );
  415.  
  416.    for ( U32 i = 0; i < mClipper.mVertexList.size(); i++ )
  417.    {
  418.       const ClippedPolyList::Vertex &vert = mClipper.mVertexList[i];
  419.       vertPoint = vert.point;
  420.  
  421.       // Transform this point to
  422.       // object space to look up the
  423.       // UV coordinate for this vertex.
  424.       projMat.mulP( vertPoint );
  425.  
  426.       // Clamp the point to be within the quad.
  427.       vertPoint.x = mClampF( vertPoint.x, -decalHalfSize.x, decalHalfSize.x );
  428.       vertPoint.y = mClampF( vertPoint.y, -decalHalfSize.y, decalHalfSize.y );
  429.  
  430.       // Get our UV.
  431.       uv = quadToSquare.transform( Point2F( vertPoint.x, vertPoint.y ) );
  432.  
  433.       const RectF &rect = decal->mDataBlock->texRect[decal->mTextureRectIdx];
  434.  
  435.       uv *= rect.extent;
  436.       uv += rect.point;      
  437.  
  438.       // Set the world space vertex position.
  439.       decal->mVerts[i].point = vert.point;
  440.      
  441.       decal->mVerts[i].texCoord.set( uv.x, uv.y );
  442.      
  443.       if ( mClipper.mNormalList.empty() )
  444.          continue;
  445.  
  446.       decal->mVerts[i].normal = mClipper.mNormalList[i];
  447.       decal->mVerts[i].normal.normalize();
  448.  
  449.       if( mFabs( decal->mVerts[i].normal.z ) > 0.8f )
  450.          mCross( decal->mVerts[i].normal, Point3F( 1.0f, 0.0f, 0.0f ), &vecX );
  451.       else if ( mFabs( decal->mVerts[i].normal.x ) > 0.8f )
  452.          mCross( decal->mVerts[i].normal, Point3F( 0.0f, 1.0f, 0.0f ), &vecX );
  453.       else if ( mFabs( decal->mVerts[i].normal.y ) > 0.8f )
  454.          mCross( decal->mVerts[i].normal, Point3F( 0.0f, 0.0f, 1.0f ), &vecX );
  455.    
  456.       decal->mVerts[i].tangent = mCross( decal->mVerts[i].normal, vecX );
  457.    }
  458.  
  459.    U32 curIdx = 0;
  460.    for ( U32 j = 0; j < mClipper.mPolyList.size(); j++ )
  461.    {
  462.       // Write indices for each Poly
  463.       ClippedPolyList::Poly *poly = &mClipper.mPolyList[j];                  
  464.  
  465.       AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" );
  466.  
  467.       decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart];        
  468.       curIdx++;
  469.       decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart + 1];            
  470.       curIdx++;
  471.       decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart + 2];                
  472.       curIdx++;
  473.    }
  474.  
  475.    if ( !edgeVerts )
  476.       return true;
  477.  
  478.    Point3F tmpHullPt( 0, 0, 0 );
  479.    Vector<Point3F> tmpHullPts;
  480.  
  481.    for ( U32 i = 0; i < mClipper.mVertexList.size(); i++ )
  482.    {
  483.       const ClippedPolyList::Vertex &vert = mClipper.mVertexList[i];
  484.       tmpHullPt = vert.point;
  485.       projMat.mulP( tmpHullPt );
  486.       tmpHullPts.push_back( tmpHullPt );
  487.    }
  488.  
  489.    edgeVerts->clear();
  490.    U32 verts = _generateConvexHull( tmpHullPts, edgeVerts );
  491.    edgeVerts->setSize( verts );
  492.  
  493.    projMat.inverse();
  494.    for ( U32 i = 0; i < edgeVerts->size(); i++ )
  495.       projMat.mulP( (*edgeVerts)[i] );
  496.  
  497.    return true;
  498. }
  499.  
  500. DecalInstance* DecalManager::addDecal( const Point3F &pos,
  501.                                        const Point3F &normal,
  502.                                        F32 rotAroundNormal,
  503.                                        DecalData *decalData,
  504.                                        F32 decalScale,
  505.                                        S32 decalTexIndex,
  506.                                        U8 flags )
  507. {      
  508.    MatrixF mat( true );
  509.    MathUtils::getMatrixFromUpVector( normal, &mat );
  510.  
  511.    AngAxisF rot( normal, rotAroundNormal );
  512.    MatrixF rotmat;
  513.    rot.setMatrix( &rotmat );
  514.    mat.mul( rotmat );
  515.  
  516.    Point3F tangent;
  517.    mat.getColumn( 1, &tangent );
  518.  
  519.    return addDecal( pos, normal, tangent, decalData, decalScale, decalTexIndex, flags );  
  520. }
  521.  
  522. DecalInstance* DecalManager::addDecal( const Point3F& pos,
  523.                                        const Point3F& normal,
  524.                                        const Point3F& tangent,
  525.                                        DecalData* decalData,
  526.                                        F32 decalScale,
  527.                                        S32 decalTexIndex,
  528.                                        U8 flags )
  529. {
  530.    if ( !mData && !_createDataFile() )
  531.       return NULL;
  532.  
  533.    // only dirty the manager if this decal should be saved
  534.    if ( flags & SaveDecal )
  535.       mDirty = true;
  536.  
  537.    return mData->addDecal( pos, normal, tangent, decalData, decalScale, decalTexIndex, flags );
  538. }
  539.  
  540. void DecalManager::removeDecal( DecalInstance *inst )
  541. {
  542.    // If this is a decal we save then we need
  543.    // to set the dirty flag.
  544.    if ( inst->mFlags & SaveDecal )
  545.       mDirty = true;
  546.  
  547.    // Remove the decal from the instance vector.
  548.    
  549.     if( inst->mId != -1 && inst->mId < mDecalInstanceVec.size() )
  550.       mDecalInstanceVec[ inst->mId ] = NULL;
  551.    
  552.    // Release its geometry (if it has any).
  553.  
  554.    _freeBuffers( inst );
  555.    
  556.    // Remove it from the decal file.
  557.  
  558.    if ( mData )      
  559.       mData->removeDecal( inst );
  560. }
  561.  
  562. DecalInstance* DecalManager::getDecal( S32 id )
  563. {
  564.    if( id < 0 || id >= mDecalInstanceVec.size() )
  565.       return NULL;
  566.  
  567.    return mDecalInstanceVec[id];
  568. }
  569.  
  570. void DecalManager::notifyDecalModified( DecalInstance *inst )
  571. {
  572.    // If this is a decal we save then we need
  573.    // to set the dirty flag.
  574.    if ( inst->mFlags & SaveDecal )
  575.       mDirty = true;
  576.  
  577.    if ( mData )
  578.       mData->notifyDecalModified( inst );
  579. }
  580.  
  581. DecalInstance* DecalManager::getClosestDecal( const Point3F &pos )
  582. {
  583.    if ( !mData )
  584.       return NULL;
  585.  
  586.    const Vector<DecalSphere*> &grid = mData->getSphereList();
  587.  
  588.    DecalInstance *inst = NULL;
  589.    SphereF worldPickSphere( pos, 0.5f );
  590.    SphereF worldInstSphere( Point3F( 0, 0, 0 ), 1.0f );
  591.  
  592.    Vector<DecalInstance*> collectedInsts;
  593.  
  594.    for ( U32 i = 0; i < grid.size(); i++ )
  595.    {
  596.       DecalSphere *decalSphere = grid[i];
  597.       const SphereF &worldSphere = decalSphere->mWorldSphere;
  598.       if (  !worldSphere.isIntersecting( worldPickSphere ) &&
  599.             !worldSphere.isContained( pos ) )
  600.          continue;
  601.  
  602.       const Vector<DecalInstance*> &items = decalSphere->mItems;
  603.       for ( U32 n = 0; n < items.size(); n++ )
  604.       {
  605.          inst = items[n];
  606.          if ( !inst )
  607.             continue;
  608.  
  609.          worldInstSphere.center = inst->mPosition;
  610.          worldInstSphere.radius = inst->mSize;
  611.  
  612.          if ( !worldInstSphere.isContained( inst->mPosition ) )
  613.             continue;
  614.  
  615.          collectedInsts.push_back( inst );
  616.       }
  617.    }
  618.  
  619.    F32 closestDistance = F32_MAX;
  620.    F32 currentDist = 0;
  621.    U32 closestIndex = 0;
  622.    for ( U32 i = 0; i < collectedInsts.size(); i++ )
  623.    {
  624.       inst = collectedInsts[i];
  625.       currentDist = (inst->mPosition - pos).len();
  626.       if ( currentDist < closestDistance )
  627.       {
  628.          closestIndex = i;
  629.          closestDistance = currentDist;
  630.          worldInstSphere.center = inst->mPosition;
  631.          worldInstSphere.radius = inst->mSize;
  632.       }
  633.    }
  634.  
  635.    if (  !collectedInsts.empty() &&
  636.          collectedInsts[closestIndex] &&
  637.          closestDistance < 1.0f ||
  638.          worldInstSphere.isContained( pos ) )
  639.       return collectedInsts[closestIndex];
  640.    else
  641.       return NULL;
  642. }
  643.  
  644. DecalInstance* DecalManager::raycast( const Point3F &start, const Point3F &end, bool savedDecalsOnly )
  645. {
  646.    if ( !mData )
  647.       return NULL;
  648.  
  649.    const Vector<DecalSphere*> &grid = mData->getSphereList();
  650.  
  651.    DecalInstance *inst = NULL;
  652.    SphereF worldSphere( Point3F( 0, 0, 0 ), 1.0f );
  653.  
  654.    Vector<DecalInstance*> hitDecals;
  655.  
  656.    for ( U32 i = 0; i < grid.size(); i++ )
  657.    {
  658.       DecalSphere *decalSphere = grid[i];
  659.       if ( !decalSphere->mWorldSphere.intersectsRay( start, end ) )
  660.          continue;
  661.  
  662.       const Vector<DecalInstance*> &items = decalSphere->mItems;
  663.       for ( U32 n = 0; n < items.size(); n++ )
  664.       {
  665.          inst = items[n];
  666.          if ( !inst )
  667.             continue;
  668.  
  669.          if ( savedDecalsOnly && !(inst->mFlags & SaveDecal) )
  670.             continue;
  671.  
  672.          worldSphere.center = inst->mPosition;
  673.          worldSphere.radius = inst->mSize;
  674.  
  675.          if ( !worldSphere.intersectsRay( start, end ) )
  676.             continue;
  677.            
  678.             RayInfo ri;
  679.             bool containsPoint = false;
  680.             if ( gServerContainer.castRayRendered( start, end, STATIC_COLLISION_TYPEMASK, &ri ) )
  681.             {        
  682.                 Point2F poly[4];
  683.                 poly[0].set( inst->mPosition.x - (inst->mSize / 2), inst->mPosition.y + (inst->mSize / 2));
  684.                 poly[1].set( inst->mPosition.x - (inst->mSize / 2), inst->mPosition.y - (inst->mSize / 2));
  685.                 poly[2].set( inst->mPosition.x + (inst->mSize / 2), inst->mPosition.y - (inst->mSize / 2));
  686.                 poly[3].set( inst->mPosition.x + (inst->mSize / 2), inst->mPosition.y + (inst->mSize / 2));
  687.                
  688.                 if ( MathUtils::pointInPolygon( poly, 4, Point2F(ri.point.x, ri.point.y) ) )
  689.                     containsPoint = true;
  690.             }
  691.  
  692.             if( !containsPoint )
  693.                 continue;
  694.  
  695.          hitDecals.push_back( inst );
  696.       }
  697.    }
  698.  
  699.    if ( hitDecals.empty() )
  700.       return NULL;
  701.  
  702.    gSortPoint = start;
  703.    dQsort( hitDecals.address(), hitDecals.size(), sizeof(DecalInstance*), cmpDecalDistance );
  704.    return hitDecals[0];
  705. }
  706.  
  707. U32 DecalManager::_generateConvexHull( const Vector<Point3F> &points, Vector<Point3F> *outPoints )
  708. {
  709.    PROFILE_SCOPE( DecalManager_generateConvexHull );
  710.  
  711.    // chainHull_2D(): Andrew's monotone chain 2D convex hull algorithm
  712.    //     Input:  P[] = an array of 2D points
  713.    //                   presorted by increasing x- and y-coordinates
  714.    //             n = the number of points in P[]
  715.    //     Output: H[] = an array of the convex hull vertices (max is n)
  716.    //     Return: the number of points in H[]
  717.    //int
  718.  
  719.    if ( points.size() < 3 )
  720.    {
  721.       outPoints->merge( points );
  722.       return outPoints->size();
  723.    }
  724.  
  725.    // Sort our input points.
  726.    dQsort( points.address(), points.size(), sizeof( Point3F ), cmpPointsXY );
  727.  
  728.    U32 n = points.size();
  729.  
  730.    Vector<Point3F> tmpPoints;
  731.    tmpPoints.setSize( n );
  732.  
  733.    // the output array H[] will be used as the stack
  734.    S32    bot=0, top=(-1);  // indices for bottom and top of the stack
  735.    S32    i;                // array scan index
  736.    S32 toptmp = 0;
  737.  
  738.    // Get the indices of points with min x-coord and min|max y-coord
  739.    S32 minmin = 0, minmax;
  740.    F32 xmin = points[0].x;
  741.    for ( i = 1; i < n; i++ )
  742.      if (points[i].x != xmin)
  743.         break;
  744.  
  745.    minmax = i - 1;
  746.    if ( minmax == n - 1 )
  747.    {      
  748.       // degenerate case: all x-coords == xmin
  749.       toptmp = top + 1;
  750.       if ( toptmp < n )
  751.          tmpPoints[++top] = points[minmin];
  752.  
  753.       if ( points[minmax].y != points[minmin].y ) // a nontrivial segment
  754.       {
  755.          toptmp = top + 1;
  756.          if ( toptmp < n )
  757.             tmpPoints[++top] = points[minmax];
  758.       }
  759.  
  760.       toptmp = top + 1;
  761.       if ( toptmp < n )
  762.          tmpPoints[++top] = points[minmin];           // add polygon endpoint
  763.  
  764.       return top+1;
  765.    }
  766.  
  767.    // Get the indices of points with max x-coord and min|max y-coord
  768.    S32 maxmin, maxmax = n-1;
  769.    F32 xmax = points[n-1].x;
  770.  
  771.    for ( i = n - 2; i >= 0; i-- )
  772.      if ( points[i].x != xmax )
  773.         break;
  774.    
  775.    maxmin = i + 1;
  776.  
  777.    // Compute the lower hull on the stack H
  778.    toptmp = top + 1;
  779.    if ( toptmp < n )
  780.       tmpPoints[++top] = points[minmin];      // push minmin point onto stack
  781.  
  782.    i = minmax;
  783.    while ( ++i <= maxmin )
  784.    {
  785.       // the lower line joins P[minmin] with P[maxmin]
  786.       if ( isLeft( points[minmin], points[maxmin], points[i]) >= 0 && i < maxmin )
  787.          continue;          // ignore P[i] above or on the lower line
  788.  
  789.       while (top > 0)        // there are at least 2 points on the stack
  790.       {
  791.          // test if P[i] is left of the line at the stack top
  792.          if ( isLeft( tmpPoints[top-1], tmpPoints[top], points[i]) > 0)
  793.              break;         // P[i] is a new hull vertex
  794.          else
  795.             top--;         // pop top point off stack
  796.       }
  797.  
  798.       toptmp = top + 1;
  799.       if ( toptmp < n )
  800.          tmpPoints[++top] = points[i];       // push P[i] onto stack
  801.    }
  802.  
  803.    // Next, compute the upper hull on the stack H above the bottom hull
  804.    if (maxmax != maxmin)      // if distinct xmax points
  805.    {
  806.       toptmp = top + 1;
  807.       if ( toptmp < n )
  808.          tmpPoints[++top] = points[maxmax];  // push maxmax point onto stack
  809.    }
  810.  
  811.    bot = top;                 // the bottom point of the upper hull stack
  812.    i = maxmin;
  813.    while (--i >= minmax)
  814.    {
  815.       // the upper line joins P[maxmax] with P[minmax]
  816.       if ( isLeft( points[maxmax], points[minmax], points[i] ) >= 0 && i > minmax )
  817.          continue;          // ignore P[i] below or on the upper line
  818.  
  819.       while ( top > bot )    // at least 2 points on the upper stack
  820.       {
  821.          // test if P[i] is left of the line at the stack top
  822.          if ( isLeft( tmpPoints[top-1], tmpPoints[top], points[i] ) > 0 )
  823.              break;         // P[i] is a new hull vertex
  824.          else
  825.             top--;         // pop top point off stack
  826.       }
  827.  
  828.       toptmp = top + 1;
  829.       if ( toptmp < n )
  830.          tmpPoints[++top] = points[i];       // push P[i] onto stack
  831.    }
  832.  
  833.    if (minmax != minmin)
  834.    {
  835.       toptmp = top + 1;
  836.       if ( toptmp < n )
  837.          tmpPoints[++top] = points[minmin];  // push joining endpoint onto stack
  838.    }
  839.  
  840.    outPoints->merge( tmpPoints );
  841.  
  842.    return top + 1;
  843. }
  844.  
  845. void DecalManager::_generateWindingOrder( const Point3F &cornerPoint, Vector<Point3F> *sortPoints )
  846. {
  847.    // This block of code is used to find
  848.    // the winding order for the points in our quad.
  849.  
  850.    // First, choose an arbitrary corner point.
  851.    // We'll use the "lowerRight" point.
  852.    Point3F relPoint( 0, 0, 0 );
  853.  
  854.    // See comment below about radius.
  855.    //F32 radius = 0;
  856.  
  857.    F32 theta = 0;
  858.    
  859.    Vector<Point4F> tmpPoints;
  860.  
  861.    for ( U32 i = 0; i < (*sortPoints).size(); i++ )
  862.    {
  863.       const Point3F &pnt = (*sortPoints)[i];
  864.       relPoint = cornerPoint - pnt;
  865.  
  866.       // Get the radius (r^2 = x^2 + y^2).
  867.      
  868.       // This is commented because for a quad
  869.       // you typically can't have the same values
  870.       // for theta, which is the caveat which would
  871.       // require sorting by the radius.
  872.       //radius = mSqrt( (relPoint.x * relPoint.x) + (relPoint.y * relPoint.y) );
  873.  
  874.       // Get the theta value for the
  875.       // interval -PI, PI.
  876.  
  877.       // This algorithm for determining the
  878.       // theta value is defined by
  879.       //          | arctan( y / x )  if x > 0
  880.       //          | arctan( y / x )  if x < 0 and y >= 0
  881.       // theta =  | arctan( y / x )  if x < 0 and y < 0
  882.       //          | PI / 2           if x = 0 and y > 0
  883.       //          | -( PI / 2 )      if x = 0 and y < 0
  884.       if ( relPoint.x > 0.0f )
  885.          theta = mAtan2( relPoint.y, relPoint.x );
  886.       else if ( relPoint.x < 0.0f )
  887.       {
  888.          if ( relPoint.y >= 0.0f )
  889.             theta = mAtan2( relPoint.y, relPoint.x ) + M_PI_F;
  890.          else if ( relPoint.y < 0.0f )
  891.             theta = mAtan2( relPoint.y, relPoint.x ) - M_PI_F;
  892.       }
  893.       else if ( relPoint.x == 0.0f )
  894.       {
  895.          if ( relPoint.y > 0.0f )
  896.             theta = M_PI_F / 2.0f;
  897.          else if ( relPoint.y < 0.0f )
  898.             theta = -(M_PI_F / 2.0f);
  899.       }
  900.  
  901.       tmpPoints.push_back( Point4F( pnt.x, pnt.y, pnt.z, theta ) );
  902.    }
  903.  
  904.    dQsort( tmpPoints.address(), tmpPoints.size(), sizeof( Point4F ), cmpQuadPointTheta );
  905.  
  906.    for ( U32 i = 0; i < tmpPoints.size(); i++ )
  907.    {
  908.       const Point4F &tmpPoint = tmpPoints[i];
  909.       (*sortPoints)[i].set( tmpPoint.x, tmpPoint.y, tmpPoint.z );
  910.    }
  911. }
  912.  
  913. void DecalManager::_allocBuffers( DecalInstance *inst )
  914. {
  915.    const S32 sizeClass = _getSizeClass( inst );
  916.    
  917.    void* data;
  918.    if ( sizeClass == -1 )
  919.       data = dMalloc( sizeof( DecalVertex ) * inst->mVertCount + sizeof( U16 ) * inst->mIndxCount );
  920.    else
  921.       data = mChunkers[sizeClass]->alloc();
  922.  
  923.    inst->mVerts = reinterpret_cast< DecalVertex* >( data );
  924.    data = (U8*)data + sizeof( DecalVertex ) * inst->mVertCount;
  925.    inst->mIndices = reinterpret_cast< U16* >( data );
  926. }
  927.  
  928. void DecalManager::_freeBuffers( DecalInstance *inst )
  929. {
  930.    if ( inst->mVerts != NULL )
  931.    {
  932.       const S32 sizeClass = _getSizeClass( inst );
  933.      
  934.       if ( sizeClass == -1 )
  935.          dFree( inst->mVerts );
  936.       else
  937.       {
  938.          // Use FreeListChunker
  939.          mChunkers[sizeClass]->free( inst->mVerts );      
  940.       }
  941.  
  942.       inst->mVerts = NULL;
  943.       inst->mVertCount = 0;
  944.       inst->mIndices = NULL;
  945.       inst->mIndxCount = 0;
  946.    }  
  947. }
  948.  
  949. void DecalManager::_freePools()
  950. {
  951.    while ( !mVBPool.empty() )
  952.    {
  953.       delete mVBPool.last();
  954.       mVBPool.pop_back();
  955.    }
  956.  
  957.    while ( !mVBs.empty() )
  958.    {
  959.       delete mVBs.last();
  960.       mVBs.pop_back();
  961.    }
  962.  
  963.    while ( !mPBPool.empty() )
  964.    {
  965.       delete mPBPool.last();
  966.       mPBPool.pop_back();
  967.    }
  968.  
  969.    while ( !mPBs.empty() )
  970.    {
  971.       delete mPBs.last();
  972.       mPBs.pop_back();
  973.    }
  974. }
  975.  
  976. S32 DecalManager::_getSizeClass( DecalInstance *inst ) const
  977. {
  978.    U32 bytes = inst->mVertCount * sizeof( DecalVertex ) + inst->mIndxCount * sizeof ( U16 );
  979.  
  980.    if ( bytes <= SIZE_CLASS_0 )
  981.       return 0;
  982.    if ( bytes <= SIZE_CLASS_1 )
  983.       return 1;
  984.    if ( bytes <= SIZE_CLASS_2 )
  985.       return 2;
  986.  
  987.    // Size is outside of the largest chunker.
  988.    return -1;
  989. }
  990.  
  991. void DecalManager::prepRenderImage( SceneRenderState* state )
  992. {
  993.    PROFILE_SCOPE( DecalManager_RenderDecals );
  994.  
  995.    if ( !smDecalsOn || !mData )
  996.       return;
  997.  
  998.    // Decals only render in the diffuse pass!
  999.    // We technically could render them into reflections but prefer to save
  1000.    // the performance. This would also break the DecalInstance::mLastAlpha
  1001.    // optimization.
  1002.    if ( !state->isDiffusePass() )
  1003.       return;
  1004.  
  1005.    PROFILE_START( DecalManager_RenderDecals_SphereTreeCull );
  1006.  
  1007.    const Frustum& rootFrustum = state->getCameraFrustum();
  1008.  
  1009.    // Populate vector of decal instances to be rendered with all
  1010.    // decals from visible decal spheres.
  1011.  
  1012.    SceneManager* sceneManager = state->getSceneManager();
  1013.    SceneZoneSpaceManager* zoneManager = sceneManager->getZoneManager();
  1014.    AssertFatal( zoneManager, "DecalManager::prepRenderImage - No zone manager!" );
  1015.    const Vector<DecalSphere*> &grid = mData->getSphereList();
  1016.    const bool haveOnlyOutdoorZone = ( zoneManager->getNumActiveZones() == 1 );
  1017.    
  1018.    mDecalQueue.clear();
  1019.    for ( U32 i = 0; i < grid.size(); i++ )
  1020.    {
  1021.       DecalSphere* decalSphere = grid[i];
  1022.       const SphereF& worldSphere = decalSphere->mWorldSphere;
  1023.  
  1024.       // See if this decal sphere can be culled.
  1025.  
  1026.       const SceneCullingState& cullingState = state->getCullingState();
  1027.       if( haveOnlyOutdoorZone )
  1028.       {
  1029.          U32 outdoorZone = SceneZoneSpaceManager::RootZoneId;
  1030.          if( cullingState.isCulled( worldSphere, &outdoorZone, 1 ) )
  1031.             continue;
  1032.       }
  1033.       else
  1034.       {
  1035.          // Update the zoning state of the sphere, if we need to.
  1036.  
  1037.          if( decalSphere->mZones.size() == 0 )
  1038.             decalSphere->updateZoning( zoneManager );
  1039.  
  1040.          // Skip the sphere if it is not visible in any of its zones.
  1041.  
  1042.          if( cullingState.isCulled( worldSphere, decalSphere->mZones.address(), decalSphere->mZones.size() ) )
  1043.             continue;
  1044.       }  
  1045.  
  1046.       // TODO: If each sphere stored its largest decal instance we
  1047.       // could do an LOD step on it here and skip adding any of the
  1048.       // decals in the sphere.
  1049.  
  1050.       mDecalQueue.merge( decalSphere->mItems );
  1051.    }
  1052.  
  1053.    PROFILE_END();
  1054.  
  1055.    PROFILE_START( DecalManager_RenderDecals_Update );
  1056.  
  1057.    const U32 &curSimTime = Sim::getCurrentTime();  
  1058.    F32 pixelSize;
  1059.    U32 delta, diff;
  1060.    DecalInstance *dinst;
  1061.    DecalData *ddata;
  1062.  
  1063.    // Loop through DecalQueue once for preRendering work.
  1064.    // 1. Update DecalInstance fade (over time)
  1065.    // 2. Clip geometry if flagged to do so.
  1066.    // 3. Calculate lod - if decal is far enough away it will not render.
  1067.    for ( U32 i = 0; i < mDecalQueue.size(); i++ )
  1068.    {
  1069.       dinst = mDecalQueue[i];
  1070.       ddata = dinst->mDataBlock;
  1071.  
  1072.       // LOD calculation...
  1073.  
  1074.       pixelSize = dinst->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y );
  1075.  
  1076.       if ( pixelSize != F32_MAX && pixelSize < ddata->fadeEndPixelSize )      
  1077.       {
  1078.          mDecalQueue.erase_fast( i );
  1079.          i--;
  1080.          continue;
  1081.       }
  1082.  
  1083.       // We will try to render this decal... so do any
  1084.       // final adjustments to it before rendering.
  1085.  
  1086.       // Update fade and delete expired.
  1087.       if ( !( dinst->mFlags & PermanentDecal || dinst->mFlags & CustomDecal ) )
  1088.       {        
  1089.          delta = ( curSimTime - dinst->mCreateTime );
  1090.          if ( delta > dinst->mDataBlock->lifeSpan )        
  1091.          {            
  1092.             diff = delta - dinst->mDataBlock->lifeSpan;
  1093.             dinst->mVisibility = 1.0f - (F32)diff / (F32)dinst->mDataBlock->fadeTime;
  1094.  
  1095.             if ( dinst->mVisibility <= 0.0f )
  1096.             {
  1097.                mDecalQueue.erase_fast( i );
  1098.                removeDecal( dinst );              
  1099.                i--;
  1100.                continue;
  1101.             }
  1102.          }
  1103.          if(dinst->surfaceObject)
  1104.          {
  1105.             if(!Sim::findObject(dinst->surfaceObject))
  1106.             {
  1107.                //the object that was under the decal does not exist anymore
  1108.                mDecalQueue.erase_fast( i );
  1109.                removeDecal( dinst );              
  1110.                i--;
  1111.                continue;
  1112.             }
  1113.             RayInfo rInfo;
  1114.             Point3F oldPosition = dinst->mPosition;
  1115.             Point3F newPosition = dinst->mPosition - (dinst->mNormal*0.05);
  1116.             bool hit = false;
  1117.             //disableCollision();
  1118.             //let's ignore bullet physics and go the poor castRay in the client
  1119.             hit = gClientContainer.castRay(oldPosition, newPosition, (PlayerObjectType | VehicleObjectType) | (TerrainObjectType | StaticShapeObjectType), &rInfo);
  1120.             //enableCollision();
  1121.             if(!hit || ((rInfo.object)->getId() != dinst->surfaceObject))
  1122.             {
  1123.                //the object that was under the decal moved or transformed and the decal is in the air
  1124.                mDecalQueue.erase_fast( i );
  1125.                removeDecal( dinst );              
  1126.                i--;
  1127.                continue;
  1128.             }
  1129.          }
  1130.       }
  1131.  
  1132.       // Build clipped geometry for this decal if needed.
  1133.       if ( dinst->mFlags & ClipDecal && !( dinst->mFlags & CustomDecal ) )
  1134.       {  
  1135.          // Turn off the flag so we don't continually try to clip
  1136.          // if it fails.
  1137.          dinst->mFlags = dinst->mFlags & ~ClipDecal;
  1138.  
  1139.          if ( !(dinst->mFlags & CustomDecal) && !clipDecal( dinst ) )
  1140.          {
  1141.             // Clipping failed to get any geometry...
  1142.  
  1143.             // Remove it from the render queue.
  1144.             mDecalQueue.erase_fast( i );
  1145.             i--;
  1146.  
  1147.             // If the decal is one placed at run-time (not the editor)
  1148.             // then we should also permanently delete the decal instance.
  1149.             if ( !(dinst->mFlags & SaveDecal) )
  1150.             {
  1151.                removeDecal( dinst );
  1152.             }
  1153.  
  1154.             // If this is a decal placed by the editor it will be
  1155.             // flagged to attempt clipping again the next time it is
  1156.             // modified. For now we just skip rendering it.      
  1157.             continue;
  1158.          }        
  1159.       }
  1160.  
  1161.       // If we get here and the decal still does not have any geometry
  1162.       // skip rendering it. It must be an editor placed decal that failed
  1163.       // to clip any geometry but has not yet been flagged to try again.
  1164.       if ( !dinst->mVerts || dinst->mVertCount == 0 || dinst->mIndxCount == 0 )
  1165.       {
  1166.          mDecalQueue.erase_fast( i );
  1167.          i--;
  1168.          continue;
  1169.       }
  1170.            
  1171.       // Calculate the alpha value for this decal and apply it to the verts.            
  1172.       {
  1173.          PROFILE_START( DecalManager_RenderDecals_Update_SetAlpha );
  1174.  
  1175.          F32 alpha = 1.0f;
  1176.  
  1177.          // Only necessary for decals which fade over time or distance.
  1178.          if ( !( dinst->mFlags & PermanentDecal ) || dinst->mDataBlock->fadeStartPixelSize >= 0.0f )
  1179.          {
  1180.             if ( pixelSize < ddata->fadeStartPixelSize )
  1181.             {
  1182.                const F32 range = ddata->fadeStartPixelSize - ddata->fadeEndPixelSize;
  1183.                alpha = 1.0f - mClampF( ( ddata->fadeStartPixelSize - pixelSize ) / range, 0.0f, 1.0f );
  1184.             }
  1185.  
  1186.             alpha *= dinst->mVisibility;
  1187.          }
  1188.            
  1189.          // If the alpha value has not changed since last render avoid
  1190.          // looping through all the verts!
  1191.          if ( alpha != dinst->mLastAlpha )
  1192.          {
  1193.             // calculate the swizzles color once, outside the loop.
  1194.             GFXVertexColor color;
  1195.             color.set( 255, 255, 255, (U8)(alpha * 255.0f) );
  1196.  
  1197.             for ( U32 v = 0; v < dinst->mVertCount; v++ )
  1198.                dinst->mVerts[v].color = color;
  1199.  
  1200.             dinst->mLastAlpha = alpha;
  1201.          }      
  1202.  
  1203.          PROFILE_END();
  1204.       }
  1205.    }
  1206.  
  1207.    PROFILE_END();      
  1208.  
  1209.    if ( mDecalQueue.empty() )
  1210.       return;
  1211.  
  1212.    // Sort queued decals...
  1213.    // 1. Editor decals - in render priority order first, creation time second, and material third.
  1214.    // 2. Dynamic decals - in render priority order first and creation time second.
  1215.    //
  1216.    // With the constraint that decals with different render priority cannot
  1217.    // be rendered together in the same draw call.
  1218.  
  1219.    PROFILE_START( DecalManager_RenderDecals_Sort );
  1220.    dQsort( mDecalQueue.address(), mDecalQueue.size(), sizeof(DecalInstance*), cmpDecalRenderOrder );
  1221.    PROFILE_END();
  1222.  
  1223.    PROFILE_SCOPE( DecalManager_RenderDecals_RenderBatch );
  1224.  
  1225.    RenderPassManager *renderPass = state->getRenderPass();
  1226.  
  1227.    // Base render instance we use for convenience.
  1228.    // Data shared by all instances we allocate below can be copied
  1229.    // from the base instance at the same time.
  1230.    MeshRenderInst baseRenderInst;
  1231.    baseRenderInst.clear();  
  1232.  
  1233.    MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) );
  1234.    MathUtils::getZBiasProjectionMatrix( gDecalBias, rootFrustum, tempMat );
  1235.    baseRenderInst.projection = tempMat;
  1236.  
  1237.    baseRenderInst.objectToWorld = &MatrixF::Identity;
  1238.    baseRenderInst.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
  1239.  
  1240.    baseRenderInst.type = RenderPassManager::RIT_Decal;      
  1241.  
  1242.    // Make it the sort distance the max distance so that
  1243.    // it renders after all the other opaque geometry in
  1244.    // the prepass bin.
  1245.    baseRenderInst.sortDistSq = F32_MAX;
  1246.  
  1247.    Vector<DecalBatch> batches;
  1248.    DecalBatch *currentBatch = NULL;
  1249.  
  1250.    // Loop through DecalQueue collecting them into render batches.
  1251.    for ( U32 i = 0; i < mDecalQueue.size(); i++ )
  1252.    {
  1253.       DecalInstance *decal = mDecalQueue[i];      
  1254.       DecalData *data = decal->mDataBlock;
  1255.       Material *mat = data->getMaterial();
  1256.  
  1257.       if ( currentBatch == NULL )
  1258.       {
  1259.          // Start a new batch, beginning with this decal.
  1260.  
  1261.          batches.increment();
  1262.          currentBatch = &batches.last();
  1263.          currentBatch->startDecal = i;
  1264.          currentBatch->decalCount = 1;
  1265.  
  1266.          // Shrink and warning: preventing a potential crash.
  1267.          currentBatch->iCount =
  1268.              (decal->mIndxCount > smMaxIndices) ? smMaxIndices : decal->mIndxCount;
  1269.          currentBatch->vCount =
  1270.              (decal->mVertCount > smMaxVerts) ? smMaxVerts : decal->mVertCount;
  1271. #ifdef TORQUE_DEBUG
  1272.          // we didn't mean send a spam to the console
  1273.          static U32 countMsgIndx = 0;
  1274.          if ( (decal->mIndxCount > smMaxIndices) && ((countMsgIndx++ % 1024) == 0) ) {
  1275.             Con::warnf(
  1276.                "DecalManager::prepRenderImage() - Shrinked indices of decal."
  1277.                " Lost %u.",  (decal->mIndxCount - smMaxIndices)
  1278.             );
  1279.          }
  1280.          static U32 countMsgVert = 0;
  1281.          if ( (decal->mVertCount > smMaxVerts) && ((countMsgVert++ % 1024) == 0) ) {
  1282.             Con::warnf(
  1283.                "DecalManager::prepRenderImage() - Shrinked vertices of decal."
  1284.                " Lost %u.",  (decal->mVertCount - smMaxVerts)
  1285.             );
  1286.          }
  1287. #endif
  1288.  
  1289.          currentBatch->mat = mat;
  1290.          currentBatch->matInst = decal->mDataBlock->getMaterialInstance();
  1291.          currentBatch->priority = decal->getRenderPriority();        
  1292.          currentBatch->dynamic = !(decal->mFlags & SaveDecal);
  1293.  
  1294.          continue;
  1295.       }
  1296.  
  1297.       if ( currentBatch->iCount + decal->mIndxCount >= smMaxIndices ||
  1298.            currentBatch->vCount + decal->mVertCount >= smMaxVerts ||
  1299.            currentBatch->mat != mat ||
  1300.            currentBatch->priority != decal->getRenderPriority() ||
  1301.            decal->mCustomTex )
  1302.       {
  1303.          // End batch.
  1304.  
  1305.          currentBatch = NULL;
  1306.          i--;
  1307.          continue;
  1308.       }
  1309.  
  1310.       // Add on to current batch.
  1311.       currentBatch->decalCount++;
  1312.       currentBatch->iCount += decal->mIndxCount;
  1313.       currentBatch->vCount += decal->mVertCount;
  1314.    }
  1315.    
  1316.    // Make sure our primitive and vertex buffer handle storage is
  1317.    // big enough to take all batches.  Doing this now avoids reallocation
  1318.    // later on which would invalidate all the pointers we already had
  1319.    // passed into render instances.
  1320.    
  1321.    //mPBs.reserve( batches.size() );
  1322.    //mVBs.reserve( batches.size() );
  1323.  
  1324.    // System memory array of verts and indices so we can fill them incrementally
  1325.    // and then memcpy to the graphics device buffers in one call.
  1326.    static DecalVertex vertData[smMaxVerts];
  1327.    static U16 indexData[smMaxIndices];
  1328.  
  1329.    // Loop through batches allocating buffers and submitting render instances.
  1330.    for ( U32 i = 0; i < batches.size(); i++ )
  1331.    {
  1332.       DecalBatch &currentBatch = batches[i];      
  1333.  
  1334.       // Copy data into the system memory arrays, from all decals in this batch...
  1335.  
  1336.       DecalVertex *vpPtr = vertData;
  1337.       U16 *pbPtr = indexData;            
  1338.  
  1339.       U32 lastDecal = currentBatch.startDecal + currentBatch.decalCount;
  1340.  
  1341.       U32 voffset = 0;
  1342.       U32 ioffset = 0;
  1343.  
  1344.       // This is an ugly hack for ProjectedShadow!
  1345.       GFXTextureObject *customTex = NULL;
  1346.  
  1347.       for ( U32 j = currentBatch.startDecal; j < lastDecal; j++ )
  1348.       {
  1349.          DecalInstance *dinst = mDecalQueue[j];
  1350.  
  1351.          const U32 indxCount =
  1352.              (dinst->mIndxCount > currentBatch.iCount) ?
  1353.              currentBatch.iCount : dinst->mIndxCount;
  1354.          for ( U32 k = 0; k < indxCount; k++ )
  1355.          {
  1356.             *( pbPtr + ioffset + k ) = dinst->mIndices[k] + voffset;            
  1357.          }
  1358.  
  1359.          ioffset += indxCount;
  1360.  
  1361.          const U32 vertCount =
  1362.              (dinst->mVertCount > currentBatch.vCount) ?
  1363.              currentBatch.vCount : dinst->mVertCount;
  1364.          dMemcpy( vpPtr + voffset, dinst->mVerts, sizeof( DecalVertex ) * vertCount );
  1365.          voffset += vertCount;
  1366.  
  1367.          // Ugly hack for ProjectedShadow!
  1368.          if ( (dinst->mFlags & CustomDecal) && dinst->mCustomTex != NULL )
  1369.             customTex = *dinst->mCustomTex;
  1370.       }
  1371.  
  1372.       AssertFatal( ioffset == currentBatch.iCount, "bad" );
  1373.       AssertFatal( voffset == currentBatch.vCount, "bad" );
  1374.        
  1375.       // Get handles to video memory buffers we will be filling...
  1376.  
  1377.       GFXVertexBufferHandle<DecalVertex> *vb = NULL;
  1378.      
  1379.       if ( mVBPool.empty() )
  1380.       {
  1381.          // If the Pool is empty allocate a new one.
  1382.          vb = new GFXVertexBufferHandle<DecalVertex>;
  1383.          vb->set( GFX, smMaxVerts, GFXBufferTypeDynamic );                  
  1384.       }      
  1385.       else
  1386.       {
  1387.          // Otherwise grab from the pool.
  1388.          vb = mVBPool.last();
  1389.          mVBPool.pop_back();
  1390.       }
  1391.  
  1392.       // Push into our vector of 'in use' buffers.
  1393.       mVBs.push_back( vb );
  1394.      
  1395.       // Ready to start filling.
  1396.       vpPtr = vb->lock();
  1397.  
  1398.       // Same deal as above...      
  1399.       GFXPrimitiveBufferHandle *pb = NULL;
  1400.       if ( mPBPool.empty() )
  1401.       {
  1402.          pb = new GFXPrimitiveBufferHandle;
  1403.          pb->set( GFX, smMaxIndices, 0, GFXBufferTypeDynamic );  
  1404.       }
  1405.       else
  1406.       {
  1407.          pb = mPBPool.last();
  1408.          mPBPool.pop_back();
  1409.       }
  1410.       mPBs.push_back( pb );
  1411.      
  1412.       pb->lock( &pbPtr );
  1413.  
  1414.       // Memcpy from system to video memory.
  1415.       const U32 vpCount = sizeof( DecalVertex ) * currentBatch.vCount;
  1416.       dMemcpy( vpPtr, vertData, vpCount );
  1417.       const U32 pbCount = sizeof( U16 ) * currentBatch.iCount;
  1418.       dMemcpy( pbPtr, indexData, pbCount );
  1419.  
  1420.       pb->unlock();
  1421.       vb->unlock();
  1422.  
  1423.       // DecalManager must hold handles to these buffers so they remain valid,
  1424.       // we don't actually use them elsewhere.
  1425.       //mPBs.push_back( pb );
  1426.       //mVBs.push_back( vb );
  1427.  
  1428.       // Get the best lights for the current camera position
  1429.       // if the materail is forward lit and we haven't got them yet.
  1430.       if ( currentBatch.matInst->isForwardLit() && !baseRenderInst.lights[0] )
  1431.       {
  1432.          LightQuery query;
  1433.          query.init( rootFrustum.getPosition(),
  1434.                      rootFrustum.getTransform().getForwardVector(),
  1435.                      rootFrustum.getFarDist() );
  1436.            query.getLights( baseRenderInst.lights, 8 );
  1437.       }
  1438.  
  1439.       // Submit render inst...
  1440.       MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();
  1441.       *ri = baseRenderInst;
  1442.  
  1443.       ri->primBuff = pb;
  1444.       ri->vertBuff = vb;
  1445.  
  1446.       ri->matInst = currentBatch.matInst;
  1447.  
  1448.       ri->prim = renderPass->allocPrim();
  1449.       ri->prim->type = GFXTriangleList;
  1450.       ri->prim->minIndex = 0;
  1451.       ri->prim->startIndex = 0;
  1452.       ri->prim->numPrimitives = currentBatch.iCount / 3;
  1453.       ri->prim->startVertex = 0;
  1454.       ri->prim->numVertices = currentBatch.vCount;
  1455.  
  1456.       // Ugly hack for ProjectedShadow!
  1457.       if ( customTex )
  1458.          ri->miscTex = customTex;
  1459.  
  1460.       // The decal bin will contain render instances for both decals and decalRoad's.
  1461.       // Dynamic decals render last, then editor decals and roads in priority order.
  1462.       // DefaultKey is sorted in descending order.
  1463.       ri->defaultKey = currentBatch.dynamic ? 0xFFFFFFFF : (U32)currentBatch.priority;
  1464.       ri->defaultKey2 = 1;//(U32)lastDecal->mDataBlock;
  1465.  
  1466.       renderPass->addInst( ri );
  1467.    }
  1468.  
  1469. #ifdef TORQUE_GATHER_METRICS
  1470.    Con::setIntVariable( "$Decal::Batches", batches.size() );
  1471.    Con::setIntVariable( "$Decal::Buffers", mPBs.size() + mPBPool.size() );
  1472.    Con::setIntVariable( "$Decal::DecalsRendered", mDecalQueue.size() );
  1473. #endif
  1474.  
  1475.    if( smDebugRender && gEditingMission )
  1476.    {
  1477.       ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >();
  1478.  
  1479.       ri->renderDelegate.bind( this, &DecalManager::_renderDecalSpheres );
  1480.       ri->type = RenderPassManager::RIT_Editor;
  1481.       ri->defaultKey = 0;
  1482.       ri->defaultKey2 = 0;
  1483.  
  1484.       state->getRenderPass()->addInst( ri );
  1485.    }
  1486. }
  1487.  
  1488. void DecalManager::_renderDecalSpheres( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat )
  1489. {
  1490.    if( !mData )
  1491.       return;
  1492.  
  1493.    const Vector<DecalSphere*> &grid = mData->getSphereList();
  1494.  
  1495.    GFXDrawUtil *drawUtil = GFX->getDrawUtil();
  1496.    ColorI sphereColor( 0, 255, 0, 45 );
  1497.  
  1498.    GFXStateBlockDesc desc;
  1499.    desc.setBlend( true );
  1500.    desc.setZReadWrite( true, false );
  1501.    desc.setCullMode( GFXCullNone );
  1502.  
  1503.    for ( U32 i = 0; i < grid.size(); i++ )
  1504.    {
  1505.       DecalSphere *decalSphere = grid[i];
  1506.       const SphereF &worldSphere = decalSphere->mWorldSphere;
  1507.  
  1508.       if( state->getCullingFrustum().isCulled( worldSphere ) )
  1509.          continue;
  1510.  
  1511.       drawUtil->drawSphere( desc, worldSphere.radius, worldSphere.center, sphereColor );
  1512.    }
  1513. }
  1514.  
  1515. bool DecalManager::_createDataFile()
  1516. {
  1517.    AssertFatal( !mData, "DecalManager::tried to create duplicate data file?" );
  1518.  
  1519.    // We need to construct a default file name
  1520.    char fileName[1024];
  1521.    fileName[0] = 0;
  1522.  
  1523.    // See if we know our current mission name
  1524.    char missionName[1024];
  1525.    dStrcpy( missionName, Con::getVariable( "$Client::MissionFile" ) );
  1526.    char *dot = dStrstr((const char*)missionName, ".mis");
  1527.    if(dot)
  1528.       *dot = '\0';
  1529.    
  1530.    dSprintf( fileName, sizeof(fileName), "%s.mis.decals", missionName );
  1531.  
  1532.    mDataFileName = StringTable->insert( fileName );
  1533.  
  1534.    // If the file doesn't exist, create an empty file.
  1535.  
  1536.    if( !Torque::FS::IsFile( fileName ) )
  1537.    {
  1538.       FileStream stream;
  1539.       if( stream.open( mDataFileName, Torque::FS::File::Write ) )
  1540.       {
  1541.          DecalDataFile dataFile;
  1542.          dataFile.write( stream );
  1543.       }
  1544.    }
  1545.  
  1546.    mData = ResourceManager::get().load( mDataFileName );
  1547.    return (bool)mData;
  1548. }
  1549.  
  1550. void DecalManager::saveDecals( const UTF8* fileName )
  1551. {
  1552.    if( !mData )
  1553.       return;
  1554.  
  1555.    // Create the file.
  1556.  
  1557.    FileStream stream;
  1558.    if ( !stream.open( fileName, Torque::FS::File::Write ) )
  1559.    {
  1560.       Con::errorf( "DecalManager::saveDecals - Could not open '%s' for writing!", fileName );
  1561.       return;
  1562.    }
  1563.  
  1564.    // Write the data.
  1565.  
  1566.    if( !mData->write( stream ) )
  1567.    {
  1568.       Con::errorf( "DecalManager::saveDecals - Failed to write '%s'", fileName );
  1569.       return;
  1570.    }
  1571.  
  1572.    mDirty = false;
  1573. }
  1574.  
  1575. bool DecalManager::loadDecals( const UTF8 *fileName )
  1576. {
  1577.    if( mData )
  1578.       clearData();
  1579.  
  1580.    mData = ResourceManager::get().load( fileName );
  1581.  
  1582.    mDirty = false;
  1583.  
  1584.    return mData != NULL;
  1585. }
  1586.  
  1587. void DecalManager::clearData()
  1588. {
  1589.    mClearDataSignal.trigger();
  1590.    
  1591.    // Free all geometry buffers.
  1592.    
  1593.    if( mData )
  1594.    {
  1595.       const Vector< DecalSphere* > grid = mData->getSphereList();
  1596.       for( U32 i = 0; i < grid.size(); ++ i )
  1597.       {
  1598.          DecalSphere* sphere = grid[ i ];
  1599.          for( U32 n = 0; n < sphere->mItems.size(); ++ n )
  1600.             _freeBuffers( sphere->mItems[ n ] );
  1601.       }
  1602.    }
  1603.    
  1604.    mData = NULL;
  1605.     mDecalInstanceVec.clear();
  1606.  
  1607.    _freePools();  
  1608. }
  1609.  
  1610. bool DecalManager::onSceneAdd()
  1611. {
  1612.    if( !Parent::onSceneAdd() )
  1613.       return false;
  1614.  
  1615.    SceneZoneSpaceManager::getZoningChangedSignal().notify( this, &DecalManager::_handleZoningChangedEvent );
  1616.  
  1617.    return true;
  1618. }
  1619.  
  1620. void DecalManager::onSceneRemove()
  1621. {
  1622.    SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &DecalManager::_handleZoningChangedEvent );
  1623.    Parent::onSceneRemove();
  1624. }
  1625.  
  1626. void DecalManager::_handleZoningChangedEvent( SceneZoneSpaceManager* zoneManager )
  1627. {
  1628.    if( zoneManager != getSceneManager()->getZoneManager() || !getDecalDataFile() )
  1629.       return;
  1630.  
  1631.    // Clear the zoning state of all DecalSpheres in the data file.
  1632.  
  1633.    const Vector< DecalSphere* > grid = getDecalDataFile()->getSphereList();
  1634.    const U32 numSpheres = grid.size();
  1635.  
  1636.    for( U32 i = 0; i < numSpheres; ++ i )
  1637.       grid[ i ]->mZones.clear();
  1638. }
  1639.  
  1640. DefineEngineFunction( decalManagerSave, void, ( String decalSaveFile ), ( "" ),
  1641.    "Saves the decals for the active mission in the entered filename.\n"
  1642.    "@param decalSaveFile Filename to save the decals to.\n"
  1643.    "@tsexample\n"
  1644.    "// Set the filename to save the decals in. If no filename is set, then the\n"
  1645.    "// decals will default to <activeMissionName>.mis.decals\n"
  1646.    "%fileName = \"./missionDecals.mis.decals\";\n"
  1647.    "// Inform the decal manager to save the decals for the active mission.\n"
  1648.    "decalManagerSave( %fileName );\n"
  1649.    "@endtsexample\n"
  1650.    "@ingroup Decals" )
  1651. {
  1652.    // If not given a file name, synthesize one.
  1653.  
  1654.    if( decalSaveFile.isEmpty() )
  1655.    {
  1656.       String fileName = String::ToString( "%s.decals", Con::getVariable( "$Client::MissionFile" ) );
  1657.  
  1658.       char fullName[ 4096 ];
  1659.       Platform::makeFullPathName( fileName, fullName, sizeof( fullName ) );
  1660.  
  1661.       decalSaveFile = String( fullName );
  1662.    }
  1663.  
  1664.    // Write the data.
  1665.  
  1666.    gDecalManager->saveDecals( decalSaveFile );
  1667. }
  1668.  
  1669. DefineEngineFunction( decalManagerLoad, bool, ( const char* fileName ),,
  1670.    "Clears existing decals and replaces them with decals loaded from the specified file.\n"
  1671.    "@param fileName Filename to load the decals from.\n"
  1672.    "@return True if the decal manager was able to load the requested file, "
  1673.    "false if it could not.\n"
  1674.    "@tsexample\n"
  1675.    "// Set the filename to load the decals from.\n"
  1676.    "%fileName = \"./missionDecals.mis.decals\";\n"
  1677.    "// Inform the decal manager to load the decals from the entered filename.\n"
  1678.    "decalManagerLoad( %fileName );\n"
  1679.    "@endtsexample\n"
  1680.    "@ingroup Decals" )
  1681. {
  1682.    return gDecalManager->loadDecals( fileName );
  1683. }
  1684.  
  1685. DefineEngineFunction( decalManagerDirty, bool, (),,
  1686.    "Returns whether the decal manager has unsaved modifications.\n"
  1687.    "@return True if the decal manager has unsaved modifications, false if "
  1688.    "everything has been saved.\n"
  1689.    "@tsexample\n"
  1690.    "// Ask the decal manager if it has unsaved modifications.\n"
  1691.    "%hasUnsavedModifications = decalManagerDirty();\n"
  1692.    "@endtsexample\n"
  1693.    "@ingroup Decals" )
  1694. {
  1695.    return gDecalManager->isDirty();
  1696. }
  1697.  
  1698. DefineEngineFunction( decalManagerClear, void, (),,
  1699.    "Removes all decals currently loaded in the decal manager.\n"
  1700.    "@tsexample\n"
  1701.    "// Tell the decal manager to remove all existing decals.\n"
  1702.    "decalManagerClear();\n"
  1703.    "@endtsexample\n"
  1704.    "@ingroup Decals" )
  1705. {
  1706.    gDecalManager->clearData();
  1707. }
  1708.  
  1709. DefineEngineFunction( decalManagerAddDecal, S32,
  1710.    ( Point3F position, Point3F normal, F32 rot, F32 scale, DecalData* decalData, bool isImmortal), ( false ),
  1711.    "Adds a new decal to the decal manager.\n"
  1712.    "@param position World position for the decal.\n"
  1713.    "@param normal Decal normal vector (if the decal was a tire lying flat on a "
  1714.    "surface, this is the vector pointing in the direction of the axle).\n"
  1715.    "@param rot Angle (in radians) to rotate this decal around its normal vector.\n"
  1716.    "@param scale Scale factor applied to the decal.\n"
  1717.    "@param decalData DecalData datablock to use for the new decal.\n"
  1718.    "@param isImmortal Whether or not this decal is immortal. If immortal, it "
  1719.    "does not expire automatically and must be removed explicitly.\n"
  1720.    "@return Returns the ID of the new Decal object or -1 on failure.\n"
  1721.    "@tsexample\n"
  1722.    "// Specify the decal position\n"
  1723.    "%position = \"1.0 1.0 1.0\";\n\n"
  1724.    "// Specify the up vector\n"
  1725.    "%normal = \"0.0 0.0 1.0\";\n\n"
  1726.    "// Add the new decal.\n"
  1727.    "%decalObj = decalManagerAddDecal( %position, %normal, 0.5, 0.35, ScorchBigDecal, false );\n"
  1728.    "@endtsexample\n"
  1729.    "@ingroup Decals" )
  1730. {
  1731.    if( !decalData )
  1732.    {
  1733.       Con::errorf( "decalManagerAddDecal - Invalid Decal DataBlock" );
  1734.       return -1;
  1735.    }
  1736.  
  1737.    U8 flags = 0;
  1738.    if( isImmortal )
  1739.       flags |= PermanentDecal;
  1740.  
  1741.    DecalInstance* inst = gDecalManager->addDecal( position, normal, rot, decalData, scale, -1, flags );
  1742.    if( !inst )
  1743.    {
  1744.       Con::errorf( "decalManagerAddDecal - Unable to create decal instance." );
  1745.       return -1;
  1746.    }
  1747.  
  1748.    // Add the decal to the instance vector.
  1749.    inst->mId = gDecalManager->mDecalInstanceVec.size();
  1750.    gDecalManager->mDecalInstanceVec.push_back( inst );
  1751.  
  1752.    return inst->mId;
  1753. }
  1754.  
  1755. DefineEngineFunction( decalManagerRemoveDecal, bool, ( S32 decalID ),,
  1756.    "Remove specified decal from the scene.\n"
  1757.    "@param decalID ID of the decal to remove.\n"
  1758.    "@return Returns true if successful, false if decal ID not found.\n"
  1759.    "@tsexample\n"
  1760.    "// Specify a decal ID to be removed\n"
  1761.    "%decalID = 1;\n\n"
  1762.    "// Tell the decal manager to remove the specified decal ID.\n"
  1763.    "decalManagerRemoveDecal( %decalId )\n"
  1764.    "@endtsexample\n"
  1765.    "@ingroup Decals" )
  1766. {
  1767.    DecalInstance *inst = gDecalManager->getDecal( decalID );
  1768.    if( !inst )
  1769.       return false;
  1770.  
  1771.    gDecalManager->removeDecal(inst);
  1772.    return true;
  1773. }
  1774.  
  1775. DefineEngineFunction( decalManagerEditDecal, bool, ( S32 decalID, Point3F pos, Point3F normal, F32 rotAroundNormal, F32 decalScale ),,
  1776.    "Edit specified decal of the decal manager.\n"
  1777.    "@param decalID ID of the decal to edit.\n"
  1778.    "@param pos World position for the decal.\n"
  1779.    "@param normal Decal normal vector (if the decal was a tire lying flat on a "
  1780.    "surface, this is the vector pointing in the direction of the axle).\n"
  1781.    "@param rotAroundNormal Angle (in radians) to rotate this decal around its normal vector.\n"
  1782.    "@param decalScale Scale factor applied to the decal.\n"
  1783.    "@return Returns true if successful, false if decalID not found.\n"
  1784.    "" )
  1785. {
  1786.    DecalInstance *decalInstance = gDecalManager->getDecal( decalID );
  1787.    if( !decalInstance )
  1788.         return false;
  1789.  
  1790.    //Internally we need Point3F tangent instead of the user friendly F32 rotAroundNormal
  1791.    MatrixF mat( true );
  1792.    MathUtils::getMatrixFromUpVector( normal, &mat );
  1793.  
  1794.    AngAxisF rot( normal, rotAroundNormal );
  1795.    MatrixF rotmat;
  1796.    rot.setMatrix( &rotmat );
  1797.    mat.mul( rotmat );
  1798.  
  1799.    Point3F tangent;
  1800.    mat.getColumn( 1, &tangent );
  1801.    
  1802.    //if everything is unchanged just do nothing and  return "everything is ok"
  1803.    if ( pos.equal(decalInstance->mPosition) &&
  1804.         normal.equal(decalInstance->mNormal) &&
  1805.         tangent.equal(decalInstance->mTangent) &&
  1806.         mFabs( decalInstance->mSize - (decalInstance->mDataBlock->size * decalScale) ) < POINT_EPSILON )
  1807.            return true;
  1808.  
  1809.    decalInstance->mPosition = pos;
  1810.    decalInstance->mNormal = normal;
  1811.    decalInstance->mTangent = tangent;
  1812.    decalInstance->mSize = decalInstance->mDataBlock->size * decalScale;
  1813.  
  1814.    gDecalManager->clipDecal( decalInstance, NULL, NULL);
  1815.    
  1816.    gDecalManager->notifyDecalModified( decalInstance );
  1817.    return true;
  1818. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement