EWTOOLS-QBSP(1)

πŸ“¦ version 2.0.0~alpha11, in ubuntu stonking

✨ NAME ✨

ewtools-qbsp β€” compile a Quake BSP file from a MAP source file πŸ’–

And it also goes by qbsp, same energy, same job, bestie 🫢


πŸ’« SYNOPSIS πŸ’«

qbsp [OPTION]... SOURCFILE [DESTFILE]

(okay yes, it says SOURCFILE up there and SOURCEFILE down below β€” that's literally what the page says, no I will not be fixing it, it's iconic πŸ’…)


🌈 DESCRIPTION 🌈

So qbsp is one of the tools you use to make maps for Quake, the id Software game, and honestly? She's the backbone of the whole operation 😀

You hand her a .map file as input and she hands you back a .bsp file that the Quake engine can actually play. That's the whole vibe check.

And if you don't give her a DESTFILE argument? No stress β€” she picks the output filename herself by taking the file extension (if there even is one πŸ’€) off the SOURCEFILE and slapping .bsp on the end. Auto-naming queen behavior πŸ‘‘βœ¨


πŸ‘‘ OPTIONS πŸ‘‘

okay so here's the whole menu, buckle up bestie, it's a long one πŸ“

  • -nofill β€” doesn't perform outside filling. we're skipping that step entirely βœ‹
  • -filltype auto | inside | outside β€” how do we fill this map? from the outside in (that's the lenient one 🫢), from the inside out (that's the aggressive one 😀), or auto and let her decide for herself based on the hull being used.
  • -filldetail [n] β€” do we fill enclosed pockets of empty space that are surrounded by solid detail? Default is 1 (enabled). so, yes, by default πŸ’…
  • -nomerge β€” don't do face merging. just... don't 🚫
  • -noedgereuse β€” don't reuse edges. the page notes this may be useful for debugging software rendering πŸ‘€
  • -noclip β€” doesn't build clip hulls. and heads up, this is only applicable for Q1-like BSP formats.
  • -noskip β€” doesn't remove faces that use the skip texture 🫣
  • -nodetail β€” treat all detail brushes as structural. no special treatment for anybody 😌
  • -onlyents β€” only updates .map entities. that's it, that's the whole job.
  • -verbose ✨
  • -v β€” print out more .map information πŸ‘€
Not me having to tell you this: this switch no longer implies -loghulls. if you want those collision hull statistics you gotta use that one yourself, fr fr.
  • -loghulls β€” print log output for collision hulls πŸͺ΅
  • -logbmodels β€” print log output for bmodels πŸͺ΅
  • -quiet 🀫
  • -noverbose β€” suppress non-important messages. this is equivalent to -nopercent -nostat -noprogress, so it's a two-for-one situation πŸŽ€
  • -log β€” write log files. Enabled by default, so you're already doing this bestie πŸ’–
  • -nolog β€” don't write log files. opting out, valid πŸ’…
  • -chop β€” adjust brushes to remove intersections if possible. Enabled by default.
  • -nochop β€” do not chop intersecting brushes. we're leaving them be 😀
  • -chopfragment β€” always do full fragmentation for chop. go big or go home πŸ”₯
  • -splitsky β€” doesn't combine sky faces into one large face. one sky, but make it many 🌈
  • -splitspecial β€” doesn't combine sky and water faces into one large face. the reason you'd want this? it allows for statically lit water πŸ’§
  • -litwater πŸ’§
  • -splitturb β€” enable lit liquids. this gives you statically lit water in compatible source ports, which still works but renders as fullbright on source ports that don't support it. the only downside is that water faces get split up more, as per -subdivide. Enabled by default ✨
  • -transwater β€” in Q1 mode, compiles maps for transparent water (lets vis see through liquids πŸ‘€). Enabled by default. has no effect in Q2 mode, because in Q2 this is controlled by the mapper, not by us πŸ’…
  • -notranswater β€” computes portal information for opaque water. no see-through here 🚫
  • -oldaxis β€” use the original QBSP texture alignment algorithm. Enabled by default.
  • -nooldaxis β€” use the alternate texture alignment algorithm. the default is to use the original QBSP texture alignment algorithm, which required the -oldaxis switch in tyrutils-ericw v0.15.1 and earlier πŸ“œ
  • -forcegoodtree β€” force use of the expensive processing for the SolidBSP stage. often results in a more optimal BSP file in terms of file size, at the expense of extra processing time. beauty is pain, and it's also slow 😭
  • -leaktest β€” makes it a compile error if a leak is detected. no leaky maps on this watch πŸ’€
  • -nopercent β€” prevents output of percent completion information. you don't need to know how far along we are 🫣
  • -nostat β€” don't output statistic messages πŸ“Š
  • -noprogress β€” don't output progress messages 🚫
  • -nocolor β€” don't output ANSI color codes, for when the terminal doesn't recognize colors (e.g. TB). tragic, but understood πŸ’”
  • -q2bsp β€” target Quake II and the vanilla Q2BSP format, automatically switching to Qbism format if necessary (unless -noallowupgrade is specified). she'll upgrade herself, respectfully πŸ‘‘
  • -qbism β€” target Quake II and use Qbism's extended Quake II BSP format πŸ’–
  • -q2rtx β€” adjust settings to best support Q2RTX. glow-up for the ray tracing girlies ✨
  • -hexen2 β€” generate a hexen2 bsp. this can be used in addition to -bsp2 to avoid clipnode issues 🧚
  • -bsp2 β€” create the output BSP file in BSP2 format. this allows the creation of much larger and more complex maps than the original BSP 29 format πŸ—οΈ
  • -2psb β€” create the output BSP file in 2PSB format. this is an earlier version of the BSP2 format, supported by the RMQ engine (and thus is also known as the BSP2rmq or RMQe bsp format).
Deprecated since version 1.0: Use -bsp2 instead. she's retired bestie, let her rest πŸ™ˆ
  • -allowupgrade β€” allow formats to "upgrade" to compatible extended formats when a limit is exceeded (e.g. Quake BSP to BSP2, or Quake 2 BSP to Qbism BSP). Enabled by default. glow-up privileges granted πŸ‘‘
  • -noallowupgrade β€” opt out of -allowupgrade. no glow-up for you 🚫
  • -hlbsp β€” create the output BSP file in Half-Life's format. note that the hull size differences prevent this from being generally usable for the vanilla quake gamecode. and this cannot be used in combination with the -bsp2 argument ⚠️
  • -add [mapfile] β€” the given map file will be appended to the base map. bringing a friend along 🫢
  • -leakdist [n] β€” space between leakfile points. default 0, which does not write any inbetween points πŸ•³οΈ
  • -subdivide [n] β€” use different texture subdivision. default is 240. heads up: lower values will harm framerates. higher values may not be supported. DP+FTEQW+QSS support up to 4080 (unless lightmap scaling is in use), but such values will cause other engines to crash-to-console πŸ’€. use 0 to specify no subdivision.
  • -nosubdivide β€” disable subdivision. requires a compatible source port. see also -subdivide πŸ‘€
  • -lmscale n β€” change global lmscale (force _lmscale key on all entities). outputs the LMSCALE BSPX lump πŸ“
  • -software β€” change settings to allow for (or make adjustments to optimize for the lack of) software support πŸ–₯️
  • -nosoftware β€” explicitly drop support for software renderers. no software girlies allowed 😀
  • -wadpath <dir> β€” search this directory for wad files, and the default is cwd. multiple -wadpath args may be used 🫢. this argument is ignored for wads specified using an absolute path.
  • -xwadpath <dir> β€” like -wadpath, except textures found using the specified path will NOT be embedded into the bsp (equivalent to -notex, but only for textures from specific wads). you should use this for wads like halflife's standard wad files, but q1bsps require an engine extension and players are not nearly as likely to have the same wad version. it's giving compatibility anxiety 😭
  • -path "/path/to/folder" <multiple allowed> β€” additional paths or archives to add to the search path, mostly for loose files πŸ—‚οΈ
  • -defaultpaths β€” whether the compiler should attempt to automatically derive game/base paths for games that support it. Enabled by default ✨
  • -nodefaultpaths β€” opt out of -defaultpaths 🚫
  • -oldrottex β€” use the old method of texturing rotate_ brushes, where the mapper aligns textures for the object at (0 0 0) πŸ”„
  • -midsplitsurffraction n β€” if 0 (the default), use -maxnodesize for deciding when to switch to the 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) 🌌

  • -midsplitbrushfraction n β€” switch to the cheaper BSP partitioning test if a node contains this % of brushes in the map πŸ–ŒοΈ
  • -maxnodesize [n] β€” switch to the cheap spatial subdivion bsp heuristic when splitting nodes of this size (in any dimension). this gives much faster qbsp processing times on large maps and should generate better bsp trees as well. from txqbsp-xt, thanks rebb 🫢 (default 1024, 0 to disable)
  • -wrbrushes πŸ–ŒοΈ
  • -bspx β€” includes a list of brushes for brush-based collision. this allows for arbitrary collision sizes in engines that support it, currently only FTEQW πŸ‘‘
  • -wrbrushesonly πŸ–ŒοΈ
  • -bspxonly β€” -wrbrushes combined with the -noclip argument. this is NOT backwards compatible πŸ’€
  • -bmodelcontents β€” allow bmodels to have contents other than "solid" in Q1 based games, e.g. water in a func_door πŸ’§. this is supported in FTEQW; in winquake, the bmodel will have no collision 😭

Q2 supports this feature natively and this option has no effect.

  • -notriggermodels β€” for supported game code only: triggers will not write a model out, and will instead just write out their mins/maxs πŸ“
  • -notex β€” write only placeholder textures, to depend upon replacements. this avoids inclusion of third-party copyrighted images inside your maps, but is not backwards compatible β€” it will work in FTEQW and QSS though πŸ’…

note that the textures still need to be available to qbsp πŸ‘€

technical details: LUMP_TEXTURES is still written, but each texture within is the dmiptex_t header only (with no texture data following), with offsets all set to 0.

this only has effect in Q1 family games πŸŽ€

  • -missing_textures_as_zero_size β€” writes missing textures (i.e. ones that qbsp can't find) as 0x0 placeholders 🫣

this is not backwards compatible and will produce .bsp's that crash unsupporting engines. however, it can be useful when you want to run qbsp + light on a map that you are missing the corresponding .wad for.

this only has effect in Q1 family games πŸŽ€

  • -notjunc β€” alias for -tjunc none πŸ’…
  • -tjunc mwt | none | retopologize | rotate β€” T-junction fix level, pick your fighter:
    • none β€” don't attempt to fix T-junctions. this is only for engines or formats that prefer micro-cracks over degenerate triangles. if you don't know what that means, don't set this 🫣
    • rotate β€” allow faces' vertices to be rotated to prevent zero-area triangles πŸ”„
    • retopologize β€” if a face still has zero-area triangles, allow it to be re-topologized by splitting it into multiple fans πŸŒ€
    • mwt β€” attempt to triangulate faces (along with their T-junction fixes) using a MWT (https://en.wikipedia.org/wiki/Minimum-weight_triangulation) first, only falling back to the prior two steps if it fails 🧠

  • -noextendedsurfflags β€” bestie, this one straight up refuses to write the .texinfo file, even in the situations where it would normally be needed. it's a debug flag, so like, use it when you're poking at stuff πŸ™ˆ
  • -omitdetail β€” detail brushes get entirely left out of the compile. gone. not invited. ghosted πŸ’€
  • -omitdetailwall β€” same energy, but specifically func_detail_wall brushes get omitted from the compile.
  • -omitdetailillusionary β€” func_detail_illusionary brushes get omitted from the compile. bye bb πŸ¦‹
  • -omitdetailfence β€” func_detail_fence brushes get omitted from the compile.
  • -convert <fmt> β€” convert a .MAP into a different .MAP format. the fmt you can pass is:
    • quake β€” Q1 vanilla map format ✨
    • quake2 β€” Q2 vanilla map format ✨
    • valve β€” Valve 220 map format ✨
    • bp β€” Brush Primitives format ✨
real talk: converting to quake or quake2 might not be able to match the texture alignment from the source map, so like… it can come out a little unaligned, ngl. every other conversion is lossless, so those are the safe ones πŸ’–

the converted map gets saved to <source map name>-<fmt>.map, so no, it does not overwrite your original, you're fine.

  • -includeskip β€” this one actually emits skip/nodraw faces into the output (the default behaviour is to discard them, so this is the opposite vibe). mainly a Q2RTX thing πŸ‘‘
  • -threads n β€” set how many threads to use. by default, qbsp will just attempt to use all available hardware threads, because she's greedy like that πŸ’…
  • -lowpriority β€” run at a lower priority so there's headroom left for your other processes. this is enabled by default, so she's already being polite to your system 🫢
  • -aliasdef <aliases.def> [...] β€” adds alias definition files, which can transform entities in the .map into other entities. it's giving remix energy ✨

so like, say your alias definition file (aliases.def) looks like this:

misc_torch1 // source classname
{
"classname" "misc_model" // classname to transform into
"model" "torch1.mdl"
}

misc_torch2
{
"classname" "misc_model"
"model" "torch2.mdl"
}

and your input map file looks like this:

{
"classname" "misc_torch1"
"model" "override.mdl"
}

{
"classname" "misc_torch2"
}

then the following gets output in the .bsp's entity lump:

{
"classname" "misc_model"
"model" "override.mdl" // key/value from map takes precedence
}

{
"classname" "misc_model"
"model" "torch2.mdl" // key/value from alias file
}

notice how the key/value from the map takes precedence over the alias file? that's the whole plot twist 😭

  • -texturedefs "path/to/file.def" <multiple allowed> β€” path to a texture definition file, which can transform textures in the .map into other textures. and yes, you can pass more than one, multiple allowed, she's flexible 🌈
  • -epsilon n β€” customize the epsilon value used for point-on-plane checks. tiny number, big consequences, iykyk ⭐
  • -microvolume n β€” sets the microbrush volume.
  • -outsidedebug β€” writes a .map after outside filling, showing you the non-visible brush sides. for when you wanna see what's under the hood πŸ”
  • -debugchop β€” writes a .map after ChopBrushes.
  • -debugleak β€” writes more diagnostic files specifically for debugging leaks. leak girlies this one's for you 😭
  • -debugbspbrushes β€” saves bsp brushes after BrushBSP to a .map, so you can visualize the BSP splits.
  • -debugleafvolumes β€” saves bsp leaf volumes after BrushBSP to a .map, so you can visualize the BSP splits. sibling behaviour with the flag above πŸ’…
  • -debugexpand [single hull index] or [mins_x mins_y mins_z maxs_x maxs_y maxs_z] β€” writes an expanded hull .map for debugging/inspecting hulls and brush bevelling. you can hand it either a single hull index or a full set of mins/maxs values, pick your fighter 🌸
  • -keepprt β€” avoids deleting the .prt file on leaking maps. she's not throwing away your evidence πŸŽ€
  • -maxedges n β€” the max number of edges/vertices allowed on a single face before it gets split into another face. 0 means unlimited.
default is 0 for Q2, and 64 for Q1. different defaults, different games, don't @ me ✨
  • -worldextent n β€” explicitly provide world extents; 0 will auto-detect. default is 0.
  • -forceprt1 β€” force a PRT1 output file even if PRT2 is actually required for vis. gonna do it anyway πŸ’…
  • -objexport β€” export the map file as .OBJ models during the various compilation phases. cute lil export bestie πŸŽ€

🌈 GAME PATH SPECIFICATION 🌈

okay so, real talk: to compile a Q2 map, the compilers usually need to be able to find an installation of the game. like, the .map might reference a texture name like e1u1/clip, but qbsp needs to open the corresponding .wal file to look up the content/surface flags (playerclip, etc.) which then get written to the .bsp. she needs to read the receipts 🧚

we use this terminology:

  • basedir β€” the directory containing the base game (e.g. id1 or baseq2). can be an absolute path, e.g. c:/quake2/baseq2 or c:/quake/id1.
  • gamedir β€” optional mod directory, e.g. ad or c:/quake/ad. if a gamedir is specified, it will be added to the search path at a higher priority than the basedir πŸ‘‘

the common cases are:

  • place your .map in <quake2>/baseq2/maps and compile it there β€” qbsp will auto-detect the basedir/gamedir for you. iconic, no notes πŸ’–
  • for compiling a .map that lives somewhere else, use e.g.:
qbsp -basedir "c:/quake2/baseq2" input.map

or

qbsp -basedir "c:/quake2/baseq2" -gamedir mymod input.map
  • -gamedir "relative/path" or "C:/absolute/path" β€” override the default mod base directory. if this is not set, or if it is relative, it will be derived from the input file or the basedir if specified.
  • -basedir "relative/path" or "C:/absolute/path" β€” override the default game base directory. if this is not set, or if it is relative, it will be derived from the input file or the gamedir if specified.
  • -filepriority archive | loose β€” which types of archives (folders/loose files or packed archives) are higher priority and chosen first for path searching. pick a lane, bestie πŸ¦‹

πŸ’– SPECIAL TEXTURE NAMES πŸ’–

the contents inside a brush depend on the texture name(s) you slap onto it. so like, the texture name is the personality of the brush, and it decides the behaviour πŸ’…

by default, brush contents are solid unless they have a special name. all faces of a brush must have textures which indicate the same contents. mixed content types will cause qbsp to print an error and exit β€” so no chaotic half-water-half-solid brushes, she will catch you 😭

  • *slime
  • *lava
  • * β€” in all Q1-like games, names beginning with an asterisk are liquids. a prefix of *slime indicates slime, *lava is for lava, and anything else beginning with * will have contents as water. literally just vibes and asterisks ✨
  • skip β€” any surfaces assigned a texture name of skip will be compiled into the bsp as invisible surfaces. solid surfaces will still be solid (e.g. the player can't walk or shoot through them) but they will not be drawn. water, slime and lava surfaces can be made invisible using the texture names *waterskip, *slimeskip and *lavaskip respectively. stealth mode activated πŸ‘»
  • hint β€” hint surfaces cause a bsp split and portal to be generated on the surface plane, after which they are removed from the final bsp β€” so they are neither visible nor structural. strategic placement of hint surfaces can be used by a map author to optimise the PVS calculations so as to limit overdraw by the engine (see also: vis(1)). big brain move 🧠

use a texture with the name hintskip on any surfaces of a hint brush which you don't want to generate bsp splits or portals. all surfaces of a hint brush must use either the hint or hintskip texture name β€” no third options allowed periodt πŸ’…

  • origin β€” an origin brush (all faces textured with "origin") can be added to a brush entity (but not detail or compiler-internal entities like func_group). doing so causes all of the brushes in the brush entity to be translated so the center of the origin brush lines up with 0 0 0. the entity key "origin" is then automatically set on the brush entity to the original coordinates of the center of the "origin" brush before it was translated to 0 0 0. ✨

in Hexen 2, origin brushes are the native way of marking the center point of the rotation axis for rotating entities.

in Quake, origin brushes can be used to make some map hacks easier to set up that would otherwise require placing brushes at the world origin and entering an "origin" value by hand.

note that, unlike the Hipnotic rotation support in QBSP, using origin brushes does not cause the model bounds to be expanded. (with Hipnotic rotation this was to ensure that the model is not vis culled, regardless of its rotated angle.) origin brushes are useful for more than just rotation, and doing this bounds expansion would break some use cases, so if you're going to rotate a model with an origin brush you might need to expand the bounds of it a bit using clip brushes so it doesn't get vis culled. 🫢

πŸ¦‹ Half-Life BSP Specific πŸ¦‹

if -hlbsp is in use, the following extra texture prefixes are recognized:

  • !slime
  • !lava
  • ! β€” these act the same as names beginning with * and create liquids.
  • !cur_0
  • !cur_90
  • !cur_180
  • !cur_270
  • !cur_up
  • !cur_dwn β€” these prefixes generate water with the given current direction:
    • CONTENTS_CURRENT_0 β€” -9
    • CONTENTS_CURRENT_90 β€” -10
    • CONTENTS_CURRENT_180 β€” -11
    • CONTENTS_CURRENT_270 β€” -12
    • CONTENTS_CURRENT_UP β€” -13
    • CONTENTS_CURRENT_DOWN β€” -14

during the BSP process, they act like water with extra modifier flags. 🌊

Note: different from the vanilla HL compiler, we don't generate faces between different CONTENTS_CURRENT_* textured volumes.

✨ EXTERNAL MAP PREFAB SUPPORT πŸŽ€

So this qbsp? It has a whole prefab system, and it runs on a point entity named misc_external_map. The vibe is: every misc_external_map yanks brushes in from an external .map file, slaps on whatever rotations you gave it in the _external_map_angles key, then translates the whole thing over to the origin key of the misc_external_map entity. And then β€” plot twist β€” the classname of the misc_external_map gets switched to whatever the mapper put in the _external_map_classname key. (Oh and the origin key gets cleared to 0 0 0 before the .bsp is saved, so it's not still squatting there.)

Your external .map file should contain worldspawn brushes only β€” though for editing convenience you're allowed to use func_group. Brush entities get merged with the worldspawn brushes during the import 🫢. All worldspawn keys and any point entities? Ignored, sorry bestie. And rn that means the wad key is not handled, so any texture wads the external .map needs? You have to add them to your main map yourself. No cap.

Also lowkey important: you can set other entity keys on the misc_external_map to configure what the final entity ends up being. Like, if you set _external_map_classname to func_door, you can also set a targetname key on the misc_external_map, or literally any other keys that func_door takes. Iconic.

  • _external_map β€” Names the filename of the .map you wanna import.
  • _external_map_classname β€” What entity you want that external map to turn into. Internal qbsp entity types like func_detail work, or a regular bmodel classname like "func_wall" or "func_door".
  • _external_map_angles β€” Rotation for the prefab, in "pitch yaw roll" format. Assume the external map is facing the +X axis, and positive pitch is down. Yaw of 180, for example, would rotate it to face -X. πŸ‘‘
  • _external_map_angle β€” The short version of _external_map_angles, for when you just want to specify a yaw rotation.
  • _external_map_scale β€” Scale factor for the prefab, defaults to 1. Either hand it a single value or three scales, "x y z".

🌸 DETAIL BRUSH SUPPORT 🌸

This version of qbsp supports detail brushes, which are similar in concept to Quake 2's detail brushes. And the big one: they don't seal the map. (Previous versions did. We don't talk about previous versions.)

So to stay compatible with existing Quake 1 mapping tools, you add detail brushes by making an entity with classname "func_detail". When qbsp reads the map file, it takes any brushes inside a func_detail entity, shoves them into the worldspawn as details, and deletes the func_detail entity. You can use any number of func_detail entities (useful for grouping, fr fr) and all the brushes inside them get added to the worldspawn.

Here's an example entity definition, suitable for adding to the .QC files used by BSP Editor:

/*QUAKED func_detail (0.5 0.5 0.9) ?
Detail brushes add visual details to
the world, but do not block visibility.
func_detail entities are merged into
the worldspawn entity by the qbsp compiler
and do not appear as separate entities in
the compiled bsp.
*/

For WorldCraft, in .FGD format (untested, iykyk):

@SolidClass color(128 128 230) = func_detail: "Detail" []

For Radiant, in .ENT format:

<group name="func_detail" color="0 .5 .8">
Detail brushes add visual details to
the world, but do not block visibility. func_detail entities are merged into the
worldspawn entity by the qbsp compiler and do not appear as separate entities in
the compiled bsp.
</group>

What actually goes in the .map file is just a plain entity with one or more brushes. Like this:

{
"classname" "func_detail"
{
( -176  80  0 ) ( -208  80  0 ) ( -208  48  0 ) COP1_1 0 0 0 1.0 1.0
( -192 -80 64 ) ( -208 -80  0 ) ( -192 -64 64 ) COP1_1 0 0 0 1.0 1.0
( -176 -80  0 ) ( -192 -80 64 ) ( -176 -64  0 ) COP1_1 0 0 0 1.0 1.0
( -16   48  0 ) (  -16  64 64 ) (    0  48  0 ) COP1_1 0 0 0 1.0 1.0
( -16   64 64 ) (  -16  80  0 ) (    0  64 64 ) COP1_1 0 0 0 1.0 1.0
}
}

And here's the thing bestie πŸ‘€ β€” when qbsp detects detail brushes, it outputs a modified portal file format with the header PRT2 (the default is PRT1). That portal file carries extra info that vis needs to compute the potentially visible set (PVS) for the map/bsp. So you're also gonna need a vis util that can handle the PRT2 file format. Otherwise it's giving… nothing.


πŸ’… COMPILER-INTERNAL BMODELS πŸ’…

  • func_group β€” A bmodel that has literally no effect; qbsp just moves the brushes back into the worldspawn entity. The usefulness comes from the ability to set Model Entity Keys on the brushes inside the func_group. Slay.
  • func_detail β€” Solid detail. Can't be seen inside, splits world faces. Doesn't seal map.
  • func_detail_illusionary β€” A func_detail variant with no collision (players / monsters / gunfire) and it doesn't split world faces. Doesn't cast shadows unless enabled with _shadow <#bmodel-key-_shadow> "1". Useful for hanging vines πŸ“. It still creates BSP leafs, which is unavoidable without a new .bsp file format, so that's just the deal. Intersecting func_detail_illusionary brushes don't clip each other; this is intended to make trees/shrubs/foliage easier with _mirrorinside "1". 🌈
  • func_detail_wall β€” A func_detail variant that doesn't split world faces. Useful when you want a decoration touching a floor or wall to not split the floor/wall faces (you'll get some overdraw instead, oops). If it completely covers up a world face, that face will get clipped away, so it's not suitable for fence textures β€” see func_detail_fence instead. Intersecting func_detail_wall brushes don't clip each other.
  • func_detail_fence β€” Similar to func_detail_wall, except it's suitable for fence textures and it never clips away world faces. Useful for fences, grates, etc., that are solid and block gunfire. Intersecting func_detail_fence brushes don't clip each other.

πŸ¦‹ MODEL ENTITY KEYS πŸ¦‹

  • "_lmscale" "n" β€” Generates an LMSHIFT bspx lump for use by a light util. Note that both scaled and unscaled lighting will normally be used.

Todo

This is broken since 2.0.0-alpha1. 😭

  • "_mirrorinside" "n" β€” Set to 1 to save mirrored inside faces for bmodels, so when the player view is inside the bmodel, they will still see the faces. (e.g. for func_water, func_illusionary, func_detail_illusionary, func_detail_fence, etc.) πŸ’–
  • "_chop_order" "n" β€” Override the order of brushes in the map file, which affects which face "wins" in the CSG phase when there are multiple overlapping coplanar brushes. It's provided since most .map editors don't directly expose the brush order. All brushes are initially 0; setting this to 1 would make a brush appear after anything in the map file, and so clip away coplanar faces from other brushes. For example, you could make a func_group with "_chop_order" "1", and apply it to a light fixture which is coplanar with a floor or ceiling, to ensure the light fixture is visible in-game, without going to the trouble of cutting a hole in the floor/ceiling for it. Main character energy fr fr.
  • "_hulls" "n" β€” Bitmap ("Flags" type in FGD) that selects for which hulls collision data will be generated. eg. a decimal value of 11 (0b1011) would generate hull 0, hull 1, and hull 3. Faces are computed using data from hull 0, not generating this hull will prevent a bmodel from being rendered, acting as a CLIP brush only active for the specified hulls. Defaults to 0 which will generate clipnodes for all hulls.
  • "_chop" "n" β€” Set to 0 to prevent these brushes from being chopped; this is an alias for setting _chop_order to 1.
Deprecated since version 2.0.0: Prefer the more flexible _chop_order instead.
  • "_noclipfaces" "n" β€” Set to 1 to suppress the usual qbsp behaviour where two touching brushes of the same content type will have the touching portion clipped away. This is useful for things that can be seen inside, such as func_detail_illusionary, func_detail_fence, liquids, bmodels, etc. Behaviour is unspecified for coplanar faces; in 0.18 it caused z-fighting, and in 2.0.0-alpha it doesn't currently. πŸ’€

πŸ‘‘ OTHER SPECIAL-PURPOSE ENTITIES πŸ‘‘

  • func_illusionary_visblocker β€” For creating vis-blocking illusionary brushes (similar to func_detail_illusionary or "func_illusionary". The player can walk through them.) This gives the same effect as water brushes when the -notranswater flag is used, except the interior of these brushes are saved as CONTENTS_EMPTY. One thing to be aware of: if the player's view is very close to the faces of these brushes, they might be able to see into the void (depending on the engine πŸ‘€). Fitzquake family engines have a workaround for this that is enabled if the brushes are textured with a water texture ("*" prefix).

🌈 MAP COMPATIBILITY 🌈

Besides standard Quake 1 .map files, ericw-tools QBSP is compatible with:

  • Floating point brush coordinates and texture alignments
  • Valve's 220 map format as produced by the Hammer editor
  • Extended texture positioning as supported by the QuArK editor
  • Standard Quake 2 map format (leading paths in texture names are stripped and any extra surface properties are ignored)
  • Brush Primitives produce by Radiant editors (normally a Quake 3 format)

πŸ—‚οΈ METADATA FORMATS πŸ—‚οΈ

🧊 .wal_json

okay so for Q2 mapping, .wal textures carry metadata that says what the texture's content flags are, what its surface flags are, and its "value" β€” and all of that gets used only if the mapper doesn't override it in the .map file. like, imagine a lava texture lowkey defaulting to emitting light, having a lava content type, and having a surface warp effect. iconic defaults, honestly. πŸ’…

the final values get baked right into the .bsp, so the game engine literally doesn't need the metadata for anything. it's giving "one and done."

but for ✨increased flexibility✨ we support a JSON metadata format β€” and it can either override the .wal's metadata, or supply metadata entirely on its own when you're using an image format like .tga instead of .wal.

to use a .wal_json file, drop it in the same directory where the .wal would live. so for example, baseq2/textures/e1u1/water.wal_json would correspond to:

baseq2/textures/e1u1/water.png

🫢 and bestie β€” all of the values are optional. every single one.

{
    // valid instances of "contents"; either:
    // - a case-insensitive string containing the textual representation
    //   of the content type
    // - a number
    // - an array of the two above, which will be OR'd together
    "contents": [ "SOLID", 8 ],
    "contents": 24,
    "contents": "SOLID",

    // valid instances of "flags"; either:
    // - a case-insensitive string containing the textual representation
    //   of the surface flags
    // - a number
    // - an array of the two above, which will be OR'd together
    "flags": [ "SKY", 16 ],
    "flags": 24,
    "flags": "SKY",

    // "value" must be an integer
    "value": 1234,

    // "animation" must be the name of the next texture in
    // the chain.
    "animation": "e1u1/comp2",

    // width/height are allowed to be supplied in order to
    // have the editor treat the surface as if its dimensions
    // are these rather than the ones pulled in from the image
    // itself. they must be integers.
    "width": 64,
    "height": 64,

    // color to use for lighting bounces. if specified, this
    // is used instead of averaging the pixels of the image.
    "color": [255, 128, 64]
}

πŸ‘‘ AUTHOR πŸ‘‘

Eric Wasylishen Kevin Shanahan (aka Tyrann) β€” http://disenchant.net Based on source provided by id Software and Greg Lewis


πŸ› REPORTING BUGS πŸ›

bestie if something's cursed, please post bug reports at https://github.com/ericwa/ericw-tools/issues.

and improvements to the documentation are welcome and encouraged β€” like actually, we want them, no cap. πŸ“πŸ’–


Copyright (C) 2017 Eric Wasylishen
Copyright (C) 2013 Kevin Shanahan
Copyright (C) 1997 Greg Lewis
Copyright (C) 1997 id Software
License GPLv2+: GNU GPL version 2 or later
<http://gnu.org/licenses/gpl2.html>.

This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

fr fr, "no warranty" is not a vibe β€” it's a legal fact. respect it. πŸ™

and that's a wrap on the whole page, bestie πŸŽ€ this edition of EWTOOLS-QBSP(1) is from Jul 29, 2026, and it's the source/version date you'd normally spot in the footer. go forth and compile something gorgeous πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from