// Dereferencing a compound shape releases the hold on all the child shapes. public override void Dereference(BSScene physicsScene) { lock (physShapeInfo) { this.DecrementReference(); physicsScene.DetailLog("{0},BSShapeCompound.Dereference,shape={1}", BSScene.DetailLogZero, this); if (referenceCount <= 0) { if (!physicsScene.PE.IsCompound(physShapeInfo)) { // Failed the sanity check!! physicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", LogHeader, physShapeInfo.shapeType, physShapeInfo.AddrString); physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", BSScene.DetailLogZero, physShapeInfo.shapeType, physShapeInfo.AddrString); return; } int numChildren = physicsScene.PE.GetNumberOfCompoundChildren(physShapeInfo); physicsScene.DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, physShapeInfo, numChildren); // Loop through all the children dereferencing each. for (int ii = numChildren - 1; ii >= 0; ii--) { BulletShape childShape = physicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(physShapeInfo, ii); DereferenceAnonCollisionShape(physicsScene, childShape); } physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); } } }
private BSShapeNative(BSScene physicsScene, BSPhysObject prim, BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) { ShapeData nativeShapeData = new ShapeData(); nativeShapeData.Type = shapeType; nativeShapeData.ID = prim.LocalID; nativeShapeData.Scale = prim.Scale; nativeShapeData.Size = prim.Scale; nativeShapeData.MeshKey = (ulong)shapeKey; nativeShapeData.HullKey = (ulong)shapeKey; if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) { ptr = BulletSimAPI.BuildCapsuleShape2(physicsScene.World.ptr, 1f, 1f, prim.Scale); physicsScene.DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); } else { ptr = BulletSimAPI.BuildNativeShape2(physicsScene.World.ptr, nativeShapeData); } if (ptr == null) { physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", LogHeader, prim.LocalID, shapeType); } type = shapeType; key = (UInt64)shapeKey; }
public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { float lod; System.UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); BSShapeHull retHull = null; lock (Hulls) { if (Hulls.TryGetValue(newHullKey, out retHull)) { // The mesh has already been created. Return a new reference to same. retHull.IncrementReference(); } else { retHull = new BSShapeHull(new BulletShape()); // An instance of this mesh has not been created. Build and remember same. BulletShape newShape = retHull.CreatePhysicalHull(physicsScene, prim, newHullKey, prim.BaseShape, prim.Size, lod); // Check to see if hull was created (might require an asset). newShape = VerifyMeshCreated(physicsScene, newShape, prim); if (!newShape.isNativeShape || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) { // If a mesh was what was created, remember the built shape for later sharing. Hulls.Add(newHullKey, retHull); } retHull.physShapeInfo = newShape; } } physicsScene.DetailLog("{0},BSShapeHull,getReference,hull={1},size={2},lod={3}", prim.LocalID, retHull, prim.Size, lod); return(retHull); }
private static BulletShape CreatePhysicalNativeShape(BSScene physicsScene, BSPhysObject prim, BSPhysicsShapeType shapeType, FixedShapeKey shapeKey) { BulletShape newShape; ShapeData nativeShapeData = new ShapeData(); nativeShapeData.Type = shapeType; nativeShapeData.ID = prim.LocalID; nativeShapeData.Scale = prim.Scale; nativeShapeData.Size = prim.Scale; nativeShapeData.MeshKey = (ulong)shapeKey; nativeShapeData.HullKey = (ulong)shapeKey; if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) { newShape = physicsScene.PE.BuildCapsuleShape(physicsScene.World, 1f, 1f, prim.Scale); physicsScene.DetailLog("{0},BSShapeNative,capsule,scale={1}", prim.LocalID, prim.Scale); } else { newShape = physicsScene.PE.BuildNativeShape(physicsScene.World, nativeShapeData); } if (!newShape.HasPhysicalShape) { physicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", LogHeader, prim.LocalID, shapeType); } newShape.shapeType = shapeType; newShape.isNativeShape = true; newShape.shapeKey = (UInt64)shapeKey; return(newShape); }
// Make this reference to the physical shape go away since native shapes are not shared. public override void Dereference(BSScene physicsScene) { // Native shapes are not tracked and are released immediately physicsScene.DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); BulletSimAPI.DeleteCollisionShape2(physicsScene.World.ptr, ptr); ptr = null; // Garbage collection will free up this instance. }
// Dereferencing a compound shape releases the hold on all the child shapes. public override void Dereference(BSScene physicsScene) { lock (ConvexHulls) { this.DecrementReference(); physicsScene.DetailLog("{0},BSShapeConvexHull.Dereference,shape={1}", BSScene.DetailLogZero, this); // TODO: schedule aging and destruction of unused meshes. } }
// Make this reference to the physical shape go away since native shapes are not shared. public override void Dereference(BSScene physicsScene) { // Native shapes are not tracked and are released immediately lock (physShapeInfo) { if (physShapeInfo.HasPhysicalShape) { physicsScene.DetailLog("{0},BSShapeNative.Dereference,deleteNativeShape,shape={1}", BSScene.DetailLogZero, this); physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo); } physShapeInfo.Clear(); // Garbage collection will free up this instance. } }
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); }
public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim) { float lod; System.UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod); BSShapeMesh retMesh = null; lock (Meshes) { if (Meshes.TryGetValue(newMeshKey, out retMesh)) { // The mesh has already been created. Return a new reference to same. retMesh.IncrementReference(); } else { retMesh = new BSShapeMesh(new BulletShape()); // An instance of this mesh has not been created. Build and remember same. BulletShape newShape = retMesh.CreatePhysicalMesh(physicsScene, prim, newMeshKey, prim.BaseShape, prim.Size, lod); // Check to see if mesh was created (might require an asset). newShape = VerifyMeshCreated(physicsScene, newShape, prim); if (!newShape.isNativeShape || prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) { // If a mesh was what was created, remember the built shape for later sharing. // Also note that if meshing failed we put it in the mesh list as there is nothing else to do about the mesh. Meshes.Add(newMeshKey, retMesh); } retMesh.physShapeInfo = newShape; } } physicsScene.DetailLog("{0},BSShapeMesh,getReference,mesh={1},size={2},lod={3}", prim.LocalID, retMesh, prim.Size, lod); return(retMesh); }
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). // Version that handles magnification. // Return 'true' if successfully created. public static bool ConvertHeightmapToMesh2(BSScene physicsScene, float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap int magnification, // number of vertices per heighmap step Vector3 extent, // dimensions of the output mesh Vector3 extentBase, // base to be added to all vertices out int indicesCountO, out int[] indicesO, out int verticesCountO, out float[] verticesO) { bool ret = false; int indicesCount = 0; int verticesCount = 0; int[] indices = new int[0]; float[] vertices = new float[0]; HeightMapGetter hmap = new HeightMapGetter(heightMap, sizeX, sizeY); // The vertices dimension of the output mesh int meshX = sizeX * magnification; int meshY = sizeY * magnification; // The output size of one mesh step float meshXStep = extent.X / meshX; float meshYStep = extent.Y / meshY; // Create an array of vertices that is meshX+1 by meshY+1 (note the loop // from zero to <= meshX). The triangle indices are then generated as two triangles // per heightmap point. There are meshX by meshY of these squares. The extra row and // column of vertices are used to complete the triangles of the last row and column // of the heightmap. try { // Vertices for the output heightmap plus one on the side and bottom to complete triangles int totalVertices = (meshX + 1) * (meshY + 1); vertices = new float[totalVertices * 3]; int totalIndices = meshX * meshY * 6; indices = new int[totalIndices]; if (physicsScene != null) { physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,inSize={1},outSize={2},totVert={3},totInd={4},extentBase={5}", BSScene.DetailLogZero, new Vector2(sizeX, sizeY), new Vector2(meshX, meshY), totalVertices, totalIndices, extentBase); } float minHeight = float.MaxValue; // Note that sizeX+1 vertices are created since there is land between this and the next region. // Loop through the output vertices and compute the mediun height in between the input vertices for (int yy = 0; yy <= meshY; yy++) { for (int xx = 0; xx <= meshX; xx++) // Hint: the "<=" means we go around sizeX + 1 times { float offsetY = (float)yy * (float)sizeY / (float)meshY; // The Y that is closest to the mesh point int stepY = (int)offsetY; float fractionalY = offsetY - (float)stepY; float offsetX = (float)xx * (float)sizeX / (float)meshX; // The X that is closest to the mesh point int stepX = (int)offsetX; float fractionalX = offsetX - (float)stepX; // physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,xx={1},yy={2},offX={3},stepX={4},fractX={5},offY={6},stepY={7},fractY={8}", // BSScene.DetailLogZero, xx, yy, offsetX, stepX, fractionalX, offsetY, stepY, fractionalY); // get the four corners of the heightmap square the mesh point is in float heightUL = hmap.GetHeight(stepX, stepY); float heightUR = hmap.GetHeight(stepX + 1, stepY); float heightLL = hmap.GetHeight(stepX, stepY + 1); float heightLR = hmap.GetHeight(stepX + 1, stepY + 1); // bilinear interplolation float height = heightUL * (1 - fractionalX) * (1 - fractionalY) + heightUR * fractionalX * (1 - fractionalY) + heightLL * (1 - fractionalX) * fractionalY + heightLR * fractionalX * fractionalY; // physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh2,heightUL={1},heightUR={2},heightLL={3},heightLR={4},heightMap={5}", // BSScene.DetailLogZero, heightUL, heightUR, heightLL, heightLR, height); minHeight = Math.Min(minHeight, height); vertices[verticesCount + 0] = (float)xx * meshXStep + extentBase.X; vertices[verticesCount + 1] = (float)yy * meshYStep + extentBase.Y; vertices[verticesCount + 2] = height + extentBase.Z; verticesCount += 3; } } // The number of vertices generated verticesCount /= 3; // Loop through all the heightmap squares and create indices for the two triangles for that square for (int yy = 0; yy < meshY; yy++) { for (int xx = 0; xx < meshX; xx++) { int offset = yy * (meshX + 1) + xx; // Each vertices is presumed to be the upper left corner of a box of two triangles indices[indicesCount + 0] = offset; indices[indicesCount + 1] = offset + 1; indices[indicesCount + 2] = offset + meshX + 1; // accounting for the extra column indices[indicesCount + 3] = offset + 1; indices[indicesCount + 4] = offset + meshX + 2; indices[indicesCount + 5] = offset + meshX + 1; indicesCount += 6; } } ret = true; } catch (Exception e) { if (physicsScene != null) { physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}", LogHeader, physicsScene.RegionName, extentBase, e); } } indicesCountO = indicesCount; indicesO = indices; verticesCountO = verticesCount; verticesO = vertices; return(ret); }
// Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). // Return 'true' if successfully created. public static bool ConvertHeightmapToMesh(BSScene physicsScene, float[] heightMap, int sizeX, int sizeY, // parameters of incoming heightmap Vector3 extentBase, // base to be added to all vertices out int indicesCountO, out int[] indicesO, out int verticesCountO, out float[] verticesO) { bool ret = false; int indicesCount = 0; int verticesCount = 0; int[] indices = new int[0]; float[] vertices = new float[0]; // Simple mesh creation which assumes magnification == 1. // TODO: do a more general solution that scales, adds new vertices and smoothes the result. // Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop // from zero to <= sizeX). The triangle indices are then generated as two triangles // per heightmap point. There are sizeX by sizeY of these squares. The extra row and // column of vertices are used to complete the triangles of the last row and column // of the heightmap. try { // One vertice per heightmap value plus the vertices off the side and bottom edge. int totalVertices = (sizeX + 1) * (sizeY + 1); vertices = new float[totalVertices * 3]; int totalIndices = sizeX * sizeY * 6; indices = new int[totalIndices]; if (physicsScene != null) { physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3}", BSScene.DetailLogZero, totalVertices, totalIndices, extentBase); } float minHeight = float.MaxValue; // Note that sizeX+1 vertices are created since there is land between this and the next region. for (int yy = 0; yy <= sizeY; yy++) { for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times { int offset = yy * sizeX + xx; // Extend the height with the height from the last row or column if (yy == sizeY) { offset -= sizeX; } if (xx == sizeX) { offset -= 1; } float height = heightMap[offset]; minHeight = Math.Min(minHeight, height); vertices[verticesCount + 0] = (float)xx + extentBase.X; vertices[verticesCount + 1] = (float)yy + extentBase.Y; vertices[verticesCount + 2] = height + extentBase.Z; verticesCount += 3; } } verticesCount = verticesCount / 3; for (int yy = 0; yy < sizeY; yy++) { for (int xx = 0; xx < sizeX; xx++) { int offset = yy * (sizeX + 1) + xx; // Each vertices is presumed to be the upper left corner of a box of two triangles indices[indicesCount + 0] = offset; indices[indicesCount + 1] = offset + 1; indices[indicesCount + 2] = offset + sizeX + 1; // accounting for the extra column indices[indicesCount + 3] = offset + 1; indices[indicesCount + 4] = offset + sizeX + 2; indices[indicesCount + 5] = offset + sizeX + 1; indicesCount += 6; } } ret = true; } catch (Exception e) { if (physicsScene != null) { physicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh. For={1}/{2}, e={3}", LogHeader, physicsScene.RegionName, extentBase, e); } } indicesCountO = indicesCount; indicesO = indices; verticesCountO = verticesCount; verticesO = vertices; 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); }
private BulletShape CreatePhysicalMesh(BSScene physicsScene, BSPhysObject prim, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { BulletShape newShape = new BulletShape(); IMesh meshData = physicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, false, // say it is not physical so a bounding box is not built false // do not cache the mesh and do not use previously built versions ); 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(); int realIndicesIndex = indices.Length; float[] verticesAsFloats = meshData.getVertexListAsFloat(); if (BSParam.ShouldRemoveZeroWidthTriangles) { // Remove degenerate triangles. These are triangles with two of the vertices // are the same. This is complicated by the problem that vertices are not // made unique in sculpties so we have to compare the values in the vertex. realIndicesIndex = 0; for (int tri = 0; tri < indices.Length; tri += 3) { // Compute displacements into vertex array for each vertex of the triangle int v1 = indices[tri + 0] * 3; int v2 = indices[tri + 1] * 3; int v3 = indices[tri + 2] * 3; // Check to see if any two of the vertices are the same if (!((verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0] && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1] && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2]) || (verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0] && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1] && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2]) || (verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0] && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1] && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2])) ) { // None of the vertices of the triangles are the same. This is a good triangle; indices[realIndicesIndex + 0] = indices[tri + 0]; indices[realIndicesIndex + 1] = indices[tri + 1]; indices[realIndicesIndex + 2] = indices[tri + 2]; realIndicesIndex += 3; } } } physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,key={1},origTri={2},realTri={3},numVerts={4}", BSScene.DetailLogZero, newMeshKey.ToString("X"), indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); if (realIndicesIndex != 0) { newShape = physicsScene.PE.CreateMeshShape(physicsScene.World, realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats); } else { // Force the asset condition to 'failed' so we won't try to keep fetching and processing this mesh. prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; physicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim {1} at {2} in {3}", LogHeader, prim.PhysObjectName, prim.RawPosition, physicsScene.Name); physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,allDegenerate,key={1}", prim.LocalID, newMeshKey); } } newShape.shapeKey = newMeshKey; return(newShape); }