Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- public class ArticulatorManager : Singleton<ArticulatorManager>
- {
- public void LateUpdate()
- {
- ArticulatorSystem.Update();
- }
- [BurstCompile]
- public struct UpdateJob : IJobParallelFor
- {
- public UnmanagedList<ArticulatorData> activeArticulators;
- public void Execute(int i)
- {
- activeArticulators[i].Execute();
- }
- }
- public void OnEnable()
- {
- activeArticulators = new(32, Allocator.Persistent);
- }
- public void OnDisable()
- {
- if (activeArticulators.IsCreated)
- {
- activeArticulators.Dispose();
- activeArticulators = default;
- }
- }
- }
- public class Articulator : SBPMonoBehaviour
- {
- [GetComponentsInChildren]
- public IArticulationJobProvider[] jobProviders;
- public void OnEnable() => ArticulatorManager.Register(this);
- public void OnDisable() => ArticulatorManager.Unregister(this);
- private List<Transform> boundTransforms = new();
- public ArticulatorChildHandle Bind(Transform transform)
- {
- var i = boundTransforms.IndexOf(transform);
- if (i < 0)
- {
- boundTransforms.Add(transform);
- i = boundTransforms.Length - 1;
- }
- return new(GetInstanceID(), i);
- }
- }
- public struct ArticulatorChildHandle
- {
- public InstanceID articulatorId;
- public int childIndex;
- }
- public interface IArticulationJob : IDisposable
- {
- public void Execute(ref ArticulationHeirachy articulator);
- }
- // to be put on a monobehaviour, will have transform references and stuff.
- public interface IArticulationJobProvider<T> where T : IArticulationJob
- {
- public ArticulatorJobData GetJobData(Articulator articulator);
- }
- public struct ArticulatorJobData : IDisposable
- {
- public void* data; // these pointers will go all over the place; DoD says we should use aligned arrays
- public FunctionPointer<ArticulationJobDel> func;
- public FunctionPointer<BurstDelegate> dispose;
- public static ArticulatorJobData Create<T>(T job) where T : unmanaged, IArticulationJob
- {
- var ptr = Ptr.Create(job, Allocator.Persistent);
- var func = Gen.Get<T>();
- return new(ptr, func);
- }
- public void Execute(ref ArticulationHeirachy articulator)
- {
- func.Invoke(ref articulator, data);
- }
- public void Dispose()
- {
- dispose.Invoke(data);
- Ptr.FreeTracked(data, Allocator.Persistent);
- }
- }
- public class TestArticulation : MonoBehaviour, IArticulationJobProvider
- {
- public Transform root;
- public Transform tip;
- public ArticulatorJobData GetJobData(Articulator articulator)
- {
- var job = new TestArticulationJob()
- {
- bones = AnimationUtility.ExtractChain(root, tip)
- .Select(x => articulator.Bind(x))
- .ToNativeArray(Allocator.Persistent);
- }
- return ArticulatorJobData.Create(job);
- }
- [BurstCompile]
- public struct TestArticulationJob : IArticulationJob
- {
- public NativeArray<ArticulatorChildHandle> bones;
- public void Execute(ref ArticulationHeirachy articulator)
- {
- for (int i = 0; i < bones.Length; i++)
- {
- ref var bone = ref articulator.RefChildTransform(bones[i]);
- bone.position += 1;
- }
- }
- public void Dispose()
- {
- bones.Dispose();
- }
- }
- }
- public delegate void ArticulationJobDel(ref ArticulationHeirachy art, void* jobData);
- public delegate void BurstDelegate(void* data);
- // to be codegenned, create a delegate based on all IArticulationJob types.
- [BurstCompile]
- public static class Gen
- {
- public static FunctionPointer<ArticulationJobDel> Get<T>()
- {
- return T switch
- {
- TestArticulation => BurstCompiler.CompileFunctionPointer<ArticulationJobDel>(TestArticulationDel),
- }
- }
- [BurstCompile, MonoPInvokeCallback(typeof(ArticulationJobDel))]
- public static void TestArticulationDel(ref ArticulationHeirachy a, void* data) => UnsafeUtility.AsRef<TestArticulation>(data).Execute(ref a);
- }
- public struct InstanceID
- {
- public int Value;
- }
- [InitializeSharedStatic]
- public static class ArticulatorSystem
- {
- private static UnmanagedList<ArticulationHeirachy> activeArticulators;
- private static UnmanagedList<InstanceID> instanceIDs;
- private static UnsafeParallelHashMap<InstanceID, int> articulatorMap;
- private static TransformAccessArray transformAccessArray;
- public static void Update()
- {
- }
- public static void Register(Articulator articulator)
- {
- if (!activeArticulators.IsCreated)
- return;
- activeArticulators.Add(ArticulationHeirachy.Create(articulator));
- var instanceId = articulator.GetInstanceID();
- instanceIDs.Add(instanceId));
- articulatorMap[instanceId] = activeArticulators.Length - 1;
- }
- public static void Unregister(Articulator articulator)
- {
- if (!activeArticulators.IsCreated)
- return;
- var instanceId = articulator.GetInstanceID();
- var index = articulatorMap[instanceId];
- activeArticulators[index].Dispose();
- activeArticulators.RemoveAtSwapBack(index);
- instanceIDs.RemoveAtSwapBack(index);
- articulatorMap.Remove(instanceId);
- articulatorMap[instanceIDs[index]] = index;
- }
- // Run all jobs for each articulator on a separate thread
- [BurstCompile]
- public struct UpdateArticulatorJob : IJobParallelFor
- {
- public UnmanagedList<ArticulationHeirachy> articulators;
- public void Execute(int i)
- {
- articulators[i].RunJobs();
- }
- }
- [BurstCompile]
- public struct UpdateArticulatorJob : IJobParallelForTransform
- {
- // the hashmap lookups are a bit wasteful because we have to do it each time for every child. this is almost certainly still faster than scheduling one job per articulator.
- public UnsafeParallelHashMap<InstanceID, int> articulatorMap;
- public UnmanagedList<ArticulationHeirachy> articulators;
- // updating the transforms needs to be done in one big job due to how the taa works - unity will batch together
- // transforms in the same heirachy into the same thread, which for us will give the effect of one thread per articulator.
- // however the order is not guaranteed so we need to map back to the originator.
- public UnmanagedList<ArticulatorChildHandle> transformAccessMap;
- public void Execute(int i, TransformAccess transform)
- {
- var n = transformAccessMap[i];
- var tx = articulators[articulatorMap[n.articulatorId]].children[n.childIndex];
- transform.SetPositionAndRotation(tx.position, tx.rotation);
- }
- }
- [BurstCompile]
- public struct UpdateArticulatorJob : IJob
- {
- public UnmanagedList<ArticulationHeirachy> articulators;
- public InstanceRenderer.Writer instanceRendererWriter;
- public void Execute()
- {
- for (int i = 0; i < articulators.Length; i++)
- {
- ref var art = ref articulators[i];
- art.AddInstances(instanceRendererWriter);
- }
- }
- }
- }
- public struct ArticulationHeirachy
- {
- // flat array of all children excluding the root - world space
- // how do we ensure we have only the transforms that we care about in here? will have rather 'furry' trees if we collect everything
- UnmanagedArray<EntityTransform> children;
- // cache the original state for the children so they can be reset - mostly useful for previewing animations in editor mode
- UnmanagedArray<EntityTransform> initialChildrenState;
- UnmanagedArray<ArticulationJobData> jobs;
- UnmanagedArray<RenderItem> renderItems; // each child could have zero or many prefabs to render
- private struct RenderItem
- {
- // index of parent transform in children array
- public int parent;
- // prefab to render
- public PrefabReference prefab;
- // each prefab is not necessarily located at identity of child
- public EntityTransform localTransform;
- }
- public static ArticulationHeirachy Create(Articulator articulator)
- {
- // get all children
- var allTransforms = articulator.boundTransforms;
- children = new(allTransforms.Length, Allocator.Persistent);
- var tempRenderItems = new UnmanagedList<RenderItem>(32, Allocator.Temp);
- for (int i = 0; i < allTransforms.Length; i++)
- {
- children[i] = initialChildrenState[i] = allTransforms[i];
- }
- // populate jobs
- var providers = articulator.jobProviders;
- jobs = new(providers.Length, Allocator.Persistent);
- for (int i = 0; i < providers.Length; i++)
- {
- var provider = providers[i];
- jobs[i] = provider.GetJobData();
- }
- }
- public ref EntityTransform RefChildTransform(ArticulatorChildHandle handle)
- {
- return ref children[handle.childIndex];
- }
- public void RunJobs()
- {
- for (int i = 0; i < jobs.Length; i++)
- {
- jobs[i].Invoke(ref this);
- }
- }
- public void ApplyTo(TransformAccessArray taa)
- {
- for (int i = 0; i < children.Length; i++)
- {
- var tx = children[i];
- // nope. this is not how this works.
- taa[transformToUnityMap[i]].SetPositionAndRotation(tx.position, tx.rotation);
- }
- }
- // idea is this is done in burst so we pass in a struct writer
- public void AddInstances(InstanceRenderer.Writer instanceRendererWriter)
- {
- for (int i = 0; i < renderItems.Length; i++)
- {
- var r = renderItems[i];
- instanceRendererWriter.AddInstance(r.prefab, children[r.parent] * r.localTransform);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement