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

269 lines
6.3 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class TextureMerger:Node
{
public enum SourceMode
{
Viewports,
Textures
}
[Export]
public bool initialize;
[Export]
public bool createLayout;
[ExportGroup("Source")]
[Export]
public SourceMode sourceMode;
[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 );
[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;
}
Nodes.RemoveAndDeleteChildren( outputTarget );
X_textureMergerViewport = outputTarget.CreateChild<SubViewport>( "Texture Merger Viewport" );
X_mergerCamera = X_textureMergerViewport.CreateChild<Camera3D>( "Texture Merger Camera" );
X_mergerCamera.Projection = Camera3D.ProjectionType.Orthogonal;
X_textureMergerViewport.Size = (Vector2I) textureSize;
X_textureMergerViewport.OwnWorld3D = true;
X_textureMergerViewport.TransparentBg = true;
X_mergerCamera.Size = 1;
}
void GrabTextures()
{
_textures = new List<Texture2D>();
if ( SourceMode.Textures == sourceMode )
{
_textures.AddRange( sourceTextures );
return;
}
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 );
}
}
void CreateLayout()
{
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 );
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 );
SetMeshCoordinates( mesh, uvRectangle );
var material = new StandardMaterial3D();
material.Transparency = transparencyMode;
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 ) );
SetMeshCoordinates( mesh, customPositions[ i ], customSizes[ i ] );
var material = new StandardMaterial3D();
material.Transparency = transparencyMode;
material.ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded;
material.AlbedoTexture = _textures[ i ];
mesh.Material = material;
}
}
void SetMeshCoordinates( CsgMesh3D mesh, Rect2 rectangle )
{
SetMeshCoordinates( mesh, rectangle.Position, rectangle.Size );
}
void SetMeshCoordinates( CsgMesh3D mesh, Vector2 start, Vector2 size )
{
start = ConvertUVtoCameraSpace( start );
var end = ConvertUVtoCameraSpace( start + size );
size = end - start;
var quadMesh = new QuadMesh();
quadMesh.Size = size;
mesh.Mesh = quadMesh;
mesh.GlobalPosition = new Vector3( start.X - size.X , start.Y - size.Y, -1 );
}
public static Vector2I ComputeTextureAlignment( int numElements )
{
var root = Mathf.Sqrt( numElements );
var ceiled = Mathf.CeilToInt( root );
var floored = ceiled - 1;
var weight = Mathf.RoundToInt( root % 1 );
var height = weight * ceiled + ( 1 - weight ) * floored;
return new Vector2I( ceiled, height );
}
public static Rect2 GetUVRectangle( Vector2I textureAlignment, int index )
{
var x = index % textureAlignment.X;
var y = index / textureAlignment.Y;
var xs = 1f / textureAlignment.X;
var ys = 1f / textureAlignment.Y;
return new Rect2( x * xs, y * ys, new Vector2( xs, ys ) );
}
}
}