Skip to content

Splat map importer#907

Draft
stan4dbunny wants to merge 1 commit intoTokisanGames:mainfrom
stan4dbunny:import_splat_maps
Draft

Splat map importer#907
stan4dbunny wants to merge 1 commit intoTokisanGames:mainfrom
stan4dbunny:import_splat_maps

Conversation

@stan4dbunny
Copy link
Contributor

I have created this first draft to import splat maps in the Terrain3D importer.

I have tested this for my use case, where I used 3 splat images (raw RGBA8) with the same size as the terrain/heightmap/colormap. Each splat image uses the 4 color channels to map to a detail texture like described here:

Example - Splat 01

R - Grass
G - Rocks
B - Dirt
A - Peak (Snow)

You also have to specify a list that maps the index of each color channel to the corresponding texture asset list ids. If you have for example 3 splat images the list needs to be 12 for all color channels (so splat_count * 4), even if you don't use all of them (mark -1 for unused).

image image

The splat maps should be able to be imported as PNGs as well, but I started with raw because it's what my team uses. I can't share the splats we use unfortunately.

I'm of course open to try to make this more usable for others if you have any advice, I don't have much knowledge about the usual formats of other terrain generators.

@TokisanGames TokisanGames added this to the 1.2 milestone Dec 22, 2025
@TokisanGames TokisanGames added the enhancement New feature or request label Dec 22, 2025
else:
var splat_images : Array[Image]
for file : String in splat_map_paths:
var splat : Image = Terrain3DUtil.load_raw_image(file, r16_size.x, r16_size.y)
Copy link

@LucaTuerk LucaTuerk Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

				var texture : Texture2D = ResourceLoader.load(file)
				var splat : Image = texture.get_image()
				if splat.is_compressed():
					splat.decompress()
				splat_images.append(splat)

This works for me for splat maps exported as png from WorldMaschine, and should work with all image formats supported by godot.

Maybe it would make sense to implement your raw format using an EditorImportPlugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad that it works for you! What do you mean by implementing my raw format with an editorimportplugin?:)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I loaded the image using the default ResourceLoader instead of the load_raw_image method, as WorldMachine does not do raw image export as far as I can tell. This way I can import as PNG.

If you implement a EditorImportPlugin for your raw format you should be able import it using the ResourceLoader as well, so the same splat map importer can be used for both.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the advice. I have now fixed so that the ResourceLoader is used instead so you should be able to use it for your stuff, as well as the clear settings that you mentioned. I also fixed a bug in the noise value in compute_control_ids_and_blend that you might want to fix as well if you're using the code:)

Also, I'm just curious, are your splats RGB or RGBA? Mine are RGBA and I noticed that I had to change the import setting Fix alpha border for the splats to work correctly.

@LucaTuerk
Copy link

image

Works nice for my test WorldMachine exports with adjustments to the importer 👍

I did not test the raw image import.

@stan4dbunny
Copy link
Contributor Author

image

Works nice for my test WorldMachine exports with adjustments to the importer 👍

I did not test the raw image import.

What changes did you make? Maybe I could add the functionality so that we can import from WorldMachine.

@LucaTuerk
Copy link

image Works nice for my test WorldMachine exports with adjustments to the importer 👍 I did not test the raw image import.

What changes did you make? Maybe I could add the functionality so that we can import from WorldMachine.

I only changed the importer using the ResourceLoader instead of load_raw_image as seen in #907 (comment)

@stan4dbunny stan4dbunny force-pushed the import_splat_maps branch 2 times, most recently from b9719f7 to 7f1d5e0 Compare January 5, 2026 13:30
weights[w_i + 2] = 0.f;
weights[w_i + 3] = 0.f;
} else {
Color color = splat->get_pixel(x, y);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERROR: Can't get_pixel() on compressed image, sorry. at: _get_color_at_ofs (core/io/image.cpp:3257) GDScript backtrace (most recent call first): [0] start_import (res://addons/terrain_3d/tools/importer.gd:120)

I get this error because the image is not decompressed.

Copy link
Contributor Author

@stan4dbunny stan4dbunny Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried using VRAM compressed splats and changing the code in importer.gd to this. Does it work for you?

for file : String in splat_map_paths:
				var splat : Texture2D = ResourceLoader.load(file, "Texture2D", ResourceLoader.CACHE_MODE_IGNORE)
				var splat_img : Image = splat.get_image()
				if splat_img.is_compressed():
					splat_img.decompress()
				splat_images.append(splat_img)

color_file_name = ""
import_splat_maps = false
splat_map_paths.clear()
material_ids.clear()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thanks 👍

else:
var splat_images : Array[Image]
for file : String in splat_map_paths:
var splat : CompressedTexture2D = ResourceLoader.load(file, "CompressedTexture2D", ResourceLoader.CACHE_MODE_IGNORE)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to use CompressedTexture2D here? I think Godot will import all image textures as CompressedTexture3D but this will fail for other texture types such as NoiseTexture2D.

SCRIPT ERROR: Trying to assign value of type 'NoiseTexture2D' to a variable of type 'CompressedTexture2D'. at: start_import (res://addons/terrain_3d/tools/importer.gd:117) GDScript backtrace (most recent call first): [0] start_import (res://addons/terrain_3d/tools/importer.gd:117)

I think procedural textures could be useful.
And only the base Texture2D get_image method is needed.

@stan4dbunny stan4dbunny force-pushed the import_splat_maps branch 2 times, most recently from 65bd024 to 8fdafa3 Compare January 13, 2026 15:48
…splat import settings. Now works if texture is compressed.
@ChampionX1001
Copy link
Contributor

This would be incredibly helpful for my Gaea -> Terrain3D workflow! Great work!

PNG8 and PNG16 splat support would be nice. That's what I primarily use, although I can use raw too.

With this PR, the terrain software -> godot workflow is pretty much fully supported. Since really, all Terrain3D needs is a heightmap and splat maps for texturing.

@TokisanGames
Copy link
Owner

PNG8 ... splat support would be nice

Op used RGBA8 which is PNG8.

PNG16 splat support would be nice

PNG16 is not an option until Godot supports it, or someone writes a format loader, or an external library is bundled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants