forked from Unity-Technologies/EntityComponentSystemSamples
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathColliderBakeTransformSystem.cs
More file actions
235 lines (200 loc) · 10.2 KB
/
Copy pathColliderBakeTransformSystem.cs
File metadata and controls
235 lines (200 loc) · 10.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
using Unity.Burst;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Extensions;
using Unity.Physics.Systems;
using Unity.Transforms;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
[UpdateInGroup(typeof(AfterPhysicsSystemGroup))]
partial struct ColliderBakeTransformSystem : ISystem
{
private NativeQueue<BlobAssetReference<Unity.Physics.Collider>> m_ColliderBlobsToDisposeNow;
[BurstCompile]
public partial struct BakeTransformJob : IJobEntity
{
public float TimeStep;
public NativeQueue<BlobAssetReference<Unity.Physics.Collider>>.ParallelWriter ColliderBlobsToDisposeNow;
void Execute(ref ColliderBakeTransform transformData, ref SaveColliderBlobForDisposal saveCollider, ref PhysicsCollider collider, ref PhysicsMass mass,
ref PostTransformMatrix postTransformMatrix, Entity entity, [ChunkIndexInQuery] int chunkIndex)
{
// Collider should have been made unique before this job runs
if (!collider.IsUnique)
return;
// Skip if the transformation has already been applied to the collider previously and no animation is requested
if (transformData.FrameCount > 0 && transformData.AnimationDuration <= 0)
return;
// If both drift prevention and the collider baking animation is enabled,
// store some data for later collider reset if the drift threshold has been reached.
if (transformData.DriftPrevention && transformData.AnimationDuration > 0)
{
// store original geometry data for later reset if geometry drifts when the animation is reset, and
// replace baked collider with guaranteed unique clone right away.
if (transformData.FrameCount == 0 && !transformData.OriginalCollider.IsCreated)
{
transformData.OriginalCollider = collider.Value;
collider.Value = transformData.OriginalCollider.Value.Clone();
saveCollider.Collider = collider.Value;
transformData.OriginalPostTransformMatrix = postTransformMatrix;
}
}
var animationFactor = 1f;
if (transformData.AnimationDuration > 0)
{
var animationFrames = math.ceil(transformData.AnimationDuration / TimeStep);
if (transformData.FrameCount >= animationFrames)
{
transformData.FrameCount = 0;
if (transformData.DriftPrevention)
{
var lengthSq = math.lengthsq(postTransformMatrix.Value.c0)
+ math.lengthsq(postTransformMatrix.Value.c1)
+ math.lengthsq(postTransformMatrix.Value.c2)
+ math.lengthsq(postTransformMatrix.Value.c3);
var lengthSqOrig = math.lengthsq(transformData.OriginalPostTransformMatrix.Value.c0)
+ math.lengthsq(transformData.OriginalPostTransformMatrix.Value.c1)
+ math.lengthsq(transformData.OriginalPostTransformMatrix.Value.c2)
+ math.lengthsq(transformData.OriginalPostTransformMatrix.Value.c3);
if (math.abs(lengthSq - lengthSqOrig) > transformData.DriftErrorThreshold)
{
var driftedCollider = collider.Value;
//clone and store the blob in save for disposal, in order to dispose at the end of the frame
collider.Value = transformData.OriginalCollider.Value.Clone();
saveCollider.Collider = collider.Value;
// We can't dispose the blob in Collider.value yet because it may be needed by the
// DebugDraw system. Instead, add it to a queue to be disposed of next frame.
ColliderBlobsToDisposeNow.Enqueue(driftedCollider);
postTransformMatrix = transformData.OriginalPostTransformMatrix;
}
}
}
// Normalize animation factor considering the sum of the animation function weights over the animation duration.
// Here, we are using an identity of the discrete sum of sines.
var N = math.ceil(animationFrames / 2);
var d = math.PI / N;
var s = math.sin(0.5f * d);
var o_t = 1f;
if (math.abs(s) > math.EPSILON)
{
var R = math.sin(N * 0.5f * d) / s;
o_t = R * math.sin((N - 1) * 0.5f * d);
}
float o = math.sin(2f * math.PI * (transformData.FrameCount / animationFrames));
animationFactor = o / math.abs(o_t);
}
++transformData.FrameCount;
if (math.abs(animationFactor) < math.EPSILON)
{
return;
}
var deltaScale = transformData.Scale - 1f;
// Compute the affine transformation from the translation, rotation, scale and shear provided in the baking data.
var bakeTransform = new AffineTransform(
animationFactor * transformData.Translation,
math.slerp(math.conjugate(transformData.Rotation), transformData.Rotation,
(animationFactor + 1f) / 2f),
1 + animationFactor * deltaScale);
float3x3 shearXZ, shearYZ;
var shearXY = shearXZ = shearYZ = float3x3.identity;
shearXY[2][0] = animationFactor * transformData.ShearXY.x;
shearXY[2][1] = animationFactor * transformData.ShearXY.y;
shearXZ[1][0] = animationFactor * transformData.ShearXZ.x;
shearXZ[1][2] = animationFactor * transformData.ShearXZ.y;
shearYZ[0][1] = animationFactor * transformData.ShearYZ.x;
shearYZ[0][2] = animationFactor * transformData.ShearYZ.y;
bakeTransform = math.mul(bakeTransform, math.mul(shearXY, math.mul(shearXZ, shearYZ)));
// Apply the affine transformation to the collider geometry.
collider.Value.Value.BakeTransform(bakeTransform);
// Update the rigid body's mass properties for if available and dynamic by copying the
// new, modified collider's mass properties into the PhysicsMass component.
if (!mass.IsKinematic)
{
var massProperties = collider.MassProperties;
mass.Transform = massProperties.MassDistribution.Transform;
mass.InverseInertia = math.rcp(massProperties.MassDistribution.InertiaTensor);
mass.AngularExpansionFactor = massProperties.AngularExpansionFactor;
}
// Apply bake transform also to the PostTransformMatrix to affect the visuals.
postTransformMatrix = new PostTransformMatrix
{
Value = math.mul((float4x4)bakeTransform, postTransformMatrix.Value)
};
}
}
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<ColliderBakeTransform>();
m_ColliderBlobsToDisposeNow = new NativeQueue<BlobAssetReference<Unity.Physics.Collider>>(Allocator.Persistent);
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
using var ecb = new EntityCommandBuffer(Allocator.Temp);
var dt = SystemAPI.Time.DeltaTime;
// Make sure all colliders we want to apply transformations to have a PostTransformMatrix component, so that
// we can also affect their visuals.
foreach (var(scaleAndShearData, collider, entity) in SystemAPI
.Query<ColliderBakeTransform, RefRW<PhysicsCollider>>()
.WithNone<PostTransformMatrix>()
.WithEntityAccess())
{
ecb.AddComponent(entity, new PostTransformMatrix { Value = float4x4.identity });
ecb.AddComponent(entity, new SaveColliderBlobForDisposal
{
Collider = BlobAssetReference<Unity.Physics.Collider>.Null
});
if (!collider.ValueRO.IsUnique)
{
collider.ValueRW.MakeUnique(entity, ecb);
}
}
ecb.Playback(state.EntityManager);
var disposeJobHandle = new DisposeJob()
{
DisposeNow = m_ColliderBlobsToDisposeNow
}.Schedule(state.Dependency);
// Perform collider transform baking on NON-STATIC bodies with unique colliders
state.Dependency = new BakeTransformJob()
{
TimeStep = dt,
ColliderBlobsToDisposeNow = m_ColliderBlobsToDisposeNow.AsParallelWriter()
}.ScheduleParallel(disposeJobHandle);
}
[BurstCompile]
public void OnDestroy(ref SystemState state)
{
using var ecb = new EntityCommandBuffer(Allocator.Temp);
// Clean up any saved collider blobs that haven't been disposed of yet via the m_ColliderBlobsToDisposeNow queue
foreach (var(saveCollider, entity) in
SystemAPI.Query<RefRW<SaveColliderBlobForDisposal>>().WithEntityAccess())
{
//dispose the latest collider clone inside our bake data component
if (saveCollider.ValueRO.Collider.IsCreated)
{
saveCollider.ValueRW.Collider.Dispose();
ecb.RemoveComponent<SaveColliderBlobForDisposal>(entity);
}
}
ecb.Playback(state.EntityManager);
// Disposes blobs from clone of transformData.OriginalCollider that are used in the animation drift reset
while (!m_ColliderBlobsToDisposeNow.IsEmpty())
{
m_ColliderBlobsToDisposeNow.Dequeue().Dispose();
}
m_ColliderBlobsToDisposeNow.Dispose();
}
private struct DisposeJob : IJob
{
public NativeQueue<BlobAssetReference<Unity.Physics.Collider>> DisposeNow;
public void Execute()
{
while (!DisposeNow.IsEmpty())
{
DisposeNow.Dequeue().Dispose();
}
}
}
}