+/*
+ MapBlockBspTree
+*/
+
+void MapBlockBspTree::buildTree(const std::vector<MeshTriangle> *triangles)
+{
+ this->triangles = triangles;
+
+ nodes.clear();
+
+ // assert that triangle index can fit into s32
+ assert(triangles->size() <= 0x7FFFFFFFL);
+ std::vector<s32> indexes;
+ indexes.reserve(triangles->size());
+ for (u32 i = 0; i < triangles->size(); i++)
+ indexes.push_back(i);
+
+ root = buildTree(v3f(1, 0, 0), v3f(85, 85, 85), 40, indexes, 0);
+}
+
+/**
+ * @brief Find a candidate plane to split a set of triangles in two
+ *
+ * The candidate plane is represented by one of the triangles from the set.
+ *
+ * @param list Vector of indexes of the triangles in the set
+ * @param triangles Vector of all triangles in the BSP tree
+ * @return Address of the triangle that represents the proposed split plane
+ */
+static const MeshTriangle *findSplitCandidate(const std::vector<s32> &list, const std::vector<MeshTriangle> &triangles)
+{
+ // find the center of the cluster.
+ v3f center(0, 0, 0);
+ size_t n = list.size();
+ for (s32 i : list) {
+ center += triangles[i].centroid / n;
+ }
+
+ // find the triangle with the largest area and closest to the center
+ const MeshTriangle *candidate_triangle = &triangles[list[0]];
+ const MeshTriangle *ith_triangle;
+ for (s32 i : list) {
+ ith_triangle = &triangles[i];
+ if (ith_triangle->areaSQ > candidate_triangle->areaSQ ||
+ (ith_triangle->areaSQ == candidate_triangle->areaSQ &&
+ ith_triangle->centroid.getDistanceFromSQ(center) < candidate_triangle->centroid.getDistanceFromSQ(center))) {
+ candidate_triangle = ith_triangle;
+ }
+ }
+ return candidate_triangle;
+}
+
+s32 MapBlockBspTree::buildTree(v3f normal, v3f origin, float delta, const std::vector<s32> &list, u32 depth)
+{
+ // if the list is empty, don't bother
+ if (list.empty())
+ return -1;
+
+ // if there is only one triangle, or the delta is insanely small, this is a leaf node
+ if (list.size() == 1 || delta < 0.01) {
+ nodes.emplace_back(normal, origin, list, -1, -1);
+ return nodes.size() - 1;
+ }
+
+ std::vector<s32> front_list;
+ std::vector<s32> back_list;
+ std::vector<s32> node_list;
+
+ // split the list
+ for (s32 i : list) {
+ const MeshTriangle &triangle = (*triangles)[i];
+ float factor = normal.dotProduct(triangle.centroid - origin);
+ if (factor == 0)
+ node_list.push_back(i);
+ else if (factor > 0)
+ front_list.push_back(i);
+ else
+ back_list.push_back(i);
+ }
+
+ // define the new split-plane
+ v3f candidate_normal(normal.Z, normal.X, normal.Y);
+ float candidate_delta = delta;
+ if (depth % 3 == 2)
+ candidate_delta /= 2;
+
+ s32 front_index = -1;
+ s32 back_index = -1;
+
+ if (!front_list.empty()) {
+ v3f next_normal = candidate_normal;
+ v3f next_origin = origin + delta * normal;
+ float next_delta = candidate_delta;
+ if (next_delta < 10) {
+ const MeshTriangle *candidate = findSplitCandidate(front_list, *triangles);
+ next_normal = candidate->getNormal();
+ next_origin = candidate->centroid;
+ }
+ front_index = buildTree(next_normal, next_origin, next_delta, front_list, depth + 1);
+
+ // if there are no other triangles, don't create a new node
+ if (back_list.empty() && node_list.empty())
+ return front_index;
+ }
+
+ if (!back_list.empty()) {
+ v3f next_normal = candidate_normal;
+ v3f next_origin = origin - delta * normal;
+ float next_delta = candidate_delta;
+ if (next_delta < 10) {
+ const MeshTriangle *candidate = findSplitCandidate(back_list, *triangles);
+ next_normal = candidate->getNormal();
+ next_origin = candidate->centroid;
+ }
+
+ back_index = buildTree(next_normal, next_origin, next_delta, back_list, depth + 1);
+
+ // if there are no other triangles, don't create a new node
+ if (front_list.empty() && node_list.empty())
+ return back_index;
+ }
+
+ nodes.emplace_back(normal, origin, node_list, front_index, back_index);
+
+ return nodes.size() - 1;
+}
+
+void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector<s32> &output) const
+{
+ if (node < 0) return; // recursion break;
+
+ const TreeNode &n = nodes[node];
+ float factor = n.normal.dotProduct(viewpoint - n.origin);
+
+ if (factor > 0)
+ traverse(n.back_ref, viewpoint, output);
+ else
+ traverse(n.front_ref, viewpoint, output);
+
+ if (factor != 0)
+ for (s32 i : n.triangle_refs)
+ output.push_back(i);
+
+ if (factor > 0)
+ traverse(n.front_ref, viewpoint, output);
+ else
+ traverse(n.back_ref, viewpoint, output);
+}
+
+
+
+/*
+ PartialMeshBuffer
+*/
+
+void PartialMeshBuffer::beforeDraw() const
+{
+ // Patch the indexes in the mesh buffer before draw
+
+ m_buffer->Indices.clear();
+ if (!m_vertex_indexes.empty()) {
+ for (auto index : m_vertex_indexes)
+ m_buffer->Indices.push_back(index);
+ }
+ m_buffer->setDirty(scene::EBT_INDEX);
+}
+