// tr_map.c // leave this as first line for PCH reasons... // #include "../server/exe_headers.h" #include "tr_local.h" #include "../qcommon/cm_local.h" /* Loads and prepares a map file for scene rendering. A single entry point: void RE_LoadWorldMap( const char *name ); */ static world_t s_worldData; byte *fileBase; int c_subdivisions; int c_gridVerts; void R_RMGInit(void); //=============================================================================== // We use a special hack to prevent slight differences in channels // from exploding into big differences, as it causes lighting problems // later on. This is the maximum channel separation for which we // enable the hack. #define MAX_GREYSCALE_CHANNEL_DIFF 15 static void R_ColorShiftLightingBytes16( const byte in[4], byte out[2] ) { // What's the largest separation between the red, green, and blue // channels? int chanDiff = max(in[0],max(in[1],in[2])) - min(in[0],min(in[1],in[2])); if (chanDiff <= MAX_GREYSCALE_CHANNEL_DIFF) { // Ensure that all color channels compress to the same value byte channelAvg = (in[0] + in[1] + in[2] + 1) / 3; out[0] = channelAvg & 0xF0; out[0] |= (channelAvg & 0xF0) >> 4; out[1] = channelAvg & 0xF0; out[1] |= (in[3] & 0xF0) >> 4; if (channelAvg % 16 >= 8) { out[0] |= 0x10; out[0] |= 0x01; out[1] |= 0x10; } if (in[4] % 16 >= 8) { out[1] |= 0x01; } return; } // Normal case for vertex colors that are not "near" greyscale out[0] = in[0] & 0xF0; out[0] |= (in[1] & 0xF0) >> 4; out[1] = in[2] & 0xF0; out[1] |= (in[3] & 0xF0) >> 4; if(in[0] % 16 >= 8) { out[0] |= 0x10; } if(in[1] % 16 >= 8) { out[0] |= 0x1; } if(in[2] % 16 >= 8) { out[1] |= 0x10; } if(in[3] % 16 >= 8) { out[1] |= 0x1; } } static void HSVtoRGB( float h, float s, float v, float rgb[3] ) { int i; float f; float p, q, t; h *= 5; i = floor( h ); f = h - i; p = v * ( 1 - s ); q = v * ( 1 - s * f ); t = v * ( 1 - s * ( 1 - f ) ); switch ( i ) { case 0: rgb[0] = v; rgb[1] = t; rgb[2] = p; break; case 1: rgb[0] = q; rgb[1] = v; rgb[2] = p; break; case 2: rgb[0] = p; rgb[1] = v; rgb[2] = t; break; case 3: rgb[0] = p; rgb[1] = q; rgb[2] = v; break; case 4: rgb[0] = t; rgb[1] = p; rgb[2] = v; break; case 5: rgb[0] = v; rgb[1] = p; rgb[2] = q; break; } } /* =============== R_ColorShiftLightingBytes =============== */ void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) { int shift=0, r, g, b; // should NOT do it if overbrightBits is 0 if (tr.overbrightBits) shift = 1 - tr.overbrightBits; if (!shift) { out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; out[3] = in[3]; return; } // shift the data based on overbright range r = in[0] << shift; g = in[1] << shift; b = in[2] << shift; // normalize by color instead of saturating to white if ( ( r | g | b ) > 255 ) { int max; max = r > g ? r : g; max = max > b ? max : b; r = r * 255 / max; g = g * 255 / max; b = b * 255 / max; } out[0] = r; out[1] = g; out[2] = b; out[3] = in[3]; } /* =============== R_ColorShiftLightingBytes =============== */ static void R_ColorShiftLightingBytes( byte in[3]) { int shift=0, r, g, b; // should NOT do it if overbrightBits is 0 if (tr.overbrightBits) shift = 1 - tr.overbrightBits; if (!shift) { return; //no need if not overbright } // shift the data based on overbright range r = in[0] << shift; g = in[1] << shift; b = in[2] << shift; // normalize by color instead of saturating to white if ( ( r | g | b ) > 255 ) { int max; max = r > g ? r : g; max = max > b ? max : b; r = r * 255 / max; g = g * 255 / max; b = b * 255 / max; } in[0] = r; in[1] = g; in[2] = b; } /* =============== R_LoadLightmaps =============== */ #define LIGHTMAP_SIZE 128 void R_LoadLightmaps( void *data, int len, const char *psMapName ) { byte *buf, *buf_p; int i; if ( !len ) { return; } buf = (byte *)data + sizeof(int); tr.numLightmaps = 0; // we are about to upload textures R_SyncRenderThread(); // create all the lightmaps int size = *(int*)data; tr.numLightmaps = len / size; byte* image = (byte*)Z_Malloc(size, TAG_BSP, qfalse, 32); char sMapName[MAX_QPATH]; COM_StripExtension(psMapName,sMapName); // will already by MAX_QPATH legal, so no length check for ( i = 0 ; i < tr.numLightmaps ; i++ ) { buf_p = buf + i * size; memcpy(image, buf_p, size); char lmapName[MAX_QPATH + 32]; Com_sprintf(lmapName, MAX_QPATH + 32, "*%s/lightmap%d",sMapName,i); tr.lightmaps[i] = R_CreateImage( lmapName, image, LIGHTMAP_SIZE, LIGHTMAP_SIZE, GL_DDS_RGB16_EXT, qfalse, 0, GL_CLAMP); } Z_Free(image); } /* ================= RE_SetWorldVisData This is called by the clipmodel subsystem so we can share the 1.8 megs of space in big maps... ================= */ void RE_SetWorldVisData( SPARC *vis ) { tr.externalVisData = vis; } /* ================= R_LoadVisibility ================= */ static void R_LoadVisibility( void *data, int len ) { int length; char *buf; length = ( s_worldData.numClusters + 63 ) & ~63; s_worldData.novis = ( unsigned char *) Hunk_Alloc( length, h_low ); memset( s_worldData.novis, 0xff, length ); if ( !len ) { s_worldData.vis = NULL; return; } buf = (char*)data; s_worldData.numClusters = ((int *)buf)[0]; s_worldData.clusterBytes = ((int *)buf)[1]; // CM_Load should have given us the vis data to share, so // we don't need to allocate another copy if ( tr.externalVisData ) { s_worldData.vis = tr.externalVisData; } else { assert(0); } } //=============================================================================== qhandle_t R_GetShaderByNum(int shaderNum, world_t &worldData) { qhandle_t shader; if ( (shaderNum < 0) || (shaderNum >= worldData.numShaders) ) { Com_Printf( "Warning: Bad index for R_GetShaderByNum - %i", shaderNum ); return(0); } shader = RE_RegisterShader(worldData.shaders[ shaderNum ].shader); return(shader); } /* =============== ShaderForShaderNum =============== */ static shader_t *ShaderForShaderNum( int shaderNum, const int *lightmapNum, const byte *lightmapStyles ) { shader_t *shader; dshader_t *dsh; shaderNum = shaderNum; if ( shaderNum < 0 || shaderNum >= s_worldData.numShaders ) { Com_Error( ERR_DROP, "ShaderForShaderNum: bad num %i", shaderNum ); } dsh = &s_worldData.shaders[ shaderNum ]; shader = R_FindShader( dsh->shader, lightmapNum, lightmapStyles, qtrue ); // if the shader had errors, just use default shader if ( shader->defaultShader ) { return tr.defaultShader; } return shader; } bool NeedVertexColors(shader_t *shader) { int i; shaderStage_t *stage; for(i=0; inumUnfoggedPasses; i++) { stage = &shader->stages[i]; switch(stage->rgbGen) { case CGEN_EXACT_VERTEX: case CGEN_VERTEX: case CGEN_ONE_MINUS_VERTEX: return true; } switch(stage->alphaGen) { case AGEN_VERTEX: case AGEN_ONE_MINUS_VERTEX: return true; } } return false; } int NumLightMaps(shader_t *shader) { int count = 0; int i; for(i=0; ilightmapIndex[i] >= 0) { count++; } else { return count; } } return count; } int SurfaceFaceSize(int numVerts, int numLightMaps, bool needVertexColors, int numIndexes) { int sfaceSize = ( int ) &((srfSurfaceFace_t *)0)->srfPoints + 4 /*sizeof srfPoints*/ + (numVerts * sizeof(unsigned short) * (VERTEX_LM + numLightMaps * 2 + (int)needVertexColors * 4)); // Add in tangent size sfaceSize += sizeof(vec3_t) * numVerts; //Indices stored in 8 bits now. sfaceSize += numIndexes; return sfaceSize; } void BuildDrawVertTangents( drawVert_t *verts, int *indexes, int numIndexes, int numVertexes ) { int i = 0; for(i = 0; i < numVertexes; i++) { verts[i].tangent[0] = 0.0f; verts[i].tangent[1] = 0.0f; verts[i].tangent[2] = 0.0f; } for(i = 0; i < numIndexes; i += 3) { vec3_t vec1, vec2, du, dv, cp; float st0[2], st1[2], st2[2]; Q_CastShort2FloatScale(&st0[0], &verts[indexes[i]].dvst[0], 1.f / DRAWVERT_ST_SCALE); Q_CastShort2FloatScale(&st0[1], &verts[indexes[i]].dvst[1], 1.f / DRAWVERT_ST_SCALE); Q_CastShort2FloatScale(&st1[0], &verts[indexes[i+1]].dvst[0], 1.f / DRAWVERT_ST_SCALE); Q_CastShort2FloatScale(&st1[1], &verts[indexes[i+1]].dvst[1], 1.f / DRAWVERT_ST_SCALE); Q_CastShort2FloatScale(&st2[0], &verts[indexes[i+2]].dvst[0], 1.f / DRAWVERT_ST_SCALE); Q_CastShort2FloatScale(&st2[1], &verts[indexes[i+2]].dvst[1], 1.f / DRAWVERT_ST_SCALE); vec1[0] = verts[indexes[i+1]].xyz[0] - verts[indexes[i]].xyz[0]; vec1[1] = st1[0] - st0[0]; vec1[2] = st1[1] - st0[1]; vec2[0] = verts[indexes[i+2]].xyz[0] - verts[indexes[i]].xyz[0]; vec2[1] = st2[0] - st0[0]; vec2[2] = st2[1] - st0[1]; CrossProduct(vec1, vec2, cp); if(cp[0] == 0.0f) cp[0] = 0.001f; du[0] = -cp[1] / cp[0]; dv[0] = -cp[2] / cp[0]; vec1[0] = verts[indexes[i+1]].xyz[1] - verts[indexes[i]].xyz[1]; vec2[0] = verts[indexes[i+2]].xyz[1] - verts[indexes[i]].xyz[1]; CrossProduct(vec1, vec2, cp); if(cp[0] == 0.0f) cp[0] = 0.001f; du[1] = -cp[1] / cp[0]; dv[1] = -cp[2] / cp[0]; vec1[0] = verts[indexes[i+1]].xyz[2] - verts[indexes[i]].xyz[2]; vec2[0] = verts[indexes[i+2]].xyz[2] - verts[indexes[i]].xyz[2]; CrossProduct(vec1, vec2, cp); if(cp[0] == 0.0f) cp[0] = 0.001f; du[2] = -cp[1] / cp[0]; dv[2] = -cp[2] / cp[0]; verts[indexes[i]].tangent[0] += du[0]; verts[indexes[i]].tangent[1] += du[1]; verts[indexes[i]].tangent[2] += du[2]; verts[indexes[i+1]].tangent[0] += du[0]; verts[indexes[i+1]].tangent[1] += du[1]; verts[indexes[i+1]].tangent[2] += du[2]; verts[indexes[i+2]].tangent[0] += du[0]; verts[indexes[i+2]].tangent[1] += du[1]; verts[indexes[i+2]].tangent[2] += du[2]; } for(i = 0; i < numVertexes; i++) { VectorNormalizeFast(verts[i].tangent); } } void BuildMapVertTangents( mapVert_t *verts, vec3_t *tangents, short *indexes, int numIndexes, int numVertexes ) { int i = 0; for(i = 0; i < numVertexes; i++) { tangents[i][0] = 0.0f; tangents[i][1] = 0.0f; tangents[i][2] = 0.0f; } for(i = 0; i < numIndexes; i += 3) { vec3_t vec1, vec2, du, dv, cp; vec1[0] = verts[indexes[i+1]].xyz[0] - verts[indexes[i]].xyz[0]; vec1[1] = (verts[indexes[i+1]].st[0] * POINTS_ST_SCALE) - (verts[indexes[i]].st[0] * POINTS_ST_SCALE); vec1[2] = (verts[indexes[i+1]].st[1] * POINTS_ST_SCALE) - (verts[indexes[i]].st[1] * POINTS_ST_SCALE); vec2[0] = verts[indexes[i+2]].xyz[0] - verts[indexes[i]].xyz[0]; vec2[1] = (verts[indexes[i+2]].st[0] * POINTS_ST_SCALE) - (verts[indexes[i]].st[0] * POINTS_ST_SCALE); vec2[2] = (verts[indexes[i+2]].st[1]* POINTS_ST_SCALE) - (verts[indexes[i]].st[1] * POINTS_ST_SCALE); CrossProduct(vec1, vec2, cp); if(cp[0] == 0.0f) cp[0] = 0.001f; du[0] = -cp[1] / cp[0]; dv[0] = -cp[2] / cp[0]; vec1[0] = verts[indexes[i+1]].xyz[1] - verts[indexes[i]].xyz[1]; vec2[0] = verts[indexes[i+2]].xyz[1] - verts[indexes[i]].xyz[1]; CrossProduct(vec1, vec2, cp); if(cp[0] == 0.0f) cp[0] = 0.001f; du[1] = -cp[1] / cp[0]; dv[1] = -cp[2] / cp[0]; vec1[0] = verts[indexes[i+1]].xyz[2] - verts[indexes[i]].xyz[2]; vec2[0] = verts[indexes[i+2]].xyz[2] - verts[indexes[i]].xyz[2]; CrossProduct(vec1, vec2, cp); if(cp[0] == 0.0f) cp[0] = 0.001f; du[2] = -cp[1] / cp[0]; dv[2] = -cp[2] / cp[0]; tangents[indexes[i]][0] += du[0]; tangents[indexes[i]][1] += du[1]; tangents[indexes[i]][2] += du[2]; tangents[indexes[i+1]][0] += du[0]; tangents[indexes[i+1]][1] += du[1]; tangents[indexes[i+1]][2] += du[2]; tangents[indexes[i+2]][0] += du[0]; tangents[indexes[i+2]][1] += du[1]; tangents[indexes[i+2]][2] += du[2]; } for(i = 0; i < numVertexes; i++) { VectorNormalizeFast(tangents[i]); } } /* =============== ParseFace =============== */ static void ParseFace( dface_t *ds, mapVert_t *verts, msurface_t *surf, short *indexes, byte *&pFaceDataBuffer) { int i, j, k; srfSurfaceFace_t *cv; int numPoints, numIndexes; int lightmapNum[MAXLIGHTMAPS]; int sfaceSize, ofsIndexes; vec3_t tangents[1000]; for(i=0;ilightmapNum[i] - 4; } // get fog volume surf->fogIndex = ds->fogNum + 1; // get shader value surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum, ds->lightmapStyles ); if ( r_singleShader->integer && !surf->shader->sky ) { surf->shader = tr.defaultShader; } bool needVertexColors = NeedVertexColors(surf->shader); int numLightMaps = NumLightMaps(surf->shader); assert(numLightMaps <= 0x7F); numPoints = ds->verts & 0xFFF; if (numPoints > MAX_FACE_POINTS) { Com_Printf (S_COLOR_YELLOW "WARNING: MAX_FACE_POINTS exceeded: %i\n", numPoints); } numIndexes = ds->indexes & 0xFFF; // create the srfSurfaceFace_t sfaceSize = SurfaceFaceSize(numPoints, numLightMaps, needVertexColors, numIndexes); ofsIndexes = sfaceSize - numIndexes; cv = (srfSurfaceFace_t *) pFaceDataBuffer;//ri.Hunk_Alloc( sfaceSize ); pFaceDataBuffer += sfaceSize; // :-) cv->surfaceType = SF_FACE; cv->numPoints = numPoints; cv->numIndices = numIndexes; cv->ofsIndices = ofsIndexes; cv->srfPoints = (unsigned short *)(((byte*)cv) + ( int ) &((srfSurfaceFace_t *)0)->srfPoints + 4); if(needVertexColors) { cv->flags = 1 << 7; } else { cv->flags = 0; } cv->flags |= (numLightMaps & 0x7F); //Make sure we don't overflow storage. assert(numPoints < 256); assert(numIndexes < 65536); assert(ofsIndexes < 65536); int nextSurfPoint = NEXT_SURFPOINT(cv->flags); verts += ds->verts >> 12; indexes += ds->indexes >> 12; BuildMapVertTangents(verts, tangents, indexes, numIndexes, numPoints); for ( i = 0 ; i < numPoints ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) { *(cv->srfPoints + i * nextSurfPoint + j) = verts[i].xyz[j]; } for ( j = 0; j < 3 ; j++ ) { assert(tangents[i][j] >= -1 && tangents[i][j] <= 1); *(cv->srfPoints + i * nextSurfPoint + 3 + j) = (short)(tangents[i][j] * 32767.0f); } for ( j = 0 ; j < 2 ; j++ ) { *(cv->srfPoints + i * nextSurfPoint + 6 + j) = (short)(verts[i].st[j] * POINTS_ST_SCALE); for(k=0;ksrfPoints + i * nextSurfPoint + VERTEX_LM+j+(k*2)) = verts[i].lightmap[k][j]; } } if(needVertexColors) { for(k=0;ksrfPoints + i * nextSurfPoint + VERTEX_COLOR(cv->flags) + k)); } } } unsigned char *indexStorage = ((unsigned char*)cv) + cv->ofsIndices; for ( i = 0 ; i < numIndexes ; i++ ) { indexStorage[i] = indexes[ i ]; } // take the plane information from the lightmap vector for ( i = 0 ; i < 3 ; i++ ) { cv->plane.normal[i] = (float)ds->lightmapVecs[i] / 32767.f; } vec3_t fVec; fVec[0] = (float)((short)cv->srfPoints[0]); fVec[1] = (float)((short)cv->srfPoints[1]); fVec[2] = (float)((short)cv->srfPoints[2]); cv->plane.dist = DotProduct( fVec, cv->plane.normal ); SetPlaneSignbits( &cv->plane ); cv->plane.type = PlaneTypeForNormal( cv->plane.normal ); surf->data = (surfaceType_t *)cv; } /* =============== ParseMesh =============== */ static void ParseMesh ( dpatch_t *ds, mapVert_t *verts, msurface_t *surf, drawVert_t* points, drawVert_t* ctrl, float* errorTable ) { srfGridMesh_t *grid; int i, j, k; int width, height, numPoints; int lightmapNum[MAXLIGHTMAPS]; vec3_t bounds[2]; vec3_t tmpVec; static surfaceType_t skipData = SF_SKIP; for(i=0;ilightmapNum[i] - 4; } // get fog volume surf->fogIndex = ds->fogNum + 1; // get shader value surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum, ds->lightmapStyles ); if ( r_singleShader->integer && !surf->shader->sky ) { surf->shader = tr.defaultShader; } // we may have a nodraw surface, because they might still need to // be around for movement clipping if ( s_worldData.shaders[ ds->shaderNum ].surfaceFlags & SURF_NODRAW ) { surf->data = &skipData; return; } width = ds->patchWidth; height = ds->patchHeight; verts += ds->verts >> 12; numPoints = width * height; for ( i = 0 ; i < numPoints ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) { points[i].xyz[j] = (float)verts[i].xyz[j]; points[i].normal[j] = (float)verts[i].normal[j] / 32767.f; } for ( j = 0 ; j < 2 ; j++ ) { // Sanity check that alternate fixed point representation // is good enough assert( verts[i].st[j] * GRID_DRAWVERT_ST_SCALE < 32767 && verts[i].st[j] * GRID_DRAWVERT_ST_SCALE >= -32768 ); points[i].dvst[j] = verts[i].st[j] * GRID_DRAWVERT_ST_SCALE; for(k=0;kdata = (surfaceType_t *)grid; // copy the level of detail origin, which is the center // of the group of all curves that must subdivide the same // to avoid cracking for ( i = 0 ; i < 3 ; i++ ) { bounds[0][i] = ds->lightmapVecs[0][i]; bounds[1][i] = ds->lightmapVecs[1][i]; } VectorAdd( bounds[0], bounds[1], bounds[1] ); VectorScale( bounds[1], 0.5f, grid->lodOrigin ); VectorSubtract( bounds[0], grid->lodOrigin, tmpVec ); grid->lodRadius = VectorLength( tmpVec ); } /* =============== ParseTriSurf =============== */ static void ParseTriSurf( dtrisurf_t *ds, mapVert_t *verts, msurface_t *surf, short *indexes ) { srfTriangles_t *tri; int i, j, k; int numVerts, numIndexes; // get fog volume surf->fogIndex = ds->fogNum + 1; // get shader surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapsVertex, ds->lightmapStyles ); if ( r_singleShader->integer && !surf->shader->sky ) { surf->shader = tr.defaultShader; } numVerts = ds->verts & 0xFFF; numIndexes = ds->indexes & 0xFFF; tri = (srfTriangles_t *) Hunk_Alloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) + numIndexes * sizeof( tri->indexes[0] ), h_low ); tri->surfaceType = SF_TRIANGLES; tri->numVerts = numVerts; tri->numIndexes = numIndexes; tri->verts = (drawVert_t *)(tri + 1); tri->indexes = (int *)(tri->verts + tri->numVerts ); surf->data = (surfaceType_t *)tri; // copy vertexes verts += ds->verts >> 12; ClearBounds( tri->bounds[0], tri->bounds[1] ); for ( i = 0 ; i < numVerts ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) { tri->verts[i].xyz[j] = (float)verts[i].xyz[j]; tri->verts[i].normal[j] = (float)verts[i].normal[j] / 32767.f; } AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] ); for ( j = 0 ; j < 2 ; j++ ) { // Sanity check that alternate fixed point representation // is good enough // MATT! - double check this! assert( verts[i].st[j] * DRAWVERT_ST_SCALE <= 32767 && verts[i].st[j] * DRAWVERT_ST_SCALE >= -32768 ); tri->verts[i].dvst[j] = verts[i].st[j] * DRAWVERT_ST_SCALE; for(k=0;kverts[i].dvlightmap[k][j] = ((float)verts[i].lightmap[k][j] / POINTS_LIGHT_SCALE) * DRAWVERT_LIGHTMAP_SCALE; } } for(k=0;kverts[i].dvcolor[k]); } } // copy indexes indexes += ds->indexes >> 12; for ( i = 0 ; i < numIndexes ; i++ ) { tri->indexes[i] = indexes[i]; if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) { Com_Error( ERR_DROP, "Bad index in triangle surface" ); } } // Build the tangent vectors BuildDrawVertTangents(tri->verts, tri->indexes, numIndexes, numVerts); } /* =============== ParseFlare =============== */ static void ParseFlare( dflare_t *df, msurface_t *surf ) { srfFlare_t *flare; int i; surf->fogIndex = df->fogNum + 1; // get shader surf->shader = ShaderForShaderNum( df->shaderNum, lightmapsVertex, stylesDefault ); flare = (srfFlare_t *) Hunk_Alloc( sizeof( *flare ), h_low ); flare->surfaceType = SF_FLARE; for ( i = 0 ; i < 3 ; i++ ) { flare->origin[i] = df->origin[i]; flare->color[i] = df->color[i]; flare->normal[i] = df->normal[i]; } surf->data = (surfaceType_t *)flare; } void R_LoadFlares( void *surfaces, int surfacelen ) { int count, i; dflare_t *in = NULL; msurface_t *out; count = surfacelen / sizeof(*in); for ( i = 0 ; i < count ; i++ ) { in = (dflare_t *)surfaces + i; out = s_worldData.surfaces + in->code; ParseFlare( in, out ); } } /* =============== R_LoadSurfaces =============== */ void R_LoadSurfaces( int count ) { s_worldData.surfaces = (struct msurface_s *) Hunk_Alloc ( count * sizeof(msurface_s), h_low ); s_worldData.numsurfaces = count; } /* =============== R_LoadPatches =============== */ void R_LoadPatches( void *verts, int vertlen, void *surfaces, int surfacelen ) { dpatch_t *in = NULL; msurface_t *out; mapVert_t *dv; int count; int i; if (surfacelen == 0) { return; } count = surfacelen / sizeof(*in); dv = (mapVert_t *)(verts); if (vertlen % sizeof(*dv)) Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); drawVert_t* points = (drawVert_t*)Z_Malloc( MAX_PATCH_SIZE*MAX_PATCH_SIZE*sizeof(drawVert_t), TAG_TEMP_WORKSPACE, qfalse); drawVert_t* ctrl = (drawVert_t*)Z_Malloc( MAX_GRID_SIZE*MAX_GRID_SIZE*sizeof(drawVert_t), TAG_TEMP_WORKSPACE, qfalse); float* errorTable = (float*)Z_Malloc( 2*MAX_GRID_SIZE*sizeof(float), TAG_TEMP_WORKSPACE, qfalse); for ( i = 0 ; i < count ; i++ ) { in = (dpatch_t *)surfaces + i; out = s_worldData.surfaces + in->code; ParseMesh ( in, dv, out, points, ctrl, errorTable ); } Z_Free(errorTable); Z_Free(ctrl); Z_Free(points); Com_Printf( "...loaded %i meshes\n", count ); } /* =============== R_LoadTriSurfs =============== */ void R_LoadTriSurfs( void *indexdata, int indexlen, void *verts, int vertlen, void *surfaces, int surfacelen ) { dtrisurf_t *in = NULL; msurface_t *out; mapVert_t *dv; short *indexes; int count; int i; if (surfacelen == 0) { return; } count = surfacelen / sizeof(*in); dv = (mapVert_t *)(verts); if (vertlen % sizeof(*dv)) Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); indexes = (short *)(indexdata); if ( indexlen % sizeof(*indexes)) Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); for ( i = 0 ; i < count ; i++ ) { in = (dtrisurf_t *)surfaces + i; out = s_worldData.surfaces + in->code; ParseTriSurf( in, dv, out, indexes ); } Com_Printf( "...loaded %i trisurfs\n", count ); } /* =============== R_LoadFaces =============== */ void R_LoadFaces( void *indexdata, int indexlen, void *verts, int vertlen, void *surfaces, int surfacelen ) { dface_t *in = NULL; msurface_t *out; mapVert_t *dv; short *indexes; int count; int i; if (surfacelen == 0) { return; } count = surfacelen / sizeof(*in); dv = (mapVert_t *)(verts); if (vertlen % sizeof(*dv)) Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); indexes = (short *)(indexdata); if ( indexlen % sizeof(*indexes)) Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); // new bit, the face code on our biggest map requires over 15,000 mallocs, which was no problem on the hunk, // bit hits the zone pretty bad (even the tagFree takes about 9 seconds for that many memblocks), // so special-case pre-alloc enough space for this data (the patches etc can stay as they are)... // int nTimes = count / 100; int nToGo = nTimes; int iFaceDataSizeRequired = 0; for ( i = 0 ; i < count ; i++) { in = (dface_t *)surfaces + i; int lightmapNum[MAXLIGHTMAPS]; for(int j=0; j<4; j++) { lightmapNum[j] = (int)in->lightmapNum[j] - 4; } shader_t *shader = ShaderForShaderNum( in->shaderNum, lightmapNum, in->lightmapStyles ); bool needVertexColors = NeedVertexColors(shader); int numLightMaps = NumLightMaps(shader); int sfaceSize = SurfaceFaceSize(in->verts & 0xFFF, numLightMaps, needVertexColors, in->indexes & 0xFFF); iFaceDataSizeRequired += sfaceSize; assert(sfaceSize < 100 * 1024); if (--nToGo <= 0) { nToGo = nTimes; } } in -= count; // back it up, ready for loop-proper // since this ptr is to hunk data, I can pass it in and have it advanced without worrying about losing // the original alloc ptr... // byte *orgFaceData; byte *pFaceDataBuffer = (byte *)Hunk_Alloc( iFaceDataSizeRequired, h_low ); orgFaceData = pFaceDataBuffer; // now do regular loop... // for ( i = 0 ; i < count ; i++ ) { in = (dface_t *)surfaces + i; out = s_worldData.surfaces + in->code; ParseFace( in, dv, out, indexes, pFaceDataBuffer ); if (--nToGo <= 0) { nToGo = nTimes; } } Com_Printf( "...loaded %d faces\n", count ); } /* ================= R_LoadSubmodels ================= */ static void R_LoadSubmodels( void *data, int len ) { dmodel_t *in; bmodel_t *out; int i, j, count; in = (dmodel_t *)(data); if (len % sizeof(*in)) Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); count = len / sizeof(*in); s_worldData.bmodels = out = (bmodel_t *) Hunk_Alloc( count * sizeof(*out), h_low ); for ( i=0 ; itype = MOD_BRUSH; model->bmodel = out; Com_sprintf( model->name, sizeof( model->name ), "*%d", i ); for (j=0 ; j<3 ; j++) { out->bounds[0][j] = in->mins[j]; out->bounds[1][j] = in->maxs[j]; } RE_InsertModelIntoHash(model->name, model); out->firstSurface = s_worldData.surfaces + in->firstSurface; out->numSurfaces = in->numSurfaces; } } //================================================================== /* ================= R_SetParent ================= */ static void R_SetParent (mnode_t *node, mnode_t *parent) { node->parent = parent; if (node->contents != -1) return; R_SetParent (node->children[0], node); R_SetParent (node->children[1], node); } /* ================= R_LoadNodesAndLeafs ================= */ static void R_LoadNodesAndLeafs (void *nodes, int nodelen, void *leafs, int leaflen) { int i, j, p; dnode_t *in; dleaf_t *inLeaf; mnode_t *outNode; mleaf_s *outLeaf; int numNodes, numLeafs; in = (dnode_t *)(nodes); if (nodelen % sizeof(dnode_t) || leaflen % sizeof(dleaf_t) ) { Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); } numNodes = nodelen / sizeof(dnode_t); numLeafs = leaflen / sizeof(dleaf_t); outNode = (struct mnode_s *) Hunk_Alloc ( (numNodes) * sizeof(*outNode), h_low ); outLeaf = (struct mleaf_s *) Hunk_Alloc ( (numLeafs) * sizeof(*outLeaf), h_low ); s_worldData.nodes = outNode; s_worldData.leafs = outLeaf; s_worldData.numnodes = numNodes; s_worldData.numleafs = numLeafs; // load nodes for ( i=0 ; imins[j] = in->mins[j]; outNode->maxs[j] = in->maxs[j]; } outNode->planeNum = in->planeNum; outNode->contents = CONTENTS_NODE; // differentiate from leafs for (j=0 ; j<2 ; j++) { p = in->children[j]; if (p >= 0) { if(p < numNodes) { outNode->children[j] = s_worldData.nodes + p; } else { outNode->children[j] = (mnode_s*) (s_worldData.leafs + (p - numNodes)); } } else { if(numNodes + (-1 - p) < numNodes) { outNode->children[j] = s_worldData.nodes + numNodes + (-1 - p); } else { outNode->children[j] = (mnode_s*) (s_worldData.leafs + (-1 - p)); } } } } // load leafs inLeaf = (dleaf_t *)(leafs); for ( i=0 ; imins[j] = inLeaf->mins[j]; outLeaf->maxs[j] = inLeaf->maxs[j]; } outLeaf->cluster = inLeaf->cluster; outLeaf->area = inLeaf->area; if ( outLeaf->cluster >= s_worldData.numClusters ) { s_worldData.numClusters = outLeaf->cluster + 1; } outLeaf->firstMarkSurfNum = inLeaf->firstLeafSurface; outLeaf->nummarksurfaces = inLeaf->numLeafSurfaces; } // chain decendants R_SetParent (s_worldData.nodes, NULL); } //============================================================================= /* ================= R_LoadShaders ================= */ void R_LoadShaders( void *data, int len) { dshader_t *in, *out; int i, count; in = (dshader_t *)(data); if (len % sizeof(*in)) { Com_Error (ERR_DROP, "CMod_LoadShaders: funny lump size"); } count = len / sizeof(*in); if (count < 1) { Com_Error (ERR_DROP, "Map with no shaders"); } out = (dshader_t *)Hunk_Alloc( count*sizeof(*out), h_low); s_worldData.shaders = out; s_worldData.numShaders = count; Com_Memcpy( out, in, count*sizeof(*out) ); for ( i = 0; i < count; i++, in++, out++ ) { // Q_strncpyz(out->shader, in->shader, MAX_QPATH); out->contentFlags = in->contentFlags; out->surfaceFlags = in->surfaceFlags; } } /* ================= R_LoadMarksurfaces ================= */ static void R_LoadMarksurfaces (void *data, int len) { int i, count; int *in; msurface_t **out; in = (int *)(data); if (len % sizeof(*in)) Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); count = len / sizeof(*in); out = (struct msurface_s **) Hunk_Alloc ( count*sizeof(*out), h_low ); s_worldData.marksurfaces = out; s_worldData.nummarksurfaces = count; for ( i=0 ; i s_worldData.numsurfaces) assert(0); out[i] = s_worldData.surfaces + in[i]; if (out[i]->shader && out[i]->shader->sort == SS_PORTAL) { s_worldData.portalPresent = qtrue; } } } /* ================= R_LoadPlanes ================= */ static void R_LoadPlanes( void *data, int len ) { int i, j; cplane_t *out; dplane_t *in; int count; int bits; in = (dplane_t *)(data); if (len % sizeof(*in)) Com_Error (ERR_DROP, "LoadMap: funny lump size"); count = len / sizeof(*in); if (count < 1) Com_Error (ERR_DROP, "Map with no planes"); out = (struct cplane_s *) Hunk_Alloc( count * 2 * sizeof( *out ), h_low); s_worldData.planes = out; s_worldData.numplanes = count; for ( i=0 ; inormal[j] = in->normal[j]; if (out->normal[j] < 0) bits |= 1<dist = in->dist; out->type = PlaneTypeForNormal( out->normal ); out->signbits = bits; } } /* ================= R_LoadFogs ================= */ static void R_LoadFogs( void *fogdata, int foglen, void *brushdata, int brushlen, void *sidedata, int sidelen ) { int i; fog_t *out; dfog_t *fogs; dbrush_t *brushes, *brush; dbrushside_t *sides; int count, brushesCount, sidesCount; int sideNum; int planeNum; shader_t *shader; float d; int firstSide=0; int lightmaps[MAXLIGHTMAPS] = { LIGHTMAP_NONE } ; fogs = (dfog_t *)(fogdata); if (foglen % sizeof(*fogs)) { Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); } count = foglen / sizeof(*fogs); // create fog structres for them // NOTE: we allocate memory for an extra one so that the LA goggles can turn on their own fog s_worldData.numfogs = count + 1; s_worldData.fogs = (fog_t *)Hunk_Alloc (( s_worldData.numfogs + 1)*sizeof(*out), h_low ); s_worldData.globalFog = -1; out = s_worldData.fogs + 1; if ( !count ) { return; } brushes = (dbrush_t *)(brushdata); if (brushlen % sizeof(*brushes)) { Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); } brushesCount = brushlen / sizeof(*brushes); sides = (dbrushside_t *)(sidedata); if (sidelen % sizeof(*sides)) { Com_Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); } sidesCount = sidelen / sizeof(*sides); for ( i=0 ; ioriginalBrushNumber = fogs->brushNum; if (out->originalBrushNumber == -1) { out->bounds[0][0] = out->bounds[0][1] = out->bounds[0][2] = MIN_WORLD_COORD; out->bounds[1][0] = out->bounds[1][1] = out->bounds[1][2] = MAX_WORLD_COORD; s_worldData.globalFog = i+1; } else { if ( (unsigned)out->originalBrushNumber >= brushesCount ) { Com_Error( ERR_DROP, "fog brushNumber out of range" ); } brush = brushes + out->originalBrushNumber; firstSide = brush->firstSide; if ( (unsigned)firstSide > sidesCount - 6 ) { Com_Error( ERR_DROP, "fog brush sideNumber out of range" ); } // brushes are always sorted with the axial sides first sideNum = firstSide + 0; planeNum = sides[ sideNum ].planeNum; out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 1; planeNum = sides[ sideNum ].planeNum; out->bounds[1][0] = s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 2; planeNum = sides[ sideNum ].planeNum; out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 3; planeNum = sides[ sideNum ].planeNum; out->bounds[1][1] = s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 4; planeNum = sides[ sideNum ].planeNum; out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist; sideNum = firstSide + 5; planeNum = sides[ sideNum ].planeNum; out->bounds[1][2] = s_worldData.planes[ planeNum ].dist; } // get information from the shader for fog parameters shader = R_FindShader( fogs->shader, lightmaps, stylesDefault, qtrue ); if (!shader->fogParms) {//bad shader!! assert(shader->fogParms); out->parms.color[0] = 1.0f; out->parms.color[1] = 0.0f; out->parms.color[2] = 0.0f; out->parms.color[3] = 0.0f; out->parms.depthForOpaque = 250.0f; } else { out->parms = *shader->fogParms; } out->colorInt = ColorBytes4 ( out->parms.color[0] * tr.identityLight, out->parms.color[1] * tr.identityLight, out->parms.color[2] * tr.identityLight, 1.0 ); d = out->parms.depthForOpaque < 1 ? 1 : out->parms.depthForOpaque; out->tcScale = 1.0 / ( d * 8 ); // set the gradient vector sideNum = fogs->visibleSide; if ( sideNum == -1 ) { out->hasSurface = qfalse; } else { out->hasSurface = qtrue; planeNum = sides[ firstSide + sideNum ].planeNum; VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); out->surface[3] = -s_worldData.planes[ planeNum ].dist; } out++; } // Initialise the last fog so we can use it with the LA Goggles // NOTE: We are might appear to be off the end of the array, but we allocated an extra memory slot above but [purposely] didn't // increment the total world numFogs to match our array size VectorSet(out->bounds[0], MIN_WORLD_COORD, MIN_WORLD_COORD, MIN_WORLD_COORD); VectorSet(out->bounds[1], MAX_WORLD_COORD, MAX_WORLD_COORD, MAX_WORLD_COORD); out->originalBrushNumber = -1; out->parms.color[0] = 0.0f; out->parms.color[1] = 0.0f; out->parms.color[2] = 0.0f; out->parms.color[3] = 0.0f; out->parms.depthForOpaque = 0.0f; out->colorInt = 0x00000000; out->tcScale = 0.0f; out->hasSurface = false; } /* ================ R_LoadLightGrid ================ */ void R_LoadLightGrid( void *data, int len ) { vec3_t maxs; world_t *w; int i; float *wMins, *wMaxs; w = &s_worldData; w->lightGridInverseSize[0] = 1.0 / w->lightGridSize[0]; w->lightGridInverseSize[1] = 1.0 / w->lightGridSize[1]; w->lightGridInverseSize[2] = 1.0 / w->lightGridSize[2]; wMins = w->bmodels[0].bounds[0]; wMaxs = w->bmodels[0].bounds[1]; for ( i = 0 ; i < 3 ; i++ ) { w->lightGridOrigin[i] = w->lightGridSize[i] * ceil( wMins[i] / w->lightGridSize[i] ); maxs[i] = w->lightGridSize[i] * floor( wMaxs[i] / w->lightGridSize[i] ); w->lightGridBounds[i] = (maxs[i] - w->lightGridOrigin[i])/w->lightGridSize[i] + 1; } w->lightGridData = (mgrid_t *)Hunk_Alloc( len, h_low ); memcpy( w->lightGridData, data, len ); } /* ================ R_LoadLightGridArray ================ */ void R_LoadLightGridArray( void *data, int len ) { world_t *w; w = &s_worldData; w->numGridArrayElements = w->lightGridBounds[0] * w->lightGridBounds[1] * w->lightGridBounds[2]; if ( len != w->numGridArrayElements * sizeof(*w->lightGridArray) ) { if (len>0)//don't warn if not even lit Com_Printf( "WARNING: light grid array mismatch\n" ); w->lightGridData = NULL; return; } w->lightGridArray = (unsigned short *)Hunk_Alloc( len, h_low ); memcpy( w->lightGridArray, data, len ); } /* ================ R_LoadEntities ================ */ void R_LoadEntities( void *data, int len ) { const char *p, *token; char keyname[MAX_TOKEN_CHARS]; char value[MAX_TOKEN_CHARS]; world_t *w; w = &s_worldData; w->lightGridSize[0] = 64; w->lightGridSize[1] = 64; w->lightGridSize[2] = 128; VectorSet(tr.sunAmbient, 1, 1, 1); tr.distanceCull = 6000;//DEFAULT_DISTANCE_CULL; p = (char *)(data); // store for reference by the cgame w->entityString = (char *)Hunk_Alloc( len + 1, h_low ); strcpy( w->entityString, p ); w->entityParsePoint = w->entityString; token = COM_ParseExt( &p, qtrue ); if (!*token || *token != '{') { return; } // only parse the world spawn while ( 1 ) { // parse key token = COM_ParseExt( &p, qtrue ); if ( !*token || *token == '}' ) { break; } Q_strncpyz(keyname, token, sizeof(keyname)); // parse value token = COM_ParseExt( &p, qtrue ); if ( !*token || *token == '}' ) { break; } Q_strncpyz(value, token, sizeof(value)); if (!Q_stricmp(keyname, "distanceCull")) { sscanf(value, "%f", &tr.distanceCull ); continue; } // check for a different grid size if (!Q_stricmp(keyname, "gridsize")) { sscanf(value, "%f %f %f", &w->lightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] ); continue; } // find the optional world ambient for arioche if (!Q_stricmp(keyname, "_color")) { sscanf(value, "%f %f %f", &tr.sunAmbient[0], &tr.sunAmbient[1], &tr.sunAmbient[2] ); continue; } } } /* ================= R_GetEntityToken ================= */ qboolean R_GetEntityToken( char *buffer, int size ) { const char *s; if (size == -1) { //force reset s_worldData.entityParsePoint = s_worldData.entityString; return qtrue; } s = COM_Parse( (const char **) &s_worldData.entityParsePoint ); Q_strncpyz( buffer, s, size ); if ( !s_worldData.entityParsePoint || !s[0] ) { return qfalse; } else { return qtrue; } } /* ================= RE_LoadWorldMap Called directly from cgame ================= */ void RE_LoadWorldMap_Actual( const char *name, world_t &worldData, int index ) { char stripName[MAX_QPATH]; Lump outputLumps[3]; if ( tr.worldMapLoaded ) { Com_Error( ERR_DROP, "ERROR: attempted to redundantly load world map\n" ); } skyboxportal = 0; // set default sun direction to be used if it isn't // overridden by a shader tr.sunDirection[0] = 0.45f; tr.sunDirection[1] = 0.3f; tr.sunDirection[2] = 0.9f; Cvar_SetValue( "r_sundir_x", tr.sunDirection[0] ); Cvar_SetValue( "r_sundir_y", tr.sunDirection[1] ); Cvar_SetValue( "r_sundir_z", tr.sunDirection[2] ); VectorNormalize( tr.sunDirection ); tr.worldMapLoaded = qtrue; // clear tr.world so if the level fails to load, the next // try will not look at the partially loaded version tr.world = NULL; memset( &s_worldData, 0, sizeof( s_worldData ) ); Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) ); Q_strncpyz( s_worldData.baseName, COM_SkipPath( s_worldData.name ), sizeof( s_worldData.name ) ); COM_StripExtension( s_worldData.baseName, s_worldData.baseName ); COM_StripExtension(name, stripName); c_gridVerts = 0; // load into heap outputLumps[0].load(stripName, "shaders"); R_LoadShaders(outputLumps[0].data, outputLumps[0].len); outputLumps[0].load(stripName, "lightmaps"); R_LoadLightmaps(outputLumps[0].data, outputLumps[0].len, name); outputLumps[0].load(stripName, "planes"); R_LoadPlanes(outputLumps[0].data, outputLumps[0].len); outputLumps[0].load(stripName, "fogs"); outputLumps[1].load(stripName, "brushes"); outputLumps[2].load(stripName, "brushsides"); R_LoadFogs( outputLumps[0].data, outputLumps[0].len, outputLumps[1].data, outputLumps[1].len, outputLumps[2].data, outputLumps[2].len ); outputLumps[2].clear(); outputLumps[1].clear(); Lump misc; misc.load(stripName, "misc"); int num_surfs = *(int*)misc.data; misc.clear(); R_LoadSurfaces(num_surfs); Lump verts; verts.load(stripName, "verts"); Lump patches; patches.load(stripName, "patches"); R_LoadPatches(verts.data, verts.len, patches.data, patches.len); patches.clear(); Lump indexes; indexes.load(stripName, "indexes"); Lump trisurfs; trisurfs.load(stripName, "trisurfs"); R_LoadTriSurfs(indexes.data, indexes.len, verts.data, verts.len, trisurfs.data, trisurfs.len); trisurfs.clear(); Lump faces; faces.load(stripName, "faces"); R_LoadFaces(indexes.data, indexes.len, verts.data, verts.len, faces.data, faces.len); Lump flares; flares.load(stripName, "flares"); R_LoadFlares(flares.data, flares.len); outputLumps[0].load(stripName, "leafsurfaces"); R_LoadMarksurfaces (outputLumps[0].data, outputLumps[0].len); outputLumps[0].load(stripName, "nodes"); outputLumps[1].load(stripName, "leafs"); R_LoadNodesAndLeafs (outputLumps[0].data, outputLumps[0].len, outputLumps[1].data, outputLumps[1].len); outputLumps[1].clear(); outputLumps[0].load(stripName, "models"); R_LoadSubmodels (outputLumps[0].data, outputLumps[0].len); outputLumps[0].load(stripName, "visibility"); R_LoadVisibility(outputLumps[0].data, outputLumps[0].len); outputLumps[0].load(stripName, "entities"); R_LoadEntities( outputLumps[0].data, outputLumps[0].len ); outputLumps[0].load(stripName, "lightgrid"); R_LoadLightGrid( outputLumps[0].data, outputLumps[0].len ); outputLumps[0].load(stripName, "lightarray"); R_LoadLightGridArray( outputLumps[0].data, outputLumps[0].len ); // only set tr.world now that we know the entire level has loaded properly tr.world = &s_worldData; // Load the light parms for this level R_LoadLevelLightParms(); R_GetLightParmsForLevel(); } // new wrapper used for convenience to tell z_malloc()-fail recovery code whether it's safe to dump the cached-bsp or not. // extern qboolean gbUsingCachedMapDataRightNow; void RE_LoadWorldMap( const char *name ) { gbUsingCachedMapDataRightNow = qtrue; // !!!!!!!!!!!! RE_LoadWorldMap_Actual( name, s_worldData, 0 ); gbUsingCachedMapDataRightNow = qfalse; // !!!!!!!!!!!! }