void MovableBuilder::MakeDefault ()
{
	AddPosition(VectorF());
	AddMovement(VectorF(0.0, 1.0), 0.0, 30.0);

	// Note that a body isn't created by default!
}
Exemple #2
0
shared_ptr<RenderObjectEntity> EntityPlayer::getEntity(EntityData & data, shared_ptr<World>) const
{
    assert(data.extraData);
    auto eData = dynamic_pointer_cast<ExtraData>(data.extraData);
    assert(eData);
    if(data.entity == nullptr)
    {
        BlockDescriptorPtr block = BlockDescriptors.get(L"builtin.glass");
        shared_ptr<RenderObjectEntityMesh> mesh;
        mesh = make_shared<RenderObjectEntityMesh>(VectorF(0), VectorF(0), getPhysicsObjectConstructor(data), true);
        Mesh blockMesh = block->makeBlockEntityMesh();
        mesh->addPart(invert(blockMesh), Script::parse(program1));
        mesh->addPart(blockMesh, Script::parse(program2));
        data.entity = make_shared<RenderObjectEntity>(mesh, data.position, data.velocity, data.acceleration, data.deltaAcceleration, 0);
    }
    data.entity->scriptIOObject->value[L"theta"] = make_shared<Scripting::DataFloat>(eData->theta);
    data.entity->scriptIOObject->value[L"phi"] = make_shared<Scripting::DataFloat>(eData->phi);
    data.entity->scriptIOObject->value[L"isCurrentPlayer"] = make_shared<Scripting::DataBoolean>(false);
    data.entity->position = data.position;
    data.entity->velocity = data.velocity;
    data.entity->acceleration = data.acceleration;
    data.entity->deltaAcceleration = data.deltaAcceleration;
    data.entity->age = 0;
    return data.entity;
}
EditorCamera::EditorCamera()
{
   mRenderCamera     = NULL;
   mForwardVelocity  = Point3F::Zero;
   mWorldPosition    = Point3F(0.0f, 0.0f, 0.0f);
   mMouseDown        = false;
   mMouseStart       = Point2I(0, 0);
   mHorizontalAngle  = 0.0f;
   mVerticalAngle    = 0.0f;

   mTransform.set(Point3F(0.0f, 0.0f, 0.0f), VectorF(0.0f, 0.0f, 0.0f), VectorF(1.0f, 1.0f, 1.0f));
}
Exemple #4
0
 void
 GuiObject::GenerateBackButton(void)
 {
	 ButtonControl* newcon = new ButtonControl();
	 newcon->ConnectionID = newcon->TypeHashCode = typeid(ButtonControl).hash_code();
	 newcon->SetConnection(this);
	 newcon->ConnectionID = 10;
	 conXtor->setConnectables(9,newcon);
	 newcon->SetText("   back");
	 conXtor->GetConnected<ButtonControl>(10)->PositionOnPanel = VectorF(15,15);
	 conXtor->GetConnected<ButtonControl>(10)->SizeScaledPanel = VectorF(0.4,0.1);
	 conXtor->GetConnected<ButtonControl>(10)->SetClickerFunc(_backButtonClicked);
 }
TurretShape::TurretShape()
{
   mTypeMask |= VehicleObjectType | DynamicShapeObjectType;
   mDataBlock = 0;

   allowManualRotation = true;
   allowManualFire = true;

   mTurretDelta.rot = Point3F(0.0f, 0.0f, 0.0f);
   mTurretDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f);
   mTurretDelta.dt = 1;

   mRot = mTurretDelta.rot;

   mPitchAllowed = true;
   mHeadingAllowed = true;

   mPitchRate = -1;
   mHeadingRate = -1;

   mPitchUp = 0;
   mPitchDown = 0;
   mHeadingMax = mDegToRad(180.0f);

   mRespawn = false;

   mPitchThread = 0;
   mHeadingThread = 0;

   mSubclassTurretShapeHandlesScene = false;

   // For the Item class
   mSubclassItemHandlesScene = true;
}
//-----------------------------------------------------------------------------
//
// VActorPhysicsController::onActorEvent( pEvent );
//
// ...
//
//-----------------------------------------------------------------------------
void VActorPhysicsController::onActorEvent( const VActor::eEventType &pEvent )
{
    switch( pEvent )
    {
    case VActor::k_MountEvent :
        {
            // Set Control State.
            setControlState( k_PathControlState );

            // Store Path.
            mMountedPath = dynamic_cast<VPath*>( mObject->getObjectMount() );

        } break;

    case VActor::k_UnmountEvent :
        {
            // Clear Control State.
            clearControlState( k_PathControlState );

            // Clear Path.
            mMountedPath = NULL;
            // Clear X & Y Velocity.
            setVelocity( VectorF( 0.f, 0.f, mVelocity.z ) );

        } break;
    }
}
Exemple #7
0
void Torch::onReplace(World &world, Block b, BlockIterator bi, WorldLockManager &lock_manager) const
{
    ItemDescriptor::addToWorld(world,
                               lock_manager,
                               ItemStack(Item(Items::builtin::Torch::descriptor())),
                               bi.position() + VectorF(0.5));
}
bool GroundPlane::onAdd()
{
   if( !Parent::onAdd() )
      return false;

   if( isClientObject() )
      _updateMaterial();
      
   if( mSquareSize < sMIN_SQUARE_SIZE )
   {
      Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
      mSquareSize = sMIN_SQUARE_SIZE;
   }

   setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
   setGlobalBounds();
   resetWorldBox();

   addToScene();

   if( gPhysicsPlugin )
      mPhysicsRep = gPhysicsPlugin->createStatic( this );

   return true;
}
bool GroundPlane::onAdd()
{
   if( !Parent::onAdd() )
      return false;

   if( isClientObject() )
      _updateMaterial();
      
   if( mSquareSize < sMIN_SQUARE_SIZE )
   {
      Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
      mSquareSize = sMIN_SQUARE_SIZE;
   }

   Parent::setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
   Parent::setTransform( MatrixF::Identity );
   setGlobalBounds();
   resetWorldBox();

   addToScene();

   if ( PHYSICSMGR )
   {
      PhysicsCollision *colShape = PHYSICSMGR->createCollision();
      colShape->addPlane( PlaneF( Point3F::Zero, Point3F( 0, 0, 1 ) ) ); 

      PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" );
      mPhysicsRep = PHYSICSMGR->createBody();
      mPhysicsRep->init( colShape, 0, 0, this, world );
   }

   return true;
}
Exemple #10
0
void DotNode::WriteMyOutputs(std::string & outCode) const
{
    std::string vecType = VectorF(GetInputs()[0].GetSize(), 0).GetGLSLType();

    outCode += "\tfloat " + GetOutputName(0) +
               " = dot(" + GetInputs()[0].GetValue() + ", " +
               GetInputs()[1].GetValue() + ");\n";
}
Exemple #11
0
void Grass::onBreak(
    World &world, Block b, BlockIterator bi, WorldLockManager &lock_manager, Item &tool) const
{
    ItemDescriptor::addToWorld(world,
                               lock_manager,
                               ItemStack(Item(Items::builtin::Dirt::descriptor())),
                               bi.position() + VectorF(0.5));
    handleToolDamage(tool);
}
Exemple #12
0
//----------------------------------------------------------------------------
void RigidBody::createPhysShape()
{
	//Physics* physics = isServerObject() ? gServerPhysics : gClientPhysics;
	Physics* physics = Physics::getPhysics(isServerObject());
	if (physics)
	{
		PhysInfo physDescr;
		//transform into radian
		VectorF angleRadians = mDataBlock->mRotation/180.f*float(M_PI);
		physDescr.transform.set(angleRadians, mDataBlock->mPos);
		physDescr.owner = this;
		physDescr.shapeType = (PhysInfo::ShapeType)mDataBlock->mShapeType;
		physDescr.mass = mDataBlock->mass;
		if (physDescr.shapeType==PhysInfo::ST_SPHERE)
		{
			Box3F scaledObjBox = mObjBox;
			scaledObjBox.minExtents.convolve(mObjScale);
			scaledObjBox.maxExtents.convolve(mObjScale);
			F32 radius = (scaledObjBox.maxExtents - scaledObjBox.getCenter()).len();
			physDescr.params = VectorF(radius,0.f,0.f);
		}
		else //if (physDescr.shapeType==PhysInfo::ST_BOX)
		{
			Box3F rotBox = mObjBox;
			physDescr.transform.mul(rotBox);
			VectorF length = VectorF(rotBox.len_x(),rotBox.len_y(),rotBox.len_z());
			length.convolve(mObjScale);

			physDescr.params = length;
		}
		//physDescr.params = VectorF(1.f,1.f,1.f);
		//physDescr.shapeType = PhysInfo::ST_SPHERE;
		//physDescr.mass = 5.f;
		//physDescr.params = VectorF(0.5f,0.f,0.f);
		mPhysShape = physics->createPhysShape(physDescr);
		mPhysShape->setTransform(mObjToWorld);
		mPhysShape->setForce(mForce);
		mPhysShape->setTorque(mTorque);
		mPhysShape->setLinVelocity(mLinVelocity);
		mPhysShape->setAngVelocity(mAngVelocity);
	}
}
Exemple #13
0
void Torch::onDisattach(World &world,
                        const Block &block,
                        BlockIterator blockIterator,
                        WorldLockManager &lock_manager,
                        BlockUpdateKind blockUpdateKind) const
{
    ItemDescriptor::addToWorld(world,
                               lock_manager,
                               ItemStack(Item(Items::builtin::Torch::descriptor())),
                               blockIterator.position() + VectorF(0.5));
    world.setBlock(blockIterator, lock_manager, Block(Air::descriptor(), block.lighting));
}
Exemple #14
0
void DiamondOre::onBreak(
    World &world, Block b, BlockIterator bi, WorldLockManager &lock_manager, Item &tool) const
{
    if(isMatchingTool(tool))
    {
        ItemDescriptor::addToWorld(world,
                                   lock_manager,
                                   ItemStack(Item(Items::builtin::Diamond::descriptor())),
                                   bi.position() + VectorF(0.5));
    }
    handleToolDamage(tool);
}
Exemple #15
0
//----------------------------------------------------------------------------
void RigidBody::setEnabled(const bool enabled)
{
	SimComponent().setEnabled(enabled);
	if(mPhysShape)
		mPhysShape->setEnable(enabled);

	// When enabling add a tiniest force, to
	// prevent body from flying motionless in the air.
	if(enabled)
	{
		addForce(VectorF(0.0001f, 0.0001f, 0.0001f));
	}
}
// This function is meant to be used with a button to put everything back to default settings.
void GuiMaterialPreview::resetViewport()
{
   // Reset the camera's orientation.
   mCameraRot.set( mDegToRad(30.0f), 0, mDegToRad(-30.0f) );
   mCameraPos.set(0.0f, 1.75f, 1.25f);
   mOrbitDist = 5.0f;
   mOrbitPos = mModel->getShape()->center;

   // Reset the viewport's lighting.
   GuiMaterialPreview::mFakeSun->setColor( ColorF( 1.0f, 1.0f, 1.0f ) );
   GuiMaterialPreview::mFakeSun->setAmbient( ColorF( 0.5f, 0.5f, 0.5f ) );
   GuiMaterialPreview::mFakeSun->setDirection( VectorF( 0.0f, 0.707f, -0.707f ) );
}
void GroundPlane::inspectPostApply()
{
   Parent::inspectPostApply();
   setMaskBits( U32( -1 ) );

   if( mSquareSize < sMIN_SQUARE_SIZE )
   {
      Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE );
      mSquareSize = sMIN_SQUARE_SIZE;
   }

   setScale( VectorF( 1.0f, 1.0f, 1.0f ) );
}
Exemple #18
0
void LapisLazuliOre::onBreak(
    World &world, Block b, BlockIterator bi, WorldLockManager &lock_manager, Item &tool) const
{
    if(isMatchingTool(tool))
    {
        int dropCount = std::uniform_int_distribution<int>(4, 8)(world.getRandomGenerator());
        for(int i = 0; i < dropCount; i++)
            ItemDescriptor::addToWorld(world,
                                       lock_manager,
                                       ItemStack(Item(Items::builtin::LapisLazuli::descriptor())),
                                       bi.position() + VectorF(0.5));
    }
    handleToolDamage(tool);
}
Exemple #19
0
//----------------------------------------------------------------------------
// Explode
//----------------------------------------------------------------------------
void Splash::spawnExplosion()
{
   if( !mDataBlock->explosion ) return;

   Explosion* pExplosion = new Explosion;
   pExplosion->onNewDataBlock(mDataBlock->explosion, false);

   MatrixF trans = getTransform();
   trans.setPosition( getPosition() );

   pExplosion->setTransform( trans );
   pExplosion->setInitialState( trans.getPosition(), VectorF(0,0,1), 1);
   if (!pExplosion->registerObject())
      delete pExplosion;
}
Exemple #20
0
void
GuiObject::draw(void)
{
	Sprite::draw();
	char i = -1;
	char id = i;
	while(++i < conXtor->NumberOfConnectedObjects)
	{id++;
		if(conXtor->getConnectables(id))
			((ControllElement*)conXtor->getConnectables(id))->draw();
		else
			id++;}
	if(ShowTitle)
		GuiManager::getInstance()->Write(this->GetText(),Area.GetPosition()+VectorF(20,20),this->color);

}
bool GuiMaterialPreview::onWake()
{
   if( !Parent::onWake() )
      return false;

   if (!mFakeSun)
      mFakeSun = LightManager::createLightInfo();

   mFakeSun->setColor( ColorF( 1.0f, 1.0f, 1.0f ) );
   mFakeSun->setAmbient( ColorF( 0.5f, 0.5f, 0.5f ) );
   mFakeSun->setDirection( VectorF( 0.0f, 0.707f, -0.707f ) );
	mFakeSun->setPosition( mFakeSun->getDirection() * -10000.0f );
   mFakeSun->setRange( 2000000.0f );

   return true;
}
Exemple #22
0
void EigenSolver::compute_batch_symmetric_2x2(const VectorF& matrices) {
    const size_t dim = 2;
    const size_t flatten_size = 3;
    const size_t num_matrices = matrices.size() / flatten_size;
    m_eigen_values = VectorF(num_matrices * dim);
    m_eigen_vectors = MatrixF(num_matrices * dim, dim);
    for (size_t i=0; i<num_matrices; i++) {
        const VectorF& entries = matrices.segment(i*flatten_size, flatten_size);
        MatrixF M(dim, dim);
        size_t base_idx = i*flatten_size;
        M << matrices[base_idx  ], matrices[base_idx+2],
             matrices[base_idx+2], matrices[base_idx+1],
        m_solver.compute(M);
        m_eigen_values.segment(i*dim, dim) = m_solver.eigenvalues().real();
        m_eigen_vectors.block(i*dim, 0, dim, dim) =
            m_solver.eigenvectors().real();
    }
}
Exemple #23
0
//Mouse-clicks observing function:
void 
SliderX::mouseClicks(int button,bool IsPressed,VectorF position)
{
	GetArea();	 // check if mouse is clicked and if its over this element
	 if((button!=1) &&  Area.Containes(position))
	 {
		 if(IsPressed)	  // if clicked, sign in for Mouse-Move-Event invocation   / as mouse-movement-observer
			INPUT->attachMouseMove(this);

		 XIsUnderControll = button==0?IsPressed:XIsUnderControll;	   // set flags signaling "under control" if button pressed
		 YIsUnderControll = button==2?IsPressed:YIsUnderControll;

		 //set lastMouse to new warp-position, to prevent counting the warp as a regular mouse-movement,
		 //when warping the cursor to the actual slider-position when clicked...
		 lastMouse = VectorF(left + ((DimensionsSwitched?ValueY:ValueX) *(right-left)),Area.GetCenter().y);
		 glutWarpPointer(lastMouse.x,lastMouse.y);
	 }
}
Exemple #24
0
void SFXEmitter::setScale( const VectorF &scale )
{
   F32 maxDistance;
   
   if( mUseTrackDescriptionOnly && mTrack )
      maxDistance = mTrack->getDescription()->mMaxDistance;
   else
   {
      // Use the average of the three coords.
      maxDistance = ( scale.x + scale.y + scale.z ) / 3.0f;
      maxDistance = getMax( maxDistance, mDescription.mMinDistance );
      
      mDescription.mMaxDistance = maxDistance;
      
      mDirty.set( MaxDistance );
      setMaskBits( DirtyUpdateMask );
   }

   Parent::setScale( VectorF( maxDistance, maxDistance, maxDistance ) );
}
//--------------------------------------------------------------------------
//--------------------------------------
//
Lightning::Lightning()
{
   mNetFlags.set(Ghostable|ScopeAlways);
   mTypeMask |= StaticObjectType|EnvironmentObjectType;

   mLastThink = 0;

   mStrikeListHead  = NULL;
   mThunderListHead = NULL;

   strikesPerMinute = 12;
   strikeWidth = 2.5;
   chanceToHitTarget = 0.5f;
   strikeRadius = 20.0f;
   boltStartRadius = 20.0f;
   color.set( 1.0f, 1.0f, 1.0f, 1.0f );
   fadeColor.set( 0.1f, 0.1f, 1.0f, 1.0f );
   useFog = true;

   setScale( VectorF( 512.0f, 512.0f, 300.0f ) );
}
void TurretShape::unpackUpdate(NetConnection *connection, BitStream *stream)
{
   Parent::unpackUpdate(connection,stream);

   // InitialUpdateMask
   if (stream->readFlag()) {
      mRespawn = stream->readFlag();
   }

   // Item::RotationMask
   if ( stream->readFlag() )
   {
      QuatF rot;
      mathRead( *stream, &rot );

      Point3F pos = mObjToWorld.getPosition();
      rot.setMatrix( &mObjToWorld );
      mObjToWorld.setPosition( pos );
   }

   // controlled by the client?
   if(stream->readFlag())
      return;

   // TurretUpdateMask
   if (stream->readFlag())
   {
      Point3F rot(0.0f, 0.0f, 0.0f);
      stream->read(&rot.x);
      stream->read(&rot.z);
      _setRotation(rot);

      // New delta for client side interpolation
      mTurretDelta.rot = rot;
      mTurretDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f);

      stream->read(&allowManualRotation);
      stream->read(&allowManualFire);
   }
}
Exemple #27
0
Item::Item()
{
   mTypeMask |= ItemObjectType | DynamicShapeObjectType;
   mDataBlock = 0;
   mStatic = false;
   mRotate = false;
   mVelocity = VectorF(0,0,0);
   mAtRest = true;
   mAtRestCounter = 0;
   mInLiquid = false;
   delta.warpTicks = 0;
   delta.dt = 1;
   mCollisionObject = 0;
   mCollisionTimeout = 0;
   mPhysicsRep = NULL;

   mConvex.init(this);
   mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9);
   mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9);

   mLight = NULL;

   mSubclassItemHandlesScene = false;
}
Exemple #28
0
F32 CubeReflector::calcFaceScore( const ReflectParams &params, U32 faceidx )
{
   if ( Parent::calcScore( params ) <= 0.0f )
      return score;
   
   VectorF vLookatPt(0.0f, 0.0f, 0.0f);

   switch( faceidx )
   {
   case 0 : // D3DCUBEMAP_FACE_POSITIVE_X:
      vLookatPt = VectorF( 1.0f, 0.0f, 0.0f );      
      break;
   case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X:
      vLookatPt = VectorF( -1.0f, 0.0f, 0.0f );      
      break;
   case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y:
      vLookatPt = VectorF( 0.0f, 1.0f, 0.0f );      
      break;
   case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y:
      vLookatPt = VectorF( 0.0f, -1.0f, 0.0f );      
      break;
   case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z:
      vLookatPt = VectorF( 0.0f, 0.0f, 1.0f );      
      break;
   case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z:
      vLookatPt = VectorF( 0.0f, 0.0f, -1.0f );      
      break;
   }

   VectorF cameraDir;
   params.query->cameraMatrix.getColumn( 1, &cameraDir );

   F32 dot = mDot( cameraDir, -vLookatPt );

   dot = getMax( ( dot + 1.0f ) / 2.0f, 0.1f );

   score *= dot;

   return score;
}
Exemple #29
0
Mesh itemDamage(float damageValue)
{
    damageValue = limit<float>(damageValue, 0, 1);
    if(damageValue == 0)
        return Mesh();
    TextureDescriptor backgroundTexture = TextureAtlas::DamageBarGray.td();
    TextureDescriptor foregroundTexture = TextureAtlas::DamageBarGreen.td();
    if(damageValue > 1.0f / 3)
    {
        foregroundTexture = TextureAtlas::DamageBarYellow.td();
    }
    if(damageValue > 2.0f / 3)
    {
        foregroundTexture = TextureAtlas::DamageBarRed.td();
    }
    const float minX = 2 / 16.0f;
    const float maxX = 14 / 16.0f;
    float splitX = interpolate(damageValue, maxX, minX);
    const float minY = 2 / 16.0f;
    const float maxY = 4 / 16.0f;
    constexpr ColorF c = colorizeIdentity();
    const VectorF nxny = VectorF(minX, minY, 0);
    VectorF cxny = VectorF(splitX, minY, 0);
    const VectorF pxny = VectorF(maxX, minY, 0);
    const VectorF nxpy = VectorF(minX, maxY, 0);
    VectorF cxpy = VectorF(splitX, maxY, 0);
    const VectorF pxpy = VectorF(maxX, maxY, 0);
    Mesh retval = quadrilateral(foregroundTexture,
                             nxny, c,
                             cxny, c,
                             cxpy, c,
                             nxpy, c);
    retval.append(quadrilateral(backgroundTexture,
                             cxny, c,
                             pxny, c,
                             pxpy, c,
                             cxpy, c));
    return retval;
}
Exemple #30
0
void BtPlayer::findContact(   SceneObject **contactObject, 
                              VectorF *contactNormal, 
                              Vector<SceneObject*> *outOverlapObjects ) const
{
   AssertFatal( mGhostObject, "BtPlayer::findContact - The controller is null!" );

   VectorF normal;
   F32 maxDot = -1.0f;

   // Go thru the contact points... get the first contact.
   btHashedOverlappingPairCache *pairCache = mGhostObject->getOverlappingPairCache();
   btBroadphasePairArray& pairArray = pairCache->getOverlappingPairArray();
   U32 numPairs = pairArray.size();
   btManifoldArray manifoldArray;

   for ( U32 i=0; i < numPairs; i++ )
   {
      const btBroadphasePair &pair = pairArray[i];
      
      btBroadphasePair *collisionPair = pairCache->findPair( pair.m_pProxy0, pair.m_pProxy1 );
      if ( !collisionPair || !collisionPair->m_algorithm )
         continue;

      btCollisionObject *other = (btCollisionObject*)pair.m_pProxy0->m_clientObject;
      if ( other == mGhostObject )
         other = (btCollisionObject*)pair.m_pProxy1->m_clientObject;

	  //.logicking >>
	  if (outOverlapObjects->contains( PhysicsUserData::getObject( other->getUserPointer() ) ))
		  continue;
      //AssertFatal( !outOverlapObjects->contains( PhysicsUserData::getObject( other->getUserPointer() ) ),
      //   "Got multiple pairs of the same object!" );
	  //.logicking <<
      outOverlapObjects->push_back( PhysicsUserData::getObject( other->getUserPointer() ) );

      if ( other->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE )
         continue;

      manifoldArray.clear();
      collisionPair->m_algorithm->getAllContactManifolds( manifoldArray );

      for ( U32 j=0; j < manifoldArray.size(); j++ )
      {                                 
         btPersistentManifold *manifold = manifoldArray[j];
      	btScalar directionSign = manifold->getBody0() == mGhostObject ? 1.0f : -1.0f;

         for ( U32 p=0; p < manifold->getNumContacts(); p++ )
         {
            const btManifoldPoint &pt = manifold->getContactPoint(p);

            // Test the normal... is it the most vertical one we got?
            normal = btCast<Point3F>( pt.m_normalWorldOnB * directionSign );
            F32 dot = mDot( normal, VectorF( 0, 0, 1 ) );
            if ( dot > maxDot )
            {
               maxDot = dot;

               btCollisionObject *colObject = (btCollisionObject*)collisionPair->m_pProxy0->m_clientObject;
               *contactObject = PhysicsUserData::getObject( colObject->getUserPointer() );
               *contactNormal = normal; 
            }
         }
      }
   }
}