using System; using UnityEngine; using Framework.Services; namespace Framework.Support { /// Stores a material to be assigned to a renderer's material slot [Serializable] public class MaterialAssignment { /// Renderer on which the material assignment will take place /// /// This can be null, in which case the material will be assigned to the renderer /// to which the MaterialRandomizer has been added. /// public Renderer Renderer; /// Index of the slot to which hte material will be assigned public int SlotIndex; /// Material that will be assigned to the slot public Material Material; } /// Set of material assignments that can be picked by the randomizer [Serializable] public class MaterialAssignmentSet { /// Likelihood that this material set will be chosen /// /// /// The fraction of these points against the sum of all points becomes the likelihood /// with which this material will be chosen. /// /// /// Example 1: Two assignments, both with Likelihood 1: each assignment has /// a 50% chance of being selected. /// /// /// Example 2: Two assignments, with with Likelihood 10, the other 20. The one with /// likelihood 20 will have a 67% chance (20 out of 30) of being selected. /// /// public int Likelihood; /// Material assignments this set consists of public MaterialAssignment[] Assignments; } /// Assigns a random set of materials to a mesh upon load public class MaterialRandomizer : ScriptComponent { /// Material variants that can be assigned to the randomizer's renderer public MaterialAssignmentSet[] Variants; /// Called once before the game object is updated for the first time protected void Start() { if((this.Variants == null) || (this.Variants.Length == 0)) { Debug.LogWarning( "Material randomizer component on '" + name + "' has no material sets " + "sets defined. Randomizer will do nothing" ); return; } int variantIndex = pickVariant(); applyMaterialSet(this.Variants[variantIndex]); } /// Picks a random material variant using the defined likelihoods /// The picked material variant private int pickVariant() { // Sum up the likelihoods of all defined variants int sum = this.Variants[0].Likelihood; for(int index = 1; index < this.Variants.Length; ++index) { sum += this.Variants[index].Likelihood; } // Pick a random number within that sum int choice = UnityEngine.Random.Range(0, sum); // Now figure out which variant this number falls into sum = 0; for(int index = 0; index < this.Variants.Length; ++index) { sum += this.Variants[0].Likelihood; if(choice < sum) { return index; } } // Never reached, but static analysis can't see this return 0; } /// Apply the specified material set to the game object's renderer /// Material set that will be applied private void applyMaterialSet(MaterialAssignmentSet set) { if(set.Assignments == null) { return; } bool warningDisplayed = false; Renderer ownRenderer = null; for(int index = 0; index < set.Assignments.Length; ++index) { Renderer renderer; // If there's no renderer defined, the assignment goes to our own renderer if(set.Assignments[index].Renderer == null) { // If we alaready displayed a warning, we know our own renderer is missing // and can stop trying right away if(warningDisplayed) { continue; } // If we haven't looked up our renderer yet, see if the game object to which // we're attached has a renderer component. if(ownRenderer == null) { ownRenderer = GetComponent(); if(ownRenderer == null) { Debug.LogWarning( "MaterialRandomizer on '" + name + "' does not have a renderer to " + "assign one or more materials to." ); warningDisplayed = true; continue; } } renderer = ownRenderer; } else { // Renderer has been specified explicitly, use it renderer = set.Assignments[index].Renderer; } // We've got our renderer, do the material assignment (which requires us to replace // the whole array because the Unity developers decided to return a copy of // the material array instead of exposing a proxy IList) Material[] materials = renderer.materials; int slotIndex = set.Assignments[index].SlotIndex; materials[slotIndex] = set.Assignments[index].Material; renderer.materials = materials; } } } } // namespace Framework.Support