keenanwoodall / Deform

A fully-featured deformer system for Unity that lets you stack effects to animate models in real-time

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Lattice Lerp feature

usernameHed opened this issue · comments

Hello, I have a feature I would like to add and I would like to know if it's possible.
I would like to create 2 lattice, "Look Left", and "Look Right", then create a Slider from -1 to 1.
Then I would like to operate a Lerp between the 2 lattices array points.

As their is a lot of editor stuff for the Lattice, creating 2 lattice in one new Deformer would be too tricky for me. In which way do I have to go to accomplish this feature ?

Thanks!

left

I think the easiest solution would be to create 3 lattices. Two of them can be the "source" lattices, and the third can be the final lattice which is a blend of the former. The source lattices are just used as an easy way to author the control points, but they won't actually be added to any deformables.

As long as the three lattices have the same number of control points, a simple script is all that's needed to make the final lattice be a blend of the two source lattices.

using UnityEngine;
using Deform;

public class LatticeBlend : MonoBehaviour
{
    [Range(0f, 1f)]
    [SerializeField] private float _blend;
    [SerializeField] private LatticeDeformer _final;
    [SerializeField] private LatticeDeformer _sourceA;
    [SerializeField] private LatticeDeformer _sourceB;

    public float Blend
    {
        get => _blend;
        set
        {
            _blend = Mathf.Clamp01(value);
            Apply();
        }
    }

    private void OnValidate()
    {
        Apply();
    }

    public void Apply()
    {
        if (_final == null || _sourceA == null || _sourceB == null)
            return;

        if (_final.Resolution != _sourceA.Resolution)
            return;
        if (_final.Resolution != _sourceB.Resolution)
            return;

        for (int i = 0; i < _final.ControlPoints.Length; i++)
        {
            Vector3 a = _sourceA.ControlPoints[i];
            Vector3 b = _sourceB.ControlPoints[i];

            Vector3 p = Vector3.Lerp(a, b, _blend);
            _final.ControlPoints[i] = p;
        }
    }
}

mErGiA5olM

I must say, Thanks you soo much for you kind & fast answers, with GIF & working code.

There is just one bug / feature missing: At first I created the Left Lattice, and then I just duplicated the Left one, inverted the scale to -1 in X, to have a perfect symetry between left & right. But with this pieces of code it doesn't work, I have to manually create the right one with the same scale.

One more feature: is there a way with the latice component to show the array of points, to change the value directly in inspector ? for some stuff like that it could be andy.

Thansk again. Where can I give you a tip ?

I just duplicated the Left one, inverted the scale to -1 in X, to have a perfect symetry between left & right

Unfortunately I don't think this will work. Inverting the scale of a lattice will invert the mesh itself. In the following gif I modified the LatticeBlend script to blend the control points in worldspace which does let you blend between lattices with different transforms, but as you can see the mesh gets inverted.

Script:

using UnityEngine;
using Deform;

public class LatticeBlend : MonoBehaviour
{
    [Range(0f, 1f)]
    [SerializeField] private float _blend;
    [SerializeField] private LatticeDeformer _final;
    [SerializeField] private LatticeDeformer _sourceA;
    [SerializeField] private LatticeDeformer _sourceB;

    public float Blend
    {
        get => _blend;
        set
        {
            _blend = Mathf.Clamp01(value);
            Apply();
        }
    }

    private void OnValidate() => Apply();

    public void Apply()
    {
        if (_final == null || _sourceA == null || _sourceB == null)
            return;

        if (_final.Resolution != _sourceA.Resolution)
            return;
        if (_final.Resolution != _sourceB.Resolution)
            return;

        for (int i = 0; i < _final.ControlPoints.Length; i++)
        {
            Vector3 localA = _sourceA.ControlPoints[i];
            Vector3 localB = _sourceB.ControlPoints[i];
            Vector3 worldA = _sourceA.transform.TransformPoint(localA);
            Vector3 worldB = _sourceB.transform.TransformPoint(localB);

            Vector3 worldPoint = Vector3.Lerp(worldA, worldB, _blend);
            Vector3 localPoint = _final.transform.InverseTransformPoint(worldPoint);
            _final.ControlPoints[i] = localPoint;
        }
    }
}

dAgHfg6VRf

I think the easiest way to flip lattices with minimal modifications/tooling would be a custom context menu. Here's a script you can drop in an Editor folder to get some Mirror options on the component's context menu. This file includes all the functions necessary to flip a lattice so it could also be a good starting point for some other ux if you wanted to write a component to automate this or something.

using UnityEngine;
using UnityEditor;
using Deform;

public static class LatticeMenuItems
{
    [MenuItem("CONTEXT/LatticeDeformer/Mirror/X")]
    private static void MirrorX(MenuCommand command)
    {
        if (command.context is LatticeDeformer lattice)
        {
            Undo.RecordObject(command.context, "Mirror Lattice");
            Mirror(lattice, 0);
        }
    }
    [MenuItem("CONTEXT/LatticeDeformer/Mirror/Y")]
    private static void MirrorY(MenuCommand command)
    {
        if (command.context is LatticeDeformer lattice)
        {
            Undo.RecordObject(command.context, "Mirror Lattice");
            Mirror(lattice, 1);
        }
    }
    [MenuItem("CONTEXT/LatticeDeformer/Mirror/Z")]
    private static void MirrorZ(MenuCommand command)
    {
        if (command.context is LatticeDeformer lattice)
        {
            Undo.RecordObject(command.context, "Mirror Lattice");
            Mirror(lattice, 2);
        }
    }

    private static void Mirror(LatticeDeformer lattice, int axis)
    {
        Vector3Int res = lattice.Resolution;
        Vector3[] newControlPoints = new Vector3[lattice.ControlPoints.Length];

        for (int x = 0; x < res.x; x++)
        {
            for (int y = 0; y < res.y; y++)
            {
                for (int z = 0; z < res.z; z++)
                {
                    var currentIndex3D = new Vector3Int(x, y, z);
                    var mirroredIndex3D = MirroredIndex(currentIndex3D, res, axis);
                    
                    Vector3 offset = GetControlPointOffset(lattice, mirroredIndex3D);
                    offset[axis] *= -1f;

                    int currentIndex = lattice.GetIndex(x, y, z);
                    newControlPoints[currentIndex] = GetDefaultControlPointPosition(lattice, currentIndex3D) + offset;
                }
            }
        }

        for (int i = 0; i < newControlPoints.Length; i++)
            lattice.ControlPoints[i] = newControlPoints[i];
    }

    private static Vector3Int MirroredIndex(Vector3Int index, Vector3Int resolution, int axis)
    {
        index[axis] = resolution[axis] - 1 - index[axis];
        return index;
    }
    
    private static Vector3 GetControlPointOffset(LatticeDeformer lattice, Vector3Int resolution)
    {
        return (Vector3)lattice.GetControlPoint(resolution.x, resolution.y, resolution.z) - GetDefaultControlPointPosition(lattice,resolution);
    }
    private static Vector3 GetDefaultControlPointPosition(LatticeDeformer lattice, Vector3Int resolution)
    {
        return new Vector3
        (
            0.5f - resolution.x / (float)(lattice.Resolution.x - 1),
            0.5f - resolution.y / (float)(lattice.Resolution.y - 1),
            resolution.z / (float)(lattice.Resolution.z - 1) - 0.5f
        );
    }
}

and here's a gif showing how you can use it.
KxU420nkPs

One more feature: is there a way with the latice component to show the array of points, to change the value directly in inspector ? for some stuff like that it could be andy.

That would be a nice feature! I'll add it to my list of things to explore :)
For now you can change the Inspector window to debug mode and view all the points, but I know it's far from ideal.