using Unity.Burst; using Unity.Collections; using Unity.Entities; using Unity.Assertions; using Unity.Burst.Intrinsics; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; using Unity.Transforms; namespace HelloCube.CustomTransforms { // This system computes a transform matrix for each entity with a LocalTransform2D. // For root-level / world-space entities with no Parent, the LocalToWorld can be // computed directly from the entity's LocalTransform2D. // For child entities, each unique hierarchy is traversed recursively, computing each child's LocalToWorld // by composing its LocalTransform with its parent's transform. [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)] [UpdateInGroup(typeof(TransformSystemGroup))] [UpdateAfter(typeof(ParentSystem))] [BurstCompile] public partial struct LocalToWorld2DSystem : ISystem { [BurstCompile] public void OnCreate(ref SystemState state) { state.RequireForUpdate(); } [BurstCompile] public void OnUpdate(ref SystemState state) { var rootsQuery = SystemAPI.QueryBuilder().WithAll().WithAllRW() .WithNone().Build(); var parentsQuery = SystemAPI.QueryBuilder().WithAll() .WithAllRW() .WithNone().Build(); var localToWorldWriteGroupMask = SystemAPI.QueryBuilder() .WithAll() .WithAllRW().Build().GetEntityQueryMask(); // compute LocalToWorld for all root-level entities var rootJob = new ComputeRootLocalToWorldJob { LocalTransform2DTypeHandleRO = SystemAPI.GetComponentTypeHandle(true), PostTransformMatrixTypeHandleRO = SystemAPI.GetComponentTypeHandle(true), LocalToWorldTypeHandleRW = SystemAPI.GetComponentTypeHandle(), LastSystemVersion = state.LastSystemVersion, }; state.Dependency = rootJob.ScheduleParallelByRef(rootsQuery, state.Dependency); // compute LocalToWorld for all child entities var childJob = new ComputeChildLocalToWorldJob { LocalToWorldWriteGroupMask = localToWorldWriteGroupMask, ChildTypeHandle = SystemAPI.GetBufferTypeHandle(true), ChildLookup = SystemAPI.GetBufferLookup(true), LocalToWorldTypeHandleRW = SystemAPI.GetComponentTypeHandle(), LocalTransform2DLookup = SystemAPI.GetComponentLookup(true), PostTransformMatrixLookup = SystemAPI.GetComponentLookup(true), LocalToWorldLookup = SystemAPI.GetComponentLookup(), LastSystemVersion = state.LastSystemVersion, }; state.Dependency = childJob.ScheduleParallelByRef(parentsQuery, state.Dependency); } [BurstCompile] unsafe struct ComputeRootLocalToWorldJob : IJobChunk { [ReadOnly] public ComponentTypeHandle LocalTransform2DTypeHandleRO; [ReadOnly] public ComponentTypeHandle PostTransformMatrixTypeHandleRO; public ComponentTypeHandle LocalToWorldTypeHandleRW; public uint LastSystemVersion; public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { Assert.IsFalse(useEnabledMask); LocalTransform2D* chunk2DLocalTransforms = (LocalTransform2D*)chunk.GetRequiredComponentDataPtrRO(ref LocalTransform2DTypeHandleRO); if (chunk.DidChange(ref LocalTransform2DTypeHandleRO, LastSystemVersion) || chunk.DidChange(ref PostTransformMatrixTypeHandleRO, LastSystemVersion)) { LocalToWorld* chunkLocalToWorlds = (LocalToWorld*)chunk.GetRequiredComponentDataPtrRW(ref LocalToWorldTypeHandleRW); PostTransformMatrix* chunkPostTransformMatrices = (PostTransformMatrix*)chunk.GetComponentDataPtrRO(ref PostTransformMatrixTypeHandleRO); if (chunkPostTransformMatrices != null) { for (int i = 0, chunkEntityCount = chunk.Count; i < chunkEntityCount; ++i) { chunkLocalToWorlds[i].Value = math.mul(chunk2DLocalTransforms[i].ToMatrix(), chunkPostTransformMatrices[i].Value); } } else { for (int i = 0, chunkEntityCount = chunk.Count; i < chunkEntityCount; ++i) { chunkLocalToWorlds[i].Value = chunk2DLocalTransforms[i].ToMatrix(); } } } } } [BurstCompile] unsafe struct ComputeChildLocalToWorldJob : IJobChunk { [NativeDisableContainerSafetyRestriction] public ComponentLookup LocalToWorldLookup; [ReadOnly] public EntityQueryMask LocalToWorldWriteGroupMask; [ReadOnly] public BufferTypeHandle ChildTypeHandle; [ReadOnly] public BufferLookup ChildLookup; public ComponentTypeHandle LocalToWorldTypeHandleRW; [ReadOnly] public ComponentLookup LocalTransform2DLookup; [ReadOnly] public ComponentLookup PostTransformMatrixLookup; public uint LastSystemVersion; public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) { Assert.IsFalse(useEnabledMask); bool updateChildrenTransform = chunk.DidChange(ref ChildTypeHandle, LastSystemVersion); BufferAccessor chunkChildBuffers = chunk.GetBufferAccessor(ref ChildTypeHandle); updateChildrenTransform = updateChildrenTransform || chunk.DidChange(ref LocalToWorldTypeHandleRW, LastSystemVersion); LocalToWorld* chunkLocalToWorlds = (LocalToWorld*)chunk.GetRequiredComponentDataPtrRO(ref LocalToWorldTypeHandleRW); for (int i = 0, chunkEntityCount = chunk.Count; i < chunkEntityCount; i++) { var localToWorld = chunkLocalToWorlds[i].Value; var children = chunkChildBuffers[i]; for (int j = 0, childCount = children.Length; j < childCount; j++) { ChildLocalToWorldFromTransformMatrix(localToWorld, children[j].Value, updateChildrenTransform); } } } void ChildLocalToWorldFromTransformMatrix(in float4x4 parentLocalToWorld, Entity childEntity, bool updateChildrenTransform) { updateChildrenTransform = updateChildrenTransform || PostTransformMatrixLookup.DidChange(childEntity, LastSystemVersion) || LocalTransform2DLookup.DidChange(childEntity, LastSystemVersion); float4x4 localToWorld; if (updateChildrenTransform && LocalToWorldWriteGroupMask.MatchesIgnoreFilter(childEntity)) { var localTransform2D = LocalTransform2DLookup[childEntity]; localToWorld = math.mul(parentLocalToWorld, localTransform2D.ToMatrix()); if (PostTransformMatrixLookup.HasComponent(childEntity)) { localToWorld = math.mul(localToWorld, PostTransformMatrixLookup[childEntity].Value); } LocalToWorldLookup[childEntity] = new LocalToWorld { Value = localToWorld }; } else { localToWorld = LocalToWorldLookup[childEntity].Value; updateChildrenTransform = LocalToWorldLookup.DidChange(childEntity, LastSystemVersion); } if (ChildLookup.TryGetBuffer(childEntity, out DynamicBuffer children)) { for (int i = 0, childCount = children.Length; i < childCount; i++) { ChildLocalToWorldFromTransformMatrix(localToWorld, children[i].Value, updateChildrenTransform); } } } } } }