/* Copyright (C) 1999-2007 id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "qrad.h" vec3_t texture_reflectivity[MAX_MAP_TEXINFO]; /* =================================================================== TEXTURE LIGHT VALUES =================================================================== */ /* ====================== CalcTextureReflectivity_Quake2 ====================== */ void CalcTextureReflectivity_Quake2( void ){ int i; int j, k, texels; int color[3]; int texel; byte *palette; char path[1024]; float r, scale; miptex_t *mt; sprintf( path, "%spics/colormap.pcx", gamedir ); // get the game palette Load256Image( path, NULL, &palette, NULL, NULL ); // allways set index 0 even if no textures texture_reflectivity[0][0] = 0.5; texture_reflectivity[0][1] = 0.5; texture_reflectivity[0][2] = 0.5; for ( i = 0 ; i < numtexinfo ; i++ ) { // see if an earlier texinfo allready got the value for ( j = 0 ; j < i ; j++ ) { if ( !strcmp( texinfo[i].texture, texinfo[j].texture ) ) { VectorCopy( texture_reflectivity[j], texture_reflectivity[i] ); break; } } if ( j != i ) { continue; } // load the wal file sprintf( path, "%stextures/%s.wal", gamedir, texinfo[i].texture ); if ( TryLoadFile( path, (void **)&mt ) == -1 ) { Sys_Printf( "Couldn't load %s\n", path ); texture_reflectivity[i][0] = 0.5; texture_reflectivity[i][1] = 0.5; texture_reflectivity[i][2] = 0.5; continue; } texels = LittleLong( mt->width ) * LittleLong( mt->height ); color[0] = color[1] = color[2] = 0; for ( j = 0 ; j < texels ; j++ ) { texel = ( (byte *)mt )[LittleLong( mt->offsets[0] ) + j]; for ( k = 0 ; k < 3 ; k++ ) color[k] += palette[texel * 3 + k]; } for ( j = 0 ; j < 3 ; j++ ) { r = color[j] / texels / 255.0; texture_reflectivity[i][j] = r; } // scale the reflectivity up, because the textures are // so dim scale = ColorNormalize( texture_reflectivity[i], texture_reflectivity[i] ); if ( scale < 0.5 ) { scale *= 2; VectorScale( texture_reflectivity[i], scale, texture_reflectivity[i] ); } #if 0 texture_reflectivity[i][0] = 0.5; texture_reflectivity[i][1] = 0.5; texture_reflectivity[i][2] = 0.5; #endif } } /* ====================== CalcTextureReflectivity_Heretic2 ====================== */ void CalcTextureReflectivity_Heretic2( void ){ int i; int j, texels; int color[3]; int texel; char path[1024]; float r; miptex_m8_t *mt; miptex_m32_t *mt32; byte *pos; // allways set index 0 even if no textures texture_reflectivity[0][0] = 0.5; texture_reflectivity[0][1] = 0.5; texture_reflectivity[0][2] = 0.5; for ( i = 0 ; i < numtexinfo ; i++ ) { // see if an earlier texinfo allready got the value for ( j = 0 ; j < i ; j++ ) { if ( !strcmp( texinfo[i].texture, texinfo[j].texture ) ) { VectorCopy( texture_reflectivity[j], texture_reflectivity[i] ); break; } } if ( j != i ) { continue; } // load the wal file sprintf( path, "%stextures/%s.m32", gamedir, texinfo[i].texture ); if ( TryLoadFile( path, (void **)&mt32 ) == -1 ) { sprintf( path, "%stextures/%s.m8", gamedir, texinfo[i].texture ); if ( TryLoadFile( path, (void **)&mt ) == -1 ) { Sys_Printf( "Couldn't load %s\n", path ); texture_reflectivity[i][0] = 0.5; texture_reflectivity[i][1] = 0.5; texture_reflectivity[i][2] = 0.5; continue; } texels = LittleLong( mt->width[0] ) * LittleLong( mt->height[0] ); color[0] = color[1] = color[2] = 0; for ( j = 0 ; j < texels ; j++ ) { texel = ( (byte *)mt )[LittleLong( mt->offsets[0] ) + j]; color[0] += mt->palette[texel].r; color[1] += mt->palette[texel].g; color[2] += mt->palette[texel].b; } free( mt ); } else { texels = LittleLong( mt32->width[0] ) * LittleLong( mt32->height[0] ); color[0] = color[1] = color[2] = 0; for ( j = 0 ; j < texels ; j++ ) { pos = (byte *)mt32 + mt32->offsets[0] + ( j << 2 ); color[0] += *pos++; // r color[1] += *pos++; // g color[2] += *pos++; // b } free( mt32 ); } for ( j = 0 ; j < 3 ; j++ ) { r = color[j] / ( (float) texels * 255.0 ); texture_reflectivity[i][j] = r; } } } /* ======================================================================= MAKE FACES ======================================================================= */ /* ============= WindingFromFace ============= */ winding_t *WindingFromFace( dface_t *f ){ int i; int se; dvertex_t *dv; int v; winding_t *w; w = AllocWinding( f->numedges ); w->numpoints = f->numedges; for ( i = 0 ; i < f->numedges ; i++ ) { se = dsurfedges[f->firstedge + i]; if ( se < 0 ) { v = dedges[-se].v[1]; } else{ v = dedges[se].v[0]; } dv = &dvertexes[v]; VectorCopy( dv->point, w->p[i] ); } RemoveColinearPoints( w ); return w; } /* ============= BaseLightForFace ============= */ void BaseLightForFace( dface_t *f, vec3_t color ){ texinfo_t *tx; // // check for light emited by texture // tx = &texinfo[f->texinfo]; if ( !( tx->flags & SURF_LIGHT ) || tx->value == 0 ) { VectorClear( color ); return; } VectorScale( texture_reflectivity[f->texinfo], tx->value, color ); } qboolean IsSky( dface_t *f ){ texinfo_t *tx; tx = &texinfo[f->texinfo]; if ( tx->flags & SURF_SKY ) { return true; } return false; } /* ============= MakePatchForFace ============= */ float totalarea; void MakePatchForFace( int fn, winding_t *w ){ dface_t *f; float area; patch_t *patch; dplane_t *pl; int i; vec3_t color; dleaf_t *leaf; f = &dfaces[fn]; area = WindingArea( w ); totalarea += area; patch = &patches[num_patches]; if ( num_patches == MAX_PATCHES ) { Error( "num_patches == MAX_PATCHES" ); } patch->next = face_patches[fn]; face_patches[fn] = patch; patch->winding = w; if ( f->side ) { patch->plane = &backplanes[f->planenum]; } else{ patch->plane = &dplanes[f->planenum]; } if ( face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] ) { // origin offset faces must create new planes if ( numplanes + fakeplanes >= MAX_MAP_PLANES ) { Error( "numplanes + fakeplanes >= MAX_MAP_PLANES" ); } pl = &dplanes[numplanes + fakeplanes]; fakeplanes++; *pl = *( patch->plane ); pl->dist += DotProduct( face_offset[fn], pl->normal ); patch->plane = pl; } WindingCenter( w, patch->origin ); VectorAdd( patch->origin, patch->plane->normal, patch->origin ); leaf = Rad_PointInLeaf( patch->origin ); patch->cluster = leaf->cluster; if ( patch->cluster == -1 ) { Sys_FPrintf( SYS_VRB, "patch->cluster == -1\n" ); } patch->area = area; if ( patch->area <= 1 ) { patch->area = 1; } patch->sky = IsSky( f ); VectorCopy( texture_reflectivity[f->texinfo], patch->reflectivity ); // non-bmodel patches can emit light if ( fn < dmodels[0].numfaces ) { BaseLightForFace( f, patch->baselight ); ColorNormalize( patch->reflectivity, color ); for ( i = 0 ; i < 3 ; i++ ) patch->baselight[i] *= color[i]; VectorCopy( patch->baselight, patch->totallight ); } num_patches++; } entity_t *EntityForModel( int modnum ){ int i; char *s; char name[16]; sprintf( name, "*%i", modnum ); // search the entities for one using modnum for ( i = 0 ; i < num_entities ; i++ ) { s = ValueForKey( &entities[i], "model" ); if ( !strcmp( s, name ) ) { return &entities[i]; } } return &entities[0]; } /* ============= MakePatches ============= */ void MakePatches( void ){ int i, j, k; dface_t *f; int fn; winding_t *w; dmodel_t *mod; vec3_t origin; entity_t *ent; Sys_FPrintf( SYS_VRB, "%i faces\n", numfaces ); for ( i = 0 ; i < nummodels ; i++ ) { mod = &dmodels[i]; ent = EntityForModel( i ); // bmodels with origin brushes need to be offset into their // in-use position GetVectorForKey( ent, "origin", origin ); //VectorCopy (vec3_origin, origin); for ( j = 0 ; j < mod->numfaces ; j++ ) { fn = mod->firstface + j; face_entity[fn] = ent; VectorCopy( origin, face_offset[fn] ); f = &dfaces[fn]; w = WindingFromFace( f ); for ( k = 0 ; k < w->numpoints ; k++ ) { VectorAdd( w->p[k], origin, w->p[k] ); } MakePatchForFace( fn, w ); } } Sys_FPrintf( SYS_VRB, "%i sqaure feet\n", (int)( totalarea / 64 ) ); } /* ======================================================================= SUBDIVIDE ======================================================================= */ void FinishSplit( patch_t *patch, patch_t *newp ){ dleaf_t *leaf; VectorCopy( patch->baselight, newp->baselight ); VectorCopy( patch->totallight, newp->totallight ); VectorCopy( patch->reflectivity, newp->reflectivity ); newp->plane = patch->plane; newp->sky = patch->sky; patch->area = WindingArea( patch->winding ); newp->area = WindingArea( newp->winding ); if ( patch->area <= 1 ) { patch->area = 1; } if ( newp->area <= 1 ) { newp->area = 1; } WindingCenter( patch->winding, patch->origin ); VectorAdd( patch->origin, patch->plane->normal, patch->origin ); leaf = Rad_PointInLeaf( patch->origin ); patch->cluster = leaf->cluster; if ( patch->cluster == -1 ) { Sys_FPrintf( SYS_VRB, "patch->cluster == -1\n" ); } WindingCenter( newp->winding, newp->origin ); VectorAdd( newp->origin, newp->plane->normal, newp->origin ); leaf = Rad_PointInLeaf( newp->origin ); newp->cluster = leaf->cluster; if ( newp->cluster == -1 ) { Sys_FPrintf( SYS_VRB, "patch->cluster == -1\n" ); } } /* ============= SubdividePatch Chops the patch only if its local bounds exceed the max size ============= */ void SubdividePatch( patch_t *patch ){ winding_t *w, *o1, *o2; vec3_t mins, maxs, total; vec3_t split; vec_t dist; int i, j; vec_t v; patch_t *newp; w = patch->winding; mins[0] = mins[1] = mins[2] = 99999; maxs[0] = maxs[1] = maxs[2] = -99999; for ( i = 0 ; i < w->numpoints ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) { v = w->p[i][j]; if ( v < mins[j] ) { mins[j] = v; } if ( v > maxs[j] ) { maxs[j] = v; } } } VectorSubtract( maxs, mins, total ); for ( i = 0 ; i < 3 ; i++ ) if ( total[i] > ( subdiv + 1 ) ) { break; } if ( i == 3 ) { // no splitting needed return; } // // split the winding // VectorCopy( vec3_origin, split ); split[i] = 1; dist = ( mins[i] + maxs[i] ) * 0.5; ClipWindingEpsilon( w, split, dist, ON_EPSILON, &o1, &o2 ); // // create a new patch // if ( num_patches == MAX_PATCHES ) { Error( "MAX_PATCHES" ); } newp = &patches[num_patches]; num_patches++; newp->next = patch->next; patch->next = newp; patch->winding = o1; newp->winding = o2; FinishSplit( patch, newp ); SubdividePatch( patch ); SubdividePatch( newp ); } /* ============= DicePatch Chops the patch by a global grid ============= */ void DicePatch( patch_t *patch ){ winding_t *w, *o1, *o2; vec3_t mins, maxs; vec3_t split; vec_t dist; int i; patch_t *newp; w = patch->winding; WindingBounds( w, mins, maxs ); for ( i = 0 ; i < 3 ; i++ ) if ( floor( ( mins[i] + 1 ) / subdiv ) < floor( ( maxs[i] - 1 ) / subdiv ) ) { break; } if ( i == 3 ) { // no splitting needed return; } // // split the winding // VectorCopy( vec3_origin, split ); split[i] = 1; dist = subdiv * ( 1 + floor( ( mins[i] + 1 ) / subdiv ) ); ClipWindingEpsilon( w, split, dist, ON_EPSILON, &o1, &o2 ); // // create a new patch // if ( num_patches == MAX_PATCHES ) { Error( "MAX_PATCHES" ); } newp = &patches[num_patches]; num_patches++; newp->next = patch->next; patch->next = newp; patch->winding = o1; newp->winding = o2; FinishSplit( patch, newp ); DicePatch( patch ); DicePatch( newp ); } /* ============= SubdividePatches ============= */ void SubdividePatches( void ){ int i, num; if ( subdiv < 1 ) { return; } num = num_patches; // because the list will grow for ( i = 0 ; i < num ; i++ ) { // SubdividePatch (&patches[i]); DicePatch( &patches[i] ); } Sys_FPrintf( SYS_VRB, "%i patches after subdivision\n", num_patches ); } //=====================================================================