void GroundPlane::prepRenderImage( SceneRenderState* state )
{
   PROFILE_SCOPE( GroundPlane_prepRenderImage );
   
   // TODO: Should we skip rendering the ground plane into
   // the shadows?  Its not like you can ever get under it.

   if ( !mMaterial )
      return;

   // If we don't have a material instance after the override then 
   // we can skip rendering all together.
   BaseMatInstance *matInst = state->getOverrideMaterial( mMaterial );
   if ( !matInst )
      return;

   PROFILE_SCOPE( GroundPlane_prepRender );

   // Update the geometry.
   createGeometry( state->getCullingFrustum() );
   if( mVertexBuffer.isNull() )
      return;

   // Add a render instance.

   RenderPassManager*   pass  = state->getRenderPass();
   MeshRenderInst*      ri    = pass->allocInst< MeshRenderInst >();

   ri->type                   = RenderPassManager::RIT_Mesh;
   ri->vertBuff               = &mVertexBuffer;
   ri->primBuff               = &mPrimitiveBuffer;
   ri->prim                   = &mPrimitive;
   ri->matInst                = matInst;
   ri->objectToWorld          = pass->allocUniqueXform( MatrixF::Identity );
   ri->worldToCamera          = pass->allocSharedXform( RenderPassManager::View );
   ri->projection             = pass->allocSharedXform( RenderPassManager::Projection );
   ri->visibility             = 1.0f;
   ri->translucentSort        = matInst->getMaterial()->isTranslucent();
   ri->defaultKey             = matInst->getStateHint();

   if( ri->translucentSort )
      ri->type = RenderPassManager::RIT_Translucent;

	// If we need lights then set them up.
   if ( matInst->isForwardLit() )
   {
      LightQuery query;
      query.init( getWorldSphere() );
		query.getLights( ri->lights, 8 );
   }

   pass->addInst( ri );
}
void PxCloth::prepRenderImage( SceneRenderState *state )
{  
   if ( mIsVBDirty )
      _updateVBIB();

   // Recreate the material if we need to.
   if ( !mMatInst )
      _initMaterial();

   // If we don't have a material instance after the override then 
   // we can skip rendering all together.
   BaseMatInstance *matInst = state->getOverrideMaterial( mMatInst );
   if ( !matInst )
      return;

   MeshRenderInst *ri = state->getRenderPass()->allocInst<MeshRenderInst>();

	// If we need lights then set them up.
   if ( matInst->isForwardLit() )
   {
      LightQuery query;
      query.init( getWorldSphere() );
		query.getLights( ri->lights, 8 );
   }

   ri->projection = state->getRenderPass()->allocSharedXform(RenderPassManager::Projection);
   ri->objectToWorld = &MatrixF::Identity;

   ri->worldToCamera = state->getRenderPass()->allocSharedXform(RenderPassManager::View);   
   ri->type = RenderPassManager::RIT_Mesh;

   ri->primBuff = &mPrimBuffer;
   ri->vertBuff = &mVB;

   ri->matInst = matInst;
   ri->prim = state->getRenderPass()->allocPrim();
   ri->prim->type = GFXTriangleList;
   ri->prim->minIndex = 0;
   ri->prim->startIndex = 0;
   ri->prim->numPrimitives = mNumIndices / 3;

   ri->prim->startVertex = 0;
   ri->prim->numVertices = mNumVertices;

   ri->defaultKey = matInst->getStateHint();
   ri->defaultKey2 = (U32)ri->vertBuff;

   state->getRenderPass()->addInst( ri );
}
Example #3
0
void DecalRoad::prepRenderImage( SceneRenderState* state )
{
   PROFILE_SCOPE( DecalRoad_prepRenderImage );

   if (  mNodes.size() <= 1 || 
         mBatches.size() == 0 ||
         !mMatInst ||
         state->isShadowPass() )
      return;

   // If we don't have a material instance after the override then 
   // we can skip rendering all together.
   BaseMatInstance *matInst = state->getOverrideMaterial( mMatInst );
   if ( !matInst )
      return;
      
   RenderPassManager *renderPass = state->getRenderPass();

   // Debug RenderInstance
   // Only when editor is open.
   if ( smEditorOpen )
   {
      ObjectRenderInst *ri = renderPass->allocInst<ObjectRenderInst>();
      ri->type = RenderPassManager::RIT_Editor;
      ri->renderDelegate.bind( this, &DecalRoad::_debugRender );
      state->getRenderPass()->addInst( ri );
   }

   // Normal Road RenderInstance
   // Always rendered when the editor is not open
   // otherwise obey the smShowRoad flag
   if ( !smShowRoad && smEditorOpen )
      return;

   const Frustum &frustum = state->getCameraFrustum();

   MeshRenderInst coreRI;
   coreRI.clear();
   coreRI.objectToWorld = &MatrixF::Identity;
   coreRI.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
   
   MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) );   
   MathUtils::getZBiasProjectionMatrix( gDecalBias, frustum, tempMat );
   coreRI.projection = tempMat;

   coreRI.type = RenderPassManager::RIT_Decal;
   coreRI.vertBuff = &mVB;
   coreRI.primBuff = &mPB;
   coreRI.matInst = matInst;

   // Make it the sort distance the max distance so that 
   // it renders after all the other opaque geometry in 
   // the prepass bin.
   coreRI.sortDistSq = F32_MAX;

	// If we need lights then set them up.
   if ( matInst->isForwardLit() )
   {
      LightQuery query;
      query.init( getWorldSphere() );
		query.getLights( coreRI.lights, 8 );
   }
   
   U32 startBatchIdx = -1;
   U32 endBatchIdx = 0;

   for ( U32 i = 0; i < mBatches.size(); i++ )   
   {
      const RoadBatch &batch = mBatches[i];
      const bool isVisible = !frustum.isCulled( batch.bounds );         
      if ( isVisible )
      {
         // If this is the start of a set of batches.
         if ( startBatchIdx == -1 )
            endBatchIdx = startBatchIdx = i;

         // Else we're extending the end batch index.
         else
            ++endBatchIdx; 

         // If this isn't the last batch then continue.
         if ( i < mBatches.size()-1 )
            continue;
      }

      // We we still don't have a start batch, so skip.
      if ( startBatchIdx == -1 )
         continue;

      // Render this set of batches.
      const RoadBatch &startBatch = mBatches[startBatchIdx];
      const RoadBatch &endBatch = mBatches[endBatchIdx]; 

      U32 startVert = startBatch.startVert;
      U32 startIdx = startBatch.startIndex;
      U32 vertCount = endBatch.endVert - startVert;
      U32 idxCount = ( endBatch.endIndex - startIdx ) + 1;
      U32 triangleCount = idxCount / 3;

      AssertFatal( startVert + vertCount <= mVertCount, "DecalRoad, bad draw call!" );
      AssertFatal( startIdx + triangleCount < mTriangleCount * 3, "DecalRoad, bad draw call!" );

      MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();

      *ri = coreRI;

      ri->prim = renderPass->allocPrim();
      ri->prim->type = GFXTriangleList;
      ri->prim->minIndex = 0;
      ri->prim->startIndex = startIdx;
      ri->prim->numPrimitives = triangleCount;
      ri->prim->startVertex = 0;
      ri->prim->numVertices = endBatch.endVert + 1;

      // For sorting we first sort by render priority
      // and then by objectId. 
      //
      // Since a road can submit more than one render instance, we want all 
      // draw calls for a single road to occur consecutively, since they
      // could use the same vertex buffer.
      ri->defaultKey =  mRenderPriority << 0 | mId << 16;
      ri->defaultKey2 = 0;

      renderPass->addInst( ri );

      // Reset the batching.
      startBatchIdx = -1;
   }   
}
Example #4
0
void TerrainBlock::_renderBlock( SceneRenderState *state )
{
   PROFILE_SCOPE( TerrainBlock_RenderBlock );

   // Prevent rendering shadows if feature is disabled
   if ( !mCastShadows && state->isShadowPass() ) 
      return;
	  
   MatrixF worldViewXfm = state->getWorldViewMatrix();
   worldViewXfm.mul( getRenderTransform() );

   MatrixF worldViewProjXfm = state->getProjectionMatrix();
   worldViewProjXfm.mul( worldViewXfm );

   const MatrixF &objectXfm = getRenderWorldTransform();

   Point3F objCamPos = state->getDiffuseCameraPosition();
   objectXfm.mulP( objCamPos );

   // Get the shadow material.
   if ( !mDefaultMatInst )
      mDefaultMatInst = TerrainCellMaterial::getShadowMat();

   // Make sure we have a base material.
   if ( !mBaseMaterial )
   {
      mBaseMaterial = new TerrainCellMaterial();
      mBaseMaterial->init( this, 0, false, false, true );
   }

   // Did the detail layers change?
   if ( mDetailsDirty )
   {   
      _updateMaterials();
      mDetailsDirty = false;
   }

   // If the layer texture has been cleared or is 
   // dirty then update it.
   if ( mLayerTex.isNull() || mLayerTexDirty )
      _updateLayerTexture();

   // If the layer texture is dirty or we lost the base
   // texture then regenerate it.
   if ( mLayerTexDirty || mBaseTex.isNull() )
   {
      _updateBaseTexture( false );
      mLayerTexDirty = false;
   }   

   static Vector<TerrCell*> renderCells;
   renderCells.clear();

   mCell->cullCells( state,
                     objCamPos,
                     &renderCells );

   RenderPassManager *renderPass = state->getRenderPass();

   MatrixF *riObjectToWorldXfm = renderPass->allocUniqueXform( getRenderTransform() );

   const bool isColorDrawPass = state->isDiffusePass() || state->isReflectPass();

   // This is here for shadows mostly... it allows the
   // proper shadow material to be generated.
   BaseMatInstance *defaultMatInst = state->getOverrideMaterial( mDefaultMatInst );

   // Only pass and use the light manager if this is not a shadow pass.
   LightManager *lm = NULL;
   if ( isColorDrawPass )
      lm = LIGHTMGR;

   for ( U32 i=0; i < renderCells.size(); i++ )
   {
      TerrCell *cell = renderCells[i];

      // Ok this cell is fit to render.
      TerrainRenderInst *inst = renderPass->allocInst<TerrainRenderInst>();

      // Setup lights for this cell.
      if ( lm )
      {
         SphereF bounds = cell->getSphereBounds();
         getRenderTransform().mulP( bounds.center );

         LightQuery query;
         query.init( bounds );
		   query.getLights( inst->lights, 8 );
      }

      GFXVertexBufferHandleBase vertBuff;
      GFXPrimitiveBufferHandle  primBuff;

      cell->getRenderPrimitive( &inst->prim, &vertBuff, &primBuff );

      inst->mat = defaultMatInst;
      inst->vertBuff = vertBuff.getPointer();

      if ( primBuff.isValid() )
      {
         // Use the cell's custom primitive buffer
         inst->primBuff = primBuff.getPointer();
      }
      else
      {
         // Use the standard primitive buffer for this cell
         inst->primBuff = mPrimBuffer.getPointer();
      }

      inst->objectToWorldXfm = riObjectToWorldXfm;

      // If we're not drawing to the shadow map then we need
      // to include the normal rendering materials. 
      if ( isColorDrawPass )
      {         
         const SphereF &bounds = cell->getSphereBounds();         

         F32 sqDist = ( bounds.center - objCamPos ).lenSquared();         

         F32 radiusSq = mSquared( ( mMaxDetailDistance + bounds.radius ) * smDetailScale );

         // If this cell is near enough to get detail textures then
         // use the full detail mapping material.  Else we use the
         // simple base only material.
         if ( !state->isReflectPass() && sqDist < radiusSq )
            inst->cellMat = cell->getMaterial();
         else if ( state->isReflectPass() )
            inst->cellMat = mBaseMaterial->getReflectMat();
         else
            inst->cellMat = mBaseMaterial;
      }

      inst->defaultKey = (U32)cell->getMaterials();

      // Submit it for rendering.
      renderPass->addInst( inst );
   }

   // Trigger the debug rendering.
   if (  state->isDiffusePass() && 
         !renderCells.empty() && 
         smDebugRender )
   {      
      // Store the render cells for later.
      mDebugCells = renderCells;

      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
      ri->renderDelegate.bind( this, &TerrainBlock::_renderDebug );
      ri->type = RenderPassManager::RIT_Editor;
      state->getRenderPass()->addInst( ri );
   }
}
Example #5
0
void ConvexShape::prepRenderImage( SceneRenderState *state )
{   
   /*
   if ( state->isDiffusePass() )
   {
      ObjectRenderInst *ri2 = state->getRenderPass()->allocInst<ObjectRenderInst>();
      ri2->renderDelegate.bind( this, &ConvexShape::_renderDebug );
      ri2->type = RenderPassManager::RIT_Editor;
      state->getRenderPass()->addInst( ri2 );
   }
   */

   if ( mVertexBuffer.isNull() )
      return;

   // If we don't have a material instance after the override then 
   // we can skip rendering all together.
   BaseMatInstance *matInst = state->getOverrideMaterial( mMaterialInst ? mMaterialInst : MATMGR->getWarningMatInstance() );
   if ( !matInst )
      return;

   // Get a handy pointer to our RenderPassmanager
   RenderPassManager *renderPass = state->getRenderPass();

   // Allocate an MeshRenderInst so that we can submit it to the RenderPassManager
   MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();

   // Set our RenderInst as a standard mesh render
   ri->type = RenderPassManager::RIT_Mesh;

   // Calculate our sorting point
   if ( state )
   {
      // Calculate our sort point manually.
      const Box3F& rBox = getRenderWorldBox();
      ri->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() );      
   } 
   else 
      ri->sortDistSq = 0.0f;

   // Set up our transforms
   MatrixF objectToWorld = getRenderTransform();
   objectToWorld.scale( getScale() );

   ri->objectToWorld = renderPass->allocUniqueXform( objectToWorld );
   ri->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
   ri->projection    = renderPass->allocSharedXform(RenderPassManager::Projection);

	// If we need lights then set them up.
   if ( matInst->isForwardLit() )
   {
      LightQuery query;
      query.init( getWorldSphere() );
		query.getLights( ri->lights, 8 );
   }

   // Make sure we have an up-to-date backbuffer in case
   // our Material would like to make use of it
   // NOTICE: SFXBB is removed and refraction is disabled!
   //ri->backBuffTex = GFX->getSfxBackBuffer();

   // Set our Material
   ri->matInst = matInst;
   if ( matInst->getMaterial()->isTranslucent() )
   {
      ri->translucentSort = true;
      ri->type = RenderPassManager::RIT_Translucent;
   }

   // Set up our vertex buffer and primitive buffer
   ri->vertBuff = &mVertexBuffer;
   ri->primBuff = &mPrimitiveBuffer;

   ri->prim = renderPass->allocPrim();
   ri->prim->type = GFXTriangleList;
   ri->prim->minIndex = 0;
   ri->prim->startIndex = 0;
   ri->prim->numPrimitives = mPrimCount;
   ri->prim->startVertex = 0;
   ri->prim->numVertices = mVertCount;

   // We sort by the material then vertex buffer.
   ri->defaultKey = matInst->getStateHint();
   ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe!

   // Submit our RenderInst to the RenderPassManager
   state->getRenderPass()->addInst( ri );
}
Example #6
0
void RenderMeshExample::prepRenderImage( SceneRenderState *state )
{
   // Do a little prep work if needed
   if ( mVertexBuffer.isNull() )
      createGeometry();

   // If we have no material then skip out.
   if ( !mMaterialInst )
      return;

   // If we don't have a material instance after the override then 
   // we can skip rendering all together.
   BaseMatInstance *matInst = state->getOverrideMaterial( mMaterialInst );
   if ( !matInst )
      return;

   // Get a handy pointer to our RenderPassmanager
   RenderPassManager *renderPass = state->getRenderPass();

   // Allocate an MeshRenderInst so that we can submit it to the RenderPassManager
   MeshRenderInst *ri = renderPass->allocInst<MeshRenderInst>();

   // Set our RenderInst as a standard mesh render
   ri->type = RenderPassManager::RIT_Mesh;

   // Calculate our sorting point
   if ( state )
   {
      // Calculate our sort point manually.
      const Box3F& rBox = getRenderWorldBox();
      ri->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() );      
   } 
   else 
      ri->sortDistSq = 0.0f;

   // Set up our transforms
   MatrixF objectToWorld = getRenderTransform();
   objectToWorld.scale( getScale() );

   ri->objectToWorld = renderPass->allocUniqueXform( objectToWorld );
   ri->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
   ri->projection    = renderPass->allocSharedXform(RenderPassManager::Projection);

	// If our material needs lights then fill the RIs 
   // light vector with the best lights.
   if ( matInst->isForwardLit() )
   {
      LightQuery query;
      query.init( getWorldSphere() );
		query.getLights( ri->lights, 8 );
   }

   // Make sure we have an up-to-date backbuffer in case
   // our Material would like to make use of it
   // NOTICE: SFXBB is removed and refraction is disabled!
   //ri->backBuffTex = GFX->getSfxBackBuffer();

   // Set our Material
   ri->matInst = matInst;

   // Set up our vertex buffer and primitive buffer
   ri->vertBuff = &mVertexBuffer;
   ri->primBuff = &mPrimitiveBuffer;

   ri->prim = renderPass->allocPrim();
   ri->prim->type = GFXTriangleList;
   ri->prim->minIndex = 0;
   ri->prim->startIndex = 0;
   ri->prim->numPrimitives = 12;
   ri->prim->startVertex = 0;
   ri->prim->numVertices = 36;

   // We sort by the material then vertex buffer
   ri->defaultKey = matInst->getStateHint();
   ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe!

   // Submit our RenderInst to the RenderPassManager
   state->getRenderPass()->addInst( ri );
}
bool ProjectedShadow::_updateDecal( const SceneRenderState *state )
{
    PROFILE_SCOPE( ProjectedShadow_UpdateDecal );

    if ( !LIGHTMGR )
        return false;

    // Get the position of the decal first.
    const Box3F &objBox = mParentObject->getObjBox();
    const Point3F boxCenter = objBox.getCenter();
    Point3F decalPos = boxCenter;
    const MatrixF &renderTransform = mParentObject->getRenderTransform();
    {
        // Set up the decal position.
        // We use the object space box center
        // multiplied by the render transform
        // of the object to ensure we benefit
        // from interpolation.
        MatrixF t( renderTransform );
        t.setColumn(2,Point3F::UnitZ);
        t.mulP( decalPos );
    }

    if ( mDecalInstance )
    {
        mDecalInstance->mPosition = decalPos;
        if ( !shouldRender( state ) )
            return false;
    }

    // Get the sunlight for the shadow projection.
    // We want the LightManager to return NULL if it can't
    // get the "real" sun, so we specify false for the useDefault parameter.
    LightInfo *lights[4] = {0};
    LightQuery query;
    query.init( mParentObject->getWorldSphere() );
    query.getLights( lights, 4 );

    Point3F pos = renderTransform.getPosition();

    Point3F lightDir( 0, 0, 0 );
    Point3F tmp( 0, 0, 0 );
    F32 weight = 0;
    F32 range = 0;
    U32 lightCount = 0;
    F32 dist = 0;
    F32 fade = 0;
    for ( U32 i = 0; i < 4; i++ )
    {
        // If we got a NULL light,
        // we're at the end of the list.
        if ( !lights[i] )
            break;

        if ( !lights[i]->getCastShadows() )
            continue;

        if ( lights[i]->getType() != LightInfo::Point )
            tmp = lights[i]->getDirection();
        else
            tmp = pos - lights[i]->getPosition();

        range = lights[i]->getRange().x;
        dist = ( (tmp.lenSquared()) / ((range * range) * 0.5f));
        weight = mClampF( 1.0f - ( tmp.lenSquared() / (range * range)), 0.00001f, 1.0f );

        if ( lights[i]->getType() == LightInfo::Vector )
            fade = getMax( fade, 1.0f );
        else
            fade = getMax( fade, mClampF( 1.0f - dist, 0.00001f, 1.0f ) );

        lightDir += tmp * weight;
        lightCount++;
    }

    lightDir.normalize();

    // No light... no shadow.
    if ( !lights[0] )
        return false;

    // Has the light direction
    // changed since last update?
    bool lightDirChanged = !mLastLightDir.equal( lightDir );

    // Has the parent object moved
    // or scaled since the last update?
    bool hasMoved = !mLastObjectPosition.equal( mParentObject->getRenderPosition() );
    bool hasScaled = !mLastObjectScale.equal( mParentObject->getScale() );

    // Set the last light direction
    // to the current light direction.
    mLastLightDir = lightDir;
    mLastObjectPosition = mParentObject->getRenderPosition();
    mLastObjectScale = mParentObject->getScale();


    // Temps used to generate
    // tangent vector for DecalInstance below.
    VectorF right( 0, 0, 0 );
    VectorF fwd( 0, 0, 0 );
    VectorF tmpFwd( 0, 0, 0 );

    U32 idx = lightDir.getLeastComponentIndex();

    tmpFwd[idx] = 1.0f;

    right = mCross( tmpFwd, lightDir );
    fwd = mCross( lightDir, right );
    right = mCross( fwd, lightDir );

    right.normalize();

    // Set up the world to light space
    // matrix, along with proper position
    // and rotation to be used as the world
    // matrix for the render to texture later on.
    static MatrixF sRotMat(EulerF( 0.0f, -(M_PI_F/2.0f), 0.0f));
    mWorldToLight.identity();
    MathUtils::getMatrixFromForwardVector( lightDir, &mWorldToLight );
    mWorldToLight.setPosition( ( pos + boxCenter ) - ( ( (mRadius * smDepthAdjust) + 0.001f ) * lightDir ) );
    mWorldToLight.mul( sRotMat );
    mWorldToLight.inverse();

    // Get the shapebase datablock if we have one.
    ShapeBaseData *data = NULL;
    if ( mShapeBase )
        data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );

    // We use the object box's extents multiplied
    // by the object's scale divided by 2 for the radius
    // because the object's worldsphere radius is not
    // rotationally invariant.
    mRadius = (objBox.getExtents() * mParentObject->getScale()).len() * 0.5f;

    if ( data )
        mRadius *= data->shadowSphereAdjust;

    // Create the decal if we don't have one yet.
    if ( !mDecalInstance )
        mDecalInstance = gDecalManager->addDecal( decalPos,
                         lightDir,
                         right,
                         mDecalData,
                         1.0f,
                         0,
                         PermanentDecal | ClipDecal | CustomDecal );

    if ( !mDecalInstance )
        return false;

    mDecalInstance->mVisibility = fade;

    // Setup decal parameters.
    mDecalInstance->mSize = mRadius * 2.0f;
    mDecalInstance->mNormal = -lightDir;
    mDecalInstance->mTangent = -right;
    mDecalInstance->mRotAroundNormal = 0;
    mDecalInstance->mPosition = decalPos;
    mDecalInstance->mDataBlock = mDecalData;

    // If the position of the world
    // space box center is the same
    // as the decal's position, and
    // the light direction has not
    // changed, we don't need to clip.
    bool shouldClip = lightDirChanged || hasMoved || hasScaled;

    // Now, check and see if the object is visible.
    const Frustum &frust = state->getCullingFrustum();
    if ( frust.isCulled( SphereF( mDecalInstance->mPosition, mDecalInstance->mSize * mDecalInstance->mSize ) ) && !shouldClip )
        return false;

    F32 shadowLen = 10.0f;
    if ( data )
        shadowLen = data->shadowProjectionDistance;

    const Point3F &boxExtents = objBox.getExtents();


    mShadowLength = shadowLen * mParentObject->getScale().z;

    // Set up clip depth, and box half
    // offset for decal clipping.
    Point2F clipParams(  mShadowLength, (boxExtents.x + boxExtents.y) * 0.25f );

    bool render = false;
    bool clipSucceeded = true;

    // Clip!
    if ( shouldClip )
    {
        clipSucceeded = gDecalManager->clipDecal( mDecalInstance,
                        NULL,
                        &clipParams );
    }

    // If the clip failed,
    // we'll return false in
    // order to keep from
    // unnecessarily rendering
    // into the texture.  If
    // there was no reason to clip
    // on this update, we'll assume we
    // should update the texture.
    render = clipSucceeded;

    // Tell the DecalManager we've changed this decal.
    gDecalManager->notifyDecalModified( mDecalInstance );

    return render;
}