Pose & Geometry Updates
This commit is contained in:
		
							parent
							
								
									31704452be
								
							
						
					
					
						commit
						1bcbcaaf99
					
				|  | @ -0,0 +1,12 @@ | |||
| using Godot; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace Rokojori | ||||
| {   | ||||
|   public class ResourceHelper | ||||
|   {  | ||||
|     public static void Overwrite<T>( List<string> overwriteProperties, T sourceResource, T targetResource ) where T:Resource | ||||
|     {   | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | @ -202,6 +202,20 @@ namespace Rokojori | |||
|       target.SetGlobalQuaternion( source.GetGlobalQuaternion() ); | ||||
|     } | ||||
| 
 | ||||
|     public void Rotate( Quaternion rotation ) | ||||
|     { | ||||
|       position = rotation * position; | ||||
|       this.rotation *= rotation; | ||||
|       this.rotation = this.rotation.Normalized(); | ||||
|     } | ||||
| 
 | ||||
|     public void RotateAround( Quaternion rotation, Vector3 pivot ) | ||||
|     { | ||||
|       position -= pivot; | ||||
|       Rotate( rotation ); | ||||
|       position += pivot; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static Pose Merge( List<Pose> poses, List<float> weights = null ) | ||||
|     { | ||||
|  |  | |||
|  | @ -32,6 +32,23 @@ namespace Rokojori | |||
|     public SplineCurveTangent tangentBefore = new SplineCurveTangent(); | ||||
|     public SplineCurveTangent tangentNext = new SplineCurveTangent(); | ||||
| 
 | ||||
|     public Pose pose  | ||||
|     {  | ||||
|       get => Pose.Create( position, rotation ); | ||||
|       set  | ||||
|       { | ||||
|         position = value.position; | ||||
|         rotation = value.rotation; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public void RotateAround( Quaternion rotation, Vector3 pivot ) | ||||
|     { | ||||
|       var p = pose; | ||||
|       p.RotateAround( rotation, pivot ); | ||||
|       pose = p; | ||||
|     } | ||||
| 
 | ||||
|     public SplineCurvePoint Clone() | ||||
|     { | ||||
|       var scp = new SplineCurvePoint(); | ||||
|  | @ -77,6 +94,11 @@ namespace Rokojori | |||
|       return cloned; | ||||
|     } | ||||
| 
 | ||||
|     public void LookTowards( Vector3 direction, Vector3 up ) | ||||
|     { | ||||
|       rotation = Math3D.LookRotation( direction, up ); | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
|   } | ||||
|  | @ -94,6 +116,34 @@ namespace Rokojori | |||
|       return splineCurve; | ||||
|     } | ||||
| 
 | ||||
|     public void AutoOrientateByTangents( Vector3 up, bool closed = false, float autoOrientationTangentAdjustment = 0f ) | ||||
|     { | ||||
|       var list = _points; | ||||
| 
 | ||||
|       for ( int i = 0; i < list.Count; i++ ) | ||||
|       { | ||||
|         var point = 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; | ||||
|         } | ||||
| 
 | ||||
|         point.LookTowards( tangentForward, up ); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public Vector3 MinPointPosition() | ||||
|     { | ||||
|       if ( _points.Count == 0 ) | ||||
|  |  | |||
|  | @ -8,7 +8,25 @@ namespace Rokojori | |||
|   { | ||||
|     bool closed;    | ||||
| 
 | ||||
|     public SplineCurve Create( List<SplinePoint> splinePoints, bool close ) | ||||
|     public SplineCurve Create( List<SplinePoint> splinePoints, bool close = false ) | ||||
|     { | ||||
|       closed = close; | ||||
|       var points = new List<SplineCurvePoint>(); | ||||
| 
 | ||||
|       for ( int i = 0; i < splinePoints.Count; i++ ) | ||||
|       { | ||||
|         points.Add( CreatePoint( splinePoints, i ) ); | ||||
|       } | ||||
| 
 | ||||
|       if ( closed ) | ||||
|       { | ||||
|         points.Add( CreatePoint( splinePoints, 0 ) ); | ||||
|       } | ||||
| 
 | ||||
|       return SplineCurve.From( points ); | ||||
|     } | ||||
| 
 | ||||
|     public SplineCurve Create( List<Vector3> splinePoints, bool close = false ) | ||||
|     { | ||||
|       closed = close; | ||||
|       var points = new List<SplineCurvePoint>(); | ||||
|  | @ -45,6 +63,26 @@ namespace Rokojori | |||
|       return splineCurvePoint; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     SplineCurvePoint CreatePoint( List<Vector3> splinePoints, int index ) | ||||
|     { | ||||
|       var splineCurvePoint = new SplineCurvePoint(); | ||||
|       var splinePoint = splinePoints[ index ]; | ||||
|       splineCurvePoint.position = splinePoint; | ||||
|       splineCurvePoint.rotation = Quaternion.Identity; | ||||
|       splineCurvePoint.scale  = Vector3.One; | ||||
|       splineCurvePoint.twist = 0; | ||||
|       splineCurvePoint.weight = 1f; | ||||
|        | ||||
|       splineCurvePoint.tangentBefore.position = GetTangentPosition( splinePoints, index, true, closed ); | ||||
|       splineCurvePoint.tangentNext.position   = GetTangentPosition( splinePoints, index, false, closed ); | ||||
| 
 | ||||
|       splineCurvePoint.tangentBefore.weight = 1; | ||||
|       splineCurvePoint.tangentNext.weight = 1; | ||||
| 
 | ||||
|       return splineCurvePoint; | ||||
|     } | ||||
| 
 | ||||
|     public static Vector3 GetTangentDirectionSmoothed( float smoothing, List<SplinePoint> splinePoints, int index, bool before, bool closed ) | ||||
|     { | ||||
|       var previousIndex = MathX.SafeIndex( index - 1, splinePoints.Count, closed ); | ||||
|  | @ -84,12 +122,106 @@ namespace Rokojori | |||
|       return unsmoothedTangent + smoothing * ( smoothedTangent - unsmoothedTangent ); | ||||
|     } | ||||
| 
 | ||||
|     public static Vector3 GetTangentDirectionSmoothed( int numSamples, float smoothing, List<SplineCurvePoint> splinePoints, int index, bool before, bool closed ) | ||||
|     { | ||||
|       var smoothedTangent = Vector3.Zero; | ||||
|       var unsmoothedTangent = Vector3.Zero; | ||||
| 
 | ||||
|       for ( int i = -numSamples; i <= numSamples; i++ ) | ||||
|       { | ||||
|         var sampleIndex = MathX.SafeIndex( index + i, splinePoints.Count, closed ); | ||||
|         var direction = GetTangentDirection( splinePoints, sampleIndex, before, closed ); | ||||
|         smoothedTangent += direction; | ||||
| 
 | ||||
|         if ( i == 0 ) | ||||
|         { | ||||
|           unsmoothedTangent = direction; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       smoothedTangent /= ( numSamples * 2 + 1 ); | ||||
| 
 | ||||
|       return unsmoothedTangent + smoothing * ( smoothedTangent - unsmoothedTangent ); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static Vector3 GetTangentDirection( List<SplinePoint> splinePoints, int index, bool before, bool closed ) | ||||
|     { | ||||
|       var splinePoint = splinePoints[ index ]; | ||||
| 
 | ||||
|       return GetTangentPosition( splinePoints, index, before, closed ) - splinePoint.GlobalPosition; | ||||
|     }  | ||||
| 
 | ||||
|     public static Vector3 GetTangentDirection( List<SplineCurvePoint> splinePoints, int index, bool before, bool closed, | ||||
|     float overshootPrevention = 0, float tangentScale = 1, float symmetricTangentLength = 0 ) | ||||
|     { | ||||
|       var splinePoint = splinePoints[ index ]; | ||||
| 
 | ||||
|       return GetTangentPosition( splinePoints, index, before, closed, overshootPrevention, tangentScale, symmetricTangentLength ) - splinePoint.position; | ||||
|     }  | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
|     public static Vector3 GetTangentPosition( List<SplineCurvePoint> splinePoints, int index, bool before, bool closed, | ||||
|     float overshootPrevention = 0, float tangentScale = 1, float symmetricTangentLength = 0 ) | ||||
|     { | ||||
|       var splinePoint = splinePoints[ index ]; | ||||
|        | ||||
|       var previousIndex = index - 1; | ||||
|        | ||||
|       if ( previousIndex == -1 )       | ||||
|       { | ||||
|         previousIndex = closed ? splinePoints.Count -1  : 0; | ||||
|       } | ||||
| 
 | ||||
|       var nextIndex = index + 1; | ||||
| 
 | ||||
|       if ( nextIndex == splinePoints.Count )   | ||||
|       { | ||||
|         nextIndex = closed ? 0 : splinePoints.Count - 1; | ||||
|       } | ||||
| 
 | ||||
|       var previous = splinePoints[ previousIndex ]; | ||||
|       var next     = splinePoints[ nextIndex ]; | ||||
| 
 | ||||
|       var previousPosition = previous.position; | ||||
|       var nextPosition     = next.position; | ||||
| 
 | ||||
|       var position =  splinePoint.position; | ||||
| 
 | ||||
|       return GetTangentPosition( position, previousPosition, nextPosition, before, overshootPrevention, tangentScale, symmetricTangentLength ); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static Vector3 GetTangentPosition( List<Vector3> splinePoints, int index, bool before, bool closed, | ||||
|     float overshootPrevention = 0, float tangentScale = 1, float symmetricTangentLength = 0 ) | ||||
|     { | ||||
|       var splinePoint = splinePoints[ index ]; | ||||
|        | ||||
|       var previousIndex = index - 1; | ||||
|        | ||||
|       if ( previousIndex == -1 )       | ||||
|       { | ||||
|         previousIndex = closed ? splinePoints.Count -1  : 0; | ||||
|       } | ||||
| 
 | ||||
|       var nextIndex = index + 1; | ||||
| 
 | ||||
|       if ( nextIndex == splinePoints.Count )   | ||||
|       { | ||||
|         nextIndex = closed ? 0 : splinePoints.Count - 1; | ||||
|       } | ||||
| 
 | ||||
|       var previous = splinePoints[ previousIndex ]; | ||||
|       var next     = splinePoints[ nextIndex ]; | ||||
| 
 | ||||
|       var previousPosition = previous; | ||||
|       var nextPosition     = next; | ||||
| 
 | ||||
|       var position =  splinePoint; | ||||
| 
 | ||||
|       return GetTangentPosition( position, previousPosition, nextPosition, before, overshootPrevention, tangentScale, symmetricTangentLength ); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static Vector3 GetTangentPosition( List<SplinePoint> splinePoints, int index, bool before, bool closed ) | ||||
|  | @ -124,12 +256,22 @@ namespace Rokojori | |||
| 
 | ||||
|        | ||||
| 
 | ||||
|       var point =  splinePoint.GlobalPosition; | ||||
|       var position =  splinePoint.GlobalPosition; | ||||
| 
 | ||||
|       var overshootPrevention = splinePoint.overshootPrevention; | ||||
|       var tangentScale = splinePoint.tangentScale; | ||||
|       var symmetricTangentLength = splinePoint.symmetricTangentLength; | ||||
| 
 | ||||
|       return GetTangentPosition( position, previousPosition, nextPosition, before, overshootPrevention, tangentScale, symmetricTangentLength ); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     public static Vector3 GetTangentPosition( Vector3 point, Vector3 previousPosition, Vector3 nextPosition, bool before, | ||||
|                                               float overshootPrevention = 0, float tangentScale = 1, float symmetricTangentLength = 0 ) | ||||
|     { | ||||
|        | ||||
| 
 | ||||
|       if ( overshootPrevention > 0 ) | ||||
|       { | ||||
|         var previousDirection = ( previousPosition - point ) ; | ||||
|  | @ -179,9 +321,6 @@ namespace Rokojori | |||
| 
 | ||||
|       return point + direction.Normalized() * length * 0.33333f * tangentScale; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -53,7 +53,7 @@ namespace Rokojori | |||
| 
 | ||||
| 
 | ||||
|     [ExportGroup( "Blade Triangles")] | ||||
|     [Export( PropertyHint.Range, "1,20")] | ||||
|     [Export( PropertyHint.Range, "1,256")] | ||||
|     public int bladeSegments = 3; | ||||
| 
 | ||||
|     [Export] | ||||
|  | @ -82,6 +82,13 @@ namespace Rokojori | |||
|     [Export] | ||||
|     public Curve uvSegmentWeightsFar = null; | ||||
| 
 | ||||
|     [Export( PropertyHint.Range, "0,1" )] | ||||
|     public float uvSegmentDistortion = 0.5f; | ||||
| 
 | ||||
|     [Export( PropertyHint.Range, "0,0.5" )] | ||||
|     public float uvSegmentMaxRange = 0.3f; | ||||
| 
 | ||||
| 
 | ||||
|     [ExportGroup( "Blade Shape")] | ||||
|     [Export] | ||||
|     public Curve bladeScale = MathX.Curve( 1f ); | ||||
|  | @ -104,6 +111,23 @@ namespace Rokojori | |||
|     [Export] | ||||
|     public Curve bladeBending2 = null; | ||||
| 
 | ||||
|      [Export] | ||||
|     public Curve bladeArching = MathX.Curve( 0f ); | ||||
| 
 | ||||
|     [Export] | ||||
|     public Curve bladeArching2 = null; | ||||
| 
 | ||||
| 
 | ||||
|     [Export] | ||||
|     public Curve bladeTwisting = null; | ||||
| 
 | ||||
|     [Export] | ||||
|     public Curve bladeTwisting2 = null; | ||||
| 
 | ||||
|     [Export] | ||||
|     public Curve rolling = MathX.Curve( 0f ); | ||||
| 
 | ||||
| 
 | ||||
|     [ExportGroup( "Blade Offset & Scale")] | ||||
| 
 | ||||
|     [Export] | ||||
|  | @ -134,6 +158,9 @@ namespace Rokojori | |||
|     [Export] | ||||
|     public Curve normalBlending = MathX.Curve( 0.5f ); | ||||
| 
 | ||||
|     [Export] | ||||
|     public Curve normalBlendingAmountOverY = MathX.Curve( 1f ); | ||||
| 
 | ||||
|     [Export] | ||||
|     public Vector3 normalBlendingDirection = Vector3.Up; | ||||
| 
 | ||||
|  | @ -239,6 +266,8 @@ namespace Rokojori | |||
|       var allBladesX = bladesX + blades; | ||||
|       var allBladesZ = bladesZ + blades; | ||||
| 
 | ||||
|       var numDebugs = 0; | ||||
| 
 | ||||
|       for ( int i = 0; i < allBladesX; i++ ) | ||||
|       { | ||||
|         var x = ( i + 0.5f ) * cellSizeX; | ||||
|  | @ -268,8 +297,11 @@ namespace Rokojori | |||
|               worldPosition | ||||
|             ); | ||||
| 
 | ||||
|           var bladeMG = CreateBlade( random, worldPosition ); | ||||
|           debugBlade = X_numBlades < 10; | ||||
| 
 | ||||
| 
 | ||||
|           var bladeMG = CreateBlade( random, worldPosition ); | ||||
|            | ||||
|           if ( filterValue > filterTreshold ) | ||||
|           { | ||||
|             continue; | ||||
|  | @ -296,15 +328,19 @@ namespace Rokojori | |||
|             clonedBladeMG = bladeMG.Clone(); | ||||
|           } | ||||
| 
 | ||||
|           bladeMG.BlendNormals( normalBlendingDirection, normalBlendingAmount );         | ||||
|            | ||||
|           var yRange = bladeMG.GetRangeY(); | ||||
| 
 | ||||
|           // bladeMG.BlendNormals( normalBlendingDirection, normalBlendingAmount );         | ||||
|           bladeMG.BlendNormalsOverY( normalBlendingDirection, normalBlendingAmount, yRange.min, yRange.max, normalBlendingAmountOverY  ); | ||||
| 
 | ||||
|           mg.Add( bladeMG );   | ||||
| 
 | ||||
|           if ( createBackFaces ) | ||||
|           { | ||||
|              | ||||
|             clonedBladeMG.FlipNormalDirection();   | ||||
|             clonedBladeMG.BlendNormals( normalBlendingDirection, normalBlendingAmount );    | ||||
|             // clonedBladeMG.BlendNormals( normalBlendingDirection, normalBlendingAmount );    | ||||
|             clonedBladeMG.BlendNormalsOverY( normalBlendingDirection, normalBlendingAmount, yRange.min, yRange.max, normalBlendingAmountOverY  ); | ||||
|              | ||||
|             mg.Add( clonedBladeMG );        | ||||
|           }           | ||||
|  | @ -318,9 +354,15 @@ namespace Rokojori | |||
|       output.Mesh = mg.GenerateMesh(); | ||||
|     } | ||||
| 
 | ||||
|     bool debugBlade = false; | ||||
| 
 | ||||
|     MeshGeometry CreateBlade( RandomEngine random, Vector3 position ) | ||||
|     { | ||||
|       // if ( debugBlade ) | ||||
|       // { | ||||
|       //   this.LogInfo( "Blade:", X_numBlades ); | ||||
|       // } | ||||
| 
 | ||||
|       var bladeSegments = this.bladeSegments; | ||||
| 
 | ||||
|       var currentLodLevel = Mathf.Clamp( currentLODLevel, -1, lodLevels.Length - 1 ); | ||||
|  | @ -405,7 +447,6 @@ namespace Rokojori | |||
| 
 | ||||
|         endingTriangles = TrilleanLogic.ToBool( useTris, endingTriangles );         | ||||
|       } | ||||
| 
 | ||||
|       | ||||
| 
 | ||||
|       for ( int i = 0; i <= bladeSegments; i++ ) | ||||
|  | @ -421,10 +462,14 @@ namespace Rokojori | |||
| 
 | ||||
|         var width = Mathf.Lerp( bladeWidth.Sample( v ), bw2.Sample( v ), bladeWidthLerp ); | ||||
|         width *= scaling * bladeWidthMultiplier; | ||||
|         var bending = Mathf.Lerp( bladeBending.Sample( v ), bb2.Sample( v ), bladeBendLerp ) * Vector3.Forward.Z * scaling; | ||||
|         var bendingNormalAngle = Mathf.LerpAngle( MathX.CurveAngle( bladeBending, v ), MathX.CurveAngle( bb2, v ), bladeBendLerp ); | ||||
|         var bendingNormal = Math3D.NormalAngle( bendingNormalAngle ); | ||||
|        | ||||
| 
 | ||||
|          | ||||
|         // var bending = Mathf.Lerp( bladeBending.Sample( v ), bb2.Sample( v ), bladeBendLerp ) * Vector3.Forward.Z * scaling; | ||||
|         // var bendingNormalAngle = Mathf.LerpAngle( MathX.CurveAngle( bladeBending, v ), MathX.CurveAngle( bb2, v ), bladeBendLerp ); | ||||
|         // var bendingNormal = Math3D.NormalAngle( bendingNormalAngle );       | ||||
| 
 | ||||
|         var bending = 0; | ||||
|         var bendingNormal = Vector3.Back; | ||||
| 
 | ||||
|         if ( endingTriangles && width < 0.005f && ( i == 0 || ( i == bladeSegments - 1 )) ) | ||||
|         { | ||||
|  | @ -443,11 +488,27 @@ namespace Rokojori | |||
|           bmg.normals.Add( bendingNormal ); | ||||
|           bmg.normals.Add( bendingNormal ); | ||||
| 
 | ||||
|           bmg.uvs.Add( MapUV( new Vector2( -width / _maxWidth, v ), uvMin, uvMax ) ); | ||||
|           bmg.uvs.Add( MapUV( new Vector2(  width / _maxWidth, v ), uvMin, uvMax ) ); | ||||
|           var uvCoord = width / _maxWidth; | ||||
| 
 | ||||
|           uvCoord = Mathf.Lerp( uvCoord, 1f, uvSegmentDistortion ); | ||||
| 
 | ||||
|           uvCoord *= uvSegmentMaxRange; | ||||
|           var uvL = MapUV( new Vector2( -uvCoord + 0.5f, v ), uvMin, uvMax ); | ||||
|           var uvR = MapUV( new Vector2(  uvCoord + 0.5f, v ), uvMin, uvMax ); | ||||
| 
 | ||||
|           bmg.uvs.Add( uvL ); | ||||
|           bmg.uvs.Add( uvR ); | ||||
| 
 | ||||
|           // if ( debugBlade ) | ||||
|           // { | ||||
|           //   this.LogInfo( i, ">>", uvL, uvR ); | ||||
|           // } | ||||
| 
 | ||||
| 
 | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|        | ||||
|       for ( int i = 0; i < bladeSegments; i++ ) | ||||
|       { | ||||
|         var index = i * 2; | ||||
|  | @ -471,9 +532,83 @@ namespace Rokojori | |||
|         } | ||||
|       } | ||||
| 
 | ||||
|       var yStart = size * 0f - inGround; | ||||
|       var yEnd   = size * 1f - inGround; | ||||
| 
 | ||||
|       var startPosition = new Vector3( 0, yStart, 0 ); | ||||
|       var endPosition   = new Vector3( 0, yEnd, 0 ); | ||||
| 
 | ||||
|       var inputList = new List<Vector3>(){ startPosition, endPosition }; | ||||
|       var inputSpline = new SplineCurveCreator().Create( inputList ); | ||||
|       inputSpline.AutoOrientateByTangents( Vector3.Back ); | ||||
|        | ||||
|       var outputList = new List<Vector3>(); | ||||
| 
 | ||||
|       var numPoints = Mathf.Max( 2, bladeSegments * 2 ); | ||||
| 
 | ||||
|       for ( int i = 0; i < numPoints ; i++ ) | ||||
|       { | ||||
|         var t = (float)i / ( numPoints - 1f ); | ||||
|         var y = size * t - inGround; | ||||
|         var bb2 = bladeBending2 == null ? bladeBending : bladeBending2;  | ||||
|         var v = 1f - t; | ||||
|         var bending = Mathf.Lerp( bladeBending.Sample( v ), bb2.Sample( v ), bladeBendLerp ) * Vector3.Forward.Z * scaling; | ||||
| 
 | ||||
|         var p = new Vector3( 0, y, bending ); | ||||
| 
 | ||||
|         outputList.Add( p ); | ||||
|       }  | ||||
| 
 | ||||
|       var outputSpline = new SplineCurveCreator().Create( outputList ); | ||||
|        | ||||
| 
 | ||||
|       var outputPoints = outputSpline.points; | ||||
| 
 | ||||
|       if ( bladeTwisting != null ) | ||||
|       {      | ||||
|         for ( int i = 0; i < outputPoints.Count; i++ ) | ||||
|         { | ||||
|           var t = (float)i / ( numPoints - 1f ); | ||||
|           var v = 1f - t; | ||||
| 
 | ||||
|           var bt2 = bladeTwisting2 == null ? bladeTwisting : bladeTwisting2;  | ||||
|           var tw = Mathf.Lerp( bladeTwisting.Sample( v ), bt2.Sample( v ), bladeBendLerp ); | ||||
|           var p = outputPoints[ i ]; | ||||
|           p.twist = tw; | ||||
|         } | ||||
| 
 | ||||
|       } | ||||
| 
 | ||||
|       outputSpline.AutoOrientateByTangents( Vector3.Back ); | ||||
| 
 | ||||
|       for ( int i = 0; i < outputPoints.Count - 1; i++ ) | ||||
|       { | ||||
|         var t = i / (float)( outputPoints.Count - 2 ); | ||||
|         var rotation = rolling.Sample( t ); | ||||
|         RollSpline( outputPoints, i, rotation ); | ||||
|       } | ||||
| 
 | ||||
|       var modifier = new SplinesDeformModifier(); | ||||
|       modifier.sourceSplines = new SplineCurve[]{ inputSpline }; | ||||
|       modifier.targetSplines = new SplineCurve[]{ outputSpline }; | ||||
|       modifier.settings = new SplinesDeformerSettings(); | ||||
| 
 | ||||
|       bmg = modifier.Modify( bmg ); | ||||
| 
 | ||||
| 
 | ||||
|       return bmg; | ||||
|     } | ||||
| 
 | ||||
|     void RollSpline( List<SplineCurvePoint> points, int start, float amount ) | ||||
|     { | ||||
|       var pivot = points[ start ].position; | ||||
|       var rotation = Math3D.RotateX( MathX.DegreesToRadians * amount / (float) points.Count ).Normalized(); | ||||
| 
 | ||||
|       for ( int i = start + 1; i < points.Count; i++ ) | ||||
|       {         | ||||
|         points[ i ].RotateAround( rotation, pivot ); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     Vector2 MapUV( Vector2 uv, Vector2 min, Vector2 max ) | ||||
|     { | ||||
|  |  | |||
|  | @ -0,0 +1,80 @@ | |||
| // NOTE: Shader automatically converted from Godot Engine 4.3.stable.mono's StandardMaterial3D. | ||||
| 
 | ||||
| shader_type spatial; | ||||
| render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx; | ||||
| 
 | ||||
| #include "res://addons/rokojori_action_library/Runtime/Shading/Library/Math.gdshaderinc" | ||||
| #include "res://addons/rokojori_action_library/Runtime/Shading/Library/Transform.gdshaderinc" | ||||
| #include "res://addons/rokojori_action_library/Runtime/Shading/Library/Noise.gdshaderinc" | ||||
| 
 | ||||
| 
 | ||||
| uniform vec4 albedo : source_color; | ||||
| uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable; | ||||
| 
 | ||||
| uniform float roughness : hint_range(0.0, 1.0); | ||||
| uniform sampler2D texture_metallic : hint_default_white, filter_linear_mipmap, repeat_enable; | ||||
| uniform vec4 metallic_texture_channel; | ||||
| uniform sampler2D texture_roughness : hint_roughness_r, filter_linear_mipmap, repeat_enable; | ||||
| 
 | ||||
| uniform float specular : hint_range(0.0, 1.0, 0.01); | ||||
| uniform float metallic : hint_range(0.0, 1.0, 0.01); | ||||
| 
 | ||||
| uniform sampler2D texture_normal : hint_roughness_normal, filter_linear_mipmap, repeat_enable; | ||||
| uniform float normal_scale : hint_range(-16.0, 16.0); | ||||
| 
 | ||||
| uniform sampler2D texture_ambient_occlusion : hint_default_white, filter_linear_mipmap, repeat_enable; | ||||
| uniform vec4 ao_texture_channel; | ||||
| uniform float ao_light_affect : hint_range(0.0, 1.0, 0.01); | ||||
| 
 | ||||
| uniform vec3 uv1_scale; | ||||
| uniform vec3 uv1_offset; | ||||
| 
 | ||||
| uniform float windStrength = 0; | ||||
| uniform vec2 windSpeed = vec2(1,1); | ||||
| uniform float windScale = 1; | ||||
| uniform sampler2D windNoise; | ||||
| uniform vec2 windNoiseAngleOffset; | ||||
| uniform vec2 windNoiseStrengthOffset; | ||||
| uniform float windStart = 0; | ||||
| uniform float windEnd = 1; | ||||
| uniform float windWeightCurve:hint_range(0,1) = 0.5f; | ||||
| uniform float windHeightCompensation :hint_range(0,1) = 0.5f; | ||||
| 
 | ||||
| void vertex()  | ||||
| { | ||||
| 	UV = UV * uv1_scale.xy + uv1_offset.xy; | ||||
|    | ||||
|   float windAmount = normalizeToRange01( VERTEX.y, windStart, windEnd ); | ||||
|   float rawWindAmount = windAmount; | ||||
|   windAmount = mix( windAmount, windAmount * windAmount, windWeightCurve ); | ||||
|   vec3 worldVertex = localToWorld( VERTEX, MODEL_MATRIX ).xyz; | ||||
|   vec2 windUV = TIME * windSpeed + worldVertex.xz * windScale; | ||||
|   float angle = texture( windNoise, windUV + windNoiseAngleOffset).r * PI * 2.0; | ||||
|   float strength = texture( windNoise, windUV + windNoiseStrengthOffset ).r * windStrength; | ||||
|   vec2 circle = onCircle( angle ) * strength; | ||||
|   VERTEX = worldToLocal( worldVertex + vec3( circle.x, 0, circle.y ) * windAmount, MODEL_MATRIX ); | ||||
|   VERTEX.y = mix( VERTEX.y, max( 0, VERTEX.y - strength * windAmount), windHeightCompensation * 2.0f ); | ||||
|    | ||||
| } | ||||
| 
 | ||||
| void fragment()  | ||||
| { | ||||
| 	vec2 base_uv = UV; | ||||
| 
 | ||||
| 	vec4 albedo_tex = texture(texture_albedo, base_uv); | ||||
| 	ALBEDO = albedo.rgb * albedo_tex.rgb; | ||||
|    | ||||
| 	float metallic_tex = dot(texture(texture_metallic, base_uv), metallic_texture_channel); | ||||
| 	METALLIC = metallic_tex * metallic; | ||||
| 	SPECULAR = specular; | ||||
|    | ||||
| 	vec4 roughness_texture_channel = vec4(1.0, 0.0, 0.0, 0.0); | ||||
| 	float roughness_tex = dot(texture(texture_roughness, base_uv), roughness_texture_channel); | ||||
| 	ROUGHNESS = roughness_tex * roughness; | ||||
| 
 | ||||
| 	NORMAL_MAP = texture(texture_normal, base_uv).rgb; | ||||
| 	NORMAL_MAP_DEPTH = normal_scale; | ||||
| 
 | ||||
| 	AO = dot(texture(texture_ambient_occlusion, base_uv), ao_texture_channel); | ||||
| 	AO_LIGHT_AFFECT = ao_light_affect; | ||||
| } | ||||
|  | @ -167,7 +167,7 @@ namespace Rokojori | |||
| 
 | ||||
|       if ( amount >= 1 ) | ||||
|       { | ||||
|          for ( int i = 0; i < normals.Count; i++ ) | ||||
|         for ( int i = 0; i < normals.Count; i++ ) | ||||
|         { | ||||
|           normals[ i ] = direction; | ||||
|         } | ||||
|  | @ -181,6 +181,36 @@ namespace Rokojori | |||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public Range GetRangeY() | ||||
|     { | ||||
|       var minY = float.MaxValue; | ||||
|       var maxY = -float.MaxValue; | ||||
| 
 | ||||
|       for ( int i = 0; i < vertices.Count; i++ ) | ||||
|       { | ||||
|         minY = Mathf.Min( minY, vertices[ i ].Y ); | ||||
|         maxY = Mathf.Max( maxY, vertices[ i ].Y ); | ||||
|       }  | ||||
| 
 | ||||
|       return new Range( minY, maxY ); | ||||
|     } | ||||
| 
 | ||||
|     public void BlendNormalsOverY( Vector3 direction, float amount, float startY, float endY, Curve curve ) | ||||
|     { | ||||
|       if ( amount <= 0 ) | ||||
|       { | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       for ( int i = 0; i < normals.Count; i++ ) | ||||
|       { | ||||
|         var yPositionAmount = MathX.RemapClamped( vertices[ i ].Y, startY, endY, 0, 1 ); | ||||
|         var blendAmount = curve.Sample( yPositionAmount ) * amount;  | ||||
|         blendAmount = MathX.Clamp01( blendAmount ); | ||||
|         normals[ i ] = Math3D.BlendNormals( normals[ i ], direction, blendAmount ); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public void Offset( Vector3 offset ) | ||||
|     { | ||||
|       for ( int i = 0; i < vertices.Count; i++ ) | ||||
|  |  | |||
|  | @ -0,0 +1,15 @@ | |||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using Godot; | ||||
| using System; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| namespace Rokojori | ||||
| { | ||||
|   public class MeshGeometryModifier | ||||
|   { | ||||
|     public MeshGeometry Modify( MeshGeometry mg ){ return mg; } | ||||
|      | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,129 @@ | |||
| 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; | ||||
|     public SplineCurve[] targetSplines; | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
|      | ||||
|   } | ||||
| } | ||||
|  | @ -0,0 +1,30 @@ | |||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using Godot; | ||||
| using System; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| namespace Rokojori | ||||
| { | ||||
|   [GlobalClass,Tool] | ||||
|   public partial class SplinesDeformerSettings:Resource | ||||
|   { | ||||
|     [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; | ||||
|   } | ||||
| } | ||||
|  | @ -14,7 +14,7 @@ namespace Rokojori | |||
| #endif | ||||
| 
 | ||||
|   { | ||||
|     public class MappingData | ||||
|     public class DeformerMappingData | ||||
|     { | ||||
|       public Vector3 localPosition; | ||||
|       public Vector3 localNormal; | ||||
|  | @ -72,10 +72,10 @@ namespace Rokojori | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     MappingData[] deformerMappings; | ||||
|     DeformerMappingData[] deformerMappings; | ||||
|     MeshGeometry meshGeometry; | ||||
| 
 | ||||
|     MappingData CreateSourceMapping( Spline s, Vector3 worldPosition, Vector3 worldNormal ) | ||||
|     DeformerMappingData CreateSourceMapping( Spline s, Vector3 worldPosition, Vector3 worldNormal ) | ||||
|     { | ||||
|       var curve = s.GetCurve(); | ||||
|       var closestParameter = curve.GetClosestParameterTo( worldPosition, splineMappingResolution, splineMappingDepth ); | ||||
|  | @ -85,7 +85,7 @@ namespace Rokojori | |||
|       var localPosition = pose.ApplyInverse( worldPosition ); | ||||
|       var localNormal   = pose.rotation.Inverse() * worldNormal; | ||||
| 
 | ||||
|       var mappingData = new MappingData(); | ||||
|       var mappingData = new DeformerMappingData(); | ||||
|        | ||||
|       mappingData.localPosition = localPosition; | ||||
|       mappingData.localNormal   = localNormal; | ||||
|  | @ -103,7 +103,7 @@ namespace Rokojori | |||
| 
 | ||||
|       if ( deformerMappings == null || deformerMappings.Length != mappingSize ) | ||||
|       { | ||||
|         deformerMappings = new MappingData[ mappingSize]; | ||||
|         deformerMappings = new DeformerMappingData[ mappingSize]; | ||||
|       } | ||||
| 
 | ||||
|       this.LogInfo( "Mappings:", deformerMappings.Length, meshGeometry ); | ||||
|  |  | |||
|  | @ -46,7 +46,7 @@ namespace Rokojori | |||
|       } | ||||
|       else if ( node is CsgPrimitive3D ) | ||||
|       { | ||||
|         return ReflectionHelper.GetMemberValue( node, "material" ) as M; | ||||
|         return ReflectionHelper.GetDataMemberValue<M>( node, "material" ); | ||||
|       } | ||||
| 
 | ||||
|       else if ( node is GpuParticles3D gp) | ||||
|  | @ -99,7 +99,7 @@ namespace Rokojori | |||
| 
 | ||||
|       if ( node is CsgPrimitive3D csg ) | ||||
|       { | ||||
|         ReflectionHelper.SetMemberValue( node, "material", material ); | ||||
|         ReflectionHelper.SetDataMemberValue( node, "material", material ); | ||||
| 
 | ||||
|         if ( csg.MaterialOverride != null ) | ||||
|         { | ||||
|  |  | |||
|  | @ -260,7 +260,7 @@ namespace Rokojori | |||
| 
 | ||||
|     void SetMemberValue( object instance, string name, object value ) | ||||
|     { | ||||
|       ReflectionHelper.SetMemberValue( instance, name, value ); | ||||
|       ReflectionHelper.SetDataMemberValue( instance, name, value ); | ||||
|     } | ||||
| 
 | ||||
|     void SetListValue( IList list, int index, object value ) | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ namespace Rokojori | |||
|         }  | ||||
|         else | ||||
|         { | ||||
|           ReflectionHelper.SetMemberValue( target, name, value ); | ||||
|           ReflectionHelper.SetDataMemberValue( target, name, value ); | ||||
|         } | ||||
|          | ||||
|       } | ||||
|  |  | |||
|  | @ -187,6 +187,7 @@ namespace Rokojori | |||
|       } | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     public static FieldInfo GetFieldInfo( object instance, string memberName ) | ||||
|     { | ||||
|       var type = instance.GetType(); | ||||
|  | @ -205,58 +206,87 @@ namespace Rokojori | |||
|       return index == -1 ? null : fields[ index ]; | ||||
|     }  | ||||
| 
 | ||||
|     public static bool HasMember( object instance, string memberName ) | ||||
|     /*public static bool HasMember( object instance, string memberName ) | ||||
|     { | ||||
|       return GetFieldInfo( instance, memberName ) != null; | ||||
|     } | ||||
|     }*/ | ||||
| 
 | ||||
|     public static object GetMemberValue( object instance, string memberName ) | ||||
|     public static MemberInfo GetDataMemberInfo( object instance, string memberName, BindingFlags flags = BindingFlags.Public | BindingFlags.Instance ) | ||||
|     { | ||||
|       try | ||||
|       var type = instance.GetType(); | ||||
|       var fieldInfo = type.GetField( memberName, flags ); | ||||
|        | ||||
|       if ( fieldInfo != null ) | ||||
|       { | ||||
|         var field = ReflectionHelper.GetFieldInfo( instance, memberName ); | ||||
|          | ||||
|         if ( field == null ) | ||||
|         { | ||||
|           RJLog.Log(instance, "GetValue: member not found with name ", "'" + memberName + "'" ); | ||||
| 
 | ||||
|           return null; | ||||
|         } | ||||
| 
 | ||||
|         return field.GetValue( instance ); | ||||
| 
 | ||||
|         return fieldInfo; | ||||
|       } | ||||
|       catch( System.Exception e ) | ||||
|       { | ||||
|         RJLog.Log(e ); | ||||
|       } | ||||
| 
 | ||||
|       return null;       | ||||
|        | ||||
|       return type.GetProperty( memberName, flags ); | ||||
|     } | ||||
| 
 | ||||
|     public static void SetMemberValue( object instance, string memberName, object value ) | ||||
|     public const BindingFlags defaultBindings =  BindingFlags.Public |  | ||||
|                                                  BindingFlags.NonPublic |  | ||||
|                                                  BindingFlags.Instance |  | ||||
|                                                  BindingFlags.Static; | ||||
| 
 | ||||
|     public static bool HasDataMember( object instance, string memberName, BindingFlags flags = ReflectionHelper.defaultBindings ) | ||||
|     { | ||||
|       try | ||||
|       return GetDataMemberInfo( instance, memberName, flags ) != null; | ||||
|     }     | ||||
| 
 | ||||
|     public static T GetDataMemberValue<T>( object instance, string memberName, BindingFlags flags = ReflectionHelper.defaultBindings )  | ||||
|     { | ||||
|       var info = GetDataMemberInfo( instance, memberName, flags ); | ||||
| 
 | ||||
|       if ( info == null ) | ||||
|       { | ||||
|         var field = ReflectionHelper.GetFieldInfo( instance, memberName ); | ||||
|          | ||||
|         if ( field == null ) | ||||
|         { | ||||
|           RJLog.Log(instance, "SetValue: member not found with name ", "'" + memberName + "'" ); | ||||
| 
 | ||||
|           return; | ||||
|         } | ||||
| 
 | ||||
|         field.SetValue( instance, value ); | ||||
| 
 | ||||
|         return default(T); | ||||
|       } | ||||
|       catch( System.Exception e ) | ||||
| 
 | ||||
|       if ( info is FieldInfo fieldInfo ) | ||||
|       { | ||||
|         RJLog.Log(e ); | ||||
|         return (T) fieldInfo.GetValue( instance ); | ||||
|       } | ||||
|      | ||||
| 
 | ||||
|       if ( info is PropertyInfo propertyInfo ) | ||||
|       { | ||||
|         return (T) propertyInfo.GetValue( instance ); | ||||
|       } | ||||
| 
 | ||||
|       return default(T); | ||||
|        | ||||
|     } | ||||
| 
 | ||||
|     public static void SetDataMemberValue( object instance, string memberName, object value, BindingFlags flags = ReflectionHelper.defaultBindings ) | ||||
|     { | ||||
|       var info = GetDataMemberInfo( instance, memberName, flags ); | ||||
| 
 | ||||
|       if ( info == null ) | ||||
|       { | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|       if ( info is FieldInfo fieldInfo ) | ||||
|       { | ||||
|         fieldInfo.SetValue( instance, value ); | ||||
|       } | ||||
| 
 | ||||
|       if ( info is PropertyInfo propertyInfo ) | ||||
|       { | ||||
|         propertyInfo.SetValue( instance, value ); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     public static void CopyDataMembersFromTo( object source, object target, List<string> dataMembers ) | ||||
|     { | ||||
|       dataMembers.ForEach( | ||||
|         dm => | ||||
|         { | ||||
|           var value = GetDataMemberValue<object>( source, dm ); | ||||
|           SetDataMemberValue( target, dm, value ); | ||||
|         } | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|   } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	 Josef
						Josef