public void Execute()
            {
                int halfBVHArrayLength = BVHAABB.Length / 2;

                // Populate leaf nodes
                for (int i = 0; i < AABB.Length; i++)
                {
                    int colliderIndex = sortResultsArrayIsA[0] == 1 ? indexConverterA[i] : indexConverterB[i];
                    int bvhIndex      = halfBVHArrayLength + i;

                    BVHAABB[bvhIndex]               = AABB[colliderIndex];
                    BVHAssociatedEntity[bvhIndex]   = Entity[sortResultsArrayIsA[0] == 1 ? indexConverterA[i] : indexConverterB[i]];
                    BVHRightmostLeafIndex[bvhIndex] = bvhIndex;
                    BVHFirstChildIndex[bvhIndex]    = -1;
                    BVHIsValid[bvhIndex]            = 1;
                    BVHColliderType[bvhIndex]       = ColliderType[colliderIndex].Value;
                }

                // Set all parent nodes to valid
                int indexOfLastValidLeafOnLevel = halfBVHArrayLength + AABB.Length - 1;

                while (indexOfLastValidLeafOnLevel > 0)
                {
                    indexOfLastValidLeafOnLevel = PhysicsMath.GetParentBVHNodeIndex(indexOfLastValidLeafOnLevel);

                    if (indexOfLastValidLeafOnLevel == 0)
                    {
                        BVHIsValid[0] = 1;
                    }
                    else
                    {
                        for (int i = PhysicsMath.GetNextLowestPowerOf2(indexOfLastValidLeafOnLevel) - 1; i <= indexOfLastValidLeafOnLevel; i++)
                        {
                            BVHIsValid[i] = 1;
                        }
                    }
                }

                // Populate parent nodes bottom-up
                for (int i = halfBVHArrayLength - 1; i >= 0; i--)
                {
                    int childNode1 = PhysicsMath.GetFirstChildBVHNodeIndex(i);
                    int childNode2 = childNode1 + 1;

                    int nbNodesOnLevel   = PhysicsMath.GetNextLowestPowerOf2(i + 1);
                    int nodeOrderOnLevel = i - nbNodesOnLevel + 2;
                    int leafNodesCount   = (BVHAABB.Length + 1) / 2;

                    BVHFirstChildIndex[i]    = PhysicsMath.GetFirstChildBVHNodeIndex(i);
                    BVHRightmostLeafIndex[i] = (BVHAABB.Length - 1) - ((leafNodesCount / nbNodesOnLevel) * (nbNodesOnLevel - nodeOrderOnLevel));
                    BVHAABB[i] = PhysicsMath.GetEncompassingAABB(BVHAABB[childNode1], BVHAABB[childNode2]);
                }
            }
            public void CompareNodes(int A, int B)
            {
                int childA1Index = BVHArray[A].FirstChildIndex;
                int childA2Index = childA1Index + 1;
                int childB1Index = BVHArray[B].FirstChildIndex;
                int childB2Index = childB1Index + 1;

                // always compare own children
                if (BVHArray[A].IsValid > 0 &&
                    BVHArray[A].FirstChildIndex >= 0)
                {
                    CompareNodes(childA1Index, childA2Index);
                }
                if (BVHArray[B].IsValid > 0 &&
                    BVHArray[B].FirstChildIndex >= 0)
                {
                    CompareNodes(childB1Index, childB2Index);
                }

                if (BVHArray[A].IsValid > 0 &&
                    BVHArray[B].IsValid > 0)
                {
                    if (PhysicsMath.AABBToAABBOverlap(BVHArray[A].aabb, BVHArray[B].aabb))
                    {
                        // if leaf node, add as collision pair
                        if (BVHArray[A].FirstChildIndex < 0)
                        {
                            CollisionPair newPair = new CollisionPair
                            {
                                ColliderEntityA = BVHArray[A].AssociatedEntity,
                                ColliderEntityB = BVHArray[B].AssociatedEntity,
                            };
                            CollisionPairsQueue.Enqueue(newPair);
                        }
                        // if not, compare their children
                        else
                        {
                            // Compare child nodes
                            CompareNodes(childA1Index, childB1Index);
                            CompareNodes(childA1Index, childB2Index);
                            CompareNodes(childA2Index, childB1Index);
                            CompareNodes(childA2Index, childB2Index);
                        }
                    }
                }
            }
 void QueryBVHNode(int comparedToNode, int leafNodeIndex)
 {
     if (BVHRightmostLeafIndex[comparedToNode] > leafNodeIndex &&
         BVHIsValid[comparedToNode] > 0 &&
         PhysicsMath.AABBToAABBOverlap(BVHAABB[leafNodeIndex], BVHAABB[comparedToNode]))
     {
         // leaf node
         if (BVHFirstChildIndex[comparedToNode] < 0)
         {
             CollisionPairsQueue.Enqueue(new CollisionPair
             {
                 ColliderEntityA = BVHAssociatedEntity[leafNodeIndex],
                 ColliderEntityB = BVHAssociatedEntity[comparedToNode],
             });
         }
         else
         {
             int firstChildIndex = BVHFirstChildIndex[comparedToNode];
             QueryBVHNode(firstChildIndex, leafNodeIndex);
             QueryBVHNode(firstChildIndex + 1, leafNodeIndex);
         }
     }
 }
 void QueryBVHNode(int comparedToNode, int leafNodeIndex)
 {
     if (BVHArray[comparedToNode].RightmostLeafIndex > leafNodeIndex &&
         BVHArray[comparedToNode].IsValid > 0 &&
         PhysicsMath.AABBToAABBOverlap(BVHArray[leafNodeIndex].aabb, BVHArray[comparedToNode].aabb))
     {
         // leaf node
         if (BVHArray[comparedToNode].FirstChildIndex < 0)
         {
             CollisionPair newPair = new CollisionPair
             {
                 ColliderEntityA = BVHArray[leafNodeIndex].AssociatedEntity,
                 ColliderEntityB = BVHArray[comparedToNode].AssociatedEntity,
             };
             CollisionPairsQueue.Enqueue(newPair);
         }
         else
         {
             int firstChildIndex = BVHArray[comparedToNode].FirstChildIndex;
             QueryBVHNode(firstChildIndex, leafNodeIndex);
             QueryBVHNode(firstChildIndex + 1, leafNodeIndex);
         }
     }
 }
            public void Execute()
            {
                // Calculate all morton codes and init sorted index map
                for (int i = 0; i < aabbs.Length; i++)
                {
                    mortonCodesA[i] = PhysicsMath.CalculateMortonCode(PhysicsMath.GetAABBCenter(aabbs[i]));

                    indexConverterA[i] = i;
                    indexConverterB[i] = i;
                }

                // Radix sort ascending
                for (int bitPosition = 0; bitPosition < 31; bitPosition++)
                {
                    bool isEvenIteration = math.mod(bitPosition, 2) == 0;

                    // init histogram counts
                    zeroesHistogramCounter = 0;
                    onesHistogramCounter   = 0;
                    zeroesPrefixSum        = 0;
                    onesPrefixSum          = 0;

                    // Compute histograms and offsets
                    for (int i = 0; i < aabbs.Length; i++)
                    {
                        int bitVal = 0;
                        if (isEvenIteration)
                        {
                            bitVal = (mortonCodesA[i] & (1 << bitPosition)) >> bitPosition;
                        }
                        else
                        {
                            bitVal = (mortonCodesB[i] & (1 << bitPosition)) >> bitPosition;
                        }

                        radixSortBitValues[i] = bitVal;

                        if (bitVal == 0)
                        {
                            radixSortOffsets[i]     = zeroesHistogramCounter;
                            zeroesHistogramCounter += 1;
                        }
                        else
                        {
                            radixSortOffsets[i]   = onesHistogramCounter;
                            onesHistogramCounter += 1;
                        }
                    }

                    // calc prefix sum from histogram
                    zeroesPrefixSum = 0;
                    onesPrefixSum   = zeroesHistogramCounter;

                    // Reorder array
                    for (int i = 0; i < aabbs.Length; i++)
                    {
                        int newIndex = 0;
                        if (radixSortBitValues[i] == 0)
                        {
                            newIndex = zeroesPrefixSum + radixSortOffsets[i];
                        }
                        else
                        {
                            newIndex = onesPrefixSum + radixSortOffsets[i];
                        }

                        if (isEvenIteration)
                        {
                            mortonCodesB[newIndex]    = mortonCodesA[i];
                            indexConverterB[newIndex] = indexConverterA[i];
                        }
                        else
                        {
                            mortonCodesA[newIndex]    = mortonCodesB[i];
                            indexConverterA[newIndex] = indexConverterB[i];
                        }
                    }

                    sortResultsArrayIsA[0] = isEvenIteration ? 0 : 1; // it's the A array only for odd number iterations
                }
            }
        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            int collidersCount = _colliderGroup.AABB.Length;

            if (collidersCount <= 0)
            {
                return(inputDeps);
            }

            // Resize arrays if needed
            // TODO: just make these NativeLists?
            if (mortonCodesA.Length < collidersCount)
            {
                mortonCodesA.Dispose();
                mortonCodesB.Dispose();
                indexConverterB.Dispose();
                indexConverterA.Dispose();
                radixSortBitValues.Dispose();
                radixSortOffsets.Dispose();

                mortonCodesA       = new NativeArray <int>(collidersCount, Allocator.Persistent);
                mortonCodesB       = new NativeArray <int>(collidersCount, Allocator.Persistent);
                indexConverterB    = new NativeArray <int>(collidersCount, Allocator.Persistent);
                indexConverterA    = new NativeArray <int>(collidersCount, Allocator.Persistent);
                radixSortBitValues = new NativeArray <int>(collidersCount, Allocator.Persistent);
                radixSortOffsets   = new NativeArray <int>(collidersCount, Allocator.Persistent);
            }

            var sortMortonCodes = new ComputeAndSortMortonCodes
            {
                aabbs               = _colliderGroup.AABB,
                mortonCodesA        = mortonCodesA,
                mortonCodesB        = mortonCodesB,
                indexConverterA     = indexConverterA,
                indexConverterB     = indexConverterB,
                radixSortBitValues  = radixSortBitValues,
                radixSortOffsets    = radixSortOffsets,
                sortResultsArrayIsA = sortResultsArrayIsA,
            }.Schedule(inputDeps);

            // Debug sorted mortons
            if (PhysicsSystem.Settings.ShowZOrderCurve)
            {
                sortMortonCodes.Complete();
                bool indexConverterIsA = sortResultsArrayIsA[0] == 1;

                for (int i = 0; i < _colliderGroup.AABB.Length - 1; i++)
                {
                    float3 fromPoint;
                    float3 toPoint;

                    if (indexConverterIsA)
                    {
                        fromPoint = PhysicsMath.GetAABBCenter(_colliderGroup.AABB[indexConverterA[i]]);
                        toPoint   = PhysicsMath.GetAABBCenter(_colliderGroup.AABB[indexConverterA[i + 1]]);
                    }
                    else
                    {
                        fromPoint = PhysicsMath.GetAABBCenter(_colliderGroup.AABB[indexConverterB[i]]);
                        toPoint   = PhysicsMath.GetAABBCenter(_colliderGroup.AABB[indexConverterB[i + 1]]);
                    }

                    Debug.DrawLine(fromPoint, toPoint, Color.red);
                }
            }

            // Build BVH
            int requiredBVHLength = (PhysicsMath.GetNextHighestPowerOf2(math.ceil_pow2(collidersCount) + 1)) - 1;

            BVHArray.Dispose();
            BVHArray = new NativeArray <BVHNode>(requiredBVHLength, Allocator.TempJob);

            var constructBVH = new ConstructBVH()
            {
                Entity              = _colliderGroup.Entity,
                AABB                = _colliderGroup.AABB,
                ColliderType        = _colliderGroup.ColliderType,
                BVHArray            = BVHArray,
                indexConverterA     = indexConverterA,
                indexConverterB     = indexConverterB,
                sortResultsArrayIsA = sortResultsArrayIsA,
            }.Schedule(sortMortonCodes);

            // Draw bvh second-to-last groups
            if (PhysicsSystem.Settings.ShowBVHBounds)
            {
                constructBVH.Complete();
                for (int i = BVHArray.Length / 4; i < BVHArray.Length / 2; i++)
                {
                    if (BVHArray[i].IsValid > 0)
                    {
                        DebugUtils.DrawAABB(BVHArray[i].aabb, UnityEngine.Random.ColorHSV());
                    }
                }
            }

            // Init pairs queue
            SphereSphereCollisionPairsQueue.Clear();

            JobHandle buildCollisionPairs = new JobHandle();

            //Build Pairs job
            {
                BuildCollisionPairsParallel buildCollisionPairsJob = new BuildCollisionPairsParallel()
                {
                    HalfBVHArrayLength  = BVHArray.Length / 2,
                    BVHArray            = BVHArray,
                    CollisionPairsQueue = SphereSphereCollisionPairsQueue,
                };
                buildCollisionPairs = buildCollisionPairsJob.Schedule(_colliderGroup.AABB.Length, PhysicsSystem.Settings.BroadphaseSystemBatchCount, constructBVH);
            }

            //{
            //    BuildCollisionPairsSingle buildCollisionPairsJob = new BuildCollisionPairsSingle()
            //    {
            //        StartIndex = BVHArray.Length / 2,
            //        IndexCount = _colliderGroup.AABB.Length,
            //        BVHArray = BVHArray,
            //        CollisionPairsQueue = SphereSphereCollisionPairsQueue,
            //    };
            //    buildCollisionPairs = buildCollisionPairsJob.Schedule(constructBVH);
            //}

            //{
            //    NativeQueue<NodePair> NodeStack = new NativeQueue<NodePair>(Allocator.TempJob);
            //    BuildCollisionPairsGroupStack buildCollisionPairsJob = new BuildCollisionPairsGroupStack()
            //    {
            //        BVHArray = BVHArray,
            //        NodePairsStack = NodeStack,
            //        CollisionPairsQueue = SphereSphereCollisionPairsQueue,
            //    };
            //    buildCollisionPairs = buildCollisionPairsJob.Schedule(constructBVH);

            //    buildCollisionPairs.Complete();
            //    NodeStack.Dispose();
            //}

            //{
            //    BuildCollisionPairsGroupRecursive buildCollisionPairsJob = new BuildCollisionPairsGroupRecursive()
            //    {
            //        BVHArray = BVHArray,
            //        CollisionPairsQueue = SphereSphereCollisionPairsQueue,
            //    };
            //    buildCollisionPairs = buildCollisionPairsJob.Schedule(constructBVH);
            //}

            // Init pairs array
            buildCollisionPairs.Complete();

            if (PhysicsSystem.SphereSphereCollisionPairsArray.IsCreated)
            {
                PhysicsSystem.SphereSphereCollisionPairsArray.Dispose();
            }
            PhysicsSystem.SphereSphereCollisionPairsArray = new NativeArray <CollisionPair>(SphereSphereCollisionPairsQueue.Count, Allocator.TempJob);

            // Dequeue pairs into array
            DequeueIntoArray <CollisionPair> dequeuePairsJob = new DequeueIntoArray <CollisionPair>()
            {
                InputQueue  = SphereSphereCollisionPairsQueue,
                OutputArray = PhysicsSystem.SphereSphereCollisionPairsArray,
            };
            JobHandle dequeueCollisionPairs = dequeuePairsJob.Schedule(buildCollisionPairs);

            //// Debug CollisionPairsArray
            if (PhysicsSystem.Settings.ShowCollisionPairs)
            {
                dequeueCollisionPairs.Complete();

                for (int i = 0; i < PhysicsSystem.SphereSphereCollisionPairsArray.Length; i++)
                {
                    Color         randomCol = new Color(UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f), UnityEngine.Random.Range(0f, 1f));
                    CollisionPair pair      = PhysicsSystem.SphereSphereCollisionPairsArray[i];
                    Debug.DrawLine(PhysicsMath.GetAABBCenter(AABBFromEntity[pair.ColliderEntityA]), PhysicsMath.GetAABBCenter(AABBFromEntity[pair.ColliderEntityB]), randomCol);
                }
            }

            // Need to complete jobs here because counter will be read in the next system
            dequeueCollisionPairs.Complete();

            return(dequeueCollisionPairs);
        }