Generating terrain from bitmap

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
jaja1
Posts: 43
Joined: Sat Nov 15, 2014 3:27 am

Generating terrain from bitmap

Post by jaja1 »

So I decided to switch from XNA's heightmap demo to BEPUs Terrain class for my ground.
For some reason server-side, BEPU renders a very low quality model of my heightmap. I am using brightness information from a 32bit tif file that I generated in Blender. Client side I am using unity's terrain as the display version. Even when downgrading to 16 bit png, client-side everything renders nicely. Server-side clearly something went wrong because when my player (sphere) moves along the map I get HUGE jumps as I move up slopes. At first I thought my heightmap was inverted but when I down scaled on the y-axis I noticed those jumps instead of the player just sinking through hills.

Here is the code I use to generate my terrain:

Code: Select all

public static Terrain CreateFromBitmap(string path_to_config, string path_to_bmp, Vector3 posOffset)
        {
            Bitmap heightmap = new Bitmap(path_to_bmp);

            int width, length;
            float xScale = 1, zScale = 1, heightScale = 1;

            #region Get heightmap settings from config file

            XmlDocument config = new XmlDocument();
            config.Load(path_to_config);
            
            width = Convert.ToInt32(config.SelectSingleNode("Config/Width").InnerText);
            length = Convert.ToInt32(config.SelectSingleNode("Config/Length").InnerText);
            xScale = Convert.ToSingle(config.SelectSingleNode("Config/xScale").InnerText);
            zScale = Convert.ToSingle(config.SelectSingleNode("Config/zScale").InnerText);
            heightScale = Convert.ToSingle(config.SelectSingleNode("Config/yScale").InnerText);

            if (width <= 0 || length <= 0 || xScale <= 0 || zScale <= 0 || heightScale <= 0)
                return null;

            #endregion

            float[,] heights = new float[width, length];

            //populate heights array based on pixel brightness levels
            for (int x = 0; x < width; x++)
            {
                for (int z = 0; z < length; z++)
                {
                    heights[x, z] = heightmap.GetPixel(x, z).GetBrightness();
                }
            }

            Terrain terrain = new Terrain(heights, new AffineTransform(new Vector3(-xScale, heightScale, -zScale),
                                                   Quaternion.Identity,
                                                   new Vector3(posOffset.X, posOffset.Y, posOffset.Z)));
            terrain.Shape.QuadTriangleOrganization = BEPUphysics.CollisionShapes.QuadTriangleOrganization.BottomRightUpperLeft;
            terrain.Thickness = 1;

            return terrain;
        }
Can you also please explain in as much detail as possible what the scaling parameter in the Affine transform does to the Terrain? I changed the parameters in the terrain demo but got some very unexpected results. For example it doesn't make sense to me why the x and z scale factors need to be negative. Making them positive creates some weird, hard to describe terrain.

Another thing I noted is my player doesn't "sit" as expected on the terrain. For example my player is a sphere with radius 0.5, my y-position would be something like 0.498... even on ground defined to be at the 0 level (pure black on the heightmap). This wasn't a problem using XNA.

Final question...if I spawn an entity at the level of the terrain would it fall through? For example say I have a completely flat terrain (a plane at y =0). I spawn a cube of dimensions (1,1,1) with its center 0.5 units above the terrain. Will it fall through? Do I need to spawn it slightly above the terrain so it falls into place?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Generating terrain from bitmap

Post by Norbo »

Can you also please explain in as much detail as possible what the scaling parameter in the Affine transform does to the Terrain? I changed the parameters in the terrain demo but got some very unexpected results. For example it doesn't make sense to me why the x and z scale factors need to be negative. Making them positive creates some weird, hard to describe terrain.
The terrain has a 'local' space where every vertex is a single unit from its neighbors, the height is unscaled, there is no shearing, there is no rotation, and so on. The transform you supply just transforms it from that local space into world space.

The scales do not need to be negative. Making them positive just flips the terrain over its origin. Since that origin is at a corner of the terrain, it will appear to move. Adjusting the translation component of the transform can compensate for the movement.

The weirdness you see may be strictly graphical. Flipped cull modes, for example.

(As for why it is negative in the demo, I think I just did that to confirm that it works properly many moons ago and never changed it back. :))
Another thing I noted is my player doesn't "sit" as expected on the terrain. For example my player is a sphere with radius 0.5, my y-position would be something like 0.498... even on ground defined to be at the 0 level (pure black on the heightmap). This wasn't a problem using XNA.
Some variance is expected. There is a slop region where no penetration recovery force will be applied, CollisionDetectionSettings.AllowedPenetration. It defaults to 0.01. It helps avoid the jitter caused by contacts constantly popping into and out of existence.

This behavior is independent from XNA or any other rendering pipeline- the physics engine has no dependency on the rendering side of things.

(Technically, the solver uses speculative contacts that could do away with the slop region, but some contact generation routines can't quite handle it yet. This is something that should change in v2.)
Final question...if I spawn an entity at the level of the terrain would it fall through? For example say I have a completely flat terrain (a plane at y =0). I spawn a cube of dimensions (1,1,1) with its center 0.5 units above the terrain. Will it fall through? Do I need to spawn it slightly above the terrain so it falls into place?
If the terrain has zero thickness, it may fall through if positioned exactly on the triangle boundary, so positioning it above is a good idea. You can also set the Terrain.Thickness to some nonzero value to catch stuff that would otherwise fall through, but doing so isn't completely free.
jaja1
Posts: 43
Joined: Sat Nov 15, 2014 3:27 am

Re: Generating terrain from bitmap

Post by jaja1 »

Thanks for the fast reply! That answers most of my questions. Still not sure why my player sinks through hills though..that part still has me stumped? Am I missing something when generating the terrain?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Generating terrain from bitmap

Post by Norbo »

Do the graphics actually match the physics? It might appear to be stuck in the ground, but the graphics might just be above the physical representation.

If not that, is the character being controlled with teleportation? That is, is it being moved around by setting its position directly? Collisions can't slow down or stop a teleporting object. Using velocities or other physical control would fix this problem.
jaja1
Posts: 43
Joined: Sat Nov 15, 2014 3:27 am

Re: Generating terrain from bitmap

Post by jaja1 »

Norbo wrote:Do the graphics actually match the physics? It might appear to be stuck in the ground, but the graphics might just be above the physical representation.

If not that, is the character being controlled with teleportation? That is, is it being moved around by setting its position directly? Collisions can't slow down or stop a teleporting object. Using velocities or other physical control would fix this problem.
Well I know the graphics definitely does not match the physics, the question is why. The sphere seems to roll into an undulation as if the hills were "flipped" so to speak. I tried flipping the y scaling on the affine transform but that seems to move the terrain in an unexpected manner and I simply "fall through" the rendered ground client side. I am not teleporting the player either. I set the linear velocity when translating and use a transformation matrix to "turn" the velocity vector if I need to rotate the player.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Generating terrain from bitmap

Post by Norbo »

Is the problem occurring at a small scale, like within a single terrain quad? The graphical layout of triangles may not match the physical layout. The passed-in QuadTriangleOrganization can be used to flip the order, but the graphics might be doing something the physics can't match.

If the error occurs over a larger area, are the transforms consistent? For example, if the graphical terrain's local origin is in the center of the terrain and the graphics are transformed using the terrain's world transform, they won't match up because the terrain's local origin is at a corner. In order to use the terrain's world transform for graphics, the graphical representation must have the same form in local space as the physical representation.

Regarding flipping y scale, it makes up point down, so the triangle faces point downward, so stuff falls through.
jaja1
Posts: 43
Joined: Sat Nov 15, 2014 3:27 am

Re: Generating terrain from bitmap

Post by jaja1 »

Ha! Solved it! It was a transform issue. I thought that BEPU's world axes were not consistent with Unity's (which XNA's apparently were and required offsetting). I set the offset to Vector.Zero and the scaling to (1,1,1) and everything worked absolutely perfectly. I admit that BEPU does a much better job at generating terrains from heightmaps than XNA did. Despite the slop regions, the sphere seems to roll around perfectly along the surface. I even managed to get it working in the demos. If you'd like to add a pixel generated heightmap section for future users.

Just use a bitmap of your choice and increase the y scaling as needed if the terrain seems a bit flat. Of course in the demos the size of the bitmap you can use will be limited. I found 32 bit, per channel pixel order, no compression, windows, tif files to work best.

Code: Select all

System.Drawing.Bitmap heightmap = new System.Drawing.Bitmap(@"PATH_TO_TIF");

            var heights = new float[xLength, zLength];
            for (int i = 0; i < xLength; i++)
            {
                for (int j = 0; j < zLength; j++)
                {
                    heights[i, j] = heightmap.GetPixel(i, j).GetBrightness();
                }
            }

            /*
            for (int i = 0; i < xLength; i++)
            {
                for (int j = 0; j < zLength; j++)
                {
                    float x = i - xLength / 2;
                    float z = j - zLength / 2;
                    //heights[i,j] = (float)(x * y / 1000f);
                    heights[i, j] = (float)(10 * (Math.Sin(x / 8) + Math.Sin(z / 8)));
                    //heights[i,j] = 3 * (float)Math.Sin(x * z / 100f);
                    //heights[i,j] = (x * x * x * y - y * y * y * x) / 1000f;
                }
            }
            */
Post Reply