Example #1
0
void TerrCell::_updatePrimitiveBuffer()
{
   PROFILE_SCOPE( TerrCell_UpdatePrimitiveBuffer );

   if ( !mHasEmpty )
   {
      if ( mPrimBuffer.isValid() )
      {
         // There are no more empty squares for this cell, so
         // get rid of the primitive buffer to use the standard one.
         mPrimBuffer = NULL;
      }

      return;
   }

   // Build our custom primitive buffer.  We're setting it to the maximum allowed
   // size, but should be just shy of it depending on the number of empty squares
   // in this cell.  We could calculate it, but note that it would be different
   // from mEmptyVertexList.size() as that can include vertices on the edges that
   // are really considered part of another cell's squares.  So we take a slightly
   // larger buffer over running through the calculation.
   mPrimBuffer.set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" );

   GFXPrimitive *prim = mPrimBuffer.getPointer()->mPrimitiveArray;
   prim->type = GFXTriangleList;
   prim->numVertices = smVBSize;

   mTriCount = 0;

   // Lock and fill it up!
   U16 *idxBuff;
   mPrimBuffer.lock( &idxBuff );
   U32 counter = 0;
   U32 maxIndex = 0;

   for ( U32 y = 0; y < smMinCellSize; y++ )
   {
      const U32 yTess = y % 2;

      for ( U32 x = 0; x < smMinCellSize; x++ )
      {
         U32 index = ( y * smVBStride ) + x;
         
         // Should this square be skipped?
         if ( _isVertIndexEmpty(index) )
            continue;

         const U32 xTess = x % 2;

         if ( ( xTess == 0 && yTess == 0 ) ||
              ( xTess != 0 && yTess != 0 ) )
         {
            idxBuff[0] = index + 0;
            idxBuff[1] = index + smVBStride;
            idxBuff[2] = index + smVBStride + 1;

            idxBuff[3] = index + 0;
            idxBuff[4] = index + smVBStride + 1;
            idxBuff[5] = index + 1;
         }
         else
         {
            idxBuff[0] = index + 1;
            idxBuff[1] = index;
            idxBuff[2] = index + smVBStride;

            idxBuff[3] = index + 1;
            idxBuff[4] = index + smVBStride;
            idxBuff[5] = index + smVBStride + 1;
         }

         idxBuff += 6;
         maxIndex = index + 1 + smVBStride;         
         counter += 6;         

         mTriCount += 2;
      }
   }

   // Now add indices for the 'skirts'.
   // These could probably be reduced to a loop.

   // Temporaries that hold triangle indices.
   // Top/Bottom - 0,1
   U32 t0, t1, b0, b1;

   // Top edge skirt...

   // Index to the first vert of the top row.
   U32 startIndex = 0;
   // Index to the first vert of the skirt under the top row.
   U32 skirtStartIdx = smVBStride * smVBStride;
   // Step to go one vert to the right.
   U32 step = 1;

   for ( U32 i = 0; i < smMinCellSize; i++ )
   {
      t0 = startIndex + i * step;
         
      // Should this square be skipped?
      if ( _isVertIndexEmpty(t0) )
         continue;

      t1 = t0 + step;
      b0 = skirtStartIdx + i;
      b1 = skirtStartIdx + i + 1;

      idxBuff[0] = b0;
      idxBuff[1] = t0;
      idxBuff[2] = t1;

      idxBuff[3] = b1;
      idxBuff[4] = b0;
      idxBuff[5] = t1;

      idxBuff += 6;
      maxIndex = b1;
      counter += 6;

      mTriCount += 2;
   }

   // Bottom edge skirt...

   // Index to the first vert of the bottom row.
   startIndex = smVBStride * smVBStride - smVBStride;
   // Index to the first vert of the skirt under the bottom row.
   skirtStartIdx = startIndex + smVBStride * 2;
   // Step to go one vert to the right.
   step = 1;
   
   for ( U32 i = 0; i < smMinCellSize; i++ )
   {
      t0 = startIndex + ( i * step );
         
      // Should this square be skipped?  We actually need to test
      // the vertex one row down as it defines the empty state
      // for this square.
      if ( _isVertIndexEmpty( t0 - smVBStride ) )
         continue;

      t1 = t0 + step;
      b0 = skirtStartIdx + i;
      b1 = skirtStartIdx + i + 1;

      idxBuff[0] = t1;
      idxBuff[1] = t0;
      idxBuff[2] = b0;

      idxBuff[3] = t1;
      idxBuff[4] = b0;
      idxBuff[5] = b1;

      idxBuff += 6;
      maxIndex = b1;
      counter += 6;

      mTriCount += 2;
   }

   // Left edge skirt...

   // Index to the first vert of the left column.
   startIndex = 0;
   // Index to the first vert of the skirt under the left column.
   skirtStartIdx = smVBStride * smVBStride + smVBStride * 2;
   // Step to go one vert down.
   step = smVBStride;

   for ( U32 i = 0; i < smMinCellSize; i++ )
   {
      t0 = startIndex + ( i * step );
         
      // Should this square be skipped?
      if ( _isVertIndexEmpty(t0) )
         continue;

      t1 = t0 + step;
      b0 = skirtStartIdx + i;
      b1 = skirtStartIdx + i + 1;

      idxBuff[0] = t1;
      idxBuff[1] = t0;
      idxBuff[2] = b0;

      idxBuff[3] = t1;
      idxBuff[4] = b0;
      idxBuff[5] = b1;

      idxBuff += 6;
      maxIndex = b1;
      counter += 6;

      mTriCount += 2;
   }

   // Right edge skirt...

   // Index to the first vert of the right column.
   startIndex = smVBStride - 1;
   // Index to the first vert of the skirt under the right column.
   skirtStartIdx = smVBStride * smVBStride + smVBStride * 3;
   // Step to go one vert down.
   step = smVBStride;

   for ( U32 i = 0; i < smMinCellSize; i++ )
   {
      t0 = startIndex + ( i * step );
         
      // Should this square be skipped?  We actually need to test
      // the vertex one column to the left as it defines the empty
      // state for this square.
      if ( _isVertIndexEmpty( t0 - 1 ) )
         continue;

      t1 = t0 + step;
      b0 = skirtStartIdx + i;
      b1 = skirtStartIdx + i + 1;

      idxBuff[0] = b0;
      idxBuff[1] = t0;
      idxBuff[2] = t1;

      idxBuff[3] = b1;
      idxBuff[4] = b0;
      idxBuff[5] = t1;

      idxBuff += 6;
      maxIndex = b1;
      counter += 6;

      mTriCount += 2;
   }

   mPrimBuffer.unlock();
   prim->numPrimitives = mTriCount;
}
Example #2
0
bool ProcessedShaderMaterial::stepInstance()
{
   PROFILE_SCOPE(ProcessedShaderMaterial_stepInstance);
   AssertFatal( mInstancingState, "ProcessedShaderMaterial::stepInstance - This material isn't instanced!" );  
   return mInstancingState->step( &_getShaderConstBuffer( 0 )->mInstPtr );
}
Example #3
0
void ProcessedShaderMaterial::setTextureStages( SceneRenderState *state, const SceneData &sgData, U32 pass )
{
   PROFILE_SCOPE( ProcessedShaderMaterial_SetTextureStages );

   ShaderConstHandles *handles = _getShaderConstHandles(pass);

   // Set all of the textures we need to render the give pass.
#ifdef TORQUE_DEBUG
   AssertFatal( pass<mPasses.size(), "Pass out of bounds" );
#endif

   RenderPassData *rpd = mPasses[pass];
   GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass);
   NamedTexTarget *texTarget;
   GFXTextureObject *texObject; 

   for( U32 i=0; i<rpd->mNumTex; i++ )
   {
      U32 currTexFlag = rpd->mTexType[i];
      if (!LIGHTMGR || !LIGHTMGR->setTextureStage(sgData, currTexFlag, i, shaderConsts, handles))
      {
         switch( currTexFlag )
         {
         // If the flag is unset then assume its just
         // a regular texture to set... nothing special.
         case 0:
         default:
            GFX->setTexture(i, rpd->mTexSlot[i].texObject);
            break;

         case Material::NormalizeCube:
            GFX->setCubeTexture(i, Material::GetNormalizeCube());
            break;

         case Material::Lightmap:
            GFX->setTexture( i, sgData.lightmap );
            break;

         case Material::ToneMapTex:
            shaderConsts->setSafe(handles->mToneMapTexSC, (S32)i);
            GFX->setTexture(i, rpd->mTexSlot[i].texObject);
            break;

         case Material::Cube:
            GFX->setCubeTexture( i, rpd->mCubeMap );
            break;

         case Material::SGCube:
            GFX->setCubeTexture( i, sgData.cubemap );
            break;

         case Material::BackBuff:
            GFX->setTexture( i, sgData.backBuffTex );
            break;
            
         case Material::TexTarget:
            {
               texTarget = rpd->mTexSlot[i].texTarget;
               if ( !texTarget )
               {
                  GFX->setTexture( i, NULL );
                  break;
               }
            
               texObject = texTarget->getTexture();

               // If no texture is available then map the default 2x2
               // black texture to it.  This at least will ensure that
               // we get consistant behavior across GPUs and platforms.
               if ( !texObject )
                  texObject = GFXTexHandle::ZERO;

               if ( handles->mRTParamsSC[i]->isValid() && texObject )
               {
                  const Point3I &targetSz = texObject->getSize();
                  const RectI &targetVp = texTarget->getViewport();
                  Point4F rtParams;

                  ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams);

                  shaderConsts->set(handles->mRTParamsSC[i], rtParams);
               }

               GFX->setTexture( i, texObject );
               break;
            }
         }
      }
   }
}
void TSShapeInstance::animateVisibility(S32 ss)
{
   PROFILE_SCOPE( TSShapeInstance_animateVisibility );

   S32 i;
   if (!mMeshObjects.size())
      return;

   // find out who needs default values set
   TSIntegerSet beenSet;
   beenSet.setAll(mMeshObjects.size());
   for (i=0; i<mThreadList.size(); i++)
      beenSet.takeAway(mThreadList[i]->getSequence()->visMatters);

   // set defaults
   S32 a = mShape->subShapeFirstObject[ss];
   S32 b = a + mShape->subShapeNumObjects[ss];
   for (i=a; i<b; i++)
   {
      if (beenSet.test(i))
         mMeshObjects[i].visible = mShape->objectStates[i].vis;
   }

   // go through each thread and set visibility on those objects that
   // are not set yet and are controlled by that thread
   for (i=0; i<mThreadList.size(); i++)
   {
      TSThread * th = mThreadList[i];

      // For better or worse, object states are stored together (frame,
      // matFrame, visibility all in one structure).  Thus, indexing into
      // object state array for animation for any of these attributes needs to
      // take into account whether or not the other attributes are also animated.
      // The object states should eventually be separated (like the node states were)
      // in order to save memory and save the following step.
      TSIntegerSet objectMatters = th->getSequence()->frameMatters;
      objectMatters.overlap(th->getSequence()->matFrameMatters);
      objectMatters.overlap(th->getSequence()->visMatters);

      // skip to beginning of this sub-shape
      S32 j=0;
      S32 start = objectMatters.start();
      S32 end = b;
      for (S32 objectIndex = start; objectIndex<end; objectMatters.next(objectIndex), j++)
      {
         if (!beenSet.test(objectIndex) && th->getSequence()->visMatters.test(objectIndex))
         {
            F32 state1 = mShape->getObjectState(*th->getSequence(),th->keyNum1,j).vis;
            F32 state2 = mShape->getObjectState(*th->getSequence(),th->keyNum2,j).vis;
            if ((state1-state2) * (state1-state2) > 0.99f)
               // goes from 0 to 1 -- discreet jump
               mMeshObjects[objectIndex].visible = th->keyPos<0.5f ? state1 : state2;
            else
               // interpolate between keyframes when visibility change is gradual
               mMeshObjects[objectIndex].visible = (1.0f-th->keyPos) * state1 + th->keyPos * state2;

            // record change so that later threads don't over-write us...
            beenSet.set(objectIndex);
         }
      }
   }
}
void SceneManager::_renderScene( SceneRenderState* state, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone )
{
   AssertFatal( this == gClientSceneGraph, "SceneManager::_buildSceneGraph - Only the client scenegraph can support this call!" );

   PROFILE_SCOPE( SceneGraph_batchRenderImages );

   // In the editor, override the type mask for diffuse passes.

   if( gEditingMission && state->isDiffusePass() )
      objectMask = EDITOR_RENDER_TYPEMASK;

   // Update the zoning state and traverse zones.

   if( getZoneManager() )
   {
      // Update.

      getZoneManager()->updateZoningState();

      // If zone culling isn't disabled, traverse the
      // zones now.

      if( !state->getCullingState().disableZoneCulling() )
      {
         // Find the start zone if we haven't already.

         if( !baseObject )
         {
            getZoneManager()->findZone( state->getCameraPosition(), baseObject, baseZone );
            AssertFatal( baseObject != NULL, "SceneManager::_renderScene - findZone() did not return an object" );
         }

         // Traverse zones starting in base object.

         SceneTraversalState traversalState( &state->getCullingState() );
         PROFILE_START( Scene_traverseZones );
         baseObject->traverseZones( &traversalState, baseZone );
         PROFILE_END();

         // Set the scene render box to the area we have traversed.

         state->setRenderArea( traversalState.getTraversedArea() );
      }
   }

   // Set the query box for the container query.  Never
   // make it larger than the frustum's AABB.  In the editor,
   // always query the full frustum as that gives objects
   // the opportunity to render editor visualizations even if
   // they are otherwise not in view.

   if( !state->getFrustum().getBounds().isOverlapped( state->getRenderArea() ) )
   {
      // This handles fringe cases like flying backwards into a zone where you
      // end up pretty much standing on a zone border and looking directly into
      // its "walls".  In that case the traversal area will be behind the frustum
      // (remember that the camera isn't where visibility starts, it's the near
      // distance).

      return;
   }

   Box3F queryBox = state->getFrustum().getBounds();
   if( !gEditingMission )
   {
      queryBox.minExtents.setMax( state->getRenderArea().minExtents );
      queryBox.maxExtents.setMin( state->getRenderArea().maxExtents );
   }

   PROFILE_START( Scene_cullObjects );

   //TODO: We should split the codepaths here based on whether the outdoor zone has visible space.
   //    If it has, we should use the container query-based path.
   //    If it hasn't, we should fill the object list directly from the zone lists which will usually
   //       include way fewer objects.
   
   // Gather all objects that intersect the scene render box.

   mBatchQueryList.clear();
   getContainer()->findObjectList( queryBox, objectMask, &mBatchQueryList );

   // Cull the list.

   U32 numRenderObjects = state->getCullingState().cullObjects(
      mBatchQueryList.address(),
      mBatchQueryList.size(),
      !state->isDiffusePass() ? SceneCullingState::CullEditorOverrides : 0 // Keep forced editor stuff out of non-diffuse passes.
   );

   //HACK: If the control object is a Player and it is not in the render list, force
   // it into it.  This really should be solved by collision bounds being separate from
   // object bounds; only because the Player class is using bounds not encompassing
   // the actual player object is it that we have this problem in the first place.
   // Note that we are forcing the player object into ALL passes here but such
   // is the power of proliferation of things done wrong.

   GameConnection* connection = GameConnection::getConnectionToServer();
   if( connection )
   {
      Player* player = dynamic_cast< Player* >( connection->getControlObject() );
      if( player )
      {
         mBatchQueryList.setSize( numRenderObjects );
         if( !mBatchQueryList.contains( player ) )
         {
            mBatchQueryList.push_back( player );
            numRenderObjects ++;
         }
      }
   }

   PROFILE_END();

   // Render the remaining objects.

   PROFILE_START( Scene_renderObjects );
   state->renderObjects( mBatchQueryList.address(), numRenderObjects );
   PROFILE_END();

   // Render bounding boxes, if enabled.

   if( smRenderBoundingBoxes && state->isDiffusePass() )
   {
      GFXDEBUGEVENT_SCOPE( Scene_renderBoundingBoxes, ColorI::WHITE );

      GameBase* cameraObject = 0;
      if( connection )
         cameraObject = connection->getCameraObject();

      GFXStateBlockDesc desc;
      desc.setFillModeWireframe();
      desc.setZReadWrite( true, false );

      for( U32 i = 0; i < numRenderObjects; ++ i )
      {
         SceneObject* object = mBatchQueryList[ i ];

         // Skip global bounds object.
         if( object->isGlobalBounds() )
            continue;

         // Skip camera object as we're viewing the scene from it.
         if( object == cameraObject )
            continue;

         const Box3F& worldBox = object->getWorldBox();
         GFX->getDrawUtil()->drawObjectBox(
            desc,
            Point3F( worldBox.len_x(), worldBox.len_y(), worldBox.len_z() ),
            worldBox.getCenter(),
            MatrixF::Identity,
            ColorI::WHITE
         );
      }
   }
}
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;
}
void TSShapeInstance::sortThreads()
{
   PROFILE_SCOPE( TSShapeInstance_sortThreads );
   dQsort(mThreadList.address(),mThreadList.size(),sizeof(TSThread*),compareThreads);
   dQsort(mTransitionThreads.address(),mTransitionThreads.size(),sizeof(TSThread*),compareThreads);
}
Example #8
0
void LightManager::registerGlobalLights( const Frustum *frustum, bool staticLighting )
{
   PROFILE_SCOPE( LightManager_RegisterGlobalLights );

   // TODO: We need to work this out...
   //
   // 1. Why do we register and unregister lights on every 
   //    render when they don't often change... shouldn't we
   //    just register once and keep them?
   // 
   // 2. If we do culling of lights should this happen as part
   //    of registration or somewhere else?
   //

   // Grab the lights to process.
   Vector<SceneObject*> activeLights;
   const U32 lightMask = LightObjectType;
   
   if ( staticLighting || !frustum )
   {
      // We're processing static lighting or want all the lights
      // in the container registerd...  so no culling.
      getSceneManager()->getContainer()->findObjectList( lightMask, &activeLights );
   }
   else
   {
      // Cull the lights using the frustum.
      getSceneManager()->getContainer()->findObjectList( *frustum, lightMask, &activeLights );

      for (U32 i = 0; i < activeLights.size(); ++i)
      {
         if (!getSceneManager()->mRenderedObjectsList.contains(activeLights[i]))
         {
            activeLights.erase(i);
            --i;
         }
      }

      // Store the culling position for sun placement
      // later... see setSpecialLight.
      mCullPos = frustum->getPosition();

      // HACK: Make sure the control object always gets 
      // processed as lights mounted to it don't change
      // the shape bounds and can often get culled.

      GameConnection *conn = GameConnection::getConnectionToServer();
      if ( conn->getControlObject() )
      {
         GameBase *conObject = conn->getControlObject();
         activeLights.push_back_unique( conObject );
      }
   }

   // Let the lights register themselves.
   for ( U32 i = 0; i < activeLights.size(); i++ )
   {
      ISceneLight *lightInterface = dynamic_cast<ISceneLight*>( activeLights[i] );
      if ( lightInterface )
         lightInterface->submitLights( this, staticLighting );
   }
}
Example #9
0
void LightManager::_update4LightConsts(   const SceneData &sgData,
                                          GFXShaderConstHandle *lightPositionSC,
                                          GFXShaderConstHandle *lightDiffuseSC,
                                          GFXShaderConstHandle *lightAmbientSC,
                                          GFXShaderConstHandle *lightInvRadiusSqSC,
                                          GFXShaderConstHandle *lightSpotDirSC,
                                          GFXShaderConstHandle *lightSpotAngleSC,
										  GFXShaderConstHandle *lightSpotFalloffSC,
                                          GFXShaderConstBuffer *shaderConsts )
{
   PROFILE_SCOPE( LightManager_Update4LightConsts );

   // Skip over gathering lights if we don't have to!
   if (  lightPositionSC->isValid() || 
         lightDiffuseSC->isValid() ||
         lightInvRadiusSqSC->isValid() ||
         lightSpotDirSC->isValid() ||
         lightSpotAngleSC->isValid() ||
		 lightSpotFalloffSC->isValid() )
   {
      PROFILE_SCOPE( LightManager_Update4LightConsts_setLights );

         static AlignedArray<Point4F> lightPositions( 3, sizeof( Point4F ) );
         static AlignedArray<Point4F> lightSpotDirs( 3, sizeof( Point4F ) );
      static AlignedArray<Point4F> lightColors( 4, sizeof( Point4F ) );
      static Point4F lightInvRadiusSq;
      static Point4F lightSpotAngle;
	  static Point4F lightSpotFalloff;
      F32 range;
      
      // Need to clear the buffers so that we don't leak
      // lights from previous passes or have NaNs.
      dMemset( lightPositions.getBuffer(), 0, lightPositions.getBufferSize() );
      dMemset( lightSpotDirs.getBuffer(), 0, lightSpotDirs.getBufferSize() );
      dMemset( lightColors.getBuffer(), 0, lightColors.getBufferSize() );
      lightInvRadiusSq = Point4F::Zero;
      lightSpotAngle.set( -1.0f, -1.0f, -1.0f, -1.0f );
      lightSpotFalloff.set( F32_MAX, F32_MAX, F32_MAX, F32_MAX );

      // Gather the data for the first 4 lights.
      const LightInfo *light;
      for ( U32 i=0; i < 4; i++ )
      {
         light = sgData.lights[i];
         if ( !light )            
            break;

            // The light positions and spot directions are 
            // in SoA order to make optimal use of the GPU.
            const Point3F &lightPos = light->getPosition();
            lightPositions[0][i] = lightPos.x;
            lightPositions[1][i] = lightPos.y;
            lightPositions[2][i] = lightPos.z;

            const VectorF &lightDir = light->getDirection();
            lightSpotDirs[0][i] = lightDir.x;
            lightSpotDirs[1][i] = lightDir.y;
            lightSpotDirs[2][i] = lightDir.z;
            
            if ( light->getType() == LightInfo::Spot )
			{
               lightSpotAngle[i] = mCos( mDegToRad( light->getOuterConeAngle() / 2.0f ) ); 
			   lightSpotFalloff[i] = 1.0f / getMax( F32_MIN, mCos( mDegToRad( light->getInnerConeAngle() / 2.0f ) ) - lightSpotAngle[i] );
			}

         // Prescale the light color by the brightness to 
         // avoid doing this in the shader.
         lightColors[i] = Point4F(light->getColor()) * light->getBrightness();

         // We need 1 over range^2 here.
         range = light->getRange().x;
         lightInvRadiusSq[i] = 1.0f / ( range * range );
      }

      shaderConsts->setSafe( lightPositionSC, lightPositions );   
      shaderConsts->setSafe( lightDiffuseSC, lightColors );
      shaderConsts->setSafe( lightInvRadiusSqSC, lightInvRadiusSq );

         shaderConsts->setSafe( lightSpotDirSC, lightSpotDirs );
         shaderConsts->setSafe( lightSpotAngleSC, lightSpotAngle );
		 shaderConsts->setSafe( lightSpotFalloffSC, lightSpotFalloff );

      
   }

   // Setup the ambient lighting from the first 
   // light which is the directional light if 
   // one exists at all in the scene.
   if ( lightAmbientSC->isValid() )
      shaderConsts->set( lightAmbientSC, sgData.ambientLightColor );
}
Example #10
0
//-----------------------------------------------------------------------------
// render
//-----------------------------------------------------------------------------
void RenderMeshMgr::render(SceneRenderState * state)
{
   PROFILE_SCOPE(RenderMeshMgr_render);

   // Early out if nothing to draw.
   if(!mElementList.size())
      return;


   GFXDEBUGEVENT_SCOPE( RenderMeshMgr_Render, ColorI::GREEN );

   // Automagically save & restore our viewport and transforms.
   GFXTransformSaver saver;

   // Restore transforms
   MatrixSet &matrixSet = getRenderPass()->getMatrixSet();
   matrixSet.restoreSceneViewProjection();

   // init loop data
   GFXTextureObject *lastLM = NULL;
   GFXCubemap *lastCubemap = NULL;
   GFXTextureObject *lastReflectTex = NULL;
   GFXTextureObject *lastMiscTex = NULL;
   GFXTextureObject *lastAccuTex = NULL;

   SceneData sgData;
   sgData.init( state );

   U32 binSize = mElementList.size();

   for( U32 j=0; j<binSize; )
   {
      MeshRenderInst *ri = static_cast<MeshRenderInst*>(mElementList[j].inst);

      setupSGData( ri, sgData );
      BaseMatInstance *mat = ri->matInst;

      // If we have an override delegate then give it a 
      // chance to swap the material with another.
      if ( mMatOverrideDelegate )
      {
         mat = mMatOverrideDelegate( mat );
         if ( !mat )
         {
            j++;
            continue;
         }
      }

      if( !mat )
         mat = MATMGR->getWarningMatInstance();


      U32 matListEnd = j;
      lastMiscTex = sgData.miscTex;
      U32 a;

      while(mat && mat->setupPass(state, sgData ))
      {
         for( a=j; a<binSize; a++ )
         {
            MeshRenderInst *passRI = static_cast<MeshRenderInst*>(mElementList[a].inst);

            // Check to see if we need to break this batch.
            if (  newPassNeeded( ri, passRI ) ||
                  lastMiscTex != passRI->miscTex )
            {
               lastLM = NULL;
               break;
            }

            matrixSet.setWorld(*passRI->objectToWorld);
            matrixSet.setView(*passRI->worldToCamera);
            matrixSet.setProjection(*passRI->projection);
            mat->setTransforms(matrixSet, state);

            setupSGData( passRI, sgData );
            sgData.palette = passRI->palette;
            mat->setSceneInfo( state, sgData );

            // If we're instanced then don't render yet.
            if ( mat->isInstanced() )
            {
               // Let the material increment the instance buffer, but
               // break the batch if it runs out of room for more.
               if ( !mat->stepInstance() )
               {
                  a++;
                  break;
               }

               continue;
            }

            // TODO: This could proably be done in a cleaner way.
            //
            // This section of code is dangerous, it overwrites the
            // lightmap values in sgData.  This could be a problem when multiple
            // render instances use the same multi-pass material.  When
            // the first pass is done, setupPass() is called again on
            // the material, but the lightmap data has been changed in
            // sgData to the lightmaps in the last renderInstance rendered.

            // This section sets the lightmap data for the current batch.
            // For the first iteration, it sets the same lightmap data,
            // however the redundancy will be caught by GFXDevice and not
            // actually sent to the card.  This is done for simplicity given
            // the possible condition mentioned above.  Better to set always
            // than to get bogged down into special case detection.
            //-------------------------------------
            bool dirty = false;

            // set the lightmaps if different
            if( passRI->lightmap && passRI->lightmap != lastLM )
            {
               sgData.lightmap = passRI->lightmap;
               lastLM = passRI->lightmap;
               dirty = true;
            }

            // set the cubemap if different.
            if ( passRI->cubemap != lastCubemap )
            {
               sgData.cubemap = passRI->cubemap;
               lastCubemap = passRI->cubemap;
               dirty = true;
            }

            if ( passRI->reflectTex != lastReflectTex )
            {
               sgData.reflectTex = passRI->reflectTex;
               lastReflectTex = passRI->reflectTex;
               dirty = true;
            }

            // Update accumulation texture if it changed.
            // Note: accumulation texture can be NULL, and must be updated.
            if ( passRI->accuTex != lastAccuTex )
            {
               sgData.accuTex = passRI->accuTex;
               lastAccuTex = lastAccuTex;
               dirty = true;
            }

            if ( dirty )
               mat->setTextureStages( state, sgData );

            // Setup the vertex and index buffers.
            mat->setBuffers( passRI->vertBuff, passRI->primBuff );

            // Render this sucker.
            if ( passRI->prim )
               GFX->drawPrimitive( *passRI->prim );
            else
               GFX->drawPrimitive( passRI->primBuffIndex );
         }

         // Draw the instanced batch.
         if ( mat->isInstanced() )
         {
            // Sets the buffers including the instancing stream.
            mat->setBuffers( ri->vertBuff, ri->primBuff );

            // Render the instanced stream.
            if ( ri->prim )
               GFX->drawPrimitive( *ri->prim );
            else
               GFX->drawPrimitive( ri->primBuffIndex );
         }

         matListEnd = a;
      }

      // force increment if none happened, otherwise go to end of batch
      j = ( j == matListEnd ) ? j+1 : matListEnd;
   }
}
Example #11
0
void Portal::_traverseConnectedZoneSpaces( SceneTraversalState* state )
{
   PROFILE_SCOPE( Portal_traverseConnectedZoneSpaces );

   // Don't traverse out from the portal if it is invalid.

   if( mClassification == InvalidPortal )
      return;

   AssertFatal( !mIsGeometryDirty, "Portal::_traverseConnectedZoneSpaces - Geometry not up-to-date!" );

   // When starting traversal within a portal zone, we cannot really use the portal
   // plane itself to direct our visibility queries.  For example, the camera might
   // actually be located in front of the portal plane and thus cannot actually look
   // through the portal, though it will still see what lies on front of where the
   // portal leads.
   //
   // So if we're the start of the traversal chain, i.e. the traversal has started
   // out in the portal zone, then just put the traversal through to SceneZoneSpace
   // so it can hand it over to all connected zone managers.
   //
   // Otherwise, just do a normal traversal by stepping through the portal.

   if( state->getTraversalDepth() == 1 )
   {
      Parent::_traverseConnectedZoneSpaces( state );
      return;
   }
   
   SceneCullingState* cullingState = state->getCullingState();
   const SceneCameraState& cameraState = cullingState->getCameraState();

   // Get the data of the zone we're coming from.  Note that at this point
   // the portal zone itself is already on top of the traversal stack, so
   // we skip over the bottom-most entry.

   const U32 sourceZoneId = state->getZoneIdFromStack( 1 );
   const SceneZoneSpace* sourceZoneSpace = state->getZoneFromStack( 1 );

   // Find out which side of the portal we are on given the
   // source zone.

   const Portal::Side currentZoneSide =
      sourceZoneId == SceneZoneSpaceManager::RootZoneId
      ? ( getInteriorSideOfExteriorPortal() == FrontSide ? BackSide : FrontSide )
      : getSideRelativeToPortalPlane( sourceZoneSpace->getPosition() );

   // Don't step through portal if the side we're interested in isn't passable.

   if( !isSidePassable( currentZoneSide ) )
      return;

   // If the viewpoint isn't on the same side of the portal as the source zone,
   // then stepping through the portal would mean we are stepping back towards
   // the viewpoint which doesn't make sense; so, skip the portal.

   const Point3F& viewPos = cameraState.getViewPosition();
   const F32 viewPosDistToPortal = mFabs( getPortalPlane().distToPlane( viewPos ) );
   if( !mIsZero( viewPosDistToPortal ) && getSideRelativeToPortalPlane( viewPos ) != currentZoneSide )
      return;

   // Before we go ahead and do the real work, try to find out whether
   // the portal is at a perpendicular or near-perpendicular angle to the view
   // direction.  If so, there's no point in going further since we can't really
   // see much through the portal anyway.  It also prevents us from stepping
   // over front/back side ambiguities.

   Point3F viewDirection = cameraState.getViewDirection();
   const F32 dotProduct = mDot( viewDirection, getPortalPlane() );
   if( mIsZero( dotProduct ) )
      return;

   // Finally, if we have come through a portal to the current zone, check if the target
   // portal we are trying to step through now completely lies on the "backside"--i.e.
   // the side of portal on which our current zone lies--of the source portal.  If so,
   // we can be sure this portal leads us in the wrong direction.  This prevents the
   // outdoor zone from having just arrived through a window on one side of a house just
   // to go round the house and re-enter it from the other side.

   Portal* sourcePortal = state->getTraversalDepth() > 2 ? dynamic_cast< Portal* >( state->getZoneFromStack( 2 ) ) : NULL;
   if( sourcePortal != NULL )
   {
      const Side sourcePortalFrontSide =
         sourceZoneId == SceneZoneSpaceManager::RootZoneId
         ? sourcePortal->getInteriorSideOfExteriorPortal()
         : sourcePortal->getSideRelativeToPortalPlane( sourceZoneSpace->getPosition() );
      const PlaneF::Side sourcePortalPlaneFrontSide =
         sourcePortalFrontSide == FrontSide ? PlaneF::Front : PlaneF::Back;

      bool allPortalVerticesOnBackside = true;
      const U32 numVertices = mPortalPolygonWS.size();
      for( U32 i = 0; i < numVertices; ++ i )
      {
         // Not using getSideRelativeToPortalPlane here since we want PlaneF::On to be
         // counted as backside here.
         if( sourcePortal->mPortalPlane.whichSide( mPortalPolygonWS[ i ] ) == sourcePortalPlaneFrontSide )
         {
            allPortalVerticesOnBackside = false;
            break;
         }
      }

      if( allPortalVerticesOnBackside )
         return;
   }

   // If we come from the outdoor zone, then we don't want to step through any portal
   // where the interior zones are actually on the same side as our camera since that
   // would mean we are stepping into an interior through the backside of a portal.

   if( sourceZoneId == SceneZoneSpaceManager::RootZoneId )
   {
      const Portal::Side cameraSide = getSideRelativeToPortalPlane( viewPos );
      if( cameraSide == getInteriorSideOfExteriorPortal() )
         return;
   }

   // Clip the current culling volume against the portal's polygon.  If the polygon
   // lies completely outside the volume or for some other reason there's no good resulting
   // volume, _generateCullingVolume() will return false and we terminate this portal sequence
   // here.
   //
   // However, don't attempt to clip the portal if we are standing really close to or
   // even below the near distance away from the portal plane.  In that case, trying to
   // clip the portal will only result in trouble so we stick to the original culling volume
   // in that case.

   bool haveClipped = false;
   if( viewPosDistToPortal > ( cameraState.getFrustum().getNearDist() + 0.1f ) )
   {
      SceneCullingVolume volume;
      if( !_generateCullingVolume( state, volume ) )
         return;

      state->pushCullingVolume( volume );
      haveClipped = true;
   }

   // Short-circuit things if we are stepping from an interior zone outside.  In this
   // case we know that the only zone we care about is the outdoor zone so head straight
   // into it.

   if( isExteriorPortal() && sourceZoneId != SceneZoneSpaceManager::RootZoneId )
      getSceneManager()->getZoneManager()->getRootZone()->traverseZones( state );
   else
   {
      // Go through the zones that the portal connects to and
      // traverse into them.

      for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext )
      {
         SceneZoneSpace* targetSpace = ref->mZoneSpace;
         if( targetSpace == sourceZoneSpace )
            continue; // Skip space we originated from.

         // We have handled the case of stepping into the outdoor zone above and
         // by skipping the zone we originated from, we have implicitly handled the
         // case of stepping out of the outdoor zone.  Thus, we should not see the
         // outdoor zone here.  Important as getPosition() is meaningless for it.
         AssertFatal( targetSpace->getZoneRangeStart() != SceneZoneSpaceManager::RootZoneId,
            "Portal::_traverseConnectedZoneSpaces - Outdoor zone must have been handled already" );

         // Skip zones that lie on the same side as the zone
         // we originated from.

         if( getSideRelativeToPortalPlane( targetSpace->getPosition() ) == currentZoneSide )
            continue;

         // Traverse into the space.

         targetSpace->traverseZones( state );
      }
   }

   // If we have pushed our own clipping volume,
   // remove that from the stack now.

   if( haveClipped )
      state->popCullingVolume();
}
Example #12
0
void ConvexShape::Geometry::generate( const Vector< PlaneF > &planes, const Vector< Point3F > &tangents )
{
   PROFILE_SCOPE( Geometry_generate );

   points.clear();
   faces.clear();	

   AssertFatal( planes.size() == tangents.size(), "ConvexShape - incorrect plane/tangent count." );

#ifdef TORQUE_ENABLE_ASSERTS
   for ( S32 i = 0; i < planes.size(); i++ )
   {
      F32 dt = mDot( planes[i], tangents[i] );
      AssertFatal( mIsZero( dt, 0.0001f ), "ConvexShape - non perpendicular input vectors." );
      AssertFatal( planes[i].isUnitLength() && tangents[i].isUnitLength(), "ConvexShape - non unit length input vector." );
   }
#endif

   const U32 planeCount = planes.size();

   Point3F linePt, lineDir;   

   for ( S32 i = 0; i < planeCount; i++ )
   {      
      Vector< MathUtils::Line > collideLines;

      // Find the lines defined by the intersection of this plane with all others.

      for ( S32 j = 0; j < planeCount; j++ )
      {         
         if ( i == j )
            continue;

         if ( planes[i].intersect( planes[j], linePt, lineDir ) )
         {
            collideLines.increment();
            MathUtils::Line &line = collideLines.last();
            line.origin = linePt;
            line.direction = lineDir;   
         }         
      }

      if ( collideLines.empty() )
         continue;

      // Find edges and points defined by the intersection of these lines.
      // As we find them we fill them into our working ConvexShape::Face
      // structure.
      
      Face newFace;

      for ( S32 j = 0; j < collideLines.size(); j++ )
      {
         Vector< Point3F > collidePoints;

         for ( S32 k = 0; k < collideLines.size(); k++ )
         {
            if ( j == k )
               continue;

            MathUtils::LineSegment segment;
            MathUtils::mShortestSegmentBetweenLines( collideLines[j], collideLines[k], &segment );

            F32 dist = ( segment.p0 - segment.p1 ).len();

            if ( dist < 0.0005f )
            {
               S32 l = 0;
               for ( ; l < planeCount; l++ )
               {
                  if ( planes[l].whichSide( segment.p0 ) == PlaneF::Front )
                     break;
               }

               if ( l == planeCount )
                  collidePoints.push_back( segment.p0 );
            }
         }

         //AssertFatal( collidePoints.size() <= 2, "A line can't collide with more than 2 other lines in a convex shape..." );

         if ( collidePoints.size() != 2 )
            continue;

         // Push back collision points into our points vector
         // if they are not duplicates and determine the id
         // index for those points to be used by Edge(s).    

         const Point3F &pnt0 = collidePoints[0];
         const Point3F &pnt1 = collidePoints[1];
         S32 idx0 = -1;
         S32 idx1 = -1;

         for ( S32 k = 0; k < points.size(); k++ )
         {
            if ( pnt0.equal( points[k] ) )
            {
               idx0 = k;
               break;
            }
         }

         for ( S32 k = 0; k < points.size(); k++ )
         {
            if ( pnt1.equal( points[k] ) )
            {
               idx1 = k;
               break;
            }
         }

         if ( idx0 == -1 )
         {
            points.push_back( pnt0 );               
            idx0 = points.size() - 1;
         }

         if ( idx1 == -1 )
         {
            points.push_back( pnt1 );
            idx1 = points.size() - 1;
         }

         // Construct the Face::Edge defined by this collision.

         S32 localIdx0 = newFace.points.push_back_unique( idx0 );
         S32 localIdx1 = newFace.points.push_back_unique( idx1 );

         newFace.edges.increment();
         ConvexShape::Edge &newEdge = newFace.edges.last();
         newEdge.p0 = localIdx0;
         newEdge.p1 = localIdx1;
      }    

      if ( newFace.points.size() < 3 )
         continue;

      //AssertFatal( newFace.points.size() == newFace.edges.size(), "ConvexShape - face point count does not equal edge count." );


		// Fill in some basic Face information.

		newFace.id = i;
		newFace.normal = planes[i];
		newFace.tangent = tangents[i];


		// Make a working array of Point3Fs on this face.

		U32 pntCount = newFace.points.size();		
		Point3F *workPoints = new Point3F[ pntCount ];

		for ( S32 j = 0; j < pntCount; j++ )
			workPoints[j] = points[ newFace.points[j] ];


      // Calculate the average point for calculating winding order.

      Point3F averagePnt = Point3F::Zero;

		for ( S32 j = 0; j < pntCount; j++ )
			averagePnt += workPoints[j];

		averagePnt /= pntCount;		


		// Sort points in correct winding order.

		U32 *vertMap = new U32[pntCount];

      MatrixF quadMat( true );
      quadMat.setPosition( averagePnt );
      quadMat.setColumn( 0, newFace.tangent );
      quadMat.setColumn( 1, mCross( newFace.normal, newFace.tangent ) );
      quadMat.setColumn( 2, newFace.normal );
		quadMat.inverse();

      // Transform working points into quad space 
      // so we can work with them as 2D points.

      for ( S32 j = 0; j < pntCount; j++ )
         quadMat.mulP( workPoints[j] );

		MathUtils::sortQuadWindingOrder( true, workPoints, vertMap, pntCount );

      // Save points in winding order.

      for ( S32 j = 0; j < pntCount; j++ )
         newFace.winding.push_back( vertMap[j] );

      // Calculate the area and centroid of the face.

      newFace.area = 0.0f;
      for ( S32 j = 0; j < pntCount; j++ )
      {
         S32 k = ( j + 1 ) % pntCount;
         const Point3F &p0 = workPoints[ vertMap[j] ];
         const Point3F &p1 = workPoints[ vertMap[k] ];
         
         // Note that this calculation returns positive area for clockwise winding
         // and negative area for counterclockwise winding.
         newFace.area += p0.y * p1.x;
         newFace.area -= p0.x * p1.y;                  
      }

      //AssertFatal( newFace.area > 0.0f, "ConvexShape - face area was not positive." );
      if ( newFace.area > 0.0f )
         newFace.area /= 2.0f;      

      F32 factor;
      F32 cx = 0.0f, cy = 0.0f;
      
      for ( S32 j = 0; j < pntCount; j++ )
      {
         S32 k = ( j + 1 ) % pntCount;
         const Point3F &p0 = workPoints[ vertMap[j] ];
         const Point3F &p1 = workPoints[ vertMap[k] ];

         factor = p0.x * p1.y - p1.x * p0.y;
         cx += ( p0.x + p1.x ) * factor;
         cy += ( p0.y + p1.y ) * factor;
      }
      
      factor = 1.0f / ( newFace.area * 6.0f );
      newFace.centroid.set( cx * factor, cy * factor, 0.0f );
      quadMat.inverse();
      quadMat.mulP( newFace.centroid );

      delete [] workPoints;
      workPoints = NULL;

		// Make polygons / triangles for this face.

		const U32 polyCount = pntCount - 2;

		newFace.triangles.setSize( polyCount );

		for ( S32 j = 0; j < polyCount; j++ )
		{
			ConvexShape::Triangle &poly = newFace.triangles[j];

			poly.p0 = vertMap[0];

			if ( j == 0 )
			{
				poly.p1 = vertMap[ 1 ];
				poly.p2 = vertMap[ 2 ];
			}
			else
			{
				poly.p1 = vertMap[ 1 + j ];
				poly.p2 = vertMap[ 2 + j ];
			}
		}

		delete [] vertMap;


		// Calculate texture coordinates for each point in this face.

		const Point3F binormal = mCross( newFace.normal, newFace.tangent );
		PlaneF planey( newFace.centroid - 0.5f * binormal, binormal );
		PlaneF planex( newFace.centroid - 0.5f * newFace.tangent, newFace.tangent );

		newFace.texcoords.setSize( newFace.points.size() );

		for ( S32 j = 0; j < newFace.points.size(); j++ )
		{
			F32 x = planex.distToPlane( points[ newFace.points[ j ] ] );
			F32 y = planey.distToPlane( points[ newFace.points[ j ] ] );

			newFace.texcoords[j].set( -x, -y );
		}

      // Data verification tests.
#ifdef TORQUE_ENABLE_ASSERTS
      //S32 triCount = newFace.triangles.size();
      //S32 edgeCount = newFace.edges.size();
      //AssertFatal( triCount == edgeCount - 2, "ConvexShape - triangle/edge count do not match." );

      /*
      for ( S32 j = 0; j < triCount; j++ )
      {
         F32 area = MathUtils::mTriangleArea( points[ newFace.points[ newFace.triangles[j][0] ] ], 
                                              points[ newFace.points[ newFace.triangles[j][1] ] ],
                                              points[ newFace.points[ newFace.triangles[j][2] ] ] );
         AssertFatal( area > 0.0f, "ConvexShape - triangle winding bad." );
      }*/
#endif


      // Done with this Face.
      
      faces.push_back( newFace );
   }
}
Example #13
0
bool TerrainCellMaterial::setupPass(   const SceneRenderState *state, 
                                       const SceneData &sceneData )
{
   PROFILE_SCOPE( TerrainCellMaterial_SetupPass );

   if ( mCurrPass >= mPasses.size() )
   {
      mCurrPass = 0;
      return false;
   }

   Pass &pass = mPasses[mCurrPass];

   _updateMaterialConsts( &pass );

   if ( pass.baseTexMapConst->isValid() )
      GFX->setTexture( pass.baseTexMapConst->getSamplerRegister(), mTerrain->mBaseTex.getPointer() );

   if ( pass.layerTexConst->isValid() )
      GFX->setTexture( pass.layerTexConst->getSamplerRegister(), mTerrain->mLayerTex.getPointer() );

   if ( pass.lightMapTexConst->isValid() )
      GFX->setTexture( pass.lightMapTexConst->getSamplerRegister(), mTerrain->getLightMapTex() );

   if ( sceneData.wireframe )
      GFX->setStateBlock( pass.wireframeStateBlock );
   else
      GFX->setStateBlock( pass.stateBlock );

   GFX->setShader( pass.shader );
   GFX->setShaderConstBuffer( pass.consts );

   // Let the light manager prepare any light stuff it needs.
   LIGHTMGR->setLightInfo( NULL,
                           NULL,
                           sceneData,
                           state,
                           mCurrPass,
                           pass.consts );

   for ( U32 i=0; i < pass.materials.size(); i++ )
   {
      MaterialInfo *matInfo = pass.materials[i];

      if ( matInfo->detailTexConst->isValid() )
         GFX->setTexture( matInfo->detailTexConst->getSamplerRegister(), matInfo->detailTex );
      if ( matInfo->macroTexConst->isValid() )
         GFX->setTexture( matInfo->macroTexConst->getSamplerRegister(), matInfo->macroTex );
      if ( matInfo->normalTexConst->isValid() )
         GFX->setTexture( matInfo->normalTexConst->getSamplerRegister(), matInfo->normalTex );
   }

   pass.consts->setSafe( pass.layerSizeConst, (F32)mTerrain->mLayerTex.getWidth() );

   if ( pass.oneOverTerrainSize->isValid() )
   {
      F32 oneOverTerrainSize = 1.0f / mTerrain->getWorldBlockSize();
      pass.consts->set( pass.oneOverTerrainSize, oneOverTerrainSize );
   }

   pass.consts->setSafe( pass.squareSize, mTerrain->getSquareSize() );

   if ( pass.fogDataConst->isValid() )
   {
      Point3F fogData;
      fogData.x = sceneData.fogDensity;
      fogData.y = sceneData.fogDensityOffset;
      fogData.z = sceneData.fogHeightFalloff;     
      pass.consts->set( pass.fogDataConst, fogData );
   }

   pass.consts->setSafe( pass.fogColorConst, sceneData.fogColor );

   if (  pass.lightInfoBufferConst->isValid() &&
         pass.lightParamsConst->isValid() )
   {
      if ( !mLightInfoTarget )
         mLightInfoTarget = NamedTexTarget::find( "lightinfo" );

      GFXTextureObject *texObject = mLightInfoTarget->getTexture();
      
      // TODO: Sometimes during reset of the light manager we get a
      // NULL texture here.  This is corrected on the next frame, but
      // we should still investigate why that happens.
      
      if ( texObject )
      {
         GFX->setTexture( pass.lightInfoBufferConst->getSamplerRegister(), texObject );

         const Point3I &targetSz = texObject->getSize();
         const RectI &targetVp = mLightInfoTarget->getViewport();
         Point4F rtParams;
         ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams);
         pass.consts->setSafe( pass.lightParamsConst, rtParams );
      }
   }

   ++mCurrPass;
   return true;
}
Example #14
0
void TerrainCellMaterial::_updateMaterialConsts( Pass *pass )
{
   PROFILE_SCOPE( TerrainCellMaterial_UpdateMaterialConsts );

   for ( U32 j=0; j < pass->materials.size(); j++ )
   {
      MaterialInfo *matInfo = pass->materials[j];

      F32 detailSize = matInfo->mat->getDetailSize();
      F32 detailScale = 1.0f;
      if ( !mIsZero( detailSize ) )
         detailScale = mTerrain->getWorldBlockSize() / detailSize;

      // Scale the distance by the global scalar.
      const F32 distance = mTerrain->smDetailScale * matInfo->mat->getDetailDistance();

      // NOTE: The negation of the y scale is to make up for 
      // my mistake early in development and passing the wrong
      // y texture coord into the system.
      //
      // This negation fixes detail, normal, and parallax mapping
      // without harming the layer id blending code.
      //
      // Eventually we should rework this to correct this little
      // mistake, but there isn't really a hurry to.
      //
      Point4F detailScaleAndFade(   detailScale,
                                    -detailScale,
                                    distance, 
                                    0 );

      if ( !mIsZero( distance ) )
         detailScaleAndFade.w = 1.0f / distance;

      Point3F detailIdStrengthParallax( matInfo->layerId,
                                        matInfo->mat->getDetailStrength(),
                                        matInfo->mat->getParallaxScale() );

      pass->consts->setSafe( matInfo->detailInfoVConst, detailScaleAndFade );
      pass->consts->setSafe( matInfo->detailInfoPConst, detailIdStrengthParallax );

	// macro texture info

      F32 macroSize = matInfo->mat->getMacroSize();
      F32 macroScale = 1.0f;
      if ( !mIsZero( macroSize ) )
         macroScale = mTerrain->getWorldBlockSize() / macroSize;

      // Scale the distance by the global scalar.
      const F32 macroDistance = mTerrain->smDetailScale * matInfo->mat->getMacroDistance();

      Point4F macroScaleAndFade(   macroScale,
                                    -macroScale,
                                    macroDistance, 
                                    0 );

      if ( !mIsZero( macroDistance ) )
         macroScaleAndFade.w = 1.0f / macroDistance;

      Point3F macroIdStrengthParallax( matInfo->layerId,
                                        matInfo->mat->getMacroStrength(),
                                        0 );

      pass->consts->setSafe( matInfo->macroInfoVConst, macroScaleAndFade );
      pass->consts->setSafe( matInfo->macroInfoPConst, macroIdStrengthParallax );
   }
}
Example #15
0
bool Etherform::updatePos(const F32 travelTime)
{
   PROFILE_SCOPE(Etherform_UpdatePos);
   getTransform().getColumn(3, &delta.posVec);

   // When mounted to another object, only Z rotation used.
   if(isMounted()) 
	{
      mVelocity = mMount.object->getVelocity();
      setPosition(Point3F(0.0f, 0.0f, 0.0f), mRot);
      setMaskBits(MoveMask);
      return true;
   }

   Point3F newPos;

   Collision col;
   dMemset(&col, 0, sizeof(col));

   // DEBUG:
   //Point3F savedVelocity = mVelocity;

   if(mVelocity.isZero())
      newPos = delta.posVec;
   else
      newPos = _move(travelTime, &col);
   
   _handleCollision(col);

   // DEBUG:
   //if ( isClientObject() )
   //   Con::printf( "(client) vel: %g %g %g", mVelocity.x, mVelocity.y, mVelocity.z );
   //else
   //   Con::printf( "(server) vel: %g %g %g", mVelocity.x, mVelocity.y, mVelocity.z );

   // Set new position
	// If on the client, calc delta for backstepping
	if(isClientObject()) 
	{
		delta.pos = newPos;
		delta.rot = mRot;
		delta.posVec = delta.posVec - delta.pos;
		delta.rotVec = delta.rotVec - delta.rot;
		delta.dt = 1.0f;
	}

   this->setPosition(newPos, mRot);
   this->setMaskBits(MoveMask);
   this->updateContainer();

   if (!isGhost())  
   {
      // Collisions are only queued on the server and can be
      // generated by either updateMove or updatePos
      notifyCollision();

      // Do mission area callbacks on the server as well
      //checkMissionArea();
   }

   // Check the total distance moved.  If it is more than 1000th of the velocity, then
   //  we moved a fair amount...
   //if (totalMotion >= (0.001f * initialSpeed))
      return true;
   //else
      //return false;
}
Example #16
0
void FrustumData::_update() const
{
   if( !mDirty )
      return;

   PROFILE_SCOPE( Frustum_update );

   const Point3F& cameraPos = mPosition;

   // Build the frustum points in camera space first.

   if( mIsOrtho )
   {
      mPoints[ NearTopLeft ].set( mNearLeft, mNearDist, mNearTop );
      mPoints[ NearTopRight ].set( mNearRight, mNearDist, mNearTop );
      mPoints[ NearBottomLeft ].set( mNearLeft, mNearDist, mNearBottom );
      mPoints[ NearBottomRight ].set( mNearRight, mNearDist, mNearBottom );
      mPoints[ FarTopLeft ].set( mNearLeft, mFarDist, mNearTop );
      mPoints[ FarTopRight ].set( mNearRight, mFarDist, mNearTop );
      mPoints[ FarBottomLeft ].set( mNearLeft, mFarDist, mNearBottom );
      mPoints[ FarBottomRight ].set( mNearRight, mFarDist, mNearBottom );
   }
   else
   {
      const F32 farOverNear = mFarDist / mNearDist;

      mPoints[ NearTopLeft ].set( mNearLeft, mNearDist, mNearTop );
      mPoints[ NearTopRight ].set( mNearRight, mNearDist, mNearTop );
      mPoints[ NearBottomLeft ].set( mNearLeft, mNearDist, mNearBottom );
      mPoints[ NearBottomRight ].set( mNearRight, mNearDist, mNearBottom );
      mPoints[ FarTopLeft ].set( mNearLeft * farOverNear, mFarDist, mNearTop * farOverNear );
      mPoints[ FarTopRight ].set( mNearRight * farOverNear, mFarDist, mNearTop * farOverNear );
      mPoints[ FarBottomLeft ].set( mNearLeft * farOverNear, mFarDist, mNearBottom * farOverNear );
      mPoints[ FarBottomRight ].set( mNearRight * farOverNear, mFarDist, mNearBottom * farOverNear );
   }

   // Transform the points into the desired culling space.

   for( U32 i = 0; i < mPoints.size(); ++ i )
      mTransform.mulP( mPoints[ i ] );

   // Update the axis aligned bounding box from 
   // the newly transformed points.

   mBounds = Box3F::aroundPoints( mPoints.address(), mPoints.size() );

   // Finally build the planes.

   if( mIsOrtho )
   {
      mPlanes[ PlaneLeft ].set(   mPoints[ NearBottomLeft ], 
                                  mPoints[ FarTopLeft ], 
                                  mPoints[ FarBottomLeft ] );

      mPlanes[ PlaneRight ].set(  mPoints[ NearTopRight ], 
                                  mPoints[ FarBottomRight ], 
                                  mPoints[ FarTopRight ] );

      mPlanes[ PlaneTop ].set(    mPoints[ FarTopRight ], 
                                  mPoints[ NearTopLeft ], 
                                  mPoints[ NearTopRight ] );

      mPlanes[ PlaneBottom ].set( mPoints[ NearBottomRight ], 
                                  mPoints[ FarBottomLeft ], 
                                  mPoints[ FarBottomRight ] );

      mPlanes[ PlaneNear ].set(   mPoints[ NearTopLeft ], 
                                  mPoints[ NearBottomLeft ], 
                                  mPoints[ NearTopRight ] );

      mPlanes[ PlaneFar ].set(    mPoints[ FarTopLeft ], 
                                  mPoints[ FarTopRight ], 
                                  mPoints[ FarBottomLeft ] );
   }
   else
   {
      mPlanes[ PlaneLeft ].set(   cameraPos,
                                  mPoints[ NearTopLeft ], 
                                  mPoints[ NearBottomLeft ] );

      mPlanes[ PlaneRight ].set(  cameraPos,
                                  mPoints[ NearBottomRight ], 
                                  mPoints[ NearTopRight ] );

      mPlanes[ PlaneTop ].set(    cameraPos,
                                  mPoints[ NearTopRight ], 
                                  mPoints[ NearTopLeft ] );

      mPlanes[ PlaneBottom ].set( cameraPos,
                                  mPoints[ NearBottomLeft ], 
                                  mPoints[ NearBottomRight ] );

      mPlanes[ PlaneNear ].set(   mPoints[ NearTopLeft ],
                                  mPoints[ NearBottomLeft ], 
                                  mPoints[ NearTopRight ] );

      mPlanes[ PlaneFar ].set(    mPoints[ FarTopLeft ],
                                  mPoints[ FarTopRight ], 
                                  mPoints[ FarBottomLeft ] );
   }

   // If the frustum plane orientation doesn't match mIsInverted
   // now, invert all the plane normals.
   //
   // Note that if we have a transform matrix with a negative scale,
   // then the initial planes we have computed will always be inverted.

   const bool inverted = mPlanes[ PlaneNear ].whichSide( cameraPos ) == PlaneF::Front;
   if( inverted != mIsInverted )
   {
      for( U32 i = 0; i < mPlanes.size(); ++ i )
         mPlanes[ i ].invert();
   }

   AssertFatal( mPlanes[ PlaneNear ].whichSide( cameraPos ) != PlaneF::Front,
      "Frustum::_update - Viewpoint lies on front side of near plane!" );

   // And now the center points which are mostly just used in debug rendering.

   mPlaneCenters[ PlaneLeftCenter ] = (   mPoints[ NearTopLeft ] + 
                                          mPoints[ NearBottomLeft ] + 
                                          mPoints[ FarTopLeft ] + 
                                          mPoints[ FarBottomLeft ] ) / 4.0f;

   mPlaneCenters[ PlaneRightCenter ] = (  mPoints[ NearTopRight ] + 
                                          mPoints[ NearBottomRight ] + 
                                          mPoints[ FarTopRight ] + 
                                          mPoints[ FarBottomRight ] ) / 4.0f;

   mPlaneCenters[ PlaneTopCenter ] = ( mPoints[ NearTopLeft ] + 
                                       mPoints[ NearTopRight ] + 
                                       mPoints[ FarTopLeft ] + 
                                       mPoints[ FarTopRight ] ) / 4.0f;

   mPlaneCenters[ PlaneBottomCenter ] = ( mPoints[ NearBottomLeft ] + 
                                          mPoints[ NearBottomRight ] + 
                                          mPoints[ FarBottomLeft ] + 
                                          mPoints[ FarBottomRight ] ) / 4.0f;

   mPlaneCenters[ PlaneNearCenter ] = (   mPoints[ NearTopLeft ] + 
                                          mPoints[ NearTopRight ] + 
                                          mPoints[ NearBottomLeft ] + 
                                          mPoints[ NearBottomRight ] ) / 4.0f;

   mPlaneCenters[ PlaneFarCenter ] = ( mPoints[ FarTopLeft ] + 
                                       mPoints[ FarTopRight ] + 
                                       mPoints[ FarBottomLeft ] + 
                                       mPoints[ FarBottomRight ] ) / 4.0f;

   // Done.

   mDirty = false;
}
Example #17
0
void ScatterSky::_getColor( const Point3F &pos, ColorF *outColor )
{
   PROFILE_SCOPE( ScatterSky_GetColor );

   F32 scaleOverScaleDepth = mScale / mRayleighScaleDepth;
   F32 rayleighBrightness = mRayleighScattering * mSkyBrightness;
   F32 mieBrightness = mMieScattering * mSkyBrightness;

   Point3F invWaveLength(  1.0f / mWavelength4[0],
                           1.0f / mWavelength4[1],
                           1.0f / mWavelength4[2] );

   Point3F v3Pos = pos / 6378000.0f;
   v3Pos.z += mSphereInnerRadius;

   Point3F newCamPos( 0, 0, smViewerHeight );

   VectorF v3Ray = v3Pos - newCamPos;
   F32 fFar = v3Ray.len();
   v3Ray / fFar;
   v3Ray.normalizeSafe();

   Point3F v3Start = newCamPos;
   F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight ) );
   F32 fStartAngle = mDot( v3Ray, v3Start );

   F32 fStartOffset = fDepth * _vernierScale( fStartAngle );

   F32 fSampleLength = fFar / 2.0f;
   F32 fScaledLength = fSampleLength * mScale;
   VectorF v3SampleRay = v3Ray * fSampleLength;
   Point3F v3SamplePoint = v3Start + v3SampleRay * 0.5f;

   Point3F v3FrontColor( 0, 0, 0 );
   for ( U32 i = 0; i < 2; i++ )
   {
      F32 fHeight = v3SamplePoint.len();
      F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight) );
      F32 fLightAngle = mDot( mLightDir, v3SamplePoint ) / fHeight;
      F32 fCameraAngle = mDot( v3Ray, v3SamplePoint ) / fHeight;

      F32 fScatter = (fStartOffset + fDepth * ( _vernierScale( fLightAngle ) - _vernierScale( fCameraAngle ) ));
      Point3F v3Attenuate( 0, 0, 0 );

      F32 tmp = mExp( -fScatter * (invWaveLength[0] * mRayleighScattering4PI + mMieScattering4PI) );
      v3Attenuate.x = tmp;

      tmp = mExp( -fScatter * (invWaveLength[1] * mRayleighScattering4PI + mMieScattering4PI) );
      v3Attenuate.y = tmp;

      tmp = mExp( -fScatter * (invWaveLength[2] * mRayleighScattering4PI + mMieScattering4PI) );
      v3Attenuate.z = tmp;

      v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
      v3SamplePoint += v3SampleRay;
   }

   Point3F mieColor = v3FrontColor * mieBrightness;
   Point3F rayleighColor = v3FrontColor * (invWaveLength * rayleighBrightness);
   Point3F v3Direction = newCamPos - v3Pos;
   v3Direction.normalize();

   F32 fCos = mDot( mLightDir, v3Direction ) / v3Direction.len();
   F32 fCos2 = fCos * fCos;

   F32 g = -0.991f;
   F32 g2 = g * g;
   F32 miePhase = _getMiePhase( fCos, fCos2, g, g2 );

   Point3F color = rayleighColor + (miePhase * mieColor);
   ColorF tmp( color.x, color.y, color.z, color.y );

   Point3F expColor( 0, 0, 0 );
   expColor.x = 1.0f - exp(-mExposure * color.x);
   expColor.y = 1.0f - exp(-mExposure * color.y);
   expColor.z = 1.0f - exp(-mExposure * color.z);

   tmp.set( expColor.x, expColor.y, expColor.z, 1.0f );

   if ( !tmp.isValidColor() )
   {
      F32 len = expColor.len();
      if ( len > 0 )
         expColor /= len;
   }

   outColor->set( expColor.x, expColor.y, expColor.z, 1.0f );
}
void ClippedPolyList::end()
{
   PROFILE_SCOPE( ClippedPolyList_Clip );

   Poly& poly = mPolyList.last();
   
   // Reject polygons facing away from our normal.   
   if ( mDot( poly.plane, mNormal ) < mNormalTolCosineRadians )
   {
      mIndexList.setSize(poly.vertexStart);
      mPolyList.decrement();
      return;
   }

   // Build initial inside/outside plane masks
   U32 indexStart = poly.vertexStart;
   U32 vertexCount = mIndexList.size() - indexStart;

   U32 frontMask = 0,backMask = 0;
   U32 i;
   for (i = indexStart; i < mIndexList.size(); i++) 
   {
      U32 mask = mVertexList[mIndexList[i]].mask;
      frontMask |= mask;
      backMask |= ~mask;
   }

   // Trivial accept if all the vertices are on the backsides of
   // all the planes.
   if (!frontMask) 
   {
      poly.vertexCount = vertexCount;
      return;
   }

   // Trivial reject if any plane not crossed has all it's points
   // on the front.
   U32 crossMask = frontMask & backMask;
   if (~crossMask & frontMask) 
   {
      mIndexList.setSize(poly.vertexStart);
      mPolyList.decrement();
      return;
   }

   // Potentially, this will add up to mPlaneList.size() * (indexStart - indexEnd) 
   // elements to mIndexList, so ensure that it has enough space to store that
   // so we can use push_back_noresize. If you find this code block getting hit
   // frequently, changing the value of 'IndexListReserveSize' or doing some selective
   // allocation is suggested
   //
   // TODO: Re-visit this, since it obviously does not work correctly, and than
   // re-enable the push_back_noresize
   //while(mIndexList.size() + mPlaneList.size() * (mIndexList.size() - indexStart) > mIndexList.capacity() )
   //   mIndexList.reserve(mIndexList.capacity() * 2);

   // Need to do some clipping
   for (U32 p = 0; p < mPlaneList.size(); p++) 
   {
      U32 pmask = 1 << p;

      // Only test against this plane if we have something
      // on both sides
      if (!(crossMask & pmask))
         continue;

      U32 indexEnd = mIndexList.size();
      U32 i1 = indexEnd - 1;
      U32 mask1 = mVertexList[mIndexList[i1]].mask;

      for (U32 i2 = indexStart; i2 < indexEnd; i2++) 
      {
         U32 mask2 = mVertexList[mIndexList[i2]].mask;
         if ((mask1 ^ mask2) & pmask) 
         {
            //
            mVertexList.increment();
            VectorF& v1 = mVertexList[mIndexList[i1]].point;
            VectorF& v2 = mVertexList[mIndexList[i2]].point;
            VectorF vv = v2 - v1;
            F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);

            mNormalList.increment();
            VectorF& n1 = mNormalList[mIndexList[i1]];
            VectorF& n2 = mNormalList[mIndexList[i1]];
            VectorF nn = mLerp( n1, n2, t );
            nn.normalizeSafe();
            mNormalList.last() = nn;

            mIndexList.push_back/*_noresize*/(mVertexList.size() - 1);
            Vertex& iv = mVertexList.last();
            iv.point.x = v1.x + vv.x * t;
            iv.point.y = v1.y + vv.y * t;
            iv.point.z = v1.z + vv.z * t;
            iv.mask = 0;

            // Test against the remaining planes
            for (U32 i = p + 1; i < mPlaneList.size(); i++)
               if (mPlaneList[i].distToPlane(iv.point) > 0) 
               {
                  iv.mask = 1 << i;
                  break;
               }
         }

         if (!(mask2 & pmask)) 
         {
            U32 index = mIndexList[i2];
            mIndexList.push_back/*_noresize*/(index);
         }

         mask1 = mask2;
         i1 = i2;
      }

      // Check for degenerate
      indexStart = indexEnd;
      if (mIndexList.size() - indexStart < 3) 
      {
         mIndexList.setSize(poly.vertexStart);
         mPolyList.decrement();
         return;
      }
   }

   // Emit what's left and compress the index list.
   poly.vertexCount = mIndexList.size() - indexStart;
   memcpy(&mIndexList[poly.vertexStart],
      &mIndexList[indexStart],poly.vertexCount);
   mIndexList.setSize(poly.vertexStart + poly.vertexCount);
}
void ProjectedShadow::_renderToTexture( F32 camDist, const TSRenderState &rdata )
{
    PROFILE_SCOPE( ProjectedShadow_RenderToTexture );

    GFXDEBUGEVENT_SCOPE( ProjectedShadow_RenderToTexture, ColorI( 255, 0, 0 ) );

    RenderPassManager *renderPass = _getRenderPass();
    if ( !renderPass )
        return;

    GFXTransformSaver saver;

    // NOTE: GFXTransformSaver does not save/restore the frustum
    // so we must save it here before we modify it.
    F32 l, r, b, t, n, f;
    bool ortho;
    GFX->getFrustum( &l, &r, &b, &t, &n, &f, &ortho );

    // Set the orthographic projection
    // matrix up, to be based on the radius
    // generated based on our shape.
    GFX->setOrtho( -mRadius, mRadius, -mRadius, mRadius, 0.001f, (mRadius * 2) * smDepthAdjust, true );

    // Set the world to light space
    // matrix set up in shouldRender().
    GFX->setWorldMatrix( mWorldToLight );

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

    // Init or update the shadow texture size.
    if ( mShadowTexture.isNull() || ( data && data->shadowSize != mShadowTexture.getWidth() ) )
    {
        U32 texSize = getNextPow2( data ? data->shadowSize : 256 * LightShadowMap::smShadowTexScalar );
        mShadowTexture.set( texSize, texSize, GFXFormatR8G8B8A8, &PostFxTargetProfile, "BLShadow" );
    }

    GFX->pushActiveRenderTarget();

    if ( !mRenderTarget )
        mRenderTarget = GFX->allocRenderToTextureTarget();

    mRenderTarget->attachTexture( GFXTextureTarget::DepthStencil, _getDepthTarget( mShadowTexture->getWidth(), mShadowTexture->getHeight() ) );
    mRenderTarget->attachTexture( GFXTextureTarget::Color0, mShadowTexture );
    GFX->setActiveRenderTarget( mRenderTarget );

    GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0, 0 ), 1.0f, 0 );

    const SceneRenderState *diffuseState = rdata.getSceneState();
    SceneManager *sceneManager = diffuseState->getSceneManager();

    SceneRenderState baseState
    (
        sceneManager,
        SPT_Shadow,
        SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
        renderPass
    );

    baseState.getMaterialDelegate().bind( &ProjectedShadow::_getShadowMaterial );
    baseState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
    baseState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );
    baseState.getCullingState().disableZoneCulling( true );

    mParentObject->prepRenderImage( &baseState );
    renderPass->renderPass( &baseState );

    // Delete the SceneRenderState we allocated.
    mRenderTarget->resolve();
    GFX->popActiveRenderTarget();

    // If we're close enough then filter the shadow.
    if ( camDist < BasicLightManager::getShadowFilterDistance() )
    {
        if ( !smShadowFilter )
        {
            PostEffect *filter = NULL;

            if ( !Sim::findObject( "BL_ShadowFilterPostFx", filter ) )
                Con::errorf( "ProjectedShadow::_renderToTexture() - 'BL_ShadowFilterPostFx' not found!" );

            smShadowFilter = filter;
        }

        if ( smShadowFilter )
            smShadowFilter->process( NULL, mShadowTexture );
    }

    // Restore frustum
    if (!ortho)
        GFX->setFrustum(l, r, b, t, n, f);
    else
        GFX->setOrtho(l, r, b, t, n, f);

    // Set the last render time.
    mLastRenderTime = Platform::getVirtualMilliseconds();

    // HACK: Will remove in future release!
    mDecalInstance->mCustomTex = &mShadowTexture;
}
void ClippedPolyList::cullUnusedVerts()
{
   PROFILE_SCOPE( ClippedPolyList_CullUnusedVerts );

   U32 i = 0;
   U32 k, n, numDeleted;
   bool result;

   IndexListIterator iNextIter;
   VertexListIterator nextVIter;
   VertexListIterator vIter;

   for ( vIter = mVertexList.begin(); vIter != mVertexList.end(); vIter++, i++ )
   {
      // Is this vertex used?
      iNextIter = find( mIndexList.begin(), mIndexList.end(), i );
      if ( iNextIter != mIndexList.end() )
         continue;

      // If not, find the next used vertex.

      // i is an unused vertex
      // k is a used vertex
      // delete the vertices from i to j - 1
      k = 0;
      n = i + 1;
      result = false;
      numDeleted = 0;

      for ( nextVIter = vIter + 1; nextVIter != mVertexList.end(); nextVIter++, n++ )
      {
         iNextIter = find( mIndexList.begin(), mIndexList.end(), n );
         
         // If we found a used vertex
         // grab its index for later use
         // and set our result bool.
         if ( (*iNextIter) == n )
         {
            k = n;
            result = true;
            break;
         }
      }

      // All the remaining verts are unused.
      if ( !result )
      {
         mVertexList.setSize( i );
         mNormalList.setSize( i );
         break;
      }

      // Erase unused verts.
      numDeleted = (k-1) - i + 1;       
      mVertexList.erase( i, numDeleted );
      mNormalList.erase( i, numDeleted );

      // Find any references to vertices after those deleted
      // in the mIndexList and correct with an offset
      for ( iNextIter = mIndexList.begin(); iNextIter != mIndexList.end(); iNextIter++ )
      {
         if ( (*iNextIter) > i )
             (*iNextIter) -= numDeleted;
      }

      // After the erase the current iter should
      // point at the used vertex we found... the
      // loop will continue with the next vert.
   }
}
void TSShapeInstance::animateNodes(S32 ss)
{
   PROFILE_SCOPE( TSShapeInstance_animateNodes );

   if (!mShape->nodes.size())
      return;

   // @todo: When a node is added, we need to make sure to resize the nodeTransforms array as well
   mNodeTransforms.setSize(mShape->nodes.size());

   // temporary storage for node transforms
   smNodeCurrentRotations.setSize(mShape->nodes.size());
   smNodeCurrentTranslations.setSize(mShape->nodes.size());
   smNodeLocalTransforms.setSize(mShape->nodes.size());
   smRotationThreads.setSize(mShape->nodes.size());
   smTranslationThreads.setSize(mShape->nodes.size());

   TSIntegerSet rotBeenSet;
   TSIntegerSet tranBeenSet;
   TSIntegerSet scaleBeenSet;
   rotBeenSet.setAll(mShape->nodes.size());
   tranBeenSet.setAll(mShape->nodes.size());
   scaleBeenSet.setAll(mShape->nodes.size());
   smNodeLocalTransformDirty.clearAll();

   S32 i,j,nodeIndex,a,b,start,end,firstBlend = mThreadList.size();
   for (i=0; i<mThreadList.size(); i++)
   {
      TSThread * th = mThreadList[i];

      if (th->getSequence()->isBlend())
      {
         // blend sequences need default (if not set by other sequence)
         // break rather than continue because the rest will be blends too
         firstBlend = i;
         break;
      }
      rotBeenSet.takeAway(th->getSequence()->rotationMatters);
      tranBeenSet.takeAway(th->getSequence()->translationMatters);
      scaleBeenSet.takeAway(th->getSequence()->scaleMatters);
   }
   rotBeenSet.takeAway(mCallbackNodes);
   rotBeenSet.takeAway(mHandsOffNodes);
   rotBeenSet.overlap(mMaskRotationNodes);

   TSIntegerSet maskPosNodes=mMaskPosXNodes;
   maskPosNodes.overlap(mMaskPosYNodes);
   maskPosNodes.overlap(mMaskPosZNodes);
   tranBeenSet.overlap(maskPosNodes);

   tranBeenSet.takeAway(mCallbackNodes);
   tranBeenSet.takeAway(mHandsOffNodes);
   // can't add masked nodes since x, y, & z masked separately...
   // we'll set default regardless of mask status

   // all the nodes marked above need to have the default transform
   a = mShape->subShapeFirstNode[ss];
   b = a + mShape->subShapeNumNodes[ss];
   for (i=a; i<b; i++)
   {
      if (rotBeenSet.test(i))
      {
         mShape->defaultRotations[i].getQuatF(&smNodeCurrentRotations[i]);
         smRotationThreads[i] = NULL;
      }
      if (tranBeenSet.test(i))
      {
         smNodeCurrentTranslations[i] = mShape->defaultTranslations[i];
         smTranslationThreads[i] = NULL;
      }
   }

   // don't want a transform in these cases...
   rotBeenSet.overlap(mHandsOffNodes);
   rotBeenSet.overlap(mCallbackNodes);
   tranBeenSet.takeAway(maskPosNodes);
   tranBeenSet.overlap(mHandsOffNodes);
   tranBeenSet.overlap(mCallbackNodes);

   // default scale
   if (scaleCurrentlyAnimated())
      handleDefaultScale(a,b,scaleBeenSet);

   // handle non-blend sequences
   for (i=0; i<firstBlend; i++)
   {
      TSThread * th = mThreadList[i];

      j=0;
      start = th->getSequence()->rotationMatters.start();
      end   = b;
      for (nodeIndex=start; nodeIndex<end; th->getSequence()->rotationMatters.next(nodeIndex), j++)
      {
         // skip nodes outside of this detail
         if (nodeIndex<a)
            continue;
         if (!rotBeenSet.test(nodeIndex))
         {
            QuatF q1,q2;
            mShape->getRotation(*th->getSequence(),th->keyNum1,j,&q1);
            mShape->getRotation(*th->getSequence(),th->keyNum2,j,&q2);
            TSTransform::interpolate(q1,q2,th->keyPos,&smNodeCurrentRotations[nodeIndex]);
            rotBeenSet.set(nodeIndex);
            smRotationThreads[nodeIndex] = th;
         }
      }

      j=0;
      start = th->getSequence()->translationMatters.start();
      end   = b;
      for (nodeIndex=start; nodeIndex<end; th->getSequence()->translationMatters.next(nodeIndex), j++)
      {
         if (nodeIndex<a)
            continue;
         if (!tranBeenSet.test(nodeIndex))
         {
            if (maskPosNodes.test(nodeIndex))
               handleMaskedPositionNode(th,nodeIndex,j);
            else
            {
               const Point3F & p1 = mShape->getTranslation(*th->getSequence(),th->keyNum1,j);
               const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,j);
               TSTransform::interpolate(p1,p2,th->keyPos,&smNodeCurrentTranslations[nodeIndex]);
               smTranslationThreads[nodeIndex] = th;
            }
            tranBeenSet.set(nodeIndex);
         }
      }

      if (scaleCurrentlyAnimated())
         handleAnimatedScale(th,a,b,scaleBeenSet);
   }

   // compute transforms
   for (i=a; i<b; i++)
   {
      if (!mHandsOffNodes.test(i))
         TSTransform::setMatrix(smNodeCurrentRotations[i],smNodeCurrentTranslations[i],&smNodeLocalTransforms[i]);
      else
         smNodeLocalTransforms[i] = mNodeTransforms[i];     // in case mNodeTransform was changed externally
   }

   // add scale onto transforms
   if (scaleCurrentlyAnimated())
      handleNodeScale(a,b);

   // get callbacks...
   start = getMax(mCallbackNodes.start(),a);
   end = getMin(mCallbackNodes.end(),b);
   for (i=0; i<mNodeCallbacks.size(); i++)
   {
      AssertFatal(mNodeCallbacks[i].callback, "No callback method defined");
      S32 nodeIndex = mNodeCallbacks[i].nodeIndex;
      if (nodeIndex>=start && nodeIndex<end)
      {
         mNodeCallbacks[i].callback->setNodeTransform(this, nodeIndex, smNodeLocalTransforms[nodeIndex]);
         smNodeLocalTransformDirty.set(nodeIndex);
      }
   }

   // handle blend sequences
   for (i=firstBlend; i<mThreadList.size(); i++)
   {
      TSThread * th = mThreadList[i];
      if (th->blendDisabled)
         continue;

      handleBlendSequence(th,a,b);
   }

   // transitions...
   if (inTransition())
      handleTransitionNodes(a,b);

   // multiply transforms...
   for (i=a; i<b; i++)
   {
      S32 parentIdx = mShape->nodes[i].parentIndex;
      if (parentIdx < 0)
         mNodeTransforms[i] = smNodeLocalTransforms[i];
      else
         mNodeTransforms[i].mul(mNodeTransforms[parentIdx],smNodeLocalTransforms[i]);
   }
}
void SingleLightShadowMap::_render( RenderPassManager* renderPass,
                                    const SceneRenderState *diffuseState )
{
   PROFILE_SCOPE(SingleLightShadowMap_render);

   const LightMapParams *lmParams = mLight->getExtended<LightMapParams>();
   const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true;

   const U32 texSize = getBestTexSize();

   if (  mShadowMapTex.isNull() ||
         mTexSize != texSize )
   {
      mTexSize = texSize;

      mShadowMapTex.set(   mTexSize, mTexSize, 
                           ShadowMapFormat, &ShadowMapProfile, 
                           "SingleLightShadowMap" );
   }

   GFXFrustumSaver frustSaver;
   GFXTransformSaver saver;

   MatrixF lightMatrix;
   calcLightMatrices( lightMatrix, diffuseState->getCameraFrustum() );
   lightMatrix.inverse();
   GFX->setWorldMatrix(lightMatrix);

   const MatrixF& lightProj = GFX->getProjectionMatrix();
   mWorldToLightProj = lightProj * lightMatrix;

   // Render the shadowmap!
   GFX->pushActiveRenderTarget();
   mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex );
   mTarget->attachTexture( GFXTextureTarget::DepthStencil, 
      _getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) );
   GFX->setActiveRenderTarget(mTarget);
   GFX->clear(GFXClearStencil | GFXClearZBuffer | GFXClearTarget, ColorI(255,255,255), 1.0f, 0);

   SceneManager* sceneManager = diffuseState->getSceneManager();
   
   SceneRenderState shadowRenderState
   (
      sceneManager,
      SPT_Shadow,
      SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ),
      renderPass
   );

   shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial );
   shadowRenderState.renderNonLightmappedMeshes( true );
   shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry );
   shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() );
   shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() );

   sceneManager->renderSceneNoLights( &shadowRenderState, SHADOW_TYPEMASK );

   _debugRender( &shadowRenderState );

   mTarget->resolve();
   GFX->popActiveRenderTarget();
}
void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone )
{
   PROFILE_SCOPE( SceneGraph_renderScene );

   // Get the lights for rendering the scene.

   PROFILE_START( SceneGraph_registerLights );
      LIGHTMGR->registerGlobalLights( &renderState->getFrustum(), false );
   PROFILE_END();

   // If its a diffuse pass, update the current ambient light level.
   // To do that find the starting zone and determine whether it has a custom
   // ambient light color.  If so, pass it on to the ambient light manager.
   // If not, use the ambient light color of the sunlight.
   //
   // Note that we retain the starting zone information here and pass it
   // on to renderSceneNoLights so that we don't need to look it up twice.

   if( renderState->isDiffusePass() )
   {
      if( !baseObject && getZoneManager() )
      {
         getZoneManager()->findZone( renderState->getCameraPosition(), baseObject, baseZone );
         AssertFatal( baseObject != NULL, "SceneManager::renderScene - findZone() did not return an object" );
      }

      ColorF zoneAmbient;
      if( baseObject && baseObject->getZoneAmbientLightColor( baseZone, zoneAmbient ) )
         mAmbientLightColor.setTargetValue( zoneAmbient );
      else
      {
         const LightInfo* sunlight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType );
         if( sunlight )
            mAmbientLightColor.setTargetValue( sunlight->getAmbient() );
      }

      renderState->setAmbientLightColor( mAmbientLightColor.getCurrentValue() );
   }

   // Trigger the pre-render signal.

   PROFILE_START( SceneGraph_preRenderSignal);
      mCurrentRenderState = renderState;
      getPreRenderSignal().trigger( this, renderState );
      mCurrentRenderState = NULL;
   PROFILE_END();

   // Render the scene.

   if(GFX->getCurrentRenderStyle() == GFXDevice::RS_StereoSideBySide)
   {
      // Store previous values
      RectI originalVP = GFX->getViewport();
      MatrixF originalWorld = GFX->getWorldMatrix();

      Point2F projOffset = GFX->getCurrentProjectionOffset();
      Point3F eyeOffset = GFX->getStereoEyeOffset();

      // Render left half of display
      RectI leftVP = originalVP;
      leftVP.extent.x *= 0.5;
      GFX->setViewport(leftVP);

      MatrixF leftWorldTrans(true);
      leftWorldTrans.setPosition(Point3F(eyeOffset.x, eyeOffset.y, eyeOffset.z));
      MatrixF leftWorld(originalWorld);
      leftWorld.mulL(leftWorldTrans);
      GFX->setWorldMatrix(leftWorld);

      Frustum gfxFrustum = GFX->getFrustum();
      gfxFrustum.setProjectionOffset(Point2F(projOffset.x, projOffset.y));
      GFX->setFrustum(gfxFrustum);

      SceneCameraState cameraStateLeft = SceneCameraState::fromGFX();
      SceneRenderState renderStateLeft( this, renderState->getScenePassType(), cameraStateLeft );
      renderStateLeft.setSceneRenderStyle(SRS_SideBySide);
      renderStateLeft.setSceneRenderField(0);

      renderSceneNoLights( &renderStateLeft, objectMask, baseObject, baseZone );

      // Render right half of display
      RectI rightVP = originalVP;
      rightVP.extent.x *= 0.5;
      rightVP.point.x += rightVP.extent.x;
      GFX->setViewport(rightVP);

      MatrixF rightWorldTrans(true);
      rightWorldTrans.setPosition(Point3F(-eyeOffset.x, eyeOffset.y, eyeOffset.z));
      MatrixF rightWorld(originalWorld);
      rightWorld.mulL(rightWorldTrans);
      GFX->setWorldMatrix(rightWorld);

      gfxFrustum = GFX->getFrustum();
      gfxFrustum.setProjectionOffset(Point2F(-projOffset.x, projOffset.y));
      GFX->setFrustum(gfxFrustum);

      SceneCameraState cameraStateRight = SceneCameraState::fromGFX();
      SceneRenderState renderStateRight( this, renderState->getScenePassType(), cameraStateRight );
      renderStateRight.setSceneRenderStyle(SRS_SideBySide);
      renderStateRight.setSceneRenderField(1);

      renderSceneNoLights( &renderStateRight, objectMask, baseObject, baseZone );

      // Restore previous values
      GFX->setWorldMatrix(originalWorld);
      gfxFrustum.clearProjectionOffset();
      GFX->setFrustum(gfxFrustum);
      GFX->setViewport(originalVP);
   }
   else
   {
      renderSceneNoLights( renderState, objectMask, baseObject, baseZone );
   }

   // Trigger the post-render signal.

   PROFILE_START( SceneGraphRender_postRenderSignal );
      mCurrentRenderState = renderState;
      getPostRenderSignal().trigger( this, renderState );
      mCurrentRenderState = NULL;
   PROFILE_END();

   // Remove the previously registered lights.

   PROFILE_START( SceneGraph_unregisterLights);
      LIGHTMGR->unregisterAllLights();
   PROFILE_END();
}
void ForestWind::processTick()
{
   PROFILE_SCOPE( ForestWind_ProcessTick );

   const F32 deltaTime = 0.032f;
   const U32 simTime = Sim::getCurrentTime();
   
   Point2F finalVec( 0, 0 );
   Point2F windDir( mParent->mWindDirection.x, mParent->mWindDirection.y );

   if ( mLastGustTime < simTime )
   {
      Point2F turbVec( 0, 0 );
      if ( mLastGustTime < simTime + (mParent->mWindTurbulenceFrequency * 1000.0f) )
         turbVec = (mRandom.randF() * mParent->mWindTurbulenceStrength) * windDir;

      mLastGustTime = simTime + (mParent->mWindGustFrequency * 1000.0f);
      Point2F gustVec = (mRandom.randF() * mParent->mWindGustStrength + mRandom.randF() * mParent->mWindGustWobbleStrength) * windDir;

      finalVec += gustVec + turbVec;
      //finalVec.normalizeSafe();
   }
   
   //bool rotationChange = false;

   if ( mLastYawTime < simTime )
   {
      mLastYawTime = simTime + (mParent->mWindGustYawFrequency * 1000.0f);
      F32 rotateAmt = mRandom.randF() * mParent->mWindGustYawAngle + mRandom.randF() * mParent->mWindGustWobbleStrength;
      
      if ( mRandom.randF() <= 0.5f )
         rotateAmt = -rotateAmt;

      rotateAmt = mDegToRad( rotateAmt );

      if ( rotateAmt > M_2PI_F )
         rotateAmt -= M_2PI_F;
      else if ( rotateAmt < -M_2PI_F )
         rotateAmt += M_2PI_F;

      mTargetYawAngle = rotateAmt;

      //finalVec.rotate( rotateAmt );
      mCurrentTarget.rotate( rotateAmt );
   }
   
   //mCurrentTarget.normalizeSafe();

   if ( mCurrentTarget.isZero() || mCurrentInterp >= 1.0f )
   {
      mCurrentInterp = 0;
      mCurrentTarget.set( 0, 0 );
   
      Point2F windDir( mDirection.x, mDirection.y );
      windDir.normalizeSafe();

      mCurrentTarget = finalVec + windDir;
   }
   else
   {
      mCurrentInterp += deltaTime;
      mCurrentInterp = mClampF( mCurrentInterp, 0.0f, 1.0f );
      mDirection.interpolate( mDirection, Point3F( mCurrentTarget.x, mCurrentTarget.y, 0 ), mCurrentInterp );
      //F32 rotateAmt = mLerp( 0, mTargetYawAngle, mCurrentInterp );

      //mTargetYawAngle -= rotateAmt;

      //Point2F dir( mDirection.x, mDirection.y );
      //if ( mTargetYawAngle > 0.0f )
        // dir.rotate( rotateAmt );

      //mDirection.set( dir.x, dir.y, 0 );
   }
}
Example #25
0
void ShaderGen::generateShader( const MaterialFeatureData &featureData,
                                char *vertFile, 
                                char *pixFile, 
                                F32 *pixVersion,
                                const GFXVertexFormat *vertexFormat,
                                const char* cacheName,
                                Vector<GFXShaderMacro> &macros )
{
   PROFILE_SCOPE( ShaderGen_GenerateShader );

   mFeatureData = featureData;
   mVertexFormat = vertexFormat;

   _uninit();
   _init();

   char vertShaderName[256];
   char pixShaderName[256];

   // Note:  We use a postfix of _V/_P here so that it sorts the matching
   // vert and pixel shaders together when listed alphabetically.   
   dSprintf( vertShaderName, sizeof(vertShaderName), "shadergen:/%s_V.%s", cacheName, mFileEnding.c_str() );
   dSprintf( pixShaderName, sizeof(pixShaderName), "shadergen:/%s_P.%s", cacheName, mFileEnding.c_str() );
   
   dStrcpy( vertFile, vertShaderName );
   dStrcpy( pixFile, pixShaderName );   
   
   // this needs to change - need to optimize down to ps v.1.1
   *pixVersion = GFX->getPixelShaderVersion();
   
   if ( !Con::getBoolVariable( "ShaderGen::GenNewShaders", true ) )
   {
      // If we are not regenerating the shader we will return here.
      // But we must fill in the shader macros first!

      _processVertFeatures( macros, true );
      _processPixFeatures( macros, true );      

      return;
   }

   // create vertex shader
   //------------------------
   FileStream* s = new FileStream();
   if(!s->open(vertShaderName, Torque::FS::File::Write ))
   {
      AssertFatal(false, "Failed to open Shader Stream" );
      return;
   }

   mOutput = new MultiLine;
   mInstancingFormat.clear();
   _processVertFeatures(macros);
   _printVertShader( *s );
   delete s;
   
   ((ShaderConnector*)mComponents[C_CONNECTOR])->reset();
   LangElement::deleteElements();

   // create pixel shader
   //------------------------
   s = new FileStream();
   if(!s->open(pixShaderName, Torque::FS::File::Write ))
   {
      AssertFatal(false, "Failed to open Shader Stream" );
      return;
   }   

   mOutput = new MultiLine;
   _processPixFeatures(macros);
   _printPixShader( *s );

   delete s;
   LangElement::deleteElements();
}
Example #26
0
void HoverVehicle::updateForces(F32 /*dt*/)
{
    PROFILE_SCOPE( HoverVehicle_UpdateForces );

    Point3F gravForce(0, 0, sHoverVehicleGravity * mRigid.mass * mGravityMod);

    MatrixF currTransform;
    mRigid.getTransform(&currTransform);
    mRigid.atRest = false;

    mThrustLevel = (mForwardThrust * mDataBlock->mainThrustForce    +
                    mReverseThrust * mDataBlock->reverseThrustForce +
                    mLeftThrust    * mDataBlock->strafeThrustForce  +
                    mRightThrust   * mDataBlock->strafeThrustForce);

    Point3F thrustForce = ((Point3F( 0,  1, 0) * (mForwardThrust * mDataBlock->mainThrustForce))    +
                           (Point3F( 0, -1, 0) * (mReverseThrust * mDataBlock->reverseThrustForce)) +
                           (Point3F(-1,  0, 0) * (mLeftThrust    * mDataBlock->strafeThrustForce))  +
                           (Point3F( 1,  0, 0) * (mRightThrust   * mDataBlock->strafeThrustForce)));
    currTransform.mulV(thrustForce);
    if (mJetting)
        thrustForce *= mDataBlock->turboFactor;

    Point3F torque(0, 0, 0);
    Point3F force(0, 0, 0);

    Point3F vel = mRigid.linVelocity;
    F32 baseStabLen = getBaseStabilizerLength();
    Point3F stabExtend(0, 0, -baseStabLen);
    currTransform.mulV(stabExtend);

    StabPoint stabPoints[2];
    stabPoints[0].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
                                    mObjBox.maxExtents.y,
                                    (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5);
    stabPoints[1].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
                                    mObjBox.minExtents.y,
                                    (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5);
    U32 j, i;
    for (i = 0; i < 2; i++) {
        currTransform.mulP(stabPoints[i].osPoint, &stabPoints[i].wsPoint);
        stabPoints[i].wsExtension = stabExtend;
        stabPoints[i].extension   = baseStabLen;
        stabPoints[i].wsVelocity  = mRigid.linVelocity;
    }

    RayInfo rinfo;

    mFloating = true;
    bool reallyFloating = true;
    F32 compression[2] = { 0.0f, 0.0f };
    F32  normalMod[2]  = { 0.0f, 0.0f };
    bool normalSet[2]  = { false, false };
    Point3F normal[2];

    for (j = 0; j < 2; j++) {
        if (getContainer()->castRay(stabPoints[j].wsPoint, stabPoints[j].wsPoint + stabPoints[j].wsExtension * 2.0,
                                    TerrainObjectType |
                                    WaterObjectType, &rinfo))
        {
            reallyFloating = false;

            if (rinfo.t <= 0.5) {
                // Ok, stab is in contact with the ground, let's calc the forces...
                compression[j] = (1.0 - (rinfo.t * 2.0)) * baseStabLen;
            }
            normalSet[j] = true;
            normalMod[j] = rinfo.t < 0.5 ? 1.0 : (1.0 - ((rinfo.t - 0.5) * 2.0));

            normal[j] = rinfo.normal;
        }

        if ( pointInWater( stabPoints[j].wsPoint ) )
            compression[j] = baseStabLen;
    }

    for (j = 0; j < 2; j++) {
        if (compression[j] != 0.0) {
            mFloating = false;

            // Spring force and damping
            Point3F springForce = -stabPoints[j].wsExtension;
            springForce.normalize();
            springForce *= compression[j] * mDataBlock->stabSpringConstant;

            Point3F springDamping = -stabPoints[j].wsExtension;
            springDamping.normalize();
            springDamping *= -getMin(mDot(springDamping, stabPoints[j].wsVelocity), 0.7f) * mDataBlock->stabDampingConstant;

            force += springForce + springDamping;
        }
    }

    // Gravity
    if (reallyFloating == false)
        force += gravForce;
    else
        force += gravForce * mDataBlock->floatingGravMag;

    // Braking
    F32 vellen = mRigid.linVelocity.len();
    if (mThrottle == 0.0f &&
            mLeftThrust == 0.0f &&
            mRightThrust == 0.0f &&
            vellen != 0.0f &&
            vellen < mDataBlock->brakingActivationSpeed)
    {
        Point3F dir = mRigid.linVelocity;
        dir.normalize();
        dir.neg();
        force += dir *  mDataBlock->brakingForce;
    }

    // Gyro Drag
    torque = -mRigid.angMomentum * mDataBlock->gyroDrag;

    // Move to proper normal
    Point3F sn, r;
    currTransform.getColumn(2, &sn);
    if (normalSet[0] || normalSet[1]) {
        if (normalSet[0] && normalSet[1]) {
            F32 dot = mDot(normal[0], normal[1]);
            if (dot > 0.999) {
                // Just pick the first normal.  They're too close to call
                if ((sn - normal[0]).lenSquared() > 0.00001) {
                    mCross(sn, normal[0], &r);
                    torque += r * mDataBlock->normalForce * normalMod[0];
                }
            } else {
                Point3F rotAxis;
                mCross(normal[0], normal[1], &rotAxis);
                rotAxis.normalize();

                F32 angle = mAcos(dot) * (normalMod[0] / (normalMod[0] + normalMod[1]));
                AngAxisF aa(rotAxis, angle);
                QuatF q(aa);
                MatrixF tempMat(true);
                q.setMatrix(&tempMat);
                Point3F newNormal;
                tempMat.mulV(normal[1], &newNormal);

                if ((sn - newNormal).lenSquared() > 0.00001) {
                    mCross(sn, newNormal, &r);
                    torque += r * (mDataBlock->normalForce * ((normalMod[0] + normalMod[1]) * 0.5));
                }
            }
        } else {
            Point3F useNormal;
            F32     useMod;
            if (normalSet[0]) {
                useNormal = normal[0];
                useMod    = normalMod[0];
            } else {
                useNormal = normal[1];
                useMod    = normalMod[1];
            }

            if ((sn - useNormal).lenSquared() > 0.00001) {
                mCross(sn, useNormal, &r);
                torque += r * mDataBlock->normalForce * useMod;
            }
        }
    } else {
        if ((sn - Point3F(0, 0, 1)).lenSquared() > 0.00001) {
            mCross(sn, Point3F(0, 0, 1), &r);
            torque += r * mDataBlock->restorativeForce;
        }
    }

    Point3F sn2;
    currTransform.getColumn(0, &sn);
    currTransform.getColumn(1, &sn2);
    mCross(sn, sn2, &r);
    r.normalize();
    torque -= r * (mSteering.x * mDataBlock->steeringForce);

    currTransform.getColumn(0, &sn);
    currTransform.getColumn(2, &sn2);
    mCross(sn, sn2, &r);
    r.normalize();
    torque -= r * (mSteering.x * mDataBlock->rollForce);

    currTransform.getColumn(1, &sn);
    currTransform.getColumn(2, &sn2);
    mCross(sn, sn2, &r);
    r.normalize();
    torque -= r * (mSteering.y * mDataBlock->pitchForce);

    // Apply drag
    Point3F vDrag = mRigid.linVelocity;
    if (!mFloating) {
        vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor));
    } else {
        vDrag.convolve(Point3F(0.25, 0.25, mDataBlock->vertFactor));
    }
    force -= vDrag * mDataBlock->dragForce;

    force += mFloating ? thrustForce * mDataBlock->floatingThrustFactor : thrustForce;

    // Add in physical zone force
    force += mAppliedForce;

    // Container buoyancy & drag
    force  += Point3F(0, 0,-mBuoyancy * sHoverVehicleGravity * mRigid.mass * mGravityMod);
    force  -= mRigid.linVelocity * mDrag;
    torque -= mRigid.angMomentum * mDrag;

    mRigid.force  = force;
    mRigid.torque = torque;
}
Example #27
0
void ProcessedShaderMaterial::_determineFeatures(  U32 stageNum, 
                                                   MaterialFeatureData &fd, 
                                                   const FeatureSet &features )
{
   PROFILE_SCOPE( ProcessedShaderMaterial_DetermineFeatures );

   const float shaderVersion = GFX->getPixelShaderVersion();
   AssertFatal(shaderVersion > 0.0 , "Cannot create a shader material if we don't support shaders");

   bool lastStage = stageNum == (mMaxStages-1);

   // First we add all the features which the 
   // material has defined.

   if ( mMaterial->isTranslucent() )
   {
      // Note: This is for decal blending into the prepass
      // for AL... it probably needs to be made clearer.
      if (  mMaterial->mTranslucentBlendOp == Material::LerpAlpha &&
            mMaterial->mTranslucentZWrite )
         fd.features.addFeature( MFT_IsTranslucentZWrite );
      else
      {
         fd.features.addFeature( MFT_IsTranslucent );
         fd.features.addFeature( MFT_ForwardShading );
      }
   }

   // TODO: This sort of sucks... BL should somehow force this
   // feature on from the outside and not this way.
   if ( dStrcmp( LIGHTMGR->getId(), "BLM" ) == 0 )
      fd.features.addFeature( MFT_ForwardShading );

   // Disabling the InterlacedPrePass feature for now. It is not ready for prime-time
   // and it should not be triggered off of the DoubleSided parameter. [2/5/2010 Pat]
   /*if ( mMaterial->isDoubleSided() )
   {
      fd.features.addFeature( MFT_InterlacedPrePass );
   }*/

   // Allow instancing if it was requested and the card supports
   // SM 3.0 or above.
   //
   // We also disable instancing for non-single pass materials
   // and glowing materials because its untested/unimplemented.
   //
   if (  features.hasFeature( MFT_UseInstancing ) &&
         mMaxStages == 1 &&
         !mMaterial->mGlow[0] &&
         shaderVersion >= 3.0f )
      fd.features.addFeature( MFT_UseInstancing );

   if ( mMaterial->mAlphaTest )
      fd.features.addFeature( MFT_AlphaTest );

// start jc
   if ( mMaterial->mAlphaScatter )
      fd.features.addFeature( MFT_AlphaScatter );
// end jc

   if ( mMaterial->mEmissive[stageNum] )
      fd.features.addFeature( MFT_IsEmissive );
   else
      fd.features.addFeature( MFT_RTLighting );

   if ( mMaterial->mAnimFlags[stageNum] )
      fd.features.addFeature( MFT_TexAnim );  

   if ( mMaterial->mVertLit[stageNum] )
      fd.features.addFeature( MFT_VertLit );
   
   // cubemaps only available on stage 0 for now - bramage   
   if ( stageNum < 1 && 
         (  (  mMaterial->mCubemapData && mMaterial->mCubemapData->mCubemap ) ||
               mMaterial->mDynamicCubemap ) )
   fd.features.addFeature( MFT_CubeMap );

   fd.features.addFeature( MFT_Visibility );

// start jc
/*
   if (  lastStage && 
         (  !gClientSceneGraph->usePostEffectFog() ||
            fd.features.hasFeature( MFT_IsTranslucent ) ||
            fd.features.hasFeature( MFT_ForwardShading )) )
      fd.features.addFeature( MFT_Fog );
*/

   if (  lastStage && 
         (  !gClientSceneGraph->usePostEffectFog() ||
            fd.features.hasFeature( MFT_IsTranslucent ) ||
            fd.features.hasFeature( MFT_ForwardShading )) )
   {
      if(mMaterial->mTranslucentBlendOp == Material::Add)
         fd.features.addFeature( MFT_FogBlendAdd );
      else
         fd.features.addFeature( MFT_Fog );
   }
// end jc

   if ( mMaterial->mMinnaertConstant[stageNum] > 0.0f )
      fd.features.addFeature( MFT_MinnaertShading );

   if ( mMaterial->mSubSurface[stageNum] )
      fd.features.addFeature( MFT_SubSurface );

   if ( !mMaterial->mCellLayout[stageNum].isZero() )
   {
      fd.features.addFeature( MFT_DiffuseMapAtlas );

      if ( mMaterial->mNormalMapAtlas )
         fd.features.addFeature( MFT_NormalMapAtlas );
   }

   // Grab other features like normal maps, base texture, etc.
   FeatureSet mergeFeatures;
   mStages[stageNum].getFeatureSet( &mergeFeatures );
   fd.features.merge( mergeFeatures );
   
   if ( fd.features[ MFT_NormalMap ] )   
   {   
      if (  mStages[stageNum].getTex( MFT_NormalMap )->mFormat == GFXFormatDXT5 &&   
           !mStages[stageNum].getTex( MFT_NormalMap )->mHasTransparency )   
         fd.features.addFeature( MFT_IsDXTnm );   

   // start jc
      if (  mMaterial->mObjectSpaceNormals )
         fd.features.addFeature( MFT_IsObjectSpaceNormals );
   // end jc
   }

   // Now for some more advanced features that we 
   // cannot do on SM 2.0 and below.
   if ( shaderVersion > 2.0f )
   {
      // Only allow parallax if we have a normal map and
      // we're not using DXTnm compression.
      if (  mMaterial->mParallaxScale[stageNum] > 0.0f &&
         fd.features[ MFT_NormalMap ] &&
         !fd.features[ MFT_IsDXTnm ] )
         fd.features.addFeature( MFT_Parallax );

      // If not parallax then allow per-pixel specular if
      // we have real time lighting enabled.
      else if (   fd.features[MFT_RTLighting] && 
                  mMaterial->mPixelSpecular[stageNum] )
         fd.features.addFeature( MFT_PixSpecular );
   }

   // Without realtime lighting and on lower end 
   // shader models disable the specular map.
   if (  !fd.features[ MFT_RTLighting ] || shaderVersion == 2.0 )
      fd.features.removeFeature( MFT_SpecularMap );

   // If we have a specular map then make sure we
   // have per-pixel specular enabled.
   if( fd.features[ MFT_SpecularMap ] )
   {
      fd.features.addFeature( MFT_PixSpecular );

      // Check for an alpha channel on the specular map. If it has one (and it
      // has values less than 255) than the artist has put the gloss map into
      // the alpha channel.
      if( mStages[stageNum].getTex( MFT_SpecularMap )->mHasTransparency )
         fd.features.addFeature( MFT_GlossMap );
   }

   // Without a base texture use the diffuse color
   // feature to ensure some sort of output.
   if (!fd.features[MFT_DiffuseMap])
   {
      fd.features.addFeature( MFT_DiffuseColor );

      // No texture coords... no overlay.
      fd.features.removeFeature( MFT_OverlayMap );
   }

   // If we have a diffuse map and the alpha on the diffuse isn't
   // zero and the color isn't pure white then multiply the color.
   else if (   mMaterial->mDiffuse[stageNum].alpha > 0.0f && 
               mMaterial->mDiffuse[stageNum] != ColorF::WHITE )
      fd.features.addFeature( MFT_DiffuseColor );

   // If lightmaps or tonemaps are enabled or we 
   // don't have a second UV set then we cannot 
   // use the overlay texture.
   if (  fd.features[MFT_LightMap] || 
         fd.features[MFT_ToneMap] || 
         mVertexFormat->getTexCoordCount() < 2 )
      fd.features.removeFeature( MFT_OverlayMap );

   // If tonemaps are enabled don't use lightmap
   if ( fd.features[MFT_ToneMap] || mVertexFormat->getTexCoordCount() < 2 )
      fd.features.removeFeature( MFT_LightMap );

   // Don't allow tonemaps if we don't have a second UV set
   if ( mVertexFormat->getTexCoordCount() < 2 )
      fd.features.removeFeature( MFT_ToneMap );

   // Always add the HDR output feature.  
   //
   // It will be filtered out if it was disabled 
   // for this material creation below.
   //
   // Also the shader code will evaluate to a nop
   // if HDR is not enabled in the scene.
   //
   fd.features.addFeature( MFT_HDROut );

   // If vertex color is enabled on the material's stage and
   // color is present in vertex format, add diffuse vertex
   // color feature.
   
   if (  mMaterial->mVertColor[ stageNum ] &&
         mVertexFormat->hasColor() )
      fd.features.addFeature( MFT_DiffuseVertColor );

   // Allow features to add themselves.
   for ( U32 i = 0; i < FEATUREMGR->getFeatureCount(); i++ )
   {
      const FeatureInfo &info = FEATUREMGR->getAt( i );
      info.feature->determineFeature(  mMaterial, 
                                       mVertexFormat, 
                                       stageNum, 
                                       *info.type, 
                                       features, 
                                       &fd );
   }

   // Now disable any features that were 
   // not part of the input feature handle.
   fd.features.filter( features );
}
Example #28
0
void StdServerProcessList::onTickObject( ProcessObject *pobj )
{
   PROFILE_SCOPE( StdServerProcessList_OnTickObject );
   
   // Each object is either advanced a single tick, or if it's
   // being controlled by a client, ticked once for each pending move.

   GameConnection *con = pobj->getControllingClient();

   if ( pobj->mIsGameBase && con && con->getControlObject() == pobj )
   {
      // In case the object is deleted during its own tick.
      SimObjectPtr<GameBase> obj = getGameBase( pobj );

      Move* movePtr;
      U32 m, numMoves;
      con->mMoveList->getMoves( &movePtr, &numMoves );

      // For debugging it can be useful to know when this happens.
      //if ( numMoves > 1 )
      //   Con::printf( "numMoves: %i", numMoves );

      // Do we really need to test the control object each iteration? Does it change?
      for ( m = 0; m < numMoves && con && con->getControlObject() == obj; m++, movePtr++ )
      {         
         #ifdef TORQUE_DEBUG_NET_MOVES
         U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient());
         #endif
      
         if ( obj->isTicking() )
            obj->processTick( movePtr );

         if ( con && con->getControlObject() == obj )
         {
            U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() );

            // check move checksum
            if ( movePtr->checksum != newsum )
            {
               #ifdef TORQUE_DEBUG_NET_MOVES
               if( !obj->isAIControlled() )
                  Con::printf("move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)",
                     movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z);
               #endif

               movePtr->checksum = Move::ChecksumMismatch;
            }
            else
            {
               #ifdef TORQUE_DEBUG_NET_MOVES
               Con::printf("move %i checksum agree: %i == %i, (start %i), (move %f %f %f)",
                  movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z);
               #endif
            }
         }
      }

      con->mMoveList->clearMoves( m );
   }
   else if ( pobj->isTicking() )
      pobj->processTick( 0 );
}
Example #29
0
void ProcessedShaderMaterial::_setTextureTransforms(const U32 pass)
{
   PROFILE_SCOPE( ProcessedShaderMaterial_SetTextureTransforms );

   ShaderConstHandles* handles = _getShaderConstHandles(pass);
   if (handles->mTexMatSC->isValid())
   {   
      MatrixF texMat( true );

      mMaterial->updateTimeBasedParams();
      F32 waveOffset = _getWaveOffset( pass ); // offset is between 0.0 and 1.0

      // handle scroll anim type
      if(  mMaterial->mAnimFlags[pass] & Material::Scroll )
      {
         if( mMaterial->mAnimFlags[pass] & Material::Wave )
         {
            Point3F scrollOffset;
            scrollOffset.x = mMaterial->mScrollDir[pass].x * waveOffset;
            scrollOffset.y = mMaterial->mScrollDir[pass].y * waveOffset;
            scrollOffset.z = 1.0;

            texMat.setColumn( 3, scrollOffset );
         }
         else
         {
            Point3F offset( mMaterial->mScrollOffset[pass].x, 
               mMaterial->mScrollOffset[pass].y, 
               1.0 );

            texMat.setColumn( 3, offset );
         }

      }

      // handle rotation
      if( mMaterial->mAnimFlags[pass] & Material::Rotate )
      {
         if( mMaterial->mAnimFlags[pass] & Material::Wave )
         {
            F32 rotPos = waveOffset * M_2PI;
            texMat.set( EulerF( 0.0, 0.0, rotPos ) );
            texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) );

            MatrixF test( true );
            test.setColumn( 3, Point3F( mMaterial->mRotPivotOffset[pass].x, 
               mMaterial->mRotPivotOffset[pass].y,
               0.0 ) );
            texMat.mul( test );
         }
         else
         {
            texMat.set( EulerF( 0.0, 0.0, mMaterial->mRotPos[pass] ) );

            texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) );

            MatrixF test( true );
            test.setColumn( 3, Point3F( mMaterial->mRotPivotOffset[pass].x, 
               mMaterial->mRotPivotOffset[pass].y,
               0.0 ) );
            texMat.mul( test );
         }
      }

      // Handle scale + wave offset
      if(  mMaterial->mAnimFlags[pass] & Material::Scale &&
         mMaterial->mAnimFlags[pass] & Material::Wave )
      {
         F32 wOffset = fabs( waveOffset );

         texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) );

         MatrixF temp( true );
         temp.setRow( 0, Point3F( wOffset,  0.0,  0.0 ) );
         temp.setRow( 1, Point3F( 0.0,  wOffset,  0.0 ) );
         temp.setRow( 2, Point3F( 0.0,  0.0,  wOffset ) );
         temp.setColumn( 3, Point3F( -wOffset * 0.5, -wOffset * 0.5, 0.0 ) );
         texMat.mul( temp );
      }

      // handle sequence
      if( mMaterial->mAnimFlags[pass] & Material::Sequence )
      {
         U32 frameNum = (U32)(MATMGR->getTotalTime() * mMaterial->mSeqFramePerSec[pass]);
         F32 offset = frameNum * mMaterial->mSeqSegSize[pass];

         if ( mMaterial->mAnimFlags[pass] & Material::Scale )
            texMat.scale( Point3F( mMaterial->mSeqSegSize[pass], 1.0f, 1.0f ) );

         Point3F texOffset = texMat.getPosition();
         texOffset.x += offset;
         texMat.setPosition( texOffset );
      }

      GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass);
      shaderConsts->setSafe(handles->mTexMatSC, texMat);
   }
}
Example #30
0
void TerrCell::createPrimBuffer( GFXPrimitiveBufferHandle *primBuffer )
{
   PROFILE_SCOPE( TerrCell_AllocPrimBuffer );

   primBuffer->set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" );
   
   // We don't use the primitive for normal clipmap
   // rendering, but it is used for the shadow pass.
   GFXPrimitive *prim = primBuffer->getPointer()->mPrimitiveArray;
   prim->type = GFXTriangleList;
   prim->numPrimitives = smTriCount;
   prim->numVertices = smVBSize;

   //
   // The vertex pattern for the terrain is as
   // follows...
   //
   //     0----1----2.....n
   //     |\   |   /|
   //     | \  |  / |
   //     |  \ | /  |
   //     |   \|/   |
   //     n----n----n
   //     |   /|\   |
   //     |  / | \  |
   //     | /  |  \ |
   //     |/   |   \|
   //     n----n----n
   //

   // Lock and fill it up!
   U16 *idxBuff;
   primBuffer->lock( &idxBuff );
   U32 counter = 0;
   U32 maxIndex = 0;

   for ( U32 y = 0; y < smMinCellSize; y++ )
   {
      const U32 yTess = y % 2;

      for ( U32 x = 0; x < smMinCellSize; x++ )
      {
         U32 index = ( y * smVBStride ) + x;
         
         const U32 xTess = x % 2;

         if ( ( xTess == 0 && yTess == 0 ) ||
              ( xTess != 0 && yTess != 0 ) )
         {
            idxBuff[0] = index + 0;
            idxBuff[1] = index + smVBStride;
            idxBuff[2] = index + smVBStride + 1;

            idxBuff[3] = index + 0;
            idxBuff[4] = index + smVBStride + 1;
            idxBuff[5] = index + 1;
         }
         else
         {
            idxBuff[0] = index + 1;
            idxBuff[1] = index;
            idxBuff[2] = index + smVBStride;

            idxBuff[3] = index + 1;
            idxBuff[4] = index + smVBStride;
            idxBuff[5] = index + smVBStride + 1;
         }

         idxBuff += 6;
         maxIndex = index + 1 + smVBStride;         
         counter += 6;         
      }
   }

   // Now add indices for the 'skirts'.
   // These could probably be reduced to a loop.

   // Temporaries that hold triangle indices.
   // Top/Bottom - 0,1
   U32 t0, t1, b0, b1;

   // Top edge skirt...

   // Index to the first vert of the top row.
   U32 startIndex = 0;
   // Index to the first vert of the skirt under the top row.
   U32 skirtStartIdx = smVBStride * smVBStride;
   // Step to go one vert to the right.
   U32 step = 1;

   for ( U32 i = 0; i < smMinCellSize; i++ )
   {
      t0 = startIndex + i * step;
      t1 = t0 + step;
      b0 = skirtStartIdx + i;
      b1 = skirtStartIdx + i + 1;

      idxBuff[0] = b0;
      idxBuff[1] = t0;
      idxBuff[2] = t1;

      idxBuff[3] = b1;
      idxBuff[4] = b0;
      idxBuff[5] = t1;

      idxBuff += 6;
      maxIndex = b1;
      counter += 6;
   }

   // Bottom edge skirt...

   // Index to the first vert of the bottom row.
   startIndex = smVBStride * smVBStride - smVBStride;
   // Index to the first vert of the skirt under the bottom row.
   skirtStartIdx = startIndex + smVBStride * 2;
   // Step to go one vert to the right.
   step = 1;
   
   for ( U32 i = 0; i < smMinCellSize; i++ )
   {
      t0 = startIndex + ( i * step );
      t1 = t0 + step;
      b0 = skirtStartIdx + i;
      b1 = skirtStartIdx + i + 1;

      idxBuff[0] = t1;
      idxBuff[1] = t0;
      idxBuff[2] = b0;

      idxBuff[3] = t1;
      idxBuff[4] = b0;
      idxBuff[5] = b1;

      idxBuff += 6;
      maxIndex = b1;
      counter += 6;
   }

   // Left edge skirt...

   // Index to the first vert of the left column.
   startIndex = 0;
   // Index to the first vert of the skirt under the left column.
   skirtStartIdx = smVBStride * smVBStride + smVBStride * 2;
   // Step to go one vert down.
   step = smVBStride;

   for ( U32 i = 0; i < smMinCellSize; i++ )
   {
      t0 = startIndex + ( i * step );
      t1 = t0 + step;
      b0 = skirtStartIdx + i;
      b1 = skirtStartIdx + i + 1;

      idxBuff[0] = t1;
      idxBuff[1] = t0;
      idxBuff[2] = b0;

      idxBuff[3] = t1;
      idxBuff[4] = b0;
      idxBuff[5] = b1;

      idxBuff += 6;
      maxIndex = b1;
      counter += 6;
   }

   // Right edge skirt...

   // Index to the first vert of the right column.
   startIndex = smVBStride - 1;
   // Index to the first vert of the skirt under the right column.
   skirtStartIdx = smVBStride * smVBStride + smVBStride * 3;
   // Step to go one vert down.
   step = smVBStride;

   for ( U32 i = 0; i < smMinCellSize; i++ )
   {
      t0 = startIndex + ( i * step );
      t1 = t0 + step;
      b0 = skirtStartIdx + i;
      b1 = skirtStartIdx + i + 1;

      idxBuff[0] = b0;
      idxBuff[1] = t0;
      idxBuff[2] = t1;

      idxBuff[3] = b1;
      idxBuff[4] = b0;
      idxBuff[5] = t1;

      idxBuff += 6;
      maxIndex = b1;
      counter += 6;
   }

   primBuffer->unlock();
}