226 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C#
		
	
	
	
 | 
						|
using Godot;
 | 
						|
using Rokojori;
 | 
						|
using System.Collections.Generic;
 | 
						|
 | 
						|
namespace Rokojori
 | 
						|
{  
 | 
						|
  
 | 
						|
  [Tool]
 | 
						|
  [GlobalClass, Icon("res://addons/rokojori_action_library/Icons/Spline.svg") ]
 | 
						|
  public partial class Deformer : Node3D
 | 
						|
#if TOOLS 
 | 
						|
 , GizmoDrawer
 | 
						|
#endif
 | 
						|
 | 
						|
  {
 | 
						|
    public class DeformerMappingData
 | 
						|
    {
 | 
						|
      public Vector3 localPosition;
 | 
						|
      public Vector3 localNormal;
 | 
						|
      public float normalizedSplineParameter;
 | 
						|
      public float weight;
 | 
						|
    }
 | 
						|
 | 
						|
    public enum WeightsMappingMethod
 | 
						|
    {
 | 
						|
      Max_Distance
 | 
						|
    }
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public bool update = false;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public bool updateAlways = false;
 | 
						|
 | 
						|
    [ExportGroup( "Input")]
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Spline[] sourceSplines = new Spline[ 0 ];
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public MeshInstance3D sourceMesh;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public bool updateSourceMesh;
 | 
						|
 | 
						|
    [ExportGroup( "Output")]
 | 
						|
    [Export]
 | 
						|
    public Spline[] deformerSplines = new Spline[ 0 ];
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public MeshInstance3D outputMesh;
 | 
						|
 | 
						|
    [Export( PropertyHint.Range, "0,1")]
 | 
						|
    public float targetSmoothing = 0f;
 | 
						|
 | 
						|
 | 
						|
    [ExportGroup( "Spline Settings")]
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public float splineMaxDistance = 1000f;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public float splineMinDistance = 0f;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public int splineMappingResolution = 40;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public int splineMappingDepth = 3;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    DeformerMappingData[] deformerMappings = new DeformerMappingData[ 0 ];
 | 
						|
    MeshGeometry meshGeometry;
 | 
						|
 | 
						|
    DeformerMappingData CreateSourceMapping( Spline s, Vector3 worldPosition, Vector3 worldNormal )
 | 
						|
    {
 | 
						|
      var curve = s.GetCurve();
 | 
						|
      var closestParameter = curve.GetClosestParameterTo( worldPosition, splineMappingResolution, splineMappingDepth );
 | 
						|
      var pointIndex = curve.NormalizedToPointIndex( closestParameter );
 | 
						|
      var pose = curve.GetPoseByPointIndex( pointIndex );
 | 
						|
      
 | 
						|
      var localPosition = pose.ApplyInverse( worldPosition );
 | 
						|
      var localNormal   = pose.rotation.Inverse() * worldNormal;
 | 
						|
 | 
						|
      var mappingData = new DeformerMappingData();
 | 
						|
      
 | 
						|
      mappingData.localPosition = localPosition;
 | 
						|
      mappingData.localNormal   = localNormal;
 | 
						|
      mappingData.normalizedSplineParameter = closestParameter;
 | 
						|
      mappingData.weight = 0;
 | 
						|
 | 
						|
      return mappingData;
 | 
						|
    }
 | 
						|
 | 
						|
    void CreateSourceMappings()
 | 
						|
    {
 | 
						|
      meshGeometry = MeshGeometry.From( sourceMesh  );
 | 
						|
 | 
						|
      var mappingSize = meshGeometry.vertices.Count * sourceSplines.Length;
 | 
						|
 | 
						|
      if ( deformerMappings == null || deformerMappings.Length != mappingSize )
 | 
						|
      {
 | 
						|
        deformerMappings = new DeformerMappingData[ mappingSize];
 | 
						|
      }
 | 
						|
 | 
						|
      this.LogInfo( "Mappings:", deformerMappings.Length, meshGeometry );
 | 
						|
       
 | 
						|
      for ( int i = 0; i < meshGeometry.vertices.Count; i++ )
 | 
						|
      {
 | 
						|
        var weights = 0f;
 | 
						|
 | 
						|
        for ( int j = 0; j < sourceSplines.Length; j++ )
 | 
						|
        {
 | 
						|
          var vertex = meshGeometry.vertices[ i ];
 | 
						|
          var normal = meshGeometry.normals[ i ];
 | 
						|
          var mapping = CreateSourceMapping( sourceSplines[ j ], vertex, normal );
 | 
						|
          var curve = sourceSplines[ j ].GetCurve();
 | 
						|
          var distance = curve.PositionAt( mapping.normalizedSplineParameter ) - vertex;
 | 
						|
          var inverseWeight = MathX.NormalizeClamped( distance.Length(), splineMinDistance, splineMaxDistance );
 | 
						|
          mapping.weight = 1f - inverseWeight; 
 | 
						|
          weights += mapping.weight;
 | 
						|
          deformerMappings[ i * sourceSplines.Length + j  ] = mapping;
 | 
						|
        }
 | 
						|
 | 
						|
        if ( weights > 0 && weights != 1f )
 | 
						|
        {
 | 
						|
          for ( int j = 0; j < sourceSplines.Length; j++ )
 | 
						|
          {
 | 
						|
            var mapping = deformerMappings[ i * sourceSplines.Length + j  ];
 | 
						|
            mapping.weight /= weights;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    void CreateDeformed()
 | 
						|
    {
 | 
						|
      // RJLog.Log( "CreateDeformed" );
 | 
						|
 | 
						|
      var cloned = meshGeometry.Clone();
 | 
						|
 | 
						|
      for ( int i = 0; i < cloned.vertices.Count; i++ )
 | 
						|
      {
 | 
						|
        var vertex = Vector3.Zero;
 | 
						|
        var normal = Vector3.Zero;
 | 
						|
 | 
						|
        for ( int j = 0; j < deformerSplines.Length; j++ )
 | 
						|
        {
 | 
						|
          var mapping = deformerMappings[ i * deformerSplines.Length + j  ];
 | 
						|
          var curve = deformerSplines[ j ].GetCurve();
 | 
						|
 | 
						|
          if ( targetSmoothing > 0 )
 | 
						|
          {            
 | 
						|
            var pose = curve.SmoothedPoseAt( mapping.normalizedSplineParameter, targetSmoothing * 0.5f, 2, targetSmoothing ); 
 | 
						|
            pose.ApplyTwist( curve.TwistAt( mapping.normalizedSplineParameter ) );
 | 
						|
            vertex += pose.Apply( mapping.localPosition ) * mapping.weight;
 | 
						|
            normal += pose.rotation * mapping.localNormal * mapping.weight;
 | 
						|
 | 
						|
          }
 | 
						|
          else
 | 
						|
          { 
 | 
						|
            var pose = curve.PoseAt( mapping.normalizedSplineParameter );
 | 
						|
            pose.ApplyTwist( curve.TwistAt( mapping.normalizedSplineParameter ) );
 | 
						|
            vertex += pose.Apply( mapping.localPosition ) * mapping.weight;
 | 
						|
            normal += pose.rotation * mapping.localNormal * mapping.weight;
 | 
						|
          } 
 | 
						|
          
 | 
						|
        }
 | 
						|
 | 
						|
        cloned.vertices[ i ] = vertex;
 | 
						|
        cloned.normals[ i ] = normal.Normalized();
 | 
						|
      }
 | 
						|
 | 
						|
      // RJLog.Log( cloned.vertices.Count );
 | 
						|
      outputMesh.Mesh = cloned.GenerateMesh();
 | 
						|
    }
 | 
						|
 | 
						|
#if TOOLS
 | 
						|
    
 | 
						|
    public void DrawGizmo( EditorNode3DGizmoPlugin gizmoPlugin, EditorNode3DGizmo gizmo )
 | 
						|
    {
 | 
						|
 | 
						|
      gizmo.Clear();
 | 
						|
 | 
						|
 | 
						|
    }
 | 
						|
    
 | 
						|
    public override void _Process( double delta )
 | 
						|
    {
 | 
						|
      if ( updateSourceMesh )
 | 
						|
      {        
 | 
						|
        updateSourceMesh = false;
 | 
						|
 | 
						|
        CreateSourceMappings();
 | 
						|
      }
 | 
						|
 | 
						|
      if ( update || updateAlways )
 | 
						|
      {
 | 
						|
        update = false;
 | 
						|
        UpdateMesh();
 | 
						|
      }
 | 
						|
 | 
						|
      UpdateGizmos();
 | 
						|
    }
 | 
						|
 | 
						|
    void UpdateMesh()
 | 
						|
    {
 | 
						|
      if ( meshGeometry == null )
 | 
						|
      {
 | 
						|
        CreateSourceMappings();
 | 
						|
      }
 | 
						|
 | 
						|
      if ( meshGeometry == null )
 | 
						|
      {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      CreateDeformed();
 | 
						|
    }
 | 
						|
 | 
						|
#endif
 | 
						|
  }
 | 
						|
} |