Tanoda
pb_SelectionHandle.cs
Go to the documentation of this file.
1using System.Collections;
2using UnityEngine;
4
5namespace GILES
6{
12 public class pb_SelectionHandle : pb_MonoBehaviourSingleton<pb_SelectionHandle>
13 {
14 #region Member
15
16 public bool ModelViewerOnly = false;
17 public static float positionSnapValue = 0.0001f;
18 public static float scaleSnapValue = .1f;
19 public static float rotationSnapValue = 1f;
20
21 private Transform _trs;
22 private Transform trs
23 { get { if (_trs == null) _trs = gameObject.GetComponent<Transform>(); return _trs; } }
24 private Camera _cam;
25 private Camera cam
26 { get { if (_cam == null) _cam = Camera.main; return _cam; } }
27
28 private const int MAX_DISTANCE_TO_HANDLE = 15;
29
30 private static Mesh _HandleLineMesh = null, _HandleTriangleMesh = null;
31
32 public Mesh ConeMesh; // Used as translation handle cone caps.
33 public Mesh CubeMesh; // Used for scale handle
34
35 private Material HandleOpaqueMaterial
36 {
37 get
38 {
39 return pb_BuiltinResource.GetMaterial(pb_BuiltinResource.mat_HandleOpaque);
40 }
41 }
42
43 private Material RotateLineMaterial
44 {
45 get
46 {
47 return pb_BuiltinResource.GetMaterial(pb_BuiltinResource.mat_RotateHandle);
48 }
49 }
50
51 private Material HandleTransparentMaterial
52 {
53 get
54 {
55 return pb_BuiltinResource.GetMaterial(pb_BuiltinResource.mat_HandleTransparent);
56 }
57 }
58
59 private Mesh HandleLineMesh
60 {
61 get
62 {
63 if (_HandleLineMesh == null)
64 {
65 _HandleLineMesh = new Mesh();
66 CreateHandleLineMesh(ref _HandleLineMesh, Vector3.one);
67 }
68 return _HandleLineMesh;
69 }
70 }
71
72 private Mesh HandleTriangleMesh
73 {
74 get
75 {
76 if (_HandleTriangleMesh == null)
77 {
78 _HandleTriangleMesh = new Mesh();
79 CreateHandleTriangleMesh(ref _HandleTriangleMesh, Vector3.one);
80 }
81 return _HandleTriangleMesh;
82 }
83 }
84
85 private Tool tool = Tool.Position; // Current tool.
86
87 private Mesh _coneRight, _coneUp, _coneForward;
88
89 private const float CAP_SIZE = .07f;
90
91 public float HandleSize = 90f;
92
94
95 private Vector2 mouseOrigin = Vector2.zero;
96 public bool draggingHandle { get; private set; }
97 private int draggingAxes = 0; // In how many directions is the handle able to move
98 private Vector3 scale = Vector3.one;
99 private pb_Transform handleOrigin = pb_Transform.identity;
100
101 public bool isHidden { get; private set; }
102
103 public bool InUse()
104 { return draggingHandle; }
105
106 #endregion Member
107
108 #region Initialization
109
110 protected override void Awake()
111 {
112 _trs = null;
113 _cam = null;
114 base.Awake();
115 }
116
117 private void Start()
118 {
119 pb_SceneCamera.AddOnCameraMoveDelegate(this.OnCameraMove);
120
121 SetIsHidden(true);
122 }
123
124 public void SetTRS(Vector3 position, Quaternion rotation, Vector3 scale)
125 {
126 trs.position = position;
127 trs.localRotation = rotation;
128 trs.localScale = scale;
129
130 RebuildGizmoMatrix();
131 }
132
133 #endregion Initialization
134
135 #region Delegate
136
137 public delegate void OnHandleMoveEvent(pb_Transform transform);
138
140
141 public delegate void OnHandleBeginEvent(pb_Transform transform);
142
144
145 public delegate void OnHandleFinishEvent();
146
148
149 private void OnCameraMove(pb_SceneCamera cam)
150 {
151 RebuildGizmoMesh(Vector3.one);
152 RebuildGizmoMatrix();
153 }
154
155 #endregion Delegate
156
157 #region Update
158
159 private class DragOrientation
160 {
161 public Vector3 origin;
162 public Vector3 axis;
163 public Vector3 mouse;
164 public Vector3 cross;
165 public Vector3 offset;
166 public Plane plane;
167
168 public DragOrientation()
169 {
170 origin = Vector3.zero;
171 axis = Vector3.zero;
172 mouse = Vector3.zero;
173 cross = Vector3.zero;
174 offset = Vector3.zero;
175 plane = new Plane(Vector3.up, Vector3.zero);
176 }
177 }
178
179 private DragOrientation drag = new DragOrientation();
180
181 private void Update()
182 {
183 if (isHidden || EventSystem.current.IsPointerOverGameObject())
184 {
185 return;
186 }
187
188 if (!ModelViewerOnly)
189 {
190 if (Input.GetMouseButtonDown(0) && !Input.GetKey(KeyCode.LeftAlt))
191 {
192 OnMouseDown();
193 }
194 }
195
196 if (draggingHandle)
197 {
198 if (Input.GetKey(KeyCode.LeftAlt))
199 OnFinishHandleMovement();
200
201#if DEBUG
202 Vector3 dir = drag.axis * 2f;
203 Color col = new Color(Mathf.Abs(drag.axis.x), Mathf.Abs(drag.axis.y), Mathf.Abs(drag.axis.z), 1f);
204 float lineTime = 0f;
205 Debug.DrawRay(drag.origin, dir * 1f, col, lineTime, false);
206 Debug.DrawLine(drag.origin + dir, (drag.origin + dir * .9f) + (trs.up * .1f), col, lineTime, false);
207 Debug.DrawLine(drag.origin + dir, (drag.origin + dir * .9f) + (trs.forward * .1f), col, lineTime, false);
208 Debug.DrawLine(drag.origin + dir, (drag.origin + dir * .9f) + (trs.right * .1f), col, lineTime, false);
209 Debug.DrawLine(drag.origin + dir, (drag.origin + dir * .9f) + (-trs.up * .1f), col, lineTime, false);
210 Debug.DrawLine(drag.origin + dir, (drag.origin + dir * .9f) + (-trs.forward * .1f), col, lineTime, false);
211 Debug.DrawLine(drag.origin + dir, (drag.origin + dir * .9f) + (-trs.right * .1f), col, lineTime, false);
212
213 Debug.DrawLine(drag.origin, drag.origin + drag.mouse, Color.red, lineTime, false);
214 Debug.DrawLine(drag.origin, drag.origin + drag.cross, Color.black, lineTime, false);
215#endif
216 }
217
218 if (!ModelViewerOnly)
219 if (Input.GetMouseButton(0) && draggingHandle)
220 {
221 Vector3 a = Vector3.zero;
222
223 bool valid = false;
224
225 if (draggingAxes < 2 && tool != Tool.Rotate)
226 {
227 Vector3 b;
228 valid = pb_HandleUtility.PointOnLine(new Ray(trs.position, drag.axis), cam.ScreenPointToRay(Input.mousePosition), out a, out b);
229 }
230 else
231 {
232 Ray ray = cam.ScreenPointToRay(Input.mousePosition);
233 float hit = 0f;
234 if (drag.plane.Raycast(ray, out hit))
235 {
236 a = ray.GetPoint(hit);
237 valid = true;
238 }
239 }
240
241 if (valid)
242 {
243 drag.origin = trs.position;
244
245 switch (tool)
246 {
247 case Tool.Position:
248 {
249 trs.position = pb_Snap.Snap(a - drag.offset, positionSnapValue);
250 }
251 break;
252
253 case Tool.Rotate:
254 {
255 Vector2 delta = (Vector2)Input.mousePosition - mouseOrigin;
256 mouseOrigin = Input.mousePosition;
257 float sign = pb_HandleUtility.CalcMouseDeltaSignWithAxes(cam, drag.origin, drag.axis, drag.cross, delta);
258 axisAngle += delta.magnitude * sign;
259 trs.localRotation = Quaternion.AngleAxis(pb_Snap.Snap(axisAngle, rotationSnapValue), drag.axis) * handleOrigin.rotation;// trs.localRotation;
260 }
261 break;
262
263 case Tool.Scale:
264 {
265 Vector3 v;
266
267 if (draggingAxes > 1)
268 {
269 v = SetUniformMagnitude(((a - drag.offset) - trs.position));
270 }
271 else
272 {
273 v = Quaternion.Inverse(handleOrigin.rotation) * ((a - drag.offset) - trs.position);
274 }
275
276 v += Vector3.one;
277 scale = pb_Snap.Snap(v, scaleSnapValue);
278 RebuildGizmoMesh(scale);
279 }
280 break;
281 }
282
283 if (OnHandleMove != null)
285
286 RebuildGizmoMatrix();
287 }
288 }
289
290 if (!ModelViewerOnly)
291 if (Input.GetMouseButtonUp(0))
292 {
293 OnFinishHandleMovement();
294 }
295 }
296
297 private float axisAngle = 0f;
298
303 private Vector3 SetUniformMagnitude(Vector3 a)
304 {
305 float max = Mathf.Abs(a.x) > Mathf.Abs(a.y) && Mathf.Abs(a.x) > Mathf.Abs(a.z) ? a.x : Mathf.Abs(a.y) > Mathf.Abs(a.z) ? a.y : a.z;
306
307 a.x = max;
308 a.y = max;
309 a.z = max;
310
311 return a;
312 }
313
314 private void OnMouseDown()
315 {
316 scale = Vector3.one;
317
318 Vector3 a, b;
319 drag.offset = Vector3.zero;
320 Axis plane;
321
322 axisAngle = 0f;
323
324 draggingHandle = CheckHandleActivated(Input.mousePosition, out plane);
325
326 mouseOrigin = Input.mousePosition;
327 handleOrigin.SetTRS(trs);
328
329 drag.axis = Vector3.zero;
330 draggingAxes = 0;
331
332 if (draggingHandle)
333 {
334 Ray ray = cam.ScreenPointToRay(Input.mousePosition);
335
336 if ((plane & Axis.X) == Axis.X)
337 {
338 draggingAxes++;
339 drag.axis = trs.right;
340 drag.plane.SetNormalAndPosition(trs.right.normalized, trs.position);
341 }
342
343 if ((plane & Axis.Y) == Axis.Y)
344 {
345 draggingAxes++;
346 if (draggingAxes > 1)
347 drag.plane.SetNormalAndPosition(Vector3.Cross(drag.axis, trs.up).normalized, trs.position);
348 else
349 drag.plane.SetNormalAndPosition(trs.up.normalized, trs.position);
350 drag.axis += trs.up;
351 }
352
353 if ((plane & Axis.Z) == Axis.Z)
354 {
355 draggingAxes++;
356 if (draggingAxes > 1)
357 drag.plane.SetNormalAndPosition(Vector3.Cross(drag.axis, trs.forward).normalized, trs.position);
358 else
359 drag.plane.SetNormalAndPosition(trs.forward.normalized, trs.position);
360 drag.axis += trs.forward;
361 }
362
363 if (draggingAxes < 2)
364 {
365 if (pb_HandleUtility.PointOnLine(new Ray(trs.position, drag.axis), ray, out a, out b))
366 drag.offset = a - trs.position;
367
368 float hit = 0f;
369
370 if (drag.plane.Raycast(ray, out hit))
371 {
372 drag.mouse = (ray.GetPoint(hit) - trs.position).normalized;
373 drag.cross = Vector3.Cross(drag.axis, drag.mouse);
374 }
375 }
376 else
377 {
378 float hit = 0f;
379
380 if (drag.plane.Raycast(ray, out hit))
381 {
382 drag.offset = ray.GetPoint(hit) - trs.position;
383 drag.mouse = (ray.GetPoint(hit) - trs.position).normalized;
384 drag.cross = Vector3.Cross(drag.axis, drag.mouse);
385 }
386 }
387
388 if (OnHandleBegin != null)
390 }
391 }
392
393 private void OnFinishHandleMovement()
394 {
395 RebuildGizmoMesh(Vector3.one);
396 RebuildGizmoMatrix();
397
398 if (OnHandleFinish != null)
400
401 StartCoroutine(SetDraggingFalse());
402 }
403
404 private IEnumerator SetDraggingFalse()
405 {
406 yield return new WaitForEndOfFrame();
407 draggingHandle = false;
408 }
409
410 #endregion Update
411
412 #region Interface
413
414 private Vector2 screenToGUIPoint(Vector2 v)
415 {
416 v.y = Screen.height - v.y;
417 return v;
418 }
419
421 {
422 return new pb_Transform(
423 trs.position,
424 trs.localRotation,
425 scale);
426 }
427
428 private bool CheckHandleActivated(Vector2 mousePosition, out Axis plane)
429 {
430 plane = (Axis)0x0;
431
432 if (tool == Tool.Position || tool == Tool.Scale)
433 {
434 float sceneHandleSize = pb_HandleUtility.GetHandleSize(trs.position);
435
436 // cen
437 Vector2 cen = cam.WorldToScreenPoint(trs.position);
438
439 // up
440 Vector2 up = cam.WorldToScreenPoint((trs.position + (trs.up + trs.up * CAP_SIZE * 4f) * (sceneHandleSize * HandleSize)));
441
442 // right
443 Vector2 right = cam.WorldToScreenPoint((trs.position + (trs.right + trs.right * CAP_SIZE * 4f) * (sceneHandleSize * HandleSize)));
444
445 // forward
446 Vector2 forward = cam.WorldToScreenPoint((trs.position + (trs.forward + trs.forward * CAP_SIZE * 4f) * (sceneHandleSize * HandleSize)));
447
448 // First check if the plane boxes have been activated
449
450 Vector3 cameraMask = pb_HandleUtility.DirectionMask(trs, cam.transform.forward);
451
452 Vector2 p_right = (cen + ((right - cen) * cameraMask.x) * HANDLE_BOX_SIZE);
453 Vector2 p_up = (cen + ((up - cen) * cameraMask.y) * HANDLE_BOX_SIZE);
454 Vector2 p_forward = (cen + ((forward - cen) * cameraMask.z) * HANDLE_BOX_SIZE);
455
456 // x plane
457 if (pb_HandleUtility.PointInPolygon(new Vector2[] {
458 cen, p_up,
459 p_up, (p_up+p_forward) - cen,
460 (p_up+p_forward) - cen, p_forward,
461 p_forward, cen
462 }, mousePosition))
463 plane = Axis.Y | Axis.Z;
464 // y plane
465 else if (pb_HandleUtility.PointInPolygon(new Vector2[] {
466 cen, p_right,
467 p_right, (p_right+p_forward)-cen,
468 (p_right+p_forward)-cen, p_forward,
469 p_forward, cen
470 }, mousePosition))
471 plane = Axis.X | Axis.Z;
472 // z plane
473 else if (pb_HandleUtility.PointInPolygon(new Vector2[] {
474 cen, p_up,
475 p_up, (p_up + p_right) - cen,
476 (p_up + p_right) - cen, p_right,
477 p_right, cen
478 }, mousePosition))
479 plane = Axis.X | Axis.Y;
480 else
481 if (pb_HandleUtility.DistancePointLineSegment(mousePosition, cen, up) < MAX_DISTANCE_TO_HANDLE)
482 plane = Axis.Y;
483 else if (pb_HandleUtility.DistancePointLineSegment(mousePosition, cen, right) < MAX_DISTANCE_TO_HANDLE)
484 plane = Axis.X;
485 else if (pb_HandleUtility.DistancePointLineSegment(mousePosition, cen, forward) < MAX_DISTANCE_TO_HANDLE)
486 plane = Axis.Z;
487 else
488 return false;
489
490 return true;
491 }
492 else
493 {
494 Vector3[][] vertices = pb_HandleMesh.GetRotationVertices(16, 1f);
495
496 float best = Mathf.Infinity;
497
498 Vector2 cur, prev = Vector2.zero;
499 plane = Axis.X;
500
501 for (int i = 0; i < 3; i++)
502 {
503 cur = cam.WorldToScreenPoint(vertices[i][0]);
504
505 for (int n = 0; n < vertices[i].Length - 1; n++)
506 {
507 prev = cur;
508 cur = cam.WorldToScreenPoint(handleMatrix.MultiplyPoint3x4(vertices[i][n + 1]));
509
510 float dist = pb_HandleUtility.DistancePointLineSegment(mousePosition, prev, cur);
511
512 if (dist < best && dist < MAX_DISTANCE_TO_HANDLE)
513 {
514 Vector3 viewDir = (handleMatrix.MultiplyPoint3x4((vertices[i][n] + vertices[i][n + 1]) * .5f) - cam.transform.position).normalized;
515 Vector3 nrm = transform.TransformDirection(vertices[i][n]).normalized;
516
517 if (Vector3.Dot(nrm, viewDir) > .5f)
518 continue;
519
520 best = dist;
521
522 switch (i)
523 {
524 case 0: // Y
525 plane = Axis.Y; // Axis.X | Axis.Z;
526 break;
527
528 case 1: // Z
529 plane = Axis.Z;// Axis.X | Axis.Y;
530 break;
531
532 case 2: // X
533 plane = Axis.X;// Axis.Y | Axis.Z;
534 break;
535 }
536 }
537 }
538 }
539
540 if (best < MAX_DISTANCE_TO_HANDLE + .1f)
541 {
542 return true;
543 }
544 }
545
546 return false;
547 }
548
549 #endregion Interface
550
551 #region Render
552
553 private Matrix4x4 handleMatrix;
554
555 private void OnRenderObject()
556 {
557 if (isHidden || Camera.current != cam)
558 return;
559
560 switch (tool)
561 {
562 case Tool.Position:
563 case Tool.Scale:
564 HandleOpaqueMaterial.SetPass(0);
565 Graphics.DrawMeshNow(HandleLineMesh, handleMatrix);
566 Graphics.DrawMeshNow(HandleTriangleMesh, handleMatrix, 1); // Cones
567
568 HandleTransparentMaterial.SetPass(0);
569 Graphics.DrawMeshNow(HandleTriangleMesh, handleMatrix, 0); // Box
570 break;
571
572 case Tool.Rotate:
573 RotateLineMaterial.SetPass(0);
574 Graphics.DrawMeshNow(HandleLineMesh, handleMatrix);
575 break;
576 }
577 }
578
579 private void RebuildGizmoMatrix()
580 {
581 float handleSize = pb_HandleUtility.GetHandleSize(trs.position);
582 Matrix4x4 scale = Matrix4x4.Scale(Vector3.one * handleSize * HandleSize);
583
584 handleMatrix = transform.localToWorldMatrix * scale;
585 }
586
587 private void RebuildGizmoMesh(Vector3 scale)
588 {
589 if (_HandleLineMesh == null)
590 _HandleLineMesh = new Mesh();
591
592 if (_HandleTriangleMesh == null)
593 _HandleTriangleMesh = new Mesh();
594
595 CreateHandleLineMesh(ref _HandleLineMesh, scale);
596 CreateHandleTriangleMesh(ref _HandleTriangleMesh, scale);
597 }
598
599 #endregion Render
600
601 #region Set Functionality
602
603 public void SetTool(Tool tool)
604 {
605 if (this.tool != tool)
606 {
607 this.tool = tool;
608 RebuildGizmoMesh(Vector3.one);
609
610 if (onHandleTypeChanged != null)
611 onHandleTypeChanged();
612 }
613 }
614
615 public Tool GetTool()
616 {
617 return tool;
618 }
619
620 public void SetIsHidden(bool isHidden)
621 {
622 draggingHandle = false;
623 this.isHidden = isHidden;
624
625 if (onHandleTypeChanged != null)
626 onHandleTypeChanged();
627 }
628
629 public bool GetIsHidden()
630 {
631 return this.isHidden;
632 }
633
634 #endregion Set Functionality
635
636 #region Mesh Generation
637
638 private const float HANDLE_BOX_SIZE = .25f;
639
640 private void CreateHandleLineMesh(ref Mesh mesh, Vector3 scale)
641 {
642 switch (tool)
643 {
644 case Tool.Position:
645 case Tool.Scale:
646 pb_HandleMesh.CreatePositionLineMesh(ref mesh, trs, scale, cam, HANDLE_BOX_SIZE);
647 break;
648
649 case Tool.Rotate:
650 pb_HandleMesh.CreateRotateMesh(ref mesh, 48, 1f);
651 break;
652
653 default:
654 return;
655 }
656 }
657
658 private void CreateHandleTriangleMesh(ref Mesh mesh, Vector3 scale)
659 {
660 if (tool == Tool.Position)
661 pb_HandleMesh.CreateTriangleMesh(ref mesh, trs, scale, cam, ConeMesh, HANDLE_BOX_SIZE, CAP_SIZE);
662 else if (tool == Tool.Scale)
663 pb_HandleMesh.CreateTriangleMesh(ref mesh, trs, scale, cam, CubeMesh, HANDLE_BOX_SIZE, CAP_SIZE);
664 }
665
666 #endregion Mesh Generation
667 }
668}
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
UnityEngine.Color Color
Definition: TestScript.cs:32
static float GetHandleSize(Vector3 position)
static bool PointInPolygon(Vector2[] polygon, Vector2 point)
static Vector3 DirectionMask(Transform target, Vector3 rayDirection)
static void AddOnCameraMoveDelegate(OnCameraMoveEvent del)
OnHandleFinishEvent OnHandleFinish
OnHandleMoveEvent OnHandleMove
delegate void OnHandleBeginEvent(pb_Transform transform)
delegate void OnHandleMoveEvent(pb_Transform transform)
void SetTRS(Vector3 position, Quaternion rotation, Vector3 scale)
delegate void OnHandleFinishEvent()
void SetIsHidden(bool isHidden)
OnHandleBeginEvent OnHandleBegin
Quaternion rotation
Definition: pb_Transform.cs:32
void SetTRS(Transform trs)
static readonly pb_Transform identity
Definition: pb_Transform.cs:44
delegate void Callback()
Axis
Definition: pb_Enum.cs:13
Tool
Definition: pb_Enum.cs:24