Exemple #1
0
/*
  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;
}