Files
2025-08-02 20:11:41 +08:00

211 lines
6.2 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace NovaSamples.DummyScripts
{
[ExecuteAlways]
public class DummyCubeAnimator : MonoBehaviour
{
private static readonly Color Magenta = new Color(1, 0.2f, 1);
private static readonly Color Orange = new Color(1, 0.6f, 0);
private static readonly Color Yellow = new Color(0.87f, 1,0.2f);
private static readonly Color Green = new Color(0, 1, 0.33f);
private static readonly Color Cyan = new Color(0, 1, 1);
private static readonly Color Blue = new Color(0, 0.33f, 1);
private static readonly float XRotation = Mathf.Atan(1 / Mathf.Sqrt(2)) * Mathf.Rad2Deg;
private static readonly float ZRotation = 45;
[Min(0)]
public int Rows = 5;
[Min(0)]
public int Columns = 5;
[Min(1)]
public float DistanceFromCamera = 15;
[Tooltip("Degrees per second")]
public float RotateBy = 45;
public List<Color> CubeColors = new List<Color>()
{
Magenta,
Orange,
Yellow,
Green,
Cyan,
Blue
};
[NonSerialized]
private List<Material> materials = new List<Material>();
[NonSerialized]
private List<List<Matrix4x4>> matrices = new List<List<Matrix4x4>>();
#pragma warning disable 0414 // The private field is assigned but its value is never used - logged when building
[NonSerialized]
private bool primitivesInitialized = false;
[SerializeField, HideInInspector]
private Shader shader = null;
#pragma warning restore 0414
[SerializeField, HideInInspector]
private Material material = null;
private Material Material
{
get
{
EnsurePrimitives();
return material;
}
}
[SerializeField, HideInInspector]
private Mesh mesh = null;
private Mesh Mesh
{
get
{
EnsurePrimitives();
return mesh;
}
}
private void Update()
{
EnsureMaterials();
float yRotation = Application.IsPlaying(this) ? Time.time * RotateBy : 0;
Quaternion rotation = Quaternion.Euler(XRotation, yRotation, ZRotation);
Camera camera = Camera.main;
if (camera == null)
{
return;
}
float rowHeight = 1f / Rows;
float colWidth = 1f / Columns;
int numDrawCalls = 0;
for (int row = 0; row < Rows; ++row)
{
float viewportY = (row + 0.5f) * rowHeight;
for (int col = 0; col < Columns; ++col)
{
int cubeIndex = row * Columns + col;
int materialIndex = cubeIndex % materials.Count;
numDrawCalls = Mathf.Max(numDrawCalls, materialIndex);
float viewportX = (col + 0.5f) * colWidth;
Vector3 position = camera.ViewportToWorldPoint(new Vector3(viewportX, viewportY, DistanceFromCamera));
matrices[materialIndex].Add(Matrix4x4.TRS(position, rotation, Vector3.one));
}
}
for (int i = 0; i <= numDrawCalls; ++i)
{
Graphics.DrawMeshInstanced(Mesh, 0, materials[i], matrices[i], null, UnityEngine.Rendering.ShadowCastingMode.Off, receiveShadows: false);
}
}
private void OnDisable()
{
for (int i = 0; i < materials.Count; ++i)
{
Material.DestroyImmediate(materials[i]);
}
materials.Clear();
}
private void EnsureMaterials()
{
int materialCount = Mathf.Max(CubeColors.Count, 1);
while (materials.Count > materialCount)
{
Material.DestroyImmediate(materials[materials.Count - 1]);
materials.RemoveAt(materials.Count - 1);
matrices.RemoveAt(matrices.Count - 1);
}
while (materials.Count < materialCount)
{
materials.Add(new Material(Material) { enableInstancing = true });
matrices.Add(new List<Matrix4x4>());
}
if (CubeColors.Count == 0)
{
materials[0].color = Color.white;
matrices[0].Clear();
}
else
{
for (int i = 0; i < CubeColors.Count; ++i)
{
materials[i].color = CubeColors[i];
matrices[i].Clear();
}
}
}
private void EnsurePrimitives()
{
#if !UNITY_EDITOR
return;
#else
if (primitivesInitialized && mesh != null && material != null)
{
return;
}
bool saveScene = gameObject.scene == SceneManager.GetActiveScene() && !gameObject.scene.isDirty;
primitivesInitialized = true;
GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
Mesh primitiveMesh = go.GetComponent<MeshFilter>().sharedMesh;
Material primitiveMaterial = new Material(go.GetComponent<MeshRenderer>().sharedMaterial);
GameObject.DestroyImmediate(go);
if (primitiveMesh == mesh && shader == primitiveMaterial.shader)
{
return;
}
UnityEditor.Undo.RecordObject(this, "Init Cubes");
mesh = primitiveMesh;
shader = primitiveMaterial.shader;
material = new Material(shader);
material.enableInstancing = true;
if (saveScene)
{
UnityEditor.EditorApplication.delayCall += () =>
{
if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
{
return;
}
UnityEditor.SceneManagement.EditorSceneManager.SaveScene(gameObject.scene);
};
}
#endif
}
}
}