Ejemplo n.º 1
0
        // 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;
        }
Ejemplo n.º 2
0
        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;
        }
Ejemplo n.º 3
0
        // 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;
        }
Ejemplo n.º 4
0
        public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
        {
            float lod;
            UInt64 newHullKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);

            BSShapeHull retHull;
            bool foundHull = false;
            lock (Hulls)
                foundHull = Hulls.TryGetValue (newHullKey, out retHull);

            if (foundHull)
            {
                // 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.AssetFailed())
                {
                    // If a mesh was what was created, remember the built shape for later sharing.
                    lock(Hulls)
                        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;
        }
Ejemplo n.º 5
0
 public override void Dereference(BSScene physicsScene)
 {
     lock (Hulls)
     {
         DecrementReference();
         physicsScene.DetailLog("{0},BSShapeHull.Dereference,shape={1}", BSScene.DetailLogZero, this);
         // TODO: schedule aging and destruction of unused meshes.
     }
 }
Ejemplo n.º 6
0
        public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
        {
            float lod;
            UInt64 newMeshKey = BSShape.ComputeShapeKey(prim.Size, prim.BaseShape, out lod);

            BSShapeMesh retMesh;
            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.AssetFailed())
                    {
                        // 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;
        }
Ejemplo n.º 7
0
        // Code that uses the mesher to create the index/vertices info for a trimesh shape.
        // This is used by the passed 'makeShape' call to create the Bullet mesh shape.
        // The actual build call is passed so this logic can be used by several of the shapes that use a
        //     simple mesh as their base shape.
        public static BulletShape CreatePhysicalMeshShape(BSScene physicsScene, BSPhysObject prim, UInt64 newMeshKey,
            PrimitiveBaseShape pbs, OMV.Vector3 size, float lod, CreateShapeCall makeShape)
        {
            BulletShape newShape = new BulletShape();

            IMesh meshData;
            lock (physicsScene.mesher)
            {
                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 = makeShape(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.FailedMeshing;
                    physicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim={1}", LogHeader,
                        UsefulPrimInfo(physicsScene, prim));
                    physicsScene.DetailLog("{0},BSShapeMesh.CreatePhysicalMesh,allDegenerate,key={1}", prim.LocalID,
                        newMeshKey);
                }
            }
            newShape.shapeKey = newMeshKey;

            return newShape;
        }
Ejemplo n.º 8
0
 // 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.
     }
 }
Ejemplo n.º 9
0
        public 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;
        }
Ejemplo n.º 10
0
        public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
        {
            float lod;
            UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);

            physicsScene.DetailLog("{0},BSShapeGImpact,getReference,newKey={1},size={2},lod={3}", prim.LocalID,
                newMeshKey.ToString("X"), prim.Size, lod);
            BSShapeGImpact retGImpact;

            lock (GImpacts)
            {
                if (GImpacts.TryGetValue(newMeshKey, out retGImpact))
                {
                    // Teh mesh has already been created. Return a new reference to same.
                    retGImpact.IncrementReference();
                }
                else
                {
                    retGImpact = new BSShapeGImpact(new BulletShape());
                    BulletShape newShape = retGImpact.CreatePhysicalGImpact(physicsScene, prim, newMeshKey,
                        prim.BaseShape, prim.Size, lod);

                    // Check to see if mesh was created (might require an asset).
                    newShape = VerifyMeshCreated(physicsScene, newShape, prim);
                    newShape.shapeKey = newMeshKey;
                    if (!newShape.isNativeShape || prim.AssetFailed())
                    {
                        // 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.
                        GImpacts.Add(newMeshKey, retGImpact);
                    }
                    retGImpact.physShapeInfo = newShape;
                }
            }
            return retGImpact;
        }
Ejemplo n.º 11
0
        // The creation of a mesh or hull can fail if an underlying asset is not available.
        // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
        //     and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
        //     The first case causes the asset to be fetched. The second case requires
        //     us to not loop forever.
        // Called after creating a physical mesh or hull. If the physical shape was created,
        //     just return.
        public static BulletShape VerifyMeshCreated(BSScene physicsScene, BulletShape newShape, BSPhysObject prim)
        {
            // If the shape was successfully created, nothing more to do
            if (newShape.HasPhysicalShape)
                return newShape;

            // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been
            //    fetched but we end up here again, the meshing of the asset must have failed.
            // Prevent trying to keep fetching the mesh by declaring failure.
            if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched)
            {
                prim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedMeshing;
                physicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. prim={1}, texture={2}",
                    LogHeader, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
                physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,setFailed,prim={1},tex={2}",
                    prim.LocalID, UsefulPrimInfo(physicsScene, prim), prim.BaseShape.SculptTexture);
            }
            else
            {
                // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
                if (prim.BaseShape.SculptEntry
                    && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.FailedAssetFetch
                    && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting
                    && prim.BaseShape.SculptTexture != OMV.UUID.Zero
                )
                {
                    physicsScene.DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID);
                    // Multiple requestors will know we're waiting for this asset
                    prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting;

                    BSPhysObject xprim = prim;
                    Util.FireAndForget(delegate
                        {
                            BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
                            physicsScene.Scene.AssetService.Get(yprim.BaseShape.SculptTexture.ToString(), null,
                                delegate(string id, Object sender, AssetBase asset)
                                {
                                    bool assetFound = false;
                                    string mismatchIDs = String.Empty; // DEBUG DEBUG
                                    if (asset != null && yprim.BaseShape.SculptEntry)
                                    {
                                        if (yprim.BaseShape.SculptTexture == asset.ID)
                                        {
                                            yprim.BaseShape.SculptData = asset.Data;
                                            // This will cause the prim to see that the filler shape is not the right
                                            //    one and try again to build the object.
                                            // No race condition with the normal shape setting since the rebuild is at taint time.
                                            yprim.ForceBodyShapeRebuild(false /* inTaintTime */);
                                            assetFound = true;
                                        }
                                        else
                                        {
                                            mismatchIDs = yprim.BaseShape.SculptTexture + "/" + asset.ID;
                                        }
                                    }
                                    if (assetFound)
                                        yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched;
                                    else
                                        yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.FailedAssetFetch;
                                    physicsScene.DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}",
                                        yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs);
                                });
                        });
                }
                else
                {
                    if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.FailedAssetFetch)
                    {
                        physicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}",
                            LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture);
                    }
                }
            }

            // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object.
            BSShape fillShape = BSShapeNative.GetReference(physicsScene, prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
            physicsScene.DetailLog("{0},BSShape.VerifyMeshCreated,boxTempShape", prim.LocalID);

            return fillShape.physShapeInfo;
        }
Ejemplo n.º 12
0
        public static BSShape GetReference(BSScene physicsScene, bool forceRebuild, BSPhysObject prim)
        {
            float lod;
            UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);

            physicsScene.DetailLog("{0},BSShapeConvexHull,getReference,newKey={1},size={2},lod={3}",
                prim.LocalID, newMeshKey.ToString("X"), prim.Size, lod);
            BSShapeConvexHull retConvexHull;

            bool foundMesh = false;
            lock (ConvexHulls) {
                foundMesh = ConvexHulls.TryGetValue (newMeshKey, out retConvexHull);
            }
             
            if (foundMesh)
            {
                // The mesh has already been created. Return a new reference to same.
                retConvexHull.IncrementReference();
            }
            else
            {
                retConvexHull = new BSShapeConvexHull(new BulletShape());
                BulletShape convexShape;

                // Get a handle to a mesh to buld 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;
                    lock (ConvexHulls)
                        ConvexHulls.Add(convexShape.shapeKey, retConvexHull);
                    physicsScene.DetailLog("{0},BSShapeConvexHull.GetReference,addingNewlyCreatedShape,shape={1}",
                                           BSScene.DetailLogZero, convexShape);
                }

                // Done with the base mesh
                baseMesh.Dereference(physicsScene);
                retConvexHull.physShapeInfo = convexShape;
            }

            return retConvexHull;
        }
Ejemplo n.º 13
0
 // Sometimes we have a pointer to a collision shape but don't know what type it is.
 // Figure out type and call the correct dereference routine.
 // Called at taint-time.
 void DereferenceAnonCollisionShape(BSScene physicsScene, BulletShape pShape)
 {
     // TODO: figure a better way to go through all the shape types and find a possible instance.
     physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,shape={1}",
         BSScene.DetailLogZero, pShape);
     BSShapeMesh meshDesc;
     if (BSShapeMesh.TryGetMeshByPtr(pShape, out meshDesc))
     {
         meshDesc.Dereference(physicsScene);
         // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundMesh,shape={1}", BSScene.DetailLogZero, pShape);
     }
     else
     {
         BSShapeHull hullDesc;
         if (BSShapeHull.TryGetHullByPtr(pShape, out hullDesc))
         {
             hullDesc.Dereference(physicsScene);
             // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundHull,shape={1}", BSScene.DetailLogZero, pShape);
         }
         else
         {
             BSShapeConvexHull chullDesc;
             if (BSShapeConvexHull.TryGetConvexHullByPtr(pShape, out chullDesc))
             {
                 chullDesc.Dereference(physicsScene);
                 // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundConvexHull,shape={1}", BSScene.DetailLogZero, pShape);
             }
             else
             {
                 BSShapeGImpact gImpactDesc;
                 if (BSShapeGImpact.TryGetGImpactByPtr(pShape, out gImpactDesc))
                 {
                     gImpactDesc.Dereference(physicsScene);
                     // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,foundgImpact,shape={1}", BSScene.DetailLogZero, pShape);
                 }
                 else
                 {
                     // Didn't find it in the lists of specific types. It could be compound.
                     BSShapeCompound compoundDesc;
                     if (TryGetCompoundByPtr(pShape, out compoundDesc))
                     {
                         compoundDesc.Dereference(physicsScene);
                         // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,recursiveCompoundShape,shape={1}", BSScene.DetailLogZero, pShape);
                     }
                     else
                     {
                         // If none of the above, maybe it is a simple native shape.
                         if (physicsScene.PE.IsNativeShape(pShape))
                         {
                             // physicsScene.DetailLog("{0},BSShapeCompound.DereferenceAnonCollisionShape,assumingNative,shape={1}", BSScene.DetailLogZero, pShape);
                             BSShapeNative nativeShape = new BSShapeNative(pShape);
                             nativeShape.Dereference(physicsScene);
                         }
                         else
                         {
                             physicsScene.Logger.WarnFormat(
                                 "{0} DereferenceAnonCollisionShape. Did not find shape. {1}",
                                 LogHeader, pShape);
                         }
                     }
                 }
             }
         }
     }
 }
Ejemplo n.º 14
0
        // Dereferencing a compound shape releases the hold on all the child shapes.
        public override void Dereference(BSScene physicsScene)
        {
            lock (physShapeInfo)
            {
                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);
                    }

                    lock (CompoundShapes) CompoundShapes.Remove(physShapeInfo.AddrString);
                    physicsScene.PE.DeleteCollisionShape(physicsScene.World, physShapeInfo);
                }
            }
        }
Ejemplo n.º 15
0
        // 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);
        }
Ejemplo n.º 16
0
        // 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);
        }