2025-12-05 18:47:59 +09:00

159 lines
5.8 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
namespace MaterialTrack.Group
{
public class MaterialGroupMixer : PlayableBehaviour, IMixer
{
private MaterialGroup materialGroup;
// Dictionary to store original material states for restoration
private Dictionary<int, Material> originalMaterials = new Dictionary<int, Material>();
private bool firstFrameHappened = false;
// IMixer implementation
private readonly RenderTextureCache renderTextureCache = new RenderTextureCache();
private readonly Texture2DCache texture2DCache = new Texture2DCache();
public RenderTextureCache RenderTextureCache => renderTextureCache;
public Texture2DCache Texture2DCache => texture2DCache;
public IEnumerable<Material> Materials
{
get
{
if (materialGroup != null && materialGroup.materials != null)
{
return materialGroup.materials;
}
return new Material[0];
}
}
public override void OnPlayableDestroy(Playable playable)
{
RestoreMaterials();
firstFrameHappened = false;
// renderTextureCache.Dispose(); // Not available
// texture2DCache.Dispose(); // Not available
}
private void RestoreMaterials()
{
if (materialGroup == null || materialGroup.materials == null) return;
foreach (var mat in materialGroup.materials)
{
if (mat == null) continue;
if (originalMaterials.TryGetValue(mat.GetInstanceID(), out Material original))
{
mat.CopyPropertiesFromMaterial(original);
}
}
// Cleanup copies
foreach(var copy in originalMaterials.Values)
{
if (copy != null) Object.DestroyImmediate(copy);
}
originalMaterials.Clear();
}
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
materialGroup = playerData as MaterialGroup;
if (materialGroup == null || materialGroup.materials == null)
return;
int inputCount = playable.GetInputCount();
if (inputCount == 0)
return;
// Initialize / Backup on first frame
if (!firstFrameHappened)
{
foreach (var mat in materialGroup.materials)
{
if (mat == null) continue;
if (!originalMaterials.ContainsKey(mat.GetInstanceID()))
{
originalMaterials[mat.GetInstanceID()] = new Material(mat);
}
}
firstFrameHappened = true;
}
// Calculate the mixed behaviour
RendererBehaviour mixedBehaviour = null;
float totalWeight = 0f;
for (int i = 0; i < inputCount; i++)
{
float weight = playable.GetInputWeight(i);
if (weight <= 0) continue;
var scriptPlayable = (ScriptPlayable<RendererBehaviour>)playable.GetInput(i);
var behaviour = scriptPlayable.GetBehaviour();
weight *= behaviour.weightMultiplier;
totalWeight += weight;
if (mixedBehaviour == null)
{
mixedBehaviour = new RendererBehaviour();
mixedBehaviour.propertyName = behaviour.propertyName;
mixedBehaviour.propertyType = behaviour.propertyType;
mixedBehaviour.textureTarget = behaviour.textureTarget;
mixedBehaviour.vector = behaviour.vector;
mixedBehaviour.texture = behaviour.texture;
}
else
{
mixedBehaviour.Lerp(mixedBehaviour, behaviour, weight / totalWeight);
}
}
if (mixedBehaviour != null)
{
// Apply to all materials
foreach (var mat in materialGroup.materials)
{
if (mat == null) continue;
ApplyBehaviourToMaterial(mixedBehaviour, mat);
}
}
}
private void ApplyBehaviourToMaterial(RendererBehaviour behaviour, Material target)
{
switch (behaviour.propertyType)
{
case UnityEngine.Rendering.ShaderPropertyType.Color:
target.SetColor(behaviour.propertyName, behaviour.vector);
break;
case UnityEngine.Rendering.ShaderPropertyType.Vector:
target.SetVector(behaviour.propertyName, behaviour.vector);
break;
case UnityEngine.Rendering.ShaderPropertyType.Float:
case UnityEngine.Rendering.ShaderPropertyType.Range:
target.SetFloat(behaviour.propertyName, behaviour.vector.x);
break;
case UnityEngine.Rendering.ShaderPropertyType.Texture:
if (behaviour.textureTarget == RendererBehaviour.TextureTarget.TilingOffset)
{
target.SetTextureScale(behaviour.propertyName, new Vector2(behaviour.vector.x, behaviour.vector.y));
target.SetTextureOffset(behaviour.propertyName, new Vector2(behaviour.vector.z, behaviour.vector.w));
}
else
{
target.SetTexture(behaviour.propertyName, behaviour.texture);
}
break;
}
}
}
}