rokojori_action_library/Runtime/Procedural/Textures/TextureStack/TextureStackBaker.cs

129 lines
3.6 KiB
C#
Raw Normal View History

using Godot;
using System;
using Rokojori.Extensions;
namespace Rokojori
{
/** Reads a composited TextureStack back off the GPU and writes it to disk.
Thin on purpose - the two halves it needs already existed before this
system did:
- Readback. Spike gate G3 found <code>GetImage()</code> works directly on
a DrawableTexture2D, so none of the fallbacks the plan budgeted for
(RenderingServer.Texture2DGet, a viewport quad) are needed.
- Writing. <code>Procedural/Baking/Textures.Save</code> already handles
png / jpg / webp / exr by extension.
So baking really is the one-liner the research addendum predicted; what is
left here is the editor bookkeeping (telling the filesystem a file
appeared) and compositing into a throwaway target so that baking a stack
does not need a TextureStackTexture2D to exist first.
*/
public static class TextureStackBaker
{
/** Composites a stack into a temporary DrawableTexture2D and reads it back.
The target is local to this call, so nothing is cached and nothing holds
VRAM afterwards - which is the point for a one-off 4k bake. Incremental
redraw is off for the same reason: a checkpoint buffer here would be
allocated and dropped immediately.
*/
public static Image Render( TextureStack stack, Vector2I size, TextureStackFormat format,
Color clearColor, bool mipmaps )
{
if ( stack == null || size.X <= 0 || size.Y <= 0 )
{
return null;
}
var target = new DrawableTexture2D();
var context = new TextureStackCompositorContext();
context.stack = stack;
context.size = size;
context.format = format;
context.clearColor = clearColor;
context.mipmaps = mipmaps;
context.fromLayer = 0;
context.incrementalRedraw = false;
context.targets.Add( target );
new TextureStackCompositor().Composite( context );
context.targets.Clear();
return target.GetImage();
}
/** Writes an image to a project path and makes the editor notice it.
Without the rescan the file exists but has no .import entry, so nothing
can reference it until the editor is refocused - which looks exactly like
the bake having failed.
*/
public static bool Save( Image image, string path, float quality = 1 )
{
if ( image == null || string.IsNullOrEmpty( path ) )
{
return false;
}
Textures.Save( image, path, quality );
Rescan();
return true;
}
public static void Rescan()
{
#if TOOLS
if ( ! Engine.IsEditorHint() )
{
return;
}
EditorInterface.Singleton.GetResourceFilesystem().Scan();
#endif
}
/** The baked file as an Image, or null when there is none yet.
Two paths, because the file is a different thing in the editor than in an
export: in the editor it is the png on disk, in an export it is the
imported .ctex the png produced and the png itself may not be shipped at
all. ResourceLoader finds the imported one; LoadFromFile is the editor
and user:// case.
*/
public static Image Load( string path )
{
if ( string.IsNullOrEmpty( path ) )
{
return null;
}
if ( ResourceLoader.Exists( path ) )
{
var texture = ResourceLoader.Load<Texture2D>( path );
if ( texture != null )
{
return texture.GetImage();
}
}
if ( ! FileAccess.FileExists( path ) )
{
return null;
}
return Image.LoadFromFile( path );
}
}
}