You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
15 KiB
294 lines
15 KiB
using Unity.Collections;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
using UnityEngine.Assertions;
|
|
using UnityEngine.Jobs;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
partial class DecalSystem
|
|
{
|
|
public void StartDecalUpdateJobs()
|
|
{
|
|
var decalSetsEnum = m_DecalSets.GetEnumerator();
|
|
while (decalSetsEnum.MoveNext())
|
|
{
|
|
DecalSet decalSet = decalSetsEnum.Current.Value;
|
|
if (decalSet.Count == 0)
|
|
continue;
|
|
|
|
decalSet.updateJobHandle.Complete();
|
|
decalSet.StartUpdateJob();
|
|
}
|
|
}
|
|
|
|
partial class DecalSet
|
|
{
|
|
internal JobHandle updateJobHandle { get { return m_UpdateJobHandle; } }
|
|
private JobHandle m_UpdateJobHandle;
|
|
|
|
private TransformAccessArray m_CachedTransforms = new TransformAccessArray(kDecalBlockSize);
|
|
private NativeArray<float3> m_Positions = new NativeArray<float3>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<quaternion> m_Rotations = new NativeArray<quaternion>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<float3> m_Scales = new NativeArray<float3>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<float3> m_Sizes = new NativeArray<float3>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<float3> m_Offsets = new NativeArray<float3>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<quaternion> m_ResolvedRotations = new NativeArray<quaternion>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<float3> m_ResolvedScales = new NativeArray<float3>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<float4x4> m_ResolvedSizeOffsets = new NativeArray<float4x4>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<DecalScaleMode> m_ScaleModes = new NativeArray<DecalScaleMode>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<float4x4> m_NormalToWorlds = new NativeArray<float4x4>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<float4x4> m_DecalToWorlds = new NativeArray<float4x4>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<BoundingSphere> m_BoundingSpheres = new NativeArray<BoundingSphere>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
private NativeArray<bool> m_Dirty = new NativeArray<bool>(kDecalBlockSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
|
|
|
|
private BoundingSphere[] m_CachedBoundingSpheres = new BoundingSphere[kDecalBlockSize];
|
|
|
|
public void ResolveUpdateJob()
|
|
{
|
|
m_UpdateJobHandle.Complete();
|
|
Assert.IsTrue(m_CachedBoundingSpheres.Length == m_BoundingSpheres.Length);
|
|
m_BoundingSpheres.CopyTo(m_CachedBoundingSpheres);
|
|
}
|
|
|
|
private void GrowJobArrays(int growByAmount)
|
|
{
|
|
int newCapacity = m_DecalsCount + growByAmount;
|
|
|
|
m_CachedTransforms.capacity = newCapacity;
|
|
|
|
m_Positions.ResizeArray(newCapacity);
|
|
m_Rotations.ResizeArray(newCapacity);
|
|
m_Scales.ResizeArray(newCapacity);
|
|
m_Sizes.ResizeArray(newCapacity);
|
|
m_Offsets.ResizeArray(newCapacity);
|
|
m_ResolvedRotations.ResizeArray(newCapacity);
|
|
m_ResolvedScales.ResizeArray(newCapacity);
|
|
m_ResolvedSizeOffsets.ResizeArray(newCapacity);
|
|
m_ScaleModes.ResizeArray(newCapacity);
|
|
m_NormalToWorlds.ResizeArray(newCapacity);
|
|
m_DecalToWorlds.ResizeArray(newCapacity);
|
|
m_BoundingSpheres.ResizeArray(newCapacity);
|
|
m_Dirty.ResizeArray(newCapacity);
|
|
ArrayExtensions.ResizeArray(ref m_CachedBoundingSpheres, newCapacity);
|
|
}
|
|
|
|
private void UpdateJobArrays(int index, DecalProjector decalProjector)
|
|
{
|
|
Assert.IsTrue(index <= m_DecalsCount, "Inconsistent indices found on m_CachedTransforms for decals");
|
|
if (index == m_CachedTransforms.length)
|
|
{
|
|
m_CachedTransforms.Add(decalProjector.transform);
|
|
}
|
|
else
|
|
{
|
|
m_CachedTransforms[index] = decalProjector.transform;
|
|
}
|
|
|
|
m_Positions[index] = decalProjector.transform.position;
|
|
m_Rotations[index] = decalProjector.transform.rotation;
|
|
m_Scales[index] = decalProjector.transform.lossyScale;
|
|
m_Sizes[index] = decalProjector.size;
|
|
m_Offsets[index] = decalProjector.pivot;
|
|
m_ScaleModes[index] = decalProjector.scaleMode;
|
|
m_Dirty[index] = true;
|
|
}
|
|
|
|
private void RemoveFromJobArrays(int removeAtIndex)
|
|
{
|
|
m_CachedTransforms.RemoveAtSwapBack(removeAtIndex);
|
|
m_Positions[removeAtIndex] = m_Positions[m_DecalsCount - 1];
|
|
m_Rotations[removeAtIndex] = m_Rotations[m_DecalsCount - 1];
|
|
m_Scales[removeAtIndex] = m_Scales[m_DecalsCount - 1];
|
|
m_Sizes[removeAtIndex] = m_Sizes[m_DecalsCount - 1];
|
|
m_Offsets[removeAtIndex] = m_Offsets[m_DecalsCount - 1];
|
|
m_ResolvedRotations[removeAtIndex] = m_ResolvedRotations[m_DecalsCount - 1];
|
|
m_ResolvedScales[removeAtIndex] = m_ResolvedScales[m_DecalsCount - 1];
|
|
m_ResolvedSizeOffsets[removeAtIndex] = m_ResolvedSizeOffsets[m_DecalsCount - 1];
|
|
m_ScaleModes[removeAtIndex] = m_ScaleModes[m_DecalsCount - 1];
|
|
m_NormalToWorlds[removeAtIndex] = m_NormalToWorlds[m_DecalsCount - 1];
|
|
m_DecalToWorlds[removeAtIndex] = m_DecalToWorlds[m_DecalsCount - 1];
|
|
m_BoundingSpheres[removeAtIndex] = m_BoundingSpheres[m_DecalsCount - 1];
|
|
m_Dirty[removeAtIndex] = m_Dirty[m_DecalsCount - 1];
|
|
m_CachedBoundingSpheres[removeAtIndex] = m_CachedBoundingSpheres[m_DecalsCount - 1];
|
|
}
|
|
|
|
private void DisposeJobArrays()
|
|
{
|
|
m_CachedTransforms.Dispose();
|
|
m_Positions.Dispose();
|
|
m_Rotations.Dispose();
|
|
m_Scales.Dispose();
|
|
m_Sizes.Dispose();
|
|
m_Offsets.Dispose();
|
|
m_ResolvedRotations.Dispose();
|
|
m_ResolvedScales.Dispose();
|
|
m_ResolvedSizeOffsets.Dispose();
|
|
m_ScaleModes.Dispose();
|
|
m_NormalToWorlds.Dispose();
|
|
m_DecalToWorlds.Dispose();
|
|
m_BoundingSpheres.Dispose();
|
|
m_Dirty.Dispose();
|
|
m_CachedBoundingSpheres = null;
|
|
}
|
|
|
|
internal void StartUpdateJob()
|
|
{
|
|
m_UpdateJobHandle.Complete();
|
|
var updateJob = new UpdateJob()
|
|
{
|
|
positions = m_Positions,
|
|
rawRotations = m_Rotations,
|
|
rawScales = m_Scales,
|
|
resolvedScales = m_ResolvedScales,
|
|
resolvedRotations = m_ResolvedRotations,
|
|
resolvedSizesOffsets = m_ResolvedSizeOffsets,
|
|
dirty = m_Dirty,
|
|
rawSizes = m_Sizes,
|
|
rawOffsets = m_Offsets,
|
|
scaleModes = m_ScaleModes,
|
|
normalToWorlds = m_NormalToWorlds,
|
|
decalToWorlds = m_DecalToWorlds,
|
|
boundingSpheres = m_BoundingSpheres,
|
|
minDistance = System.Single.Epsilon
|
|
};
|
|
m_UpdateJobHandle = updateJob.Schedule(m_CachedTransforms);
|
|
}
|
|
}
|
|
|
|
#if ENABLE_BURST_1_5_0_OR_NEWER
|
|
[Unity.Burst.BurstCompile]
|
|
#endif
|
|
internal struct UpdateJob : IJobParallelForTransform
|
|
{
|
|
private static readonly quaternion k_MinusYtoZRotation = quaternion.EulerXYZ(-math.PI / 2.0f, 0, 0);
|
|
private static readonly quaternion k_YtoZRotation = quaternion.EulerXYZ(math.PI / 2.0f, 0, 0);
|
|
private static readonly float3 sFloat3One = new float3(1, 1, 1);
|
|
public float minDistance;
|
|
|
|
public NativeArray<float3> positions;
|
|
public NativeArray<quaternion> rawRotations;
|
|
public NativeArray<float3> rawScales;
|
|
public NativeArray<float3> resolvedScales;
|
|
public NativeArray<quaternion> resolvedRotations;
|
|
public NativeArray<float4x4> resolvedSizesOffsets;
|
|
public NativeArray<bool> dirty;
|
|
|
|
[ReadOnly] public NativeArray<float3> rawSizes;
|
|
[ReadOnly] public NativeArray<float3> rawOffsets;
|
|
[ReadOnly] public NativeArray<DecalScaleMode> scaleModes;
|
|
[WriteOnly] public NativeArray<float4x4> normalToWorlds;
|
|
[WriteOnly] public NativeArray<float4x4> decalToWorlds;
|
|
[WriteOnly] public NativeArray<BoundingSphere> boundingSpheres;
|
|
|
|
private float DistanceBetweenQuaternions(quaternion a, quaternion b)
|
|
{
|
|
return math.distancesq(a.value, b.value);
|
|
}
|
|
|
|
private float3 effectiveScale(int index, in TransformAccess transform)
|
|
{
|
|
return scaleModes[index] == DecalScaleMode.InheritFromHierarchy ? transform.localToWorldMatrix.lossyScale : sFloat3One;
|
|
}
|
|
|
|
private float3 resolveDecalSize(int index, float3 scale, in TransformAccess transform)
|
|
{
|
|
// If Z-scale is negative the forward direction for rendering will be fixed by rotation,
|
|
// so we need to flip the scale of the affected axes back.
|
|
// The final sign of Z will depend on the other two axes, so we actually need to fix only Y here.
|
|
if (scale.z < 0f)
|
|
scale.y *= -1f;
|
|
|
|
// Flipped projector (with 1 or 3 negative components of scale) would be invisible.
|
|
// In this case we additionally flip Z.
|
|
bool flipped = scale.x < 0f ^ scale.y < 0f ^ scale.z < 0f;
|
|
if (flipped)
|
|
scale.z *= -1f;
|
|
|
|
float3 decalSize = rawSizes[index];
|
|
return new float3(decalSize.x * scale.x, decalSize.z * scale.z, decalSize.y * scale.y);
|
|
}
|
|
|
|
private float3 resolveDecalOffset(int index, float3 scale, in TransformAccess transform)
|
|
{
|
|
// If Z-scale is negative the forward direction for rendering will be fixed by rotation,
|
|
// so we need to flip the scale of the affected axes back.
|
|
if (scale.z < 0f)
|
|
{
|
|
scale.y *= -1f;
|
|
scale.z *= -1f;
|
|
}
|
|
|
|
float3 decalOffset = rawOffsets[index];
|
|
return new float3(decalOffset.x * scale.x, -decalOffset.z * scale.z, decalOffset.y * scale.y);
|
|
}
|
|
|
|
private quaternion resolveRotation(int index, in float3 scale, in TransformAccess transform)
|
|
{
|
|
return transform.rotation * (scale.z >= 0f ? k_MinusYtoZRotation : k_YtoZRotation);
|
|
}
|
|
|
|
public void Execute(int index, TransformAccess transform)
|
|
{
|
|
bool isDirty = dirty[index];
|
|
// Check if transform changed
|
|
bool positionChanged = math.distancesq(transform.position, positions[index]) > minDistance;
|
|
if (positionChanged)
|
|
positions[index] = transform.position;
|
|
|
|
bool scaleChanged = math.distancesq(transform.localToWorldMatrix.lossyScale, rawScales[index]) > minDistance;
|
|
if (scaleChanged)
|
|
rawScales[index] = transform.localToWorldMatrix.lossyScale;
|
|
|
|
if (scaleChanged || isDirty)
|
|
resolvedScales[index] = effectiveScale(index, transform);
|
|
|
|
bool rotationChanged = DistanceBetweenQuaternions(transform.rotation, rawRotations[index]) > minDistance;
|
|
if (rotationChanged)
|
|
rawRotations[index] = transform.rotation;
|
|
|
|
if (rotationChanged || isDirty)
|
|
resolvedRotations[index] = resolveRotation(index, resolvedScales[index], transform);
|
|
|
|
// Early out if transform did not changed
|
|
if (!positionChanged && !rotationChanged && !scaleChanged && !isDirty)
|
|
return;
|
|
|
|
if (isDirty || rotationChanged || scaleChanged)
|
|
resolvedSizesOffsets[index] = math.mul(float4x4.Translate(resolveDecalOffset(index, resolvedScales[index], transform)), float4x4.Scale(resolveDecalSize(index, resolvedScales[index], transform)));
|
|
|
|
float4x4 localToWorld = float4x4.TRS(transform.position, resolvedRotations[index], sFloat3One);
|
|
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 = resolvedSizesOffsets[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;
|
|
}
|
|
}
|
|
}
|
|
}
|