Skip to content

Commit 05840c4

Browse files
authored
Merge pull request #3537 from Railboy/bezier_line_data_provider
Bezier Line Data Provider
2 parents b37f6f1 + 3fb3610 commit 05840c4

File tree

4 files changed

+297
-0
lines changed

4 files changed

+297
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using Microsoft.MixedReality.Toolkit.Core.Definitions.Utilities;
5+
using Microsoft.MixedReality.Toolkit.Core.Utilities.Lines.DataProviders;
6+
using System.Collections.Generic;
7+
using UnityEditor;
8+
using UnityEditorInternal;
9+
using UnityEngine;
10+
11+
namespace Microsoft.MixedReality.Toolkit.Core.Inspectors.Utilities.Lines
12+
{
13+
[CustomEditor(typeof(BezierDataProvider))]
14+
public class BezierDataProviderInspector : BaseLineDataProviderInspector
15+
{
16+
private const float OverlappingPointThreshold = 0.015f;
17+
private const float HandleSizeModifier = 0.04f;
18+
private const float PickSizeModifier = 0.06f;
19+
20+
private static readonly HashSet<int> OverlappingPointIndexes = new HashSet<int>();
21+
22+
private static readonly Vector2 ControlPointButtonSize = new Vector2(16, 16);
23+
private static readonly Vector2 LeftControlPointPositionOffset = Vector2.left * 12;
24+
private static readonly Vector2 RightControlPointPositionOffset = Vector2.right * 24;
25+
26+
private static readonly Vector2 ControlPointButtonHandleOffset = Vector3.up * 24;
27+
28+
private static readonly GUIContent PositionContent = new GUIContent("Position");
29+
30+
private SerializedProperty controlPoints;
31+
private SerializedProperty useLocalTangentPoints;
32+
33+
private BezierDataProvider bezierData;
34+
35+
private int selectedHandleIndex = -1;
36+
37+
protected override void OnEnable()
38+
{
39+
base.OnEnable();
40+
41+
controlPoints = serializedObject.FindProperty("controlPoints");
42+
useLocalTangentPoints = serializedObject.FindProperty("useLocalTangentPoints");
43+
44+
bezierData = (BezierDataProvider)target;
45+
}
46+
47+
public override void OnInspectorGUI()
48+
{
49+
base.OnInspectorGUI();
50+
serializedObject.Update();
51+
52+
// We always draw line points for bezier.
53+
DrawLinePoints = true;
54+
55+
EditorGUILayout.PropertyField(controlPoints, true);
56+
EditorGUILayout.PropertyField(useLocalTangentPoints);
57+
58+
serializedObject.ApplyModifiedProperties();
59+
}
60+
61+
protected override void OnSceneGUI()
62+
{
63+
base.OnSceneGUI();
64+
65+
// We skip the first point as it should always remain at the GameObject's local origin (Pose.ZeroIdentity)
66+
for (int i = 0; i < 4; i++)
67+
{
68+
bool isTangentHandle = i % 3 != 0;
69+
70+
serializedObject.Update();
71+
72+
bool isLastPoint = i == 3;
73+
74+
var controlPointPosition = LineData.GetPoint(i);
75+
var controlPointProperty = controlPoints.FindPropertyRelative("point" + (i + 1));
76+
77+
// Draw our tangent lines
78+
Handles.color = Color.gray;
79+
if (i == 0)
80+
{
81+
Handles.DrawLine(LineData.GetPoint(0), LineData.GetPoint(1));
82+
}
83+
else if (!isTangentHandle)
84+
{
85+
Handles.DrawLine(LineData.GetPoint(i), LineData.GetPoint(i - 1));
86+
87+
if (!isLastPoint)
88+
{
89+
Handles.DrawLine(LineData.GetPoint(i), LineData.GetPoint(i + 1));
90+
}
91+
}
92+
93+
Handles.color = isTangentHandle ? Color.white : Color.green;
94+
float handleSize = HandleUtility.GetHandleSize(controlPointPosition);
95+
96+
if (Handles.Button(controlPointPosition, Quaternion.identity, handleSize * HandleSizeModifier, handleSize * PickSizeModifier, Handles.DotHandleCap))
97+
{
98+
selectedHandleIndex = i;
99+
}
100+
101+
// Draw our handles
102+
if (Tools.current == Tool.Move && selectedHandleIndex == i)
103+
{
104+
EditorGUI.BeginChangeCheck();
105+
106+
var newTargetPosition = Handles.PositionHandle(controlPointPosition, Quaternion.identity);
107+
108+
if (EditorGUI.EndChangeCheck())
109+
{
110+
Undo.RecordObject(LineData, "Change Bezier Point Position");
111+
LineData.SetPoint(i, newTargetPosition);
112+
}
113+
}
114+
115+
serializedObject.ApplyModifiedProperties();
116+
}
117+
}
118+
119+
private void DrawControlPointElement(Rect rect, int index, bool isActive, bool isFocused)
120+
{
121+
bool lastMode = EditorGUIUtility.wideMode;
122+
EditorGUIUtility.wideMode = true;
123+
124+
var lastLabelWidth = EditorGUIUtility.labelWidth;
125+
EditorGUIUtility.labelWidth = 88f;
126+
127+
var property = controlPoints.GetArrayElementAtIndex(index);
128+
var fieldHeight = EditorGUIUtility.singleLineHeight * 0.5f;
129+
var labelRect = new Rect(rect.x - 8f, rect.y + fieldHeight * 2, rect.width, EditorGUIUtility.singleLineHeight);
130+
var positionRect = new Rect(rect.x, rect.y + fieldHeight, rect.width, EditorGUIUtility.singleLineHeight);
131+
var rotationRect = new Rect(rect.x, rect.y + fieldHeight * 3, rect.width, EditorGUIUtility.singleLineHeight);
132+
133+
EditorGUI.LabelField(labelRect, $"{index + 1}");
134+
135+
EditorGUI.indentLevel++;
136+
137+
GUI.enabled = index != 0;
138+
139+
EditorGUI.BeginChangeCheck();
140+
EditorGUI.PropertyField(positionRect, property.FindPropertyRelative("position"), PositionContent);
141+
bool hasPositionChanged = EditorGUI.EndChangeCheck();
142+
143+
if (hasPositionChanged)
144+
{
145+
EditorUtility.SetDirty(target);
146+
}
147+
148+
GUI.enabled = true;
149+
EditorGUI.indentLevel--;
150+
EditorGUIUtility.wideMode = lastMode;
151+
EditorGUIUtility.labelWidth = lastLabelWidth;
152+
}
153+
}
154+
}

Assets/MixedRealityToolkit/Inspectors/Utilities/Lines/DataProviders/BezierDataProviderInspector.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using Microsoft.MixedReality.Toolkit.Core.Definitions.Utilities;
5+
using System;
6+
using UnityEngine;
7+
8+
namespace Microsoft.MixedReality.Toolkit.Core.Utilities.Lines.DataProviders
9+
{
10+
public class BezierDataProvider : BaseMixedRealityLineDataProvider
11+
{
12+
[Serializable]
13+
private struct BezierPointSet
14+
{
15+
public BezierPointSet(float spread)
16+
{
17+
Point1 = Vector3.right * spread * 0.5f;
18+
Point2 = Vector3.right * spread * 0.25f;
19+
Point3 = Vector3.left * spread * 0.25f;
20+
Point4 = Vector3.left * spread * 0.5f;
21+
}
22+
23+
public Vector3 Point1;
24+
public Vector3 Point2;
25+
public Vector3 Point3;
26+
public Vector3 Point4;
27+
}
28+
29+
public override int PointCount { get { return 4; } }
30+
31+
[Header("Bezier Settings")]
32+
[SerializeField]
33+
private BezierPointSet controlPoints = new BezierPointSet(0.5f);
34+
[Tooltip("If true, control points 2 and 3 will be transformed relative to points 1 and 4 respectively")]
35+
[SerializeField]
36+
private bool useLocalTangentPoints = false;
37+
private Vector3 localOffset;
38+
39+
protected override Vector3 GetPointInternal(int pointIndex)
40+
{
41+
switch (pointIndex)
42+
{
43+
case 0:
44+
return controlPoints.Point1;
45+
46+
case 1:
47+
return controlPoints.Point2;
48+
49+
case 2:
50+
return controlPoints.Point3;
51+
52+
case 3:
53+
return controlPoints.Point4;
54+
55+
default:
56+
return Vector3.zero;
57+
}
58+
}
59+
60+
protected override void SetPointInternal(int pointIndex, Vector3 point)
61+
{
62+
switch (pointIndex)
63+
{
64+
case 0:
65+
localOffset = Vector3.zero;
66+
// If we're using local tangent points, apply this change to control point 2
67+
if (useLocalTangentPoints)
68+
localOffset = point - controlPoints.Point1;
69+
70+
controlPoints.Point1 = point;
71+
controlPoints.Point2 = controlPoints.Point2 + localOffset;
72+
break;
73+
74+
case 1:
75+
controlPoints.Point2 = point;
76+
break;
77+
78+
case 2:
79+
controlPoints.Point3 = point;
80+
break;
81+
82+
case 3:
83+
localOffset = Vector3.zero;
84+
if (useLocalTangentPoints)
85+
localOffset = point - controlPoints.Point4;
86+
87+
controlPoints.Point4 = point;
88+
controlPoints.Point3 = controlPoints.Point3 + localOffset;
89+
break;
90+
91+
default:
92+
break;
93+
}
94+
}
95+
96+
protected override Vector3 GetPointInternal(float normalizedDistance)
97+
{
98+
return LineUtility.InterpolateBezierPoints(controlPoints.Point1, controlPoints.Point2, controlPoints.Point3, controlPoints.Point4, normalizedDistance);
99+
}
100+
101+
protected override float GetUnClampedWorldLengthInternal()
102+
{
103+
// Crude approximation
104+
// TODO optimize
105+
float distance = 0f;
106+
Vector3 last = GetUnClampedPoint(0f);
107+
for (int i = 1; i < 10; i++)
108+
{
109+
Vector3 current = GetUnClampedPoint((float)i / 10);
110+
distance += Vector3.Distance(last, current);
111+
}
112+
return distance;
113+
}
114+
115+
protected override Vector3 GetUpVectorInternal(float normalizedLength)
116+
{
117+
// Bezeir up vectors just use transform up
118+
return transform.up;
119+
}
120+
}
121+
}

Assets/MixedRealityToolkit/Utilities/Lines/DataProviders/BezierDataProvider.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)