/// <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 rootNode, 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 * rootNode.i_instancesAllowedCount;


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

            // Check objects in root
            for (int i = 0; i < rootNode.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;
                            }
                        }
                        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_group8NodesIndex, 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(rootNode, 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_group8NodesIndex);
        }
        } // Job


        /*
         * /// <summary>
         *  /// Remove an instance. Makes the assumption that the instance only exists once in the tree.
         *  /// </summary>
         *  /// <param name="i_instanceID">External instance to remove.</param>
         *  /// <returns>True if the object was removed successfully.</returns>
         *  public bool _OctreeRemoveInstance ( ref RootNodeData rootNodeData, int i_instanceID, DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer, ref DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, ref DynamicBuffer <InstanceBufferElement> a_instanceBuffer, ref DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer )
         * {
         *
         *  bool removed = _NodeRemoveInstance (
         *      ref rootNodeData,
         *      rootNodeData.i_rootNodeIndex,
         *      i_instanceID,
         *      ref a_nodesBuffer,
         *      ref a_nodeSparesBuffer,
         *      a_nodeChildrenBuffer,
         *      a_nodeInstancesIndexBuffer,
         *      ref a_instanceBuffer,
         *      ref a_instancesSpareIndexBuffer
         *  );
         *
         *          // See if we can shrink the octree down now that we've removed the item
         *          if ( removed )
         *  {
         *                  rootNodeData.i_totalInstancesCountInTree -- ;
         *
         *      // Shrink if possible.
         *      rootNodeData.i_rootNodeIndex = _ShrinkIfPossible (
         *          rootNodeData,
         *          rootNodeData.i_rootNodeIndex,
         *          rootNodeData.f_initialSize,
         *          a_nodesBuffer,
         *          ref a_nodeChildrenBuffer,
         *          a_nodeInstancesIndexBuffer,
         *          a_instanceBuffer
         *      ) ;
         *          }
         *
         *          return removed ;
         *  }
         */


        /// <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 rootNode, 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 * rootNode.i_instancesAllowedCount;

            if (nodeBuffer.i_instancesCount > 0)
            {
                // Try remove instance from this node
                for (int i = 0; i < rootNode.i_instancesAllowedCount; i++)
                {
                    int i_nodeInstanceIndex = i_nodeInstancesIndexOffset + 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 rootNode, i_existingInstanceIndex, i_nodeInstanceIndex, 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 ;
 *  }
 *
 *     Debug.LogWarning ( "Node: Remove #" + i_nodeIndex ) ;
 *  GameObject.Destroy ( GameObject.Find ( "Node " + i_nodeIndex.ToString () ) ) ;
 */
                            nodeBuffer.i_instancesCount--;
                            instanceBuffer.i_ID = -1;                                    // Reset
                            a_instanceBuffer [i_existingInstanceIndex] = instanceBuffer; // Set back



                            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_group8NodesIndex;

                    // Ignore negative index
                    if (i_childNodeIndex >= 0)
                    {
                        removed = _NodeRemoveInstance(
                            ref rootNode,
                            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 rootNode, i_nodeIndex, a_nodesBuffer, ref a_nodeChildrenBuffer))
                {
                    _MergeNodes(
                        ref rootNode,
                        i_nodeIndex,
                        ref a_nodesBuffer,
                        ref a_nodeSparesBuffer,
                        a_nodeChildrenBuffer,
                        a_nodeInstancesIndexBuffer,
                        ref a_instancesSpareIndexBuffer
                        );
                }
            }

            return(removed);
        }