void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) { int i; Com_MatchToken( buf_p, "(" ); for (i = 0 ; i < z ; i++) { Com_Parse2DMatrix (buf_p, y, x, m + i * x*y); } Com_MatchToken( buf_p, ")" ); }
/* ================= ParseRawBrush parses the sides into buildBrush->sides[], nothing else. no validation, back plane removal, etc. ================= */ static void ParseRawBrush( bool onlyLights ) { side_t *side; float planePoints[3][3]; int planenum; char name[MAX_SHADERPATH]; char shader[MAX_SHADERPATH]; shaderInfo_t *si; token_t token; vects_t vects; int flags; buildBrush->numsides = 0; buildBrush->detail = false; if( g_bBrushPrimit == BRUSH_RADIANT ) Com_CheckToken( mapfile, "{" ); while( 1 ) { if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES|SC_COMMENT_SEMICOLON, &token )) break; if( !com.stricmp( token.string, "}" )) break; if( g_bBrushPrimit == BRUSH_RADIANT ) { while( 1 ) { if( com.strcmp( token.string, "(" )) Com_ReadToken( mapfile, 0, &token ); else break; Com_ReadToken( mapfile, SC_ALLOW_NEWLINES, &token ); } } Com_SaveToken( mapfile, &token ); if( buildBrush->numsides >= MAX_BUILD_SIDES ) Sys_Break( "Entity %i, Brush %i MAX_BUILD_SIDES\n", buildBrush->entityNum, buildBrush->brushNum ); side = &buildBrush->sides[ buildBrush->numsides ]; Mem_Set( side, 0, sizeof( *side )); buildBrush->numsides++; // read the three point plane definition Com_Parse1DMatrix( mapfile, 3, planePoints[0] ); Com_Parse1DMatrix( mapfile, 3, planePoints[1] ); Com_Parse1DMatrix( mapfile, 3, planePoints[2] ); // read the texture matrix if( g_bBrushPrimit == BRUSH_RADIANT ) Com_Parse2DMatrix( mapfile, 2, 3, (float *)side->texMat ); // read the texturedef or shadername Com_ReadToken( mapfile, SC_ALLOW_PATHNAMES|SC_PARSE_GENERIC, &token ); com.strncpy( name, token.string, sizeof( name )); if( g_bBrushPrimit == BRUSH_WORLDCRAFT_22 || g_bBrushPrimit == BRUSH_GEARCRAFT_40 ) // Worldcraft 2.2+ { // texture U axis Com_ReadToken( mapfile, 0, &token ); if( com.strcmp( token.string, "[" )) Sys_Break( "missing '[' in texturedef (U)\n" ); Com_ReadFloat( mapfile, false, &vects.hammer.UAxis[0] ); Com_ReadFloat( mapfile, false, &vects.hammer.UAxis[1] ); Com_ReadFloat( mapfile, false, &vects.hammer.UAxis[2] ); Com_ReadFloat( mapfile, false, &vects.hammer.shift[0] ); Com_ReadToken( mapfile, 0, &token ); if( com.strcmp( token.string, "]" )) Sys_Break( "missing ']' in texturedef (U)\n" ); // texture V axis Com_ReadToken( mapfile, 0, &token ); if( com.strcmp( token.string, "[" )) Sys_Break( "missing '[' in texturedef (V)\n" ); Com_ReadFloat( mapfile, false, &vects.hammer.VAxis[0] ); Com_ReadFloat( mapfile, false, &vects.hammer.VAxis[1] ); Com_ReadFloat( mapfile, false, &vects.hammer.VAxis[2] ); Com_ReadFloat( mapfile, false, &vects.hammer.shift[1] ); Com_ReadToken( mapfile, 0, &token ); if( com.strcmp( token.string, "]" )) Sys_Break( "missing ']' in texturedef (V)\n"); // texture rotation is implicit in U/V axes. Com_ReadToken( mapfile, 0, &token ); vects.hammer.rotate = 0; // texure scale Com_ReadFloat( mapfile, false, &vects.hammer.scale[0] ); Com_ReadFloat( mapfile, false, &vects.hammer.scale[1] ); if( g_bBrushPrimit == BRUSH_GEARCRAFT_40 ) { Com_ReadLong( mapfile, false, &flags ); // read gearcraft flags Com_SkipRestOfLine( mapfile ); // gearcraft lightmap scale and rotate if( flags & GEARBOX_DETAIL ) side->compileFlags |= C_DETAIL; } } else if( g_bBrushPrimit == BRUSH_WORLDCRAFT_21 || g_bBrushPrimit == BRUSH_QUARK ) { // worldcraft 2.1-, old Radiant, QuArK Com_ReadFloat( mapfile, false, &vects.hammer.shift[0] ); Com_ReadFloat( mapfile, false, &vects.hammer.shift[1] ); Com_ReadFloat( mapfile, false, &vects.hammer.rotate ); Com_ReadFloat( mapfile, false, &vects.hammer.scale[0] ); Com_ReadFloat( mapfile, SC_COMMENT_SEMICOLON, &vects.hammer.scale[1] ); } // set default flags and values com.sprintf( shader, "textures/%s", name ); if( onlyLights ) si = &shaderInfo[0]; else si = ShaderInfoForShader( shader ); side->shaderInfo = si; side->surfaceFlags = si->surfaceFlags; side->contentFlags = si->contentFlags; side->compileFlags = si->compileFlags; side->value = si->value; // bias texture shift for non-radiant sides if( g_bBrushPrimit != BRUSH_RADIANT && si->globalTexture == false ) { vects.hammer.shift[0] -= (floor( vects.hammer.shift[0] / si->shaderWidth ) * si->shaderWidth); vects.hammer.shift[1] -= (floor( vects.hammer.shift[1] / si->shaderHeight ) * si->shaderHeight); } // historically, there are 3 integer values at the end of a brushside line in a .map file. // in quake 3, the only thing that mattered was the first of these three values, which // was previously the content flags. and only then did a single bit matter, the detail // bit. because every game has its own special flags for specifying detail, the // traditionally game-specified BASECONT_DETAIL flag was overridden for Q3Map 2.3.0 // by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but // is stored in compileFlags, as opposed to contentFlags, for multiple-game // portability. :sigh: if( g_bBrushPrimit != BRUSH_QUARK && Com_ReadToken( mapfile, SC_COMMENT_SEMICOLON, &token )) { // overwrite shader values directly from .map file Com_SaveToken( mapfile, &token ); Com_ReadLong( mapfile, false, &flags ); Com_ReadLong( mapfile, false, NULL ); Com_ReadLong( mapfile, false, NULL ); if( flags & C_DETAIL ) side->compileFlags |= C_DETAIL; } if( mapfile->TXcommand == '1' || mapfile->TXcommand == '2' ) { // we are QuArK mode and need to translate some numbers to align textures its way // from QuArK, the texture vectors are given directly from the three points vec3_t texMat[2]; float dot22, dot23, dot33, mdet, aa, bb, dd; int j, k; g_bBrushPrimit = BRUSH_QUARK; // we can detect it only here k = mapfile->TXcommand - '0'; for( j = 0; j < 3; j++ ) texMat[1][j] = (planePoints[k][j] - planePoints[0][j]) * (0.0078125f); // QuArK magic value k = 3 - k; for( j = 0; j < 3; j++ ) texMat[0][j] = (planePoints[k][j] - planePoints[0][j]) * (0.0078125f); // QuArK magic value dot22 = DotProduct( texMat[0], texMat[0] ); dot23 = DotProduct( texMat[0], texMat[1] ); dot33 = DotProduct( texMat[1], texMat[1] ); mdet = dot22 * dot33 - dot23 * dot23; if( mdet < 1E-6 && mdet > -1E-6 ) { aa = bb = dd = 0; MsgDev( D_WARN, "Entity %i, Brush %i: degenerate QuArK-style texture: \n", buildBrush->entityNum, buildBrush->brushNum ); } else { mdet = 1.0 / mdet; aa = dot33 * mdet; bb = -dot23 * mdet; dd = dot22 * mdet; } for( j = 0; j < 3; j++ ) { vects.quark.vecs[0][j] = aa * texMat[0][j] + bb * texMat[1][j]; vects.quark.vecs[1][j] = -(bb * texMat[0][j] + dd * texMat[1][j]); } vects.quark.vecs[0][3] = -DotProduct( vects.quark.vecs[0], planePoints[0] ); vects.quark.vecs[1][3] = -DotProduct( vects.quark.vecs[1], planePoints[0] ); } // find the plane number planenum = MapPlaneFromPoints( planePoints ); if( planenum == -1 ) { MsgDev( D_ERROR, "Entity %i, Brush %i: plane with no normal\n", buildBrush->entityNum, buildBrush->brushNum ); continue; } side->planenum = planenum; if( g_bBrushPrimit == BRUSH_QUARK ) { // QuArK format completely matched with internal // FIXME: don't calculate vecs, use QuArK texMat instead ? Mem_Copy( side->vecs, vects.quark.vecs, sizeof( side->vecs )); } else if( g_bBrushPrimit != BRUSH_RADIANT ) { vec3_t vecs[2]; float ang, sinv, cosv, ns, nt; int i, j, sv, tv; if( g_bBrushPrimit == BRUSH_WORLDCRAFT_21 ) TextureAxisFromPlane( &mapplanes[planenum], vecs[0], vecs[1] ); if( !vects.hammer.scale[0] ) vects.hammer.scale[0] = 1.0f; if( !vects.hammer.scale[1] ) vects.hammer.scale[1] = 1.0f; if( g_bBrushPrimit == BRUSH_WORLDCRAFT_21 ) { // rotate axis if( vects.hammer.rotate == 0 ) { sinv = 0; cosv = 1; } else if( vects.hammer.rotate == 90 ) { sinv = 1; cosv = 0; } else if( vects.hammer.rotate == 180 ) { sinv = 0; cosv = -1; } else if( vects.hammer.rotate == 270 ) { sinv = -1; cosv = 0; } else { ang = vects.hammer.rotate / 180 * M_PI; sinv = sin( ang ); cosv = cos( ang ); } if( vecs[0][0] ) sv = 0; else if( vecs[0][1] ) sv = 1; else sv = 2; if( vecs[1][0] ) tv = 0; else if( vecs[1][1] ) tv = 1; else tv = 2; for( i = 0; i < 2; i++ ) { ns = cosv * vecs[i][sv] - sinv * vecs[i][tv]; nt = sinv * vecs[i][sv] + cosv * vecs[i][tv]; vecs[i][sv] = ns; vecs[i][tv] = nt; } for( i = 0; i < 2; i++ ) for( j = 0; j < 3; j++ ) side->vecs[i][j] = vecs[i][j] / vects.hammer.scale[i]; } else if( g_bBrushPrimit == BRUSH_WORLDCRAFT_22 || g_bBrushPrimit == BRUSH_GEARCRAFT_40 ) { float scale; scale = 1.0f / vects.hammer.scale[0]; VectorScale( vects.hammer.UAxis, scale, side->vecs[0] ); scale = 1.0f / vects.hammer.scale[1]; VectorScale( vects.hammer.VAxis, scale, side->vecs[1] ); } // add shifts side->vecs[0][3] = vects.hammer.shift[0]; side->vecs[1][3] = vects.hammer.shift[1]; } } if( g_bBrushPrimit == BRUSH_RADIANT ) { Com_SaveToken( mapfile, &token ); Com_CheckToken( mapfile, "}" ); Com_CheckToken( mapfile, "}" ); } }