static void ComputeLightmapColorFromAverage( dface_t* pFace, directlight_t* pSkylight, float scale, Vector pColor[MAX_LIGHTSTYLES] ) { texinfo_t* pTex = &texinfo[pFace->texinfo]; if (pTex->flags & SURF_SKY) { if (pSkylight) { // add in sky ambient Vector amb = pSkylight->light.intensity / 255.0f; pColor[0] += amb * scale; } return; } for (int maps = 0 ; maps < MAXLIGHTMAPS && pFace->styles[maps] != 255 ; ++maps) { ColorRGBExp32* pAvgColor = dface_AvgLightColor( pFace, maps ); // this code expects values from [0..1] not [0..255] Vector color; color[0] = TexLightToLinear( pAvgColor->r, pAvgColor->exponent ); color[1] = TexLightToLinear( pAvgColor->g, pAvgColor->exponent ); color[2] = TexLightToLinear( pAvgColor->b, pAvgColor->exponent ); ComputeAmbientFromSurface( pFace, pSkylight, color ); int style = pFace->styles[maps]; pColor[style] += color * scale; } }
//----------------------------------------------------------------------------- // Trace hemispherical rays from a vertex, accumulating indirect // sources at each ray termination. //----------------------------------------------------------------------------- void ComputeIndirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor, int iThread, bool force_fast, bool bIgnoreNormals ) { Ray_t ray; CLightSurface surfEnum(iThread); outColor.Init(); int nSamples = NUMVERTEXNORMALS; if ( do_fast || force_fast ) nSamples /= 4; else nSamples *= g_flSkySampleScale; float totalDot = 0; DirectionalSampler_t sampler; for (int j = 0; j < nSamples; j++) { Vector samplingNormal = sampler.NextValue(); float dot; if ( bIgnoreNormals ) dot = (0.7071/2); else dot = DotProduct( normal, samplingNormal ); if ( dot <= EQUAL_EPSILON ) { // reject angles behind our plane continue; } totalDot += dot; // trace to determine surface Vector vEnd; VectorScale( samplingNormal, MAX_TRACE_LENGTH, vEnd ); VectorAdd( position, vEnd, vEnd ); ray.Init( position, vEnd, vec3_origin, vec3_origin ); if ( !surfEnum.FindIntersection( ray ) ) continue; // get color from surface lightmap texinfo_t* pTex = &texinfo[surfEnum.m_pSurface->texinfo]; if ( !pTex || pTex->flags & SURF_SKY ) { // ignore contribution from sky // sky ambient already accounted for during direct pass continue; } if ( surfEnum.m_pSurface->styles[0] == 255 || surfEnum.m_pSurface->lightofs < 0 ) { // no light affects this face continue; } Vector lightmapColor; if ( !surfEnum.m_bHasLuxel ) { ColorRGBExp32* pAvgLightmapColor = dface_AvgLightColor( surfEnum.m_pSurface, 0 ); ColorRGBExp32ToVector( *pAvgLightmapColor, lightmapColor ); } else { // get color from displacement int smax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[0] ) + 1; int tmax = ( surfEnum.m_pSurface->m_LightmapTextureSizeInLuxels[1] ) + 1; // luxelcoord is in the space of the accumulated lightmap page; we need to convert // it to be in the space of the surface int ds = clamp( (int)surfEnum.m_LuxelCoord.x, 0, smax-1 ); int dt = clamp( (int)surfEnum.m_LuxelCoord.y, 0, tmax-1 ); ColorRGBExp32* pLightmap = (ColorRGBExp32*)&(*pdlightdata)[surfEnum.m_pSurface->lightofs]; pLightmap += dt * smax + ds; ColorRGBExp32ToVector( *pLightmap, lightmapColor ); } VectorMultiply( lightmapColor, dtexdata[pTex->texdata].reflectivity, lightmapColor ); VectorAdd( outColor, lightmapColor, outColor ); } if ( totalDot ) { VectorScale( outColor, 1.0f/totalDot, outColor ); } }
/* ============= FinalLightFace Add the indirect lighting on top of the direct lighting and save into final map format ============= */ void FinalLightFace( int iThread, int facenum ) { dface_t *f; int i, j, k; facelight_t *fl; float minlight; int lightstyles; LightingValue_t lb[NUM_BUMP_VECTS + 1], v[NUM_BUMP_VECTS + 1]; unsigned char *pdata[NUM_BUMP_VECTS + 1]; int bumpSample; radial_t *rad = NULL; radial_t *prad = NULL; f = &g_pFaces[facenum]; // test for non-lit texture if ( texinfo[f->texinfo].flags & TEX_SPECIAL) return; fl = &facelight[facenum]; for (lightstyles=0; lightstyles < MAXLIGHTMAPS; lightstyles++ ) { if ( f->styles[lightstyles] == 255 ) break; } if ( !lightstyles ) return; // // sample the triangulation // minlight = FloatForKey (face_entity[facenum], "_minlight") * 128; bool needsBumpmap = ( texinfo[f->texinfo].flags & SURF_BUMPLIGHT ) ? true : false; int bumpSampleCount = needsBumpmap ? NUM_BUMP_VECTS + 1 : 1; bool bDisp = ( f->dispinfo != -1 ); //#define RANDOM_COLOR #ifdef RANDOM_COLOR unsigned char randomColor[3]; GetRandomColor( randomColor ); #endif // NOTE: I'm using these RB trees to sort all the illumination values // to compute median colors. Turns out that this is a somewhat better // method that using the average; usually if there are surfaces // with a large light intensity variation, the extremely bright regions // have a very small area and tend to influence the average too much. CUtlRBTree< float, int > m_Red( 0, 256, FloatLess ); CUtlRBTree< float, int > m_Green( 0, 256, FloatLess ); CUtlRBTree< float, int > m_Blue( 0, 256, FloatLess ); for (k=0 ; k < lightstyles; k++ ) { m_Red.RemoveAll(); m_Green.RemoveAll(); m_Blue.RemoveAll(); if (!do_fast) { if( !bDisp ) { rad = BuildLuxelRadial( facenum, k ); } else { rad = StaticDispMgr()->BuildLuxelRadial( facenum, k, needsBumpmap ); } } if (numbounce > 0 && k == 0) { // currently only radiosity light non-displacement surfaces! if( !bDisp ) { prad = BuildPatchRadial( facenum ); } else { prad = StaticDispMgr()->BuildPatchRadial( facenum, needsBumpmap ); } } // pack the nonbump texture and the three bump texture for the given // lightstyle right next to each other. // NOTE: Even though it's building positions for all bump-mapped data, // it isn't going to use those positions (see loop over bumpSample below) // The file offset is correctly computed to only store space for 1 set // of light data if we don't have bumped lighting. for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample ) { pdata[bumpSample] = &(*pdlightdata)[f->lightofs + (k * bumpSampleCount + bumpSample) * fl->numluxels*4]; } // Compute the average luxel color, but not for the bump samples Vector avg( 0.0f, 0.0f, 0.0f ); int avgCount = 0; for (j=0 ; j<fl->numluxels; j++) { // garymct - direct lighting bool baseSampleOk = true; if (!do_fast) { if( !bDisp ) { baseSampleOk = SampleRadial( rad, fl->luxel[j], lb, bumpSampleCount ); } else { baseSampleOk = StaticDispMgr()->SampleRadial( facenum, rad, fl->luxel[j], j, lb, bumpSampleCount, false ); } } else { for ( int iBump = 0 ; iBump < bumpSampleCount; iBump++ ) { lb[iBump] = fl->light[0][iBump][j]; } } if (prad) { // garymct - bounced light // v is indirect light that is received on the luxel. if( !bDisp ) { SampleRadial( prad, fl->luxel[j], v, bumpSampleCount ); } else { StaticDispMgr()->SampleRadial( facenum, prad, fl->luxel[j], j, v, bumpSampleCount, true ); } for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample ) { lb[bumpSample].AddLight( v[bumpSample] ); } } if ( bDisp && g_bDumpPatches ) { for( bumpSample = 0; bumpSample < bumpSampleCount; ++bumpSample ) { DumpDispLuxels( facenum, lb[bumpSample].m_vecLighting, j, bumpSample ); } } if (fl->numsamples == 0) { for( i = 0; i < bumpSampleCount; i++ ) { lb[i].Init( 255, 0, 0 ); } baseSampleOk = false; } int bumpSample; for( bumpSample = 0; bumpSample < bumpSampleCount; bumpSample++ ) { // clip from the bottom first // garymct: minlight is a per entity minimum light value? for( i=0; i<3; i++ ) { lb[bumpSample].m_vecLighting[i] = max( lb[bumpSample].m_vecLighting[i], minlight ); } // Do the average light computation, I'm assuming (perhaps incorrectly?) // that all luxels in a particular lightmap have the same area here. // Also, don't bother doing averages for the bump samples. Doing it here // because of the minlight clamp above + the random color testy thingy. // Also have to do it before Vec3toColorRGBExp32 because it // destructively modifies lb[bumpSample] (Feh!) if ((bumpSample == 0) && baseSampleOk) { ++avgCount; ApplyMacroTextures( facenum, fl->luxel[j], lb[0].m_vecLighting ); // For median computation m_Red.Insert( lb[bumpSample].m_vecLighting[0] ); m_Green.Insert( lb[bumpSample].m_vecLighting[1] ); m_Blue.Insert( lb[bumpSample].m_vecLighting[2] ); } #ifdef RANDOM_COLOR pdata[bumpSample][0] = randomColor[0] / ( bumpSample + 1 ); pdata[bumpSample][1] = randomColor[1] / ( bumpSample + 1 ); pdata[bumpSample][2] = randomColor[2] / ( bumpSample + 1 ); pdata[bumpSample][3] = 0; #else // convert to a 4 byte r,g,b,signed exponent format VectorToColorRGBExp32( Vector( lb[bumpSample].m_vecLighting.x, lb[bumpSample].m_vecLighting.y, lb[bumpSample].m_vecLighting.z ), *( ColorRGBExp32 *)pdata[bumpSample] ); #endif pdata[bumpSample] += 4; } } FreeRadial( rad ); if (prad) { FreeRadial( prad ); prad = NULL; } // Compute the median color for this lightstyle // Remember, the data goes *before* the specified light_ofs, in *reverse order* ColorRGBExp32 *pAvgColor = dface_AvgLightColor( f, k ); if (avgCount == 0) { Vector median( 0, 0, 0 ); VectorToColorRGBExp32( median, *pAvgColor ); } else { unsigned int r, g, b; r = m_Red.FirstInorder(); g = m_Green.FirstInorder(); b = m_Blue.FirstInorder(); avgCount >>= 1; while (avgCount > 0) { r = m_Red.NextInorder(r); g = m_Green.NextInorder(g); b = m_Blue.NextInorder(b); --avgCount; } Vector median( m_Red[r], m_Green[g], m_Blue[b] ); VectorToColorRGBExp32( median, *pAvgColor ); } } }