Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic Lattice/FFD Implementation #44

Merged
merged 15 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Code/Editor/DeformEditor.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"name": "DeformEditor",
"references": [
"Beans.Unity.Editor",
"Deform"
"Deform",
"Unity.Mathematics"
],
"optionalUnityReferences": [],
"includePlatforms": [
Expand Down
9 changes: 9 additions & 0 deletions Code/Editor/DeformEditorSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ public static float ScreenspaceAngleHandleSize
EditorUtility.SetDirty (SettingsAsset);
}
}
public static float ScreenspaceLatticeHandleCapSize
{
get => SettingsAsset.screenspaceLatticeCapSize;
set
{
SettingsAsset.screenspaceLatticeCapSize = value;
EditorUtility.SetDirty (SettingsAsset);
}
}

public static bool ModelsReadableByDefault
{
Expand Down
1 change: 1 addition & 0 deletions Code/Editor/DeformEditorSettingsAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class DeformEditorSettingsAsset : ScriptableObject
public float dottedLineSize = 5f;
public float screenspaceHandleCapSize = 0.0275f;
public float screenspaceAngleHandleSize = 1.25f;
public float screenspaceLatticeCapSize = 0.035f;

[Header("Importer")]
public bool modelsReadableByDefault = false;
Expand Down
8 changes: 7 additions & 1 deletion Code/Editor/MenuItemActions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ public static void CleanAllDeformables ()
Undo.RecordObjects (deformables, "Cleaned All Deformables");
foreach (var deformable in deformables)
deformable.DeformerElements.RemoveAll (t => t.Component == null);

var groupDeformers = GameObject.FindObjectsOfType<GroupDeformer> ();

EditorUtility.DisplayDialog ("Cleaned All Deformables", $"{deformables.Length} found and cleaned.", "OK");
Undo.RecordObjects (groupDeformers, "Cleaned All Deformables");
foreach (var groupDeformer in groupDeformers)
groupDeformer.DeformerElements.RemoveAll (t => t.Component == null);

EditorUtility.DisplayDialog ("Cleaned All Deformables", $"{deformables.Length + groupDeformers.Length} found and cleaned.", "OK");
}

[MenuItem ("Tools/Deform/Actions/Strip All Deformables", priority = 10101)]
Expand Down
249 changes: 249 additions & 0 deletions Code/Editor/Mesh/Deformers/LatticeDeformerEditor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using Deform;
using Unity.Mathematics;
using UnityEngine.Rendering;

namespace DeformEditor
{
[CustomEditor(typeof(LatticeDeformer)), CanEditMultipleObjects]
public class LatticeDeformerEditor : DeformerEditor
{
private Vector3Int newResolution;

HashSet<int> selectedIndices = new HashSet<int>();

private static class Content
{
public static readonly GUIContent Target = new GUIContent(text: "Target", tooltip: DeformEditorGUIUtility.Strings.AxisTooltip);
public static readonly GUIContent Resolution = new GUIContent(text: "Resolution", tooltip: "Per axis control point counts, the higher the resolution the more splits");
}

private class Properties
{
public SerializedProperty Target;
public SerializedProperty Resolution;

public Properties(SerializedObject obj)
{
Target = obj.FindProperty("target");
Resolution = obj.FindProperty("resolution");
}
}

private Properties properties;

protected override void OnEnable()
{
base.OnEnable();

properties = new Properties(serializedObject);
newResolution = properties.Resolution.vector3IntValue;
}

public override void OnInspectorGUI()
{
base.OnInspectorGUI();

serializedObject.UpdateIfRequiredOrScript();

EditorGUILayout.PropertyField(properties.Target, Content.Target);

newResolution = EditorGUILayout.Vector3IntField(Content.Resolution, newResolution);
// Make sure we have at least two control points per axis
newResolution = Vector3Int.Max(newResolution, new Vector3Int(2, 2, 2));
// Don't let the lattice resolution get ridiculously high
newResolution = Vector3Int.Min(newResolution, new Vector3Int(32, 32, 32));

if (GUILayout.Button("Update Lattice"))
{
Undo.RecordObject(target, "Update Lattice");
((LatticeDeformer) target).GenerateControlPoints(newResolution, true);
selectedIndices.Clear();
}

if (GUILayout.Button("Reset Lattice Points"))
{
Undo.RecordObject(target, "Reset Lattice Points");
((LatticeDeformer) target).GenerateControlPoints(newResolution);
selectedIndices.Clear();
}

serializedObject.ApplyModifiedProperties();

EditorApplication.QueuePlayerLoopUpdate();
}

public override void OnSceneGUI()
{
base.OnSceneGUI();

var lattice = target as LatticeDeformer;
var controlPoints = lattice.ControlPoints;

using (new Handles.DrawingScope(lattice.transform.localToWorldMatrix))
{
var cachedZTest = Handles.zTest;

// Change the depth testing to only show handles in front of solid objects (i.e. typical depth testing)
Handles.zTest = CompareFunction.LessEqual;

// Draw the lattice
var resolution = lattice.Resolution;
for (int z = 0; z < resolution.z - 1; z++)
{
for (int y = 0; y < resolution.y - 1; y++)
{
for (int x = 0; x < resolution.x - 1; x++)
{
int index000 = lattice.GetIndex(x, y, z);
int index100 = lattice.GetIndex(x + 1, y, z);
int index010 = lattice.GetIndex(x, y + 1, z);
int index110 = lattice.GetIndex(x + 1, y + 1, z);
int index001 = lattice.GetIndex(x, y, z + 1);
int index101 = lattice.GetIndex(x + 1, y, z + 1);
int index011 = lattice.GetIndex(x, y + 1, z + 1);
int index111 = lattice.GetIndex(x + 1, y + 1, z + 1);

var lineMode = DeformHandles.LineMode.Solid;
DeformHandles.Line(controlPoints[index000], controlPoints[index100], lineMode);
DeformHandles.Line(controlPoints[index010], controlPoints[index110], lineMode);
DeformHandles.Line(controlPoints[index001], controlPoints[index101], lineMode);
DeformHandles.Line(controlPoints[index011], controlPoints[index111], lineMode);

DeformHandles.Line(controlPoints[index000], controlPoints[index010], lineMode);
DeformHandles.Line(controlPoints[index100], controlPoints[index110], lineMode);
DeformHandles.Line(controlPoints[index001], controlPoints[index011], lineMode);
DeformHandles.Line(controlPoints[index101], controlPoints[index111], lineMode);

DeformHandles.Line(controlPoints[index000], controlPoints[index001], lineMode);
DeformHandles.Line(controlPoints[index100], controlPoints[index101], lineMode);
DeformHandles.Line(controlPoints[index010], controlPoints[index011], lineMode);
DeformHandles.Line(controlPoints[index110], controlPoints[index111], lineMode);
}
}
}

// Restore the original z test value now we're done with our drawing
Handles.zTest = cachedZTest;

for (int z = 0; z < resolution.z; z++)
{
for (int y = 0; y < resolution.y; y++)
{
for (int x = 0; x < resolution.x; x++)
{
var controlPointHandleID = GUIUtility.GetControlID("LatticeDeformerControlPoint".GetHashCode(), FocusType.Passive);
var activeColor = DeformEditorSettings.SolidHandleColor;
var controlPointIndex = lattice.GetIndex(x, y, z);

if (GUIUtility.hotControl == controlPointHandleID || selectedIndices.Contains(controlPointIndex))
{
activeColor = Handles.selectedColor;
}
else if (HandleUtility.nearestControl == controlPointHandleID)
{
activeColor = Handles.preselectionColor;
}

Event e = Event.current;
if (e.type == EventType.MouseDown && HandleUtility.nearestControl == controlPointHandleID && e.button == 0)
{
GUIUtility.hotControl = controlPointHandleID;
GUIUtility.keyboardControl = controlPointHandleID;
e.Use();

bool modifierKeyPressed = (e.modifiers & EventModifiers.Control) != 0 || (e.modifiers & EventModifiers.Shift) != 0;

if (modifierKeyPressed && selectedIndices.Contains(controlPointIndex))
{
// Pressed a modifier key so toggle the selection
selectedIndices.Remove(controlPointIndex);
}
else
{
if (!modifierKeyPressed)
{
selectedIndices.Clear();
}

selectedIndices.Add(controlPointIndex);
if (selectedIndices.Count == 1) // Is this our first selection?
{
// Make sure when we start selecting control points we don't have a transform tool active as it'll be confusing having multiple ones
Tools.current = Tool.None;
}
}
}

if (Tools.current != Tool.None && selectedIndices.Count != 0)
{
// The user has changed to a tool but we have control points selected, so clear the selection
selectedIndices.Clear();
}

using (new Handles.DrawingScope(activeColor))
{
var position = controlPoints[controlPointIndex];
var size = HandleUtility.GetHandleSize(position) * DeformEditorSettings.ScreenspaceLatticeHandleCapSize;

Handles.DotHandleCap(
controlPointHandleID,
position,
Quaternion.identity,
size,
Event.current.type);
}
}
}
}
}

if (selectedIndices.Count != 0)
{
// Get the average position
var position = float3.zero;

if (Tools.pivotMode == PivotMode.Center)
{
foreach (var index in selectedIndices)
{
position += controlPoints[index];
}

position /= selectedIndices.Count;
}
else
{
position = controlPoints[selectedIndices.First()];
}

position = lattice.Target.TransformPoint(position);

EditorGUI.BeginChangeCheck();
var rotation = lattice.Target.rotation;
if (Tools.pivotRotation == PivotRotation.Global)
{
rotation = Quaternion.identity;
}

float3 newPosition = Handles.PositionHandle(position, rotation);
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Update Lattice");

var delta = newPosition - position;
delta = lattice.Target.InverseTransformVector(delta);
foreach (var selectedIndex in selectedIndices)
{
controlPoints[selectedIndex] += delta;
}
}
}

EditorApplication.QueuePlayerLoopUpdate();
}
}
}
3 changes: 3 additions & 0 deletions Code/Editor/Mesh/Deformers/LatticeDeformerEditor.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading