// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Generic; using System.Linq; using UnityEngine; namespace Microsoft.MixedReality.Toolkit.Examples.Demos.StandardShader { /// /// Builds a matrix of spheres demonstrating a spectrum of two material properties. /// [AddComponentMenu("Scripts/MRTK/Examples/MaterialMatrix")] public class MaterialMatrix : MonoBehaviour { [SerializeField] private Material material = null; [SerializeField] private Mesh mesh = null; [SerializeField] [Range(2, 100)] private int dimension = 5; [SerializeField] [Range(0.0f, 10.0f)] private float positionOffset = 0.1f; [SerializeField] private string firstPropertyName = "_Metallic"; [SerializeField] private string secondPropertyName = "_Glossiness"; [SerializeField] private Vector3 localRotation = Vector3.zero; private void Awake() { BuildMatrix(); } public void BuildMatrix() { List children = transform.Cast().ToList(); for (int i = 0; i < children.Count; ++i) { Transform child = children[i]; DestroyObject(child.gameObject); } if (material == null) { Debug.LogError("Failed to build material matrix due to missing material."); return; } float center = (dimension - 1) * positionOffset * -0.5f; Vector3 position = new Vector3(center, 0.0f, center); int firstPropertyId = Shader.PropertyToID(firstPropertyName); int secondPropertyId = Shader.PropertyToID(secondPropertyName); float firstProperty = 0.0f; float secondProperty = 0.0f; for (int i = 0; i < dimension; ++i) { for (int j = 0; j < dimension; ++j) { GameObject element = GameObject.CreatePrimitive(PrimitiveType.Sphere); element.name = "Element" + (i * dimension + j); element.transform.parent = transform; element.transform.localPosition = position; element.transform.localRotation = Quaternion.Euler(localRotation); position.x += positionOffset; Material newMaterial = new Material(material); newMaterial.SetFloat(firstPropertyId, firstProperty); newMaterial.SetFloat(secondPropertyId, secondProperty); Renderer _renderer = element.GetComponent(); MeshFilter meshFilter = element.GetComponent(); if (Application.isPlaying == true) { _renderer.material = newMaterial; if (mesh != null) { meshFilter.mesh = mesh; Destroy(element.GetComponent()); element.AddComponent(); } } else { _renderer.sharedMaterial = newMaterial; if (mesh != null) { meshFilter.mesh = mesh; DestroyImmediate(element.GetComponent()); element.AddComponent(); } } firstProperty += 1.0f / (dimension - 1); } position.x = center; position.z += positionOffset; firstProperty = 0.0f; secondProperty += 1.0f / (dimension - 1); } } /// /// Destroys a Unity object appropriately depending if running in edit or play mode. /// /// Unity object to destroy /// Time in seconds at which to destroy the object, if applicable. private static void DestroyObject(Object obj, float t = 0.0f) { if (Application.isPlaying) { Object.Destroy(obj, t); } else { #if UNITY_EDITOR // Must use DestroyImmediate in edit mode but it is not allowed when called from // trigger/contact, animation event callbacks or OnValidate. Must use Destroy instead. // Delay call to counter this issue in editor UnityEditor.EditorApplication.delayCall += () => { Object.DestroyImmediate(obj); }; #else Object.DestroyImmediate(obj); #endif } } } }