void T3DSceneComponent::AddSceneClient(T3DSceneClient * sceneClient)
{
   AssertFatal(sceneClient,"Cannot add a NULL scene client");

   // add to the front of the list
   sceneClient->setNextSceneClient(_sceneClientList);
   _sceneClientList = sceneClient;

   // extend object box
   ISolid3D * solid = dynamic_cast<ISolid3D*>(sceneClient);
   if (solid != NULL)
   {
      if (isObjectBoxLocked())
         setDirtyObjectBox(true);
      else
      {
         Box3F box = solid->getObjectBox();
         if (solid->getTransform3D() != NULL)
         {
            MatrixF mat;
            solid->getTransform3D()->getObjectMatrix(mat, true);
            mat.mul(box);
         }
         box.extend(_objectBox->get().min);
         box.extend(_objectBox->get().max);
         _objectBox->set(box);
      }

      // policy is that we become parent transform
      if (solid->getTransform3D() != NULL && solid->getTransform3D()->isChildOf(_transform, true))
         solid->getTransform3D()->setParentTransform(_transform);
   }
}
void T3DSceneComponent::_ComputeObjectBox()
{
   _objectBox->set(Box3F(10E30f, 10E30f, 10E30f, -10E30f, -10E30f, -10E30f));
   bool gotone = false;
   for (T3DSceneClient * walk = _sceneClientList; walk != NULL; walk = walk->getNextSceneClient())
   {
      ISolid3D * solid = dynamic_cast<ISolid3D*>(walk);
      if (solid == NULL)
         continue;

      Box3F box = solid->getObjectBox();
      if (solid->getTransform3D() != NULL)
      {
         MatrixF mat;
         solid->getTransform3D()->getObjectMatrix(mat, true);
         mat.mul(box);
      }
      box.extend(_objectBox->get().min);
      box.extend(_objectBox->get().max);
      _objectBox->set(box);
      gotone = true;
   }
   if (!gotone)
      _objectBox->set(Box3F());

   setDirtyObjectBox(false);
   setDirtyWorldBox(true);
}
Ejemplo n.º 3
0
void AppMesh::computeBounds(Box3F& bounds)
{
   bounds = Box3F::Invalid;

   if ( isSkin() )
   {
      // Need to skin the mesh before we can compute the bounds

      // Setup bone transforms
      Vector<MatrixF> boneTransforms;
      boneTransforms.setSize( nodeIndex.size() );
      for (S32 iBone = 0; iBone < boneTransforms.size(); iBone++)
      {
         MatrixF nodeMat = bones[iBone]->getNodeTransform( TSShapeLoader::DefaultTime );
         TSShapeLoader::zapScale(nodeMat);
         boneTransforms[iBone].mul( nodeMat, initialTransforms[iBone] );
      }

      // Multiply verts by weighted bone transforms
      for (S32 iVert = 0; iVert < initialVerts.size(); iVert++)
         points[iVert].set( Point3F::Zero );

      for (S32 iWeight = 0; iWeight < vertexIndex.size(); iWeight++)
      {
         const S32& vertIndex = vertexIndex[iWeight];
         const MatrixF& deltaTransform = boneTransforms[ boneIndex[iWeight] ];

         Point3F v;
         deltaTransform.mulP( initialVerts[vertIndex], &v );
         v *= weight[iWeight];

         points[vertIndex] += v;
      }

      // compute bounds for the skinned mesh
      for (S32 iVert = 0; iVert < initialVerts.size(); iVert++)
         bounds.extend( points[iVert] );
   }
   else
   {
      MatrixF transform = getMeshTransform(TSShapeLoader::DefaultTime);
      TSShapeLoader::zapScale(transform);

      for (S32 iVert = 0; iVert < points.size(); iVert++)
      {
         Point3F p;
         transform.mulP(points[iVert], &p);
         bounds.extend(p);
      }
   }
}
Ejemplo n.º 4
0
void OrientedBox3F::set( const MatrixF& transform, const Box3F& aabb )
{
   mCenter = aabb.getCenter();
   transform.mulP( mCenter );

   mAxes[ RightVector ] = transform.getRightVector();
   mAxes[ ForwardVector ] = transform.getForwardVector();
   mAxes[ UpVector ] = transform.getUpVector();

   mHalfExtents[ 0 ] = aabb.len_x() / 2.f;
   mHalfExtents[ 1 ] = aabb.len_y() / 2.f;
   mHalfExtents[ 2 ] = aabb.len_z() / 2.f;

   _initPoints();
}
Ejemplo n.º 5
0
void ColladaShapeLoader::computeBounds(Box3F& bounds)
{
   TSShapeLoader::computeBounds(bounds);

   // Check if the model origin needs adjusting
   if ( bounds.isValidBox() &&
       (ColladaUtils::getOptions().adjustCenter ||
        ColladaUtils::getOptions().adjustFloor) )
   {
      // Compute shape offset
      Point3F shapeOffset = Point3F::Zero;
      if ( ColladaUtils::getOptions().adjustCenter )
      {
         bounds.getCenter( &shapeOffset );
         shapeOffset = -shapeOffset;
      }
      if ( ColladaUtils::getOptions().adjustFloor )
         shapeOffset.z = -bounds.minExtents.z;

      // Adjust bounds
      bounds.minExtents += shapeOffset;
      bounds.maxExtents += shapeOffset;

      // Now adjust all positions for root level nodes (nodes with no parent)
      for (S32 iNode = 0; iNode < shape->nodes.size(); iNode++)
      {
         if ( !appNodes[iNode]->isParentRoot() )
            continue;

         // Adjust default translation
         shape->defaultTranslations[iNode] += shapeOffset;

         // Adjust animated translations
         for (S32 iSeq = 0; iSeq < shape->sequences.size(); iSeq++)
         {
            const TSShape::Sequence& seq = shape->sequences[iSeq];
            if ( seq.translationMatters.test(iNode) )
            {
               for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++)
               {
                  S32 index = seq.baseTranslation + seq.translationMatters.count(iNode)*seq.numKeyframes + iFrame;
                  shape->nodeTranslations[index] += shapeOffset;
               }
            }
         }
      }
   }
}
Ejemplo n.º 6
0
void Convex::updateWorkingList(const Box3F& box, const U32 colMask)
{
#if 0
   PROFILE_SCOPE( Convex_UpdateWorkingList );

   sTag++;

   // Clear objects off the working list that are no longer intersecting
   for (CollisionWorkingList* itr = mWorking.wLink.mNext; itr != &mWorking; itr = itr->wLink.mNext) {
      itr->mConvex->mTag = sTag;
      if ((!box.isOverlapped(itr->mConvex->getBoundingBox())) || (!itr->mConvex->getObject()->isCollisionEnabled())) {
         CollisionWorkingList* cl = itr;
         itr = itr->wLink.mPrev;
         cl->free();
      }
   }

   // Special processing for the terrain and interiors...
   AssertFatal(mObject->getContainer(), "Must be in a container!");

   SimpleQueryList sql;
   mObject->getContainer()->findObjects(box, colMask,SimpleQueryList::insertionCallback, &sql);
   for (U32 i = 0; i < sql.mList.size(); i++)
      sql.mList[i]->buildConvex(box, this);
#endif
}
Ejemplo n.º 7
0
void VehicleBlocker::buildConvex(const Box3F& box, Convex* convex)
{
   // These should really come out of a pool
   mConvexList->collectGarbage();

   if (box.isOverlapped(getWorldBox()) == false)
      return;

   // Just return a box convex for the entire shape...
   Convex* cc = 0;
   CollisionWorkingList& wl = convex->getWorkingList();
   for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) 
   {
      if (itr->mConvex->getType() == BoxConvexType &&
          itr->mConvex->getObject() == this) {
         cc = itr->mConvex;
         break;
      }
   }
   if (cc)
      return;

   // Create a new convex.
   BoxConvex* cp = new BoxConvex;
   mConvexList->registerObject(cp);
   convex->addToWorkingList(cp);
   cp->init(this);

   mObjBox.getCenter(&cp->mCenter);
   cp->mSize.x = mObjBox.len_x() / 2.0f;
   cp->mSize.y = mObjBox.len_y() / 2.0f;
   cp->mSize.z = mObjBox.len_z() / 2.0f;
}
Ejemplo n.º 8
0
void GroundPlane::buildConvex( const Box3F& box, Convex* convex )
{
   mConvexList->collectGarbage();

   Box3F planeBox = getPlaneBox();
   if ( !box.isOverlapped( planeBox ) )
      return;

   // See if we already have a convex in the working set.
   BoxConvex *boxConvex = NULL;
   CollisionWorkingList &wl = convex->getWorkingList();
   CollisionWorkingList *itr = wl.wLink.mNext;
   for ( ; itr != &wl; itr = itr->wLink.mNext )
   {
      if (  itr->mConvex->getType() == BoxConvexType &&
            itr->mConvex->getObject() == this )
      {
         boxConvex = (BoxConvex*)itr->mConvex;
         break;
      }
   }

   if ( !boxConvex )
   {
      boxConvex = new BoxConvex;
      mConvexList->registerObject( boxConvex );
      boxConvex->init( this );

      convex->addToWorkingList( boxConvex );
   }

   // Update our convex to best match the queried box
   if ( boxConvex )
   {
      Point3F queryCenter = box.getCenter();

      boxConvex->mCenter      = Point3F( queryCenter.x, queryCenter.y, -GROUND_PLANE_BOX_HEIGHT_HALF );
      boxConvex->mSize        = Point3F( box.getExtents().x,
                                         box.getExtents().y,
                                         GROUND_PLANE_BOX_HEIGHT_HALF );
   }
}
Ejemplo n.º 9
0
void ConvexShape::buildConvex( const Box3F &box, Convex *convex )
{
   if ( mGeometry.faces.empty() )
      return;

   mConvexList->collectGarbage();

   Box3F realBox = box;
   mWorldToObj.mul( realBox );
   realBox.minExtents.convolveInverse( mObjScale );
   realBox.maxExtents.convolveInverse( mObjScale );

   if ( realBox.isOverlapped( getObjBox() ) == false )
      return;

   // See if this convex exists in the working set already...
   Convex *cc = 0;
   CollisionWorkingList &wl = convex->getWorkingList();
   for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) 
   {
      if ( itr->mConvex->getType() == ConvexShapeCollisionConvexType )
      {
         ConvexShapeCollisionConvex *pConvex = static_cast<ConvexShapeCollisionConvex*>(itr->mConvex);

         if ( pConvex->pShape == this )              
         {
            cc = itr->mConvex;
            return;
         }
      }
   }

   // Set up the convex...

   ConvexShapeCollisionConvex *cp = new ConvexShapeCollisionConvex();

   mConvexList->registerObject( cp );
   convex->addToWorkingList( cp );

   cp->mObject = this;
   cp->pShape  = this;   
}
Ejemplo n.º 10
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);
	}
}
Ejemplo n.º 11
0
void TSShapeLoader::computeBounds(Box3F& bounds)
{
   // Compute the box that encloses the model geometry
   bounds = Box3F::Invalid;

   // Use bounds node geometry if present
   if ( boundsNode && boundsNode->getNumMesh() )
   {
      for (S32 iMesh = 0; iMesh < boundsNode->getNumMesh(); iMesh++)
      {
         AppMesh* mesh = boundsNode->getMesh( iMesh );
         if ( !mesh )
            continue;

         Box3F meshBounds;
         mesh->computeBounds( meshBounds );
         if ( meshBounds.isValidBox() )
            bounds.intersect( meshBounds );
      }
   }
   else
   {
      // Compute bounds based on all geometry in the model
      for (S32 iMesh = 0; iMesh < appMeshes.size(); iMesh++)
      {
         AppMesh* mesh = appMeshes[iMesh];
         if ( !mesh )
            continue;

         Box3F meshBounds;
         mesh->computeBounds( meshBounds );
         if ( meshBounds.isValidBox() )
            bounds.intersect( meshBounds );
      }
   }
}
Ejemplo n.º 12
0
bool AITurretShape::_testTargetLineOfSight(Point3F& aimPoint, ShapeBase* target, Point3F& sightPoint)
{
   Point3F targetCenter = target->getBoxCenter();
   RayInfo ri;
   bool hit = false;

   target->disableCollision();
   
   // First check for a clear line of sight to the target's center
   Point3F testPoint =  targetCenter;
   hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
   if (hit)
   {
      // No clear line of sight to center, so try to the target's right.  Players holding
      // a gun in their right hand will tend to stick their right shoulder out first if
      // they're peering around some cover to shoot, like a wall.
      Box3F targetBounds = target->getObjBox();
      F32 radius = targetBounds.len_x() > targetBounds.len_y() ? targetBounds.len_x() : targetBounds.len_y();
      radius *= 0.5;

      VectorF toTurret = aimPoint - targetCenter;
      toTurret.normalizeSafe();
      VectorF toTurretRight = mCross(toTurret, Point3F::UnitZ);

      testPoint = targetCenter + toTurretRight * radius;

      hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);

      if (hit)
      {
         // No clear line of sight to right, so try the target's left
         VectorF toTurretLeft = toTurretRight * -1.0f;
         testPoint = targetCenter + toTurretLeft * radius;
         hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
      }

      if (hit)
      {
         // No clear line of sight to left, so try the target's top
         testPoint = targetCenter;
         testPoint.z += targetBounds.len_z() * 0.5f;
         hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
      }

      if (hit)
      {
         // No clear line of sight to top, so try the target's bottom
         testPoint = targetCenter;
         testPoint.z -= targetBounds.len_z() * 0.5f;
         hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri);
      }
   }
   
   target->enableCollision();

   if (!hit)
   {
      // Line of sight point is that last one  we tested
      sightPoint = testPoint;
   }

   return !hit;
}
Ejemplo n.º 13
0
bool AtlasGeomChunkTracer::castLeafRay(const Point2I pos, const Point3F &start,
                                       const Point3F &end, const F32 &startT, 
                                       const F32 &endT, RayInfo *info)
{
   if(AtlasInstance::smRayCollisionDebugLevel == AtlasInstance::RayCollisionDebugToColTree)
   {
      const F32 invSize = 1.f / F32(BIT(mTreeDepth-1));

      // This is a bit of a hack. But good for testing.
      // Do collision against the collision tree leaf bounding box and return the result...
      F32 t; Point3F n;
      Box3F box;

      box.minExtents.set(Point3F(F32(pos.x  ) * invSize, F32(pos.y  ) * invSize, getSquareMin(0, pos)));
      box.maxExtents.set(Point3F(F32(pos.x+1) * invSize, F32(pos.y+1) * invSize, getSquareMax(0, pos)));

      //Con::printf("   checking at xy = {%f, %f}->{%f, %f}, [%d, %d]", start.x, start.y, end.x, end.y, pos.x, pos.y);

      if(box.collideLine(start, end, &t, &n) && t >= startT && t <= endT)
      {
         info->t      = t;
         info->normal = n;
         return true;
      }

      return false;
   }
   else if( AtlasInstance::smRayCollisionDebugLevel == AtlasInstance::RayCollisionDebugToMesh )
   {
	   bool haveHit = false;
	   U32 currentIdx = 0;
	   U32 numIdx = mChunk->mIndexCount;
	   F32 bestT = F32_MAX;
	   U32 bestTri = -1;
	   Point2F bestBary;

	   while( !haveHit && currentIdx < numIdx )
	   {
		   const Point3F& a = mChunk->mVert[ mChunk->mIndex[ currentIdx ] ].point;
		   const Point3F& b = mChunk->mVert[ mChunk->mIndex[ currentIdx + 1 ] ].point;
		   const Point3F& c = mChunk->mVert[ mChunk->mIndex[ currentIdx + 2 ] ].point;

		   F32     localT;
		   Point2F localBary;

		   // Do the cast, using our conveniently precalculated ray delta...
		   if(castRayTriangle(mRayStart, mRayDelta, a,b,c, localT, localBary))
		   {
			   if(localT < bestT)
			   {
				   // And it hit before anything else we've seen.
				   bestTri  = currentIdx;
				   bestT    = localT;
				   bestBary = localBary;

				   haveHit = true;
			   }
		   }

		   currentIdx += 3;
	   }

      // Fill in extra info for the hit.
      if(!haveHit)
         return false;

      // Calculate the normal, we skip that for the initial check.
      Point3F norm; // Hi norm!

      const Point3F &a = mChunk->mVert[mChunk->mIndex[bestTri+0]].point;
      const Point3F &b = mChunk->mVert[mChunk->mIndex[bestTri+1]].point;
      const Point3F &c = mChunk->mVert[mChunk->mIndex[bestTri+2]].point;

      const Point2F &aTC = mChunk->mVert[mChunk->mIndex[bestTri+0]].texCoord;
      const Point2F &bTC = mChunk->mVert[mChunk->mIndex[bestTri+1]].texCoord;
      const Point2F &cTC = mChunk->mVert[mChunk->mIndex[bestTri+2]].texCoord;

      // Store everything relevant into the info structure.
      info->t = bestT;

      const Point3F e0 = b-a;
      const Point3F e1 = c-a;

      info->normal = mCross(e1, e0);
      info->normal.normalize();

      // Calculate and store the texture coords.
      const Point2F e0TC = bTC-aTC;
      const Point2F e1TC = cTC-aTC;
      info->texCoord = e0TC * bestBary.x + e1TC * bestBary.y + aTC;

      // Return true, we hit something!
      return true;
   }
   else
   {
      // Get the triangle list...
      U16 *triOffset = mChunk->mColIndicesBuffer + 
         mChunk->mColIndicesOffsets[pos.x * BIT(mChunk->mColTreeDepth-1) + pos.y];

      // Store best hit results...
      bool gotHit = false;
      F32 bestT = F32_MAX;
      U16 bestTri = -1, offset;
      Point2F bestBary;

      while((offset = *triOffset) != 0xFFFF)
      {
         // Advance to the next triangle..
         triOffset++;

         // Get each triangle, and do a raycast against it.
         Point3F a,b,c;

         AssertFatal(offset <  mChunk->mIndexCount, 
            "AtlasGeomTracer2::castLeafRay - offset past end of index list.");

         a = mChunk->mVert[mChunk->mIndex[offset+0]].point;
         b = mChunk->mVert[mChunk->mIndex[offset+1]].point;
         c = mChunk->mVert[mChunk->mIndex[offset+2]].point;

         /*Con::printf("  o testing triangle %d ({%f,%f,%f},{%f,%f,%f},{%f,%f,%f})",
                              offset, a.x, a.y, a.z, b.x, b.y, b.z, c.x, c.y, c.z); */

         F32     localT;
         Point2F localBary;

         // Do the cast, using our conveniently precalculated ray delta...
         if(castRayTriangle(mRayStart, mRayDelta, a,b,c, localT, localBary))
         {
            //Con::printf(" - hit triangle %d at  t=%f", offset, localT);

            // The ray intersected, but we have to make sure we hit actually on
            // the line segment. (ie, a ray isn't a ray, Ray.)

            // BJGTODO - This should prevent some nasty edge cases, but we
            //           seem to be calculating the wrong start and end T's.
            //           So I've disabled this for now but it will cause
            //           problems later!
            //if(localT < startT || localT > endT)
            //   continue;

            // It really, really hit, wow!
            if(localT < bestT)
            {
               // And it hit before anything else we've seen.
               bestTri  = offset;
               bestT    = localT;
               bestBary = localBary;

               gotHit = true;
            }
         }
         else
         {
            //Con::printf(" - didn't hit triangle %d at  t=%f", offset, localT);
         }
      }

      // Fill in extra info for the hit.
      if(!gotHit)
         return false;

      // Calculate the normal, we skip that for the initial check.
      Point3F norm; // Hi norm!

      const Point3F &a = mChunk->mVert[mChunk->mIndex[bestTri+0]].point;
      const Point3F &b = mChunk->mVert[mChunk->mIndex[bestTri+1]].point;
      const Point3F &c = mChunk->mVert[mChunk->mIndex[bestTri+2]].point;

      const Point2F &aTC = mChunk->mVert[mChunk->mIndex[bestTri+0]].texCoord;
      const Point2F &bTC = mChunk->mVert[mChunk->mIndex[bestTri+1]].texCoord;
      const Point2F &cTC = mChunk->mVert[mChunk->mIndex[bestTri+2]].texCoord;

      // Store everything relevant into the info structure.
      info->t = bestT;

      const Point3F e0 = b-a;
      const Point3F e1 = c-a;

      info->normal = mCross(e1, e0);
      info->normal.normalize();

      // Calculate and store the texture coords.
      const Point2F e0TC = bTC-aTC;
      const Point2F e1TC = cTC-aTC;
      info->texCoord = e0TC * bestBary.x + e1TC * bestBary.y + aTC;

      // Return true, we hit something!
      return true;
   }
}
Ejemplo n.º 14
0
Point3F Etherform::_move( const F32 travelTime, Collision *outCol )
{
   // Try and move to new pos
   F32 totalMotion  = 0.0f;
   
   // TODO: not used?
   //F32 initialSpeed = mVelocity.len();

   Point3F start;
   Point3F initialPosition;
   getTransform().getColumn(3,&start);
   initialPosition = start;

   static CollisionList collisionList;
   static CollisionList physZoneCollisionList;

   collisionList.clear();
   physZoneCollisionList.clear();

   MatrixF collisionMatrix(true);
   collisionMatrix.setColumn(3, start);

   VectorF firstNormal(0.0f, 0.0f, 0.0f);
   F32 time = travelTime;
   U32 count = 0;

   static Polyhedron sBoxPolyhedron;
   static ExtrudedPolyList sExtrudedPolyList;
   static ExtrudedPolyList sPhysZonePolyList;

   for (; count < sMoveRetryCount; count++) {
      F32 speed = mVelocity.len();
      if(!speed)
         break;

      Point3F end = start + mVelocity * time;
      Point3F distance = end - start;

      if (mFabs(distance.x) < mObjBox.len_x() &&
          mFabs(distance.y) < mObjBox.len_y() &&
          mFabs(distance.z) < mObjBox.len_z())
      {
         // We can potentially early out of this.  If there are no polys in the clipped polylist at our
         //  end position, then we can bail, and just set start = end;
         Box3F wBox = mScaledBox;
         wBox.minExtents += end;
         wBox.maxExtents += end;

         static EarlyOutPolyList eaPolyList;
         eaPolyList.clear();
         eaPolyList.mNormal.set(0.0f, 0.0f, 0.0f);
         eaPolyList.mPlaneList.clear();
         eaPolyList.mPlaneList.setSize(6);
         eaPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f));
         eaPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f));
         eaPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f));
         eaPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f));
         eaPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f));
         eaPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f));

         // Build list from convex states here...
         CollisionWorkingList& rList = mConvex.getWorkingList();
         CollisionWorkingList* pList = rList.wLink.mNext;
         while (pList != &rList) {
            Convex* pConvex = pList->mConvex;
            if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) {
               Box3F convexBox = pConvex->getBoundingBox();
               if (wBox.isOverlapped(convexBox))
               {
                  // No need to separate out the physical zones here, we want those
                  //  to cause a fallthrough as well...
                  pConvex->getPolyList(&eaPolyList);
               }
            }
            pList = pList->wLink.mNext;
         }

         if (eaPolyList.isEmpty())
         {
            totalMotion += (end - start).len();
            start = end;
            break;
         }
      }

      collisionMatrix.setColumn(3, start);
      sBoxPolyhedron.buildBox(collisionMatrix, mScaledBox, true);

      // Setup the bounding box for the extrudedPolyList
      Box3F plistBox = mScaledBox;
      collisionMatrix.mul(plistBox);
      Point3F oldMin = plistBox.minExtents;
      Point3F oldMax = plistBox.maxExtents;
      plistBox.minExtents.setMin(oldMin + (mVelocity * time) - Point3F(0.1f, 0.1f, 0.1f));
      plistBox.maxExtents.setMax(oldMax + (mVelocity * time) + Point3F(0.1f, 0.1f, 0.1f));

      // Build extruded polyList...
      VectorF vector = end - start;
      sExtrudedPolyList.extrude(sBoxPolyhedron,vector);
      sExtrudedPolyList.setVelocity(mVelocity);
      sExtrudedPolyList.setCollisionList(&collisionList);

      sPhysZonePolyList.extrude(sBoxPolyhedron,vector);
      sPhysZonePolyList.setVelocity(mVelocity);
      sPhysZonePolyList.setCollisionList(&physZoneCollisionList);

      // Build list from convex states here...
      CollisionWorkingList& rList = mConvex.getWorkingList();
      CollisionWorkingList* pList = rList.wLink.mNext;
      while (pList != &rList) {
         Convex* pConvex = pList->mConvex;
         if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) {
            Box3F convexBox = pConvex->getBoundingBox();
            if (plistBox.isOverlapped(convexBox))
            {
               if (pConvex->getObject()->getTypeMask() & PhysicalZoneObjectType)
                  pConvex->getPolyList(&sPhysZonePolyList);
               else
                  pConvex->getPolyList(&sExtrudedPolyList);
            }
         }
         pList = pList->wLink.mNext;
      }

      // Take into account any physical zones...
      for (U32 j = 0; j < physZoneCollisionList.getCount(); j++) 
      {
         AssertFatal(dynamic_cast<PhysicalZone*>(physZoneCollisionList[j].object), "Bad phys zone!");
         const PhysicalZone* pZone = (PhysicalZone*)physZoneCollisionList[j].object;
         if (pZone->isActive())
            mVelocity *= pZone->getVelocityMod();
      }

      if (collisionList.getCount() != 0 && collisionList.getTime() < 1.0f) 
      {
         // Set to collision point
         F32 velLen = mVelocity.len();

         F32 dt = time * getMin(collisionList.getTime(), 1.0f);
         start += mVelocity * dt;
         time -= dt;

         totalMotion += velLen * dt;

         // Back off...
         if ( velLen > 0.f ) {
            F32 newT = getMin(0.01f / velLen, dt);
            start -= mVelocity * newT;
            totalMotion -= velLen * newT;
         }

         // Pick the surface most parallel to the face that was hit.
         const Collision *collision = &collisionList[0];
         const Collision *cp = collision + 1;
         const Collision *ep = collision + collisionList.getCount();
         for (; cp != ep; cp++)
         {
            if (cp->faceDot > collision->faceDot)
               collision = cp;
         }

         F32 bd = -mDot(mVelocity, collision->normal);

         // Copy this collision out so
         // we can use it to do impacts
         // and query collision.
         *outCol = *collision;

         // Subtract out velocity
         VectorF dv = collision->normal * (bd + sNormalElasticity);
         mVelocity += dv;
         if (count == 0)
         {
            firstNormal = collision->normal;
         }
         else
         {
            if (count == 1)
            {
               // Re-orient velocity along the crease.
               if (mDot(dv,firstNormal) < 0.0f &&
                   mDot(collision->normal,firstNormal) < 0.0f)
               {
                  VectorF nv;
                  mCross(collision->normal,firstNormal,&nv);
                  F32 nvl = nv.len();
                  if (nvl)
                  {
                     if (mDot(nv,mVelocity) < 0.0f)
                        nvl = -nvl;
                     nv *= mVelocity.len() / nvl;
                     mVelocity = nv;
                  }
               }
            }
         }
      }
      else
      {
         totalMotion += (end - start).len();
         start = end;
         break;
      }
   }

   if (count == sMoveRetryCount)
   {
      // Failed to move
      start = initialPosition;
      mVelocity.set(0.0f, 0.0f, 0.0f);
   }

   return start;
}
Ejemplo n.º 15
0
void Polytope::buildBox(const MatrixF& transform,const Box3F& box)
{
   // Box is assumed to be axis aligned in the source space.
   // Transform into geometry space
   Point3F xvec,yvec,zvec,min;
   transform.getColumn(0,&xvec);
   xvec *= box.len_x();
   transform.getColumn(1,&yvec);
   yvec *= box.len_y();
   transform.getColumn(2,&zvec);
   zvec *= box.len_z();
   transform.mulP(box.minExtents,&min);

   // Initial vertices
   mVertexList.setSize(8);
   mVertexList[0].point = min;
   mVertexList[1].point = min + yvec;
   mVertexList[2].point = min + xvec + yvec;
   mVertexList[3].point = min + xvec;
   mVertexList[4].point = mVertexList[0].point + zvec;
   mVertexList[5].point = mVertexList[1].point + zvec;
   mVertexList[6].point = mVertexList[2].point + zvec;
   mVertexList[7].point = mVertexList[3].point + zvec;
   S32 i;
   for (i = 0; i < 8; i++)
      mVertexList[i].side = 0;

   // Initial faces
   mFaceList.setSize(6);
   for (S32 f = 0; f < 6; f++) {
      Face& face = mFaceList[f];
      face.original = true;
      face.vertex = 0;
   }

   mFaceList[0].plane.set(mVertexList[0].point,xvec);
   mFaceList[0].plane.invert();
   mFaceList[1].plane.set(mVertexList[2].point,yvec);
   mFaceList[2].plane.set(mVertexList[2].point,xvec);
   mFaceList[3].plane.set(mVertexList[0].point,yvec);
   mFaceList[3].plane.invert();
   mFaceList[4].plane.set(mVertexList[0].point,zvec);
   mFaceList[4].plane.invert();
   mFaceList[5].plane.set(mVertexList[4].point,zvec);

   // Initial edges
   mEdgeList.setSize(12);
   Edge* edge = mEdgeList.begin();
   S32 nextEdge = 0;
   for (i = 0; i < 4; i++) {
      S32 n = (i == 3)? 0: i + 1;
      S32 p = (i == 0)? 3: i - 1;
      edge->vertex[0] = i;
      edge->vertex[1] = n;
      edge->face[0] = i;
      edge->face[1] = 4;
      edge->next = ++nextEdge;
      edge++;
      edge->vertex[0] = 4 + i;
      edge->vertex[1] = 4 + n;
      edge->face[0] = i;
      edge->face[1] = 5;
      edge->next = ++nextEdge;
      edge++;
      edge->vertex[0] = i;
      edge->vertex[1] = 4 + i;
      edge->face[0] = i;
      edge->face[1] = p;
      edge->next = ++nextEdge;
      edge++;
   }
   edge[-1].next = -1;

   // Volume
   mVolumeList.setSize(1);
   Volume& volume = mVolumeList.last();
   volume.edgeList = 0;
   volume.material = -1;
   volume.object = 0;
   sideCount = 0;
}
Ejemplo n.º 16
0
bool SceneCullingState::createCullingVolume( const Point3F* vertices, U32 numVertices, SceneCullingVolume::Type type, SceneCullingVolume& outVolume )
{
   const Point3F& viewPos = getCameraState().getViewPosition();
   const Point3F& viewDir = getCameraState().getViewDirection();
   const bool isOrtho = getCullingFrustum().isOrtho();

   //TODO: check if we need to handle penetration of the near plane for occluders specially

   // Allocate space for the clipping planes we generate.  Assume the worst case
   // of every edge generating a plane and, for includers, all edges meeting at
   // steep angles so we need to insert extra planes (the latter is not possible,
   // of course, but it makes things less complicated here).  For occluders, add
   // an extra plane for the near cap.

   const U32 maxPlanes = ( type == SceneCullingVolume::Occluder ? numVertices + 1 : numVertices * 2 );
   PlaneF* planes = allocateData< PlaneF >( maxPlanes );

   // Keep track of the world-space bounds of the polygon.  We use this later
   // to derive some metrics.

   Box3F wsPolyBounds;

   wsPolyBounds.minExtents = Point3F( TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX );
   wsPolyBounds.maxExtents = Point3F( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN );

   // For occluders, also keep track of the nearest, and two farthest silhouette points.  We use
   // this later to construct a near capping plane.
   F32 minVertexDistanceSquared = TypeTraits< F32 >::MAX;
   U32 leastDistantVert = 0;

   F32 maxVertexDistancesSquared[ 2 ] = { TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN };
   U32 mostDistantVertices[ 2 ] = { 0, 0 };

   // Generate the extrusion volume.  For orthographic projections, extrude
   // parallel to the view direction whereas for parallel projections, extrude
   // from the viewpoint.

   U32 numPlanes = 0;
   U32 lastVertex = numVertices - 1;
   bool invert = false;

   for( U32 i = 0; i < numVertices; lastVertex = i, ++ i )
   {
      AssertFatal( numPlanes < maxPlanes, "SceneCullingState::createCullingVolume - Did not allocate enough planes!" );

      const Point3F& v1 = vertices[ i ];
      const Point3F& v2 = vertices[ lastVertex ];

      // Keep track of bounds.

      wsPolyBounds.minExtents.setMin( v1 );
      wsPolyBounds.maxExtents.setMax( v1 );

      // Skip the edge if it's length is really short.

      const Point3F edgeVector = v2 - v1;
      const F32 edgeVectorLenSquared = edgeVector.lenSquared();
      if( edgeVectorLenSquared < 0.025f )
         continue;

      //TODO: might need to do additional checks here for non-planar polygons used by occluders
      //TODO: test for colinearity of edge vector with view vector (occluders only)

      // Create a plane for the edge.

      if( isOrtho )
      {
         // Compute a plane through the two edge vertices and one
         // of the vertices extended along the view direction.

         if( !invert )
            planes[ numPlanes ] = PlaneF( v1, v1 + viewDir, v2 );
         else
            planes[ numPlanes ] = PlaneF( v2, v1 + viewDir, v1 );
      }
      else
      {
         // Compute a plane going through the viewpoint and the two
         // edge vertices.

         if( !invert )
            planes[ numPlanes ] = PlaneF( v1, viewPos, v2 );
         else
            planes[ numPlanes ] = PlaneF( v2, viewPos, v1 );
      }

      numPlanes ++;

      // If this is the first plane that we have created, find out whether
      // the vertex ordering is giving us the plane orientations that we want
      // (facing inside).  If not, invert vertex order from now on.

      if( numPlanes == 1 )
      {
         Point3F center( 0, 0, 0 );
         for( U32 n = 0; n < numVertices; ++ n )
            center += vertices[n];
         center /= numVertices;

         if( planes[numPlanes - 1].whichSide( center ) == PlaneF::Back )
         {
            invert = true;
            planes[ numPlanes - 1 ].invert();
         }
      }

      // For occluders, keep tabs of the nearest, and two farthest vertices.

      if( type == SceneCullingVolume::Occluder )
      {
         const F32 distSquared = ( v1 - viewPos ).lenSquared();
         if( distSquared < minVertexDistanceSquared )
         {
            minVertexDistanceSquared = distSquared;
            leastDistantVert = i;
         }
         if( distSquared > maxVertexDistancesSquared[ 0 ] )
         {
            // Move 0 to 1.
            maxVertexDistancesSquared[ 1 ] = maxVertexDistancesSquared[ 0 ];
            mostDistantVertices[ 1 ] = mostDistantVertices[ 0 ];

            // Replace 0.
            maxVertexDistancesSquared[ 0 ] = distSquared;
            mostDistantVertices[ 0 ] = i;
         }
         else if( distSquared > maxVertexDistancesSquared[ 1 ] )
         {
            // Replace 1.
            maxVertexDistancesSquared[ 1 ] = distSquared;
            mostDistantVertices[ 1 ] = i;
         }
      }
   }

   // If the extrusion produced no useful result, abort.

   if( numPlanes < 3 )
      return false;

   // For includers, test the angle of the edges at the current vertex.
   // If too steep, add an extra plane to improve culling efficiency.

   if( false )//type == SceneCullingVolume::Includer )
   {
      const U32 numOriginalPlanes = numPlanes;
      U32 lastPlaneIndex = numPlanes - 1;

      for( U32 i = 0; i < numOriginalPlanes; lastPlaneIndex = i, ++ i )
      {
         const PlaneF& currentPlane = planes[ i ];
         const PlaneF& lastPlane = planes[ lastPlaneIndex ];

         // Compute the cosine of the angle between the two plane normals.

         const F32 cosAngle = mFabs( mDot( currentPlane, lastPlane ) );

         // The planes meet at increasingly steep angles the more they point
         // in opposite directions, i.e the closer the angle of their normals
         // is to 180 degrees.  Skip any two planes that don't get near that.

         if( cosAngle > 0.1f )
            continue;

         //TODO

         const Point3F addNormals = currentPlane + lastPlane;
         const Point3F crossNormals = mCross( currentPlane, lastPlane );

         Point3F newNormal = currentPlane + lastPlane;//addNormals - mDot( addNormals, crossNormals ) * crossNormals;

         //

         planes[ numPlanes ] = PlaneF( currentPlane.getPosition(), newNormal );
         numPlanes ++;
      }
   }

   // Compute the metrics of the culling volume in relation to the view frustum.
   //
   // For this, we are short-circuiting things slightly.  The correct way (other than doing
   // full screen projections) would be to transform all the polygon points into camera
   // space, lay an AABB around those points, and then find the X and Z extents on the near plane.
   //
   // However, while not as accurate, a faster way is to just project the axial vectors
   // of the bounding box onto both the camera right and up vector.  This gives us a rough
   // estimate of the camera-space size of the polygon we're looking at.
   
   const MatrixF& cameraTransform = getCameraState().getViewWorldMatrix();
   const Point3F cameraRight = cameraTransform.getRightVector();
   const Point3F cameraUp = cameraTransform.getUpVector();

   const Point3F wsPolyBoundsExtents = wsPolyBounds.getExtents();
   
   F32 widthEstimate =
      getMax( mFabs( wsPolyBoundsExtents.x * cameraRight.x ),
         getMax( mFabs( wsPolyBoundsExtents.y * cameraRight.y ),
                 mFabs( wsPolyBoundsExtents.z * cameraRight.z ) ) );

   F32 heightEstimate =
      getMax( mFabs( wsPolyBoundsExtents.x * cameraUp.x ),
         getMax( mFabs( wsPolyBoundsExtents.y * cameraUp.y ),
                 mFabs( wsPolyBoundsExtents.z * cameraUp.z ) ) );

   // If the current camera is a perspective one, divide the two estimates
   // by the distance of the nearest bounding box vertex to the camera
   // to account for perspective distortion.

   if( !isOrtho )
   {
      const Point3F nearestVertex = wsPolyBounds.computeVertex(
         Box3F::getPointIndexFromOctant( - viewDir )
      );

      const F32 distance = ( nearestVertex - viewPos ).len();

      widthEstimate /= distance;
      heightEstimate /= distance;
   }

   // If we are creating an occluder, check to see if the estimates fit
   // our minimum requirements.

   if( type == SceneCullingVolume::Occluder )
   {
      const F32 widthEstimatePercentage = widthEstimate / getCullingFrustum().getWidth();
      const F32 heightEstimatePercentage = heightEstimate / getCullingFrustum().getHeight();

      if( widthEstimatePercentage < smOccluderMinWidthPercentage ||
          heightEstimatePercentage < smOccluderMinHeightPercentage )
         return false; // Reject.
   }

   // Use the area estimate as the volume's sort point.

   const F32 sortPoint = widthEstimate * heightEstimate;

   // Finally, if it's an occluder, compute a near cap.  The near cap prevents objects
   // in front of the occluder from testing positive.  The same could be achieved by
   // manually comparing distances before testing objects but since that would amount
   // to the same checks the plane/AABB tests do, it's easier to just add another plane.
   // Additionally, it gives the benefit of being able to create more precise culling
   // results by angling the plane.

   //NOTE: Could consider adding a near cap for includers too when generating a volume
   //  for the outdoor zone as that may prevent quite a bit of space from being included.
   //  However, given that this space will most likely just be filled with interior
   //  stuff anyway, it's probably not worth it.

   if( type == SceneCullingVolume::Occluder )
   {
      const U32 nearCapIndex = numPlanes;
      planes[ nearCapIndex ] = PlaneF(
         vertices[ mostDistantVertices[ 0 ] ],
         vertices[ mostDistantVertices[ 1 ] ],
         vertices[ leastDistantVert ] );

      // Invert the plane, if necessary.
      if( planes[ nearCapIndex ].whichSide( viewPos ) == PlaneF::Front )
         planes[ nearCapIndex ].invert();

      numPlanes ++;
   }

   // Create the volume from the planes.

   outVolume = SceneCullingVolume(
      type,
      PlaneSetF( planes, numPlanes )
   );
   outVolume.setSortPoint( sortPoint );

   // Done.

   return true;
}
Ejemplo n.º 17
0
void Etherform::_findContact( SceneObject **contactObject, 
                           VectorF *contactNormal, 
                           Vector<SceneObject*> *outOverlapObjects )
{
   Point3F pos;
   getTransform().getColumn(3,&pos);

   Box3F wBox;
   Point3F exp(0,0,sTractionDistance);
   wBox.minExtents = pos + mScaledBox.minExtents - exp;
   wBox.maxExtents.x = pos.x + mScaledBox.maxExtents.x;
   wBox.maxExtents.y = pos.y + mScaledBox.maxExtents.y;
   wBox.maxExtents.z = pos.z + mScaledBox.minExtents.z + sTractionDistance;

   static ClippedPolyList polyList;
   polyList.clear();
   polyList.doConstruct();
   polyList.mNormal.set(0.0f, 0.0f, 0.0f);
   polyList.setInterestNormal(Point3F(0.0f, 0.0f, -1.0f));

   polyList.mPlaneList.setSize(6);
   polyList.mPlaneList[0].setYZ(wBox.minExtents, -1.0f);
   polyList.mPlaneList[1].setXZ(wBox.maxExtents, 1.0f);
   polyList.mPlaneList[2].setYZ(wBox.maxExtents, 1.0f);
   polyList.mPlaneList[3].setXZ(wBox.minExtents, -1.0f);
   polyList.mPlaneList[4].setXY(wBox.minExtents, -1.0f);
   polyList.mPlaneList[5].setXY(wBox.maxExtents, 1.0f);
   Box3F plistBox = wBox;

   // Expand build box as it will be used to collide with items.
   // PickupRadius will be at least the size of the box.
   F32 pd = 0;
   wBox.minExtents.x -= pd; wBox.minExtents.y -= pd;
   wBox.maxExtents.x += pd; wBox.maxExtents.y += pd;
   wBox.maxExtents.z = pos.z + mScaledBox.maxExtents.z;

   // Build list from convex states here...
   CollisionWorkingList& rList = mConvex.getWorkingList();
   CollisionWorkingList* pList = rList.wLink.mNext;
   while (pList != &rList)
   {
      Convex* pConvex = pList->mConvex;

      U32 objectMask = pConvex->getObject()->getTypeMask();
      
      if (  ( objectMask & sCollisionMoveMask ) &&
            !( objectMask & PhysicalZoneObjectType ) )
      {
         Box3F convexBox = pConvex->getBoundingBox();
         if (plistBox.isOverlapped(convexBox))
            pConvex->getPolyList(&polyList);
      }
      else
         outOverlapObjects->push_back( pConvex->getObject() );

      pList = pList->wLink.mNext;
   }

   if (!polyList.isEmpty())
   {
      // Pick flattest surface
      F32 bestVd = -1.0f;
      ClippedPolyList::Poly* poly = polyList.mPolyList.begin();
      ClippedPolyList::Poly* end = polyList.mPolyList.end();
      for (; poly != end; poly++)
      {
         F32 vd = poly->plane.z;       // i.e.  mDot(Point3F(0,0,1), poly->plane);
         if (vd > bestVd)
         {
            bestVd = vd;
            *contactObject = poly->object;
            *contactNormal = poly->plane;
         }
      }
   }
}
Ejemplo n.º 18
0
//-----------------------------------------------------------------------------
//
// VActorPhysicsController::findGroundContact( pContactObject, pContactPoint, pContactNormal );
//
// ...
//
//-----------------------------------------------------------------------------
bool VActorPhysicsController::findGroundContact( SceneObject *&pContactObject, Point3F &pContactPoint, VectorF &pContactNormal )
{
    // Setup Collision List.
    static CollisionList sCollisionList;
    sCollisionList.clear();

    static Polyhedron       sBoxPolyhedron;
    static ExtrudedPolyList sExtrudedPolyList;

    // Fetch Max Step Height.
    const F32 stepHeight = mObject->getDataBlock()->getMaxStepHeight();

    // Determine Positions.
    const Point3F preTickPosition  = getPosition() + Point3F( 0.f, 0.f, stepHeight );
    const VectorF preTickVelocity  = getVelocity() + mGravity - VectorF( 0.f, 0.f, stepHeight / TickSec );
    const Point3F postTickPosition = preTickPosition + ( preTickVelocity * TickSec );
    const VectorF postTickVector   = postTickPosition - preTickPosition;

    // Construct Scaled Box.
    Box3F scaledBox = mObject->getObjBox();
    scaledBox.minExtents.convolve( mObject->getScale() );
    scaledBox.maxExtents.convolve( mObject->getScale() );

    // Setup Polyherdron.
    MatrixF collisionMatrix( true );
    collisionMatrix.setPosition( preTickPosition );
    sBoxPolyhedron.buildBox( collisionMatrix, scaledBox, true );

    // Setup Extruded Poly List.
    sExtrudedPolyList.extrude( sBoxPolyhedron, postTickVector );
    sExtrudedPolyList.setVelocity( preTickVelocity );
    sExtrudedPolyList.setCollisionList( &sCollisionList );

    // Construct World Convex Box & Adjust for Sweep.
    Box3F convexBox = scaledBox;
    getTransform().mul( convexBox );
    convexBox.minExtents += postTickVector;
    convexBox.maxExtents += postTickVector;

    // Build List of Contacts.
    CollisionWorkingList &rList = mConvex.getWorkingList();
    for ( CollisionWorkingList *pList = rList.wLink.mNext; pList != &rList; pList = pList->wLink.mNext )
    {
        Convex *convexShape = pList->mConvex;

        // Ground Object?
        if ( !( convexShape->getObject()->getTypeMask() & sGroundCollisionMask ) )
        {
            // No, Continue.
            continue;
        }

        // Overlap?
        const Box3F &collisionConvexBox = convexShape->getBoundingBox();
        if ( convexBox.isOverlapped( collisionConvexBox ) )
        {
            // Build Contact Information.
            convexShape->getPolyList( &sExtrudedPolyList );
        }
    }

    // Valid Collision?
    if ( sCollisionList.getCount() == 0 || sCollisionList.getTime() < 0.f || sCollisionList.getTime() > 1.f )
    {
        // No, Quit Now.
        return false;
    }

    // Use First Collision.
    Collision *collision = &sCollisionList[0];

    // More Collisions?
    if ( sCollisionList.getCount() > 1 )
    {
        // Check for Better Contacts.
        for ( Collision *cp = ( collision + 1 ); cp != ( collision + sCollisionList.getCount() ); cp++ )
        {
            if ( cp->faceDot > collision->faceDot )
            {
                // Use this One.
                collision = cp;
            }
        }
    }

    // Set Properties.
    pContactObject = collision->object;
    //pContactPoint  = collision->point;
    pContactPoint  = ( preTickPosition + ( preTickVelocity * TickSec * sCollisionList.getTime() ) );
    pContactNormal = collision->normal;

    // Valid Contact.
    return true;
}
Ejemplo n.º 19
0
bool EditTSCtrl::processCameraQuery(CameraQuery * query)
{
   if(mDisplayType == DisplayTypePerspective)
   {
      query->ortho = false;
   }
   else
   {
      query->ortho = true;
   }

   if (getCameraTransform(&query->cameraMatrix))
   {
      query->farPlane = gClientSceneGraph->getVisibleDistance() * smVisibleDistanceScale;
      query->nearPlane = gClientSceneGraph->getNearClip();
      query->fov = mDegToRad(smCamFOV);

      if(query->ortho)
      {
         MatrixF camRot(true);
         const F32 camBuffer = 1.0f;
         Point3F camPos = query->cameraMatrix.getPosition();

         F32 isocamplanedist = 0.0f;
         if(mDisplayType == DisplayTypeIsometric)
         {
            const RectI& vp = GFX->getViewport();
            isocamplanedist = 0.25 * vp.extent.y * mSin(mIsoCamAngle);
         }

         // Calculate the scene bounds
         Box3F sceneBounds;
         computeSceneBounds(sceneBounds);

         if(!sceneBounds.isValidBox())
         {
            sceneBounds.maxExtents = camPos + smMinSceneBounds;
            sceneBounds.minExtents = camPos - smMinSceneBounds;
         }
         else
         {
            query->farPlane = getMax(smMinSceneBounds.x * 2.0f, (sceneBounds.maxExtents - sceneBounds.minExtents).len() + camBuffer * 2.0f + isocamplanedist);
         }

         mRawCamPos = camPos;
         camPos += mOrthoCamTrans;

         switch(mDisplayType)
         {
            case DisplayTypeTop:
               camRot.setColumn(0, Point3F(1.0, 0.0,  0.0));
               camRot.setColumn(1, Point3F(0.0, 0.0, -1.0));
               camRot.setColumn(2, Point3F(0.0, 1.0,  0.0));
               camPos.z = getMax(camPos.z + smMinSceneBounds.z, sceneBounds.maxExtents.z + camBuffer);
               break;

            case DisplayTypeBottom:
               camRot.setColumn(0, Point3F(1.0,  0.0,  0.0));
               camRot.setColumn(1, Point3F(0.0,  0.0,  1.0));
               camRot.setColumn(2, Point3F(0.0, -1.0,  0.0));
               camPos.z = getMin(camPos.z - smMinSceneBounds.z, sceneBounds.minExtents.z - camBuffer);
               break;

            case DisplayTypeFront:
               camRot.setColumn(0, Point3F(-1.0,  0.0,  0.0));
               camRot.setColumn(1, Point3F( 0.0, -1.0,  0.0));
               camRot.setColumn(2, Point3F( 0.0,  0.0,  1.0));
               camPos.y = getMax(camPos.y + smMinSceneBounds.y, sceneBounds.maxExtents.y + camBuffer);
               break;

            case DisplayTypeBack:
               camRot.setColumn(0, Point3F(1.0,  0.0,  0.0));
               camRot.setColumn(1, Point3F(0.0,  1.0,  0.0));
               camRot.setColumn(2, Point3F(0.0,  0.0,  1.0));
               camPos.y = getMin(camPos.y - smMinSceneBounds.y, sceneBounds.minExtents.y - camBuffer);
               break;

            case DisplayTypeLeft:
               camRot.setColumn(0, Point3F( 0.0, -1.0,  0.0));
               camRot.setColumn(1, Point3F( 1.0,  0.0,  0.0));
               camRot.setColumn(2, Point3F( 0.0,  0.0,  1.0));
               camPos.x = getMin(camPos.x - smMinSceneBounds.x, sceneBounds.minExtents.x - camBuffer);
               break;

            case DisplayTypeRight:
               camRot.setColumn(0, Point3F( 0.0,  1.0,  0.0));
               camRot.setColumn(1, Point3F(-1.0,  0.0,  0.0));
               camRot.setColumn(2, Point3F( 0.0,  0.0,  1.0));
               camPos.x = getMax(camPos.x + smMinSceneBounds.x, sceneBounds.maxExtents.x + camBuffer);
               break;

            case DisplayTypeIsometric:
               camPos.z = sceneBounds.maxExtents.z + camBuffer + isocamplanedist;
               MatrixF angle(EulerF(mIsoCamAngle, 0, 0));
               MatrixF rot(mIsoCamRot);
               camRot.mul(rot, angle);
               break;
         }

         query->cameraMatrix = camRot;
         query->cameraMatrix.setPosition(camPos);
         query->fov = mOrthoFOV;
      }

      smCamMatrix = query->cameraMatrix;
      smCamMatrix.getColumn(3,&smCamPos);
      smCamOrtho = query->ortho;
      smCamNearPlane = query->nearPlane;

      return true;
   }
   return false;
}
Ejemplo n.º 20
0
void PolyBSPClip::box(const TMat3F transform,const Box3F& box)
{
	sideCount = 0;

	// Box is assumed to be axis aligned in the source space.
	// Transform into geometry space
	Point3F xvec,yvec,zvec,min;
	transform.getRow(0,&xvec);
	xvec *= box.len_x();
	transform.getRow(1,&yvec);
	yvec *= box.len_y();
	transform.getRow(2,&zvec);
	zvec *= box.len_z();
	m_mul(box.fMin,transform,&min);

	// Initial vertices
	vertexList.setSize(8);
	vertexList[0].point = min;
	vertexList[1].point = min + yvec;
	vertexList[2].point = min + xvec + yvec;
	vertexList[3].point = min + xvec;
	vertexList[4].point = vertexList[0].point + zvec;
	vertexList[5].point = vertexList[1].point + zvec;
	vertexList[6].point = vertexList[2].point + zvec;
	vertexList[7].point = vertexList[3].point + zvec;
   int i;
	for (i = 0; i < 8; i++) {
		vertexList[i].side = 0;
		vertexList[i].step = false;
	}

	// Initial faces
	faceList.setSize(6);
	for (int f = 0; f < 6; f++) {
		Face& face = faceList[f];
		face.vertex = 0;
		face.plane = 0;
		face.planeId = -1;
	}

	// Initial edges
	stack[0].setSize(12);
	Edge* edge = stack[0].begin();
	for (i = 0; i < 4; i++) {
		int n = (i == 3)? 0: i + 1;
		int p = (i == 0)? 3: i - 1;
		edge->vertex[0] = i;
		edge->vertex[1] = n;
		edge->face[0] = i;
		edge->face[1] = 4;
		edge++;
		edge->vertex[0] = 4 + i;
		edge->vertex[1] = 4 + n;
		edge->face[0] = i;
		edge->face[1] = 5;
		edge++;
		edge->vertex[0] = i;
		edge->vertex[1] = 4 + i;
		edge->face[0] = i;
		edge->face[1] = p;
		edge++;
	}

	// Starting stack
	stack[1].setSize(0);
	stack[2].setSize(0);
	Stack poly;
	poly.edge = &stack[0];
	poly.start = 0;
	poly.end = stack[0].size();
	split(rootNode,poly);
}
Ejemplo n.º 21
0
void
SimInterior::RenderImage::render( TSRenderContext &rc )
{
   //
   ITRMetrics.render.reset();
   ITRMetrics.numRenderedInteriors++;
   rc.getCamera()->pushTransform(transform);
   
   // Select the detail level we will be drawing at.  This is a function of the
   //  bounding box for the highest level of detail, and the radius of the minimum
   //  level in the current state.
   //  - If camera is inside object's bounding box, draw highest detail level.
   //  - else check the projected radius of the lowest level of detail...
   //
   Point3F cameraCoord = rc.getCamera()->getCC();
   Box3F   bbox        = instance->getHighestBoundingBox();

   // We only set the detail by the pixels iff we're outside the interior, it's
   //  not a linked interior, and we're not rendering it through a link.  Otherwise,
   //  the highest detail is drawn...
   //
   if (bbox.contains(cameraCoord) == false) {
      float projPixels = rc.getCamera()->
         transformProjectRadius(instance->getLowestCenterPt(),
            instance->getLowestRadius());
		projPixels *= 2.0f * rc.getCamera()->getPixelScale();
      instance->setDetailByPixels(projPixels);
   } else {
      // If we're inside or rendering through a link,
      //  we draw at the highest detail level...
      //
      instance->setDetailLevel(0);
   }

   rend.render(rc, instance);

   // Draw the bounding box if the flag is set...
   //
   if (g_drawSimInteriorBBox == true) {
      TS::PointArray* pArray = rc.getPointArray();
      Point3F bboxPts[8];
      bboxPts[0].set(bbox.fMin.x, bbox.fMin.y, bbox.fMin.z);
      bboxPts[1].set(bbox.fMin.x, bbox.fMax.y, bbox.fMin.z);
      bboxPts[2].set(bbox.fMin.x, bbox.fMax.y, bbox.fMax.z);
      bboxPts[3].set(bbox.fMin.x, bbox.fMin.y, bbox.fMax.z);
      bboxPts[4].set(bbox.fMax.x, bbox.fMin.y, bbox.fMin.z);
      bboxPts[5].set(bbox.fMax.x, bbox.fMax.y, bbox.fMin.z);
      bboxPts[6].set(bbox.fMax.x, bbox.fMax.y, bbox.fMax.z);
      bboxPts[7].set(bbox.fMax.x, bbox.fMin.y, bbox.fMax.z);
      
      int start = pArray->addPoints(8, bboxPts);
      
      pArray->drawLine(start + 0, start + 1, 253);
      pArray->drawLine(start + 1, start + 2, 253);
      pArray->drawLine(start + 2, start + 3, 253);
      pArray->drawLine(start + 3, start + 0, 253);
      pArray->drawLine(start + 4, start + 5, 253);
      pArray->drawLine(start + 5, start + 6, 253);
      pArray->drawLine(start + 6, start + 7, 253);
      pArray->drawLine(start + 7, start + 4, 253);
      pArray->drawLine(start + 0, start + 4, 253);
      pArray->drawLine(start + 1, start + 5, 253);
      pArray->drawLine(start + 2, start + 6, 253);
      pArray->drawLine(start + 3, start + 7, 253);
   }

   rc.getCamera()->popTransform();
}
Ejemplo n.º 22
0
void DecalRoad::_captureVerts()
{  
   PROFILE_SCOPE( DecalRoad_captureVerts );

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

   if ( isServerObject() )
   {
      //Con::errorf( "DecalRoad::_captureVerts - called on the server side!" );
      return;
   }

   if ( mEdges.size() == 0 )
      return;

   //
   // Construct ClippedPolyList objects for each pair
   // of roadEdges.
   // Use them to capture Terrain verts.
   //
   SphereF sphere;
   RoadEdge *edge = NULL;
   RoadEdge *nextEdge = NULL;

   mTriangleCount = 0;
   mVertCount = 0;

   Vector<ClippedPolyList> clipperList;

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

      box.minExtents = edge->p1;
      box.maxExtents = edge->p1;
      box.extend( edge->p0 );
      box.extend( edge->p2 );
      box.extend( nextEdge->p0 );
      box.extend( nextEdge->p1 );
      box.extend( nextEdge->p2 );
      box.minExtents.z -= 5.0f;
      box.maxExtents.z += 5.0f;

      sphere.center = ( nextEdge->p1 + edge->p1 ) * 0.5f;
      sphere.radius = 100.0f; // NOTE: no idea how to calculate this

      ClippedPolyList clipper;
      clipper.mNormal.set(0.0f, 0.0f, 0.0f);
      VectorF n;
      PlaneF plane0, plane1;

      // Construct Back Plane
      n = edge->p2 - edge->p0;
      n.normalize();
      n = mCross( n, edge->uvec );      
      plane0.set( edge->p0, n );   
      clipper.mPlaneList.push_back( plane0 );

      // Construct Front Plane
      n = nextEdge->p2 - nextEdge->p0;
      n.normalize();
      n = -mCross( edge->uvec, n );
      plane1.set( nextEdge->p0, -n );
      //clipper.mPlaneList.push_back( plane1 );

      // Test if / where the planes intersect.
      bool discardLeft = false;
      bool discardRight = false;
      Point3F iPos; 
      VectorF iDir;

      if ( plane0.intersect( plane1, iPos, iDir ) )
      {                  
         Point2F iPos2F( iPos.x, iPos.y );
         Point2F cPos2F( edge->p1.x, edge->p1.y );
         Point2F rVec2F( edge->rvec.x, edge->rvec.y );

         Point2F iVec2F = iPos2F - cPos2F;
         F32 iLen = iVec2F.len();
         iVec2F.normalize();

         if ( iLen < edge->width * 0.5f )
         {
            F32 dot = mDot( rVec2F, iVec2F );

            // The clipping planes intersected on the right side,
            // discard the right side clipping plane.
            if ( dot > 0.0f )
               discardRight = true;         
            // The clipping planes intersected on the left side,
            // discard the left side clipping plane.
            else
               discardLeft = true;
         }
      }

      // Left Plane
      if ( !discardLeft )
      {
         n = ( nextEdge->p0 - edge->p0 );
         n.normalize();
         n = mCross( edge->uvec, n );
         clipper.mPlaneList.push_back( PlaneF(edge->p0, n) );
      }
      else
      {
         nextEdge->p0 = edge->p0;         
      }

      // Right Plane
      if ( !discardRight )
      {
         n = ( nextEdge->p2 - edge->p2 );
         n.normalize();            
         n = -mCross( n, edge->uvec );
         clipper.mPlaneList.push_back( PlaneF(edge->p2, -n) );
      }
      else
      {
         nextEdge->p2 = edge->p2;         
      }

      n = nextEdge->p2 - nextEdge->p0;
      n.normalize();
      n = -mCross( edge->uvec, n );
      plane1.set( nextEdge->p0, -n );
      clipper.mPlaneList.push_back( plane1 );

      // We have constructed the clipping planes,
      // now grab/clip the terrain geometry
      getContainer()->buildPolyList( PLC_Decal, box, TerrainObjectType, &clipper );         
      clipper.cullUnusedVerts();
      clipper.triangulate();
      clipper.generateNormals();

      // If we got something, add it to the ClippedPolyList Vector
      if ( !clipper.isEmpty() && !( smDiscardAll && ( discardRight || discardLeft ) ) )
      {
         clipperList.push_back( clipper );       
      
         mVertCount += clipper.mVertexList.size();
         mTriangleCount += clipper.mPolyList.size();
      }
   }

   //
   // Set the roadEdge height to be flush with terrain
   // This is not really necessary but makes the debug spline rendering better.
   //
   for ( U32 i = 0; i < mEdges.size() - 1; i++ )
   {            
      edge = &mEdges[i];      
      
      _getTerrainHeight( edge->p0.x, edge->p0.y, edge->p0.z );

      _getTerrainHeight( edge->p2.x, edge->p2.y, edge->p2.z );
   }

   //
   // Allocate the RoadBatch(s)   
   //

   // If we captured no verts, then we can return here without
   // allocating any RoadBatches or the Vert/Index Buffers.
   // PreprenderImage will not allocate a render instance while
   // mBatches.size() is zero.
   U32 numClippers = clipperList.size();
   if ( numClippers == 0 )
      return;

   mBatches.clear();

   // Allocate the VertexBuffer and PrimitiveBuffer
   mVB.set( GFX, mVertCount, GFXBufferTypeStatic );
   mPB.set( GFX, mTriangleCount * 3, 0, GFXBufferTypeStatic );

   // Lock the VertexBuffer
   GFXVertexPNTBT *vertPtr = mVB.lock();
   if(!vertPtr) return;
   U32 vertIdx = 0;

   //
   // Fill the VertexBuffer and vertex data for the RoadBatches
   // Loop through the ClippedPolyList Vector   
   //
   RoadBatch *batch = NULL;
   F32 texStart = 0.0f;
   F32 texEnd;
   
   for ( U32 i = 0; i < clipperList.size(); i++ )
   {   
      ClippedPolyList *clipper = &clipperList[i];
      RoadEdge &edge = mEdges[i];
      RoadEdge &nextEdge = mEdges[i+1];      

      VectorF segFvec = nextEdge.p1 - edge.p1;
      F32 segLen = segFvec.len();
      segFvec.normalize();

      F32 texLen = segLen / mTextureLength;
      texEnd = texStart + texLen;

      BiQuadToSqr quadToSquare(  Point2F( edge.p0.x, edge.p0.y ), 
                                 Point2F( edge.p2.x, edge.p2.y ), 
                                 Point2F( nextEdge.p2.x, nextEdge.p2.y ), 
                                 Point2F( nextEdge.p0.x, nextEdge.p0.y ) );  

      //
      if ( i % mSegmentsPerBatch == 0 )
      {
         mBatches.increment();
         batch = &mBatches.last();

         batch->bounds.minExtents = clipper->mVertexList[0].point;
         batch->bounds.maxExtents = clipper->mVertexList[0].point;
         batch->startVert = vertIdx;
      }

      // Loop through each ClippedPolyList        
      for ( U32 j = 0; j < clipper->mVertexList.size(); j++ )
      {
         // Add each vert to the VertexBuffer
         Point3F pos = clipper->mVertexList[j].point;
         vertPtr[vertIdx].point = pos;
         vertPtr[vertIdx].normal = clipper->mNormalList[j];                     
                  
         Point2F uv = quadToSquare.transform( Point2F(pos.x,pos.y) );
         vertPtr[vertIdx].texCoord.x = uv.x;
         vertPtr[vertIdx].texCoord.y = -(( texEnd - texStart ) * uv.y + texStart);   

         vertPtr[vertIdx].tangent = mCross( segFvec, clipper->mNormalList[j] );
         vertPtr[vertIdx].binormal = segFvec;

         vertIdx++;

         // Expand the RoadBatch bounds to contain this vertex   
         batch->bounds.extend( pos );
      }    

      batch->endVert = vertIdx - 1;

      texStart = texEnd;
   }   

   // Unlock the VertexBuffer, we are done filling it.
   mVB.unlock();

   // Lock the PrimitiveBuffer
   U16 *idxBuff;
   mPB.lock(&idxBuff);     
   U32 curIdx = 0;
   U16 vertOffset = 0;   
   batch = NULL;
   S32 batchIdx = -1;

   // Fill the PrimitiveBuffer   
   // Loop through each ClippedPolyList in the Vector
   for ( U32 i = 0; i < clipperList.size(); i++ )
   {      
      ClippedPolyList *clipper = &clipperList[i];

      if ( i % mSegmentsPerBatch == 0 )
      {
         batchIdx++;
         batch = &mBatches[batchIdx];
         batch->startIndex = curIdx;         
      }        

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

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

         idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart] + vertOffset;         
         curIdx++;
         idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart + 1] + vertOffset;            
         curIdx++;
         idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart + 2] + vertOffset;                
         curIdx++;
      } 

      batch->endIndex = curIdx - 1;

      vertOffset += clipper->mVertexList.size();
   }

   // Unlock the PrimitiveBuffer, we are done filling it.
   mPB.unlock();

   // Generate the object/world bounds
   // Is the union of all batch bounding boxes.

   Box3F box;
   for ( U32 i = 0; i < mBatches.size(); i++ )
   {
      const RoadBatch &batch = mBatches[i];

      if ( i == 0 )      
         box = batch.bounds;               
      else      
         box.intersect( batch.bounds );               
   }

   Point3F pos = getPosition();

   mWorldBox = box;
   resetObjectBox();

   // Make sure we are in the correct bins given our world box.
   if( getSceneManager() != NULL )
      getSceneManager()->notifyObjectDirty( this );
}
Ejemplo n.º 23
0
void GFXDrawUtil::drawCube( const GFXStateBlockDesc &desc, const Box3F &box, const ColorI &color, const MatrixF *xfm )
{
   drawCube( desc, box.getExtents(), box.getCenter(), color, xfm );
}
Ejemplo n.º 24
0
void Forest::buildConvex( const Box3F &box, Convex *convex )
{
   mConvexList->collectGarbage();

   // Get all ForestItem(s) within the box.
   Vector<ForestItem> trees;
   mData->getItems( box, &trees );
   if ( trees.empty() )
      return;

   for ( U32 i = 0; i < trees.size(); i++ )
   {
      const ForestItem &forestItem = trees[i];

      Box3F realBox = box;
      mWorldToObj.mul( realBox );
      realBox.minExtents.convolveInverse( mObjScale );
      realBox.maxExtents.convolveInverse( mObjScale );

      // JCF: is this really necessary if we already got this ForestItem
      // as a result from getItems?
      if ( realBox.isOverlapped( getObjBox() ) == false )
         continue;      

      TSForestItemData *data = (TSForestItemData*)forestItem.getData();

      // Find CollisionDetail(s) that are defined...
      const Vector<S32> &details = data->getCollisionDetails();
      for ( U32 j = 0; j < details.size(); j++ ) 
      {
         // JCFHACK: need to fix this if we want this to work with speedtree
         // or other cases in which we don't have a TSForestItemData.
         // Most likely via preventing this method and other torque collision
         // specific stuff from ever getting called.
         if ( details[j] == -1 ) 
            continue;         

         // See if this convex exists in the working set already...
         Convex* cc = 0;
         CollisionWorkingList& wl = convex->getWorkingList();
         for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) 
         {
            if ( itr->mConvex->getType() == ForestConvexType )
            {
               ForestConvex *pConvex = static_cast<ForestConvex*>(itr->mConvex);

               if ( pConvex->mObject == this &&
                  pConvex->mForestItemKey == forestItem.getKey() &&
                  pConvex->hullId == j )
               {
                  cc = itr->mConvex;
                  break;
               }
            }
         }
         if (cc)
            continue;

         // Then we need to make one.
         ForestConvex *cp = new ForestConvex;
         mConvexList->registerObject(cp);
         convex->addToWorkingList(cp);
         cp->mObject          = this;
         cp->mForestItemKey   = forestItem.getKey();
         cp->mData            = data;
         cp->mScale           = forestItem.getScale();
         cp->hullId           = j;
         cp->box              = forestItem.getObjBox();
         cp->calculateTransform( forestItem.getTransform() );
      }
   }
}
Ejemplo n.º 25
0
void Item::updatePos(const U32 /*mask*/, const F32 dt)
{
   // Try and move
   Point3F pos;
   mObjToWorld.getColumn(3,&pos);
   delta.posVec = pos;

   bool contact = false;
   bool nonStatic = false;
   bool stickyNotify = false;
   CollisionList collisionList;
   F32 time = dt;

   static Polyhedron sBoxPolyhedron;
   static ExtrudedPolyList sExtrudedPolyList;
   static EarlyOutPolyList sEarlyOutPolyList;
   MatrixF collisionMatrix(true);
   Point3F end = pos + mVelocity * time;
   U32 mask = isServerObject() ? sServerCollisionMask : sClientCollisionMask;

   // Part of our speed problem here is that we don't track contact surfaces, like we do
   //  with the player.  In order to handle the most common and performance impacting
   //  instance of this problem, we'll use a ray cast to detect any contact surfaces below
   //  us.  This won't be perfect, but it only needs to catch a few of these to make a
   //  big difference.  We'll cast from the top center of the bounding box at the tick's
   //  beginning to the bottom center of the box at the end.
   Point3F startCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
                     (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
                     mObjBox.maxExtents.z);
   Point3F endCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5,
                   (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5,
                   mObjBox.minExtents.z);
   collisionMatrix.setColumn(3, pos);
   collisionMatrix.mulP(startCast);
   collisionMatrix.setColumn(3, end);
   collisionMatrix.mulP(endCast);
   RayInfo rinfo;
   bool doToughCollision = true;
   disableCollision();
   if (mCollisionObject)
      mCollisionObject->disableCollision();
   if (getContainer()->castRay(startCast, endCast, mask, &rinfo))
   {
      F32 bd = -mDot(mVelocity, rinfo.normal);

      if (bd >= 0.0)
      {
         // Contact!
         if (mDataBlock->sticky && rinfo.object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
            mVelocity.set(0, 0, 0);
            mAtRest = true;
            mAtRestCounter = 0;
            stickyNotify = true;
            mStickyCollisionPos    = rinfo.point;
            mStickyCollisionNormal = rinfo.normal;
            doToughCollision = false;;
         } else {
            // Subtract out velocity into surface and friction
            VectorF fv = mVelocity + rinfo.normal * bd;
            F32 fvl = fv.len();
            if (fvl) {
               F32 ff = bd * mDataBlock->friction;
               if (ff < fvl) {
                  fv *= ff / fvl;
                  fvl = ff;
               }
            }
            bd *= 1 + mDataBlock->elasticity;
            VectorF dv = rinfo.normal * (bd + 0.002);
            mVelocity += dv;
            mVelocity -= fv;

            // Keep track of what we hit
            contact = true;
            U32 typeMask = rinfo.object->getTypeMask();
            if (!(typeMask & StaticObjectType))
               nonStatic = true;
            if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
               ShapeBase* col = static_cast<ShapeBase*>(rinfo.object);
               queueCollision(col,mVelocity - col->getVelocity());
            }
         }
      }
   }
   enableCollision();
   if (mCollisionObject)
      mCollisionObject->enableCollision();

   if (doToughCollision)
   {
      U32 count;
      for (count = 0; count < 3; count++)
      {
         // Build list from convex states here...
         end = pos + mVelocity * time;


         collisionMatrix.setColumn(3, end);
         Box3F wBox = getObjBox();
         collisionMatrix.mul(wBox);
         Box3F testBox = wBox;
         Point3F oldMin = testBox.minExtents;
         Point3F oldMax = testBox.maxExtents;
         testBox.minExtents.setMin(oldMin + (mVelocity * time));
         testBox.maxExtents.setMin(oldMax + (mVelocity * time));

         sEarlyOutPolyList.clear();
         sEarlyOutPolyList.mNormal.set(0,0,0);
         sEarlyOutPolyList.mPlaneList.setSize(6);
         sEarlyOutPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1,0,0));
         sEarlyOutPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0,1,0));
         sEarlyOutPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1,0,0));
         sEarlyOutPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0,-1,0));
         sEarlyOutPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0,0,-1));
         sEarlyOutPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0,0,1));

         CollisionWorkingList& eorList = mConvex.getWorkingList();
         CollisionWorkingList* eopList = eorList.wLink.mNext;
         while (eopList != &eorList) {
            if ((eopList->mConvex->getObject()->getTypeMask() & mask) != 0)
            {
               Box3F convexBox = eopList->mConvex->getBoundingBox();
               if (testBox.isOverlapped(convexBox))
               {
                  eopList->mConvex->getPolyList(&sEarlyOutPolyList);
                  if (sEarlyOutPolyList.isEmpty() == false)
                     break;
               }
            }
            eopList = eopList->wLink.mNext;
         }
         if (sEarlyOutPolyList.isEmpty())
         {
            pos = end;
            break;
         }

         collisionMatrix.setColumn(3, pos);
         sBoxPolyhedron.buildBox(collisionMatrix, mObjBox, true);

         // Build extruded polyList...
         VectorF vector = end - pos;
         sExtrudedPolyList.extrude(sBoxPolyhedron, vector);
         sExtrudedPolyList.setVelocity(mVelocity);
         sExtrudedPolyList.setCollisionList(&collisionList);

         CollisionWorkingList& rList = mConvex.getWorkingList();
         CollisionWorkingList* pList = rList.wLink.mNext;
         while (pList != &rList) {
            if ((pList->mConvex->getObject()->getTypeMask() & mask) != 0)
            {
               Box3F convexBox = pList->mConvex->getBoundingBox();
               if (testBox.isOverlapped(convexBox))
               {
                  pList->mConvex->getPolyList(&sExtrudedPolyList);
               }
            }
            pList = pList->wLink.mNext;
         }

         if (collisionList.getTime() < 1.0)
         {
            // Set to collision point
            F32 dt = time * collisionList.getTime();
            pos += mVelocity * dt;
            time -= dt;

            // Pick the most resistant surface
            F32 bd = 0;
            const Collision* collision = 0;
            for (int c = 0; c < collisionList.getCount(); c++) {
               const Collision &cp = collisionList[c];
               F32 dot = -mDot(mVelocity,cp.normal);
               if (dot > bd) {
                  bd = dot;
                  collision = &cp;
               }
            }

            if (collision && mDataBlock->sticky && collision->object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) {
               mVelocity.set(0, 0, 0);
               mAtRest = true;
               mAtRestCounter = 0;
               stickyNotify = true;
               mStickyCollisionPos    = collision->point;
               mStickyCollisionNormal = collision->normal;
               break;
            } else {
               // Subtract out velocity into surface and friction
               if (collision) {
                  VectorF fv = mVelocity + collision->normal * bd;
                  F32 fvl = fv.len();
                  if (fvl) {
                     F32 ff = bd * mDataBlock->friction;
                     if (ff < fvl) {
                        fv *= ff / fvl;
                        fvl = ff;
                     }
                  }
                  bd *= 1 + mDataBlock->elasticity;
                  VectorF dv = collision->normal * (bd + 0.002);
                  mVelocity += dv;
                  mVelocity -= fv;

                  // Keep track of what we hit
                  contact = true;
                  U32 typeMask = collision->object->getTypeMask();
                  if (!(typeMask & StaticObjectType))
                     nonStatic = true;
                  if (isServerObject() && (typeMask & ShapeBaseObjectType)) {
                     ShapeBase* col = static_cast<ShapeBase*>(collision->object);
                     queueCollision(col,mVelocity - col->getVelocity());
                  }
               }
            }
         }
         else
         {
            pos = end;
            break;
         }
      }
      if (count == 3)
      {
         // Couldn't move...
         mVelocity.set(0, 0, 0);
      }
   }

   // If on the client, calculate delta for backstepping
   if (isGhost()) {
      delta.pos     = pos;
      delta.posVec -= pos;
      delta.dt = 1;
   }

   // Update transform
   MatrixF mat = mObjToWorld;
   mat.setColumn(3,pos);
   Parent::setTransform(mat);
   enableCollision();
   if (mCollisionObject)
      mCollisionObject->enableCollision();
   updateContainer();

   if ( mPhysicsRep )
      mPhysicsRep->setTransform( mat );

   //
   if (contact) {
      // Check for rest condition
      if (!nonStatic && mVelocity.len() < sAtRestVelocity) {
         mVelocity.x = mVelocity.y = mVelocity.z = 0;
         mAtRest = true;
         mAtRestCounter = 0;
      }

      // Only update the client if we hit a non-static shape or
      // if this is our final rest pos.
      if (nonStatic || mAtRest)
         setMaskBits(PositionMask);
   }

   // Collision callbacks. These need to be processed whether we hit
   // anything or not.
   if (!isGhost())
   {
      SimObjectPtr<Item> safePtr(this);
      if (stickyNotify)
      {
         notifyCollision();
         if(bool(safePtr))
			 onStickyCollision_callback( getIdString() );
      }
      else
         notifyCollision();

      // water
      if(bool(safePtr))
      {
         if(!mInLiquid && mWaterCoverage != 0.0f)
         {
            mInLiquid = true;
            if ( !isGhost() )
             mDataBlock->onEnterLiquid_callback( this, mWaterCoverage, mLiquidType.c_str() );
         }

         else if(mInLiquid && mWaterCoverage == 0.0f)
         {
            mInLiquid = false;
            if ( !isGhost() )
             mDataBlock->onLeaveLiquid_callback( this, mLiquidType.c_str() );
         }
      }
   }
}
Ejemplo n.º 26
0
//-----------------------------------------------------------------------------
//
// VActorPhysicsController::findCollision( pCollision );
//
// ...
//
//-----------------------------------------------------------------------------
bool VActorPhysicsController::findCollision( Collision *&pCollision )
{
    // Setup Collision List.
    static CollisionList sCollisionList;
    sCollisionList.clear();

    static Polyhedron       sBoxPolyhedron;
    static ExtrudedPolyList sExtrudedPolyList;

    // Determine Positions.
    const Point3F preTickPosition  = getPosition();
    const VectorF preTickVelocity  = getVelocity();
    const Point3F postTickPosition = preTickPosition + ( preTickVelocity * TickSec );
    const VectorF postTickVector   = postTickPosition - preTickPosition;

    // Construct Scaled Box.
    Box3F scaledBox = mObject->getObjBox();
    scaledBox.minExtents.convolve( mObject->getScale() );
    scaledBox.maxExtents.convolve( mObject->getScale() );

    // Setup Polyherdron.
    MatrixF collisionMatrix( true );
    collisionMatrix.setPosition( preTickPosition );
    sBoxPolyhedron.buildBox( collisionMatrix, scaledBox );

    // Setup Extruded Poly List.
    sExtrudedPolyList.extrude( sBoxPolyhedron, postTickVector );
    sExtrudedPolyList.setVelocity( preTickVelocity );
    sExtrudedPolyList.setCollisionList( &sCollisionList );

    // Construct World Convex Box & Adjust for Sweep.
    Box3F convexBox = scaledBox;
    getTransform().mul( convexBox );
    convexBox.minExtents += postTickVector;
    convexBox.maxExtents += postTickVector;

    // Determine the Collision Mask.
    const U32 collisionMask = ( isInWater() ) ? ( sGroundCollisionMask | sMoveCollisionMask ) : sMoveCollisionMask;

    // Build List of Contacts.
    CollisionWorkingList &rList = mConvex.getWorkingList();
    for ( CollisionWorkingList *pList = rList.wLink.mNext; pList != &rList; pList = pList->wLink.mNext )
    {
        Convex *convexShape = pList->mConvex;

        // Valid Collision Target?
        if ( !( convexShape->getObject()->getTypeMask() & collisionMask ) )
        {
            // No, Continue.
            continue;
        }

        // Overlap?
        const Box3F &collisionConvexBox = convexShape->getBoundingBox();
        if ( convexBox.isOverlapped( collisionConvexBox ) )
        {
            // Build Contact Information.
            convexShape->getPolyList( &sExtrudedPolyList );
        }
    }

    // Valid Collision?
    if ( sCollisionList.getCount() == 0 || sCollisionList.getTime() > 1.f )
    {
        // No, Quit Now.
        return false;
    }

    // Use First Collision.
    Collision *collision = &sCollisionList[0];

    // More Collisions?
    if ( sCollisionList.getCount() > 1 )
    {
        // Check for Better Contacts.
        for ( Collision *cp = ( collision + 1 ); cp != ( collision + sCollisionList.getCount() ); cp++ )
        {
            if ( cp->faceDot > collision->faceDot )
            {
                // Use this One.
                collision = cp;
            }
        }
    }

    // Store Reference.
    pCollision = collision;

    // Valid Collision.
    return true;
}
Ejemplo n.º 27
0
void TransformTool::renderTool()
{
   if (!mActive)
      return;

   // Grid
   {
      Point3F editorPos = mEditorManager->mEditorCamera.getWorldPosition();
      editorPos = editorPos / 10.0f;
      editorPos.x = mFloor(editorPos.x);
      editorPos.y = mFloor(editorPos.y);
      editorPos = editorPos * 10.0f;

      Torque::Debug.ddPush();
      Torque::Debug.ddSetState(true, false, true);

      Torque::Debug.ddSetWireframe(true);
      Torque::Debug.ddSetColor(BGFXCOLOR_RGBA(255, 255, 255, 255));
      F32 gridNormal[3] = { 0.0f, 0.0f, 1.0f };
      F32 gridPos[3] = { editorPos.x, editorPos.y, -0.01f };
      Torque::Debug.ddDrawGrid(gridNormal, gridPos, 100, 10.0f);
   }

   // Draw Light Icons
   /*if (mLightIcon != NULL)
   {
      Vector<Lighting::LightData*> lightList = Torque::Lighting.getLightList();
      for (S32 n = 0; n < lightList.size(); ++n)
      {
         Lighting::LightData* light = lightList[n];

         Torque::Graphics.drawBillboard(mEditorManager->mRenderLayer4View->id,
                                              mLightIcon,
                                              light->position,
                                              1.0f, 1.0f,
                                              ColorI(light->color[0] * 255, light->color[1] * 255, light->color[2] * 255, 255),
                                              NULL);
      }
   }*/

   if (mMultiselect)
   {
      Box3F multiSelectBox;
      multiSelectBox.set(Point3F(0, 0, 0));
         
      for (S32 n = 0; n < mSelectedObjects.size(); ++n)
      {
         Scene::SceneObject* obj = dynamic_cast<Scene::SceneObject*>(mSelectedObjects[n]);
         if (obj)
         {
            if (n == 0)
               multiSelectBox = obj->mBoundingBox;
            else
               multiSelectBox.intersect(obj->mBoundingBox);
         }

         Scene::BaseComponent* component = dynamic_cast<Scene::BaseComponent*>(mSelectedObjects[n]);
         if (component)
         {
            Box3F boundingBox = component->getBoundingBox();
            boundingBox.transform(component->mTransform.matrix);

            if (n == 0)
               multiSelectBox = boundingBox;
            else
               multiSelectBox.intersect(boundingBox);
         }
      }

      mSelectionBounds        = true;
      mSelectionBoundsStart   = multiSelectBox.minExtents;
      mSelectionBoundsEnd     = multiSelectBox.maxExtents;
   }

   // Object Selected
   if (mSelectedObject != NULL && mSelectedComponent == NULL)
   {
      mSelectionBounds        = true;
      mSelectionBoundsStart   = mSelectedObject->mBoundingBox.minExtents;
      mSelectionBoundsEnd     = mSelectedObject->mBoundingBox.maxExtents;
   }

   // Component Selected
   if (mSelectedObject != NULL && mSelectedComponent != NULL)
   {
      Box3F boundingBox = mSelectedComponent->getBoundingBox();
      boundingBox.transform(mSelectedObject->mTransform.matrix);

      mSelectionBounds        = true;
      mSelectionBoundsStart   = boundingBox.minExtents;
      mSelectionBoundsEnd     = boundingBox.maxExtents;
   }

   // Selection Bounding Box
   if (mSelectionBounds)
   {
      Aabb debugBox;
      debugBox.m_min[0] = mSelectionBoundsStart.x;
      debugBox.m_min[1] = mSelectionBoundsStart.y;
      debugBox.m_min[2] = mSelectionBoundsStart.z;
      debugBox.m_max[0] = mSelectionBoundsEnd.x;
      debugBox.m_max[1] = mSelectionBoundsEnd.y;
      debugBox.m_max[2] = mSelectionBoundsEnd.z;

      Torque::Debug.ddSetWireframe(true);
      Torque::Debug.ddSetColor(BGFXCOLOR_RGBA(mSelectionBoundsColor.red, mSelectionBoundsColor.green, mSelectionBoundsColor.blue, mSelectionBoundsColor.alpha));
      Torque::Debug.ddDrawAabb(debugBox);

      Torque::Debug.ddPop();
   }

   // Gizmo
   mGizmo.render();

   // Debug
   if (mDebugWorldRay)
   {
      Torque::Debug.ddMoveTo(mLastRayStart.x, mLastRayStart.y, mLastRayStart.z);
      Torque::Debug.ddLineTo(mLastRayEnd.x, mLastRayEnd.y, mLastRayEnd.z);
   }

   if (mDebugBoxSelection)
   {
      for (U32 i = 0; i < 4; ++i)
      {
         Torque::Debug.ddMoveTo(mBoxNearPoint.x, mBoxNearPoint.y, mBoxNearPoint.z);
         Torque::Debug.ddLineTo(mBoxFarPoint[i].x, mBoxFarPoint[i].y, mBoxFarPoint[i].z);
      }
   }
}
Ejemplo n.º 28
0
void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb )
{
   PROFILE_SCOPE( TSMesh_InnerRender );

   if( vertsPerFrame <= 0 ) 
      return;

   F32 meshVisibility = rdata.getFadeOverride() * mVisibility;
   if ( meshVisibility < VISIBILITY_EPSILON )
      return;

   const SceneRenderState *state = rdata.getSceneState();
   RenderPassManager *renderPass = state->getRenderPass();

   MeshRenderInst *coreRI = renderPass->allocInst<MeshRenderInst>();
   coreRI->type = RenderPassManager::RIT_Mesh;

   const MatrixF &objToWorld = GFX->getWorldMatrix();

   // Sort by the center point or the bounds.
   if ( rdata.useOriginSort() )
      coreRI->sortDistSq = ( objToWorld.getPosition() - state->getCameraPosition() ).lenSquared();
   else
   {
      Box3F rBox = mBounds;
      objToWorld.mul( rBox );
      coreRI->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() );      
   }

   if (getFlags(Billboard))
   {
      Point3F camPos = state->getDiffuseCameraPosition();
      Point3F objPos;
      objToWorld.getColumn(3, &objPos);
      Point3F targetVector = camPos - objPos;
      if(getFlags(BillboardZAxis))
         targetVector.z = 0.0f;
      targetVector.normalize();
      MatrixF orient = MathUtils::createOrientFromDir(targetVector);
      orient.setPosition(objPos);
      orient.scale(objToWorld.getScale());

      coreRI->objectToWorld = renderPass->allocUniqueXform( orient );
   }
   else
      coreRI->objectToWorld = renderPass->allocUniqueXform( objToWorld );

   coreRI->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View);
   coreRI->projection = renderPass->allocSharedXform(RenderPassManager::Projection);

   AssertFatal( vb.isValid(), "TSMesh::innerRender() - Got invalid vertex buffer!" );
   AssertFatal( pb.isValid(), "TSMesh::innerRender() - Got invalid primitive buffer!" );

   coreRI->vertBuff = &vb;
   coreRI->primBuff = &pb;
   coreRI->defaultKey2 = (U32) coreRI->vertBuff;

   coreRI->materialHint = rdata.getMaterialHint();

   coreRI->visibility = meshVisibility;  
   coreRI->cubemap = rdata.getCubemap();

   // NOTICE: SFXBB is removed and refraction is disabled!
   //coreRI->backBuffTex = GFX->getSfxBackBuffer();

   for ( S32 i = 0; i < primitives.size(); i++ )
   {
      const TSDrawPrimitive &draw = primitives[i];

      // We need to have a material.
      if ( draw.matIndex & TSDrawPrimitive::NoMaterial )
         continue;

#ifdef TORQUE_DEBUG
      // for inspection if you happen to be running in a debugger and can't do bit 
      // operations in your head.
      S32 triangles = draw.matIndex & TSDrawPrimitive::Triangles;
      S32 strip = draw.matIndex & TSDrawPrimitive::Strip;
      S32 fan = draw.matIndex & TSDrawPrimitive::Fan;
      S32 indexed = draw.matIndex & TSDrawPrimitive::Indexed;
      S32 type = draw.matIndex & TSDrawPrimitive::TypeMask;
      TORQUE_UNUSED(triangles);
      TORQUE_UNUSED(strip);
      TORQUE_UNUSED(fan);
      TORQUE_UNUSED(indexed);
      TORQUE_UNUSED(type);
#endif

      const U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask;
      BaseMatInstance *matInst = materials->getMaterialInst( matIndex );

#ifndef TORQUE_OS_MAC

      // Get the instancing material if this mesh qualifies.
      if ( meshType != SkinMeshType && pb->mPrimitiveArray[i].numVertices < smMaxInstancingVerts )
         matInst = InstancingMaterialHook::getInstancingMat( matInst );

#endif

      // If we don't have a material instance after the overload then
      // there is nothing to render... skip this primitive.
      matInst = state->getOverrideMaterial( matInst );
      if ( !matInst || !matInst->isValid())
         continue;

      // If the material needs lights then gather them
      // here once and set them on the core render inst.
      if ( matInst->isForwardLit() && !coreRI->lights[0] && rdata.getLightQuery() )
         rdata.getLightQuery()->getLights( coreRI->lights, 8 );

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

      ri->matInst = matInst;
      ri->defaultKey = matInst->getStateHint();
      ri->primBuffIndex = i;

      // Translucent materials need the translucent type.
      if ( matInst->getMaterial()->isTranslucent() )
      {
         ri->type = RenderPassManager::RIT_Translucent;
         ri->translucentSort = true;
      }

      renderPass->addInst( ri );
   }
}