272 lines
11 KiB
C#
272 lines
11 KiB
C#
|
using Unity.Collections;
|
||
|
using Unity.Mathematics;
|
||
|
using UnityEngine.Jobs;
|
||
|
|
||
|
namespace UnityEngine.Rendering.Universal
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Contains <see cref="DecalProjector"/> cached properties needed for rendering.
|
||
|
/// </summary>
|
||
|
internal class DecalCachedChunk : DecalChunk
|
||
|
{
|
||
|
public MaterialPropertyBlock propertyBlock;
|
||
|
public int passIndexDBuffer;
|
||
|
public int passIndexEmissive;
|
||
|
public int passIndexScreenSpace;
|
||
|
public int passIndexGBuffer;
|
||
|
public int drawOrder;
|
||
|
public bool isCreated;
|
||
|
|
||
|
public NativeArray<float4x4> decalToWorlds;
|
||
|
public NativeArray<float4x4> normalToWorlds;
|
||
|
public NativeArray<float4x4> sizeOffsets;
|
||
|
public NativeArray<float2> drawDistances;
|
||
|
public NativeArray<float2> angleFades;
|
||
|
public NativeArray<float4> uvScaleBias;
|
||
|
public NativeArray<int> layerMasks;
|
||
|
public NativeArray<ulong> sceneLayerMasks;
|
||
|
public NativeArray<float> fadeFactors;
|
||
|
public NativeArray<BoundingSphere> boundingSpheres;
|
||
|
public NativeArray<DecalScaleMode> scaleModes;
|
||
|
public NativeArray<float3> positions;
|
||
|
public NativeArray<quaternion> rotation;
|
||
|
public NativeArray<float3> scales;
|
||
|
public NativeArray<bool> dirty;
|
||
|
|
||
|
public BoundingSphere[] boundingSphereArray;
|
||
|
|
||
|
public override void RemoveAtSwapBack(int entityIndex)
|
||
|
{
|
||
|
RemoveAtSwapBack(ref decalToWorlds, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref normalToWorlds, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref sizeOffsets, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref drawDistances, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref angleFades, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref uvScaleBias, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref layerMasks, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref sceneLayerMasks, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref fadeFactors, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref boundingSphereArray, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref boundingSpheres, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref scaleModes, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref positions, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref rotation, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref scales, entityIndex, count);
|
||
|
RemoveAtSwapBack(ref dirty, entityIndex, count);
|
||
|
count--;
|
||
|
}
|
||
|
|
||
|
public override void SetCapacity(int newCapacity)
|
||
|
{
|
||
|
decalToWorlds.ResizeArray(newCapacity);
|
||
|
normalToWorlds.ResizeArray(newCapacity);
|
||
|
sizeOffsets.ResizeArray(newCapacity);
|
||
|
drawDistances.ResizeArray(newCapacity);
|
||
|
angleFades.ResizeArray(newCapacity);
|
||
|
uvScaleBias.ResizeArray(newCapacity);
|
||
|
layerMasks.ResizeArray(newCapacity);
|
||
|
sceneLayerMasks.ResizeArray(newCapacity);
|
||
|
fadeFactors.ResizeArray(newCapacity);
|
||
|
boundingSpheres.ResizeArray(newCapacity);
|
||
|
scaleModes.ResizeArray(newCapacity);
|
||
|
positions.ResizeArray(newCapacity);
|
||
|
rotation.ResizeArray(newCapacity);
|
||
|
scales.ResizeArray(newCapacity);
|
||
|
dirty.ResizeArray(newCapacity);
|
||
|
|
||
|
ArrayExtensions.ResizeArray(ref boundingSphereArray, newCapacity);
|
||
|
capacity = newCapacity;
|
||
|
}
|
||
|
|
||
|
public override void Dispose()
|
||
|
{
|
||
|
if (capacity == 0)
|
||
|
return;
|
||
|
|
||
|
decalToWorlds.Dispose();
|
||
|
normalToWorlds.Dispose();
|
||
|
sizeOffsets.Dispose();
|
||
|
drawDistances.Dispose();
|
||
|
angleFades.Dispose();
|
||
|
uvScaleBias.Dispose();
|
||
|
layerMasks.Dispose();
|
||
|
sceneLayerMasks.Dispose();
|
||
|
fadeFactors.Dispose();
|
||
|
boundingSpheres.Dispose();
|
||
|
scaleModes.Dispose();
|
||
|
positions.Dispose();
|
||
|
rotation.Dispose();
|
||
|
scales.Dispose();
|
||
|
dirty.Dispose();
|
||
|
count = 0;
|
||
|
capacity = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Caches <see cref="DecalProjector"/> properties into <see cref="DecalCachedChunk"/>.
|
||
|
/// Uses jobs with <see cref="IJobParallelForTransform"/>.
|
||
|
/// </summary>
|
||
|
internal class DecalUpdateCachedSystem
|
||
|
{
|
||
|
private DecalEntityManager m_EntityManager;
|
||
|
private ProfilingSampler m_Sampler;
|
||
|
private ProfilingSampler m_SamplerJob;
|
||
|
|
||
|
public DecalUpdateCachedSystem(DecalEntityManager entityManager)
|
||
|
{
|
||
|
m_EntityManager = entityManager;
|
||
|
m_Sampler = new ProfilingSampler("DecalUpdateCachedSystem.Execute");
|
||
|
m_SamplerJob = new ProfilingSampler("DecalUpdateCachedSystem.ExecuteJob");
|
||
|
}
|
||
|
|
||
|
public void Execute()
|
||
|
{
|
||
|
using (new ProfilingScope(null, m_Sampler))
|
||
|
{
|
||
|
for (int i = 0; i < m_EntityManager.chunkCount; ++i)
|
||
|
Execute(m_EntityManager.entityChunks[i], m_EntityManager.cachedChunks[i], m_EntityManager.entityChunks[i].count);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void Execute(DecalEntityChunk entityChunk, DecalCachedChunk cachedChunk, int count)
|
||
|
{
|
||
|
if (count == 0)
|
||
|
return;
|
||
|
|
||
|
cachedChunk.currentJobHandle.Complete();
|
||
|
|
||
|
// Make sure draw order is up to date
|
||
|
var material = entityChunk.material;
|
||
|
if (material.HasProperty("_DrawOrder"))
|
||
|
cachedChunk.drawOrder = material.GetInt("_DrawOrder");
|
||
|
|
||
|
// Shader can change any time in editor, so we have to update passes each time
|
||
|
#if !UNITY_EDITOR
|
||
|
if (!cachedChunk.isCreated)
|
||
|
#endif
|
||
|
{
|
||
|
int passIndexDBuffer = material.FindPass(DecalShaderPassNames.DBufferProjector);
|
||
|
cachedChunk.passIndexDBuffer = passIndexDBuffer;
|
||
|
|
||
|
int passIndexEmissive = material.FindPass(DecalShaderPassNames.DecalProjectorForwardEmissive);
|
||
|
cachedChunk.passIndexEmissive = passIndexEmissive;
|
||
|
|
||
|
int passIndexScreenSpace = material.FindPass(DecalShaderPassNames.DecalScreenSpaceProjector);
|
||
|
cachedChunk.passIndexScreenSpace = passIndexScreenSpace;
|
||
|
|
||
|
int passIndexGBuffer = material.FindPass(DecalShaderPassNames.DecalGBufferProjector);
|
||
|
cachedChunk.passIndexGBuffer = passIndexGBuffer;
|
||
|
|
||
|
cachedChunk.isCreated = true;
|
||
|
}
|
||
|
|
||
|
using (new ProfilingScope(null, m_SamplerJob))
|
||
|
{
|
||
|
UpdateTransformsJob updateTransformJob = new UpdateTransformsJob()
|
||
|
{
|
||
|
positions = cachedChunk.positions,
|
||
|
rotations = cachedChunk.rotation,
|
||
|
scales = cachedChunk.scales,
|
||
|
dirty = cachedChunk.dirty,
|
||
|
scaleModes = cachedChunk.scaleModes,
|
||
|
sizeOffsets = cachedChunk.sizeOffsets,
|
||
|
decalToWorlds = cachedChunk.decalToWorlds,
|
||
|
normalToWorlds = cachedChunk.normalToWorlds,
|
||
|
boundingSpheres = cachedChunk.boundingSpheres,
|
||
|
minDistance = System.Single.Epsilon,
|
||
|
};
|
||
|
|
||
|
var handle = updateTransformJob.Schedule(entityChunk.transformAccessArray);
|
||
|
cachedChunk.currentJobHandle = handle;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if ENABLE_BURST_1_0_0_OR_NEWER
|
||
|
[Unity.Burst.BurstCompile]
|
||
|
#endif
|
||
|
public unsafe struct UpdateTransformsJob : IJobParallelForTransform
|
||
|
{
|
||
|
private static readonly quaternion k_MinusYtoZRotation = quaternion.EulerXYZ(-math.PI / 2.0f, 0, 0);
|
||
|
|
||
|
public NativeArray<float3> positions;
|
||
|
public NativeArray<quaternion> rotations;
|
||
|
public NativeArray<float3> scales;
|
||
|
public NativeArray<bool> dirty;
|
||
|
|
||
|
[ReadOnly] public NativeArray<DecalScaleMode> scaleModes;
|
||
|
[ReadOnly] public NativeArray<float4x4> sizeOffsets;
|
||
|
[WriteOnly] public NativeArray<float4x4> decalToWorlds;
|
||
|
[WriteOnly] public NativeArray<float4x4> normalToWorlds;
|
||
|
[WriteOnly] public NativeArray<BoundingSphere> boundingSpheres;
|
||
|
|
||
|
public float minDistance;
|
||
|
|
||
|
private float DistanceBetweenQuaternions(quaternion a, quaternion b)
|
||
|
{
|
||
|
return math.distancesq(a.value, b.value);
|
||
|
}
|
||
|
|
||
|
public void Execute(int index, TransformAccess transform)
|
||
|
{
|
||
|
// Check if transform changed
|
||
|
bool positionChanged = math.distancesq(transform.position, positions[index]) > minDistance;
|
||
|
if (positionChanged)
|
||
|
positions[index] = transform.position;
|
||
|
bool rotationChanged = DistanceBetweenQuaternions(transform.rotation, rotations[index]) > minDistance;
|
||
|
if (rotationChanged)
|
||
|
rotations[index] = transform.rotation;
|
||
|
bool scaleChanged = math.distancesq(transform.localScale, scales[index]) > minDistance;
|
||
|
if (scaleChanged)
|
||
|
scales[index] = transform.localScale;
|
||
|
|
||
|
// Early out if transform did not changed
|
||
|
if (!positionChanged && !rotationChanged && !scaleChanged && !dirty[index])
|
||
|
return;
|
||
|
|
||
|
float4x4 localToWorld;
|
||
|
if (scaleModes[index] == DecalScaleMode.InheritFromHierarchy)
|
||
|
{
|
||
|
localToWorld = transform.localToWorldMatrix;
|
||
|
localToWorld = math.mul(localToWorld, new float4x4(k_MinusYtoZRotation, float3.zero));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
quaternion rotation = math.mul(transform.rotation, k_MinusYtoZRotation);
|
||
|
localToWorld = float4x4.TRS(positions[index], rotation, new float3(1, 1, 1));
|
||
|
}
|
||
|
|
||
|
float4x4 decalRotation = localToWorld;
|
||
|
// z/y axis swap for normal to decal space, Unity is column major
|
||
|
float4 temp = decalRotation.c1;
|
||
|
decalRotation.c1 = decalRotation.c2;
|
||
|
decalRotation.c2 = temp;
|
||
|
normalToWorlds[index] = decalRotation;
|
||
|
|
||
|
float4x4 sizeOffset = sizeOffsets[index];
|
||
|
float4x4 decalToWorld = math.mul(localToWorld, sizeOffset);
|
||
|
decalToWorlds[index] = decalToWorld;
|
||
|
boundingSpheres[index] = GetDecalProjectBoundingSphere(decalToWorld);
|
||
|
|
||
|
dirty[index] = false;
|
||
|
}
|
||
|
|
||
|
private BoundingSphere GetDecalProjectBoundingSphere(Matrix4x4 decalToWorld)
|
||
|
{
|
||
|
float4 min = new float4(-0.5f, -0.5f, -0.5f, 1.0f);
|
||
|
float4 max = new float4(0.5f, 0.5f, 0.5f, 1.0f);
|
||
|
min = math.mul(decalToWorld, min);
|
||
|
max = math.mul(decalToWorld, max);
|
||
|
|
||
|
float3 position = ((max + min) / 2f).xyz;
|
||
|
float radius = math.length(max - min) / 2f;
|
||
|
|
||
|
BoundingSphere res = new BoundingSphere();
|
||
|
res.position = position;
|
||
|
res.radius = radius;
|
||
|
return res;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|