diff --git a/Runtime/Math/Geometry/Curve3.cs b/Runtime/Math/Geometry/Curve3.cs index fa5e1b6..4e2c752 100644 --- a/Runtime/Math/Geometry/Curve3.cs +++ b/Runtime/Math/Geometry/Curve3.cs @@ -35,7 +35,41 @@ namespace Rokojori return Pose.Create( PositionAt( t ), RotationAt( t ) ); } + public virtual Pose SmoothedPoseAt( float t, float stepSize = 0.01f, int steps = 1, float fallOff = 0.5f ) + { + var poses = new List(); + poses.Add( PoseAt( t ) ); + + var weigths = new List(); + + var weight = 1f; + weigths.Add( weight ); + + var sumWeigths = weight; + + for ( int i = 0; i < steps; i++ ) + { + var poseBefore = PoseAt( t - stepSize * i ); + var poseAfter = PoseAt( t + stepSize * i ); + + poses.Add( poseBefore ); + poses.Add( poseAfter ); + + weight *= fallOff; + weigths.Add( weight ); + weigths.Add( weight ); + + sumWeigths += weight * 2; + } + + for ( int i = 0; i < weigths.Count; i++ ) + { + weigths[ i ] = weigths[ i ] / sumWeigths; + } + + return Pose.Merge( poses, weigths ); + } public virtual void SampleMultiple( int numSamples, List output ) { diff --git a/Runtime/Math/Geometry/Pose.cs b/Runtime/Math/Geometry/Pose.cs index 8469b31..67e1f8e 100644 --- a/Runtime/Math/Geometry/Pose.cs +++ b/Runtime/Math/Geometry/Pose.cs @@ -18,6 +18,11 @@ namespace Rokojori } } + public void ApplyTwist( float twist ) + { + rotation = rotation * Math3D.RotateZ( twist ); + } + Vector3 _position = Vector3.Zero; public Vector3 position { @@ -105,6 +110,11 @@ namespace Rokojori } + public void LookAt( Vector3 forward, Vector3 up ) + { + rotation = Math3D.LookRotation( forward, up ); + } + public static Pose From( Node3D node3D ) { var p = new Pose(); @@ -137,6 +147,11 @@ namespace Rokojori return p; } + public override string ToString() + { + return "Pose{" + RJLog.Stringify( position ) + RJLog.Stringify( rotation ) + "}"; + } + public Vector3 ApplyInverse( Vector3 p ) { p -= position; @@ -170,5 +185,31 @@ namespace Rokojori } + public static Pose Merge( List poses, List weights = null ) + { + var position = Vector3.Zero; + var up = Vector3.Zero; + var forward = Vector3.Zero; + + if ( weights == null ) + { + weights = new List( poses.Count ); + + for ( int i= 0; i < poses.Count; i++ ) + { + weights[ i ] = 1f / poses.Count; + } + } + + for ( int i = 0; i < poses.Count; i++ ) + { + position += poses[ i ].position * weights[ i ]; + up += poses[ i ].up * weights[ i ]; + forward += poses[ i ].forward * weights[ i ]; + } + + return Create( position, Math3D.LookRotation( forward, up ) ); + } + } } \ No newline at end of file diff --git a/Runtime/Math/Geometry/SplineCurve.cs b/Runtime/Math/Geometry/SplineCurve.cs index 2cce770..1022dc5 100644 --- a/Runtime/Math/Geometry/SplineCurve.cs +++ b/Runtime/Math/Geometry/SplineCurve.cs @@ -114,7 +114,7 @@ namespace Rokojori { return Vector3.Zero; } - + var max = _points[ 0 ].position; points.ForEach( p => max = max.Max( p.position ) ); @@ -236,6 +236,19 @@ namespace Rokojori var lerpAmount = pointIndex - lower; return _points[ lower ].rotation.Slerp( _points[ higher ].rotation, lerpAmount ); + } + + public Transform3D GetTransformAt( float normalized ) + { + return GetTransformByPointIndex( NormalizedToPointIndex( normalized ) ); + } + + public Transform3D GetTransformByPointIndex( float pointIndex ) + { + var pose = GetPoseByPointIndex( pointIndex ); + var scale = GetScaleByPointIndex( pointIndex ); + + return Math3D.TRS( pose, scale ); } public Vector3 GetScaleByPointIndex( float pointIndex ) @@ -255,6 +268,11 @@ namespace Rokojori return _points[ lower ].scale.Lerp( _points[ higher ].scale, lerpAmount ); } + public float TwistAt( float normalized ) + { + return SmoothStepTwistByPointIndex( NormalizedToPointIndex( normalized ) ); + } + public float GetTwistByPointIndex( float pointIndex ) { if ( pointIndex <= 0 || pointIndex >= ( _points.Count - 1 ) ) diff --git a/Runtime/Math/Math3D.cs b/Runtime/Math/Math3D.cs index b59440a..e5367da 100644 --- a/Runtime/Math/Math3D.cs +++ b/Runtime/Math/Math3D.cs @@ -8,6 +8,19 @@ namespace Rokojori { public static class Math3D { + public static Transform3D TRS( Vector3 translation, Quaternion rotation, Vector3 scale ) + { + var trsf = new Transform3D( new Basis( rotation ), translation ); + trsf.ScaledLocal( scale ); + + return trsf; + } + + public static Transform3D TRS( Pose pose, Vector3 scale ) + { + return TRS( pose.position, pose.rotation, scale ); + } + public static Vector3 Clamp01( Vector3 v ) { return new Vector3( diff --git a/Runtime/Procedural/MeshGeometry.cs b/Runtime/Procedural/MeshGeometry.cs index caebfc3..b64daee 100644 --- a/Runtime/Procedural/MeshGeometry.cs +++ b/Runtime/Procedural/MeshGeometry.cs @@ -20,6 +20,67 @@ namespace Rokojori public int numTriangles => indices.Count / 3; + public static MeshGeometry From( MeshInstance3D meshInstance3D ) + { + return From( (ArrayMesh)meshInstance3D.Mesh, meshInstance3D.GlobalTransform ); + } + + public static MeshGeometry From( ArrayMesh mesh, Transform3D? trsf = null ) + { + var mg = new MeshGeometry(); + + var arrays = mesh.SurfaceGetArrays( 0 ); + + var vertices = arrays[ (int) Mesh.ArrayType.Vertex ]; + + if ( Variant.Type.Nil != vertices.VariantType ) + { + mg.vertices = new List( vertices.AsVector3Array() ); + } + + var normals = arrays[ (int) Mesh.ArrayType.Normal ]; + + if ( Variant.Type.Nil != normals.VariantType ) + { + mg.normals = new List( normals.AsVector3Array() ); + } + + var uvs = arrays[ (int) Mesh.ArrayType.TexUV ]; + + if ( Variant.Type.Nil != uvs.VariantType ) + { + mg.uvs = new List( uvs.AsVector2Array() ); + } + + var uv2s = arrays[ (int) Mesh.ArrayType.TexUV2 ]; + + if ( Variant.Type.Nil != uv2s.VariantType ) + { + mg.uv2s = new List( uv2s.AsVector2Array() ); + } + + var colors = arrays[ (int) Mesh.ArrayType.Color ]; + + if ( Variant.Type.Nil != colors.VariantType ) + { + mg.colors = new List( colors.AsColorArray() ); + } + + var indices = arrays[ (int) Mesh.ArrayType.Index ]; + + if ( Variant.Type.Nil != indices.VariantType ) + { + mg.indices = new List( indices.AsInt32Array() ); + } + + if ( trsf != null ) + { + mg.ApplyTransform( (Transform3D)trsf ); + } + + return mg; + } + public void ApplyTransform( Transform3D trsf ) { for ( int i = 0; i < vertices.Count; i++ ) @@ -217,13 +278,14 @@ namespace Rokojori public MeshGeometry Clone() { var mg = new MeshGeometry(); - mg.vertices = Lists.Clone( vertices ); - mg.indices = Lists.Clone( indices ); - mg.normals = Lists.Clone( normals ); - mg.uvs = Lists.Clone( uvs ); - mg.uv2s = Lists.Clone( uv2s ); - mg.colors = Lists.Clone( colors ); + mg.vertices = Lists.Clone( vertices ); + mg.indices = Lists.Clone( indices ); + mg.normals = Lists.Clone( normals ); + + mg.uvs = Lists.Clone( uvs ); + mg.uv2s = Lists.Clone( uv2s ); + mg.colors = Lists.Clone( colors ); return mg; } diff --git a/Runtime/Procedural/Parametric/Deformer/Deformer.cs b/Runtime/Procedural/Parametric/Deformer/Deformer.cs new file mode 100644 index 0000000..89b3693 --- /dev/null +++ b/Runtime/Procedural/Parametric/Deformer/Deformer.cs @@ -0,0 +1,212 @@ + +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]; + } + + RJLog.Log( "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 + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Parametric/Spline/Spline.cs b/Runtime/Procedural/Parametric/Spline/Spline.cs index c48860a..4fc37e4 100644 --- a/Runtime/Procedural/Parametric/Spline/Spline.cs +++ b/Runtime/Procedural/Parametric/Spline/Spline.cs @@ -60,9 +60,7 @@ namespace Rokojori { if ( splineCurve == null ) { - var splinePoints = Nodes.GetDirectChildren( this ); - - + var splinePoints = Nodes.GetDirectChildren( this ); splineCurve = new SplineCurveCreator().Create( splinePoints, closed ); @@ -76,8 +74,6 @@ namespace Rokojori - - SplineCurve splineCurveXZ; public SplineCurve GetCurveXZ() @@ -181,51 +177,7 @@ namespace Rokojori int renderedResolution = -100; - void AutoOrientate() - { - var list = Nodes.GetDirectChildren( this ); - - if ( list.Count <= 1 ) - { - return; - } - - if ( SplineAutoOrientationMode.Next_Neighbor == autoOrientationMode ) - { - for ( int i = 0; i < list.Count; i++ ) - { - var sp = list[ i ]; - - if ( i == ( list.Count - 1 ) ) - { - if ( closed ) - { - var first = list[ 0 ]; - sp.LookAt( first.GlobalPosition, autoOrientationUp ); - } - - continue; - } - - var next = list[ i + 1 ]; - sp.LookAt( next.GlobalPosition, autoOrientationUp ); - } - } - else if ( SplineAutoOrientationMode.Tangent == autoOrientationMode ) - { - - for ( int i = 0; i < list.Count; i++ ) - { - var sp = list[ i ]; - - var tangentNext = SplineCurveCreator.GetTangentDirectionSmoothed( 1, autoOrientationTangentAdjustment, list, i, false, closed ); - - sp.LookAt( sp.GlobalPosition + tangentNext, autoOrientationUp ); - } - } - - - } + public override void _Process( double delta ) { @@ -276,5 +228,66 @@ namespace Rokojori UpdateGizmos(); } #endif + + void AutoOrientate() + { + var list = Nodes.GetDirectChildren( this ); + + if ( list.Count <= 1 ) + { + return; + } + + if ( SplineAutoOrientationMode.Next_Neighbor == autoOrientationMode ) + { + for ( int i = 0; i < list.Count; i++ ) + { + var sp = list[ i ]; + + if ( i == ( list.Count - 1 ) ) + { + if ( closed ) + { + var first = list[ 0 ]; + sp.LookAt( first.GlobalPosition, autoOrientationUp ); + } + + continue; + } + + var next = list[ i + 1 ]; + sp.LookAt( next.GlobalPosition, autoOrientationUp ); + } + } + else if ( SplineAutoOrientationMode.Tangent == autoOrientationMode ) + { + + for ( int i = 0; i < list.Count; i++ ) + { + var sp = list[ i ]; + + var tangentForward = Vector3.Zero; + + if ( i == ( list.Count - 1 ) && ! closed ) + { + tangentForward = - SplineCurveCreator.GetTangentDirectionSmoothed( 1, autoOrientationTangentAdjustment, list, i, true, false ); + } + else + { + tangentForward = SplineCurveCreator.GetTangentDirectionSmoothed( 1, autoOrientationTangentAdjustment, list, i, false, closed ); + } + + if ( tangentForward.Length() == 0 ) + { + continue; + } + + sp.LookAt( sp.GlobalPosition + tangentForward, autoOrientationUp ); + } + } + + + } + } } \ No newline at end of file diff --git a/Runtime/Tools/Lists.cs b/Runtime/Tools/Lists.cs index 9cf1259..25989ef 100644 --- a/Runtime/Tools/Lists.cs +++ b/Runtime/Tools/Lists.cs @@ -22,6 +22,7 @@ namespace Rokojori public float lerpAmount = 0.5f; } + public static List Clone( List data ) { if ( data == null ) @@ -29,7 +30,7 @@ namespace Rokojori return null; } - var cloned = new List(); + var cloned = new List( data.Count ); cloned.AddRange( data ); return cloned; }