Autospawn env probes in the center of BSP areas

This commit is contained in:
Robert Beckebans 2020-11-30 18:27:06 +01:00
parent 86ff6cea87
commit 2498a17149
15 changed files with 350 additions and 56 deletions

View file

@ -60,51 +60,6 @@ struct PS_OUT
// *INDENT-ON*
/** Efficient GPU implementation of the octahedral unit vector encoding from
Cigolle, Donow, Evangelakos, Mara, McGuire, Meyer,
A Survey of Efficient Representations for Independent Unit Vectors, Journal of Computer Graphics Techniques (JCGT), vol. 3, no. 2, 1-30, 2014
Available online http://jcgt.org/published/0003/02/01/
*/
float signNotZeroFloat( float k )
{
return ( k >= 0.0 ) ? 1.0 : -1.0;
}
float2 signNotZero( float2 v )
{
return float2( signNotZeroFloat( v.x ), signNotZeroFloat( v.y ) );
}
/** Assumes that v is a unit vector. The result is an octahedral vector on the [-1, +1] square. */
float2 octEncode( float3 v )
{
float l1norm = abs( v.x ) + abs( v.y ) + abs( v.z );
float2 oct = v.xy * ( 1.0 / l1norm );
if( v.z < 0.0 )
{
oct = ( 1.0 - abs( oct.yx ) ) * signNotZero( oct.xy );
}
return oct;
}
/** Returns a unit vector. Argument o is an octahedral vector packed via octEncode,
on the [-1, +1] square*/
float3 octDecode( float2 o )
{
float3 v = float3( o.x, o.y, 1.0 - abs( o.x ) - abs( o.y ) );
if( v.z < 0.0 )
{
v.xy = ( 1.0 - abs( v.yx ) ) * signNotZero( v.xy );
}
return normalize( v );
}
void main( PS_IN fragment, out PS_OUT result )
{
half4 bumpMap = tex2D( samp0, fragment.texcoord0.xy );

View file

@ -205,8 +205,49 @@ float4 LinearRGBToSRGB( float4 c )
#endif
}
/** Efficient GPU implementation of the octahedral unit vector encoding from
Cigolle, Donow, Evangelakos, Mara, McGuire, Meyer,
A Survey of Efficient Representations for Independent Unit Vectors, Journal of Computer Graphics Techniques (JCGT), vol. 3, no. 2, 1-30, 2014
Available online http://jcgt.org/published/0003/02/01/
*/
float signNotZeroFloat( float k )
{
return ( k >= 0.0 ) ? 1.0 : -1.0;
}
float2 signNotZero( float2 v )
{
return float2( signNotZeroFloat( v.x ), signNotZeroFloat( v.y ) );
}
/** Assumes that v is a unit vector. The result is an octahedral vector on the [-1, +1] square. */
float2 octEncode( float3 v )
{
float l1norm = abs( v.x ) + abs( v.y ) + abs( v.z );
float2 oct = v.xy * ( 1.0 / l1norm );
if( v.z < 0.0 )
{
oct = ( 1.0 - abs( oct.yx ) ) * signNotZero( oct.xy );
}
return oct;
}
/** Returns a unit vector. Argument o is an octahedral vector packed via octEncode,
on the [-1, +1] square*/
float3 octDecode( float2 o )
{
float3 v = float3( o.x, o.y, 1.0 - abs( o.x ) - abs( o.y ) );
if( v.z < 0.0 )
{
v.xy = ( 1.0 - abs( v.yx ) ) * signNotZero( v.xy );
}
return normalize( v );
}
// RB end

View file

@ -0,0 +1,63 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2020 Robert Beckebans
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 "global.inc.hlsl"
// *INDENT-OFF*
uniform sampler2D samp0 : register(s0); // texture 0 is octahedron cube map
struct PS_IN {
float4 position : VPOS;
float3 texcoord0 : TEXCOORD0_centroid;
float3 texcoord1 : TEXCOORD1_centroid;
float4 color : COLOR0;
};
struct PS_OUT {
float4 color : COLOR;
};
// *INDENT-ON*
void main( PS_IN fragment, out PS_OUT result )
{
float3 globalNormal = normalize( fragment.texcoord1 );
float3 globalEye = normalize( fragment.texcoord0 );
float3 reflectionVector = _float3( dot3( globalEye, globalNormal ) );
reflectionVector *= globalNormal;
reflectionVector = ( reflectionVector * 2.0f ) - globalEye;
float2 normalizedOctCoord = octEncode( reflectionVector );
float2 normalizedOctCoordZeroOne = ( normalizedOctCoord + float2( 1.0 ) ) * 0.5;
float4 envMap = tex2D( samp0, normalizedOctCoordZeroOne );
result.color = float4( sRGBToLinearRGB( envMap.xyz ), 1.0f ) * fragment.color;
}

View file

@ -0,0 +1,119 @@
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2020 Robert Beckebans
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 "global.inc.hlsl"
uniform matrices_ubo { float4 matrices[408]; };
// *INDENT-OFF*
struct VS_IN {
float4 position : POSITION;
float2 texcoord : TEXCOORD0;
float4 normal : NORMAL;
float4 tangent : TANGENT;
float4 color : COLOR0;
float4 color2 : COLOR1;
};
struct VS_OUT {
float4 position : POSITION;
float3 texcoord0 : TEXCOORD0;
float3 texcoord1 : TEXCOORD1;
float4 color : COLOR0;
};
// *INDENT-ON*
void main( VS_IN vertex, out VS_OUT result )
{
float4 vNormal = vertex.normal * 2.0 - 1.0;
#if defined( USE_GPU_SKINNING )
//--------------------------------------------------------------
// GPU transformation of the normal / binormal / bitangent
//
// multiplying with 255.1 give us the same result and is faster than floor( w * 255 + 0.5 )
//--------------------------------------------------------------
const float w0 = vertex.color2.x;
const float w1 = vertex.color2.y;
const float w2 = vertex.color2.z;
const float w3 = vertex.color2.w;
float4 matX, matY, matZ; // must be float4 for vec4
int joint = int( vertex.color.x * 255.1 * 3.0 );
matX = matrices[int( joint + 0 )] * w0;
matY = matrices[int( joint + 1 )] * w0;
matZ = matrices[int( joint + 2 )] * w0;
joint = int( vertex.color.y * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w1;
matY += matrices[int( joint + 1 )] * w1;
matZ += matrices[int( joint + 2 )] * w1;
joint = int( vertex.color.z * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w2;
matY += matrices[int( joint + 1 )] * w2;
matZ += matrices[int( joint + 2 )] * w2;
joint = int( vertex.color.w * 255.1 * 3.0 );
matX += matrices[int( joint + 0 )] * w3;
matY += matrices[int( joint + 1 )] * w3;
matZ += matrices[int( joint + 2 )] * w3;
float3 normal;
normal.x = dot3( matX, vNormal );
normal.y = dot3( matY, vNormal );
normal.z = dot3( matZ, vNormal );
normal = normalize( normal );
float4 modelPosition;
modelPosition.x = dot4( matX, vertex.position );
modelPosition.y = dot4( matY, vertex.position );
modelPosition.z = dot4( matZ, vertex.position );
modelPosition.w = 1.0;
#else
float4 modelPosition = vertex.position;
float4 normal = vNormal;
#endif
result.position.x = dot4( modelPosition, rpMVPmatrixX );
result.position.y = dot4( modelPosition, rpMVPmatrixY );
result.position.z = dot4( modelPosition, rpMVPmatrixZ );
result.position.w = dot4( modelPosition, rpMVPmatrixW );
float4 toEye = rpLocalViewOrigin - vertex.position;
result.texcoord0 = toEye.xyz;
result.texcoord1 = normal.xyz;
result.color = sRGBAToLinearRGBA( rpColor );
}

View file

@ -1257,6 +1257,71 @@ void idGameLocal::MapPopulate()
SetScriptFPS( com_engineHz_latched );
}
/*
===================
RB idGameLocal::PopulateEnvironmentProbes
===================
*/
void idGameLocal::PopulateEnvironmentProbes()
{
idEntity* ent;
// check if there are already environment probes defined by the artist
int numEnvprobes = 0;
for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() )
{
if( !ent->IsType( EnvironmentProbe::Type ) )
{
continue;
}
numEnvprobes++;
}
if( numEnvprobes > 0 )
{
return;
}
const idDict* envProbeDef = gameLocal.FindEntityDefDict( "env_probe", false );
if( !envProbeDef )
{
return;
}
// naive approach: place an env probe into the center of each BSP area
int numAreas = gameRenderWorld->NumAreas();
for( int i = 0 ; i < numAreas ; i++ )
{
idBounds areaBounds = gameRenderWorld->AreaBounds( i );
idVec3 point = areaBounds.GetCenter();
int areaNum = gameRenderWorld->PointInArea( point );
if( areaNum < 0 )
{
Warning( "PopulateEnvironmentProbes: location '%i' is not in a valid area\n", i );
continue;
}
idDict args;
args.Set( "classname", "env_probe" );
args.Set( "origin", point.ToString() );
gameLocal.SpawnEntityDef( args, &ent );
if( !ent )
{
gameLocal.Error( "Couldn't spawn 'env_probe'" );
}
//environmentProbes.Append( probe );
}
}
// RB end
/*
===================
idGameLocal::InitFromNewMap
@ -1294,6 +1359,9 @@ void idGameLocal::InitFromNewMap( const char* mapName, idRenderWorld* renderWorl
MapPopulate();
// RB
PopulateEnvironmentProbes();
mpGame.Reset();
mpGame.Precache();
@ -1564,7 +1632,6 @@ bool idGameLocal::InitFromSaveGame( const char* mapName, idRenderWorld* renderWo
savegame.RestoreObjects();
mpGame.Reset();
mpGame.Precache();
// free up any unused animations
@ -3990,7 +4057,6 @@ void idGameLocal::SpawnMapEntities()
{
common->UpdateLevelLoadPacifier();
mapEnt = mapFile->GetEntity( i );
args = mapEnt->epairs;

View file

@ -68,6 +68,7 @@ class idThread;
class idEditEntities;
class idLocationEntity;
class idMenuHandler_Shell;
class EnvironmentProbe; // RB
const int MAX_CLIENTS = MAX_PLAYERS;
const int MAX_CLIENTS_IN_PVS = MAX_CLIENTS >> 3;
@ -638,6 +639,8 @@ private:
idLocationEntity** locationEntities; // for location names, etc
idList<EnvironmentProbe*> environmentProbes; // RB
idCamera* camera;
const idMaterial* globalMaterial; // for overriding everything
@ -704,6 +707,9 @@ private:
void MapPopulate();
void MapClear( bool clearClients );
// RB: spawn environment probes if there aren't any by default
void PopulateEnvironmentProbes();
pvsHandle_t GetClientPVS( idPlayer* player, pvsType_t type );
void SetupPlayerPVS();
void FreePlayerPVS();

View file

@ -1714,7 +1714,7 @@ void idRenderBackend::DBG_ShowViewEnvprobes()
GL_State( GLS_DEFAULT | GLS_CULL_TWOSIDED );
renderProgManager.BindShader_Environment();
renderProgManager.BindShader_Octahedron();
int count = 0;
for( viewEnvprobe_t* vProbe = viewDef->viewEnvprobes; vProbe != NULL; vProbe = vProbe->next )

View file

@ -153,6 +153,9 @@ void idRenderProgManager::Init()
{ BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL, "interactionSM", "_parallel_PBR", BIT( LIGHT_PARALLEL ) | BIT( USE_PBR ), false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL_SKINNED, "interactionSM", "_parallel_skinned_PBR", BIT( USE_GPU_SKINNING ) | BIT( LIGHT_PARALLEL ) | BIT( USE_PBR ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_OCTAHEDRON, "octahedron", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
{ BUILTIN_OCTAHEDRON_SKINNED, "octahedron", "_skinned", BIT( USE_GPU_SKINNING ), true, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
// RB end
{ BUILTIN_ENVIRONMENT, "environment", "", 0, false, SHADER_STAGE_DEFAULT, LAYOUT_DRAW_VERT },
@ -269,6 +272,7 @@ void idRenderProgManager::Init()
renderProgs[builtinShaders[BUILTIN_SHADOW_DEBUG_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_FOG_SKINNED]].usesJoints = true;
// RB begin
renderProgs[builtinShaders[BUILTIN_OCTAHEDRON_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_IBL_SKINNED]].usesJoints = true;
renderProgs[builtinShaders[BUILTIN_AMBIENT_LIGHTING_IBL_PBR_SKINNED]].usesJoints = true;

View file

@ -449,6 +449,16 @@ public:
{
BindShader_Builtin( BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL_SKINNED );
}
void BindShader_Octahedron()
{
BindShader_Builtin( BUILTIN_OCTAHEDRON );
}
void BindShader_OctahedronSkinned()
{
BindShader_Builtin( BUILTIN_OCTAHEDRON_SKINNED );
}
// RB end
void BindShader_Environment()
@ -755,6 +765,9 @@ private:
BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_POINT_SKINNED,
BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL,
BUILTIN_PBR_INTERACTION_SHADOW_MAPPING_PARALLEL_SKINNED,
BUILTIN_OCTAHEDRON,
BUILTIN_OCTAHEDRON_SKINNED,
// RB end
BUILTIN_ENVIRONMENT,
BUILTIN_ENVIRONMENT_SKINNED,

View file

@ -1241,6 +1241,23 @@ exitPortal_t idRenderWorldLocal::GetPortal( int areaNum, int portalNum )
return ret;
}
/*
===================
RB: idRenderWorldLocal::AreaBounds
===================
*/
idBounds idRenderWorldLocal::AreaBounds( int areaNum ) const
{
if( areaNum < 0 || areaNum > numPortalAreas )
{
common->Error( "idRenderWorld::GetPortal: areaNum > numAreas" );
}
portalArea_t* area = &portalAreas[areaNum];
return area->globalBounds;
}
/*
===============
idRenderWorldLocal::PointInAreaNum

View file

@ -415,6 +415,9 @@ public:
// returns one portal from an area
virtual exitPortal_t GetPortal( int areaNum, int portalNum ) = 0;
// RB: returns the AABB of a BSP area
virtual idBounds AreaBounds( int areaNum ) const = 0;
//-------------- Tracing -----------------
// Checks a ray trace against any gui surfaces in an entity, returning the

View file

@ -760,19 +760,19 @@ ENVPROBE DEFS
void R_DeriveEnvprobeData( RenderEnvprobeLocal* probe )
{
idStr basename = tr.primaryWorld->mapName;
idStr basename = probe->world->mapName;
basename.StripFileExtension();
idStr fullname;
int probeIndex = tr.primaryWorld->envprobeDefs.Num() - 1;
int probeIndex = probe->world->envprobeDefs.Num() - 1;
// TODO get preconvolved cubemaps
fullname.Format( "env/%s/envprobe%i_amb", basename.c_str(), probeIndex );
probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_HIGHQUALITY_CUBE, CF_NATIVE );
probe->irradianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_LOOKUP_TABLE_RGB1, CF_2D );
fullname.Format( "env/%s/envprobe%i_spec", basename.c_str(), probeIndex );
probe->radianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_HIGHQUALITY_CUBE, CF_NATIVE );
probe->radianceImage = globalImages->ImageFromFile( fullname, TF_DEFAULT, TR_CLAMP, TD_LOOKUP_TABLE_RGB1, CF_2D );
// ------------------------------------
// compute the light projection matrix

View file

@ -672,7 +672,7 @@ void R_MakeAmbientMap( const char* baseName, const char* suffix, int outSize, fl
bool pacifier = true;
// resample with hemispherical blending
int samples = 1000;
int samples = 1;
byte* outBuffer = ( byte* )_alloca( outSize * outSize * 4 );
@ -780,7 +780,7 @@ void R_MakeAmbientMap( const char* baseName, const char* suffix, int outSize, fl
total[2] += result[2];
}
#if 0
#if 1
outBuffer[( y * outSize + x ) * 4 + 0] = total[0] / samples;
outBuffer[( y * outSize + x ) * 4 + 1] = total[1] / samples;
outBuffer[( y * outSize + x ) * 4 + 2] = total[2] / samples;

View file

@ -1089,7 +1089,6 @@ void idRenderWorldLocal::AddWorldModelEntities()
{
common->UpdateLevelLoadPacifier();
idRenderEntityLocal* def = new( TAG_RENDER_ENTITY ) idRenderEntityLocal;
// try and reuse a free spot
@ -1141,7 +1140,11 @@ void idRenderWorldLocal::AddWorldModelEntities()
R_DeriveEntityData( def );
AddEntityRefToArea( def, &portalAreas[i] );
portalArea_t* area = &portalAreas[i];
AddEntityRefToArea( def, area );
// RB: remember BSP area AABB for quick lookup later
area->globalBounds = def->globalReferenceBounds;
}
}

View file

@ -65,6 +65,9 @@ typedef struct portalArea_s
int areaNum;
int connectedAreaNum[NUM_PORTAL_ATTRIBUTES]; // if two areas have matching connectedAreaNum, they are
// not separated by a portal with the apropriate PS_BLOCK_* blockingBits
idBounds globalBounds; // RB: AABB of the BSP area used for light grid density
int viewCount; // set by R_FindViewLightsAndEntities
portal_t* portals; // never changes after load
areaReference_t entityRefs; // head/tail of doubly linked list, may change
@ -143,6 +146,7 @@ public:
virtual int BoundsInAreas( const idBounds& bounds, int* areas, int maxAreas ) const;
virtual int NumPortalsInArea( int areaNum );
virtual exitPortal_t GetPortal( int areaNum, int portalNum );
virtual idBounds AreaBounds( int areaNum ) const; // RB
virtual guiPoint_t GuiTrace( qhandle_t entityHandle, const idVec3 start, const idVec3 end ) const;
virtual bool ModelTrace( modelTrace_t& trace, qhandle_t entityHandle, const idVec3& start, const idVec3& end, const float radius ) const;