void Sim3DAudioEvent::unpack(NetConnection *con, BitStream *bstream)
{
   SimObjectId id = bstream->readInt(DataBlockObjectIdBitSize) + DataBlockObjectIdFirst;
   Sim::findObject(id, mProfile);

   if (bstream->readFlag()) {
      QuatF q;
      q.x = bstream->readFloat(SoundRotBits);
      q.y = bstream->readFloat(SoundRotBits);
      q.z = bstream->readFloat(SoundRotBits);
      F32 value = ((q.x * q.x) + (q.y * q.y) + (q.z * q.z));
// #ifdef __linux
      // Hmm, this should never happen, but it does...
      if ( value > 1.f )
         value = 1.f;
// #endif
      q.w = mSqrt(1.f - value);
      if (bstream->readFlag())
         q.w = -q.w;
      q.setMatrix(&mTransform);
   }
   else
      mTransform.identity();

   Point3F pos;
   bstream->readCompressedPoint(&pos,SoundPosAccuracy);
   mTransform.setColumn(3, pos);
}
static U32 mSolveQuadratic_c(F32 a, F32 b, F32 c, F32 * x)
{
   // really linear?
   if(mIsZero(a))
      return(mSolveLinear(b, c, x));

   // get the descriminant:   (b^2 - 4ac)
   F32 desc = (b * b) - (4.f * a * c);

   // solutions:
   // desc < 0:   two imaginary solutions
   // desc > 0:   two real solutions (b +- sqrt(desc)) / 2a
   // desc = 0:   one real solution (b / 2a)
   if(mIsZero(desc))
   {
      x[0] = b / (2.f * a);
      return(1);
   }
   else if(desc > 0.f)
   {
      F32 sqrdesc = mSqrt(desc);
      F32 den = (2.f * a);
      x[0] = (-b + sqrdesc) / den;
      x[1] = (-b - sqrdesc) / den;

      if(x[1] < x[0])
         swap(x[0], x[1]);

      return(2);
   }
   else
      return(0);
}
Beispiel #3
0
//--------------------------------------
static void m_point3F_normalize_f_C(F32 *p, F32 val)
{
   F32 factor = val / mSqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2] );
   p[0] *= factor;
   p[1] *= factor;
   p[2] *= factor;
}
Box3F PSSMLightShadowMap::_calcClipSpaceAABB(const Frustum& f, const MatrixF& transform, F32 farDist)
{
   // Calculate frustum center
   Point3F center(0,0,0);
   for (U32 i = 0; i < 8; i++)   
   {
      const Point3F& pt = f.getPoints()[i];      
      center += pt;
   }
   center /= 8;

   // Calculate frustum bounding sphere radius
   F32 radius = 0.0f;
   for (U32 i = 0; i < 8; i++)      
      radius = getMax(radius, (f.getPoints()[i] - center).lenSquared());
   radius = mFloor( mSqrt(radius) );
      
   // Now build box for sphere
   Box3F result;
   Point3F radiusBox(radius, radius, radius);
   result.minExtents = center - radiusBox;
   result.maxExtents = center + radiusBox;

   // Transform to light projection space
   transform.mul(result);
   
   return result;   
}
Point2F BiQuadToSqr::transform( const Point2F &p ) const
{
    Point2F kA = m_kP00 - p;

    F32 fAB = mDotPerp( kA, m_kB );
    F32 fAC = mDotPerp( kA, m_kC);

    // 0 = ac*bc+(bc^2+ac*bd-ab*cd)*s+bc*bd*s^2 = k0 + k1*s + k2*s^2
    F32 fK0 = fAC*m_fBC;
    F32 fK1 = m_fBC*m_fBC + fAC*m_fBD - fAB*m_fCD;
    F32 fK2 = m_fBC*m_fBD;

    if (mFabs(fK2) > POINT_EPSILON)
    {
        // s-equation is quadratic
        F32 fInv = 0.5f/fK2;
        F32 fDiscr = fK1*fK1 - 4.0f*fK0*fK2;
        F32 fRoot = mSqrt( mFabs(fDiscr) );

        Point2F kResult0( 0, 0 );
        kResult0.x = (-fK1 - fRoot)*fInv;
        kResult0.y = fAB/(m_fBC + m_fBD*kResult0.x);
        F32 fDeviation0 = deviation(kResult0);
        if ( fDeviation0 == 0.0f )
            return kResult0;

        Point2F kResult1( 0, 0 );
        kResult1.x = (-fK1 + fRoot)*fInv;
        kResult1.y = fAB/(m_fBC + m_fBD*kResult1.x);
        F32 fDeviation1 = deviation(kResult1);
        if ( fDeviation1 == 0.0f )
            return kResult1;

        if (fDeviation0 <= fDeviation1)
        {
            if ( fDeviation0 < POINT_EPSILON )
                return kResult0;
        }
        else
        {
            if ( fDeviation1 < POINT_EPSILON )
                return kResult1;
        }
    }
    else
    {
        // s-equation is linear
        Point2F kResult( 0, 0 );

        kResult.x = -fK0/fK1;
        kResult.y = fAB/(m_fBC + m_fBD*kResult.x);
        F32 fDeviation = deviation(kResult);
        if ( fDeviation < POINT_EPSILON )
            return kResult;
    }

    // point is outside the quadrilateral, return invalid
    return Point2F(F32_MAX,F32_MAX);
}
Beispiel #6
0
QuatF & QuatF::set( const MatrixF & mat )
{
   PROFILE_SCOPE( QuatF_set_MatrixF );

   F32 const *m = mat;

   F32 trace = m[idx(0, 0)] + m[idx(1, 1)] + m[idx(2, 2)];
   if (trace > 0.0f) 
   {
      F32 s = mSqrt(trace + F32(1));
      w = s * 0.5f;
      s = 0.5f / s;
      x = (m[idx(1,2)] - m[idx(2,1)]) * s;
      y = (m[idx(2,0)] - m[idx(0,2)]) * s;
      z = (m[idx(0,1)] - m[idx(1,0)]) * s;
   } 
   else 
   {
      F32* q = &x;
      U32 i = 0;
      if (m[idx(1, 1)] > m[idx(0, 0)]) i = 1;
      if (m[idx(2, 2)] > m[idx(i, i)]) i = 2;
      U32 j = (i + 1) % 3;
      U32 k = (j + 1) % 3;

      F32 s = mSqrt((m[idx(i, i)] - (m[idx(j, j)] + m[idx(k, k)])) + 1.0f);
      q[i] = s * 0.5f;
      s = 0.5f / s;
      q[j] = (m[idx(i,j)] + m[idx(j,i)]) * s;
      q[k] = (m[idx(i,k)] + m[idx(k, i)]) * s;
      w = (m[idx(j,k)] - m[idx(k, j)]) * s;
   }

   // Added to resolve issue #2230
   normalize();

   return *this;
}
Beispiel #7
0
QuatF & QuatF::normalize()
{
   F32 l = mSqrt( x*x + y*y + z*z + w*w );
   if( l == 0.0f )
      identity();
   else
   {
      x /= l;
      y /= l;
      z /= l;
      w /= l;
   }
   return *this;
}
Beispiel #8
0
QuatF & QuatF::shortestArc( const VectorF &a, const VectorF &b )
{
   // From Game Programming Gems pg. 217
   VectorF c = mCross( a, b );
   F32 d = mDot( a, b );
   F32 s = mSqrt( ( 1 + d ) * 2 );

   x = c.x / s;
   y = c.y / s;
   z = c.z / s;
   w = s / 2.f; 

   return *this;
}
Beispiel #9
0
//--------------------------------------
static void m_point3F_normalize_C(F32 *p)
{
   F32 squared = p[0]*p[0] + p[1]*p[1] + p[2]*p[2];
   // This can happen in Container::castRay -> ForceFieldBare::castRay
   //AssertFatal(squared != 0.0, "Error, zero length vector normalized!");
   if (squared != 0.0f) {
      F32 factor = 1.0f / mSqrt(squared);
      p[0] *= factor;
      p[1] *= factor;
      p[2] *= factor;
   } else {
      p[0] = 0.0f;
      p[1] = 0.0f;
      p[2] = 1.0f;
   }
}
Beispiel #10
0
void TSPartInstance::updateBounds()
{
   // run through meshes and brute force it?
   Box3F bounds;
   mBounds.minExtents.set( 10E30f, 10E30f, 10E30f);
   mBounds.maxExtents.set(-10E30f,-10E30f,-10E30f);
   for (S32 i=0; i<mMeshObjects.size(); i++)
   {
      if (mMeshObjects[i]->getMesh(0))
         mMeshObjects[i]->getMesh(0)->computeBounds(mMeshObjects[i]->getTransform(),bounds,mMeshObjects[i]->frame);
      mBounds.minExtents.setMin(bounds.minExtents);
      mBounds.maxExtents.setMax(bounds.maxExtents);
   }
   mCenter = mBounds.minExtents + mBounds.maxExtents;
   mCenter *= 0.5f;
   Point3F r = mBounds.maxExtents-mCenter;
   mRadius = mSqrt(mDot(r,r));
}
Beispiel #11
0
void ConvexFeature::testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol)
{
   F32 tolSquared = tol*tol;

   // Test edges against edges
   const Edge* edge = mEdgeList.begin();
   const Edge* end  = mEdgeList.end();
   for (; edge != end; edge++) {
      if (cList->getCount() >= CollisionList::MaxCollisions)
         return;

      const Point3F& s2 = mVertexList[edge->vertex[0]];
      const Point3F& e2 = mVertexList[edge->vertex[1]];

      // Get the distance and closest points
      Point3F i1,i2;
      F32 distance = sqrDistanceEdges(s1, e1, s2, e2, &i1, &i2);
      if (distance > tolSquared)
         continue;
      distance = mSqrt(distance);

      // Need to figure out how to orient the collision normal.
      // The current test involves checking to see whether the collision
      // points are contained within the convex volumes, which is slow.
      if (inVolume(i1) || cf->inVolume(i2))
         distance = -distance;

      // Contact normal
      VectorF normal = i1 - i2;
      if ( mIsZero( distance ) )
         normal.zero();
      else
         normal *= 1 / distance;

      // Return a collision
      Collision& info = cList->increment();
      info.point    = i1;
      info.normal   = normal;
      info.distance = distance;
      info.material = material;
      info.object   = object;
   }
}
static void _scopeCallback( SceneObject* object, void* data )
{
   if( !object->isScopeable() )
      return;

   ScopingInfo* info = reinterpret_cast< ScopingInfo* >( data );
   NetConnection* connection = info->connection;

   F32 difSq = ( object->getWorldSphere().center - info->scopePoint ).lenSquared();
   if( difSq < info->scopeDistSquared )
   {
      // Not even close, it's in...
      connection->objectInScope( object );
   }
   else
   {
      // Check a little more closely...
      F32 realDif = mSqrt( difSq );
      if( realDif - object->getWorldSphere().radius < info->scopeDist)
         connection->objectInScope( object );
   }
}
Beispiel #13
0
/**
 * This method calculates the moves for the AI player
 *
 * @param movePtr Pointer to move the move list into
 */
bool AIPlayer::getAIMove(Move *movePtr)
{
   *movePtr = NullMove;

   // Use the eye as the current position.
   MatrixF eye;
   getEyeTransform(&eye);
   Point3F location = eye.getPosition();
   Point3F rotation = getRotation();

#ifdef TORQUE_NAVIGATION_ENABLED
   if(mDamageState == Enabled)
   {
      if(mMoveState != ModeStop)
         updateNavMesh();
      if(!mFollowData.object.isNull())
      {
         if(mPathData.path.isNull())
         {
            if((getPosition() - mFollowData.object->getPosition()).len() > mFollowData.radius)
               followObject(mFollowData.object, mFollowData.radius);
         }
         else
         {
            if((mPathData.path->mTo - mFollowData.object->getPosition()).len() > mFollowData.radius)
               repath();
            else if((getPosition() - mFollowData.object->getPosition()).len() < mFollowData.radius)
            {
               clearPath();
               mMoveState = ModeStop;
            throwCallback("onTargetInRange");
            }
            else if((getPosition() - mFollowData.object->getPosition()).len() < mAttackRadius)
            {
            throwCallback("onTargetInFiringRange");
            }
         }
      }
   }
#endif // TORQUE_NAVIGATION_ENABLED

   // Orient towards the aim point, aim object, or towards
   // our destination.
   if (mAimObject || mAimLocationSet || mMoveState != ModeStop) 
   {
      // Update the aim position if we're aiming for an object
      if (mAimObject)
         mAimLocation = mAimObject->getPosition() + mAimOffset;
      else
         if (!mAimLocationSet)
            mAimLocation = mMoveDestination;

      F32 xDiff = mAimLocation.x - location.x;
      F32 yDiff = mAimLocation.y - location.y;

      if (!mIsZero(xDiff) || !mIsZero(yDiff)) 
      {
         // First do Yaw
         // use the cur yaw between -Pi and Pi
         F32 curYaw = rotation.z;
         while (curYaw > M_2PI_F)
            curYaw -= M_2PI_F;
         while (curYaw < -M_2PI_F)
            curYaw += M_2PI_F;

         // find the yaw offset
         F32 newYaw = mAtan2( xDiff, yDiff );
         F32 yawDiff = newYaw - curYaw;

         // make it between 0 and 2PI
         if( yawDiff < 0.0f )
            yawDiff += M_2PI_F;
         else if( yawDiff >= M_2PI_F )
            yawDiff -= M_2PI_F;

         // now make sure we take the short way around the circle
         if( yawDiff > M_PI_F )
            yawDiff -= M_2PI_F;
         else if( yawDiff < -M_PI_F )
            yawDiff += M_2PI_F;

         movePtr->yaw = yawDiff;

         // Next do pitch.
         if (!mAimObject && !mAimLocationSet) 
         {
            // Level out if were just looking at our next way point.
            Point3F headRotation = getHeadRotation();
            movePtr->pitch = -headRotation.x;
         }
         else 
         {
            // This should be adjusted to run from the
            // eye point to the object's center position. Though this
            // works well enough for now.
            F32 vertDist = mAimLocation.z - location.z;
            F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff);
            F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f );
            if (mFabs(newPitch) > 0.01f) 
            {
               Point3F headRotation = getHeadRotation();
               movePtr->pitch = newPitch - headRotation.x;
            }
         }
      }
   }
   else 
   {
      // Level out if we're not doing anything else
      Point3F headRotation = getHeadRotation();
      movePtr->pitch = -headRotation.x;
   }

   // Move towards the destination
   if (mMoveState != ModeStop) 
   {
      F32 xDiff = mMoveDestination.x - location.x;
      F32 yDiff = mMoveDestination.y - location.y;

      // Check if we should mMove, or if we are 'close enough'
      if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) 
      {
         mMoveState = ModeStop;
         onReachDestination();
      }
      else 
      {
         // Build move direction in world space
         if (mIsZero(xDiff))
            movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f;
         else
            if (mIsZero(yDiff))
               movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f;
            else
               if (mFabs(xDiff) > mFabs(yDiff)) 
               {
                  F32 value = mFabs(yDiff / xDiff);
                  movePtr->y = (location.y > mMoveDestination.y) ? -value : value;
                  movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f;
               }
               else 
               {
                  F32 value = mFabs(xDiff / yDiff);
                  movePtr->x = (location.x > mMoveDestination.x) ? -value : value;
                  movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f;
               }

         // Rotate the move into object space (this really only needs
         // a 2D matrix)
         Point3F newMove;
         MatrixF moveMatrix;
         moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw)));
         moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove );
         movePtr->x = newMove.x;
         movePtr->y = newMove.y;

         // Set movement speed.  We'll slow down once we get close
         // to try and stop on the spot...
         if (mMoveSlowdown) 
         {
            F32 speed = mMoveSpeed;
            F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff);
            F32 maxDist = mMoveTolerance*2;
            if (dist < maxDist)
               speed *= dist / maxDist;
            movePtr->x *= speed;
            movePtr->y *= speed;

            mMoveState = ModeSlowing;
         }
         else 
         {
            movePtr->x *= mMoveSpeed;
            movePtr->y *= mMoveSpeed;

            mMoveState = ModeMove;
         }

         if (mMoveStuckTestCountdown > 0)
            --mMoveStuckTestCountdown;
         else
         {
            // We should check to see if we are stuck...
            F32 locationDelta = (location - mLastLocation).len();
            if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) 
            {
               // If we are slowing down, then it's likely that our location delta will be less than
               // our move stuck tolerance. Because we can be both slowing and stuck
               // we should TRY to check if we've moved. This could use better detection.
               if ( mMoveState != ModeSlowing || locationDelta == 0 )
               {
                  mMoveState = ModeStuck;
                  onStuck();
               }
            }
         }
      }
   }

   // Test for target location in sight if it's an object. The LOS is
   // run from the eye position to the center of the object's bounding,
   // which is not very accurate.
   if (mAimObject)
   {
      if (checkInLos(mAimObject.getPointer()))
      {
         if (!mTargetInLOS)
         {
            throwCallback( "onTargetEnterLOS" );
            mTargetInLOS = true;
         }
   }
      else if (mTargetInLOS)
      {
            throwCallback( "onTargetExitLOS" );
            mTargetInLOS = false;
         }
      }

   Pose desiredPose = mPose;

   if ( mSwimming )  
      desiredPose = SwimPose;   
   else if ( mAiPose == 1 && canCrouch() )   
      desiredPose = CrouchPose;  
   else if ( mAiPose == 2 && canProne() )  
      desiredPose = PronePose;  
   else if ( mAiPose == 3 && canSprint() )  
      desiredPose = SprintPose;  
   else if ( canStand() )  
      desiredPose = StandPose;  
  
   setPose( desiredPose );
   
   // Replicate the trigger state into the move so that
   // triggers can be controlled from scripts.
   for( U32 i = 0; i < MaxTriggerKeys; i++ )
      movePtr->trigger[ i ] = getImageTriggerState( i );

#ifdef TORQUE_NAVIGATION_ENABLED
   if(mJump == Now)
   {
      movePtr->trigger[2] = true;
      mJump = None;
   }
   else if(mJump == Ledge)
   {
      // If we're not touching the ground, jump!
      RayInfo info;
      if(!getContainer()->castRay(getPosition(), getPosition() - Point3F(0, 0, 0.4f), StaticShapeObjectType, &info))
      {
         movePtr->trigger[2] = true;
         mJump = None;
      }
   }
#endif // TORQUE_NAVIGATION_ENABLED

   mLastLocation = location;

   return true;
}
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 );
}
Beispiel #15
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);
}
Beispiel #16
0
void ScatterSky::_initVBIB()
{
   // Vertex Buffer...
   U32 vertStride = 50;
   U32 strideMinusOne = vertStride - 1;
   mVertCount = vertStride * vertStride;
   mPrimCount = strideMinusOne * strideMinusOne * 2;

   Point3F vertScale( 16.0f, 16.0f, 4.0f );

   F32 zOffset = -( mCos( mSqrt( 1.0f ) ) + 0.01f );

   mVB.set( GFX, mVertCount, GFXBufferTypeStatic );
   ScatterSkyVertex *pVert = mVB.lock();

   for ( U32 y = 0; y < vertStride; y++ )
   {
      F32 v = ( (F32)y / (F32)strideMinusOne - 0.5f ) * 2.0f;

      for ( U32 x = 0; x < vertStride; x++ )
      {
         F32 u = ( (F32)x / (F32)strideMinusOne - 0.5f ) * 2.0f;

         F32 sx = u;
         F32 sy = v;
         F32 sz = (mCos( mSqrt( sx*sx + sy*sy ) ) * 1.0f) + zOffset;
         //F32 sz = 1.0f;
         pVert->point.set( sx, sy, sz );
         pVert->point *= vertScale;

         pVert->point.normalize();
         pVert->point *= 200000.0f;

         pVert++;
      }
   }

   mVB.unlock();

   // Primitive Buffer...
   mPrimBuffer.set( GFX, mPrimCount * 3, mPrimCount, GFXBufferTypeStatic );

   U16 *pIdx = NULL;
   mPrimBuffer.lock(&pIdx);
   U32 curIdx = 0;

   for ( U32 y = 0; y < strideMinusOne; y++ )
   {
      for ( U32 x = 0; x < strideMinusOne; x++ )
      {
         U32 offset = x + y * vertStride;

         pIdx[curIdx] = offset;
         curIdx++;
         pIdx[curIdx] = offset + 1;
         curIdx++;
         pIdx[curIdx] = offset + vertStride + 1;
         curIdx++;

         pIdx[curIdx] = offset;
         curIdx++;
         pIdx[curIdx] = offset + vertStride + 1;
         curIdx++;
         pIdx[curIdx] = offset + vertStride;
         curIdx++;
      }
   }

   mPrimBuffer.unlock();
}
/**
 * This method calculates the moves for the AI player
 *
 * @param movePtr Pointer to move the move list into
 */
bool AIPlayer::getAIMove(Move *movePtr)
{
   *movePtr = NullMove;

   // Use the eye as the current position.
   MatrixF eye;
   getEyeTransform(&eye);
   Point3F location = eye.getPosition();
   Point3F rotation = getRotation();

   // Orient towards the aim point, aim object, or towards
   // our destination.
   if (mAimObject || mAimLocationSet || mMoveState != ModeStop) 
   {
      // Update the aim position if we're aiming for an object
      if (mAimObject)
         mAimLocation = mAimObject->getPosition() + mAimOffset;
      else
         if (!mAimLocationSet)
            mAimLocation = mMoveDestination;

      F32 xDiff = mAimLocation.x - location.x;
      F32 yDiff = mAimLocation.y - location.y;

      if (!mIsZero(xDiff) || !mIsZero(yDiff)) 
      {
         // First do Yaw
         // use the cur yaw between -Pi and Pi
         F32 curYaw = rotation.z;
         while (curYaw > M_2PI_F)
            curYaw -= M_2PI_F;
         while (curYaw < -M_2PI_F)
            curYaw += M_2PI_F;

         // find the yaw offset
         F32 newYaw = mAtan2( xDiff, yDiff );
         F32 yawDiff = newYaw - curYaw;

         // make it between 0 and 2PI
         if( yawDiff < 0.0f )
            yawDiff += M_2PI_F;
         else if( yawDiff >= M_2PI_F )
            yawDiff -= M_2PI_F;

         // now make sure we take the short way around the circle
         if( yawDiff > M_PI_F )
            yawDiff -= M_2PI_F;
         else if( yawDiff < -M_PI_F )
            yawDiff += M_2PI_F;

         movePtr->yaw = yawDiff;

         // Next do pitch.
         if (!mAimObject && !mAimLocationSet) 
         {
            // Level out if were just looking at our next way point.
            Point3F headRotation = getHeadRotation();
            movePtr->pitch = -headRotation.x;
         }
         else 
         {
            // This should be adjusted to run from the
            // eye point to the object's center position. Though this
            // works well enough for now.
            F32 vertDist = mAimLocation.z - location.z;
            F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff);
            F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f );
            if (mFabs(newPitch) > 0.01f) 
            {
               Point3F headRotation = getHeadRotation();
               movePtr->pitch = newPitch - headRotation.x;
            }
         }
      }
   }
   else 
   {
      // Level out if we're not doing anything else
      Point3F headRotation = getHeadRotation();
      movePtr->pitch = -headRotation.x;
   }

   // Move towards the destination
   if (mMoveState != ModeStop) 
   {
      F32 xDiff = mMoveDestination.x - location.x;
      F32 yDiff = mMoveDestination.y - location.y;

      // Check if we should mMove, or if we are 'close enough'
      if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) 
      {
         mMoveState = ModeStop;
         throwCallback("onReachDestination");
      }
      else 
      {
         // Build move direction in world space
         if (mIsZero(xDiff))
            movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f;
         else
            if (mIsZero(yDiff))
               movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f;
            else
               if (mFabs(xDiff) > mFabs(yDiff)) 
               {
                  F32 value = mFabs(yDiff / xDiff);
                  movePtr->y = (location.y > mMoveDestination.y) ? -value : value;
                  movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f;
               }
               else 
               {
                  F32 value = mFabs(xDiff / yDiff);
                  movePtr->x = (location.x > mMoveDestination.x) ? -value : value;
                  movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f;
               }

         // Rotate the move into object space (this really only needs
         // a 2D matrix)
         Point3F newMove;
         MatrixF moveMatrix;
         moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw)));
         moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove );
         movePtr->x = newMove.x;
         movePtr->y = newMove.y;

         // Set movement speed.  We'll slow down once we get close
         // to try and stop on the spot...
         if (mMoveSlowdown) 
         {
            F32 speed = mMoveSpeed;
            F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff);
            F32 maxDist = 5.0f;
            if (dist < maxDist)
               speed *= dist / maxDist;
            movePtr->x *= speed;
            movePtr->y *= speed;

            mMoveState = ModeSlowing;
         }
         else 
         {
            movePtr->x *= mMoveSpeed;
            movePtr->y *= mMoveSpeed;

            mMoveState = ModeMove;
         }

         if (mMoveStuckTestCountdown > 0)
            --mMoveStuckTestCountdown;
         else
         {
            // We should check to see if we are stuck...
            F32 locationDelta = (location - mLastLocation).len();
            if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) 
            {
               // If we are slowing down, then it's likely that our location delta will be less than
               // our move stuck tolerance. Because we can be both slowing and stuck
               // we should TRY to check if we've moved. This could use better detection.
               if ( mMoveState != ModeSlowing || locationDelta == 0 )
               {
                  mMoveState = ModeStuck;
                  throwCallback("onMoveStuck");
               }
            }
         }
      }
   }

   // Test for target location in sight if it's an object. The LOS is
   // run from the eye position to the center of the object's bounding,
   // which is not very accurate.
   if (mAimObject) {
      MatrixF eyeMat;
      getEyeTransform(&eyeMat);
      eyeMat.getColumn(3,&location);
      Point3F targetLoc = mAimObject->getBoxCenter();

      // This ray ignores non-static shapes. Cast Ray returns true
      // if it hit something.
      RayInfo dummy;
      if (getContainer()->castRay( location, targetLoc,
            StaticShapeObjectType | StaticObjectType |
            TerrainObjectType, &dummy)) {
         if (mTargetInLOS) {
            throwCallback( "onTargetExitLOS" );
            mTargetInLOS = false;
         }
      }
      else
         if (!mTargetInLOS) {
            throwCallback( "onTargetEnterLOS" );
            mTargetInLOS = true;
         }
   }

   // Replicate the trigger state into the move so that
   // triggers can be controlled from scripts.
   for( int i = 0; i < MaxTriggerKeys; i++ )
      movePtr->trigger[i] = getImageTriggerState(i);

   mLastLocation = location;

   return true;
}
Beispiel #18
0
void ForestWindMgr::updateWind(  const Point3F &camPos, 
                                 const TreePlacementInfo &info, 
                                 F32 timeDelta )
{
   PROFILE_SCOPE(ForestWindMgr_updateWind);

   // See if we have the blended source available.
   ForestWindAccumulator *blendDest = NULL;
   {
      IdToWindMap::Iterator iter = mPrevSources->find( info.itemKey );
      if ( iter != mPrevSources->end() )
      {
         blendDest = iter->value;
         mPrevSources->erase( iter );
      }
   }

   // Get some stuff we'll need for finding the emitters.
   F32 treeHeight = info.scale * info.dataBlock->getObjBox().len_z();
   Point3F top = info.pos;
   top.z += treeHeight;
   if ( blendDest )
      top += ( 1.0f / info.scale ) * blendDest->getDirection();

   // Go thru the emitters to accumulate the total wind force.
   VectorF windForce( 0, 0, 0 );

   F32 time = Sim::getCurrentTime() / 1000.0f;

   ForestWindEmitterList::iterator iter = mEmitters.begin();
   for ( ; iter != mEmitters.end(); iter++ )
   {
      ForestWindEmitter *emitter = (*iter);

      // If disabled or no wind object... skip it.
      if ( !emitter->isEnabled() || !emitter->getWind() )
         continue;

      ForestWind *wind = emitter->getWind();

      F32 strength = wind->getStrength();
      
      if ( emitter->isRadialEmitter() )
      {
         Point3F closest = MathUtils::mClosestPointOnSegment( info.pos, top, emitter->getPosition() );
         Point3F dir = closest - emitter->getPosition();
         F32 lenSquared = dir.lenSquared();
         if ( lenSquared > emitter->getWindRadiusSquared() )
            continue;

         dir *= 1.0f / mSqrt( lenSquared );

         F32 att = lenSquared / emitter->getWindRadiusSquared();
         strength *= 1.0f - att;
         windForce += dir * strength;
      }
      else
      {
         F32 d = mDot( info.pos, Point3F::One ); //PlaneF( Point3F::Zero, wind->getDirection() ).distToPlane( Point3F( info.pos.x, info.pos.y, 0 ) );
         //F32 d = PlaneF( Point3F::Zero, wind->getDirection() ).distToPlane( Point3F( info.pos.x, info.pos.y, 0 ) );
         F32 scale = 1.0f + ( mSin( d + ( time / 10.0 ) ) * 0.5f );
         windForce += wind->getDirection() * strength * scale;
      }
   }

   // If we need a accumulator then we also need to presimulate.
   if ( !blendDest )
   {
      blendDest = new ForestWindAccumulator( info );
      blendDest->presimulate( windForce, 4.0f / TickSec );
   }
   else
      blendDest->updateWind( windForce, timeDelta );

   mSources->insertUnique( info.itemKey, blendDest );
}
//--------------------------------------------------------------------------
// from Graphics Gems I: pp 738-742
U32 mSolveQuartic_c(F32 a, F32 b, F32 c, F32 d, F32 e, F32 * x)
{
   if(mIsZero(a))
      return(mSolveCubic(b, c, d, e, x));

   // normal form: x^4 + ax^3 + bx^2 + cx + d = 0
   F32 A = b / a;
   F32 B = c / a;
   F32 C = d / a;
   F32 D = e / a;

   // substitue x = y - A/4 to eliminate cubic term:
   // x^4 + px^2 + qx + r = 0
   F32 A2 = A * A;
   F32 A3 = A2 * A;
   F32 A4 = A2 * A2;

   F32 p = ((-3.f/8.f) * A2) + B;
   F32 q = ((1.f/8.f) * A3) - ((1.f/2.f) * A * B) + C;
   F32 r = ((-3.f/256.f) * A4) + ((1.f/16.f) * A2 * B) - ((1.f/4.f) * A * C) + D;

   U32 num = 0;
   if(mIsZero(r)) // no absolute term: y(y^3 + py + q) = 0
   {
      num = mSolveCubic(1.f, 0.f, p, q, x);
      x[num++] = 0.f;
   }
   else
   {
      // solve the resolvent cubic
      F32 q2 = q * q;

      a = 1.f;
      b = (-1.f/2.f) * p;
      c = -r;
      d = ((1.f/2.f) * r * p) - ((1.f/8.f) * q2);

      mSolveCubic(a, b, c, d, x);

      F32 z = x[0];

      // build 2 quadratic equations from the one solution
      F32 u = (z * z) - r;
      F32 v = (2.f * z) - p;

      if(mIsZero(u))
         u = 0.f;
      else if(u > 0.f)
         u = mSqrt(u);
      else
         return(0);

      if(mIsZero(v))
         v = 0.f;
      else if(v > 0.f)
         v = mSqrt(v);
      else
         return(0);

      // solve the two quadratics
      a = 1.f;
      b = v;
      c = z - u;
      num = mSolveQuadratic(a, b, c, x);

      a = 1.f;
      b = -v;
      c = z + u;
      num += mSolveQuadratic(a, b, c, x + num);
   }

   // resubstitute
   F32 sub = (1.f/4.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);
}
//--------------------------------------------------------------------------
// 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);
}
Beispiel #21
0
bool SphereF::intersectsRay( const Point3F &start, const Point3F &end ) const
{
   MatrixF worldToObj( true );
   worldToObj.setPosition( center );
   worldToObj.inverse();

   VectorF dir = end - start;
   dir.normalize();

   Point3F tmpStart = start;
   worldToObj.mulP( tmpStart ); 

   //Compute A, B and C coefficients
   F32 a = mDot(dir, dir);
   F32 b = 2 * mDot(dir, tmpStart);
   F32 c = mDot(tmpStart, tmpStart) - (radius * radius);

   //Find discriminant
   F32 disc = b * b - 4 * a * c;

   // if discriminant is negative there are no real roots, so return 
   // false as ray misses sphere
   if ( disc < 0 )
      return false;

   // compute q as described above
   F32 distSqrt = mSqrt( disc );
   F32 q;
   if ( b < 0 )
      q = (-b - distSqrt)/2.0;
   else
      q = (-b + distSqrt)/2.0;

   // compute t0 and t1
   F32 t0 = q / a;
   F32 t1 = c / q;

   // make sure t0 is smaller than t1
   if ( t0 > t1 )
   {
      // if t0 is bigger than t1 swap them around
      F32 temp = t0;
      t0 = t1;
      t1 = temp;
   }

   // This function doesn't use it
   // but t would be the interpolant
   // value for getting the exact
   // intersection point, by interpolating
   // start to end by t.

   /*
   F32 t = 0;
   TORQUE_UNUSED(t);
   */

   // if t1 is less than zero, the object is in the ray's negative direction
   // and consequently the ray misses the sphere
   if ( t1 < 0 )
      return false;

   // if t0 is less than zero, the intersection point is at t1
   if ( t0 < 0 ) // t = t1;     
      return true;
   else // else the intersection point is at t0
      return true; // t = t0;
}
Beispiel #22
0
void CloudLayer::_initBuffers()
{      
   // Vertex Buffer...

   Point3F vertScale( 16.0f, 16.0f, mHeight );
   F32 zOffset = -( mCos( mSqrt( 1.0f ) ) + 0.01f );
   
   mVB.set( GFX, smVertCount, GFXBufferTypeStatic );   
   GFXCloudVertex *pVert = mVB.lock(); 
   if(!pVert) return;

   for ( U32 y = 0; y < smVertStride; y++ )
   {
      F32 v = ( (F32)y / (F32)smStrideMinusOne - 0.5f ) * 2.0f;

      for ( U32 x = 0; x < smVertStride; x++ )
      {
         F32 u = ( (F32)x / (F32)smStrideMinusOne - 0.5f ) * 2.0f;

         F32 sx = u;
         F32 sy = v;
         F32 sz = mCos( mSqrt( sx*sx + sy*sy ) ) + zOffset;
         //F32 sz = 1.0f;
         pVert->point.set( sx, sy, sz );
         pVert->point *= vertScale;

         // The vert to our right.
         Point3F rpnt;

         F32 ru = ( (F32)( x + 1 ) / (F32)smStrideMinusOne - 0.5f ) * 2.0f;
         F32 rv = v;

         rpnt.x = ru;
         rpnt.y = rv;
         rpnt.z = mCos( mSqrt( rpnt.x*rpnt.x + rpnt.y*rpnt.y ) ) + zOffset;
         rpnt *= vertScale;

         // The vert to our front.
         Point3F fpnt;

         F32 fu = u;
         F32 fv = ( (F32)( y + 1 ) / (F32)smStrideMinusOne - 0.5f ) * 2.0f;

         fpnt.x = fu;
         fpnt.y = fv;
         fpnt.z = mCos( mSqrt( fpnt.x*fpnt.x + fpnt.y*fpnt.y ) ) + zOffset;
         fpnt *= vertScale;

         Point3F fvec = fpnt - pVert->point;
         fvec.normalize();

         Point3F rvec = rpnt - pVert->point;
         rvec.normalize();

         pVert->normal = mCross( fvec, rvec );
         pVert->normal.normalize();
         pVert->binormal = fvec;
         pVert->tangent = rvec;
         pVert->texCoord.set( u, v );   
         pVert++;
      }
   }

   mVB.unlock();


   // Primitive Buffer...   

   mPB.set( GFX, smTriangleCount * 3, smTriangleCount, GFXBufferTypeStatic );

   U16 *pIdx = NULL;   
   mPB.lock(&pIdx);     
   U32 curIdx = 0; 

   for ( U32 y = 0; y < smStrideMinusOne; y++ )
   {
      for ( U32 x = 0; x < smStrideMinusOne; x++ )
      {
         U32 offset = x + y * smVertStride;

         pIdx[curIdx] = offset;
         curIdx++;
         pIdx[curIdx] = offset + 1;
         curIdx++;
         pIdx[curIdx] = offset + smVertStride + 1;
         curIdx++;

         pIdx[curIdx] = offset;
         curIdx++;
         pIdx[curIdx] = offset + smVertStride + 1;
         curIdx++;
         pIdx[curIdx] = offset + smVertStride;
         curIdx++;
      }
   }

   mPB.unlock();   
}
Beispiel #23
0
void HifiClientProcessList::clientCatchup(GameConnection * connection)
{
#ifdef TORQUE_DEBUG_NET_MOVES
   Con::printf("client catching up... (%i)%s", mCatchup, mForceHifiReset ? " reset" : "");
#endif

   if (connection->getControlObject() && connection->getControlObject()->isGhostUpdated())
      // if control object is reset, make sure moves are reset too
      connection->mMoveList->resetCatchup();

   const F32 maxVel = mSqrt(gMaxHiFiVelSq) * 1.25f;
   F32 dt = F32(mCatchup+1) * TickSec;
   Point3F bigDelta(maxVel*dt,maxVel*dt,maxVel*dt);

   // walk through all process objects looking for ones which were updated
   // -- during first pass merely collect neighbors which need to be reset and updated in unison
   ProcessObject * pobj;
   if (mCatchup && !mForceHifiReset)
   {
      for (pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next)
      {
         GameBase *obj = getGameBase( pobj );
         static SimpleQueryList nearby;
         nearby.mList.clear();
         // check for nearby objects which need to be reset and then caught up
         // note the funky loop logic -- first time through obj is us, then
         // we start iterating through nearby list (to look for objects nearby
         // the nearby objects), which is why index starts at -1
         // [objects nearby the nearby objects also get added to the nearby list]
         for (S32 i=-1; obj; obj = ++i<nearby.mList.size() ? (GameBase*)nearby.mList[i] : NULL)
         {
            if (obj->isGhostUpdated() && (obj->getTypeMask() & GameBaseHiFiObjectType) && !obj->isNetNearbyAdded())
            {
               Point3F start = obj->getWorldSphere().center;
               Point3F end = start + 1.1f * dt * obj->getVelocity();
               F32 rad = 1.5f * obj->getWorldSphere().radius;

               // find nearby items not updated but are hi fi, mark them as updated (and restore old loc)
               // check to see if added items have neighbors that need updating
               Box3F box;
               Point3F rads(rad,rad,rad);
               box.minExtents = box.maxExtents = start;
               box.minExtents -= bigDelta + rads;
               box.maxExtents += bigDelta + rads;

               // CodeReview - this is left in for MBU, but also so we can deal with the issue later.
               // add marble blast hack so hifi networking can see hidden objects
               // (since hidden is under control of hifi networking)
               //  gForceNotHidden = true;

               S32 j = nearby.mList.size();
               gClientContainer.findObjects(box, GameBaseHiFiObjectType, SimpleQueryList::insertionCallback, &nearby);

               // CodeReview - this is left in for MBU, but also so we can deal with the issue later.
               // disable above hack
               //  gForceNotHidden = false;

               // drop anyone not heading toward us or already checked
               for (; j<nearby.mList.size(); j++)
               {
                  GameBase * obj2 = (GameBase*)nearby.mList[j];
                  // if both passive, these guys don't interact with each other
                  bool passive = obj->isHifiPassive() && obj2->isHifiPassive();
                  if (!obj2->isGhostUpdated() && !passive)
                  {
                     // compare swept spheres of obj and obj2
                     // if collide, reset obj2, setGhostUpdated(true), and continue
                     Point3F end2 = obj2->getWorldSphere().center;
                     Point3F start2 = end2 - 1.1f * dt * obj2->getVelocity();
                     F32 rad2 = 1.5f * obj->getWorldSphere().radius;
                     if (MathUtils::capsuleCapsuleOverlap(start,end,rad,start2,end2,rad2))
                     {
                        // better add obj2
                        obj2->getTickCache().beginCacheList();
                        TickCacheEntry * tce = obj2->getTickCache().incCacheList();
                        BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
                        obj2->readPacketData(connection,&bs);
                        obj2->setGhostUpdated(true);

                        // continue so we later add the neighbors too
                        continue;
                     }

                  }

                  // didn't pass above test...so don't add it or nearby objects
                  nearby.mList[j] = nearby.mList.last();
                  nearby.mList.decrement();
                  j--;
               }
               obj->setNetNearbyAdded(true);
            }
         }
      }
   }

   // save water mark -- for game base list
   FrameAllocatorMarker mark;

   // build ordered list of client objects which need to be caught up
   GameBaseListNode list;
   for (pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next)
   {
      GameBase *obj = getGameBase( pobj );
      //GameBase *obj = dynamic_cast<GameBase*>( pobj );
      //GameBase *obj = (GameBase*)pobj;

      // Not a GameBase object so nothing to do.
      if ( !obj )
         continue;

      if (obj->isGhostUpdated() && (obj->getTypeMask() & GameBaseHiFiObjectType))
      {
         // construct process object and add it to the list
         // hold pointer to our object in mAfterObject
         GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode));
         po->mObject = obj;
         po->linkBefore(&list);

         // begin iterating through tick list (skip first tick since that is the state we've been reset to)
         obj->getTickCache().beginCacheList();
         obj->getTickCache().incCacheList();
      }
      else if (mForceHifiReset && (obj->getTypeMask() & GameBaseHiFiObjectType))
      {
         // add all hifi objects
         obj->getTickCache().beginCacheList();
         TickCacheEntry * tce = obj->getTickCache().incCacheList();
         BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
         obj->readPacketData(connection,&bs);
         obj->setGhostUpdated(true);

         // construct process object and add it to the list
         // hold pointer to our object in mAfterObject
         GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode));
         po->mObject = obj;
         po->linkBefore(&list);
      }
      else if (obj == connection->getControlObject() && obj->isGhostUpdated())
      {
         // construct process object and add it to the list
         // hold pointer to our object in mAfterObject
         // .. but this is not a hi fi object, so don't mess with tick cache
         GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode));
         po->mObject = obj;
         po->linkBefore(&list);
      }
      else if (obj->isGhostUpdated())
      {
         // not hifi but we were updated, so perform net smooth now
         obj->computeNetSmooth(mLastDelta);
      }

      // clear out work flags
      obj->setNetNearbyAdded(false);
      obj->setGhostUpdated(false);
   }

   // run through all the moves in the move list so we can play them with our control object
   Move* movePtr;
   U32 numMoves;
   connection->mMoveList->resetClientMoves();
   connection->mMoveList->getMoves(&movePtr, &numMoves);
   AssertFatal(mCatchup<=numMoves,"doh");

   // tick catchup time
   for (U32 m=0; m<mCatchup; m++)
   {
      for (GameBaseListNode * walk = list.mNext; walk != &list; walk = walk->mNext)
      {
         // note that we get object from after object not getGameBase function
         // this is because we are an on the fly linked list which uses mAfterObject
         // rather than the linked list embedded in GameBase (clean this up?)
         GameBase * obj = walk->mObject;

         // it's possible for a non-hifi object to get in here, but
         // only if it is a control object...make sure we don't do any
         // of the tick cache stuff if we are not hifi.
         bool hifi = obj->getTypeMask() & GameBaseHiFiObjectType;
         TickCacheEntry * tce = hifi ? obj->getTickCache().incCacheList() : NULL;

         // tick object
         if (obj==connection->getControlObject())
         {
            obj->processTick(movePtr);
            movePtr->checksum = obj->getPacketDataChecksum(connection);
            movePtr++;
         }
         else
         {
            AssertFatal(tce && hifi,"Should not get in here unless a hi fi object!!!");
            obj->processTick(tce->move);
         }

         if (hifi)
         {
            BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize);
            obj->writePacketData(connection,&bs);
         }
      }
      if (connection->getControlObject() == NULL)
         movePtr++;
   }
   connection->mMoveList->clearMoves(mCatchup);

   // Handle network error smoothing here...but only for control object
   GameBase * control = connection->getControlObject();
   if (control && !control->isNewGhost())
   {
      control->computeNetSmooth(mLastDelta);
      control->setNewGhost(false);
   }

   if (moveSync.doAction() && moveSync.moveDiff>0)
   {
      S32 moveDiff = moveSync.moveDiff;
#ifdef TORQUE_DEBUG_NET_MOVES
      Con::printf("client timewarping to catchup %i moves",moveDiff);
#endif
      while (moveDiff--)
         advanceObjects();
      moveSync.reset();
   }

#ifdef TORQUE_DEBUG_NET_MOVES
   Con::printf("---------");
#endif

   // all caught up
   mCatchup = 0;
}
Beispiel #24
0
void BasicClouds::_initBuffers()
{      
   // Primitive Buffer...  Is shared for all Layers.

   mPB.set( GFX, smTriangleCount * 3, smTriangleCount, GFXBufferTypeStatic );

   U16 *pIdx = NULL;   
   mPB.lock(&pIdx);     
   U32 curIdx = 0; 

   for ( U32 y = 0; y < smStrideMinusOne; y++ )
   {
      for ( U32 x = 0; x < smStrideMinusOne; x++ )
      {
         U32 offset = x + y * smVertStride;

         pIdx[curIdx] = offset;
         curIdx++;
         pIdx[curIdx] = offset + 1;
         curIdx++;
         pIdx[curIdx] = offset + smVertStride + 1;
         curIdx++;

         pIdx[curIdx] = offset;
         curIdx++;
         pIdx[curIdx] = offset + smVertStride + 1;
         curIdx++;
         pIdx[curIdx] = offset + smVertStride;
         curIdx++;
      }
   }

   mPB.unlock();   

   // Vertex Buffer... 
   // Each layer has their own so they can be at different heights.

   for ( U32 i = 0; i < TEX_COUNT; i++ )
   {
      Point3F vertScale( 16.0f, 16.0f, mHeight[i] );
      F32 zOffset = -( mCos( mSqrt( 1.0f ) ) + 0.01f );
      
      mVB[i].set( GFX, smVertCount, GFXBufferTypeStatic );   
      GFXVertexPT *pVert = mVB[i].lock(); 

      for ( U32 y = 0; y < smVertStride; y++ )
      {
         F32 v = ( (F32)y / (F32)smStrideMinusOne - 0.5f ) * 2.0f;

         for ( U32 x = 0; x < smVertStride; x++ )
         {
            F32 u = ( (F32)x / (F32)smStrideMinusOne - 0.5f ) * 2.0f;

            F32 sx = u;
            F32 sy = v;
            F32 sz = mCos( mSqrt( sx*sx + sy*sy ) ) + zOffset;

            pVert->point.set( sx, sy, sz );
            pVert->point *= vertScale;
            pVert->texCoord.set( u, v );   
            pVert++;
         }
      }

      mVB[i].unlock();
   } 
}
Beispiel #25
0
//--------------------------------------
static void m_point2F_normalize_C(F32 *p)
{
   F32 factor = 1.0f / mSqrt(p[0]*p[0] + p[1]*p[1] );
   p[0] *= factor;
   p[1] *= factor;
}
Beispiel #26
0
//----------------------------------------------------------------------------
/// Core rendering method for this control.
///
/// This method scans through all the current client ShapeBase objects.
/// If one is named, it displays the name and damage information for it.
///
/// Information is offset from the center of the object's bounding box,
/// unless the object is a PlayerObjectType, in which case the eye point
/// is used.
///
/// @param   updateRect   Extents of control.
void afxGuiTextHud::onRender( Point2I, const RectI &updateRect)
{
   // Background fill first
   if (mShowFill)
      GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor.toColorI());

   // Must be in a TS Control
   GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent());
   if (!parent) return;

   // Must have a connection and control object
   GameConnection* conn = GameConnection::getConnectionToServer();
   if (!conn)
      return;

   GameBase * control = dynamic_cast<GameBase*>(conn->getControlObject());
   if (!control)
      return;

   // Get control camera info
   MatrixF cam;
   Point3F camPos;
   VectorF camDir;
   conn->getControlCameraTransform(0,&cam);
   cam.getColumn(3, &camPos);
   cam.getColumn(1, &camDir);

   F32 camFovCos;
   conn->getControlCameraFov(&camFovCos);
   camFovCos = mCos(mDegToRad(camFovCos) / 2);

   // Visible distance info & name fading
   F32 visDistance = gClientSceneGraph->getVisibleDistance();
   F32 visDistanceSqr = visDistance * visDistance;
   F32 fadeDistance = visDistance * mDistanceFade;

   // Collision info. We're going to be running LOS tests and we
   // don't want to collide with the control object.
   static U32 losMask = TerrainObjectType | TerrainLikeObjectType | ShapeBaseObjectType;

   if (!mEnableControlObjectOcclusion)
      control->disableCollision();

   if (mLabelAllShapes)
   {
     // This section works just like GuiShapeNameHud and renders labels for
     // all the shapes.

     // All ghosted objects are added to the server connection group,
     // so we can find all the shape base objects by iterating through
     // our current connection.
     for (SimSetIterator itr(conn); *itr; ++itr) 
     {
       ///if ((*itr)->getTypeMask() & ShapeBaseObjectType) 
       ///{
       ShapeBase* shape = dynamic_cast<ShapeBase*>(*itr);
       if ( shape ) {
         if (shape != control && shape->getShapeName()) 
         {

           // Target pos to test, if it's a player run the LOS to his eye
           // point, otherwise we'll grab the generic box center.
           Point3F shapePos;
           if (shape->getTypeMask() & PlayerObjectType) 
           {
             MatrixF eye;

             // Use the render eye transform, otherwise we'll see jittering
             shape->getRenderEyeTransform(&eye);
             eye.getColumn(3, &shapePos);
           }
           else 
           {
             // Use the render transform instead of the box center
             // otherwise it'll jitter.
             MatrixF srtMat = shape->getRenderTransform();
             srtMat.getColumn(3, &shapePos);
           }

           VectorF shapeDir = shapePos - camPos;

           // Test to see if it's in range
           F32 shapeDist = shapeDir.lenSquared();
           if (shapeDist == 0 || shapeDist > visDistanceSqr)
             continue;
           shapeDist = mSqrt(shapeDist);

           // Test to see if it's within our viewcone, this test doesn't
           // actually match the viewport very well, should consider
           // projection and box test.
           shapeDir.normalize();
           F32 dot = mDot(shapeDir, camDir);
           if (dot < camFovCos)
             continue;

           // Test to see if it's behind something, and we want to
           // ignore anything it's mounted on when we run the LOS.
           RayInfo info;
           shape->disableCollision();
           SceneObject *mount = shape->getObjectMount();
           if (mount)
             mount->disableCollision();
           bool los = !gClientContainer.castRay(camPos, shapePos,losMask, &info);
           shape->enableCollision();
           if (mount)
             mount->enableCollision();

           if (!los)
             continue;

           // Project the shape pos into screen space and calculate
           // the distance opacity used to fade the labels into the
           // distance.
           Point3F projPnt;
           shapePos.z += mVerticalOffset;
           if (!parent->project(shapePos, &projPnt))
             continue;
           F32 opacity = (shapeDist < fadeDistance)? 1.0:
             1.0 - (shapeDist - fadeDistance) / (visDistance - fadeDistance);

           // Render the shape's name
           drawName(Point2I((S32)projPnt.x, (S32)projPnt.y),shape->getShapeName(),opacity);
         }
       }
     }
   }

   // This section renders all text added by afxGuiText effects.
   for (S32 i = 0; i < text_items.size(); i++)
   {
     HudTextSpec* spec = &text_items[i];
     if (spec->text && spec->text[0] != '\0') 
     {
       VectorF shapeDir = spec->pos - camPos;

       // do range test
       F32 shapeDist = shapeDir.lenSquared();
       if (shapeDist == 0 || shapeDist > visDistanceSqr)
         continue;
       shapeDist = mSqrt(shapeDist);

       // Test to see if it's within our viewcone, this test doesn't
       // actually match the viewport very well, should consider
       // projection and box test.
       shapeDir.normalize();
       F32 dot = mDot(shapeDir, camDir);
       if (dot < camFovCos)
         continue;

       // Test to see if it's behind something, and we want to
       // ignore anything it's mounted on when we run the LOS.
       RayInfo info;
       if (spec->obj)
         spec->obj->disableCollision();
       bool los = !gClientContainer.castRay(camPos, spec->pos, losMask, &info);
       if (spec->obj)
         spec->obj->enableCollision();
       if (!los)
         continue;

       // Project the shape pos into screen space.
       Point3F projPnt;
       if (!parent->project(spec->pos, &projPnt))
         continue;

       // Calculate the distance opacity used to fade text into the distance.
       F32 opacity = (shapeDist < fadeDistance)? 1.0 : 1.0 - (shapeDist - fadeDistance) / (25.0f);
       if (opacity > 0.01f)
        drawName(Point2I((S32)projPnt.x, (S32)projPnt.y), spec->text, opacity, &spec->text_clr);
     }
   }

   // Restore control object collision
   if (!mEnableControlObjectOcclusion)
      control->enableCollision();

   // Border last
   if (mShowFrame)
      GFX->getDrawUtil()->drawRect(updateRect, mFrameColor.toColorI());

   reset();
}