// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #if GT_USE_INPUT_SYSTEM && ENABLE_INPUT_SYSTEM #define USE_INPUT_SYSTEM #endif using UnityEngine; #if USE_INPUT_SYSTEM using UnityEngine.InputSystem; #endif // USE_INPUT_SYSTEM namespace Microsoft.MixedReality.GraphicsTools.Samples.MeshInstancing { /// /// Renders a (dimension x dimension x dimension) cube of instances that can be clicked on and destroyed. /// public class InstancingMining : MonoBehaviour { [SerializeField] private MeshInstancer instancer = null; [SerializeField, Min(1)] private int dimension = 10; private MeshInstancer.RaycastHit lastRaycastHit; private Color lastColor; /// /// Re-spawn instances when a property changes. /// private void OnValidate() { if (instancer != null && instancer.InstanceCount != 0) { CreateInstances(); } } /// /// Create instances on enable. /// private void OnEnable() { CreateInstances(); } private void Update() { if (instancer.RaycastInstances) { // Visualize the ray and hits. Debug.DrawLine(instancer.DeferredRayQuery.origin, instancer.DeferredRayQuery.origin + instancer.DeferredRayQuery.direction * 100.0f, Color.red); int colorID = Shader.PropertyToID("_Color"); // Clear the color on the last raycast hit, if (lastRaycastHit.Instance != null) { lastRaycastHit.Instance.SetVector(colorID, lastColor); } // Get the closest hit and color it red. if (instancer.GetClosestRaycastHit(ref lastRaycastHit)) { Debug.DrawLine(lastRaycastHit.Point, lastRaycastHit.Point + lastRaycastHit.Normal, Color.green); lastColor = lastRaycastHit.Instance.GetVector(colorID); lastRaycastHit.Instance.SetVector(colorID, Color.red); if (AddCubeInput()) { Vector3 offset = Vector3.Scale(lastRaycastHit.Normal, transform.localScale) * 0.5f; var instance = instancer.Instantiate(lastRaycastHit.Point + offset, transform.rotation, transform.localScale, true); instance.SetVector("_Color", lastColor); } else if (RemoveCubeInput()) { lastRaycastHit.Instance.Destroy(); lastRaycastHit.Instance = null; } } // Update the ray for the next frame. instancer.DeferredRayQuery = GetRay(); } } /// /// Render the bounds of the instances. /// private void OnDrawGizmosSelected() { Gizmos.color = Color.yellow; Gizmos.matrix = transform.localToWorldMatrix; Gizmos.DrawWireCube(Vector3.zero, Vector3.one * dimension); } /// /// Create a bunch of instances in a cube formation. /// private void CreateInstances() { // Clear any existing instances. instancer = (instancer == null) ? GetComponent() : instancer; instancer.Clear(); int colorID = Shader.PropertyToID("_Color"); Vector3 offset = Vector3.one * (dimension - 1) * 0.5f; for (int i = 0; i < dimension; ++i) { for (int j = 0; j < dimension; ++j) { for (int k = 0; k < dimension; ++k) { var instance = instancer.Instantiate(new Vector3(i, j, k) - offset, Quaternion.identity, Vector3.one); instance.SetVector(colorID, new Color((float)i / dimension, (float)j / dimension, (float)k / dimension, 1.0f)); } } } } /// /// Returns the ray going from the mouse (or camera if no mouse) into the world. /// private Ray GetRay() { // Default to the camera look position. Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward); #if USE_INPUT_SYSTEM if (Mouse.current != null) { Vector2 mousePosition2D = Mouse.current.position.ReadValue(); Vector3 mousePosition = new Vector3(mousePosition2D.x, mousePosition2D.y, Camera.main.nearClipPlane); ray.direction = (Camera.main.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, Camera.main.farClipPlane)) - ray.origin).normalized; } #else if (Input.mousePresent) { Vector3 mousePosition = new Vector3(Input.mousePosition.x, Input.mousePosition.y, Camera.main.nearClipPlane); ray.direction = (Camera.main.ScreenToWorldPoint(new Vector3(mousePosition.x, mousePosition.y, Camera.main.farClipPlane)) - ray.origin).normalized; } #endif // USE_INPUT_SYSTEM return ray; } /// /// Returns true if input to add a cube is active. /// private bool AddCubeInput() { #if USE_INPUT_SYSTEM return Mouse.current.leftButton.wasPressedThisFrame && Keyboard.current.shiftKey.isPressed; #else return Input.GetMouseButtonDown(0) && (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)); #endif // USE_INPUT_SYSTEM } /// /// Returns true if input to remove a cube is active. /// private bool RemoveCubeInput() { #if USE_INPUT_SYSTEM return Mouse.current.leftButton.isPressed && !Keyboard.current.shiftKey.isPressed; #else return Input.GetMouseButton(0) && !(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)); #endif // USE_INPUT_SYSTEM } } }