BulletShape CreatePhysicalHull(BSScene physicsScene, BSPhysObject prim, UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { BulletShape newShape = new BulletShape(); IMesh meshData; List<List<OMV.Vector3>> allHulls = null; lock (physicsScene.mesher) { // Pass true for physicalness as this prevents the creation of bounding box which is not needed meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, true /* isPhysical */, false /* shouldCache */); // If we should use the asset's hull info, fetch it out of the locked mesher if (meshData != null && BSParam.ShouldUseAssetHulls) { Meshmerizer realMesher = physicsScene.mesher as Meshmerizer; if (realMesher != null) { allHulls = realMesher.GetConvexHulls(size); } if (allHulls == null) { physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,assetHulls,noAssetHull", prim.LocalID); } } } // If there is hull data in the mesh asset, build the hull from that if (allHulls != null && BSParam.ShouldUseAssetHulls) { int hullCount = allHulls.Count; shapeInfo.HullCount = hullCount; int totalVertices = 1; // include one for the count of the hulls // Using the structure described for HACD hulls, create the memory structure // to pass the hull data to the creater. foreach (List<OMV.Vector3> hullVerts in allHulls) { totalVertices += 4; // add four for the vertex count and centroid totalVertices += hullVerts.Count*3; // one vertex is three dimensions } float[] convHulls = new float[totalVertices]; convHulls[0] = (float) hullCount; int jj = 1; int hullIndex = 0; foreach (List<OMV.Vector3> hullVerts in allHulls) { convHulls[jj + 0] = hullVerts.Count; convHulls[jj + 1] = 0f; // centroid x,y,z convHulls[jj + 2] = 0f; convHulls[jj + 3] = 0f; jj += 4; foreach (OMV.Vector3 oneVert in hullVerts) { convHulls[jj + 0] = oneVert.X; convHulls[jj + 1] = oneVert.Y; convHulls[jj + 2] = oneVert.Z; jj += 3; } shapeInfo.SetVerticesPerHull(hullIndex, hullVerts.Count); hullIndex++; } // create the hull data structure in Bullet newShape = physicsScene.PE.CreateHullShape(physicsScene.World, hullCount, convHulls); physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,AssetHulls,hulls={1},totVert={2},shape={3}", prim.LocalID, hullCount, totalVertices, newShape); } // If no hull specified in the asset and we should use Bullet's HACD approximation... if (!newShape.HasPhysicalShape && 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,bulletHACD,entry", prim.LocalID); var meshShape = BSShapeMesh.GetReference(physicsScene, true, prim); if (meshShape.physShapeInfo.HasPhysicalShape) { HACDParams parms = new HACDParams(); 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); parms.whichHACD = 0; // Use the HACD routine that comes with Bullet 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,shape={1}", prim.LocalID, newShape); // Now done with the mesh shape. shapeInfo.HullCount = 1; BSShapeMesh maybeMesh = meshShape as BSShapeMesh; if (maybeMesh != null) shapeInfo.SetVerticesPerHull(0, maybeMesh.shapeInfo.Vertices); meshShape.Dereference(physicsScene); } physicsScene.DetailLog("{0},BSShapeHull.CreatePhysicalHull,bulletHACD,exit,hasBody={1}", prim.LocalID, newShape.HasPhysicalShape); } // If no other hull specifications, use our HACD hull approximation. if (!newShape.HasPhysicalShape && 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(); //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]); } // greythane - use the integer array instead of OS type vertex list // List<OMV.Vector3> vertices = meshData.getVertexList(); // foreach (OMV.Vector3 vv in vertices) // { // convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); // } var vertices = meshData.getVertexListAsFloat(); var vertexCount = vertices.Length / 3; for (int i = 0; i < vertexCount; i++) { convVertices.Add(new float3(vertices[3 * i + 0], vertices[3 * i + 1], vertices[3 * i + 2])); } 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.Length, 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; }
private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { BulletShape newShape = new BulletShape(); // 20131224 not used IntPtr hullPtr = IntPtr.Zero; HullDesc hullDesc; if (Hulls.TryGetValue(newHullKey, out hullDesc)) { // If the hull shape already has been created, just use the one shared instance. newShape = hullDesc.shape.Clone(); } else { // Build a new hull in the physical world. // Pass true for physicalness as this prevents the creation of bounding box which is not needed IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true /* isPhysical */); if (meshData != null) { int[] indices = meshData.getIndexListAsInt(); float[] vertices = meshData.getVertexListAsFloat(); //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.Length; ii++) { convIndices.Add(indices[ii]); } for (int ii = 0; ii < vertices.Length; ii += 3) { convVertices.Add(new float3(vertices[ii], vertices[ii + 1], vertices[ii + 2])); } 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 (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); DetailLog( "{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", BSScene.DetailLogZero, newHullKey, indices.Length, vertices.Length / 3, 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; }