Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Texture sets #212

Open
Zylann opened this issue Nov 7, 2020 · 5 comments
Open

Texture sets #212

Zylann opened this issue Nov 7, 2020 · 5 comments
Labels
enhancement New feature or request Need engine feature The feature can't be made without a new Godot feature

Comments

@Zylann
Copy link
Owner

Zylann commented Nov 7, 2020

This is an upcoming feature I'd like to expose before I merge it.
Texture sets would be a new custom resource to setup terrain textures, replacing the old way.

Existing system

So far setting up textures for the ground was up to the user, as explained here: https://github.com/Zylann/godot_heightmap_plugin/blob/master/addons/zylann.hterrain/doc/main.md#texturing

If you used a classic shader, you had to make 4 pairs of textures that are each packed like this:

  • Albedo in RGB, bump in Alpha
  • Normal in RGB, roughness in Alpha

You had to do this packing yourself in an image editor, or using a separate plugin.

If you used an array-based shader, you had to not only do that, but also assemble all your textures in an atlas, and use the default TextureArray importer of Godot, which is impractical and time consuming (also you better have lots of RAM to spare).

Proposal

HTerrainTextureSet will be a new resource that can hold a collection of terrain textures. It lets you define source images (albedo, bump, normal, roughness), and allows to import them into the kind of texture the plugin expects. This way, you won't need to use an external tool anymore, and changing one of the source images is a lot simpler in the case of texture arrays.

Each HTerrain node will start with one of these resources by default, and once you choose the data directory, it will be saved next to the data.hterrain resource, as a simple .tres file. The generated textures will then be saved in the same place, either as .stex or .texarr.
In this workflow, source images may not be used by the game, so it will be possible to place them in a folder with a .gdignore file, so they won't bloat the exported build. This leads to the following folder organization:

terrain_test/
	
	source_textures/
		.gdignore
		grass col.png
		grass nrm.png
		grass rgh.png
		grass bmp.png
		Rocks_Albedo.jpg
		Rocks_Bump.jpg
		Rocks_Normal.jpg
		Rocks_Roughness.jpg
		...

	terrain_data/
		color.png
		data.hterrain
		detail.png
		detail2.png
		global_albedo.png
		height.res
		normal.png
		splat.png
		
		textures.tres <-- TEXTURE SET
		textures_t0_albedo_bump.stex <-- IMPORTED TEXTURES
		textures_t1_albedo_bump.stex
		...
		textures_t0_normal_roughness.stex
		textures_t1_normal_roughness.stex
		...

	terrain_test.tscn

Note: because it behaves like a regular resource, it is also possible to re-use the same pre-made set of textures for multiple terrains, by assigning it in the inspector.

Interface

There will be a custom interface to configure a HTerrainTextureSet, which can be shown either by inspecting the .tres resource, or with a shortcut from the terrain editor (double-click on any texture slot, or choose Edit...).

Work-in-progress:
image

Other options may also include default values for non-filled slots, normalmap strength to allow using DX convention from CC0Textures, and a mode to switch between texture arrays and individual textures, depending on the shader you are using.

@Zylann Zylann added the enhancement New feature or request label Nov 7, 2020
@RonanZe
Copy link

RonanZe commented Nov 7, 2020

Seems to be a nice workflow improvement.
What are the drawbacks?

@Zylann
Copy link
Owner Author

Zylann commented Nov 7, 2020

@RonanZe if you only want to use albedo textures without normals, without fancy blending, and only 4 textures, then you might find the process a bit redundant compared to assigning textures directly without import step (although Godot still imports from PNGs to STEXs in any case).

@Zylann
Copy link
Owner Author

Zylann commented Nov 8, 2020

I was in the testing phase when I found the following blockers.
I was hoping that I would be able to generate StreamTextures or TextureArray from code, save them, assign them to the TextureSet resource, and finally save it. Simple? NOPE:

  • 1: generating native Godot formats (.texarr, .stex) really sucks. ResourceSaver does not allow to save TextureArray resources, and StreamTexture can't even be made from scripts.
    So I ended up re-implementing the entire logic of texture importers in GDScript... sadly there is a bunch of things GDScript can't do because they are not exposed, in particular when dealing with image compression.

  • 2: it appears some VRAM compression formats may depend on feature tags sent to EditorImportPlugin, depending on your project settings and the platforms you export for... which means to produce these imported formats, I would HAVE to write one. But the problem is, the import dock won't let me do anything close to the nice free UI I posted earlier.

  • 3: if I have to make an EditorImportPlugin, it means TextureSet can't be the only file, because... to react to the importer dock, it has to be an "importable" format. So the idea would be to introduce a filetype and custom importer, which will import as a TextureSet.

    • There is a problem with importers to custom resources: Files imported as custom resource types are not considered as resources godotengine/godot#20496
    • A variant would be to introduce a PackedTextureImporter and PackedTextureArrayImporter, which would import textures from descriptor files listing the channel operations and other options. Then TextureSet would be able to reference the resulting textures. This would be very nice for re-usability and simplicity of each part, but unmanageable because of the next point...
  • 4: if I rely on the import system, it means I have to:

    • Be able to trigger import from script for specific files, which is not exposed.
    • Be 100% sure that a file has been imported properly, so my editor can add the textures to the TextureSet resource after the user clicks "import". Otherwise, doing something like textures.add(load(path)) would just fail because import either failed or did not happen yet. Right now I can trigger a full scan, but I have no guarantee as to when I'll be able to call load to add the texture in the list...
    • If either of these isn't possible, then I have to write an importer that directly imports as a TextureSet, which sucks for re-usability and API simplicity. Otherwise, it means I can't automate this step to users.
  • Bonus 1: I did the math, and if someone makes a game with terrains having 16 2K PBR textures on them (and it happened), the required space in the project can quickly go above 1 Gb, just from PNG files. Godot's TextureArray importer holds all images im memory uncompressed, TWICE (once for the input, once for slicing that big atlas it wants), so you better have lots of RAM to spare while importing... unless I take the opportunity to implement the importer differently (what about the loading of that resource tho?? time will tell...).

  • Bonus 2: the LOSSY compression mode (which just saves files with the WEBP format) is missing for texture arrays, and WEBP is missing from the script API, so I won't be able to make it available. A lost opportunity to lower this big amount of data...

The alternative is to not import, and just generate PNGs, then reverse-engineer .import files. It's also dirty, but not as bad, and I did this already with terrain data.
Downside is:

  • It will bloat your project because then all those 2K PBR textures will now appear THREE times in your project (source PNGs, processed PNGs, and imported textures from Godot). This is going to be really heavy.
  • It will force you to manually unfocus/refocus the editor each time you make a change to these textures, because scripts can't invoke the importer.
  • I still can't automatically reference the resulting textures anyways, because there is no "transaction" in the import system to tell you when it's done or if it failed (see point 4).

Conclusion:

  • I can make a monolithic importer that will look like the UI above, with internal limitations and bugs beyond my control
  • Or I can make a tool to generate PNGs, separate from the UI because with this approach I can't integrate it reliably. In this approach you would also loose more disk space, and be limited by the builtin importer RAM usage.

I wasted two nights on this so I'll make a pause there... I need to figure out what to do from here, because it appears what I wanted to do is impossible, not with extra steps from the user (crazy, right)... I would really like to get the design from my previous post though... the "correct" way Godot is exposing feels very limiting.

@Zylann Zylann added the Need engine feature The feature can't be made without a new Godot feature label Nov 9, 2020
@Zylann
Copy link
Owner Author

Zylann commented Nov 21, 2020

Update:
I've been working hard to overcome some of the issues I had earlier, and I managed to get this feature working in the end, with a few differences from the initial proposal.

The feature is available to test in the texture_sets branch, along with new documentation https://github.com/Zylann/godot_heightmap_plugin/blob/texture_sets/addons/zylann.hterrain/doc/main.md#texturing

@Zylann
Copy link
Owner Author

Zylann commented Nov 28, 2020

This is now in master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Need engine feature The feature can't be made without a new Godot feature
Projects
None yet
Development

No branches or pull requests

2 participants