// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.UI.BoundsControlTypes; using System.Collections.Generic; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.UI.BoundsControl { /// /// Links that are rendered in between the corners of /// public class Links { /// /// defines a bounds control link - the line between 2 corners of a box /// it keeps track of the transform and the axis the link is representing /// private class Link { public Transform transform; public CardinalAxisType axisType; public Link(Transform linkTransform, CardinalAxisType linkAxis) { transform = linkTransform; axisType = linkAxis; } } private List links = new List(); private LinksConfiguration config; private Vector3 cachedExtents; private FlattenModeType cachedFlattenAxis; internal Links(LinksConfiguration configuration) { Debug.Assert(configuration != null, "Can't create BoundsControlLinks without valid configuration"); config = configuration; config.wireFrameChanged.AddListener(UpdateWireframe); } ~Links() { config.wireFrameChanged.RemoveListener(UpdateWireframe); } private void UpdateWireframe(LinksConfiguration.WireframeChangedEventType changedType) { switch (changedType) { case LinksConfiguration.WireframeChangedEventType.Visibility: Reset(config.ShowWireFrame, cachedFlattenAxis); break; case LinksConfiguration.WireframeChangedEventType.Radius: UpdateLinkScales(cachedExtents); break; case LinksConfiguration.WireframeChangedEventType.Shape: UpdateLinkPrimitive(); break; case LinksConfiguration.WireframeChangedEventType.Material: UpdateMaterial(); break; } } internal void Clear() { if (links != null) { foreach (Link link in links) { Object.Destroy(link.transform.gameObject); } links.Clear(); } } internal void UpdateMaterial() { if (links != null && config.WireframeMaterial != null) { for (int i = 0; i < links.Count; ++i) { Renderer linkRenderer = links[i].transform.gameObject.GetComponent(); if (linkRenderer != null) { linkRenderer.material = config.WireframeMaterial; } } } } internal void UpdateVisibilityInInspector(HideFlags flags) { if (links != null) { foreach (var link in links) { link.transform.hideFlags = flags; } } } internal void Reset(bool isVisible, FlattenModeType flattenAxis) { cachedFlattenAxis = flattenAxis; if (links != null) { for (int i = 0; i < links.Count; ++i) { links[i].transform.gameObject.SetActive(isVisible && config.ShowWireFrame); } List flattenedHandles = VisualUtils.GetFlattenedIndices(cachedFlattenAxis, VisualUtils.EdgeAxisType); if (flattenedHandles != null && VisualUtils.EdgeAxisType.Length == links.Count) { for (int i = 0; i < flattenedHandles.Count; ++i) { links[flattenedHandles[i]].transform.gameObject.SetActive(false); } } } } private Vector3 GetLinkDimensions(Vector3 currentBoundsExtents) { float wireframeEdgeRadius = config.WireframeEdgeRadius; float linkLengthAdjustor = config.WireframeShape == WireframeType.Cubic ? 2.0f : 1.0f - (6.0f * wireframeEdgeRadius); return (currentBoundsExtents * linkLengthAdjustor) + new Vector3(wireframeEdgeRadius, wireframeEdgeRadius, wireframeEdgeRadius); } internal void UpdateLinkPositions(ref Vector3[] boundsCorners) { if (boundsCorners != null) { for (int i = 0; i < links.Count; ++i) { links[i].transform.position = VisualUtils.GetLinkPosition(i, ref boundsCorners); } } } internal void UpdateLinkScales(Vector3 currentBoundsExtents) { if (links != null) { for (int i = 0; i < links.Count; ++i) { Transform parent = links[i].transform.parent; Vector3 rootScale = parent.lossyScale; Vector3 invRootScale = new Vector3(1.0f / rootScale[0], 1.0f / rootScale[1], 1.0f / rootScale[2]); // Compute the local scale that produces the desired world space dimensions Vector3 linkDimensions = Vector3.Scale(GetLinkDimensions(currentBoundsExtents), invRootScale); float wireframeEdgeRadius = config.WireframeEdgeRadius; if (links[i].axisType == CardinalAxisType.X) { links[i].transform.localScale = new Vector3(wireframeEdgeRadius, linkDimensions.x, wireframeEdgeRadius); } else if (links[i].axisType == CardinalAxisType.Y) { links[i].transform.localScale = new Vector3(wireframeEdgeRadius, linkDimensions.y, wireframeEdgeRadius); } else//Z { links[i].transform.localScale = new Vector3(wireframeEdgeRadius, linkDimensions.z, wireframeEdgeRadius); } } cachedExtents = currentBoundsExtents; } } private void UpdateLinkPrimitive() { GameObject sharedMeshPrimitive; if (config.WireframeShape == WireframeType.Cubic) { sharedMeshPrimitive = GameObject.CreatePrimitive(PrimitiveType.Cube); } else { sharedMeshPrimitive = GameObject.CreatePrimitive(PrimitiveType.Cylinder); } var sharedMeshFilter = sharedMeshPrimitive.GetComponent(); if (sharedMeshFilter) { foreach (var link in links) { GameObject linkObj = link.transform.gameObject; // replace shared mesh - note that we don't have a collider to replace in case of wireframe linkObj.GetComponent().sharedMesh = sharedMeshFilter.sharedMesh; } } Object.Destroy(sharedMeshPrimitive); UpdateLinkScales(cachedExtents); } internal void CreateLinks(ref Vector3[] boundsCorners, Transform parent, Vector3 currentBoundsExtents) { // create links if (links != null) { GameObject link; Vector3 linkDimensions = GetLinkDimensions(currentBoundsExtents); for (int i = 0; i < VisualUtils.EdgeAxisType.Length; ++i) { if (config.WireframeShape == WireframeType.Cubic) { link = GameObject.CreatePrimitive(PrimitiveType.Cube); Object.Destroy(link.GetComponent()); } else { link = GameObject.CreatePrimitive(PrimitiveType.Cylinder); Object.Destroy(link.GetComponent()); } link.name = "link_" + i.ToString(); CardinalAxisType axisType = VisualUtils.EdgeAxisType[i]; float wireframeEdgeRadius = config.WireframeEdgeRadius; if (axisType == CardinalAxisType.Y) { link.transform.localScale = new Vector3(wireframeEdgeRadius, linkDimensions.y, wireframeEdgeRadius); link.transform.Rotate(new Vector3(0.0f, 90.0f, 0.0f)); } else if (axisType == CardinalAxisType.Z) { link.transform.localScale = new Vector3(wireframeEdgeRadius, linkDimensions.z, wireframeEdgeRadius); link.transform.Rotate(new Vector3(90.0f, 0.0f, 0.0f)); } else//X { link.transform.localScale = new Vector3(wireframeEdgeRadius, linkDimensions.x, wireframeEdgeRadius); link.transform.Rotate(new Vector3(0.0f, 0.0f, 90.0f)); } link.transform.position = VisualUtils.GetLinkPosition(i, ref boundsCorners); link.transform.parent = parent; Renderer linkRenderer = link.GetComponent(); if (config.WireframeMaterial != null) { linkRenderer.material = config.WireframeMaterial; } link.SetActive(config.ShowWireFrame); links.Add(new Link(link.transform, axisType)); } } } } }