mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-29 07:32:25 +00:00
Merge pull request #578 from DanielGibson/soft-particles
Implement Soft Particles (and capturing depth buffer), based on old The Dark Mod code
This commit is contained in:
commit
d3b2794c24
22 changed files with 787 additions and 87 deletions
15
Changelog.md
15
Changelog.md
|
@ -14,6 +14,11 @@ Note: Numbers starting with a "#" like #330 refer to the bugreport with that num
|
|||
It can also be opened while in the game, which then is paused (if Single Player) but still visible,
|
||||
so the effect of most graphics settings can be seen immediately.
|
||||
Needs SDL2 and C++11.
|
||||
* "Soft" Particles (that don't "cut" into geometry but fade smoothly), based on code from The Dark Mod
|
||||
2.04. Can be enabled/disabled with `r_useSoftParticles`, is applied automatically for all appropriate
|
||||
particles (view-aligned, using additive or alpha blending and not too small)
|
||||
* `r_enableDepthCapture`: Enable capturing depth buffer to texture, needed for the soft particles.
|
||||
Can be used in custom materials by using the `"_currentDepth"` texture
|
||||
* Replaced dependency on (external) zlib with integrated [miniz](https://github.com/richgel999/miniz)
|
||||
* HighDPI/Retina support
|
||||
* Allow inverted mouse look (horizontally, vertically or both) with `m_invertLook`
|
||||
|
@ -28,7 +33,15 @@ Note: Numbers starting with a "#" like #330 refer to the bugreport with that num
|
|||
downscaling by OpenAL's output limiter
|
||||
* If `r_windowResizable` is set, the dhewm3 window (when in windowed mode..) can be freely resized.
|
||||
Needs SDL2; with 2.0.5 and newer it's applied immediately, otherwise when creating the window.
|
||||
|
||||
* If switching between fullscreen and windowed mode or similar changes causes issues (like
|
||||
[here](https://github.com/dhewm/dhewm3/issues/587#issuecomment-2205807989)), you can set
|
||||
`r_vidRestartAlwaysFull 1`, so (again) a full `vid_restart` is done, instead of the partial one
|
||||
which *usually* suffices for just changing the resolution or fullscreen state. If you run into that
|
||||
issue (probably a driver bug), you'll probably also want to set `r_windowResizable 0`, because
|
||||
resizing the window that way also triggered the bug, and in that case no `vid_restart` is done at all
|
||||
* Fixed screenshots when using native Wayland (`SDL_VIDEODRIVER=wayland`)
|
||||
* If you enter the `map` command in the console, without any arguments, the current map name is printed
|
||||
* Support OpenGL debug contexts and messages (`GL_ARB_debug_output`). Can be enabled with `r_glDebugContext 1`. Changing that CVar requires a `vid_restart` (or set it as startup argument)
|
||||
|
||||
1.5.3 (2024-03-29)
|
||||
------------------------------------------------------------------------
|
||||
|
|
|
@ -210,6 +210,11 @@ This can be configured with the following CVars:
|
|||
at the end of each frame. Needed at least when using Wayland.
|
||||
`1`: do this, `0`: don't do it, `-1`: let dhewm3 decide (default)
|
||||
|
||||
- `r_useSoftParticles` Soften particle transitions when player walks through them or they cross solid geometry.
|
||||
Needs r_enableDepthCapture. Can slow down rendering! `1`: enable (default), `0`: disable
|
||||
- `r_enableDepthCapture` Enable capturing depth buffer to texture. `0`: disable, `1`: enable,
|
||||
`-1`: enable automatically (if soft particles are enabled; the default).
|
||||
This can be used in custom materials with the "_currentDepth" texture.
|
||||
- `r_useCarmacksReverse` Use Z-Fail ("Carmack's Reverse") when rendering shadows (default `1`)
|
||||
- `r_useStencilOpSeparate` Use glStencilOpSeparate() (if available) when rendering shadow (default `1`)
|
||||
- `r_scaleMenusTo43` Render full-screen menus in 4:3 by adding black bars on the left/right if necessary (default `1`)
|
||||
|
|
|
@ -322,9 +322,9 @@ if(D3_COMPILER_IS_GCC_OR_CLANG)
|
|||
set(CMAKE_C_FLAGS_DEBUG "-g -ggdb -D_DEBUG -O0")
|
||||
set(CMAKE_C_FLAGS_DEBUGALL "-g -ggdb -D_DEBUG")
|
||||
set(CMAKE_C_FLAGS_PROFILE "-g -ggdb -D_DEBUG -O1 -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O2 -fno-math-errno -fno-trapping-math -fomit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -ggdb -O2 -fno-math-errno -fno-trapping-math -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS_MINSIZEREL "-Os -fno-math-errno -fno-trapping-math -fomit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS_RELEASE "-O2 -fno-math-errno -fno-trapping-math -ffinite-math-only -fomit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g -ggdb -O2 -fno-math-errno -fno-trapping-math -ffinite-math-only -fno-omit-frame-pointer")
|
||||
set(CMAKE_C_FLAGS_MINSIZEREL "-Os -fno-math-errno -fno-trapping-math -ffinite-math-only -fomit-frame-pointer")
|
||||
|
||||
set(CMAKE_CXX_FLAGS_DEBUGALL ${CMAKE_C_FLAGS_DEBUGALL})
|
||||
set(CMAKE_CXX_FLAGS_PROFILE ${CMAKE_C_FLAGS_PROFILE})
|
||||
|
|
|
@ -409,6 +409,13 @@ idParticleStage *idDeclParticle::ParseParticleStage( idLexer &src ) {
|
|||
stage->gravity = src.ParseFloat();
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "softeningRadius" ) ) { // #3878 soft particles
|
||||
common->Warning( "Particle %s from %s has stage with \"softeningRadius\" attribute, which is currently ignored (we soften all suitable particles)\n",
|
||||
this->GetName(), src.GetFileName() );
|
||||
// DG: disable this for now to avoid breaking the game ABI
|
||||
//stage->softeningRadius = src.ParseFloat();
|
||||
continue;
|
||||
}
|
||||
|
||||
src.Error( "unknown token %s\n", token.c_str() );
|
||||
}
|
||||
|
@ -733,6 +740,9 @@ idParticleStage::idParticleStage( void ) {
|
|||
hidden = false;
|
||||
boundsExpansion = 0.0f;
|
||||
bounds.Clear();
|
||||
// DG: disable softeningRadius for now to avoid breaking the game ABI
|
||||
// (will always behave like if softeningRadius = -2.0f)
|
||||
//softeningRadius = -2.0f; // -2 means "auto" - #3878 soft particles
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -806,6 +816,8 @@ void idParticleStage::Default() {
|
|||
randomDistribution = true;
|
||||
entityColor = false;
|
||||
cycleMsec = ( particleLife + deadTime ) * 1000;
|
||||
// DG: disable softeningRadius for now to avoid breaking game ABI
|
||||
//softeningRadius = -2.0f; // -2 means "auto" - #3878 soft particles
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -193,6 +193,18 @@ public:
|
|||
float boundsExpansion; // user tweak to fix poorly calculated bounds
|
||||
|
||||
idBounds bounds; // derived
|
||||
|
||||
/* Soft particles -- SteveL #3878
|
||||
-2.0 is the value at initialization, meaning no user specification: "auto".
|
||||
-1.0 means no change to old system: suppress soft particles, but allow modelDepthhack if specified.
|
||||
0 means disable all softening for this stage, including modelDepthHack.
|
||||
+ve value means apply soft particle effect, allowing overdraw up to the specified depth.
|
||||
This is more flexible even when not using soft particles, as modelDepthHack
|
||||
can be turned off for specific stages to stop them poking through walls.
|
||||
*/
|
||||
// DG: disable this for now because it breaks the game DLL's ABI (re-enable in dhewm3 1.6.0 or 2.0.0)
|
||||
// (this header is part of the SDK)
|
||||
//float softeningRadius;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1142,6 +1142,14 @@ static void InitBindingEntries()
|
|||
bindingEntries.Append( BindingEntry( bet ) );
|
||||
}
|
||||
|
||||
// player.def defines, in player_base, used by player_doommarine and player_doommarine_mp (and player_doommarine_ctf),
|
||||
// "def_weapon0" "weapon_fists", "def_weapon1" "weapon_pistol" etc
|
||||
// => get all those definitions (up to MAX_WEAPONS=16) from Player, and then
|
||||
// get the entities for the corresponding keys ("weapon_fists" etc),
|
||||
// which should have an entry like "inv_name" "Pistol" (could also be #str_00100207 though!)
|
||||
|
||||
// hardcorps uses: idCVar pm_character("pm_character", "0", CVAR_GAME | CVAR_BOOL, "Change Player character. 1 = Scarlet. 0 = Doom Marine");
|
||||
// but I guess (hope) they use the same weapons..
|
||||
const idDict* playerDict = GetEntityDefDict( "player_doommarine" );
|
||||
const idDict* playerDictMP = GetEntityDefDict( "player_doommarine_mp" );
|
||||
bool impulse27used = false;
|
||||
|
@ -1209,15 +1217,6 @@ static void InitBindingEntries()
|
|||
idStr impName = idStr::Format( "_impulse%d", i );
|
||||
bindingEntries.Append( BindingEntry( impName, impName ) );
|
||||
}
|
||||
|
||||
// player.def defines, in player_base, used by player_doommarine and player_doommarine_mp (and player_doommarine_ctf),
|
||||
// "def_weapon0" "weapon_fists", "def_weapon1" "weapon_pistol" etc
|
||||
// => get all those definitions (up to MAX_WEAPONS=16) from Player, and then
|
||||
// get the entities for the corresponding keys ("weapon_fists" etc),
|
||||
// which should have an entry like "inv_name" "Pistol" (could also be #str_00100207 though!)
|
||||
|
||||
// hardcorps uses: idCVar pm_character("pm_character", "0", CVAR_GAME | CVAR_BOOL, "Change Player character. 1 = Scarlet. 0 = Doom Marine");
|
||||
// but I guess (hope) they use the same weapons..
|
||||
}
|
||||
|
||||
// this initialization should be done every time the bindings tab is opened,
|
||||
|
@ -1666,8 +1665,31 @@ static CVarOption videoOptionsImmediately[] = {
|
|||
} ),
|
||||
CVarOption( "r_screenshotPngCompression", "Compression level for PNG screenshots", OT_INT, 0, 9 ),
|
||||
CVarOption( "r_screenshotJpgQuality", "Quality level for JPG screenshots", OT_INT, 1, 100 ),
|
||||
CVarOption( "r_useSoftParticles", []( idCVar& cvar ) {
|
||||
bool enable = cvar.GetBool();
|
||||
if ( ImGui::Checkbox( "Use Soft Particles", &enable ) ) {
|
||||
cvar.SetBool( enable );
|
||||
if ( enable && r_enableDepthCapture.GetInteger() == 0 ) {
|
||||
r_enableDepthCapture.SetInteger(-1);
|
||||
D3::ImGuiHooks::ShowWarningOverlay( "Capturing the Depth Buffer was disabled.\nEnabled it because soft particles need it!" );
|
||||
}
|
||||
}
|
||||
AddCVarOptionTooltips( cvar );
|
||||
} ),
|
||||
|
||||
CVarOption( "Advanced Options" ),
|
||||
CVarOption( "r_enableDepthCapture", []( idCVar& cvar ) {
|
||||
int sel = idMath::ClampInt( -1, 1, cvar.GetInteger() ) + 1; // +1 for -1..1 to 0..2
|
||||
if ( ImGui::Combo( "Capture Depth Buffer to Texture", &sel, "Auto (enable if needed for Soft Particles)\0Disabled\0Always Enabled\0" ) ) {
|
||||
--sel; // back to -1..1 from 0..2
|
||||
cvar.SetInteger( sel );
|
||||
if ( sel == 0 && r_useSoftParticles.GetBool() ) {
|
||||
r_useSoftParticles.SetBool( false );
|
||||
D3::ImGuiHooks::ShowWarningOverlay( "You disabled capturing the Depth Buffer.\nDisabling Soft Particles because they need the depth buffer texture." );
|
||||
}
|
||||
}
|
||||
AddCVarOptionTooltips( cvar );
|
||||
}),
|
||||
CVarOption( "r_skipNewAmbient", "Disable High Quality Special Effects", OT_BOOL ),
|
||||
CVarOption( "r_shadows", "Enable Shadows", OT_BOOL ),
|
||||
CVarOption( "r_skipSpecular", "Disable Specular", OT_BOOL ),
|
||||
|
@ -2262,7 +2284,7 @@ void DrawGameOptionsMenu()
|
|||
playerNameIso[40] = '\0'; // limit to 40 chars, like the original menu
|
||||
ui_nameVar->SetString( playerNameIso );
|
||||
// update the playerNameBuf to reflect the name as it is now: limited to 40 chars
|
||||
// and possibly containing '?' from non-translatable unicode chars
|
||||
// and possibly containing '!' from non-translatable unicode chars
|
||||
D3_ISO8859_1toUTF8( ui_nameVar->GetString(), playerNameBuf, sizeof(playerNameBuf) );
|
||||
} else {
|
||||
D3::ImGuiHooks::ShowWarningOverlay( "Player Name way too long (max 40 chars) or contains invalid UTF-8 encoding!" );
|
||||
|
|
|
@ -1911,7 +1911,7 @@ char * D3_UTF8toISO8859_1( const char *utf8str, char *isobuf, int isobufLen, cha
|
|||
else if ( invalidChar != 0 )
|
||||
buffer[i++] = invalidChar;
|
||||
} else if ((*str & 0xf0) == 0xe0) {
|
||||
// Unicode character between 0x0800 and 0xFFF => way out of range for ISO8859-1
|
||||
// Unicode character between 0x0800 and 0xFFFF => way out of range for ISO8859-1
|
||||
// so just validate and skip the input bytes
|
||||
if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL;
|
||||
if (*str == 0xed && str[1] > 0x9f) return NULL; // str[1] < 0x80 is checked below
|
||||
|
|
|
@ -183,7 +183,8 @@ public:
|
|||
|
||||
void CopyFramebuffer( int x, int y, int width, int height, bool useOversizedBuffer );
|
||||
|
||||
void CopyDepthbuffer( int x, int y, int width, int height );
|
||||
void CopyDepthbuffer( int x, int y, int width, int height, bool useOversizedBuffer );
|
||||
|
||||
|
||||
void UploadScratch( const byte *pic, int width, int height );
|
||||
|
||||
|
@ -417,6 +418,9 @@ public:
|
|||
idImage * specular2DTableImage; // 2D intensity texture with our specular function with variable specularity
|
||||
idImage * borderClampImage; // white inside, black outside
|
||||
|
||||
|
||||
idImage * currentDepthImage; // #3877. Allow shaders to access scene depth
|
||||
|
||||
//--------------------------------------------------------
|
||||
|
||||
idImage * AllocImage( const char *name );
|
||||
|
|
|
@ -388,6 +388,19 @@ static void R_RGBA8Image( idImage *image ) {
|
|||
TF_DEFAULT, false, TR_REPEAT, TD_HIGH_QUALITY );
|
||||
}
|
||||
|
||||
static void R_DepthImage( idImage *image ) {
|
||||
byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
|
||||
|
||||
memset( data, 0, sizeof( data ) );
|
||||
data[0][0][0] = 16;
|
||||
data[0][0][1] = 32;
|
||||
data[0][0][2] = 48;
|
||||
data[0][0][3] = 96;
|
||||
|
||||
image->GenerateImage( (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE,
|
||||
TF_NEAREST, false, TR_CLAMP, TD_HIGH_QUALITY );
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void R_RGB8Image( idImage *image ) {
|
||||
byte data[DEFAULT_SIZE][DEFAULT_SIZE][4];
|
||||
|
@ -1993,6 +2006,7 @@ void idImageManager::Init() {
|
|||
accumImage = ImageFromFunction("_accum", R_RGBA8Image );
|
||||
scratchCubeMapImage = ImageFromFunction("_scratchCubeMap", makeNormalizeVectorCubeMap );
|
||||
currentRenderImage = ImageFromFunction("_currentRender", R_RGBA8Image );
|
||||
currentDepthImage = ImageFromFunction( "_currentDepth", R_DepthImage ); // #3877. Allow shaders to access scene depth
|
||||
|
||||
cmdSystem->AddCommand( "reloadImages", R_ReloadImages_f, CMD_FL_RENDERER, "reloads images" );
|
||||
cmdSystem->AddCommand( "listImages", R_ListImages_f, CMD_FL_RENDERER, "lists images" );
|
||||
|
|
|
@ -1898,34 +1898,38 @@ CopyDepthbuffer
|
|||
This should just be part of copyFramebuffer once we have a proper image type field
|
||||
====================
|
||||
*/
|
||||
void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight ) {
|
||||
Bind();
|
||||
|
||||
void idImage::CopyDepthbuffer( int x, int y, int imageWidth, int imageHeight, bool useOversizedBuffer )
|
||||
{
|
||||
this->Bind();
|
||||
// if the size isn't a power of 2, the image must be increased in size
|
||||
int potWidth, potHeight;
|
||||
|
||||
potWidth = MakePowerOfTwo( imageWidth );
|
||||
potHeight = MakePowerOfTwo( imageHeight );
|
||||
|
||||
if ( uploadWidth != potWidth || uploadHeight != potHeight ) {
|
||||
GetDownsize( imageWidth, imageHeight );
|
||||
GetDownsize( potWidth, potHeight );
|
||||
// Ensure we are reading from the back buffer:
|
||||
qglReadBuffer( GL_BACK );
|
||||
// only resize if the current dimensions can't hold it at all,
|
||||
// otherwise subview renderings could thrash this
|
||||
if ( ( useOversizedBuffer && ( uploadWidth < potWidth || uploadHeight < potHeight ) ) || ( !useOversizedBuffer && ( uploadWidth != potWidth || uploadHeight != potHeight ) ) )
|
||||
{
|
||||
uploadWidth = potWidth;
|
||||
uploadHeight = potHeight;
|
||||
if ( potWidth == imageWidth && potHeight == imageHeight ) {
|
||||
qglCopyTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, x, y, imageWidth, imageHeight, 0 );
|
||||
} else {
|
||||
// we need to create a dummy image with power of two dimensions,
|
||||
// then do a qglCopyTexSubImage2D of the data we want
|
||||
qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
|
||||
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
|
||||
}
|
||||
// This bit runs once only at map start, because it tests whether the image is too small to hold the screen.
|
||||
// It resizes the texture to a power of two that can hold the screen,
|
||||
// and then subsequent captures to the texture put the depth component into the RGB channels
|
||||
qglTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL );
|
||||
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
|
||||
|
||||
} else {
|
||||
// otherwise, just subimage upload it so that drivers can tell we are going to be changing
|
||||
// it and don't try and do a texture compression or some other silliness
|
||||
qglCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight );
|
||||
}
|
||||
|
||||
// qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
||||
// qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
||||
qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
|
||||
qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
|
||||
|
||||
qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
||||
qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
||||
|
|
|
@ -1434,6 +1434,10 @@ void idMaterial::ParseStage( idLexer &src, const textureRepeat_t trpDefault ) {
|
|||
ss->drawStateBits |= GLS_DEPTHMASK;
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "ignoreDepth" ) ) { // Added in #3877.
|
||||
ss->drawStateBits |= GLS_DEPTHFUNC_ALWAYS;
|
||||
continue;
|
||||
}
|
||||
if ( !token.Icmp( "alphaTest" ) ) {
|
||||
ss->hasAlphaTest = true;
|
||||
ss->alphaTestRegister = ParseExpression( src );
|
||||
|
|
|
@ -302,8 +302,13 @@ public:
|
|||
virtual float DepthHack() const;
|
||||
virtual int Memory() const;
|
||||
|
||||
float SofteningRadius( const int stage ) const; // #3878 soft particles
|
||||
|
||||
private:
|
||||
void SetSofteningRadii(); // #3878 soft particles
|
||||
|
||||
const idDeclParticle * particleSystem;
|
||||
idList<float> softeningRadii; // #3878 soft particles
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -50,6 +50,7 @@ idRenderModelPrt::InitFromFile
|
|||
void idRenderModelPrt::InitFromFile( const char *fileName ) {
|
||||
name = fileName;
|
||||
particleSystem = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, fileName ) );
|
||||
SetSofteningRadii(); // # 3878
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -286,3 +287,67 @@ int idRenderModelPrt::Memory() const {
|
|||
|
||||
return total;
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::SetSofteningRadii
|
||||
|
||||
Calculate "depth" of each particle stage that represents a 3d volume, so the particle can
|
||||
be allowed to overdraw solid geometry by the right amount, and the particle "thickness" (visibility)
|
||||
can be adjusted by how much of it is visible in front of the background surface.
|
||||
|
||||
"depth" is by default 0.8 of the particle radius, and particles less than 2 units in size won't be softened.
|
||||
The particles that represent 3d volumes are the view-aligned ones. Others have depth set to 0.
|
||||
|
||||
Cache these values rather than calculate them for each stage every frame.
|
||||
Added for soft particles -- SteveL #3878.
|
||||
====================
|
||||
*/
|
||||
void idRenderModelPrt::SetSofteningRadii()
|
||||
{
|
||||
softeningRadii.AssureSize( particleSystem->stages.Num() );
|
||||
|
||||
for ( int i = 0; i < particleSystem->stages.Num(); ++i )
|
||||
{
|
||||
const idParticleStage* ps = particleSystem->stages[i];
|
||||
// DG: for now softeningRadius isn't configurable to avoid breaking the game DLL's ABI
|
||||
// => always behave like if ps->softeningRadius == -2, which means "auto"
|
||||
// (doesn't make a difference, so far only TDM particles set the softeningRadius)
|
||||
/* if ( ps->softeningRadius > -2.0f ) // User has specified a setting
|
||||
{
|
||||
softeningRadii[i] = ps->softeningRadius;
|
||||
}
|
||||
else */ if ( ps->orientation == POR_VIEW ) // Only view-aligned particle stages qualify for softening
|
||||
{
|
||||
float diameter = Max( ps->size.from, ps->size.to );
|
||||
float scale = Max( ps->aspect.from, ps->aspect.to );
|
||||
diameter *= Max( scale, 1.0f ); // aspect applies to 1 axis only. If it's < 1, the other axis will still be at scale 1
|
||||
if ( diameter > 2.0f ) // Particle is big enough to soften
|
||||
{
|
||||
softeningRadii[i] = diameter * 0.8f / 2.0f;
|
||||
}
|
||||
else // Particle is small. Disable both softening and modelDepthHack
|
||||
{
|
||||
softeningRadii[i] = 0.0f;
|
||||
}
|
||||
}
|
||||
else // Particle isn't view-aligned, and no user setting. Don't change anything.
|
||||
{
|
||||
softeningRadii[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
idRenderModelPrt::SofteningRadius
|
||||
|
||||
Return the max radius of the individual quads that make up this stage.
|
||||
Added for soft particles #3878.
|
||||
====================
|
||||
*/
|
||||
float idRenderModelPrt::SofteningRadius( const int stage ) const {
|
||||
assert( particleSystem );
|
||||
assert( stage > -1 && stage < softeningRadii.Num() );
|
||||
return softeningRadii[stage];
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ typedef struct glconfig_s {
|
|||
bool twoSidedStencilAvailable;
|
||||
bool textureNonPowerOfTwoAvailable;
|
||||
bool depthBoundsTestAvailable;
|
||||
bool glDebugOutputAvailable;
|
||||
|
||||
// GL framebuffer size, see also winWidth and winHeight
|
||||
int vidWidth, vidHeight; // passed to R_BeginFrame
|
||||
|
@ -95,6 +96,8 @@ typedef struct glconfig_s {
|
|||
bool shouldFillWindowAlpha;
|
||||
bool isWayland; // DG: for other wayland-specific hacks.. (does *not* detect XWayland!)
|
||||
|
||||
bool haveDebugContext;
|
||||
|
||||
// For some reason people decided that we need displays with ultra small pixels,
|
||||
// so everything rendered on them must be scaled up to be legible.
|
||||
// unfortunately, this bullshit feature was "improved" upon by deciding that the best
|
||||
|
|
|
@ -239,6 +239,13 @@ idCVar r_screenshotPngCompression("r_screenshotPngCompression", "3", CVAR_RENDER
|
|||
idCVar r_windowResizable("r_windowResizable", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Allow resizing (and maximizing) the window (needs SDL2; with 2.0.5 or newer it's applied immediately)" );
|
||||
idCVar r_vidRestartAlwaysFull( "r_vidRestartAlwaysFull", 0, CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Always do a full vid_restart (ignore 'partial' argument), e.g. when changing window size" );
|
||||
|
||||
// DG: for soft particles (ported from TDM)
|
||||
idCVar r_enableDepthCapture( "r_enableDepthCapture", "-1", CVAR_RENDERER | CVAR_INTEGER,
|
||||
"enable capturing depth buffer to texture. -1: enable automatically (if soft particles are enabled), 0: disable, 1: enable", -1, 1 ); // #3877
|
||||
idCVar r_useSoftParticles( "r_useSoftParticles", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Soften particle transitions when player walks through them or they cross solid geometry. Needs r_enableDepthCapture. Can slow down rendering!" ); // #3878
|
||||
|
||||
idCVar r_glDebugContext( "r_glDebugContext", "0", CVAR_RENDERER | CVAR_BOOL, "Enable OpenGL Debug context - requires vid_restart, needs SDL2" );
|
||||
|
||||
// define qgl functions
|
||||
#define QGLPROC(name, rettype, args) rettype (APIENTRYP q##name) args;
|
||||
#include "renderer/qgl_proc.h"
|
||||
|
@ -288,6 +295,9 @@ PFNGLDEPTHBOUNDSEXTPROC qglDepthBoundsEXT;
|
|||
// DG: couldn't find any extension for this, it's supported in GL2.0 and newer, incl OpenGL ES2.0
|
||||
PFNGLSTENCILOPSEPARATEPROC qglStencilOpSeparate;
|
||||
|
||||
// GL_ARB_debug_output
|
||||
PFNGLDEBUGMESSAGECALLBACKARBPROC qglDebugMessageCallbackARB;
|
||||
|
||||
// eez: This is a slight hack for letting us select the desired screenshot format in other functions
|
||||
// This is a hack to avoid adding another function parameter to idRenderSystem::TakeScreenshot(),
|
||||
// which would break the API of the dhewm3 SDK for mods.
|
||||
|
@ -296,6 +306,60 @@ PFNGLSTENCILOPSEPARATEPROC qglStencilOpSeparate;
|
|||
// it must set g_screenshotFormat accordingly before each call to TakeScreenshot().
|
||||
int g_screenshotFormat = -1;
|
||||
|
||||
enum {
|
||||
// Not all GL.h header know about GL_DEBUG_SEVERITY_NOTIFICATION_*.
|
||||
QGL_DEBUG_SEVERITY_NOTIFICATION = 0x826B
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback function for debug output.
|
||||
*/
|
||||
static void APIENTRY
|
||||
DebugCallback( GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar *message, const void *userParam )
|
||||
{
|
||||
const char* sourceStr = "Source: Unknown";
|
||||
const char* typeStr = "Type: Unknown";
|
||||
const char* severityStr = "Severity: Unknown";
|
||||
|
||||
switch (severity)
|
||||
{
|
||||
#define SVRCASE(X, STR) case GL_DEBUG_SEVERITY_ ## X ## _ARB : severityStr = STR; break;
|
||||
case QGL_DEBUG_SEVERITY_NOTIFICATION: return;
|
||||
SVRCASE(HIGH, "Severity: High")
|
||||
SVRCASE(MEDIUM, "Severity: Medium")
|
||||
SVRCASE(LOW, "Severity: Low")
|
||||
#undef SVRCASE
|
||||
}
|
||||
|
||||
switch (source)
|
||||
{
|
||||
#define SRCCASE(X) case GL_DEBUG_SOURCE_ ## X ## _ARB: sourceStr = "Source: " #X; break;
|
||||
SRCCASE(API);
|
||||
SRCCASE(WINDOW_SYSTEM);
|
||||
SRCCASE(SHADER_COMPILER);
|
||||
SRCCASE(THIRD_PARTY);
|
||||
SRCCASE(APPLICATION);
|
||||
SRCCASE(OTHER);
|
||||
#undef SRCCASE
|
||||
}
|
||||
|
||||
switch(type)
|
||||
{
|
||||
#define TYPECASE(X) case GL_DEBUG_TYPE_ ## X ## _ARB: typeStr = "Type: " #X; break;
|
||||
TYPECASE(ERROR);
|
||||
TYPECASE(DEPRECATED_BEHAVIOR);
|
||||
TYPECASE(UNDEFINED_BEHAVIOR);
|
||||
TYPECASE(PORTABILITY);
|
||||
TYPECASE(PERFORMANCE);
|
||||
TYPECASE(OTHER);
|
||||
#undef TYPECASE
|
||||
}
|
||||
|
||||
common->Warning( "GLDBG %s %s %s: %s\n", sourceStr, typeStr, severityStr, message );
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
R_CheckExtension
|
||||
|
@ -415,15 +479,15 @@ static void R_CheckPortableExtensions( void ) {
|
|||
qglActiveStencilFaceEXT = (PFNGLACTIVESTENCILFACEEXTPROC)GLimp_ExtensionPointer( "glActiveStencilFaceEXT" );
|
||||
|
||||
if( glConfig.glVersion >= 2.0) {
|
||||
common->Printf( "... got GL2.0+ glStencilOpSeparate()\n" );
|
||||
common->Printf( "...got GL2.0+ glStencilOpSeparate()\n" );
|
||||
qglStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)GLimp_ExtensionPointer( "glStencilOpSeparate" );
|
||||
} else if( R_CheckExtension( "GL_ATI_separate_stencil" ) ) {
|
||||
common->Printf( "... got glStencilOpSeparateATI() (GL_ATI_separate_stencil)\n" );
|
||||
common->Printf( "...got glStencilOpSeparateATI() (GL_ATI_separate_stencil)\n" );
|
||||
// the ATI version of glStencilOpSeparate() has the same signature and should also
|
||||
// behave identical to the GL2 version (in Mesa3D it's just an alias)
|
||||
qglStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)GLimp_ExtensionPointer( "glStencilOpSeparateATI" );
|
||||
} else {
|
||||
common->Printf( "... don't have glStencilOpSeparateATI() or (GL2.0+) glStencilOpSeparate()\n" );
|
||||
common->Printf( "X..don't have glStencilOpSeparateATI() or (GL2.0+) glStencilOpSeparate()\n" );
|
||||
qglStencilOpSeparate = NULL;
|
||||
}
|
||||
|
||||
|
@ -482,6 +546,37 @@ static void R_CheckPortableExtensions( void ) {
|
|||
qglDepthBoundsEXT = (PFNGLDEPTHBOUNDSEXTPROC)GLimp_ExtensionPointer( "glDepthBoundsEXT" );
|
||||
}
|
||||
|
||||
// GL_ARB_debug_output
|
||||
glConfig.glDebugOutputAvailable = false;
|
||||
if ( glConfig.haveDebugContext ) {
|
||||
if ( strstr( glConfig.extensions_string, "GL_ARB_debug_output" ) ) {
|
||||
glConfig.glDebugOutputAvailable = true;
|
||||
qglDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC)GLimp_ExtensionPointer( "glDebugMessageCallbackARB" );
|
||||
if ( r_glDebugContext.GetBool() ) {
|
||||
common->Printf( "...using GL_ARB_debug_output (r_glDebugContext is set)\n" );
|
||||
qglDebugMessageCallbackARB(DebugCallback, NULL);
|
||||
qglEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
|
||||
} else {
|
||||
common->Printf( "...found GL_ARB_debug_output, but not using it (r_glDebugContext is not set)\n" );
|
||||
}
|
||||
} else {
|
||||
common->Printf( "X..GL_ARB_debug_output not found\n" );
|
||||
qglDebugMessageCallbackARB = NULL;
|
||||
if ( r_glDebugContext.GetBool() ) {
|
||||
common->Warning( "r_glDebugContext is set, but can't be used because GL_ARB_debug_output is not supported" );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( strstr( glConfig.extensions_string, "GL_ARB_debug_output" ) ) {
|
||||
if ( r_glDebugContext.GetBool() ) {
|
||||
common->Printf( "...found GL_ARB_debug_output, but not using it (no debug context)\n" );
|
||||
} else {
|
||||
common->Printf( "...found GL_ARB_debug_output, but not using it (r_glDebugContext is not set)\n" );
|
||||
}
|
||||
} else {
|
||||
common->Printf( "X..GL_ARB_debug_output not found\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -31,6 +31,14 @@ If you have questions concerning this license or the applicable additional terms
|
|||
|
||||
#include "renderer/tr_local.h"
|
||||
|
||||
// DG: if this is defined, the soft particle shaders will be compiled into the executable
|
||||
// otherwise soft_particle.vfp will be opened as a file just like the other shaders
|
||||
// (useful when tweaking that shader - when loaded from disk, you can use `reloadARBprograms`
|
||||
// instead of recompiling the executable)
|
||||
#ifndef D3_INTEGRATE_SOFTPART_SHADERS
|
||||
#define D3_INTEGRATE_SOFTPART_SHADERS 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
=========================================================================================
|
||||
|
||||
|
@ -104,7 +112,7 @@ void RB_ARB2_DrawInteraction( const drawInteraction_t *din ) {
|
|||
float parm[4];
|
||||
parm[0] = parm[1] = parm[2] = r_brightness.GetFloat();
|
||||
parm[3] = 1.0/r_gamma.GetFloat(); // 1.0/gamma so the shader doesn't have to do this calculation
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_GAMMA_BRIGHTNESS, parm );
|
||||
}
|
||||
|
||||
// set the textures
|
||||
|
@ -345,9 +353,100 @@ static progDef_t progs[MAX_GLPROGS] = {
|
|||
{ GL_VERTEX_PROGRAM_ARB, VPROG_GLASSWARP, "arbVP_glasswarp.txt" },
|
||||
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_GLASSWARP, "arbFP_glasswarp.txt" },
|
||||
|
||||
// SteveL #3878: Particle softening applied by the engine
|
||||
{ GL_VERTEX_PROGRAM_ARB, VPROG_SOFT_PARTICLE, "soft_particle.vfp" },
|
||||
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_SOFT_PARTICLE, "soft_particle.vfp" },
|
||||
|
||||
// additional programs can be dynamically specified in materials
|
||||
};
|
||||
|
||||
#if D3_INTEGRATE_SOFTPART_SHADERS
|
||||
// DG: the following two shaders are taken from TheDarkMod 2.04 (glprogs/soft_particle.vfp)
|
||||
// (C) 2005-2016 Broken Glass Studios (The Dark Mod Team) and the individual authors
|
||||
// released under a revised BSD license and GPLv3
|
||||
const char* softpartVShader = "!!ARBvp1.0 \n"
|
||||
"OPTION ARB_position_invariant; \n"
|
||||
"# NOTE: unlike the TDM shader, the following lines use .texcoord and .color \n"
|
||||
"# instead of .attrib[8] and .attrib[3], to make it work with non-nvidia drivers \n"
|
||||
"MOV result.texcoord, vertex.texcoord; \n"
|
||||
"MOV result.color, vertex.color; \n"
|
||||
"END \n";
|
||||
|
||||
const char* softpartFShader = "!!ARBfp1.0 \n"
|
||||
"# == Fragment Program == \n"
|
||||
"# taken from The Dark Mod 2.04, adjusted for dhewm3 \n"
|
||||
"# (C) 2005-2016 Broken Glass Studios (The Dark Mod Team) \n"
|
||||
"# \n"
|
||||
"# Input textures \n"
|
||||
"# texture[0] particle diffusemap \n"
|
||||
"# texture[1] _currentDepth \n"
|
||||
"# \n"
|
||||
"# Constants set by the engine: \n"
|
||||
"# program.env[22] is reciprocal of _currentDepth size. Lets us convert a screen position to a texcoord in _currentDepth \n"
|
||||
"# { 1.0f / depthtex.width, 1.0f / depthtex.height, float(depthtex.width)/int(depthtex.width), \n"
|
||||
"# float(depthtex.height)/int(depthtex.height) } \n"
|
||||
"# program.env[23] is the particle radius, given as { radius, 1/(fadeRange), 1/radius } \n"
|
||||
"# fadeRange is the particle diameter for alpha blends (like smoke), but the particle radius for additive \n"
|
||||
"# blends (light glares), because additive effects work differently. Fog is half as apparent when a wall \n"
|
||||
"# is in the middle of it. Light glares lose no visibility when they have something to reflect off. \n"
|
||||
"# program.env[24] is the color channel mask. Particles with additive blend need their RGB channels modified to blend them out. \n"
|
||||
"# Particles with an alpha blend need their alpha channel modified. \n"
|
||||
"# \n"
|
||||
"# Hard-coded constants \n"
|
||||
"# depth_consts allows us to recover the original depth in Doom units of anything in the depth \n"
|
||||
"# buffer. Doom3's and thus TDM's projection matrix differs slightly from the classic projection matrix as \n"
|
||||
"# it implements a \"nearly-infinite\" zFar. The matrix is hard-coded in the engine, so we use hard-coded \n"
|
||||
"# constants here for efficiency. depth_consts is derived from the numbers in that matrix. \n"
|
||||
"# \n"
|
||||
"# next line: prevent dhewm3 from injecting gamma in shader code into this shader, \n"
|
||||
"# because that looks bad when rendered with additive blending (gets too bright) \n"
|
||||
"# nodhewm3gammahack \n"
|
||||
"\n"
|
||||
"PARAM depth_consts = { 0.33333333, -0.33316667, 0.0, 0.0 }; \n"
|
||||
"PARAM particle_radius = program.env[23]; \n"
|
||||
"TEMP tmp, scene_depth, particle_depth, near_fade, fade; \n"
|
||||
"\n"
|
||||
"# Map the fragment to a texcoord on our depth image, and sample to find scene_depth \n"
|
||||
"MUL tmp.xy, fragment.position, program.env[22]; \n"
|
||||
"TEX scene_depth, tmp, texture[1], 2D; \n"
|
||||
"MIN scene_depth, scene_depth, 0.9994; # Required by TDM projection matrix. Equates to max recoverable \n"
|
||||
" # depth of 30k units, which is enough. 0.9995 is infinite depth. \n"
|
||||
" # This is needed only if there is caulk sky on show (which writes \n"
|
||||
" # no depth, so leaves 1 in the depth texture). \n"
|
||||
"\n"
|
||||
"# Recover original depth in doom units \n"
|
||||
"MAD tmp, scene_depth, depth_consts.x, depth_consts.y; \n"
|
||||
"RCP scene_depth, tmp.x; \n"
|
||||
"\n"
|
||||
"# Convert particle depth to doom units too \n"
|
||||
"MAD tmp, fragment.position.z, depth_consts.x, depth_consts.y; \n"
|
||||
"RCP particle_depth, tmp.x; \n"
|
||||
"\n"
|
||||
"# Scale the depth difference by the particle diameter to calc an alpha \n"
|
||||
"# value based on how much of the 3d volume represented by the particle \n"
|
||||
"# is in front of the solid scene \n"
|
||||
"ADD tmp, -scene_depth, particle_depth; # NB depth is negative. 0 at the eye, -100 at 100 units into the screen. \n"
|
||||
"ADD tmp, tmp, particle_radius.x; # Add the radius so a depth difference of particle radius now equals 0 \n"
|
||||
"MUL_SAT fade, tmp, particle_radius.y; # divide by the particle radius or diameter and clamp \n"
|
||||
"\n"
|
||||
"# Also fade if the particle is too close to our eye position, so it doesn't 'pop' in and out of view \n"
|
||||
"# Start a linear fade at particle_radius distance from the particle. \n"
|
||||
"MUL_SAT near_fade, particle_depth, -particle_radius.z; \n"
|
||||
"\n"
|
||||
"# Calculate final fade and apply the channel mask \n"
|
||||
"MUL fade, near_fade, fade; \n"
|
||||
"ADD_SAT fade, fade, program.env[24]; # saturate the channels that don't want modifying \n"
|
||||
"\n"
|
||||
"# Set the color. Multiply by vertex/fragment color as that's how the particle system fades particles in and out \n"
|
||||
"TEMP oColor; \n"
|
||||
"TEX oColor, fragment.texcoord, texture[0], 2D; \n"
|
||||
"MUL oColor, oColor, fade; \n"
|
||||
"MUL result.color, oColor, fragment.color; \n"
|
||||
"\n"
|
||||
"END \n";
|
||||
|
||||
#endif // D3_INTEGRATE_SOFTPART_SHADERS
|
||||
|
||||
/*
|
||||
=================
|
||||
R_LoadARBProgram
|
||||
|
@ -391,26 +490,40 @@ static ID_INLINE bool isARBidentifierChar( int c ) {
|
|||
void R_LoadARBProgram( int progIndex ) {
|
||||
int ofs;
|
||||
int err;
|
||||
idStr fullPath = "glprogs/";
|
||||
fullPath += progs[progIndex].name;
|
||||
char *fileBuffer;
|
||||
char *buffer;
|
||||
char *start = NULL, *end;
|
||||
|
||||
common->Printf( "%s", fullPath.c_str() );
|
||||
#if D3_INTEGRATE_SOFTPART_SHADERS
|
||||
if ( progs[progIndex].ident == VPROG_SOFT_PARTICLE || progs[progIndex].ident == FPROG_SOFT_PARTICLE ) {
|
||||
// these shaders are loaded directly from a string
|
||||
common->Printf( "<internal> %s", progs[progIndex].name );
|
||||
const char* srcstr = (progs[progIndex].ident == VPROG_SOFT_PARTICLE) ? softpartVShader : softpartFShader;
|
||||
|
||||
// load the program even if we don't support it, so
|
||||
// fs_copyfiles can generate cross-platform data dumps
|
||||
fileSystem->ReadFile( fullPath.c_str(), (void **)&fileBuffer, NULL );
|
||||
if ( !fileBuffer ) {
|
||||
common->Printf( ": File not found\n" );
|
||||
return;
|
||||
// copy to stack memory
|
||||
buffer = (char *)_alloca( strlen( srcstr ) + 1 );
|
||||
strcpy( buffer, srcstr );
|
||||
}
|
||||
else
|
||||
#endif // D3_INTEGRATE_SOFTPART_SHADERS
|
||||
{
|
||||
idStr fullPath = "glprogs/";
|
||||
fullPath += progs[progIndex].name;
|
||||
char *fileBuffer;
|
||||
common->Printf( "%s", fullPath.c_str() );
|
||||
|
||||
// copy to stack memory and free
|
||||
buffer = (char *)_alloca( strlen( fileBuffer ) + 1 );
|
||||
strcpy( buffer, fileBuffer );
|
||||
fileSystem->FreeFile( fileBuffer );
|
||||
// load the program even if we don't support it, so
|
||||
// fs_copyfiles can generate cross-platform data dumps
|
||||
fileSystem->ReadFile( fullPath.c_str(), (void **)&fileBuffer, NULL );
|
||||
if ( !fileBuffer ) {
|
||||
common->Printf( ": File not found\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
// copy to stack memory and free
|
||||
buffer = (char *)_alloca( strlen( fileBuffer ) + 1 );
|
||||
strcpy( buffer, fileBuffer );
|
||||
fileSystem->FreeFile( fileBuffer );
|
||||
}
|
||||
|
||||
if ( !glConfig.isInitialized ) {
|
||||
return;
|
||||
|
@ -454,12 +567,14 @@ void R_LoadARBProgram( int progIndex ) {
|
|||
end[3] = 0;
|
||||
|
||||
// DG: hack gamma correction into shader
|
||||
if ( r_gammaInShader.GetBool() && progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB ) {
|
||||
if ( r_gammaInShader.GetBool() && progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB
|
||||
&& strstr( start, "nodhewm3gammahack" ) == NULL )
|
||||
{
|
||||
|
||||
// note that strlen("dhewm3tmpres") == strlen("result.color")
|
||||
const char* tmpres = "TEMP dhewm3tmpres; # injected by dhewm3 for gamma correction\n";
|
||||
|
||||
// Note: program.env[4].xyz = r_brightness; program.env[4].w = 1.0/r_gamma
|
||||
// Note: program.env[21].xyz = r_brightness; program.env[21].w = 1.0/r_gamma
|
||||
// outColor.rgb = pow(dhewm3tmpres.rgb*r_brightness, vec3(1.0/r_gamma))
|
||||
// outColor.a = dhewm3tmpres.a;
|
||||
const char* extraLines =
|
||||
|
@ -468,10 +583,10 @@ void R_LoadARBProgram( int progIndex ) {
|
|||
// POW might not work with a negative base (it looks wrong with intel's Linux driver)
|
||||
// and clamping values >1 to 1 is ok because when writing to result.color
|
||||
// it's clamped anyway and pow(base, exp) is always >= 1 for base >= 1
|
||||
"MUL_SAT dhewm3tmpres.xyz, program.env[4], dhewm3tmpres;\n" // first multiply with brightness
|
||||
"POW result.color.x, dhewm3tmpres.x, program.env[4].w;\n" // then do pow(dhewm3tmpres.xyz, vec3(1/gamma))
|
||||
"POW result.color.y, dhewm3tmpres.y, program.env[4].w;\n" // (apparently POW only supports scalars, not whole vectors)
|
||||
"POW result.color.z, dhewm3tmpres.z, program.env[4].w;\n"
|
||||
"MUL_SAT dhewm3tmpres.xyz, program.env[21], dhewm3tmpres;\n" // first multiply with brightness
|
||||
"POW result.color.x, dhewm3tmpres.x, program.env[21].w;\n" // then do pow(dhewm3tmpres.xyz, vec3(1/gamma))
|
||||
"POW result.color.y, dhewm3tmpres.y, program.env[21].w;\n" // (apparently POW only supports scalars, not whole vectors)
|
||||
"POW result.color.z, dhewm3tmpres.z, program.env[21].w;\n"
|
||||
"MOV result.color.w, dhewm3tmpres.w;\n" // alpha remains unmodified
|
||||
"\nEND\n\n"; // we add this block right at the end, replacing the original "END" string
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ If you have questions concerning this license or the applicable additional terms
|
|||
|
||||
extern idCVar r_useCarmacksReverse;
|
||||
extern idCVar r_useStencilOpSeparate;
|
||||
|
||||
/*
|
||||
=====================
|
||||
RB_BakeTextureMatrixIntoTexgen
|
||||
|
@ -313,7 +314,7 @@ void RB_FinishStageTexturing( const shaderStage_t *pStage, const drawSurf_t *sur
|
|||
qglDisable( GL_FRAGMENT_PROGRAM_ARB );
|
||||
qglDisable( GL_VERTEX_PROGRAM_ARB );
|
||||
// Fixme: Hack to get around an apparent bug in ATI drivers. Should remove as soon as it gets fixed.
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 );
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 ); // FIXME ...
|
||||
} else {
|
||||
qglDisable( GL_TEXTURE_GEN_S );
|
||||
qglDisable( GL_TEXTURE_GEN_T );
|
||||
|
@ -513,6 +514,9 @@ void RB_T_FillDepthBuffer( const drawSurf_t *surf ) {
|
|||
|
||||
}
|
||||
|
||||
|
||||
void RB_SetProgramEnvironment( bool isPostProcess ); // so RB_STD_FillDepthBuffer() can use it
|
||||
|
||||
/*
|
||||
=====================
|
||||
RB_STD_FillDepthBuffer
|
||||
|
@ -553,6 +557,21 @@ void RB_STD_FillDepthBuffer( drawSurf_t **drawSurfs, int numDrawSurfs ) {
|
|||
|
||||
RB_RenderDrawSurfListWithFunction( drawSurfs, numDrawSurfs, RB_T_FillDepthBuffer );
|
||||
|
||||
// Make the early depth pass available to shaders. #3877
|
||||
bool getDepthCapture = r_enableDepthCapture.GetInteger() == 1
|
||||
|| (r_enableDepthCapture.GetInteger() == -1 && r_useSoftParticles.GetBool());
|
||||
|
||||
if ( getDepthCapture && backEnd.viewDef->renderView.viewID >= 0 ) // Suppress for lightgem rendering passes
|
||||
{
|
||||
globalImages->currentDepthImage->CopyDepthbuffer( backEnd.viewDef->viewport.x1,
|
||||
backEnd.viewDef->viewport.y1,
|
||||
backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1,
|
||||
backEnd.viewDef->viewport.y2 - backEnd.viewDef->viewport.y1 + 1,
|
||||
true );
|
||||
bool isPostProcess = false;
|
||||
RB_SetProgramEnvironment( isPostProcess );
|
||||
}
|
||||
|
||||
if ( backEnd.viewDef->numClipPlanes ) {
|
||||
GL_SelectTexture( 1 );
|
||||
globalImages->BindNull();
|
||||
|
@ -575,6 +594,18 @@ SHADER PASSES
|
|||
RB_SetProgramEnvironment
|
||||
|
||||
Sets variables that can be used by all vertex programs
|
||||
|
||||
[SteveL #3877] Note on the use of fragment program environmental variables.
|
||||
Parameters 0 and 1 are set here to allow conversion of screen coordinates to
|
||||
texture coordinates, for use when sampling _currentRender.
|
||||
Those same parameters 0 and 1, plus 2 and 3, are given entirely different
|
||||
meanings in draw_arb2.cpp while light interactions are being drawn.
|
||||
This function is called again before currentRender size is needed by post processing
|
||||
effects are done, so there's no clash.
|
||||
Only parameters 0..3 were in use before #3877 - and in dhewm3 also 4, for gamma in shader.
|
||||
Now I've used a new parameter 22 for the size of _currentDepth. It's needed throughout,
|
||||
including by light interactions, and its size might in theory differ from _currentRender.
|
||||
Parameters 23 and 24 are used by soft particles #3878. Note these can be freely reused by different draw calls.
|
||||
==================
|
||||
*/
|
||||
void RB_SetProgramEnvironment( bool isPostProcess ) {
|
||||
|
@ -643,9 +674,23 @@ void RB_SetProgramEnvironment( bool isPostProcess ) {
|
|||
// (setting them to 1.0 makes them no-ops)
|
||||
parm[0] = parm[1] = parm[2] = parm[3] = 1.0f;
|
||||
}
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_GAMMA_BRIGHTNESS, parm );
|
||||
}
|
||||
|
||||
// #3877: Allow shaders to access depth buffer.
|
||||
// Two useful ratios are packed into this parm: [0] and [1] hold the x,y multipliers you need to map a screen
|
||||
// coordinate (fragment position) to the depth image: those are simply the reciprocal of the depth
|
||||
// image size, which has been rounded up to a power of two. Slots [3] and [4] hold the ratio of the depth image
|
||||
// size to the current render image size. These sizes can differ if the game crops the render viewport temporarily
|
||||
// during post-processing effects. The depth render is smaller during the effect too, but the depth image doesn't
|
||||
// need to be downsized, whereas the current render image does get downsized when it's captured by the game after
|
||||
// the skybox render pass. The ratio is needed to map between the two render images.
|
||||
parm[0] = 1.0f / globalImages->currentDepthImage->uploadWidth;
|
||||
parm[1] = 1.0f / globalImages->currentDepthImage->uploadHeight;
|
||||
parm[2] = static_cast<float>(globalImages->currentRenderImage->uploadWidth) / globalImages->currentDepthImage->uploadWidth;
|
||||
parm[3] = static_cast<float>(globalImages->currentRenderImage->uploadHeight) / globalImages->currentDepthImage->uploadHeight;
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_CURDEPTH_RECIPR, parm );
|
||||
|
||||
//
|
||||
// set eye position in global space
|
||||
//
|
||||
|
@ -654,8 +699,6 @@ void RB_SetProgramEnvironment( bool isPostProcess ) {
|
|||
parm[2] = backEnd.viewDef->renderView.vieworg[2];
|
||||
parm[3] = 1.0;
|
||||
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 1, parm );
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -749,6 +792,9 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
|||
return;
|
||||
}
|
||||
|
||||
// check whether we're drawing a soft particle surface #3878
|
||||
const bool soft_particle = ( surf->dsFlags & DSF_SOFT_PARTICLE ) != 0;
|
||||
|
||||
// get the expressions for conditionals / color / texcoords
|
||||
regs = surf->shaderRegisters;
|
||||
|
||||
|
@ -765,7 +811,8 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
|||
RB_EnterWeaponDepthHack();
|
||||
}
|
||||
|
||||
if ( surf->space->modelDepthHack != 0.0f ) {
|
||||
if ( surf->space->modelDepthHack != 0.0f && !soft_particle ) // #3878 soft particles don't want modelDepthHack, which is
|
||||
{ // an older way to slightly "soften" particles
|
||||
RB_EnterModelDepthHack( surf->space->modelDepthHack );
|
||||
}
|
||||
|
||||
|
@ -791,6 +838,9 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// determine the blend mode (used by soft particles #3878)
|
||||
const int src_blend = pStage->drawStateBits & GLS_SRCBLEND_BITS;
|
||||
|
||||
// see if we are a new-style stage
|
||||
newShaderStage_t *newStage = pStage->newStage;
|
||||
if ( newStage ) {
|
||||
|
@ -866,7 +916,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
|||
qglDisable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglDisable( GL_FRAGMENT_PROGRAM_ARB );
|
||||
// Fixme: Hack to get around an apparent bug in ATI drivers. Should remove as soon as it gets fixed.
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 );
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, 0 ); // FIXME: ...
|
||||
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
qglDisableVertexAttribArrayARB( 9 );
|
||||
|
@ -874,6 +924,148 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
|||
qglDisableClientState( GL_NORMAL_ARRAY );
|
||||
continue;
|
||||
}
|
||||
else if ( soft_particle
|
||||
&& surf->particle_radius > 0.0f
|
||||
&& ( src_blend == GLS_SRCBLEND_ONE || src_blend == GLS_SRCBLEND_SRC_ALPHA )
|
||||
&& tr.backEndRenderer == BE_ARB2
|
||||
&& !r_skipNewAmbient.GetBool() )
|
||||
{
|
||||
// SteveL #3878. Particles are automatically softened by the engine, unless they have shader programs of
|
||||
// their own (i.e. are "newstages" handled above). This section comes after the newstage part so that if a
|
||||
// designer has specified their own shader programs, those will be used instead of the soft particle program.
|
||||
if ( pStage->vertexColor == SVC_IGNORE )
|
||||
{
|
||||
// Ignoring vertexColor is not recommended for particles. The particle system uses vertexColor for fading.
|
||||
// However, there are existing particle effects that don't use it, in which case we default to using the
|
||||
// rgb color modulation specified in the material like the "old stages" do below.
|
||||
color[0] = regs[pStage->color.registers[0]];
|
||||
color[1] = regs[pStage->color.registers[1]];
|
||||
color[2] = regs[pStage->color.registers[2]];
|
||||
color[3] = regs[pStage->color.registers[3]];
|
||||
qglColor4fv( color );
|
||||
}
|
||||
else
|
||||
{
|
||||
// A properly set-up particle shader
|
||||
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), (void *)&ac->color );
|
||||
qglEnableClientState( GL_COLOR_ARRAY );
|
||||
}
|
||||
|
||||
#if 0 // debug stuff: render particles opaque so debug colors written in the shader are properly visible
|
||||
int dsbits = pStage->drawStateBits | GLS_DEPTHFUNC_ALWAYS;
|
||||
dsbits &= ~(GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS);
|
||||
//dsbits |= GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO; both values are 0, so this would be a noop
|
||||
GL_State( dsbits );
|
||||
#endif
|
||||
GL_State( pStage->drawStateBits | GLS_DEPTHFUNC_ALWAYS ); // Disable depth clipping. The fragment program will
|
||||
// handle it to allow overdraw.
|
||||
|
||||
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_SOFT_PARTICLE );
|
||||
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
||||
|
||||
// Bind image and _currentDepth
|
||||
GL_SelectTexture( 0 );
|
||||
pStage->texture.image->Bind();
|
||||
GL_SelectTexture( 1 );
|
||||
globalImages->currentDepthImage->Bind();
|
||||
|
||||
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_SOFT_PARTICLE );
|
||||
qglEnable( GL_FRAGMENT_PROGRAM_ARB );
|
||||
|
||||
#if 0 // debug stuff
|
||||
// Set up parameters for fragment program
|
||||
const char* srcblendstr = "???";
|
||||
if ( src_blend >= 0 && src_blend <= 9 ) {
|
||||
const char* blendModes[] = {
|
||||
"ONE",
|
||||
"ZERO",
|
||||
"!! INVALID !!",
|
||||
"DST_COLOR",
|
||||
"ONE_MINUS_DST_COLOR",
|
||||
"SRC_ALPHA",
|
||||
"ONE_MINUS_SRC_ALPHA",
|
||||
"DST_ALPHA",
|
||||
"ONE_MINUS_DST_ALPHA",
|
||||
"ALPHA_SATURATE"
|
||||
};
|
||||
srcblendstr = blendModes[src_blend];
|
||||
}
|
||||
|
||||
|
||||
int dst_blend = pStage->drawStateBits & GLS_DSTBLEND_BITS;
|
||||
const char* dstblend = "???";
|
||||
switch ( dst_blend ) {
|
||||
#define MY_CASE(X) case GLS_DSTBLEND_ ##X : dstblend = #X; break;
|
||||
MY_CASE(ZERO)
|
||||
MY_CASE(ONE)
|
||||
MY_CASE(SRC_COLOR)
|
||||
MY_CASE(ONE_MINUS_SRC_COLOR)
|
||||
MY_CASE(SRC_ALPHA)
|
||||
MY_CASE(ONE_MINUS_SRC_ALPHA)
|
||||
MY_CASE(DST_ALPHA)
|
||||
MY_CASE(ONE_MINUS_DST_ALPHA)
|
||||
#undef MY_CASE
|
||||
}
|
||||
|
||||
printf("XX mat: %s, src_blend = %s dest_blend = %s radius = %g\n", shader->GetName(), srcblendstr, dstblend, surf->particle_radius);
|
||||
#endif
|
||||
|
||||
// program.env[23] is the particle radius, given as { radius, 1/(faderange), 1/radius }
|
||||
float fadeRange = 1.0f;
|
||||
// fadeRange is the particle diameter for alpha blends (like smoke), but the particle radius for additive
|
||||
// blends (light glares), because additive effects work differently. Fog is half as apparent when a wall
|
||||
// is in the middle of it. Light glares lose no visibility when they have something to reflect off. See
|
||||
// The Dark Mod issue #3878 for diagram
|
||||
if ( src_blend == GLS_SRCBLEND_SRC_ALPHA ) // an alpha blend material
|
||||
{
|
||||
fadeRange = surf->particle_radius * 2.0f;
|
||||
}
|
||||
else if ( src_blend == GLS_SRCBLEND_ONE ) // an additive (blend add) material
|
||||
{
|
||||
fadeRange = surf->particle_radius;
|
||||
}
|
||||
|
||||
float parm[4] = {
|
||||
surf->particle_radius,
|
||||
1.0f / ( fadeRange ),
|
||||
1.0f / surf->particle_radius,
|
||||
0.0f
|
||||
};
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_PARTICLE_RADIUS, parm );
|
||||
|
||||
// program.env[24] is the color channel mask. It gets added to the fade multiplier, so adding 1
|
||||
// to a channel will make sure it doesn't get faded at all. Particles with additive blend
|
||||
// need their RGB channels modifying to blend them out. Particles with an alpha blend need
|
||||
// their alpha channel modifying.
|
||||
if ( src_blend == GLS_SRCBLEND_SRC_ALPHA ) // an alpha blend material
|
||||
{
|
||||
parm[0] = parm[1] = parm[2] = 1.0f; // Leave the rgb channels at full strength when fading
|
||||
parm[3] = 0.0f; // but fade the alpha channel
|
||||
}
|
||||
else if ( src_blend == GLS_SRCBLEND_ONE ) // an additive (blend add) material
|
||||
{
|
||||
parm[0] = parm[1] = parm[2] = 0.0f; // Fade the rgb channels but
|
||||
parm[3] = 1.0f; // leave the alpha channel at full strength
|
||||
}
|
||||
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_PARTICLE_COLCHAN_MASK, parm );
|
||||
|
||||
// draw it
|
||||
RB_DrawElementsWithCounters( tri );
|
||||
|
||||
// Clean up GL state
|
||||
GL_SelectTexture( 1 );
|
||||
globalImages->BindNull();
|
||||
GL_SelectTexture( 0 );
|
||||
globalImages->BindNull();
|
||||
|
||||
qglDisable( GL_VERTEX_PROGRAM_ARB );
|
||||
qglDisable( GL_FRAGMENT_PROGRAM_ARB );
|
||||
|
||||
if ( pStage->vertexColor != SVC_IGNORE ) {
|
||||
qglDisableClientState( GL_COLOR_ARRAY );
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
//
|
||||
|
@ -972,7 +1164,8 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
|
|||
if ( shader->TestMaterialFlag(MF_POLYGONOFFSET) ) {
|
||||
qglDisable( GL_POLYGON_OFFSET_FILL );
|
||||
}
|
||||
if ( surf->space->weaponDepthHack || surf->space->modelDepthHack != 0.0f ) {
|
||||
if ( surf->space->weaponDepthHack || ( !soft_particle && surf->space->modelDepthHack != 0.0f ) ) // #3878 soft particles
|
||||
{
|
||||
RB_LeaveDepthHack();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,6 +119,9 @@ extern PFNGLPROGRAMLOCALPARAMETER4FVARBPROC qglProgramLocalParameter4fvARB;
|
|||
// GL_EXT_depth_bounds_test
|
||||
extern PFNGLDEPTHBOUNDSEXTPROC qglDepthBoundsEXT;
|
||||
|
||||
// GL_ARB_debug_output
|
||||
extern PFNGLDEBUGMESSAGECALLBACKARBPROC qglDebugMessageCallbackARB;
|
||||
|
||||
#if defined( _WIN32 ) && defined(ID_ALLOW_TOOLS)
|
||||
|
||||
extern BOOL(WINAPI * qwglSwapBuffers)(HDC);
|
||||
|
|
|
@ -35,6 +35,9 @@ If you have questions concerning this license or the applicable additional terms
|
|||
|
||||
#include "renderer/tr_local.h"
|
||||
|
||||
#include "Model_local.h"
|
||||
|
||||
|
||||
static const float CHECK_BOUNDS_EPSILON = 1.0f;
|
||||
|
||||
/*
|
||||
|
@ -673,6 +676,8 @@ void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const
|
|||
drawSurf->material = shader;
|
||||
drawSurf->scissorRect = scissor;
|
||||
drawSurf->dsFlags = 0;
|
||||
drawSurf->particle_radius = 0.0f; // #3878
|
||||
|
||||
if ( viewInsideShadow ) {
|
||||
drawSurf->dsFlags |= DSF_VIEW_INSIDE_SHADOW;
|
||||
}
|
||||
|
@ -1186,7 +1191,8 @@ R_AddDrawSurf
|
|||
=================
|
||||
*/
|
||||
void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity,
|
||||
const idMaterial *shader, const idScreenRect &scissor ) {
|
||||
const idMaterial *shader, const idScreenRect &scissor, const float soft_particle_radius )
|
||||
{
|
||||
drawSurf_t *drawSurf;
|
||||
const float *shaderParms;
|
||||
static float refRegs[MAX_EXPRESSION_REGISTERS]; // don't put on stack, or VC++ will do a page touch
|
||||
|
@ -1198,7 +1204,17 @@ void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const
|
|||
drawSurf->material = shader;
|
||||
drawSurf->scissorRect = scissor;
|
||||
drawSurf->sort = shader->GetSort() + tr.sortOffset;
|
||||
drawSurf->dsFlags = 0;
|
||||
|
||||
if ( soft_particle_radius != -1.0f ) // #3878
|
||||
{
|
||||
drawSurf->dsFlags = DSF_SOFT_PARTICLE;
|
||||
drawSurf->particle_radius = soft_particle_radius;
|
||||
}
|
||||
else
|
||||
{
|
||||
drawSurf->dsFlags = 0;
|
||||
drawSurf->particle_radius = 0.0f;
|
||||
}
|
||||
|
||||
// bumping this offset each time causes surfaces with equal sort orders to still
|
||||
// deterministically draw in the order they are added
|
||||
|
@ -1218,7 +1234,7 @@ void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const
|
|||
}
|
||||
tr.viewDef->drawSurfs = (drawSurf_t **)R_FrameAlloc( tr.viewDef->maxDrawSurfs * sizeof( tr.viewDef->drawSurfs[0] ) );
|
||||
if(count > 0)
|
||||
memcpy( tr.viewDef->drawSurfs, old, count ); // XXX null pointer passed as argument 2, which is declared to never be null
|
||||
memcpy( tr.viewDef->drawSurfs, old, count );
|
||||
}
|
||||
tr.viewDef->drawSurfs[tr.viewDef->numDrawSurfs] = drawSurf;
|
||||
tr.viewDef->numDrawSurfs++;
|
||||
|
@ -1423,8 +1439,21 @@ static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) {
|
|||
vertexCache.Touch( tri->indexCache );
|
||||
}
|
||||
|
||||
// Soft Particles -- SteveL #3878
|
||||
float particle_radius = -1.0f; // Default = disallow softening, but allow modelDepthHack if specified in the decl.
|
||||
if ( r_useSoftParticles.GetBool() && r_enableDepthCapture.GetInteger() != 0
|
||||
&& !shader->ReceivesLighting() // don't soften surfaces that are meant to be solid
|
||||
&& tr.viewDef->renderView.viewID >= 0 ) // Skip during "invisible" rendering passes (e.g. lightgem)
|
||||
{
|
||||
const idRenderModelPrt* prt = dynamic_cast<const idRenderModelPrt*>( def->parms.hModel ); // yuck.
|
||||
if ( prt )
|
||||
{
|
||||
particle_radius = prt->SofteningRadius( surf->id );
|
||||
}
|
||||
}
|
||||
|
||||
// add the surface for drawing
|
||||
R_AddDrawSurf( tri, vEntity, &vEntity->entityDef->parms, shader, vEntity->scissorRect );
|
||||
R_AddDrawSurf( tri, vEntity, &vEntity->entityDef->parms, shader, vEntity->scissorRect, particle_radius );
|
||||
|
||||
// ambientViewCount is used to allow light interactions to be rejected
|
||||
// if the ambient surface isn't visible at all
|
||||
|
@ -1514,6 +1543,14 @@ void R_AddModelSurfaces( void ) {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Don't let particle entities re-instantiate their dynamic model during non-visible
|
||||
// views (in TDM, the light gem render) -- SteveL #3970
|
||||
if ( tr.viewDef->renderView.viewID < 0
|
||||
&& dynamic_cast<const idRenderModelPrt*>( vEntity->entityDef->parms.hModel ) != NULL ) // yuck.
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// add the ambient surface if it has a visible rectangle
|
||||
if ( !vEntity->scissorRect.IsEmpty() ) {
|
||||
model = R_EntityDefDynamicModel( vEntity->entityDef );
|
||||
|
|
|
@ -109,6 +109,7 @@ SURFACES
|
|||
|
||||
// drawSurf_t are always allocated and freed every frame, they are never cached
|
||||
static const int DSF_VIEW_INSIDE_SHADOW = 1;
|
||||
static const int DSF_SOFT_PARTICLE = 2; // #3878 - soft particles
|
||||
|
||||
typedef struct drawSurf_s {
|
||||
const srfTriangles_t *geo;
|
||||
|
@ -121,6 +122,7 @@ typedef struct drawSurf_s {
|
|||
int dsFlags; // DSF_VIEW_INSIDE_SHADOW, etc
|
||||
struct vertCache_s *dynamicTexCoords; // float * in vertex cache memory
|
||||
// specular directions for non vertex program cards, skybox texcoords, etc
|
||||
float particle_radius; // The radius of individual quads for soft particles #3878
|
||||
} drawSurf_t;
|
||||
|
||||
|
||||
|
@ -981,6 +983,10 @@ extern idCVar r_materialOverride; // override all materials
|
|||
|
||||
extern idCVar r_debugRenderToTexture;
|
||||
|
||||
extern idCVar r_glDebugContext; // DG: use debug context to call logging callbacks on GL errors
|
||||
extern idCVar r_enableDepthCapture; // DG: disable capturing depth buffer, used for soft particles
|
||||
extern idCVar r_useSoftParticles;
|
||||
|
||||
/*
|
||||
====================================================================
|
||||
|
||||
|
@ -1179,7 +1185,7 @@ viewEntity_t *R_SetEntityDefViewEntity( idRenderEntityLocal *def );
|
|||
viewLight_t *R_SetLightDefViewLight( idRenderLightLocal *def );
|
||||
|
||||
void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const renderEntity_t *renderEntity,
|
||||
const idMaterial *shader, const idScreenRect &scissor );
|
||||
const idMaterial *shader, const idScreenRect &scissor, const float soft_particle_radius = -1.0f ); // soft particles in #3878
|
||||
|
||||
void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const viewEntity_t *space,
|
||||
const idRenderLightLocal *light, const idMaterial *shader, const idScreenRect &scissor, bool viewInsideShadow );
|
||||
|
@ -1315,6 +1321,10 @@ typedef enum {
|
|||
FPROG_AMBIENT,
|
||||
VPROG_GLASSWARP,
|
||||
FPROG_GLASSWARP,
|
||||
// SteveL #3878: soft particles
|
||||
VPROG_SOFT_PARTICLE,
|
||||
FPROG_SOFT_PARTICLE,
|
||||
//
|
||||
PROG_USER
|
||||
} program_t;
|
||||
|
||||
|
@ -1362,9 +1372,22 @@ typedef enum {
|
|||
PP_SPECULAR_MATRIX_S,
|
||||
PP_SPECULAR_MATRIX_T,
|
||||
PP_COLOR_MODULATE,
|
||||
PP_COLOR_ADD,
|
||||
PP_COLOR_ADD, // 17
|
||||
|
||||
PP_LIGHT_FALLOFF_TQ = 20 // only for NV programs
|
||||
PP_LIGHT_FALLOFF_TQ = 20, // only for NV programs - DG: unused
|
||||
PP_GAMMA_BRIGHTNESS = 21, // DG: for gamma in shader: { r_brightness, r_brightness, r_brightness, 1/r_gamma }
|
||||
// DG: for soft particles from TDM: reciprocal of _currentDepth size.
|
||||
// Lets us convert a screen position to a texcoord in _currentDepth
|
||||
PP_CURDEPTH_RECIPR = 22,
|
||||
// DG: for soft particles from TDM: particle radius, given as { radius, 1/(fadeRange), 1/radius }
|
||||
// fadeRange is the particle diameter for alpha blends (like smoke), but the particle radius for additive
|
||||
// blends (light glares), because additive effects work differently. Fog is half as apparent when a wall
|
||||
// is in the middle of it. Light glares lose no visibility when they have something to reflect off.
|
||||
PP_PARTICLE_RADIUS = 23,
|
||||
// DG: for soft particles from TDM: color channel mask.
|
||||
// Particles with additive blend need their RGB channels modifying to blend them out
|
||||
// Particles with an alpha blend need their alpha channel modifying.
|
||||
PP_PARTICLE_COLCHAN_MASK = 24,
|
||||
} programParameter_t;
|
||||
|
||||
|
||||
|
|
|
@ -437,7 +437,6 @@ Draw the depth buffer as colors
|
|||
===================
|
||||
*/
|
||||
void RB_ShowDepthBuffer( void ) {
|
||||
void *depthReadback;
|
||||
|
||||
if ( !r_showDepth.GetBool() ) {
|
||||
return;
|
||||
|
@ -450,30 +449,81 @@ void RB_ShowDepthBuffer( void ) {
|
|||
qglLoadIdentity();
|
||||
qglOrtho( 0, 1, 0, 1, -1, 1 );
|
||||
qglRasterPos2f( 0, 0 );
|
||||
qglPopMatrix();
|
||||
qglMatrixMode( GL_MODELVIEW );
|
||||
qglPopMatrix();
|
||||
|
||||
GL_State( GLS_DEPTHFUNC_ALWAYS );
|
||||
qglColor3f( 1, 1, 1 );
|
||||
globalImages->BindNull();
|
||||
|
||||
depthReadback = R_StaticAlloc( glConfig.vidWidth * glConfig.vidHeight*4 );
|
||||
memset( depthReadback, 0, glConfig.vidWidth * glConfig.vidHeight*4 );
|
||||
bool haveDepthCapture = r_enableDepthCapture.GetInteger() == 1
|
||||
|| (r_enableDepthCapture.GetInteger() == -1 && r_useSoftParticles.GetBool());
|
||||
|
||||
qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_DEPTH_COMPONENT , GL_FLOAT, depthReadback );
|
||||
if ( haveDepthCapture ) {
|
||||
//GL_SelectTexture( 0 );
|
||||
//qglEnable(GL_TEXTURE_2D);
|
||||
globalImages->currentDepthImage->Bind();
|
||||
|
||||
#if 0
|
||||
for ( i = 0 ; i < glConfig.vidWidth * glConfig.vidHeight ; i++ ) {
|
||||
((byte *)depthReadback)[i*4] =
|
||||
((byte *)depthReadback)[i*4+1] =
|
||||
((byte *)depthReadback)[i*4+2] = 255 * ((float *)depthReadback)[i];
|
||||
((byte *)depthReadback)[i*4+3] = 1;
|
||||
}
|
||||
const float x=0, y=0, w=1, h=1;
|
||||
// debug values:
|
||||
//const float x = 0.1, y = 0.1, w = 0.8, h = 0.8;
|
||||
//qglColor4f( 0.0f, 0.0f, 0.5f, 1.0f );
|
||||
|
||||
const float tx=0, ty=0;
|
||||
// the actual texturesize of currentDepthImage is the next bigger power of two (POT),
|
||||
// so the normalized width/height of the part of it we actually wanna show is the following
|
||||
const float tw = float(glConfig.vidWidth) / float(globalImages->currentDepthImage->uploadWidth);
|
||||
const float th = float(glConfig.vidHeight) / float(globalImages->currentDepthImage->uploadHeight);
|
||||
|
||||
qglBegin( GL_QUADS );
|
||||
qglTexCoord2f(tx, ty);
|
||||
qglVertex2f( x, y ); // ( 0,0 );
|
||||
|
||||
qglTexCoord2f(tx, ty+th);
|
||||
qglVertex2f( x, y+h ); // ( 0,1 );
|
||||
|
||||
qglTexCoord2f(tx+tw, ty+th);
|
||||
qglVertex2f( x+w, y+h ); // ( 1,1 );
|
||||
|
||||
qglTexCoord2f(tx+tw, ty);
|
||||
qglVertex2f( x+w, y ); // ( 1,0 );
|
||||
qglEnd();
|
||||
|
||||
// TODO: probably a shader transforming this to something viewable
|
||||
|
||||
qglPopMatrix();
|
||||
qglMatrixMode( GL_MODELVIEW );
|
||||
qglPopMatrix();
|
||||
|
||||
} else {
|
||||
qglPopMatrix();
|
||||
qglMatrixMode( GL_MODELVIEW );
|
||||
qglPopMatrix();
|
||||
globalImages->BindNull();
|
||||
|
||||
void* depthReadback = R_StaticAlloc( glConfig.vidWidth * glConfig.vidHeight*4 );
|
||||
memset( depthReadback, 0, glConfig.vidWidth * glConfig.vidHeight*4 );
|
||||
|
||||
qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_DEPTH_COMPONENT , GL_FLOAT, depthReadback );
|
||||
#if 0 // the following looks better, but is different from the !r_skipDepthCapture.GetBool() case above
|
||||
// (which draws the captured depth buffer unaltered, unless we add a shader)
|
||||
for ( int i = 0, n=glConfig.vidWidth * glConfig.vidHeight; i < n ; i++ ) {
|
||||
float& px = ((float *)depthReadback)[i];
|
||||
float d = px;
|
||||
// the following calculation is based on how the TDM soft particle shader translates the depth value to doom units
|
||||
|
||||
// 0.9995 is practically infinite distance, clamping to 0.9994 clamps to max 30k doom units,
|
||||
// which is more than enough (and prevents a potential division by 0 below)
|
||||
d = (d < 0.9994f) ? d : 0.9994f;
|
||||
|
||||
d = d - 0.9995f; // d is now negative, between -0.0001 and -0.9995
|
||||
d = 1.0f / d;
|
||||
// d *= -3.0f; // this would translate d to distance in doom units, doing it together with the next step
|
||||
|
||||
d *= (-3.0f / 3000.0f); // now 3000 units is 1.0, i.e. completely white (=> more details for closer distances)
|
||||
px = d;
|
||||
}
|
||||
#endif
|
||||
|
||||
qglDrawPixels( glConfig.vidWidth, glConfig.vidHeight, GL_RGBA , GL_UNSIGNED_BYTE, depthReadback );
|
||||
R_StaticFree( depthReadback );
|
||||
qglDrawPixels( glConfig.vidWidth, glConfig.vidHeight, GL_LUMINANCE, GL_FLOAT, depthReadback );
|
||||
R_StaticFree( depthReadback );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -279,6 +279,11 @@ try_again:
|
|||
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
|
||||
if ( r_glDebugContext.GetBool() ) {
|
||||
common->Printf( "Requesting an OpenGL Debug Context (r_glDebugContext is enabled)\n" );
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
|
||||
}
|
||||
|
||||
if ( parms.fullScreen && parms.fullScreenDesktop ) {
|
||||
common->Printf( "Will create a pseudo-fullscreen window at the current desktop resolution\n" );
|
||||
} else {
|
||||
|
@ -435,6 +440,10 @@ try_again:
|
|||
if (SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, r_swapInterval.GetInteger()) < 0)
|
||||
common->Warning("SDL_GL_SWAP_CONTROL not supported");
|
||||
|
||||
if ( r_glDebugContext.GetBool() ) {
|
||||
common->Warning( "r_glDebugContext is set, but not supported by SDL1.2!\n" );
|
||||
}
|
||||
|
||||
r_swapInterval.ClearModified();
|
||||
|
||||
window = SDL_SetVideoMode(parms.width, parms.height, colorbits, flags);
|
||||
|
@ -588,6 +597,18 @@ try_again:
|
|||
}
|
||||
#endif
|
||||
|
||||
glConfig.haveDebugContext = false;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
int cflags = 0;
|
||||
if ( SDL_GL_GetAttribute( SDL_GL_CONTEXT_FLAGS, &cflags ) == 0 ) {
|
||||
glConfig.haveDebugContext = (cflags & SDL_GL_CONTEXT_DEBUG_FLAG) != 0;
|
||||
if ( glConfig.haveDebugContext )
|
||||
common->Printf( "Got a debug context!\n" );
|
||||
else if( r_glDebugContext.GetBool() ) {
|
||||
common->Warning( "Requested a debug context, but didn't get one!\n" );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue