rj-action-library/Runtime/Procedural/Baking/TextureMerger.cs

329 lines
7.7 KiB
C#
Raw Normal View History

2024-12-01 17:07:41 +00:00
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TextureMerger:Node
{
public enum SourceMode
{
2025-01-03 12:09:23 +00:00
MultiBaker,
2024-12-01 17:07:41 +00:00
Viewports,
Textures
}
[Export]
public bool initialize;
[Export]
public bool createLayout;
[ExportGroup("Source")]
[Export]
public SourceMode sourceMode;
2025-01-03 12:09:23 +00:00
[ExportGroup("Source/MultiBaker")]
[Export]
public MultiBaker multiBaker = null;
2024-12-01 17:07:41 +00:00
[ExportGroup("Source/Viewports")]
[Export]
public Node sourceViewportsContainer = null;
[Export]
public SubViewport[] sourceViewports;
[ExportGroup("Source/Textures")]
[Export]
public Texture2D[] sourceTextures;
[ExportGroup("Output")]
[Export]
public Node outputTarget;
public enum LayoutMode
{
Grid,
Custom
}
[Export]
public LayoutMode layoutMode = LayoutMode.Grid;
[ExportGroup("Output/Custom")]
[Export]
public Vector2[] customPositions;
[Export]
public Vector2[] customSizes;
[ExportGroup("Viewport")]
[Export]
public Vector2 textureSize = new Vector2( 2048, 2048 );
2025-01-03 12:09:23 +00:00
[Export]
public float alphaThreshold = 0.1f;
2024-12-01 17:07:41 +00:00
[Export]
public BaseMaterial3D.TransparencyEnum transparencyMode = BaseMaterial3D.TransparencyEnum.AlphaScissor;
[ExportGroup("Debugging")]
[Export]
public SubViewport X_textureMergerViewport;
[Export]
public Camera3D X_mergerCamera;
List<Texture2D> _textures = new List<Texture2D>();
public override void _Process( double delta )
{
if ( initialize )
{
initialize = false;
Initialize();
}
if ( createLayout )
{
createLayout = false;
CreateLayout();
}
}
public void Initialize()
{
if ( outputTarget == null )
{
outputTarget = this;
}
2025-01-03 12:09:23 +00:00
if ( X_textureMergerViewport == null )
{
X_textureMergerViewport = outputTarget.CreateChild<SubViewport>( "Texture Merger Viewport" );
}
2024-12-01 17:07:41 +00:00
2025-01-03 12:09:23 +00:00
Nodes.RemoveAndDeleteChildren( X_textureMergerViewport );
2024-12-01 17:07:41 +00:00
X_mergerCamera = X_textureMergerViewport.CreateChild<Camera3D>( "Texture Merger Camera" );
2025-01-03 12:09:23 +00:00
X_mergerCamera.Projection = Camera3D.ProjectionType.Orthogonal;
X_mergerCamera.Size = 2;
2024-12-01 17:07:41 +00:00
X_textureMergerViewport.Size = (Vector2I) textureSize;
X_textureMergerViewport.OwnWorld3D = true;
X_textureMergerViewport.TransparentBg = true;
}
void GrabTextures()
{
_textures = new List<Texture2D>();
if ( SourceMode.Textures == sourceMode )
{
_textures.AddRange( sourceTextures );
return;
}
2025-01-03 12:09:23 +00:00
if ( SourceMode.MultiBaker == sourceMode )
{
var viewports = multiBaker.GetAllViewports().ToArray();
var vpTextures = Arrays.Map( viewports,
s =>
{
var vt = new ViewportTexture();
vt.ViewportPath = s.GetPath();
return vt;
}
);
_textures.AddRange( vpTextures );
}
2024-12-01 17:07:41 +00:00
if ( SourceMode.Viewports == sourceMode )
{
if ( sourceViewportsContainer != null )
{
sourceViewports = Nodes.AllIn<SubViewport>( sourceViewportsContainer, null, false ).ToArray();
}
var vpTextures = Arrays.Map( sourceViewports,
s =>
{
var vt = new ViewportTexture();
vt.ViewportPath = s.GetPath();
return vt;
}
);
_textures.AddRange( vpTextures );
}
}
2025-01-03 12:09:23 +00:00
public void CreateLayout()
2024-12-01 17:07:41 +00:00
{
GrabTextures();
if ( LayoutMode.Grid == layoutMode )
{
CreateGridLayout();
}
else if ( LayoutMode.Custom == layoutMode )
{
CreateCustomLayout();
}
}
Vector2 ConvertUVtoCameraSpace( Vector2 uv )
{
var scale = Vector2.One;
var w = 1;
var h = 1;
if ( X_textureMergerViewport.Size.X != X_textureMergerViewport.Size.Y )
{
if ( X_textureMergerViewport.Size.X > X_textureMergerViewport.Size.Y )
{
w = X_textureMergerViewport.Size.X / X_textureMergerViewport.Size.Y;
}
else
{
h = X_textureMergerViewport.Size.Y / X_textureMergerViewport.Size.X;
}
}
var x = Mathf.Remap( uv.X, 0, 1, -w, w );
var y = Mathf.Remap( uv.Y, 0, 1, -h, h );
return new Vector2( x, y );
}
void CreateGridLayout()
{
var alignment = ComputeTextureAlignment( _textures.Count );
2025-01-03 12:09:23 +00:00
// RJLog.Log( "Grid Alignment:", alignment );
2024-12-01 17:07:41 +00:00
Nodes.RemoveAndDeleteChildrenOfType<CsgMesh3D>( X_textureMergerViewport );
for ( int i = 0; i < _textures.Count; i++ )
{
var mesh = X_textureMergerViewport.CreateChild<CsgMesh3D>( "Texture " + ( i + 1 ) );
var uvRectangle = GetUVRectangle( alignment, i );
2025-01-03 12:09:23 +00:00
SetMeshCoordinates( mesh, uvRectangle, i );
2024-12-01 17:07:41 +00:00
var material = new StandardMaterial3D();
material.Transparency = transparencyMode;
2025-01-03 12:09:23 +00:00
material.AlphaScissorThreshold = alphaThreshold;
2024-12-01 17:07:41 +00:00
material.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded;
material.AlbedoTexture = _textures[ i ];
mesh.Material = material;
}
}
void CreateCustomLayout()
{
for ( int i = 0; i < _textures.Count; i++ )
{
var mesh = outputTarget.CreateChild<CsgMesh3D>( "Texture " + ( i + 1 ) );
2025-01-03 12:09:23 +00:00
SetMeshCoordinates( mesh, customPositions[ i ], customSizes[ i ], i );
2024-12-01 17:07:41 +00:00
var material = new StandardMaterial3D();
material.Transparency = transparencyMode;
material.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded;
2025-01-03 12:09:23 +00:00
material.AlphaScissorThreshold = alphaThreshold;
2024-12-01 17:07:41 +00:00
material.AlbedoTexture = _textures[ i ];
mesh.Material = material;
}
}
2025-01-03 12:09:23 +00:00
void SetMeshCoordinates( CsgMesh3D mesh, Box2 rectangle, int index )
2024-12-01 17:07:41 +00:00
{
2025-01-03 12:09:23 +00:00
SetMeshCoordinates( mesh, rectangle.min, rectangle.max, index );
2024-12-01 17:07:41 +00:00
}
2025-01-03 12:09:23 +00:00
void SetMeshCoordinates( CsgMesh3D mesh, Vector2 min, Vector2 max, int index )
2024-12-01 17:07:41 +00:00
{
2025-01-03 12:09:23 +00:00
var start = ConvertUVtoCameraSpace( min );
var end = ConvertUVtoCameraSpace( max );
2024-12-01 17:07:41 +00:00
2025-01-03 12:09:23 +00:00
var size = end - start;
2024-12-01 17:07:41 +00:00
var quadMesh = new QuadMesh();
quadMesh.Size = size;
mesh.Mesh = quadMesh;
2025-01-03 12:09:23 +00:00
mesh.GlobalPosition = new Vector3( start.X + size.X/2, start.Y + size.Y/2, -1 );
// RJLog.Log( "Set Mesh", index, "Min:", min, ">>", start, "Max:", max, ">>", end );
2024-12-01 17:07:41 +00:00
}
public static Vector2I ComputeTextureAlignment( int numElements )
{
var root = Mathf.Sqrt( numElements );
var ceiled = Mathf.CeilToInt( root );
var floored = ceiled - 1;
2025-01-03 12:09:23 +00:00
if ( ceiled * floored >= numElements )
{
return new Vector2I( ceiled, floored );
}
2024-12-01 17:07:41 +00:00
2025-01-03 12:09:23 +00:00
return new Vector2I( ceiled, ceiled );
2024-12-01 17:07:41 +00:00
}
2025-01-03 12:09:23 +00:00
public static Box2 GetUVRectangle( Vector2I textureAlignment, int index, bool flipY )
{
var b = GetUVRectangle( textureAlignment, index );
if ( ! flipY )
{
return b;
}
var uvA = b.min;
var uvB = b.max;
uvA.Y = 1f - uvA.Y;
uvB.Y = 1f - uvB.Y;
b.min = uvA;
b.max = uvB;
return b;
}
public static Box2 GetUVRectangle( Vector2I textureAlignment, int index )
2024-12-01 17:07:41 +00:00
{
var x = index % textureAlignment.X;
2025-01-03 12:09:23 +00:00
var y = index / textureAlignment.X;
2024-12-01 17:07:41 +00:00
var xs = 1f / textureAlignment.X;
var ys = 1f / textureAlignment.Y;
2025-01-03 12:09:23 +00:00
var size = new Vector2( xs, ys );
var min = new Vector2( x * xs, y * ys );
var max = min + size;
return new Box2( min, max );
2024-12-01 17:07:41 +00:00
}
}
}