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( "Multi Baker Viewport" ); X_bakingViewport.Size = (Vector2I) textureSize; X_bakingViewport.OwnWorld3D = true; X_bakingViewport.TransparentBg = true; X_worldEnvironment = X_bakingViewport.CreateChild( "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( "Target Container" ); X_views = X_bakingViewport.CreateChild( "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 _bakers; public void CreateViews() { Nodes.RemoveAndDeleteChildren( X_views ); var numViews = GetNumViews(); _bakers = new List(); for ( int i = 0; i < numViews; i++ ) { var userIndex = ( i + 1 ); var bakingView = X_views.CreateChild( "Baking View " + userIndex ); bakingView.TransparentBg = true; var bakingCamera = bakingView.CreateChild( "Camera View " + userIndex ); var baker = bakingView.CreateChild( "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(); } } } }