/// <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); }