PxVec3 EmitterGeomSphereShellImpl::randomPosInFullVolume(const PxMat44& pose, QDSRand& rand) const { float hemisphere = 2.0f * *mHemisphere - 1.0f; bool moreThanHalf = true; if (*mHemisphere > 0.5f) { moreThanHalf = false; } // There are two cases here - 1-st for hemisphere cut above the center of the sphere // and 2-nd for hemisphere cut below the center of the sphere. // The reason for this is that in case very high hemisphere cut is set, so the area // of the actual emitter is very small in compare to the whole sphere emitter, it would take too // much time [on average] to generate suitable point using randomPointOnUnitSphere // function, so in this case it is more efficient to use another method. // in case we have at least half of the sphere shell present the randomPointOnUnitSphere should // be sufficient. PxVec3 pos; if(!moreThanHalf) { // 1-st case : // * generate random unit vector within a cone // * clamp to big radius const float sphereCapBaseHeight = -1.0f + 2 * (*mHemisphere); const float phi = rand.getScaled(0.0f, PxTwoPi); const float cos_theta = sphereCapBaseHeight; const float z = rand.getScaled(cos_theta, 1.0f); const float oneMinusZSquared = PxSqrt(1.0f - z * z); pos = PxVec3(oneMinusZSquared * PxCos(phi), z, oneMinusZSquared * PxSin(phi)); } else { // 2-nd case : // * get random pos on unit sphere, until its height is above hemisphere cut do { pos = randomPointOnUnitSphere(rand); } while(pos.y < hemisphere); } // * add negative offset withing the thickness // * solve edge case [for the 1-st case] - regenerate offset from the previous step // in case point is below hemisphere cut PxVec3 tmp; const float sphereCapBaseHeight = -(*mRadius + *mShellThickness) + 2 * (*mRadius + *mShellThickness) * (*mHemisphere); do { float thickness = rand.getScaled(0, *mShellThickness); tmp = pos * (*mRadius + *mShellThickness - thickness); } while(tmp.y < sphereCapBaseHeight); pos = tmp; pos += pose.getPosition(); return pos; }
void EmitterGeomSphereShellImpl::visualize(const PxTransform& pose, RenderDebugInterface& renderDebug) { using RENDER_DEBUG::DebugColors; RENDER_DEBUG_IFACE(&renderDebug)->pushRenderState(); RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkGreen)); // outer sphere RENDER_DEBUG_IFACE(&renderDebug)->setPose(pose); RENDER_DEBUG_IFACE(&renderDebug)->debugSphere(PxVec3(0.0f), *mRadius); // intter sphere RENDER_DEBUG_IFACE(&renderDebug)->debugSphere(PxVec3(0.0f), *mRadius + *mShellThickness); const float radius = *mRadius + *mShellThickness; const float radiusSquared = radius * radius; const float hemisphere = *mHemisphere; const float sphereCapBaseHeight = -radius + 2 * radius * hemisphere; const float sphereCapBaseRadius = PxSqrt(radiusSquared - sphereCapBaseHeight * sphereCapBaseHeight); // cone depicting the hemisphere if(hemisphere > 0.0f) { RENDER_DEBUG_IFACE(&renderDebug)->setCurrentColor(RENDER_DEBUG_IFACE(&renderDebug)->getDebugColor(DebugColors::DarkPurple)); PxMat44 circlePose = PxMat44(PxIdentity); circlePose.setPosition(PxVec3(0.0f, sphereCapBaseHeight, 0.0f)); RENDER_DEBUG_IFACE(&renderDebug)->setPose(circlePose); RENDER_DEBUG_IFACE(&renderDebug)->debugCircle(PxVec3(0.0f), sphereCapBaseRadius, 3); RENDER_DEBUG_IFACE(&renderDebug)->debugLine(circlePose.getPosition(), circlePose.getPosition() + PxVec3(0.0f, radius - sphereCapBaseHeight, 0.0f)); for(float t = 0.0f; t < 2 * PxPi; t += PxPi / 3) { PxVec3 offset(PxSin(t) * sphereCapBaseRadius, 0.0f, PxCos(t) * sphereCapBaseRadius); RENDER_DEBUG_IFACE(&renderDebug)->debugLine(circlePose.getPosition() + offset, circlePose.getPosition() + PxVec3(0.0f, radius - sphereCapBaseHeight, 0.0f)); } RENDER_DEBUG_IFACE(&renderDebug)->setPose(PxIdentity); } RENDER_DEBUG_IFACE(&renderDebug)->popRenderState(); }
static void outputConvexToStream(PxShape* convexShape, const PxRigidActor* actor, const PxTransform& absPose_, IntArray& geomStream, TriArray& worldTriangles, IntArray& triIndicesArray, const PxExtendedVec3& origin, const PxBounds3& tmpBounds, const CCTParams& params, Cm::RenderBuffer* renderBuffer, PxU16& nbTessellation) { PX_ASSERT(convexShape->getGeometryType() == PxGeometryType::eCONVEXMESH); PxConvexMeshGeometry cg; convexShape->getConvexMeshGeometry(cg); PX_ASSERT(cg.convexMesh); // Do AABB-mesh query PxU32* TF; // Collide AABB against current mesh // The overlap function doesn't exist for convexes so let's just dump all tris PxConvexMesh& cm = *cg.convexMesh; // PT: convex triangles are not exposed anymore so we need to access convex polygons & triangulate them // PT: TODO: this is copied from "DrawObjects", move this to a shared place. Actually a helper directly in PxConvexMesh would be useful. PxU32 Nb = 0; { const PxU32 nbPolys = cm.getNbPolygons(); const PxU8* polygons = cm.getIndexBuffer(); for(PxU32 i=0;i<nbPolys;i++) { PxHullPolygon data; cm.getPolygonData(i, data); Nb += data.mNbVerts - 2; } // PT: revisit this code. We don't use the polygon offset? TF = (PxU32*)PxAlloca(sizeof(PxU32)*Nb*3); PxU32* t = TF; for(PxU32 i=0;i<nbPolys;i++) { PxHullPolygon data; cm.getPolygonData(i, data); const PxU32 nbV = data.mNbVerts; const PxU32 nbTris = nbV - 2; const PxU8 vref0 = *polygons; for(PxU32 j=0;j<nbTris;j++) { const PxU32 vref1 = polygons[(j+1)%nbV]; const PxU32 vref2 = polygons[(j+2)%nbV]; *t++ = vref0; *t++ = vref1; *t++ = vref2; } polygons += nbV; } } // PT: you can't use PxTransform with a non-uniform scaling const PxMat33 rot = PxMat33(absPose_.q) * cg.scale.toMat33(); const PxMat44 absPose(rot, absPose_.p); const PxVec3 p = absPose.getPosition(); const PxVec3 MeshOffset(float(p.x - origin.x), float(p.y - origin.y), float(p.z - origin.z)); // LOSS OF ACCURACY const PxVec3 offset(float(-origin.x), float(-origin.y), float(-origin.z)); TouchedMesh* touchedMesh = (TouchedMesh*)reserve(geomStream, sizeof(TouchedMesh)/sizeof(PxU32)); touchedMesh->mType = TouchedGeomType::eMESH; touchedMesh->mTGUserData = convexShape; touchedMesh->mActor = actor; touchedMesh->mOffset = origin; touchedMesh->mIndexWorldTriangles = worldTriangles.size(); const PxVec3* verts = cm.getVertices(); // Loop through touched triangles if(params.mTessellation) { const PxBoxGeometry boxGeom(tmpBounds.getExtents()); const PxBounds3 cullingBox = PxBounds3::centerExtents(tmpBounds.getCenter() + offset, boxGeom.halfExtents); PxU32 nbCreatedTris = 0; while(Nb--) { // Compute triangle in world space, add to array PxTriangle currentTriangle; const PxU32 vref0 = *TF++; const PxU32 vref1 = *TF++; const PxU32 vref2 = *TF++; currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); PxU32 nbNewTris = 0; tessellateTriangle(nbNewTris, currentTriangle, PX_INVALID_U32, worldTriangles, triIndicesArray, cullingBox, params, nbTessellation); nbCreatedTris += nbNewTris; } touchedMesh->mNbTris = nbCreatedTris; } else { // Reserve memory for incoming triangles PxTriangle* TouchedTriangles = reserve(worldTriangles, Nb); touchedMesh->mNbTris = Nb; while(Nb--) { // Compute triangle in world space, add to array PxTriangle& currentTriangle = *TouchedTriangles++; const PxU32 vref0 = *TF++; const PxU32 vref1 = *TF++; const PxU32 vref2 = *TF++; currentTriangle.verts[0] = MeshOffset + absPose.rotate(verts[vref0]); currentTriangle.verts[1] = MeshOffset + absPose.rotate(verts[vref1]); currentTriangle.verts[2] = MeshOffset + absPose.rotate(verts[vref2]); triIndicesArray.pushBack(PX_INVALID_U32); } } if(gVisualizeTouchedTris) visualizeTouchedTriangles(touchedMesh->mNbTris, touchedMesh->mIndexWorldTriangles, &worldTriangles[0], renderBuffer, offset, params.mUpDirection); }