/// <summary> /// Add an object. /// </summary> /// <param name="i_rootNodeIndex">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> /// <param name="i_entityVersion">Optional, used when Id is used as entity index.</param> /// <param name="instanceBounds">External 3D bounding box around the instance.</param> /// <returns>True if the object fits entirely within this node.</returns> static private bool _AddNodeInstance(ref RootNodeData rootNodeData, int i_instanceID, int i_entityVersion, Bounds instanceBounds, ref DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer, DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, ref DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, ref DynamicBuffer <InstanceBufferElement> a_instanceBuffer, ref DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer) { NodeBufferElement nodeBufferElement = a_nodesBuffer [rootNodeData.i_rootNodeIndex]; if (!CommonMethods._Encapsulates(nodeBufferElement.bounds, instanceBounds)) { return(false); // Early exit } int i_requiredNumberOfInstances = a_nodesBuffer.Length; // l_nodeBounds.Count ; _NodeInstanceSubAdd( ref rootNodeData, rootNodeData.i_rootNodeIndex, 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 ); return(true); }
/// <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> /// Constructor for the bounds octree. /// For minimum size of 1 initial size could be for example 1, 2, 4, 8, 16 ect. /// For minimum size of 3 initial size could be for example 3, 9, 27, 81 etc. /// Also: /// Size 2 per node, with up to 16 instances per node (1x1x1 size each); /// Size 4 per node, with up to 64 instances per node (1x1x1 size each); Less memory usage, but more cpu demanding (for searching and removing elements). /// </summary> /// <param name="f_initialSize">Size of the sides of the initial node, in metres. The octree will never shrink smaller than this.</param> /// <param name="f3_initialPosition">Position of the centre of the initial node.</param> /// <param name="f_minNodeSize">Nodes will stop splitting if the new nodes would be smaller than this (metres).</param> /// <param name="f_looseness">Clamped between 1 and 2. Values > 1 let nodes overlap.</param> static public void _CreateNewOctree(EntityCommandBuffer ecb, Entity newOctreeEntity, float f_initialSize, float3 f3_initialPosition, float f_minNodeSize, float f_looseness) { Debug.Log("Create new octree #" + newOctreeEntity.Index); ecb.AddComponent(newOctreeEntity, new AddNewOctreeData() { f3_initialPosition = f3_initialPosition, f_initialSize = f_initialSize }); // This tag is removed, after octree is created. if (f_minNodeSize > f_initialSize) { Debug.LogWarning("Minimum node size must be at least as big as the initial world size. Was: " + f_initialSize + " Adjusted to: " + f_minNodeSize); f_minNodeSize = f_initialSize; } // Minimum size to power 3 // For example, size of 2, gives 8 instances per node int i_instancesAllowedInNodeCount = (int)math.round(f_minNodeSize * f_minNodeSize * f_minNodeSize); RootNodeData rootNodeData = new RootNodeData() { i_rootNodeIndex = 0, f_initialSize = f_initialSize, f_minSize = f_minNodeSize, f_looseness = math.clamp(f_looseness, 1.0f, 2.0f), i_totalInstancesCountInTree = 0, i_instancesSpareLastIndex = 0, i_nodeSpareLastIndex = 0, i_instancesAllowedCount = i_instancesAllowedInNodeCount }; // ***** Core Components ***** // ecb.AddComponent(newOctreeEntity, rootNodeData); // Core of octree structure. // Add buffer arrays ecb.AddBuffer <NodeBufferElement> (newOctreeEntity); ecb.AddBuffer <NodeChildrenBufferElement> (newOctreeEntity); ecb.AddBuffer <NodeInstancesIndexBufferElement> (newOctreeEntity); ecb.AddBuffer <NodeSparesBufferElement> (newOctreeEntity); ecb.AddBuffer <InstanceBufferElement> (newOctreeEntity); ecb.AddBuffer <InstancesSpareIndexBufferElement> (newOctreeEntity); // ***** Instance Optional Components ***** // // ecb.AddBuffer <AddInstanceBufferElement> ( newOctreeEntity ) ; // Once system executed and instances were added, buffer will be deleted. // ecb.AddComponent ( newOctreeEntity, new RemoveInstanceBufferElement () ) ; // Once system executed and instances were removed, tag will be deleted. }
/// <summary> /// Get total octree bounds (boundary box). /// </summary> /// <returns></returns> public Bounds _GetOctreeMaxBounds(RootNodeData rootNodeData, DynamicBuffer <NodeBufferElement> a_nodesBuffer) { NodeBufferElement nodeBuffer = a_nodesBuffer [rootNodeData.i_rootNodeIndex]; // return _GetNodeBounds ( i_rootNodeIndex ) ; return(nodeBuffer.bounds); }
protected override JobHandle OnUpdate(JobHandle inputDeps) { // Debug.LogWarning ( "Col" ) ; Bounds checkBounds = new Bounds() { center = new float3(10, 2, 10), size = new float3(1, 1, 1) * 5 // Total size of boundry }; EntityArray a_entities = group.GetEntityArray(); Entity rootNodeEntity = a_entities [0]; ComponentDataArray <RootNodeData> a_rootNodeData = group.GetComponentDataArray <RootNodeData> ( ); RootNodeData rootNodeData = a_rootNodeData [0]; BufferFromEntity <NodeBufferElement> nodeBufferElement = GetBufferFromEntity <NodeBufferElement> (); DynamicBuffer <NodeBufferElement> a_nodesBuffer = nodeBufferElement [rootNodeEntity]; Bounds maxBouds = _GetOctreeMaxBounds(rootNodeData, a_nodesBuffer); return(base.OnUpdate(inputDeps)); }
public void Execute(int i_arrayIndex) { Entity octreeRootNodeEntity = a_octreeEntities [i_arrayIndex]; DynamicBuffer <RemoveInstanceBufferElement> a_removeInstanceBufferElement = removeInstanceBufferElement [octreeRootNodeEntity]; RootNodeData rootNodeData = a_rootNodeData [octreeRootNodeEntity]; DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer = nodeSparesBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeBufferElement> a_nodesBuffer = nodeBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer = nodeInstancesIndexBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer = nodeChildrenBufferElement [octreeRootNodeEntity]; DynamicBuffer <InstanceBufferElement> a_instanceBuffer = instanceBufferElement [octreeRootNodeEntity]; DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer = instancesSpareIndexBufferElement [octreeRootNodeEntity]; // Iterate through number of instances to add, from the buffer for (int i = 0; i < a_removeInstanceBufferElement.Length; i++) { // RootNodeData rootNodeData = a_rootNodeData [octreeRootNodeEntity] ; RemoveInstanceBufferElement removeInstanceBuffer = a_removeInstanceBufferElement [i]; bool removed = _NodeRemoveInstance( ref rootNodeData, rootNodeData.i_rootNodeIndex, removeInstanceBuffer.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 ); } a_rootNodeData [octreeRootNodeEntity] = rootNodeData; } // for }
/// <summary> /// Node Constructor. /// </summary> static public void _CreateNewNode(ref RootNodeData rootNodeData, int i_nodeIndex, float f_baseLength, float3 f3_center, ref DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer, ref DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, ref DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer) { // Expand storage if needed if (rootNodeData.i_nodeSpareLastIndex <= 9) { int i_baseAddNodeCount = 100; int i_initialSparesCount = a_nodeSparesBuffer.Length + i_baseAddNodeCount - 1; NodeSparesBufferElement nodeSpareBuffer = new NodeSparesBufferElement(); NodeBufferElement nodeBuffer = new NodeBufferElement(); // Populate with default data. for (int i = 0; i < i_baseAddNodeCount; i++) { // Add spares in reversed order, from higher index, to lower index. nodeSpareBuffer.i = -1; a_nodeSparesBuffer.Add(nodeSpareBuffer); rootNodeData.i_nodeSpareLastIndex++; nodeSpareBuffer.i = i_initialSparesCount - i; a_nodeSparesBuffer [rootNodeData.i_nodeSpareLastIndex] = nodeSpareBuffer; nodeBuffer.f_baseLength = -1; nodeBuffer.f_adjLength = -1; nodeBuffer.f3_center = float3.zero; nodeBuffer.bounds = new Bounds(); a_nodesBuffer.Add(nodeBuffer); nodeBuffer.i_childrenCount = 0; nodeBuffer.i_instancesCount = 0; for (int j = 0; j < rootNodeData.i_instancesAllowedCount; j++) { a_nodeInstancesIndexBuffer.Add(new NodeInstancesIndexBufferElement() { i = -1 }); } NodeChildrenBufferElement nodeChildrenBuffer = new NodeChildrenBufferElement(); // Generate 8 new empty children. for (int j = 0; j < 8; j++) { nodeChildrenBuffer.i_nodesIndex = -1; nodeChildrenBuffer.bounds = new Bounds(); a_nodeChildrenBuffer.Add(new NodeChildrenBufferElement()); } } } _SetValues(rootNodeData, i_nodeIndex, f_baseLength, f3_center, ref a_nodesBuffer, ref a_nodeChildrenBuffer); }
public void Execute(int i_arrayIndex) { Entity octreeRootNodeEntity = a_collisionChecksEntities [i_arrayIndex]; // Its value should be 0, if no collision is detected. // And >= 1, if instance collision is detected, or there is more than one collision, // indicating number of collisions. IsCollidingData isCollidingData = a_isCollidingData [octreeRootNodeEntity]; // Stores reference to detected colliding instance. DynamicBuffer <CollisionInstancesBufferElement> a_collisionInstancesBuffer = collisionInstancesBufferElement [octreeRootNodeEntity]; isCollidingData.i_nearestInstanceCollisionIndex = 0; isCollidingData.f_nearestDistance = float.PositiveInfinity; isCollidingData.i_collisionsCount = 0; // Reset colliding instances counter. RootNodeData octreeRootNodeData = a_octreeRootNodeData [octreeRootNodeEntity]; DynamicBuffer <NodeBufferElement> a_nodesBuffer = nodeBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer = nodeInstancesIndexBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer = nodeChildrenBufferElement [octreeRootNodeEntity]; DynamicBuffer <InstanceBufferElement> a_instanceBuffer = instanceBufferElement [octreeRootNodeEntity]; BoundsEntityPair4CollisionData rayEntityPair4CollisionData = a_boundsEntityPair4CollisionData [octreeRootNodeEntity]; // Ray entity pair, for collision checks Entity bounds2CheckEntity = rayEntityPair4CollisionData.bounds2CheckEntity; // Is target octree active if (a_isActiveTag.Exists(bounds2CheckEntity)) { BoundsData checkBounds = a_boundsData [bounds2CheckEntity]; // To even allow instances collision checks, octree must have at least one instance. if (octreeRootNodeData.i_totalInstancesCountInTree > 0) { if (GetCollidingBoundsInstances_Common._GetNodeColliding(octreeRootNodeData, octreeRootNodeData.i_rootNodeIndex, checkBounds.bounds, ref a_collisionInstancesBuffer, ref isCollidingData, a_nodesBuffer, a_nodeChildrenBuffer, a_nodeInstancesIndexBuffer, a_instanceBuffer)) { /* * // Debug * Debug.Log ( "Is colliding." ) ; */ } } } a_isCollidingData [octreeRootNodeEntity] = isCollidingData; // Set back. }
public void Execute(int i_arrayIndex) { Entity octreeRayEntity = a_collisionChecksEntities [i_arrayIndex]; // Its value should be 0, if no collision is detected. // And >= 1, if instance collision is detected, or there is more than one collision, // indicating number of collisions. IsCollidingData isCollidingData = a_isCollidingData [octreeRayEntity]; isCollidingData.i_collisionsCount = 0; // Reset colliding instances counter. // isCollidingData.i_nearestInstanceCollisionIndex = 0 ; // Unused // isCollidingData.f_nearestDistance = float.PositiveInfinity ; // Unused OctreeEntityPair4CollisionData octreeEntityPair4CollisionData = a_octreeEntityPair4CollisionData [octreeRayEntity]; RayData rayData = a_rayData [octreeRayEntity]; RayMaxDistanceData rayMaxDistanceData = a_rayMaxDistanceData [octreeRayEntity]; // Octree entity pair, for collision checks Entity octreeRootNodeEntity = octreeEntityPair4CollisionData.octree2CheckEntity; // Is target octree active if (a_isActiveTag.Exists(octreeRootNodeEntity)) { RootNodeData octreeRootNodeData = a_octreeRootNodeData [octreeRootNodeEntity]; DynamicBuffer <NodeBufferElement> a_nodesBuffer = nodeBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer = nodeInstancesIndexBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer = nodeChildrenBufferElement [octreeRootNodeEntity]; DynamicBuffer <InstanceBufferElement> a_instanceBuffer = instanceBufferElement [octreeRootNodeEntity]; // To even allow instances collision checks, octree must have at least one instance. if (octreeRootNodeData.i_totalInstancesCountInTree > 0) { if (IsRayColliding_Common._IsNodeColliding(octreeRootNodeData, octreeRootNodeData.i_rootNodeIndex, rayData.ray, ref isCollidingData, a_nodesBuffer, a_nodeChildrenBuffer, a_nodeInstancesIndexBuffer, a_instanceBuffer, rayMaxDistanceData.f)) { /* * // Debug * Debug.Log ( "Is colliding." ) ; */ } } } a_isCollidingData [octreeRayEntity] = isCollidingData; // Set back. }
/// <summary> /// Set values for this node. /// </summary> /// <param name="f_baseLength">Length of this node, not taking looseness into account.</param> /// <param name="f_minSize">Minimum size of nodes in this octree.</param> /// <param name="f_looseness">Multiplier for baseLengthVal to get the actual size.</param> /// <param name="f3_center">Centre position of this node.</param> static public void _SetValues(RootNodeData rootNodeData, int i_nodeIndex, float f_baseLength, float3 f3_center, ref DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer) { NodeBufferElement nodeBuffer = new NodeBufferElement(); nodeBuffer.f_baseLength = f_baseLength; nodeBuffer.f3_center = f3_center; float f_adjustLength = rootNodeData.f_looseness * f_baseLength; nodeBuffer.f_adjLength = f_adjustLength; float3 size = new float3(f_adjustLength, f_adjustLength, f_adjustLength); // Create the bounding box. nodeBuffer.bounds = new Bounds(f3_center, size); a_nodesBuffer [i_nodeIndex] = nodeBuffer; float f_quarter = f_baseLength / 4f; float f_childActualLength = (f_baseLength / 2) * rootNodeData.f_looseness; float3 f3_childActualSize = new float3(f_childActualLength, f_childActualLength, f_childActualLength); int i_childrenIndexOffset = i_nodeIndex * 8; NodeChildrenBufferElement nodeChildrenBuffer; // Set 8 children for this node nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset]; // Get nodeChildrenBuffer.bounds = new Bounds(f3_center + new float3(-f_quarter, f_quarter, -f_quarter), f3_childActualSize); a_nodeChildrenBuffer [i_childrenIndexOffset] = nodeChildrenBuffer; // Set back nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 1]; // Get nodeChildrenBuffer.bounds = new Bounds(f3_center + new float3(f_quarter, f_quarter, -f_quarter), f3_childActualSize); a_nodeChildrenBuffer [i_childrenIndexOffset + 1] = nodeChildrenBuffer; // Set back nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 2]; // Get nodeChildrenBuffer.bounds = new Bounds(f3_center + new float3(-f_quarter, f_quarter, f_quarter), f3_childActualSize); a_nodeChildrenBuffer [i_childrenIndexOffset + 2] = nodeChildrenBuffer; // Set back nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 3]; // Get nodeChildrenBuffer.bounds = new Bounds(f3_center + new float3(f_quarter, f_quarter, f_quarter), f3_childActualSize); a_nodeChildrenBuffer [i_childrenIndexOffset + 3] = nodeChildrenBuffer; // Set back nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 4]; // Get nodeChildrenBuffer.bounds = new Bounds(f3_center + new float3(-f_quarter, -f_quarter, -f_quarter), f3_childActualSize); a_nodeChildrenBuffer [i_childrenIndexOffset + 4] = nodeChildrenBuffer; // Set back nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 5]; // Get nodeChildrenBuffer.bounds = new Bounds(f3_center + new float3(f_quarter, -f_quarter, -f_quarter), f3_childActualSize); a_nodeChildrenBuffer [i_childrenIndexOffset + 5] = nodeChildrenBuffer; // Set back nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 6]; // Get nodeChildrenBuffer.bounds = new Bounds(f3_center + new float3(-f_quarter, -f_quarter, f_quarter), f3_childActualSize); a_nodeChildrenBuffer [i_childrenIndexOffset + 6] = nodeChildrenBuffer; // Set back nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 7]; // Get nodeChildrenBuffer.bounds = new Bounds(f3_center + new float3(f_quarter, -f_quarter, f_quarter), f3_childActualSize); a_nodeChildrenBuffer [i_childrenIndexOffset + 7] = nodeChildrenBuffer; // Set back }
/// <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> /// Checks if there are few enough objects in this node and its children that the children should all be merged into this. /// </summary> /// <param name="i_nodeIndex">Internal octree node index.</param> /// <returns>True there are less or the same abount of objects in this and its children than numObjectsAllowed.</returns> static private bool _ShouldMerge(ref RootNodeData rootNodeData, int i_nodeIndex, DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer) { NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex]; int i_totalInstancesCount = nodeBuffer.i_instancesCount; int i_nodeChildrenIndexOffset = i_nodeIndex * 8; int i_childrenCount = nodeBuffer.i_childrenCount; // Has children? if (i_childrenCount > 0) { for (int i = 0; i < 8; i++) { NodeChildrenBufferElement nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i]; int i_childNodeIndex = nodeChildrenBuffer.i_nodesIndex; if (i_childNodeIndex >= 0) { nodeBuffer = a_nodesBuffer [i_childNodeIndex]; int i_nodefChildChildrenCount = nodeBuffer.i_childrenCount; if (i_nodefChildChildrenCount > 0) { // If any of the *children* have children, there are definitely too many to merge, // or the child would have been merged already return(false); } i_totalInstancesCount += nodeBuffer.i_instancesCount; i_childrenCount--; if (i_childrenCount == 0) { break; } } } } return(i_totalInstancesCount <= rootNodeData.i_instancesAllowedCount); }
/// <summary> /// Assign instance to node. /// </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> /// // Optional, used when Id is used as entity index /// <param name="instanceBounds">Boundary of external instance index.</param> static private void _AssingInstance2Node(RootNodeData rootNodeData, int i_nodeIndex, int i_instanceID, int i_entityVersion, Bounds instanceBounds, ref DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <InstanceBufferElement> a_instanceBuffer, ref DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer) { int i_nodeInstanceIndexOffset = i_nodeIndex * rootNodeData.i_instancesAllowedCount; // Reuse spare store InstancesSpareIndexBufferElement instancesSpareIndexBuffer = a_instancesSpareIndexBuffer [rootNodeData.i_instancesSpareLastIndex]; NodeInstancesIndexBufferElement nodeInstancesIndexBuffer; // Find next spare instance allocation for this node. for (int i = 0; i < rootNodeData.i_instancesAllowedCount; i++) { int i_instanceIndexOffset = i_nodeInstanceIndexOffset + i; nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_instanceIndexOffset]; // Is spare. if (nodeInstancesIndexBuffer.i == -1) { // Assign instance index. nodeInstancesIndexBuffer.i = instancesSpareIndexBuffer.i; a_nodeInstancesIndexBuffer [i_instanceIndexOffset] = nodeInstancesIndexBuffer; // Set back. break; } } NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex]; nodeBuffer.i_instancesCount++; a_nodesBuffer [i_nodeIndex] = nodeBuffer; // Set back. InstanceBufferElement instanceBuffer = new InstanceBufferElement() { i_ID = i_instanceID, i_entityVersion = i_entityVersion, // Optional, used when Id is used as entity index. bounds = instanceBounds }; a_instanceBuffer [instancesSpareIndexBuffer.i] = instanceBuffer; }
public void Execute(int i_arrayIndex) { Entity octreeRootNodeEntity = a_newOctreeEntities [i_arrayIndex]; AddNewOctreeData addNewOctreeData = a_addNewOctreeData [octreeRootNodeEntity]; RootNodeData rootNodeData = a_rootNodeData [octreeRootNodeEntity]; rootNodeData.i_nodeSpareLastIndex--; DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer = nodeSparesBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeBufferElement> a_nodesBuffer = nodeBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer = nodeInstancesIndexBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer = nodeChildrenBufferElement [octreeRootNodeEntity]; CommonMethods._CreateNewNode(ref rootNodeData, rootNodeData.i_rootNodeIndex, addNewOctreeData.f_initialSize, addNewOctreeData.f3_initialPosition, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); rootNodeData.i_nodeSpareLastIndex--; rootNodeData.i_instancesSpareLastIndex = 0; DynamicBuffer <InstanceBufferElement> a_instanceBuffer = instanceBufferElement [octreeRootNodeEntity]; DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer = instancesSpareIndexBufferElement [octreeRootNodeEntity]; // Add some spares if needed. //int i_requiredNumberOfInstances = CommonMethods.numOfSpareInstances2Add ; //int i_requiredNumberOfInstances = Octree.Examples.ExampleSelector.i_generateInstanceInOctreeCount ; int i_requiredNumberOfSpareInstances = 100; CommonMethods._AddInstanceSpares(ref rootNodeData, ref a_instanceBuffer, ref a_instancesSpareIndexBuffer, i_requiredNumberOfSpareInstances); a_rootNodeData [octreeRootNodeEntity] = rootNodeData; // Set back }
/// <summary> /// Add required new spare instances. /// </summary> static public void _AddInstanceSpares(ref RootNodeData rootNodeData, ref DynamicBuffer <InstanceBufferElement> a_instanceBuffer, ref DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer, int i_requiredNumberOfInstances) { rootNodeData.i_instancesSpareLastIndex--; int i_initialSparesCount = a_instanceBuffer.Length; int i_spareInstances2AddCount = i_requiredNumberOfInstances - i_initialSparesCount; InstancesSpareIndexBufferElement instancesSpareIndexBuffer = new InstancesSpareIndexBufferElement(); instancesSpareIndexBuffer.i = -1; InstanceBufferElement instanceBuffer = new InstanceBufferElement(); instanceBuffer.bounds = new Bounds(); instanceBuffer.i_ID = -1; instanceBuffer.i_entityVersion = -1; // Optional, used when Id is used as entity index // Add new spares, from the end of storage. for (int i = 0; i < i_spareInstances2AddCount; i++) { // Need to expand spare store. a_instancesSpareIndexBuffer.Add(instancesSpareIndexBuffer); a_instanceBuffer.Add(instanceBuffer); } // Populate indexes references, with new spares. for (int i = 0; i < i_spareInstances2AddCount; i++) { rootNodeData.i_instancesSpareLastIndex++; // Add new spares. // Add spares in reversed order, from higher index, to lower index. instancesSpareIndexBuffer.i = i_initialSparesCount + i_spareInstances2AddCount - i - 1; a_instancesSpareIndexBuffer [rootNodeData.i_instancesSpareLastIndex] = instancesSpareIndexBuffer; } }
/// <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); }
public void Execute(int i_arrayIndex) { Entity octreeRootNodeEntity = a_collisionChecksEntities [i_arrayIndex]; // Its value should be 0, if no collision is detected. // And >= 1, if instance collision is detected, or there is more than one collision, // indicating number of collisions. IsCollidingData isCollidingData = a_isCollidingData [octreeRootNodeEntity]; // Stores reference to detected colliding instance. DynamicBuffer <CollisionInstancesBufferElement> a_collisionInstancesBuffer = collisionInstancesBufferElement [octreeRootNodeEntity]; isCollidingData.i_nearestInstanceCollisionIndex = 0; isCollidingData.f_nearestDistance = float.PositiveInfinity; isCollidingData.i_collisionsCount = 0; // Reset colliding instances counter. RootNodeData octreeRootNodeData = a_octreeRootNodeData [octreeRootNodeEntity]; DynamicBuffer <NodeBufferElement> a_nodesBuffer = nodeBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer = nodeInstancesIndexBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer = nodeChildrenBufferElement [octreeRootNodeEntity]; DynamicBuffer <InstanceBufferElement> a_instanceBuffer = instanceBufferElement [octreeRootNodeEntity]; RayEntityPair4CollisionData rayEntityPair4CollisionData = a_rayEntityPair4CollisionData [octreeRootNodeEntity]; // Ray entity pair, for collision checks Entity ray2CheckEntity = rayEntityPair4CollisionData.ray2CheckEntity; // Is target octree active if (a_isActiveTag.Exists(ray2CheckEntity)) { RayData rayData = a_rayData [ray2CheckEntity]; RayMaxDistanceData rayMaxDistanceData = a_rayMaxDistanceData [ray2CheckEntity]; // To even allow instances collision checks, octree must have at least one instance. if (octreeRootNodeData.i_totalInstancesCountInTree > 0) { if (GetCollidingRayInstances_Common._GetNodeColliding(octreeRootNodeData, octreeRootNodeData.i_rootNodeIndex, rayData.ray, ref a_collisionInstancesBuffer, ref isCollidingData, a_nodesBuffer, a_nodeChildrenBuffer, a_nodeInstancesIndexBuffer, a_instanceBuffer, rayMaxDistanceData.f)) { /* * // Debug * string s_collidingIDs = "" ; * int i_collisionsCount = isCollidingData.i_collisionsCount ; * * for ( int i = 0; i < i_collisionsCount; i ++ ) * { * CollisionInstancesBufferElement collisionInstancesBuffer = a_collisionInstancesBuffer [i] ; * s_collidingIDs += collisionInstancesBuffer.i_ID + ", " ; * } * * Debug.Log ( "Is colliding with #" + i_collisionsCount + " instances of IDs: " + s_collidingIDs + "; Nearest collided instance is at " + isCollidingData.f_nearestDistance + "m, with index #" + isCollidingData.i_nearestInstanceCollisionIndex ) ; */ } } } a_isCollidingData [octreeRootNodeEntity] = isCollidingData; // Set back. }
/// <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> /// 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> /// 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> /// Grow the octree to fit in all objects. /// </summary> /// <param name="f3_direction">Direction to grow.</param> static private void _GrowOctree(ref RootNodeData rootNodeData, float3 f3_direction, 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 xDirection = f3_direction.x >= 0 ? 1 : -1; int yDirection = f3_direction.y >= 0 ? 1 : -1; int zDirection = f3_direction.z >= 0 ? 1 : -1; int i_oldRootNodeIndex = rootNodeData.i_rootNodeIndex; NodeBufferElement nodeBufferElement = a_nodesBuffer [i_oldRootNodeIndex]; float f_baseLength = nodeBufferElement.f_baseLength; float f_half = f_baseLength / 2; float f_newBaseLength = f_baseLength * 2; float3 f3_newCenter = nodeBufferElement.f3_center + new float3(xDirection * f_half, yDirection * f_half, zDirection * f_half); // Create a new, bigger octree root node if (!CommonMethods._HasAnyInstances(i_oldRootNodeIndex, a_nodesBuffer, a_nodeChildrenBuffer)) { CommonMethods._CreateNewNode( ref rootNodeData, rootNodeData.i_rootNodeIndex, f_newBaseLength, f3_newCenter, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer ); } else { NodeSparesBufferElement nodeSparesBuffer = a_nodeSparesBuffer [rootNodeData.i_nodeSpareLastIndex]; rootNodeData.i_rootNodeIndex = nodeSparesBuffer.i; rootNodeData.i_nodeSpareLastIndex--; CommonMethods._CreateNewNode( ref rootNodeData, rootNodeData.i_rootNodeIndex, f_newBaseLength, f3_newCenter, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer ); // Create 7 new octree children to go with the old root as children of the new root int i_rootPos = _GetRootPosIndex(xDirection, yDirection, zDirection); NodeBufferElement nodeBuffer = a_nodesBuffer [rootNodeData.i_rootNodeIndex]; nodeBuffer.i_childrenCount = 8; a_nodesBuffer [rootNodeData.i_rootNodeIndex] = nodeBuffer; // Set back. int i_newRootNodeChildrenIndexOffset = rootNodeData.i_rootNodeIndex * 8; for (int i = 0; i < 8; i++) { int i_childIndexOffset = i_newRootNodeChildrenIndexOffset + i; NodeChildrenBufferElement nodeChildrenBuffer = a_nodeChildrenBuffer [i_childIndexOffset]; if (i == i_rootPos) { // Assign old root node as a child. nodeChildrenBuffer.i_nodesIndex = i_oldRootNodeIndex; InstanceBufferElement instanceBuffer = a_instanceBuffer [i_oldRootNodeIndex]; nodeChildrenBuffer.bounds = instanceBuffer.bounds; } else { // Assign rest 7 children xDirection = i % 2 == 0 ? -1 : 1; yDirection = i > 3 ? -1 : 1; zDirection = (i < 2 || (i > 3 && i < 6)) ? -1 : 1; InstancesSpareIndexBufferElement instancesSpareIndexBuffer; instancesSpareIndexBuffer = a_instancesSpareIndexBuffer [rootNodeData.i_nodeSpareLastIndex]; int i_newNodeIndex = instancesSpareIndexBuffer.i; // Expected output 0 at initialization rootNodeData.i_nodeSpareLastIndex--; float3 f3_childVector = f3_newCenter + new float3(xDirection * f_half, yDirection * f_half, zDirection * f_half); CommonMethods._CreateNewNode( ref rootNodeData, i_newNodeIndex, f_newBaseLength, f3_childVector, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer ); nodeChildrenBuffer.i_nodesIndex = i_newNodeIndex; Bounds bounds = new Bounds( ) { center = f3_childVector, size = Vector3.one * f_newBaseLength }; nodeChildrenBuffer.bounds = bounds; } a_nodeChildrenBuffer [i_childIndexOffset] = nodeChildrenBuffer; // Set back. } // for } }
/// <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> /// 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); }
/// <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); }
public void Execute(int i_arrayIndex) { Entity octreeRootNodeEntity = a_octreeEntities [i_arrayIndex]; DynamicBuffer <AddInstanceBufferElement> a_addInstanceBufferElement = addInstanceBufferElement [octreeRootNodeEntity]; // RootNodeData rootNodeData = a_rootNodeData [octreeRootNodeEntity] ; DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer = nodeSparesBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeBufferElement> a_nodesBuffer = nodeBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer = nodeInstancesIndexBufferElement [octreeRootNodeEntity]; DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer = nodeChildrenBufferElement [octreeRootNodeEntity]; DynamicBuffer <InstanceBufferElement> a_instanceBuffer = instanceBufferElement [octreeRootNodeEntity]; DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer = instancesSpareIndexBufferElement [octreeRootNodeEntity]; /// <summary> /// Add an Instance. /// </summary> /// <param name="i_instanceID">External instance to add.</param> /// <param name="instanceBounds">External instance 3D bounding box around the instance.</param> //public void _OctreeAddInstance ( ref RootNodeData rootNodeData, int i_instanceID, 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 ) //{ // Iterate through number of instances to add, from the buffer for (int i = 0; i < a_addInstanceBufferElement.Length; i++) { RootNodeData rootNodeData = a_rootNodeData [octreeRootNodeEntity]; AddInstanceBufferElement addInstanceBuffer = a_addInstanceBufferElement [i]; // Add object or expand the octree until it can be added int i_count = 0; // Safety check against infinite/excessive growth while ( !_AddNodeInstance(ref rootNodeData, addInstanceBuffer.i_instanceID, addInstanceBuffer.i_version, addInstanceBuffer.instanceBounds, ref a_nodesBuffer, ref a_nodeSparesBuffer, a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer, ref a_instanceBuffer, ref a_instancesSpareIndexBuffer ) ) { NodeBufferElement nodeBufferElement = a_nodesBuffer [rootNodeData.i_rootNodeIndex]; _GrowOctree(ref rootNodeData, (float3)addInstanceBuffer.instanceBounds.center - nodeBufferElement.f3_center, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer, ref a_instanceBuffer, ref a_instancesSpareIndexBuffer ); if (++i_count > 20) { // Debug.LogError("Aborted Add operation as it seemed to be going on forever (" + (i_count - 1) + ") attempts at growing the octree."); return; } } rootNodeData.i_totalInstancesCountInTree++; a_rootNodeData [octreeRootNodeEntity] = rootNodeData; } }
/// <summary> /// Merge all children into this node - the opposite of Split. /// Note: We only have to check one level down since a merge will never happen if the children already have children, /// since THAT won't happen unless there are already too many objects to merge. /// </summary> /// <param name="i_nodeIndex">Internal octree node index.</param> static private void _MergeNodes(ref RootNodeData rootNodeData, int i_nodeIndex, ref DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer, DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, ref DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer) { NodeBufferElement nodeBuffer; NodeChildrenBufferElement nodeChildrenBuffer; NodeInstancesIndexBufferElement nodeInstancesIndexBuffer; int i_nodeChildrenIndexOffset = i_nodeIndex * 8; int i_nodeUnusedInstancesIndexOffset = i_nodeIndex * rootNodeData.i_instancesAllowedCount; // Note: We know children != null or we wouldn't be merging for (int i = 0; i < 8; i++) { nodeChildrenBuffer = a_nodeChildrenBuffer [i_nodeChildrenIndexOffset + i]; int i_childNodeIndex = nodeChildrenBuffer.i_nodesIndex; if (i_childNodeIndex >= 0) { nodeBuffer = a_nodesBuffer [i_childNodeIndex]; int i_childNodeInstanceCount = nodeBuffer.i_instancesCount; if (i_childNodeInstanceCount > 0) { int i_childModeInstancesIndexOffset = i_childNodeIndex * rootNodeData.i_instancesAllowedCount; for (int i_unusedInstance = 0; i_unusedInstance < rootNodeData.i_instancesAllowedCount; i_unusedInstance++) { int i_unusedInstanceIndexOffset = i_nodeUnusedInstancesIndexOffset + i_unusedInstance; nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_unusedInstanceIndexOffset]; if (nodeInstancesIndexBuffer.i == -1) { // Iterate through number of children instances. for (int j = rootNodeData.i_instancesAllowedCount - 1; j >= 0; j--) { // Store old instance index nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_childModeInstancesIndexOffset + j]; int i_childInstanceIndex = nodeInstancesIndexBuffer.i; // If node instance exists (none negative), assign to node if (i_childInstanceIndex >= 0) { // Reassign instance index, to next available spare index. nodeInstancesIndexBuffer.i = i_childInstanceIndex; a_nodeInstancesIndexBuffer [i_unusedInstanceIndexOffset] = nodeInstancesIndexBuffer; // Set back nodeBuffer = a_nodesBuffer [i_nodeIndex]; nodeBuffer.i_instancesCount++; a_nodesBuffer [i_nodeIndex] = nodeBuffer; // Set back nodeInstancesIndexBuffer.i = -1; // Reset a_nodeInstancesIndexBuffer [i_childModeInstancesIndexOffset + j] = nodeInstancesIndexBuffer; // Set back nodeBuffer = a_nodesBuffer [i_childNodeIndex]; nodeBuffer.i_instancesCount--; a_nodesBuffer [i_childNodeIndex] = nodeBuffer; // Set back i_childNodeInstanceCount--; } } // for } } // for } } } // for // Reset children // Remove the child nodes (and the objects in them - they've been added elsewhere now) for (int i = 0; i < 8; i++) { int i_childNodeIndexOffset = i_nodeChildrenIndexOffset + i; nodeChildrenBuffer = a_nodeChildrenBuffer [i_childNodeIndexOffset]; int i_childNodeIndex = nodeChildrenBuffer.i_nodesIndex; if (i_childNodeIndex >= 0) { // Get child node ; nodeBuffer = a_nodesBuffer [i_childNodeIndex]; // Iterate though node children. if (nodeBuffer.i_childrenCount > 0) { // Reset node children node index reference. for (int j = 0; j < 8; j++) { // Reset child nodeChildrenBuffer.i_nodesIndex = -1; // Bounds are ignored a_nodeChildrenBuffer [i_childNodeIndex + j] = nodeChildrenBuffer; // Set back } nodeBuffer.i_instancesCount = 0; // Reset a_nodesBuffer [i_childNodeIndex] = nodeBuffer; // Set back // Put back node instances to spare instance. for (int j = 0; j < rootNodeData.i_instancesAllowedCount; j++) { nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_childNodeIndex]; // Set back int i_instanceIndex = nodeInstancesIndexBuffer.i; // Remove from here CommonMethods._PutBackSpareInstance(ref rootNodeData, i_instanceIndex + j, i_nodeIndex, ref a_nodeInstancesIndexBuffer, ref a_instancesSpareIndexBuffer); } } // Pu back child nodes to spares rootNodeData.i_nodeSpareLastIndex++; a_nodeSparesBuffer [rootNodeData.i_nodeSpareLastIndex] = new NodeSparesBufferElement() { i = i_childNodeIndex }; // Reset child nodeChildrenBuffer.i_nodesIndex = -1; // Bounds are ignored a_nodeChildrenBuffer [i_childNodeIndexOffset] = nodeChildrenBuffer; // Set back } } nodeBuffer = a_nodesBuffer [i_nodeIndex]; nodeBuffer.i_childrenCount = 0; a_nodesBuffer [i_nodeIndex] = nodeBuffer; // Set back }
/// <summary> /// Assign instance to node. /// </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> /// // Optional, used when Id is used as entity index /// <param name="instanceBounds">Boundary of external instance index.</param> static private bool _AssingInstance2Node(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 <InstanceBufferElement> a_instanceBuffer, ref DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer, ref DynamicBuffer <InstancesSpareIndexBufferElement> a_instancesSpareIndexBuffer) { int i_nodeInstanceIndexOffset = i_nodeIndex * rootNodeData.i_instancesAllowedCount; // Reuse spare store InstancesSpareIndexBufferElement instancesSpareIndexBuffer = a_instancesSpareIndexBuffer [rootNodeData.i_instancesSpareLastIndex]; NodeInstancesIndexBufferElement nodeInstancesIndexBuffer; bool isInstanceAdded = false; // Find next spare instance allocation for this node. // Find next spare instance allocation for this node. for (int i = 0; i < rootNodeData.i_instancesAllowedCount; i++) { int i_instanceIndexOffset = i_nodeInstanceIndexOffset + i; nodeInstancesIndexBuffer = a_nodeInstancesIndexBuffer [i_instanceIndexOffset]; // Is spare. if (nodeInstancesIndexBuffer.i == -1) { // Assign instance index. nodeInstancesIndexBuffer.i = instancesSpareIndexBuffer.i; a_nodeInstancesIndexBuffer [i_instanceIndexOffset] = nodeInstancesIndexBuffer; // Set back. isInstanceAdded = true; break; } } // for if (!isInstanceAdded) { // Node has 8 children and an more instances, than capacity. // Try to move instance to child, with instance space. // if ( i_instancesCount < rootNodeData.i_instancesAllowedCount && i_childrenCount > 0 ) // Iterate though node's children for (int i_childIndex = 0; i_childIndex < 8; i_childIndex++) { int i_childIndexOffset = i_nodeIndex * 8 + i_childIndex; NodeChildrenBufferElement nodeChildrenBuffer = a_nodeChildrenBuffer [i_childIndexOffset]; // Check if instance bounds fit in child if (CommonMethods._Encapsulates(nodeChildrenBuffer.bounds, instanceBounds)) { //NodeBufferElement childNode = a_nodesBuffer [nodeChildrenBuffer.i_nodesIndex] ; // Debug.LogWarning ( "ChNode: " + childNode.i_instancesCount + "; " + childNode.bounds ) ; int i_requiredNumberOfInstances = a_nodeInstancesIndexBuffer.Length; // l_nodeBounds.Count ; isInstanceAdded = _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 ); if (isInstanceAdded) { return(true); // Added instance } } } // Debug.LogError ( "Found no child node, to fit instance. Try grow octree." ) ; NodeBufferElement nodeBufferElement = a_nodesBuffer [rootNodeData.i_rootNodeIndex]; // Try add node at the root // Debug.LogError ( "Grow again." ) ; _GrowOctree(ref rootNodeData, (float3)instanceBounds.center - nodeBufferElement.f3_center, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer, ref a_instanceBuffer, ref a_instancesSpareIndexBuffer ); _AddNodeInstance(ref rootNodeData, i_instanceID, i_entityVersion, instanceBounds, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer, ref a_instanceBuffer, ref a_instancesSpareIndexBuffer, out isInstanceAdded ); // Debug.LogWarning ( "Is instant added? " + (isInstanceAdded? "y" : "n") ) ; return(isInstanceAdded); } // if NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex]; nodeBuffer.i_instancesCount++; a_nodesBuffer [i_nodeIndex] = nodeBuffer; // Set back. InstanceBufferElement instanceBuffer = new InstanceBufferElement() { i_ID = i_instanceID, i_entityVersion = i_entityVersion, // Optional, used when Id is used as entity index. bounds = instanceBounds }; a_instanceBuffer [instancesSpareIndexBuffer.i] = instanceBuffer; return(true); }
/// <summary> /// Splits the octree into eight children. /// </summary> /// <param name="i_nodeIndex">Internal octree node index.</param> static private void _Split(ref RootNodeData rootNodeData, int i_nodeIndex, ref DynamicBuffer <NodeBufferElement> a_nodesBuffer, ref DynamicBuffer <NodeSparesBufferElement> a_nodeSparesBuffer, ref DynamicBuffer <NodeChildrenBufferElement> a_nodeChildrenBuffer, ref DynamicBuffer <NodeInstancesIndexBufferElement> a_nodeInstancesIndexBuffer) { NodeBufferElement nodeBuffer = a_nodesBuffer [i_nodeIndex]; float f_nodeBaseLength = nodeBuffer.f_baseLength; float f_quarter = f_nodeBaseLength / 4f; float f_newBaseLength = f_nodeBaseLength / 2; float3 f3_center = nodeBuffer.f3_center; nodeBuffer.i_childrenCount = 8; a_nodesBuffer [i_nodeIndex] = nodeBuffer; int i_childrenIndexOffset = i_nodeIndex * 8; // Create for this node, 8 new children nodes NodeChildrenBufferElement nodeChildrenBuffer; // Allocate spare nodes, to children nodes. // Is assumed, there is enough spare nodes for (int i = 0; i < 8; i++) { NodeSparesBufferElement nodeSparesBuffer = a_nodeSparesBuffer [rootNodeData.i_nodeSpareLastIndex]; nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + i]; nodeChildrenBuffer.i_nodesIndex = nodeSparesBuffer.i; a_nodeChildrenBuffer [i_childrenIndexOffset + i] = nodeChildrenBuffer; // Set back rootNodeData.i_nodeSpareLastIndex--; } float3 f3_childCenterQuater; nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset]; f3_childCenterQuater = f3_center + new float3(-f_quarter, f_quarter, -f_quarter); CommonMethods._CreateNewNode(ref rootNodeData, nodeChildrenBuffer.i_nodesIndex, f_newBaseLength, f3_childCenterQuater, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 1]; f3_childCenterQuater = f3_center + new float3(f_quarter, f_quarter, -f_quarter); CommonMethods._CreateNewNode(ref rootNodeData, nodeChildrenBuffer.i_nodesIndex, f_newBaseLength, f3_childCenterQuater, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 2]; f3_childCenterQuater = f3_center + new float3(-f_quarter, f_quarter, f_quarter); CommonMethods._CreateNewNode(ref rootNodeData, nodeChildrenBuffer.i_nodesIndex, f_newBaseLength, f3_childCenterQuater, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 3]; f3_childCenterQuater = f3_center + new float3(f_quarter, f_quarter, f_quarter); CommonMethods._CreateNewNode(ref rootNodeData, nodeChildrenBuffer.i_nodesIndex, f_newBaseLength, f3_childCenterQuater, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 4]; f3_childCenterQuater = f3_center + new float3(-f_quarter, -f_quarter, -f_quarter); CommonMethods._CreateNewNode(ref rootNodeData, nodeChildrenBuffer.i_nodesIndex, f_newBaseLength, f3_childCenterQuater, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 5]; f3_childCenterQuater = f3_center + new float3(f_quarter, -f_quarter, -f_quarter); CommonMethods._CreateNewNode(ref rootNodeData, nodeChildrenBuffer.i_nodesIndex, f_newBaseLength, f3_childCenterQuater, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 6]; f3_childCenterQuater = f3_center + new float3(-f_quarter, -f_quarter, f_quarter); CommonMethods._CreateNewNode(ref rootNodeData, nodeChildrenBuffer.i_nodesIndex, f_newBaseLength, f3_childCenterQuater, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); nodeChildrenBuffer = a_nodeChildrenBuffer [i_childrenIndexOffset + 7]; f3_childCenterQuater = f3_center + new float3(f_quarter, -f_quarter, f_quarter); CommonMethods._CreateNewNode(ref rootNodeData, nodeChildrenBuffer.i_nodesIndex, f_newBaseLength, f3_childCenterQuater, ref a_nodesBuffer, ref a_nodeSparesBuffer, ref a_nodeChildrenBuffer, ref a_nodeInstancesIndexBuffer); }