Universe.Physics.BulletSPlugin.BSTerrainMesh.ConvertHeightmapToMesh2 C# (CSharp) Method

ConvertHeightmapToMesh2() public static method

public static ConvertHeightmapToMesh2 ( BSScene physicsScene, float heightMap, int sizeX, int sizeY, int magnification, System.Vector3 extent, System.Vector3 extentBase, int &indicesCountO, int &indicesO, int &verticesCountO, float &verticesO ) : bool
physicsScene BSScene
heightMap float
sizeX int
sizeY int
magnification int
extent System.Vector3
extentBase System.Vector3
indicesCountO int
indicesO int
verticesCountO int
verticesO float
return bool
        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;
        }
    }

Usage Example

        // Create terrain mesh from a heightmap.
        public BSTerrainMesh(BSScene physicsScene, Vector3 regionBase, uint id, float[] initialMap,
                             Vector3 minCoords, Vector3 maxCoords)
            : base(physicsScene, regionBase, id)
        {
            int indicesCount;

            int[] indices;
            int   verticesCount;

            float[] vertices;

            m_savedHeightMap = initialMap;

            m_sizeX = (int)(maxCoords.X - minCoords.X);
            m_sizeY = (int)(maxCoords.Y - minCoords.Y);

            bool meshCreationSuccess = false;

            if (BSParam.TerrainMeshMagnification == 1)
            {
                // If a magnification of one, use the old routine that is tried and true.
                meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh(PhysicsScene,
                                                                           initialMap, m_sizeX, m_sizeY, // input size
                                                                           Vector3.Zero,                 // base for mesh
                                                                           out indicesCount, out indices, out verticesCount, out vertices);
            }
            else
            {
                // Other magnifications use the newer routine
                meshCreationSuccess = BSTerrainMesh.ConvertHeightmapToMesh2(PhysicsScene,
                                                                            initialMap, m_sizeX, m_sizeY, // input size
                                                                            BSParam.TerrainMeshMagnification,
                                                                            physicsScene.TerrainManager.WorldMax,
                                                                            Vector3.Zero, // base for mesh
                                                                            out indicesCount, out indices, out verticesCount, out vertices);
            }
            if (!meshCreationSuccess)
            {
                // DISASTER!!
                PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedConversionOfHeightmap,id={1}",
                                       BSScene.DetailLogZero, ID);
                PhysicsScene.Logger.ErrorFormat("{0} Failed conversion of heightmap to mesh! base={1}", LogHeader,
                                                TerrainBase);
                // Something is very messed up and a crash is in our future.
                return;
            }

            PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshid,id={1},indices={2},indSz={3},vertices={4},vertSz={5}",
                                   BSScene.DetailLogZero, ID, indicesCount, indices.Length, verticesCount, vertices.Length);

            m_terrainShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, indicesCount, indices, verticesCount,
                                                             vertices);

            if (!m_terrainShape.HasPhysicalShape)
            {
                // DISASTER!!
                PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape,id={1}", BSScene.DetailLogZero,
                                       ID);
                PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain mesh! base={1}", LogHeader, TerrainBase);
                // Something is very messed up and a crash is in our future.
                return;
            }

            Vector3    pos = regionBase;
            Quaternion rot = Quaternion.Identity;

            m_terrainBody = PhysicsScene.PE.CreateBodyWithDefaultMotionState(m_terrainShape, ID, pos, rot);
            if (!m_terrainBody.HasPhysicalBody)
            {
                // DISASTER!!
                PhysicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
                // Something is very messed up and a crash is in our future.
                return;
            }

            physicsScene.PE.SetShapeCollisionMargin(m_terrainShape, BSParam.TerrainCollisionMargin);

            // Set current terrain attributes
            PhysicsScene.PE.SetFriction(m_terrainBody, BSParam.TerrainFriction);
            PhysicsScene.PE.SetHitFraction(m_terrainBody, BSParam.TerrainHitFraction);
            PhysicsScene.PE.SetRestitution(m_terrainBody, BSParam.TerrainRestitution);
            PhysicsScene.PE.SetContactProcessingThreshold(m_terrainBody, BSParam.TerrainContactProcessingThreshold);
            PhysicsScene.PE.SetCollisionFlags(m_terrainBody, CollisionFlags.CF_STATIC_OBJECT);

            // Static objects are not very massive.
            PhysicsScene.PE.SetMassProps(m_terrainBody, 0.1f, Vector3.Zero);

            // Put the new terrain to the world of physical objects
            PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, m_terrainBody);

            // Redo its bounding box now that it is in the world
            PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, m_terrainBody);

            m_terrainBody.collisionType = CollisionType.Terrain;
            m_terrainBody.ApplyCollisionMask(PhysicsScene);

            if (BSParam.UseSingleSidedMeshes)
            {
                PhysicsScene.DetailLog("{0},BSTerrainMesh.settingCustomMaterial,id={1}", BSScene.DetailLogZero, id);
                PhysicsScene.PE.AddToCollisionFlags(m_terrainBody, CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
            }

            // Make it so the terrain will not move or be considered for movement.
            PhysicsScene.PE.ForceActivationState(m_terrainBody, ActivationState.DISABLE_SIMULATION);
        }