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

320 lines
7.2 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
[Tool]
[GlobalClass]
public partial class MultiBaker:Node
{
[Export]
public bool initialize;
[Export]
public bool setupViews;
public enum BakeMode
{
Cylindric_Billboard
}
[Export]
public BakeMode bakeMode;
[Export]
public Node3D sourceTarget;
[Export]
public Node outputTarget;
[ExportGroup( "Camera Settings")]
[Export]
public Baker.CameraDistanceDetectionType distanceDetectionType = Baker.CameraDistanceDetectionType.Automatic_Distance_Detection;
[Export]
public float customDistance = 50;
[Export]
public float cameraZoom = 1;
[Export]
public Baker.CameraFOVMode fovMode = Baker.CameraFOVMode.Compute_Fov_With_Distance;
[Export]
public float originalFOV = 75;
[Export]
public float fovPlacingDistance = 200;
[Export]
public float customFOV = 75;
[Export]
public Baker.MeshMode meshMode = Baker.MeshMode.World_Scale;
[ExportGroup("Cylindric Billboard")]
[Export]
public int cylinderSides = 4;
[Export]
public bool cylinderTop = false;
[Export]
public bool cylinderBottom = false;
[Export]
public float cylinderSideOffset = 0;
[Export]
public float cylinderTopOffset = 0.5f;
[Export]
public float cylinderBottomOffset = 0.5f;
[Export]
public MeshInstance3D cylinderMesh;
[ExportGroup("Viewport")]
[Export]
public Vector2 textureSize = new Vector2( 2048, 2048 );
[ExportGroup("Debugging")]
[Export]
public SubViewport X_bakingViewport;
[Export]
public Node3D X_bakingTargetContainer;
[Export]
public Node X_views;
[Export]
public WorldEnvironment X_worldEnvironment;
public override void _Process( double delta )
{
if ( initialize )
{
initialize = false;
Initialize();
}
if ( setupViews )
{
setupViews = false;
CreateViews();
SetupViews();
}
}
public void Initialize()
{
if ( outputTarget == null )
{
outputTarget = this;
}
Nodes.RemoveAndDeleteChildren( outputTarget );
X_bakingViewport = outputTarget.CreateChild<SubViewport>( "Multi Baker Viewport" );
X_bakingViewport.Size = (Vector2I) textureSize;
X_bakingViewport.OwnWorld3D = true;
X_bakingViewport.TransparentBg = true;
X_worldEnvironment = X_bakingViewport.CreateChild<WorldEnvironment>( "Multi Baker Environment" );
X_worldEnvironment.Environment = new Godot.Environment();
X_worldEnvironment.Environment.AmbientLightSource = Godot.Environment.AmbientSource.Color;
X_worldEnvironment.Environment.AmbientLightColor = HSLColor.white;
X_bakingTargetContainer = X_bakingViewport.CreateChild<Node3D>( "Target Container" );
X_views = X_bakingViewport.CreateChild<Node>( "Views" );
sourceTarget.DeepCopyTo( X_bakingTargetContainer );
}
public int GetNumViews()
{
if ( BakeMode.Cylindric_Billboard == bakeMode )
{
var cylinderViews = cylinderSides;
if ( cylinderBottom )
{
cylinderViews ++;
}
if ( cylinderTop )
{
cylinderViews ++;
}
return cylinderViews;
}
return 0;
}
List<Baker> _bakers;
public void CreateViews()
{
Nodes.RemoveAndDeleteChildren( X_views );
var numViews = GetNumViews();
_bakers = new List<Baker>();
for ( int i = 0; i < numViews; i++ )
{
var userIndex = ( i + 1 );
var bakingView = X_views.CreateChild<SubViewport>( "Baking View " + userIndex );
bakingView.TransparentBg = true;
var bakingCamera = bakingView.CreateChild<Camera3D>( "Camera View " + userIndex );
var baker = bakingView.CreateChild<Baker>( "Baker " + userIndex );
baker.camera = bakingCamera;
baker.target = X_bakingTargetContainer;
baker.viewport = bakingView;
baker.update = true;
_bakers.Add( baker );
}
}
public void SetupViews()
{
if ( BakeMode.Cylindric_Billboard == bakeMode )
{
CreateCylinderBillboardView();
}
}
Sphere _targetBoundingSphere;
Sphere targetBoundingSphere
{
get
{
if ( _targetBoundingSphere == null )
{
ComputeBoundingSphere();
}
return _targetBoundingSphere;
}
}
void ComputeBoundingSphere()
{
var worldBounds = X_bakingTargetContainer.GetWorldBounds();
_targetBoundingSphere = Sphere.ContainingBox( worldBounds );
}
float GetCameraFOV()
{
if ( Baker.CameraFOVMode.Custom_Fov == fovMode )
{
return customFOV;
}
if ( Baker.CameraFOVMode.Keep_Fov == fovMode )
{
return originalFOV;
}
return Cameras.ComputeFOVForBillboard( originalFOV, targetBoundingSphere.radius, fovPlacingDistance );
}
float GetCameraDistance()
{
if ( Baker.CameraDistanceDetectionType.Custom_Distance == distanceDetectionType )
{
return customDistance;
}
var fov = GetCameraFOV();
return Cameras.ComputeCameraFrameFittingDistance( fov, targetBoundingSphere.radius / cameraZoom );
}
float GetOutputScale()
{
var fov = GetCameraFOV();
var distance = GetCameraDistance();
return Cameras.ComputeCameraFittingScale( fov, distance );
}
void CreateCylinderBillboardView()
{
_targetBoundingSphere = null;
var fov = GetCameraFOV();
var distance = GetCameraDistance();
var outputScale = GetOutputScale();
_bakers.ForEach(
b =>
{
b.useCustomFOV = true;
b.customFOV = fov;
b.useCustomDistance = true;
b.customDistance = distance;
}
);
var index = 0;
var mg = new MeshGeometry();
var numTextures = ( cylinderTop ? 1 : 0 ) + ( cylinderBottom ? 1 : 0 ) + cylinderSides;
var textureAlignment = TextureMerger.ComputeTextureAlignment( numTextures );
if ( cylinderTop )
{
_bakers[ index ].yaw = 0;
_bakers[ index ].pitch = 90f;
var uvRectangle = TextureMerger.GetUVRectangle( textureAlignment, index );
mg.AddQuad( _bakers[ index ].bakingRotation, outputScale, uvRectangle );
index ++;
}
if ( cylinderBottom )
{
_bakers[ index ].yaw = 0;
_bakers[ index ].pitch = -90f;
var uvRectangle = TextureMerger.GetUVRectangle( textureAlignment, index );
mg.AddQuad( _bakers[ index ].bakingRotation, outputScale, uvRectangle );
index ++;
}
for ( int i = 0; i < cylinderSides; i++ )
{
var angle = ( 360f * i ) / (float) cylinderSides;
_bakers[ index + i ].yaw = angle;
_bakers[ index + i ].pitch = 0;
var uv = TextureMerger.GetUVRectangle( textureAlignment, index + 1);
mg.AddQuad( _bakers[ index + i ].bakingRotation, outputScale, uv );
}
if ( cylinderMesh != null )
{
cylinderMesh.Mesh = mg.GenerateMesh();
}
}
}
}