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 );
}
Example #2
0
bool GroundPlane::prepRenderImage( SceneState* state, const U32 stateKey, const U32, const bool )
{
   PROFILE_SCOPE( GroundPlane_prepRenderImage );
   
   if( isLastState( state, stateKey ) )
      return false;

   setLastState( state, stateKey );

   if( !state->isObjectRendered( this )
       || !mMaterial )
      return false;

   PROFILE_SCOPE( GroundPlane_prepRender );

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

   // 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                = mMaterial;
   ri->objectToWorld          = pass->allocUniqueXform( mRenderObjToWorld );
   ri->worldToCamera          = pass->allocSharedXform(RenderPassManager::View);
   ri->projection             = pass->allocSharedXform(RenderPassManager::Projection);
   ri->visibility             = 1.0f;
   ri->translucentSort        = ( ri->matInst->getMaterial()->isTranslucent() );
   // NOTICE: SFXBB is removed and refraction is disabled!
   //ri->backBuffTex            = GFX->getSfxBackBuffer();
   ri->defaultKey             = ( U32 ) mMaterial;

   // TODO: Get the best lights for the plane in a better
   // way.... maybe the same way as we do for terrain?
   ri->lights[0] = state->getLightManager()->getDefaultLight();

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

   pass->addInst( ri );

   return true;
}
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
bool LightFlareData::_testVisibility(const SceneRenderState *state, LightFlareState *flareState, U32 *outVisDelta, F32 *outOcclusionFade, Point3F *outLightPosSS)
{
   // Reflections use the results from the last forward
   // render so we don't need multiple queries.
   if ( state->isReflectPass() )
   {
      *outOcclusionFade = flareState->occlusion;
      *outVisDelta = Sim::getCurrentTime() - flareState->visChangedTime;
      return flareState->visible;
   }

   // Initialize it to something first.
   *outOcclusionFade = 0;

   // First check to see if the flare point 
   // is on scren at all... if not then return
   // the last result.
   const Point3F &lightPos = flareState->lightMat.getPosition();  
   const RectI &viewport = GFX->getViewport();
   MatrixF projMatrix;
   state->getCameraFrustum().getProjectionMatrix(&projMatrix);
   if( state->isReflectPass() )
      projMatrix = state->getSceneManager()->getNonClipProjection();
   bool onScreen = MathUtils::mProjectWorldToScreen( lightPos, outLightPosSS, viewport, GFX->getWorldMatrix(), projMatrix );

   // It is onscreen, so raycast as a simple occlusion test.
   const LightInfo *lightInfo = flareState->lightInfo;
   const bool isVectorLight = lightInfo->getType() == LightInfo::Vector;

   const bool useOcclusionQuery = isVectorLight ? flareState->worldRadius > 0.0f : mOcclusionRadius > 0.0f;
   bool needsRaycast = true;

   // NOTE: if hardware does not support HOQ it will return NULL
   // and we will retry every time but there is not currently a good place
   // for one-shot initialization of LightFlareState
   if ( useOcclusionQuery )
   {
      // Always treat light as onscreen if using HOQ
      // it will be faded out if offscreen anyway.
      onScreen = true;
	  needsRaycast = false;

      // Test the hardware queries for rendered pixels.
      U32 pixels = 0, fullPixels = 0;
      GFXOcclusionQuery::OcclusionQueryStatus status;
      flareState->occlusionQuery.getLastStatus( false, &status, &pixels );      
      flareState->fullPixelQuery.getLastStatus( false, NULL, &fullPixels );
      
      if ( status == GFXOcclusionQuery::NotOccluded && fullPixels != 0 )
         *outOcclusionFade = mClampF( (F32)pixels / (F32)fullPixels, 0.0f, 1.0f );

        if( !flareState->occlusionQuery.isWaiting() )
        {
            // Setup the new queries.
            RenderPassManager *rpm = state->getRenderPass();
            OccluderRenderInst *ri = rpm->allocInst<OccluderRenderInst>();   
            ri->type = RenderPassManager::RIT_Occluder;
            ri->query = flareState->occlusionQuery.getQuery();
            ri->query2 = flareState->fullPixelQuery.getQuery();
            ri->isSphere = true;
            ri->position = lightPos;
            if ( isVectorLight && flareState->worldRadius > 0.0f )         
                ri->scale.set( flareState->worldRadius );
            else
                ri->scale.set( mOcclusionRadius );
            ri->orientation = rpm->allocUniqueXform( lightInfo->getTransform() );         
      
            // Submit the queries.
            state->getRenderPass()->addInst( ri );
        }
   }

   const Point3F &camPos = state->getCameraPosition();

   if ( needsRaycast )
   {
      // Use a raycast to determine occlusion.
      GameConnection *conn = GameConnection::getConnectionToServer();
      if ( !conn )
         return false;

      const bool fps = conn->isFirstPerson();
      GameBase *control = conn->getControlObject();
      if ( control && fps )
         control->disableCollision();

      RayInfo rayInfo;

      if ( !gClientContainer.castRay( camPos, lightPos, LosMask, &rayInfo ) )
         *outOcclusionFade = 1.0f;

      if ( control && fps )
         control->enableCollision();
   }

   // The raycast and hardware occlusion query only calculate if
   // the flare is on screen... if does not account for being 
   // partially offscreen.
   //
   // The code here clips a box against the viewport to 
   // get an approximate percentage of onscreen area.
   //
   F32 worldRadius = flareState->worldRadius > 0 ? flareState->worldRadius : mOcclusionRadius;
   if ( worldRadius > 0.0f )
   {
      F32 dist = ( camPos - lightPos ).len();
      F32 pixelRadius = state->projectRadius(dist, worldRadius);

      RectI visRect( outLightPosSS->x - pixelRadius, outLightPosSS->y - pixelRadius, 
                     pixelRadius * 2.0f, pixelRadius * 2.0f ); 
      F32 fullArea = visRect.area();

      if ( visRect.intersect( viewport ) )
      {
         F32 visArea = visRect.area();
         *outOcclusionFade *= visArea / fullArea;
         onScreen = true;
      }
      else
         *outOcclusionFade = 0.0f;
   }
   
   const bool lightVisible = onScreen && *outOcclusionFade > 0.0f;

   // To perform a fade in/out when we gain or lose visibility
   // we must update/store the visibility state and time.
   const U32 currentTime = Sim::getCurrentTime();
   if ( lightVisible != flareState->visible )
   {
      flareState->visible = lightVisible;
      flareState->visChangedTime = currentTime;
   }

   // Return the visibility delta for time fading.
   *outVisDelta = currentTime - flareState->visChangedTime;

   // Store the final occlusion fade so that it can
   // be used in reflection rendering later.
   flareState->occlusion = *outOcclusionFade;

   return lightVisible;
}
Example #5
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 #6
0
bool DecalManager::prepRenderImage(SceneState* state, const U32 stateKey,
                                   const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
   PROFILE_SCOPE( DecalManager_RenderDecals );

   if ( !smDecalsOn || !mData ) 
      return false;

   if (isLastState(state, stateKey))
      return false;
   setLastState(state, stateKey);

   if ( !state->isDiffusePass() && !state->isReflectPass() )
      return false;

   PROFILE_START( DecalManager_RenderDecals_SphereTreeCull );

   // Grab this before anything here changes it.
   mCuller = state->getFrustum();

   // Populate vector of decal instances to be rendered with all
   // decals from visible decal spheres.

   mDecalQueue.clear();

   const Vector<DecalSphere*> &grid = mData->getGrid();
   
   for ( U32 i = 0; i < grid.size(); i++ )
   {
      const DecalSphere *decalSphere = grid[i];
      const SphereF &worldSphere = decalSphere->mWorldSphere;
      if ( !mCuller.sphereInFrustum( worldSphere.center, worldSphere.radius ) )
         continue;

      // TODO: If each sphere stored its largest decal instance we
      // could do an LOD step on it here and skip adding any of the
      // decals in the sphere.

      mDecalQueue.merge( decalSphere->mItems );
   }

   PROFILE_END();

   PROFILE_START( DecalManager_RenderDecals_Update );

   const U32 &curSimTime = Sim::getCurrentTime();
   const Point2I &viewportExtent = state->getViewportExtent();
   Point3F cameraOffset; 
   F32   decalSize, 
         pixelRadius;
   U32 delta, diff;
   DecalInstance *dinst;

   // Loop through DecalQueue once for preRendering work.
   // 1. Update DecalInstance fade (over time)
   // 2. Clip geometry if flagged to do so.
   // 3. Calculate lod - if decal is far enough away it will not render.
   for ( U32 i = 0; i < mDecalQueue.size(); i++ )
   {
      dinst = mDecalQueue[i];

      // LOD calculation.
      // See TSShapeInstance::setDetailFromDistance for more
      // information on these calculations.
      decalSize = getMax( dinst->mSize, 0.001f );
      pixelRadius = dinst->calcPixelRadius( state );

      // Need to clamp here.
      if ( pixelRadius < dinst->calcEndPixRadius( viewportExtent ) )
      {
         mDecalQueue.erase_fast( i );
         i--;
         continue;
      }

      // We're gonna try to render this decal... so do any 
      // final adjustments to it before rendering.

      // Update fade and delete expired.
      if ( !( dinst->mFlags & PermanentDecal || dinst->mFlags & CustomDecal ) )
      {         
         delta = ( curSimTime - dinst->mCreateTime );
         if ( delta > dinst->mDataBlock->lifeSpan )         
         {            
            diff = delta - dinst->mDataBlock->lifeSpan;
            dinst->mVisibility = 1.0f - (F32)diff / (F32)dinst->mDataBlock->fadeTime;

            if ( dinst->mVisibility <= 0.0f )
            {
               mDecalQueue.erase_fast( i );
               removeDecal( dinst );               
               i--;
               continue;
            }
         }
      }

      // Build clipped geometry for this decal if needed.
      if ( dinst->mFlags & ClipDecal/* && !( dinst->mFlags & CustomDecal ) */)
      {  
         // Turn off the flag so we don't continually try to clip
         // if it fails.
		  if(!clipDecal( dinst ))
		  {
				dinst->mFlags = dinst->mFlags & ~ClipDecal;
				if ( !(dinst->mFlags & CustomDecal) )
				{
					// Clipping failed to get any geometry...

					// Remove it from the render queue.
					mDecalQueue.erase_fast( i );
					i--;

					// If the decal is one placed at run-time (not the editor)
					// then we should also permanently delete the decal instance.
					if ( !(dinst->mFlags & SaveDecal) )
					{
						removeDecal( dinst );
					}
				}       
				// If this is a decal placed by the editor it will be
				// flagged to attempt clipping again the next time it is
				// modified. For now we just skip rendering it.      
				continue;
		  }  
      }

      // If we get here and the decal still does not have any geometry
      // skip rendering it. It must be an editor placed decal that failed
      // to clip any geometry but has not yet been flagged to try again.
      if ( !dinst->mVerts || dinst->mVertCount == 0 || dinst->mIndxCount == 0 )
      {
         mDecalQueue.erase_fast( i );
         i--;
         continue;
      }

      //
      F32 alpha = pixelRadius / (dinst->mDataBlock->startPixRadius * decalSize) - 1.0f;
      if ( dinst->mFlags & CustomDecal )
      {
         alpha = mClampF( alpha, 0.0f, 1.0f );
         alpha *= dinst->mVisibility;
      }
      else
         alpha = mClampF( alpha * dinst->mVisibility, 0.0f, 1.0f );

      //
      for ( U32 v = 0; v < dinst->mVertCount; v++ )
         dinst->mVerts[v].color.set( 255, 255, 255, alpha * 255.0f );
   }

   PROFILE_END();   

   if ( mDecalQueue.empty() )
      return false;

   // Sort queued decals...
   // 1. Editor decals - in render priority order first, creation time second, and material third.
   // 2. Dynamic decals - in render priority order first and creation time second.
   //
   // With the constraint that decals with different render priority cannot
   // be rendered together in the same draw call.

   PROFILE_START( DecalManager_RenderDecals_Sort );
   dQsort( mDecalQueue.address(), mDecalQueue.size(), sizeof(DecalInstance*), cmpDecalRenderOrder );
   PROFILE_END();

   PROFILE_SCOPE( DecalManager_RenderDecals_RenderBatch );

   mPrimBuffs.clear();
   mVBs.clear();

   RenderPassManager *renderPass = state->getRenderPass();

   // Base render instance for convenience we use for convenience.
   // Data shared by all instances we allocate below can be copied
   // from the base instance at the same time.
   MeshRenderInst baseRenderInst;
   baseRenderInst.clear();   

   MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) );
   MathUtils::getZBiasProjectionMatrix( gDecalBias, mCuller, tempMat );
   baseRenderInst.projection = tempMat;

   baseRenderInst.objectToWorld = &MatrixF::Identity;
   baseRenderInst.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);

   baseRenderInst.type = RenderPassManager::RIT_Decal;      

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

   // Get the best lights for the current camera position.
   LightManager *lm = state->getLightManager();
   if ( lm )
   {
      lm->setupLights(  NULL, 
                        mCuller.getPosition(),
                        mCuller.getTransform().getForwardVector(),
                        mCuller.getFarDist() );
      lm->getBestLights( baseRenderInst.lights, 4 );
      lm->resetLights();
   }

   Vector<DecalBatch> batches;
   DecalBatch *currentBatch = NULL;

   // Loop through DecalQueue collecting them into render batches.
   for ( U32 i = 0; i < mDecalQueue.size(); i++ )
   {
      DecalInstance *decal = mDecalQueue[i];      
      DecalData *data = decal->mDataBlock;
      Material *mat = data->getMaterial();

      if ( currentBatch == NULL )
      {
         // Start a new batch, beginning with this decal.

         batches.increment();
         currentBatch = &batches.last();
         currentBatch->startDecal = i;
         currentBatch->decalCount = 1;
         currentBatch->iCount = decal->mIndxCount;
         currentBatch->vCount = decal->mVertCount;
         currentBatch->mat = mat;
         currentBatch->matInst = decal->mDataBlock->getMaterialInstance();
         currentBatch->priority = decal->getRenderPriority();         
         currentBatch->dynamic = !(decal->mFlags & SaveDecal);

         continue;
      }

      if ( currentBatch->iCount + decal->mIndxCount >= smMaxIndices || 
           currentBatch->vCount + decal->mVertCount >= smMaxVerts ||
           currentBatch->mat != mat ||
           currentBatch->priority != decal->getRenderPriority() ||
           decal->mCustomTex )
      {
         // End batch.

         currentBatch = NULL;
         i--;
         continue;
      }

      // Add on to current batch.
      currentBatch->decalCount++;
      currentBatch->iCount += decal->mIndxCount;
      currentBatch->vCount += decal->mVertCount;
   }

   // Loop through batches allocating buffers and submitting render instances.
   for ( U32 i = 0; i < batches.size(); i++ )
   {
      DecalBatch &currentBatch = batches[i];

      // Allocate buffers...

      GFXVertexBufferHandle<DecalVertex> vb;
      vb.set( GFX, currentBatch.vCount, GFXBufferTypeDynamic );
      DecalVertex *vpPtr = vb.lock();

      GFXPrimitiveBufferHandle pb;
      pb.set( GFX, currentBatch.iCount, 0, GFXBufferTypeDynamic );
      U16 *pbPtr;
      pb.lock( &pbPtr );

      // Copy data into the buffers from all decals in this batch...

      U32 lastDecal = currentBatch.startDecal + currentBatch.decalCount;

      U32 voffset = 0;
      U32 ioffset = 0;

      // This is an ugly hack for ProjectedShadow!
      GFXTextureObject *customTex = NULL;

      for ( U32 j = currentBatch.startDecal; j < lastDecal; j++ )
      {
         DecalInstance *dinst = mDecalQueue[j];

         for ( U32 k = 0; k < dinst->mIndxCount; k++ )
         {
            *( pbPtr + ioffset + k ) = dinst->mIndices[k] + voffset;            
         }

         ioffset += dinst->mIndxCount;

         dMemcpy( vpPtr + voffset, dinst->mVerts, sizeof( DecalVertex ) * dinst->mVertCount );
         voffset += dinst->mVertCount;

         // Ugly hack for ProjectedShadow!
         if ( (dinst->mFlags & CustomDecal) && dinst->mCustomTex != NULL )
            customTex = *dinst->mCustomTex;
      }

      AssertFatal( ioffset == currentBatch.iCount, "bad" );
      AssertFatal( voffset == currentBatch.vCount, "bad" );
        
      pb.unlock();
      vb.unlock();

      // DecalManager must hold handles to these buffers so they remain valid,
      // we don't actually use them elsewhere.
      mPrimBuffs.push_back( pb );
      mVBs.push_back( vb );

      // Submit render inst...

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

      *ri = baseRenderInst;

      ri->primBuff = &mPrimBuffs.last();
      ri->vertBuff = &mVBs.last();

      ri->matInst = currentBatch.matInst;

      ri->prim = renderPass->allocPrim();
      ri->prim->type = GFXTriangleList;
      ri->prim->minIndex = 0;
      ri->prim->startIndex = 0;
      ri->prim->numPrimitives = currentBatch.iCount / 3;
      ri->prim->startVertex = 0;
      ri->prim->numVertices = currentBatch.vCount;

      // Ugly hack for ProjectedShadow!
      if ( customTex )
         ri->miscTex = customTex;

      // The decal bin will contain render instances for both decals and decalRoad's.
      // Dynamic decals render last, then editor decals and roads in priority order.
      // DefaultKey is sorted in descending order.
      ri->defaultKey = currentBatch.dynamic ? 0xFFFFFFFF : (U32)currentBatch.priority;
      ri->defaultKey2 = 1;//(U32)lastDecal->mDataBlock;

      renderPass->addInst( ri );
   }

   return false;
}
Example #7
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 #8
0
void TSStatic::prepRenderImage( SceneRenderState* state )
{
   if( !mShapeInstance )
      return;

   Point3F cameraOffset;
   getRenderTransform().getColumn(3,&cameraOffset);
   cameraOffset -= state->getDiffuseCameraPosition();
   F32 dist = cameraOffset.len();
   if (dist < 0.01f)
      dist = 0.01f;

   if (mUseAlphaFade)
   {
      mAlphaFade = 1.0f;
      if ((mAlphaFadeStart < mAlphaFadeEnd) && mAlphaFadeStart > 0.1f)
      {
         if (mInvertAlphaFade)
         {
            if (dist <= mAlphaFadeStart)
            {
               return;
            }
            if (dist < mAlphaFadeEnd)
            {
               mAlphaFade = ((dist - mAlphaFadeStart) / (mAlphaFadeEnd - mAlphaFadeStart));
            }
         }
         else
         {
            if (dist >= mAlphaFadeEnd)
            {
               return;
            }
            if (dist > mAlphaFadeStart)
            {
               mAlphaFade -= ((dist - mAlphaFadeStart) / (mAlphaFadeEnd - mAlphaFadeStart));
            }
         }
      }
   }

   F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z));   

   // If we're currently rendering our own reflection we
   // don't want to render ourselves into it.
   if ( mCubeReflector.isRendering() )
      return;


   if ( mForceDetail == -1 )
      mShapeInstance->setDetailFromDistance( state, dist * invScale );
   else
      mShapeInstance->setCurrentDetail( mForceDetail );

   if ( mShapeInstance->getCurrentDetail() < 0 )
      return;

   GFXTransformSaver saver;
   
   // Set up our TS render state.
   TSRenderState rdata;
   rdata.setSceneState( state );
   rdata.setFadeOverride( 1.0f );
   rdata.setOriginSort( mUseOriginSort );

   if ( mCubeReflector.isEnabled() )
      rdata.setCubemap( mCubeReflector.getCubemap() );

   // Acculumation
   rdata.setAccuTex(mAccuTex);

   // If we have submesh culling enabled then prepare
   // the object space frustum to pass to the shape.
   Frustum culler;
   if ( mMeshCulling )
   {
      culler = state->getCullingFrustum();
      MatrixF xfm( true );
      xfm.scale( Point3F::One / getScale() );
      xfm.mul( getRenderWorldTransform() );
      xfm.mul( culler.getTransform() );
      culler.setTransform( xfm );
      rdata.setCuller( &culler );
   }

   // We might have some forward lit materials
   // so pass down a query to gather lights.
   LightQuery query;
   query.init( getWorldSphere() );
   rdata.setLightQuery( &query );

   MatrixF mat = getRenderTransform();
   mat.scale( mObjScale );
   GFX->setWorldMatrix( mat );

   if ( state->isDiffusePass() && mCubeReflector.isEnabled() && mCubeReflector.getOcclusionQuery() )
   {
       RenderPassManager *pass = state->getRenderPass();
       OccluderRenderInst *ri = pass->allocInst<OccluderRenderInst>();  
       
       ri->type = RenderPassManager::RIT_Occluder;
       ri->query = mCubeReflector.getOcclusionQuery();
       mObjToWorld.mulP( mObjBox.getCenter(), &ri->position );
       ri->scale.set( mObjBox.getExtents() );
       ri->orientation = pass->allocUniqueXform( mObjToWorld ); 
       ri->isSphere = false;
       state->getRenderPass()->addInst( ri );
   }

   mShapeInstance->animate();
   if(mShapeInstance)
   {
      if (mUseAlphaFade)
      {
         mShapeInstance->setAlphaAlways(mAlphaFade);
         S32 s = mShapeInstance->mMeshObjects.size();
         
         for(S32 x = 0; x < s; x++)
         {
            mShapeInstance->mMeshObjects[x].visible = mAlphaFade;
         }
      }
   }
   mShapeInstance->render( rdata );

   if ( mRenderNormalScalar > 0 )
   {
      ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
      ri->renderDelegate.bind( this, &TSStatic::_renderNormals );
      ri->type = RenderPassManager::RIT_Editor;
      state->getRenderPass()->addInst( ri );
   }
}
Example #9
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 );
}
Example #10
0
bool DecalRoad::prepRenderImage(	SceneState* state, 
                                 const U32 stateKey, 
                                 const U32 startZone,
                                 const bool modifyBaseZoneState)
{
   if (  mNodes.size() <= 1 || 
         isLastState(state, stateKey) || 
         mBatches.size() == 0 ||
         !mMatInst ||
         state->isShadowPass() )
      return false;

   // Set Last State.
   setLastState( state, stateKey );

   // Is Object Rendered?
   if ( !state->isObjectRendered( this ) )
      return false;

   RenderPassManager *renderPass = state->getRenderPass();

   // Debug RenderInstance
   // Only when editor is open.
   if ( smEditorOpen )
   {
      ObjectRenderInst *ri = renderPass->allocInst<ObjectRenderInst>();
      ri->type = RenderPassManager::RIT_Object;
      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 false;

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

   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.matInst = mMatInst;
   coreRI.vertBuff = &mVB;
   coreRI.primBuff = &mPB;
	
   // 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;

	// Get the light manager and setup lights
   LightManager *lm = state->getLightManager();
   if ( lm )
   {
      lm->setupLights( this, getWorldSphere() );
		lm->getBestLights( coreRI.lights, 8 );
   }

   U32 startBatchIdx = -1;
   U32 endBatchIdx = 0;

   for ( U32 i = 0; i < mBatches.size(); i++ )   
   {
      // TODO: visibility is bugged... must fix!
      //const RoadBatch &batch = mBatches[i];
      //const bool isVisible = frustum.intersects( batch.bounds );         
      if ( true /*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]; // mBatches[0]; 
      const RoadBatch &endBatch = mBatches[endBatchIdx]; // mBatches.last(); 

      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 = startVert;
      ri->prim->numVertices = vertCount;

      // 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;
   }   

   return false;
}
Example #11
0
void LightFlareData::prepRender( SceneState *state, LightFlareState *flareState )
{    
   PROFILE_SCOPE( LightFlareData_prepRender );

   // No elements then nothing to render.
   if ( mElementCount == 0 )
      return;

   // We need these all over the place later.
   const Point3F &camPos = state->getCameraPosition();
   const RectI &viewport = GFX->getViewport();
   const Point3F &lightPos = flareState->lightMat.getPosition();
   LightInfo *lightInfo = flareState->lightInfo;

   bool isVectorLight = lightInfo->getType() == LightInfo::Vector;

   // Perform visibility testing on the light...
   // Project the light position from world to screen space, we need this
   // position later, and it tells us if it is actually onscreen.   
   
   Point3F lightPosSS;
   bool onscreen = MathUtils::mProjectWorldToScreen( lightPos, &lightPosSS, viewport, GFX->getWorldMatrix(), gClientSceneGraph->getNonClipProjection() );  

   U32 visDelta = U32_MAX;
   U32 fadeOutTime = 20;
   U32 fadeInTime = 125;    

   // Fade factor based on amount of occlusion.
   F32 occlusionFade = 1.0f;

   bool lightVisible = true;
   
   if ( !state->isReflectPass() )
   {
      // It is onscreen, so raycast as a simple occlusion test.

      U32 losMask =	STATIC_COLLISION_MASK |
                     ShapeBaseObjectType |
                     StaticTSObjectType |
                     ItemObjectType |
                     PlayerObjectType;

      GameConnection *conn = GameConnection::getConnectionToServer();
      if ( !conn )
         return;

      bool needsRaycast = true;

      // NOTE: if hardware does not support HOQ it will return NULL
      // and we will retry every time but there is not currently a good place
      // for one-shot initialization of LightFlareState
      if ( flareState->occlusionQuery == NULL )
         flareState->occlusionQuery = GFX->createOcclusionQuery();
      if ( flareState->fullPixelQuery == NULL )
         flareState->fullPixelQuery = GFX->createOcclusionQuery();

      if ( flareState->occlusionQuery && 
           ( ( isVectorLight && flareState->worldRadius > 0.0f ) || 
             ( !isVectorLight && mOcclusionRadius > 0.0f ) ) )
      {
         // Always treat light as onscreen if using HOQ
         // it will be faded out if offscreen anyway.
         onscreen = true;

         U32 pixels = -1;
         GFXOcclusionQuery::OcclusionQueryStatus status = flareState->occlusionQuery->getStatus( true, &pixels );

         String str = flareState->occlusionQuery->statusToString( status );
         Con::setVariable( "$Flare::OcclusionStatus", str.c_str() );
         Con::setIntVariable( "$Flare::OcclusionVal", pixels );
         
         if ( status == GFXOcclusionQuery::Occluded )
            occlusionFade = 0.0f;

         if ( status != GFXOcclusionQuery::Unset )         
            needsRaycast = false;

         RenderPassManager *pass = state->getRenderPass();

         OccluderRenderInst *ri = pass->allocInst<OccluderRenderInst>();   

         Point3F scale( Point3F::One );

         if ( isVectorLight && flareState->worldRadius > 0.0f )         
            scale *= flareState->worldRadius;
         else
            scale *= mOcclusionRadius;
         
         ri->type = RenderPassManager::RIT_Occluder;
         ri->query = flareState->occlusionQuery;   
         ri->query2 = flareState->fullPixelQuery;
         ri->position = lightPos;
         ri->scale = scale;
         ri->orientation = pass->allocUniqueXform( lightInfo->getTransform() );         
         ri->isSphere = true;
         state->getRenderPass()->addInst( ri );

         if ( status == GFXOcclusionQuery::NotOccluded )
         {
            U32 fullPixels;
            flareState->fullPixelQuery->getStatus( true, &fullPixels );

            occlusionFade = (F32)pixels / (F32)fullPixels;

            // Approximation of the full pixel count rather than doing
            // two queries, but it is not very accurate.
            /*
            F32 dist = ( camPos - lightPos ).len();
            F32 radius = scale.x;
            radius = ( radius / dist ) * state->getWorldToScreenScale().y;

            occlusionFade = (F32)pixels / (4.0f * radius * radius);
            occlusionFade = mClampF( occlusionFade, 0.0f, 1.0f );            
            */
         }
      }

      Con::setFloatVariable( "$Flare::OcclusionFade", occlusionFade );

      if ( needsRaycast )
      {
         // Use a raycast to determine occlusion.

         bool fps = conn->isFirstPerson();

         GameBase *control = conn->getControlObject();
         if ( control && fps )
            control->disableCollision();

         RayInfo rayInfo;

         if ( gClientContainer.castRayRendered( camPos, lightPos, losMask, &rayInfo ) )
            occlusionFade = 0.0f;

         if ( control && fps )
            control->enableCollision();
      }

      lightVisible = onscreen && occlusionFade > 0.0f;

      // To perform a fade in/out when we gain or lose visibility
      // we must update/store the visibility state and time.

      U32 currentTime = Sim::getCurrentTime();

      if ( lightVisible != flareState->visible )
      {
         flareState->visible = lightVisible;
         flareState->visChangedTime = currentTime;
      }      

      // Save this in the state so that we have it during the reflect pass.
      flareState->occlusion = occlusionFade;

      visDelta = currentTime - flareState->visChangedTime;      
   }
   else // state->isReflectPass()
   {
      occlusionFade = flareState->occlusion;
      lightVisible = flareState->visible;
      visDelta = Sim::getCurrentTime() - flareState->visChangedTime;
   }

   // We can only skip rendering if the light is not visible, and it
   // has elapsed the fadeOutTime.
   if ( !lightVisible && visDelta > fadeOutTime )
      return;

   // In a reflection we only render the elements with zero distance.   
   U32 elementCount = mElementCount;
   if ( state->isReflectPass()  )
   {
      elementCount = 0;
      for ( ; elementCount < mElementCount; elementCount++ )
      {
         if ( mElementDist[elementCount] > 0.0f )
            break;
      }
   }

   if ( elementCount == 0 )
      return;

   // A bunch of preparatory math before generating verts...

   const Point2I &vpExtent = viewport.extent;
   Point3F viewportExtent( vpExtent.x, vpExtent.y, 1.0f );
   Point2I halfViewportExtentI( viewport.extent / 2 );
   Point3F halfViewportExtentF( (F32)halfViewportExtentI.x * 0.5f, (F32)halfViewportExtentI.y, 0.0f );
   Point3F screenCenter( 0,0,0 );
   Point3F oneOverViewportExtent( 1.0f / viewportExtent.x, 1.0f / viewportExtent.y, 1.0f );

   lightPosSS.y -= viewport.point.y;
   lightPosSS *= oneOverViewportExtent;
   lightPosSS = ( lightPosSS * 2.0f ) - Point3F::One;
   lightPosSS.y = -lightPosSS.y;
   lightPosSS.z = 0.0f;

   Point3F flareVec( screenCenter - lightPosSS );
   F32 flareLength = flareVec.len();   
   flareVec.normalizeSafe();

   Point3F basePoints[4];
   basePoints[0] = Point3F( -0.5, 0.5, 0.0 );  
   basePoints[1] = Point3F( -0.5, -0.5, 0.0 );   
   basePoints[2] = Point3F( 0.5, -0.5, 0.0 );   
   basePoints[3] = Point3F( 0.5, 0.5, 0.0 );

   Point3F rotatedBasePoints[4];
   rotatedBasePoints[0] = basePoints[0];
   rotatedBasePoints[1] = basePoints[1];
   rotatedBasePoints[2] = basePoints[2];
   rotatedBasePoints[3] = basePoints[3];

   Point3F fvec( -1, 0, 0 );   
   F32 rot = mAcos( mDot( fvec, flareVec ) );
   Point3F rvec( 0, -1, 0 );
   rot *= mDot( rvec, flareVec ) > 0.0f ? 1.0f : -1.0f;

   vectorRotateZAxis( rotatedBasePoints[0], rot );
   vectorRotateZAxis( rotatedBasePoints[1], rot );
   vectorRotateZAxis( rotatedBasePoints[2], rot );
   vectorRotateZAxis( rotatedBasePoints[3], rot );

   // Here we calculate a the light source's influence on the effect's size
   // and brightness...

   // Scale based on the current light brightness compared to its normal output.
   F32 lightSourceBrightnessScale = lightInfo->getBrightness() / flareState->fullBrightness;
   // Scale based on world space distance from camera to light source.
   F32 lightSourceWSDistanceScale = ( isVectorLight ) ? 1.0f : getMin( 10.0f / ( lightPos - camPos ).len(), 1.5f );   
   // Scale based on screen space distance from screen position of light source to the screen center.
   F32 lightSourceSSDistanceScale = ( 1.5f - ( lightPosSS - screenCenter ).len() ) / 1.5f;

   // Scale based on recent visibility changes, fading in or out.
   F32 fadeInOutScale = 1.0f;
   if ( lightVisible && visDelta < fadeInTime && flareState->occlusion )
      fadeInOutScale = (F32)visDelta / (F32)fadeInTime;
   else if ( !lightVisible && visDelta < fadeOutTime )
      fadeInOutScale = 1.0f - (F32)visDelta / (F32)fadeOutTime;

   // This combined scale influences the size of all elements this effect renders.
   // Note we also add in a scale that is user specified in the Light.
   F32 lightSourceIntensityScale = lightSourceBrightnessScale * 
                                   lightSourceWSDistanceScale * 
                                   lightSourceSSDistanceScale * 
                                   fadeInOutScale * 
                                   flareState->scale *
                                   occlusionFade;

   // The baseColor which modulates the color of all elements.
   ColorF baseColor;
   if ( flareState->fullBrightness == 0.0f )
      baseColor = ColorF::BLACK;
   else
      // These are the factors which affect the "alpha" of the flare effect.
      // Modulate more in as appropriate.
      baseColor = ColorF::WHITE * lightSourceBrightnessScale * occlusionFade;

   // Fill in the vertex buffer...
   const U32 vertCount = 4 * elementCount;
   if (  flareState->vertBuffer.isNull() || 
         flareState->vertBuffer->mNumVerts != vertCount )
         flareState->vertBuffer.set( GFX, vertCount, GFXBufferTypeDynamic );

   GFXVertexPCT *pVert = flareState->vertBuffer.lock();

   const Point2I &widthHeightI = mFlareTexture.getWidthHeight();
   Point2F oneOverTexSize( 1.0f / (F32)widthHeightI.x, 1.0f / (F32)widthHeightI.y );

   for ( U32 i = 0; i < elementCount; i++ )
   {      
      Point3F *basePos = mElementRotate[i] ? rotatedBasePoints : basePoints;

      ColorF elementColor( baseColor * mElementTint[i] );
      if ( mElementUseLightColor[i] )
         elementColor *= lightInfo->getColor();

      Point3F elementPos;
      elementPos = lightPosSS + flareVec * mElementDist[i] * flareLength;      
      elementPos.z = 0.0f;

      F32 maxDist = 1.5f;
      F32 elementDist = mSqrt( ( elementPos.x * elementPos.x ) + ( elementPos.y * elementPos.y ) );
      F32 distanceScale = ( maxDist - elementDist ) / maxDist;
      distanceScale = 1.0f;

      const RectF &elementRect = mElementRect[i];
      Point3F elementSize( elementRect.extent.x, elementRect.extent.y, 1.0f );
      elementSize *= mElementScale[i] * distanceScale * mScale * lightSourceIntensityScale;

      if ( elementSize.x < 100.0f )
      {
         F32 alphaScale = mPow( elementSize.x / 100.0f, 2 );
         elementColor *= alphaScale;
      }

      elementColor.clamp();

      Point2F texCoordMin, texCoordMax;
      texCoordMin = elementRect.point * oneOverTexSize;
      texCoordMax = ( elementRect.point + elementRect.extent ) * oneOverTexSize;          

      pVert->color = elementColor;
      pVert->point = ( basePos[0] * elementSize * oneOverViewportExtent ) + elementPos;      
      pVert->texCoord.set( texCoordMin.x, texCoordMax.y );
      pVert++;

      pVert->color = elementColor;
      pVert->point = ( basePos[1] * elementSize * oneOverViewportExtent ) + elementPos;
      pVert->texCoord.set( texCoordMax.x, texCoordMax.y );
      pVert++;

      pVert->color = elementColor;
      pVert->point = ( basePos[2] * elementSize * oneOverViewportExtent ) + elementPos;
      pVert->texCoord.set( texCoordMax.x, texCoordMin.y );
      pVert++;

      pVert->color = elementColor;
      pVert->point = ( basePos[3] * elementSize * oneOverViewportExtent ) + elementPos;
      pVert->texCoord.set( texCoordMin.x, texCoordMin.y );
      pVert++;
   }   

   flareState->vertBuffer.unlock();   

   // Create and submit the render instance...
   
   RenderPassManager *renderManager = state->getRenderPass();
   ParticleRenderInst *ri = renderManager->allocInst<ParticleRenderInst>();

   ri->vertBuff = &flareState->vertBuffer;
   ri->primBuff = &mFlarePrimBuffer;
   ri->translucentSort = true;
   ri->type = RenderPassManager::RIT_Particle;
   ri->sortDistSq = ( lightPos - camPos ).lenSquared();

   ri->modelViewProj = &MatrixF::Identity;
   ri->bbModelViewProj = ri->modelViewProj;

   ri->count = elementCount;

   // Only draw the light flare in high-res mode, never off-screen mode
   ri->systemState = ParticleRenderInst::AwaitingHighResDraw;

   ri->blendStyle = ParticleRenderInst::BlendGreyscale;

   ri->diffuseTex = &*(mFlareTexture);

   ri->softnessDistance = 1.0f; 

   // Sort by texture too.
   ri->defaultKey = ri->diffuseTex ? (U32)ri->diffuseTex : (U32)ri->vertBuff;

   renderManager->addInst( ri );
}
Example #12
0
void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb )
{
   PROFILE_SCOPE( TSMesh_InnerRender );

   if( vertsPerFrame <= 0 ) 
      return;

   F32 meshVisibility = rdata.getFadeOverride() * mVisibility;
   if ( meshVisibility < VISIBILITY_EPSILON )
      return;

   const SceneRenderState *state = rdata.getSceneState();
   RenderPassManager *renderPass = state->getRenderPass();

   MeshRenderInst *coreRI = renderPass->allocInst<MeshRenderInst>();
   coreRI->type = RenderPassManager::RIT_Mesh;

   const MatrixF &objToWorld = GFX->getWorldMatrix();

   // Sort by the center point or the bounds.
   if ( rdata.useOriginSort() )
      coreRI->sortDistSq = ( objToWorld.getPosition() - state->getCameraPosition() ).lenSquared();
   else
   {
      Box3F rBox = mBounds;
      objToWorld.mul( rBox );
      coreRI->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() );      
   }

   if (getFlags(Billboard))
   {
      Point3F camPos = state->getDiffuseCameraPosition();
      Point3F objPos;
      objToWorld.getColumn(3, &objPos);
      Point3F targetVector = camPos - objPos;
      if(getFlags(BillboardZAxis))
         targetVector.z = 0.0f;
      targetVector.normalize();
      MatrixF orient = MathUtils::createOrientFromDir(targetVector);
      orient.setPosition(objPos);
      orient.scale(objToWorld.getScale());

      coreRI->objectToWorld = renderPass->allocUniqueXform( orient );
   }
   else
      coreRI->objectToWorld = renderPass->allocUniqueXform( objToWorld );

   coreRI->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
   coreRI->projection = renderPass->allocSharedXform(RenderPassManager::Projection);

   AssertFatal( vb.isValid(), "TSMesh::innerRender() - Got invalid vertex buffer!" );
   AssertFatal( pb.isValid(), "TSMesh::innerRender() - Got invalid primitive buffer!" );

   coreRI->vertBuff = &vb;
   coreRI->primBuff = &pb;
   coreRI->defaultKey2 = (U32) coreRI->vertBuff;

   coreRI->materialHint = rdata.getMaterialHint();

   coreRI->visibility = meshVisibility;  
   coreRI->cubemap = rdata.getCubemap();

   // NOTICE: SFXBB is removed and refraction is disabled!
   //coreRI->backBuffTex = GFX->getSfxBackBuffer();

   for ( S32 i = 0; i < primitives.size(); i++ )
   {
      const TSDrawPrimitive &draw = primitives[i];

      // We need to have a material.
      if ( draw.matIndex & TSDrawPrimitive::NoMaterial )
         continue;

#ifdef TORQUE_DEBUG
      // for inspection if you happen to be running in a debugger and can't do bit 
      // operations in your head.
      S32 triangles = draw.matIndex & TSDrawPrimitive::Triangles;
      S32 strip = draw.matIndex & TSDrawPrimitive::Strip;
      S32 fan = draw.matIndex & TSDrawPrimitive::Fan;
      S32 indexed = draw.matIndex & TSDrawPrimitive::Indexed;
      S32 type = draw.matIndex & TSDrawPrimitive::TypeMask;
      TORQUE_UNUSED(triangles);
      TORQUE_UNUSED(strip);
      TORQUE_UNUSED(fan);
      TORQUE_UNUSED(indexed);
      TORQUE_UNUSED(type);
#endif

      const U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask;
      BaseMatInstance *matInst = materials->getMaterialInst( matIndex );

#ifndef TORQUE_OS_MAC

      // Get the instancing material if this mesh qualifies.
      if ( meshType != SkinMeshType && pb->mPrimitiveArray[i].numVertices < smMaxInstancingVerts )
         matInst = InstancingMaterialHook::getInstancingMat( matInst );

#endif

      // If we don't have a material instance after the overload then
      // there is nothing to render... skip this primitive.
      matInst = state->getOverrideMaterial( matInst );
      if ( !matInst || !matInst->isValid())
         continue;

      // If the material needs lights then gather them
      // here once and set them on the core render inst.
      if ( matInst->isForwardLit() && !coreRI->lights[0] && rdata.getLightQuery() )
         rdata.getLightQuery()->getLights( coreRI->lights, 8 );

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

      ri->matInst = matInst;
      ri->defaultKey = matInst->getStateHint();
      ri->primBuffIndex = i;

      // Translucent materials need the translucent type.
      if ( matInst->getMaterial()->isTranslucent() )
      {
         ri->type = RenderPassManager::RIT_Translucent;
         ri->translucentSort = true;
      }

      renderPass->addInst( ri );
   }
}