Tanoda
PinchDraw.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
3 * *
4 * Use subject to the terms of the Apache License 2.0 available at *
5 * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6 * between Ultraleap and you, your company or other organization. *
7 ******************************************************************************/
8
9using UnityEngine;
10using System.Collections.Generic;
11
13
14 public class PinchDraw : MonoBehaviour {
15
16 #pragma warning disable 0649
17 [Tooltip("Each pinch detector can draw one line at a time.")]
18 [SerializeField]
19 private PinchDetector[] _pinchDetectors;
20
21 [SerializeField]
22 private Material _material;
23
24 [SerializeField]
25 private Color _drawColor = Color.white;
26
27 [SerializeField]
28 private float _smoothingDelay = 0.01f;
29
30 [SerializeField]
31 private float _drawRadius = 0.002f;
32
33 [SerializeField]
34 private int _drawResolution = 8;
35
36 [SerializeField]
37 private float _minSegmentLength = 0.005f;
38 #pragma warning restore 0649
39
40 private DrawState[] _drawStates;
41
43 get {
44 return _drawColor;
45 }
46 set {
47 _drawColor = value;
48 }
49 }
50
51 public float DrawRadius {
52 get {
53 return _drawRadius;
54 }
55 set {
56 _drawRadius = value;
57 }
58 }
59
60 void OnValidate() {
61 _drawRadius = Mathf.Max(0, _drawRadius);
62 _drawResolution = Mathf.Clamp(_drawResolution, 3, 24);
63 _minSegmentLength = Mathf.Max(0, _minSegmentLength);
64 }
65
66 void Awake() {
67 if (_pinchDetectors.Length == 0) {
68 Debug.LogWarning("No pinch detectors were specified! PinchDraw can not draw any lines without PinchDetectors.");
69 }
70 }
71
72 void Start() {
73 _drawStates = new DrawState[_pinchDetectors.Length];
74 for (int i = 0; i < _pinchDetectors.Length; i++) {
75 _drawStates[i] = new DrawState(this);
76 }
77 }
78
79 void Update() {
80 for (int i = 0; i < _pinchDetectors.Length; i++) {
81 var detector = _pinchDetectors[i];
82 var drawState = _drawStates[i];
83
84 if (detector.DidStartHold) {
85 drawState.BeginNewLine();
86 }
87
88 if (detector.DidRelease) {
89 drawState.FinishLine();
90 }
91
92 if (detector.IsHolding) {
93 drawState.UpdateLine(detector.Position);
94 }
95 }
96 }
97
98 private class DrawState {
99 private List<Vector3> _vertices = new List<Vector3>();
100 private List<int> _tris = new List<int>();
101 private List<Vector2> _uvs = new List<Vector2>();
102 private List<Color> _colors = new List<Color>();
103
104 private PinchDraw _parent;
105
106 private int _rings = 0;
107
108 private Vector3 _prevRing0 = Vector3.zero;
109 private Vector3 _prevRing1 = Vector3.zero;
110
111 private Vector3 _prevNormal0 = Vector3.zero;
112
113 private Mesh _mesh;
114 private SmoothedVector3 _smoothedPosition;
115
116 public DrawState(PinchDraw parent) {
117 _parent = parent;
118
119 _smoothedPosition = new SmoothedVector3();
120 _smoothedPosition.delay = parent._smoothingDelay;
121 _smoothedPosition.reset = true;
122 }
123
124 public GameObject BeginNewLine() {
125 _rings = 0;
126 _vertices.Clear();
127 _tris.Clear();
128 _uvs.Clear();
129 _colors.Clear();
130
131 _smoothedPosition.reset = true;
132
133 _mesh = new Mesh();
134 _mesh.name = "Line Mesh";
135 _mesh.MarkDynamic();
136
137 GameObject lineObj = new GameObject("Line Object");
138 lineObj.transform.position = Vector3.zero;
139 lineObj.transform.rotation = Quaternion.identity;
140 lineObj.transform.localScale = Vector3.one;
141 lineObj.AddComponent<MeshFilter>().mesh = _mesh;
142 lineObj.AddComponent<MeshRenderer>().sharedMaterial = _parent._material;
143
144 return lineObj;
145 }
146
147 public void UpdateLine(Vector3 position) {
148 _smoothedPosition.Update(position, Time.deltaTime);
149
150 bool shouldAdd = false;
151
152 shouldAdd |= _vertices.Count == 0;
153 shouldAdd |= Vector3.Distance(_prevRing0, _smoothedPosition.value) >= _parent._minSegmentLength;
154
155 if (shouldAdd) {
156 addRing(_smoothedPosition.value);
157 updateMesh();
158 }
159 }
160
161 public void FinishLine() {
162 _mesh.UploadMeshData(true);
163 }
164
165 private void updateMesh() {
166 _mesh.SetVertices(_vertices);
167 _mesh.SetColors(_colors);
168 _mesh.SetUVs(0, _uvs);
169 _mesh.SetIndices(_tris.ToArray(), MeshTopology.Triangles, 0);
170 _mesh.RecalculateBounds();
171 _mesh.RecalculateNormals();
172 }
173
174 private void addRing(Vector3 ringPosition) {
175 _rings++;
176
177 if (_rings == 1) {
178 addVertexRing();
179 addVertexRing();
180 addTriSegment();
181 }
182
183 addVertexRing();
184 addTriSegment();
185
186 Vector3 ringNormal = Vector3.zero;
187 if (_rings == 2) {
188 Vector3 direction = ringPosition - _prevRing0;
189 float angleToUp = Vector3.Angle(direction, Vector3.up);
190
191 if (angleToUp < 10 || angleToUp > 170) {
192 ringNormal = Vector3.Cross(direction, Vector3.right);
193 } else {
194 ringNormal = Vector3.Cross(direction, Vector3.up);
195 }
196
197 ringNormal = ringNormal.normalized;
198
199 _prevNormal0 = ringNormal;
200 } else if (_rings > 2) {
201 Vector3 prevPerp = Vector3.Cross(_prevRing0 - _prevRing1, _prevNormal0);
202 ringNormal = Vector3.Cross(prevPerp, ringPosition - _prevRing0).normalized;
203 }
204
205 if (_rings == 2) {
206 updateRingVerts(0,
207 _prevRing0,
208 ringPosition - _prevRing1,
209 _prevNormal0,
210 0);
211 }
212
213 if (_rings >= 2) {
214 updateRingVerts(_vertices.Count - _parent._drawResolution,
215 ringPosition,
216 ringPosition - _prevRing0,
217 ringNormal,
218 0);
219 updateRingVerts(_vertices.Count - _parent._drawResolution * 2,
220 ringPosition,
221 ringPosition - _prevRing0,
222 ringNormal,
223 1);
224 updateRingVerts(_vertices.Count - _parent._drawResolution * 3,
225 _prevRing0,
226 ringPosition - _prevRing1,
227 _prevNormal0,
228 1);
229 }
230
231 _prevRing1 = _prevRing0;
232 _prevRing0 = ringPosition;
233
234 _prevNormal0 = ringNormal;
235 }
236
237 private void addVertexRing() {
238 for (int i = 0; i < _parent._drawResolution; i++) {
239 _vertices.Add(Vector3.zero); //Dummy vertex, is updated later
240 _uvs.Add(new Vector2(i / (_parent._drawResolution - 1.0f), 0));
241 _colors.Add(_parent._drawColor);
242 }
243 }
244
245 //Connects the most recently added vertex ring to the one before it
246 private void addTriSegment() {
247 for (int i = 0; i < _parent._drawResolution; i++) {
248 int i0 = _vertices.Count - 1 - i;
249 int i1 = _vertices.Count - 1 - ((i + 1) % _parent._drawResolution);
250
251 _tris.Add(i0);
252 _tris.Add(i1 - _parent._drawResolution);
253 _tris.Add(i0 - _parent._drawResolution);
254
255 _tris.Add(i0);
256 _tris.Add(i1);
257 _tris.Add(i1 - _parent._drawResolution);
258 }
259 }
260
261 private void updateRingVerts(int offset, Vector3 ringPosition, Vector3 direction, Vector3 normal, float radiusScale) {
262 direction = direction.normalized;
263 normal = normal.normalized;
264
265 for (int i = 0; i < _parent._drawResolution; i++) {
266 float angle = 360.0f * (i / (float)(_parent._drawResolution));
267 Quaternion rotator = Quaternion.AngleAxis(angle, direction);
268 Vector3 ringSpoke = rotator * normal * _parent._drawRadius * radiusScale;
269 _vertices[offset + i] = ringPosition + ringSpoke;
270 }
271 }
272 }
273 }
274}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
UnityEngine.Color Color
Definition: TestScript.cs:32
A basic utility class to aid in creating pinch based actions. Once linked with a HandModelBase,...