/* 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; }
bool CExportNel::mirrorPhysiqueSelection(INode &node, TimeValue tvTime, const std::vector<uint> &vertIn, float threshold) { bool ok; uint i; // no vertices selected? if(vertIn.empty()) return true; // **** Get all the skeleton node std::vector<INode*> skeletonNodes; INode *skelRoot= getSkeletonRootBone(node); if(!skelRoot) return false; getObjectNodes(skeletonNodes, tvTime, skelRoot); // **** Build the Vector (world) part std::vector<CTempSkinVertex> tempVertex; uint vertCount; // Get a pointer on the object's node. ObjectState os = node.EvalWorldState(tvTime); Object *obj = os.obj; // Check if there is an object ok= false; if (obj) { // Object can be converted in triObject ? if (obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) { // Get a triobject from the node TriObject *tri = (TriObject*)obj->ConvertToType(tvTime, Class_ID(TRIOBJ_CLASS_ID, 0)); if (tri) { // Note that the TriObject should only be deleted // if the pointer to it is not equal to the object // pointer that called ConvertToType() bool deleteIt=false; if (obj != tri) deleteIt = true; // Get the node matrix. TODO: Matrix headhache? /*Matrix3 nodeMatrixMax; CMatrix nodeMatrix; getLocalMatrix (nodeMatrixMax, node, tvTime); convertMatrix (nodeMatrix, nodeMatrixMax);*/ // retrive Position geometry vertCount= tri->NumPoints(); tempVertex.resize(vertCount); for(uint i=0;i<vertCount;i++) { Point3 v= tri->GetPoint(i); tempVertex[i].Pos.set(v.x, v.y, v.z); } // Delete the triObject if we should... if (deleteIt) tri->MaybeAutoDelete(); tri = NULL; // ok! ok= true; } } } if(!ok) return false; // no vertices? abort if(vertCount==0) return true; // **** Mark all Input vertices for(i=0;i<vertIn.size();i++) { nlassert(vertIn[i]<vertCount); tempVertex[vertIn[i]].Input= true; } // **** Build the output vertices std::vector<uint> vertOut; vertOut.reserve(tempVertex.size()); // Build the in bbox CAABBox bbox; bbox.setCenter(tempVertex[vertIn[0]].Pos); for(i=0;i<vertIn.size();i++) { bbox.extend(tempVertex[vertIn[i]].Pos); } bbox.setHalfSize(bbox.getHalfSize()+CVector(threshold, threshold, threshold)); // mirror in X CVector vMin= bbox.getMin(); CVector vMax= bbox.getMax(); vMin.x= -vMin.x; vMax.x= -vMax.x; std::swap(vMin.x, vMax.x); bbox.setMinMax(vMin, vMax); // get all out vertices in the mirrored bbox. for(i=0;i<tempVertex.size();i++) { if(bbox.include(tempVertex[i].Pos)) { vertOut.push_back(i); } } // **** Build the skin information // Get the skin modifier Modifier* skin=getModifier (&node, PHYSIQUE_CLASS_ID); // Found it ? ok= false; if (skin) { // Get a com_skin2 interface IPhysiqueExport *physiqueInterface=(IPhysiqueExport *)skin->GetInterface (I_PHYINTERFACE); // Found com_skin2 ? if (physiqueInterface) { // Get local data IPhyContextExport *localData= physiqueInterface->GetContextInterface(&node); // Found ? if (localData) { // Use rigid export localData->ConvertToRigid (TRUE); // Allow blending localData->AllowBlending (TRUE); // Skinned ok=true; // TODO? nlassert(tempVertex.size()<=(uint)localData->GetNumberVertices()); // For each vertex for (uint vert=0; vert<vertCount; vert++) { // Get a vertex interface IPhyVertexExport *vertexInterface= localData->GetVertexInterface (vert); // Check if it is a rigid vertex or a blended vertex IPhyRigidVertex *rigidInterface=NULL; IPhyBlendedRigidVertex *blendedInterface=NULL; int type=vertexInterface->GetVertexType (); if (type==RIGID_TYPE) { // this is a rigid vertex rigidInterface=(IPhyRigidVertex*)vertexInterface; } else { // It must be a blendable vertex nlassert (type==RIGID_BLENDED_TYPE); blendedInterface=(IPhyBlendedRigidVertex*)vertexInterface; } // Get bones count for this vertex uint boneCount; if (blendedInterface) { // If blenvertex, only one bone boneCount=blendedInterface->GetNumberNodes(); } else { // If rigid vertex, only one bone boneCount=1; } if(boneCount>TEMP_MAX_WEIGHT) boneCount= TEMP_MAX_WEIGHT; // NB: if input 0, won't be mirrored tempVertex[vert].NumWeight= boneCount; for(uint bone=0;bone<boneCount;bone++) { if (blendedInterface) { tempVertex[vert].Bone[bone]= blendedInterface->GetNode(bone); nlassert(tempVertex[vert].Bone[bone]); tempVertex[vert].Weight[bone]= blendedInterface->GetWeight(bone); } else { tempVertex[vert].Bone[bone]= rigidInterface->GetNode(); tempVertex[vert].Weight[bone]= 1; } } // Release vertex interfaces localData->ReleaseVertexInterface (vertexInterface); } } // release context interface physiqueInterface->ReleaseContextInterface(localData); } // Release the interface skin->ReleaseInterface (I_PHYINTERFACE, physiqueInterface); } if(!ok) return false; // **** Real Algo stuff: // For all vertices wanted to be mirrored std::vector<CSortVertex> sortVert; sortVert.reserve(tempVertex.size()); for(i=0;i<vertIn.size();i++) { CTempSkinVertex &svIn= tempVertex[vertIn[i]]; // if it still has no bones set, skip if(svIn.NumWeight==0) continue; // mirror vert to test CVector vertTest= svIn.Pos; vertTest.x*= -1; // get the best vertex sortVert.clear(); // Search for all output vertices if ones match for(uint j=0;j<vertOut.size();j++) { uint dstIdx= vertOut[j]; nlassert(dstIdx<tempVertex.size()); CTempSkinVertex &skinv= tempVertex[dstIdx]; // take only if not an input, and if not already mirrored if(!skinv.Input && !skinv.Mirrored) { CSortVertex sortv; sortv.Index= dstIdx; sortv.SqrDist= (skinv.Pos - vertTest).sqrnorm(); // Finally, take it only if sufficiently near if(sortv.SqrDist <= threshold*threshold) sortVert.push_back(sortv); } } // if some found. if(!sortVert.empty()) { // sort array. std::sort(sortVert.begin(), sortVert.end()); // take the first, mirror setup uint dstIdx= sortVert[0].Index; tempVertex[dstIdx].NumWeight= svIn.NumWeight; for(uint k=0;k<svIn.NumWeight;k++) { tempVertex[dstIdx].Weight[k]= svIn.Weight[k]; tempVertex[dstIdx].Bone[k]= getMirrorBone( skeletonNodes, svIn.Bone[k] ); } // mark as mirrored! tempVertex[dstIdx].Mirrored= true; } } // **** Write the result to the skin. ok= false; if (skin) { // Get a com_skin2 interface IPhysiqueImport *physiqueInterface=(IPhysiqueImport *)skin->GetInterface (I_PHYIMPORT); // Found com_skin2 ? if (physiqueInterface) { // Get local data IPhyContextImport *localData= physiqueInterface->GetContextInterface(&node); // TODO? nlassert(tempVertex.size()<=(uint)localData->GetNumberVertices()); // Found ? if (localData) { // Skinned ok=true; for(uint i=0;i<tempVertex.size();i++) { CTempSkinVertex &sv= tempVertex[i]; // if its a mirrored output vertex if(sv.Mirrored) { IPhyBlendedRigidVertexImport *blendedInterface= NULL; blendedInterface= (IPhyBlendedRigidVertexImport*)localData->SetVertexInterface(i, RIGID_BLENDED_TYPE); if(blendedInterface) { // set the vertex data for(uint bone=0;bone<sv.NumWeight;bone++) { blendedInterface->SetWeightedNode(sv.Bone[bone], sv.Weight[bone], bone==0); } // UI bonus: lock it blendedInterface->LockVertex(TRUE); // release localData->ReleaseVertexInterface(blendedInterface); } } } } // release physiqueInterface->ReleaseContextInterface(localData); } // release skin->ReleaseInterface(I_PHYIMPORT, physiqueInterface); } return ok; }