2021-01-31 20:51:35 +08:00
|
|
|
|
using Unity.Burst;
|
|
|
|
|
using Unity.Collections;
|
|
|
|
|
using Unity.Jobs;
|
|
|
|
|
using Unity.Mathematics;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
using static Unity.Mathematics.math;
|
|
|
|
|
using quaternion = Unity.Mathematics.quaternion;
|
2021-01-31 15:48:04 +08:00
|
|
|
|
|
|
|
|
|
public class Fractal : MonoBehaviour
|
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
[BurstCompile(FloatPrecision.Standard, FloatMode.Fast, CompileSynchronously = true)]
|
|
|
|
|
private struct UpdateFractalLevelJob : IJobFor
|
|
|
|
|
{
|
|
|
|
|
public float spinAngleDelta;
|
|
|
|
|
public float scale;
|
|
|
|
|
|
|
|
|
|
[ReadOnly]
|
|
|
|
|
public NativeArray<FractalPart> parents;
|
|
|
|
|
|
|
|
|
|
public NativeArray<FractalPart> parts;
|
|
|
|
|
|
|
|
|
|
[WriteOnly]
|
|
|
|
|
public NativeArray<float3x4> matrices;
|
|
|
|
|
|
|
|
|
|
public void Execute(int i)
|
|
|
|
|
{
|
|
|
|
|
var parent = parents[i / 5];
|
|
|
|
|
var part = parts[i];
|
|
|
|
|
part.spinAngle += spinAngleDelta;
|
|
|
|
|
part.worldRotation = mul(parent.worldRotation,
|
|
|
|
|
mul(part.rotation, quaternion.RotateY(part.spinAngle))
|
|
|
|
|
);
|
|
|
|
|
part.worldPosition =
|
|
|
|
|
parent.worldPosition +
|
|
|
|
|
mul(parent.worldRotation, 1.5f * scale * part.direction);
|
|
|
|
|
parts[i] = part;
|
|
|
|
|
|
|
|
|
|
float3x3 r = float3x3(part.worldRotation) * scale;
|
|
|
|
|
matrices[i] = float3x4(r.c0, r.c1, r.c2, part.worldPosition);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-31 16:25:50 +08:00
|
|
|
|
private struct FractalPart
|
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
public float3 direction, worldPosition;
|
|
|
|
|
public quaternion rotation, worldRotation;
|
2021-01-31 17:55:48 +08:00
|
|
|
|
public float spinAngle;
|
2021-01-31 16:25:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-31 20:51:35 +08:00
|
|
|
|
NativeArray<FractalPart>[] parts;
|
2021-01-31 16:25:50 +08:00
|
|
|
|
|
2021-01-31 20:51:35 +08:00
|
|
|
|
NativeArray<float3x4>[] matrices;
|
2021-01-31 17:55:48 +08:00
|
|
|
|
|
2021-01-31 15:48:04 +08:00
|
|
|
|
[SerializeField, Range(1, 8)]
|
|
|
|
|
int depth = 4;
|
|
|
|
|
|
2021-01-31 16:25:50 +08:00
|
|
|
|
[SerializeField]
|
|
|
|
|
Mesh mesh = default;
|
|
|
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
|
Material material = default;
|
|
|
|
|
|
2021-01-31 20:51:35 +08:00
|
|
|
|
static float3[] directions =
|
2021-01-31 16:25:50 +08:00
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
up(), right(), left(), forward(), back()
|
2021-01-31 16:25:50 +08:00
|
|
|
|
};
|
|
|
|
|
|
2021-01-31 20:51:35 +08:00
|
|
|
|
static quaternion[] rotations =
|
2021-01-31 15:48:04 +08:00
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
quaternion.identity,
|
|
|
|
|
quaternion.RotateZ(-0.5f * PI), quaternion.RotateZ(0.5f * PI),
|
|
|
|
|
quaternion.RotateX(0.5f * PI), quaternion.RotateX(-0.5f * PI)
|
2021-01-31 16:25:50 +08:00
|
|
|
|
};
|
|
|
|
|
|
2021-01-31 17:55:48 +08:00
|
|
|
|
private FractalPart CreatePart(int childIndex)
|
2021-01-31 16:25:50 +08:00
|
|
|
|
{
|
|
|
|
|
return new FractalPart()
|
|
|
|
|
{
|
|
|
|
|
direction = directions[childIndex],
|
2021-01-31 17:55:48 +08:00
|
|
|
|
rotation = rotations[childIndex]
|
2021-01-31 16:25:50 +08:00
|
|
|
|
};
|
|
|
|
|
}
|
2021-01-31 15:48:04 +08:00
|
|
|
|
|
2021-01-31 17:55:48 +08:00
|
|
|
|
ComputeBuffer[] matricesBuffers;
|
|
|
|
|
|
|
|
|
|
static readonly int matricesId = Shader.PropertyToID("_Matrices");
|
|
|
|
|
|
|
|
|
|
static MaterialPropertyBlock propertyBlock;
|
|
|
|
|
|
|
|
|
|
private void OnEnable()
|
2021-01-31 16:25:50 +08:00
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
parts = new NativeArray<FractalPart>[depth];
|
|
|
|
|
matrices = new NativeArray<float3x4>[depth];
|
2021-01-31 17:55:48 +08:00
|
|
|
|
matricesBuffers = new ComputeBuffer[depth];
|
2021-01-31 20:51:35 +08:00
|
|
|
|
int stride = 12 * 4;
|
2021-01-31 16:25:50 +08:00
|
|
|
|
for (int i = 0, length = 1; i < parts.Length; i++, length *= 5)
|
2021-01-31 15:48:04 +08:00
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
parts[i] = new NativeArray<FractalPart>(length, Allocator.Persistent);
|
|
|
|
|
matrices[i] = new NativeArray<float3x4>(length, Allocator.Persistent);
|
2021-01-31 17:55:48 +08:00
|
|
|
|
matricesBuffers[i] = new ComputeBuffer(length, stride);
|
2021-01-31 15:48:04 +08:00
|
|
|
|
}
|
2021-01-31 17:55:48 +08:00
|
|
|
|
|
|
|
|
|
parts[0][0] = CreatePart(0);
|
2021-01-31 16:25:50 +08:00
|
|
|
|
for (int li = 1; li < parts.Length; li++)
|
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
NativeArray<FractalPart> levelParts = parts[li];
|
2021-01-31 16:25:50 +08:00
|
|
|
|
for (int fpi = 0; fpi < levelParts.Length; fpi += 5)
|
|
|
|
|
{
|
|
|
|
|
for (int ci = 0; ci < 5; ci++)
|
|
|
|
|
{
|
2021-01-31 17:55:48 +08:00
|
|
|
|
levelParts[fpi + ci] = CreatePart(ci);
|
2021-01-31 16:25:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-31 17:55:48 +08:00
|
|
|
|
|
|
|
|
|
if (propertyBlock == null)
|
|
|
|
|
{
|
|
|
|
|
propertyBlock = new MaterialPropertyBlock();
|
|
|
|
|
}
|
2021-01-31 15:48:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-31 17:55:48 +08:00
|
|
|
|
private void OnDisable()
|
2021-01-31 15:48:04 +08:00
|
|
|
|
{
|
2021-01-31 17:55:48 +08:00
|
|
|
|
for (int i = 0; i < matricesBuffers.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
matricesBuffers[i].Release();
|
2021-01-31 20:51:35 +08:00
|
|
|
|
parts[i].Dispose();
|
|
|
|
|
matrices[i].Dispose();
|
2021-01-31 17:55:48 +08:00
|
|
|
|
}
|
|
|
|
|
parts = null;
|
|
|
|
|
matrices = null;
|
|
|
|
|
matricesBuffers = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OnValidate()
|
|
|
|
|
{
|
|
|
|
|
if (parts != null && enabled)
|
|
|
|
|
{
|
|
|
|
|
OnDisable();
|
|
|
|
|
OnEnable();
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-31 15:48:04 +08:00
|
|
|
|
|
2021-01-31 17:55:48 +08:00
|
|
|
|
private void Update()
|
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
float spinAngleDelta = 0.125f * PI * Time.deltaTime;
|
2021-01-31 16:25:50 +08:00
|
|
|
|
FractalPart rootPart = parts[0][0];
|
2021-01-31 17:55:48 +08:00
|
|
|
|
rootPart.spinAngle += spinAngleDelta;
|
2021-01-31 20:51:35 +08:00
|
|
|
|
rootPart.worldRotation = mul(transform.rotation,
|
|
|
|
|
mul(rootPart.rotation, quaternion.RotateY(rootPart.spinAngle))
|
|
|
|
|
);
|
2021-01-31 17:55:48 +08:00
|
|
|
|
rootPart.worldPosition = transform.position;
|
2021-01-31 16:25:50 +08:00
|
|
|
|
parts[0][0] = rootPart;
|
2021-01-31 17:55:48 +08:00
|
|
|
|
float objectScale = transform.lossyScale.x;
|
2021-01-31 20:51:35 +08:00
|
|
|
|
float3x3 r = float3x3(rootPart.worldRotation) * objectScale;
|
|
|
|
|
matrices[0][0] = float3x4(r.c0, r.c1, r.c2, rootPart.worldPosition);
|
2021-01-31 16:25:50 +08:00
|
|
|
|
|
2021-01-31 17:55:48 +08:00
|
|
|
|
float scale = objectScale;
|
2021-01-31 20:51:35 +08:00
|
|
|
|
JobHandle jobHandle = default;
|
2021-01-31 16:25:50 +08:00
|
|
|
|
for (int li = 1; li < parts.Length; li++)
|
|
|
|
|
{
|
2021-01-31 17:55:48 +08:00
|
|
|
|
scale *= 0.5f;
|
2021-01-31 20:51:35 +08:00
|
|
|
|
jobHandle = new UpdateFractalLevelJob
|
2021-01-31 16:25:50 +08:00
|
|
|
|
{
|
2021-01-31 20:51:35 +08:00
|
|
|
|
spinAngleDelta = spinAngleDelta,
|
|
|
|
|
scale = scale,
|
|
|
|
|
parents = parts[li - 1],
|
|
|
|
|
parts = parts[li],
|
|
|
|
|
matrices = matrices[li]
|
|
|
|
|
}.ScheduleParallel(parts[li].Length, 5, jobHandle);
|
2021-01-31 16:25:50 +08:00
|
|
|
|
}
|
2021-01-31 20:51:35 +08:00
|
|
|
|
jobHandle.Complete();
|
2021-01-31 17:55:48 +08:00
|
|
|
|
|
2021-01-31 20:51:35 +08:00
|
|
|
|
var bounds = new Bounds(rootPart.worldPosition, float3(3f * objectScale));
|
2021-01-31 17:55:48 +08:00
|
|
|
|
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);
|
|
|
|
|
}
|
2021-01-31 15:48:04 +08:00
|
|
|
|
}
|
|
|
|
|
}
|