ericw-tools/include/qbsp/qbsp.hh

336 lines
9.8 KiB
C++

/*
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 1997 Greg Lewis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
// qbsp.h
#pragma once
#include <list>
#include <vector>
#include <map>
#include <unordered_map>
#include <array>
#include <optional>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <cmath>
#include <cstdint>
//#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <common/bspfile.hh>
#include <common/aabb.hh>
enum texcoord_style_t
{
TX_QUAKED = 0,
TX_QUARK_TYPE1 = 1,
TX_QUARK_TYPE2 = 2,
TX_VALVE_220 = 3,
TX_BRUSHPRIM = 4
};
enum class conversion_t
{
quake,
quake2,
valve,
bp
};
class options_t
{
public:
bool fNofill = false;
bool fNoclip = false;
bool fNoskip = false;
bool fNodetail = false;
bool fOnlyents = false;
bool fConvertMapFormat = false;
conversion_t convertMapFormat = conversion_t::quake;
bool fVerbose = true;
bool fAllverbose = false;
bool fSplitspecial = false;
bool fSplitturb = false;
bool fSplitsky = false;
bool fTranswater = true;
bool fTranssky = false;
bool fOldaxis = true;
bool fNoverbose = false;
bool fNopercent = false;
bool forceGoodTree = false;
bool fixRotateObjTexture = true;
bool fbspx_brushes = false;
bool fNoTextures = false;
const bspversion_t *target_version = &bspver_q1;
const gamedef_t *target_game = target_version->game;
int dxSubdivide = 240;
int dxLeakDist = 2;
int maxNodeSize = 1024;
/**
* if 0 (default), use maxNodeSize for deciding when to switch to midsplit bsp heuristic.
*
* if 0 < midsplitSurfFraction <=1, switch to midsplit if the node contains more than this fraction of the model's
* total surfaces. Try 0.15 to 0.5. Works better than maxNodeSize for maps with a 3D skybox (e.g. +-128K unit maps)
*/
float midsplitSurfFraction = 0.f;
std::filesystem::path szMapName;
std::filesystem::path szBSPName;
struct wadpath
{
std::filesystem::path path;
bool external; // wads from this path are not to be embedded into the bsp, but will instead require the engine
// to load them from elsewhere. strongly recommended for eg halflife.wad
};
std::vector<wadpath> wadPathsVec;
vec_t on_epsilon = 0.0001;
bool fObjExport = false;
bool fOmitDetail = false;
bool fOmitDetailWall = false;
bool fOmitDetailIllusionary = false;
bool fOmitDetailFence = false;
bool fForcePRT1 = false;
bool fTestExpand = false;
bool fLeakTest = false;
bool fContentHack = false;
vec_t worldExtent = 0.f;
int threads = 0; // 0 = let TBB auto select ideal number of threads
bool includeSkip = false;
bool fNoTJunc = false;
};
extern options_t options;
/*
* Clipnodes need to be stored as a 16-bit offset. Originally, this was a
* signed value and only the positive values up to 32767 were available. Since
* the negative range was unused apart from a few values reserved for flags,
* this has been extended to allow up to 65520 (0xfff0) clipnodes (with a
* suitably modified engine).
*/
#define MAX_BSP_CLIPNODES 0xfff0
// Various other geometry maximums
constexpr size_t MAXEDGES = 64;
// 0-2 are axial planes
// 3-5 are non-axial planes snapped to the nearest
#define PLANE_X 0
#define PLANE_Y 1
#define PLANE_Z 2
#define PLANE_ANYX 3
#define PLANE_ANYY 4
#define PLANE_ANYZ 5
// planenum for a leaf (?)
constexpr int32_t PLANENUM_LEAF = -1;
/*
* The quality of the bsp output is highly sensitive to these epsilon values.
* Notes:
* - T-junction calculations are sensitive to errors and need the various
* epsilons to be such that EQUAL_EPSILON < T_EPSILON < CONTINUOUS_EPSILON.
* ( TODO: re-check if CONTINUOUS_EPSILON is still directly related )
*/
#define ANGLEEPSILON 0.000001
#define ZERO_EPSILON 0.0001
#define DISTEPSILON 0.0001
#define POINT_EPSILON 0.0001
#define ON_EPSILON options.on_epsilon
#define EQUAL_EPSILON 0.0001
#define T_EPSILON 0.0002
#define CONTINUOUS_EPSILON 0.0005
// from q3map
#define MAX_WORLD_COORD (128 * 1024)
#define MIN_WORLD_COORD (-128 * 1024)
#define WORLD_SIZE (MAX_WORLD_COORD - MIN_WORLD_COORD)
// the exact bounding box of the brushes is expanded some for the headnode
// volume. is this still needed?
#define SIDESPACE 24
// AllocMem types
enum
{
WINDING,
OTHER
};
#include <common/cmdlib.hh>
#include <common/mathlib.hh>
#include <qbsp/winding.hh>
struct mtexinfo_t
{
texvecf vecs; /* [s/t][xyz offset] */
int32_t miptex = 0;
surfflags_t flags = {};
int32_t value = 0; // Q2-specific
int32_t next = -1; // Q2-specific
std::optional<size_t> outputnum = std::nullopt; // nullopt until added to bsp
constexpr auto as_tuple() const { return std::tie(vecs, miptex, flags, value, next); }
constexpr bool operator<(const mtexinfo_t &other) const { return as_tuple() < other.as_tuple(); }
constexpr bool operator>(const mtexinfo_t &other) const { return as_tuple() > other.as_tuple(); }
};
class mapentity_t;
struct face_fragment_t
{
winding_t w;
std::vector<size_t> edges; // only filled in MakeFaceEdges
std::optional<size_t> outputnumber; // only valid for original faces after
// write surfaces
};
struct face_t : face_fragment_t
{
int planenum;
int planeside; // which side is the front of the face
int texinfo;
twosided<contentflags_t> contents;
twosided<int16_t> lmshift;
mapentity_t *src_entity; // source entity
face_t *original; // face on node
bool touchesOccupiedLeaf; // internal use in outside.cc
qvec3d origin;
vec_t radius;
// filled by TJunc
std::vector<face_fragment_t> fragments;
};
struct surface_t
{
int planenum;
bool onnode; // true if surface has already been used
// as a splitting node
bool detail_separator; // true if ALL faces are detail
std::list<face_t *> faces; // links to all faces on either side of the surf
// bounds of all the face windings; calculated via calculateInfo
aabb3d bounds;
// 1 if the surface has non-detail brushes; calculated via calculateInfo
bool has_struct;
// smallest lmshift of all faces; calculated via calculateInfo
short lmshift;
std::optional<size_t> outputplanenum; // only valid after WriteSurfacePlanes
inline surface_t shallowCopy()
{
surface_t copy;
copy.planenum = planenum;
copy.onnode = onnode;
copy.detail_separator = detail_separator;
copy.has_struct = has_struct;
copy.lmshift = lmshift;
return copy;
}
// calculate bounds & info
inline void calculateInfo()
{
bounds = {};
lmshift = std::numeric_limits<short>::max();
has_struct = false;
for (auto &f : faces) {
for (auto &contents : f->contents) {
if (!contents.is_valid(options.target_game, false)) {
FError("Bad contents in face: {}", contents.to_string(options.target_game));
}
}
lmshift = min(f->lmshift.front, f->lmshift.back);
if (!((f->contents[0].extended | f->contents[1].extended) &
(CFLAGS_DETAIL | CFLAGS_DETAIL_ILLUSIONARY | CFLAGS_DETAIL_FENCE | CFLAGS_WAS_ILLUSIONARY))) {
has_struct = true;
}
bounds += f->w.bounds();
//Q_assert(!qv::emptyExact(bounds.size()));
}
}
};
// there is a node_t structure for every node and leaf in the bsp tree
struct brush_t;
struct portal_t;
struct node_t
{
aabb3d bounds; // bounding volume, not just points inside
// information for decision nodes
int planenum; // -1 = leaf node
int firstface; // decision node only
int numfaces; // decision node only
node_t *children[2]; // children[0] = front side, children[1] = back side of plane. only valid for decision nodes
std::list<face_t *> facelist; // decision nodes only, list for both sides
// information for leafs
contentflags_t contents; // leaf nodes (0 for decision nodes)
std::vector<face_t *> markfaces; // leaf nodes only, point to node faces
portal_t *portals;
int visleafnum; // -1 = solid
int viscluster; // detail cluster for faster vis
int outside_distance; // -1 = can't reach outside, 0 = first void node, >0 = distance from void, in number of portals
// used to write leak lines that take the shortest path to the void
mapentity_t *occupant; // example occupant, for leak hunting
bool detail_separator; // for vis portal generation. true if ALL faces on node, and on all descendant nodes/leafs,
// are detail.
uint32_t firstleafbrush; // Q2
uint32_t numleafbrushes;
int32_t area;
bool opaque() const;
};
#include <qbsp/brush.hh>
#include <qbsp/csg4.hh>
#include <qbsp/solidbsp.hh>
#include <qbsp/merge.hh>
#include <qbsp/surfaces.hh>
#include <qbsp/portals.hh>
#include <qbsp/region.hh>
#include <qbsp/writebsp.hh>
#include <qbsp/outside.hh>
#include <qbsp/map.hh>
int qbsp_main(int argc, const char **argv);