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); }
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); } } }
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(); }
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; } } } } } }
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 }
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; }
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 ); } }
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 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); } }
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 ); } } }
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; }
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; } }
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; }
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; }
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; }
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; } } } }
//----------------------------------------------------------------------------- // // 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; }
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; }
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); }
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(); }
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 ); }
void GFXDrawUtil::drawCube( const GFXStateBlockDesc &desc, const Box3F &box, const ColorI &color, const MatrixF *xfm ) { drawCube( desc, box.getExtents(), box.getCenter(), color, xfm ); }
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() ); } } } }
//----------------------------------------------------------------------------- // // 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; }
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); } } }
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 ); } }