bool LevelAABBTree::GenerateTree(const FVector2 *centroids, bool dynamicsubtree) { // Create a list of level lines we want to add: TArray<int> line_elements; auto &maplines = Level->lines; for (unsigned int i = 0; i < maplines.Size(); i++) { if (!maplines[i].backsector) { bool isPolyLine = maplines[i].sidedef[0] && (maplines[i].sidedef[0]->Flags & WALLF_POLYOBJ); if (isPolyLine && dynamicsubtree) { line_elements.Push(mapLines.Size()); mapLines.Push(i); } else if (!isPolyLine && !dynamicsubtree) { line_elements.Push(mapLines.Size()); mapLines.Push(i); } } } if (line_elements.Size() == 0) return false; // GenerateTreeNode needs a buffer where it can store line indices temporarily when sorting lines into the left and right child AABB buckets TArray<int> work_buffer; work_buffer.Resize(line_elements.Size() * 2); // Generate the AABB tree GenerateTreeNode(&line_elements[0], (int)line_elements.Size(), centroids, &work_buffer[0]); return true; }
LevelAABBTree::LevelAABBTree() { // Calculate the center of all lines TArray<FVector2> centroids; for (unsigned int i = 0; i < level.lines.Size(); i++) { FVector2 v1 = { (float)level.lines[i].v1->fX(), (float)level.lines[i].v1->fY() }; FVector2 v2 = { (float)level.lines[i].v2->fX(), (float)level.lines[i].v2->fY() }; centroids.Push((v1 + v2) * 0.5f); } // Create a list of level lines we want to add: TArray<int> line_elements; for (unsigned int i = 0; i < level.lines.Size(); i++) { if (!level.lines[i].backsector) { line_elements.Push(i); } } // GenerateTreeNode needs a buffer where it can store line indices temporarily when sorting lines into the left and right child AABB buckets TArray<int> work_buffer; work_buffer.Resize(line_elements.Size() * 2); // Generate the AABB tree GenerateTreeNode(&line_elements[0], (int)line_elements.Size(), ¢roids[0], &work_buffer[0]); // Add the lines referenced by the leaf nodes lines.Resize(level.lines.Size()); for (unsigned int i = 0; i < level.lines.Size(); i++) { const auto &line = level.lines[i]; auto &treeline = lines[i]; treeline.x = (float)line.v1->fX(); treeline.y = (float)line.v1->fY(); treeline.dx = (float)line.v2->fX() - treeline.x; treeline.dy = (float)line.v2->fY() - treeline.y; } }
int LevelAABBTree::GenerateTreeNode(int *lines, int num_lines, const FVector2 *centroids, int *work_buffer) { if (num_lines == 0) return -1; // Find bounding box and median of the lines FVector2 median = FVector2(0.0f, 0.0f); FVector2 aabb_min, aabb_max; auto &maplines = Level->lines; aabb_min.X = (float)maplines[mapLines[lines[0]]].v1->fX(); aabb_min.Y = (float)maplines[mapLines[lines[0]]].v1->fY(); aabb_max = aabb_min; for (int i = 0; i < num_lines; i++) { float x1 = (float)maplines[mapLines[lines[i]]].v1->fX(); float y1 = (float)maplines[mapLines[lines[i]]].v1->fY(); float x2 = (float)maplines[mapLines[lines[i]]].v2->fX(); float y2 = (float)maplines[mapLines[lines[i]]].v2->fY(); aabb_min.X = MIN(aabb_min.X, x1); aabb_min.X = MIN(aabb_min.X, x2); aabb_min.Y = MIN(aabb_min.Y, y1); aabb_min.Y = MIN(aabb_min.Y, y2); aabb_max.X = MAX(aabb_max.X, x1); aabb_max.X = MAX(aabb_max.X, x2); aabb_max.Y = MAX(aabb_max.Y, y1); aabb_max.Y = MAX(aabb_max.Y, y2); median += centroids[mapLines[lines[i]]]; } median /= (float)num_lines; if (num_lines == 1) // Leaf node { nodes.Push(AABBTreeNode(aabb_min, aabb_max, lines[0])); return (int)nodes.Size() - 1; } // Find the longest axis float axis_lengths[2] = { aabb_max.X - aabb_min.X, aabb_max.Y - aabb_min.Y }; int axis_order[2] = { 0, 1 }; FVector2 axis_plane[2] = { FVector2(1.0f, 0.0f), FVector2(0.0f, 1.0f) }; std::sort(axis_order, axis_order + 2, [&](int a, int b) { return axis_lengths[a] > axis_lengths[b]; }); // Try sort at longest axis, then if that fails then the other one. // We place the sorted lines into work_buffer and then move the result back to the lines list when done. int left_count, right_count; for (int attempt = 0; attempt < 2; attempt++) { // Find the sort plane for axis FVector2 axis = axis_plane[axis_order[attempt]]; FVector3 plane(axis, -(median | axis)); // Sort lines into two based ib whether the line center is on the front or back side of a plane left_count = 0; right_count = 0; for (int i = 0; i < num_lines; i++) { int line_index = lines[i]; float side = FVector3(centroids[mapLines[lines[i]]], 1.0f) | plane; if (side >= 0.0f) { work_buffer[left_count] = line_index; left_count++; } else { work_buffer[num_lines + right_count] = line_index; right_count++; } } if (left_count != 0 && right_count != 0) break; } // Check if something went wrong when sorting and do a random sort instead if (left_count == 0 || right_count == 0) { left_count = num_lines / 2; right_count = num_lines - left_count; } else { // Move result back into lines list: for (int i = 0; i < left_count; i++) lines[i] = work_buffer[i]; for (int i = 0; i < right_count; i++) lines[i + left_count] = work_buffer[num_lines + i]; } // Create child nodes: int left_index = -1; int right_index = -1; if (left_count > 0) left_index = GenerateTreeNode(lines, left_count, centroids, work_buffer); if (right_count > 0) right_index = GenerateTreeNode(lines + left_count, right_count, centroids, work_buffer); // Store resulting node and return its index nodes.Push(AABBTreeNode(aabb_min, aabb_max, left_index, right_index)); return (int)nodes.Size() - 1; }