Esempio n. 1
0
        /// <summary>
        /// Allows future reuse of spare instance, by putting it into the end of spares store.
        /// </summary>
        /// <param name="i_instanceIndex">Instance index, to pu back into spares of instances.</param>
        /// <param name="i_nodeIntstanceIndex">Node instance index holder, to be reset.</param>
        static public void _PutBackSpareInstance(ref RootNodeData rootNodeData, int i_instanceIndex, int i_nodeIntstanceIndex, ref DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, ref DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer)
        {
            if (i_instanceIndex < 0)
            {
                return;                               // This instance index has not been used.
            }
            rootNodeData.i_instancesSpareLastIndex++; // Put back to spare

            InstancesSpareIndexBufferElement instancesSpareIndexBuffer = new InstancesSpareIndexBufferElement();

            instancesSpareIndexBuffer.i = i_instanceIndex;
            a_instancesSpareIndexBuffer [rootNodeData.i_instancesSpareLastIndex] = instancesSpareIndexBuffer;

            // Is assumed, that size of spares store, is appropriate.
            NodeInstancesIndexBufferElement nodeInstancesIndexBuffer = new NodeInstancesIndexBufferElement();

            nodeInstancesIndexBuffer.i = -1;  // Reset instance index.
            a_nodeInstancesIndexBuffer [i_nodeIntstanceIndex] = nodeInstancesIndexBuffer;
        }
Esempio n. 2
0
        /// <summary>
        /// Remove an instace. Makes the assumption that the instance only exists once in the tree.
        /// </summary>
        /// <param name="i_nodeIndex">Internal octree node index.</param>
        /// <param name="i_instanceID">External instance index ID to remove. Is assumed, only one unique instance ID exists in the tree.</param>
        /// <returns>True if the object was removed successfully.</returns>
        static private bool _NodeRemoveInstance(ref RootNodeData rootNodeData, int i_nodeIndex, int i_instanceID, ref DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer, DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, ref DynamicBuffer <InstanceBufferElement> a_instanceBuffer, ref DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer)
        {
            bool removed = false;

            NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex];

            int i_nodeInstancesIndexOffset = i_nodeIndex * rootNodeData.i_instancesAllowedCount;

            if (nodeBuffer.i_instancesCount > 0)
            {
                // Try remove instance from this node
                for (int i = 0; i < rootNodeData.i_instancesAllowedCount; i++)
                {
                    NodeInstancesIndexBufferElement nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_nodeInstancesIndexOffset + i];
                    int i_existingInstanceIndex = nodeInstancesIndexBuffer.i;

                    // If instance exists
                    if (i_existingInstanceIndex >= 0)
                    {
                        InstanceBufferElement instanceBuffer = a_instanceBuffer [i_existingInstanceIndex];
                        if (instanceBuffer.i_ID == i_instanceID)
                        {
                            removed = true;

                            // Remove from here
                            CommonMethods._PutBackSpareInstance(ref rootNodeData, i_existingInstanceIndex, i_nodeIndex, ref a_nodeInstancesIndexBuffer, ref a_instancesSpareIndexBuffer);

/*
 *                          // Debugging
 *  GameObject go = GameObject.Find ( "Instance " + i_instanceID.ToString () ) ;
 *
 *  if ( go != null )
 *  {
 *      Debug.Log ( "Instance: Hide game object #" + i_instanceID.ToString () ) ;
 *      go.SetActive ( false ) ;
 *      // go.transform.localScale = instanceBounds.size ;
 *  }
 *
 *                          nodeBuffer.i_instancesCount -- ;
 *                          instanceBuffer.i_ID = -1 ; // Reset
 *                          a_instanceBuffer [i_existingInstanceIndex] = instanceBuffer ; // Set back
 *
 *
 *  Debug.LogWarning ( "Node: Remove #" + i_nodeIndex ) ;
 *  GameObject.Destroy ( GameObject.Find ( "Node " + i_nodeIndex.ToString () ) ) ;
 */
                            break;
                        }
                    }
                }                                         // for

                a_nodesBuffer [i_nodeIndex] = nodeBuffer; // Set back
            }


            int i_nodeChildrenCount       = nodeBuffer.i_childrenCount;
            int i_nodeChildrenIndexOffset = i_nodeIndex * 8;

            // Try remove instance from this node children, if node don't have this instance
            if (!removed && i_nodeChildrenCount > 0)
            {
                for (int i = 0; i < 8; i++)
                {
                    // Get children index of this node

                    NodeChildrenBufferElement nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i];
                    int i_childNodeIndex = nodeChildrenBuffer.i_nodesIndex;

                    // Ignore negative index
                    if (i_childNodeIndex >= 0)
                    {
                        removed = _NodeRemoveInstance(
                            ref rootNodeData,
                            i_childNodeIndex,
                            i_instanceID,
                            ref a_nodesBuffer,
                            ref a_nodeSparesBuffer,
                            a_nodeChildrenBuffer,
                            a_nodeInstancesIndexBuffer,
                            ref a_instanceBuffer,
                            ref a_instancesSpareIndexBuffer
                            );

                        if (removed)
                        {
                            break;
                        }
                    }
                }
            }

            if (removed && i_nodeChildrenCount > 0)
            {
                // Check if we should merge nodes now that we've removed an item
                if (_ShouldMerge(ref rootNodeData, i_nodeIndex, a_nodesBuffer, ref a_nodeChildrenBuffer))
                {
                    _MergeNodes(
                        ref rootNodeData,
                        i_nodeIndex,
                        ref a_nodesBuffer,
                        ref a_nodeSparesBuffer,
                        a_nodeChildrenBuffer,
                        a_nodeInstancesIndexBuffer,
                        ref a_instancesSpareIndexBuffer
                        );
                }
            }

            return(removed);
        }
Esempio n. 3
0
        /// <summary>
        /// Shrink the octree if possible, else leave it the same.
        /// We can shrink the octree if:
        /// - This node is >= double minLength in length
        /// - All objects in the root node are within one octant
        /// - This node doesn't have children, or does but 7/8 children are empty
        /// We can also shrink it if there are no objects left at all!
        /// </summary>
        /// <param name="i_nodeIndex">Internal octree node index.</param>
        /// <param name="minLength">Minimum dimensions of a node in this octree.</param>
        /// <returns>The new root index, or the existing one if we didn't shrink.</returns>
        static private int _ShrinkIfPossible(RootNodeData rootNodeData, int i_nodeIndex, float minLength, DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, DynamicBuffer <InstanceBufferElement> a_instanceBuffer)
        {
            NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex];


            NodeChildrenBufferElement nodeChildrenBuffer;


            if (nodeBuffer.f_baseLength < (2 * minLength))
            {
                return(i_nodeIndex);
            }

            if (nodeBuffer.i_instancesCount == 0 && nodeBuffer.i_childrenCount == 0)
            {
                return(i_nodeIndex);
            }

            int i_nodeChildrenIndexOffset  = i_nodeIndex * 8;
            int i_nodeInstancesIndexOffset = i_nodeIndex * rootNodeData.i_instancesAllowedCount;


            // -1 to 7, where -1 is no result found
            int i_bestFit = -1;

            // Check objects in root
            for (int i = 0; i < rootNodeData.i_instancesAllowedCount; i++)
            {
                if (nodeBuffer.i_instancesCount == 0)
                {
                    break;
                }

                NodeInstancesIndexBufferElement nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_nodeInstancesIndexOffset + i];


                if (nodeInstancesIndexBuffer.i >= 0)
                {
                    InstanceBufferElement instanceBuffer = a_instanceBuffer [nodeInstancesIndexBuffer.i];

                    int newBestFit = CommonMethods._BestFitChild(i_nodeIndex, instanceBuffer.bounds, a_nodesBuffer);

                    if (i == 0 || newBestFit == i_bestFit)
                    {
                        nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + newBestFit];

                        // In same octant as the other(s). Does it fit completely inside that octant?
                        if (CommonMethods._Encapsulates(nodeChildrenBuffer.bounds, instanceBuffer.bounds))
                        {
                            if (i_bestFit < 0)
                            {
                                i_bestFit = newBestFit;
                            }

                            break;
                        }
                        else
                        {
                            // Nope, so we can't reduce. Otherwise we continue
                            return(i_nodeIndex);
                        }
                    }
                    else
                    {
                        return(i_nodeIndex);                 // Can't reduce - objects fit in different octants
                    }
                }
            }         // for


            // Check instances in children if there are any
            if (nodeBuffer.i_childrenCount > 0)
            {
                bool childHadContent = false;

                for (int i = 0; i < 8; i++)
                {
                    nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i];

                    // Has child any instances
                    if (CommonMethods._HasAnyInstances(nodeChildrenBuffer.i_nodesIndex, a_nodesBuffer, a_nodeChildrenBuffer))
                    {
                        if (childHadContent)
                        {
                            return(i_nodeIndex);                         // Can't shrink - another child had content already
                        }
                        if (i_bestFit >= 0 && i_bestFit != i)
                        {
                            return(i_nodeIndex);                         // Can't reduce - objects in root are in a different octant to objects in child
                        }

                        childHadContent = true;
                        i_bestFit       = i;
                    }
                }
            }
            // Can reduce
            else if (nodeBuffer.i_childrenCount == 0)
            {
                nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i_bestFit];
                Bounds childBounds = nodeChildrenBuffer.bounds;

                // We don't have any children, so just shrink this node to the new size
                // We already know that everything will still fit in it
                CommonMethods._SetValues(rootNodeData, i_nodeIndex, nodeBuffer.f_baseLength / 2, childBounds.center, ref a_nodesBuffer, ref a_nodeChildrenBuffer);

                return(i_nodeIndex);
            }

            // No objects in entire octree
            if (i_bestFit == -1)
            {
                return(i_nodeIndex);
            }


            // We have children. Use the appropriate child as the new root node
            nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i_bestFit];
            return(nodeChildrenBuffer.i_nodesIndex);
        }
        /// <summary>
        /// Returns an collection of instances, that intersect with the specified bounds, if any. Otherwise returns an empty array. See also: IsColliding.
        /// </summary>
        /// <param name="i_nodeIndex">Internal octree node index.</param>
        /// <param name="checkBounds">Bounds to check. Passing by ref as it improves performance with structs.</param>
        /// <param name="l_resultInstanceIDs">List result.</param>
        static public bool _GetNodeColliding(RootNodeData octreeRootNodeData, int i_nodeIndex, Bounds checkBounds, ref DynamicBuffer <CollisionInstancesBufferElement> a_collisionInstancesBuffer, ref IsCollidingData isCollidingData, DynamicBuffer <NodeBufferElement> a_nodesBuffer, DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, DynamicBuffer <InstanceBufferElement> a_instanceBuffer)
        {
            // float f_distance ;
            CollisionInstancesBufferElement collisionInstancesBuffer = new CollisionInstancesBufferElement();

            NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex];

            // Are the input bounds at least partially in this node?
            if (!nodeBuffer.bounds.Intersects(checkBounds))
            {
                return(isCollidingData.i_collisionsCount > 0 ? true : false);
            }


            if (nodeBuffer.i_instancesCount >= 0)
            {
                int i_nodeInstancesIndexOffset = i_nodeIndex * octreeRootNodeData.i_instancesAllowedCount;

                // Check against any objects in this node
                for (int i = 0; i < octreeRootNodeData.i_instancesAllowedCount; i++)
                {
                    NodeInstancesIndexBufferElement nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_nodeInstancesIndexOffset + i];

                    // Get index of instance
                    int i_instanceIndex = nodeInstancesIndexBuffer.i;

                    // Check if instance exists, and if has intersecting bounds.
                    if (i_instanceIndex >= 0)
                    {
                        InstanceBufferElement instanceBuffer = a_instanceBuffer [i_instanceIndex];

                        // Check if instance exists, and if has intersecting bounds.
                        if (instanceBuffer.bounds.Intersects(checkBounds))
                        {
                            // l_resultInstanceIDs.Add ( instanceBuffer.i_ID ) ;

                            //if ( f_distance < isCollidingData.f_nearestDistance )
                            //{
                            //    isCollidingData.f_nearestDistance = f_distance ;
                            //    isCollidingData.i_nearestInstanceCollisionIndex = isCollidingData.i_collisionsCount ;
                            //}



                            // Is expected, that the required length is no greater than current length + 1
                            // And is not negative.
                            int i_collisionsCount = isCollidingData.i_collisionsCount;
                            collisionInstancesBuffer.i_ID      = instanceBuffer.i_ID;
                            collisionInstancesBuffer.i_version = instanceBuffer.i_entityVersion;  // Optional, used when Id is used as entity index

                            // Assign colliding instance ID to buffer.
                            if (a_collisionInstancesBuffer.Length <= i_collisionsCount)
                            {
                                // Expand buffer if needed
                                a_collisionInstancesBuffer.Add(collisionInstancesBuffer);
                            }
                            else
                            {
                                a_collisionInstancesBuffer [i_collisionsCount] = collisionInstancesBuffer;
                            }

                            isCollidingData.i_collisionsCount++;
                        }
                    }
                }
            }

            // Check children for collisions
            // Check if having children
            if (nodeBuffer.i_childrenCount > 0)
            {
                int i_nodeChildrenIndexOffset = i_nodeIndex * 8;

                // We checked that is having children.
                for (int i = 0; i < 8; i++)
                {
                    NodeChildrenBufferElement nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i];
                    int i_nodeChildIndex = nodeChildrenBuffer.i_nodesIndex;

                    // Check if node exists
                    if (i_nodeChildIndex >= 0)
                    {
                        _GetNodeColliding(octreeRootNodeData, i_nodeChildIndex, checkBounds, ref a_collisionInstancesBuffer, ref isCollidingData, a_nodesBuffer, a_nodeChildrenBuffer, a_nodeInstancesIndexBuffer, a_instanceBuffer);
                    }
                }
            }

            return(isCollidingData.i_collisionsCount > 0 ? true : false);
        }
        /// <summary>
        /// Private counterpart to the public Add method.
        /// </summary>
        /// <param name="i_nodeIndex">Internal octree node index.</param>
        /// <param name="i_instanceID">External instance index, ot unique entity index.</param>
        /// <param name="i_entityVersion">Optional, used when Id is used as entity index.</param>
        /// <param name="instanceBounds">External 3D bounding box around the instance to add.</param>
        static private void _NodeInstanceSubAdd(ref RootNodeData rootNodeData, int i_nodeIndex, int i_instanceID, int i_entityVersion, Bounds instanceBounds, ref DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer, ref DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, ref DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, ref DynamicBuffer <InstanceBufferElement> a_instanceBuffer, ref DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer, int i_requiredNumberOfInstances)
        {
            NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex];


            // We know it fits at this level if we've got this far
            // Just add if few objects are here, or children would be below min size
            int i_instancesCount = nodeBuffer.i_instancesCount;

            if (i_instancesCount < rootNodeData.i_instancesAllowedCount || (nodeBuffer.f_baseLength / 2) < rootNodeData.f_minSize)
            {
                _AssingInstance2Node(rootNodeData, i_nodeIndex, i_instanceID, i_entityVersion, instanceBounds, ref a_nodesBuffer, ref a_instanceBuffer, ref a_nodeInstancesIndexBuffer, a_instancesSpareIndexBuffer);

                // a_nodesBuffer
                if (rootNodeData.i_instancesSpareLastIndex == 0 || i_requiredNumberOfInstances > a_instanceBuffer.Length)
                {
                    // Add some spares if needed.
                    CommonMethods._AddInstanceSpares(ref rootNodeData, ref a_instanceBuffer, ref a_instancesSpareIndexBuffer, i_requiredNumberOfInstances);
                }
                else
                {
                    rootNodeData.i_instancesSpareLastIndex--;
                }


                /*
                 * // Debugging
                 * GameObject go = GameObject.Find ( "Instance " + i_instanceID.ToString () ) ;
                 *
                 * if ( go != null )
                 * {
                 * Debug.Log ( "Instance: New game object #" + i_instanceID.ToString () ) ;
                 * go.SetActive ( true ) ;
                 * go.transform.localScale = instanceBounds.size ;
                 * }
                 * else
                 * {
                 * Debug.Log ( "Instance: New game object #" + i_instanceID.ToString () ) ;
                 *
                 * GameObject newGameObject = GameObject.Instantiate ( GameObject.Find ( "TempInstance" ), instanceBounds.center, Quaternion.identity ) ;
                 * newGameObject.transform.localScale = instanceBounds.size ;
                 *
                 * newGameObject.name = "Instance " + i_instanceID.ToString () ;
                 * }
                 */
            }
            else
            {
                // Fits at this level, but we can go deeper. Would it fit there?

                // Create the 8 children
                int i_bestFitChildLocalIndex;
                int i_bestChildIndex;

                nodeBuffer = a_nodesBuffer [i_nodeIndex];
                int i_childrenCount       = nodeBuffer.i_childrenCount;
                int i_childrenIndexOffset = i_nodeIndex * 8;

                NodeChildrenBufferElement nodeChildrenBuffer;

                if (i_childrenCount == 0)
                {
                    // Split Octree node, into 8 new smaller nodes as children nodex.
                    _Split(ref rootNodeData, i_nodeIndex, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer);

                    NodeInstancesIndexBufferElement nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_nodeIndex];

                    int i_nodeInstanceIndexOffset = nodeInstancesIndexBuffer.i;

                    // Now that we have the new children, see if this node's existing objects would fit there
                    for (int i = i_instancesCount - 1; i >= 0; i--)
                    {
                        int i_instanceIndexOffset = i_nodeInstanceIndexOffset + i;

                        if (i_instanceIndexOffset >= 0)
                        {
                            InstanceBufferElement existingInstanceBuffer = a_instanceBuffer [i_instanceIndexOffset];

                            // Find which child the object is closest to based on, where the
                            // object's center is located in relation to the octree's center.
                            i_bestFitChildLocalIndex = CommonMethods._BestFitChild(i_nodeIndex, existingInstanceBuffer.bounds, a_nodesBuffer);

                            i_bestChildIndex   = i_childrenIndexOffset + i_bestFitChildLocalIndex;
                            nodeChildrenBuffer = a_nodeChildrenBuffer [i_bestChildIndex];


                            // Does it fit?
                            if (CommonMethods._Encapsulates(nodeChildrenBuffer.bounds, existingInstanceBuffer.bounds))
                            {
                                _NodeInstanceSubAdd(
                                    ref rootNodeData,
                                    nodeChildrenBuffer.i_nodesIndex,
                                    existingInstanceBuffer.i_ID,
                                    existingInstanceBuffer.i_entityVersion,
                                    existingInstanceBuffer.bounds,
                                    ref a_nodesBuffer,
                                    ref a_nodeSparesBuffer,
                                    ref a_nodeChildrenBuffer,
                                    ref a_nodeInstancesIndexBuffer,
                                    ref a_instanceBuffer,
                                    ref a_instancesSpareIndexBuffer,
                                    i_requiredNumberOfInstances
                                    ); // Go a level deeper


                                // Remove from here
                                CommonMethods._PutBackSpareInstance(ref rootNodeData, i_instanceIndexOffset, i_nodeIndex, ref a_nodeInstancesIndexBuffer, ref a_instancesSpareIndexBuffer);

                                nodeBuffer = a_nodesBuffer [i_nodeIndex];
                                nodeBuffer.i_instancesCount--;
                                a_nodesBuffer [i_nodeIndex] = nodeBuffer;
                            }
                        }
                    }
                }


                // Now handle the new object we're adding now.
                i_bestFitChildLocalIndex = CommonMethods._BestFitChild(i_nodeIndex, instanceBounds, a_nodesBuffer);
                i_bestChildIndex         = i_childrenIndexOffset + i_bestFitChildLocalIndex;

                nodeChildrenBuffer = a_nodeChildrenBuffer [i_bestChildIndex];

                if (CommonMethods._Encapsulates(nodeChildrenBuffer.bounds, instanceBounds))
                {
                    _NodeInstanceSubAdd(
                        ref rootNodeData,
                        nodeChildrenBuffer.i_nodesIndex,
                        i_instanceID,
                        i_entityVersion,
                        instanceBounds,
                        ref a_nodesBuffer,
                        ref a_nodeSparesBuffer,
                        ref a_nodeChildrenBuffer,
                        ref a_nodeInstancesIndexBuffer,
                        ref a_instanceBuffer,
                        ref a_instancesSpareIndexBuffer,
                        i_requiredNumberOfInstances
                        );
                }
                else
                {
                    _AssingInstance2Node(rootNodeData, i_nodeIndex, i_instanceID, i_entityVersion, instanceBounds, ref a_nodesBuffer, ref a_instanceBuffer, ref a_nodeInstancesIndexBuffer, a_instancesSpareIndexBuffer);

                    if (rootNodeData.i_instancesSpareLastIndex == 0 || i_requiredNumberOfInstances > a_instanceBuffer.Length)
                    {
                        // Add some spares if needed.
                        CommonMethods._AddInstanceSpares(ref rootNodeData, ref a_instanceBuffer, ref a_instancesSpareIndexBuffer, i_requiredNumberOfInstances);
                    }
                    else
                    {
                        rootNodeData.i_instancesSpareLastIndex--;
                    }

/*
 * // Debugging
 * Debug.Log ( "Instance: New game object #" + i_instanceID.ToString () ) ;
 *
 * GameObject newGameObject = GameObject.Instantiate ( GameObject.Find ( "TempInstance" ), instanceBounds.center, Quaternion.identity ) ;
 * newGameObject.transform.localScale = instanceBounds.size ;
 * newGameObject.name = i_instanceID.ToString () ;
 */
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Check if the specified bounds intersect with anything in the tree. See also: GetColliding.
        /// </summary>
        /// <param name="i_nodeIndex">Internal octree node index.</param>
        /// <param name="checkBounds">Bounds to check.</param>
        /// <returns>True if there was a collision.</returns>
        static public bool _IsNodeColliding(RootNodeData rootNodeData, int i_nodeIndex, Bounds checkBounds, ref IsCollidingData isCollidingData, DynamicBuffer <NodeBufferElement> a_nodesBuffer, DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, DynamicBuffer <InstanceBufferElement> a_instanceBuffer)
        {
            NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex];

            // Are the input bounds at least partially in this node?
            if (!nodeBuffer.bounds.Intersects(checkBounds))
            {
                return(false);
            }


            if (nodeBuffer.i_instancesCount >= 0)
            {
                int i_nodeInstancesIndexOffset = i_nodeIndex * rootNodeData.i_instancesAllowedCount;

                // Check against any objects in this node
                for (int i = 0; i < rootNodeData.i_instancesAllowedCount; i++)
                {
                    NodeInstancesIndexBufferElement nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_nodeInstancesIndexOffset + i];

                    // Get index of instance
                    int i_instanceIndex = nodeInstancesIndexBuffer.i;

                    // Check if instance exists, and if has intersecting bounds.
                    if (i_instanceIndex >= 0)
                    {
                        InstanceBufferElement instanceBuffer = a_instanceBuffer [i_instanceIndex];

                        // Check if instance exists, and if has intersecting bounds.
                        if (instanceBuffer.bounds.Intersects(checkBounds))
                        {
                            isCollidingData.i_collisionsCount = 1;
                            return(true);
                        }
                    }
                }
            }

            // Check children for collisions
            // Check if having children
            if (nodeBuffer.i_childrenCount > 0)
            {
                int i_nodeChildrenIndexOffset = i_nodeIndex * 8;

                // We checked that is having children.
                for (int i = 0; i < 8; i++)
                {
                    NodeChildrenBufferElement nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i];
                    int i_nodeChildIndex = nodeChildrenBuffer.i_nodesIndex;

                    // Check if node exists
                    if (i_nodeChildIndex >= 0)
                    {
                        if (_IsNodeColliding(rootNodeData, i_nodeChildIndex, checkBounds, ref isCollidingData, a_nodesBuffer, a_nodeChildrenBuffer, a_nodeInstancesIndexBuffer, a_instanceBuffer))
                        {
                            isCollidingData.i_collisionsCount = 1;
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// Check if the specified ray intersects with anything in the tree. See also: GetColliding.
        /// </summary>
        /// <param name="i_nodeIndex">Internal octree node index.</param>
        /// <param name="checkRay">Ray to check.</param>
        /// <param name="f_maxDistance">Distance to check.</param>
        /// <returns>True if there was a collision.</returns>
        static public bool _IsNodeColliding(RootNodeData rootNodeData, int i_nodeIndex, Ray checkRay, ref IsCollidingData isCollidingData, DynamicBuffer <NodeBufferElement> a_nodesBuffer, DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, DynamicBuffer <InstanceBufferElement> a_instanceBuffer, float f_maxDistance = float.PositiveInfinity)
        {
            // Is the input ray at least partially in this node?

            float f_distance;

            NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex];

            if (!nodeBuffer.bounds.IntersectRay(checkRay, out f_distance) || f_distance > f_maxDistance)
            {
                return(false);
            }

            if (nodeBuffer.i_instancesCount >= 0)
            {
                int i_nodeInstancesIndexOffset = i_nodeIndex * rootNodeData.i_instancesAllowedCount;

                // Check against any objects in this node
                for (int i = 0; i < rootNodeData.i_instancesAllowedCount; i++)
                {
                    NodeInstancesIndexBufferElement nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_nodeInstancesIndexOffset + i];

                    // Get index of instance
                    int i_instanceIndex = nodeInstancesIndexBuffer.i;

                    // Check if instance exists, and if has intersecting bounds.
                    if (i_instanceIndex >= 0)
                    {
                        InstanceBufferElement instanceBuffer = a_instanceBuffer [i_instanceIndex];

                        if (instanceBuffer.bounds.IntersectRay(checkRay, out f_distance) && f_distance <= f_maxDistance)
                        {
                            isCollidingData.i_collisionsCount = 1;  // Is colliding
                            return(true);
                        }
                    }
                }
            }

            // Check children for collisions
            // Check if having children
            if (nodeBuffer.i_childrenCount > 0)
            {
                int i_nodeChildrenIndexOffset = i_nodeIndex * 8;

                // We checked that is having children.
                for (int i = 0; i < 8; i++)
                {
                    NodeChildrenBufferElement nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i];
                    int i_nodeChildIndex = nodeChildrenBuffer.i_nodesIndex;

                    // Check if node exists
                    if (i_nodeChildIndex >= 0)
                    {
                        if (_IsNodeColliding(rootNodeData, i_nodeChildIndex, checkRay, ref isCollidingData, a_nodesBuffer, a_nodeChildrenBuffer, a_nodeInstancesIndexBuffer, a_instanceBuffer, f_maxDistance))
                        {
                            isCollidingData.i_collisionsCount = 1;  // Is colliding
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// Returns an array of objects that intersect with the specified ray, if any. Otherwise returns an empty array. See also: IsColliding.
        /// </summary>
        /// <param name="i_nodeIndex">Internal octree node index.</param>
        /// <param name="checkRay">Ray to check. Passing by ref as it improves performance with structs.</param>
        /// <param name="l_resultInstanceIDs">List result.</param>
        /// <param name="i_nearestIndex">Nerest collision index from the lits.</param>
        /// <param name="f_nearestDistance">Nerest collision distance.</param>
        /// <param name="maxDistance">Distance to check.</param>
        /// <returns>Instances index, that intersect with the specified ray.</returns>
        static public bool _GetNodeColliding(RootNodeData rootNodeData, int i_nodeIndex, Ray checkRay, ref DynamicBuffer <CollisionInstancesBufferElement> a_collisionInstancesBuffer, ref IsCollidingData isCollidingData, DynamicBuffer <NodeBufferElement> a_nodesBuffer, DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, DynamicBuffer <InstanceBufferElement> a_instanceBuffer, float f_maxDistance = float.PositiveInfinity)
        {
            float f_distance;

            NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex];

            if (!nodeBuffer.bounds.IntersectRay(checkRay, out f_distance) || f_distance > f_maxDistance)
            {
                return(isCollidingData.i_collisionsCount > 0 ? true : false);
            }

            if (nodeBuffer.i_instancesCount > 0)
            {
                int i_nodeInstancesIndexOffset = i_nodeIndex * rootNodeData.i_instancesAllowedCount;

                CollisionInstancesBufferElement collisionInstancesBuffer = new CollisionInstancesBufferElement();

                // Check against any objects in this node
                for (int i = 0; i < rootNodeData.i_instancesAllowedCount; i++)
                {
                    NodeInstancesIndexBufferElement nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_nodeInstancesIndexOffset + i];

                    // Get index of instance
                    int i_instanceIndex = nodeInstancesIndexBuffer.i;

                    // Check if instance exists, and if has intersecting bounds.
                    if (i_instanceIndex >= 0)
                    {
                        InstanceBufferElement instanceBuffer = a_instanceBuffer [i_instanceIndex];


                        if (instanceBuffer.bounds.IntersectRay(checkRay, out f_distance) && f_distance <= f_maxDistance)
                        {
                            if (f_distance < isCollidingData.f_nearestDistance)
                            {
                                isCollidingData.f_nearestDistance = f_distance;
                                isCollidingData.i_nearestInstanceCollisionIndex = isCollidingData.i_collisionsCount;
                            }


                            // Is expected, that the required length is no greater than current length + 1
                            // And is not negative.
                            int i_collisionsCount = isCollidingData.i_collisionsCount;
                            collisionInstancesBuffer.i_ID      = instanceBuffer.i_ID;
                            collisionInstancesBuffer.i_version = instanceBuffer.i_entityVersion;  // Optional, used when Id is used as entity index

                            // Assign colliding instance ID to buffer.
                            if (a_collisionInstancesBuffer.Length <= i_collisionsCount)
                            {
                                // Expand buffer if needed
                                a_collisionInstancesBuffer.Add(collisionInstancesBuffer);
                            }
                            else
                            {
                                a_collisionInstancesBuffer [i_collisionsCount] = collisionInstancesBuffer;
                            }

                            isCollidingData.i_collisionsCount++;
                        }
                    }
                }
            }


            // Check children for collisions
            // Check if having children
            if (nodeBuffer.i_childrenCount > 0)
            {
                int i_nodeChildrenIndexOffset = i_nodeIndex * 8;

                // We checked that is having children.
                for (int i = 0; i < 8; i++)
                {
                    NodeChildrenBufferElement nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i];
                    int i_nodeChildIndex = nodeChildrenBuffer.i_nodesIndex;

                    // Check if node exists
                    if (i_nodeChildIndex >= 0)
                    {
                        _GetNodeColliding(rootNodeData, i_nodeChildIndex, checkRay, ref a_collisionInstancesBuffer, ref isCollidingData, a_nodesBuffer, a_nodeChildrenBuffer, a_nodeInstancesIndexBuffer, a_instanceBuffer, f_maxDistance);
                    }
                }
            }

            return(isCollidingData.i_collisionsCount > 0 ? true : false);
        }