AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
LineMeshes.cs
Go to the documentation of this file.
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 HoloToolkit.Unity;
5 using System.Collections.Generic;
6 using UnityEngine;
7 using UnityEngine.Rendering;
8 
9 namespace HoloToolkit.Unity.UX
10 {
11  [UseWith(typeof(LineBase))]
13  {
14  readonly string InvisibleShaderName = "MixedRealityToolkit/InvisibleShader";
15 
16  [Header("Instanced Mesh Settings")]
17  public Mesh LineMesh;
18 
19  public Material LineMaterial;
20 
21  [MaterialProperty(MaterialPropertyAttribute.PropertyTypeEnum.Color, "LineMaterial")]
22  public string ColorProp = "_Color";
23 
24  // Command buffer properties
25  private MaterialPropertyBlock linePropertyBlock;
26  private int colorID;
27  private Matrix4x4[] meshTransforms;
28  private Vector4[] colorValues;
29  private bool executeCommandBuffer = false;
30  private Dictionary<Camera, CommandBuffer> cameras = new Dictionary<Camera, CommandBuffer>();
31  // OnWillRenderObject helpers
32  private MeshRenderer onWillRenderHelper;
33  private Mesh onWillRenderMesh;
34  private Material onWillRenderMat;
35  private Vector3[] meshVertices = new Vector3[3];
36 
37  protected void OnEnable()
38  {
39  if (LineMaterial == null)
40  {
41  Debug.LogError("Line material cannot be null.");
42  enabled = false;
43  return;
44  }
45 
46  if (linePropertyBlock == null)
47  {
48  LineMaterial.enableInstancing = true;
49  linePropertyBlock = new MaterialPropertyBlock();
50  colorID = Shader.PropertyToID(ColorProp);
51  }
52 
53  if (onWillRenderHelper == null)
54  { // OnWillRenderObject won't be called unless there's a renderer attached
55  // and if the renderer's bounds are visible.
56  // So we create a simple 1-triangle mesh to ensure it's always called.
57  // Hacky, but it works.
58  onWillRenderHelper = gameObject.AddComponent<MeshRenderer>();
59  onWillRenderHelper.receiveShadows = false;
60  onWillRenderHelper.shadowCastingMode = ShadowCastingMode.Off;
61  onWillRenderHelper.lightProbeUsage = LightProbeUsage.Off;
62  onWillRenderHelper.motionVectorGenerationMode = MotionVectorGenerationMode.ForceNoMotion;
63 
64  onWillRenderMesh = new Mesh();
65  onWillRenderMesh.vertices = meshVertices;
66  onWillRenderMesh.triangles = new int[] { 0, 1, 2 };
67 
68  MeshFilter helperMeshFilter = gameObject.AddComponent<MeshFilter>();
69  helperMeshFilter.sharedMesh = onWillRenderMesh;
70 
71  // Create an 'invisible' material so the mesh doesn't show up pink
72  onWillRenderMat = new Material(Shader.Find(InvisibleShaderName));
73  onWillRenderHelper.sharedMaterial = onWillRenderMat;
74  }
75  }
76 
77  private void Update()
78  {
79  executeCommandBuffer = false;
80 
81  if (Source.enabled)
82  {
83  if (meshTransforms == null || meshTransforms.Length != NumLineSteps)
84  {
85  meshTransforms = new Matrix4x4[NumLineSteps];
86  }
87 
88  if (colorValues == null || colorValues.Length != NumLineSteps)
89  {
90  colorValues = new Vector4[NumLineSteps];
91  linePropertyBlock.Clear();
92  }
93 
94  for (int i = 0; i < NumLineSteps; i++)
95  {
96  float normalizedDistance = (1f / (NumLineSteps - 1)) * i;
97  colorValues[i] = GetColor(normalizedDistance);
98  meshTransforms[i] = Matrix4x4.TRS(Source.GetPoint(normalizedDistance), Source.GetRotation(normalizedDistance), Vector3.one * GetWidth(normalizedDistance));
99  }
100 
101  linePropertyBlock.SetVectorArray(colorID, colorValues);
102 
103  executeCommandBuffer = true;
104  }
105  }
106 
107  private void OnDisable()
108  {
109  foreach (KeyValuePair<Camera, CommandBuffer> cam in cameras)
110  {
111  if (cam.Key != null)
112  {
113  cam.Key.RemoveCommandBuffer(CameraEvent.AfterForwardOpaque, cam.Value);
114  }
115  }
116  cameras.Clear();
117  }
118 
119  private void OnWillRenderObject()
120  {
121  Camera cam = Camera.current;
122  CommandBuffer buffer = null;
123  if (!cameras.TryGetValue(cam, out buffer))
124  {
125  buffer = new CommandBuffer();
126  buffer.name = "Line Mesh Renderer " + cam.name;
127  cam.AddCommandBuffer(CameraEvent.AfterForwardOpaque, buffer);
128  cameras.Add(cam, buffer);
129  }
130 
131  buffer.Clear();
132  if (executeCommandBuffer)
133  {
134  buffer.DrawMeshInstanced(LineMesh, 0, LineMaterial, 0, meshTransforms, meshTransforms.Length, linePropertyBlock);
135  }
136  }
137 
138  private void LateUpdate()
139  {
140  // Update our helper mesh so OnWillRenderObject will be called
141  meshVertices[0] = transform.InverseTransformPoint(Source.GetPoint(0.0f));// - transform.position;
142  meshVertices[1] = transform.InverseTransformPoint(Source.GetPoint(0.5f));// - transform.position;
143  meshVertices[2] = transform.InverseTransformPoint(Source.GetPoint(1.0f));// - transform.position;
144  onWillRenderMesh.vertices = meshVertices;
145  onWillRenderMesh.RecalculateBounds();
146  }
147 
148 #if UNITY_EDITOR
149  [UnityEditor.CustomEditor(typeof(LineMeshes))]
150  public class CustomEditor : MRTKEditor { }
151 #endif
152  }
153 }