diff --git a/bsputil/decompile.cpp b/bsputil/decompile.cpp index 8d815a4c..3356df77 100644 --- a/bsputil/decompile.cpp +++ b/bsputil/decompile.cpp @@ -224,6 +224,7 @@ struct decomp_plane_t : qplane3d { const bsp2_dnode_t *node = nullptr; // can be nullptr const q2_dbrushside_qbism_t *source = nullptr; + const bsp2_dclipnode_t *clipnode = nullptr; // can be nullptr }; // brush creation @@ -336,6 +337,7 @@ struct leaf_decompile_task const mleaf_t *leaf = nullptr; const dbrush_t *brush = nullptr; const dmodelh2_t *model = nullptr; + std::optional contents = std::nullopt; // for clipnodes }; /** @@ -775,7 +777,7 @@ static compiled_brush_t DecompileLeafTaskGeometryOnly( compiled_brush_t brush; brush.source = task.brush; brush.brush_offset = brush_offset; - brush.contents = {task.brush ? task.brush->contents : task.leaf->contents}; + brush.contents = {task.brush ? task.brush->contents : task.leaf ? task.leaf->contents : task.contents.value()}; brush.sides.reserve(task.allPlanes.size()); @@ -796,7 +798,7 @@ static compiled_brush_t DecompileLeafTask( compiled_brush_t brush; brush.source = task.brush; brush.brush_offset = brush_offset; - brush.contents = {task.brush ? task.brush->contents : task.leaf->contents}; + brush.contents = {task.brush ? task.brush->contents : task.leaf ? task.leaf->contents : task.contents.value()}; std::vector finalBrushes; if (bsp->loadversion->game->id == GAME_QUAKE_II && !options.ignoreBrushes) { @@ -908,6 +910,14 @@ decomp_plane_t MakeDecompPlane(const mbsp_t *bsp, const bsp2_dnode_t *node, cons front ? -dplane : dplane, node}; } +decomp_plane_t MakeClipDecompPlane(const mbsp_t *bsp, const bsp2_dclipnode_t *clipnode, const bool front) +{ + const dplane_t &dplane = *BSP_GetPlane(bsp, clipnode->planenum); + + return {// flip the plane if we went down the front side, since we want the outward-facing plane + front ? -dplane : dplane, nullptr, nullptr, clipnode}; +} + /** * Preconditions: * - The existing path of plane side choices have been pushed onto `planestack` (but not `node`) @@ -937,6 +947,41 @@ static void DecompileNode(std::vector &planestack, const mbsp_t handleSide(false); } +static void DecompileClipLeaf(const std::vector &planestack, const mbsp_t *bsp, const int32_t contents, + std::vector &result) +{ + if (contents == CONTENTS_EMPTY) { + return; + } + + // NOTE: copies the whole plane stack + result.push_back({planestack, nullptr, nullptr, nullptr, contents}); +} + +static void DecompileClipNode(std::vector &planestack, const mbsp_t *bsp, const bsp2_dclipnode_t *node, + std::vector &result) +{ + auto handleSide = [&](const bool front) { + planestack.push_back(MakeClipDecompPlane(bsp, node, front)); + + const int32_t child = node->children[front ? 0 : 1]; + + if (child < 0) { + // it's a leaf on this side + DecompileClipLeaf(planestack, bsp, child, result); + } else { + // it's another node - process it recursively + DecompileClipNode(planestack, bsp, &bsp->dclipnodes[child], result); + } + + planestack.pop_back(); + }; + + // handle the front and back + handleSide(true); + handleSide(false); +} + static void AddMapBoundsToStack( std::vector &planestack, const mbsp_t *bsp, const bsp2_dnode_t *headnode) { @@ -995,17 +1040,20 @@ static void DecompileEntity( // the model. We're also assuming that the areaportal brushes are // emitted in the same order as the func_areaportal entities. if (dict.find("classname")->second == "func_areaportal") { - size_t brush_offset = std::stoull(dict.find("style")->second); - for (auto &brush : bsp->dbrushes) { - if (brush.contents & Q2_CONTENTS_AREAPORTAL) { - if (brush_offset == 1) { - // we'll use this one - areaportal_brush = &brush; - break; + if (dict.has("style")) { + size_t brush_offset = std::stoull(dict.find("style")->second); + + for (auto &brush : bsp->dbrushes) { + if (brush.contents & Q2_CONTENTS_AREAPORTAL) { + if (brush_offset == 1) { + // we'll use this one + areaportal_brush = &brush; + break; + } + + brush_offset--; } - - brush_offset--; } } } else if (dict.find("classname")->second == "func_group") { @@ -1112,7 +1160,9 @@ static void DecompileEntity( std::vector stack; std::vector tasks; AddMapBoundsToStack(stack, bsp, headnode); + DecompileNode(stack, bsp, headnode, tasks); + //DecompileClipNode(stack, bsp, &bsp->dclipnodes[model->headnode[1]], tasks); // decompile the leafs in parallel compiledBrushes.resize(tasks.size()); diff --git a/qbsp/qbsp.cc b/qbsp/qbsp.cc index 4b4dee77..d3c2fc06 100644 --- a/qbsp/qbsp.cc +++ b/qbsp/qbsp.cc @@ -490,6 +490,7 @@ static void ProcessEntity(mapentity_t *entity, hull_index_t hullnum) // fill again so PruneNodes works MakeTreePortals(tree.get()); FillOutside(entity, tree.get(), hullnum, brushes); + FreeTreePortals(tree.get()); PruneNodes(tree->headnode); } }