rj-action-library/Runtime/Procedural/Mesh/Modifiers/SplinesDeformer/SplinesDeformerModifier.cs

129 lines
4.2 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
namespace Rokojori
{
public class SplinesDeformerMappingData
{
public Vector3 localPosition;
public Vector3 localNormal;
public float normalizedSplineParameter;
public float weight;
}
public class SplinesDeformModifier
{
public SplineCurve[] sourceSplines = new SplineCurve[ 0 ];
public SplineCurve[] targetSplines = new SplineCurve[ 0 ];
public SplinesDeformerSettings settings;
public MeshGeometry Modify( MeshGeometry mg )
{
var mappings = CreateSourceMappings( mg );
return CreateDeformed( mg, mappings );
}
SplinesDeformerMappingData CreateSourceMapping( SplineCurve curve, Vector3 worldPosition, Vector3 worldNormal )
{
var closestParameter = curve.GetClosestParameterTo( worldPosition, settings.splineMappingResolution, settings.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 SplinesDeformerMappingData();
mappingData.localPosition = localPosition;
mappingData.localNormal = localNormal;
mappingData.normalizedSplineParameter = closestParameter;
mappingData.weight = 0;
return mappingData;
}
SplinesDeformerMappingData[] CreateSourceMappings( MeshGeometry meshGeometry)
{
var mappingSize = meshGeometry.vertices.Count * sourceSplines.Length;
var deformerMappings = new SplinesDeformerMappingData[ mappingSize];
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 curve = sourceSplines[ j ];
var mapping = CreateSourceMapping( curve, vertex, normal );
var distance = curve.PositionAt( mapping.normalizedSplineParameter ) - vertex;
var inverseWeight = MathX.NormalizeClamped( distance.Length(), settings.splineMinDistance, settings.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;
}
}
}
return deformerMappings;
}
MeshGeometry CreateDeformed( MeshGeometry meshGeometry, SplinesDeformerMappingData[] mappingData )
{
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 < targetSplines.Length; j++ )
{
var mapping = mappingData[ i * targetSplines.Length + j ];
var curve = targetSplines[ j ];
if ( settings.targetSmoothing > 0 )
{
var pose = curve.SmoothedPoseAt( mapping.normalizedSplineParameter, settings.targetSmoothing * 0.5f, 2, settings.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();
}
return cloned;
}
}
}