574 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			574 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
using System.Collections;
 | 
						|
using System.Collections.Generic;
 | 
						|
using Godot;
 | 
						|
using System;
 | 
						|
 | 
						|
using System.Threading.Tasks;
 | 
						|
using System.Linq;
 | 
						|
 | 
						|
namespace Rokojori
 | 
						|
{
 | 
						|
  [Tool]
 | 
						|
  [GlobalClass]
 | 
						|
  public partial class MultiBaker:Node
 | 
						|
  {
 | 
						|
    [ExportToolButton( "Bake")]
 | 
						|
    public Callable BakeButton => Callable.From( () => Bake() );
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public bool cleanUpAfterBaking = true;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public _XX_MultiBakeMode bakeMode;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public _XX_MultiTextureBaker textureBaker;
 | 
						|
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public BakingViewSettings viewSettings;
 | 
						|
 | 
						|
    [ExportGroup( "Object")]
 | 
						|
    
 | 
						|
    [Export]
 | 
						|
    public Node3D target;
 | 
						|
    
 | 
						|
    [Export]
 | 
						|
    public bool autoTargetPivot = false;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Vector3 targetPivot;
 | 
						|
 | 
						|
    
 | 
						|
    public float cameraZoom
 | 
						|
    {
 | 
						|
      get 
 | 
						|
      {
 | 
						|
        if ( viewSettings.fovDistance is AutoDistance_BakingFDSettings ad )
 | 
						|
        {
 | 
						|
          return ad.zoom;
 | 
						|
        }
 | 
						|
        
 | 
						|
        if ( viewSettings.fovDistance is AutoDistance_BakingFDSettings afd )
 | 
						|
        {
 | 
						|
          return afd.zoom;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        return 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    [ExportGroup( "Output")]
 | 
						|
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public string outputDirectory;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public string outputFileName;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public float outputQuality = 1f;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Vector2 outputTextureSize = new Vector2( 2048, 2048 );    
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public bool showOutputTexture = false;
 | 
						|
 | 
						|
 | 
						|
    [ExportGroup("Read Only")]
 | 
						|
    [Export]
 | 
						|
    public SubViewport X_bakingViewport;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Node3D X_bakingTargetContainer;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Node X_views;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public WorldEnvironment X_worldEnvironment;    
 | 
						|
  
 | 
						|
    [Export]
 | 
						|
    public MeshInstance3D X_outputMesh;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public TextureMerger X_textureMerger;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public TextureDilate X_textureDilate;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public SetBakingMaterials X_setBakingMaterials;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public MeshInstance3D X_texturePreview;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public CsgMesh3D X_depthMapper;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Texture2D X_bakedTextureAlbedo;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Texture2D X_bakedTextureNormal;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Texture2D X_bakedTextureORM;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Texture2D X_bakedTextureDepth;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public bool X_baking = false;
 | 
						|
 | 
						|
    public async Task Bake()
 | 
						|
    {
 | 
						|
      Initialize();
 | 
						|
      await CreateBakes();
 | 
						|
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    public void Initialize()
 | 
						|
    {     
 | 
						|
      this.LogInfo( "Initializing" );
 | 
						|
      
 | 
						|
      Nodes.RemoveAndDeleteChildren( this );
 | 
						|
 | 
						|
      _bakerCameras = null;
 | 
						|
 | 
						|
      X_bakingViewport = this.CreateChild<SubViewport>( "Multi Baker Viewport" );
 | 
						|
 | 
						|
      X_bakingViewport.Size = (Vector2I) outputTextureSize;
 | 
						|
      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" );
 | 
						|
 | 
						|
      X_textureMerger = this.CreateChild<TextureMerger>( "Texture Merger" );
 | 
						|
      X_textureMerger.multiBaker = this;
 | 
						|
      // X_textureMerger.dilationRadius = dilationRadius;
 | 
						|
      X_textureMerger.Initialize();
 | 
						|
 | 
						|
      X_textureDilate = this.CreateChild<TextureDilate>( "Texture Dilate" );
 | 
						|
      X_textureDilate.viewport = X_textureMerger.X_textureMergerViewport;
 | 
						|
 | 
						|
      X_outputMesh = this.CreateChild<MeshInstance3D>( "Output Mesh" );
 | 
						|
 | 
						|
      X_texturePreview = this.CreateChild<MeshInstance3D>( "Texture Preview" );
 | 
						|
      X_texturePreview.Mesh = new QuadMesh();
 | 
						|
      X_texturePreview.Scale = Vector3.One * 100;
 | 
						|
 | 
						|
      var pm = new StandardMaterial3D();
 | 
						|
      pm.Transparency = BaseMaterial3D.TransparencyEnum.AlphaScissor;
 | 
						|
      pm.ResourceLocalToScene = true;
 | 
						|
 | 
						|
      var vt = new ViewportTexture();
 | 
						|
      vt.ViewportPath = X_textureMerger.X_textureMergerViewport.GetPath();
 | 
						|
      pm.AlbedoTexture = vt;
 | 
						|
 | 
						|
      X_setBakingMaterials = this.CreateChild<SetBakingMaterials>( "Set Baking Materials" );
 | 
						|
 | 
						|
      Materials.Set( X_texturePreview, pm );                  
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    public async Task<Texture2D> GrabDilatedTexture( bool srgb, bool alpha, bool mipmaps )
 | 
						|
    {
 | 
						|
      X_textureDilate.viewport = X_textureMerger.X_textureMergerViewport;
 | 
						|
      var maxRadius = X_textureMerger.GetMaxTextureDimension() / 2;
 | 
						|
      X_textureDilate.SetDilationRadius( maxRadius );
 | 
						|
      var texture = await X_textureDilate.Grab( srgb, alpha, mipmaps );
 | 
						|
 | 
						|
      return texture;
 | 
						|
    }   
 | 
						|
 | 
						|
    bool _targetIsInInitialState = false;
 | 
						|
 | 
						|
    public void SetTargetDirty()
 | 
						|
    {
 | 
						|
      _targetIsInInitialState = false;
 | 
						|
    }
 | 
						|
 | 
						|
    public void ResetOriginalTargetState()
 | 
						|
    {
 | 
						|
      if ( _targetIsInInitialState )
 | 
						|
      {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      // _bakerCameras.ForEach( b => b.viewport.en)
 | 
						|
 | 
						|
      Nodes.RemoveAndDeleteChildren( X_bakingTargetContainer );
 | 
						|
      var root = (Node3D)target.DeepCopyTo( X_bakingTargetContainer );
 | 
						|
 | 
						|
      root.GlobalPosition = Vector3.Zero;
 | 
						|
      root.SetGlobalQuaternion( Quaternion.Identity );
 | 
						|
      root.Scale = Vector3.One;
 | 
						|
 | 
						|
 | 
						|
      _targetIsInInitialState = true;
 | 
						|
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    bool _compositorsClean = false;
 | 
						|
 | 
						|
    public void SetCompositorsDirty()
 | 
						|
    {
 | 
						|
      _compositorsClean = false;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    public void ResetCompositorStates()
 | 
						|
    {
 | 
						|
      X_worldEnvironment.Compositor = null;
 | 
						|
      _compositorsClean = true;
 | 
						|
    }
 | 
						|
 | 
						|
    public void AddCompositorEffect( CompositorEffect compositorEffect )
 | 
						|
    {
 | 
						|
      if ( X_worldEnvironment.Compositor == null )
 | 
						|
      {
 | 
						|
        X_worldEnvironment.Compositor = new Compositor();
 | 
						|
      }
 | 
						|
 | 
						|
      var compositorEffects = X_worldEnvironment.Compositor.CompositorEffects;
 | 
						|
      compositorEffects.Add( compositorEffect );
 | 
						|
 | 
						|
      X_worldEnvironment.Compositor.CompositorEffects = compositorEffects;
 | 
						|
 | 
						|
      _compositorsClean = false; 
 | 
						|
    } 
 | 
						|
   
 | 
						|
 | 
						|
    public async Task CreateBakes()
 | 
						|
    {
 | 
						|
      if ( X_baking )
 | 
						|
      {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      X_baking = true;
 | 
						|
 | 
						|
 | 
						|
      try
 | 
						|
      {
 | 
						|
        bakeMode.multiBaker = this;
 | 
						|
 | 
						|
        _targetIsInInitialState = false;
 | 
						|
 | 
						|
        this.LogInfo( "Started baking" );
 | 
						|
 | 
						|
        ResetOriginalTargetState();
 | 
						|
 | 
						|
        if ( _bakerCameras == null || _bakerCameras.Count != GetNumViews() )
 | 
						|
        {
 | 
						|
          CreateBakerCameras();
 | 
						|
          await this.RequestNextFrame();
 | 
						|
        }
 | 
						|
 | 
						|
        SetupBakerCameras();
 | 
						|
 | 
						|
        this.LogInfo( "Bake Cameras set up" );
 | 
						|
        await this.RequestNextFrame();
 | 
						|
 | 
						|
        this.LogInfo( "Render passes" );
 | 
						|
        var passes = textureBaker.GetPasses( this );
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        bakeMode.CreateMaterial( passes );
 | 
						|
 | 
						|
 | 
						|
        this.LogInfo( "Prepared baking modes" );
 | 
						|
        X_textureMerger.textureSize = outputTextureSize;
 | 
						|
        X_textureMerger.Initialize();
 | 
						|
        X_textureMerger.CreateLayout();
 | 
						|
 | 
						|
 | 
						|
        for ( int i = 0; i < passes.Count; i++ )
 | 
						|
        { 
 | 
						|
          ResetOriginalTargetState();
 | 
						|
          
 | 
						|
          _bakerCameras.ForEach( b => b.ApplyCameraSettings() );
 | 
						|
          await passes[ i ].Bake();
 | 
						|
          await this.RequestNextFrame();
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        bakeMode.AssignMaterial( passes );
 | 
						|
 | 
						|
        X_outputMesh.GlobalPosition = target.GlobalPosition;// - targetPivot;
 | 
						|
 | 
						|
        X_outputMesh.Scale = Vector3.One * cameraZoom;
 | 
						|
 | 
						|
       
 | 
						|
        this.LogInfo( "All Baking done" );
 | 
						|
 | 
						|
      }
 | 
						|
      catch ( System.Exception e )
 | 
						|
      {
 | 
						|
        this.LogError( "Baking failed" );
 | 
						|
        this.LogError( e );
 | 
						|
      }
 | 
						|
 | 
						|
      if ( cleanUpAfterBaking )
 | 
						|
      {
 | 
						|
        this.LogInfo( "Cleaning Up" );
 | 
						|
        CleanUp();
 | 
						|
      }
 | 
						|
 | 
						|
 | 
						|
      X_baking = false;
 | 
						|
      return;
 | 
						|
 | 
						|
    }
 | 
						|
    
 | 
						|
    public List<SubViewport> GetAllViewports()
 | 
						|
    {
 | 
						|
      return Nodes.GetAll<SubViewport>( X_views, null, false );
 | 
						|
    }
 | 
						|
 | 
						|
    public void CleanUp()
 | 
						|
    {
 | 
						|
      var mesh = X_outputMesh;
 | 
						|
      mesh.Reparent( GetParent(), true );
 | 
						|
 | 
						|
      X_bakingViewport = null;
 | 
						|
 | 
						|
      X_bakingTargetContainer = null;
 | 
						|
 | 
						|
      X_views = null;
 | 
						|
 | 
						|
      X_worldEnvironment = null;    
 | 
						|
    
 | 
						|
      X_outputMesh = null;
 | 
						|
 | 
						|
      X_textureMerger = null;
 | 
						|
 | 
						|
      X_textureDilate = null;
 | 
						|
 | 
						|
      X_setBakingMaterials = null;
 | 
						|
 | 
						|
      X_texturePreview = null;
 | 
						|
 | 
						|
      X_depthMapper = null;
 | 
						|
 | 
						|
      X_bakedTextureAlbedo = null;
 | 
						|
 | 
						|
      X_bakedTextureNormal = null;
 | 
						|
 | 
						|
      X_bakedTextureORM = null;
 | 
						|
 | 
						|
      X_bakedTextureDepth = null;
 | 
						|
 | 
						|
      Nodes.RemoveAndDeleteChildren( this );
 | 
						|
 | 
						|
      mesh.Reparent( this, true );
 | 
						|
    }
 | 
						|
 | 
						|
    
 | 
						|
 | 
						|
    public int GetNumViews()
 | 
						|
    {
 | 
						|
      return bakeMode.GetNumViews();
 | 
						|
    }
 | 
						|
 | 
						|
    List<Baker> _bakerCameras;
 | 
						|
 | 
						|
    public List<Baker> bakerCameras => _bakerCameras;
 | 
						|
 | 
						|
    public void CreateBakerCameras()
 | 
						|
    {    
 | 
						|
 | 
						|
      Nodes.RemoveAndDeleteChildren( X_views );
 | 
						|
 | 
						|
      var numViews = GetNumViews();
 | 
						|
 | 
						|
      _bakerCameras = new List<Baker>();
 | 
						|
 | 
						|
      var alginment = TextureMerger.ComputeTextureAlignment( numViews );
 | 
						|
      var minViewsPerAxis = Mathf.Max( alginment.X, alginment.Y );
 | 
						|
 | 
						|
      this.LogInfo( "Size per Baker:", "nv", numViews, "al", alginment, "mvpa", minViewsPerAxis,"s", (Vector2I) ( outputTextureSize / minViewsPerAxis ) );
 | 
						|
      
 | 
						|
      for ( int i = 0; i < numViews; i++ )
 | 
						|
      {
 | 
						|
        var userIndex = ( i + 1 );
 | 
						|
        var bakingView = X_views.CreateChild<SubViewport>( "Baking View " + userIndex );
 | 
						|
        bakingView.TransparentBg = true;
 | 
						|
        bakingView.Size = (Vector2I) ( outputTextureSize / minViewsPerAxis );
 | 
						|
        
 | 
						|
        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.viewport.Msaa3D = Viewport.Msaa.Msaa8X;
 | 
						|
        
 | 
						|
 | 
						|
 | 
						|
        
 | 
						|
 | 
						|
        _bakerCameras.Add( baker );
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    public void SetupBakerCameras()
 | 
						|
    {
 | 
						|
      var numViews = GetNumViews();
 | 
						|
      var minViewsPerAxis = TextureMerger.ComputeTextureAlignment( numViews ).Y;
 | 
						|
 | 
						|
      X_bakingViewport.Size = (Vector2I) outputTextureSize;
 | 
						|
 | 
						|
      for ( int i = 0; i < numViews; i++ )
 | 
						|
      {
 | 
						|
        var baker = _bakerCameras[ i ];
 | 
						|
        var bakingView = baker.viewport as SubViewport;
 | 
						|
        bakingView.Size = (Vector2I) ( outputTextureSize / minViewsPerAxis );
 | 
						|
      }
 | 
						|
 | 
						|
      _targetBoundingSphere = null;
 | 
						|
      _targetBoundingBox = null;
 | 
						|
 | 
						|
      ComputeBoundingSphere();
 | 
						|
 | 
						|
      if ( _targetBoundingSphere == null )
 | 
						|
      {
 | 
						|
        this.LogError( "No bounding sphere created, ensure there are visible targets" );
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      ComputeCameraViewSettings();
 | 
						|
 | 
						|
      bakeMode.CreateBakers();
 | 
						|
 | 
						|
      
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    Sphere _targetBoundingSphere;
 | 
						|
    Box3 _targetBoundingBox;
 | 
						|
 | 
						|
    Sphere targetBoundingSphere 
 | 
						|
    {
 | 
						|
      get 
 | 
						|
      {
 | 
						|
        if ( _targetBoundingSphere == null )
 | 
						|
        {
 | 
						|
          ComputeBoundingSphere();
 | 
						|
        }
 | 
						|
 | 
						|
        return _targetBoundingSphere;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    void ComputeBoundingSphere()
 | 
						|
    {
 | 
						|
      if ( X_bakingTargetContainer.GetChildCount() == 0 )
 | 
						|
      {
 | 
						|
        _targetBoundingSphere = null;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      var firstChild = X_bakingTargetContainer.GetChild( 0 ) as Node3D;
 | 
						|
 | 
						|
      if ( firstChild == null )
 | 
						|
      {
 | 
						|
        _targetBoundingSphere = null;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      if ( ! firstChild.Visible )
 | 
						|
      {
 | 
						|
        firstChild.Visible = true;
 | 
						|
      }
 | 
						|
 | 
						|
      _targetBoundingBox = X_bakingTargetContainer.GetWorldBounds();
 | 
						|
 | 
						|
      if ( _targetBoundingBox == null )
 | 
						|
      {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      if ( autoTargetPivot )
 | 
						|
      { 
 | 
						|
        Box3 box = target.GetWorldBounds();
 | 
						|
 | 
						|
        if ( box == null )
 | 
						|
        {
 | 
						|
          _targetBoundingSphere = null;
 | 
						|
          return;
 | 
						|
        }
 | 
						|
 | 
						|
        targetPivot = new Vector3( 0, -box.center.Y, 0 );
 | 
						|
      }
 | 
						|
      
 | 
						|
      _targetBoundingSphere = Sphere.ContainingBox( _targetBoundingBox );
 | 
						|
    }
 | 
						|
 | 
						|
    float _cameraFOV = 0;
 | 
						|
    float _cameraDistance = 0;
 | 
						|
    float _outputScale = 1;
 | 
						|
    
 | 
						|
    void ComputeCameraViewSettings()
 | 
						|
    {
 | 
						|
      var fovDistance = viewSettings.fovDistance;
 | 
						|
      var size = targetBoundingSphere.radius;
 | 
						|
 | 
						|
      var computeScale = false;
 | 
						|
 | 
						|
      if ( fovDistance is AutoDistance_BakingFDSettings ad )
 | 
						|
      {
 | 
						|
        size = ad.sizeEstimation == _XX_BakingFDSettings.SizeEstimationType.Bounding_Sphere ? 
 | 
						|
                targetBoundingSphere.radius : ( _targetBoundingBox.size.Y / 2f );
 | 
						|
        computeScale = true;
 | 
						|
      } 
 | 
						|
 | 
						|
      if ( fovDistance is AutoFOVDistance_BakingFDSettings afd )
 | 
						|
      {
 | 
						|
        size = afd.sizeEstimation == _XX_BakingFDSettings.SizeEstimationType.Bounding_Sphere ? 
 | 
						|
                targetBoundingSphere.radius : ( _targetBoundingBox.size.Y / 2f );
 | 
						|
        computeScale = true;
 | 
						|
      } 
 | 
						|
      
 | 
						|
      var fd = fovDistance.ComputeFOVDistance( size / 2f );
 | 
						|
 | 
						|
      _cameraFOV = Mathf.Clamp( fd.X, 1, 179 );
 | 
						|
      _cameraDistance = fd.Y;
 | 
						|
      _outputScale = computeScale ? Cameras.ComputeCameraFittingScale( _cameraFOV, _cameraDistance ) : 1;
 | 
						|
 | 
						|
      RJLog.Log( "Computed FOV Distance", _cameraFOV, _cameraDistance );
 | 
						|
    }
 | 
						|
    
 | 
						|
    public float GetCameraFOV()
 | 
						|
    {
 | 
						|
      return Mathf.Clamp( _cameraFOV, 1, 179 );
 | 
						|
    }
 | 
						|
 | 
						|
    public float GetCameraDistance()
 | 
						|
    {
 | 
						|
      return _cameraDistance;
 | 
						|
    }
 | 
						|
 | 
						|
    public float GetOutputScale()
 | 
						|
    {
 | 
						|
      return _outputScale;
 | 
						|
    }
 | 
						|
  }
 | 
						|
} |