Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Variable Mix Shape Interpolation Not Spanning Entire Input Spine
#1
I'm having trouble with the variable mix shapes module. It seems like it's not properly matching the length of the input path spline to the 0-1 time field of the Mix Curve module. The picture below shows the incorrect result. The left side is interpolating correctly, but it peters out too early on the right and creates and uneven mesh. Any ideas on how I can fix this?

Thanks in advance!
[Image: cv15t7q.png]
Reply
#2
Hi
Can you send me the scene please?
Please consider leaving a review for Curvy. This will help a lot keeping Curvy relevant in the eyes of the Asset Store algorithm.
Reply
#3
I apologize, I can't send the scene, but I can send the code used to generate / reproduce this issue:

I have two custom shape classes
Code:
using FluffyUnderware.Curvy;
using UnityEngine;

namespace CustomShapes {
  [CurvyShapeInfo("Custom/ShapeA")]
  [RequireComponent(typeof(CurvySpline))]
  [AddComponentMenu("Custom/ShapeA")]
  public class ShapeA : CurvyShape2D {

#if UNITY_EDITOR
    protected override void OnValidate() {
      base.OnValidate();
    }
#endif

    protected override void Reset() {
      base.Reset();
    }

    protected Vector3[] ControlPoints() {
      Vector3[] crossPoints = new Vector3[] {
        new Vector3(-0.254f, 0.0127f / 2f + 0.01f, 0),
        new Vector3(-0.05159375f, 0.0127f / 2f, 0),
        new Vector3(0.05159375f, 0.0127f / 2f, 0),
        new Vector3(0.254f, 0.0127f / 2f + 0.01f, 0),
        new Vector3(0.254f, -0.0127f / 2f + 0.01f, 0),
        new Vector3(0.05159375f, -0.0127f / 2f, 0 ),
        new Vector3(-0.05159375f, -0.0127f / 2f, 0),
        new Vector3(-0.254f, -0.0127f / 2f + 0.01f, 0)
      };
      return crossPoints;
    }

    protected override void ApplyShape() {
      base.ApplyShape();
      Vector3[] controlPoints = ControlPoints();
      PrepareSpline(CurvyInterpolation.Linear, CurvyOrientation.Dynamic);
      PrepareControlPoints(controlPoints.Length);
      for (int i = 0; i < controlPoints.Length; i++) {
        Spline.ControlPointsList[i].transform.localPosition = controlPoints[i];
      }
    }
  }
}

Code:
using FluffyUnderware.Curvy;
using UnityEngine;

namespace CustomShapes {
  [CurvyShapeInfo("Custom/ShapeB")]
  [RequireComponent(typeof(CurvySpline))]
  [AddComponentMenu("Custom/ShapeB")]
  public class ShapeB : CurvyShape2D {

#if UNITY_EDITOR
    protected override void OnValidate() {
      base.OnValidate();
    }
#endif

    protected override void Reset() {
      base.Reset();
    }

    protected Vector3[] ControlPoints() {
      Vector3[] crossPoints = new Vector3[] {
        new Vector3(-0.254f, 0.0127f / 2f, 0),
        new Vector3(-0.05159375f, 0.0127f / 2f, 0),
        new Vector3(0.05159375f, 0.0127f / 2f, 0),
        new Vector3(0.254f, 0.0127f / 2f, 0),
        new Vector3(0.254f, -0.0127f / 2f, 0),
        new Vector3(0.05159375f, -0.0127f / 2f, 0 ),
        new Vector3(-0.05159375f, -0.0127f / 2f, 0),
        new Vector3(-0.254f, -0.0127f / 2f, 0)
      };
      return crossPoints;
    }

    protected override void ApplyShape() {
      base.ApplyShape();
      Vector3[] controlPoints = ControlPoints();
      PrepareSpline(CurvyInterpolation.Linear, CurvyOrientation.Dynamic);
      PrepareControlPoints(controlPoints.Length);
      for (int i = 0; i < controlPoints.Length; i++) {
        Spline.ControlPointsList[i].transform.localPosition = controlPoints[i];
      }
    }
  }
}

And a function that creates the whole thing:
Code:
public void GenerateVolume() {
    GameObject parent = new GameObject("Parent");
    parent.transform.localPosition = Vector3.zero;

    GameObject splineObject = new GameObject("Spline");
    splineObject.transform.parent = parent.transform;
    splineObject.transform.localPosition = Vector3.zero;

    GameObject shapeAObject = new GameObject("ShapeA");
    shapeAObject.transform.parent = parent.transform;
    shapeAObject.transform.localPosition = Vector3.up * 0.1f;

    GameObject shapeBObject = new GameObject("ShapeB");
    shapeBObject.transform.parent = parent.transform;
    shapeBObject.transform.localPosition = Vector3.up * 0.2f;

    CurvySpline spline = splineObject.AddComponent<CurvySpline>();
    Vector3[] splinePoints = new Vector3[] {
      new Vector3(-0.508f, 0, 0),
      new Vector3(0.508f, 0, 0)
    };
    spline.Add(splinePoints);

    CustomShapes.ShapeA shapeA = shapeAObject.AddComponent<CustomShapes.ShapeA>();
    CustomShapes.ShapeB shapeB = shapeBObject.AddComponent<CustomShapes.ShapeB>();

    CurvyGenerator generator = parent.AddComponent<CurvyGenerator>();
    generator.ShowDebug = true;
    generator.AutoRefresh = true;

    InputSplinePath inputSplinePath = generator.AddModule<InputSplinePath>();
    InputSplineShape inputSplineShapeA = generator.AddModule<InputSplineShape>();
    InputSplineShape inputSplineShapeB = generator.AddModule<InputSplineShape>();
    ModifierVariableMixShapes mixShape = generator.AddModule<ModifierVariableMixShapes>();
    BuildShapeExtrusion shapeExtrusion = generator.AddModule<BuildShapeExtrusion>();
    BuildVolumeMesh volumeMesh = generator.AddModule<BuildVolumeMesh>();
    CreateMesh createMesh = generator.AddModule<CreateMesh>();

    shapeExtrusion.CrossIncludeControlPoints = true;
    shapeExtrusion.CrossResolution = 100;
    shapeExtrusion.Resolution = 100;
    shapeExtrusion.Optimize = false;
    shapeExtrusion.CrossOptimize = false;

    Keyframe[] keys = new Keyframe[4];

    keys[0] = new Keyframe(0, 1);
    keys[0].inTangent = 0f;
    keys[0].outTangent = 0f;

    keys[1] = new Keyframe(0.2f, -1);
    keys[1].inTangent = 0f;
    keys[1].outTangent = 0f;

    keys[2] = new Keyframe(0.8f, -1);
    keys[2].inTangent = 0f;
    keys[2].outTangent = 0f;

    keys[3] = new Keyframe(1, 1);
    keys[3].inTangent = 0f;
    keys[3].outTangent = 0f;

    AnimationCurve animationCurve = new AnimationCurve(keys);
    mixShape.MixCurve = animationCurve;

    inputSplinePath.Spline = spline;
    inputSplinePath.Path.LinkTo(shapeExtrusion.InPath);

    inputSplineShapeA.Shape = shapeA.Spline;
    inputSplineShapeB.Shape = shapeB.Spline;

    inputSplineShapeA.OutShape.LinkTo(mixShape.InShapeA);
    inputSplineShapeB.OutShape.LinkTo(mixShape.InShapeB);
    mixShape.OutShape.LinkTo(shapeExtrusion.InCross);
    shapeExtrusion.OutVolume.LinkTo(volumeMesh.InVolume);
    volumeMesh.OutVMesh.LinkTo(createMesh.InVMeshArray);
  }

It's worth noting that changing the Path Range on the Shape Extrusion module to something like 0.98 can get it pretty close. But I suspect that's not a fix all and will require eyeballing by hand for every scenario (which won't work for me).
Reply
#4
I may have actually figured out a solution / workaround. Increasing the Max Points Per Unit on the Input Spline Path from it's default of 8 to something arbitrarily higher (30-100 range) seems to fix it.

Are there any "gotchas" I might run into with setting that number so high?

Edit/Update 2: Looks like Max Point Points Per Unit can still have variable results depending on the length of the Input Spline Path. I'm not sure of the perfect way to auto calculate the lowest necessary number to get a perfect mirror result, but setting it to something ridiculous like 1000 seems to work in most of my cases, although I have a feeling that's not ideal from a performance perspective.

Final Edit: Solved for the formula. Code is as follows:
Code:
Keyframe[] keys = new Keyframe[4];
keys[0] = new Keyframe(0, 1);
keys[1] = new Keyframe(timeOffset, -1);
keys[2] = new Keyframe(1f - timeOffset, -1);
keys[3] = new Keyframe(1, 1);
AnimationCurve animationCurve = new AnimationCurve(keys);

float length = 5f; // arbitrary length
float fidelity = 10f; // Can be any number between 1 and infinity
CurvySpline spline = splineObject.AddComponent<CurvySpline>();
Vector3[] splinePoints = new Vector3[] {
  new Vector3(0, 0, 0),
  new Vector3(length, 0, 0)
};
spline.Add(splinePoints);
spline.MaxPointsPerUnit = (float)System.Math.Round((1f / length) * (animationCurve.length - 1), 2) * fidelity;

This ensures each longitudinal segment of the volume extrusion are as even as possible. Without it you might end up with a strange "hanging" segment at the end of the volume that doesn't match the length of the other segments. It's this uneven segment that throws off the symmetry of the animation curve.

Hopefully this helps anyone else that runs into the symmetry issue. It's technically not perfect since it requires rounding down, but increasing the fidelity makes it pretty much irrelevant.
Reply
#5
I like it when customers find the solutions by themselves Smile Thanks for posting the update here for other people. Let me know if you have any future question.
Have a nice day
Please consider leaving a review for Curvy. This will help a lot keeping Curvy relevant in the eyes of the Asset Store algorithm.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Curvy discards Input Spline Range VoltDriver 3 5 11-28-2023, 07:14 PM
Last Post: _Aka_
  "MetaData" Of Entire Spline Heightmap SAMYTHEBIGJUICY 3 6 10-16-2023, 08:42 AM
Last Post: _Aka_
  Skip Input Mesh if spline has too much bend Zilk1 3 9 09-27-2023, 09:45 PM
Last Post: _Aka_
  I want to improve the performance of Variable Mix Shapes yanke 7 8 07-27-2023, 09:15 PM
Last Post: _Aka_

Forum Jump: