diff --git a/bsputil/decompile.cpp b/bsputil/decompile.cpp index 36b5e118..ab45390c 100644 --- a/bsputil/decompile.cpp +++ b/bsputil/decompile.cpp @@ -120,6 +120,7 @@ WriteNullTexdef(fmt::memory_buffer& file) struct decomp_plane_t { const bsp2_dnode_t* node; // can be nullptr + const bsp2_dclipnode_t* clipnode; // can be nullptr bool nodefront; // only set if node is non-null. true = we are visiting the front side of the plane // this should be an outward-facing plane @@ -127,7 +128,7 @@ struct decomp_plane_t { double distance; static decomp_plane_t make(const qvec3d& normalIn, double distanceIn) { - return { nullptr, false, normalIn, distanceIn }; + return { nullptr, nullptr, false, normalIn, distanceIn }; } }; @@ -617,6 +618,11 @@ struct leaf_decompile_task { const mleaf_t *leaf; }; +struct clipleaf_decompile_task { + std::vector allPlanes; + int32_t contents; +}; + /** * Preconditions: * - The existing path of plane side choices have been pushed onto `planestack` @@ -712,13 +718,20 @@ DecompileLeafTask(const mbsp_t *bsp, const leaf_decompile_task& task) /** * @param front whether we are visiting the front side of the node plane */ -decomp_plane_t MakeDecompPlane(const mbsp_t *bsp, const bsp2_dnode_t *node, const bool front) { +static decomp_plane_t MakeDecompPlane(const mbsp_t *bsp, + const bsp2_dnode_t *node, + const bsp2_dclipnode_t *clipnode, + const bool front) +{ + assert(node == nullptr || clipnode == nullptr); + decomp_plane_t result; result.node = node; + result.clipnode = clipnode; result.nodefront = front; - const dplane_t *dplane = BSP_GetPlane(bsp, node->planenum); + const dplane_t *dplane = BSP_GetPlane(bsp, (node != nullptr) ? node->planenum : clipnode->planenum); result.normal = qvec3d(dplane->normal[0], dplane->normal[1], @@ -743,7 +756,7 @@ static void DecompileNode(std::vector* planestack, const mbsp_t *bsp, const bsp2_dnode_t *node, std::vector* result) { auto handleSide = [&](const bool front) { - planestack->push_back(MakeDecompPlane(bsp, node, front)); + planestack->push_back(MakeDecompPlane(bsp, node, nullptr, front)); const int32_t child = node->children[front ? 0 : 1]; @@ -763,8 +776,37 @@ DecompileNode(std::vector* planestack, const mbsp_t *bsp, const handleSide(false); } +/** + * Same as above but for clipnodes + */ +static void +DecompileClipNode(std::vector* planestack, const mbsp_t *bsp, const bsp2_dclipnode_t *clipnode, std::vector* result) +{ + auto handleSide = [&](const bool front) { + planestack->push_back(MakeDecompPlane(bsp, nullptr, clipnode, front)); + + const int32_t child = clipnode->children[front ? 0 : 1]; + + if (child < 0) { + const int32_t contents = child; + + // it's a leaf on this side + result->push_back(clipleaf_decompile_task{*planestack, contents}); + } else { + // it's another clip node - process it recursively + DecompileClipNode(planestack, bsp, BSP_GetClipNode(bsp, child), result); + } + + planestack->pop_back(); + }; + + // handle the front and back + handleSide(true); + handleSide(false); +} + void -AddMapBoundsToStack(std::vector* planestack, const mbsp_t *bsp, const bsp2_dnode_t* headnode) +AddMapBoundsToStack(std::vector* planestack, const mbsp_t *bsp, const dmodelh2_t* model) { for (int i=0; i<3; ++i) { for (int sign=0; sign<2; ++sign) { @@ -775,9 +817,9 @@ AddMapBoundsToStack(std::vector* planestack, const mbsp_t *bsp, double dist; if (sign == 0) { // positive - dist = headnode->maxs[i]; + dist = model->maxs[i]; } else { - dist = -headnode->mins[i]; + dist = -model->mins[i]; } // we want outward-facing planes @@ -786,6 +828,72 @@ AddMapBoundsToStack(std::vector* planestack, const mbsp_t *bsp, } } +static std::vector +ExpandHull1Brush(const std::vector& planes) +{ + std::vector result = planes; + for (auto& plane : result) { + // from LoadBrush + const qvec3d hull_size[2] = { {-16, -16, -32}, {16, 16, 24} }; + + // from ExpandBrush + qvec3d corner; + for (int x = 0; x < 3; x++) { + if (plane.normal[x] > 0) { + corner[x] = hull_size[1][x]; + } else if (plane.normal[x] < 0) { + corner[x] = hull_size[0][x]; + } + } + plane.distance += qv::dot(corner, plane.normal); + } + return result; +} + +static void +SubtractBrush(const std::vector& lhs, + const std::vector& rhs, + std::vector>* results) +{ + +} + + + +static std::string +DecompileHull1(const mbsp_t *bsp, const std::vector& tasks) +{ + // 1. collect the empty leafs + + fmt::memory_buffer file; + + for (auto& task : tasks) { + if (task.contents == CONTENTS_EMPTY) { + auto reducedPlanes = RemoveRedundantPlanes(task.allPlanes); + if (reducedPlanes.empty()) { + printf("warning, skipping empty brush\n"); + continue; + } + + reducedPlanes = ExpandHull1Brush(reducedPlanes); + + // write brush + fmt::format_to(file, "{{\n"); + for (const auto& side : reducedPlanes) { + PrintPlanePoints(bsp, side, file); + + // print a default face + fmt::format_to(file, " {} ", "clip"); + WriteNullTexdef(file); + fmt::format_to(file, "\n"); + } + fmt::format_to(file, "}}\n"); + } + } + + return fmt::to_string(file); +} + static void DecompileEntity(const mbsp_t *bsp, const decomp_options& options, FILE* file, const entdict_t& dict, bool isWorld) { @@ -824,7 +932,7 @@ DecompileEntity(const mbsp_t *bsp, const decomp_options& options, FILE* file, co // recursively visit the nodes to gather up a list of leafs to decompile std::vector stack; std::vector tasks; - AddMapBoundsToStack(&stack, bsp, headnode); + AddMapBoundsToStack(&stack, bsp, model); DecompileNode(&stack, bsp, headnode, &tasks); // decompile the leafs in parallel @@ -842,6 +950,16 @@ DecompileEntity(const mbsp_t *bsp, const decomp_options& options, FILE* file, co for (auto& leafString : leafStrings) { fprintf(file, "%s", leafString.c_str()); } + + // decompile hull1 to clip brushes + { + const bsp2_dclipnode_t *clipnode_head = BSP_GetClipNode(bsp, model->headnode[1]); + std::vector stack1; + std::vector tasks1; + AddMapBoundsToStack(&stack1, bsp, model); + DecompileClipNode(&stack1, bsp, clipnode_head, &tasks1); + fprintf(file, "%s", DecompileHull1(bsp, tasks1).c_str()); + } } fprintf(file, "}\n"); diff --git a/common/bsputils.cc b/common/bsputils.cc index 52ce3042..2b753e4f 100644 --- a/common/bsputils.cc +++ b/common/bsputils.cc @@ -47,6 +47,13 @@ const bsp2_dnode_t *BSP_GetNode(const mbsp_t *bsp, int nodenum) return &bsp->dnodes[nodenum]; } +const bsp2_dclipnode_t *BSP_GetClipNode(const mbsp_t *bsp, int nodenum) +{ + Q_assert(nodenum >= 0 && nodenum < bsp->numclipnodes); + return &bsp->dclipnodes[nodenum]; + +} + const mleaf_t* BSP_GetLeaf(const mbsp_t *bsp, int leafnum) { if (leafnum < 0 || leafnum >= bsp->numleafs) { diff --git a/include/common/bsputils.hh b/include/common/bsputils.hh index e198b824..ba846b20 100644 --- a/include/common/bsputils.hh +++ b/include/common/bsputils.hh @@ -31,6 +31,7 @@ int Face_GetNum(const mbsp_t *bsp, const bsp2_dface_t *f); // bounds-checked array access (assertion failure on out-of-bounds) const bsp2_dnode_t *BSP_GetNode(const mbsp_t *bsp, int nodenum); +const bsp2_dclipnode_t *BSP_GetClipNode(const mbsp_t *bsp, int nodenum); const mleaf_t* BSP_GetLeaf(const mbsp_t *bsp, int leafnum); const mleaf_t* BSP_GetLeafFromNodeNum(const mbsp_t *bsp, int nodenum); const dplane_t *BSP_GetPlane(const mbsp_t *bsp, int planenum);