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 MappingData { public Vector3 localPosition; public float parameter; public float weight; } public enum WeightsMappingMethod { Max_Distance } [Export] public Spline[] sourceSplines; [Export] public MeshInstance3D sourceMesh; [Export] public bool updateSourceMesh; [Export] public Spline[] deformerSplines; [Export] public MeshInstance3D outputMesh; [Export( PropertyHint.Range, "0,1")] public float targetSmoothing = 0f; [Export] public float splineMaxDistance = 1000f; [Export] public float splineMinDistance = 0f; [Export] public int splineMappingResolution = 40; [Export] public int splineMappingDepth = 3; [Export] public bool update = false; [Export] public bool updateAlways = false; MappingData[] deformerMappings; MeshGeometry meshGeometry; MappingData CreateSourceMapping( Spline s, Vector3 worldPosition ) { var curve = s.GetCurve(); var closestParameter = curve.GetClosestParameterTo( worldPosition, splineMappingResolution, splineMappingDepth ); var pose = curve.GetPoseByPointIndex( closestParameter ); var localPosition = pose.ApplyInverse( worldPosition ); var mappingData = new MappingData(); mappingData.localPosition = localPosition; mappingData.parameter = 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 MappingData[ 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 mapping = CreateSourceMapping( sourceSplines[ j ], vertex ); var curve = sourceSplines[ j ].GetCurve(); var distance = curve.PositionAt( mapping.parameter ) - 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 vertexPosition = 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.parameter, targetSmoothing * 0.5f, 2, targetSmoothing ); vertexPosition += pose.Apply( mapping.localPosition ) * mapping.weight; } else { var pose = curve.PoseAt( mapping.parameter ); pose.ApplyTwist( curve.TwistAt( mapping.parameter ) ); vertexPosition += pose.Apply( mapping.localPosition ) * mapping.weight; } } cloned.vertices[ i ] = vertexPosition; } // 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 } }