diff --git a/src/common/Color.h b/src/common/Color.h index fba2482fb4..2ecb4d3988 100644 --- a/src/common/Color.h +++ b/src/common/Color.h @@ -55,6 +55,7 @@ inline void convertFromSRGB( float* v, bool accurate = true ) v[ 2 ] = convertFromSRGB( v[ 2 ], accurate ); } +// Beware: this instantly loses precision, there are less than 256 possible outputs! inline void convertFromSRGB( byte* bytes, bool accurate = true ) { vec3_t v; diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 41f26a2de6..b4290b1932 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -2237,7 +2237,7 @@ static void RB_RenderDebugUtils() // set uniforms gl_genericShader->SetUniform_AlphaTest( GLS_ATEST_NONE ); - SetUniform_ColorModulateColorGen( gl_genericShader, colorGen_t::CGEN_VERTEX, alphaGen_t::AGEN_VERTEX ); + SetUniform_ColorModulateColorGen( gl_genericShader, colorGen_t::CGEN_VERTEX, alphaGen_t::AGEN_IDENTITY ); SetUniform_Color( gl_genericShader, Color::Black ); GL_State( GLS_DEFAULT ); @@ -2263,8 +2263,6 @@ static void RB_RenderDebugUtils() for ( y = 0; y < tr.world->lightGridBounds[ 1 ]; y++ ) { for ( x = 0; x < tr.world->lightGridBounds[ 0 ]; x++ ) { vec3_t origin; - Color::Color ambientColor; - Color::Color directedColor; vec3_t lightDir; VectorCopy( tr.world->lightGridOrigin, origin ); @@ -2277,8 +2275,19 @@ static void RB_RenderDebugUtils() continue; } - R_LightForPoint( origin, ambientColor.ToArray(), - directedColor.ToArray(), lightDir ); + // read out grid... + int gridIndex = x + tr.world->lightGridBounds[ 0 ] * ( y + tr.world->lightGridBounds[ 1 ] * z ); + const bspGridPoint1_t *gp1 = tr.world->lightGridData1 + gridIndex; + const bspGridPoint2_t *gp2 = tr.world->lightGridData2 + gridIndex; + Color::Color generalColor = Color::Adapt( gp1->color ); + float ambientScale = 2.0f * unorm8ToFloat( gp1->ambientPart ); + float directedScale = 2.0f - ambientScale; + Color::Color ambientColor = generalColor * ambientScale; + Color::Color directedColor = generalColor * directedScale; + lightDir[ 0 ] = snorm8ToFloat( gp2->direction[ 0 ] - 128 ); + lightDir[ 1 ] = snorm8ToFloat( gp2->direction[ 1 ] - 128 ); + lightDir[ 2 ] = snorm8ToFloat( gp2->direction[ 2 ] - 128 ); + VectorNegate( lightDir, lightDir ); length = 8; diff --git a/src/engine/renderer/tr_bsp.cpp b/src/engine/renderer/tr_bsp.cpp index d03844d401..865c5a2ec4 100644 --- a/src/engine/renderer/tr_bsp.cpp +++ b/src/engine/renderer/tr_bsp.cpp @@ -3400,7 +3400,7 @@ static void R_SetConstantColorLightGrid( const byte color[3] ) gridPoint2->direction[ 0 ] = floatToSnorm8(0.0f); gridPoint2->direction[ 1 ] = floatToSnorm8(0.0f); gridPoint2->direction[ 2 ] = floatToSnorm8(1.0f); - gridPoint2->unused = 0; + gridPoint2->isSet = 255; w->lightGridData1 = gridPoint1; w->lightGridData2 = gridPoint2; @@ -3411,7 +3411,11 @@ static void R_SetConstantColorLightGrid( const byte color[3] ) imageParams.wrapType = wrapTypeEnum_t::WT_EDGE_CLAMP; tr.lightGrid1Image = R_Create3DImage("", (const byte *)w->lightGridData1, w->lightGridBounds[ 0 ], w->lightGridBounds[ 1 ], w->lightGridBounds[ 2 ], imageParams ); - tr.lightGrid2Image = R_Create3DImage("", (const byte *)w->lightGridData2, w->lightGridBounds[ 0 ], w->lightGridBounds[ 1 ], w->lightGridBounds[ 2 ], imageParams ); + + if ( glConfig.deluxeMapping ) + { + tr.lightGrid2Image = R_Create3DImage("", (const byte *)w->lightGridData2, w->lightGridBounds[ 0 ], w->lightGridBounds[ 1 ], w->lightGridBounds[ 2 ], imageParams ); + } } /* @@ -3539,14 +3543,18 @@ void R_LoadLightGrid( lump_t *l ) tmpAmbient[ 2 ] = in->ambient[ 2 ]; tmpAmbient[ 3 ] = 255; + /* Make sure we don't change the (0, 0, 0) points because those are points in walls, + which we'll fill up by interpolating nearby points later */ + if ( tmpAmbient[ 0 ] == 0 && tmpAmbient[ 1 ] == 0 && tmpAmbient[ 2 ] == 0 ) + { + continue; + } + tmpDirected[ 0 ] = in->directed[ 0 ]; tmpDirected[ 1 ] = in->directed[ 1 ]; tmpDirected[ 2 ] = in->directed[ 2 ]; tmpDirected[ 3 ] = 255; - R_LinearizeLightingColorBytes( tmpAmbient ); - R_LinearizeLightingColorBytes( tmpDirected ); - R_ColorShiftLightingBytes( tmpAmbient ); R_ColorShiftLightingBytes( tmpDirected ); @@ -3556,55 +3564,53 @@ void R_LoadLightGrid( lump_t *l ) directedColor[ j ] = tmpDirected[ j ] * ( 1.0f / 255.0f ); } + if ( tr.worldLinearizeTexture ) + { + convertFromSRGB( ambientColor ); + convertFromSRGB( directedColor ); + } + const float forceAmbient = r_forceAmbient.Get(); if ( ambientColor[0] < forceAmbient && ambientColor[1] < forceAmbient && - ambientColor[2] < forceAmbient && - /* Make sure we don't change the (0, 0, 0) points because those are points in walls, - which we'll fill up by interpolating nearby points later */ - ( ambientColor[0] != 0 || - ambientColor[1] != 0 || - ambientColor[2] != 0 ) ) { + ambientColor[2] < forceAmbient ) + { VectorSet( ambientColor, forceAmbient, forceAmbient, forceAmbient ); } // standard spherical coordinates to cartesian coordinates conversion - - // decode X as cos( lat ) * sin( long ) - // decode Y as sin( lat ) * sin( long ) - // decode Z as cos( long ) - // RB: having a look in NormalToLatLong used by q3map2 shows the order of latLong + // Lng = 0 at (1,0,0), 90 at (0,1,0), etc., encoded in 8-bit sine table format + // Lat = 0 at (0,0,1) to 180 (0,0,-1), encoded in 8-bit sine table format + // (so the upper bit of lat is wasted) - // Lat = 0 at (1,0,0) to 360 (-1,0,0), encoded in 8-bit sine table format - // Lng = 0 at (0,0,1) to 180 (0,0,-1), encoded in 8-bit sine table format + lat = DEG2RAD( in->latLong[ 0 ] * ( 360.0f / 255.0f ) ); + lng = DEG2RAD( in->latLong[ 1 ] * ( 360.0f / 255.0f ) ); - lat = DEG2RAD( in->latLong[ 1 ] * ( 360.0f / 255.0f ) ); - lng = DEG2RAD( in->latLong[ 0 ] * ( 360.0f / 255.0f ) ); - - direction[ 0 ] = cosf( lat ) * sinf( lng ); - direction[ 1 ] = sinf( lat ) * sinf( lng ); - direction[ 2 ] = cosf( lng ); + direction[ 0 ] = cosf( lng ) * sinf( lat ); + direction[ 1 ] = sinf( lng ) * sinf( lat ); + direction[ 2 ] = cosf( lat ); // Pack data into an bspGridPoint gridPoint1->color[ 0 ] = floatToUnorm8( 0.5f * (ambientColor[ 0 ] + directedColor[ 0 ]) ); gridPoint1->color[ 1 ] = floatToUnorm8( 0.5f * (ambientColor[ 1 ] + directedColor[ 1 ]) ); gridPoint1->color[ 2 ] = floatToUnorm8( 0.5f * (ambientColor[ 2 ] + directedColor[ 2 ]) ); - // Avoid division-by-zero. float ambientLength = VectorLength(ambientColor); float directedLength = VectorLength(directedColor); float length = ambientLength + directedLength; - gridPoint1->ambientPart = length ? floatToUnorm8( ambientLength / length ) : 0; + gridPoint1->ambientPart = floatToUnorm8( ambientLength / length ); gridPoint2->direction[0] = 128 + floatToSnorm8( direction[ 0 ] ); gridPoint2->direction[1] = 128 + floatToSnorm8( direction[ 1 ] ); gridPoint2->direction[2] = 128 + floatToSnorm8( direction[ 2 ] ); - gridPoint2->unused = 0; + gridPoint2->isSet = 255; } // fill in gridpoints with zero light (samples in walls) to avoid // darkening of objects near walls + // FIXME: the interpolation includes other interpolated data points so the + // result depends on iteration order gridPoint1 = w->lightGridData1; gridPoint2 = w->lightGridData2; @@ -3621,10 +3627,10 @@ void R_LoadLightGrid( lump_t *l ) from[ 0 ] = i - 1; to[ 0 ] = i + 1; - if( gridPoint1->color[ 0 ] || - gridPoint1->color[ 1 ] || - gridPoint1->color[ 2 ] ) + if ( gridPoint2->isSet ) + { continue; + } scale = R_InterpolateLightGrid( w, from, to, factors, ambientColor, directedColor, @@ -3645,7 +3651,7 @@ void R_LoadLightGrid( lump_t *l ) gridPoint2->direction[0] = 128 + floatToSnorm8(direction[0]); gridPoint2->direction[1] = 128 + floatToSnorm8(direction[1]); gridPoint2->direction[2] = 128 + floatToSnorm8(direction[2]); - gridPoint2->unused = 0; + gridPoint2->isSet = 255; } } } @@ -3657,7 +3663,11 @@ void R_LoadLightGrid( lump_t *l ) imageParams.wrapType = wrapTypeEnum_t::WT_EDGE_CLAMP; tr.lightGrid1Image = R_Create3DImage("", (const byte *)w->lightGridData1, w->lightGridBounds[ 0 ], w->lightGridBounds[ 1 ], w->lightGridBounds[ 2 ], imageParams ); - tr.lightGrid2Image = R_Create3DImage("", (const byte *)w->lightGridData2, w->lightGridBounds[ 0 ], w->lightGridBounds[ 1 ], w->lightGridBounds[ 2 ], imageParams ); + + if ( glConfig.deluxeMapping ) + { + tr.lightGrid2Image = R_Create3DImage("", (const byte *)w->lightGridData2, w->lightGridBounds[ 0 ], w->lightGridBounds[ 1 ], w->lightGridBounds[ 2 ], imageParams ); + } Log::Debug("%i light grid points created", w->numLightGridPoints ); } @@ -4618,14 +4628,13 @@ static void SetWorldLight() { tr.modelLight = lightMode_t::GRID; } - if ( glConfig.deluxeMapping ) { - // Enable deluxe mapping emulation if light direction grid is there. - if ( tr.lightGrid2Image ) { - // Game model surfaces use grid lighting, they don't have vertex light colors. - tr.modelDeluxe = deluxeMode_t::GRID; + // Enable deluxe mapping emulation if light direction grid is there. + if ( tr.lightGrid2Image ) { + ASSERT( glConfig.deluxeMapping ); + // Game model surfaces use grid lighting, they don't have vertex light colors. + tr.modelDeluxe = deluxeMode_t::GRID; - // Only game models use emulated deluxe map from light direction grid. - } + // Only game models use emulated deluxe map from light direction grid. } } @@ -4678,7 +4687,11 @@ static void SetConstUniforms() { } globalUBOProxy->SetUniform_LightGrid1Bindless( GL_BindToTMU( BIND_LIGHTGRID1, tr.lightGrid1Image ) ); - globalUBOProxy->SetUniform_LightGrid2Bindless( GL_BindToTMU( BIND_LIGHTGRID2, tr.lightGrid2Image ) ); + + if ( tr.lightGrid2Image ) + { + globalUBOProxy->SetUniform_LightGrid2Bindless( GL_BindToTMU( BIND_LIGHTGRID2, tr.lightGrid2Image ) ); + } } if ( glConfig.usingMaterialSystem ) { diff --git a/src/engine/renderer/tr_light.cpp b/src/engine/renderer/tr_light.cpp index b7e0740060..37130f697f 100644 --- a/src/engine/renderer/tr_light.cpp +++ b/src/engine/renderer/tr_light.cpp @@ -67,7 +67,7 @@ float R_InterpolateLightGrid( world_t *w, int from[3], int to[3], gp1 = w->lightGridData1 + x * gridStep[ 0 ] + y * gridStep[ 1 ] + z * gridStep[ 2 ]; gp2 = w->lightGridData2 + x * gridStep[ 0 ] + y * gridStep[ 1 ] + z * gridStep[ 2 ]; - if ( !( gp1->color[ 0 ] || gp1->color[ 1 ] || gp1->color[ 2 ]) ) + if ( !gp2->isSet ) { continue; // ignore samples in walls } diff --git a/src/engine/renderer/tr_local.h b/src/engine/renderer/tr_local.h index d67b4ba3c5..7aa78d50b6 100644 --- a/src/engine/renderer/tr_local.h +++ b/src/engine/renderer/tr_local.h @@ -1704,7 +1704,8 @@ enum struct bspGridPoint2_t { byte direction[3]; - byte unused; + // use 0 and 255 as the boolean values so that the image uploader skips the alpha component + byte isSet; }; struct AABB { @@ -3229,7 +3230,7 @@ void GLimp_LogComment_( std::string comment ); @param tetraVerts[0..2] are the ground vertices, tetraVerts[3] is the pyramid offset */ - void Tess_AddTetrahedron( vec4_t tetraVerts[ 4 ], const Color::Color& color ); + void Tess_AddTetrahedron( vec4_t tetraVerts[ 4 ], Color::Color color ); void Tess_AddCube( const vec3_t position, const vec3_t minSize, const vec3_t maxSize, const Color::Color& color ); void Tess_AddCubeWithNormals( const vec3_t position, const vec3_t minSize, const vec3_t maxSize, const Color::Color& color ); diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index 6874af2267..660bc5e0a6 100644 --- a/src/engine/renderer/tr_surface.cpp +++ b/src/engine/renderer/tr_surface.cpp @@ -399,12 +399,13 @@ void Tess_AddQuadStamp2WithNormals( vec4_t quadVerts[ 4 ], const Color::Color& c } // Defines ATTR_POSITION, ATTR_COLOR -void Tess_AddTetrahedron( vec4_t tetraVerts[ 4 ], const Color::Color& colorf ) +void Tess_AddTetrahedron( vec4_t tetraVerts[ 4 ], Color::Color colorf ) { int k; Tess_CheckOverflow( 12, 12 ); + colorf.Clamp(); Color::Color32Bit color = colorf; // ground triangle