/** Returns bounds description associated with the Novodex shape description. */ Bounds::Desc::Ptr PhysScene::createBoundsDesc(const NxShapeDesc &nxShapeDesc) { // check if bound description has been already created BoundsDescMap::const_iterator pos = boundsDescMap.find(&nxShapeDesc); if (pos != boundsDescMap.end()) return pos->second; Bounds::Desc::Ptr pBoundsDesc; // check if description is valid if (!nxShapeDesc.isValid()) throw MsgPhysSceneNxShapeDescInvalidDesc(Message::LEVEL_ERROR, "PhysScene::createBoundsDesc(): invalid Novodex shape description"); NxShapeDescPtr nxShapeDescPtr; if (nxShapeDesc.getType() == NX_SHAPE_PLANE) { const NxPlaneShapeDesc *pDescSrc = dynamic_cast<const NxPlaneShapeDesc*>(&nxShapeDesc); if (pDescSrc != NULL) { NxPlaneShapeDesc *pDescDst = new NxPlaneShapeDesc(); nxShapeDescPtr.reset(pDescDst); ::memcpy(pDescDst, pDescSrc, sizeof(NxPlaneShapeDesc)); pBoundsDesc = createBoundsDesc(pDescDst); } } else if (nxShapeDesc.getType() == NX_SHAPE_SPHERE) { const NxSphereShapeDesc *pDescSrc = dynamic_cast<const NxSphereShapeDesc*>(&nxShapeDesc); if (pDescSrc != NULL) { NxSphereShapeDesc *pDescDst = new NxSphereShapeDesc(); nxShapeDescPtr.reset(pDescDst); ::memcpy(pDescDst, pDescSrc, sizeof(NxSphereShapeDesc)); pBoundsDesc = createBoundsDesc(pDescDst); } } else if (nxShapeDesc.getType() == NX_SHAPE_BOX) { const NxBoxShapeDesc *pDescSrc = dynamic_cast<const NxBoxShapeDesc*>(&nxShapeDesc); if (pDescSrc != NULL) { NxBoxShapeDesc *pDescDst = new NxBoxShapeDesc(); nxShapeDescPtr.reset(pDescDst); ::memcpy(pDescDst, pDescSrc, sizeof(NxBoxShapeDesc)); pBoundsDesc = createBoundsDesc(pDescDst); } } else if (nxShapeDesc.getType() == NX_SHAPE_CONVEX) { const NxConvexShapeDesc *pDescSrc = dynamic_cast<const NxConvexShapeDesc*>(&nxShapeDesc); if (pDescSrc != NULL) { NxConvexShapeDesc *pDescDst = new NxConvexShapeDesc(); nxShapeDescPtr.reset(pDescDst); ::memcpy(pDescDst, pDescSrc, sizeof(NxConvexShapeDesc)); pBoundsDesc = createBoundsDesc(pDescDst); // Access to PhysX } } else { ASSERT(false) } if (pBoundsDesc == NULL) throw MsgSceneBoundsDescCreate(Message::LEVEL_ERROR, "PhysScene::createBoundsDesc(): failed to create bounds description"); nxShapeDescMap[pBoundsDesc.get()] = nxShapeDescPtr; boundsDescMap[nxShapeDescPtr.get()] = pBoundsDesc; return pBoundsDesc; }
bool PxBody::init( PhysicsCollision *shape, F32 mass, U32 bodyFlags, SceneObject *obj, PhysicsWorld *world ) { AssertFatal( obj, "PxBody::init - Got a null scene object!" ); AssertFatal( world, "PxBody::init - Got a null world!" ); AssertFatal( dynamic_cast<PxWorld*>( world ), "PxBody::init - The world is the wrong type!" ); AssertFatal( shape, "PxBody::init - Got a null collision shape!" ); AssertFatal( dynamic_cast<PxCollision*>( shape ), "PxBody::init - The collision shape is the wrong type!" ); AssertFatal( !((PxCollision*)shape)->getShapes().empty(), "PxBody::init - Got empty collision shape!" ); // Cleanup any previous actor. _releaseActor(); mWorld = (PxWorld*)world; mColShape = (PxCollision*)shape; mBodyFlags = bodyFlags; NxActorDesc actorDesc; NxBodyDesc bodyDesc; const bool isKinematic = mBodyFlags & BF_KINEMATIC; const bool isTrigger = mBodyFlags & BF_TRIGGER; const bool isDebris = mBodyFlags & BF_DEBRIS; if ( isKinematic ) { // Kinematics are dynamics... so they need // a body description. actorDesc.body = &bodyDesc; bodyDesc.mass = getMax( mass, 1.0f ); bodyDesc.flags |= NX_BF_KINEMATIC; } else if ( mass > 0.0f ) { // We have mass so its a dynamic. actorDesc.body = &bodyDesc; bodyDesc.mass = mass; } if ( isTrigger ) actorDesc.flags |= NX_AF_DISABLE_RESPONSE; // Add all the shapes. const Vector<NxShapeDesc*> &shapes = mColShape->getShapes(); for ( U32 i=0; i < shapes.size(); i++ ) { NxShapeDesc *desc = shapes[i]; // If this hits then something is broken with // this descrption... check all the fields to be // sure their values are correctly filled out. AssertFatal( desc->isValid(), "PxBody::init - Got invalid shape description!" ); if ( isTrigger ) desc->group = 31; if ( isDebris ) desc->group = 30; actorDesc.shapes.push_back( desc ); } // This sucks, but it has to happen if we want // to avoid write lock errors from PhysX right now. mWorld->releaseWriteLock(); mActor = mWorld->getScene()->createActor( actorDesc ); mIsEnabled = true; if ( isDebris ) mActor->setDominanceGroup( 31 ); mUserData.setObject( obj ); mUserData.setBody( this ); mActor->userData = &mUserData; return true; }
/* Use the shape of node to create Bullet shape for the actor. ActorNode is the main Max node. */ btCollisionShape* MxActor::createShape(NxActorDesc& actorDesc, ccMaxNode* node, ccMaxNode* actorNode) { actorDesc.localPose.IdentityMatrix(); btCollisionShape* shape = NULL; const TimeValue t = ccMaxWorld::MaxTime(); Matrix3 nodePose = node->PhysicsNodePoseTM; // MaxMsgBox(NULL, _T("createShape"), _T("Error"), MB_OK); int geomType = MxUserPropUtils::GetUserPropInt(m_node, "GeometryType",1); NxShapeType type = node->ShapeType; //first apply manual overrides switch (geomType) { case 2: { type = NX_SHAPE_SPHERE; break; } case 3: { type = NX_SHAPE_BOX; break; } case 4: { type = NX_SHAPE_CAPSULE; break; } case 5: { type = NX_SHAPE_CONVEX; break; } case 6: { type = NX_SHAPE_MESH; break; } default: { } }; actorDesc.localPose = node->PhysicsNodePoseTM * actorNode->PhysicsNodePoseTMInv; switch (type) { case NX_SHAPE_SPHERE: { btScalar radius = node->PrimaryShapePara.Radius; shape = new btSphereShape(radius); break; }; case NX_SHAPE_BOX: { //adjust for difference in pivot points between 3ds max and Bullet (Bullet uses the box center) Matrix3 offset; offset.IdentityMatrix(); offset.SetTrans(2,node->PrimaryShapePara.BoxDimension.z()); actorDesc.localPose = actorDesc.localPose * offset; shape = new btBoxShape(node->PrimaryShapePara.BoxDimension.absolute()); break; } case NX_SHAPE_CAPSULE: { char bla[1024]; sprintf(bla,"capsule not properly supported yet, radius=%f,height=%f",node->PrimaryShapePara.Radius,node->PrimaryShapePara.Height); MaxMsgBox(NULL, _T(bla), _T("Error"), MB_OK); shape = new btCapsuleShape(node->PrimaryShapePara.Radius,node->PrimaryShapePara.Height); break; } case NX_SHAPE_CONVEX: { if(m_proxyNode) { MaxMsgBox(NULL, _T("Error: convex shape proxy not supported (yet)"), _T("Error"), MB_OK); //d->meshData = MxUtils::nodeToNxConvexMesh(proxyMesh); //Matrix3 pose = nodePose * actorNode->PhysicsNodePoseTMInv; //d->localPose = MxMathUtils::MaxMatrixToNx(pose); } else { if(node->SimpleMesh.numFaces > 255) { MaxMsgBox(NULL, _T("Error: number of vertices in a convex shape should be less than 256"), _T("Error"), MB_OK); //warning/Error } else { BOOL needDel = FALSE; TriObject* tri = MxUtils::GetTriObjectFromNode(node->GetMaxNode(),0.f,needDel); if (tri) { int numVerts = tri->NumPoints(); btConvexHullShape* convexHull = new btConvexHullShape(); //for center of mass computation, simplify and assume mass is at the vertices btCompoundShape* compound = new btCompoundShape(); btSphereShape sphere(0.1); btTransform tr; tr.setIdentity(); btAlignedObjectArray<btScalar> masses; btScalar childMass = actorDesc.mass/(btScalar)numVerts; for (int i=0;i<numVerts;i++) { btVector3 pt(tri->GetPoint(i).x,tri->GetPoint(i).y,tri->GetPoint(i).z); convexHull->addPoint(pt); tr.setOrigin(pt); compound->addChildShape(tr,&sphere); masses.push_back(childMass); } btTransform principal; btVector3 inertia; compound->calculatePrincipalAxisTransform(&masses[0],principal,inertia); delete compound; btTransform principalInv = principal.inverse(); compound = new btCompoundShape(); compound->addChildShape(principalInv,convexHull); shape = compound; Matrix3 offset; bullet2Max(principal,offset); actorDesc.localPose = actorDesc.localPose * offset; if (needDel) delete tri; } } //d->meshData = MxUtils::nodeToNxConvexMesh(node->SimpleMesh); //Matrix3 pose = nodePose * actorNode->PhysicsNodePoseTMInv; //d->localPose = MxMathUtils::MaxMatrixToNx(pose); } break; } case NX_SHAPE_MESH: { BOOL needDel = FALSE; TriObject* tri = MxUtils::GetTriObjectFromNode(node->GetMaxNode(),0.f,needDel); if (tri) { int numVerts = tri->NumPoints(); btTriangleMesh* meshInterface = new btTriangleMesh(); Mesh& mesh = tri->GetMesh(); if (mesh.getNumFaces()>0) { for (int i=0;i<mesh.getNumFaces();i++) { Point3 p0=tri->GetPoint(mesh.faces[i].v[0]); Point3 p1=tri->GetPoint(mesh.faces[i].v[1]); Point3 p2=tri->GetPoint(mesh.faces[i].v[2]); meshInterface->addTriangle( btVector3(p0.x,p0.y,p0.z),btVector3(p1.x,p1.y,p1.z),btVector3(p2.x,p2.y,p2.z)); } if (actorDesc.mass>0) { // MaxMsgBox(NULL, _T("btGImpactMeshShape"), _T("Error"), MB_OK); btGImpactMeshShape* trimesh = new btGImpactMeshShape(meshInterface); shape = trimesh; } else { // MaxMsgBox(NULL, _T("btBvhTriangleMeshShape"), _T("Error"), MB_OK); btBvhTriangleMeshShape* trimesh = new btBvhTriangleMeshShape(meshInterface,true); shape = trimesh; } } else { MaxMsgBox(NULL, _T("Error: no faces"), _T("Error"), MB_OK); } if (needDel) delete tri; } else { MaxMsgBox(NULL, _T("Error: couldn't GetTriObjectFromNode"), _T("Error"), MB_OK); } break; } default: { MaxMsgBox(NULL, _T("unknown shape type"), _T("Error"), MB_OK); } }; #if 0 NxShapeDesc* pd = NULL; NxShapeType type = node->ShapeType; PxSimpleMesh proxyMesh; Matrix3 nodePose = node->PhysicsNodePoseTM; if(m_proxyNode) { // for proxy, using mesh //type = NX_SHAPE_MESH; proxyMesh.clone(node->SimpleMesh); Point3 pos = nodePose.GetRow(3); pos = pos + ProxyDistance; nodePose.SetRow(3, pos); } if((type == NX_SHAPE_MESH) && (Interactivity != RB_STATIC)) { type = NX_SHAPE_CONVEX; } // /* bool NeedCCDSkeleton = (Interactivity != RB_STATIC) && (MxUserPropUtils::GetUserPropBool(node->GetMaxNode(), "EnableCCD", false)); if(NeedCCDSkeleton) { NxPhysicsSDK* psdk = gPluginData->getPhysicsSDK(); psdk->setParameter(NX_CONTINUOUS_CD, 1); psdk->setParameter(NX_CCD_EPSILON, 0.01f); } */ // create CCD skeleton for the shape //TODO("implement CCD skeleton creation"); PX_CCD_SKELETON ccdType = (PX_CCD_SKELETON) MxUserPropUtils::GetUserPropInt(node->GetMaxNode(), "px_shape_ccdtype", 1); switch(type) { default: if (gCurrentstream) gCurrentstream->printf("Unable to create a shape of node \"%s\", unknown shape type: %d.\n", node->GetMaxNode()->GetName(), type); return false; } // load property settings and material things. //LoadShapeProperties(*pd, node->GetMaxNode()); //CCD flag pd->name = node->GetMaxNode()->GetName(); pd->userData = node; // bool isvalid = pd->isValid(); actorDesc.shapes.push_back(pd); #endif return shape; }