// TODO: I'm sure there is a more efficient approach. static void TransformNormalToShapeSpace(const PxMeshScale& meshScale, const PxVec3& nIn, PxVec3& nOut) { // Uniform scale makes this unnecessary if (meshScale.scale.x == meshScale.scale.y && meshScale.scale.x == meshScale.scale.z) { nOut = nIn; return; } PxMat33 R(meshScale.rotation); PxMat33 vertex2ShapeSkew = R.getTranspose(); PxMat33 diagonal = PxMat33::createDiagonal(meshScale.scale); vertex2ShapeSkew = vertex2ShapeSkew * diagonal; vertex2ShapeSkew = vertex2ShapeSkew * R; PxMat33 shape2VertexSkew = vertex2ShapeSkew.getInverse(); const PxVec3 tmp = shape2VertexSkew.transformTranspose(nIn); const PxReal Denom = 1.0f / tmp.magnitude(); nOut = tmp * Denom; }
void collideWithSphereNonContinuous(PxsParticleCollData& collData, const PxVec3& pos, const PxReal& radius, const PxReal& proxRadius) { if(collData.localFlags & PXS_FLUID_COLL_FLAG_CC) return; // Only apply discrete and proximity collisions if no continuous collisions was detected so far (for any colliding shape) PxReal dist = pos.magnitude(); collData.localSurfaceNormal = pos; if(dist < (radius + proxRadius)) { if (dist != 0.0f) collData.localSurfaceNormal *= (1.0f / dist); else collData.localSurfaceNormal = PxVec3(0); // Push particle to surface such that the distance to the surface is equal to the collision radius collData.localSurfacePos = collData.localSurfaceNormal * (radius + collData.restOffset); collData.localFlags |= PXS_FLUID_COLL_FLAG_L_PROX; if(dist < (radius + collData.restOffset)) collData.localFlags |= PXS_FLUID_COLL_FLAG_L_DC; } }
// PT: ray-capsule intersection code, originally from the old Magic Software library. PxU32 Gu::intersectRayCapsuleInternal(const PxVec3& origin, const PxVec3& dir, const PxVec3& p0, const PxVec3& p1, float radius, PxReal s[2]) { // set up quadratic Q(t) = a*t^2 + 2*b*t + c PxVec3 kW = p1 - p0; const float fWLength = kW.magnitude(); if(fWLength!=0.0f) kW /= fWLength; // PT: if the capsule is in fact a sphere, switch back to dedicated sphere code. // This is not just an optimization, the rest of the code fails otherwise. if(fWLength<=1e-6f) { const float d0 = (origin - p0).magnitudeSquared(); const float d1 = (origin - p1).magnitudeSquared(); const float approxLength = (PxMax(d0, d1) + radius)*2.0f; return PxU32(Gu::intersectRaySphere(origin, dir, approxLength, p0, radius, s[0])); } // generate orthonormal basis PxVec3 kU(0.0f); if (fWLength > 0.0f) { PxReal fInvLength; if ( PxAbs(kW.x) >= PxAbs(kW.y) ) { // W.x or W.z is the largest magnitude component, swap them fInvLength = PxRecipSqrt(kW.x*kW.x + kW.z*kW.z); kU.x = -kW.z*fInvLength; kU.y = 0.0f; kU.z = kW.x*fInvLength; } else { // W.y or W.z is the largest magnitude component, swap them fInvLength = PxRecipSqrt(kW.y*kW.y + kW.z*kW.z); kU.x = 0.0f; kU.y = kW.z*fInvLength; kU.z = -kW.y*fInvLength; } } PxVec3 kV = kW.cross(kU); kV.normalize(); // PT: fixed november, 24, 2004. This is a bug in Magic. // compute intersection PxVec3 kD(kU.dot(dir), kV.dot(dir), kW.dot(dir)); const float fDLength = kD.magnitude(); const float fInvDLength = fDLength!=0.0f ? 1.0f/fDLength : 0.0f; kD *= fInvDLength; const PxVec3 kDiff = origin - p0; const PxVec3 kP(kU.dot(kDiff), kV.dot(kDiff), kW.dot(kDiff)); const PxReal fRadiusSqr = radius*radius; // Is the velocity parallel to the capsule direction? (or zero) if ( PxAbs(kD.z) >= 1.0f - PX_EPS_REAL || fDLength < PX_EPS_REAL ) { const float fAxisDir = dir.dot(kW); const PxReal fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y; if ( fAxisDir < 0 && fDiscr >= 0.0f ) { // Velocity anti-parallel to the capsule direction const PxReal fRoot = PxSqrt(fDiscr); s[0] = (kP.z + fRoot)*fInvDLength; s[1] = -(fWLength - kP.z + fRoot)*fInvDLength; return 2; } else if ( fAxisDir > 0 && fDiscr >= 0.0f ) { // Velocity parallel to the capsule direction const PxReal fRoot = PxSqrt(fDiscr); s[0] = -(kP.z + fRoot)*fInvDLength; s[1] = (fWLength - kP.z + fRoot)*fInvDLength; return 2; } else { // sphere heading wrong direction, or no velocity at all return 0; } } // test intersection with infinite cylinder PxReal fA = kD.x*kD.x + kD.y*kD.y; PxReal fB = kP.x*kD.x + kP.y*kD.y; PxReal fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr; PxReal fDiscr = fB*fB - fA*fC; if ( fDiscr < 0.0f ) { // line does not intersect infinite cylinder return 0; } PxU32 iQuantity = 0; if ( fDiscr > 0.0f ) { // line intersects infinite cylinder in two places const PxReal fRoot = PxSqrt(fDiscr); const PxReal fInv = 1.0f/fA; PxReal fT = (-fB - fRoot)*fInv; PxReal fTmp = kP.z + fT*kD.z; const float epsilon = 1e-3f; // PT: see TA35174 if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon ) s[iQuantity++] = fT*fInvDLength; fT = (-fB + fRoot)*fInv; fTmp = kP.z + fT*kD.z; if ( fTmp >= -epsilon && fTmp <= fWLength+epsilon ) s[iQuantity++] = fT*fInvDLength; if ( iQuantity == 2 ) { // line intersects capsule wall in two places return 2; } } else { // line is tangent to infinite cylinder const PxReal fT = -fB/fA; const PxReal fTmp = kP.z + fT*kD.z; if ( 0.0f <= fTmp && fTmp <= fWLength ) { s[0] = fT*fInvDLength; return 1; } } // test intersection with bottom hemisphere // fA = 1 fB += kP.z*kD.z; fC += kP.z*kP.z; fDiscr = fB*fB - fC; if ( fDiscr > 0.0f ) { const PxReal fRoot = PxSqrt(fDiscr); PxReal fT = -fB - fRoot; PxReal fTmp = kP.z + fT*kD.z; if ( fTmp <= 0.0f ) { s[iQuantity++] = fT*fInvDLength; if ( iQuantity == 2 ) return 2; } fT = -fB + fRoot; fTmp = kP.z + fT*kD.z; if ( fTmp <= 0.0f ) { s[iQuantity++] = fT*fInvDLength; if ( iQuantity == 2 ) return 2; } } else if ( fDiscr == 0.0f ) { const PxReal fT = -fB; const PxReal fTmp = kP.z + fT*kD.z; if ( fTmp <= 0.0f ) { s[iQuantity++] = fT*fInvDLength; if ( iQuantity == 2 ) return 2; } } // test intersection with top hemisphere // fA = 1 fB -= kD.z*fWLength; fC += fWLength*(fWLength - 2.0f*kP.z); fDiscr = fB*fB - fC; if ( fDiscr > 0.0f ) { const PxReal fRoot = PxSqrt(fDiscr); PxReal fT = -fB - fRoot; PxReal fTmp = kP.z + fT*kD.z; if ( fTmp >= fWLength ) { s[iQuantity++] = fT*fInvDLength; if ( iQuantity == 2 ) return 2; } fT = -fB + fRoot; fTmp = kP.z + fT*kD.z; if ( fTmp >= fWLength ) { s[iQuantity++] = fT*fInvDLength; if ( iQuantity == 2 ) return 2; } } else if ( fDiscr == 0.0f ) { const PxReal fT = -fB; const PxReal fTmp = kP.z + fT*kD.z; if ( fTmp >= fWLength ) { s[iQuantity++] = fT*fInvDLength; if ( iQuantity == 2 ) return 2; } } return iQuantity; }
bool Character::move(PxReal speed, bool jump, bool active ) { if (mCurrentMotion == NULL) return false; if (mBlendCounter > 0) mBlendCounter--; if (mTargetMotion && (mBlendCounter == 0)) { mBlendCounter = 0; mCurrentMotion = mTargetMotion; mFrameTime = 0.0f; mTargetMotion = NULL; } PxU32 nbFrames = mCurrentMotion->mNbFrames; PxReal distance = mCurrentMotion->mDistance; PxReal frameDelta = 0.0f; const PxReal angleLimitPerFrame = 3.0f; if (jump) { frameDelta = 1.0f; } else if (active && (mBlendCounter == 0)) { // compute target orientation PxVec3 dir = mTargetPosition - mCharacterPose.p; dir.y = 0.0f; PxReal curDistance = dir.magnitude(); if (curDistance > 0.01f) faceToward(dir, angleLimitPerFrame); frameDelta = speed * PxReal(nbFrames) * (curDistance / distance); } else { frameDelta = 0; } mCharacterPose.p = mTargetPosition; mFrameTime += frameDelta; PxU32 frameNo = PxU32(mFrameTime); if (frameNo >= nbFrames) { if (jump) mFrameTime = PxReal(nbFrames) - 1.0f; else mFrameTime = 0.0f; } // compute pose of all the bones at current frame (results are used by both getFramePose and Skin) computeFramePose(); return true; }
void PxClothGeodesicTetherCookerImpl::createTetherData(const PxClothMeshDesc &desc) { mNumParticles = desc.points.count; if (!desc.invMasses.data) return; // assemble points mVertices.resize(mNumParticles); mAttached.resize(mNumParticles); PxStrideIterator<const PxVec3> pIt((const PxVec3*)desc.points.data, desc.points.stride); PxStrideIterator<const PxReal> wIt((const PxReal*)desc.invMasses.data, desc.invMasses.stride); for(PxU32 i=0; i<mNumParticles; ++i) { mVertices[i] = *pIt++; mAttached[i] = PxU8(wIt.ptr() ? (*wIt++ == 0.0f) : 0); } // build triangle indices if(desc.flags & PxMeshFlag::e16_BIT_INDICES) gatherIndices<PxU16>(mIndices, desc.triangles, desc.quads); else gatherIndices<PxU32>(mIndices, desc.triangles, desc.quads); // build vertex-triangle adjacencies findVertTriNeighbors(); // build triangle-triangle adjacencies mCookerStatus = findTriNeighbors(); if (mCookerStatus != 0) return; // build adjacent vertex list shdfnd::Array<PxU32> valency(mNumParticles+1, 0); shdfnd::Array<PxU32> adjacencies; if(desc.flags & PxMeshFlag::e16_BIT_INDICES) gatherAdjacencies<PxU16>(valency, adjacencies, desc.triangles, desc.quads); else gatherAdjacencies<PxU32>(valency, adjacencies, desc.triangles, desc.quads); // build unique neighbors from adjacencies shdfnd::Array<PxU32> mark(valency.size(), 0); shdfnd::Array<PxU32> neighbors; neighbors.reserve(adjacencies.size()); for(PxU32 i=1, j=0; i<valency.size(); ++i) { for(; j<valency[i]; ++j) { PxU32 k = adjacencies[j]; if(mark[k] != i) { mark[k] = i; neighbors.pushBack(k); } } valency[i] = neighbors.size(); } // create islands of attachment points shdfnd::Array<PxU32> vertexIsland(mNumParticles); shdfnd::Array<VertexDistanceCount> vertexIslandHeap; // put all the attachments in heap for (PxU32 i = 0; i < mNumParticles; ++i) { // we put each attached point with large distance so that // we can prioritize things that are added during mesh traversal. vertexIsland[i] = PxU32(-1); if (mAttached[i]) vertexIslandHeap.pushBack(VertexDistanceCount((int)i, FLT_MAX, 0)); } PxU32 attachedCnt = vertexIslandHeap.size(); // no attached vertices if (vertexIslandHeap.empty()) return; // identify islands of attached vertices shdfnd::Array<PxU32> islandIndices; shdfnd::Array<PxU32> islandFirst; PxU32 islandCnt = 0; PxU32 islandIndexCnt = 0; islandIndices.reserve(attachedCnt); islandFirst.reserve(attachedCnt+1); // while the island heap is not empty while (!vertexIslandHeap.empty()) { // pop vi from heap VertexDistanceCount vi = popHeap(vertexIslandHeap); // new cluster if (vertexIsland[(PxU32)vi.vertNr] == PxU32(-1)) { islandFirst.pushBack(islandIndexCnt++); vertexIsland[(PxU32)vi.vertNr] = islandCnt++; vi.distance = 0; islandIndices.pushBack((PxU32)vi.vertNr); } // for each adjacent vj that's not visited const PxU32 begin = (PxU32)valency[(PxU32)vi.vertNr]; const PxU32 end = (PxU32)valency[PxU32(vi.vertNr + 1)]; for (PxU32 j = begin; j < end; ++j) { const PxU32 vj = neighbors[j]; // do not expand unattached vertices if (!mAttached[vj]) continue; // already visited if (vertexIsland[vj] != PxU32(-1)) continue; islandIndices.pushBack(vj); islandIndexCnt++; vertexIsland[vj] = vertexIsland[PxU32(vi.vertNr)]; pushHeap(vertexIslandHeap, VertexDistanceCount((int)vj, vi.distance + 1.0f, 0)); } } islandFirst.pushBack(islandIndexCnt); PX_ASSERT(islandCnt == (islandFirst.size() - 1)); ///////////////////////////////////////////////////////// PxU32 bufferSize = mNumParticles * islandCnt; PX_ASSERT(bufferSize > 0); shdfnd::Array<float> vertexDistanceBuffer(bufferSize, PX_MAX_F32); shdfnd::Array<PxU32> vertexParentBuffer(bufferSize, 0); shdfnd::Array<VertexDistanceCount> vertexHeap; // now process each island for (PxU32 i = 0; i < islandCnt; i++) { vertexHeap.clear(); float* vertexDistance = &vertexDistanceBuffer[0] + (i * mNumParticles); PxU32* vertexParent = &vertexParentBuffer[0] + (i * mNumParticles); // initialize parent and distance for (PxU32 j = 0; j < mNumParticles; ++j) { vertexParent[j] = j; vertexDistance[j] = PX_MAX_F32; } // put all the attached vertices in this island to heap const PxU32 beginIsland = islandFirst[i]; const PxU32 endIsland = islandFirst[i+1]; for (PxU32 j = beginIsland; j < endIsland; j++) { PxU32 vj = islandIndices[j]; vertexDistance[vj] = 0.0f; vertexHeap.pushBack(VertexDistanceCount((int)vj, 0.0f, 0)); } // no attached vertices in this island (error?) PX_ASSERT(vertexHeap.empty() == false); if (vertexHeap.empty()) continue; // while heap is not empty while (!vertexHeap.empty()) { // pop vi from heap VertexDistanceCount vi = popHeap(vertexHeap); // obsolete entry ( we already found better distance) if (vi.distance > vertexDistance[vi.vertNr]) continue; // for each adjacent vj that's not visited const PxI32 begin = (PxI32)valency[(PxU32)vi.vertNr]; const PxI32 end = (PxI32)valency[PxU32(vi.vertNr + 1)]; for (PxI32 j = begin; j < end; ++j) { const PxI32 vj = (PxI32)neighbors[(PxU32)j]; PxVec3 edge = mVertices[(PxU32)vj] - mVertices[(PxU32)vi.vertNr]; const PxF32 edgeLength = edge.magnitude(); float newDistance = vi.distance + edgeLength; if (newDistance < vertexDistance[vj]) { vertexDistance[vj] = newDistance; vertexParent[vj] = vertexParent[vi.vertNr]; pushHeap(vertexHeap, VertexDistanceCount(vj, newDistance, 0)); } } } } const PxU32 maxTethersPerParticle = 4; // max tethers const PxU32 nbTethersPerParticle = (islandCnt > maxTethersPerParticle) ? maxTethersPerParticle : islandCnt; PxU32 nbTethers = nbTethersPerParticle * mNumParticles; mTetherAnchors.resize(nbTethers); mTetherLengths.resize(nbTethers); // now process the parent and distance and add to fibers for (PxU32 i = 0; i < mNumParticles; i++) { // we use the heap to sort out N-closest island vertexHeap.clear(); for (PxU32 j = 0; j < islandCnt; j++) { int parent = (int)vertexParentBuffer[j * mNumParticles + i]; float edgeDistance = vertexDistanceBuffer[j * mNumParticles + i]; pushHeap(vertexHeap, VertexDistanceCount(parent, edgeDistance, 0)); } // take out N-closest island from the heap for (PxU32 j = 0; j < nbTethersPerParticle; j++) { VertexDistanceCount vi = popHeap(vertexHeap); PxU32 parent = (PxU32)vi.vertNr; float distance = 0.0f; if (parent != i) { float euclideanDistance = (mVertices[i] - mVertices[parent]).magnitude(); float dijkstraDistance = vi.distance; int errorCode = 0; float geodesicDistance = computeGeodesicDistance(i,parent, errorCode); if (errorCode < 0) geodesicDistance = dijkstraDistance; distance = PxMax(euclideanDistance, geodesicDistance); } PxU32 tetherLoc = j * mNumParticles + i; mTetherAnchors[ tetherLoc ] = parent; mTetherLengths[ tetherLoc ] = distance; } } }
bool doRaycastCCD(PxShape* shape, PxTransform& newPose, PxVec3& newShapeCenter, const PxVec3& ccdWitness, const PxVec3& ccdWitnessOffset) { PxRigidDynamic* dyna = canDoCCD(shape); if(!dyna) return true; bool updateCCDWitness = true; const PxVec3 offset = newPose.p - newShapeCenter; //printf("CCD0: %f | %f | %f\n", newShapeCenter.x, newShapeCenter.y, newShapeCenter.z); const PxVec3& origin = ccdWitness; // const PxVec3& dest = newPose.p; const PxVec3& dest = newShapeCenter; PxVec3 dir = dest - origin; const PxReal length = dir.magnitude(); if(length!=0.0f) { dir /= length; // Compute internal radius // PxVec3 localCenter; const PxReal internalRadius = computeInternalRadius(shape, dir, /*localCenter,*/ ccdWitnessOffset); // Compute distance to impact PxRaycastHit hit; // if(internalRadius!=0.0f && CCDRaycast(shape->getActor().getActiveScene(), origin + localCenter, dir, length, hit)) if(internalRadius!=0.0f && CCDRaycast(shape->getActor().getScene(), origin, dir, length, hit)) { #ifdef RAYCAST_CCD_PRINT_DEBUG static int count=0; printf("CCD hit %d\n", count++); #endif updateCCDWitness = false; const PxReal radiusLimit = internalRadius * 0.75f; if(hit.distance>radiusLimit) { // newPose.p = origin + dir * (hit.distance - radiusLimit); newShapeCenter = origin + dir * (hit.distance - radiusLimit); #ifdef RAYCAST_CCD_PRINT_DEBUG printf(" Path0: %f | %f\n", hit.distance, radiusLimit); #endif } else { // newPose.p = origin; newShapeCenter = origin; // newShapeCenter = origin + hit.normal * (radiusLimit - hit.distance); #ifdef RAYCAST_CCD_PRINT_DEBUG printf(" Path1: %f\n", hit.distance); #endif } { newPose.p = offset + newShapeCenter; //newPose.p.y += 10.0f; //printf("%f | %f | %f\n", newPose.p.x, newPose.p.y, newPose.p.z); // dyna->setGlobalPose(newPose); // newPose = actorGlobalPose * shapeLocalPose // newPose * inverse(shapeLocalPose) = actorGlobalPose const PxTransform shapeLocalPose = shape->getLocalPose(); const PxTransform inverseShapeLocalPose = shapeLocalPose.getInverse(); PxTransform newGlobalPose = newPose * inverseShapeLocalPose; dyna->setGlobalPose(newGlobalPose); //dyna->setGlobalPose(newPose); //printf("%f | %f | %f\n", newGlobalPose.p.x, newGlobalPose.p.y, newGlobalPose.p.z); //printf("%f | %f | %f\n", shapeLocalPose.p.x, shapeLocalPose.p.y, shapeLocalPose.p.z); /*PX_INLINE PxTransform PxShapeExt::getGlobalPose(const PxShape& shape) { PxRigidActor& ra = shape.getActor(); return ra.getGlobalPose() * shape.getLocalPose(); }*/ const PxVec3 testShapeCenter = getShapeCenter(shape, ccdWitnessOffset); float d = (testShapeCenter - newShapeCenter).magnitude(); //printf("%f\n", d); //printf("CCD1: %f | %f | %f\n", testShapeCenter.x, testShapeCenter.y, testShapeCenter.z); //dyna->clearForce(PxForceMode::eFORCE); //dyna->clearForce(PxForceMode::eIMPULSE); //dyna->setLinearVelocity(PxVec3(0)); // PT: this helps the CCT but stops small objects dead, which doesn't look great } } } return updateCCDWitness; }
/** @brief @date 2014-01-05 */ void RendererCompositionShape::FindMostCloseFace( void *positions0, PxU32 positionStride0, void *normals0, PxU32 normalStride0, PxU16 *indices0, PxU32 idx0Size, void *positions1, PxU32 positionStride1, void *normals1, PxU32 normalStride1, PxU16 *indices1, PxU32 idx1Size, OUT std::pair<int,int> &closeFace0, OUT std::pair<int,int> &closeFace1, OUT set<PxU16> &vtx0, OUT set<PxU16> &vtx1 ) { int foundCount = 0; set<int> checkV0, checkV1; while (foundCount < 2) { float minLen = 100000.f; int minFaceIdx0 = -1; int minFaceIdx1 = -1; // find most close face for (PxU8 i=0; i<idx0Size; i+=3) { if (checkV0.find(i) != checkV0.end()) continue; // already exist face index PxVec3 center0; PxVec3 center0Normal; { const PxU16 vidx0 = indices0[ i]; const PxU16 vidx1 = indices0[ i+1]; const PxU16 vidx2 = indices0[ i+2]; PxVec3 &p0 = *(PxVec3*)(((PxU8*)positions0) + (positionStride0 * vidx0)); PxVec3 &p1 = *(PxVec3*)(((PxU8*)positions0) + (positionStride0 * vidx1)); PxVec3 &p2 = *(PxVec3*)(((PxU8*)positions0) + (positionStride0 * vidx2)); PxVec3 &n0 = *(PxVec3*)(((PxU8*)normals0) + (normalStride0 * vidx0)); PxVec3 &n1 = *(PxVec3*)(((PxU8*)normals0) + (normalStride0 * vidx1)); PxVec3 &n2 = *(PxVec3*)(((PxU8*)normals0) + (normalStride0 * vidx2)); center0 = p0 + p1 + p2; center0 /= 3.f; center0Normal = n0; } for (PxU8 k=0; k<idx1Size; k+=3) { if (checkV1.find(k) != checkV1.end()) continue; // already exist face index PxVec3 center1; PxVec3 center1Normal; { const PxU16 vidx0 = indices1[ k]; const PxU16 vidx1 = indices1[ k+1]; const PxU16 vidx2 = indices1[ k+2]; PxVec3 &p0 = *(PxVec3*)(((PxU8*)positions1) + (positionStride1 * vidx0)) + PxVec3(1,0,0); PxVec3 &p1 = *(PxVec3*)(((PxU8*)positions1) + (positionStride1 * vidx1)) + PxVec3(1,0,0); PxVec3 &p2 = *(PxVec3*)(((PxU8*)positions1) + (positionStride1 * vidx2)) + PxVec3(1,0,0); PxVec3 &n0 = *(PxVec3*)(((PxU8*)normals1) + (normalStride1 * vidx0)); PxVec3 &n1 = *(PxVec3*)(((PxU8*)normals1) + (normalStride1 * vidx1)); PxVec3 &n2 = *(PxVec3*)(((PxU8*)normals1) + (normalStride1 * vidx2)); center1 = p0 + p1 + p2; center1 /= 3.f; center1Normal = n0; } PxVec3 len = center0 - center1; if ((minLen > len.magnitude()) && (center0Normal.dot(center1Normal) < 0) ) { minFaceIdx0 = i; minFaceIdx1 = k; minLen = len.magnitude(); } } } checkV0.insert(minFaceIdx0); checkV1.insert(minFaceIdx1); if (foundCount == 0) { closeFace0 = std::pair<int,int>(minFaceIdx0, minFaceIdx1); } else { closeFace1 = std::pair<int,int>(minFaceIdx0, minFaceIdx1); } ++foundCount; } vtx0.insert( indices0[ closeFace0.first] ); vtx0.insert( indices0[ closeFace0.first+1] ); vtx0.insert( indices0[ closeFace0.first+2] ); vtx0.insert( indices0[ closeFace1.first] ); vtx0.insert( indices0[ closeFace1.first+1] ); vtx0.insert( indices0[ closeFace1.first+2] ); vtx1.insert( indices1[ closeFace0.second] ); vtx1.insert( indices1[ closeFace0.second+1] ); vtx1.insert( indices1[ closeFace0.second+2] ); vtx1.insert( indices1[ closeFace1.second] ); vtx1.insert( indices1[ closeFace1.second+1] ); vtx1.insert( indices1[ closeFace1.second+2] ); }
/** @brief Mouse Move @date 2014-03-04 */ void COrientationEditController::MouseMove(physx::PxU32 x, physx::PxU32 y) { switch (m_editMode) { case MODE_NONE: break; case MODE_POSITION: { RET(!m_selectNode); using namespace genotype_parser; //const PxTransform tm = GetJointTransformAccumulate(m_rootNode, m_selectNode); const SConnection *joint = m_rootNode->GetJoint(m_selectNode); BRK(!joint); const PxVec3 initialPos = utility::Vec3toPxVec3(joint->pos); const PxVec3 dimension = utility::Vec3toPxVec3(m_selectNode->m_expr->dimension); const float len = initialPos.magnitude() + dimension.magnitude(); PxVec3 orig, dir, pickOrig; m_sample.GetPicking()->computeCameraRay(orig, dir, pickOrig, x, y); PxVec3 pos = pickOrig + (dir*3); PxVec3 v = pos - m_rootNode->GetPos(); if (v.magnitude() > len) { v.normalize(); pos = m_rootNode->GetPos() + (v*len); } m_selectNode->SetWorldTransform(PxTransform(pos)); //m_selectNode->m_renderNode->setTransform(PxTransform(pos)); } break; case MODE_PICKUP: PickupNodes(m_sample, m_nodes, x, y, true); break; case MODE_ROTATION: { PxVec3 v0; const Int2 pos(x,y); if (pos == m_ptOrig) break; //const Int2 diff = pos - m_ptOrig; { PxVec3 orig0, dir0, pickOrig0; m_sample.GetPicking()->computeCameraRay(orig0, dir0, pickOrig0, x, y); PxVec3 orig1, dir1, pickOrig1; m_sample.GetPicking()->computeCameraRay(orig1, dir1, pickOrig1, m_ptOrig.x, m_ptOrig.y); PxVec3 v1 = pickOrig0 - pickOrig1; v1.normalize(); v0 = dir0.cross(v1); v0.normalize(); } m_ptOrig = pos; PxTransform tm = m_selectNode->GetLocalTransform(); PxTransform m = PxTransform(PxQuat(-0.03f, v0)) * tm; m_selectNode->SetLocalTransform(m); m_selectNode->UpdateTransform(); //PxQuat q0(diff.x*0.05f, PxVec3(0,1,0)); //PxQuat q1(-diff.y*0.05f, PxVec3(1,0,0)); //PxTransform m = tm * PxTransform(q1) * PxTransform(q0); //m_selectNode->m_renderNode->setTransform(m); } break; } if (m_cursor) m_cursor->MouseMove(x,y); }
PX_FORCE_INLINE PxU32 collideWithMeshTriangle(PxVec3& surfaceNormal, PxVec3& surfacePos, PxVec3& proxSurfaceNormal, PxVec3& proxSurfacePos, PxReal& ccTime, PxReal& distOldToSurface, const PxVec3& oldPos, const PxVec3& newPos, const PxVec3& origin, const PxVec3& e0, const PxVec3& e1, bool hasCC, const PxReal& collRadius, const PxReal& proxRadius) { PxU32 flags = 0; PxReal collisionRadius2 = collRadius * collRadius; PxReal proximityRadius2 = proxRadius * proxRadius; PxVec3 motion = newPos - oldPos; // dc and proximity tests PxVec3 tmpV = origin - newPos; PxReal a = e0.dot(e0); PxReal b = e0.dot(e1); PxReal c = e1.dot(e1); PxReal d = e0.dot(tmpV); PxReal e = e1.dot(tmpV); PxVec3 coords; coords.x = b*e - c*d; // s * det coords.y = b*d - a*e; // t * det coords.z = a*c - b*b; // det bool insideCase = false; PxVec3 clampedCoords(PxVec3(0)); if (coords.x <= 0.0f) { c = PxMax(c, FLT_MIN); clampedCoords.y = -e/c; } else if (coords.y <= 0.0f) { a = PxMax(a, FLT_MIN); clampedCoords.x = -d/a; } else if (coords.x + coords.y > coords.z) { PxReal denominator = a + c - b - b; PxReal numerator = c + e - b - d; denominator = PxMax(denominator, FLT_MIN); clampedCoords.x = numerator / denominator; clampedCoords.y = 1.0f - clampedCoords.x; } else // all inside { PxReal tmpF = PxMax(coords.z, FLT_MIN); tmpF = 1.0f / tmpF; clampedCoords.x = coords.x * tmpF; clampedCoords.y = coords.y * tmpF; insideCase = true; } clampedCoords.x = PxMax(clampedCoords.x, 0.0f); clampedCoords.y = PxMax(clampedCoords.y, 0.0f); clampedCoords.x = PxMin(clampedCoords.x, 1.0f); clampedCoords.y = PxMin(clampedCoords.y, 1.0f); // Closest point to particle inside triangle PxVec3 closest = origin + e0 * clampedCoords.x + e1 * clampedCoords.y; PxVec3 triangleOffset = newPos - closest; PxReal triangleDistance2 = triangleOffset.magnitudeSquared(); PxVec3 triangleNormal = e0.cross(e1); PxReal e0e1Span = triangleNormal.magnitude(); bool isInFront = triangleOffset.dot(triangleNormal) > 0.0f; // MS: Possible optimzation /* if (isInFront && (triangleDistance2 >= proximityRadius2)) return flags; */ bool isInProximity = insideCase && (triangleDistance2 < proximityRadius2) && isInFront; bool isInDiscrete = (triangleDistance2 < collisionRadius2) && isInFront; if (!hasCC) { // Only apply discrete and proximity collisions if no continuous collisions was detected so far (for any colliding shape) if (isInDiscrete) { if (triangleDistance2 > PXS_FLUID_COLL_TRI_DISTANCE) { surfaceNormal = triangleOffset * PxRecipSqrt(triangleDistance2); } else { surfaceNormal = triangleNormal * (1.0f / e0e1Span); } surfacePos = closest + (surfaceNormal * collRadius); flags |= PXS_FLUID_COLL_FLAG_L_DC; } if (isInProximity) { proxSurfaceNormal = triangleNormal * (1.0f / e0e1Span); proxSurfacePos = closest + (proxSurfaceNormal * collRadius); flags |= PXS_FLUID_COLL_FLAG_L_PROX; tmpV = (oldPos - origin); //this time it's not the newPosition offset. distOldToSurface = proxSurfaceNormal.dot(tmpV); // Need to return the distance to decide which constraints should be thrown away } } if (!isInDiscrete && !isInProximity) { // cc test (let's try only executing this if no discrete coll, or proximity happend). tmpV = origin - oldPos; //this time it's not the newPosition offset. PxReal pDistN = triangleNormal.dot(tmpV); PxReal rLengthN = triangleNormal.dot(motion); if (pDistN > 0.0f || rLengthN >= pDistN) return flags; //we are in the half closed interval [0.0f, 1.0) PxReal t = pDistN / rLengthN; PX_ASSERT((t >= 0.0f) && (t < 1.0f)); PxVec3 relativePOSITION = (motion * t); PxVec3 testPoint = oldPos + relativePOSITION; // a,b,c and coords.z don't depend on test point -> still valid tmpV = origin - testPoint; d = e0.dot(tmpV); e = e1.dot(tmpV); coords.x = b*e - c*d; coords.y = b*d - a*e; //maybe we don't need this for rare case leaking on triangle boundaries? PxReal eps = coords.z * PXS_FLUID_COLL_RAY_EPSILON_FACTOR; if ((coords.x >= -eps) && (coords.y >= -eps) && (coords.x + coords.y <= coords.z + eps)) { PxReal invLengthN = (1.0f / e0e1Span); distOldToSurface = -pDistN * invLengthN; // Need to return the distance to decide which constraints should be thrown away surfaceNormal = triangleNormal * invLengthN; //surfacePos = testPoint + (surfaceNormal * collRadius); computeContinuousTargetPosition(surfacePos, oldPos, relativePOSITION, surfaceNormal, collRadius); ccTime = t; flags |= PXS_FLUID_COLL_FLAG_L_CC; } } return flags; }
/** @brief MouseRButtonUp @date 2014-03-04 */ void COrientationEditController::MouseRButtonUp(physx::PxU32 x, physx::PxU32 y) { switch (m_editMode) { case MODE_POSITION: { RET(!m_selectNode); using namespace genotype_parser; SConnection *joint = m_rootNode->GetJoint(m_selectNode); RET(!joint); PxVec3 dir = m_selectNode->GetPos() - m_rootNode->GetPos(); const float len = dir.magnitude(); dir.normalize(); PxQuat rotateToXAxis; if (boost::iequals(joint->type, "revolute")) { PxVec3 jointAxis = PxVec3(0,0,1).cross(dir); jointAxis.normalize(); utility::quatRotationArc(rotateToXAxis, PxVec3(1,0,0), jointAxis); } else { rotateToXAxis = m_rootNode->GetWorldTransform().q; } { // setting parent orientation PxReal angle; PxVec3 axis; rotateToXAxis.toRadiansAndUnitAxis(angle, axis); joint->parentOrient.angle = angle; joint->parentOrient.dir = utility::PxVec3toVec3(axis); printf( "parent angle = %f, dir= %f %f %f\n", angle, axis.x, axis.y, axis.z); } { // setting select orientation PxVec3 pos; PxTransform rotTm; const PxTransform tm = m_selectNode->GetWorldTransform(); if (boost::iequals(joint->type, "revolute")) { rotTm = PxTransform(rotateToXAxis) * PxTransform(tm.q); pos = rotTm.rotate(dir*len); } else { rotTm = PxTransform(tm.q); pos = tm.p; } PxReal angle; PxVec3 axis; rotTm.q.toRadiansAndUnitAxis(angle, axis); joint->orient.angle = angle; joint->orient.dir = utility::PxVec3toVec3(axis); joint->pos = utility::PxVec3toVec3(pos); printf( "connect angle = %f, dir= %f %f %f\n", angle, axis.x, axis.y, axis.z); } SelectNode(NULL); } break; } if (m_cursor) m_cursor->MouseRButtonUp(x,y); }
void DeformableMesh::calculateInvMasses(Ps::Array<PxReal>& invMasses, PxReal mass) const { PX_ASSERT(mVertexMasses.empty()); PX_ASSERT(invMasses.size() == mVertexPositions.size()); // clear output array for (PxU32 i = 0; i < invMasses.size(); i++) invMasses[i] = 0.0f; switch (mPrimitiveType) { case PxDeformablePrimitiveType::eTRIANGLE: { // first, we compute area associated with each vertex and temporarily store it in invMasses array PxReal totalArea = 0.0f; const PxU32* i0 = mPrimitives.begin(); for (PxU32 i = 0; i < mPrimitives.size() / 3; ++i) { const PxU32* i1 = i0 + 1; const PxU32* i2 = i0 + 2; PxVec3 d0(mVertexPositions[*i0].x, mVertexPositions[*i0].y, mVertexPositions[*i0].z); PxVec3 d1(mVertexPositions[*i1].x, mVertexPositions[*i1].y, mVertexPositions[*i1].z); PxVec3 d2(mVertexPositions[*i2].x, mVertexPositions[*i2].y, mVertexPositions[*i2].z); d1 = d1 - d0; d2 = d2 - d0; PxVec3 n = d1.cross(d2); PxReal area = 0.5f * n.magnitude(); invMasses[*i0] += area / 3.0f; invMasses[*i1] += area / 3.0f; invMasses[*i2] += area / 3.0f; totalArea += area; i0 += 3; } // distribute overall mass by associated area for (PxU32 i = 0; i < invMasses.size(); i++) { PxReal area = invMasses[i]; invMasses[i] = (1.0f / mass) * (totalArea / area); } } break; case PxDeformablePrimitiveType::eTETRAHEDRON: { // first, we compute volume associated with each vertex and temporarily store it in invMasses array PxReal totalVolume = 0.0f; const PxU32* i0 = mPrimitives.begin(); for (PxU32 i = 0; i < mPrimitives.size() / 4; i++) { const PxU32 *i1 = i0 + 1; const PxU32 *i2 = i0 + 2; const PxU32 *i3 = i0 + 3; PxVec3 d0(mVertexPositions[*i0].x, mVertexPositions[*i0].y, mVertexPositions[*i0].z); PxVec3 d1(mVertexPositions[*i1].x, mVertexPositions[*i1].y, mVertexPositions[*i1].z); PxVec3 d2(mVertexPositions[*i2].x, mVertexPositions[*i2].y, mVertexPositions[*i2].z); PxVec3 d3(mVertexPositions[*i3].x, mVertexPositions[*i3].y, mVertexPositions[*i3].z); d1 = d1 - d0; d2 = d2 - d0; d3 = d3 - d0; PxVec3 n = d1.cross(d2); PxReal volume = n.dot(d3) / 6.0f; invMasses[*i0] += volume / 4.0f; invMasses[*i1] += volume / 4.0f; invMasses[*i2] += volume / 4.0f; invMasses[*i3] += volume / 4.0f; totalVolume += volume; i0 += 4; } // distribute overall mass by associated volume for (PxU32 i = 0; i < invMasses.size(); i++) { PxReal volume = invMasses[i]; invMasses[i] = (1.0f / mass) * (totalVolume / volume); } } break; default: PX_ASSERT(0); break; } }
void DeformableMesh::generateConstraintsFromTetrahedra() { PxU32 edgeIndices[6][2] = { {0,1}, {0,2}, {0,3}, {1,2}, {1,3}, {2,3} }; PxU32 *tetIndices; // - tetrahedra are assumed to be unique (thus no parent code here) PxU32 numTetrahedra = mPrimitives.size() / 4; DeformableTetraEdge e; Ps::Array<DeformableTetraEdge> edges PX_DEBUG_EXP("defoMeshEdges2"); edges.reserve(numTetrahedra * 6); tetIndices = mPrimitives.begin(); PxU32 i, j; for (i = 0; i < numTetrahedra; i++, tetIndices += 4) { for(j = 0; j < 6; j++) { PxU32 e0 = mVertexToParticleMap[tetIndices[edgeIndices[j][0]]]; PxU32 e1 = mVertexToParticleMap[tetIndices[edgeIndices[j][1]]]; e.set(e0, e1, i); edges.pushBack(e); } } quickSortTetraEdges(edges, 0, edges.size()-1); mConstraints.resize(numTetrahedra); DeformableConstraint constraint; DeformableTetraEdge *tetEdges[6]; tetIndices = mPrimitives.begin(); bool warningIssued = false; for (i = 0; i < numTetrahedra; i++, tetIndices += 4) { for (j = 0; j < 6; j++) { PxU32 e0 = mVertexToParticleMap[tetIndices[edgeIndices[j][0]]]; PxU32 e1 = mVertexToParticleMap[tetIndices[edgeIndices[j][1]]]; DeformableTetraEdge goalEdge; goalEdge.set(e0, e1, i); tetEdges[j] = binarySearchTetraEdge(edges, goalEdge); } for (j = 0; j < 4; j++) constraint.particleId[j] = mVertexToParticleMap[tetIndices[j]]; PxVec3 groundArea = (mWeldedVertices[tetIndices[1]] - mWeldedVertices[tetIndices[0]]).cross(mWeldedVertices[tetIndices[2]] - mWeldedVertices[tetIndices[0]]); constraint.restVolume = groundArea.dot(mWeldedVertices[tetIndices[3]] - mWeldedVertices[tetIndices[0]]); constraint.flags = 0; if (!warningIssued && constraint.restVolume < 0.0f) { Ps::getFoundation().error(PxErrorCode::eDEBUG_WARNING, __FILE__, __LINE__, "Soft body mesh tetrahedron %d has illegal winding order.", i); warningIssued = true; } for (j = 0; j < 6; j++) { PxU32 e0 = mVertexToParticleMap[tetIndices[edgeIndices[j][0]]]; PxU32 e1 = mVertexToParticleMap[tetIndices[edgeIndices[j][1]]]; PxVec3 edgeVec = mWeldedVertices[e1] - mWeldedVertices[e0]; if(tetEdges[j]) { PX_ASSERT(tetEdges[j]->tetrahedron == i); constraint.restEdgeLengths[j] = edgeVec.magnitude(); } else constraint.restEdgeLengths[j] = -edgeVec.magnitude(); } mConstraints[i] = constraint; } }
void RenderPhysX3Debug::addConeExt(float r0, float r1, const PxVec3& p0, const PxVec3& p1 , const RendererColor& color, PxU32 renderFlags) { PxVec3 axis = p1 - p0; PxReal length = axis.magnitude(); PxReal rdiff = r0 - r1; PxReal sinAngle = rdiff / length; PxReal x0 = r0 * sinAngle; PxReal x1 = r1 * sinAngle; PxVec3 center = 0.5f * (p0 + p1); if (length < fabs(rdiff)) return; PxReal r0p = sqrt(r0 * r0 - x0 * x0); PxReal r1p = sqrt(r1 * r1 - x1 * x1); if (length == 0.0f) axis = PxVec3(1,0,0); else axis.normalize(); PxVec3 axis1(0.0f); axis1[minArgument(abs(axis))] = 1.0f; axis1 = axis1.cross(axis); axis1.normalize(); PxVec3 axis2 = axis.cross(axis1); axis2.normalize(); PxMat44 m; m.column0 = PxVec4(axis, 0.0f); m.column1 = PxVec4(axis1, 0.0f); m.column2 = PxVec4(axis2, 0.0f); m.column3 = PxVec4(center, 1.0f); PxTransform tr(m); #define NUM_CONE_VERTS 72 const PxU32 nbVerts = NUM_CONE_VERTS; PxVec3 pts0[NUM_CONE_VERTS] ; PxVec3 pts1[NUM_CONE_VERTS]; PxVec3 normals[NUM_CONE_VERTS] ; const float step = PxTwoPi / float(nbVerts); for (PxU32 i = 0; i < nbVerts; i++) { const float angle = float(i) * step; const float x = cosf(angle); const float y = sinf(angle); PxVec3 p = PxVec3(0.0f, x, y); pts0[i] = tr.transform(r0p * p + PxVec3(-0.5f * length + x0,0,0)); pts1[i] = tr.transform(r1p * p + PxVec3(0.5f * length + x1, 0, 0)); normals[i] = tr.q.rotate(p.getNormalized()); normals[i] = x0 * axis + r0p * normals[i]; normals[i].normalize(); } #undef NUM_CONE_VERTS if(renderFlags & RENDER_DEBUG_WIREFRAME) { for(PxU32 i=0;i<nbVerts;i++) { addLine(pts1[i], pts0[i], color); } } if(renderFlags & RENDER_DEBUG_SOLID) { for(PxU32 i=0;i<nbVerts;i++) { const PxU32 j = (i+1) % nbVerts; addTriangle(pts0[i], pts1[j], pts0[j], normals[i], normals[j], normals[j], color); addTriangle(pts0[i], pts1[i], pts1[j], normals[i], normals[i], normals[j], color); } } }
const BoxPadded* cullBox) // Cull data { if(!nbTris) return false; const bool meshBothSides = hitFlags & PxHitFlag::eMESH_BOTH_SIDES; const bool doBackfaceCulling = !isDoubleSided && !meshBothSides; const bool anyHit = hitFlags & PxHitFlag::eMESH_ANY; const bool testInitialOverlap = !(hitFlags & PxHitFlag::eASSUME_NO_INITIAL_OVERLAP); // PT: we can fallback to sphere sweep: // - if the capsule is degenerate (i.e. it's a sphere) // - if the sweep direction is the same as the capsule axis, in which case we can just sweep the top or bottom sphere const PxVec3 extrusionDir = (capsule.p0 - capsule.p1)*0.5f; // Extrusion dir = capsule segment const PxReal halfHeight = extrusionDir.magnitude(); bool mustExtrude = halfHeight!=0.0f; if(!mustExtrude) { // PT: capsule is a sphere. Switch to sphere path (intersectCapsuleTriangle doesn't work for degenerate capsules) return sweepSphereTriangles(nbTris, triangles, capsule.p0, capsule.radius, unitDir, distance, cachedIndex, hit, triNormalOut, isDoubleSided, meshBothSides, anyHit, testInitialOverlap); } else { const PxVec3 capsuleAxis = extrusionDir/halfHeight; const PxReal colinearity = PxAbs(capsuleAxis.dot(unitDir)); mustExtrude = (colinearity < (1.0f - COLINEARITY_EPSILON)); } const PxVec3 capsuleCenter = capsule.computeCenter();