Exemple #1
0
// Used to build potential target list
static void _scanCallback( SceneObject* object, void* data )
{
   AITurretShape* turret = (AITurretShape*)data;

   ShapeBase* shape = dynamic_cast<ShapeBase*>(object);
   if (shape && shape->getDamageState() == ShapeBase::Enabled)
   {
      Point3F targetPos = shape->getBoxCenter();

      // Put target position into the scan node's space
      turret->mScanWorkspaceScanWorldMat.mulP(targetPos);

      // Is the target within scanning distance
      if (targetPos.lenSquared() > turret->getMaxScanDistanceSquared())
         return;

      // Make sure the target is in front and within the maximum
      // heading range
      Point2F targetXY(targetPos.x, targetPos.y);
      targetXY.normalizeSafe();
      F32 headingDot = mDot(Point2F(0, 1), targetXY);
      F32 heading = mAcos(headingDot);
      if (headingDot < 0 || heading > turret->getMaxScanHeading())
         return;

      // Make sure the target is in front and within the maximum
      // pitch range
      Point2F targetZY(targetPos.z, targetPos.y);
      targetZY.normalizeSafe();
      F32 pitchDot = mDot(Point2F(0, 1), targetZY);
      F32 pitch = mAcos(pitchDot);
      if (pitchDot < 0 || pitch > turret->getMaxScanPitch())
         return;

      turret->addPotentialTarget(shape);
   }
}
Exemple #2
0
QuatF & QuatF::interpolate( const QuatF & q1, const QuatF & q2, F32 t )
{
   //-----------------------------------
   // Calculate the cosine of the angle:

   double cosOmega = q1.dot( q2 );

   //-----------------------------------
   // adjust signs if necessary:

   F32 sign2;
   if ( cosOmega < 0.0 )
   {
      cosOmega = -cosOmega;
      sign2 = -1.0f;
   }
   else
      sign2 = 1.0f;

   //-----------------------------------
   // calculate interpolating coeffs:

   double scale1, scale2;
   if ( (1.0 - cosOmega) > 0.00001 )
   {
      // standard case
      double omega = mAcos(cosOmega);
      double sinOmega = mSin(omega);
      scale1 = mSin((1.0 - t) * omega) / sinOmega;
      scale2 = sign2 * mSin(t * omega) / sinOmega;
   }
   else
   {
      // if quats are very close, just do linear interpolation
      scale1 = 1.0 - t;
      scale2 = sign2 * t;
   }


   //-----------------------------------
   // actually do the interpolation:

   x = F32(scale1 * q1.x + scale2 * q2.x);
   y = F32(scale1 * q1.y + scale2 * q2.y);
   z = F32(scale1 * q1.z + scale2 * q2.z);
   w = F32(scale1 * q1.w + scale2 * q2.w);
   return *this;
}
Exemple #3
0
QuatF & QuatF::extrapolate( const QuatF & q1, const QuatF & q2, F32 t )
{
   // assert t >= 0 && t <= 1
   // q1 is value at time = 0
   // q2 is value at time = t
   // Computes quaternion at time = 1
   F64 flip,cos = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
   if (cos < 0.0)
   {
      cos = -cos;
      flip = -1.0;
   }
   else
      flip = 1.0;

   F64 s1,s2;
   if ((1.0 - cos) > 0.00001)
   {
      F64 om = mAcos(cos) / t;
      F64 sd = 1.0 / mSin(t * om);
      s1 = flip * mSin(om) * sd;
      s2 = mSin((1.0 - t) * om) * sd;
   }
   else
   {
      // If quats are very close, do linear interpolation
      s1 = flip / t;
      s2 = (1.0 - t) / t;
   }

   x = F32(s1 * q2.x - s2 * q1.x);
   y = F32(s1 * q2.y - s2 * q1.y);
   z = F32(s1 * q2.z - s2 * q1.z);
   w = F32(s1 * q2.w - s2 * q1.w);

   return *this;
}
Exemple #4
0
void DecalRoad::_generateEdges()
{      
   PROFILE_SCOPE( DecalRoad_generateEdges );

   //Con::warnf( "%s - generateEdges", isServerObject() ? "server" : "client" );

   if ( mNodes.size() > 0 )
   {
      // Set our object position to the first node.
      const Point3F &nodePt = mNodes.first().point;
      MatrixF mat( true );
      mat.setPosition( nodePt );
      Parent::setTransform( mat );

      // The server object has global bounds, which Parent::setTransform 
      // messes up so we must reset it.
      if ( isServerObject() )
      {
         mObjBox.minExtents.set(-1e10, -1e10, -1e10);
         mObjBox.maxExtents.set( 1e10,  1e10,  1e10);
      }
   }


   if ( mNodes.size() < 2 )
      return;

   // Ensure nodes are above the terrain height at their xy position
   for ( U32 i = 0; i < mNodes.size(); i++ )
   {
      _getTerrainHeight( mNodes[i].point );
   }

   // Now start generating edges...

   U32 nodeCount = mNodes.size();
   Point3F *positions = new Point3F[nodeCount];

   for ( U32 i = 0; i < nodeCount; i++ )
   {
      const RoadNode &node = mNodes[i];
      positions[i].set( node.point.x, node.point.y, node.width );
   }

   CatmullRom<Point3F> spline;
   spline.initialize( nodeCount, positions );
   delete [] positions;

   mEdges.clear();

   Point3F lastBreakVector(0,0,0);      
   RoadEdge slice;
   Point3F lastBreakNode;
   lastBreakNode = spline.evaluate(0.0f);

   for ( U32 i = 1; i < mNodes.size(); i++ )
   {
      F32 t1 = spline.getTime(i);
      F32 t0 = spline.getTime(i-1);

      F32 segLength = spline.arcLength( t0, t1 );

      U32 numSegments = mCeil( segLength / MIN_METERS_PER_SEGMENT );
      numSegments = getMax( numSegments, (U32)1 );
      F32 tstep = ( t1 - t0 ) / numSegments; 

      U32 startIdx = 0;
      U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments;

      for ( U32 j = startIdx; j < endIdx; j++ )
      {
         F32 t = t0 + tstep * j;
         Point3F splineNode = spline.evaluate(t);
         F32 width = splineNode.z;
         _getTerrainHeight( splineNode );

         Point3F toNodeVec = splineNode - lastBreakNode;
         toNodeVec.normalizeSafe();

         if ( lastBreakVector.isZero() )
            lastBreakVector = toNodeVec;

         F32 angle = mRadToDeg( mAcos( mDot( toNodeVec, lastBreakVector ) ) );

         if ( j == startIdx || 
            ( j == endIdx - 1 && i == mNodes.size() - 1 ) ||
            angle > mBreakAngle )
         {
            // Push back a spline node
            //slice.p1.set( splineNode.x, splineNode.y, 0.0f );
            //_getTerrainHeight( slice.p1 );
            slice.p1 = splineNode;
            slice.uvec.set(0,0,1);
            slice.width = width;            
            slice.parentNodeIdx = i-1;
            mEdges.push_back( slice );         

            lastBreakVector = splineNode - lastBreakNode;
            lastBreakVector.normalizeSafe();

            lastBreakNode = splineNode;
         }          
      }
   }

   /*
   for ( U32 i = 1; i < nodeCount; i++ )
   {
      F32 t0 = spline.getTime( i-1 );
      F32 t1 = spline.getTime( i );

      F32 segLength = spline.arcLength( t0, t1 );

      U32 numSegments = mCeil( segLength / mBreakAngle );
      numSegments = getMax( numSegments, (U32)1 );
      F32 tstep = ( t1 - t0 ) / numSegments;

      AssertFatal( numSegments > 0, "DecalRoad::_generateEdges, got zero segments!" );   

      U32 startIdx = 0;
      U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments;

      for ( U32 j = startIdx; j < endIdx; j++ )
      {
         F32 t = t0 + tstep * j;
         Point3F val = spline.evaluate(t);

         RoadEdge edge;         
         edge.p1.set( val.x, val.y, 0.0f );    
         _getTerrainHeight( val.x, val.y, edge.p1.z );    
         edge.uvec.set(0,0,1);
         edge.width = val.z;
         edge.parentNodeIdx = i-1;
         mEdges.push_back( edge );
      }   
   }
   */

   //
   // Calculate fvec and rvec for all edges
   //
   RoadEdge *edge = NULL;
   RoadEdge *nextEdge = NULL;

   for ( U32 i = 0; i < mEdges.size() - 1; i++ )
   {
      edge = &mEdges[i];
      nextEdge = &mEdges[i+1];

      edge->fvec = nextEdge->p1 - edge->p1;
      edge->fvec.normalize();

      edge->rvec = mCross( edge->fvec, edge->uvec );
      edge->rvec.normalize();
   }

   // Must do the last edge outside the loop
   RoadEdge *lastEdge = &mEdges[mEdges.size()-1];
   RoadEdge *prevEdge = &mEdges[mEdges.size()-2];
   lastEdge->fvec = prevEdge->fvec;
   lastEdge->rvec = prevEdge->rvec;


   //
   // Calculate p0/p2 for all edges
   //      
   for ( U32 i = 0; i < mEdges.size(); i++ )
   {
      RoadEdge *edge = &mEdges[i];
      edge->p0 = edge->p1 - edge->rvec * edge->width * 0.5f;
      edge->p2 = edge->p1 + edge->rvec * edge->width * 0.5f;
      _getTerrainHeight( edge->p0 );
      _getTerrainHeight( edge->p2 );
   }   
}
Exemple #5
0
void LightFlareData::prepRender( SceneRenderState *state, LightFlareState *flareState )
{
   PROFILE_SCOPE( LightFlareData_prepRender );

   const LightInfo *lightInfo = flareState->lightInfo;

   if (  mIsZero( flareState->fullBrightness ) ||
         mIsZero( lightInfo->getBrightness() ) )
      return;

   // Figure out the element count to render.
   U32 elementCount = mElementCount;
   const bool isReflectPass = state->isReflectPass();
   if ( isReflectPass )
   {
      // Then we don't render anything this pass.
      if ( !mRenderReflectPass )
         return;

      // Find the zero distance elements which make 
      // up the corona of the light flare.
      elementCount = 0.0f;
      for ( U32 i=0; i < mElementCount; i++ )
         if ( mIsZero( mElementDist[i] ) )
            elementCount++;
   }

   // Better have something to render.
   if ( elementCount == 0 )
      return;
  
   U32 visDelta = U32_MAX;
   F32 occlusionFade = 1.0f;
   Point3F lightPosSS;
   bool lightVisible = _testVisibility( state, flareState, &visDelta, &occlusionFade, &lightPosSS );
   
   // We can only skip rendering if the light is not 
   // visible, and it has elapsed the fade out time.
   if (  mIsZero( occlusionFade ) ||
         !lightVisible && visDelta > FadeOutTime )
      return;

   const RectI &viewport = GFX->getViewport();
   Point3F oneOverViewportExtent( 1.0f / (F32)viewport.extent.x, 1.0f / (F32)viewport.extent.y, 0.0f );

   // Really convert it to screen space.
   lightPosSS.x -= viewport.point.x;
   lightPosSS.y -= viewport.point.y;
   lightPosSS *= oneOverViewportExtent;
   lightPosSS = ( lightPosSS * 2.0f ) - Point3F::One;
   lightPosSS.y = -lightPosSS.y;
   lightPosSS.z = 0.0f;

   // Take any projection offset into account so that the point where the flare's
   // elements converge is at the 'eye' point rather than the center of the viewport.
   const Point2F& projOffset = state->getCameraFrustum().getProjectionOffset();
   Point3F flareVec( -lightPosSS + Point3F(projOffset.x, projOffset.y, 0.0f) );
   const F32 flareLength = flareVec.len();
   if ( flareLength > 0.0f )
      flareVec *= 1.0f / flareLength;

   // Setup the flare quad points.
   Point3F rotatedBasePoints[4];
   dMemcpy(rotatedBasePoints, sBasePoints, sizeof( sBasePoints ));

   // Rotate the flare quad.
   F32 rot = mAcos( -1.0f * flareVec.x );
   rot *= flareVec.y > 0.0f ? -1.0f : 1.0f;
   MathUtils::vectorRotateZAxis( rot, rotatedBasePoints, 4 );

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

   // Scale based on the current light brightness compared to its normal output.
   F32 lightSourceBrightnessScale = lightInfo->getBrightness() / flareState->fullBrightness;

   const Point3F &camPos = state->getCameraPosition();
   const Point3F &lightPos = flareState->lightMat.getPosition();   
   const bool isVectorLight = lightInfo->getType() == LightInfo::Vector;

   // Scale based on world space distance from camera to light source.
   F32 distToCamera = ( camPos - lightPos ).len();
   F32 lightSourceWSDistanceScale = isVectorLight && distToCamera > 0.0f ? 1.0f : getMin( 10.0f / distToCamera, 10.0f );

   // Scale based on screen space distance from screen position of light source to the screen center.
   F32 lightSourceSSDistanceScale = getMax( ( 1.5f - flareLength ) / 1.5f, 0.0f );

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

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

   if ( mIsZero( lightSourceIntensityScale ) )
      return;

   // The baseColor which modulates the color of all elements.
   //
   // These are the factors which affect the "alpha" of the flare effect.
   // Modulate more in as appropriate.
   ColorF baseColor = ColorF::WHITE * lightSourceBrightnessScale * occlusionFade;

   // Setup the vertex buffer for the maximum flare elements.
   const U32 vertCount = 4 * mElementCount;
   if (  flareState->vertBuffer.isNull() || 
         flareState->vertBuffer->mNumVerts != vertCount )
         flareState->vertBuffer.set( GFX, vertCount, GFXBufferTypeDynamic );

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

   const Point2F oneOverTexSize( 1.0f / (F32)mFlareTexture.getWidth(), 1.0f / (F32)mFlareTexture.getHeight() );

   for ( U32 i = 0; i < mElementCount; i++ )
   {      
      // Skip non-zero elements for reflections.
      if ( isReflectPass && mElementDist[i] > 0.0f )
         continue;

      Point3F *basePos = mElementRotate[i] ? rotatedBasePoints : sBasePoints;

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

      Point3F pos( lightPosSS + flareVec * mElementDist[i] * flareLength );

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

      AssertFatal( size.x >= 0.0f, "LightFlareData::prepRender - Got a negative element size?" );

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

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

      size.x = getMax( size.x, 1.0f );
      size.y = getMax( size.y, 1.0f );
      size *= oneOverViewportExtent;

      vert->color = color;
      vert->point = ( basePos[0] * size ) + pos;      
      vert->texCoord.set( texCoordMin.x, texCoordMax.y );
      vert++;

      vert->color = color;
      vert->point = ( basePos[1] * size ) + pos;
      vert->texCoord.set( texCoordMax.x, texCoordMax.y );
      vert++;

      vert->color = color;
      vert->point = ( basePos[2] * size ) + pos;
      vert->texCoord.set( texCoordMax.x, texCoordMin.y );
      vert++;

      vert->color = color;
      vert->point = ( basePos[3] * size ) + pos;
      vert->texCoord.set( texCoordMin.x, texCoordMin.y );
      vert++;
   }   

   flareState->vertBuffer.unlock();   

   RenderPassManager *rpm = state->getRenderPass();

   // Create and submit the render instance.   
   ParticleRenderInst *ri = rpm->allocInst<ParticleRenderInst>();
   ri->type = RenderPassManager::RIT_Particle;
   ri->vertBuff = &flareState->vertBuffer;
   ri->primBuff = &mFlarePrimBuffer;
   ri->translucentSort = true;
   ri->sortDistSq = ( lightPos - camPos ).lenSquared();
   ri->modelViewProj = &MatrixF::Identity;
   ri->bbModelViewProj = &MatrixF::Identity;
   ri->count = elementCount;
   ri->blendStyle = ParticleRenderInst::BlendGreyscale;
   ri->diffuseTex = mFlareTexture;
   ri->softnessDistance = 1.0f; 
   ri->defaultKey = ri->diffuseTex ? (U32)ri->diffuseTex : (U32)ri->vertBuff; // Sort by texture too.

   // NOTE: Offscreen partical code is currently disabled.
   ri->systemState = PSS_AwaitingHighResDraw;

   rpm->addInst( ri );
}
//--------------------------------------------------------------------------
// from Graphics Gems I: pp 738-742
U32 mSolveCubic_c(F32 a, F32 b, F32 c, F32 d, F32 * x)
{
   if(mIsZero(a))
      return(mSolveQuadratic(b, c, d, x));

   // normal form: x^3 + Ax^2 + BX + C = 0
   F32 A = b / a;
   F32 B = c / a;
   F32 C = d / a;

   // substitute x = y - A/3 to eliminate quadric term and depress
   // the cubic equation to (x^3 + px + q = 0)
   F32 A2 = A * A;
   F32 A3 = A2 * A;

   F32 p = (1.f/3.f) * (((-1.f/3.f) * A2) + B);
   F32 q = (1.f/2.f) * (((2.f/27.f) * A3) - ((1.f/3.f) * A * B) + C);

   // use Cardano's fomula to solve the depressed cubic
   F32 p3 = p * p * p;
   F32 q2 = q * q;

   F32 D = q2 + p3;

   U32 num = 0;

   if(mIsZero(D))          // 1 or 2 solutions
   {
      if(mIsZero(q)) // 1 triple solution
      {
         x[0] = 0.f;
         num = 1;
      }
      else // 1 single and 1 double
      {
         F32 u = mCbrt(-q);
         x[0] = 2.f * u;
         x[1] = -u;
         num = 2;
      }
   }
   else if(D < 0.f)        // 3 solutions: casus irreducibilis
   {
      F32 phi = (1.f/3.f) * mAcos(-q / mSqrt(-p3));
      F32 t = 2.f * mSqrt(-p);

      x[0] = t * mCos(phi);
      x[1] = -t * mCos(phi + (M_PI / 3.f));
      x[2] = -t * mCos(phi - (M_PI / 3.f));
      num = 3;
   }
   else                    // 1 solution
   {
      F32 sqrtD = mSqrt(D);
      F32 u = mCbrt(sqrtD - q);
      F32 v = -mCbrt(sqrtD + q);

      x[0] = u + v;
      num = 1;
   }

   // resubstitute
   F32 sub = (1.f/3.f) * A;
   for(U32 i = 0; i < num; i++)
      x[i] -= sub;

   // sort the roots
   for(S32 j = 0; j < (num - 1); j++)
      for(S32 k = j + 1; k < num; k++)
         if(x[k] < x[j])
            swap(x[k], x[j]);

   return(num);
}
Exemple #7
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;
}
void LightFlareData::prepRender( SceneState *state, LightFlareState *flareState )
{    
   PROFILE_SCOPE( LightFlareData_prepRender );

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

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

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

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

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

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

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

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

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

      bool needsRaycast = true;

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

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

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

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

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

         RenderPassManager *pass = state->getRenderPass();

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

         Point3F scale( Point3F::One );

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

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

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

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

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

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

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

         bool fps = conn->isFirstPerson();

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

         RayInfo rayInfo;

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

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

      lightVisible = onscreen && occlusionFade > 0.0f;

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

      U32 currentTime = Sim::getCurrentTime();

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

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

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

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

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

   if ( elementCount == 0 )
      return;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      elementColor.clamp();

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

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

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

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

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

   flareState->vertBuffer.unlock();   

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

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

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

   ri->count = elementCount;

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

   ri->blendStyle = ParticleRenderInst::BlendGreyscale;

   ri->diffuseTex = &*(mFlareTexture);

   ri->softnessDistance = 1.0f; 

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

   renderManager->addInst( ri );
}