// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Utilities;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace Microsoft.MixedReality.Toolkit.UI
{
///
/// Theme Engine to control the value of a particular Shader Property based on state changes
/// Targets the first Renderer component on the initialized GameObject and use MaterialPropertyBlocks
///
public class InteractableShaderTheme : InteractableThemeBase
{
///
public override bool AreShadersSupported => true;
private static ThemePropertyValue emptyValue = new ThemePropertyValue();
protected MaterialPropertyBlock propertyBlock = new MaterialPropertyBlock();
protected List shaderProperties;
protected Renderer renderer;
private Graphic graphic;
protected const string DefaultShaderProperty = "_Color";
public InteractableShaderTheme()
{
Types = new Type[] { typeof(Renderer), typeof(Graphic) };
Name = "Shader Float";
}
///
public override ThemeDefinition GetDefaultThemeDefinition()
{
return new ThemeDefinition()
{
ThemeType = GetType(),
StateProperties = new List()
{
new ThemeStateProperty()
{
Name = "Shader Value",
Type = ThemePropertyTypes.ShaderFloat,
Values = new List(),
Default = new ThemePropertyValue() { Float = 0},
TargetShader = StandardShaderUtility.MrtkStandardShader,
ShaderPropertyName = DefaultShaderProperty,
},
},
CustomProperties = new List(),
};
}
///
public override void Init(GameObject host, ThemeDefinition definition)
{
renderer = host.GetComponent();
graphic = host.GetComponent();
base.Init(host, definition);
shaderProperties = new List();
foreach (var prop in StateProperties)
{
if (ThemeStateProperty.IsShaderPropertyType(prop.Type))
{
shaderProperties.Add(prop);
}
}
if (renderer != null)
{
propertyBlock = InteractableThemeShaderUtils.InitMaterialPropertyBlock(host, shaderProperties);
}
else if (graphic != null)
{
UIMaterialInstantiator.TryCreateMaterialCopy(graphic);
}
// Need to update reset history tracking now that property blocks are populated correctly via above code
var keys = new List(originalStateValues.Keys);
foreach (var value in keys)
{
originalStateValues[value] = GetProperty(value);
}
}
///
public override ThemePropertyValue GetProperty(ThemeStateProperty property)
{
var result = new ThemePropertyValue();
int propId = property.GetShaderPropertyId();
if (renderer != null)
{
renderer.GetPropertyBlock(propertyBlock);
switch (property.Type)
{
case ThemePropertyTypes.Color:
result.Color = propertyBlock.GetVector(propId);
break;
case ThemePropertyTypes.Texture:
result.Texture = propertyBlock.GetTexture(propId);
break;
case ThemePropertyTypes.ShaderFloat:
case ThemePropertyTypes.ShaderRange:
result.Float = propertyBlock.GetFloat(propId);
break;
default:
break;
}
}
else if (graphic != null)
{
switch (property.Type)
{
case ThemePropertyTypes.Color:
result.Color = graphic.material.GetVector(propId);
break;
case ThemePropertyTypes.Texture:
result.Texture = graphic.material.GetTexture(propId);
break;
case ThemePropertyTypes.ShaderFloat:
case ThemePropertyTypes.ShaderRange:
result.Float = graphic.material.GetFloat(propId);
break;
default:
break;
}
}
return result;
}
///
public override void SetValue(ThemeStateProperty property, int index, float percentage)
{
var propValue = property.Values[index];
Color newColor = property.StartValue.Color;
float newFloatValue = property.StartValue.Float;
switch (property.Type)
{
case ThemePropertyTypes.Color:
newColor = Color.Lerp(newColor, propValue.Color, percentage);
break;
case ThemePropertyTypes.ShaderFloat:
case ThemePropertyTypes.ShaderRange:
newFloatValue = LerpFloat(newFloatValue, propValue.Float, percentage);
break;
default:
break;
}
SetShaderValue(property, newColor, propValue.Texture, newFloatValue);
}
///
protected override void SetValue(ThemeStateProperty property, ThemePropertyValue value)
{
SetShaderValue(property, value.Color, value.Texture, value.Float);
}
private void SetShaderValue(ThemeStateProperty property, Color color, Texture tex, float floatValue)
{
int propId = property.GetShaderPropertyId();
if (renderer != null)
{
renderer.GetPropertyBlock(propertyBlock);
switch (property.Type)
{
case ThemePropertyTypes.Color:
propertyBlock.SetColor(propId, color);
break;
case ThemePropertyTypes.Texture:
if (tex != null)
{
propertyBlock.SetTexture(propId, tex);
}
break;
case ThemePropertyTypes.ShaderFloat:
case ThemePropertyTypes.ShaderRange:
propertyBlock.SetFloat(propId, floatValue);
break;
default:
break;
}
renderer.SetPropertyBlock(propertyBlock);
}
else if (graphic != null)
{
switch (property.Type)
{
case ThemePropertyTypes.Color:
graphic.material.SetColor(propId, color);
break;
case ThemePropertyTypes.Texture:
graphic.material.SetTexture(propId, tex);
break;
case ThemePropertyTypes.ShaderFloat:
case ThemePropertyTypes.ShaderRange:
graphic.material.SetFloat(propId, floatValue);
break;
default:
break;
}
}
}
#region Obsolete
[System.Obsolete("GetFloat is no longer supported. Access the material block directly on the GameObject provided.")]
public static float GetFloat(GameObject host, int propId)
{
if (host == null)
{
return 0;
}
MaterialPropertyBlock block = InteractableThemeShaderUtils.GetPropertyBlock(host);
return block.GetFloat(propId);
}
[System.Obsolete("GetColor is no longer supported. Access the material block directly on the GameObject provided.")]
public static Color GetColor(GameObject host, int propId)
{
if (host == null)
{
return Color.white;
}
MaterialPropertyBlock block = InteractableThemeShaderUtils.GetPropertyBlock(host);
return block.GetVector(propId);
}
#endregion
}
}