public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { float lod; System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); physicsScene.DetailLog("{0},BSShapeMesh,getReference,newKey={1},size={2},lod={3}", prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod); BSShapeConvexHull retConvexHull = null; lock (ConvexHulls) { if (ConvexHulls.TryGetValue(newMeshKey, out retConvexHull)) { // The mesh has already been created. Return a new reference to same. retConvexHull.IncrementReference(); } else { retConvexHull = new BSShapeConvexHull(new BulletShape()); BulletShape convexShape = null; // Get a handle to a mesh to build the hull from BSShape baseMesh = BSShapeMesh.GetReference(physicsScene, false /* forceRebuild */, prim); if (baseMesh.physShapeInfo.isNativeShape) { // We get here if the mesh was not creatable. Could be waiting for an asset from the disk. // In the short term, we return the native shape and a later ForceBodyShapeRebuild should // get back to this code with a buildable mesh. // TODO: not sure the temp native shape is freed when the mesh is rebuilt. When does this get freed? convexShape = baseMesh.physShapeInfo; } else { convexShape = physicsScene.PE.BuildConvexHullShapeFromMesh(physicsScene.World, baseMesh.physShapeInfo); convexShape.shapeKey = newMeshKey; ConvexHulls.Add(convexShape.shapeKey, retConvexHull); } // Done with the base mesh baseMesh.Dereference(physicsScene); retConvexHull.physShapeInfo = convexShape; } } return(retConvexHull); }
// return 'true' if the prim's shape was changed. private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) { bool ret = false; // Note that if it's a native shape, the check for physical/non-physical is not // made. Native shapes work in either case. if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) { // Use a simple, single mesh convex hull shape if the object is simple enough BSShape potentialHull = null; PrimitiveBaseShape pbs = prim.BaseShape; // Use a simple, one section convex shape for prims that are probably convex (no cuts or twists) if (BSParam.ShouldUseSingleConvexHullForPrims && pbs != null && !pbs.SculptEntry && PrimHasNoCuts(pbs) ) { potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim); } // Use the GImpact shape if it is a prim that has some concaveness if (potentialHull == null && BSParam.ShouldUseGImpactShapeForPrims && pbs != null && !pbs.SculptEntry ) { potentialHull = BSShapeGImpact.GetReference(m_physicsScene, false /* forceRebuild */, prim); } // If not any of the simple cases, just make a hull if (potentialHull == null) { potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); } // If the current shape is not what is on the prim at the moment, time to change. if (!prim.PhysShape.HasPhysicalShape || potentialHull.ShapeType != prim.PhysShape.ShapeType || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) { DereferenceExistingShape(prim, shapeCallback); prim.PhysShape = potentialHull; ret = true; } else { // The current shape on the prim is the correct one. We don't need the potential reference. potentialHull.Dereference(m_physicsScene); } if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape); } } else { // Non-physical objects should be just meshes. BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim); // If the current shape is not what is on the prim at the moment, time to change. if (!prim.PhysShape.HasPhysicalShape || potentialMesh.ShapeType != prim.PhysShape.ShapeType || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) { DereferenceExistingShape(prim, shapeCallback); prim.PhysShape = potentialMesh; ret = true; } else { // We don't need this reference to the mesh that is already being using. potentialMesh.Dereference(m_physicsScene); } if (DDetail) { DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape); } } return(ret); }
private BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { BulletShape newShape = new BulletShape(); IntPtr hullPtr = IntPtr.Zero; if (BSParam.ShouldUseBulletHACD) { // Build the hull shape from an existing mesh shape. // The mesh should have already been created in Bullet. physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,entry", prim.LocalID); BSShape meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); if (meshShape.physShapeInfo.HasPhysicalShape) { HACDParams parms; parms.maxVerticesPerHull = BSParam.BHullMaxVerticesPerHull; parms.minClusters = BSParam.BHullMinClusters; parms.compacityWeight = BSParam.BHullCompacityWeight; parms.volumeWeight = BSParam.BHullVolumeWeight; parms.concavity = BSParam.BHullConcavity; parms.addExtraDistPoints = BSParam.NumericBool(BSParam.BHullAddExtraDistPoints); parms.addNeighboursDistPoints = BSParam.NumericBool(BSParam.BHullAddNeighboursDistPoints); parms.addFacesPoints = BSParam.NumericBool(BSParam.BHullAddFacesPoints); parms.shouldAdjustCollisionMargin = BSParam.NumericBool(BSParam.BHullShouldAdjustCollisionMargin); physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,beforeCall", prim.LocalID, newShape.HasPhysicalShape); newShape = physicsScene.PE.BuildHullShapeFromMesh(physicsScene.World, meshShape.physShapeInfo, parms); physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,hullFromMesh,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); // Now done with the mesh shape. meshShape.Dereference(physicsScene); } physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,shouldUseBulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); } if (!newShape.HasPhysicalShape) { // Build a new hull in the physical world using the C# HACD algorigthm. // Pass true for physicalness as this prevents the creation of bounding box which is not needed IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); if (meshData != null) { if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) { // Release the fetched asset data once it has been used. pbs.SculptData = new byte[0]; prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Unknown; } int[] indices = meshData.getIndexListAsInt(); List <OMV.Vector3> vertices = meshData.getVertexList(); //format conversion from IMesh format to DecompDesc format List <int> convIndices = new List <int>(); List <float3> convVertices = new List <float3>(); for (int ii = 0; ii < indices.GetLength(0); ii++) { convIndices.Add(indices[ii]); } foreach (OMV.Vector3 vv in vertices) { convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); } uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) { // Simple primitive shapes we know are convex so they are better implemented with // fewer hulls. // Check for simple shape (prim without cuts) and reduce split parameter if so. if (BSShapeCollection.PrimHasNoCuts(pbs)) { maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; } } // setup and do convex hull conversion m_hulls = new List <ConvexResult>(); DecompDesc dcomp = new DecompDesc(); dcomp.mIndices = convIndices; dcomp.mVertices = convVertices; dcomp.mDepth = maxDepthSplit; dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); // create the hull into the _hulls variable convexBuilder.process(dcomp); physicsScene.DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); // Convert the vertices and indices for passing to unmanaged. // The hull information is passed as a large floating point array. // The format is: // convHulls[0] = number of hulls // convHulls[1] = number of vertices in first hull // convHulls[2] = hull centroid X coordinate // convHulls[3] = hull centroid Y coordinate // convHulls[4] = hull centroid Z coordinate // convHulls[5] = first hull vertex X // convHulls[6] = first hull vertex Y // convHulls[7] = first hull vertex Z // convHulls[8] = second hull vertex X // ... // convHulls[n] = number of vertices in second hull // convHulls[n+1] = second hull centroid X coordinate // ... // // TODO: is is very inefficient. Someday change the convex hull generator to return // data structures that do not need to be converted in order to pass to Bullet. // And maybe put the values directly into pinned memory rather than marshaling. int hullCount = m_hulls.Count; int totalVertices = 1; // include one for the count of the hulls foreach (ConvexResult cr in m_hulls) { totalVertices += 4; // add four for the vertex count and centroid totalVertices += cr.HullIndices.Count * 3; // we pass just triangles } float[] convHulls = new float[totalVertices]; convHulls[0] = (float)hullCount; int jj = 1; foreach (ConvexResult cr in m_hulls) { // copy vertices for index access float3[] verts = new float3[cr.HullVertices.Count]; int kk = 0; foreach (float3 ff in cr.HullVertices) { verts[kk++] = ff; } // add to the array one hull's worth of data convHulls[jj++] = cr.HullIndices.Count; convHulls[jj++] = 0f; // centroid x,y,z convHulls[jj++] = 0f; convHulls[jj++] = 0f; foreach (int ind in cr.HullIndices) { convHulls[jj++] = verts[ind].x; convHulls[jj++] = verts[ind].y; convHulls[jj++] = verts[ind].z; } } // create the hull data structure in Bullet newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); } newShape.shapeKey = newHullKey; } return(newShape); }