2021-03-22 19:45:25 +00:00
/*
2020-05-16 15:40:30 +00:00
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Doom 3 BFG Edition GPL Source Code
Copyright ( C ) 1993 - 2012 id Software LLC , a ZeniMax Media company .
2021-03-17 20:01:43 +00:00
Copyright ( C ) 2020 - 2021 Robert Beckebans
2022-02-21 16:59:13 +00:00
Copyright ( C ) 2022 Stephen Pridham
2020-05-16 15:40:30 +00:00
This file is part of the Doom 3 BFG Edition GPL Source Code ( " Doom 3 BFG Edition Source Code " ) .
Doom 3 BFG Edition Source Code is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code . If not , see < http : //www.gnu.org/licenses/>.
In addition , the Doom 3 BFG Edition Source Code is also subject to certain additional terms . You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code . If not , please request a copy in writing from id Software at the address below .
If you have questions concerning this license or the applicable additional terms , you may contact in writing id Software LLC , c / o ZeniMax Media Inc . , Suite 120 , Rockville , Maryland 20850 USA .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# include "precompiled.h"
2021-07-29 13:42:02 +00:00
# pragma hdrstop
2020-05-16 15:40:30 +00:00
2021-03-17 20:01:43 +00:00
# include "../libs/mesa/format_r11g11b10f.h"
2020-05-16 15:40:30 +00:00
# include "RenderCommon.h"
2022-01-22 15:20:21 +00:00
# include "CmdlineProgressbar.h"
2021-05-01 14:28:05 +00:00
# include "../framework/Common_local.h" // commonLocal.WaitGameThread();
2020-05-16 15:40:30 +00:00
2022-02-21 16:59:13 +00:00
# if defined( USE_NVRHI )
2022-02-21 20:26:36 +00:00
# include <sys/DeviceManager.h>
extern DeviceManager * deviceManager ;
2022-02-21 16:59:13 +00:00
# endif
2020-05-16 15:40:30 +00:00
/*
= = = = = = = = = = = = =
R_SetEnvprobeDefViewEnvprobe
If the envprobeDef is not already on the viewEnvprobe list , create
a viewEnvprobe and add it to the list with an empty scissor rect .
= = = = = = = = = = = = =
*/
viewEnvprobe_t * R_SetEnvprobeDefViewEnvprobe ( RenderEnvprobeLocal * probe )
{
if ( probe - > viewCount = = tr . viewCount )
{
// already set up for this frame
return probe - > viewEnvprobe ;
}
probe - > viewCount = tr . viewCount ;
// add to the view light chain
viewEnvprobe_t * vProbe = ( viewEnvprobe_t * ) R_ClearedFrameAlloc ( sizeof ( * vProbe ) , FRAME_ALLOC_VIEW_LIGHT ) ;
vProbe - > envprobeDef = probe ;
// the scissorRect will be expanded as the envprobe bounds is accepted into visible portal chains
// and the scissor will be reduced in R_AddSingleEnvprobe based on the screen space projection
vProbe - > scissorRect . Clear ( ) ;
2020-05-17 11:29:18 +00:00
// copy data used by backend
// RB: this would normaly go into R_AddSingleEnvprobe
vProbe - > globalOrigin = probe - > parms . origin ;
2021-04-09 20:28:09 +00:00
vProbe - > globalProbeBounds = probe - > globalProbeBounds ;
vProbe - > inverseBaseProbeProject = probe - > inverseBaseProbeProject ;
2020-05-17 11:29:18 +00:00
2020-05-17 15:37:06 +00:00
//if( probe->irradianceImage->IsLoaded() )
{
vProbe - > irradianceImage = probe - > irradianceImage ;
}
//else
//{
// vProbe->irradianceImage = globalImages->defaultUACIrradianceCube;
//}
//if( probe->radianceImage->IsLoaded() )
{
vProbe - > radianceImage = probe - > radianceImage ;
}
//else
//{
// vProbe->radianceImage = globalImages->defaultUACRadianceCube;
//}
2020-05-16 15:40:30 +00:00
// link the view light
vProbe - > next = tr . viewDef - > viewEnvprobes ;
tr . viewDef - > viewEnvprobes = vProbe ;
probe - > viewEnvprobe = vProbe ;
return vProbe ;
}
/*
= = = = = = = = = = = = = = = =
CullEnvprobeByPortals
Return true if the light frustum does not intersect the current portal chain .
= = = = = = = = = = = = = = = =
*/
bool idRenderWorldLocal : : CullEnvprobeByPortals ( const RenderEnvprobeLocal * probe , const portalStack_t * ps )
{
if ( r_useLightPortalCulling . GetInteger ( ) = = 1 )
{
ALIGNTYPE16 frustumCorners_t corners ;
2021-04-09 20:28:09 +00:00
idRenderMatrix : : GetFrustumCorners ( corners , probe - > inverseBaseProbeProject , bounds_zeroOneCube ) ;
2020-05-16 15:40:30 +00:00
for ( int i = 0 ; i < ps - > numPortalPlanes ; i + + )
{
if ( idRenderMatrix : : CullFrustumCornersToPlane ( corners , ps - > portalPlanes [ i ] ) = = FRUSTUM_CULL_FRONT )
{
return true ;
}
}
}
return false ;
}
/*
= = = = = = = = = = = = = = = = = = =
AddAreaViewEnvprobes
This is the only point where lights get added to the viewLights list .
Any lights that are visible through the current portalStack will have their scissor rect updated .
= = = = = = = = = = = = = = = = = = =
*/
void idRenderWorldLocal : : AddAreaViewEnvprobes ( int areaNum , const portalStack_t * ps )
{
portalArea_t * area = & portalAreas [ areaNum ] ;
for ( areaReference_t * lref = area - > envprobeRefs . areaNext ; lref ! = & area - > envprobeRefs ; lref = lref - > areaNext )
{
RenderEnvprobeLocal * probe = lref - > envprobe ;
// debug tool to allow viewing of only one light at a time
if ( r_singleEnvprobe . GetInteger ( ) > = 0 & & r_singleEnvprobe . GetInteger ( ) ! = probe - > index )
{
continue ;
}
2020-12-21 14:47:41 +00:00
#if 0
2020-05-16 15:40:30 +00:00
// check for being closed off behind a door
// a light that doesn't cast shadows will still light even if it is behind a door
if ( r_useLightAreaCulling . GetBool ( ) //&& !envprobe->LightCastsShadows()
& & probe - > areaNum ! = - 1 & & ! tr . viewDef - > connectedAreas [ probe - > areaNum ] )
{
continue ;
}
// cull frustum
if ( CullEnvprobeByPortals ( probe , ps ) )
{
// we are culled out through this portal chain, but it might
// still be visible through others
continue ;
}
2020-12-21 14:47:41 +00:00
# endif
2020-05-16 15:40:30 +00:00
viewEnvprobe_t * vProbe = R_SetEnvprobeDefViewEnvprobe ( probe ) ;
// expand the scissor rect
vProbe - > scissorRect . Union ( ps - > rect ) ;
}
}
2020-05-17 15:37:06 +00:00
/*
= = = = = = = = = = = = = = = = = =
2021-03-17 20:01:43 +00:00
R_SampleCubeMapHDR
2020-05-17 15:37:06 +00:00
= = = = = = = = = = = = = = = = = =
*/
2021-04-08 16:36:23 +00:00
void R_SampleCubeMapHDR ( const idVec3 & dir , int size , byte * buffers [ 6 ] , float result [ 3 ] , float & u , float & v )
2020-05-17 15:37:06 +00:00
{
float adir [ 3 ] ;
int axis , x , y ;
adir [ 0 ] = fabs ( dir [ 0 ] ) ;
adir [ 1 ] = fabs ( dir [ 1 ] ) ;
adir [ 2 ] = fabs ( dir [ 2 ] ) ;
if ( dir [ 0 ] > = adir [ 1 ] & & dir [ 0 ] > = adir [ 2 ] )
{
axis = 0 ;
}
else if ( - dir [ 0 ] > = adir [ 1 ] & & - dir [ 0 ] > = adir [ 2 ] )
{
axis = 1 ;
}
else if ( dir [ 1 ] > = adir [ 0 ] & & dir [ 1 ] > = adir [ 2 ] )
{
axis = 2 ;
}
else if ( - dir [ 1 ] > = adir [ 0 ] & & - dir [ 1 ] > = adir [ 2 ] )
{
axis = 3 ;
}
else if ( dir [ 2 ] > = adir [ 1 ] & & dir [ 2 ] > = adir [ 2 ] )
{
axis = 4 ;
}
else
{
axis = 5 ;
}
2021-04-13 13:50:46 +00:00
float fx = ( dir * tr . cubeAxis [ axis ] [ 1 ] ) / ( dir * tr . cubeAxis [ axis ] [ 0 ] ) ;
float fy = ( dir * tr . cubeAxis [ axis ] [ 2 ] ) / ( dir * tr . cubeAxis [ axis ] [ 0 ] ) ;
2020-05-17 15:37:06 +00:00
fx = - fx ;
fy = - fy ;
x = size * 0.5 * ( fx + 1 ) ;
y = size * 0.5 * ( fy + 1 ) ;
if ( x < 0 )
{
x = 0 ;
}
else if ( x > = size )
{
x = size - 1 ;
}
if ( y < 0 )
{
y = 0 ;
}
else if ( y > = size )
{
y = size - 1 ;
}
2021-04-08 16:36:23 +00:00
u = x ;
v = y ;
2021-03-17 20:01:43 +00:00
// unpack RGBA8 to 3 floats
union
{
uint32 i ;
byte b [ 4 ] ;
} tmp ;
tmp . b [ 0 ] = buffers [ axis ] [ ( y * size + x ) * 4 + 0 ] ;
tmp . b [ 1 ] = buffers [ axis ] [ ( y * size + x ) * 4 + 1 ] ;
tmp . b [ 2 ] = buffers [ axis ] [ ( y * size + x ) * 4 + 2 ] ;
tmp . b [ 3 ] = buffers [ axis ] [ ( y * size + x ) * 4 + 3 ] ;
//uint32_t value = ( *( const uint32_t* )buffers[axis][( y * size + x ) * 4 + 0] );
r11g11b10f_to_float3 ( tmp . i , result ) ;
2020-05-17 15:37:06 +00:00
}
2021-04-13 15:51:16 +00:00
void R_SampleCubeMapHDR16F ( const idVec3 & dir , int size , halfFloat_t * buffers [ 6 ] , float result [ 3 ] , float & u , float & v )
{
float adir [ 3 ] ;
int axis , x , y ;
adir [ 0 ] = fabs ( dir [ 0 ] ) ;
adir [ 1 ] = fabs ( dir [ 1 ] ) ;
adir [ 2 ] = fabs ( dir [ 2 ] ) ;
if ( dir [ 0 ] > = adir [ 1 ] & & dir [ 0 ] > = adir [ 2 ] )
{
axis = 0 ;
}
else if ( - dir [ 0 ] > = adir [ 1 ] & & - dir [ 0 ] > = adir [ 2 ] )
{
axis = 1 ;
}
else if ( dir [ 1 ] > = adir [ 0 ] & & dir [ 1 ] > = adir [ 2 ] )
{
axis = 2 ;
}
else if ( - dir [ 1 ] > = adir [ 0 ] & & - dir [ 1 ] > = adir [ 2 ] )
{
axis = 3 ;
}
else if ( dir [ 2 ] > = adir [ 1 ] & & dir [ 2 ] > = adir [ 2 ] )
{
axis = 4 ;
}
else
{
axis = 5 ;
}
float fx = ( dir * tr . cubeAxis [ axis ] [ 1 ] ) / ( dir * tr . cubeAxis [ axis ] [ 0 ] ) ;
float fy = ( dir * tr . cubeAxis [ axis ] [ 2 ] ) / ( dir * tr . cubeAxis [ axis ] [ 0 ] ) ;
fx = - fx ;
fy = - fy ;
x = size * 0.5 * ( fx + 1 ) ;
y = size * 0.5 * ( fy + 1 ) ;
if ( x < 0 )
{
x = 0 ;
}
else if ( x > = size )
{
x = size - 1 ;
}
if ( y < 0 )
{
y = 0 ;
}
else if ( y > = size )
{
y = size - 1 ;
}
u = x ;
v = y ;
// unpack RGB16F to 3 floats
result [ 0 ] = F16toF32 ( buffers [ axis ] [ ( y * size + x ) * 3 + 0 ] ) ;
result [ 1 ] = F16toF32 ( buffers [ axis ] [ ( y * size + x ) * 3 + 1 ] ) ;
result [ 2 ] = F16toF32 ( buffers [ axis ] [ ( y * size + x ) * 3 + 2 ] ) ;
}
2020-05-17 15:37:06 +00:00
// http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
// To implement the Hammersley point set we only need an efficent way to implement the Van der Corput radical inverse phi2(i).
// Since it is in base 2 we can use some basic bit operations to achieve this.
// The brilliant book Hacker's Delight [warren01] provides us a a simple way to reverse the bits in a given 32bit integer. Using this, the following code then implements phi2(i)
// RB: radical inverse implementation from the Mitsuba PBR system
// Van der Corput radical inverse in base 2 with single precision
inline float RadicalInverse_VdC ( uint32_t n , uint32_t scramble = 0U )
{
/* Efficiently reverse the bits in 'n' using binary operations */
# if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2))) || defined(__clang__)
n = __builtin_bswap32 ( n ) ;
# else
n = ( n < < 16 ) | ( n > > 16 ) ;
n = ( ( n & 0x00ff00ff ) < < 8 ) | ( ( n & 0xff00ff00 ) > > 8 ) ;
# endif
n = ( ( n & 0x0f0f0f0f ) < < 4 ) | ( ( n & 0xf0f0f0f0 ) > > 4 ) ;
n = ( ( n & 0x33333333 ) < < 2 ) | ( ( n & 0xcccccccc ) > > 2 ) ;
n = ( ( n & 0x55555555 ) < < 1 ) | ( ( n & 0xaaaaaaaa ) > > 1 ) ;
// Account for the available precision and scramble
n = ( n > > ( 32 - 24 ) ) ^ ( scramble & ~ - ( 1 < < 24 ) ) ;
return ( float ) n / ( float ) ( 1U < < 24 ) ;
}
// The ith point xi is then computed by
inline idVec2 Hammersley2D ( uint i , uint N )
{
return idVec2 ( float ( i ) / float ( N ) , RadicalInverse_VdC ( i ) ) ;
}
idVec3 ImportanceSampleGGX ( const idVec2 & Xi , const idVec3 & N , float roughness )
{
float a = roughness * roughness ;
// cosinus distributed direction (Z-up or tangent space) from the hammersley point xi
float Phi = 2 * idMath : : PI * Xi . x ;
float cosTheta = idMath : : Sqrt ( ( 1 - Xi . y ) / ( 1 + ( a * a - 1 ) * Xi . y ) ) ;
float sinTheta = idMath : : Sqrt ( 1 - cosTheta * cosTheta ) ;
idVec3 H ;
H . x = sinTheta * idMath : : Cos ( Phi ) ;
H . y = sinTheta * idMath : : Sin ( Phi ) ;
H . z = cosTheta ;
// rotate from tangent space to world space along N
idVec3 upVector = abs ( N . z ) < 0.999f ? idVec3 ( 0 , 0 , 1 ) : idVec3 ( 1 , 0 , 0 ) ;
idVec3 tangentX = upVector . Cross ( N ) ;
tangentX . Normalize ( ) ;
idVec3 tangentY = N . Cross ( tangentX ) ;
idVec3 sampleVec = tangentX * H . x + tangentY * H . y + N * H . z ;
sampleVec . Normalize ( ) ;
return sampleVec ;
}
float Geometry_SchlickGGX ( float NdotV , float roughness )
{
// note that we use a different k for IBL
float a = roughness ;
float k = ( a * a ) / 2.0 ;
float nom = NdotV ;
float denom = NdotV * ( 1.0 - k ) + k ;
return nom / denom ;
}
float Geometry_Smith ( idVec3 N , idVec3 V , idVec3 L , float roughness )
{
float NdotV = Max ( ( N * V ) , 0.0f ) ;
float NdotL = Max ( ( N * L ) , 0.0f ) ;
float ggx2 = Geometry_SchlickGGX ( NdotV , roughness ) ;
float ggx1 = Geometry_SchlickGGX ( NdotL , roughness ) ;
return ggx1 * ggx2 ;
}
idVec2 IntegrateBRDF ( float NdotV , float roughness , int sampleCount )
{
idVec3 V ;
V . x = sqrt ( 1.0 - NdotV * NdotV ) ;
V . y = 0.0 ;
V . z = NdotV ;
float A = 0.0 ;
float B = 0.0 ;
idVec3 N ( 0.0f , 0.0f , 1.0f ) ;
for ( int i = 0 ; i < sampleCount ; + + i )
{
// generates a sample vector that's biased towards the
// preferred alignment direction (importance sampling).
idVec2 Xi = Hammersley2D ( i , sampleCount ) ;
idVec3 H = ImportanceSampleGGX ( Xi , N , roughness ) ;
idVec3 L = ( 2.0 * ( V * H ) * H - V ) ;
L . Normalize ( ) ;
float NdotL = Max ( L . z , 0.0f ) ;
float NdotH = Max ( H . z , 0.0f ) ;
float VdotH = Max ( ( V * H ) , 0.0f ) ;
if ( NdotL > 0.0 )
{
float G = Geometry_Smith ( N , V , L , roughness ) ;
float G_Vis = ( G * VdotH ) / ( NdotH * NdotV ) ;
float Fc = idMath : : Pow ( 1.0 - VdotH , 5.0 ) ;
A + = ( 1.0 - Fc ) * G_Vis ;
B + = Fc * G_Vis ;
}
}
A / = float ( sampleCount ) ;
B / = float ( sampleCount ) ;
return idVec2 ( A , B ) ;
}
2020-11-14 15:04:03 +00:00
// Compute normalized oct coord, mapping top left of top left pixel to (-1,-1)
2021-04-22 22:21:22 +00:00
idVec2 NormalizedOctCoord ( int x , int y , const int probeWithBorderSide )
2020-11-14 15:04:03 +00:00
{
2021-04-22 22:21:22 +00:00
#if 0
// 1 pixel border
const int margin = 1 ;
int probeSideLength = Max ( 2 , probeWithBorderSide - ( margin * 2 ) ) ;
idVec2 octFragCoord ;
octFragCoord . x = idMath : : ClampInt ( 0 , probeSideLength - 1 , x - margin ) ;
octFragCoord . y = idMath : : ClampInt ( 0 , probeSideLength - 1 , y - margin ) ;
return ( idVec2 ( octFragCoord ) ) * ( 2.0f / float ( probeSideLength ) ) - idVec2 ( 1.0f , 1.0f ) ;
# else
2020-11-14 15:04:03 +00:00
2021-04-22 22:21:22 +00:00
const int margin = 2 ;
// RB: FIXME - margin * 2 is wrong but looks better
2021-04-27 12:15:11 +00:00
// figure out why
2021-04-22 22:21:22 +00:00
int probeSideLength = Max ( 2 , probeWithBorderSide - ( margin * 2 ) ) ;
2020-11-14 15:04:03 +00:00
idVec2 octFragCoord = idVec2 ( ( x - margin ) % probeWithBorderSide , ( y - margin ) % probeWithBorderSide ) ;
2021-03-22 19:45:25 +00:00
2020-11-14 15:04:03 +00:00
// Add back the half pixel to get pixel center normalized coordinates
return ( idVec2 ( octFragCoord ) + idVec2 ( 0.5f , 0.5f ) ) * ( 2.0f / float ( probeSideLength ) ) - idVec2 ( 1.0f , 1.0f ) ;
2021-04-22 22:21:22 +00:00
# endif
2020-11-14 15:04:03 +00:00
}
2021-04-27 12:15:11 +00:00
static inline idVec2 NormalizedOctCoordNoBorder ( int x , int y , const int probeWithBorderSide )
{
int probeSideLength = probeWithBorderSide ;
idVec2 octFragCoord = idVec2 ( x % probeWithBorderSide , y % probeWithBorderSide ) ;
// Add back the half pixel to get pixel center normalized coordinates
return ( idVec2 ( octFragCoord ) + idVec2 ( 0.5f , 0.5f ) ) * ( 2.0f / float ( probeSideLength ) ) - idVec2 ( 1.0f , 1.0f ) ;
}
2020-05-17 15:37:06 +00:00
/*
2021-04-08 16:36:23 +00:00
static inline float LatLongTexelArea ( const idVec2i & pos , const idVec2i & imageSize )
{
idVec2 uv0 ;
uv0 . x = pos . x / imageSize . x ;
uv0 . y = pos . y / imageSize . y ;
2020-05-17 15:37:06 +00:00
2021-04-08 16:36:23 +00:00
idVec2 uv1 ;
uv1 . x = ( pos . x + 1 ) / imageSize . x ;
uv1 . y = ( pos . y + 1 ) / imageSize . y ;
2020-05-17 15:37:06 +00:00
2021-04-08 16:36:23 +00:00
float theta0 = idMath : : PI * ( uv0 . x * 2.0f - 1.0f ) ;
float theta1 = idMath : : PI * ( uv1 . x * 2.0f - 1.0f ) ;
2020-05-17 15:37:06 +00:00
2021-04-08 16:36:23 +00:00
float phi0 = idMath : : PI * ( uv0 . y - 0.5f ) ;
float phi1 = idMath : : PI * ( uv1 . y - 0.5f ) ;
2020-12-21 14:47:41 +00:00
2021-04-08 16:36:23 +00:00
return abs ( theta1 - theta0 ) * abs ( sin ( phi1 ) - sin ( phi0 ) ) ;
}
2021-03-22 19:45:25 +00:00
2020-05-17 15:37:06 +00:00
2021-04-08 16:36:23 +00:00
static inline idVec2 CartesianToLatLongTexcoord ( const idVec3 & p )
{
// http://gl.ict.usc.edu/Data/HighResProbes
2020-05-17 15:37:06 +00:00
2021-04-08 16:36:23 +00:00
float u = ( 1.0f + idMath : : ATan ( p . x , - p . z ) / idMath : : PI ) ;
float v = idMath : : ACos ( p . y ) / idMath : : PI ;
2021-03-22 19:45:25 +00:00
2021-04-08 16:36:23 +00:00
return idVec2 ( u * 0.5f , v ) ;
}
*/
2020-05-17 15:37:06 +00:00
2020-11-14 15:04:03 +00:00
2021-04-08 16:36:23 +00:00
/// http://www.mpia-hd.mpg.de/~mathar/public/mathar20051002.pdf
/// http://www.rorydriscoll.com/2012/01/15/cubemap-texel-solid-angle/
static inline float AreaElement ( float _x , float _y )
{
return atan2f ( _x * _y , sqrtf ( _x * _x + _y * _y + 1.0f ) ) ;
}
2020-11-14 15:04:03 +00:00
2021-04-08 16:36:23 +00:00
/// u and v should be center adressing and in [-1.0 + invSize.. 1.0 - invSize] range.
static inline float CubemapTexelSolidAngle ( float u , float v , float _invFaceSize )
{
// Specify texel area.
const float x0 = u - _invFaceSize ;
const float x1 = u + _invFaceSize ;
const float y0 = v - _invFaceSize ;
const float y1 = v + _invFaceSize ;
// Compute solid angle of texel area.
const float solidAngle = AreaElement ( x1 , y1 )
- AreaElement ( x0 , y1 )
- AreaElement ( x1 , y0 )
+ AreaElement ( x0 , y0 )
;
return solidAngle ;
}
2020-11-14 15:04:03 +00:00
2021-04-08 16:36:23 +00:00
static inline idVec3 MapXYSToDirection ( uint64 x , uint64 y , uint64 s , uint64 width , uint64 height )
{
float u = ( ( x + 0.5f ) / float ( width ) ) * 2.0f - 1.0f ;
float v = ( ( y + 0.5f ) / float ( height ) ) * 2.0f - 1.0f ;
v * = - 1.0f ;
idVec3 dir ( 0 , 0 , 0 ) ;
// +x, -x, +y, -y, +z, -z
switch ( s )
{
case 0 :
dir = idVec3 ( 1.0f , v , - u ) ;
break ;
case 1 :
dir = idVec3 ( - 1.0f , v , u ) ;
break ;
case 2 :
dir = idVec3 ( u , 1.0f , - v ) ;
break ;
case 3 :
dir = idVec3 ( u , - 1.0f , v ) ;
break ;
case 4 :
dir = idVec3 ( u , v , 1.0f ) ;
break ;
case 5 :
dir = idVec3 ( - u , v , - 1.0f ) ;
break ;
}
2020-11-14 15:04:03 +00:00
2021-04-08 16:36:23 +00:00
dir . Normalize ( ) ;
2021-03-22 19:45:25 +00:00
2021-04-08 16:36:23 +00:00
return dir ;
}
2020-11-14 15:04:03 +00:00
2021-04-08 16:36:23 +00:00
void CalculateIrradianceJob ( calcEnvprobeParms_t * parms )
{
2021-04-30 13:40:27 +00:00
halfFloat_t * buffers [ 6 ] ;
2020-11-14 15:04:03 +00:00
2021-04-08 16:36:23 +00:00
int start = Sys_Milliseconds ( ) ;
2020-11-14 15:04:03 +00:00
2021-04-08 16:36:23 +00:00
for ( int i = 0 ; i < 6 ; i + + )
{
2021-04-30 13:40:27 +00:00
buffers [ i ] = ( halfFloat_t * ) parms - > radiance [ i ] ;
2021-04-08 16:36:23 +00:00
}
2021-03-22 19:45:25 +00:00
2021-05-03 18:36:26 +00:00
const float invDstSize = 1.0f / float ( ENVPROBE_CAPTURE_SIZE ) ;
const idVec2i sourceImageSize ( ENVPROBE_CAPTURE_SIZE , ENVPROBE_CAPTURE_SIZE ) ;
2021-03-22 19:45:25 +00:00
2021-05-03 18:36:26 +00:00
CommandlineProgressBar progressBar ( R_CalculateUsedAtlasPixels ( parms - > outHeight ) , parms - > printWidth , parms - > printHeight ) ;
2021-04-08 16:36:23 +00:00
if ( parms - > printProgress )
{
progressBar . Start ( ) ;
}
2021-03-22 19:45:25 +00:00
2021-04-30 13:02:34 +00:00
// build L4 Spherical Harmonics from source image
SphericalHarmonicsT < idVec3 , 4 > shRadiance ;
2021-03-22 19:45:25 +00:00
2021-04-30 13:02:34 +00:00
for ( int i = 0 ; i < shSize ( 4 ) ; i + + )
2021-04-08 16:36:23 +00:00
{
shRadiance [ i ] . Zero ( ) ;
}
2021-03-22 19:45:25 +00:00
2021-04-08 16:36:23 +00:00
// build SH by iterating over all cubemap pixels
2020-11-14 15:04:03 +00:00
2021-04-08 16:36:23 +00:00
idVec4 dstRect = R_CalculateMipRect ( parms - > outHeight , 0 ) ;
2020-05-17 15:37:06 +00:00
2021-04-08 16:36:23 +00:00
for ( int side = 0 ; side < 6 ; side + + )
2020-05-17 15:37:06 +00:00
{
2021-04-08 16:36:23 +00:00
for ( int x = 0 ; x < sourceImageSize . x ; x + + )
2020-05-17 15:37:06 +00:00
{
2021-04-08 16:36:23 +00:00
for ( int y = 0 ; y < sourceImageSize . y ; y + + )
{
// convert UV coord to 3D direction
idVec3 dir = MapXYSToDirection ( x , y , side , sourceImageSize . x , sourceImageSize . y ) ;
2021-03-17 20:01:43 +00:00
2021-04-08 16:36:23 +00:00
float u , v ;
idVec3 radiance ;
2021-05-03 18:36:26 +00:00
R_SampleCubeMapHDR16F ( dir , ENVPROBE_CAPTURE_SIZE , buffers , & radiance [ 0 ] , u , v ) ;
2021-03-23 09:00:38 +00:00
2021-04-08 16:36:23 +00:00
//radiance = dir * 0.5 + idVec3( 0.5f, 0.5f, 0.5f );
2021-03-23 09:00:38 +00:00
2021-04-08 16:36:23 +00:00
// convert from [0 .. size-1] to [-1.0 + invSize .. 1.0 - invSize]
const float uu = 2.0f * ( u * invDstSize ) - 1.0f ;
const float vv = 2.0f * ( v * invDstSize ) - 1.0f ;
2020-05-17 15:37:06 +00:00
2021-04-08 16:36:23 +00:00
float texelArea = CubemapTexelSolidAngle ( uu , vv , invDstSize ) ;
2020-05-17 15:37:06 +00:00
2021-04-30 13:02:34 +00:00
const SphericalHarmonicsT < float , 4 > & sh = shEvaluate < 4 > ( dir ) ;
2021-03-23 20:53:21 +00:00
2021-04-08 16:36:23 +00:00
bool shValid = true ;
2021-04-30 13:02:34 +00:00
for ( int i = 0 ; i < shSize ( 4 ) ; i + + )
2021-04-08 16:36:23 +00:00
{
if ( IsNAN ( sh [ i ] ) )
{
shValid = false ;
break ;
}
}
2021-03-23 20:53:21 +00:00
2021-04-08 16:36:23 +00:00
if ( shValid )
{
shAddWeighted ( shRadiance , sh , radiance * texelArea ) ;
}
}
}
2020-05-17 15:37:06 +00:00
}
2021-03-23 20:53:21 +00:00
// reset image to black
for ( int x = 0 ; x < parms - > outWidth ; x + + )
2020-05-17 15:37:06 +00:00
{
2021-03-23 20:53:21 +00:00
for ( int y = 0 ; y < parms - > outHeight ; y + + )
{
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 0 ] = F32toF16 ( 0 ) ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 1 ] = F32toF16 ( 0 ) ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 2 ] = F32toF16 ( 0 ) ;
}
2020-05-17 15:37:06 +00:00
}
2021-03-23 20:53:21 +00:00
2021-05-03 18:36:26 +00:00
const int numMips = idMath : : BitsForInteger ( parms - > outHeight ) ;
2021-03-23 20:53:21 +00:00
for ( int mip = 0 ; mip < numMips ; mip + + )
2020-05-17 15:37:06 +00:00
{
2021-03-23 20:53:21 +00:00
idVec4 dstRect = R_CalculateMipRect ( parms - > outHeight , mip ) ;
for ( int x = dstRect . x ; x < ( dstRect . x + dstRect . z ) ; x + + )
{
for ( int y = dstRect . y ; y < ( dstRect . y + dstRect . w ) ; y + + )
{
idVec2 octCoord ;
if ( mip > 0 )
{
// move back to [0, 1] coords
2021-04-27 12:15:11 +00:00
octCoord = NormalizedOctCoordNoBorder ( x - dstRect . x , y - dstRect . y , dstRect . z ) ;
2021-03-23 20:53:21 +00:00
}
else
{
2021-04-27 12:15:11 +00:00
octCoord = NormalizedOctCoordNoBorder ( x , y , dstRect . z ) ;
2021-03-23 20:53:21 +00:00
}
// convert UV coord to 3D direction
2021-04-08 16:36:23 +00:00
idVec3 dir ;
2021-03-23 20:53:21 +00:00
2021-04-08 16:36:23 +00:00
dir . FromOctahedral ( octCoord ) ;
2021-03-23 20:53:21 +00:00
idVec3 outColor ( 0 , 0 , 0 ) ;
2021-04-08 16:36:23 +00:00
# if 1
2021-04-30 13:02:34 +00:00
// generate ambient colors by evaluating the L4 Spherical Harmonics
SphericalHarmonicsT < float , 4 > shDirection = shEvaluate < 4 > ( dir ) ;
2021-04-08 16:36:23 +00:00
2021-04-30 13:02:34 +00:00
idVec3 sampleIrradianceSh = shEvaluateDiffuse < idVec3 , 4 > ( shRadiance , dir ) / idMath : : PI ;
2021-04-08 16:36:23 +00:00
outColor [ 0 ] = Max ( 0.0f , sampleIrradianceSh . x ) ;
outColor [ 1 ] = Max ( 0.0f , sampleIrradianceSh . y ) ;
outColor [ 2 ] = Max ( 0.0f , sampleIrradianceSh . z ) ;
# else
// generate ambient colors using Monte Carlo method
2021-03-23 20:53:21 +00:00
for ( int s = 0 ; s < parms - > samples ; s + + )
{
idVec2 Xi = Hammersley2D ( s , parms - > samples ) ;
2021-04-08 16:36:23 +00:00
idVec3 H = ImportanceSampleGGX ( Xi , dir , 0.95f ) ;
2021-03-23 20:53:21 +00:00
2021-04-08 16:36:23 +00:00
float u , v ;
idVec3 radiance ;
R_SampleCubeMapHDR ( H , parms - > outHeight , buffers , & radiance [ 0 ] , u , v ) ;
2021-03-23 20:53:21 +00:00
2021-04-08 16:36:23 +00:00
outColor [ 0 ] + = radiance [ 0 ] ;
outColor [ 1 ] + = radiance [ 1 ] ;
outColor [ 2 ] + = radiance [ 2 ] ;
2021-03-23 20:53:21 +00:00
}
outColor [ 0 ] / = parms - > samples ;
outColor [ 1 ] / = parms - > samples ;
outColor [ 2 ] / = parms - > samples ;
2021-04-08 16:36:23 +00:00
# endif
//outColor = dir * 0.5 + idVec3( 0.5f, 0.5f, 0.5f );
2021-03-23 20:53:21 +00:00
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 0 ] = F32toF16 ( outColor [ 0 ] ) ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 1 ] = F32toF16 ( outColor [ 1 ] ) ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 2 ] = F32toF16 ( outColor [ 2 ] ) ;
2021-04-08 16:36:23 +00:00
if ( parms - > printProgress )
{
2021-05-01 13:59:46 +00:00
progressBar . Increment ( true ) ;
2021-04-08 16:36:23 +00:00
}
2021-03-23 20:53:21 +00:00
}
}
2020-05-17 15:37:06 +00:00
}
2021-03-23 20:53:21 +00:00
int end = Sys_Milliseconds ( ) ;
parms - > time = end - start ;
}
void CalculateRadianceJob ( calcEnvprobeParms_t * parms )
{
2021-04-30 13:40:27 +00:00
halfFloat_t * buffers [ 6 ] ;
2021-03-23 20:53:21 +00:00
int start = Sys_Milliseconds ( ) ;
for ( int i = 0 ; i < 6 ; i + + )
2020-05-17 15:37:06 +00:00
{
2021-04-30 13:40:27 +00:00
buffers [ i ] = ( halfFloat_t * ) parms - > radiance [ i ] ;
2020-05-17 15:37:06 +00:00
}
2021-03-23 20:53:21 +00:00
const float invDstSize = 1.0f / float ( parms - > outHeight ) ;
const int numMips = idMath : : BitsForInteger ( parms - > outHeight ) ;
2021-05-03 18:36:26 +00:00
const int numOctahedronMips = numMips - 3 ; // the last 3 mips are too low quality for filtering
2021-03-23 20:53:21 +00:00
2021-05-03 18:36:26 +00:00
CommandlineProgressBar progressBar ( R_CalculateUsedAtlasPixels ( parms - > outHeight ) , parms - > printWidth , parms - > printHeight ) ;
2021-04-08 16:36:23 +00:00
if ( parms - > printProgress )
{
progressBar . Start ( ) ;
}
// reset output image to black
2021-03-23 20:53:21 +00:00
for ( int x = 0 ; x < parms - > outWidth ; x + + )
2020-05-17 15:37:06 +00:00
{
2021-03-23 20:53:21 +00:00
for ( int y = 0 ; y < parms - > outHeight ; y + + )
{
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 0 ] = F32toF16 ( 0 ) ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 1 ] = F32toF16 ( 0 ) ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 2 ] = F32toF16 ( 0 ) ;
}
2020-05-17 15:37:06 +00:00
}
2021-05-03 18:36:26 +00:00
for ( int mip = 0 ; mip < numOctahedronMips ; mip + + )
2020-05-17 15:37:06 +00:00
{
2021-05-03 18:36:26 +00:00
float roughness = ( float ) mip / ( float ) ( numOctahedronMips - 1 ) ;
2021-03-23 20:53:21 +00:00
idVec4 dstRect = R_CalculateMipRect ( parms - > outHeight , mip ) ;
for ( int x = dstRect . x ; x < ( dstRect . x + dstRect . z ) ; x + + )
{
for ( int y = dstRect . y ; y < ( dstRect . y + dstRect . w ) ; y + + )
{
idVec2 octCoord ;
if ( mip > 0 )
{
// move back to [0, 1] coords
2021-04-27 12:15:11 +00:00
octCoord = NormalizedOctCoordNoBorder ( x - dstRect . x , y - dstRect . y , dstRect . z ) ;
2021-03-23 20:53:21 +00:00
}
else
{
2021-04-27 12:15:11 +00:00
octCoord = NormalizedOctCoordNoBorder ( x , y , dstRect . z ) ;
2021-03-23 20:53:21 +00:00
}
// convert UV coord to 3D direction
idVec3 N ;
N . FromOctahedral ( octCoord ) ;
idVec3 outColor ( 0 , 0 , 0 ) ;
// RB: Split Sum approximation explanation
// Epic Games makes a further approximation by assuming the view direction
// (and thus the specular reflection direction) to be equal to the output sample direction ωo.
// This translates itself to the following code:
const idVec3 R = N ;
const idVec3 V = R ;
float totalWeight = 0.0f ;
for ( int s = 0 ; s < parms - > samples ; s + + )
{
idVec2 Xi = Hammersley2D ( s , parms - > samples ) ;
idVec3 H = ImportanceSampleGGX ( Xi , N , roughness ) ;
idVec3 L = ( 2.0 * ( H * ( V * H ) ) - V ) ;
float NdotL = Max ( ( N * L ) , 0.0f ) ;
if ( NdotL > 0.0 )
{
float sample [ 3 ] ;
2021-04-08 16:36:23 +00:00
float u , v ;
2021-03-23 20:53:21 +00:00
2021-05-03 18:36:26 +00:00
R_SampleCubeMapHDR16F ( H , ENVPROBE_CAPTURE_SIZE , buffers , sample , u , v ) ;
2021-03-23 20:53:21 +00:00
outColor [ 0 ] + = sample [ 0 ] * NdotL ;
outColor [ 1 ] + = sample [ 1 ] * NdotL ;
outColor [ 2 ] + = sample [ 2 ] * NdotL ;
totalWeight + = NdotL ;
}
}
outColor [ 0 ] / = totalWeight ;
outColor [ 1 ] / = totalWeight ;
outColor [ 2 ] / = totalWeight ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 0 ] = F32toF16 ( outColor [ 0 ] ) ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 1 ] = F32toF16 ( outColor [ 1 ] ) ;
parms - > outBuffer [ ( y * parms - > outWidth + x ) * 3 + 2 ] = F32toF16 ( outColor [ 2 ] ) ;
2021-04-08 16:36:23 +00:00
if ( parms - > printProgress )
{
2021-05-01 13:59:46 +00:00
progressBar . Increment ( true ) ;
2021-04-08 16:36:23 +00:00
}
2021-03-23 20:53:21 +00:00
}
}
}
int end = Sys_Milliseconds ( ) ;
parms - > time = end - start ;
}
REGISTER_PARALLEL_JOB ( CalculateIrradianceJob , " CalculateIrradianceJob " ) ;
REGISTER_PARALLEL_JOB ( CalculateRadianceJob , " CalculateRadianceJob " ) ;
2021-04-30 13:40:27 +00:00
void R_MakeAmbientMap ( const char * baseName , byte * buffers [ 6 ] , const char * suffix , int outSize , bool specular , bool useThreads )
2021-03-23 20:53:21 +00:00
{
idStr fullname ;
renderView_t ref ;
viewDef_t primary ;
2021-04-30 13:40:27 +00:00
//byte* buffers[6];
//int width = 0, height = 0;
2021-03-23 20:53:21 +00:00
// set up the job
calcEnvprobeParms_t * jobParms = new calcEnvprobeParms_t ;
for ( int i = 0 ; i < 6 ; i + + )
{
2021-04-30 13:40:27 +00:00
jobParms - > radiance [ i ] = buffers [ i ] ;
2021-03-23 20:53:21 +00:00
}
2021-04-30 13:40:27 +00:00
jobParms - > freeRadiance = specular ? 1 : 0 ;
2021-03-23 20:53:21 +00:00
jobParms - > samples = 1000 ;
jobParms - > filename . Format ( " env/%s%s.exr " , baseName , suffix ) ;
2021-04-08 16:36:23 +00:00
jobParms - > printProgress = ! useThreads ;
2021-04-30 12:33:51 +00:00
jobParms - > printWidth = renderSystem - > GetWidth ( ) ;
jobParms - > printHeight = renderSystem - > GetHeight ( ) ;
2021-04-08 16:36:23 +00:00
2021-03-23 20:53:21 +00:00
jobParms - > outWidth = int ( outSize * 1.5f ) ;
jobParms - > outHeight = outSize ;
jobParms - > outBuffer = ( halfFloat_t * ) R_StaticAlloc ( idMath : : Ceil ( outSize * outSize * 3 * sizeof ( halfFloat_t ) * 1.5f ) , TAG_IMAGE ) ;
2021-04-13 13:50:46 +00:00
tr . envprobeJobs . Append ( jobParms ) ;
2021-03-23 20:53:21 +00:00
2021-04-08 10:41:40 +00:00
if ( useThreads )
2021-03-23 20:53:21 +00:00
{
2021-04-08 10:41:40 +00:00
if ( specular )
{
tr . envprobeJobList - > AddJob ( ( jobRun_t ) CalculateRadianceJob , jobParms ) ;
}
else
{
tr . envprobeJobList - > AddJob ( ( jobRun_t ) CalculateIrradianceJob , jobParms ) ;
}
2020-05-17 15:37:06 +00:00
}
else
{
2021-04-08 10:41:40 +00:00
if ( specular )
{
CalculateRadianceJob ( jobParms ) ;
}
else
{
CalculateIrradianceJob ( jobParms ) ;
}
2021-03-23 20:53:21 +00:00
}
2020-05-17 15:37:06 +00:00
}
2021-04-16 11:21:43 +00:00
CONSOLE_COMMAND ( bakeEnvironmentProbes , " Bake environment probes " , NULL )
2020-05-16 15:40:30 +00:00
{
idStr fullname ;
idStr baseName ;
renderView_t ref ;
2021-04-30 13:40:27 +00:00
int captureSize ;
2020-05-16 15:40:30 +00:00
2021-03-22 19:45:25 +00:00
if ( ! tr . primaryWorld )
{
common - > Printf ( " No primary world loaded. \n " ) ;
return ;
}
2021-04-30 12:33:51 +00:00
int sysWidth = renderSystem - > GetWidth ( ) ;
int sysHeight = renderSystem - > GetHeight ( ) ;
2021-03-23 20:53:21 +00:00
bool useThreads = true ;
2020-05-16 15:40:30 +00:00
baseName = tr . primaryWorld - > mapName ;
baseName . StripFileExtension ( ) ;
2021-05-03 18:36:26 +00:00
captureSize = ENVPROBE_CAPTURE_SIZE ;
2020-05-16 15:40:30 +00:00
if ( ! tr . primaryView )
{
common - > Printf ( " No primary view. \n " ) ;
return ;
}
2021-03-14 12:22:17 +00:00
const viewDef_t primary = * tr . primaryView ;
2020-05-16 15:40:30 +00:00
2020-05-17 15:37:06 +00:00
//--------------------------------------------
2021-04-30 13:40:27 +00:00
// CONVOLVE CUBEMAPS
2020-05-17 15:37:06 +00:00
//--------------------------------------------
2021-05-01 14:28:05 +00:00
// make sure the game / draw thread has completed
commonLocal . WaitGameThread ( ) ;
glConfig . nativeScreenWidth = captureSize ;
glConfig . nativeScreenHeight = captureSize ;
// disable scissor, so we don't need to adjust all those rects
r_useScissor . SetBool ( false ) ;
// RB: this really sucks but prevents a crash I couldn't track down
extern idCVar r_useParallelAddModels ;
extern idCVar r_useParallelAddShadows ;
extern idCVar r_useParallelAddLights ;
r_useParallelAddModels . SetBool ( false ) ;
r_useParallelAddShadows . SetBool ( false ) ;
r_useParallelAddLights . SetBool ( false ) ;
// discard anything currently on the list (this triggers SwapBuffers)
tr . SwapCommandBuffers ( NULL , NULL , NULL , NULL , NULL , NULL ) ;
tr . takingEnvprobe = true ;
2021-05-02 12:29:01 +00:00
int totalProcessedProbes = 0 ;
int totalStart = Sys_Milliseconds ( ) ;
for ( int i = 0 ; i < tr . primaryWorld - > envprobeDefs . Num ( ) ; i + + )
{
RenderEnvprobeLocal * def = tr . primaryWorld - > envprobeDefs [ i ] ;
if ( def = = NULL )
{
continue ;
}
totalProcessedProbes + + ;
}
2021-04-30 13:40:27 +00:00
2021-10-12 19:08:59 +00:00
idLib : : Printf ( " Deleting old probes... \n " ) ;
fullname . Format ( " env/%s " , baseName . c_str ( ) ) ;
idFileList * files = fileSystem - > ListFilesTree ( fullname , " envprobe*.exr " , true ) ;
for ( int i = 0 ; i < files - > GetNumFiles ( ) ; i + + )
{
idLib : : Printf ( " deleting old envprobe data '%s' \n " , files - > GetFile ( i ) ) ;
fileSystem - > RemoveFile ( files - > GetFile ( i ) ) ;
}
fileSystem - > FreeFileList ( files ) ;
2021-05-02 12:29:01 +00:00
idLib : : Printf ( " Shooting %i environment probes... \n " , totalProcessedProbes ) ;
2021-05-01 14:28:05 +00:00
2021-05-02 12:29:01 +00:00
CommandlineProgressBar progressBar ( totalProcessedProbes , sysWidth , sysHeight ) ;
2021-05-01 14:28:05 +00:00
progressBar . Start ( ) ;
2021-05-03 18:36:26 +00:00
int start = Sys_Milliseconds ( ) ;
2020-05-16 15:40:30 +00:00
for ( int i = 0 ; i < tr . primaryWorld - > envprobeDefs . Num ( ) ; i + + )
{
RenderEnvprobeLocal * def = tr . primaryWorld - > envprobeDefs [ i ] ;
if ( def = = NULL )
{
continue ;
}
2021-04-30 13:40:27 +00:00
byte * buffers [ 6 ] ;
for ( int j = 0 ; j < 6 ; j + + )
2020-05-16 15:40:30 +00:00
{
ref = primary . renderView ;
2021-03-14 14:59:02 +00:00
ref . rdflags = RDF_NOAMBIENT | RDF_IRRADIANCE ;
2020-05-16 15:40:30 +00:00
ref . fov_x = ref . fov_y = 90 ;
ref . vieworg = def - > parms . origin ;
2021-04-13 13:50:46 +00:00
ref . viewaxis = tr . cubeAxis [ j ] ;
2021-03-14 14:59:02 +00:00
2021-05-01 14:28:05 +00:00
#if 0
2021-04-30 13:40:27 +00:00
byte * float16FRGB = tr . CaptureRenderToBuffer ( captureSize , captureSize , & ref ) ;
2021-05-01 14:28:05 +00:00
# else
glConfig . nativeScreenWidth = captureSize ;
glConfig . nativeScreenHeight = captureSize ;
int pix = captureSize * captureSize ;
const int bufferSize = pix * 3 * 2 ;
byte * float16FRGB = ( byte * ) R_StaticAlloc ( bufferSize ) ;
// discard anything currently on the list
tr . SwapCommandBuffers ( NULL , NULL , NULL , NULL , NULL , NULL ) ;
// build commands to render the scene
tr . primaryWorld - > RenderScene ( & ref ) ;
// finish off these commands
const emptyCommand_t * cmd = tr . SwapCommandBuffers ( NULL , NULL , NULL , NULL , NULL , NULL ) ;
// issue the commands to the GPU
tr . RenderCommandBuffers ( cmd ) ;
// discard anything currently on the list (this triggers SwapBuffers)
tr . SwapCommandBuffers ( NULL , NULL , NULL , NULL , NULL , NULL ) ;
# if defined(USE_VULKAN)
// TODO
2022-03-22 10:16:44 +00:00
# elif defined(USE_NVRHI)
// TODO
2021-05-01 14:28:05 +00:00
# else
glFinish ( ) ;
glReadBuffer ( GL_BACK ) ;
globalFramebuffers . envprobeFBO - > Bind ( ) ;
2021-05-03 18:36:26 +00:00
glPixelStorei ( GL_PACK_ROW_LENGTH , ENVPROBE_CAPTURE_SIZE ) ;
2021-05-01 14:28:05 +00:00
glReadPixels ( 0 , 0 , captureSize , captureSize , GL_RGB , GL_HALF_FLOAT , float16FRGB ) ;
R_VerticalFlipRGB16F ( float16FRGB , captureSize , captureSize ) ;
Framebuffer : : Unbind ( ) ;
# endif
# endif
2021-04-30 13:40:27 +00:00
buffers [ j ] = float16FRGB ;
2020-05-16 15:40:30 +00:00
}
2021-04-30 13:40:27 +00:00
2021-05-01 14:28:05 +00:00
tr . takingEnvprobe = false ;
progressBar . Increment ( true ) ;
tr . takingEnvprobe = true ;
2021-10-12 19:08:59 +00:00
int areaNum = tr . primaryWorld - > PointInArea ( def - > parms . origin ) ;
idVec3 point = def - > parms . origin ;
point . SnapInt ( ) ;
fullname . Format ( " %s/area%i_envprobe_%i_%i_%i " , baseName . c_str ( ) , areaNum , int ( point . x ) , int ( point . y ) , int ( point . z ) ) ;
fullname . ReplaceChar ( ' - ' , ' _ ' ) ;
2021-04-30 13:40:27 +00:00
// create 2 jobs
2021-05-03 18:36:26 +00:00
R_MakeAmbientMap ( fullname . c_str ( ) , buffers , " _amb " , IRRADIANCE_OCTAHEDRON_SIZE , false , useThreads ) ;
R_MakeAmbientMap ( fullname . c_str ( ) , buffers , " _spec " , RADIANCE_OCTAHEDRON_SIZE , true , useThreads ) ;
2020-05-16 15:40:30 +00:00
}
2021-05-03 18:36:26 +00:00
int end = Sys_Milliseconds ( ) ;
2021-05-01 14:28:05 +00:00
tr . takingEnvprobe = false ;
2021-04-30 12:33:51 +00:00
// restore the original resolution, same as "vid_restart"
glConfig . nativeScreenWidth = sysWidth ;
glConfig . nativeScreenHeight = sysHeight ;
R_SetNewMode ( false ) ;
2020-05-16 15:40:30 +00:00
2021-05-01 14:28:05 +00:00
r_useScissor . SetBool ( true ) ;
r_useParallelAddModels . SetBool ( true ) ;
r_useParallelAddShadows . SetBool ( true ) ;
r_useParallelAddLights . SetBool ( true ) ;
2021-05-08 09:34:12 +00:00
common - > Printf ( " captured environment probes %5.1f seconds \n \n " , ( end - start ) * 0.001f ) ;
2021-05-03 18:36:26 +00:00
2021-03-23 20:53:21 +00:00
if ( useThreads )
{
2021-05-01 14:28:05 +00:00
idLib : : Printf ( " Processing probes on all available cores... Please wait. \n " ) ;
common - > UpdateScreen ( false ) ;
common - > UpdateScreen ( false ) ;
2021-03-23 20:53:21 +00:00
//tr.envprobeJobList->Submit();
tr . envprobeJobList - > Submit ( NULL , JOBLIST_PARALLELISM_MAX_CORES ) ;
tr . envprobeJobList - > Wait ( ) ;
2021-04-08 10:41:40 +00:00
}
2021-03-23 20:53:21 +00:00
2021-04-13 13:50:46 +00:00
for ( int j = 0 ; j < tr . envprobeJobs . Num ( ) ; j + + )
2021-04-08 10:41:40 +00:00
{
2021-04-13 13:50:46 +00:00
calcEnvprobeParms_t * job = tr . envprobeJobs [ j ] ;
2021-03-23 20:53:21 +00:00
2021-04-08 10:41:40 +00:00
R_WriteEXR ( job - > filename , ( byte * ) job - > outBuffer , 3 , job - > outWidth , job - > outHeight , " fs_basepath " ) ;
2021-03-23 20:53:21 +00:00
2021-04-08 10:41:40 +00:00
common - > Printf ( " %s convolved in %5.1f seconds \n \n " , job - > filename . c_str ( ) , job - > time * 0.001f ) ;
2021-03-23 20:53:21 +00:00
2021-04-30 13:40:27 +00:00
if ( job - > freeRadiance > 0 )
2021-04-08 10:41:40 +00:00
{
2021-04-30 13:40:27 +00:00
for ( int i = 0 ; i < 6 ; i + + )
2021-03-23 20:53:21 +00:00
{
2021-04-30 13:40:27 +00:00
if ( job - > radiance [ i ] )
{
Mem_Free ( job - > radiance [ i ] ) ;
}
2021-03-23 20:53:21 +00:00
}
}
2021-04-08 10:41:40 +00:00
Mem_Free ( job - > outBuffer ) ;
delete job ;
2020-05-17 15:37:06 +00:00
}
2020-12-21 14:47:41 +00:00
2021-04-13 13:50:46 +00:00
tr . envprobeJobs . Clear ( ) ;
2021-04-08 10:41:40 +00:00
2021-05-02 12:29:01 +00:00
int totalEnd = Sys_Milliseconds ( ) ;
2021-04-22 22:21:22 +00:00
2022-02-21 16:59:13 +00:00
# if defined( USE_NVRHI )
nvrhi : : CommandListHandle commandList = deviceManager - > GetDevice ( ) - > createCommandList ( ) ;
2022-02-21 20:26:36 +00:00
2022-02-21 16:59:13 +00:00
commandList - > open ( ) ;
# endif
2021-04-22 22:21:22 +00:00
//--------------------------------------------
// LOAD CONVOLVED OCTAHEDRONS INTO THE GPU
//--------------------------------------------
for ( int i = 0 ; i < tr . primaryWorld - > envprobeDefs . Num ( ) ; i + + )
{
RenderEnvprobeLocal * def = tr . primaryWorld - > envprobeDefs [ i ] ;
if ( def = = NULL )
{
continue ;
}
2022-02-21 16:59:13 +00:00
def - > irradianceImage - > Reload ( true , commandList ) ;
def - > radianceImage - > Reload ( true , commandList ) ;
2021-04-22 22:21:22 +00:00
}
2021-05-02 12:29:01 +00:00
2022-02-21 16:59:13 +00:00
# if defined( USE_NVRHI )
commandList - > close ( ) ;
deviceManager - > GetDevice ( ) - > executeCommandList ( commandList ) ;
# endif
2021-05-02 12:29:01 +00:00
idLib : : Printf ( " ---------------------------------- \n " ) ;
idLib : : Printf ( " Processed %i light probes \n " , totalProcessedProbes ) ;
2021-05-04 14:16:08 +00:00
common - > Printf ( " Baked SH irradiance and GGX mip maps in %5.1f seconds \n \n " , ( totalEnd - totalStart ) / ( 1000.0f ) ) ;
2020-05-16 15:40:30 +00:00
}
2021-03-22 19:45:25 +00:00
CONSOLE_COMMAND ( makeBrdfLUT , " make a GGX BRDF lookup table " , NULL )
{
int outSize = 256 ;
int width = 0 , height = 0 ;
//if( args.Argc() != 2 )
//{
// common->Printf( "USAGE: makeBrdfLut [size]\n" );
// return;
//}
//if( args.Argc() == 2 )
//{
// outSize = atoi( args.Argv( 1 ) );
//}
// resample with hemispherical blending
int samples = 1024 ;
int ldrBufferSize = outSize * outSize * 4 ;
byte * ldrBuffer = ( byte * ) Mem_Alloc ( ldrBufferSize , TAG_TEMP ) ;
int hdrBufferSize = outSize * outSize * 2 * sizeof ( halfFloat_t ) ;
halfFloat_t * hdrBuffer = ( halfFloat_t * ) Mem_Alloc ( hdrBufferSize , TAG_TEMP ) ;
2021-04-30 12:33:51 +00:00
int sysWidth = renderSystem - > GetWidth ( ) ;
int sysHeight = renderSystem - > GetHeight ( ) ;
CommandlineProgressBar progressBar ( outSize * outSize , sysWidth , sysHeight ) ;
2021-03-22 19:45:25 +00:00
int start = Sys_Milliseconds ( ) ;
for ( int x = 0 ; x < outSize ; x + + )
{
float NdotV = ( x + 0.5f ) / outSize ;
for ( int y = 0 ; y < outSize ; y + + )
{
float roughness = ( y + 0.5f ) / outSize ;
idVec2 output = IntegrateBRDF ( NdotV , roughness , samples ) ;
ldrBuffer [ ( y * outSize + x ) * 4 + 0 ] = byte ( output . x * 255 ) ;
ldrBuffer [ ( y * outSize + x ) * 4 + 1 ] = byte ( output . y * 255 ) ;
ldrBuffer [ ( y * outSize + x ) * 4 + 2 ] = 0 ;
ldrBuffer [ ( y * outSize + x ) * 4 + 3 ] = 255 ;
halfFloat_t half1 = F32toF16 ( output . x ) ;
halfFloat_t half2 = F32toF16 ( output . y ) ;
hdrBuffer [ ( y * outSize + x ) * 2 + 0 ] = half1 ;
hdrBuffer [ ( y * outSize + x ) * 2 + 1 ] = half2 ;
//hdrBuffer[( y * outSize + x ) * 4 + 2] = 0;
//hdrBuffer[( y * outSize + x ) * 4 + 3] = 1;
2021-05-01 13:59:46 +00:00
progressBar . Increment ( true ) ;
2021-03-22 19:45:25 +00:00
}
}
idStr fullname = " env/_brdfLut.png " ;
idLib : : Printf ( " writing %s \n " , fullname . c_str ( ) ) ;
R_WritePNG ( fullname , ldrBuffer , 4 , outSize , outSize , true , " fs_basepath " ) ;
//R_WriteEXR( "env/_brdfLut.exr", hdrBuffer, 4, outSize, outSize, "fs_basepath" );
idFileLocal headerFile ( fileSystem - > OpenFileWrite ( " env/Image_brdfLut.h " , " fs_basepath " ) ) ;
static const char * intro = R " (
# ifndef BRDFLUT_TEX_H
# define BRDFLUT_TEX_H
# define BRDFLUT_TEX_WIDTH 256
# define BRDFLUT_TEX_HEIGHT 256
# define BRDFLUT_TEX_PITCH (BRDFLUT_TEX_WIDTH * 2)
# define BRDFLUT_TEX_SIZE (BRDFLUT_TEX_WIDTH * BRDFLUT_TEX_PITCH)
// Stored in R16G16F format
static const unsigned char brfLutTexBytes [ ] =
{
) " ;
headerFile - > Printf ( " %s \n " , intro ) ;
const byte * hdrBytes = ( const byte * ) hdrBuffer ;
for ( int i = 0 ; i < hdrBufferSize ; i + + )
{
byte b = hdrBytes [ i ] ;
if ( i < ( hdrBufferSize - 1 ) )
{
headerFile - > Printf ( " 0x%02hhx, " , b ) ;
}
else
{
headerFile - > Printf ( " 0x%02hhx " , b ) ;
}
if ( i % 12 = = 0 )
{
headerFile - > Printf ( " \n " ) ;
}
}
headerFile - > Printf ( " \n }; \n #endif \n " ) ;
int end = Sys_Milliseconds ( ) ;
common - > Printf ( " %s integrated in %5.1f seconds \n \n " , fullname . c_str ( ) , ( end - start ) * 0.001f ) ;
Mem_Free ( ldrBuffer ) ;
Mem_Free ( hdrBuffer ) ;
}
2021-03-26 09:13:42 +00:00
CONSOLE_COMMAND ( makeImageHeader , " load an image and turn it into a .h file " , NULL )
{
byte * buffer ;
int width = 0 , height = 0 ;
if ( args . Argc ( ) < 2 )
{
common - > Printf ( " USAGE: makeImageHeader filename [exportname] \n " ) ;
return ;
}
idStr filename = args . Argv ( 1 ) ;
R_LoadImage ( filename , & buffer , & width , & height , NULL , true , NULL ) ;
if ( ! buffer )
{
common - > Printf ( " loading %s failed. \n " , filename . c_str ( ) ) ;
return ;
}
filename . StripFileExtension ( ) ;
idStr exportname ;
if ( args . Argc ( ) = = 3 )
{
exportname . Format ( " Image_%s.h " , args . Argv ( 2 ) ) ;
}
else
{
exportname . Format ( " Image_%s.h " , filename . c_str ( ) ) ;
}
for ( int i = 0 ; i < exportname . Length ( ) ; i + + )
{
if ( exportname [ i ] = = ' / ' )
{
exportname [ i ] = ' _ ' ;
}
}
idFileLocal headerFile ( fileSystem - > OpenFileWrite ( exportname , " fs_basepath " ) ) ;
idStr uppername = exportname ;
uppername . ToUpper ( ) ;
for ( int i = 0 ; i < uppername . Length ( ) ; i + + )
{
if ( uppername [ i ] = = ' . ' )
{
uppername [ i ] = ' _ ' ;
}
}
headerFile - > Printf ( " #ifndef %s_TEX_H \n " , uppername . c_str ( ) ) ;
headerFile - > Printf ( " #define %s_TEX_H \n \n " , uppername . c_str ( ) ) ;
headerFile - > Printf ( " #define %s_TEX_WIDTH %i \n " , uppername . c_str ( ) , width ) ;
headerFile - > Printf ( " #define %s_TEX_HEIGHT %i \n \n " , uppername . c_str ( ) , height ) ;
2021-03-26 09:49:16 +00:00
headerFile - > Printf ( " static const unsigned char %s_Bytes[] = { \n " , uppername . c_str ( ) ) ;
2021-03-26 09:13:42 +00:00
int bufferSize = width * height * 4 ;
for ( int i = 0 ; i < bufferSize ; i + + )
{
byte b = buffer [ i ] ;
if ( i < ( bufferSize - 1 ) )
{
headerFile - > Printf ( " 0x%02hhx, " , b ) ;
}
else
{
headerFile - > Printf ( " 0x%02hhx " , b ) ;
}
if ( i % 12 = = 0 )
{
headerFile - > Printf ( " \n " ) ;
}
}
headerFile - > Printf ( " \n }; \n #endif \n " ) ;
Mem_Free ( buffer ) ;
}