void TSStatic::_updatePhysics() { SAFE_DELETE( mPhysicsRep ); if ( !PHYSICSMGR || mCollisionType == None ) return; PhysicsCollision *colShape = NULL; if ( mCollisionType == Bounds ) { MatrixF offset( true ); offset.setPosition( mShape->center ); colShape = PHYSICSMGR->createCollision(); colShape->addBox( getObjBox().getExtents() * 0.5f * mObjScale, offset ); } else colShape = mShape->buildColShape( mCollisionType == VisibleMesh, getScale() ); if ( colShape ) { PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); mPhysicsRep = PHYSICSMGR->createBody(); mPhysicsRep->init( colShape, 0, 0, this, world ); mPhysicsRep->setTransform( getTransform() ); } }
bool Forest::castRayBase( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) { if ( !getObjBox().collideLine( start, end ) ) return false; if ( mData->castRay( start, end, outInfo, rendered ) ) { outInfo->object = this; return true; } return false; }
void ForestItem::setTransform( const MatrixF &xfm, F32 scale ) { mTransform = xfm; mScale = scale; // Cache the world box to improve culling performance. VectorF objScale( mScale, mScale, mScale ); mWorldBox = getObjBox(); mWorldBox.minExtents.convolve( objScale ); mWorldBox.maxExtents.convolve( objScale ); mTransform.mul( mWorldBox ); // Generate a radius that encompasses the entire box. mRadius = ( mWorldBox.maxExtents - mWorldBox.minExtents ).len() / 2.0f; }
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; }
void Trigger::buildConvex(const Box3F& box, Convex* convex) { // These should really come out of a pool mConvexList->collectGarbage(); Box3F realBox = box; mWorldToObj.mul(realBox); realBox.minExtents.convolveInverse(mObjScale); realBox.maxExtents.convolveInverse(mObjScale); if (realBox.isOverlapped(getObjBox()) == 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; }
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() ); } } }
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() ); } } } }
//----------------------------------------------------------------------------- // Object Rendering //----------------------------------------------------------------------------- void RenderObjectExample::createGeometry() { static const Point3F cubePoints[8] = { Point3F( 1.0f, -1.0f, -1.0f), Point3F( 1.0f, -1.0f, 1.0f), Point3F( 1.0f, 1.0f, -1.0f), Point3F( 1.0f, 1.0f, 1.0f), Point3F(-1.0f, -1.0f, -1.0f), Point3F(-1.0f, 1.0f, -1.0f), Point3F(-1.0f, -1.0f, 1.0f), Point3F(-1.0f, 1.0f, 1.0f) }; static const Point3F cubeNormals[6] = { Point3F( 1.0f, 0.0f, 0.0f), Point3F(-1.0f, 0.0f, 0.0f), Point3F( 0.0f, 1.0f, 0.0f), Point3F( 0.0f, -1.0f, 0.0f), Point3F( 0.0f, 0.0f, 1.0f), Point3F( 0.0f, 0.0f, -1.0f) }; static const ColorI cubeColors[3] = { ColorI( 255, 0, 0, 255 ), ColorI( 0, 255, 0, 255 ), ColorI( 0, 0, 255, 255 ) }; static const U32 cubeFaces[36][3] = { { 3, 0, 0 }, { 0, 0, 0 }, { 1, 0, 0 }, { 2, 0, 0 }, { 0, 0, 0 }, { 3, 0, 0 }, { 7, 1, 0 }, { 4, 1, 0 }, { 5, 1, 0 }, { 6, 1, 0 }, { 4, 1, 0 }, { 7, 1, 0 }, { 3, 2, 1 }, { 5, 2, 1 }, { 2, 2, 1 }, { 7, 2, 1 }, { 5, 2, 1 }, { 3, 2, 1 }, { 1, 3, 1 }, { 4, 3, 1 }, { 6, 3, 1 }, { 0, 3, 1 }, { 4, 3, 1 }, { 1, 3, 1 }, { 3, 4, 2 }, { 6, 4, 2 }, { 7, 4, 2 }, { 1, 4, 2 }, { 6, 4, 2 }, { 3, 4, 2 }, { 2, 5, 2 }, { 4, 5, 2 }, { 0, 5, 2 }, { 5, 5, 2 }, { 4, 5, 2 }, { 2, 5, 2 } }; // Fill the vertex buffer VertexType *pVert = NULL; mVertexBuffer.set( GFX, 36, GFXBufferTypeStatic ); pVert = mVertexBuffer.lock(); Point3F halfSize = getObjBox().getExtents() * 0.5f; for (U32 i = 0; i < 36; i++) { const U32& vdx = cubeFaces[i][0]; const U32& ndx = cubeFaces[i][1]; const U32& cdx = cubeFaces[i][2]; pVert[i].point = cubePoints[vdx] * halfSize; pVert[i].normal = cubeNormals[ndx]; pVert[i].color = cubeColors[cdx]; } mVertexBuffer.unlock(); // Set up our normal and reflection StateBlocks GFXStateBlockDesc desc; // The normal StateBlock only needs a default StateBlock mNormalSB = GFX->createStateBlock( desc ); // The reflection needs its culling reversed desc.cullDefined = true; desc.cullMode = GFXCullCW; mReflectSB = GFX->createStateBlock( desc ); }
//----------------------------------------------------------------------------- // Object Rendering //----------------------------------------------------------------------------- void MetaShapeRenderer::createGeometry() { Point3F scale = this->getScale(); Box3F box = this->getObjBox(); box.minExtents.convolve(scale); box.maxExtents.convolve(scale); MatrixF mat = this->getTransform(); mat.mul(box); mPolyList.clear(); this->getContainer()->buildPolyList( PLC_Collision, box, ShapeBaseObjectType, &mPolyList ); mUpdatePolyList = false; static const Point3F cubePoints[8] = { Point3F( 1.0f, -1.0f, -1.0f), Point3F( 1.0f, -1.0f, 1.0f), Point3F( 1.0f, 1.0f, -1.0f), Point3F( 1.0f, 1.0f, 1.0f), Point3F(-1.0f, -1.0f, -1.0f), Point3F(-1.0f, 1.0f, -1.0f), Point3F(-1.0f, -1.0f, 1.0f), Point3F(-1.0f, 1.0f, 1.0f) }; static const Point3F cubeNormals[6] = { Point3F( 1.0f, 0.0f, 0.0f), Point3F(-1.0f, 0.0f, 0.0f), Point3F( 0.0f, 1.0f, 0.0f), Point3F( 0.0f, -1.0f, 0.0f), Point3F( 0.0f, 0.0f, 1.0f), Point3F( 0.0f, 0.0f, -1.0f) }; static const ColorI cubeColors[3] = { ColorI( 0, 0, 100, 100), ColorI( 0, 0, 100, 100), ColorI( 0, 0, 100, 100) }; static const U32 cubeFaces[36][3] = { { 3, 0, 0 }, { 0, 0, 0 }, { 1, 0, 0 }, { 2, 0, 0 }, { 0, 0, 0 }, { 3, 0, 0 }, { 7, 1, 0 }, { 4, 1, 0 }, { 5, 1, 0 }, { 6, 1, 0 }, { 4, 1, 0 }, { 7, 1, 0 }, { 3, 2, 1 }, { 5, 2, 1 }, { 2, 2, 1 }, { 7, 2, 1 }, { 5, 2, 1 }, { 3, 2, 1 }, { 1, 3, 1 }, { 4, 3, 1 }, { 6, 3, 1 }, { 0, 3, 1 }, { 4, 3, 1 }, { 1, 3, 1 }, { 3, 4, 2 }, { 6, 4, 2 }, { 7, 4, 2 }, { 1, 4, 2 }, { 6, 4, 2 }, { 3, 4, 2 }, { 2, 5, 2 }, { 4, 5, 2 }, { 0, 5, 2 }, { 5, 5, 2 }, { 4, 5, 2 }, { 2, 5, 2 } }; // Fill the vertex buffer VertexType *pVert = NULL; mVertexBuffer.set( GFX, 36, GFXBufferTypeStatic ); pVert = mVertexBuffer.lock(); Point3F halfSize = getObjBox().getExtents() * 0.5f; for (U32 i = 0; i < 36; i++) { const U32& vdx = cubeFaces[i][0]; const U32& ndx = cubeFaces[i][1]; const U32& cdx = cubeFaces[i][2]; pVert[i].point = cubePoints[vdx] * halfSize; pVert[i].normal = cubeNormals[ndx]; pVert[i].color = cubeColors[cdx]; } mVertexBuffer.unlock(); // Set up our normal and reflection StateBlocks GFXStateBlockDesc desc; // The normal StateBlock only needs a default StateBlock mNormalSB = GFX->createStateBlock( desc ); // The reflection needs its culling reversed desc.cullDefined = true; desc.cullMode = GFXCullCW; mReflectSB = GFX->createStateBlock( desc ); }
//----------------------------------------------------------------------------- // Object Rendering //----------------------------------------------------------------------------- void RenderMeshExample::createGeometry() { static const Point3F cubePoints[8] = { Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1), Point3F(-1, -1, -1), Point3F(-1, 1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, 1) }; static const Point3F cubeNormals[6] = { Point3F( 1, 0, 0), Point3F(-1, 0, 0), Point3F( 0, 1, 0), Point3F( 0, -1, 0), Point3F( 0, 0, 1), Point3F( 0, 0, -1) }; static const Point2F cubeTexCoords[4] = { Point2F( 0, 0), Point2F( 0, -1), Point2F( 1, 0), Point2F( 1, -1) }; static const U32 cubeFaces[36][3] = { { 3, 0, 3 }, { 0, 0, 0 }, { 1, 0, 1 }, { 2, 0, 2 }, { 0, 0, 0 }, { 3, 0, 3 }, { 7, 1, 1 }, { 4, 1, 2 }, { 5, 1, 0 }, { 6, 1, 3 }, { 4, 1, 2 }, { 7, 1, 1 }, { 3, 2, 1 }, { 5, 2, 2 }, { 2, 2, 0 }, { 7, 2, 3 }, { 5, 2, 2 }, { 3, 2, 1 }, { 1, 3, 3 }, { 4, 3, 0 }, { 6, 3, 1 }, { 0, 3, 2 }, { 4, 3, 0 }, { 1, 3, 3 }, { 3, 4, 3 }, { 6, 4, 0 }, { 7, 4, 1 }, { 1, 4, 2 }, { 6, 4, 0 }, { 3, 4, 3 }, { 2, 5, 1 }, { 4, 5, 2 }, { 0, 5, 0 }, { 5, 5, 3 }, { 4, 5, 2 }, { 2, 5, 1 } }; // Fill the vertex buffer VertexType *pVert = NULL; mVertexBuffer.set( GFX, 36, GFXBufferTypeStatic ); pVert = mVertexBuffer.lock(); Point3F halfSize = getObjBox().getExtents() * 0.5f; for (U32 i = 0; i < 36; i++) { const U32& vdx = cubeFaces[i][0]; const U32& ndx = cubeFaces[i][1]; const U32& tdx = cubeFaces[i][2]; pVert[i].point = cubePoints[vdx] * halfSize; pVert[i].normal = cubeNormals[ndx]; pVert[i].texCoord = cubeTexCoords[tdx]; } mVertexBuffer.unlock(); // Fill the primitive buffer U16 *pIdx = NULL; mPrimitiveBuffer.set( GFX, 36, 12, GFXBufferTypeStatic ); mPrimitiveBuffer.lock(&pIdx); for (U16 i = 0; i < 36; i++) pIdx[i] = i; mPrimitiveBuffer.unlock(); }