Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

/**
* @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;
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");

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) {
// 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;
}
Comment thread
Copilot marked this conversation as resolved.

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

Expand Down
Loading