HeightMap Vertex Detail?

Thinking about the BABYLON.Mesh.CreateGroundFromHeightMap function, and how my height map has large area’s that are the same height (shade of gray)… These area’s collect a lot of vertices that probably should be optimized away OR redistributed to the more detailed area’s of the height map automatically.

For instance, mountain ridges, or coastal contour could really use some more detail that is already in the height map.

Are there any clever options or utilities to do this?

1 Like

Hi @artfiedler, welcome to the BJS forum!

You might want to look at mesh simplification/optimization options here: Babylon.js Documentation

You can look at the demo playground provided, there, or have a look at a later version that I “tweaked” at bit (see line 21 - lots of parameters to set).

This simplify command is happening within the “callback” (like an onFinished) of the CreateGroundFromHeightMap function. This is because heightMap file-retrieval and subsequent heightMap plotting… takes some time. The simplify command must wait for those things to finish… before it starts its processing.

It seems to be reducing vert-counts on the flat-lands and lonesome prairies. :slight_smile: It COULD be attacking the mountains and valleys, too. It also seems to leave the back edge and right edge… “jagged”. I’m not sure why it does that.

I don’t understand the code/principle, but maybe with some parameter tweaking, you can tell it to ignore the mountains and valleys, and only optimize the flat-lands.

Playground in-code search for ‘simplify’ gives 29 returns… might be worth touring a few playgrounds.

It’s a start. :slight_smile: Be sure to ask more questions about it, as needed (here in this topic thread). I think there are mesh-opti experts nearby. Teach us things you learn, too, eh? thx.

Ok ill take a look, i was hoping for something that worked from the create function that i didnt know about. And yes im looking to eliminate the jagged shorelines etc… ideally instead of evenly placing the vert it throws more in the areas that need it.

Maybe a function that evaluates the pixels neighbors and tags the area with how many pixel differences are in the area and distribute the vert based on that weight

1 Like

Yeah, you have the right ideas… about optimizing/culling/LOD.

Do you NEED to be “dynamic”? Could you build your terrain in Blender modeler, and bring it in from there?

If so, there might be some SERIOUS optimizers in Blender, and/or FOR Blender models.

Did you read enough of those docs… to notice that you can code-up YOUR OWN simplify formulas/algorithms? That’s WAY over my head, but… perhaps there are various formulas that can be found on the web… that can be adapted to our “write your own” system.

I know, that’s pretty heavy.

I kind-of LIKE your idea… of an “optimizeLevel” parameter… in the constructor. hmm. I think that is an idea worth further pondering.

Hopefully, we get more comments.

Also, you could grab a copy of createGroundFromHeightMap code… from the BJS VertexData object… and put it into your code.

Then, you could modify it, adding your own simplify parameter or code-call… behind the scenes. https://www.babylonjs-playground.com/#ON8FM#10

Lines 2-90… in the playground/scene, ready to abuse. :slight_smile:

Your own “custom” createGroundFromHeightMap generator. See console for proof that BJS is using the custom func instead of its default version. shrug. It’s another weird option.

I borrowed the code… from here.

If you code-up a really good optimizer and its parameter, you could get famous… when we use YOUR custom func… as the NEW createGroundFromHeightMap in BJS v5! (if you allow it)

WOW! Superstar status! Core-Contributor!

What’s that you say? Not ALL the createGroundFromHeightMap code… is inside the #10 playground?

Yep, ya caught me. The BJS createGroundFromHeightMap() is actually a 3-object, 3-function call.
https://www.babylonjs-playground.com/#ON8FM#12

First, mesh.createGroundFromHeightMap(), which calls meshBuilder.createGroundFromHeightMap(), which calls VertexData.createGroundFromHeightMap(). The #12 playground has all 3… ready to customize/modify.

TMI, eh? I hear ya. :smiley:

Lol. Thx yeah, I was thinking about this morning modifying the create. In my experience it might be too slow to do it the proper way on the cpu. I was reading and saw that some people were using webgl shaders to execute math code on the gpu, I’ll have to take a look at that as that would be the way to go, I’ve programmed something similar in the past but it was for 2d image processing on the gpu using cuda code

@Wingnut I modified your sample
https://www.babylonjs-playground.com/#ON8FM#10
with this version
https://www.babylonjs-playground.com/#ON8FM#14

It seems I’m able to detect and remove the vertices that appear flat which are next to included vertices of the same height. However I seem to be getting the indices wrong for filling the area’s in.

Initially this seems fast running on my cpu

1 Like

Wow! Coooooool!

There is something you might not yet know… and might be bad news for you.

Any “face” (a triangle area) whose 3 vertices are connected by indices… in CLOCKWISE manner… have their “front face” aimed in ONE direction. When connected counter-clockwise, the front-face and back-face aim in the OTHER direction. I think this is standard webGL/openGL GPU operations, and not something BabylonJS can adjust.

A groundMaterial.backFaceCulling = false; doesn’t seem to fill-in any holes in the your terrain, but I thought you should know about indices-connecting order, just the same.

In your case, the “holes” are likely faces whose 3 vertices… are not “triangle-complete”. One or more of the 3 indices connecting the 3 verts… is missing. (You probably know this, already, eh? Sorry)

Thanks for sharing and discussing your new playground and ideas. Stay tuned for more comments (including from you). :slight_smile:

I didn’t know that, but it doesn’t seem to apply here. I hadn’t modified the order in which they are connected just seems to try to connect to the next available vertex to the right and to the bottom right. Maybe I need to change it to allow bottom left as well. I didn’t think that through much just more so testing the speed of finding the vertices.

The way I figure it is finding the vertices to eliminate is step1. Now we have a count of vertices we could add to area’s needing more detail. Step2 I figure is detecting or defining them, however detecting those area’s might be a bit complicated. Defining them I was thinking maybe convert the height map to a rgb image and set green to the height map, r to the contour detail priority and b to the height detail priority. So any extra vertex areas could be defined in the height map manually.

Reason I would think about doing this verse just manually fixing the model in blender, is so if in the future the height map is tweaked the tweaks made in blender aren’t trashed

1 Like

Nod. You COULD do “patchwork”, too. Make MANY heightMapped grounds… piece them together in the scene. High-detail mountains and valleys get high-resolution grounds (high subdivs). Flat-lands are low-rez… low subdivs.

When patchwork is done, parent them all to a single “master terrain gizmo”, perhaps. Or, meshMerge them.

hmm. I also think underground caves are created this way. The cave is a tube-like mesh with a flat “flange” on the top. That flange acts like any other terrain “patch”… once positioned correctly.

I’m not sure, though. I don’t work-with terrains, too often. I work in the noob-assistance division, and you have wandered far-away-from noob-land. :smiley:

Your idea has potential as well, if I placed two ground planes next to each other without merging them, with the same touching vertices, will there be a render gap or will it be seamless?

Well that’s a darned good question. Likely, once mesh-merged, there will be no seam/gap, and then apply the texture to the merged mesh.

Without the mesh-merge… you would likely need to add the same texture to ALL (each of) the patches… and then “slide around” the textures on the patches… to match the adjacent patches.

Sliding, offsetting, scaling… textures… is done with some fancy texture properties, and with the UV’s on the mesh that the texture is applied-to. “U, V, W” are seen often in texture adjusting… and they are exactly the same as X, Y, and Z for mesh. For textures, the inventors simply used the 3 alphabet letters BEFORE XYZ.

So, do a little playground search… you will find many uAng, vScale, uOffset, wrapV <— (notice the naming convention change on “wrap” items). They are all properties on BJS rawTexture. U - V - W === X Y Z, but for textures.

Any UV on a mesh vertex… is a 2-value thing, and is like a percentage. The values are always between 0-1 inclusive. A UV of 0.2, 0.6… means the texture gets “stapled” (clamped) there… at a point that is 20% rightward from lower-left corner of texture, and 60% upward from lower left corner of texture. Not all mesh include UV’s, but they can be added to any mesh except empty/blanks.

Sorry for TMI… you probably know all that, already.

IF you could set ALL UV-kind data ON your patches PROPERLY, you would see NO texture seams, even though each patch uses an exact copy of the same FULL GROUND texture that every other patch uses. (they each use the very same Babylon.Texture object). IF you use uOffset/vScale, etc, each patch might need a COPY of the main Babylon.Texture.

To get this done sanely… it might be wise to ONLY add/start a new mesh patch… at 5% increments. In other words, always start/end patches at 5%, 10%, 15%, 20%, etc… distance from the terrain’s lower left (near-left) corner. When it comes time to set the UV’s on each patch… (or use texture vAng, uOffset, etc)… the 5% increment policy… might help to keep you from going insane. :slight_smile:

One more miserable item: Normals. Terrain from ribbons - Questions & Answers - HTML5 Game Devs Forum

Yep, a seam. It happens because the direction of the lighting normal on ONE side of the seam… is not “averaged” with the normal on the other side of the seam. AND, with mesh patches of differing resolutions, there might not even BE a vertex across the seam from another vertex… because of a resolution diff. YIKES!

SO, all in all, half’n’half. I once did some tiled-ground experiments… and I definitely needed the “lighting normals sewing machine” to average my cross-seam normals. We/I never got it done.

This all happened about the same time in my life… when I switched from ditch weed… to kush. :wink: Although “BJS Terrain Quilter v1.0” would be NICE, and could do lots of things “automatically” for us, it’s certainly not something that my Algebra-free pee-brain could ever code-up.

https://playground.babylonjs.com/#21TQJT#55 #54, separation-gap version, too

(Control-drag active to “dolly”, “ped”, or “truck” the arcCam target. Do ya like my TV cameraman lingo? :slight_smile: Also notice my “near-left” corner was somehow “far left”… because of some bad layout-math by Mr. Wingleberry.)

http://www.html5gamedevs.com/topic/24468-creating-multiple-meshes-from-ribbon

We’re only talking 2-way intersections. There ARE 4-way intersections, and at all 4-way intersections, FOUR normals need to be averaged, and that value written-to the normalKind data of all 4 verts that intersect there.

Phew. Getting a brain tumor yet, AF? (sorry)

See that? Topic unsolved, and poor Art has fallen dead… because Wingnut TALKED him to death.

heh.

Variable-resolution ground terrains are SUCH a pain-in-the-butt, eh?

Simplify() should NEVER clip the flat-height back and right edges of a terrain. That causes it to change bounding box size, which is a no-no for a decimation routine, imho.

Perhaps more reading and thought needs to happen… regarding custom decimation routines or advanced properties of default decimator. Babylon.js Documentation

Still on my TODO @Wingnut, I didn’t forget… One question you might be able to answer for when I revisit… does babylon have a function somewhere which I can specify a vertex or vertex index and it will remove that vertex and automatically merges the connecting faces to that vertex? And… a function to add a vertex at a specified point in the mesh where it reconnects the faces to that vertex surrounding?

@Wingnut
Seems I’ve finish the position and indices portion of the algorithm to make flat lands optimized during creation… however I have to figure out the UV’s now, a portion of the material is just coming up black but the primary portion is colored properly. I don’t know much about UV’s so I’ll be working to figure that out… is there anything in babylonjs that happens to compute the UV’s given positions and indices that maybe out of optimal order?

Hi Art. If it’s just a question of generating top-down uvs scaled to the mesh’s xz bounds, I have a function written for doing that:

var makeTopDownUVs = function(mesh){
var positions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind);
var xs = []; var ys = []; var uvs = [];
for(var p = 0; p < positions.length / 3; p++) {
    xs.push(positions[3 * p]);
    ys.push(positions[(3 * p) + 2]);
}
xs.mx = Math.max(...xs);
ys.mx = Math.max(...ys);
xs.mn = Math.min(...xs);
ys.mn = Math.min(...ys);
xs.factor = 1/(xs.mx - xs.mn);
ys.factor = 1/(ys.mx - ys.mn);
for(var p = 0; p < xs.length; p++) {
    uvs.push((xs[p]-xs.mn)*xs.factor, (ys[p]-ys.mn)*ys.factor);
}
mesh.setVerticesData(BABYLON.VertexBuffer.UVKind, uvs);
var lengthfactor = xs.factor/ys.factor;
return lengthfactor;
};
1 Like

Thanks a bunch for taking the time SpaceToast, after giving your code a whirl it became apparent to me that the issue wasn’t the UV’s and it had to be something else, I was going to post asking about what relation UV’s and indices have but I had a thought after I changed the camera angle with your UV code running, my added indices for the flat area’s had their indices added in reverse order, all I had to do was flip it around and everything is now working…

I’ll clean up my scratch pad code :stuck_out_tongue: and post it here for review then if its desired I’ll make a pull request

2 Likes

Oh man, sorry for missing your follow-on question, AF! For a month! Bad, Wingnut. Bad.

(Wingnut checks his Forum Helper License to see if it accidentally expired.)

Sorry AF.

It probably doesn’t matter, because I have very low experience with “hand-hacking” terrains.

You wish. hehe. I wish for that, too. The thought of a full-featured utility that let’s us DO that… gives me a brain tumor. :slight_smile: To me, it seems like it would be done with a visual editor (terrain modeler).

I was sort-of hoping that the author/installer of Simplify() would have visited and tell us how to TURN OFF the edges culling. There’s some “professor-level” folk nearby that I thought would JUMP at the chance to hack-on Simplify().

AF… were you going to use this “edit/add/remove-a-face” feature… to fix the culled edges caused by Simplify? Or do you have OTHER uses, too? For example… are you thinking about drilling holes in the terrain?

You want to do “patchwork hi-rez” I bet. Little pieces of terrain that are super-sub-divided in certain small areas, eh? I smell what you’re stepping-in… yep.

Are you thinking about using MANY grounds… pieced together, yet try to use a single texture that spans perfectly across all the pieces? I’m with ya. It allows underground caverns. Hard work, though, and nearly impossible to “automate”, I would guess.

I’m really not at-all qualified to be talking about this. I’m not well-read on the latest techniques, and I wasn’t well-read on the older techniques, either. :slight_smile: Sorry.

I’d say… dig into meshBuilder class… borrow the heightMap and displaceMap code… get to know it like your best friend (hard work). Then hack-in… IF (this_is_a_place_where_I_want_something_different) THEN { do something different than standard terrain-building here }.

Instead of editing a terrain that is already built, you create a new “builder” that allows interruptions and interventions… DURING the build. You could call them “deviation modules”. Your new createGroundFromHeightMap (abbrev cGFHM)… checks for AF-made deviation modules at EACH step of the build. If it finds one that is supposed to be installed at THIS point, it installs it and then continues, but it knows to compensate for your “terrain patch” (deviation module). Mainly, your custom cGFHM knows the width/height/subdivs of the patch/deviation… and connects the other parts of its build… properly.

“deev mods” :slight_smile: Heavy, huh? The secret would be… STANDARDIZE the format of the deviation modules. Make them re-usable and hand-around-able… and they might NOT be mesh at all. They are formula. Math, baby. Want 400 “ant mounds” out on your terrain, with no two looking identical? Yep… Ant Mound Deviation Module 1.0… for Arty’s Super-Sonic DeevMod-Ready Custom cGFHM!

Indented footprints generator? You bet… Arty’s Footprints deev-module.

Ahh, it’d never work… but it sure is fun to dream about. :slight_smile: Inserting little terrains during the building of a bigger terrain. Weird.

Cool UV code, @SpaceToast! Alright!

Roflmao, you went wayyy into the deep end there… nothing crazy just some preprocessing to reduce vertices and faces that arent needed when making a lot of flat areas using some 2D processing methods… i saw simplify doesnt always work right like in the sample you provided, and when i tried it on my mesh it never did anything. What I made will be perfect for what i need even if its not 200% optimized :wink:

@Wingnut @Deltakosh

Take a look at this pre-processing optimization… its not 100% perfect at the moment, but I’m not positive on how to fix the shadows on my polygon area’s.

https://www.babylonjs-playground.com/indexStable.html#QKZIHB

With the algorithm I wrote it’s producing 4,126 vertices and 23,646 indices, compared to the original which generated 10,201 vertices and 60,000 indices the shape appears to be the exact same just the lack of shadows make the flat areas stand out a bit. About a 60% reduction in vertices and 80% reduction in indices.

The basics of how it works is to detect and reduce the 3x3 area’s into connected variable height area’s and connected flat area’s. Find the edges of those areas to make a polygon and use the PolygonMeshBuilder to generate the optimal polygon faces for the flat area’s making holes for any variable height polygon’s that are contained in the area. Then find and index the vertices how the PolygonMeshBuilder linked them.

Besides the shadows for the areas the only thing I’m aware of is when the polygon walking comes around back to the starting point, the starting point or the last point could be discarded to create one less face.

I wrote HeightMapUtil in typescript this example is just using a compiled version.

Thoughts?

1 Like