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