Tanoda
RuntimeGizmoManager.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 UnityEngine.SceneManagement;
11using System;
12using System.Collections.Generic;
13using Leap.Unity.Infix;
14
16
21 public interface IRuntimeGizmoComponent {
23 }
24
25 [ExecuteInEditMode]
26 public class RuntimeGizmoManager : MonoBehaviour {
27 public const string DEFAULT_SHADER_NAME = "Hidden/Runtime Gizmos";
28 public const int CIRCLE_RESOLUTION = 32;
29
30 [Tooltip("Should the gizmos be visible in the game view.")]
31 [SerializeField]
32 protected bool _displayInGameView = true;
33
34 [Tooltip("Should the gizmos be visible in a build.")]
35 [SerializeField]
36 protected bool _enabledForBuild = true;
37
38 [Tooltip("The mesh to use for the filled sphere gizmo.")]
39 [SerializeField]
40 protected Mesh _sphereMesh;
41
42 [Tooltip("The shader to use for rendering gizmos.")]
43 [SerializeField]
44 protected Shader _gizmoShader;
45
47
48 protected static RuntimeGizmoDrawer _backDrawer = null;
49 protected static RuntimeGizmoDrawer _frontDrawer = null;
50 private bool _readyForSwap = false;
51
56 public static event Action<RuntimeGizmoDrawer> OnPostRenderGizmos;
57
65 public static bool TryGetGizmoDrawer(out RuntimeGizmoDrawer drawer) {
66 drawer = _backDrawer;
67 if (drawer != null) {
69 return true;
70 } else {
71 return false;
72 }
73 }
74
83 public static bool TryGetGizmoDrawer(GameObject attatchedGameObject, out RuntimeGizmoDrawer drawer) {
84 drawer = _backDrawer;
85 if (drawer != null && !areGizmosDisabled(attatchedGameObject.transform)) {
87 return true;
88 } else {
89 return false;
90 }
91 }
92
93 protected virtual void OnValidate() {
94 if (_gizmoShader == null) {
95 _gizmoShader = Shader.Find(DEFAULT_SHADER_NAME);
96 }
97
98 Material tempMat = new Material(_gizmoShader);
99 tempMat.hideFlags = HideFlags.HideAndDontSave;
100 if (tempMat.passCount != 4) {
101 Debug.LogError("Shader " + _gizmoShader + " does not have 4 passes and cannot be used as a gizmo shader.");
102 _gizmoShader = Shader.Find(DEFAULT_SHADER_NAME);
103 }
104
105 if (_frontDrawer != null && _backDrawer != null) {
106 assignDrawerParams();
107 }
108 }
109
110 protected virtual void Reset() {
111 _gizmoShader = Shader.Find(DEFAULT_SHADER_NAME);
112 }
113
114 protected virtual void OnEnable() {
115#if !UNITY_EDITOR
116 if (!_enabledForBuild) {
117 enabled = false;
118 return;
119 }
120#endif
121
124
126
127 if (_gizmoShader == null) {
128 _gizmoShader = Shader.Find(DEFAULT_SHADER_NAME);
129 }
130
131 generateMeshes();
132 assignDrawerParams();
133
134 //Unsubscribe to prevent double-subscription
135 Camera.onPostRender -= onPostRender;
136 Camera.onPostRender += onPostRender;
137 }
138
139 protected virtual void OnDisable() {
140 _frontDrawer = null;
141 _backDrawer = null;
142
143 Camera.onPostRender -= onPostRender;
144 }
145
146 private List<GameObject> _objList = new List<GameObject>();
147 private List<IRuntimeGizmoComponent> _gizmoList = new List<IRuntimeGizmoComponent>();
148 protected virtual void Update() {
149 Scene scene = SceneManager.GetActiveScene();
150 scene.GetRootGameObjects(_objList);
151 for (int i = 0; i < _objList.Count; i++) {
152 GameObject obj = _objList[i];
153 obj.GetComponentsInChildren(false, _gizmoList);
154 for (int j = 0; j < _gizmoList.Count; j++) {
155 if (areGizmosDisabled((_gizmoList[j] as Component).transform)) {
156 continue;
157 }
158
160
161 try {
162 _gizmoList[j].OnDrawRuntimeGizmos(_backDrawer);
163 } catch (Exception e) {
164 Debug.LogException(e);
165 }
166 }
167 }
168
169 _readyForSwap = true;
170 }
171
172 protected void onPostRender(Camera camera) {
173 if ((camera.cullingMask & gameObject.layer) == 0) { return; }
174
175#if UNITY_EDITOR
176 //Always draw scene view
177 //Never draw preview or reflection
178 switch (camera.cameraType) {
179 case CameraType.Preview:
180#if UNITY_2017_1_OR_NEWER
181 case CameraType.Reflection:
182#endif
183 return;
184 case CameraType.Game:
185 case CameraType.VR:
186 if (!_displayInGameView) {
187 return;
188 }
189 break;
190 }
191#endif
192
193 if (_readyForSwap) {
194 if (OnPostRenderGizmos != null) {
197 }
198
199 RuntimeGizmoDrawer tempDrawer = _backDrawer;
201 _frontDrawer = tempDrawer;
202
203 //Guard the front drawer for rendering
205
206 //Unguard the back drawer to allow gizmos to be drawn to it
208
209 _readyForSwap = false;
211 }
212
214 }
215
216 protected static bool areGizmosDisabled(Transform transform) {
217 bool isDisabled = false;
218 do {
219 var toggle = transform.GetComponentInParent<RuntimeGizmoToggle>();
220 if (toggle == null) {
221 break;
222 }
223
224 if (!toggle.enabled) {
225 isDisabled = true;
226 break;
227 }
228
229 transform = transform.parent;
230 } while (transform != null);
231
232 return isDisabled;
233 }
234
235 private void assignDrawerParams() {
236 if (_gizmoShader != null) {
239 }
240
245
250 }
251
252 private void generateMeshes() {
253 _cubeMesh = new Mesh();
254 _cubeMesh.name = "RuntimeGizmoCube";
255 _cubeMesh.hideFlags = HideFlags.HideAndDontSave;
256
257 List<Vector3> verts = new List<Vector3>();
258 List<int> indexes = new List<int>();
259
260 Vector3[] faces = new Vector3[] { Vector3.forward, Vector3.right, Vector3.up };
261 for (int i = 0; i < 3; i++) {
262 addQuad(verts, indexes, faces[(i + 0) % 3], -faces[(i + 1) % 3], faces[(i + 2) % 3]);
263 addQuad(verts, indexes, -faces[(i + 0) % 3], faces[(i + 1) % 3], faces[(i + 2) % 3]);
264 }
265
266 _cubeMesh.SetVertices(verts);
267 _cubeMesh.SetIndices(indexes.ToArray(), MeshTopology.Quads, 0);
268 _cubeMesh.RecalculateNormals();
269 _cubeMesh.RecalculateBounds();
270 _cubeMesh.UploadMeshData(true);
271
272 _wireCubeMesh = new Mesh();
273 _wireCubeMesh.name = "RuntimeWireCubeMesh";
274 _wireCubeMesh.hideFlags = HideFlags.HideAndDontSave;
275
276 verts.Clear();
277 indexes.Clear();
278
279 for (int dx = 1; dx >= -1; dx -= 2) {
280 for (int dy = 1; dy >= -1; dy -= 2) {
281 for (int dz = 1; dz >= -1; dz -= 2) {
282 verts.Add(0.5f * new Vector3(dx, dy, dz));
283 }
284 }
285 }
286
287 addCorner(indexes, 0, 1, 2, 4);
288 addCorner(indexes, 3, 1, 2, 7);
289 addCorner(indexes, 5, 1, 4, 7);
290 addCorner(indexes, 6, 2, 4, 7);
291
292 _wireCubeMesh.SetVertices(verts);
293 _wireCubeMesh.SetIndices(indexes.ToArray(), MeshTopology.Lines, 0);
294 _wireCubeMesh.RecalculateBounds();
295 _wireCubeMesh.UploadMeshData(true);
296
297 _wireSphereMesh = new Mesh();
298 _wireSphereMesh.name = "RuntimeWireSphereMesh";
299 _wireSphereMesh.hideFlags = HideFlags.HideAndDontSave;
300
301 verts.Clear();
302 indexes.Clear();
303
304 int totalVerts = CIRCLE_RESOLUTION * 3;
305 for (int i = 0; i < CIRCLE_RESOLUTION; i++) {
306 float angle = Mathf.PI * 2 * i / CIRCLE_RESOLUTION;
307 float dx = 0.5f * Mathf.Cos(angle);
308 float dy = 0.5f * Mathf.Sin(angle);
309
310 for (int j = 0; j < 3; j++) {
311 indexes.Add((i * 3 + j + 0) % totalVerts);
312 indexes.Add((i * 3 + j + 3) % totalVerts);
313 }
314
315 verts.Add(new Vector3(dx, dy, 0));
316 verts.Add(new Vector3(0, dx, dy));
317 verts.Add(new Vector3(dx, 0, dy));
318 }
319
320 _wireSphereMesh.SetVertices(verts);
321 _wireSphereMesh.SetIndices(indexes.ToArray(), MeshTopology.Lines, 0);
322 _wireSphereMesh.RecalculateBounds();
323 _wireSphereMesh.UploadMeshData(true);
324 }
325
326 private void addQuad(List<Vector3> verts, List<int> indexes, Vector3 normal, Vector3 axis1, Vector3 axis2) {
327 indexes.Add(verts.Count + 0);
328 indexes.Add(verts.Count + 1);
329 indexes.Add(verts.Count + 2);
330 indexes.Add(verts.Count + 3);
331
332 verts.Add(0.5f * (normal + axis1 + axis2));
333 verts.Add(0.5f * (normal + axis1 - axis2));
334 verts.Add(0.5f * (normal - axis1 - axis2));
335 verts.Add(0.5f * (normal - axis1 + axis2));
336 }
337
338 private void addCorner(List<int> indexes, int a, int b, int c, int d) {
339 indexes.Add(a); indexes.Add(b);
340 indexes.Add(a); indexes.Add(c);
341 indexes.Add(a); indexes.Add(d);
342 }
343 }
344
345 public class RuntimeGizmoDrawer {
346 public const int UNLIT_SOLID_PASS = 0;
347 public const int UNLIT_TRANSPARENT_PASS = 1;
348 public const int SHADED_SOLID_PASS = 2;
349 public const int SHADED_TRANSPARENT_PASS = 3;
350
351 private List<OperationType> _operations = new List<OperationType>();
352 private List<Matrix4x4> _matrices = new List<Matrix4x4>();
353 private List<Color> _colors = new List<Color>();
354 private List<Line> _lines = new List<Line>();
355 private List<WireSphere> _wireSpheres = new List<WireSphere>();
356 private List<Mesh> _meshes = new List<Mesh>();
357
358 private Color _currColor = Color.white;
359 private Matrix4x4 _currMatrix = Matrix4x4.identity;
360 private Stack<Matrix4x4> _matrixStack = new Stack<Matrix4x4>();
361
362 private bool _isInWireMode = false;
363 private Material _gizmoMaterial;
364 private int _operationCountOnGuard = -1;
365
366 public Shader gizmoShader {
367 get {
368 if (_gizmoMaterial == null) {
369 return null;
370 } else {
371 return _gizmoMaterial.shader;
372 }
373 }
374 set {
375 if (_gizmoMaterial == null) {
376 _gizmoMaterial = new Material(value);
377 _gizmoMaterial.name = "Runtime Gizmo Material";
378 _gizmoMaterial.hideFlags = HideFlags.HideAndDontSave;
379 } else {
380 _gizmoMaterial.shader = value;
381 }
382 }
383 }
384
386
390 public void BeginGuard() {
391 _operationCountOnGuard = _operations.Count;
392 }
393
397 public void EndGuard() {
398 bool wereGizmosDrawn = _operations.Count > _operationCountOnGuard;
399 _operationCountOnGuard = -1;
400
401 if (wereGizmosDrawn) {
402 Debug.LogError("New gizmos were drawn to the front buffer! Make sure to never keep a reference to a Drawer, always get a new one every time you want to start drawing.");
403 }
404 }
405
409 public void RelativeTo(Transform transform) {
410 matrix = transform.localToWorldMatrix;
411 }
412
416 public void PushMatrix() {
417 _matrixStack.Push(_currMatrix);
418 }
419
423 public void PopMatrix() {
424 matrix = _matrixStack.Pop();
425 }
426
431 matrix = Matrix4x4.identity;
432 color = Color.white;
433 }
434
438 public Color color {
439 get {
440 return _currColor;
441 }
442 set {
443 if (_currColor == value) {
444 return;
445 }
446 _currColor = value;
447 _operations.Add(OperationType.SetColor);
448 _colors.Add(_currColor);
449 }
450 }
451
455 public Matrix4x4 matrix {
456 get {
457 return _currMatrix;
458 }
459 set {
460 if (_currMatrix == value) {
461 return;
462 }
463 _currMatrix = value;
464 _operations.Add(OperationType.SetMatrix);
465 _matrices.Add(_currMatrix);
466 }
467 }
468
472 public void DrawMesh(Mesh mesh, Matrix4x4 matrix) {
473 setWireMode(false);
474 drawMeshInternal(mesh, matrix);
475 }
476
480 public void DrawMesh(Mesh mesh, Vector3 position, Quaternion rotation, Vector3 scale) {
481 DrawMesh(mesh, Matrix4x4.TRS(position, rotation, scale));
482 }
483
487 public void DrawWireMesh(Mesh mesh, Matrix4x4 matrix) {
488 setWireMode(true);
489 drawMeshInternal(mesh, matrix);
490 }
491
495 public void DrawWireMesh(Mesh mesh, Vector3 position, Quaternion rotation, Vector3 scale) {
496 DrawWireMesh(mesh, Matrix4x4.TRS(position, rotation, scale));
497 }
498
502 public void DrawLine(Vector3 a, Vector3 b) {
503 _operations.Add(OperationType.DrawLine);
504 _lines.Add(new Line(a, b));
505 }
506
510 public void DrawCube(Vector3 position, Vector3 size) {
511 DrawMesh(cubeMesh, position, Quaternion.identity, size);
512 }
513
517 public void DrawWireCube(Vector3 position, Vector3 size) {
518 DrawWireMesh(wireCubeMesh, position, Quaternion.identity, size);
519 }
520
524 public void DrawSphere(Vector3 center, float radius) {
525 //Throw an error here so we can give a more specific error than the more
526 //general one which will be thrown later for a null mesh.
527 if (sphereMesh == null) {
528 throw new InvalidOperationException("Cannot draw a sphere because the Runtime Gizmo Manager does not have a sphere mesh assigned!");
529 }
530
531 DrawMesh(sphereMesh, center, Quaternion.identity, Vector3.one * radius * 2);
532 }
533
534 public void DrawWireSphere(Pose pose, float radius, int numSegments = 32) {
535 _operations.Add(OperationType.DrawWireSphere);
536 _wireSpheres.Add(new WireSphere() {
537 pose = pose,
538 radius = radius,
539 numSegments = numSegments
540 });
541 }
542
546 public void DrawWireSphere(Vector3 center, float radius, int numSegments = 32) {
547 DrawWireSphere(new Pose(center, Quaternion.identity), radius, numSegments);
548 }
549
554 public void DrawEllipsoid(Vector3 foci1, Vector3 foci2, float minorAxis) {
555 PushMatrix();
556 Vector3 ellipseCenter = (foci1 + foci2) / 2f;
557 Quaternion ellipseRotation = Quaternion.LookRotation(foci1 - foci2);
558 var majorAxis = Mathf.Sqrt(Mathf.Pow(Vector3.Distance(foci1, foci2) / 2f, 2f)
559 + Mathf.Pow(minorAxis / 2f, 2f)) * 2f;
560 Vector3 ellipseScale = new Vector3(minorAxis, minorAxis, majorAxis);
561
562 matrix = Matrix4x4.TRS(ellipseCenter, ellipseRotation, ellipseScale);
563
564 DrawWireSphere(Vector3.zero, 0.5f);
565 PopMatrix();
566 }
567
572 public void DrawWireCapsule(Vector3 start, Vector3 end, float radius) {
573 Vector3 up = (end - start).normalized * radius;
574 Vector3 forward = Vector3.Slerp(up, -up, 0.5F);
575 Vector3 right = Vector3.Cross(up, forward).normalized * radius;
576
577 float height = (start - end).magnitude;
578
579 // Radial circles
580 DrawLineWireCircle(start, up, radius, 8);
581 DrawLineWireCircle(end, -up, radius, 8);
582
583 // Sides
584 DrawLine(start + right, end + right);
585 DrawLine(start - right, end - right);
586 DrawLine(start + forward, end + forward);
587 DrawLine(start - forward, end - forward);
588
589 // Endcaps
590 DrawWireArc(start, right, forward, radius, 0.5F, 8);
591 DrawWireArc(start, forward, -right, radius, 0.5F, 8);
592 DrawWireArc(end, right, -forward, radius, 0.5F, 8);
593 DrawWireArc(end, forward, right, radius, 0.5F, 8);
594 }
595
596 private void DrawLineWireCircle(Vector3 center, Vector3 normal, float radius, int numCircleSegments = 16) {
597 DrawWireArc(center, normal, Vector3.Slerp(normal, -normal, 0.5F), radius, 1.0F, numCircleSegments);
598 }
599
600 public void DrawWireArc(Vector3 center, Vector3 normal, Vector3 radialStartDirection,
601 float radius, float fractionOfCircleToDraw, int numCircleSegments = 16) {
602 normal = normal.normalized;
603 Vector3 radiusVector = radialStartDirection.normalized * radius;
604 Vector3 nextVector;
605 int numSegmentsToDraw = (int)(numCircleSegments * fractionOfCircleToDraw);
606 Quaternion rotator = Quaternion.AngleAxis(360f / numCircleSegments, normal);
607 for (int i = 0; i < numSegmentsToDraw; i++) {
608 nextVector = rotator * radiusVector;
609 DrawLine(center + radiusVector, center + nextVector);
610 radiusVector = nextVector;
611 }
612 }
613
614 private List<Collider> _colliderList = new List<Collider>();
615 public void DrawColliders(GameObject gameObject, bool useWireframe = true,
616 bool traverseHierarchy = true,
617 bool drawTriggers = false) {
618 PushMatrix();
619
620 if (traverseHierarchy) {
621 gameObject.GetComponentsInChildren(_colliderList);
622 } else {
623 gameObject.GetComponents(_colliderList);
624 }
625
626 for (int i = 0; i < _colliderList.Count; i++) {
627 Collider collider = _colliderList[i];
628 RelativeTo(collider.transform);
629
630 if (collider.isTrigger && !drawTriggers) { continue; }
631
632 DrawCollider(collider, skipMatrixSetup: true);
633 }
634
635 PopMatrix();
636 }
637
638 public void DrawCollider(Collider collider, bool useWireframe = true,
639 bool skipMatrixSetup = false) {
640 if (!skipMatrixSetup) {
641 PushMatrix();
642 RelativeTo(collider.transform);
643 }
644
645 if (collider is BoxCollider) {
646 BoxCollider box = collider as BoxCollider;
647 if (useWireframe) {
648 DrawWireCube(box.center, box.size);
649 }
650 else {
651 DrawCube(box.center, box.size);
652 }
653 }
654 else if (collider is SphereCollider) {
655 SphereCollider sphere = collider as SphereCollider;
656 if (useWireframe) {
657 DrawWireSphere(sphere.center, sphere.radius);
658 }
659 else {
660 DrawSphere(sphere.center, sphere.radius);
661 }
662 }
663 else if (collider is CapsuleCollider) {
664 CapsuleCollider capsule = collider as CapsuleCollider;
665 if (useWireframe) {
666 Vector3 capsuleDir;
667 switch (capsule.direction) {
668 case 0: capsuleDir = Vector3.right; break;
669 case 1: capsuleDir = Vector3.up; break;
670 case 2: default: capsuleDir = Vector3.forward; break;
671 }
672 DrawWireCapsule(capsule.center + capsuleDir * (capsule.height / 2F - capsule.radius),
673 capsule.center - capsuleDir * (capsule.height / 2F - capsule.radius), capsule.radius);
674 }
675 else {
676 Vector3 size = Vector3.zero;
677 size += Vector3.one * capsule.radius * 2;
678 size += new Vector3(capsule.direction == 0 ? 1 : 0,
679 capsule.direction == 1 ? 1 : 0,
680 capsule.direction == 2 ? 1 : 0) * (capsule.height - capsule.radius * 2);
681 DrawCube(capsule.center, size);
682 }
683 }
684 else if (collider is MeshCollider) {
685 MeshCollider mesh = collider as MeshCollider;
686 if (mesh.sharedMesh != null) {
687 if (useWireframe) {
688 DrawWireMesh(mesh.sharedMesh, Matrix4x4.identity);
689 }
690 else {
691 DrawMesh(mesh.sharedMesh, Matrix4x4.identity);
692 }
693 }
694 }
695
696 if (!skipMatrixSetup) {
697 PopMatrix();
698 }
699 }
700
711 public void DrawPosition(Vector3 pos, Color lerpColor, float lerpCoeff, float? overrideScale = null) {
712 float targetScale;
713 if (overrideScale.HasValue) {
714 targetScale = overrideScale.Value;
715 }
716 else {
717 targetScale = 0.06f; // 6 cm at 1m away.
718
719 var curCam = Camera.current;
720 var posWorldSpace = matrix * pos;
721 if (curCam != null) {
722 float camDistance = Vector3.Distance(posWorldSpace, curCam.transform.position);
723
724 targetScale *= camDistance;
725 }
726 }
727
728 float extent = (targetScale / 2f);
729
730 float negativeAlpha = 0.6f;
731
732 color = Color.red;
733 if (lerpCoeff != 0f) { color = color.LerpHSV(lerpColor, lerpCoeff); }
734 DrawLine(pos, pos + Vector3.right * extent);
735 color = Color.black.WithAlpha(negativeAlpha);
736 if (lerpCoeff != 0f) { color = color.LerpHSV(lerpColor, lerpCoeff); }
737 DrawLine(pos, pos - Vector3.right * extent);
738
739 color = Color.green;
740 if (lerpCoeff != 0f) { color = color.LerpHSV(lerpColor, lerpCoeff); }
741 DrawLine(pos, pos + Vector3.up * extent);
742 color = Color.black.WithAlpha(negativeAlpha);
743 if (lerpCoeff != 0f) { color = color.LerpHSV(lerpColor, lerpCoeff); }
744 DrawLine(pos, pos - Vector3.up * extent);
745
746 color = Color.blue;
747 if (lerpCoeff != 0f) { color = color.LerpHSV(lerpColor, lerpCoeff); }
748 DrawLine(pos, pos + Vector3.forward * extent);
749 color = Color.black.WithAlpha(negativeAlpha);
750 if (lerpCoeff != 0f) { color = color.LerpHSV(lerpColor, lerpCoeff); }
751 DrawLine(pos, pos - Vector3.forward * extent);
752 }
753
759 public void DrawPosition(Vector3 pos) {
760 DrawPosition(pos, Color.white, 0f);
761 }
762
763 public void DrawPosition(Vector3 pos, float overrideScale) {
764 DrawPosition(pos, Color.white, 0f, overrideScale);
765 }
766
767 public void DrawRect(Transform frame, Rect rect) {
768 PushMatrix();
769
770 this.matrix = frame.localToWorldMatrix;
771 DrawLine(rect.Corner00(), rect.Corner01());
772 DrawLine(rect.Corner01(), rect.Corner11());
773 DrawLine(rect.Corner11(), rect.Corner10());
774 DrawLine(rect.Corner10(), rect.Corner00());
775
776 PopMatrix();
777 }
778
779 public void ClearAllGizmos() {
780 _operations.Clear();
781 _matrices.Clear();
782 _colors.Clear();
783 _lines.Clear();
784 _wireSpheres.Clear();
785 _meshes.Clear();
786 _isInWireMode = false;
787 _currMatrix = Matrix4x4.identity;
788 _currColor = Color.white;
789 }
790
791 public void DrawAllGizmosToScreen() {
792 try {
793 int matrixIndex = 0;
794 int colorIndex = 0;
795 int lineIndex = 0;
796 int wireSphereIndex = 0;
797 int meshIndex = 0;
798
799 int currPass = -1;
800 _currMatrix = Matrix4x4.identity;
801 _currColor = Color.white;
802
803 GL.wireframe = false;
804
805 for (int i = 0; i < _operations.Count; i++) {
806 OperationType type = _operations[i];
807 switch (type) {
808 case OperationType.SetMatrix:
809 _currMatrix = _matrices[matrixIndex++];
810 break;
811
812 case OperationType.SetColor:
813 _currColor = _colors[colorIndex++];
814 currPass = -1; //force pass to be set the next time we need to draw
815 break;
816
817 case OperationType.ToggleWireframe:
818 GL.wireframe = !GL.wireframe;
819 break;
820
821 case OperationType.DrawLine:
822 setPass(ref currPass, isUnlit: true);
823
824 GL.Begin(GL.LINES);
825 Line line = _lines[lineIndex++];
826 GL.Vertex(_currMatrix.MultiplyPoint(line.a));
827 GL.Vertex(_currMatrix.MultiplyPoint(line.b));
828 GL.End();
829 break;
830
831 case OperationType.DrawWireSphere:
832 setPass(ref currPass, isUnlit: true);
833
834 GL.Begin(GL.LINES);
835 WireSphere wireSphere = _wireSpheres[wireSphereIndex++];
836 drawWireSphereNow(wireSphere, ref currPass);
837 GL.End();
838 break;
839
840 case OperationType.DrawMesh:
841 if (GL.wireframe) {
842 setPass(ref currPass, isUnlit: true);
843 } else {
844 setPass(ref currPass, isUnlit: false);
845 }
846
847 Graphics.DrawMeshNow(_meshes[meshIndex++],
848 _currMatrix * _matrices[matrixIndex++]);
849 break;
850
851 default:
852 throw new InvalidOperationException("Unexpected operation type " + type);
853 }
854 }
855 } finally {
856 GL.wireframe = false;
857 }
858 }
859
860 private void drawLineNow(Vector3 a, Vector3 b) {
861 GL.Vertex(_currMatrix.MultiplyPoint(a));
862 GL.Vertex(_currMatrix.MultiplyPoint(b));
863 }
864
865 private void drawWireArcNow(Vector3 center, Vector3 normal,
866 Vector3 radialStartDirection, float radius,
867 float fractionOfCircleToDraw, int numCircleSegments = 16) {
868 normal = normal.normalized;
869 Vector3 radiusVector = radialStartDirection.normalized * radius;
870 Vector3 nextVector;
871 int numSegmentsToDraw = (int)(numCircleSegments * fractionOfCircleToDraw);
872 Quaternion rotator = Quaternion.AngleAxis(360f / numCircleSegments, normal);
873 for (int i = 0; i < numSegmentsToDraw; i++) {
874 nextVector = rotator * radiusVector;
875
876 drawLineNow(center + radiusVector, center + nextVector);
877
878 radiusVector = nextVector;
879 }
880 }
881
882 private void setCurrentPassColorIfNew(Color desiredColor, ref int curPass) {
883 if (_currColor != desiredColor) {
884 _currColor = desiredColor;
885 setPass(ref curPass, isUnlit: true);
886 }
887 }
888
889 private void drawPlaneSoftenedWireArcNow(Vector3 position,
890 Vector3 circleNormal,
891 Vector3 radialStartDirection,
892 float radius,
893 Color inFrontOfPlaneColor,
894 Color behindPlaneColor,
895 Vector3 planeNormal,
896 ref int curPass,
897 float fractionOfCircleToDraw = 1.0f,
898 int numCircleSegments = 16) {
899 var origCurrColor = _currColor;
900
901 var onPlaneDir = planeNormal.Cross(circleNormal);
902 var Q = Quaternion.AngleAxis(360f / numCircleSegments, circleNormal);
903 var r = radialStartDirection * radius;
904 for (int i = 0; i < numCircleSegments + 1; i++) {
905 var nextR = Q * r;
906 var onPlaneAngle = Infix.Infix.SignedAngle(r, onPlaneDir, circleNormal);
907 var nextOnPlaneAngle = Infix.Infix.SignedAngle(nextR, onPlaneDir, circleNormal);
908 var front = onPlaneAngle < 0;
909 var nextFront = nextOnPlaneAngle < 0;
910
911 if (front != nextFront) {
912 var targetColor = Color.Lerp(inFrontOfPlaneColor, behindPlaneColor, 0.5f);
913 GL.End();
914 setPass(ref curPass, isUnlit: true, desiredCurrColor: targetColor);
915 GL.Begin(GL.LINES);
916 }
917 else if (front) {
918 var targetColor = inFrontOfPlaneColor;
919 GL.End();
920 setPass(ref curPass, isUnlit: true, desiredCurrColor: targetColor);
921 GL.Begin(GL.LINES);
922 }
923 else {
924 var targetColor = behindPlaneColor;
925 GL.End();
926 setPass(ref curPass, isUnlit: true, desiredCurrColor: targetColor);
927 GL.Begin(GL.LINES);
928 }
929
930 drawLineNow(r, nextR);
931
932 r = nextR;
933 }
934
935 _currColor = origCurrColor;
936 }
937
938 private void drawWireSphereNow(WireSphere wireSphere,
939 ref int curPass) {
940 var position = wireSphere.pose.position;
941 var rotation = wireSphere.pose.rotation;
942
943 var worldPosition = _currMatrix.MultiplyPoint3x4(position);
944
945 var dirToCamera = (Camera.current.transform.position - worldPosition).normalized;
946 var dirToCameraInMatrix = _currMatrix.inverse.MultiplyVector(dirToCamera);
947
948 // Wire sphere outline. This is just a wire sphere that faces the camera.
949 drawWireArcNow(position, dirToCameraInMatrix, dirToCameraInMatrix.Perpendicular(),
950 wireSphere.radius, 1.0f,
951 numCircleSegments: wireSphere.numSegments);
952
953
954 var x = rotation * Vector3.right;
955 var y = rotation * Vector3.up;
956 var z = rotation * Vector3.forward;
957
958 drawPlaneSoftenedWireArcNow(position, y, x, wireSphere.radius,
959 inFrontOfPlaneColor: _currColor,
960 behindPlaneColor: _currColor.WithAlpha(_currColor.a * 0.1f),
961 planeNormal: dirToCameraInMatrix,
962 curPass: ref curPass,
963 fractionOfCircleToDraw: 1.0f,
964 numCircleSegments: wireSphere.numSegments);
965 drawPlaneSoftenedWireArcNow(position, z, y, wireSphere.radius,
966 inFrontOfPlaneColor: _currColor,
967 behindPlaneColor: _currColor.WithAlpha(_currColor.a * 0.1f),
968 planeNormal: dirToCameraInMatrix,
969 curPass: ref curPass,
970 fractionOfCircleToDraw: 1.0f,
971 numCircleSegments: wireSphere.numSegments);
972 drawPlaneSoftenedWireArcNow(position, x, z, wireSphere.radius,
973 inFrontOfPlaneColor: _currColor,
974 behindPlaneColor: _currColor.WithAlpha(_currColor.a * 0.1f),
975 planeNormal: dirToCameraInMatrix,
976 curPass: ref curPass,
977 fractionOfCircleToDraw: 1.0f,
978 numCircleSegments: wireSphere.numSegments);
979 }
980
981 private void setPass(ref int currPass, bool isUnlit, Color? desiredCurrColor = null) {
982
983 bool needToUpdateColor = false;
984 if (desiredCurrColor.HasValue) {
985 needToUpdateColor = _currColor != desiredCurrColor.Value;
986 _currColor = desiredCurrColor.Value;
987 }
988
989 int newPass;
990 if (isUnlit) {
991 if (_currColor.a < 1) {
992 newPass = UNLIT_TRANSPARENT_PASS;
993 } else {
994 newPass = UNLIT_SOLID_PASS;
995 }
996 } else {
997 if (_currColor.a < 1) {
998 newPass = SHADED_TRANSPARENT_PASS;
999 } else {
1000 newPass = SHADED_SOLID_PASS;
1001 }
1002 }
1003
1004 if (currPass != newPass || needToUpdateColor) {
1005 currPass = newPass;
1006 _gizmoMaterial.color = _currColor;
1007 _gizmoMaterial.SetPass(currPass);
1008 }
1009 }
1010
1011 private void drawMeshInternal(Mesh mesh, Matrix4x4 matrix) {
1012 if (mesh == null) {
1013 throw new InvalidOperationException("Mesh cannot be null!");
1014 }
1015 _operations.Add(OperationType.DrawMesh);
1016 _meshes.Add(mesh);
1017 _matrices.Add(matrix);
1018 }
1019
1020 private void setWireMode(bool wireMode) {
1021 if (_isInWireMode != wireMode) {
1022 _operations.Add(OperationType.ToggleWireframe);
1023 _isInWireMode = wireMode;
1024 }
1025 }
1026
1027 private enum OperationType {
1028 SetMatrix,
1029 ToggleWireframe,
1030 SetColor,
1031 DrawLine,
1033 DrawMesh
1034 }
1035
1036 private struct Line {
1037 public Vector3 a, b;
1038
1039 public Line(Vector3 a, Vector3 b) {
1040 this.a = a;
1041 this.b = b;
1042 }
1043 }
1044
1045 private struct WireSphere {
1046 public Pose pose;
1047 public float radius;
1048 public int numSegments;
1049 }
1050 }
1051
1052 public static class RuntimeGizmoExtensions {
1053
1054 public static void DrawPose(this RuntimeGizmos.RuntimeGizmoDrawer drawer,
1055 Pose pose, float radius = 0.10f,
1056 bool drawCube = false) {
1057 drawer.PushMatrix();
1058
1059 drawer.matrix = Matrix4x4.TRS(pose.position, pose.rotation, Vector3.one);
1060
1061 var origColor = drawer.color;
1062
1063 //drawer.DrawWireSphere(Vector3.zero, radius);
1064 if (drawCube) {
1065 drawer.DrawCube(Vector3.zero, Vector3.one * radius * 0.3f);
1066 }
1067 drawer.DrawPosition(Vector3.zero, radius * 2);
1068
1069 drawer.color = origColor;
1070
1071 drawer.PopMatrix();
1072 }
1073
1074 public static void DrawRay(this RuntimeGizmos.RuntimeGizmoDrawer drawer,
1075 Vector3 position, Vector3 direction) {
1076 drawer.DrawLine(position, position + direction);
1077 }
1078
1079 }
1080
1081}
UnityEngine.Component Component
UnityEngine.Debug Debug
Definition: TanodaServer.cs:19
UnityEngine.Color Color
Definition: TestScript.cs:32
void DrawWireArc(Vector3 center, Vector3 normal, Vector3 radialStartDirection, float radius, float fractionOfCircleToDraw, int numCircleSegments=16)
void DrawPosition(Vector3 pos)
Draws a simple XYZ-cross position gizmo at the target position, whose size is scaled relative to the ...
void DrawColliders(GameObject gameObject, bool useWireframe=true, bool traverseHierarchy=true, bool drawTriggers=false)
void DrawWireCube(Vector3 position, Vector3 size)
Draws a wire gizmo cube at the given position with the given size.
void DrawWireSphere(Pose pose, float radius, int numSegments=32)
void DrawWireMesh(Mesh mesh, Vector3 position, Quaternion rotation, Vector3 scale)
Draws a wire gizmo mesh at the given transform location.
void PushMatrix()
Saves the current gizmo matrix to the gizmo matrix stack.
void DrawWireSphere(Vector3 center, float radius, int numSegments=32)
Draws a wire gizmo sphere at the given position with the given radius.
void DrawWireCapsule(Vector3 start, Vector3 end, float radius)
Draws a wire gizmo capsule at the given position, with the given start and end points and radius.
void RelativeTo(Transform transform)
Causes all remaining gizmos drawing to be done in the local coordinate space of the given transform.
void DrawCollider(Collider collider, bool useWireframe=true, bool skipMatrixSetup=false)
void DrawPosition(Vector3 pos, float overrideScale)
void PopMatrix()
Restores the current gizmo matrix from the gizmo matrix stack.
void DrawLine(Vector3 a, Vector3 b)
Draws a gizmo line that connects the two positions.
Matrix4x4 matrix
Sets or gets the matrix used to transform all gizmos.
void DrawCube(Vector3 position, Vector3 size)
Draws a filled gizmo cube at the given position with the given size.
void ResetMatrixAndColorState()
Resets the matrix to the identity matrix and the color to white.
void DrawRect(Transform frame, Rect rect)
void BeginGuard()
Begins a draw-guard. If any gizmos are drawn to this drawer an exception will be thrown at the end of...
Color color
Sets or gets the color for the gizmos that will be drawn next.
void DrawMesh(Mesh mesh, Matrix4x4 matrix)
Draw a filled gizmo mesh using the given matrix transform.
void DrawSphere(Vector3 center, float radius)
Draws a filled gizmo sphere at the given position with the given radius.
void DrawEllipsoid(Vector3 foci1, Vector3 foci2, float minorAxis)
Draws a wire ellipsoid gizmo with two specified foci and a specified minor axis length.
void DrawMesh(Mesh mesh, Vector3 position, Quaternion rotation, Vector3 scale)
Draws a filled gizmo mesh at the given transform location.
void EndGuard()
Ends a draw-guard. If any gizmos were drawn to this drawer during the guard, an exception will be thr...
void DrawWireMesh(Mesh mesh, Matrix4x4 matrix)
Draws a wire gizmo mesh using the given matrix transform.
void DrawPosition(Vector3 pos, Color lerpColor, float lerpCoeff, float? overrideScale=null)
Draws a simple XYZ-cross position gizmo at the target position, whose size is scaled relative to the ...
static bool TryGetGizmoDrawer(out RuntimeGizmoDrawer drawer)
Tries to get a gizmo drawer. Will fail if there is no Gizmo manager in the scene, or if it is disable...
static bool areGizmosDisabled(Transform transform)
static Action< RuntimeGizmoDrawer > OnPostRenderGizmos
Subscribe to this event if you want to draw gizmos after rendering is complete. Doing gizmo rendering...
static bool TryGetGizmoDrawer(GameObject attatchedGameObject, out RuntimeGizmoDrawer drawer)
Tries to get a gizmo drawer for a given gameObject. Will fail if there is no gizmo manager in the sce...
This class controls the display of all the runtime gizmos that are either attatched to this gameObjec...
Have your MonoBehaviour implement this interface to be able to draw runtime gizmos....
void OnDrawRuntimeGizmos(RuntimeGizmoDrawer drawer)
A position and rotation. You can multiply two poses; this acts like Matrix4x4 multiplication,...
Definition: Pose.cs:21