160 lines
4.5 KiB
C#
160 lines
4.5 KiB
C#
using UnityEngine;
|
|
|
|
public class Fractal : MonoBehaviour
|
|
{
|
|
private struct FractalPart
|
|
{
|
|
public Vector3 direction, worldPosition;
|
|
public Quaternion rotation, worldRotation;
|
|
public float spinAngle;
|
|
}
|
|
|
|
FractalPart[][] parts;
|
|
|
|
Matrix4x4[][] matrices;
|
|
|
|
[SerializeField, Range(1, 8)]
|
|
int depth = 4;
|
|
|
|
[SerializeField]
|
|
Mesh mesh = default;
|
|
|
|
[SerializeField]
|
|
Material material = default;
|
|
|
|
static Vector3[] directions =
|
|
{
|
|
Vector3.up,
|
|
Vector3.right,
|
|
Vector3.left,
|
|
Vector3.forward,
|
|
Vector3.back
|
|
};
|
|
|
|
static Quaternion[] rotations =
|
|
{
|
|
Quaternion.identity,
|
|
Quaternion.Euler(0f, 0f, -90f),
|
|
Quaternion.Euler(0f, 0f, 90f),
|
|
Quaternion.Euler(90f, 0f, 0f),
|
|
Quaternion.Euler(-90f, 0f, 0f)
|
|
};
|
|
|
|
private FractalPart CreatePart(int childIndex)
|
|
{
|
|
return new FractalPart()
|
|
{
|
|
direction = directions[childIndex],
|
|
rotation = rotations[childIndex]
|
|
};
|
|
}
|
|
|
|
ComputeBuffer[] matricesBuffers;
|
|
|
|
static readonly int matricesId = Shader.PropertyToID("_Matrices");
|
|
|
|
static MaterialPropertyBlock propertyBlock;
|
|
|
|
private void OnEnable()
|
|
{
|
|
parts = new FractalPart[depth][];
|
|
matrices = new Matrix4x4[depth][];
|
|
matricesBuffers = new ComputeBuffer[depth];
|
|
int stride = 16 * 4;
|
|
for (int i = 0, length = 1; i < parts.Length; i++, length *= 5)
|
|
{
|
|
parts[i] = new FractalPart[length];
|
|
matrices[i] = new Matrix4x4[length];
|
|
matricesBuffers[i] = new ComputeBuffer(length, stride);
|
|
}
|
|
|
|
parts[0][0] = CreatePart(0);
|
|
for (int li = 1; li < parts.Length; li++)
|
|
{
|
|
FractalPart[] levelParts = parts[li];
|
|
for (int fpi = 0; fpi < levelParts.Length; fpi += 5)
|
|
{
|
|
for (int ci = 0; ci < 5; ci++)
|
|
{
|
|
levelParts[fpi + ci] = CreatePart(ci);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (propertyBlock == null)
|
|
{
|
|
propertyBlock = new MaterialPropertyBlock();
|
|
}
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
for (int i = 0; i < matricesBuffers.Length; i++)
|
|
{
|
|
matricesBuffers[i].Release();
|
|
}
|
|
parts = null;
|
|
matrices = null;
|
|
matricesBuffers = null;
|
|
}
|
|
|
|
void OnValidate()
|
|
{
|
|
if (parts != null && enabled)
|
|
{
|
|
OnDisable();
|
|
OnEnable();
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
float spinAngleDelta = 22.5f * Time.deltaTime;
|
|
FractalPart rootPart = parts[0][0];
|
|
rootPart.spinAngle += spinAngleDelta;
|
|
rootPart.worldRotation =
|
|
transform.rotation *
|
|
(rootPart.rotation * Quaternion.Euler(0f, rootPart.spinAngle, 0f));
|
|
rootPart.worldPosition = transform.position;
|
|
parts[0][0] = rootPart;
|
|
float objectScale = transform.lossyScale.x;
|
|
matrices[0][0] = Matrix4x4.TRS(
|
|
rootPart.worldPosition, rootPart.worldRotation, objectScale * Vector3.one
|
|
);
|
|
|
|
float scale = objectScale;
|
|
for (int li = 1; li < parts.Length; li++)
|
|
{
|
|
scale *= 0.5f;
|
|
FractalPart[] parentParts = parts[li - 1];
|
|
FractalPart[] levelParts = parts[li];
|
|
Matrix4x4[] levelMatrices = matrices[li];
|
|
for (int fpi = 0; fpi < levelParts.Length; fpi++)
|
|
{
|
|
var parent = parentParts[fpi / 5];
|
|
var part = levelParts[fpi];
|
|
part.spinAngle += spinAngleDelta;
|
|
part.worldRotation =
|
|
parent.worldRotation *
|
|
(part.rotation * Quaternion.Euler(0f, part.spinAngle, 0f));
|
|
part.worldPosition =
|
|
parent.worldPosition +
|
|
parent.worldRotation * (1.5f * scale * part.direction);
|
|
levelParts[fpi] = part;
|
|
levelMatrices[fpi] = Matrix4x4.TRS(
|
|
part.worldPosition, part.worldRotation, scale * Vector3.one
|
|
);
|
|
}
|
|
}
|
|
|
|
var bounds = new Bounds(rootPart.worldPosition, 3f * objectScale * Vector3.one);
|
|
for (int i = 0; i < matricesBuffers.Length; i++)
|
|
{
|
|
ComputeBuffer buffer = matricesBuffers[i];
|
|
buffer.SetData(matrices[i]);
|
|
propertyBlock.SetBuffer(matricesId, buffer);
|
|
Graphics.DrawMeshInstancedProcedural(mesh, 0, material, bounds, buffer.count, propertyBlock);
|
|
}
|
|
}
|
|
}
|