/// <summary>
        /// Schedule a set of jobs to build the static tree of the broadphase based on the given world.
        /// </summary>
        public JobHandle ScheduleStaticTreeBuildJobs(
            ref PhysicsWorld world, int numThreadsHint, NativeArray <int> shouldDoWork, JobHandle inputDeps)
        {
            Assert.AreEqual(world.NumStaticBodies, m_StaticTree.NumBodies);
            if (world.NumStaticBodies == 0)
            {
                return(inputDeps);
            }

            var aabbs  = new NativeArray <Aabb>(world.NumStaticBodies, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
            var points = new NativeArray <PointAndIndex>(world.NumStaticBodies, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);

            var       numStaticBodiesArray = new NativeArray <int>(1, Allocator.TempJob);
            JobHandle handle = new PrepareNumStaticBodiesJob
            {
                NumStaticBodies      = world.NumStaticBodies,
                BuildStaticTree      = shouldDoWork,
                NumStaticBodiesArray = numStaticBodiesArray
            }.Schedule(inputDeps);

            var staticBodyDataJobHandle = new PrepareStaticBodyDataJob
            {
                RigidBodies = world.StaticBodies,
                Aabbs       = aabbs,
                Points      = points,
                FiltersOut  = m_StaticTree.BodyFilters,
                AabbMargin  = world.CollisionWorld.CollisionTolerance * 0.5f, // each body contributes half
            }.ScheduleUnsafeIndex0(numStaticBodiesArray, 32, handle);

            handle = JobHandle.CombineDependencies(staticBodyDataJobHandle, numStaticBodiesArray.Dispose(handle));

            return(m_StaticTree.BoundingVolumeHierarchy.ScheduleBuildJobs(
                       points, aabbs, m_StaticTree.BodyFilters, shouldDoWork, numThreadsHint, handle,
                       m_StaticTree.Nodes.Length, m_StaticTree.Ranges, m_StaticTree.BranchCount));
        }
        /// <summary>
        /// Build the static tree of the broadphase based on the given array of rigid bodies.
        /// </summary>
        public void BuildStaticTree(NativeSlice <RigidBody> staticBodies, float aabbMargin)
        {
            Assert.AreEqual(staticBodies.Length, m_StaticTree.NumBodies);

            if (staticBodies.Length == 0)
            {
                return;
            }

            // Read bodies
            var aabbs  = new NativeArray <Aabb>(staticBodies.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            var points = new NativeArray <PointAndIndex>(staticBodies.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            for (int i = 0; i < staticBodies.Length; i++)
            {
                PrepareStaticBodyDataJob.ExecuteImpl(i, aabbMargin, staticBodies, aabbs, points, m_StaticTree.BodyFilters);
            }

            // Build tree
            m_StaticTree.BoundingVolumeHierarchy.Build(points, aabbs, out int nodeCount);

            // Build node filters
            m_StaticTree.BoundingVolumeHierarchy.BuildCombinedCollisionFilter(m_StaticTree.BodyFilters, 1, nodeCount - 1);
        }