qbsp: brush primitives support

This commit is contained in:
Eric Wasylishen 2016-10-04 22:37:05 -06:00
parent 6be8d50bce
commit cc12810da3
3 changed files with 227 additions and 38 deletions

View File

@ -27,6 +27,7 @@
#include "qbsp.h"
#include "parser.h"
#include "wad.h"
#define info_player_start 1
#define info_player_deathmatch 2
@ -325,6 +326,7 @@ typedef enum {
TX_QUARK_TYPE1 = 1,
TX_QUARK_TYPE2 = 2,
TX_VALVE_220 = 3,
TX_BRUSHPRIM = 4
} texcoord_style_t;
static texcoord_style_t
@ -474,6 +476,88 @@ SetTexinfo_Valve220(vec3_t axis[2], const vec_t shift[2], const vec_t scale[2],
out->vecs[1][3] = shift[1];
}
/*
ComputeAxisBase()
from q3map2
computes the base texture axis for brush primitive texturing
note: ComputeAxisBase here and in editor code must always BE THE SAME!
warning: special case behaviour of atan2( y, x ) <-> atan( y / x ) might not be the same everywhere when x == 0
rotation by (0,RotY,RotZ) assigns X to normal
*/
static void ComputeAxisBase( const vec3_t normal_unsanitized, vec3_t texX, vec3_t texY ){
vec_t RotY, RotZ;
vec3_t normal;
VectorCopy(normal_unsanitized, normal);
/* do some cleaning */
if ( fabs( normal[ 0 ] ) < 1e-6 ) {
normal[ 0 ] = 0.0f;
}
if ( fabs( normal[ 1 ] ) < 1e-6 ) {
normal[ 1 ] = 0.0f;
}
if ( fabs( normal[ 2 ] ) < 1e-6 ) {
normal[ 2 ] = 0.0f;
}
/* compute the two rotations around y and z to rotate x to normal */
RotY = -atan2( normal[ 2 ], sqrt( normal[ 1 ] * normal[ 1 ] + normal[ 0 ] * normal[ 0 ] ) );
RotZ = atan2( normal[ 1 ], normal[ 0 ] );
/* rotate (0,1,0) and (0,0,1) to compute texX and texY */
texX[ 0 ] = -sin( RotZ );
texX[ 1 ] = cos( RotZ );
texX[ 2 ] = 0;
/* the texY vector is along -z (t texture coorinates axis) */
texY[ 0 ] = -sin( RotY ) * cos( RotZ );
texY[ 1 ] = -sin( RotY ) * sin( RotZ );
texY[ 2 ] = -cos( RotY );
}
static void
SetTexinfo_BrushPrimitives(const vec3_t texMat[2], const vec3_t faceNormal, int texWidth, int texHeight, texinfo_t *out)
{
vec3_t texX, texY;
ComputeAxisBase( faceNormal, texX, texY );
/*
derivation of the conversion below:
classic BSP texture vecs to texture coordinates:
u = (dot(vert, out->vecs[0]) + out->vecs[3]) / texWidth
brush primitives: (starting with q3map2 code, then rearranging it to look like the classic formula)
u = (texMat[0][0] * dot(vert, texX)) + (texMat[0][1] * dot(vert, texY)) + texMat[0][2]
factor out vert:
u = (vert[0] * (texX[0] * texMat[0][0] + texY[0] * texMat[0][1]))
+ (vert[1] * (texX[1] * texMat[0][0] + texY[1] * texMat[0][1]))
+ (vert[2] * (texX[2] * texMat[0][0] + texY[2] * texMat[0][1]))
+ texMat[0][2];
multiplying that by 1 = (texWidth / texWidth) gives us something in the same shape as the classic formula,
so we can get out->vecs.
*/
out->vecs[0][0] = texWidth * ((texX[0] * texMat[0][0]) + (texY[0] * texMat[0][1]));
out->vecs[0][1] = texWidth * ((texX[1] * texMat[0][0]) + (texY[1] * texMat[0][1]));
out->vecs[0][2] = texWidth * ((texX[2] * texMat[0][0]) + (texY[2] * texMat[0][1]));
out->vecs[0][3] = texWidth * texMat[0][2];
out->vecs[1][0] = texHeight * ((texX[0] * texMat[1][0]) + (texY[0] * texMat[1][1]));
out->vecs[1][1] = texHeight * ((texX[1] * texMat[1][0]) + (texY[1] * texMat[1][1]));
out->vecs[1][2] = texHeight * ((texX[2] * texMat[1][0]) + (texY[2] * texMat[1][1]));
out->vecs[1][3] = texHeight * texMat[1][2];
}
static void
ParsePlaneDef(parser_t *parser, vec3_t planepts[3])
{
@ -533,32 +617,83 @@ ParseValve220TX(parser_t *parser, vec3_t axis[2], vec_t shift[2],
}
static void
ParseTextureDef(parser_t *parser, texinfo_t *tx,
ParseBrushPrimTX(parser_t *parser, vec3_t texMat[2])
{
ParseToken(parser, PARSE_SAMELINE);
if (strcmp(parser->token, "("))
goto parse_error;
for (int i = 0; i < 2; i++) {
ParseToken(parser, PARSE_SAMELINE);
if (strcmp(parser->token, "("))
goto parse_error;
for (int j = 0; j < 3; j++) {
ParseToken(parser, PARSE_SAMELINE);
texMat[i][j] = atof(parser->token);
}
ParseToken(parser, PARSE_SAMELINE);
if (strcmp(parser->token, ")"))
goto parse_error;
}
ParseToken(parser, PARSE_SAMELINE);
if (strcmp(parser->token, ")"))
goto parse_error;
return;
parse_error:
Error("line %d: couldn't parse Brush Primitives texture info", parser->linenum);
}
static void
ParseTextureDef(parser_t *parser, const mapbrush_t *brush, texinfo_t *tx,
vec3_t planepts[3], const plane_t *plane)
{
vec3_t texMat[2];
vec3_t axis[2];
vec_t shift[2], rotate, scale[2];
texcoord_style_t tx_type;
int width, height;
memset(tx, 0, sizeof(*tx));
ParseToken(parser, PARSE_SAMELINE);
tx->miptex = FindMiptex(parser->token);
ParseToken(parser, PARSE_SAMELINE);
if (!strcmp(parser->token, "[")) {
parser->unget = true;
ParseValve220TX(parser, axis, shift, &rotate, scale);
tx_type = TX_VALVE_220;
} else {
shift[0] = atof(parser->token);
if (brush->format == brushformat_t::BRUSH_PRIMITIVES) {
ParseBrushPrimTX(parser, texMat);
tx_type = TX_BRUSHPRIM;
ParseToken(parser, PARSE_SAMELINE);
shift[1] = atof(parser->token);
tx->miptex = FindMiptex(parser->token);
EnsureTexturesLoaded();
const texture_t *texture = WADList_GetTexture(parser->token);
width = texture ? texture->width : 64;
height = texture ? texture->height : 64;
// throw away 3 extra values at end of line
ParseExtendedTX(parser);
} else if (brush->format == brushformat_t::NORMAL) {
ParseToken(parser, PARSE_SAMELINE);
rotate = atof(parser->token);
tx->miptex = FindMiptex(parser->token);
ParseToken(parser, PARSE_SAMELINE);
scale[0] = atof(parser->token);
ParseToken(parser, PARSE_SAMELINE);
scale[1] = atof(parser->token);
tx_type = ParseExtendedTX(parser);
if (!strcmp(parser->token, "[")) {
parser->unget = true;
ParseValve220TX(parser, axis, shift, &rotate, scale);
tx_type = TX_VALVE_220;
} else {
shift[0] = atof(parser->token);
ParseToken(parser, PARSE_SAMELINE);
shift[1] = atof(parser->token);
ParseToken(parser, PARSE_SAMELINE);
rotate = atof(parser->token);
ParseToken(parser, PARSE_SAMELINE);
scale[0] = atof(parser->token);
ParseToken(parser, PARSE_SAMELINE);
scale[1] = atof(parser->token);
tx_type = ParseExtendedTX(parser);
}
}
if (!planepts || !plane)
@ -572,6 +707,9 @@ ParseTextureDef(parser_t *parser, texinfo_t *tx,
case TX_VALVE_220:
SetTexinfo_Valve220(axis, shift, scale, tx);
break;
case TX_BRUSHPRIM:
SetTexinfo_BrushPrimitives(texMat, plane->normal, width, height, tx);
break;
case TX_QUAKED:
default:
SetTexinfo_QuakeEd(plane, shift, rotate, scale, tx);
@ -580,7 +718,7 @@ ParseTextureDef(parser_t *parser, texinfo_t *tx,
}
static std::unique_ptr<mapface_t>
ParseBrushFace(parser_t *parser, const mapentity_t *entity)
ParseBrushFace(parser_t *parser, const mapbrush_t *brush, const mapentity_t *entity)
{
vec3_t planepts[3], planevecs[2];
vec_t length;
@ -600,7 +738,7 @@ ParseBrushFace(parser_t *parser, const mapentity_t *entity)
length = VectorNormalize(plane->normal);
plane->dist = DotProduct(planepts[1], plane->normal);
ParseTextureDef(parser, &tx, planepts, plane);
ParseTextureDef(parser, brush, &tx, planepts, plane);
if (length < NORMAL_EPSILON) {
Message(msgWarning, warnNoPlaneNormal, parser->linenum);
@ -629,11 +767,33 @@ ParseBrush(parser_t *parser, const mapentity_t *entity)
{
mapbrush_t brush;
// ericw -- brush primitives
if (!ParseToken(parser, PARSE_NORMAL))
Error("Unexpected EOF after { beginning brush");
if (!strcmp(parser->token, "(")) {
brush.format = brushformat_t::NORMAL;
parser->unget = true;
} else {
brush.format = brushformat_t::BRUSH_PRIMITIVES;
// optional
if (!strcmp(parser->token, "brushDef")) {
if (!ParseToken(parser, PARSE_NORMAL))
Error("Brush primitives: unexpected EOF (nothing after brushDef)");
}
// mandatory
if (strcmp(parser->token, "{"))
Error("Brush primitives: expected second { at beginning of brush, got \"%s\"", parser->token);
}
// ericw -- end brush primitives
while (ParseToken(parser, PARSE_NORMAL)) {
if (!strcmp(parser->token, "}"))
break;
std::unique_ptr<mapface_t> face = ParseBrushFace(parser, entity);
std::unique_ptr<mapface_t> face = ParseBrushFace(parser, &brush, entity);
if (face.get() == nullptr)
continue;
@ -658,6 +818,16 @@ ParseBrush(parser_t *parser, const mapentity_t *entity)
brush.numfaces++;
map.faces.push_back(*face);
}
// ericw -- brush primitives - there should be another closing }
if (brush.format == brushformat_t::BRUSH_PRIMITIVES) {
if (!ParseToken(parser, PARSE_NORMAL))
Error("Brush primitives: unexpected EOF (no closing brace)");
if (strcmp(parser->token, "}"))
Error("Brush primitives: Expected }, got: %s", parser->token);
}
// ericw -- end brush primitives
return brush;
}

View File

@ -515,26 +515,20 @@ CreateHulls(void)
}
}
wad_t *wadlist = NULL;
static bool wadlist_tried_loading = false;
/*
=================
ProcessFile
=================
*/
static void
ProcessFile(void)
void
EnsureTexturesLoaded()
{
const char *wadstring;
char *defaultwad;
wad_t *wadlist;
// load brushes and entities
LoadMapFile();
if (options.fOnlyents) {
UpdateEntLump();
if (wadlist_tried_loading)
return;
}
wadlist_tried_loading = true;
wadlist = NULL;
wadstring = ValueForKey(pWorldEnt(), "_wad");
if (!wadstring[0])
@ -543,7 +537,7 @@ ProcessFile(void)
Message(msgWarning, warnNoWadKey);
else
wadlist = WADList_Init(wadstring);
if (!wadlist) {
if (wadstring[0])
Message(msgWarning, warnNoValidWads);
@ -557,7 +551,26 @@ ProcessFile(void)
Message(msgLiteral, "Using default WAD: %s\n", defaultwad);
FreeMem(defaultwad, OTHER, strlen(options.szMapName) + 5);
}
}
/*
=================
ProcessFile
=================
*/
static void
ProcessFile(void)
{
// load brushes and entities
LoadMapFile();
if (options.fOnlyents) {
UpdateEntLump();
return;
}
// this can happen earlier if brush primitives are in use, because we need texture sizes then
EnsureTexturesLoaded();
// init the tables to be shared by all models
BeginBSPFile();

View File

@ -514,12 +514,17 @@ typedef struct mapface_s {
int linenum;
} mapface_t;
enum class brushformat_t {
NORMAL, BRUSH_PRIMITIVES
};
class mapbrush_t {
public:
int firstface;
int numfaces;
brushformat_t format;
mapbrush_t() : firstface(0), numfaces(0) {}
mapbrush_t() : firstface(0), numfaces(0), format(brushformat_t::NORMAL) {}
const mapface_t &face(int i) const;
} ;
@ -573,6 +578,7 @@ typedef struct mapdata_s {
extern mapdata_t map;
extern mapentity_t *pWorldEnt();
void EnsureTexturesLoaded();
void LoadMapFile(void);
int FindMiptex(const char *name);