Ejemplo n.º 1
0
void TurretShape::_applyLimits(Point3F& rot)
{
   rot.x = mClampF(rot.x, mPitchUp, mPitchDown);
   if (mHeadingMax < mDegToRad(180.0f))
   {
      rot.z = mClampF(rot.z, -mHeadingMax, mHeadingMax);
   }
}
Ejemplo n.º 2
0
void GFXCubemap::initNormalize( U32 size )
{
   Point3F axis[6] =
         {Point3F(1.0, 0.0, 0.0), Point3F(-1.0,  0.0,  0.0),
         Point3F(0.0, 1.0, 0.0), Point3F( 0.0, -1.0,  0.0),
         Point3F(0.0, 0.0, 1.0), Point3F( 0.0,  0.0, -1.0),};
   Point3F s[6] =
         {Point3F(0.0, 0.0, -1.0), Point3F( 0.0, 0.0, 1.0),
         Point3F(1.0, 0.0,  0.0), Point3F( 1.0, 0.0, 0.0),
         Point3F(1.0, 0.0,  0.0), Point3F(-1.0, 0.0, 0.0),};
   Point3F t[6] =
         {Point3F(0.0, -1.0, 0.0), Point3F(0.0, -1.0,  0.0),
         Point3F(0.0,  0.0, 1.0), Point3F(0.0,  0.0, -1.0),
         Point3F(0.0, -1.0, 0.0), Point3F(0.0, -1.0,  0.0),};

   F32 span = 2.0;
   F32 start = -1.0;

   F32 stride = span / F32(size - 1);
   GFXTexHandle faces[6];

   for(U32 i=0; i<6; i++)
   {
      GFXTexHandle &tex = faces[i];
      GBitmap *bitmap = new GBitmap(size, size);

      // fill in...
      for(U32 v=0; v<size; v++)
      {
         for(U32 u=0; u<size; u++)
         {
            Point3F vector;
            vector = axis[i] +
               ((F32(u) * stride) + start) * s[i] +
               ((F32(v) * stride) + start) * t[i];
            vector.normalizeSafe();
            vector = ((vector * 0.5) + Point3F(0.5, 0.5, 0.5)) * 255.0;
            vector.x = mClampF(vector.x, 0.0f, 255.0f);
            vector.y = mClampF(vector.y, 0.0f, 255.0f);
            vector.z = mClampF(vector.z, 0.0f, 255.0f);
            // easy way to avoid knowledge of the format (RGB, RGBA, RGBX, ...)...
            U8 *bits = bitmap->getAddress(u, v);
            bits[0] = U8(vector.x);
            bits[1] = U8(vector.y);
            bits[2] = U8(vector.z);
         }
      }

      tex.set(bitmap, &GFXDefaultStaticDiffuseProfile, true, "Cubemap");
   }

   initStatic(faces);
}
Ejemplo n.º 3
0
void SFXSource::setCone(   F32 innerAngle,
                           F32 outerAngle,
                           F32 outerVolume )
{
   mConeInsideAngle     = mClampF( innerAngle, 0.0f, 360.0 );
   mConeOutsideAngle    = mClampF( outerAngle, mConeInsideAngle, 360.0 );
   mConeOutsideVolume   = mClampF( outerVolume, 0.0f, 1.0f );

   if ( mVoice && mIs3D )
      mVoice->setCone(  mConeInsideAngle,
                        mConeOutsideAngle,
                        mConeOutsideVolume );
}
Ejemplo n.º 4
0
void SFXSource::setVolume( F32 volume )
{
   mVolume = mClampF( volume, 0, 1 );

   if ( mVoice )
      mVoice->setVolume( mVolume * mModulativeVolume );      
}
Ejemplo n.º 5
0
void GuiTSCtrl::drawLine( Point3F p0, Point3F p1, const ColorI &color, F32 width )
{   
   if ( !mSaveFrustum.clipSegment( p0, p1 ) )
      return;

   MathUtils::mProjectWorldToScreen( p0, &p0, mSaveViewport, mSaveModelview, mSaveProjection );   
   MathUtils::mProjectWorldToScreen( p1, &p1, mSaveViewport, mSaveModelview, mSaveProjection );   

   p0.x = mClampF( p0.x, 0.0f, mSaveViewport.extent.x );
   p0.y = mClampF( p0.y, 0.0f, mSaveViewport.extent.y );
   p1.x = mClampF( p1.x, 0.0f, mSaveViewport.extent.x );
   p1.y = mClampF( p1.y, 0.0f, mSaveViewport.extent.y );
   p0.z = p1.z = 0.0f;

   _drawLine( p0, p1, color, width );
}
Ejemplo n.º 6
0
static inline F32 moveClamp(F32 v)
{
   // Support function to convert/clamp the input into a move rotation
   // which only allows 0 -> M_2PI.
   F32 a = mClampF(v, -M_2PI_F, M_2PI_F);
   return (a < 0) ? a + M_2PI_F : a;
}
Ejemplo n.º 7
0
void EditorCamera::mainLoop()
{
   if (mRenderCamera == NULL)
      return;

   mVerticalAngle = mClampF(mVerticalAngle, -4.7f, -1.7f);
   VectorF rotation = mTransform.getRotationEuler();
   rotation.y = mVerticalAngle;
   rotation.z = mHorizontalAngle;
   mTransform.setRotation(rotation);

   VectorF up(0.0f, 0.0f, 1.0f);
   Point3F look;
   Point3F cameraForward(1.0f, 0.0f, 0.0f);

   bx::vec3MulMtx(look, cameraForward, mTransform.matrix);

   if (mForwardVelocity.len() > 0.01f)
   {
      MatrixF lookMatrix;
      bx::mtxLookAt(lookMatrix, mWorldPosition, look, up);
      mWorldPosition += (lookMatrix.getForwardVector() * mForwardVelocity.x);
      mWorldPosition -= (lookMatrix.getRightVector() * mForwardVelocity.y);
      mTransform.setPosition(mWorldPosition);
   }

   bx::vec3MulMtx(look, cameraForward, mTransform.matrix);
   bx::mtxLookAt(mRenderCamera->viewMatrix, mWorldPosition, look, up);
   mRenderCamera->position = mWorldPosition;
}
Ejemplo n.º 8
0
bool GuiSliderCtrl::onWake()
{
   if( !Parent::onWake() )
      return false;
      
   mHasTexture = mProfile->constructBitmapArray() >= NumBitmaps;  
	if( mHasTexture )
   {
      mBitmapBounds = mProfile->mBitmapArrayRects.address();
		mThumbSize = Point2I( mBitmapBounds[ SliderButtonNormal ].extent.x, mBitmapBounds[ SliderButtonNormal ].extent.y );
	}

   F32 value;
   if( mConsoleVariable[ 0 ] )
      value = getFloatVariable();
   else
      value = mValue;

   mValue = mClampF( value, mRange.x, mRange.y );

   // mouse scroll increment percentage is 5% of the range
   mIncAmount = ( ( mRange.y - mRange.x ) * 0.05 );

   if( ( mThumbSize.y + mProfile->mFont->getHeight() - 4 ) <= getExtent().y )
      mDisplayValue = true;
   else
      mDisplayValue = false;

   _updateThumb( mValue, mSnap, true );

   return true;
}
Ejemplo n.º 9
0
void PathCamera::setPosition(F32 pos)
{
   mPosition = mClampF(pos, (F32)mNodeBase, (F32)(mNodeBase + mNodeCount - 1));
   MatrixF mat;
   interpolateMat(mPosition,&mat);
   Parent::setTransform(mat);
   setMaskBits(PositionMask);
}
Ejemplo n.º 10
0
void TurretShape::updateAnimation(F32 dt)
{
   if (mRecoilThread)
      mShapeInstance->advanceTime(dt,mRecoilThread);
   if (mImageStateThread)
      mShapeInstance->advanceTime(dt,mImageStateThread);

   // Update any pitch and heading threads
   if (mPitchThread)
   {
      F32 d = mPitchDown - mPitchUp;
      if (!mIsZero(d))
      {
         F32 pos = (mRot.x - mPitchUp) / d;
         mShapeInstance->setPos(mPitchThread, mClampF(pos, 0.0f, 1.0f));
      }
   }
   if (mHeadingThread)
   {
      F32 pos = 0.0f;
      if (mHeadingMax < mDegToRad(180.0f))
      {
         F32 d = mHeadingMax * 2.0f;
         if (!mIsZero(d))
         {
            pos = (mRot.z + mHeadingMax) / d;
         }
      }
      else
      {
         pos = mRot.z / M_2PI;
         if (pos < 0.0f)
         {
            // We don't want negative rotations to simply mirror the
            // positive rotations but to animate into them as if -0.0
            // is equivalent to 1.0.
            pos = mFmod(pos, 1.0f) + 1.0f;
         }
         if (pos > 1.0f)
         {
            pos = mFmod(pos, 1.0f);
         }
      }
      mShapeInstance->setPos(mHeadingThread, mClampF(pos, 0.0f, 1.0f));
   }
}
Ejemplo n.º 11
0
void SFXDSVoice::setPitch( F32 pitch )
{ 
   F32 sampleRate = _getBuffer()->getFormat().getSamplesPerSecond();
   F32 frequency = mFloor( mClampF( sampleRate * pitch, DSBFREQUENCY_MIN, DSBFREQUENCY_MAX ) );

   DSAssert( mDSBuffer->SetFrequency( ( U32 )frequency ), 
      "SFXDSVoice::setPitch - couldn't set playback frequency.");
}
Ejemplo n.º 12
0
void ScatterSky::_conformLights()
{
   _initCurves();

   F32 val = mCurves[0].getVal( mTimeOfDay );
   mNightInterpolant = 1.0f - val;

   VectorF lightDirection;
   F32 brightness;

   // Build the light direction from the azimuth and elevation.
   F32 yaw = mDegToRad(mClampF(mSunAzimuth,0,359));
   F32 pitch = mDegToRad(mClampF(mSunElevation,-360,+360));
   MathUtils::getVectorFromAngles(lightDirection, yaw, pitch);
   lightDirection.normalize();
   mSunDir = -lightDirection;

   yaw = mDegToRad(mClampF(mMoonAzimuth,0,359));
   pitch = mDegToRad(mClampF(mMoonElevation,-360,+360));
   MathUtils::getVectorFromAngles( mMoonLightDir, yaw, pitch );
   mMoonLightDir.normalize();
   mMoonLightDir = -mMoonLightDir;

   brightness = mCurves[2].getVal( mTimeOfDay );

   if ( mNightInterpolant >= 1.0f )
      lightDirection = -mMoonLightDir;

   mLight->setDirection( -lightDirection );
   mLight->setBrightness( brightness * mBrightness );
   mLightDir = lightDirection;

   // Have to do interpolation
   // after the light direction is set
   // otherwise the sun color will be invalid.
   _interpolateColors();

   mLight->setAmbient( mAmbientColor );
   mLight->setColor( mSunColor );
   mLight->setCastShadows( mCastShadows );

   FogData fog = getSceneManager()->getFogData();
   fog.color = mFogColor;
   getSceneManager()->setFogData( fog );
}
Ejemplo n.º 13
0
bool WaterObject::_checkDensity( void *object, const char *index, const char *data )
{
   //Water densities above 1000 shoot the player high and fast into the air.
   //value clamped to prevent errors.
   WaterObject *water = static_cast<WaterObject*>( object );
   water->mDensity = mClampF(dAtof( data ), 0.0f, 1000.0f);

   return false;
}
Ejemplo n.º 14
0
void GuiProgressBitmapCtrl::setScriptValue(const char *value)
{
   //set the value
   if (! value)
      mProgress = 0.0f;
   else
      mProgress = dAtof(value);

   //validate the value
   mProgress = mClampF(mProgress, 0.f, 1.f);
   setUpdate();
}
Ejemplo n.º 15
0
void GuiProgressBitmapCtrl::onPreRender()
{
   const char * var = getVariable();
   if(var)
   {
      F32 value = mClampF(dAtof(var), 0.f, 1.f);
      if(value != mProgress)
      {
         mProgress = value;
         setUpdate();
      }
   }
}
Ejemplo n.º 16
0
void Sun::_conformLights()
{
   // Build the light direction from the azimuth and elevation.
   F32 yaw = mDegToRad(mClampF(mSunAzimuth,0,359));
   F32 pitch = mDegToRad(mClampF(mSunElevation,-360,+360));
   VectorF lightDirection;
   MathUtils::getVectorFromAngles(lightDirection, yaw, pitch);
   lightDirection.normalize();
   mLight->setDirection( -lightDirection );
   mLight->setBrightness( mBrightness );

   // Now make sure the colors are within range.
   mLightColor.clamp();
   mLight->setColor( mLightColor );
   mLightAmbient.clamp();
   mLight->setAmbient( mLightAmbient );

   // Optimization... disable shadows if the ambient and 
   // directional color are the same.
   bool castShadows = mLightColor != mLightAmbient && mCastShadows; 
   mLight->setCastShadows( castShadows );
}
Ejemplo n.º 17
0
bool ParticleData::protectedSetTimes( void *object, const char *index, const char *data) 
{
   ParticleData *pData = static_cast<ParticleData*>( object );
   F32 val = dAtof(data);
   U32 i;

   if (!index)
      i = 0;
   else
      i = dAtoui(index);

   pData->times[i] = mClampF( val, 0.f, 1.f );

   return false;
}
Ejemplo n.º 18
0
bool EditorCamera::onMouseMove(int x, int y)
{
   if (!mMouseDown)
      return false;

   Point2I pos = Point2I(x, y);
   Point2I delta = mMouseStart - pos;
   mMouseStart = pos;

   if (delta.isZero()) return false;

   mHorizontalAngle += delta.x * 0.01f;
   mVerticalAngle -= delta.y * 0.01f;
   mVerticalAngle = mClampF(mVerticalAngle, -4.7f, -1.7f);
   return false;
}
Ejemplo n.º 19
0
void PSSMLightShadowMap::_calcSplitPos(const Frustum& currFrustum)
{
   const F32 nearDist = 0.01f; // TODO: Should this be adjustable or different?
   const F32 farDist = currFrustum.getFarDist();

   for ( U32 i = 1; i < mNumSplits; i++ )
   {
      F32 step = (F32) i / (F32) mNumSplits;
      F32 logSplit = nearDist * mPow(farDist / nearDist, step);
      F32 linearSplit = nearDist + (farDist - nearDist) * step;
      mSplitDist[i] = mLerp( linearSplit, logSplit, mClampF( mLogWeight, 0.0f, 1.0f ) );
   }

   mSplitDist[0] = nearDist;
   mSplitDist[mNumSplits] = farDist;
}
Ejemplo n.º 20
0
void TSShapeLoader::generateObjectState(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame)
{
   shape->objectStates.increment();
   TSShape::ObjectState& state = shape->objectStates.last();

   state.frameIndex = 0;
   state.matFrameIndex = 0;
   state.vis = mClampF(appMeshes[obj.startMeshIndex]->getVisValue(t), 0.0f, 1.0f);

   if (addFrame || addMatFrame)
   {
      generateFrame(obj, t, addFrame, addMatFrame);

      // set the frame number for the object state
      state.frameIndex = appMeshes[obj.startMeshIndex]->numFrames - 1;
      state.matFrameIndex = appMeshes[obj.startMeshIndex]->numMatFrames - 1;
   }
}
Ejemplo n.º 21
0
void ProjectedShadow::_calcScore( const SceneRenderState *state )
{
    if ( !mDecalInstance )
        return;

    F32 pixRadius = mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y );

    F32 pct = pixRadius / mDecalInstance->mDataBlock->fadeStartPixelSize;

    U32 msSinceLastRender = Platform::getVirtualMilliseconds() - getLastRenderTime();

    ShapeBaseData *data = NULL;
    if ( mShapeBase )
        data = static_cast<ShapeBaseData*>( mShapeBase->getDataBlock() );

    // For every 1s this shadow hasn't been
    // updated we'll add 10 to the score.
    F32 secs = mFloor( (F32)msSinceLastRender / 1000.0f );

    mScore = pct + secs;
    mClampF( mScore, 0.0f, 2000.0f );
}
Ejemplo n.º 22
0
bool DecalManager::clipDecal( DecalInstance *decal, Vector<Point3F> *edgeVerts, const Point2F *clipDepth )
{
   PROFILE_SCOPE( DecalManager_clipDecal );

   // Free old verts and indices.
   _freeBuffers( decal );

   ClippedPolyList clipper;

   clipper.mNormal.set( Point3F( 0, 0, 0 ) );
   clipper.mPlaneList.setSize(6);

   F32 halfSize = decal->mSize * 0.5f;
   
   // Ugly hack for ProjectedShadow!
   F32 halfSizeZ = clipDepth ? clipDepth->x : halfSize;
   F32 negHalfSize = clipDepth ? clipDepth->y : halfSize;
   Point3F decalHalfSize( halfSize, halfSize, halfSize );
   Point3F decalHalfSizeZ( halfSizeZ, halfSizeZ, halfSizeZ );

   MatrixF projMat( true );
   decal->getWorldMatrix( &projMat );

   const VectorF &crossVec = decal->mNormal;
   const Point3F &decalPos = decal->mPosition;

   VectorF newFwd, newRight;
   projMat.getColumn( 0, &newRight );
   projMat.getColumn( 1, &newFwd );   

   VectorF objRight( 1.0f, 0, 0 );
   VectorF objFwd( 0, 1.0f, 0 );
   VectorF objUp( 0, 0, 1.0f );

   // See above re: decalHalfSizeZ hack.
   clipper.mPlaneList[0].set( ( decalPos + ( -newRight * halfSize ) ), -newRight );
   clipper.mPlaneList[1].set( ( decalPos + ( -newFwd * halfSize ) ), -newFwd );
   clipper.mPlaneList[2].set( ( decalPos + ( -crossVec * decalHalfSizeZ ) ), -crossVec );
   clipper.mPlaneList[3].set( ( decalPos + ( newRight * halfSize ) ), newRight );
   clipper.mPlaneList[4].set( ( decalPos + ( newFwd * halfSize ) ), newFwd );
   clipper.mPlaneList[5].set( ( decalPos + ( crossVec * negHalfSize ) ), crossVec );

   clipper.mNormal = -decal->mNormal;

   Box3F box( -decalHalfSizeZ, decalHalfSizeZ );

   projMat.mul( box );

   DecalData *decalData = decal->mDataBlock;

   PROFILE_START( DecalManager_clipDecal_buildPolyList );
   getContainer()->buildPolyList( box, decalData->clippingMasks, &clipper );   
   PROFILE_END();

   clipper.cullUnusedVerts();
   clipper.triangulate();
   clipper.generateNormals();
   
   if ( clipper.mVertexList.empty() )
      return false;

#ifdef DECALMANAGER_DEBUG
   mDebugPlanes.clear();
   mDebugPlanes.merge( clipper.mPlaneList );
#endif

   decal->mVertCount = clipper.mVertexList.size();
   decal->mIndxCount = clipper.mIndexList.size();
   
   Vector<Point3F> tmpPoints;

   tmpPoints.push_back(( objFwd * decalHalfSize ) + ( objRight * decalHalfSize ));
   tmpPoints.push_back(( objFwd * decalHalfSize ) + ( -objRight * decalHalfSize ));
   tmpPoints.push_back(( -objFwd * decalHalfSize ) + ( -objRight * decalHalfSize ));
   
   Point3F lowerLeft(( -objFwd * decalHalfSize ) + ( objRight * decalHalfSize ));

   projMat.inverse();

   _generateWindingOrder( lowerLeft, &tmpPoints );

   BiQuadToSqr quadToSquare( Point2F( lowerLeft.x, lowerLeft.y ),
                             Point2F( tmpPoints[0].x, tmpPoints[0].y ), 
                             Point2F( tmpPoints[1].x, tmpPoints[1].y ), 
                             Point2F( tmpPoints[2].x, tmpPoints[2].y ) );  

   Point2F uv( 0, 0 );
   Point3F vecX(0.0f, 0.0f, 0.0f);

   // Allocate memory for vert and index arrays
   _allocBuffers( decal );  
   
   Point3F vertPoint( 0, 0, 0 );

   for ( U32 i = 0; i < clipper.mVertexList.size(); i++ )
   {
      const ClippedPolyList::Vertex &vert = clipper.mVertexList[i];
      vertPoint = vert.point;

      // Transform this point to
      // object space to look up the
      // UV coordinate for this vertex.
      projMat.mulP( vertPoint );

      // Clamp the point to be within the quad.
      vertPoint.x = mClampF( vertPoint.x, -decalHalfSize.x, decalHalfSize.x );
      vertPoint.y = mClampF( vertPoint.y, -decalHalfSize.y, decalHalfSize.y );

      // Get our UV.
      uv = quadToSquare.transform( Point2F( vertPoint.x, vertPoint.y ) );

      const RectF &rect = decal->mDataBlock->texRect[decal->mTextureRectIdx];

      uv *= rect.extent;
      uv += rect.point;      

      // Set the world space vertex position.
      decal->mVerts[i].point = vert.point;
      
      decal->mVerts[i].texCoord.set( uv.x, uv.y );
      
      decal->mVerts[i].normal = clipper.mNormalList[i];
      
      decal->mVerts[i].normal.normalize();

      if( mFabs( decal->mVerts[i].normal.z ) > 0.8f ) 
         mCross( decal->mVerts[i].normal, Point3F( 1.0f, 0.0f, 0.0f ), &vecX );
      else if ( mFabs( decal->mVerts[i].normal.x ) > 0.8f )
         mCross( decal->mVerts[i].normal, Point3F( 0.0f, 1.0f, 0.0f ), &vecX );
      else if ( mFabs( decal->mVerts[i].normal.y ) > 0.8f )
         mCross( decal->mVerts[i].normal, Point3F( 0.0f, 0.0f, 1.0f ), &vecX );
   
      decal->mVerts[i].tangent = mCross( decal->mVerts[i].normal, vecX );
   }

   U32 curIdx = 0;
   for ( U32 j = 0; j < clipper.mPolyList.size(); j++ )
   {
      // Write indices for each Poly
      ClippedPolyList::Poly *poly = &clipper.mPolyList[j];                  

      AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" );

      decal->mIndices[curIdx] = clipper.mIndexList[poly->vertexStart];         
      curIdx++;
      decal->mIndices[curIdx] = clipper.mIndexList[poly->vertexStart + 1];            
      curIdx++;
      decal->mIndices[curIdx] = clipper.mIndexList[poly->vertexStart + 2];                
      curIdx++;
   } 

   if ( !edgeVerts )
      return true;

   Point3F tmpHullPt( 0, 0, 0 );
   Vector<Point3F> tmpHullPts;

   for ( U32 i = 0; i < clipper.mVertexList.size(); i++ )
   {
      const ClippedPolyList::Vertex &vert = clipper.mVertexList[i];
      tmpHullPt = vert.point;
      projMat.mulP( tmpHullPt );
      tmpHullPts.push_back( tmpHullPt );
   }

   edgeVerts->clear();
   U32 verts = _generateConvexHull( tmpHullPts, edgeVerts );
   edgeVerts->setSize( verts );

   projMat.inverse();
   for ( U32 i = 0; i < edgeVerts->size(); i++ )
      projMat.mulP( (*edgeVerts)[i] );

   return true;
}
Ejemplo n.º 23
0
//-----------------------------------------------------------------------------
// Mouse Events
//-----------------------------------------------------------------------------
void WindowInputGenerator::handleMouseMove( WindowId did, U32 modifier, S32 x, S32 y, bool isRelative )
{
    if( !mInputController || !mFocused )
        return;

    // jddTODO : Clean this up
    // CodeReview currently the Torque GuiCanvas deals with mouse input
    //  as relative movement, even when the cursor is visible.  Because
    //  of this there is an asinine bit of code in there that manages
    //  updating the cursor position on the class based on relative movement.
    //  Because of this we always have to generate and send off for processing
    //  relative events, even if the mouse is not locked.
    //  I'm considering removing this in the Canvas refactor, thoughts? [7/6/2007 justind]

    // Generate a base Movement along and Axis event
    InputEventInfo event;
    event.deviceType = MouseDeviceType;
    event.deviceInst = 0;
    event.objType    = SI_AXIS;
    event.modifier   = convertModifierBits(modifier);
    event.ascii      = 0;

    // Generate delta movement along each axis
    Point2F cursDelta;
    if(isRelative)
    {
        cursDelta.x = F32(x) * mPixelsPerMickey;
        cursDelta.y = F32(y) * mPixelsPerMickey;
    }
    else
    {
        cursDelta.x = F32(x - mLastCursorPos.x);
        cursDelta.y = F32(y - mLastCursorPos.y);
    }

    // If X axis changed, generate a relative event
    if(mFabs(cursDelta.x) > 0.1)
    {
        event.objInst    = SI_XAXIS;
        event.action     = SI_MOVE;
        event.fValue     = cursDelta.x;
        generateInputEvent(event);
    }

    // If Y axis changed, generate a relative event
    if(mFabs(cursDelta.y) > 0.1)
    {
        event.objInst    = SI_YAXIS;
        event.action     = SI_MOVE;
        event.fValue     = cursDelta.y;
        generateInputEvent(event);
    }

    //  CodeReview : If we're not relative, pass along a positional update
    //  so that the canvas can update it's internal cursor tracking
    //  point. [7/6/2007 justind]
    if( !isRelative )
    {
        if( mClampToWindow )
        {
            Point2I winExtent = mWindow->getClientExtent();
            x = mClampF(x, 0.0f, F32(winExtent.x  - 1));
            y = mClampF(y, 0.0f, F32(winExtent.y  - 1));

        }

        // When the window gains focus, we send a cursor position event
        if( mNotifyPosition )
        {
            mNotifyPosition = false;

            // We use SI_MAKE to signify that the position is being set, not relatively moved.
            event.action     = SI_MAKE;

            // X Axis
            event.objInst    = SI_XAXIS;
            event.fValue     = (F32)x;
            generateInputEvent(event);

            // Y Axis
            event.objInst = SI_YAXIS;
            event.fValue     = (F32)y;
            generateInputEvent(event);
        }

        mLastCursorPos = Point2I(x,y);

    }
    else
    {
        mLastCursorPos += Point2I(x,y);
        mNotifyPosition = true;
    }
}
Ejemplo n.º 24
0
void AITurretShape::_trackTarget(F32 dt)
{
   // Only on server
   if (isClientObject())
      return;

   // We can only track a target if we have one
   if (!mTarget.isValid())
      return;

   Point3F targetPos = mTarget.target->getBoxCenter();

   // Can we see the target?
   MatrixF aimMat;
   getAimTransform(aimMat);
   Point3F start;
   aimMat.getColumn(3, &start);
   RayInfo ri;

   Point3F sightPoint;

   disableCollision();
   bool los = _testTargetLineOfSight(start, mTarget.target, sightPoint);
   enableCollision();

   if (!los)
   {
      // Target is blocked.  Should we try to track from its last
      // known position and velocity?
      SimTime curTime = Sim::getCurrentTime();
      if ( (curTime - mTarget.lastSightTime) > (mDataBlock->trackLostTargetTime * 1000.0f) )
      {
         // Time's up.  Stop tracking.
         _cleanupTargetAndTurret();
         return;
      }
      
      // Use last known information to attempt to
      // continue to track target for a while.
      targetPos = mTarget.lastPos + mTarget.lastVel * F32(curTime - mTarget.lastSightTime) / 1000.0f;
   }
   else
   {
      // Target is visible

      // We only track targets that are alive
      if (mTarget.target->getDamageState() != Enabled)
      {
         // We can't track any more
         _cleanupTargetAndTurret();
         return;
      }

      targetPos = sightPoint;

      // Store latest target info
      mTarget.lastPos = targetPos;
      mTarget.lastVel = mTarget.target->getVelocity();
      mTarget.lastSightTime = Sim::getCurrentTime();
   }

   // Calculate angles to face the target, specifically the part that we can see
   VectorF toTarget;
   MatrixF mat;
   S32 node = mDataBlock->aimNode;
   if (node != -1)
   {
      // Get the current position of our node
      MatrixF* nodeTrans = &mShapeInstance->mNodeTransforms[node];
      Point3F currentPos;
      nodeTrans->getColumn(3, &currentPos);

      // Turn this into a matrix we can use to put the target
      // position into our space.
      MatrixF nodeMat(true);
      nodeMat.setColumn(3, currentPos);
      mat.mul(mObjToWorld, nodeMat);
      mat.affineInverse();
   }
   else
   {
      mat = mWorldToObj;
   }
   mat.mulP(targetPos, &toTarget);

   // lead the target
   F32 timeToTargetSquared = (mWeaponLeadVelocitySquared > 0) ? toTarget.lenSquared() / mWeaponLeadVelocitySquared : 0;
   if (timeToTargetSquared > 1.0)
   {
      targetPos = targetPos + (mTarget.lastVel * mSqrt(timeToTargetSquared));
      mat.mulP(targetPos, &toTarget);
   }

   F32 yaw, pitch;
   MathUtils::getAnglesFromVector(toTarget, yaw, pitch);
   if (yaw > M_PI_F)
      yaw = yaw - M_2PI_F;
   //if (pitch > M_PI_F)
   //   pitch = -(pitch - M_2PI_F);

   Point3F rot(-pitch, 0.0f, yaw);

   // If we have a rotation rate make sure we follow it
   if (mHeadingRate > 0)
   {
      F32 rate = mHeadingRate * dt;
      F32 rateCheck = mFabs(rot.z - mRot.z);
      if (rateCheck > rate)
      {
         // This will clamp the new value to the rate regardless if it
         // is increasing or decreasing.
         rot.z = mClampF(rot.z, mRot.z-rate, mRot.z+rate);
      }
   }
   if (mPitchRate > 0)
   {
      F32 rate = mPitchRate * dt;
      F32 rateCheck = mFabs(rot.x - mRot.x);
      if (rateCheck > rate)
      {
         // This will clamp the new value to the rate regardless if it
         // is increasing or decreasing.
         rot.x = mClampF(rot.x, mRot.x-rate, mRot.x+rate);
      }
   }

   // Test if the rotation to the target is outside of our limits
   if (_outsideLimits(rot))
   {
      // We can't track any more
      _cleanupTargetAndTurret();
      return;
   }

   // Test if the target is out of weapons range
   if (toTarget.lenSquared() > mWeaponRangeSquared)
   {
      // We can't track any more
      _cleanupTargetAndTurret();
      return;
   }

   mRot = rot;

   _setRotation( mRot );
   setMaskBits(TurretUpdateMask);
}
Ejemplo n.º 25
0
void TimeOfDay::_getSunColor( ColorF *outColor ) const
{
	  const COLOR_TARGET *ct = NULL;

   F32 ele = mClampF( M_2PI_F - mElevation, 0.0f, M_PI_F );
	  F32 phase = -1.0f;
	  F32 div;

   if (!mColorTargets.size())
   {
      outColor->set(1.0f,1.0f,1.0f);
      return;
   }

   if (mColorTargets.size() == 1)
   {
      ct = &mColorTargets[0];
      outColor->set(ct->color.red, ct->color.green, ct->color.blue);
      return;
   }

   //simple check
   if ( mColorTargets[0].elevation != 0.0f )
   {
      AssertFatal(0, "TimeOfDay::GetColor() - First elevation must be 0.0 radians")
      outColor->set(1.0f, 1.0f, 1.0f);
      //mBandMod = 1.0f;
      //mCurrentBandColor = color;
      return;
   }

   if ( mColorTargets[mColorTargets.size()-1].elevation != M_PI_F )
   {
      AssertFatal(0, "Celestails::GetColor() - Last elevation must be PI")
      outColor->set(1.0f, 1.0f, 1.0f);
      //mBandMod = 1.0f;
      //mCurrentBandColor = color;
      return;
   }

   //we need to find the phase and interp... also loop back around
   U32 count=0;
   for (;count < mColorTargets.size() - 1; count++)
   {
      const COLOR_TARGET *one = &mColorTargets[count];
      const COLOR_TARGET *two = &mColorTargets[count+1];

      if (ele >= one->elevation && ele <= two->elevation)
      {
			      div = two->elevation - one->elevation;
			
         //catch bad input divide by zero
         if ( mFabs( div ) < 0.01f )
            div = 0.01f;
			
			      phase = (ele - one->elevation) / div;
			      outColor->interpolate( one->color, two->color, phase );

			      //mCurrentBandColor.interpolate(one->bandColor, two->bandColor, phase);
			      //mBandMod = one->bandMod * (1.0f - phase) + two->bandMod * phase;

			      return;
		    }
	  }

	  AssertFatal(0,"This isn't supposed to happen");
}
Ejemplo n.º 26
0
bool LightFlareData::_testVisibility(const SceneRenderState *state, LightFlareState *flareState, U32 *outVisDelta, F32 *outOcclusionFade, Point3F *outLightPosSS)
{
   // Reflections use the results from the last forward
   // render so we don't need multiple queries.
   if ( state->isReflectPass() )
   {
      *outOcclusionFade = flareState->occlusion;
      *outVisDelta = Sim::getCurrentTime() - flareState->visChangedTime;
      return flareState->visible;
   }

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

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

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

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

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

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

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

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

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

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

      RayInfo rayInfo;

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

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

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

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

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

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

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

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

   return lightVisible;
}
Ejemplo n.º 27
0
S32 TSShapeInstance::setDetailFromDistance( const SceneRenderState *state, F32 scaledDistance )
{
   PROFILE_SCOPE( TSShapeInstance_setDetailFromDistance );

   // For debugging/metrics.
   smLastScaledDistance = scaledDistance;

   // Shortcut if the distance is really close or negative.
   if ( scaledDistance <= 0.0f )
   {
      mShape->mDetailLevelLookup[0].get( mCurrentDetailLevel, mCurrentIntraDetailLevel );
      return mCurrentDetailLevel;
   }

   // The pixel scale is used the linearly scale the lod
   // selection based on the viewport size.
   //
   // The original calculation from TGEA was...
   //
   // pixelScale = viewport.extent.x * 1.6f / 640.0f;
   //
   // Since we now work on the viewport height, assuming
   // 4:3 aspect ratio, we've changed the reference value
   // to 300 to be more compatible with legacy shapes.
   //
   const F32 pixelScale = state->getViewport().extent.y / 300.0f;

   // This is legacy DTS support for older "multires" based
   // meshes.  The original crossbow weapon uses this.
   //
   // If we have more than one detail level and the maxError
   // is non-negative then we do some sort of screen error 
   // metric for detail selection.
   //
   if ( mShape->mUseDetailFromScreenError )
   {
      // The pixel size of 1 meter at the input distance.
      F32 pixelRadius = state->projectRadius( scaledDistance, 1.0f ) * pixelScale;
      static const F32 smScreenError = 5.0f;
      return setDetailFromScreenError( smScreenError / pixelRadius );
   }

   // We're inlining SceneRenderState::projectRadius here to 
   // skip the unnessasary divide by zero protection.
   F32 pixelRadius = ( mShape->radius / scaledDistance ) * state->getWorldToScreenScale().y * pixelScale;
   F32 pixelSize = pixelRadius * smDetailAdjust;

   if ( pixelSize < smSmallestVisiblePixelSize ) {
      mCurrentDetailLevel = -1;
      return mCurrentDetailLevel;
   }

   if (  pixelSize > smSmallestVisiblePixelSize && 
         pixelSize <= mShape->mSmallestVisibleSize )
      pixelSize = mShape->mSmallestVisibleSize + 0.01f;

   // For debugging/metrics.
   smLastPixelSize = pixelSize;

   // Clamp it to an acceptable range for the lookup table.
   U32 index = (U32)mClampF( pixelSize, 0, mShape->mDetailLevelLookup.size() - 1 );

   // Check the lookup table for the detail and intra detail levels.
   mShape->mDetailLevelLookup[ index ].get( mCurrentDetailLevel, mCurrentIntraDetailLevel );

   // Restrict the chosen detail level by cutoff value.
   if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 )
   {
      S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL );
      if ( mCurrentDetailLevel < cutoff )
      {
         mCurrentDetailLevel = cutoff;
         mCurrentIntraDetailLevel = 1.0f;
      }
   }

   return mCurrentDetailLevel;
}
Ejemplo n.º 28
0
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 );
   }
}
Ejemplo n.º 29
0
void GuiObjectView::setOrbitDistance( F32 distance )
{
   // Make sure the orbit distance is within the acceptable range
   mOrbitDist = mClampF( distance, mMinOrbitDist, mMaxOrbitDist );
}
Ejemplo n.º 30
0
bool DecalManager::prepRenderImage(SceneState* state, const U32 stateKey,
                                   const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
   PROFILE_SCOPE( DecalManager_RenderDecals );

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

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

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

   PROFILE_START( DecalManager_RenderDecals_SphereTreeCull );

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

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

   mDecalQueue.clear();

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

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

      mDecalQueue.merge( decalSphere->mItems );
   }

   PROFILE_END();

   PROFILE_START( DecalManager_RenderDecals_Update );

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

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

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

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

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

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

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

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

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

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

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

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

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

   PROFILE_END();   

   if ( mDecalQueue.empty() )
      return false;

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

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

   PROFILE_SCOPE( DecalManager_RenderDecals_RenderBatch );

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

   RenderPassManager *renderPass = state->getRenderPass();

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

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

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

   baseRenderInst.type = RenderPassManager::RIT_Decal;      

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

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

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

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

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

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

         continue;
      }

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

         currentBatch = NULL;
         i--;
         continue;
      }

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

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

      // Allocate buffers...

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

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

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

      U32 lastDecal = currentBatch.startDecal + currentBatch.decalCount;

      U32 voffset = 0;
      U32 ioffset = 0;

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

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

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

         ioffset += dinst->mIndxCount;

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

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

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

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

      // Submit render inst...

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

      *ri = baseRenderInst;

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

      ri->matInst = currentBatch.matInst;

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

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

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

      renderPass->addInst( ri );
   }

   return false;
}