Issues importing large terrain meshes from Blender

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Macryc

Hi All. What is the most optimal way to import terrain meshes from Blender? I am using Gaea to make heightmaps, then build the terrain in Blender. My terrain mesh is roughly 500K vertices (I’ve heard people say they’re able to import much larger meshes). I’m using GLTF2. Not only does the import take very long (like half a minute), but I have to wait for about a minute before the game runs after hitting the play button.

Am i using the wrong format? I mean, 500K polys is not a large mesh by any stretch. Maybe there’s something I need to do with export settings? Also, I’ve noticed that - annoyingly - anything above 500k vertices and Godot will crash saving the scene. The console shows an error that says ‘all memory pools allocated’ or something like that.

Please help - trying to find the right workflow for large terrains (16km2).

Godot 3.2.3, Win 64, 16gb ram

“500k polys is not a large mesh by any stretch” - that’s certainly debatable. Most game assets use much smaller meshes. Game assets such as trees are roughly in the 5-15k range for LOD0 even in current AAA open world games - 500k is two orders of magnitude larger. Doesn’t necessarily mean it’s a problem for a simple terrain mesh, but it’s not exactly small, either. Also, since it’s a single mesh, Godot can’t perform any view frustum occlusion culling for you, so assuming your player is positioned somewhere in the middle of the terrain and looking in one direction, you’re wasting lots of GPU cycles on running fragment shaders for invisible pixels.

I don’t know what resolution you’re planning to use for your 16km2 terrain, but assuming a grid resolution of 1 meter, the terrain mesh will use up 4000 x 4000 verts - that’s 16 million and that’s big.

Open world games handle such terrains by splitting the terrain mesh into smaller tiles and loading and discarding them on demand. They load lower-resolution meshes for terrain that’s far from the camera and higher-resolution mesh data for terrain that’s close to it.

archeron | 2021-03-03 23:28

Hi and thanks for your comment. The terrain mesh is something like 261K verts or thereabouts. Interestingly, even though it’s a drag to import this into Godot, once it’s there it’s performing better compared to Zylann’s heighmap plugin, even though I haven’t set up LoD. For a similar size map using the plugin I’m getting 135 fps maximum, with the 261K vert mesh I’m getting way over 200 FPS. Admittedly though, chunking a large mesh manually is a tedious task…

Macryc | 2021-03-05 06:10

Zylanns plugin does a lot of work, so it’s not surprising that Godot performs better with a single static mesh - it can just send the mesh off to the GPU, while Zylann’s plugin constantly needs to run (GdScript) code on the CPU to manage the terrain, and communicate with the GPU, which slows the GPU down. Personally, I don’t look at performance in FPS terms - I look at how much absolute time each part of my game uses up. I allocate a maximum time budget for how much time the terrain can use up (say, 3 ms). If you’re already in budget and don’t plan to change the terrain size/resolution, obviously there’d be no need to try other solutions.

For the loading problem, see my answer. Loading terrain data from a heightmap image and modifying the terrain mesh heights in the shader should drop the import times considerably. It’s also easier to manually chunk the terrain, if you are inclined to do so, since you can simply subdivide the image into multiple images. OTOH, you complicate other parts of your code, such as collisions with the terrain, so it might not be worth it.

archeron | 2021-03-05 06:58

My project is more about experimentation and finding the most optimal workflow - I’m not building a commercial game so I’m happy to experiment with as many approaches as I can. Using a vertex shader to create terrain from imported heightmaps is on my list. I’m using Instant Terra for heightmaps and exporting image tiles is blissfully simple. What I don’t know how to do with this approach is bake navigation meshes (on the fly, using the shader). With Zylann’s plugin I’d normally bake the terrain into a mesh first and than create a navi mesh out of that (I know you can bake a navimesh directly from the HTerrain node but it produces very mixed results).

Macryc | 2021-03-05 07:16

I got curious, so I created a 260k mesh in blender and dropped it into Godot as a glTF 2.0 with no compression (about 40 megs file size), no vertex colors etc - basically just the mesh data and uvs. Took 5 seconds to import into Godot, and the game (containing nothing but a Directionallight, a Camera and the 260k mesh) starts immediately without any delay. It’s just a bare, white mesh though.

archeron | 2021-03-05 07:27

:bust_in_silhouette: Reply From: archeron

I don’t have an answer for your specific import problem, but since you’re looking for a workflow for large terrains, I’ll offer the following suggestion:

Don’t load terrain data from a 3D file format. Instead, assuming your terrain uses a regular rectangular grid, convert the terrain data to a height map outside of Godot and store the terrain as a 16bit greyscale image (or, if you don’t need high precision for the height at each grid point, 8 bits may suffice).

(converting a grid-like 3D mesh into a heightmap is fairly simple; you can probably do it inside blender by baking with an orthogonal camera looking down on the terrain and a clever shader. Or if that fails, you can always export the mesh as a OBJ file and write a small Python/GdScript tool that reads it and translates it into a heightmap image)

Now you’ll only have to load an image file, which is much smaller and makes for faster load times. Consider that storing a mesh as a collection of vertices uses at least 12 bytes per vertex (3 floats, each take 4 bytes). And that’s just for the raw vertex data - that doesn’t include the definition of which vertices make up the triangles, or uv coordinates. Compare this to a heightmap image, where each pixel represents a vertex and only takes up 1 or 2 bytes, and no uvs and triangles need to be defined because that’s implicit in the position of a pixel in the image.

You can then create a a normal, rectangular plane mesh that’s subdivided the correct number of times in Godot and use that as your terrain mesh. Then use a custom vertex shader that reads the height coordinate from your heightmap texture.

Have a look at the “Your first spatial shader” Godot Tutorial at https://docs.godotengine.org/de/stable/tutorials/shading/your_first_shader/your_first_spatial_shader.html for details.