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