void MiniMapSetupBrushes(void) { int i, b, compileFlags; bspBrush_t *brush; bspShader_t *shader; shaderInfo_t *si; /* note it */ Sys_FPrintf(SYS_VRB, "--- MiniMapSetupBrushes ---\n"); /* allocate */ if(opaqueBrushes == NULL) opaqueBrushes = safe_malloc(numBSPBrushes / 8 + 1); /* clear */ memset(opaqueBrushes, 0, numBSPBrushes / 8 + 1); numOpaqueBrushes = 0; /* walk the list of worldspawn brushes */ for(i = 0; i < minimap.model->numBSPBrushes; i++) { /* get brush */ b = minimap.model->firstBSPBrush + i; brush = &bspBrushes[b]; #if 0 /* check all sides */ compileFlags = 0; for(j = 0; j < brush->numSides; j++) { /* do bsp shader calculations */ side = &bspBrushSides[brush->firstSide + j]; shader = &bspShaders[side->shaderNum]; /* get shader info */ si = ShaderInfoForShader(shader->shader); if(si == NULL) continue; /* or together compile flags */ compileFlags |= si->compileFlags; } #else shader = &bspShaders[brush->shaderNum]; si = ShaderInfoForShader(shader->shader); if(si == NULL) compileFlags = 0; else compileFlags = si->compileFlags; #endif /* determine if this brush is solid */ if((compileFlags & (C_SOLID | C_SKY)) == C_SOLID) { opaqueBrushes[b >> 3] |= (1 << (b & 7)); numOpaqueBrushes++; maxOpaqueBrush = i; } }
void WriteTexFile( char* name){ FILE *texfile; char filename[1024]; int i; sprintf (filename, "%s.tex", name); if(!compile_map){ RestoreSurfaceFlags(filename); } Sys_Printf("Writing %s ...\n", filename); texfile = fopen (filename, "w"); fprintf( texfile, "TEXFILE\n"); fprintf( texfile, "%i\n", numBSPShaders); for ( i = 0 ; i < numBSPShaders ; i++ ) { shaderInfo_t *se = ShaderInfoForShader(bspShaders[i].shader); fprintf( texfile, "\n%i %f %f %f", bspShaders[i].surfaceFlags, se->color[0], se->color[1], se->color[2]); bspShaders[i].surfaceFlags = i; } fclose(texfile); }
/* ============ EmitShader ============ */ int EmitShader( const char *shader ) { int i; shaderInfo_t *si; if ( !shader ) { shader = "noshader"; } for ( i = 0 ; i < numShaders ; i++ ) { if ( !Q_stricmp( shader, dshaders[i].shader ) ) { return i; } } if ( i == MAX_MAP_SHADERS ) { Error( "MAX_MAP_SHADERS" ); } numShaders++; strcpy( dshaders[i].shader, shader ); si = ShaderInfoForShader( shader ); dshaders[i].surfaceFlags = si->surfaceFlags; dshaders[i].contentFlags = si->contents; return i; }
int EmitShader( const char *shader, int *contentFlags, int *surfaceFlags ){ int i; shaderInfo_t *si; /* handle special cases */ if ( shader == NULL ) { shader = "noshader"; } /* try to find an existing shader */ for ( i = 0; i < numBSPShaders; i++ ) { /* ydnar: handle custom surface/content flags */ if ( surfaceFlags != NULL && bspShaders[ i ].surfaceFlags != *surfaceFlags ) { continue; } if ( contentFlags != NULL && bspShaders[ i ].contentFlags != *contentFlags ) { continue; } /* compare name */ if ( !Q_stricmp( shader, bspShaders[ i ].shader ) ) { return i; } } // i == numBSPShaders /* get shaderinfo */ si = ShaderInfoForShader( shader ); /* emit a new shader */ AUTOEXPAND_BY_REALLOC_BSP( Shaders, 1024 ); numBSPShaders++; strcpy( bspShaders[ i ].shader, shader ); bspShaders[ i ].surfaceFlags = si->surfaceFlags; bspShaders[ i ].contentFlags = si->contentFlags; /* handle custom content/surface flags */ if ( surfaceFlags != NULL ) { bspShaders[ i ].surfaceFlags = *surfaceFlags; } if ( contentFlags != NULL ) { bspShaders[ i ].contentFlags = *contentFlags; } /* recursively emit any damage shaders */ if ( si->damageShader != NULL && si->damageShader[ 0 ] != '\0' ) { Sys_FPrintf( SYS_VRB, "Shader %s has damage shader %s\n", si->shader, si->damageShader ); EmitShader( si->damageShader, NULL, NULL ); } /* return it */ return i; }
/* ================ ShaderForLayer ================ */ shaderInfo_t *ShaderForLayer( int minlayer, int maxlayer, const char *shadername ) { char shader[ MAX_QPATH ]; if ( minlayer == maxlayer ) { sprintf( shader, "textures/%s_%d", shadername, maxlayer ); } else { sprintf( shader, "textures/%s_%dto%d", shadername, minlayer, maxlayer ); } return ShaderInfoForShader( shader ); }
static void ConvertShader(FILE * f, dshader_t * shader, int shaderNum) { shaderInfo_t *si; char *c, filename[1024]; // get shader si = ShaderInfoForShader(shader->shader); if(si == NULL) { Sys_Printf("WARNING: NULL shader in BSP\n"); return; } // set bitmap filename if(si->shader[0] != '*') strcpy(filename, si->shader); else sprintf(filename, "%s.tga", si->shader); #if 0 for(c = filename; *c != '\0'; c++) if(*c == '/') *c = '\\'; #endif // print shader info fprintf(f, "\t*MATERIAL\t%d\t{\r\n", shaderNum); fprintf(f, "\t\t*MATERIAL_NAME\t\"%s\"\r\n", shader->shader); fprintf(f, "\t\t*MATERIAL_CLASS\t\"Standard\"\r\n"); fprintf(f, "\t\t*MATERIAL_DIFFUSE\t%f\t%f\t%f\r\n", si->color[0], si->color[1], si->color[2]); fprintf(f, "\t\t*MATERIAL_SHADING Phong\r\n"); // print map info fprintf(f, "\t\t*MAP_DIFFUSE\t{\r\n"); fprintf(f, "\t\t\t*MAP_NAME\t\"%s\"\r\n", shader->shader); fprintf(f, "\t\t\t*MAP_CLASS\t\"Bitmap\"\r\n"); fprintf(f, "\t\t\t*MAP_SUBNO\t1\r\n"); fprintf(f, "\t\t\t*MAP_AMOUNT\t1.0\r\n"); fprintf(f, "\t\t\t*MAP_TYPE\tScreen\r\n"); fprintf(f, "\t\t\t*BITMAP\t\"%s\"\r\n", filename); fprintf(f, "\t\t\t*BITMAP_FILTER\tPyramidal\r\n"); fprintf(f, "\t\t}\r\n"); fprintf(f, "\t}\r\n"); }
/* ===================== InitSurfacesForTesting Builds structures to speed the ray tracing against surfaces ===================== */ void InitSurfacesForTesting( void ) { int i, j; dsurface_t *dsurf; surfaceTest_t *test; drawVert_t *dvert; shaderInfo_t *si; for ( i = 0 ; i < numDrawSurfaces ; i++ ) { dsurf = &drawSurfaces[ i ]; if ( !dsurf->numIndexes && !dsurf->patchWidth ) { continue; } // don't make surfaces for transparent objects // because we want light to pass through them si = ShaderInfoForShader( dshaders[ dsurf->shaderNum].shader ); if ( (si->contents & CONTENTS_TRANSLUCENT) && !(si->surfaceFlags & SURF_ALPHASHADOW) ) { continue; } test = malloc( sizeof( *test ) ); surfaceTest[i] = test; ClearBounds( test->mins, test->maxs ); dvert = &drawVerts[ dsurf->firstVert ]; for ( j = 0 ; j < dsurf->numVerts ; j++, dvert++ ) { AddPointToBounds( dvert->xyz, test->mins, test->maxs ); } SphereFromBounds( test->mins, test->maxs, test->origin, &test->radius ); if ( dsurf->surfaceType == MST_TRIANGLE_SOUP || dsurf->surfaceType == MST_PLANAR ) { FacetsForTriangleSurface( dsurf, si, test ); } else if ( dsurf->surfaceType == MST_PATCH ) { FacetsForPatch( dsurf, si, test ); } } }
void LoadSurfaceExtraFile( const char *path ) { char srfPath[ 1024 ]; surfaceExtra_t *se; int surfaceNum, size; byte *buffer; /* dummy check */ if( path == NULL || path[ 0 ] == '\0' ) return; /* load the file */ strcpy( srfPath, path ); StripExtension( srfPath ); strcat( srfPath, ".srf" ); Sys_Printf( "Loading %s\n", srfPath ); size = LoadFile( srfPath, (void**) &buffer ); if( size <= 0 ) { Sys_Printf( "WARNING: Unable to find surface file %s, using defaults.\n", srfPath ); return; } /* parse the file */ ParseFromMemory( (char *) buffer, size ); /* tokenize it */ while( 1 ) { /* test for end of file */ if( !GetToken( qtrue ) ) break; /* default? */ if( !Q_stricmp( token, "default" ) ) se = &seDefault; /* surface number */ else { surfaceNum = atoi( token ); if( surfaceNum < 0 || surfaceNum > MAX_MAP_DRAW_SURFS ) Error( "ReadSurfaceExtraFile(): %s, line %d: bogus surface num %d", srfPath, scriptline, surfaceNum ); while( surfaceNum >= numSurfaceExtras ) se = AllocSurfaceExtra(); se = &surfaceExtras[ surfaceNum ]; } /* handle { } section */ if( !GetToken( qtrue ) || strcmp( token, "{" ) ) Error( "ReadSurfaceExtraFile(): %s, line %d: { not found", srfPath, scriptline ); while( 1 ) { if( !GetToken( qtrue ) ) break; if( !strcmp( token, "}" ) ) break; /* shader */ if( !Q_stricmp( token, "shader" ) ) { GetToken( qfalse ); se->si = ShaderInfoForShader( token ); } /* parent surface number */ else if( !Q_stricmp( token, "parent" ) ) { GetToken( qfalse ); se->parentSurfaceNum = atoi( token ); } /* entity number */ else if( !Q_stricmp( token, "entity" ) ) { GetToken( qfalse ); se->entityNum = atoi( token ); } /* cast shadows */ else if( !Q_stricmp( token, "castShadows" ) ) { GetToken( qfalse ); se->castShadows = atoi( token ); } /* recv shadows */ else if( !Q_stricmp( token, "receiveShadows" ) ) { GetToken( qfalse ); se->recvShadows = atoi( token ); } /* lightmap sample size */ else if( !Q_stricmp( token, "sampleSize" ) ) { GetToken( qfalse ); se->sampleSize = atoi( token ); } /* longest curve */ else if( !Q_stricmp( token, "longestCurve" ) ) { GetToken( qfalse ); se->longestCurve = atof( token ); } /* lightmap axis vector */ else if( !Q_stricmp( token, "lightmapAxis" ) ) Parse1DMatrix( 3, se->lightmapAxis ); /* ignore all other tokens on the line */ while( TokenAvailable() ) GetToken( qfalse ); } } /* free the buffer */ free( buffer ); }
/* ================= 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, "}" ); } }
/* ================= ParseMapEntity parses a single entity out of a map file ================= */ static bool ParseMapEntity( bool onlyLights ) { epair_t *ep; token_t token; const char *classname, *value; float lightmapScale; char shader[ MAX_SHADERPATH ]; shaderInfo_t *celShader = NULL; brush_t *brush; parseMesh_t *patch; bool funcGroup; int castShadows, recvShadows; static bool map_type = false; if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES|SC_COMMENT_SEMICOLON, &token )) return false; // end of .map file if( com.stricmp( token.string, "{" )) Sys_Break( "ParseEntity: found %s instead {\n", token.string ); if( numEntities >= MAX_MAP_ENTITIES ) Sys_Break( "MAX_MAP_ENTITIES limit exceeded\n" ); entitySourceBrushes = 0; mapEnt = &entities[numEntities]; numEntities++; memset( mapEnt, 0, sizeof( *mapEnt )); mapEnt->mapEntityNum = numMapEntities; numMapEntities++; while( 1 ) { if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES|SC_COMMENT_SEMICOLON, &token )) Sys_Break( "ParseEntity: EOF without closing brace\n" ); if( !com.stricmp( token.string, "}" )) break; if( !com.stricmp( token.string, "{" )) { // parse a brush or patch if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES, &token )) break; if( !com.stricmp( token.string, "patchDef2" )) { numMapPatches++; ParsePatch( onlyLights ); g_bBrushPrimit = BRUSH_RADIANT; } else if( !com.stricmp( token.string, "terrainDef" )) { MsgDev( D_WARN, "Terrain entity parsing not supported in this build.\n" ); Com_SkipBracedSection( mapfile, 0 ); g_bBrushPrimit = BRUSH_RADIANT; } else if( !com.stricmp( token.string, "brushDef" )) { // parse brush primitive g_bBrushPrimit = BRUSH_RADIANT; ParseBrush( onlyLights ); } else { if( g_bBrushPrimit == BRUSH_RADIANT ) Sys_Break( "mixed brush primitive with another format\n" ); if( g_bBrushPrimit == BRUSH_UNKNOWN ) g_bBrushPrimit = BRUSH_WORLDCRAFT_21; // QuArK or WorldCraft map (unknown at this point) Com_SaveToken( mapfile, &token ); ParseBrush( onlyLights ); } entitySourceBrushes++; } else { // parse a key / value pair ep = ParseEpair( mapfile, &token ); if( !com.strcmp( ep->key, "mapversion" )) { if( com.atoi( ep->value ) == VALVE_FORMAT ) { Msg( "Valve Format Map detected\n" ); g_bBrushPrimit = BRUSH_WORLDCRAFT_22; } else if( com.atoi( ep->value ) == GEARBOX_FORMAT ) { Msg( "Gearcraft Format Map detected\n" ); g_bBrushPrimit = BRUSH_GEARCRAFT_40; } else g_bBrushPrimit = BRUSH_WORLDCRAFT_21; } if( ep->key[0] != '\0' && ep->value[0] != '\0' ) { ep->next = mapEnt->epairs; mapEnt->epairs = ep; } } } if( !map_type && g_bBrushPrimit != BRUSH_UNKNOWN ) { MAPTYPE(); map_type = true; } classname = ValueForKey( mapEnt, "classname" ); if( onlyLights && com.strnicmp( classname, "light", 5 )) { numEntities--; return true; } if( !com.stricmp( "func_group", classname )) funcGroup = true; else funcGroup = false; // worldspawn (and func_groups) default to cast/recv shadows in worldspawn group if( funcGroup || mapEnt->mapEntityNum == 0 ) { castShadows = WORLDSPAWN_CAST_SHADOWS; recvShadows = WORLDSPAWN_RECV_SHADOWS; } else // other entities don't cast any shadows, but recv worldspawn shadows { castShadows = ENTITY_CAST_SHADOWS; recvShadows = ENTITY_RECV_SHADOWS; } // get explicit shadow flags GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows ); // get lightmap scaling value for this entity if( com.strcmp( "", ValueForKey( mapEnt, "lightmapscale" )) || com.strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ))) { // get lightmap scale from entity lightmapScale = FloatForKey( mapEnt, "lightmapscale" ); if( lightmapScale <= 0.0f ) lightmapScale = FloatForKey( mapEnt, "_lightmapscale" ); if( lightmapScale > 0.0f ) Msg( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale ); } else lightmapScale = 0.0f; // get cel shader :) for this entity value = ValueForKey( mapEnt, "_celshader" ); if( value[0] == '\0' ) value = ValueForKey( &entities[0], "_celshader" ); if( value[0] != '\0' ) { com.snprintf( shader, sizeof( shader ), "textures/%s", value ); celShader = ShaderInfoForShader( shader ); Msg( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader ); } else celShader = NULL; // attach stuff to everything in the entity for( brush = mapEnt->brushes; brush != NULL; brush = brush->next ) { brush->entityNum = mapEnt->mapEntityNum; brush->castShadows = castShadows; brush->recvShadows = recvShadows; brush->lightmapScale = lightmapScale; brush->celShader = celShader; } for( patch = mapEnt->patches; patch != NULL; patch = patch->next ) { patch->entityNum = mapEnt->mapEntityNum; patch->castShadows = castShadows; patch->recvShadows = recvShadows; patch->lightmapScale = lightmapScale; patch->celShader = celShader; } SetEntityBounds( mapEnt ); // load shader index map (equivalent to old terrain alphamap) LoadEntityIndexMap( mapEnt ); // get entity origin and adjust brushes GetVectorForKey( mapEnt, "origin", mapEnt->origin ); if( mapEnt->origin[0] || mapEnt->origin[1] || mapEnt->origin[2] ) AdjustBrushesForOrigin( mapEnt ); // group_info entities are just for editor grouping if( !com.stricmp( "group_info", classname )) { numEntities--; return true; } // group entities are just for editor convenience, toss all brushes into worldspawn if( funcGroup ) { MoveBrushesToWorld( mapEnt ); numEntities--; return true; } return true; }
static void PopulateWithPicoModel(int castShadows, picoModel_t * model, matrix_t transform) { int i, j, k, numSurfaces, numIndexes; picoSurface_t *surface; picoShader_t *shader; picoVec_t *xyz, *st; picoIndex_t *indexes; traceInfo_t ti; traceWinding_t tw; /* dummy check */ if(model == NULL || transform == NULL) return; /* get info */ numSurfaces = PicoGetModelNumSurfaces(model); /* walk the list of surfaces in this model and fill out the info structs */ for(i = 0; i < numSurfaces; i++) { /* get surface */ surface = PicoGetModelSurface(model, i); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* get shader (fixme: support shader remapping) */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) continue; ti.si = ShaderInfoForShader(PicoGetShaderName(shader)); if(ti.si == NULL) continue; /* translucent surfaces that are neither alphashadow or lightfilter don't cast shadows */ if((ti.si->compileFlags & C_NODRAW)) continue; if((ti.si->compileFlags & C_TRANSLUCENT) && !(ti.si->compileFlags & C_ALPHASHADOW) && !(ti.si->compileFlags & C_LIGHTFILTER)) continue; /* setup trace info */ ti.castShadows = castShadows; ti.surfaceNum = -1; ti.skipGrid = qtrue; // also ignore picomodels when skipping patches /* setup trace winding */ memset(&tw, 0, sizeof(tw)); tw.infoNum = AddTraceInfo(&ti); tw.numVerts = 3; /* get info */ numIndexes = PicoGetSurfaceNumIndexes(surface); indexes = PicoGetSurfaceIndexes(surface, 0); /* walk the triangle list */ for(j = 0; j < numIndexes; j += 3, indexes += 3) { for(k = 0; k < 3; k++) { xyz = PicoGetSurfaceXYZ(surface, indexes[k]); st = PicoGetSurfaceST(surface, 0, indexes[k]); VectorCopy(xyz, tw.v[k].xyz); Vector2Copy(st, tw.v[k].st); MatrixTransformPoint2(transform, tw.v[k].xyz); } FilterTraceWindingIntoNodes_r(&tw, headNodeNum); } } }
/* ================= LoadSurfaceExtraFile reads a surface info file (<map>.srf) ================= */ void LoadSurfaceExtraFile( const char *path ) { char srfPath[MAX_SYSPATH]; surfaceExtra_t *se; int surfaceNum; script_t *script; token_t token; if( path == NULL || path[0] == '\0' ) return; com.strcpy( srfPath, path ); FS_StripExtension( srfPath ); FS_DefaultExtension( srfPath, ".srf" ); Msg( "Loading %s\n", srfPath ); script = (void *)Com_OpenScript( srfPath, NULL, 0 ); if( !script ) Sys_Break( "unable to load %s\n", srfPath ); // q3map is always crashed if this missed while( 1 ) { if( !Com_ReadToken( script, SC_ALLOW_NEWLINES|SC_PARSE_GENERIC, &token )) break; if( !com.stricmp( token.string, "default" )) se = &seDefault; else { surfaceNum = com.atoi( token.string ); if( surfaceNum < 0 || surfaceNum > MAX_MAP_DRAW_SURFS ) Sys_Error( "ReadSurfaceExtraFile(): %s, line %d: bogus surface num %d", srfPath, token.line, surfaceNum ); while( surfaceNum >= numSurfaceExtras ) se = AllocSurfaceExtra(); se = &surfaceExtras[surfaceNum]; } // handle { } section if( !Com_ReadToken( script, SC_ALLOW_NEWLINES|SC_PARSE_GENERIC, &token ) || com.strcmp( token.string, "{" )) Sys_Error( "ReadSurfaceExtraFile(): %s, line %d: { not found\n", srfPath, token.line ); while( 1 ) { if( !Com_ReadToken( script, SC_ALLOW_NEWLINES|SC_PARSE_GENERIC, &token )) break; if( !com.strcmp( token.string, "}" )) break; if( !com.stricmp( token.string, "shader" )) { Com_ReadToken( script, SC_ALLOW_PATHNAMES|SC_PARSE_GENERIC, &token ); se->si = ShaderInfoForShader( token.string ); } else if( !com.stricmp( token.string, "parent" )) { Com_ReadLong( script, false, &se->parentSurfaceNum ); } else if( !com.stricmp( token.string, "entity" )) { Com_ReadLong( script, false, &se->entityNum ); } else if( !com.stricmp( token.string, "castShadows" )) { Com_ReadLong( script, false, &se->castShadows ); } else if( !com.stricmp( token.string, "receiveShadows" )) { Com_ReadLong( script, false, &se->recvShadows ); } else if( !com.stricmp( token.string, "sampleSize" )) { Com_ReadLong( script, false, &se->sampleSize ); } else if( !com.stricmp( token.string, "longestCurve" )) { Com_ReadFloat( script, false, &se->longestCurve ); } else if( !com.stricmp( token.string, "lightmapAxis" )) Com_Parse1DMatrix( script, 3, se->lightmapAxis ); // ignore all other tokens on the line Com_SkipRestOfLine( script ); } } Com_CloseScript( script ); }
static void ConvertBrush(FILE * f, int num, bspBrush_t * brush, vec3_t origin) { int i, j; bspBrushSide_t *side; side_t *buildSide; bspShader_t *shader; char *texture; bspPlane_t *plane; plane_t *buildPlane; vec3_t pts[3]; bspDrawVert_t *vert[3]; int valid; /* start brush */ fprintf(f, "\t// brush %d\n", num); fprintf(f, "\t{\n"); fprintf(f, "\tbrushDef\n"); fprintf(f, "\t{\n"); /* clear out build brush */ for(i = 0; i < buildBrush->numsides; i++) { buildSide = &buildBrush->sides[i]; if(buildSide->winding != NULL) { FreeWinding(buildSide->winding); buildSide->winding = NULL; } } buildBrush->numsides = 0; /* iterate through bsp brush sides */ for(i = 0; i < brush->numSides; i++) { /* get side */ side = &bspBrushSides[brush->firstSide + i]; /* get shader */ if(side->shaderNum < 0 || side->shaderNum >= numBSPShaders) continue; shader = &bspShaders[side->shaderNum]; if(!Q_stricmp(shader->shader, "default") || !Q_stricmp(shader->shader, "noshader")) continue; /* get plane */ plane = &bspPlanes[side->planeNum]; /* add build side */ buildSide = &buildBrush->sides[buildBrush->numsides]; buildBrush->numsides++; /* tag it */ buildSide->shaderInfo = ShaderInfoForShader(shader->shader); buildSide->planenum = side->planeNum; buildSide->winding = NULL; } /* make brush windings */ if(!CreateBrushWindings(buildBrush)) return; /* iterate through build brush sides */ for(i = 0; i < buildBrush->numsides; i++) { /* get build side */ buildSide = &buildBrush->sides[i]; /* get plane */ buildPlane = &mapplanes[buildSide->planenum]; /* dummy check */ if(buildSide->shaderInfo == NULL || buildSide->winding == NULL) continue; // st-texcoords -> texMat block // start out with dummy VectorSet(buildSide->texMat[0], 1 / 32.0, 0, 0); VectorSet(buildSide->texMat[1], 0, 1 / 32.0, 0); // find surface for this side (by brute force) // surface format: // - meshverts point in pairs of three into verts // - (triangles) // - find the triangle that has most in common with our side GetBestSurfaceTriangleMatchForBrushside(buildSide, vert); valid = 0; if(vert[0] && vert[1] && vert[2]) { int i; vec3_t texX, texY; vec3_t xy1I, xy1J, xy1K; vec2_t stI, stJ, stK; vec_t D, D0, D1, D2; ComputeAxisBase(buildPlane->normal, texX, texY); VectorSet(xy1I, DotProduct(vert[0]->xyz, texX), DotProduct(vert[0]->xyz, texY), 1); VectorSet(xy1J, DotProduct(vert[1]->xyz, texX), DotProduct(vert[1]->xyz, texY), 1); VectorSet(xy1K, DotProduct(vert[2]->xyz, texX), DotProduct(vert[2]->xyz, texY), 1); stI[0] = vert[0]->st[0]; stI[1] = vert[0]->st[1]; stJ[0] = vert[1]->st[0]; stJ[1] = vert[1]->st[1]; stK[0] = vert[2]->st[0]; stK[1] = vert[2]->st[1]; // - solve linear equations: // - (x, y) := xyz . (texX, texY) // - st[i] = texMat[i][0]*x + texMat[i][1]*y + texMat[i][2] // (for three vertices) D = Det3x3(xy1I[0], xy1I[1], 1, xy1J[0], xy1J[1], 1, xy1K[0], xy1K[1], 1); if(D != 0) { for(i = 0; i < 2; ++i) { D0 = Det3x3(stI[i], xy1I[1], 1, stJ[i], xy1J[1], 1, stK[i], xy1K[1], 1); D1 = Det3x3(xy1I[0], stI[i], 1, xy1J[0], stJ[i], 1, xy1K[0], stK[i], 1); D2 = Det3x3(xy1I[0], xy1I[1], stI[i], xy1J[0], xy1J[1], stJ[i], xy1K[0], xy1K[1], stK[i]); VectorSet(buildSide->texMat[i], D0 / D, D1 / D, D2 / D); valid = 1; } } else fprintf(stderr, "degenerate triangle found when solving texMat equations for\n(%f %f %f) (%f %f %f) (%f %f %f)\n( %f %f %f )\n( %f %f %f ) -> ( %f %f )\n( %f %f %f ) -> ( %f %f )\n( %f %f %f ) -> ( %f %f )\n", buildPlane->normal[0], buildPlane->normal[1], buildPlane->normal[2], vert[0]->normal[0], vert[0]->normal[1], vert[0]->normal[2], texX[0], texX[1], texX[2], texY[0], texY[1], texY[2], vert[0]->xyz[0], vert[0]->xyz[1], vert[0]->xyz[2], xy1I[0], xy1I[1], vert[1]->xyz[0], vert[1]->xyz[1], vert[1]->xyz[2], xy1J[0], xy1J[1], vert[2]->xyz[0], vert[2]->xyz[1], vert[2]->xyz[2], xy1K[0], xy1K[1]); } else if(strncmp(buildSide->shaderInfo->shader, "textures/common/", 16)) fprintf(stderr, "no matching triangle for brushside using %s (hopefully nobody can see this side anyway)\n", buildSide->shaderInfo->shader); /* get texture name */ if(!Q_strncasecmp(buildSide->shaderInfo->shader, "textures/", 9)) texture = buildSide->shaderInfo->shader + 9; else texture = buildSide->shaderInfo->shader; /* get plane points and offset by origin */ for(j = 0; j < 3; j++) { VectorAdd(buildSide->winding->p[j], origin, pts[j]); //% pts[ j ][ 0 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 0 ] * SNAP_FLOAT_TO_INT + 0.5f ); //% pts[ j ][ 1 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 1 ] * SNAP_FLOAT_TO_INT + 0.5f ); //% pts[ j ][ 2 ] = SNAP_INT_TO_FLOAT * floor( pts[ j ][ 2 ] * SNAP_FLOAT_TO_INT + 0.5f ); } /* print brush side */ /* ( 640 24 -224 ) ( 448 24 -224 ) ( 448 -232 -224 ) common/caulk 0 48 0 0.500000 0.500000 0 0 0 */ fprintf(f, "\t\t( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( %.3f %.3f %.3f ) ( ( %.8f %.8f %.8f ) ( %.8f %.8f %.8f ) ) %s %d 0 0\n", pts[0][0], pts[0][1], pts[0][2], pts[1][0], pts[1][1], pts[1][2], pts[2][0], pts[2][1], pts[2][2], buildSide->texMat[0][0], buildSide->texMat[0][1], buildSide->texMat[0][2], buildSide->texMat[1][0], buildSide->texMat[1][1], buildSide->texMat[1][2], texture, // DEBUG: valid ? 0 : C_DETAIL 0); // TODO write brush primitives format here } /* end brush */ fprintf(f, "\t}\n"); fprintf(f, "\t}\n\n"); }
/* ================= ParsePatch Creates a mapDrawSurface_t from the patch text ================= */ void ParsePatch( void ) { vec_t info[5]; int i, j; parseMesh_t *pm; char texture[MAX_QPATH]; char shader[MAX_QPATH]; mesh_t m; drawVert_t *verts; epair_t *ep; MatchToken( "{" ); // get texture GetToken (qtrue); strcpy( texture, token ); // save the shader name for retexturing if ( numMapIndexedShaders == MAX_MAP_BRUSHSIDES ) { Error( "MAX_MAP_BRUSHSIDES" ); } strcpy( mapIndexedShaders[numMapIndexedShaders], texture ); numMapIndexedShaders++; Parse1DMatrix( 5, info ); m.width = info[0]; m.height = info[1]; m.verts = verts = malloc( m.width * m.height * sizeof( m.verts[0] ) ); if ( m.width < 0 || m.width > MAX_PATCH_SIZE || m.height < 0 || m.height > MAX_PATCH_SIZE ) { Error("ParsePatch: bad size"); } MatchToken( "(" ); for ( j = 0 ; j < m.width ; j++ ) { MatchToken( "(" ); for ( i = 0 ; i < m.height ; i++ ) { Parse1DMatrix( 5, verts[i*m.width+j].xyz ); } MatchToken( ")" ); } MatchToken( ")" ); // if brush primitives format, we may have some epairs to ignore here GetToken(qtrue); if (g_bBrushPrimit!=BPRIMIT_OLDBRUSHES && strcmp(token,"}")) { // NOTE: we leak that! ep = ParseEpair(); } else UnGetToken(); MatchToken( "}" ); MatchToken( "}" ); if ( noCurveBrushes ) { return; } // find default flags and values pm = malloc( sizeof( *pm ) ); memset( pm, 0, sizeof( *pm ) ); sprintf( shader, "textures/%s", texture ); pm->shaderInfo = ShaderInfoForShader( shader ); pm->mesh = m; // link to the entity pm->next = mapent->patches; mapent->patches = pm; }
void InsertModel(char *name, int frame, matrix_t transform, matrix_t nTransform, remap_t * remap, shaderInfo_t * celShader, int eNum, int castShadows, int recvShadows, int spawnFlags, float lightmapScale, int lightmapSampleSize, float shadeAngle) { int i, j, k, s, numSurfaces; matrix_t identity; picoModel_t *model; picoShader_t *shader; picoSurface_t *surface; shaderInfo_t *si; mapDrawSurface_t *ds; bspDrawVert_t *dv; char *picoShaderName; char shaderName[MAX_QPATH]; picoVec_t *xyz, *normal, *st; byte *color; picoIndex_t *indexes; remap_t *rm, *glob; double normalEpsilon_save; double distanceEpsilon_save; /* get model */ model = LoadModel(name, frame); if(model == NULL) return; /* handle null matrix */ if(transform == NULL) { MatrixIdentity(identity); transform = identity; } /* create transform matrix for normals */ #if 0 MatrixCopy(transform, nTransform); if(MatrixInverse(nTransform)) { Sys_FPrintf(SYS_VRB, "WARNING: Can't invert model transform matrix, using transpose instead\n"); MatrixTranspose(transform, nTransform); } #endif /* fix bogus lightmap scale */ if(lightmapScale <= 0.0f) lightmapScale = 1.0f; /* fix bogus shade angle */ if(shadeAngle <= 0.0f) shadeAngle = 0.0f; /* each surface on the model will become a new map drawsurface */ numSurfaces = PicoGetModelNumSurfaces(model); //% Sys_FPrintf( SYS_VRB, "Model %s has %d surfaces\n", name, numSurfaces ); for(s = 0; s < numSurfaces; s++) { /* get surface */ surface = PicoGetModelSurface(model, s); if(surface == NULL) continue; /* only handle triangle surfaces initially (fixme: support patches) */ if(PicoGetSurfaceType(surface) != PICO_TRIANGLES) continue; /* fix the surface's normals */ PicoFixSurfaceNormals(surface); /* allocate a surface (ydnar: gs mods) */ ds = AllocDrawSurface(SURFACE_TRIANGLES); ds->entityNum = eNum; ds->castShadows = castShadows; ds->recvShadows = recvShadows; /* get shader name */ shader = PicoGetSurfaceShader(surface); if(shader == NULL) picoShaderName = ""; else picoShaderName = PicoGetShaderName(shader); /* handle shader remapping */ glob = NULL; for(rm = remap; rm != NULL; rm = rm->next) { if(rm->from[0] == '*' && rm->from[1] == '\0') glob = rm; else if(!Q_stricmp(picoShaderName, rm->from)) { Sys_FPrintf(SYS_VRB, "Remapping %s to %s\n", picoShaderName, rm->to); picoShaderName = rm->to; glob = NULL; break; } } if(glob != NULL) { Sys_FPrintf(SYS_VRB, "Globbing %s to %s\n", picoShaderName, glob->to); picoShaderName = glob->to; } /* shader renaming for sof2 */ if(renameModelShaders) { strcpy(shaderName, picoShaderName); StripExtension(shaderName); if(spawnFlags & 1) strcat(shaderName, "_RMG_BSP"); else strcat(shaderName, "_BSP"); si = ShaderInfoForShader(shaderName); } else { si = ShaderInfoForShader(picoShaderName); // Tr3B: HACK to support the messy Doom 3 materials provided by .ASE files if(!si->explicitDef) { picoShaderName = PicoGetShaderMapName(shader); Q_strncpyz(shaderName, picoShaderName, sizeof(shaderName)); StripExtension(shaderName); i = 0; while(shaderName[i]) { if(shaderName[i] == '\\') shaderName[i] = '/'; i++; } if(strstr(shaderName, "base/")) { si = ShaderInfoForShader(strstr(shaderName, "base/") + strlen("base/")); Sys_FPrintf(SYS_WRN, "WARNING: Applied .ASE material loader HACK to '%s' -> '%s'\n", picoShaderName, si->shader); } } } /* set shader */ ds->shaderInfo = si; /* force to meta? */ if((si != NULL && si->forceMeta) || (spawnFlags & 4)) /* 3rd bit */ ds->type = SURFACE_FORCED_META; /* fix the surface's normals (jal: conditioned by shader info) */ //if(!(spawnFlags & 64) && (shadeAngle == 0.0f || ds->type != SURFACE_FORCED_META)) // PicoFixSurfaceNormals(surface); /* set sample size */ if(lightmapSampleSize > 0.0f) ds->sampleSize = lightmapSampleSize; /* set lightmap scale */ if(lightmapScale > 0.0f) ds->lightmapScale = lightmapScale; /* set shading angle */ if(shadeAngle > 0.0f) ds->shadeAngleDegrees = shadeAngle; /* set particulars */ ds->numVerts = PicoGetSurfaceNumVertexes(surface); ds->verts = safe_malloc(ds->numVerts * sizeof(ds->verts[0])); memset(ds->verts, 0, ds->numVerts * sizeof(ds->verts[0])); ds->numIndexes = PicoGetSurfaceNumIndexes(surface); ds->indexes = safe_malloc(ds->numIndexes * sizeof(ds->indexes[0])); memset(ds->indexes, 0, ds->numIndexes * sizeof(ds->indexes[0])); /* copy vertexes */ for(i = 0; i < ds->numVerts; i++) { /* get vertex */ dv = &ds->verts[i]; /* xyz and normal */ xyz = PicoGetSurfaceXYZ(surface, i); VectorCopy(xyz, dv->xyz); MatrixTransformPoint2(transform, dv->xyz); normal = PicoGetSurfaceNormal(surface, i); VectorCopy(normal, dv->normal); MatrixTransformNormal2(nTransform, dv->normal); VectorNormalize2(dv->normal, dv->normal); /* ydnar: tek-fu celshading support for flat shaded shit */ if(flat) { dv->st[0] = si->stFlat[0]; dv->st[1] = si->stFlat[1]; } /* ydnar: gs mods: added support for explicit shader texcoord generation */ else if(si->tcGen) { /* project the texture */ dv->st[0] = DotProduct(si->vecs[0], dv->xyz); dv->st[1] = DotProduct(si->vecs[1], dv->xyz); } /* normal texture coordinates */ else { st = PicoGetSurfaceST(surface, 0, i); dv->st[0] = st[0]; dv->st[1] = st[1]; } /* set lightmap/color bits */ color = PicoGetSurfaceColor(surface, 0, i); dv->paintColor[0] = color[0] / 255.0f; dv->paintColor[1] = color[1] / 255.0f; dv->paintColor[2] = color[2] / 255.0f; dv->paintColor[3] = color[3] / 255.0f; for(j = 0; j < MAX_LIGHTMAPS; j++) { dv->lightmap[j][0] = 0.0f; dv->lightmap[j][1] = 0.0f; dv->lightColor[j][0] = 255; dv->lightColor[j][1] = 255; dv->lightColor[j][2] = 255; dv->lightColor[j][3] = 255; } } /* copy indexes */ indexes = PicoGetSurfaceIndexes(surface, 0); for(i = 0; i < ds->numIndexes; i++) ds->indexes[i] = indexes[i]; /* set cel shader */ ds->celShader = celShader; /* ydnar: giant hack land: generate clipping brushes for model triangles */ if(si->clipModel || (spawnFlags & 2)) /* 2nd bit */ { vec3_t points[4], backs[3]; vec4_t plane, reverse, pa, pb, pc; /* temp hack */ if(!si->clipModel && (((si->compileFlags & C_TRANSLUCENT) && !(si->compileFlags & C_COLLISION)) || !(si->compileFlags & C_SOLID))) continue; /* walk triangle list */ for(i = 0; i < ds->numIndexes; i += 3) { /* overflow hack */ AUTOEXPAND_BY_REALLOC(mapplanes, (nummapplanes + 64) << 1, allocatedmapplanes, 1024); /* make points and back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; /* copy xyz */ VectorCopy(dv->xyz, points[j]); VectorCopy(dv->xyz, backs[j]); /* find nearest axial to normal and push back points opposite */ /* note: this doesn't work as well as simply using the plane of the triangle, below */ for(k = 0; k < 3; k++) { if(fabs(dv->normal[k]) >= fabs(dv->normal[(k + 1) % 3]) && fabs(dv->normal[k]) >= fabs(dv->normal[(k + 2) % 3])) { backs[j][k] += dv->normal[k] < 0.0f ? 64.0f : -64.0f; break; } } } VectorCopy(points[0], points[3]); // for cyclic usage /* make plane for triangle */ // div0: add some extra spawnflags: // 0: snap normals to axial planes for extrusion // 8: extrude with the original normals // 16: extrude only with up/down normals (ideal for terrain) // 24: extrude by distance zero (may need engine changes) if(PlaneFromPoints(plane, points[0], points[1], points[2], qtrue)) { vec3_t bestNormal; float backPlaneDistance = 2; if(spawnFlags & 8) // use a DOWN normal { if(spawnFlags & 16) { // 24: normal as is, and zero width (broken) VectorCopy(plane, bestNormal); } else { // 8: normal as is VectorCopy(plane, bestNormal); } } else { if(spawnFlags & 16) { // 16: UP/DOWN normal VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } else { // 0: axial normal if(fabs(plane[0]) > fabs(plane[1])) // x>y if(fabs(plane[1]) > fabs(plane[2])) // x>y, y>z VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // x>y, z>=y if(fabs(plane[0]) > fabs(plane[2])) // x>z, z>=y VectorSet(bestNormal, (plane[0] >= 0 ? 1 : -1), 0, 0); else // z>=x, x>y VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); else // y>=x if(fabs(plane[1]) > fabs(plane[2])) // y>z, y>=x VectorSet(bestNormal, 0, (plane[1] >= 0 ? 1 : -1), 0); else // z>=y, y>=x VectorSet(bestNormal, 0, 0, (plane[2] >= 0 ? 1 : -1)); } } /* build a brush */ buildBrush = AllocBrush(48); buildBrush->entityNum = mapEntityNum; buildBrush->original = buildBrush; buildBrush->contentShader = si; buildBrush->compileFlags = si->compileFlags; buildBrush->contentFlags = si->contentFlags; buildBrush->generatedClipBrush = qtrue; normalEpsilon_save = normalEpsilon; distanceEpsilon_save = distanceEpsilon; if(si->compileFlags & C_STRUCTURAL) // allow forced structural brushes here { buildBrush->detail = qfalse; // only allow EXACT matches when snapping for these (this is mostly for caulk brushes inside a model) if(normalEpsilon > 0) normalEpsilon = 0; if(distanceEpsilon > 0) distanceEpsilon = 0; } else buildBrush->detail = qtrue; /* regenerate back points */ for(j = 0; j < 3; j++) { /* get vertex */ dv = &ds->verts[ds->indexes[i + j]]; // shift by some units VectorMA(dv->xyz, -64.0f, bestNormal, backs[j]); // 64 prevents roundoff errors a bit } /* make back plane */ VectorScale(plane, -1.0f, reverse); reverse[3] = -plane[3]; if((spawnFlags & 24) != 24) reverse[3] += DotProduct(bestNormal, plane) * backPlaneDistance; // that's at least sqrt(1/3) backPlaneDistance, unless in DOWN mode; in DOWN mode, we are screwed anyway if we encounter a plane that's perpendicular to the xy plane) if(PlaneFromPoints(pa, points[2], points[1], backs[1], qtrue) && PlaneFromPoints(pb, points[1], points[0], backs[0], qtrue) && PlaneFromPoints(pc, points[0], points[2], backs[2], qtrue)) { /* set up brush sides */ buildBrush->numsides = 5; buildBrush->sides[0].shaderInfo = si; for(j = 1; j < buildBrush->numsides; j++) buildBrush->sides[j].shaderInfo = NULL; // don't emit these faces as draw surfaces, should make smaller BSPs; hope this works buildBrush->sides[0].planenum = FindFloatPlane(plane, plane[3], 3, points); buildBrush->sides[1].planenum = FindFloatPlane(pa, pa[3], 2, &points[1]); // pa contains points[1] and points[2] buildBrush->sides[2].planenum = FindFloatPlane(pb, pb[3], 2, &points[0]); // pb contains points[0] and points[1] buildBrush->sides[3].planenum = FindFloatPlane(pc, pc[3], 2, &points[2]); // pc contains points[2] and points[0] (copied to points[3] buildBrush->sides[4].planenum = FindFloatPlane(reverse, reverse[3], 3, backs); } else { free(buildBrush); continue; } normalEpsilon = normalEpsilon_save; distanceEpsilon = distanceEpsilon_save; /* add to entity */ if(CreateBrushWindings(buildBrush)) { AddBrushBevels(); //% EmitBrushes( buildBrush, NULL, NULL ); buildBrush->next = entities[mapEntityNum].brushes; entities[mapEntityNum].brushes = buildBrush; entities[mapEntityNum].numBrushes++; } else free(buildBrush); } } } } }
void ParsePatch( qboolean onlyLights ) { vec_t info[ 5 ]; int i, j, k; parseMesh_t *pm; char texture[ MAX_QPATH ]; char shader[ MAX_QPATH ]; mesh_t m; bspDrawVert_t *verts; epair_t *ep; vec4_t delta, delta2, delta3; qboolean degenerate; float longestCurve; int maxIterations; MatchToken( "{" ); /* get texture */ GetToken( qtrue ); strcpy( texture, token ); Parse1DMatrix( 5, info ); m.width = info[0]; m.height = info[1]; m.verts = verts = (bspDrawVert_t *)safe_malloc( m.width * m.height * sizeof( m.verts[0] ) ); if( m.width < 0 || m.width > MAX_PATCH_SIZE || m.height < 0 || m.height > MAX_PATCH_SIZE ) Error( "ParsePatch: bad size" ); MatchToken( "(" ); for( j = 0; j < m.width ; j++ ) { MatchToken( "(" ); for( i = 0; i < m.height ; i++ ) { Parse1DMatrix( 5, verts[ i * m.width + j ].xyz ); /* ydnar: fix colors */ for( k = 0; k < MAX_LIGHTMAPS; k++ ) { verts[ i * m.width + j ].color[ k ][ 0 ] = 255; verts[ i * m.width + j ].color[ k ][ 1 ] = 255; verts[ i * m.width + j ].color[ k ][ 2 ] = 255; verts[ i * m.width + j ].color[ k ][ 3 ] = 255; } } MatchToken( ")" ); } MatchToken( ")" ); // if brush primitives format, we may have some epairs to ignore here GetToken(qtrue); if (g_bBrushPrimit!=BPRIMIT_OLDBRUSHES && strcmp(token,"}")) { // NOTE: we leak that! ep = ParseEPair(); } else UnGetToken(); MatchToken( "}" ); MatchToken( "}" ); /* short circuit */ if( noCurveBrushes || onlyLights ) return; /* ydnar: delete and warn about degenerate patches */ j = (m.width * m.height); VectorClear( delta ); delta[ 3 ] = 0; degenerate = qtrue; /* find first valid vector */ for( i = 1; i < j && delta[ 3 ] == 0; i++ ) { VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta ); delta[ 3 ] = VectorNormalize( delta, delta ); } /* secondary degenerate test */ if( delta[ 3 ] == 0 ) degenerate = qtrue; else { /* if all vectors match this or are zero, then this is a degenerate patch */ for( i = 1; i < j && degenerate == qtrue; i++ ) { VectorSubtract( m.verts[ 0 ].xyz, m.verts[ i ].xyz, delta2 ); delta2[ 3 ] = VectorNormalize( delta2, delta2 ); if( delta2[ 3 ] != 0 ) { /* create inverse vector */ VectorCopy( delta2, delta3 ); delta3[ 3 ] = delta2[ 3 ]; VectorNegate( delta3, delta3 ); /* compare */ if( VectorCompare( delta, delta2 ) == qfalse && VectorCompare( delta, delta3 ) == qfalse ) degenerate = qfalse; } } } /* warn and select degenerate patch */ if( degenerate ) { Sys_Warning( mapEnt->mapEntityNum, entitySourceBrushes, "Degenerate patch" ); free( m.verts ); return; } /* find longest curve on the mesh */ longestCurve = 0.0f; maxIterations = 0; for( j = 0; j + 2 < m.width; j += 2 ) { for( i = 0; i + 2 < m.height; i += 2 ) { ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ i * m.width + (j + 1) ].xyz, verts[ i * m.width + (j + 2) ].xyz ); /* row */ ExpandLongestCurve( &longestCurve, verts[ i * m.width + j ].xyz, verts[ (i + 1) * m.width + j ].xyz, verts[ (i + 2) * m.width + j ].xyz ); /* col */ ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ i * m.width + (j + 1) ].xyz, verts[ i * m.width + (j + 2) ].xyz ); /* row */ ExpandMaxIterations( &maxIterations, patchSubdivisions, verts[ i * m.width + j ].xyz, verts[ (i + 1) * m.width + j ].xyz, verts[ (i + 2) * m.width + j ].xyz ); /* col */ } } /* allocate patch mesh */ pm = (parseMesh_t *)safe_malloc( sizeof( *pm ) ); memset( pm, 0, sizeof( *pm ) ); /* ydnar: add entity/brush numbering */ pm->entityNum = mapEnt->mapEntityNum; pm->mapEntityNum = mapEnt->mapEntityNum; pm->brushNum = entitySourceBrushes; /* set shader */ sprintf( shader, "textures/%s", texture ); pm->shaderInfo = ShaderInfoForShader( shader ); /* set mesh */ pm->mesh = m; /* set longest curve */ pm->longestCurve = longestCurve; pm->maxIterations = maxIterations; /* link to the entity */ pm->next = mapEnt->patches; mapEnt->patches = pm; }
void CreateMapFogs(void) { int i; entity_t *entity; brush_t *brush; fog_t *fog; vec3_t invFogDir; const char *globalFog; /* skip? */ if(nofog) return; /* note it */ Sys_FPrintf(SYS_VRB, "--- CreateMapFogs ---\n"); /* walk entities */ for(i = 0; i < numEntities; i++) { /* get entity */ entity = &entities[i]; /* walk entity brushes */ for(brush = entity->brushes; brush != NULL; brush = brush->next) { /* ignore non-fog brushes */ if(brush->contentShader->fogParms == qfalse) continue; /* test limit */ if(numMapFogs >= MAX_MAP_FOGS) Error("Exceeded MAX_MAP_FOGS (%d)", MAX_MAP_FOGS); /* set up fog */ fog = &mapFogs[numMapFogs++]; fog->si = brush->contentShader; fog->brush = brush; fog->visibleSide = -1; /* if shader specifies an explicit direction, then find a matching brush side with an opposed normal */ if(VectorLength(fog->si->fogDir)) { /* flip it */ VectorScale(fog->si->fogDir, -1.0f, invFogDir); /* find the brush side */ for(i = 0; i < brush->numsides; i++) { if(VectorCompare(invFogDir, mapplanes[brush->sides[i].planenum].normal)) { fog->visibleSide = i; //% Sys_Printf( "Brush num: %d Side num: %d\n", fog->brushNum, fog->visibleSide ); break; } } } } } /* ydnar: global fog */ globalFog = ValueForKey(&entities[0], "_fog"); if(globalFog[0] == '\0') globalFog = ValueForKey(&entities[0], "fog"); if(globalFog[0] != '\0') { /* test limit */ if(numMapFogs >= MAX_MAP_FOGS) Error("Exceeded MAX_MAP_FOGS (%d) trying to add global fog", MAX_MAP_FOGS); /* note it */ Sys_FPrintf(SYS_VRB, "Map has global fog shader %s\n", globalFog); /* set up fog */ fog = &mapFogs[numMapFogs++]; fog->si = ShaderInfoForShader(globalFog); if(fog->si == NULL) Error("Invalid shader \"%s\" referenced trying to add global fog", globalFog); fog->brush = NULL; fog->visibleSide = -1; /* set as default fog */ defaultFogNum = numMapFogs - 1; /* mark all worldspawn brushes as fogged */ for(brush = entities[0].brushes; brush != NULL; brush = brush->next) ApplySurfaceParm("fog", &brush->contentFlags, NULL, &brush->compileFlags); } /* emit some stats */ Sys_FPrintf(SYS_VRB, "%9d fogs\n", numMapFogs); }
void AddTriangleModels(entity_t * e) { int num, frame, castShadows, recvShadows, spawnFlags; entity_t *e2; const char *targetName; const char *target, *model, *value; char shader[MAX_QPATH]; shaderInfo_t *celShader; float temp, baseLightmapScale, lightmapScale; float shadeAngle; int lightmapSampleSize; vec3_t origin, scale, angles; matrix_t rotation, rotationScaled, transform; epair_t *ep; remap_t *remap, *remap2; char *split; /* note it */ Sys_FPrintf(SYS_VRB, "--- AddTriangleModels ---\n"); /* get current brush entity targetname */ if(e == entities) targetName = ""; else { targetName = ValueForKey(e, "name"); /* misc_model entities target non-worldspawn brush model entities */ if(targetName[0] == '\0') return; } /* get lightmap scale */ /* vortex: added _ls key (short name of lightmapscale) */ baseLightmapScale = 0.0f; if(strcmp("", ValueForKey(e, "lightmapscale")) || strcmp("", ValueForKey(e, "_lightmapscale")) || strcmp("", ValueForKey(e, "_ls"))) { baseLightmapScale = FloatForKey(e, "lightmapscale"); if(baseLightmapScale <= 0.0f) baseLightmapScale = FloatForKey(e, "_lightmapscale"); if(baseLightmapScale <= 0.0f) baseLightmapScale = FloatForKey(e, "_ls"); if(baseLightmapScale < 0.0f) baseLightmapScale = 0.0f; if(baseLightmapScale > 0.0f) Sys_Printf("World Entity has lightmap scale of %.4f\n", baseLightmapScale); } /* walk the entity list */ for(num = 1; num < numEntities; num++) { /* get e2 */ e2 = &entities[num]; /* convert misc_models into raw geometry */ if(Q_stricmp("misc_model", ValueForKey(e2, "classname"))) continue; /* ydnar: added support for md3 models on non-worldspawn models */ target = ValueForKey(e2, "target"); if(strcmp(target, targetName)) continue; /* get model name */ model = ValueForKey(e2, "model"); if(model[0] == '\0') { Sys_Printf("WARNING: misc_model at %i %i %i without a model key\n", (int)origin[0], (int)origin[1], (int)origin[2]); continue; } /* get model frame */ frame = IntForKey(e2, "_frame"); /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */ if(e == entities) { castShadows = WORLDSPAWN_CAST_SHADOWS; recvShadows = WORLDSPAWN_RECV_SHADOWS; } /* other entities don't cast any shadows, but recv worldspawn shadows */ else { castShadows = ENTITY_CAST_SHADOWS; recvShadows = ENTITY_RECV_SHADOWS; } /* get explicit shadow flags */ GetEntityShadowFlags(e2, e, &castShadows, &recvShadows); /* get spawnflags */ spawnFlags = IntForKey(e2, "spawnflags"); /* Tr3B: added clipModel option */ spawnFlags |= (IntForKey(e2, "clipModel") > 0) ? 2 : 0; /* Tr3B: added forceMeta option */ spawnFlags |= (IntForKey(e2, "forceMeta") > 0) ? 4 : 0; /* get origin */ GetVectorForKey(e2, "origin", origin); VectorSubtract(origin, e->origin, origin); /* offset by parent */ /* get "angle" (yaw) or "angles" (pitch yaw roll) */ MatrixIdentity(rotation); angles[0] = angles[1] = angles[2] = 0.0f; value = ValueForKey(e2, "angle"); if(value[0] != '\0') { angles[1] = atof(value); MatrixFromAngles(rotation, angles[PITCH], angles[YAW], angles[ROLL]); } value = ValueForKey(e2, "angles"); if(value[0] != '\0') { sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]); MatrixFromAngles(rotation, angles[PITCH], angles[YAW], angles[ROLL]); } value = ValueForKey(e2, "rotation"); if(value[0] != '\0') { sscanf(value, "%f %f %f %f %f %f %f %f %f", &rotation[0], &rotation[1], &rotation[2], &rotation[4], &rotation[5], &rotation[6], &rotation[8], &rotation[9], &rotation[10]); } /* get scale */ scale[0] = scale[1] = scale[2] = 1.0f; temp = FloatForKey(e2, "modelscale"); if(temp != 0.0f) scale[0] = scale[1] = scale[2] = temp; value = ValueForKey(e2, "modelscale_vec"); if(value[0] != '\0') sscanf(value, "%f %f %f", &scale[0], &scale[1], &scale[2]); MatrixCopy(rotation, rotationScaled); MatrixMultiplyScale(rotationScaled, scale[0], scale[1], scale[2]); /* set transform matrix */ MatrixIdentity(transform); MatrixSetupTransformFromRotation(transform, rotationScaled, origin); /* get shader remappings */ remap = NULL; for(ep = e2->epairs; ep != NULL; ep = ep->next) { /* look for keys prefixed with "_remap" */ if(ep->key != NULL && ep->value != NULL && ep->key[0] != '\0' && ep->value[0] != '\0' && !Q_strncasecmp(ep->key, "_remap", 6)) { /* create new remapping */ remap2 = remap; remap = safe_malloc(sizeof(*remap)); remap->next = remap2; strcpy(remap->from, ep->value); /* split the string */ split = strchr(remap->from, ';'); if(split == NULL) { Sys_Printf("WARNING: Shader _remap key found in misc_model without a ; character\n"); free(remap); remap = remap2; continue; } /* store the split */ *split = '\0'; strcpy(remap->to, (split + 1)); /* note it */ //% Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", remap->from, remap->to ); } } /* ydnar: cel shader support */ value = ValueForKey(e2, "_celshader"); if(value[0] == '\0') value = ValueForKey(&entities[0], "_celshader"); if(value[0] != '\0') { sprintf(shader, "textures/%s", value); celShader = ShaderInfoForShader(shader); } else celShader = NULL; /* jal : entity based _samplesize */ lightmapSampleSize = 0; if(strcmp("", ValueForKey(e2, "_lightmapsamplesize"))) lightmapSampleSize = IntForKey(e2, "_lightmapsamplesize"); else if(strcmp("", ValueForKey(e2, "_samplesize"))) lightmapSampleSize = IntForKey(e2, "_samplesize"); if(lightmapSampleSize < 0) lightmapSampleSize = 0; if(lightmapSampleSize > 0.0f) Sys_Printf("misc_model has lightmap sample size of %.d\n", lightmapSampleSize); /* get lightmap scale */ /* vortex: added _ls key (short name of lightmapscale) */ lightmapScale = 0.0f; if(strcmp("", ValueForKey(e2, "lightmapscale")) || strcmp("", ValueForKey(e2, "_lightmapscale")) || strcmp("", ValueForKey(e2, "_ls"))) { lightmapScale = FloatForKey(e2, "lightmapscale"); if(lightmapScale <= 0.0f) lightmapScale = FloatForKey(e2, "_lightmapscale"); if(lightmapScale <= 0.0f) lightmapScale = FloatForKey(e2, "_ls"); if(lightmapScale < 0.0f) lightmapScale = 0.0f; if(lightmapScale > 0.0f) Sys_Printf("misc_model has lightmap scale of %.4f\n", lightmapScale); } /* jal : entity based _shadeangle */ shadeAngle = 0.0f; if(strcmp("", ValueForKey(e2, "_shadeangle"))) shadeAngle = FloatForKey(e2, "_shadeangle"); /* vortex' aliases */ else if(strcmp("", ValueForKey(mapEnt, "_smoothnormals"))) shadeAngle = FloatForKey(mapEnt, "_smoothnormals"); else if(strcmp("", ValueForKey(mapEnt, "_sn"))) shadeAngle = FloatForKey(mapEnt, "_sn"); else if(strcmp("", ValueForKey(mapEnt, "_smooth"))) shadeAngle = FloatForKey(mapEnt, "_smooth"); if(shadeAngle < 0.0f) shadeAngle = 0.0f; if(shadeAngle > 0.0f) Sys_Printf("misc_model has shading angle of %.4f\n", shadeAngle); /* insert the model */ InsertModel((char *)model, frame, transform, rotation, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale, lightmapSampleSize, shadeAngle); /* free shader remappings */ while(remap != NULL) { remap2 = remap->next; free(remap); remap = remap2; } } }
void AddTriangleModel(entity_t * e) { int frame, castShadows, recvShadows, spawnFlags; const char *name, *model, *value; char shader[MAX_QPATH]; shaderInfo_t *celShader; float temp, baseLightmapScale, lightmapScale; float shadeAngle; int lightmapSampleSize; vec3_t scale; matrix_t rotation, transform; epair_t *ep; remap_t *remap, *remap2; char *split; /* note it */ Sys_FPrintf(SYS_VRB, "--- AddTriangleModel ---\n"); /* get current brush entity name */ name = ValueForKey(e, "name"); /* misc_model entities target non-worldspawn brush model entities */ if(name[0] == '\0') return; /* get model name */ model = ValueForKey(e, "model"); if(model[0] == '\0') return; /* Tr3B: skip triggers and other entities */ if(!Q_stricmp(name, model)) return; /* get model frame */ frame = IntForKey(e, "_frame"); /* worldspawn (and func_groups) default to cast/recv shadows in worldspawn group */ if(e == entities) { castShadows = WORLDSPAWN_CAST_SHADOWS; recvShadows = WORLDSPAWN_RECV_SHADOWS; } /* other entities don't cast any shadows, but recv worldspawn shadows */ else { castShadows = ENTITY_CAST_SHADOWS; recvShadows = ENTITY_RECV_SHADOWS; } /* get explicit shadow flags */ GetEntityShadowFlags(NULL, e, &castShadows, &recvShadows); /* get spawnflags */ spawnFlags = IntForKey(e, "spawnflags"); /* Tr3B: added clipModel option */ spawnFlags |= (IntForKey(e, "clipModel") > 0) ? 2 : 0; /* Tr3B: added forceMeta option */ spawnFlags |= (IntForKey(e, "forceMeta") > 0) ? 4 : 0; /* get scale */ scale[0] = scale[1] = scale[2] = 1.0f; temp = FloatForKey(e, "modelscale"); if(temp != 0.0f) scale[0] = scale[1] = scale[2] = temp; value = ValueForKey(e, "modelscale_vec"); if(value[0] != '\0') sscanf(value, "%f %f %f", &scale[0], &scale[1], &scale[2]); /* set transform matrix */ MatrixIdentity(transform); MatrixMultiplyScale(transform, scale[0], scale[1], scale[2]); MatrixIdentity(rotation); /* get shader remappings */ remap = NULL; for(ep = e->epairs; ep != NULL; ep = ep->next) { /* look for keys prefixed with "_remap" */ if(ep->key != NULL && ep->value != NULL && ep->key[0] != '\0' && ep->value[0] != '\0' && !Q_strncasecmp(ep->key, "_remap", 6)) { /* create new remapping */ remap2 = remap; remap = safe_malloc(sizeof(*remap)); remap->next = remap2; strcpy(remap->from, ep->value); /* split the string */ split = strchr(remap->from, ';'); if(split == NULL) { Sys_Printf("WARNING: Shader _remap key found in misc_model without a ; character\n"); free(remap); remap = remap2; continue; } /* store the split */ *split = '\0'; strcpy(remap->to, (split + 1)); /* note it */ //% Sys_FPrintf( SYS_VRB, "Remapping %s to %s\n", remap->from, remap->to ); } } /* ydnar: cel shader support */ value = ValueForKey(e, "_celshader"); if(value[0] == '\0') value = ValueForKey(&entities[0], "_celshader"); if(value[0] != '\0') { sprintf(shader, "textures/%s", value); celShader = ShaderInfoForShader(shader); } else celShader = NULL; /* get lightmap scale */ /* jal : entity based _samplesize */ lightmapSampleSize = 0; if(strcmp("", ValueForKey(e, "_lightmapsamplesize"))) lightmapSampleSize = IntForKey(e, "_lightmapsamplesize"); else if(strcmp("", ValueForKey(e, "_samplesize"))) lightmapSampleSize = IntForKey(e, "_samplesize"); if(lightmapSampleSize < 0) lightmapSampleSize = 0; if(lightmapSampleSize > 0.0f) Sys_Printf(" has lightmap sample size of %.d\n", lightmapSampleSize); /* get lightmap scale */ /* vortex: added _ls key (short name of lightmapscale) */ lightmapScale = 0.0f; if(strcmp("", ValueForKey(e, "lightmapscale")) || strcmp("", ValueForKey(e, "_lightmapscale")) || strcmp("", ValueForKey(e, "_ls"))) { lightmapScale = FloatForKey(e, "lightmapscale"); if(lightmapScale <= 0.0f) lightmapScale = FloatForKey(e, "_lightmapscale"); if(lightmapScale <= 0.0f) lightmapScale = FloatForKey(e, "_ls"); if(lightmapScale < 0.0f) lightmapScale = 0.0f; if(lightmapScale > 0.0f) Sys_Printf("misc_model has lightmap scale of %.4f\n", lightmapScale); } /* jal : entity based _shadeangle */ shadeAngle = 0.0f; if(strcmp("", ValueForKey(e, "_shadeangle"))) shadeAngle = FloatForKey(e, "_shadeangle"); /* vortex' aliases */ else if(strcmp("", ValueForKey(e, "_smoothnormals"))) shadeAngle = FloatForKey(e, "_smoothnormals"); else if(strcmp("", ValueForKey(e, "_sn"))) shadeAngle = FloatForKey(e, "_sn"); else if(strcmp("", ValueForKey(e, "_smooth"))) shadeAngle = FloatForKey(e, "_smooth"); if(shadeAngle < 0.0f) shadeAngle = 0.0f; if(shadeAngle > 0.0f) Sys_Printf("misc_model has shading angle of %.4f\n", shadeAngle); /* insert the model */ InsertModel((char *)model, frame, transform, rotation, remap, celShader, mapEntityNum, castShadows, recvShadows, spawnFlags, lightmapScale, lightmapSampleSize, shadeAngle); /* free shader remappings */ while(remap != NULL) { remap2 = remap->next; free(remap); remap = remap2; } }
/* ================ SetTerrainTextures ================ */ void SetTerrainTextures( void ) { int i; int x, y; int layer; int minlayer, maxlayer; float s, t; float min_s, min_t; int alpha[ MAX_POINTS_ON_WINDING ]; shaderInfo_t *si, *terrainShader; bspbrush_t *brush; side_t *side; const char *shadername; vec3_t mins, maxs; vec3_t size; int surfwidth, surfheight, surfsize; terrainSurf_t *surf; byte *alphamap; int alphawidth, alphaheight; int num_layers; extern qboolean onlyents; if ( onlyents ) { return; } shadername = ValueForKey( mapent, "shader" ); if ( !shadername[ 0 ] ) { Error ("SetTerrainTextures: shader not specified" ); } alphamap = LoadAlphaMap( &num_layers, &alphawidth, &alphaheight ); num_layers = 3; mapent->firstDrawSurf = numMapDrawSurfs; // calculate the size of the terrain CalcTerrainSize( mins, maxs, size ); surfwidth = ( size[ 0 ] + SURF_WIDTH - 1 ) / SURF_WIDTH; surfheight = ( size[ 1 ] + SURF_HEIGHT - 1 ) / SURF_HEIGHT; surfsize = surfwidth * surfheight; lastSurface = NULL; numsurfaces = 0; maxsurfaces = 0; for( i = num_layers; i > 0; i-- ) { maxsurfaces += i * surfsize; } surfaces = malloc( maxsurfaces * sizeof( *surfaces ) ); memset( surfaces, 0, maxsurfaces * sizeof( *surfaces ) ); terrainShader = ShaderInfoForShader( "textures/common/terrain" ); for( brush = mapent->brushes; brush != NULL; brush = brush->next ) { // only create surfaces for sides marked as terrain for( side = brush->sides; side < &brush->sides[ brush->numsides ]; side++ ) { if ( !side->shaderInfo ) { continue; } if ( ( ( side->surfaceFlags | side->shaderInfo->surfaceFlags ) & SURF_NODRAW ) && !strstr( side->shaderInfo->shader, "terrain" ) ) { continue; } minlayer = num_layers; maxlayer = 0; // project each point of the winding onto the alphamap to determine which // textures to blend min_s = 1.0; min_t = 1.0; for( i = 0; i < side->winding->numpoints; i++ ) { s = floor( side->winding->p[ i ][ 0 ] + 0.1f - mins[ 0 ] ) / size[ 0 ]; t = floor( maxs[ 1 ] - side->winding->p[ i ][ 1 ] + 0.1f ) / size[ 1 ]; if ( s < 0 ) { s = 0; } if ( t < 0 ) { t = 0; } if ( s >= 1.0 ) { s = 1.0; } if ( t >= 1.0 ) { t = 1.0; } if ( s < min_s ) { min_s = s; } if ( t < min_t ) { min_t = t; } x = ( alphawidth - 1 ) * s; y = ( alphaheight - 1 ) * t; layer = alphamap[ x + y * alphawidth ]; if ( layer < minlayer ) { minlayer = layer; } if ( layer > maxlayer ) { maxlayer = layer; } alpha[ i ] = layer; } x = min_s * surfwidth; if ( x >= surfwidth ) { x = surfwidth - 1; } y = min_t * surfheight; if ( y >= surfheight ) { y = surfheight - 1; } if ( strstr( side->shaderInfo->shader, "terrain" ) ) { si = ShaderForLayer( minlayer, maxlayer, shadername ); if ( showseams ) { for( i = 0; i < side->winding->numpoints; i++ ) { if ( ( alpha[ i ] != minlayer ) && ( alpha[ i ] != maxlayer ) ) { si = ShaderInfoForShader( "textures/common/white" ); break; } } } surf = SurfaceForShader( si, x, y ); EmitTerrainVerts( side, surf, maxlayer, alpha, qtrue ); } else { si = side->shaderInfo; side->shaderInfo = terrainShader; surf = SurfaceForShader( si, x, y ); EmitTerrainVerts( side, surf, maxlayer, alpha, qfalse ); } } } // create the final surfaces for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) { if ( surf->numVerts ) { CreateTerrainSurface( surf, surf->shader ); } } // // clean up any allocated memory // for( surf = surfaces, i = 0; i < numsurfaces; i++, surf++ ) { if ( surf->verts ) { free( surf->verts ); free( surf->indexes ); } } free( alphamap ); free( surfaces ); surfaces = NULL; lastSurface = NULL; numsurfaces = 0; maxsurfaces = 0; }
int EmitShader( const char *shader, int *contentFlags, int *surfaceFlags ){ int i; shaderInfo_t *si; /* handle special cases */ if ( shader == NULL ) { shader = "noshader"; } /* try to find an existing shader */ for ( i = 0; i < numBSPShaders; i++ ) { /* ydnar: handle custom surface/content flags */ #ifndef SMOKINGUNS if ( surfaceFlags != NULL && bspShaders[ i ].surfaceFlags != *surfaceFlags ) { continue; } if ( contentFlags != NULL && bspShaders[ i ].contentFlags != *contentFlags ) { continue; } #endif /* compare name */ if ( !Q_stricmp( shader, bspShaders[ i ].shader ) ) { return i; } } /* get shaderinfo */ si = ShaderInfoForShader( shader ); /* emit a new shader */ if ( i == MAX_MAP_SHADERS ) { Error( "MAX_MAP_SHADERS" ); } numBSPShaders++; strcpy( bspShaders[ i ].shader, shader ); bspShaders[ i ].surfaceFlags = si->surfaceFlags; #ifdef SMOKINGUNS bspShaders[ i ].surfaceFlags |= GetSurfaceParm(si->shader); #endif bspShaders[ i ].contentFlags = si->contentFlags; /* handle custom content/surface flags */ #ifndef SMOKINGUNS if ( surfaceFlags != NULL ) { bspShaders[ i ].surfaceFlags = *surfaceFlags; } if ( contentFlags != NULL ) { bspShaders[ i ].contentFlags = *contentFlags; } #endif /* recursively emit any damage shaders */ if ( si->damageShader != NULL && si->damageShader[ 0 ] != '\0' ) { Sys_FPrintf( SYS_VRB, "Shader %s has damage shader %s\n", si->shader, si->damageShader ); EmitShader( si->damageShader, NULL, NULL ); } /* return it */ return i; }