Skip to content
4 changes: 4 additions & 0 deletions Quetoo.vs15/quemap.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@
<Link>
<AdditionalDependencies>SDL3.lib;SDL3_image.lib;glib-2.0.lib;intl.lib;dbghelp.lib;Wldap32.lib;physfs.lib;ws2_32.lib;dlfcn.lib;Objectively.lib;%(AdditionalDependencies)</AdditionalDependencies>
<SubSystem>Console</SubSystem>
<!-- BSP tree/portal generation recurses to tree depth; the default 1 MB Windows
main-thread stack overflows on large maps (e.g. cavern). Reserve 8 MB to match
the Linux/macOS main-thread default. -->
<StackReserveSize>8388608</StackReserveSize>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤣 Oh, fucking Windows...

</Link>
<PostBuildEvent>
<Command>COPY_DEPENDENCIES "$(QUETOO_HOME)" "$(Platform)" "$(Configuration)" &amp;&amp; COPY_QUEMAP "$(QUETOO_HOME)" "$(Platform)$(Configuration)"</Command>
Expand Down
37 changes: 31 additions & 6 deletions src/quemap/map.c
Original file line number Diff line number Diff line change
Expand Up @@ -530,12 +530,37 @@ static brush_t *ParseBrush(parser_t *parser, entity_t *entity) {

g_strlcpy(side->texture, token, sizeof(side->texture));

// read the texture parameters
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->shift.x, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->shift.y, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->rotate, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->scale.x, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->scale.y, 1);
// detect Valve-220 vs standard Q1/Q3 format by peeking for '['
Parse_PeekToken(parser, PARSE_NO_WRAP, token, sizeof(token));

if (!g_strcmp0(token, "[")) {
// Valve-220: [ ux uy uz shift_x ] [ vx vy vz shift_y ] rotation scale_x scale_y
side->valve = true;
for (int32_t i = 0; i < 2; i++) {
Parse_Token(parser, PARSE_NO_WRAP, token, sizeof(token));
if (g_strcmp0(token, "[")) {
Com_Error(ERROR_FATAL, "Invalid brush %d (%s)\n", num_brushes, token);
}
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->axis[i].x, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->axis[i].y, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->axis[i].z, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->axis[i].w, 1); // shift
Parse_Token(parser, PARSE_NO_WRAP, token, sizeof(token));
if (g_strcmp0(token, "]")) {
Com_Error(ERROR_FATAL, "Invalid brush %d (%s)\n", num_brushes, token);
}
}
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->rotate, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->scale.x, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->scale.y, 1);
} else {
// Standard Q1/Q3: shift_x shift_y rotation scale_x scale_y
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->shift.x, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->shift.y, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->rotate, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->scale.x, 1);
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_FLOAT, &side->scale.y, 1);
}

if (!Parse_IsEOL(parser)) {
Parse_Primitive(parser, PARSE_NO_WRAP, PARSE_INT32, &side->contents, 1);
Expand Down
5 changes: 5 additions & 0 deletions src/quemap/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ typedef struct brush_side_s {
*/
vec2_t scale;

/**
* @brief True if this brush side uses Valve-220 explicit texture axes.
*/
bool valve;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this boolean probably belongs as a global, right? We'd never encounter a .map file that uses some Valve brushes and some legacy / standard brushes, right?


/**
* @brief The texture axis for S and T, in xyz + offset notation.
*/
Expand Down
9 changes: 9 additions & 0 deletions src/quemap/qbsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ int32_t BSP_Main(void) {

LoadMapFile(map_name);

bool valve = false;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, what probably makes sense here is to introduce

typedef enum {
  MAP_QUAKE2,
  MAP_VALVE_220
} map_format_t;

And have LoadMapFile return the resolved format, which you can set a global to.

map_format = LoadMapFile(map_name);

And then everywhere downstream can just check map_format

for (int32_t i = 0; i < num_brush_sides; i++) {
if (brush_sides[i].valve) {
valve = true;
break;
}
}
Com_Print("Map format: %s\n", valve ? "Quake3 (Valve)" : "Quake3");

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Com_Verbose.


EmitPlanes();
EmitMaterials();
EmitBrushes();
Expand Down
26 changes: 26 additions & 0 deletions src/quemap/texture.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,32 @@ static void TextureAxisForPlane(const plane_t *plane, vec3_t *xv, vec3_t *yv) {
*/
void TextureVectorsForBrushSide(brush_side_t *side, const vec3_t origin) {

if (side->valve) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (map_format == MAP_VALVE_220) {

// Valve-220: axes are already stored in side->axis as (direction, shift).
// Note that this function is called once during parsing (origin = 0) and may be called
// a second time when applying entity origin offsets.
const vec2_t scale = {
.x = side->scale.x ?: 1.f,
.y = side->scale.y ?: 1.f,
};

if (!Vec3_Equal(origin, Vec3_Zero())) {
// Axis xyz are already scaled from the first pass; only apply the origin offset.
for (int32_t i = 0; i < 2; i++) {
side->axis[i].w += Vec3_Dot(origin, side->axis[i].xyz);
}
return;
}

// First pass: apply scale.
for (int32_t i = 0; i < 2; i++) {
for (int32_t j = 0; j < 3; j++) {
side->axis[i].xyzw[j] /= scale.xy[i];
}
}
return;
}

vec3_t axis[2];
TextureAxisForPlane(&planes[side->plane], &axis[0], &axis[1]);

Expand Down