public void ResetLinkedVoxelSpans() { int len = width * depth; LinkedVoxelSpan df = new LinkedVoxelSpan(InvalidSpanValue, InvalidSpanValue, -1, -1); linkedSpans.ResizeUninitialized(len); linkedCellMinMax.Resize(len, NativeArrayOptions.UninitializedMemory); for (int i = 0; i < len; i++) { linkedSpans[i] = df; linkedCellMinMax[i] = new CellMinMax { objectID = -1, min = 0, max = 0, }; } removedStack.Clear(); }
public void AddLinkedSpan(int index, uint bottom, uint top, int area, int voxelWalkableClimb, int objectID) { var minmax = linkedCellMinMax[index]; if (minmax.objectID != objectID) { linkedCellMinMax[index] = new CellMinMax { objectID = objectID, min = bottom, max = top, }; } else { minmax.min = math.min(minmax.min, bottom); minmax.max = math.max(minmax.max, top); linkedCellMinMax[index] = minmax; } // linkedSpans[index] is the span with the lowest y-coordinate at the position x,z such that index=x+z*width // i.e linkedSpans is a 2D array laid out in a 1D array (for performance and simplicity) // Check if there is a root span, otherwise we can just add a new (valid) span and exit if (linkedSpans[index].bottom == InvalidSpanValue) { linkedSpans[index] = new LinkedVoxelSpan(bottom, top, area); return; } int prev = -1; // Original index, the first span we visited int oindex = index; while (index != -1) { var current = linkedSpans[index]; if (current.bottom > top) { // If the current span's bottom higher up than the span we want to insert's top, then they do not intersect // and we should just insert a new span here break; } else if (current.top < bottom) { // The current span and the span we want to insert do not intersect // so just skip to the next span (it might intersect) prev = index; index = current.next; } else { // Intersection! Merge the spans // voxelWalkableClimb is flagMergeDistance, when a walkable flag is favored before an unwalkable one // So if a walkable span intersects an unwalkable span, the walkable span can be up to voxelWalkableClimb // below the unwalkable span and the merged span will still be walkable if (math.abs((int)top - (int)current.top) <= voxelWalkableClimb) { // linkedSpans[index] is the lowest span, but we might use that span's area anyway if it is walkable area = math.max(area, current.area); } else { // Pick the area from the topmost span if (top < current.top) { area = current.area; } } // Find the new bottom and top for the merged span bottom = math.min(current.bottom, bottom); top = math.max(current.top, top); // Find the next span in the linked list int next = current.next; if (prev != -1) { // There is a previous span // Remove this span from the linked list // TODO: Kinda slow. Check what asm is generated. var p = linkedSpans[prev]; p.next = next; linkedSpans[prev] = p; // Add this span index to a list for recycling PushToSpanRemovedStack(index); // Move to the next span in the list index = next; } else if (next != -1) { // This was the root span and there is a span left in the linked list // Remove this span from the linked list by assigning the next span as the root span linkedSpans[oindex] = linkedSpans[next]; // Recycle the old span index PushToSpanRemovedStack(next); // Move to the next span in the list // NOP since we just removed the current span, the next span // we want to visit will have the same index as we are on now (i.e oindex) } else { // This was the root span and there are no other spans in the linked list // Just replace the root span with the merged span and exit linkedSpans[oindex] = new LinkedVoxelSpan(bottom, top, area); return; } } } // We now have a merged span that needs to be inserted // and connected with the existing spans // The new merged span will be inserted right after 'prev' (if it exists, otherwise before index) // Take a node from the recycling stack if possible // Otherwise create a new node (well, just a new index really) int nextIndex; if (removedStack.Length > 0) { // Pop nextIndex = removedStack[removedStack.Length - 1]; removedStack.RemoveAtSwapBack(removedStack.Length - 1); } else { nextIndex = linkedSpans.Length; linkedSpans.Resize(linkedSpans.Length + 1, NativeArrayOptions.UninitializedMemory); } if (prev != -1) { linkedSpans[nextIndex] = new LinkedVoxelSpan(bottom, top, area, linkedSpans[prev].next); // TODO: Check asm var p = linkedSpans[prev]; p.next = nextIndex; linkedSpans[prev] = p; } else { linkedSpans[nextIndex] = linkedSpans[oindex]; linkedSpans[oindex] = new LinkedVoxelSpan(bottom, top, area, nextIndex); } }