using MalbersAnimations.Events;
using System.Collections.Generic;
using UnityEngine;
namespace MalbersAnimations.Utilities
{
/// Manage the Blend Shapes of a Mesh
[AddComponentMenu("Malbers/Utilities/Mesh/Blend Shapes")]
public class BlendShape : MonoBehaviour
{
[CreateScriptableAsset]
public BlendShapePreset preset;
public bool LoadPresetOnStart = true;
[RequiredField]
public SkinnedMeshRenderer mesh;
public SkinnedMeshRenderer[] LODs;
[Range(0, 100)]
public float[] blendShapes; //Value of the Blend Shape
public bool random;
public int PinnedShape;
/// Does the mesh has Blend Shapes?
internal bool HasBlendShapes => mesh && mesh.sharedMesh.blendShapeCount > 0;
private void Start()
{
if (LoadPresetOnStart)
LoadPreset();
else if (random)
Randomize();
}
private void Reset()
{
mesh = GetComponentInChildren();
if (mesh)
{
blendShapes = new float[mesh.sharedMesh.blendShapeCount];
for (int i = 0; i < blendShapes.Length; i++)
blendShapes[i] = mesh.GetBlendShapeWeight(i);
}
}
/// Returns the current Blend Shapes Values
public virtual float[] GetBlendShapeValues()
{
if (HasBlendShapes)
{
float[] BS = new float[mesh.sharedMesh.blendShapeCount];
for (int i = 0; i < BS.Length; i++)
{
BS[i] = mesh.GetBlendShapeWeight(i);
}
return BS;
}
return null;
}
public void SmoothBlendShape(BlendShapePreset preset) => LoadSmoothPreset(preset);
public void LoadSmoothPreset(BlendShapePreset preset)
{
StopAllCoroutines();
preset.SmoothBlend(mesh);
}
public void SavePreset()
{
if (preset)
{
preset.blendShapes = new float[blendShapes.Length];
for (int i = 0; i < preset.blendShapes.Length; i++)
{
preset.blendShapes[i] = blendShapes[i];
}
Debug.Log("Preset: " + preset.name + " Saved");
MTools.SetDirty(preset);
}
}
public void LoadPreset() => LoadPreset(preset);
public void LoadPreset(BlendShapePreset preset)
{
if (preset)
{
blendShapes = new float[preset.blendShapes.Length];
for (int i = 0; i < preset.blendShapes.Length; i++)
{
blendShapes[i] = preset.blendShapes[i];
}
Debug.Log("Preset: " + preset.name + " Loaded");
UpdateBlendShapes();
MTools.SetDirty(preset);
}
}
public virtual void SetShapesCount()
{
if (mesh)
{
blendShapes = new float[mesh.sharedMesh.blendShapeCount];
for (int i = 0; i < blendShapes.Length; i++)
{
blendShapes[i] = mesh.GetBlendShapeWeight(i);
}
}
}
/// Set Random Values to the Mesh Blend Shapes
public virtual void Randomize()
{
if (HasBlendShapes)
{
for (int i = 0; i < blendShapes.Length; i++)
{
blendShapes[i] = Random.Range(0, 100);
mesh.SetBlendShapeWeight(i, blendShapes[i]);
}
UpdateLODs();
}
}
/// Set a weight of a Blend Shape by its name
public virtual void SetWeight(string name, float value)
{
if (HasBlendShapes)
{
PinnedShape = mesh.sharedMesh.GetBlendShapeIndex(name);
if (PinnedShape != -1)
{
mesh.SetBlendShapeWeight(PinnedShape, value);
}
}
}
/// Set a weight of a Blend Shape by its index
public virtual void SetWeight(int index, float value)
{
if (HasBlendShapes)
mesh.SetBlendShapeWeight(PinnedShape = index, value);
}
public virtual void _PinShape(string name)
{
PinnedShape = mesh.sharedMesh.GetBlendShapeIndex(name);
}
public virtual void _PinShape(int index)
{
PinnedShape = index;
}
public virtual void _PinnedShapeSetValue(float value)
{
if (PinnedShape != -1)
{
value = Mathf.Clamp(value, 0, 100);
blendShapes[PinnedShape] = value;
mesh.SetBlendShapeWeight(PinnedShape, value);
UpdateLODs(PinnedShape);
}
}
public virtual void UpdateBlendShapes()
{
if (mesh && blendShapes != null)
{
int Length = Mathf.Min(mesh.sharedMesh.blendShapeCount, blendShapes.Length);
for (int i = 0; i < Length; i++)
{
mesh.SetBlendShapeWeight(i, blendShapes[i]);
}
UpdateLODs();
}
}
/// Update the LODs Values
protected virtual void UpdateLODs()
{
for (int i = 0; i < blendShapes.Length; i++)
{
UpdateLODs(i);
}
}
/// Updates Only a Shape in all LODS
protected virtual void UpdateLODs(int index)
{
if (LODs != null)
{
foreach (var lods in LODs)
{
if (lods != null && lods.sharedMesh.blendShapeCount > index)
lods.SetBlendShapeWeight(index, blendShapes[index]);
}
}
}
#if UNITY_EDITOR
[ContextMenu("Create Event Listeners")]
void CreateListeners()
{
MEventListener listener = this.FindComponent();
if (listener == null) listener = transform.root.gameObject.AddComponent();
if (listener.Events == null) listener.Events = new List();
MEvent BlendS = MTools.GetInstance("Blend Shapes");
if (listener.Events.Find(item => item.Event == BlendS) == null)
{
var item = new MEventItemListener()
{
Event = BlendS,
useVoid = false,
useString = true,
useInt = true,
useFloat = true
};
UnityEditor.Events.UnityEventTools.AddPersistentListener(item.ResponseInt, _PinShape);
UnityEditor.Events.UnityEventTools.AddPersistentListener(item.ResponseString, _PinShape);
UnityEditor.Events.UnityEventTools.AddPersistentListener(item.ResponseFloat, _PinnedShapeSetValue);
listener.Events.Add(item);
Debug.Log("Blend Shapes Added to the Event Listeners");
MTools.SetDirty(listener);
}
}
#endif
}
}