From 1524321396a91ef9fa1f19b4873e53e2c5d9e50a Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Tue, 3 Sep 2013 16:02:10 -0700 Subject: * Fixed Source Mod back-compatability by reordering methods in a couple interfaces. * Added missing singleplayer OSX libs * Updated vpc and libs. --- mp/src/utils/vrad/vrad.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'mp/src/utils/vrad/vrad.cpp') diff --git a/mp/src/utils/vrad/vrad.cpp b/mp/src/utils/vrad/vrad.cpp index 854956ec..8100dc3f 100644 --- a/mp/src/utils/vrad/vrad.cpp +++ b/mp/src/utils/vrad/vrad.cpp @@ -93,7 +93,7 @@ bool g_bOnlyStaticProps = false; bool g_bShowStaticPropNormals = false; -float gamma = 0.5; +float gamma_value = 0.5; float indirect_sun = 1.0; float reflectivityScale = 1.0; qboolean do_extra = true; -- cgit v1.2.3 From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/utils/vrad/vrad.cpp | 5872 ++++++++++++++++++++++---------------------- 1 file changed, 2936 insertions(+), 2936 deletions(-) (limited to 'mp/src/utils/vrad/vrad.cpp') diff --git a/mp/src/utils/vrad/vrad.cpp b/mp/src/utils/vrad/vrad.cpp index 8100dc3f..70e481fb 100644 --- a/mp/src/utils/vrad/vrad.cpp +++ b/mp/src/utils/vrad/vrad.cpp @@ -1,2936 +1,2936 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -// -//=============================================================================// - -// vrad.c - -#include "vrad.h" -#include "physdll.h" -#include "lightmap.h" -#include "tier1/strtools.h" -#include "vmpi.h" -#include "macro_texture.h" -#include "vmpi_tools_shared.h" -#include "leaf_ambient_lighting.h" -#include "tools_minidump.h" -#include "loadcmdline.h" -#include "byteswap.h" - -#define ALLOWDEBUGOPTIONS (0 || _DEBUG) - -static FileHandle_t pFpTrans = NULL; - -/* - -NOTES ------ - -every surface must be divided into at least two patches each axis - -*/ - -CUtlVector g_Patches; -CUtlVector g_FacePatches; // constains all patches, children first -CUtlVector faceParents; // contains only root patches, use next parent to iterate -CUtlVector clusterChildren; -CUtlVector emitlight; -CUtlVector addlight; - -int num_sky_cameras; -sky_camera_t sky_cameras[MAX_MAP_AREAS]; -int area_sky_cameras[MAX_MAP_AREAS]; - -entity_t *face_entity[MAX_MAP_FACES]; -Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels -int fakeplanes; - -unsigned numbounce = 100; // 25; /* Originally this was 8 */ - -float maxchop = 4; // coarsest allowed number of luxel widths for a patch -float minchop = 4; // "-chop" tightest number of luxel widths for a patch, used on edges -float dispchop = 8.0f; // number of luxel widths for a patch -float g_MaxDispPatchRadius = 1500.0f; // Maximum radius allowed for displacement patches -qboolean g_bDumpPatches; -bool bDumpNormals = false; -bool g_bDumpRtEnv = false; -bool bRed2Black = true; -bool g_bFastAmbient = false; -bool g_bNoSkyRecurse = false; - -int junk; - -Vector ambient( 0, 0, 0 ); - -float lightscale = 1.0; -float dlight_threshold = 0.1; // was DIRECT_LIGHT constant - -char source[MAX_PATH] = ""; -char platformPath[MAX_PATH] = ""; - -char level_name[MAX_PATH] = ""; // map filename, without extension or path info - -char global_lights[MAX_PATH] = ""; -char designer_lights[MAX_PATH] = ""; -char level_lights[MAX_PATH] = ""; - -char vismatfile[_MAX_PATH] = ""; -char incrementfile[_MAX_PATH] = ""; - -IIncremental *g_pIncremental = 0; -bool g_bInterrupt = false; // Wsed with background lighting in WC. Tells VRAD - // to stop lighting. -float g_SunAngularExtent=0.0; - -float g_flSkySampleScale = 1.0; - -bool g_bLargeDispSampleRadius = false; - -bool g_bOnlyStaticProps = false; -bool g_bShowStaticPropNormals = false; - - -float gamma_value = 0.5; -float indirect_sun = 1.0; -float reflectivityScale = 1.0; -qboolean do_extra = true; -bool debug_extra = false; -qboolean do_fast = false; -qboolean do_centersamples = false; -int extrapasses = 4; -float smoothing_threshold = 0.7071067; // cos(45.0*(M_PI/180)) -// Cosine of smoothing angle(in radians) -float coring = 1.0; // Light threshold to force to blackness(minimizes lightmaps) -qboolean texscale = true; -int dlight_map = 0; // Setting to 1 forces direct lighting into different lightmap than radiosity - -float luxeldensity = 1.0; -unsigned num_degenerate_faces; - -qboolean g_bLowPriority = false; -qboolean g_bLogHashData = false; -bool g_bNoDetailLighting = false; -double g_flStartTime; -bool g_bStaticPropLighting = false; -bool g_bStaticPropPolys = false; -bool g_bTextureShadows = false; -bool g_bDisablePropSelfShadowing = false; - - -CUtlVector g_FacesVisibleToLights; - -RayTracingEnvironment g_RtEnv; - -dface_t *g_pFaces=0; - -// this is a list of material names used on static props which shouldn't cast shadows. a -// sequential search is used since we allow substring matches. its not time critical, and this -// functionality is a stopgap until vrad starts reading .vmt files. -CUtlVector g_NonShadowCastingMaterialStrings; -/* -=================================================================== - -MISC - -=================================================================== -*/ - - -int leafparents[MAX_MAP_LEAFS]; -int nodeparents[MAX_MAP_NODES]; - -void MakeParents (int nodenum, int parent) -{ - int i, j; - dnode_t *node; - - nodeparents[nodenum] = parent; - node = &dnodes[nodenum]; - - for (i=0 ; i<2 ; i++) - { - j = node->children[i]; - if (j < 0) - leafparents[-j - 1] = nodenum; - else - MakeParents (j, nodenum); - } -} - - -/* -=================================================================== - - TEXTURE LIGHT VALUES - -=================================================================== -*/ - -typedef struct -{ - char name[256]; - Vector value; - char *filename; -} texlight_t; - -#define MAX_TEXLIGHTS 128 - -texlight_t texlights[MAX_TEXLIGHTS]; -int num_texlights; - -/* -============ -ReadLightFile -============ -*/ -void ReadLightFile (char *filename) -{ - char buf[1024]; - int file_texlights = 0; - - FileHandle_t f = g_pFileSystem->Open( filename, "r" ); - if (!f) - { - Warning("Warning: Couldn't open texlight file %s.\n", filename); - return; - } - - Msg("[Reading texlights from '%s']\n", filename); - while ( CmdLib_FGets( buf, sizeof( buf ), f ) ) - { - // check ldr/hdr - char * scan = buf; - if ( !strnicmp( "hdr:", scan, 4) ) - { - scan += 4; - if ( ! g_bHDR ) - { - continue; - } - } - if ( !strnicmp( "ldr:", scan, 4) ) - { - scan += 4; - if ( g_bHDR ) - { - continue; - } - } - - scan += strspn( scan, " \t" ); - char NoShadName[1024]; - if ( sscanf(scan,"noshadow %s",NoShadName)==1) - { - char * dot = strchr( NoShadName, '.' ); - if ( dot ) // if they specify .vmt, kill it - * dot = 0; - //printf("add %s as a non shadow casting material\n",NoShadName); - g_NonShadowCastingMaterialStrings.AddToTail( strdup( NoShadName )); - } - else if ( sscanf( scan, "forcetextureshadow %s", NoShadName ) == 1 ) - { - //printf("add %s as a non shadow casting material\n",NoShadName); - ForceTextureShadowsOnModel( NoShadName ); - } - else - { - char szTexlight[256]; - Vector value; - if ( num_texlights == MAX_TEXLIGHTS ) - Error ("Too many texlights, max = %d", MAX_TEXLIGHTS); - - int argCnt = sscanf (scan, "%s ",szTexlight ); - - if( argCnt != 1 ) - { - if ( strlen( scan ) > 4 ) - Msg( "ignoring bad texlight '%s' in %s", scan, filename ); - continue; - } - - LightForString( scan + strlen( szTexlight ) + 1, value ); - - int j = 0; - for( j; j < num_texlights; j ++ ) - { - if ( strcmp( texlights[j].name, szTexlight ) == 0 ) - { - if ( strcmp( texlights[j].filename, filename ) == 0 ) - { - Msg( "ERROR\a: Duplication of '%s' in file '%s'!\n", - texlights[j].name, texlights[j].filename ); - } - else if ( texlights[j].value[0] != value[0] - || texlights[j].value[1] != value[1] - || texlights[j].value[2] != value[2] ) - { - Warning( "Warning: Overriding '%s' from '%s' with '%s'!\n", - texlights[j].name, texlights[j].filename, filename ); - } - else - { - Warning( "Warning: Redundant '%s' def in '%s' AND '%s'!\n", - texlights[j].name, texlights[j].filename, filename ); - } - break; - } - } - strcpy( texlights[j].name, szTexlight ); - VectorCopy( value, texlights[j].value ); - texlights[j].filename = filename; - file_texlights ++; - - num_texlights = max( num_texlights, j + 1 ); - } - } - qprintf ( "[%i texlights parsed from '%s']\n\n", file_texlights, filename); - g_pFileSystem->Close( f ); -} - - -/* -============ -LightForTexture -============ -*/ -void LightForTexture( const char *name, Vector& result ) -{ - int i; - - result[ 0 ] = result[ 1 ] = result[ 2 ] = 0; - - char baseFilename[ MAX_PATH ]; - - if ( Q_strncmp( "maps/", name, 5 ) == 0 ) - { - // this might be a patch texture for cubemaps. try to parse out the original filename. - if ( Q_strncmp( level_name, name + 5, Q_strlen( level_name ) ) == 0 ) - { - const char *base = name + 5 + Q_strlen( level_name ); - if ( *base == '/' ) - { - ++base; // step past the path separator - - // now we've gotten rid of the 'maps/level_name/' part, so we're left with - // 'originalName_%d_%d_%d'. - strcpy( baseFilename, base ); - bool foundSeparators = true; - for ( int i=0; i<3; ++i ) - { - char *underscore = Q_strrchr( baseFilename, '_' ); - if ( underscore && *underscore ) - { - *underscore = '\0'; - } - else - { - foundSeparators = false; - } - } - - if ( foundSeparators ) - { - name = baseFilename; - } - } - } - } - - for (i=0 ; inumedges); - w->numpoints = f->numedges; - - for (i=0 ; inumedges ; i++) - { - se = dsurfedges[f->firstedge + i]; - if (se < 0) - v = dedges[-se].v[1]; - else - v = dedges[se].v[0]; - - dv = &dvertexes[v]; - VectorAdd (dv->point, origin, w->p[i]); - } - - RemoveColinearPoints (w); - - return w; -} - -/* -============= -BaseLightForFace -============= -*/ -void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity ) -{ - texinfo_t *tx; - dtexdata_t *texdata; - - // - // check for light emited by texture - // - tx = &texinfo[f->texinfo]; - texdata = &dtexdata[tx->texdata]; - - LightForTexture (TexDataStringTable_GetString( texdata->nameStringTableID ), light); - - - *parea = texdata->height * texdata->width; - - VectorScale( texdata->reflectivity, reflectivityScale, reflectivity ); - - // always keep this less than 1 or the solution will not converge - for ( int i = 0; i < 3; i++ ) - { - if ( reflectivity[i] > 0.99 ) - reflectivity[i] = 0.99; - } -} - -qboolean IsSky (dface_t *f) -{ - texinfo_t *tx; - - tx = &texinfo[f->texinfo]; - if (tx->flags & SURF_SKY) - return true; - return false; -} - -#ifdef STATIC_FOG -/*============= -IsFog -=============*/ -qboolean IsFog( dface_t *f ) -{ - texinfo_t *tx; - - tx = &texinfo[f->texinfo]; - - // % denotes a fog texture - if( tx->texture[0] == '%' ) - return true; - - return false; -} -#endif - - -void ProcessSkyCameras() -{ - int i; - num_sky_cameras = 0; - for (i = 0; i < numareas; ++i) - { - area_sky_cameras[i] = -1; - } - - for (i = 0; i < num_entities; ++i) - { - entity_t *e = &entities[i]; - const char *name = ValueForKey (e, "classname"); - if (stricmp (name, "sky_camera")) - continue; - - Vector origin; - GetVectorForKey( e, "origin", origin ); - int node = PointLeafnum( origin ); - int area = -1; - if (node >= 0 && node < numleafs) area = dleafs[node].area; - float scale = FloatForKey( e, "scale" ); - - if (scale > 0.0f) - { - sky_cameras[num_sky_cameras].origin = origin; - sky_cameras[num_sky_cameras].sky_to_world = scale; - sky_cameras[num_sky_cameras].world_to_sky = 1.0f / scale; - sky_cameras[num_sky_cameras].area = area; - - if (area >= 0 && area < numareas) - { - area_sky_cameras[area] = num_sky_cameras; - } - - ++num_sky_cameras; - } - } - -} - - -/* -============= -MakePatchForFace -============= -*/ -float totalarea; -void MakePatchForFace (int fn, winding_t *w) -{ - dface_t *f = g_pFaces + fn; - float area; - CPatch *patch; - Vector centroid(0,0,0); - int i, j; - texinfo_t *tx; - - // get texture info - tx = &texinfo[f->texinfo]; - - // No patches at all for fog! -#ifdef STATIC_FOG - if ( IsFog( f ) ) - return; -#endif - - // the sky needs patches or the form factors don't work out correctly - // if (IsSky( f ) ) - // return; - - area = WindingArea (w); - if (area <= 0) - { - num_degenerate_faces++; - // Msg("degenerate face\n"); - return; - } - - totalarea += area; - - // get a patch - int ndxPatch = g_Patches.AddToTail(); - patch = &g_Patches[ndxPatch]; - memset( patch, 0, sizeof( CPatch ) ); - patch->ndxNext = g_Patches.InvalidIndex(); - patch->ndxNextParent = g_Patches.InvalidIndex(); - patch->ndxNextClusterChild = g_Patches.InvalidIndex(); - patch->child1 = g_Patches.InvalidIndex(); - patch->child2 = g_Patches.InvalidIndex(); - patch->parent = g_Patches.InvalidIndex(); - patch->needsBumpmap = tx->flags & SURF_BUMPLIGHT ? true : false; - - // link and save patch data - patch->ndxNext = g_FacePatches.Element( fn ); - g_FacePatches[fn] = ndxPatch; -// patch->next = face_g_Patches[fn]; -// face_g_Patches[fn] = patch; - - // compute a separate scale for chop - since the patch "scale" is the texture scale - // we want textures with higher resolution lighting to be chopped up more - float chopscale[2]; - chopscale[0] = chopscale[1] = 16.0f; - if ( texscale ) - { - // Compute the texture "scale" in s,t - for( i=0; i<2; i++ ) - { - patch->scale[i] = 0.0f; - chopscale[i] = 0.0f; - for( j=0; j<3; j++ ) - { - patch->scale[i] += - tx->textureVecsTexelsPerWorldUnits[i][j] * - tx->textureVecsTexelsPerWorldUnits[i][j]; - chopscale[i] += - tx->lightmapVecsLuxelsPerWorldUnits[i][j] * - tx->lightmapVecsLuxelsPerWorldUnits[i][j]; - } - patch->scale[i] = sqrt( patch->scale[i] ); - chopscale[i] = sqrt( chopscale[i] ); - } - } - else - { - patch->scale[0] = patch->scale[1] = 1.0f; - } - - patch->area = area; - - patch->sky = IsSky( f ); - - // chop scaled up lightmaps coarser - patch->luxscale = ((chopscale[0]+chopscale[1])/2); - patch->chop = maxchop; - - -#ifdef STATIC_FOG - patch->fog = FALSE; -#endif - - patch->winding = w; - - patch->plane = &dplanes[f->planenum]; - - // make a new plane to adjust for origined bmodels - if (face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] ) - { - dplane_t *pl; - - // 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; - } - - patch->faceNumber = fn; - WindingCenter (w, patch->origin); - - // Save "center" for generating the face normals later. - VectorSubtract( patch->origin, face_offset[fn], face_centroids[fn] ); - - VectorCopy( patch->plane->normal, patch->normal ); - - WindingBounds (w, patch->face_mins, patch->face_maxs); - VectorCopy( patch->face_mins, patch->mins ); - VectorCopy( patch->face_maxs, patch->maxs ); - - BaseLightForFace( f, patch->baselight, &patch->basearea, patch->reflectivity ); - - // Chop all texlights very fine. - if ( !VectorCompare( patch->baselight, vec3_origin ) ) - { - // patch->chop = do_extra ? maxchop / 2 : maxchop; - tx->flags |= SURF_LIGHT; - } - - // get rid of do extra functionality on displacement surfaces - if( ValidDispFace( f ) ) - { - patch->chop = maxchop; - } - - // FIXME: If we wanted to add a dependency from vrad to the material system, - // we could do this. It would add a bunch of file accesses, though: - - /* - // Check for a material var which would override the patch chop - bool bFound; - const char *pMaterialName = TexDataStringTable_GetString( dtexdata[ tx->texdata ].nameStringTableID ); - MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, &bFound, false ); - if ( bFound ) - { - const char *pChopValue = GetMaterialVar( hMaterial, "%chop" ); - if ( pChopValue ) - { - float flChopValue; - if ( sscanf( pChopValue, "%f", &flChopValue ) > 0 ) - { - patch->chop = flChopValue; - } - } - } - */ -} - - -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 ; inumfaces ; j++) - { - fn = mod->firstface + j; - face_entity[fn] = ent; - VectorCopy (origin, face_offset[fn]); - f = &g_pFaces[fn]; - if( f->dispinfo == -1 ) - { - w = WindingFromFace (f, origin ); - MakePatchForFace( fn, w ); - } - } - } - - if (num_degenerate_faces > 0) - { - qprintf("%d degenerate faces\n", num_degenerate_faces ); - } - - qprintf ("%i square feet [%.2f square inches]\n", (int)(totalarea/144), totalarea ); - - // make the displacement surface patches - StaticDispMgr()->MakePatches(); -} - -/* -======================================================================= - -SUBDIVIDE - -======================================================================= -*/ - - -//----------------------------------------------------------------------------- -// Purpose: does this surface take/emit light -//----------------------------------------------------------------------------- -bool PreventSubdivision( CPatch *patch ) -{ - dface_t *f = g_pFaces + patch->faceNumber; - texinfo_t *tx = &texinfo[f->texinfo]; - - if (tx->flags & SURF_NOCHOP) - return true; - - if (tx->flags & SURF_NOLIGHT && !(tx->flags & SURF_LIGHT)) - return true; - - return false; -} - - -//----------------------------------------------------------------------------- -// Purpose: subdivide the "parent" patch -//----------------------------------------------------------------------------- -int CreateChildPatch( int nParentIndex, winding_t *pWinding, float flArea, const Vector &vecCenter ) -{ - int nChildIndex = g_Patches.AddToTail(); - - CPatch *child = &g_Patches[nChildIndex]; - CPatch *parent = &g_Patches[nParentIndex]; - - // copy all elements of parent patch to children - *child = *parent; - - // Set up links - child->ndxNext = g_Patches.InvalidIndex(); - child->ndxNextParent = g_Patches.InvalidIndex(); - child->ndxNextClusterChild = g_Patches.InvalidIndex(); - child->child1 = g_Patches.InvalidIndex(); - child->child2 = g_Patches.InvalidIndex(); - child->parent = nParentIndex; - child->m_IterationKey = 0; - - child->winding = pWinding; - child->area = flArea; - - VectorCopy( vecCenter, child->origin ); - if ( ValidDispFace( g_pFaces + child->faceNumber ) ) - { - // shouldn't get here anymore!! - Msg( "SubdividePatch: Error - Should not be here!\n" ); - StaticDispMgr()->GetDispSurfNormal( child->faceNumber, child->origin, child->normal, true ); - } - else - { - GetPhongNormal( child->faceNumber, child->origin, child->normal ); - } - - child->planeDist = child->plane->dist; - WindingBounds(child->winding, child->mins, child->maxs); - - if ( !VectorCompare( child->baselight, vec3_origin ) ) - { - // don't check edges on surf lights - return nChildIndex; - } - - // Subdivide patch towards minchop if on the edge of the face - Vector total; - VectorSubtract( child->maxs, child->mins, total ); - VectorScale( total, child->luxscale, total ); - if ( child->chop > minchop && (total[0] < child->chop) && (total[1] < child->chop) && (total[2] < child->chop) ) - { - for ( int i=0; i<3; ++i ) - { - if ( (child->face_maxs[i] == child->maxs[i] || child->face_mins[i] == child->mins[i] ) - && total[i] > minchop ) - { - child->chop = max( minchop, child->chop / 2 ); - break; - } - } - } - - return nChildIndex; -} - - -//----------------------------------------------------------------------------- -// Purpose: subdivide the "parent" patch -//----------------------------------------------------------------------------- -void SubdividePatch( int ndxPatch ) -{ - winding_t *w, *o1, *o2; - Vector total; - Vector split; - vec_t dist; - vec_t widest = -1; - int i, widest_axis = -1; - bool bSubdivide = false; - - // get the current patch - CPatch *patch = &g_Patches.Element( ndxPatch ); - if ( !patch ) - return; - - // never subdivide sky patches - if ( patch->sky ) - return; - - // get the patch winding - w = patch->winding; - - // subdivide along the widest axis - VectorSubtract (patch->maxs, patch->mins, total); - VectorScale( total, patch->luxscale, total ); - for (i=0 ; i<3 ; i++) - { - if ( total[i] > widest ) - { - widest_axis = i; - widest = total[i]; - } - - if ( (total[i] >= patch->chop) && (total[i] >= minchop) ) - { - bSubdivide = true; - } - } - - if ((!bSubdivide) && widest_axis != -1) - { - // make more square - if (total[widest_axis] > total[(widest_axis + 1) % 3] * 2 && total[widest_axis] > total[(widest_axis + 2) % 3] * 2) - { - if (patch->chop > minchop) - { - bSubdivide = true; - patch->chop = max( minchop, patch->chop / 2 ); - } - } - } - - if ( !bSubdivide ) - return; - - // split the winding - VectorCopy (vec3_origin, split); - split[widest_axis] = 1; - dist = (patch->mins[widest_axis] + patch->maxs[widest_axis])*0.5f; - ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2); - - // calculate the area of the patches to see if they are "significant" - Vector center1, center2; - float area1 = WindingAreaAndBalancePoint( o1, center1 ); - float area2 = WindingAreaAndBalancePoint( o2, center2 ); - - if( area1 == 0 || area2 == 0 ) - { - Msg( "zero area child patch\n" ); - return; - } - - // create new child patches - int ndxChild1Patch = CreateChildPatch( ndxPatch, o1, area1, center1 ); - int ndxChild2Patch = CreateChildPatch( ndxPatch, o2, area2, center2 ); - - // FIXME: This could go into CreateChildPatch if child1, child2 were stored in the patch as child[0], child[1] - patch = &g_Patches.Element( ndxPatch ); - patch->child1 = ndxChild1Patch; - patch->child2 = ndxChild2Patch; - - SubdividePatch( ndxChild1Patch ); - SubdividePatch( ndxChild2Patch ); -} - - -/* -============= -SubdividePatches -============= -*/ -void SubdividePatches (void) -{ - unsigned i, num; - - if (numbounce == 0) - return; - - unsigned int uiPatchCount = g_Patches.Size(); - qprintf ("%i patches before subdivision\n", uiPatchCount); - - for (i = 0; i < uiPatchCount; i++) - { - CPatch *pCur = &g_Patches.Element( i ); - pCur->planeDist = pCur->plane->dist; - - pCur->ndxNextParent = faceParents.Element( pCur->faceNumber ); - faceParents[pCur->faceNumber] = pCur - g_Patches.Base(); - } - - for (i=0 ; i< uiPatchCount; i++) - { - CPatch *patch = &g_Patches.Element( i ); - patch->parent = -1; - if ( PreventSubdivision(patch) ) - continue; - - if (!do_fast) - { - if( g_pFaces[patch->faceNumber].dispinfo == -1 ) - { - SubdividePatch( i ); - } - else - { - StaticDispMgr()->SubdividePatch( i ); - } - } - } - - // fixup next pointers - for (i = 0; i < (unsigned)numfaces; i++) - { - g_FacePatches[i] = g_FacePatches.InvalidIndex(); - } - - uiPatchCount = g_Patches.Size(); - for (i = 0; i < uiPatchCount; i++) - { - CPatch *pCur = &g_Patches.Element( i ); - pCur->ndxNext = g_FacePatches.Element( pCur->faceNumber ); - g_FacePatches[pCur->faceNumber] = pCur - g_Patches.Base(); - -#if 0 - CPatch *prev; - prev = face_g_Patches[g_Patches[i].faceNumber]; - g_Patches[i].next = prev; - face_g_Patches[g_Patches[i].faceNumber] = &g_Patches[i]; -#endif - } - - // Cache off the leaf number: - // We have to do this after subdivision because some patches span leaves. - // (only the faces for model #0 are split by it's BSP which is what governs the PVS, and the leaves we're interested in) - // Sub models (1-255) are only split for the BSP that their model forms. - // When those patches are subdivided their origins can end up in a different leaf. - // The engine will split (clip) those faces at run time to the world BSP because the models - // are dynamic and can be moved. In the software renderer, they must be split exactly in order - // to sort per polygon. - for ( i = 0; i < uiPatchCount; i++ ) - { - g_Patches[i].clusterNumber = ClusterFromPoint( g_Patches[i].origin ); - - // - // test for point in solid space (can happen with detail and displacement surfaces) - // - if( g_Patches[i].clusterNumber == -1 ) - { - for( int j = 0; j < g_Patches[i].winding->numpoints; j++ ) - { - int clusterNumber = ClusterFromPoint( g_Patches[i].winding->p[j] ); - if( clusterNumber != -1 ) - { - g_Patches[i].clusterNumber = clusterNumber; - break; - } - } - } - } - - // build the list of patches that need to be lit - for ( num = 0; num < uiPatchCount; num++ ) - { - // do them in reverse order - i = uiPatchCount - num - 1; - - // skip patches with children - CPatch *pCur = &g_Patches.Element( i ); - if( pCur->child1 == g_Patches.InvalidIndex() ) - { - if( pCur->clusterNumber != - 1 ) - { - pCur->ndxNextClusterChild = clusterChildren.Element( pCur->clusterNumber ); - clusterChildren[pCur->clusterNumber] = pCur - g_Patches.Base(); - } - } - -#if 0 - if (g_Patches[i].child1 == g_Patches.InvalidIndex() ) - { - if( g_Patches[i].clusterNumber != -1 ) - { - g_Patches[i].nextclusterchild = cluster_children[g_Patches[i].clusterNumber]; - cluster_children[g_Patches[i].clusterNumber] = &g_Patches[i]; - } - } -#endif - } - - qprintf ("%i patches after subdivision\n", uiPatchCount); -} - - -//===================================================================== - -/* -============= -MakeScales - - This is the primary time sink. - It can be run multi threaded. -============= -*/ -int total_transfer; -int max_transfer; - - -//----------------------------------------------------------------------------- -// Purpose: Computes the form factor from a polygon patch to a differential patch -// using formula 81 of Philip Dutre's Global Illumination Compendium, -// phil@graphics.cornell.edu, http://www.graphics.cornell.edu/~phil/GI/ -//----------------------------------------------------------------------------- -float FormFactorPolyToDiff ( CPatch *pPolygon, CPatch* pDifferential ) -{ - winding_t *pWinding = pPolygon->winding; - - float flFormFactor = 0.0f; - - for ( int iPoint = 0; iPoint < pWinding->numpoints; iPoint++ ) - { - int iNextPoint = ( iPoint < pWinding->numpoints - 1 ) ? iPoint + 1 : 0; - - Vector vGammaVector, vVector1, vVector2; - VectorSubtract( pWinding->p[ iPoint ], pDifferential->origin, vVector1 ); - VectorSubtract( pWinding->p[ iNextPoint ], pDifferential->origin, vVector2 ); - VectorNormalize( vVector1 ); - VectorNormalize( vVector2 ); - CrossProduct( vVector1, vVector2, vGammaVector ); - float flSinAlpha = VectorNormalize( vGammaVector ); - if (flSinAlpha < -1.0f || flSinAlpha > 1.0f) - return 0.0f; - vGammaVector *= asin( flSinAlpha ); - - flFormFactor += DotProduct( vGammaVector, pDifferential->normal ); - } - - flFormFactor *= ( 0.5f / pPolygon->area ); // divide by pi later, multiply by area later - - return flFormFactor; -} - - -//----------------------------------------------------------------------------- -// Purpose: Computes the form factor from a differential element to a differential -// element. This is okay when the distance between patches is 5 times -// greater than patch size. Lecture slides by Pat Hanrahan, -// http://graphics.stanford.edu/courses/cs348b-00/lectures/lecture17/radiosity.2.pdf -//----------------------------------------------------------------------------- -float FormFactorDiffToDiff ( CPatch *pDiff1, CPatch* pDiff2 ) -{ - Vector vDelta; - VectorSubtract( pDiff1->origin, pDiff2->origin, vDelta ); - float flLength = VectorNormalize( vDelta ); - - return -DotProduct( vDelta, pDiff1->normal ) * DotProduct( vDelta, pDiff2->normal ) / ( flLength * flLength ); -} - - - -void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers ) -//void MakeTransfer (CPatch *patch, CPatch *patch2, transfer_t *all_transfers ) -{ - Vector delta; - vec_t scale; - float trans; - transfer_t *transfer; - - // - // get patches - // - if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() ) - return; - - CPatch *pPatch1 = &g_Patches.Element( ndxPatch1 ); - CPatch *pPatch2 = &g_Patches.Element( ndxPatch2 ); - - if (IsSky( &g_pFaces[ pPatch2->faceNumber ] ) ) - return; - - // overflow check! - if ( pPatch1->numtransfers >= MAX_PATCHES) - { - return; - } - - // hack for patch areas that area <= 0 (degenerate) - if ( pPatch2->area <= 0) - { - return; - } - - transfer = &all_transfers[pPatch1->numtransfers]; - - scale = FormFactorDiffToDiff( pPatch2, pPatch1 ); - - // patch normals may be > 90 due to smoothing groups - if (scale <= 0) - { - //Msg("scale <= 0\n"); - return; - } - - // Test 5 times rule - Vector vDelta; - VectorSubtract( pPatch1->origin, pPatch2->origin, vDelta ); - float flThreshold = ( M_PI * 0.04 ) * DotProduct( vDelta, vDelta ); - - if (flThreshold < pPatch2->area) - { - scale = FormFactorPolyToDiff( pPatch2, pPatch1 ); - if (scale <= 0.0) - return; - } - - trans = (pPatch2->area*scale); - - if (trans <= TRANSFER_EPSILON) - { - return; - } - - transfer->patch = pPatch2 - g_Patches.Base(); - - // FIXME: why is this not trans? - transfer->transfer = trans; - -#if 0 - // DEBUG! Dump patches and transfer connection for displacements. This creates a lot of data, so only - // use it when you really want it - that is why it is #if-ed out. - if ( g_bDumpPatches ) - { - if ( !pFpTrans ) - { - pFpTrans = g_pFileSystem->Open( "trans.txt", "w" ); - } - Vector light = pPatch1->totallight.light[0] + pPatch1->directlight; - WriteWinding( pFpTrans, pPatch1->winding, light ); - light = pPatch2->totallight.light[0] + pPatch2->directlight; - WriteWinding( pFpTrans, pPatch2->winding, light ); - WriteLine( pFpTrans, pPatch1->origin, pPatch2->origin, Vector( 255, 0, 255 ) ); - } -#endif - - pPatch1->numtransfers++; -} - - -void MakeScales ( int ndxPatch, transfer_t *all_transfers ) -{ - int j; - float total; - transfer_t *t, *t2; - total = 0; - - if( ndxPatch == g_Patches.InvalidIndex() ) - return; - CPatch *patch = &g_Patches.Element( ndxPatch ); - - // copy the transfers out - if (patch->numtransfers) - { - if (patch->numtransfers > max_transfer) - { - max_transfer = patch->numtransfers; - } - - - patch->transfers = ( transfer_t* )calloc (1, patch->numtransfers * sizeof(transfer_t)); - if (!patch->transfers) - Error ("Memory allocation failure"); - - // get total transfer energy - t2 = all_transfers; - - // overflow check! - for (j=0 ; jnumtransfers ; j++, t2++) - { - total += t2->transfer; - } - - // the total transfer should be PI, but we need to correct errors due to overlaping surfaces - if (total > M_PI) - total = 1.0f/total; - else - total = 1.0f/M_PI; - - t = patch->transfers; - t2 = all_transfers; - for (j=0 ; jnumtransfers ; j++, t++, t2++) - { - t->transfer = t2->transfer*total; - t->patch = t2->patch; - } - if (patch->numtransfers > max_transfer) - { - max_transfer = patch->numtransfers; - } - } - else - { - // Error - patch has no transfers - // patch->totallight[2] = 255; - } - - ThreadLock (); - total_transfer += patch->numtransfers; - ThreadUnlock (); -} - -/* -============= -WriteWorld -============= -*/ -void WriteWorld (char *name, int iBump) -{ - unsigned j; - FileHandle_t out; - CPatch *patch; - - out = g_pFileSystem->Open( name, "w" ); - if (!out) - Error ("Couldn't open %s", name); - - unsigned int uiPatchCount = g_Patches.Size(); - for (j=0; jchild1 != g_Patches.InvalidIndex() ) - continue; - - if( patch->clusterNumber == -1 ) - { - Vector vGreen; - VectorClear( vGreen ); - vGreen[1] = 256.0f; - WriteWinding( out, patch->winding, vGreen ); - } - else - { - Vector light = patch->totallight.light[iBump] + patch->directlight; - WriteWinding( out, patch->winding, light ); - if( bDumpNormals ) - { - WriteNormal( out, patch->origin, patch->plane->normal, 15.0f, patch->plane->normal * 255.0f ); - } - } - } - - g_pFileSystem->Close( out ); -} - -void WriteRTEnv (char *name) -{ - FileHandle_t out; - - out = g_pFileSystem->Open( name, "w" ); - if (!out) - Error ("Couldn't open %s", name); - - winding_t *triw = AllocWinding( 3 ); - triw->numpoints = 3; - - for( int i = 0; i < g_RtEnv.OptimizedTriangleList.Size(); i++ ) - { - triw->p[0] = g_RtEnv.OptimizedTriangleList[i].Vertex( 0); - triw->p[1] = g_RtEnv.OptimizedTriangleList[i].Vertex( 1); - triw->p[2] = g_RtEnv.OptimizedTriangleList[i].Vertex( 2); - int id = g_RtEnv.OptimizedTriangleList[i].m_Data.m_GeometryData.m_nTriangleID; - Vector color(0, 0, 0); - if (id & TRACE_ID_OPAQUE) color.Init(0, 255, 0); - if (id & TRACE_ID_SKY) color.Init(0, 0, 255); - if (id & TRACE_ID_STATICPROP) color.Init(255, 0, 0); - WriteWinding(out, triw, color); - } - FreeWinding(triw); - - g_pFileSystem->Close( out ); -} - -void WriteWinding (FileHandle_t out, winding_t *w, Vector& color ) -{ - int i; - - CmdLib_FPrintf (out, "%i\n", w->numpoints); - for (i=0 ; inumpoints ; i++) - { - CmdLib_FPrintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", - w->p[i][0], - w->p[i][1], - w->p[i][2], - color[ 0 ] / 256, - color[ 1 ] / 256, - color[ 2 ] / 256 ); - } -} - - -void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir, - float length, Vector const &color ) -{ - CmdLib_FPrintf( out, "2\n" ); - CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", - nPos.x, nPos.y, nPos.z, - color.x / 256, color.y / 256, color.z / 256 ); - CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", - nPos.x + ( nDir.x * length ), - nPos.y + ( nDir.y * length ), - nPos.z + ( nDir.z * length ), - color.x / 256, color.y / 256, color.z / 256 ); -} - -void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color ) -{ - CmdLib_FPrintf( out, "2\n" ); - CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", - vecPos1.x, vecPos1.y, vecPos1.z, - color.x / 256, color.y / 256, color.z / 256 ); - CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", - vecPos2.x, vecPos2.y, vecPos2.z, - color.x / 256, color.y / 256, color.z / 256 ); -} - -void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result ) -{ - FileHandle_t out; - - out = g_pFileSystem->Open( pFileName, "a" ); - if (!out) - Error ("Couldn't open %s", pFileName); - - // Draws rays - for ( int i = 0; i < 4; ++i ) - { - Vector vecOrigin = rays.origin.Vec(i); - Vector vecEnd = rays.direction.Vec(i); - VectorNormalize( vecEnd ); - vecEnd *= SubFloat( result.HitDistance, i ); - vecEnd += vecOrigin; - WriteLine( out, vecOrigin, vecEnd, Vector( 256, 0, 0 ) ); - WriteNormal( out, vecEnd, result.surface_normal.Vec(i), 10.0f, Vector( 256, 265, 0 ) ); - } - - g_pFileSystem->Close( out ); -} - - -/* -============= -CollectLight -============= -*/ -// patch's totallight += new light received to each patch -// patch's emitlight = addlight (newly received light from GatherLight) -// patch's addlight = 0 -// pull received light from children. -void CollectLight( Vector& total ) -{ - int i, j; - CPatch *patch; - - VectorFill( total, 0 ); - - // process patches in reverse order so that children are processed before their parents - unsigned int uiPatchCount = g_Patches.Size(); - for( i = uiPatchCount - 1; i >= 0; i-- ) - { - patch = &g_Patches.Element( i ); - int normalCount = patch->needsBumpmap ? NUM_BUMP_VECTS+1 : 1; - // sky's never collect light, it is just dropped - if (patch->sky) - { - VectorFill( emitlight[ i ], 0 ); - } - else if ( patch->child1 == g_Patches.InvalidIndex() ) - { - // This is a leaf node. - for ( j = 0; j < normalCount; j++ ) - { - VectorAdd( patch->totallight.light[j], addlight[i].light[j], patch->totallight.light[j] ); - } - VectorCopy( addlight[i].light[0], emitlight[i] ); - VectorAdd( total, emitlight[i], total ); - } - else - { - // This is an interior node. - // Pull received light from children. - float s1, s2; - CPatch *child1; - CPatch *child2; - - child1 = &g_Patches[patch->child1]; - child2 = &g_Patches[patch->child2]; - - // BUG: This doesn't do anything? - if ((int)patch->area != (int)(child1->area + child2->area)) - s1 = 0; - - s1 = child1->area / (child1->area + child2->area); - s2 = child2->area / (child1->area + child2->area); - - // patch->totallight = s1 * child1->totallight + s2 * child2->totallight - for ( j = 0; j < normalCount; j++ ) - { - VectorScale( child1->totallight.light[j], s1, patch->totallight.light[j] ); - VectorMA( patch->totallight.light[j], s2, child2->totallight.light[j], patch->totallight.light[j] ); - } - - // patch->emitlight = s1 * child1->emitlight + s2 * child2->emitlight - VectorScale( emitlight[patch->child1], s1, emitlight[i] ); - VectorMA( emitlight[i], s2, emitlight[patch->child2], emitlight[i] ); - } - for ( j = 0; j < NUM_BUMP_VECTS+1; j++ ) - { - VectorFill( addlight[ i ].light[j], 0 ); - } - } -} - -/* -============= -GatherLight - -Get light from other patches - Run multi-threaded -============= -*/ - -#ifdef _WIN32 -#pragma warning (disable:4701) -#endif - -extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal, - const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] ); - - -void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal ) -{ - Vector vecTexU( pTexinfo->textureVecsTexelsPerWorldUnits[0][0], pTexinfo->textureVecsTexelsPerWorldUnits[0][1], pTexinfo->textureVecsTexelsPerWorldUnits[0][2] ); - Vector vecTexV( pTexinfo->textureVecsTexelsPerWorldUnits[1][0], pTexinfo->textureVecsTexelsPerWorldUnits[1][1], pTexinfo->textureVecsTexelsPerWorldUnits[1][2] ); - Vector vecLightU( pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][2] ); - Vector vecLightV( pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][2] ); - - VectorNormalize( vecTexU ); - VectorNormalize( vecTexV ); - VectorNormalize( vecLightU ); - VectorNormalize( vecLightV ); - - bool bDoConversion = false; - if ( fabs( vecTexU.Dot( vecLightU ) ) < 0.999f ) - { - bDoConversion = true; - } - - if ( fabs( vecTexV.Dot( vecLightV ) ) < 0.999f ) - { - bDoConversion = true; - } - - if ( bDoConversion ) - { - matrix3x4_t matTex( vecTexU, vecTexV, vecNormal, vec3_origin ); - matrix3x4_t matLight( vecLightU, vecLightV, vecNormal, vec3_origin ); - matrix3x4_t matTmp; - ConcatTransforms ( matLight, matTex, matTmp ); - MatrixGetColumn( matTmp, 0, vecU ); - MatrixGetColumn( matTmp, 1, vecV ); - MatrixGetColumn( matTmp, 2, vecNormal ); - - Assert( fabs( vecTexU.Dot( vecTexV ) ) <= 0.001f ); - return; - } - - vecU = vecTexU; - vecV = vecTexV; -} - -void GatherLight (int threadnum, void *pUserData) -{ - int i, j, k; - transfer_t *trans; - int num; - CPatch *patch; - Vector sum, v; - - while (1) - { - j = GetThreadWork (); - if (j == -1) - break; - - patch = &g_Patches[j]; - - trans = patch->transfers; - num = patch->numtransfers; - if ( patch->needsBumpmap ) - { - Vector delta; - Vector bumpSum[NUM_BUMP_VECTS+1]; - Vector normals[NUM_BUMP_VECTS+1]; - - // Disps - bool bDisp = ( g_pFaces[patch->faceNumber].dispinfo != -1 ); - if ( bDisp ) - { - normals[0] = patch->normal; - texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo]; - Vector vecTexU, vecTexV; - PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] ); - - // use facenormal along with the smooth normal to build the three bump map vectors - GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] ); - } - else - { - GetPhongNormal( patch->faceNumber, patch->origin, normals[0] ); - - texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo]; - // use facenormal along with the smooth normal to build the three bump map vectors - GetBumpNormals( pTexinfo->textureVecsTexelsPerWorldUnits[0], - pTexinfo->textureVecsTexelsPerWorldUnits[1], patch->normal, - normals[0], &normals[1] ); - } - - // force the base lightmap to use the flat normal instead of the phong normal - // FIXME: why does the patch not use the phong normal? - normals[0] = patch->normal; - - for ( i = 0; i < NUM_BUMP_VECTS+1; i++ ) - { - VectorFill( bumpSum[i], 0 ); - } - - float dot; - for (k=0 ; kpatch]; - - // get vector to other patch - VectorSubtract (patch2->origin, patch->origin, delta); - VectorNormalize (delta); - // find light emitted from other patch - for(i=0; i<3; i++) - { - v[i] = emitlight[trans->patch][i] * patch2->reflectivity[i]; - } - // remove normal already factored into transfer steradian - float scale = 1.0f / DotProduct (delta, patch->normal); - VectorScale( v, trans->transfer * scale, v ); - - Vector bumpTransfer; - for ( i = 0; i < NUM_BUMP_VECTS+1; i++ ) - { - dot = DotProduct( delta, normals[i] ); - if ( dot <= 0 ) - { -// Assert( i > 0 ); // if this hits, then the transfer shouldn't be here. It doesn't face the flat normal of this face! - continue; - } - bumpTransfer = v * dot; - VectorAdd( bumpSum[i], bumpTransfer, bumpSum[i] ); - } - } - for ( i = 0; i < NUM_BUMP_VECTS+1; i++ ) - { - VectorCopy( bumpSum[i], addlight[j].light[i] ); - } - } - else - { - VectorFill( sum, 0 ); - for (k=0 ; kpatch][i] * g_Patches[trans->patch].reflectivity[i]; - } - VectorScale( v, trans->transfer, v ); - VectorAdd( sum, v, sum ); - } - VectorCopy( sum, addlight[j].light[0] ); - } - } -} - -#ifdef _WIN32 -#pragma warning (default:4701) -#endif - - -/* -============= -BounceLight -============= -*/ -void BounceLight (void) -{ - unsigned i; - Vector added; - char name[64]; - qboolean bouncing = numbounce > 0; - - unsigned int uiPatchCount = g_Patches.Size(); - for (i=0 ; iOpen( "lightemit.txt", "w" ); - - unsigned int uiPatchCount = g_Patches.Size(); - for (i=0 ; iClose( dFp ); - - for (i=0; iemitlight to receiver->addlight - unsigned int uiPatchCount = g_Patches.Size(); - RunThreadsOn (uiPatchCount, true, GatherLight); - // move newly received light (addlight) to light to be sent out (emitlight) - // start at children and pull light up to parents - // light is always received to leaf patches - CollectLight( added ); - - qprintf ("\tBounce #%i added RGB(%.0f, %.0f, %.0f)\n", i+1, added[0], added[1], added[2] ); - - if ( i+1 == numbounce || (added[0] < 1.0 && added[1] < 1.0 && added[2] < 1.0) ) - bouncing = false; - - i++; - if ( g_bDumpPatches && !bouncing && i != 1) - { - sprintf (name, "bounce%i.txt", i); - WriteWorld (name, 0); - } - } -} - - - -//----------------------------------------------------------------------------- -// Purpose: Counts the number of clusters in a map with no visibility -// Output : int -//----------------------------------------------------------------------------- -int CountClusters( void ) -{ - int clusterCount = 0; - - for ( int i = 0; i < numleafs; i++ ) - { - if ( dleafs[i].cluster > clusterCount ) - clusterCount = dleafs[i].cluster; - } - - return clusterCount + 1; -} - - -/* -============= -RadWorld -============= -*/ -void RadWorld_Start() -{ - unsigned i; - - if (luxeldensity < 1.0) - { - // Remember the old lightmap vectors. - float oldLightmapVecs[MAX_MAP_TEXINFO][2][4]; - for (i = 0; i < texinfo.Count(); i++) - { - for( int j=0; j < 2; j++ ) - { - for( int k=0; k < 3; k++ ) - { - oldLightmapVecs[i][j][k] = texinfo[i].lightmapVecsLuxelsPerWorldUnits[j][k]; - } - } - } - - // rescale luxels to be no denser than "luxeldensity" - for (i = 0; i < texinfo.Count(); i++) - { - texinfo_t *tx = &texinfo[i]; - - for (int j = 0; j < 2; j++ ) - { - Vector tmp( tx->lightmapVecsLuxelsPerWorldUnits[j][0], tx->lightmapVecsLuxelsPerWorldUnits[j][1], tx->lightmapVecsLuxelsPerWorldUnits[j][2] ); - float scale = VectorNormalize( tmp ); - // only rescale them if the current scale is "tighter" than the desired scale - // FIXME: since this writes out to the BSP file every run, once it's set high it can't be reset - // to a lower value. - if (fabs( scale ) > luxeldensity) - { - if (scale < 0) - { - scale = -luxeldensity; - } - else - { - scale = luxeldensity; - } - VectorScale( tmp, scale, tmp ); - tx->lightmapVecsLuxelsPerWorldUnits[j][0] = tmp.x; - tx->lightmapVecsLuxelsPerWorldUnits[j][1] = tmp.y; - tx->lightmapVecsLuxelsPerWorldUnits[j][2] = tmp.z; - } - } - } - - UpdateAllFaceLightmapExtents(); - } - - MakeParents (0, -1); - - BuildClusterTable(); - - // turn each face into a single patch - MakePatches (); - PairEdges (); - - // store the vertex normals calculated in PairEdges - // so that the can be written to the bsp file for - // use in the engine - SaveVertexNormals(); - - // subdivide patches to a maximum dimension - SubdividePatches (); - - // add displacement faces to cluster table - AddDispsToClusterTable(); - - // create directlights out of patches and lights - CreateDirectLights (); - - // set up sky cameras - ProcessSkyCameras(); -} - - -// This function should fill in the indices into g_pFaces[] for the faces -// with displacements that touch the specified leaf. -void STUB_GetDisplacementsTouchingLeaf( int iLeaf, CUtlVector &dispFaces ) -{ -} - - -void BuildFacesVisibleToLights( bool bAllVisible ) -{ - g_FacesVisibleToLights.SetSize( numfaces/8 + 1 ); - - if( bAllVisible ) - { - memset( g_FacesVisibleToLights.Base(), 0xFF, g_FacesVisibleToLights.Count() ); - return; - } - - // First merge all the light PVSes. - CUtlVector aggregate; - aggregate.SetSize( (dvis->numclusters/8) + 1 ); - memset( aggregate.Base(), 0, aggregate.Count() ); - - int nDWords = aggregate.Count() / 4; - int nBytes = aggregate.Count() - nDWords*4; - - for( directlight_t *dl = activelights; dl != NULL; dl = dl->next ) - { - byte *pIn = dl->pvs; - byte *pOut = aggregate.Base(); - for( int iDWord=0; iDWord < nDWords; iDWord++ ) - { - *((unsigned long*)pOut) |= *((unsigned long*)pIn); - pIn += 4; - pOut += 4; - } - - for( int iByte=0; iByte < nBytes; iByte++ ) - { - *pOut |= *pIn; - ++pOut; - ++pIn; - } - } - - - // Now tag any faces that are visible to this monster PVS. - for( int iCluster=0; iCluster < dvis->numclusters; iCluster++ ) - { - if( g_ClusterLeaves[iCluster].leafCount ) - { - if( aggregate[iCluster>>3] & (1 << (iCluster & 7)) ) - { - for ( int i = 0; i < g_ClusterLeaves[iCluster].leafCount; i++ ) - { - int iLeaf = g_ClusterLeaves[iCluster].leafs[i]; - - // Tag all the faces. - int iFace; - for( iFace=0; iFace < dleafs[iLeaf].numleaffaces; iFace++ ) - { - int index = dleafs[iLeaf].firstleafface + iFace; - index = dleaffaces[index]; - - assert( index < numfaces ); - g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7)); - } - - // Fill in STUB_GetDisplacementsTouchingLeaf when it's available - // so displacements get relit. - CUtlVector dispFaces; - STUB_GetDisplacementsTouchingLeaf( iLeaf, dispFaces ); - for( iFace=0; iFace < dispFaces.Count(); iFace++ ) - { - int index = dispFaces[iFace]; - g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7)); - } - } - } - } - } - - // For stats.. figure out how many faces it's going to touch. - int nFacesToProcess = 0; - for( int i=0; i < numfaces; i++ ) - { - if( g_FacesVisibleToLights[i>>3] & (1 << (i & 7)) ) - ++nFacesToProcess; - } -} - - - -void MakeAllScales (void) -{ - // determine visibility between patches - BuildVisMatrix (); - - // release visibility matrix - FreeVisMatrix (); - - Msg("transfers %d, max %d\n", total_transfer, max_transfer ); - - qprintf ("transfer lists: %5.1f megs\n" - , (float)total_transfer * sizeof(transfer_t) / (1024*1024)); -} - - -// Helper function. This can be useful to visualize the world and faces and see which face -// corresponds to which dface. -#if 0 - #include "iscratchpad3d.h" - void ScratchPad_DrawWorld() - { - IScratchPad3D *pPad = ScratchPad3D_Create(); - pPad->SetAutoFlush( false ); - - for ( int i=0; i < numfaces; i++ ) - { - dface_t *f = &g_pFaces[i]; - - // Draw the face's outline, then put text for its face index on it too. - CUtlVector points; - for ( int iEdge = 0; iEdge < f->numedges; iEdge++ ) - { - int v; - int se = dsurfedges[f->firstedge + iEdge]; - if ( se < 0 ) - v = dedges[-se].v[1]; - else - v = dedges[se].v[0]; - - dvertex_t *dv = &dvertexes[v]; - points.AddToTail( dv->point ); - } - - // Draw the outline. - Vector vCenter( 0, 0, 0 ); - for ( iEdge=0; iEdge < points.Count(); iEdge++ ) - { - pPad->DrawLine( CSPVert( points[iEdge] ), CSPVert( points[(iEdge+1)%points.Count()] ) ); - vCenter += points[iEdge]; - } - vCenter /= points.Count(); - - // Draw the text. - char str[512]; - Q_snprintf( str, sizeof( str ), "%d", i ); - - CTextParams params; - - params.m_bCentered = true; - params.m_bOutline = true; - params.m_flLetterWidth = 2; - params.m_vColor.Init( 1, 0, 0 ); - - VectorAngles( dplanes[f->planenum].normal, params.m_vAngles ); - params.m_bTwoSided = true; - - params.m_vPos = vCenter; - - pPad->DrawText( str, params ); - } - - pPad->Release(); - } -#endif - - -bool RadWorld_Go() -{ - g_iCurFace = 0; - - InitMacroTexture( source ); - - if( g_pIncremental ) - { - g_pIncremental->PrepareForLighting(); - - // Cull out faces that aren't visible to any of the lights that we're updating with. - BuildFacesVisibleToLights( false ); - } - else - { - // Mark all faces visible.. when not doing incremental lighting, it's highly - // likely that all faces are going to be touched by at least one light so don't - // waste time here. - BuildFacesVisibleToLights( true ); - } - - // build initial facelights - if (g_bUseMPI) - { - // RunThreadsOnIndividual (numfaces, true, BuildFacelights); - RunMPIBuildFacelights(); - } - else - { - RunThreadsOnIndividual (numfaces, true, BuildFacelights); - } - - // Was the process interrupted? - if( g_pIncremental && (g_iCurFace != numfaces) ) - return false; - - // Figure out the offset into lightmap data for each face. - PrecompLightmapOffsets(); - - // If we're doing incremental lighting, stop here. - if( g_pIncremental ) - { - g_pIncremental->Finalize(); - } - else - { - // free up the direct lights now that we have facelights - ExportDirectLightsToWorldLights(); - - if ( g_bDumpPatches ) - { - for( int iBump = 0; iBump < 4; ++iBump ) - { - char szName[64]; - sprintf ( szName, "bounce0_%d.txt", iBump ); - WriteWorld( szName, iBump ); - } - } - - if (numbounce > 0) - { - // allocate memory for emitlight/addlight - emitlight.SetSize( g_Patches.Size() ); - memset( emitlight.Base(), 0, g_Patches.Size() * sizeof( Vector ) ); - addlight.SetSize( g_Patches.Size() ); - memset( addlight.Base(), 0, g_Patches.Size() * sizeof( bumplights_t ) ); - - MakeAllScales (); - - // spread light around - BounceLight (); - } - - // - // displacement surface luxel accumulation (make threaded!!!) - // - StaticDispMgr()->StartTimer( "Build Patch/Sample Hash Table(s)....." ); - StaticDispMgr()->InsertSamplesDataIntoHashTable(); - StaticDispMgr()->InsertPatchSampleDataIntoHashTable(); - StaticDispMgr()->EndTimer(); - - // blend bounced light into direct light and save - VMPI_SetCurrentStage( "FinalLightFace" ); - if ( !g_bUseMPI || g_bMPIMaster ) - RunThreadsOnIndividual (numfaces, true, FinalLightFace); - - // Distribute the lighting data to workers. - VMPI_DistributeLightData(); - - Msg("FinalLightFace Done\n"); fflush(stdout); - } - - return true; -} - -// declare the sample file pointer -- the whole debug print system should -// be reworked at some point!! -FileHandle_t pFileSamples[4][4]; - -void LoadPhysicsDLL( void ) -{ - PhysicsDLLPath( "VPHYSICS.DLL" ); -} - - -void InitDumpPatchesFiles() -{ - for( int iStyle = 0; iStyle < 4; ++iStyle ) - { - for ( int iBump = 0; iBump < 4; ++iBump ) - { - char szFilename[MAX_PATH]; - sprintf( szFilename, "samples_style%d_bump%d.txt", iStyle, iBump ); - pFileSamples[iStyle][iBump] = g_pFileSystem->Open( szFilename, "w" ); - if( !pFileSamples[iStyle][iBump] ) - { - Error( "Can't open %s for -dump.\n", szFilename ); - } - } - } -} - - -void VRAD_LoadBSP( char const *pFilename ) -{ - ThreadSetDefault (); - - g_flStartTime = Plat_FloatTime(); - - if( g_bLowPriority ) - { - SetLowPriority(); - } - - strcpy( level_name, source ); - - // This must come after InitFileSystem because the file system pointer might change. - if ( g_bDumpPatches ) - InitDumpPatchesFiles(); - - // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames, - // so we prepend qdir here. - strcpy( source, ExpandPath( source ) ); - - if ( !g_bUseMPI ) - { - // Setup the logfile. - char logFile[512]; - _snprintf( logFile, sizeof(logFile), "%s.log", source ); - SetSpewFunctionLogFile( logFile ); - } - - LoadPhysicsDLL(); - - // Set the required global lights filename and try looking in qproject - strcpy( global_lights, "lights.rad" ); - if ( !g_pFileSystem->FileExists( global_lights ) ) - { - // Otherwise, try looking in the BIN directory from which we were run from - Msg( "Could not find lights.rad in %s.\nTrying VRAD BIN directory instead...\n", - global_lights ); - GetModuleFileName( NULL, global_lights, sizeof( global_lights ) ); - Q_ExtractFilePath( global_lights, global_lights, sizeof( global_lights ) ); - strcat( global_lights, "lights.rad" ); - } - - // Set the optional level specific lights filename - strcpy( level_lights, source ); - - Q_DefaultExtension( level_lights, ".rad", sizeof( level_lights ) ); - if ( !g_pFileSystem->FileExists( level_lights ) ) - *level_lights = 0; - - ReadLightFile(global_lights); // Required - if ( *designer_lights ) ReadLightFile(designer_lights); // Command-line - if ( *level_lights ) ReadLightFile(level_lights); // Optional & implied - - strcpy(incrementfile, source); - Q_DefaultExtension(incrementfile, ".r0", sizeof(incrementfile)); - Q_DefaultExtension(source, ".bsp", sizeof( source )); - - GetPlatformMapPath( source, platformPath, 0, MAX_PATH ); - - Msg( "Loading %s\n", platformPath ); - VMPI_SetCurrentStage( "LoadBSPFile" ); - LoadBSPFile (platformPath); - - // now, set whether or not static prop lighting is present - if (g_bStaticPropLighting) - g_LevelFlags |= g_bHDR? LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR : LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR; - else - { - g_LevelFlags &= ~( LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR | LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR ); - } - - // now, we need to set our face ptr depending upon hdr, and if hdr, init it - if (g_bHDR) - { - g_pFaces = dfaces_hdr; - if (numfaces_hdr==0) - { - numfaces_hdr = numfaces; - memcpy( dfaces_hdr, dfaces, numfaces*sizeof(dfaces[0]) ); - } - } - else - { - g_pFaces = dfaces; - } - - - ParseEntities (); - ExtractBrushEntityShadowCasters(); - - StaticPropMgr()->Init(); - StaticDispMgr()->Init(); - - if (!visdatasize) - { - Msg("No vis information, direct lighting only.\n"); - numbounce = 0; - ambient[0] = ambient[1] = ambient[2] = 0.1f; - dvis->numclusters = CountClusters(); - } - - // - // patches and referencing data (ensure capacity) - // - // TODO: change the maxes to the amount from the bsp!! - // -// g_Patches.EnsureCapacity( MAX_PATCHES ); - - g_FacePatches.SetSize( MAX_MAP_FACES ); - faceParents.SetSize( MAX_MAP_FACES ); - clusterChildren.SetSize( MAX_MAP_CLUSTERS ); - - int ndx; - for ( ndx = 0; ndx < MAX_MAP_FACES; ndx++ ) - { - g_FacePatches[ndx] = g_FacePatches.InvalidIndex(); - faceParents[ndx] = faceParents.InvalidIndex(); - } - - for ( ndx = 0; ndx < MAX_MAP_CLUSTERS; ndx++ ) - { - clusterChildren[ndx] = clusterChildren.InvalidIndex(); - } - - // Setup ray tracer - AddBrushesForRayTrace(); - StaticDispMgr()->AddPolysForRayTrace(); - StaticPropMgr()->AddPolysForRayTrace(); - - // Dump raytracer for glview - if ( g_bDumpRtEnv ) - WriteRTEnv("trace.txt"); - - // Build acceleration structure - printf ( "Setting up ray-trace acceleration structure... "); - float start = Plat_FloatTime(); - g_RtEnv.SetupAccelerationStructure(); - float end = Plat_FloatTime(); - printf ( "Done (%.2f seconds)\n", end-start ); - -#if 0 // To test only k-d build - exit(0); -#endif - - RadWorld_Start(); - - // Setup incremental lighting. - if( g_pIncremental ) - { - if( !g_pIncremental->Init( source, incrementfile ) ) - { - Error( "Unable to load incremental lighting file in %s.\n", incrementfile ); - return; - } - } -} - - -void VRAD_ComputeOtherLighting() -{ - // Compute lighting for the bsp file - if ( !g_bNoDetailLighting ) - { - ComputeDetailPropLighting( THREADINDEX_MAIN ); - } - - ComputePerLeafAmbientLighting(); - - // bake the static props high quality vertex lighting into the bsp - if ( !do_fast && g_bStaticPropLighting ) - { - StaticPropMgr()->ComputeLighting( THREADINDEX_MAIN ); - } -} - -extern void CloseDispLuxels(); - -void VRAD_Finish() -{ - Msg( "Ready to Finish\n" ); - fflush( stdout ); - - if ( verbose ) - { - PrintBSPFileSizes(); - } - - Msg( "Writing %s\n", platformPath ); - VMPI_SetCurrentStage( "WriteBSPFile" ); - WriteBSPFile(platformPath); - - if ( g_bDumpPatches ) - { - for ( int iStyle = 0; iStyle < 4; ++iStyle ) - { - for ( int iBump = 0; iBump < 4; ++iBump ) - { - g_pFileSystem->Close( pFileSamples[iStyle][iBump] ); - } - } - } - - CloseDispLuxels(); - - StaticPropMgr()->Shutdown(); - - double end = Plat_FloatTime(); - - char str[512]; - GetHourMinuteSecondsString( (int)( end - g_flStartTime ), str, sizeof( str ) ); - Msg( "%s elapsed\n", str ); - - ReleasePakFileLumps(); -} - - -// Run startup code like initialize mathlib (called from main() and from the -// WorldCraft interface into vrad). -void VRAD_Init() -{ - MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false ); - InstallAllocationFunctions(); - InstallSpewFunction(); -} - - -int ParseCommandLine( int argc, char **argv, bool *onlydetail ) -{ - *onlydetail = false; - - // default to LDR - SetHDRMode( false ); - int i; - for( i=1 ; i 1.0) - luxeldensity = 1.0 / luxeldensity; - } - else - { - Warning("Error: expected a value after '-luxeldensity'\n" ); - return 1; - } - } - else if( !Q_stricmp( argv[i], "-low" ) ) - { - g_bLowPriority = true; - } - else if( !Q_stricmp( argv[i], "-loghash" ) ) - { - g_bLogHashData = true; - } - else if( !Q_stricmp( argv[i], "-onlydetail" ) ) - { - *onlydetail = true; - } - else if (!Q_stricmp(argv[i],"-softsun")) - { - if ( ++i < argc ) - { - g_SunAngularExtent=atof(argv[i]); - g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent); - printf("sun extent=%f\n",g_SunAngularExtent); - } - else - { - Warning("Error: expected an angular extent value (0..180) '-softsun'\n" ); - return 1; - } - } - else if ( !Q_stricmp( argv[i], "-maxdispsamplesize" ) ) - { - if ( ++i < argc ) - { - g_flMaxDispSampleSize = ( float )atof( argv[i] ); - } - else - { - Warning( "Error: expected a sample size after '-maxdispsamplesize'\n" ); - return 1; - } - } - else if ( stricmp( argv[i], "-StopOnExit" ) == 0 ) - { - g_bStopOnExit = true; - } - else if ( stricmp( argv[i], "-steam" ) == 0 ) - { - } - else if ( stricmp( argv[i], "-allowdebug" ) == 0 ) - { - // Don't need to do anything, just don't error out. - } - else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) ) - { - } - else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) ) - { - ++i; - } - else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) ) - { - EnableFullMinidumps( true ); - } - else if ( !Q_stricmp( argv[i], "-hdr" ) ) - { - SetHDRMode( true ); - } - else if ( !Q_stricmp( argv[i], "-ldr" ) ) - { - SetHDRMode( false ); - } - else if (!Q_stricmp(argv[i],"-maxchop")) - { - if ( ++i < argc ) - { - maxchop = (float)atof (argv[i]); - if ( maxchop < 1 ) - { - Warning("Error: expected positive value after '-maxchop'\n" ); - return 1; - } - } - else - { - Warning("Error: expected a value after '-maxchop'\n" ); - return 1; - } - } - else if (!Q_stricmp(argv[i],"-chop")) - { - if ( ++i < argc ) - { - minchop = (float)atof (argv[i]); - if ( minchop < 1 ) - { - Warning("Error: expected positive value after '-chop'\n" ); - return 1; - } - minchop = min( minchop, maxchop ); - } - else - { - Warning("Error: expected a value after '-chop'\n" ); - return 1; - } - } - else if ( !Q_stricmp( argv[i], "-dispchop" ) ) - { - if ( ++i < argc ) - { - dispchop = ( float )atof( argv[i] ); - if ( dispchop < 1.0f ) - { - Warning( "Error: expected positive value after '-dipschop'\n" ); - return 1; - } - } - else - { - Warning( "Error: expected a value after '-dispchop'\n" ); - return 1; - } - } - else if ( !Q_stricmp( argv[i], "-disppatchradius" ) ) - { - if ( ++i < argc ) - { - g_MaxDispPatchRadius = ( float )atof( argv[i] ); - if ( g_MaxDispPatchRadius < 10.0f ) - { - Warning( "Error: g_MaxDispPatchRadius < 10.0\n" ); - return 1; - } - } - else - { - Warning( "Error: expected a value after '-disppatchradius'\n" ); - return 1; - } - } - -#if ALLOWDEBUGOPTIONS - else if (!Q_stricmp(argv[i],"-scale")) - { - if ( ++i < argc ) - { - lightscale = (float)atof (argv[i]); - } - else - { - Warning("Error: expected a value after '-scale'\n" ); - return 1; - } - } - else if (!Q_stricmp(argv[i],"-ambient")) - { - if ( i+3 < argc ) - { - ambient[0] = (float)atof (argv[++i]) * 128; - ambient[1] = (float)atof (argv[++i]) * 128; - ambient[2] = (float)atof (argv[++i]) * 128; - } - else - { - Warning("Error: expected three color values after '-ambient'\n" ); - return 1; - } - } - else if (!Q_stricmp(argv[i],"-dlight")) - { - if ( ++i < argc ) - { - dlight_threshold = (float)atof (argv[i]); - } - else - { - Warning("Error: expected a value after '-dlight'\n" ); - return 1; - } - } - else if (!Q_stricmp(argv[i],"-sky")) - { - if ( ++i < argc ) - { - indirect_sun = (float)atof (argv[i]); - } - else - { - Warning("Error: expected a value after '-sky'\n" ); - return 1; - } - } - else if (!Q_stricmp(argv[i],"-notexscale")) - { - texscale = false; - } - else if (!Q_stricmp(argv[i],"-coring")) - { - if ( ++i < argc ) - { - coring = (float)atof( argv[i] ); - } - else - { - Warning("Error: expected a light threshold after '-coring'\n" ); - return 1; - } - } -#endif - // NOTE: the -mpi checks must come last here because they allow the previous argument - // to be -mpi as well. If it game before something else like -game, then if the previous - // argument was -mpi and the current argument was something valid like -game, it would skip it. - else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) ) - { - if ( stricmp( argv[i], "-mpi" ) == 0 ) - g_bUseMPI = true; - - // Any other args that start with -mpi are ok too. - if ( i == argc - 1 && V_stricmp( argv[i], "-mpi_ListParams" ) != 0 ) - break; - } - else - { - break; - } - } - - return i; -} - - -void PrintCommandLine( int argc, char **argv ) -{ - Warning( "Command line: " ); - for ( int z=0; z < argc; z++ ) - { - Warning( "\"%s\" ", argv[z] ); - } - Warning( "\n\n" ); -} - - -void PrintUsage( int argc, char **argv ) -{ - PrintCommandLine( argc, argv ); - - Warning( - "usage : vrad [options...] bspfile\n" - "example: vrad c:\\hl2\\hl2\\maps\\test\n" - "\n" - "Common options:\n" - "\n" - " -v (or -verbose): Turn on verbose output (also shows more command\n" - " -bounce # : Set max number of bounces (default: 100).\n" - " -fast : Quick and dirty lighting.\n" - " -fastambient : Per-leaf ambient sampling is lower quality to save compute time.\n" - " -final : High quality processing. equivalent to -extrasky 16.\n" - " -extrasky n : trace N times as many rays for indirect light and sky ambient.\n" - " -low : Run as an idle-priority process.\n" - " -mpi : Use VMPI to distribute computations.\n" - " -rederror : Show errors in red.\n" - "\n" - " -vproject : Override the VPROJECT environment variable.\n" - " -game : Same as -vproject.\n" - "\n" - "Other options:\n" - " -novconfig : Don't bring up graphical UI on vproject errors.\n" - " -dump : Write debugging .txt files.\n" - " -dumpnormals : Write normals to debug files.\n" - " -dumptrace : Write ray-tracing environment to debug files.\n" - " -threads : Control the number of threads vbsp uses (defaults to the #\n" - " or processors on your machine).\n" - " -lights : Load a lights file in addition to lights.rad and the\n" - " level lights file.\n" - " -noextra : Disable supersampling.\n" - " -debugextra : Places debugging data in lightmaps to visualize\n" - " supersampling.\n" - " -smooth # : Set the threshold for smoothing groups, in degrees\n" - " (default 45).\n" - " -dlightmap : Force direct lighting into different lightmap than\n" - " radiosity.\n" - " -stoponexit : Wait for a keypress on exit.\n" - " -mpi_pw : Use a password to choose a specific set of VMPI workers.\n" - " -nodetaillight : Don't light detail props.\n" - " -centersamples : Move sample centers.\n" - " -luxeldensity # : Rescale all luxels by the specified amount (default: 1.0).\n" - " The number specified must be less than 1.0 or it will be\n" - " ignored.\n" - " -loghash : Log the sample hash table to samplehash.txt.\n" - " -onlydetail : Only light detail props and per-leaf lighting.\n" - " -maxdispsamplesize #: Set max displacement sample size (default: 512).\n" - " -softsun : Treat the sun as an area light source of size degrees." - " Produces soft shadows.\n" - " Recommended values are between 0 and 5. Default is 0.\n" - " -FullMinidumps : Write large minidumps on crash.\n" - " -chop : Smallest number of luxel widths for a bounce patch, used on edges\n" - " -maxchop : Coarsest allowed number of luxel widths for a patch, used in face interiors\n" - "\n" - " -LargeDispSampleRadius: This can be used if there are splotches of bounced light\n" - " on terrain. The compile will take longer, but it will gather\n" - " light across a wider area.\n" - " -StaticPropLighting : generate backed static prop vertex lighting\n" - " -StaticPropPolys : Perform shadow tests of static props at polygon precision\n" - " -OnlyStaticProps : Only perform direct static prop lighting (vrad debug option)\n" - " -StaticPropNormals : when lighting static props, just show their normal vector\n" - " -textureshadows : Allows texture alpha channels to block light - rays intersecting alpha surfaces will sample the texture\n" - " -noskyboxrecurse : Turn off recursion into 3d skybox (skybox shadows on world)\n" - " -nossprops : Globally disable self-shadowing on static props\n" - "\n" -#if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users. - ); -#else - " -mpi_ListParams : Show a list of VMPI parameters.\n" - "\n" - ); - - // Show VMPI parameters? - for ( int i=1; i < argc; i++ ) - { - if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 ) - { - Warning( "VMPI-specific options:\n\n" ); - - bool bIsSDKMode = VMPI_IsSDKMode(); - for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ ) - { - if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode ) - continue; - - Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) ); - Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) ); - Warning( "\n\n" ); - } - break; - } - } -#endif -} - - -int RunVRAD( int argc, char **argv ) -{ -#if defined(_MSC_VER) && ( _MSC_VER >= 1310 ) - Msg("Valve Software - vrad.exe SSE (" __DATE__ ")\n" ); -#else - Msg("Valve Software - vrad.exe (" __DATE__ ")\n" ); -#endif - - Msg("\n Valve Radiosity Simulator \n"); - - verbose = true; // Originally FALSE - - bool onlydetail; - int i = ParseCommandLine( argc, argv, &onlydetail ); - if (i != argc - 1) - { - PrintUsage( argc, argv ); - DeleteCmdLine( argc, argv ); - CmdLib_Exit( 1 ); - } - - VRAD_LoadBSP( argv[i] ); - - if ( (! onlydetail) && (! g_bOnlyStaticProps ) ) - { - RadWorld_Go(); - } - - VRAD_ComputeOtherLighting(); - - VRAD_Finish(); - - VMPI_SetCurrentStage( "master done" ); - - DeleteCmdLine( argc, argv ); - CmdLib_Cleanup(); - return 0; -} - - -int VRAD_Main(int argc, char **argv) -{ - g_pFileSystem = NULL; // Safeguard against using it before it's properly initialized. - - VRAD_Init(); - - // This must come first. - VRAD_SetupMPI( argc, argv ); - - // Initialize the filesystem, so additional commandline options can be loaded - Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) ); - CmdLib_InitFileSystem( argv[ argc - 1 ] ); - Q_FileBase( source, source, sizeof( source ) ); - -#if !defined( _DEBUG ) - if ( g_bUseMPI && !g_bMPIMaster ) - { - SetupToolsMinidumpHandler( VMPI_ExceptionFilter ); - } - else -#endif - { - LoadCmdLineFromFile( argc, argv, source, "vrad" ); // Don't do this if we're a VMPI worker.. - SetupDefaultToolsMinidumpHandler(); - } - - return RunVRAD( argc, argv ); -} - - - - - +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +// vrad.c + +#include "vrad.h" +#include "physdll.h" +#include "lightmap.h" +#include "tier1/strtools.h" +#include "vmpi.h" +#include "macro_texture.h" +#include "vmpi_tools_shared.h" +#include "leaf_ambient_lighting.h" +#include "tools_minidump.h" +#include "loadcmdline.h" +#include "byteswap.h" + +#define ALLOWDEBUGOPTIONS (0 || _DEBUG) + +static FileHandle_t pFpTrans = NULL; + +/* + +NOTES +----- + +every surface must be divided into at least two patches each axis + +*/ + +CUtlVector g_Patches; +CUtlVector g_FacePatches; // constains all patches, children first +CUtlVector faceParents; // contains only root patches, use next parent to iterate +CUtlVector clusterChildren; +CUtlVector emitlight; +CUtlVector addlight; + +int num_sky_cameras; +sky_camera_t sky_cameras[MAX_MAP_AREAS]; +int area_sky_cameras[MAX_MAP_AREAS]; + +entity_t *face_entity[MAX_MAP_FACES]; +Vector face_offset[MAX_MAP_FACES]; // for rotating bmodels +int fakeplanes; + +unsigned numbounce = 100; // 25; /* Originally this was 8 */ + +float maxchop = 4; // coarsest allowed number of luxel widths for a patch +float minchop = 4; // "-chop" tightest number of luxel widths for a patch, used on edges +float dispchop = 8.0f; // number of luxel widths for a patch +float g_MaxDispPatchRadius = 1500.0f; // Maximum radius allowed for displacement patches +qboolean g_bDumpPatches; +bool bDumpNormals = false; +bool g_bDumpRtEnv = false; +bool bRed2Black = true; +bool g_bFastAmbient = false; +bool g_bNoSkyRecurse = false; + +int junk; + +Vector ambient( 0, 0, 0 ); + +float lightscale = 1.0; +float dlight_threshold = 0.1; // was DIRECT_LIGHT constant + +char source[MAX_PATH] = ""; +char platformPath[MAX_PATH] = ""; + +char level_name[MAX_PATH] = ""; // map filename, without extension or path info + +char global_lights[MAX_PATH] = ""; +char designer_lights[MAX_PATH] = ""; +char level_lights[MAX_PATH] = ""; + +char vismatfile[_MAX_PATH] = ""; +char incrementfile[_MAX_PATH] = ""; + +IIncremental *g_pIncremental = 0; +bool g_bInterrupt = false; // Wsed with background lighting in WC. Tells VRAD + // to stop lighting. +float g_SunAngularExtent=0.0; + +float g_flSkySampleScale = 1.0; + +bool g_bLargeDispSampleRadius = false; + +bool g_bOnlyStaticProps = false; +bool g_bShowStaticPropNormals = false; + + +float gamma_value = 0.5; +float indirect_sun = 1.0; +float reflectivityScale = 1.0; +qboolean do_extra = true; +bool debug_extra = false; +qboolean do_fast = false; +qboolean do_centersamples = false; +int extrapasses = 4; +float smoothing_threshold = 0.7071067; // cos(45.0*(M_PI/180)) +// Cosine of smoothing angle(in radians) +float coring = 1.0; // Light threshold to force to blackness(minimizes lightmaps) +qboolean texscale = true; +int dlight_map = 0; // Setting to 1 forces direct lighting into different lightmap than radiosity + +float luxeldensity = 1.0; +unsigned num_degenerate_faces; + +qboolean g_bLowPriority = false; +qboolean g_bLogHashData = false; +bool g_bNoDetailLighting = false; +double g_flStartTime; +bool g_bStaticPropLighting = false; +bool g_bStaticPropPolys = false; +bool g_bTextureShadows = false; +bool g_bDisablePropSelfShadowing = false; + + +CUtlVector g_FacesVisibleToLights; + +RayTracingEnvironment g_RtEnv; + +dface_t *g_pFaces=0; + +// this is a list of material names used on static props which shouldn't cast shadows. a +// sequential search is used since we allow substring matches. its not time critical, and this +// functionality is a stopgap until vrad starts reading .vmt files. +CUtlVector g_NonShadowCastingMaterialStrings; +/* +=================================================================== + +MISC + +=================================================================== +*/ + + +int leafparents[MAX_MAP_LEAFS]; +int nodeparents[MAX_MAP_NODES]; + +void MakeParents (int nodenum, int parent) +{ + int i, j; + dnode_t *node; + + nodeparents[nodenum] = parent; + node = &dnodes[nodenum]; + + for (i=0 ; i<2 ; i++) + { + j = node->children[i]; + if (j < 0) + leafparents[-j - 1] = nodenum; + else + MakeParents (j, nodenum); + } +} + + +/* +=================================================================== + + TEXTURE LIGHT VALUES + +=================================================================== +*/ + +typedef struct +{ + char name[256]; + Vector value; + char *filename; +} texlight_t; + +#define MAX_TEXLIGHTS 128 + +texlight_t texlights[MAX_TEXLIGHTS]; +int num_texlights; + +/* +============ +ReadLightFile +============ +*/ +void ReadLightFile (char *filename) +{ + char buf[1024]; + int file_texlights = 0; + + FileHandle_t f = g_pFileSystem->Open( filename, "r" ); + if (!f) + { + Warning("Warning: Couldn't open texlight file %s.\n", filename); + return; + } + + Msg("[Reading texlights from '%s']\n", filename); + while ( CmdLib_FGets( buf, sizeof( buf ), f ) ) + { + // check ldr/hdr + char * scan = buf; + if ( !strnicmp( "hdr:", scan, 4) ) + { + scan += 4; + if ( ! g_bHDR ) + { + continue; + } + } + if ( !strnicmp( "ldr:", scan, 4) ) + { + scan += 4; + if ( g_bHDR ) + { + continue; + } + } + + scan += strspn( scan, " \t" ); + char NoShadName[1024]; + if ( sscanf(scan,"noshadow %s",NoShadName)==1) + { + char * dot = strchr( NoShadName, '.' ); + if ( dot ) // if they specify .vmt, kill it + * dot = 0; + //printf("add %s as a non shadow casting material\n",NoShadName); + g_NonShadowCastingMaterialStrings.AddToTail( strdup( NoShadName )); + } + else if ( sscanf( scan, "forcetextureshadow %s", NoShadName ) == 1 ) + { + //printf("add %s as a non shadow casting material\n",NoShadName); + ForceTextureShadowsOnModel( NoShadName ); + } + else + { + char szTexlight[256]; + Vector value; + if ( num_texlights == MAX_TEXLIGHTS ) + Error ("Too many texlights, max = %d", MAX_TEXLIGHTS); + + int argCnt = sscanf (scan, "%s ",szTexlight ); + + if( argCnt != 1 ) + { + if ( strlen( scan ) > 4 ) + Msg( "ignoring bad texlight '%s' in %s", scan, filename ); + continue; + } + + LightForString( scan + strlen( szTexlight ) + 1, value ); + + int j = 0; + for( j; j < num_texlights; j ++ ) + { + if ( strcmp( texlights[j].name, szTexlight ) == 0 ) + { + if ( strcmp( texlights[j].filename, filename ) == 0 ) + { + Msg( "ERROR\a: Duplication of '%s' in file '%s'!\n", + texlights[j].name, texlights[j].filename ); + } + else if ( texlights[j].value[0] != value[0] + || texlights[j].value[1] != value[1] + || texlights[j].value[2] != value[2] ) + { + Warning( "Warning: Overriding '%s' from '%s' with '%s'!\n", + texlights[j].name, texlights[j].filename, filename ); + } + else + { + Warning( "Warning: Redundant '%s' def in '%s' AND '%s'!\n", + texlights[j].name, texlights[j].filename, filename ); + } + break; + } + } + strcpy( texlights[j].name, szTexlight ); + VectorCopy( value, texlights[j].value ); + texlights[j].filename = filename; + file_texlights ++; + + num_texlights = max( num_texlights, j + 1 ); + } + } + qprintf ( "[%i texlights parsed from '%s']\n\n", file_texlights, filename); + g_pFileSystem->Close( f ); +} + + +/* +============ +LightForTexture +============ +*/ +void LightForTexture( const char *name, Vector& result ) +{ + int i; + + result[ 0 ] = result[ 1 ] = result[ 2 ] = 0; + + char baseFilename[ MAX_PATH ]; + + if ( Q_strncmp( "maps/", name, 5 ) == 0 ) + { + // this might be a patch texture for cubemaps. try to parse out the original filename. + if ( Q_strncmp( level_name, name + 5, Q_strlen( level_name ) ) == 0 ) + { + const char *base = name + 5 + Q_strlen( level_name ); + if ( *base == '/' ) + { + ++base; // step past the path separator + + // now we've gotten rid of the 'maps/level_name/' part, so we're left with + // 'originalName_%d_%d_%d'. + strcpy( baseFilename, base ); + bool foundSeparators = true; + for ( int i=0; i<3; ++i ) + { + char *underscore = Q_strrchr( baseFilename, '_' ); + if ( underscore && *underscore ) + { + *underscore = '\0'; + } + else + { + foundSeparators = false; + } + } + + if ( foundSeparators ) + { + name = baseFilename; + } + } + } + } + + for (i=0 ; inumedges); + w->numpoints = f->numedges; + + for (i=0 ; inumedges ; i++) + { + se = dsurfedges[f->firstedge + i]; + if (se < 0) + v = dedges[-se].v[1]; + else + v = dedges[se].v[0]; + + dv = &dvertexes[v]; + VectorAdd (dv->point, origin, w->p[i]); + } + + RemoveColinearPoints (w); + + return w; +} + +/* +============= +BaseLightForFace +============= +*/ +void BaseLightForFace( dface_t *f, Vector& light, float *parea, Vector& reflectivity ) +{ + texinfo_t *tx; + dtexdata_t *texdata; + + // + // check for light emited by texture + // + tx = &texinfo[f->texinfo]; + texdata = &dtexdata[tx->texdata]; + + LightForTexture (TexDataStringTable_GetString( texdata->nameStringTableID ), light); + + + *parea = texdata->height * texdata->width; + + VectorScale( texdata->reflectivity, reflectivityScale, reflectivity ); + + // always keep this less than 1 or the solution will not converge + for ( int i = 0; i < 3; i++ ) + { + if ( reflectivity[i] > 0.99 ) + reflectivity[i] = 0.99; + } +} + +qboolean IsSky (dface_t *f) +{ + texinfo_t *tx; + + tx = &texinfo[f->texinfo]; + if (tx->flags & SURF_SKY) + return true; + return false; +} + +#ifdef STATIC_FOG +/*============= +IsFog +=============*/ +qboolean IsFog( dface_t *f ) +{ + texinfo_t *tx; + + tx = &texinfo[f->texinfo]; + + // % denotes a fog texture + if( tx->texture[0] == '%' ) + return true; + + return false; +} +#endif + + +void ProcessSkyCameras() +{ + int i; + num_sky_cameras = 0; + for (i = 0; i < numareas; ++i) + { + area_sky_cameras[i] = -1; + } + + for (i = 0; i < num_entities; ++i) + { + entity_t *e = &entities[i]; + const char *name = ValueForKey (e, "classname"); + if (stricmp (name, "sky_camera")) + continue; + + Vector origin; + GetVectorForKey( e, "origin", origin ); + int node = PointLeafnum( origin ); + int area = -1; + if (node >= 0 && node < numleafs) area = dleafs[node].area; + float scale = FloatForKey( e, "scale" ); + + if (scale > 0.0f) + { + sky_cameras[num_sky_cameras].origin = origin; + sky_cameras[num_sky_cameras].sky_to_world = scale; + sky_cameras[num_sky_cameras].world_to_sky = 1.0f / scale; + sky_cameras[num_sky_cameras].area = area; + + if (area >= 0 && area < numareas) + { + area_sky_cameras[area] = num_sky_cameras; + } + + ++num_sky_cameras; + } + } + +} + + +/* +============= +MakePatchForFace +============= +*/ +float totalarea; +void MakePatchForFace (int fn, winding_t *w) +{ + dface_t *f = g_pFaces + fn; + float area; + CPatch *patch; + Vector centroid(0,0,0); + int i, j; + texinfo_t *tx; + + // get texture info + tx = &texinfo[f->texinfo]; + + // No patches at all for fog! +#ifdef STATIC_FOG + if ( IsFog( f ) ) + return; +#endif + + // the sky needs patches or the form factors don't work out correctly + // if (IsSky( f ) ) + // return; + + area = WindingArea (w); + if (area <= 0) + { + num_degenerate_faces++; + // Msg("degenerate face\n"); + return; + } + + totalarea += area; + + // get a patch + int ndxPatch = g_Patches.AddToTail(); + patch = &g_Patches[ndxPatch]; + memset( patch, 0, sizeof( CPatch ) ); + patch->ndxNext = g_Patches.InvalidIndex(); + patch->ndxNextParent = g_Patches.InvalidIndex(); + patch->ndxNextClusterChild = g_Patches.InvalidIndex(); + patch->child1 = g_Patches.InvalidIndex(); + patch->child2 = g_Patches.InvalidIndex(); + patch->parent = g_Patches.InvalidIndex(); + patch->needsBumpmap = tx->flags & SURF_BUMPLIGHT ? true : false; + + // link and save patch data + patch->ndxNext = g_FacePatches.Element( fn ); + g_FacePatches[fn] = ndxPatch; +// patch->next = face_g_Patches[fn]; +// face_g_Patches[fn] = patch; + + // compute a separate scale for chop - since the patch "scale" is the texture scale + // we want textures with higher resolution lighting to be chopped up more + float chopscale[2]; + chopscale[0] = chopscale[1] = 16.0f; + if ( texscale ) + { + // Compute the texture "scale" in s,t + for( i=0; i<2; i++ ) + { + patch->scale[i] = 0.0f; + chopscale[i] = 0.0f; + for( j=0; j<3; j++ ) + { + patch->scale[i] += + tx->textureVecsTexelsPerWorldUnits[i][j] * + tx->textureVecsTexelsPerWorldUnits[i][j]; + chopscale[i] += + tx->lightmapVecsLuxelsPerWorldUnits[i][j] * + tx->lightmapVecsLuxelsPerWorldUnits[i][j]; + } + patch->scale[i] = sqrt( patch->scale[i] ); + chopscale[i] = sqrt( chopscale[i] ); + } + } + else + { + patch->scale[0] = patch->scale[1] = 1.0f; + } + + patch->area = area; + + patch->sky = IsSky( f ); + + // chop scaled up lightmaps coarser + patch->luxscale = ((chopscale[0]+chopscale[1])/2); + patch->chop = maxchop; + + +#ifdef STATIC_FOG + patch->fog = FALSE; +#endif + + patch->winding = w; + + patch->plane = &dplanes[f->planenum]; + + // make a new plane to adjust for origined bmodels + if (face_offset[fn][0] || face_offset[fn][1] || face_offset[fn][2] ) + { + dplane_t *pl; + + // 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; + } + + patch->faceNumber = fn; + WindingCenter (w, patch->origin); + + // Save "center" for generating the face normals later. + VectorSubtract( patch->origin, face_offset[fn], face_centroids[fn] ); + + VectorCopy( patch->plane->normal, patch->normal ); + + WindingBounds (w, patch->face_mins, patch->face_maxs); + VectorCopy( patch->face_mins, patch->mins ); + VectorCopy( patch->face_maxs, patch->maxs ); + + BaseLightForFace( f, patch->baselight, &patch->basearea, patch->reflectivity ); + + // Chop all texlights very fine. + if ( !VectorCompare( patch->baselight, vec3_origin ) ) + { + // patch->chop = do_extra ? maxchop / 2 : maxchop; + tx->flags |= SURF_LIGHT; + } + + // get rid of do extra functionality on displacement surfaces + if( ValidDispFace( f ) ) + { + patch->chop = maxchop; + } + + // FIXME: If we wanted to add a dependency from vrad to the material system, + // we could do this. It would add a bunch of file accesses, though: + + /* + // Check for a material var which would override the patch chop + bool bFound; + const char *pMaterialName = TexDataStringTable_GetString( dtexdata[ tx->texdata ].nameStringTableID ); + MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, &bFound, false ); + if ( bFound ) + { + const char *pChopValue = GetMaterialVar( hMaterial, "%chop" ); + if ( pChopValue ) + { + float flChopValue; + if ( sscanf( pChopValue, "%f", &flChopValue ) > 0 ) + { + patch->chop = flChopValue; + } + } + } + */ +} + + +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 ; inumfaces ; j++) + { + fn = mod->firstface + j; + face_entity[fn] = ent; + VectorCopy (origin, face_offset[fn]); + f = &g_pFaces[fn]; + if( f->dispinfo == -1 ) + { + w = WindingFromFace (f, origin ); + MakePatchForFace( fn, w ); + } + } + } + + if (num_degenerate_faces > 0) + { + qprintf("%d degenerate faces\n", num_degenerate_faces ); + } + + qprintf ("%i square feet [%.2f square inches]\n", (int)(totalarea/144), totalarea ); + + // make the displacement surface patches + StaticDispMgr()->MakePatches(); +} + +/* +======================================================================= + +SUBDIVIDE + +======================================================================= +*/ + + +//----------------------------------------------------------------------------- +// Purpose: does this surface take/emit light +//----------------------------------------------------------------------------- +bool PreventSubdivision( CPatch *patch ) +{ + dface_t *f = g_pFaces + patch->faceNumber; + texinfo_t *tx = &texinfo[f->texinfo]; + + if (tx->flags & SURF_NOCHOP) + return true; + + if (tx->flags & SURF_NOLIGHT && !(tx->flags & SURF_LIGHT)) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: subdivide the "parent" patch +//----------------------------------------------------------------------------- +int CreateChildPatch( int nParentIndex, winding_t *pWinding, float flArea, const Vector &vecCenter ) +{ + int nChildIndex = g_Patches.AddToTail(); + + CPatch *child = &g_Patches[nChildIndex]; + CPatch *parent = &g_Patches[nParentIndex]; + + // copy all elements of parent patch to children + *child = *parent; + + // Set up links + child->ndxNext = g_Patches.InvalidIndex(); + child->ndxNextParent = g_Patches.InvalidIndex(); + child->ndxNextClusterChild = g_Patches.InvalidIndex(); + child->child1 = g_Patches.InvalidIndex(); + child->child2 = g_Patches.InvalidIndex(); + child->parent = nParentIndex; + child->m_IterationKey = 0; + + child->winding = pWinding; + child->area = flArea; + + VectorCopy( vecCenter, child->origin ); + if ( ValidDispFace( g_pFaces + child->faceNumber ) ) + { + // shouldn't get here anymore!! + Msg( "SubdividePatch: Error - Should not be here!\n" ); + StaticDispMgr()->GetDispSurfNormal( child->faceNumber, child->origin, child->normal, true ); + } + else + { + GetPhongNormal( child->faceNumber, child->origin, child->normal ); + } + + child->planeDist = child->plane->dist; + WindingBounds(child->winding, child->mins, child->maxs); + + if ( !VectorCompare( child->baselight, vec3_origin ) ) + { + // don't check edges on surf lights + return nChildIndex; + } + + // Subdivide patch towards minchop if on the edge of the face + Vector total; + VectorSubtract( child->maxs, child->mins, total ); + VectorScale( total, child->luxscale, total ); + if ( child->chop > minchop && (total[0] < child->chop) && (total[1] < child->chop) && (total[2] < child->chop) ) + { + for ( int i=0; i<3; ++i ) + { + if ( (child->face_maxs[i] == child->maxs[i] || child->face_mins[i] == child->mins[i] ) + && total[i] > minchop ) + { + child->chop = max( minchop, child->chop / 2 ); + break; + } + } + } + + return nChildIndex; +} + + +//----------------------------------------------------------------------------- +// Purpose: subdivide the "parent" patch +//----------------------------------------------------------------------------- +void SubdividePatch( int ndxPatch ) +{ + winding_t *w, *o1, *o2; + Vector total; + Vector split; + vec_t dist; + vec_t widest = -1; + int i, widest_axis = -1; + bool bSubdivide = false; + + // get the current patch + CPatch *patch = &g_Patches.Element( ndxPatch ); + if ( !patch ) + return; + + // never subdivide sky patches + if ( patch->sky ) + return; + + // get the patch winding + w = patch->winding; + + // subdivide along the widest axis + VectorSubtract (patch->maxs, patch->mins, total); + VectorScale( total, patch->luxscale, total ); + for (i=0 ; i<3 ; i++) + { + if ( total[i] > widest ) + { + widest_axis = i; + widest = total[i]; + } + + if ( (total[i] >= patch->chop) && (total[i] >= minchop) ) + { + bSubdivide = true; + } + } + + if ((!bSubdivide) && widest_axis != -1) + { + // make more square + if (total[widest_axis] > total[(widest_axis + 1) % 3] * 2 && total[widest_axis] > total[(widest_axis + 2) % 3] * 2) + { + if (patch->chop > minchop) + { + bSubdivide = true; + patch->chop = max( minchop, patch->chop / 2 ); + } + } + } + + if ( !bSubdivide ) + return; + + // split the winding + VectorCopy (vec3_origin, split); + split[widest_axis] = 1; + dist = (patch->mins[widest_axis] + patch->maxs[widest_axis])*0.5f; + ClipWindingEpsilon (w, split, dist, ON_EPSILON, &o1, &o2); + + // calculate the area of the patches to see if they are "significant" + Vector center1, center2; + float area1 = WindingAreaAndBalancePoint( o1, center1 ); + float area2 = WindingAreaAndBalancePoint( o2, center2 ); + + if( area1 == 0 || area2 == 0 ) + { + Msg( "zero area child patch\n" ); + return; + } + + // create new child patches + int ndxChild1Patch = CreateChildPatch( ndxPatch, o1, area1, center1 ); + int ndxChild2Patch = CreateChildPatch( ndxPatch, o2, area2, center2 ); + + // FIXME: This could go into CreateChildPatch if child1, child2 were stored in the patch as child[0], child[1] + patch = &g_Patches.Element( ndxPatch ); + patch->child1 = ndxChild1Patch; + patch->child2 = ndxChild2Patch; + + SubdividePatch( ndxChild1Patch ); + SubdividePatch( ndxChild2Patch ); +} + + +/* +============= +SubdividePatches +============= +*/ +void SubdividePatches (void) +{ + unsigned i, num; + + if (numbounce == 0) + return; + + unsigned int uiPatchCount = g_Patches.Size(); + qprintf ("%i patches before subdivision\n", uiPatchCount); + + for (i = 0; i < uiPatchCount; i++) + { + CPatch *pCur = &g_Patches.Element( i ); + pCur->planeDist = pCur->plane->dist; + + pCur->ndxNextParent = faceParents.Element( pCur->faceNumber ); + faceParents[pCur->faceNumber] = pCur - g_Patches.Base(); + } + + for (i=0 ; i< uiPatchCount; i++) + { + CPatch *patch = &g_Patches.Element( i ); + patch->parent = -1; + if ( PreventSubdivision(patch) ) + continue; + + if (!do_fast) + { + if( g_pFaces[patch->faceNumber].dispinfo == -1 ) + { + SubdividePatch( i ); + } + else + { + StaticDispMgr()->SubdividePatch( i ); + } + } + } + + // fixup next pointers + for (i = 0; i < (unsigned)numfaces; i++) + { + g_FacePatches[i] = g_FacePatches.InvalidIndex(); + } + + uiPatchCount = g_Patches.Size(); + for (i = 0; i < uiPatchCount; i++) + { + CPatch *pCur = &g_Patches.Element( i ); + pCur->ndxNext = g_FacePatches.Element( pCur->faceNumber ); + g_FacePatches[pCur->faceNumber] = pCur - g_Patches.Base(); + +#if 0 + CPatch *prev; + prev = face_g_Patches[g_Patches[i].faceNumber]; + g_Patches[i].next = prev; + face_g_Patches[g_Patches[i].faceNumber] = &g_Patches[i]; +#endif + } + + // Cache off the leaf number: + // We have to do this after subdivision because some patches span leaves. + // (only the faces for model #0 are split by it's BSP which is what governs the PVS, and the leaves we're interested in) + // Sub models (1-255) are only split for the BSP that their model forms. + // When those patches are subdivided their origins can end up in a different leaf. + // The engine will split (clip) those faces at run time to the world BSP because the models + // are dynamic and can be moved. In the software renderer, they must be split exactly in order + // to sort per polygon. + for ( i = 0; i < uiPatchCount; i++ ) + { + g_Patches[i].clusterNumber = ClusterFromPoint( g_Patches[i].origin ); + + // + // test for point in solid space (can happen with detail and displacement surfaces) + // + if( g_Patches[i].clusterNumber == -1 ) + { + for( int j = 0; j < g_Patches[i].winding->numpoints; j++ ) + { + int clusterNumber = ClusterFromPoint( g_Patches[i].winding->p[j] ); + if( clusterNumber != -1 ) + { + g_Patches[i].clusterNumber = clusterNumber; + break; + } + } + } + } + + // build the list of patches that need to be lit + for ( num = 0; num < uiPatchCount; num++ ) + { + // do them in reverse order + i = uiPatchCount - num - 1; + + // skip patches with children + CPatch *pCur = &g_Patches.Element( i ); + if( pCur->child1 == g_Patches.InvalidIndex() ) + { + if( pCur->clusterNumber != - 1 ) + { + pCur->ndxNextClusterChild = clusterChildren.Element( pCur->clusterNumber ); + clusterChildren[pCur->clusterNumber] = pCur - g_Patches.Base(); + } + } + +#if 0 + if (g_Patches[i].child1 == g_Patches.InvalidIndex() ) + { + if( g_Patches[i].clusterNumber != -1 ) + { + g_Patches[i].nextclusterchild = cluster_children[g_Patches[i].clusterNumber]; + cluster_children[g_Patches[i].clusterNumber] = &g_Patches[i]; + } + } +#endif + } + + qprintf ("%i patches after subdivision\n", uiPatchCount); +} + + +//===================================================================== + +/* +============= +MakeScales + + This is the primary time sink. + It can be run multi threaded. +============= +*/ +int total_transfer; +int max_transfer; + + +//----------------------------------------------------------------------------- +// Purpose: Computes the form factor from a polygon patch to a differential patch +// using formula 81 of Philip Dutre's Global Illumination Compendium, +// phil@graphics.cornell.edu, http://www.graphics.cornell.edu/~phil/GI/ +//----------------------------------------------------------------------------- +float FormFactorPolyToDiff ( CPatch *pPolygon, CPatch* pDifferential ) +{ + winding_t *pWinding = pPolygon->winding; + + float flFormFactor = 0.0f; + + for ( int iPoint = 0; iPoint < pWinding->numpoints; iPoint++ ) + { + int iNextPoint = ( iPoint < pWinding->numpoints - 1 ) ? iPoint + 1 : 0; + + Vector vGammaVector, vVector1, vVector2; + VectorSubtract( pWinding->p[ iPoint ], pDifferential->origin, vVector1 ); + VectorSubtract( pWinding->p[ iNextPoint ], pDifferential->origin, vVector2 ); + VectorNormalize( vVector1 ); + VectorNormalize( vVector2 ); + CrossProduct( vVector1, vVector2, vGammaVector ); + float flSinAlpha = VectorNormalize( vGammaVector ); + if (flSinAlpha < -1.0f || flSinAlpha > 1.0f) + return 0.0f; + vGammaVector *= asin( flSinAlpha ); + + flFormFactor += DotProduct( vGammaVector, pDifferential->normal ); + } + + flFormFactor *= ( 0.5f / pPolygon->area ); // divide by pi later, multiply by area later + + return flFormFactor; +} + + +//----------------------------------------------------------------------------- +// Purpose: Computes the form factor from a differential element to a differential +// element. This is okay when the distance between patches is 5 times +// greater than patch size. Lecture slides by Pat Hanrahan, +// http://graphics.stanford.edu/courses/cs348b-00/lectures/lecture17/radiosity.2.pdf +//----------------------------------------------------------------------------- +float FormFactorDiffToDiff ( CPatch *pDiff1, CPatch* pDiff2 ) +{ + Vector vDelta; + VectorSubtract( pDiff1->origin, pDiff2->origin, vDelta ); + float flLength = VectorNormalize( vDelta ); + + return -DotProduct( vDelta, pDiff1->normal ) * DotProduct( vDelta, pDiff2->normal ) / ( flLength * flLength ); +} + + + +void MakeTransfer( int ndxPatch1, int ndxPatch2, transfer_t *all_transfers ) +//void MakeTransfer (CPatch *patch, CPatch *patch2, transfer_t *all_transfers ) +{ + Vector delta; + vec_t scale; + float trans; + transfer_t *transfer; + + // + // get patches + // + if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() ) + return; + + CPatch *pPatch1 = &g_Patches.Element( ndxPatch1 ); + CPatch *pPatch2 = &g_Patches.Element( ndxPatch2 ); + + if (IsSky( &g_pFaces[ pPatch2->faceNumber ] ) ) + return; + + // overflow check! + if ( pPatch1->numtransfers >= MAX_PATCHES) + { + return; + } + + // hack for patch areas that area <= 0 (degenerate) + if ( pPatch2->area <= 0) + { + return; + } + + transfer = &all_transfers[pPatch1->numtransfers]; + + scale = FormFactorDiffToDiff( pPatch2, pPatch1 ); + + // patch normals may be > 90 due to smoothing groups + if (scale <= 0) + { + //Msg("scale <= 0\n"); + return; + } + + // Test 5 times rule + Vector vDelta; + VectorSubtract( pPatch1->origin, pPatch2->origin, vDelta ); + float flThreshold = ( M_PI * 0.04 ) * DotProduct( vDelta, vDelta ); + + if (flThreshold < pPatch2->area) + { + scale = FormFactorPolyToDiff( pPatch2, pPatch1 ); + if (scale <= 0.0) + return; + } + + trans = (pPatch2->area*scale); + + if (trans <= TRANSFER_EPSILON) + { + return; + } + + transfer->patch = pPatch2 - g_Patches.Base(); + + // FIXME: why is this not trans? + transfer->transfer = trans; + +#if 0 + // DEBUG! Dump patches and transfer connection for displacements. This creates a lot of data, so only + // use it when you really want it - that is why it is #if-ed out. + if ( g_bDumpPatches ) + { + if ( !pFpTrans ) + { + pFpTrans = g_pFileSystem->Open( "trans.txt", "w" ); + } + Vector light = pPatch1->totallight.light[0] + pPatch1->directlight; + WriteWinding( pFpTrans, pPatch1->winding, light ); + light = pPatch2->totallight.light[0] + pPatch2->directlight; + WriteWinding( pFpTrans, pPatch2->winding, light ); + WriteLine( pFpTrans, pPatch1->origin, pPatch2->origin, Vector( 255, 0, 255 ) ); + } +#endif + + pPatch1->numtransfers++; +} + + +void MakeScales ( int ndxPatch, transfer_t *all_transfers ) +{ + int j; + float total; + transfer_t *t, *t2; + total = 0; + + if( ndxPatch == g_Patches.InvalidIndex() ) + return; + CPatch *patch = &g_Patches.Element( ndxPatch ); + + // copy the transfers out + if (patch->numtransfers) + { + if (patch->numtransfers > max_transfer) + { + max_transfer = patch->numtransfers; + } + + + patch->transfers = ( transfer_t* )calloc (1, patch->numtransfers * sizeof(transfer_t)); + if (!patch->transfers) + Error ("Memory allocation failure"); + + // get total transfer energy + t2 = all_transfers; + + // overflow check! + for (j=0 ; jnumtransfers ; j++, t2++) + { + total += t2->transfer; + } + + // the total transfer should be PI, but we need to correct errors due to overlaping surfaces + if (total > M_PI) + total = 1.0f/total; + else + total = 1.0f/M_PI; + + t = patch->transfers; + t2 = all_transfers; + for (j=0 ; jnumtransfers ; j++, t++, t2++) + { + t->transfer = t2->transfer*total; + t->patch = t2->patch; + } + if (patch->numtransfers > max_transfer) + { + max_transfer = patch->numtransfers; + } + } + else + { + // Error - patch has no transfers + // patch->totallight[2] = 255; + } + + ThreadLock (); + total_transfer += patch->numtransfers; + ThreadUnlock (); +} + +/* +============= +WriteWorld +============= +*/ +void WriteWorld (char *name, int iBump) +{ + unsigned j; + FileHandle_t out; + CPatch *patch; + + out = g_pFileSystem->Open( name, "w" ); + if (!out) + Error ("Couldn't open %s", name); + + unsigned int uiPatchCount = g_Patches.Size(); + for (j=0; jchild1 != g_Patches.InvalidIndex() ) + continue; + + if( patch->clusterNumber == -1 ) + { + Vector vGreen; + VectorClear( vGreen ); + vGreen[1] = 256.0f; + WriteWinding( out, patch->winding, vGreen ); + } + else + { + Vector light = patch->totallight.light[iBump] + patch->directlight; + WriteWinding( out, patch->winding, light ); + if( bDumpNormals ) + { + WriteNormal( out, patch->origin, patch->plane->normal, 15.0f, patch->plane->normal * 255.0f ); + } + } + } + + g_pFileSystem->Close( out ); +} + +void WriteRTEnv (char *name) +{ + FileHandle_t out; + + out = g_pFileSystem->Open( name, "w" ); + if (!out) + Error ("Couldn't open %s", name); + + winding_t *triw = AllocWinding( 3 ); + triw->numpoints = 3; + + for( int i = 0; i < g_RtEnv.OptimizedTriangleList.Size(); i++ ) + { + triw->p[0] = g_RtEnv.OptimizedTriangleList[i].Vertex( 0); + triw->p[1] = g_RtEnv.OptimizedTriangleList[i].Vertex( 1); + triw->p[2] = g_RtEnv.OptimizedTriangleList[i].Vertex( 2); + int id = g_RtEnv.OptimizedTriangleList[i].m_Data.m_GeometryData.m_nTriangleID; + Vector color(0, 0, 0); + if (id & TRACE_ID_OPAQUE) color.Init(0, 255, 0); + if (id & TRACE_ID_SKY) color.Init(0, 0, 255); + if (id & TRACE_ID_STATICPROP) color.Init(255, 0, 0); + WriteWinding(out, triw, color); + } + FreeWinding(triw); + + g_pFileSystem->Close( out ); +} + +void WriteWinding (FileHandle_t out, winding_t *w, Vector& color ) +{ + int i; + + CmdLib_FPrintf (out, "%i\n", w->numpoints); + for (i=0 ; inumpoints ; i++) + { + CmdLib_FPrintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", + w->p[i][0], + w->p[i][1], + w->p[i][2], + color[ 0 ] / 256, + color[ 1 ] / 256, + color[ 2 ] / 256 ); + } +} + + +void WriteNormal( FileHandle_t out, Vector const &nPos, Vector const &nDir, + float length, Vector const &color ) +{ + CmdLib_FPrintf( out, "2\n" ); + CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", + nPos.x, nPos.y, nPos.z, + color.x / 256, color.y / 256, color.z / 256 ); + CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", + nPos.x + ( nDir.x * length ), + nPos.y + ( nDir.y * length ), + nPos.z + ( nDir.z * length ), + color.x / 256, color.y / 256, color.z / 256 ); +} + +void WriteLine( FileHandle_t out, const Vector &vecPos1, const Vector &vecPos2, const Vector &color ) +{ + CmdLib_FPrintf( out, "2\n" ); + CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", + vecPos1.x, vecPos1.y, vecPos1.z, + color.x / 256, color.y / 256, color.z / 256 ); + CmdLib_FPrintf( out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", + vecPos2.x, vecPos2.y, vecPos2.z, + color.x / 256, color.y / 256, color.z / 256 ); +} + +void WriteTrace( const char *pFileName, const FourRays &rays, const RayTracingResult& result ) +{ + FileHandle_t out; + + out = g_pFileSystem->Open( pFileName, "a" ); + if (!out) + Error ("Couldn't open %s", pFileName); + + // Draws rays + for ( int i = 0; i < 4; ++i ) + { + Vector vecOrigin = rays.origin.Vec(i); + Vector vecEnd = rays.direction.Vec(i); + VectorNormalize( vecEnd ); + vecEnd *= SubFloat( result.HitDistance, i ); + vecEnd += vecOrigin; + WriteLine( out, vecOrigin, vecEnd, Vector( 256, 0, 0 ) ); + WriteNormal( out, vecEnd, result.surface_normal.Vec(i), 10.0f, Vector( 256, 265, 0 ) ); + } + + g_pFileSystem->Close( out ); +} + + +/* +============= +CollectLight +============= +*/ +// patch's totallight += new light received to each patch +// patch's emitlight = addlight (newly received light from GatherLight) +// patch's addlight = 0 +// pull received light from children. +void CollectLight( Vector& total ) +{ + int i, j; + CPatch *patch; + + VectorFill( total, 0 ); + + // process patches in reverse order so that children are processed before their parents + unsigned int uiPatchCount = g_Patches.Size(); + for( i = uiPatchCount - 1; i >= 0; i-- ) + { + patch = &g_Patches.Element( i ); + int normalCount = patch->needsBumpmap ? NUM_BUMP_VECTS+1 : 1; + // sky's never collect light, it is just dropped + if (patch->sky) + { + VectorFill( emitlight[ i ], 0 ); + } + else if ( patch->child1 == g_Patches.InvalidIndex() ) + { + // This is a leaf node. + for ( j = 0; j < normalCount; j++ ) + { + VectorAdd( patch->totallight.light[j], addlight[i].light[j], patch->totallight.light[j] ); + } + VectorCopy( addlight[i].light[0], emitlight[i] ); + VectorAdd( total, emitlight[i], total ); + } + else + { + // This is an interior node. + // Pull received light from children. + float s1, s2; + CPatch *child1; + CPatch *child2; + + child1 = &g_Patches[patch->child1]; + child2 = &g_Patches[patch->child2]; + + // BUG: This doesn't do anything? + if ((int)patch->area != (int)(child1->area + child2->area)) + s1 = 0; + + s1 = child1->area / (child1->area + child2->area); + s2 = child2->area / (child1->area + child2->area); + + // patch->totallight = s1 * child1->totallight + s2 * child2->totallight + for ( j = 0; j < normalCount; j++ ) + { + VectorScale( child1->totallight.light[j], s1, patch->totallight.light[j] ); + VectorMA( patch->totallight.light[j], s2, child2->totallight.light[j], patch->totallight.light[j] ); + } + + // patch->emitlight = s1 * child1->emitlight + s2 * child2->emitlight + VectorScale( emitlight[patch->child1], s1, emitlight[i] ); + VectorMA( emitlight[i], s2, emitlight[patch->child2], emitlight[i] ); + } + for ( j = 0; j < NUM_BUMP_VECTS+1; j++ ) + { + VectorFill( addlight[ i ].light[j], 0 ); + } + } +} + +/* +============= +GatherLight + +Get light from other patches + Run multi-threaded +============= +*/ + +#ifdef _WIN32 +#pragma warning (disable:4701) +#endif + +extern void GetBumpNormals( const float* sVect, const float* tVect, const Vector& flatNormal, + const Vector& phongNormal, Vector bumpNormals[NUM_BUMP_VECTS] ); + + +void PreGetBumpNormalsForDisp( texinfo_t *pTexinfo, Vector &vecU, Vector &vecV, Vector &vecNormal ) +{ + Vector vecTexU( pTexinfo->textureVecsTexelsPerWorldUnits[0][0], pTexinfo->textureVecsTexelsPerWorldUnits[0][1], pTexinfo->textureVecsTexelsPerWorldUnits[0][2] ); + Vector vecTexV( pTexinfo->textureVecsTexelsPerWorldUnits[1][0], pTexinfo->textureVecsTexelsPerWorldUnits[1][1], pTexinfo->textureVecsTexelsPerWorldUnits[1][2] ); + Vector vecLightU( pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[0][2] ); + Vector vecLightV( pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][0], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][1], pTexinfo->lightmapVecsLuxelsPerWorldUnits[1][2] ); + + VectorNormalize( vecTexU ); + VectorNormalize( vecTexV ); + VectorNormalize( vecLightU ); + VectorNormalize( vecLightV ); + + bool bDoConversion = false; + if ( fabs( vecTexU.Dot( vecLightU ) ) < 0.999f ) + { + bDoConversion = true; + } + + if ( fabs( vecTexV.Dot( vecLightV ) ) < 0.999f ) + { + bDoConversion = true; + } + + if ( bDoConversion ) + { + matrix3x4_t matTex( vecTexU, vecTexV, vecNormal, vec3_origin ); + matrix3x4_t matLight( vecLightU, vecLightV, vecNormal, vec3_origin ); + matrix3x4_t matTmp; + ConcatTransforms ( matLight, matTex, matTmp ); + MatrixGetColumn( matTmp, 0, vecU ); + MatrixGetColumn( matTmp, 1, vecV ); + MatrixGetColumn( matTmp, 2, vecNormal ); + + Assert( fabs( vecTexU.Dot( vecTexV ) ) <= 0.001f ); + return; + } + + vecU = vecTexU; + vecV = vecTexV; +} + +void GatherLight (int threadnum, void *pUserData) +{ + int i, j, k; + transfer_t *trans; + int num; + CPatch *patch; + Vector sum, v; + + while (1) + { + j = GetThreadWork (); + if (j == -1) + break; + + patch = &g_Patches[j]; + + trans = patch->transfers; + num = patch->numtransfers; + if ( patch->needsBumpmap ) + { + Vector delta; + Vector bumpSum[NUM_BUMP_VECTS+1]; + Vector normals[NUM_BUMP_VECTS+1]; + + // Disps + bool bDisp = ( g_pFaces[patch->faceNumber].dispinfo != -1 ); + if ( bDisp ) + { + normals[0] = patch->normal; + texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo]; + Vector vecTexU, vecTexV; + PreGetBumpNormalsForDisp( pTexinfo, vecTexU, vecTexV, normals[0] ); + + // use facenormal along with the smooth normal to build the three bump map vectors + GetBumpNormals( vecTexU, vecTexV, normals[0], normals[0], &normals[1] ); + } + else + { + GetPhongNormal( patch->faceNumber, patch->origin, normals[0] ); + + texinfo_t *pTexinfo = &texinfo[g_pFaces[patch->faceNumber].texinfo]; + // use facenormal along with the smooth normal to build the three bump map vectors + GetBumpNormals( pTexinfo->textureVecsTexelsPerWorldUnits[0], + pTexinfo->textureVecsTexelsPerWorldUnits[1], patch->normal, + normals[0], &normals[1] ); + } + + // force the base lightmap to use the flat normal instead of the phong normal + // FIXME: why does the patch not use the phong normal? + normals[0] = patch->normal; + + for ( i = 0; i < NUM_BUMP_VECTS+1; i++ ) + { + VectorFill( bumpSum[i], 0 ); + } + + float dot; + for (k=0 ; kpatch]; + + // get vector to other patch + VectorSubtract (patch2->origin, patch->origin, delta); + VectorNormalize (delta); + // find light emitted from other patch + for(i=0; i<3; i++) + { + v[i] = emitlight[trans->patch][i] * patch2->reflectivity[i]; + } + // remove normal already factored into transfer steradian + float scale = 1.0f / DotProduct (delta, patch->normal); + VectorScale( v, trans->transfer * scale, v ); + + Vector bumpTransfer; + for ( i = 0; i < NUM_BUMP_VECTS+1; i++ ) + { + dot = DotProduct( delta, normals[i] ); + if ( dot <= 0 ) + { +// Assert( i > 0 ); // if this hits, then the transfer shouldn't be here. It doesn't face the flat normal of this face! + continue; + } + bumpTransfer = v * dot; + VectorAdd( bumpSum[i], bumpTransfer, bumpSum[i] ); + } + } + for ( i = 0; i < NUM_BUMP_VECTS+1; i++ ) + { + VectorCopy( bumpSum[i], addlight[j].light[i] ); + } + } + else + { + VectorFill( sum, 0 ); + for (k=0 ; kpatch][i] * g_Patches[trans->patch].reflectivity[i]; + } + VectorScale( v, trans->transfer, v ); + VectorAdd( sum, v, sum ); + } + VectorCopy( sum, addlight[j].light[0] ); + } + } +} + +#ifdef _WIN32 +#pragma warning (default:4701) +#endif + + +/* +============= +BounceLight +============= +*/ +void BounceLight (void) +{ + unsigned i; + Vector added; + char name[64]; + qboolean bouncing = numbounce > 0; + + unsigned int uiPatchCount = g_Patches.Size(); + for (i=0 ; iOpen( "lightemit.txt", "w" ); + + unsigned int uiPatchCount = g_Patches.Size(); + for (i=0 ; iClose( dFp ); + + for (i=0; iemitlight to receiver->addlight + unsigned int uiPatchCount = g_Patches.Size(); + RunThreadsOn (uiPatchCount, true, GatherLight); + // move newly received light (addlight) to light to be sent out (emitlight) + // start at children and pull light up to parents + // light is always received to leaf patches + CollectLight( added ); + + qprintf ("\tBounce #%i added RGB(%.0f, %.0f, %.0f)\n", i+1, added[0], added[1], added[2] ); + + if ( i+1 == numbounce || (added[0] < 1.0 && added[1] < 1.0 && added[2] < 1.0) ) + bouncing = false; + + i++; + if ( g_bDumpPatches && !bouncing && i != 1) + { + sprintf (name, "bounce%i.txt", i); + WriteWorld (name, 0); + } + } +} + + + +//----------------------------------------------------------------------------- +// Purpose: Counts the number of clusters in a map with no visibility +// Output : int +//----------------------------------------------------------------------------- +int CountClusters( void ) +{ + int clusterCount = 0; + + for ( int i = 0; i < numleafs; i++ ) + { + if ( dleafs[i].cluster > clusterCount ) + clusterCount = dleafs[i].cluster; + } + + return clusterCount + 1; +} + + +/* +============= +RadWorld +============= +*/ +void RadWorld_Start() +{ + unsigned i; + + if (luxeldensity < 1.0) + { + // Remember the old lightmap vectors. + float oldLightmapVecs[MAX_MAP_TEXINFO][2][4]; + for (i = 0; i < texinfo.Count(); i++) + { + for( int j=0; j < 2; j++ ) + { + for( int k=0; k < 3; k++ ) + { + oldLightmapVecs[i][j][k] = texinfo[i].lightmapVecsLuxelsPerWorldUnits[j][k]; + } + } + } + + // rescale luxels to be no denser than "luxeldensity" + for (i = 0; i < texinfo.Count(); i++) + { + texinfo_t *tx = &texinfo[i]; + + for (int j = 0; j < 2; j++ ) + { + Vector tmp( tx->lightmapVecsLuxelsPerWorldUnits[j][0], tx->lightmapVecsLuxelsPerWorldUnits[j][1], tx->lightmapVecsLuxelsPerWorldUnits[j][2] ); + float scale = VectorNormalize( tmp ); + // only rescale them if the current scale is "tighter" than the desired scale + // FIXME: since this writes out to the BSP file every run, once it's set high it can't be reset + // to a lower value. + if (fabs( scale ) > luxeldensity) + { + if (scale < 0) + { + scale = -luxeldensity; + } + else + { + scale = luxeldensity; + } + VectorScale( tmp, scale, tmp ); + tx->lightmapVecsLuxelsPerWorldUnits[j][0] = tmp.x; + tx->lightmapVecsLuxelsPerWorldUnits[j][1] = tmp.y; + tx->lightmapVecsLuxelsPerWorldUnits[j][2] = tmp.z; + } + } + } + + UpdateAllFaceLightmapExtents(); + } + + MakeParents (0, -1); + + BuildClusterTable(); + + // turn each face into a single patch + MakePatches (); + PairEdges (); + + // store the vertex normals calculated in PairEdges + // so that the can be written to the bsp file for + // use in the engine + SaveVertexNormals(); + + // subdivide patches to a maximum dimension + SubdividePatches (); + + // add displacement faces to cluster table + AddDispsToClusterTable(); + + // create directlights out of patches and lights + CreateDirectLights (); + + // set up sky cameras + ProcessSkyCameras(); +} + + +// This function should fill in the indices into g_pFaces[] for the faces +// with displacements that touch the specified leaf. +void STUB_GetDisplacementsTouchingLeaf( int iLeaf, CUtlVector &dispFaces ) +{ +} + + +void BuildFacesVisibleToLights( bool bAllVisible ) +{ + g_FacesVisibleToLights.SetSize( numfaces/8 + 1 ); + + if( bAllVisible ) + { + memset( g_FacesVisibleToLights.Base(), 0xFF, g_FacesVisibleToLights.Count() ); + return; + } + + // First merge all the light PVSes. + CUtlVector aggregate; + aggregate.SetSize( (dvis->numclusters/8) + 1 ); + memset( aggregate.Base(), 0, aggregate.Count() ); + + int nDWords = aggregate.Count() / 4; + int nBytes = aggregate.Count() - nDWords*4; + + for( directlight_t *dl = activelights; dl != NULL; dl = dl->next ) + { + byte *pIn = dl->pvs; + byte *pOut = aggregate.Base(); + for( int iDWord=0; iDWord < nDWords; iDWord++ ) + { + *((unsigned long*)pOut) |= *((unsigned long*)pIn); + pIn += 4; + pOut += 4; + } + + for( int iByte=0; iByte < nBytes; iByte++ ) + { + *pOut |= *pIn; + ++pOut; + ++pIn; + } + } + + + // Now tag any faces that are visible to this monster PVS. + for( int iCluster=0; iCluster < dvis->numclusters; iCluster++ ) + { + if( g_ClusterLeaves[iCluster].leafCount ) + { + if( aggregate[iCluster>>3] & (1 << (iCluster & 7)) ) + { + for ( int i = 0; i < g_ClusterLeaves[iCluster].leafCount; i++ ) + { + int iLeaf = g_ClusterLeaves[iCluster].leafs[i]; + + // Tag all the faces. + int iFace; + for( iFace=0; iFace < dleafs[iLeaf].numleaffaces; iFace++ ) + { + int index = dleafs[iLeaf].firstleafface + iFace; + index = dleaffaces[index]; + + assert( index < numfaces ); + g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7)); + } + + // Fill in STUB_GetDisplacementsTouchingLeaf when it's available + // so displacements get relit. + CUtlVector dispFaces; + STUB_GetDisplacementsTouchingLeaf( iLeaf, dispFaces ); + for( iFace=0; iFace < dispFaces.Count(); iFace++ ) + { + int index = dispFaces[iFace]; + g_FacesVisibleToLights[index >> 3] |= (1 << (index & 7)); + } + } + } + } + } + + // For stats.. figure out how many faces it's going to touch. + int nFacesToProcess = 0; + for( int i=0; i < numfaces; i++ ) + { + if( g_FacesVisibleToLights[i>>3] & (1 << (i & 7)) ) + ++nFacesToProcess; + } +} + + + +void MakeAllScales (void) +{ + // determine visibility between patches + BuildVisMatrix (); + + // release visibility matrix + FreeVisMatrix (); + + Msg("transfers %d, max %d\n", total_transfer, max_transfer ); + + qprintf ("transfer lists: %5.1f megs\n" + , (float)total_transfer * sizeof(transfer_t) / (1024*1024)); +} + + +// Helper function. This can be useful to visualize the world and faces and see which face +// corresponds to which dface. +#if 0 + #include "iscratchpad3d.h" + void ScratchPad_DrawWorld() + { + IScratchPad3D *pPad = ScratchPad3D_Create(); + pPad->SetAutoFlush( false ); + + for ( int i=0; i < numfaces; i++ ) + { + dface_t *f = &g_pFaces[i]; + + // Draw the face's outline, then put text for its face index on it too. + CUtlVector points; + for ( int iEdge = 0; iEdge < f->numedges; iEdge++ ) + { + int v; + int se = dsurfedges[f->firstedge + iEdge]; + if ( se < 0 ) + v = dedges[-se].v[1]; + else + v = dedges[se].v[0]; + + dvertex_t *dv = &dvertexes[v]; + points.AddToTail( dv->point ); + } + + // Draw the outline. + Vector vCenter( 0, 0, 0 ); + for ( iEdge=0; iEdge < points.Count(); iEdge++ ) + { + pPad->DrawLine( CSPVert( points[iEdge] ), CSPVert( points[(iEdge+1)%points.Count()] ) ); + vCenter += points[iEdge]; + } + vCenter /= points.Count(); + + // Draw the text. + char str[512]; + Q_snprintf( str, sizeof( str ), "%d", i ); + + CTextParams params; + + params.m_bCentered = true; + params.m_bOutline = true; + params.m_flLetterWidth = 2; + params.m_vColor.Init( 1, 0, 0 ); + + VectorAngles( dplanes[f->planenum].normal, params.m_vAngles ); + params.m_bTwoSided = true; + + params.m_vPos = vCenter; + + pPad->DrawText( str, params ); + } + + pPad->Release(); + } +#endif + + +bool RadWorld_Go() +{ + g_iCurFace = 0; + + InitMacroTexture( source ); + + if( g_pIncremental ) + { + g_pIncremental->PrepareForLighting(); + + // Cull out faces that aren't visible to any of the lights that we're updating with. + BuildFacesVisibleToLights( false ); + } + else + { + // Mark all faces visible.. when not doing incremental lighting, it's highly + // likely that all faces are going to be touched by at least one light so don't + // waste time here. + BuildFacesVisibleToLights( true ); + } + + // build initial facelights + if (g_bUseMPI) + { + // RunThreadsOnIndividual (numfaces, true, BuildFacelights); + RunMPIBuildFacelights(); + } + else + { + RunThreadsOnIndividual (numfaces, true, BuildFacelights); + } + + // Was the process interrupted? + if( g_pIncremental && (g_iCurFace != numfaces) ) + return false; + + // Figure out the offset into lightmap data for each face. + PrecompLightmapOffsets(); + + // If we're doing incremental lighting, stop here. + if( g_pIncremental ) + { + g_pIncremental->Finalize(); + } + else + { + // free up the direct lights now that we have facelights + ExportDirectLightsToWorldLights(); + + if ( g_bDumpPatches ) + { + for( int iBump = 0; iBump < 4; ++iBump ) + { + char szName[64]; + sprintf ( szName, "bounce0_%d.txt", iBump ); + WriteWorld( szName, iBump ); + } + } + + if (numbounce > 0) + { + // allocate memory for emitlight/addlight + emitlight.SetSize( g_Patches.Size() ); + memset( emitlight.Base(), 0, g_Patches.Size() * sizeof( Vector ) ); + addlight.SetSize( g_Patches.Size() ); + memset( addlight.Base(), 0, g_Patches.Size() * sizeof( bumplights_t ) ); + + MakeAllScales (); + + // spread light around + BounceLight (); + } + + // + // displacement surface luxel accumulation (make threaded!!!) + // + StaticDispMgr()->StartTimer( "Build Patch/Sample Hash Table(s)....." ); + StaticDispMgr()->InsertSamplesDataIntoHashTable(); + StaticDispMgr()->InsertPatchSampleDataIntoHashTable(); + StaticDispMgr()->EndTimer(); + + // blend bounced light into direct light and save + VMPI_SetCurrentStage( "FinalLightFace" ); + if ( !g_bUseMPI || g_bMPIMaster ) + RunThreadsOnIndividual (numfaces, true, FinalLightFace); + + // Distribute the lighting data to workers. + VMPI_DistributeLightData(); + + Msg("FinalLightFace Done\n"); fflush(stdout); + } + + return true; +} + +// declare the sample file pointer -- the whole debug print system should +// be reworked at some point!! +FileHandle_t pFileSamples[4][4]; + +void LoadPhysicsDLL( void ) +{ + PhysicsDLLPath( "VPHYSICS.DLL" ); +} + + +void InitDumpPatchesFiles() +{ + for( int iStyle = 0; iStyle < 4; ++iStyle ) + { + for ( int iBump = 0; iBump < 4; ++iBump ) + { + char szFilename[MAX_PATH]; + sprintf( szFilename, "samples_style%d_bump%d.txt", iStyle, iBump ); + pFileSamples[iStyle][iBump] = g_pFileSystem->Open( szFilename, "w" ); + if( !pFileSamples[iStyle][iBump] ) + { + Error( "Can't open %s for -dump.\n", szFilename ); + } + } + } +} + + +void VRAD_LoadBSP( char const *pFilename ) +{ + ThreadSetDefault (); + + g_flStartTime = Plat_FloatTime(); + + if( g_bLowPriority ) + { + SetLowPriority(); + } + + strcpy( level_name, source ); + + // This must come after InitFileSystem because the file system pointer might change. + if ( g_bDumpPatches ) + InitDumpPatchesFiles(); + + // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames, + // so we prepend qdir here. + strcpy( source, ExpandPath( source ) ); + + if ( !g_bUseMPI ) + { + // Setup the logfile. + char logFile[512]; + _snprintf( logFile, sizeof(logFile), "%s.log", source ); + SetSpewFunctionLogFile( logFile ); + } + + LoadPhysicsDLL(); + + // Set the required global lights filename and try looking in qproject + strcpy( global_lights, "lights.rad" ); + if ( !g_pFileSystem->FileExists( global_lights ) ) + { + // Otherwise, try looking in the BIN directory from which we were run from + Msg( "Could not find lights.rad in %s.\nTrying VRAD BIN directory instead...\n", + global_lights ); + GetModuleFileName( NULL, global_lights, sizeof( global_lights ) ); + Q_ExtractFilePath( global_lights, global_lights, sizeof( global_lights ) ); + strcat( global_lights, "lights.rad" ); + } + + // Set the optional level specific lights filename + strcpy( level_lights, source ); + + Q_DefaultExtension( level_lights, ".rad", sizeof( level_lights ) ); + if ( !g_pFileSystem->FileExists( level_lights ) ) + *level_lights = 0; + + ReadLightFile(global_lights); // Required + if ( *designer_lights ) ReadLightFile(designer_lights); // Command-line + if ( *level_lights ) ReadLightFile(level_lights); // Optional & implied + + strcpy(incrementfile, source); + Q_DefaultExtension(incrementfile, ".r0", sizeof(incrementfile)); + Q_DefaultExtension(source, ".bsp", sizeof( source )); + + GetPlatformMapPath( source, platformPath, 0, MAX_PATH ); + + Msg( "Loading %s\n", platformPath ); + VMPI_SetCurrentStage( "LoadBSPFile" ); + LoadBSPFile (platformPath); + + // now, set whether or not static prop lighting is present + if (g_bStaticPropLighting) + g_LevelFlags |= g_bHDR? LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR : LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR; + else + { + g_LevelFlags &= ~( LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_HDR | LVLFLAGS_BAKED_STATIC_PROP_LIGHTING_NONHDR ); + } + + // now, we need to set our face ptr depending upon hdr, and if hdr, init it + if (g_bHDR) + { + g_pFaces = dfaces_hdr; + if (numfaces_hdr==0) + { + numfaces_hdr = numfaces; + memcpy( dfaces_hdr, dfaces, numfaces*sizeof(dfaces[0]) ); + } + } + else + { + g_pFaces = dfaces; + } + + + ParseEntities (); + ExtractBrushEntityShadowCasters(); + + StaticPropMgr()->Init(); + StaticDispMgr()->Init(); + + if (!visdatasize) + { + Msg("No vis information, direct lighting only.\n"); + numbounce = 0; + ambient[0] = ambient[1] = ambient[2] = 0.1f; + dvis->numclusters = CountClusters(); + } + + // + // patches and referencing data (ensure capacity) + // + // TODO: change the maxes to the amount from the bsp!! + // +// g_Patches.EnsureCapacity( MAX_PATCHES ); + + g_FacePatches.SetSize( MAX_MAP_FACES ); + faceParents.SetSize( MAX_MAP_FACES ); + clusterChildren.SetSize( MAX_MAP_CLUSTERS ); + + int ndx; + for ( ndx = 0; ndx < MAX_MAP_FACES; ndx++ ) + { + g_FacePatches[ndx] = g_FacePatches.InvalidIndex(); + faceParents[ndx] = faceParents.InvalidIndex(); + } + + for ( ndx = 0; ndx < MAX_MAP_CLUSTERS; ndx++ ) + { + clusterChildren[ndx] = clusterChildren.InvalidIndex(); + } + + // Setup ray tracer + AddBrushesForRayTrace(); + StaticDispMgr()->AddPolysForRayTrace(); + StaticPropMgr()->AddPolysForRayTrace(); + + // Dump raytracer for glview + if ( g_bDumpRtEnv ) + WriteRTEnv("trace.txt"); + + // Build acceleration structure + printf ( "Setting up ray-trace acceleration structure... "); + float start = Plat_FloatTime(); + g_RtEnv.SetupAccelerationStructure(); + float end = Plat_FloatTime(); + printf ( "Done (%.2f seconds)\n", end-start ); + +#if 0 // To test only k-d build + exit(0); +#endif + + RadWorld_Start(); + + // Setup incremental lighting. + if( g_pIncremental ) + { + if( !g_pIncremental->Init( source, incrementfile ) ) + { + Error( "Unable to load incremental lighting file in %s.\n", incrementfile ); + return; + } + } +} + + +void VRAD_ComputeOtherLighting() +{ + // Compute lighting for the bsp file + if ( !g_bNoDetailLighting ) + { + ComputeDetailPropLighting( THREADINDEX_MAIN ); + } + + ComputePerLeafAmbientLighting(); + + // bake the static props high quality vertex lighting into the bsp + if ( !do_fast && g_bStaticPropLighting ) + { + StaticPropMgr()->ComputeLighting( THREADINDEX_MAIN ); + } +} + +extern void CloseDispLuxels(); + +void VRAD_Finish() +{ + Msg( "Ready to Finish\n" ); + fflush( stdout ); + + if ( verbose ) + { + PrintBSPFileSizes(); + } + + Msg( "Writing %s\n", platformPath ); + VMPI_SetCurrentStage( "WriteBSPFile" ); + WriteBSPFile(platformPath); + + if ( g_bDumpPatches ) + { + for ( int iStyle = 0; iStyle < 4; ++iStyle ) + { + for ( int iBump = 0; iBump < 4; ++iBump ) + { + g_pFileSystem->Close( pFileSamples[iStyle][iBump] ); + } + } + } + + CloseDispLuxels(); + + StaticPropMgr()->Shutdown(); + + double end = Plat_FloatTime(); + + char str[512]; + GetHourMinuteSecondsString( (int)( end - g_flStartTime ), str, sizeof( str ) ); + Msg( "%s elapsed\n", str ); + + ReleasePakFileLumps(); +} + + +// Run startup code like initialize mathlib (called from main() and from the +// WorldCraft interface into vrad). +void VRAD_Init() +{ + MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false ); + InstallAllocationFunctions(); + InstallSpewFunction(); +} + + +int ParseCommandLine( int argc, char **argv, bool *onlydetail ) +{ + *onlydetail = false; + + // default to LDR + SetHDRMode( false ); + int i; + for( i=1 ; i 1.0) + luxeldensity = 1.0 / luxeldensity; + } + else + { + Warning("Error: expected a value after '-luxeldensity'\n" ); + return 1; + } + } + else if( !Q_stricmp( argv[i], "-low" ) ) + { + g_bLowPriority = true; + } + else if( !Q_stricmp( argv[i], "-loghash" ) ) + { + g_bLogHashData = true; + } + else if( !Q_stricmp( argv[i], "-onlydetail" ) ) + { + *onlydetail = true; + } + else if (!Q_stricmp(argv[i],"-softsun")) + { + if ( ++i < argc ) + { + g_SunAngularExtent=atof(argv[i]); + g_SunAngularExtent=sin((M_PI/180.0)*g_SunAngularExtent); + printf("sun extent=%f\n",g_SunAngularExtent); + } + else + { + Warning("Error: expected an angular extent value (0..180) '-softsun'\n" ); + return 1; + } + } + else if ( !Q_stricmp( argv[i], "-maxdispsamplesize" ) ) + { + if ( ++i < argc ) + { + g_flMaxDispSampleSize = ( float )atof( argv[i] ); + } + else + { + Warning( "Error: expected a sample size after '-maxdispsamplesize'\n" ); + return 1; + } + } + else if ( stricmp( argv[i], "-StopOnExit" ) == 0 ) + { + g_bStopOnExit = true; + } + else if ( stricmp( argv[i], "-steam" ) == 0 ) + { + } + else if ( stricmp( argv[i], "-allowdebug" ) == 0 ) + { + // Don't need to do anything, just don't error out. + } + else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) ) + { + } + else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) ) + { + ++i; + } + else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) ) + { + EnableFullMinidumps( true ); + } + else if ( !Q_stricmp( argv[i], "-hdr" ) ) + { + SetHDRMode( true ); + } + else if ( !Q_stricmp( argv[i], "-ldr" ) ) + { + SetHDRMode( false ); + } + else if (!Q_stricmp(argv[i],"-maxchop")) + { + if ( ++i < argc ) + { + maxchop = (float)atof (argv[i]); + if ( maxchop < 1 ) + { + Warning("Error: expected positive value after '-maxchop'\n" ); + return 1; + } + } + else + { + Warning("Error: expected a value after '-maxchop'\n" ); + return 1; + } + } + else if (!Q_stricmp(argv[i],"-chop")) + { + if ( ++i < argc ) + { + minchop = (float)atof (argv[i]); + if ( minchop < 1 ) + { + Warning("Error: expected positive value after '-chop'\n" ); + return 1; + } + minchop = min( minchop, maxchop ); + } + else + { + Warning("Error: expected a value after '-chop'\n" ); + return 1; + } + } + else if ( !Q_stricmp( argv[i], "-dispchop" ) ) + { + if ( ++i < argc ) + { + dispchop = ( float )atof( argv[i] ); + if ( dispchop < 1.0f ) + { + Warning( "Error: expected positive value after '-dipschop'\n" ); + return 1; + } + } + else + { + Warning( "Error: expected a value after '-dispchop'\n" ); + return 1; + } + } + else if ( !Q_stricmp( argv[i], "-disppatchradius" ) ) + { + if ( ++i < argc ) + { + g_MaxDispPatchRadius = ( float )atof( argv[i] ); + if ( g_MaxDispPatchRadius < 10.0f ) + { + Warning( "Error: g_MaxDispPatchRadius < 10.0\n" ); + return 1; + } + } + else + { + Warning( "Error: expected a value after '-disppatchradius'\n" ); + return 1; + } + } + +#if ALLOWDEBUGOPTIONS + else if (!Q_stricmp(argv[i],"-scale")) + { + if ( ++i < argc ) + { + lightscale = (float)atof (argv[i]); + } + else + { + Warning("Error: expected a value after '-scale'\n" ); + return 1; + } + } + else if (!Q_stricmp(argv[i],"-ambient")) + { + if ( i+3 < argc ) + { + ambient[0] = (float)atof (argv[++i]) * 128; + ambient[1] = (float)atof (argv[++i]) * 128; + ambient[2] = (float)atof (argv[++i]) * 128; + } + else + { + Warning("Error: expected three color values after '-ambient'\n" ); + return 1; + } + } + else if (!Q_stricmp(argv[i],"-dlight")) + { + if ( ++i < argc ) + { + dlight_threshold = (float)atof (argv[i]); + } + else + { + Warning("Error: expected a value after '-dlight'\n" ); + return 1; + } + } + else if (!Q_stricmp(argv[i],"-sky")) + { + if ( ++i < argc ) + { + indirect_sun = (float)atof (argv[i]); + } + else + { + Warning("Error: expected a value after '-sky'\n" ); + return 1; + } + } + else if (!Q_stricmp(argv[i],"-notexscale")) + { + texscale = false; + } + else if (!Q_stricmp(argv[i],"-coring")) + { + if ( ++i < argc ) + { + coring = (float)atof( argv[i] ); + } + else + { + Warning("Error: expected a light threshold after '-coring'\n" ); + return 1; + } + } +#endif + // NOTE: the -mpi checks must come last here because they allow the previous argument + // to be -mpi as well. If it game before something else like -game, then if the previous + // argument was -mpi and the current argument was something valid like -game, it would skip it. + else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) ) + { + if ( stricmp( argv[i], "-mpi" ) == 0 ) + g_bUseMPI = true; + + // Any other args that start with -mpi are ok too. + if ( i == argc - 1 && V_stricmp( argv[i], "-mpi_ListParams" ) != 0 ) + break; + } + else + { + break; + } + } + + return i; +} + + +void PrintCommandLine( int argc, char **argv ) +{ + Warning( "Command line: " ); + for ( int z=0; z < argc; z++ ) + { + Warning( "\"%s\" ", argv[z] ); + } + Warning( "\n\n" ); +} + + +void PrintUsage( int argc, char **argv ) +{ + PrintCommandLine( argc, argv ); + + Warning( + "usage : vrad [options...] bspfile\n" + "example: vrad c:\\hl2\\hl2\\maps\\test\n" + "\n" + "Common options:\n" + "\n" + " -v (or -verbose): Turn on verbose output (also shows more command\n" + " -bounce # : Set max number of bounces (default: 100).\n" + " -fast : Quick and dirty lighting.\n" + " -fastambient : Per-leaf ambient sampling is lower quality to save compute time.\n" + " -final : High quality processing. equivalent to -extrasky 16.\n" + " -extrasky n : trace N times as many rays for indirect light and sky ambient.\n" + " -low : Run as an idle-priority process.\n" + " -mpi : Use VMPI to distribute computations.\n" + " -rederror : Show errors in red.\n" + "\n" + " -vproject : Override the VPROJECT environment variable.\n" + " -game : Same as -vproject.\n" + "\n" + "Other options:\n" + " -novconfig : Don't bring up graphical UI on vproject errors.\n" + " -dump : Write debugging .txt files.\n" + " -dumpnormals : Write normals to debug files.\n" + " -dumptrace : Write ray-tracing environment to debug files.\n" + " -threads : Control the number of threads vbsp uses (defaults to the #\n" + " or processors on your machine).\n" + " -lights : Load a lights file in addition to lights.rad and the\n" + " level lights file.\n" + " -noextra : Disable supersampling.\n" + " -debugextra : Places debugging data in lightmaps to visualize\n" + " supersampling.\n" + " -smooth # : Set the threshold for smoothing groups, in degrees\n" + " (default 45).\n" + " -dlightmap : Force direct lighting into different lightmap than\n" + " radiosity.\n" + " -stoponexit : Wait for a keypress on exit.\n" + " -mpi_pw : Use a password to choose a specific set of VMPI workers.\n" + " -nodetaillight : Don't light detail props.\n" + " -centersamples : Move sample centers.\n" + " -luxeldensity # : Rescale all luxels by the specified amount (default: 1.0).\n" + " The number specified must be less than 1.0 or it will be\n" + " ignored.\n" + " -loghash : Log the sample hash table to samplehash.txt.\n" + " -onlydetail : Only light detail props and per-leaf lighting.\n" + " -maxdispsamplesize #: Set max displacement sample size (default: 512).\n" + " -softsun : Treat the sun as an area light source of size degrees." + " Produces soft shadows.\n" + " Recommended values are between 0 and 5. Default is 0.\n" + " -FullMinidumps : Write large minidumps on crash.\n" + " -chop : Smallest number of luxel widths for a bounce patch, used on edges\n" + " -maxchop : Coarsest allowed number of luxel widths for a patch, used in face interiors\n" + "\n" + " -LargeDispSampleRadius: This can be used if there are splotches of bounced light\n" + " on terrain. The compile will take longer, but it will gather\n" + " light across a wider area.\n" + " -StaticPropLighting : generate backed static prop vertex lighting\n" + " -StaticPropPolys : Perform shadow tests of static props at polygon precision\n" + " -OnlyStaticProps : Only perform direct static prop lighting (vrad debug option)\n" + " -StaticPropNormals : when lighting static props, just show their normal vector\n" + " -textureshadows : Allows texture alpha channels to block light - rays intersecting alpha surfaces will sample the texture\n" + " -noskyboxrecurse : Turn off recursion into 3d skybox (skybox shadows on world)\n" + " -nossprops : Globally disable self-shadowing on static props\n" + "\n" +#if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users. + ); +#else + " -mpi_ListParams : Show a list of VMPI parameters.\n" + "\n" + ); + + // Show VMPI parameters? + for ( int i=1; i < argc; i++ ) + { + if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 ) + { + Warning( "VMPI-specific options:\n\n" ); + + bool bIsSDKMode = VMPI_IsSDKMode(); + for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ ) + { + if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode ) + continue; + + Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) ); + Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) ); + Warning( "\n\n" ); + } + break; + } + } +#endif +} + + +int RunVRAD( int argc, char **argv ) +{ +#if defined(_MSC_VER) && ( _MSC_VER >= 1310 ) + Msg("Valve Software - vrad.exe SSE (" __DATE__ ")\n" ); +#else + Msg("Valve Software - vrad.exe (" __DATE__ ")\n" ); +#endif + + Msg("\n Valve Radiosity Simulator \n"); + + verbose = true; // Originally FALSE + + bool onlydetail; + int i = ParseCommandLine( argc, argv, &onlydetail ); + if (i != argc - 1) + { + PrintUsage( argc, argv ); + DeleteCmdLine( argc, argv ); + CmdLib_Exit( 1 ); + } + + VRAD_LoadBSP( argv[i] ); + + if ( (! onlydetail) && (! g_bOnlyStaticProps ) ) + { + RadWorld_Go(); + } + + VRAD_ComputeOtherLighting(); + + VRAD_Finish(); + + VMPI_SetCurrentStage( "master done" ); + + DeleteCmdLine( argc, argv ); + CmdLib_Cleanup(); + return 0; +} + + +int VRAD_Main(int argc, char **argv) +{ + g_pFileSystem = NULL; // Safeguard against using it before it's properly initialized. + + VRAD_Init(); + + // This must come first. + VRAD_SetupMPI( argc, argv ); + + // Initialize the filesystem, so additional commandline options can be loaded + Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) ); + CmdLib_InitFileSystem( argv[ argc - 1 ] ); + Q_FileBase( source, source, sizeof( source ) ); + +#if !defined( _DEBUG ) + if ( g_bUseMPI && !g_bMPIMaster ) + { + SetupToolsMinidumpHandler( VMPI_ExceptionFilter ); + } + else +#endif + { + LoadCmdLineFromFile( argc, argv, source, "vrad" ); // Don't do this if we're a VMPI worker.. + SetupDefaultToolsMinidumpHandler(); + } + + return RunVRAD( argc, argv ); +} + + + + + -- cgit v1.2.3