diff --git a/CMakeLists.txt b/CMakeLists.txt index b14d62bd8..0a7400ce1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,8 +30,27 @@ if(CMAKE_CROSSCOMPILING) include(${IMPORT_EXECUTABLES}) endif() +# Recursive function to place PK3 archive source files into a hierarchy of source file in the IDE +function( assort_pk3_source_folder FOLDER_NAME PK3_DIR ) + # Assort source files into folders in the IDE + file(GLOB PK3_SRCS ${PK3_DIR}/*) # Create list of all files in this folder + foreach(PK3_SRC ${PK3_SRCS}) + # If there are subfolders, recurse into them + if(IS_DIRECTORY ${PK3_SRC}) + get_filename_component(DIRNAME ${PK3_SRC} NAME) + # Exclude folder from list of source files + list(REMOVE_ITEM PK3_SRCS ${PK3_SRC}) + # Recurse deeper into the filesystem folder tree + assort_pk3_source_folder( ${FOLDER_NAME}\\${DIRNAME} ${PK3_SRC} ) + endif() + # Assign IDE group for current top-level source files + source_group(${FOLDER_NAME} FILES ${PK3_SRCS}) + endforeach() +endfunction() + # Simplify pk3 building, add_pk3(filename srcdirectory) function( add_pk3 PK3_NAME PK3_DIR ) + # message(STATUS "Creating build rule for PK3 ${PK3_NAME} ${PK3_DIR}") # Generate target name. Just use "pk3" for main pk3 target. string( REPLACE "." "_" PK3_TARGET ${PK3_NAME} ) if( ${PK3_TARGET} STREQUAL "zdoom_pk3" ) @@ -48,14 +67,33 @@ function( add_pk3 PK3_NAME PK3_DIR ) COMMAND zipdir -udf ${ZDOOM_OUTPUT_DIR}/${PK3_NAME} ${PK3_DIR} DEPENDS zipdir ) endif() - # Grab a list of top-level PK3 folder files, so we can conveniently see them in the IDE - file(GLOB PK3_SRCS ${PK3_DIR}/*) + # Create a list of source files for this PK3, for use in the IDE + # Phase 1: Create a list of all source files for this PK3 archive, except + # for a couple of strife image file names that confuse CMake. + file(GLOB_RECURSE PK3_SRCS ${PK3_DIR}/*) + # Exclude from the source list some gzdoom .png files with brackets in the + # file names here, because they confuse CMake. + # This only affects the list of source files shown in the IDE. + # It does not actually remove the files from the PK3 archive. + # First replace that toxic bracket character with something we can handle + string(REPLACE "[" confusing_bracket PK3_SRCS "${PK3_SRCS}") + string(REPLACE "]" confusing_bracket PK3_SRCS "${PK3_SRCS}") + foreach(PK3_SRC ${PK3_SRCS}) # All source files at all levels + # Exclude those quarantined source file source file names that once had a bracket + if(${PK3_SRC} MATCHES confusing_bracket) + # message(STATUS "Ignoring PK3 file name containing brackets "${PK3_SRC}) + list(REMOVE_ITEM PK3_SRCS ${PK3_SRC}) + endif() + endforeach() + # Phase 2: Create the PK3 build rule, including the source file list for the IDE # Touch the zipdir executable here so that the pk3s are forced to - # rebuild each time since their dependecy has "changed." + # rebuild each time since their dependency has "changed." add_custom_target( ${PK3_TARGET} ALL COMMAND ${CMAKE_COMMAND} -E touch $ DEPENDS ${ZDOOM_OUTPUT_DIR}/${PK3_NAME} - SOURCES "${PK3_SRCS}") + SOURCES ${PK3_SRCS}) + # Phase 3: Assign source files to a nice folder structure in the IDE + assort_pk3_source_folder("Source Files" ${PK3_DIR}) endfunction() # Macro for building libraries without debugging information diff --git a/docs/licenses/fxaa.txt b/docs/licenses/fxaa.txt new file mode 100644 index 000000000..fbd143c1d --- /dev/null +++ b/docs/licenses/fxaa.txt @@ -0,0 +1,33 @@ +//---------------------------------------------------------------------------------- +// File: es3-kepler\FXAA/FXAA3_11.h +// SDK Version: v3.00 +// Email: gameworks@nvidia.com +// Site: http://developer.nvidia.com/ +// +// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//---------------------------------------------------------------------------------- diff --git a/specs/udmf_zdoom.txt b/specs/udmf_zdoom.txt index 611a667af..9662af1bb 100644 --- a/specs/udmf_zdoom.txt +++ b/specs/udmf_zdoom.txt @@ -214,6 +214,19 @@ Note: All fields default to false unless mentioned otherwise. damagehazard = ; // Changes damage model to Strife's delayed damage for the given sector. Default = false. floorterrain = ; // Sets the terrain for the sector's floor. Default = 'use the flat texture's terrain definition.' ceilingterrain = ; // Sets the terrain for the sector's ceiling. Default = 'use the flat texture's terrain definition.' + + portal_ceil_alpha = // translucency of ceiling portal (default is 0 (not visible)) + portal_ceil_blocksound = // ceiling portal blocks sound. + portal_ceil_disabled = // ceiling portal disabled. + portal_ceil_nopass = // ceiling portal blocks movement if true. + portal_ceil_norender = // ceiling portal not rendered. + portal_ceil_overlaytype = // defines translucency style, can either be "translucent" or "additive". Default is "translucent". + portal_floor_alpha = // translucency of floor portal (default is 0 (not visible)) + portal_floor_blocksound = // floor portal blocks sound. + portal_floor_disabled = // floor portal disabled. + portal_floor_nopass = // ceiling portal blocks movement if true. + portal_floor_norender = // ceiling portal not rendered. + portal_floor_overlaytype = // defines translucency style, can either be "translucent" or "additive". Default is "translucent". * Note about dropactors diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 039a03b52..ff1772ec7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,6 +9,8 @@ endif() include( CheckCXXSourceCompiles ) include( CheckFunctionExists ) include( CheckCXXCompilerFlag ) +include( CheckIncludeFile ) +include( CheckIncludeFiles ) include( CheckLibraryExists ) include( FindPkgConfig ) include( FindOpenGL ) @@ -119,7 +121,13 @@ if( WIN32 ) PATHS ENV DXSDK_DIR PATH_SUFFIXES Include ) if( NOT D3D_INCLUDE_DIR ) - message( SEND_ERROR "Could not find DirectX 9 header files" ) + # Modern versions of the Windows SDK include d3d9.h. Unfortunately, + # CMake cannot find this file via find_path, so we check for it using + # CHECK_INCLUDE_FILE. + CHECK_INCLUDE_FILE( d3d9.h D3D9_H_FOUND ) + if ( NOT D3D9_H_FOUND ) + message( SEND_ERROR "Could not find DirectX 9 header files" ) + endif() else() include_directories( ${D3D_INCLUDE_DIR} ) endif() @@ -128,35 +136,41 @@ if( WIN32 ) PATHS ENV DXSDK_DIR PATH_SUFFIXES Include ) if( NOT XINPUT_INCLUDE_DIR ) - message( WARNING "Could not find xinput.h. XInput will be disabled." ) - add_definitions( -DNO_XINPUT ) + # Modern versions of the Windows SDK include xinput.h. Unfortunately, + # CMake cannot find this file via find_path, so we check for it using + # CHECK_INCLUDE_FILES. windows.h must be included before xinput.h. + CHECK_INCLUDE_FILES( "windows.h;xinput.h" XINPUT_H_FOUND ) + if( NOT XINPUT_H_FOUND ) + message( WARNING "Could not find xinput.h. XInput will be disabled." ) + add_definitions( -DNO_XINPUT ) + endif() else() include_directories( ${XINPUT_INCLUDE_DIR} ) endif() - find_library( DX_dxguid_LIBRARY dxguid - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Lib Lib/${XBITS} ) find_library( DX_dinput8_LIBRARY dinput8 PATHS ENV DXSDK_DIR PATH_SUFFIXES Lib Lib/${XBITS} ) + find_library( DX_dxguid_LIBRARY dxguid + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Lib Lib/${XBITS} ) - set( DX_LIBS_FOUND YES ) - if( NOT DX_dxguid_LIBRARY ) - set( DX_LIBS_FOUND NO ) - endif() + # Modern versions of the Windows SDK include dinput8.lib. Unfortunately, + # CMake cannot find these libraries via find_library. if( NOT DX_dinput8_LIBRARY ) - set( DX_LIBS_FOUND NO ) + # If we got this far, assume dinput8.lib is in the system library path. + set( DX_dinput8_LIBRARY dinput8 ) endif() - if( NOT DX_LIBS_FOUND ) - message( FATAL_ERROR "Could not find DirectX 9 libraries" ) + # Modern versions of the Windows SDK do NOT include dxguid.lib. Its contents + # were moved to dinput8.lib. + if( NOT DX_dxguid_LIBRARY ) + message( STATUS "Could not find dxguid.lib. Build may fail on old Windows SDKs.") endif() set( ZDOOM_LIBS wsock32 winmm - "${DX_dxguid_LIBRARY}" "${DX_dinput8_LIBRARY}" ole32 user32 @@ -167,6 +181,9 @@ if( WIN32 ) setupapi oleaut32 DelayImp ) + if( DX_dxguid_LIBRARY ) + list( APPEND ZDOOM_LIBS "${DX_dxguid_LIBRARY}" ) + endif() else() if( APPLE ) set( FMOD_SEARCH_PATHS "/Developer/FMOD Programmers API Mac/api" ) @@ -1116,6 +1133,7 @@ set( FASTMATH_SOURCES gl/stereo3d/gl_anaglyph.cpp gl/stereo3d/gl_quadstereo.cpp gl/stereo3d/gl_sidebyside3d.cpp + gl/stereo3d/gl_interleaved3d.cpp gl/dynlights/gl_dynlight.cpp gl/dynlights/gl_glow.cpp gl/dynlights/gl_dynlight1.cpp @@ -1124,12 +1142,14 @@ set( FASTMATH_SOURCES gl/shaders/gl_texshader.cpp gl/shaders/gl_shaderprogram.cpp gl/shaders/gl_presentshader.cpp + gl/shaders/gl_present3dRowshader.cpp gl/shaders/gl_bloomshader.cpp gl/shaders/gl_ambientshader.cpp gl/shaders/gl_blurshader.cpp gl/shaders/gl_colormapshader.cpp gl/shaders/gl_tonemapshader.cpp gl/shaders/gl_lensshader.cpp + gl/shaders/gl_fxaashader.cpp gl/system/gl_interface.cpp gl/system/gl_framebuffer.cpp gl/system/gl_debug.cpp diff --git a/src/actor.h b/src/actor.h index 2ae26e7ef..800548d2c 100644 --- a/src/actor.h +++ b/src/actor.h @@ -755,6 +755,9 @@ public: // What species am I? virtual FName GetSpecies(); + // set translation + void SetTranslation(const char *trname); + double GetBobOffset(double ticfrac = 0) const { if (!(flags2 & MF2_FLOATBOB)) diff --git a/src/d_main.cpp b/src/d_main.cpp index 2dbf1273c..dad2b86d9 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2439,6 +2439,7 @@ void D_DoomMain (void) if (!batchrun) Printf ("ParseTeamInfo: Load team definitions.\n"); TeamLibrary.ParseTeamInfo (); + R_ParseTrnslate(); PClassActor::StaticInit (); // [GRB] Initialize player class list diff --git a/src/dobject.cpp b/src/dobject.cpp index 8fba75aa6..4acb86ce4 100644 --- a/src/dobject.cpp +++ b/src/dobject.cpp @@ -330,7 +330,11 @@ DObject::~DObject () } } } - type->DestroySpecials(this); + + if (nullptr != type) + { + type->DestroySpecials(this); + } } } diff --git a/src/dsectoreffect.cpp b/src/dsectoreffect.cpp index 78e00e6b0..815b49dc4 100644 --- a/src/dsectoreffect.cpp +++ b/src/dsectoreffect.cpp @@ -60,6 +60,7 @@ void DSectorEffect::Destroy() } DSectorEffect::DSectorEffect (sector_t *sector) + : DThinker(STAT_SECTOREFFECT) { m_Sector = sector; } diff --git a/src/g_level.cpp b/src/g_level.cpp index dcf97ba4f..90b31138d 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1207,29 +1207,23 @@ void G_FinishTravel () pnum = int(pawn->player - players); pawn->ChangeStatNum (STAT_PLAYER); pawndup = pawn->player->mo; - start = NULL; assert (pawn != pawndup); - if (pawndup == NULL) - { // Oh no! there was no start for this player! - start = G_PickPlayerStart(pnum, PPS_FORCERANDOM); - if (start != NULL) pawndup = P_SpawnPlayer(start, pnum, (level.flags2 & LEVEL2_PRERAISEWEAPON) ? SPF_WEAPONFULLYUP : 0); - if (pawndup == NULL) - { - pawn->flags |= MF_NOSECTOR | MF_NOBLOCKMAP; - pawn->Destroy(); - continue; - } - } + start = G_PickPlayerStart(pnum, 0); if (start == NULL) { - start = G_PickPlayerStart(pnum, 0); - if (start == NULL) + if (pawndup != nullptr) { Printf(TEXTCOLOR_RED "No player %d start to travel to!\n", pnum + 1); // Move to the coordinates this player had when they left the level. pawn->SetXYZ(pawndup->Pos()); } + else + { + // Could not find a start for this player at all. This really should never happen but if it does, let's better abort. + DThinker::DestroyThinkersInList(STAT_TRAVELLING); + I_Error ("No player %d start to travel to!\n", pnum + 1); + } } oldpawn = pawndup; @@ -1266,8 +1260,11 @@ void G_FinishTravel () pawn->player->camera = pawn; pawn->player->viewheight = pawn->ViewHeight; pawn->flags2 &= ~MF2_BLASTED; - DObject::StaticPointerSubstitution (oldpawn, pawn); - oldpawn->Destroy(); + if (oldpawn != nullptr) + { + DObject::StaticPointerSubstitution (oldpawn, pawn); + oldpawn->Destroy(); + } if (pawndup != NULL) { pawndup->Destroy(); diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index c1253f966..82349c1a7 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -54,6 +54,12 @@ void AFastProjectile::Tick () // Handle movement if (!Vel.isZero() || (Z() != floorz)) { + // force some lateral movement so that collision detection works as intended. + if ((flags & MF_MISSILE) && Vel.X == 0 && Vel.Y == 0 && !IsZeroDamage()) + { + Vel.X = MinVel; + } + frac = Vel / count; changexy = frac.X != 0 || frac.Y != 0; int ripcount = count / 8; @@ -124,7 +130,7 @@ void AFastProjectile::Tick () P_ExplodeMissile (this, NULL, NULL); return; } - if (changexy && ripcount <= 0) + if (!frac.isZero() && ripcount <= 0) { ripcount = count >> 3; Effect(); diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index b7827f6bd..d105ab818 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -1021,7 +1021,7 @@ class CommandDrawNumber : public CommandDrawString usePrefix(false), interpolationSpeed(0), drawValue(0), length(3), lowValue(-1), lowTranslation(CR_UNTRANSLATED), highValue(-1), highTranslation(CR_UNTRANSLATED), value(CONSTANT), - inventoryItem(NULL) + inventoryItem(NULL), cvarName(nullptr) { } @@ -1166,6 +1166,37 @@ class CommandDrawNumber : public CommandDrawString if(parenthesized) sc.MustGetToken(')'); } + else if (sc.Compare("intcvar")) + { + bool parenthesized = sc.CheckToken('('); + + value = INTCVAR; + + if (!parenthesized || !sc.CheckToken(TK_StringConst)) + sc.MustGetToken(TK_Identifier); + + cvarName = sc.String; + + // We have a name, but make sure it exists. If not, send notification so modders + // are aware of the situation. + FBaseCVar *CVar = FindCVar(cvarName, nullptr); + + if (CVar != nullptr) + { + ECVarType cvartype = CVar->GetRealType(); + + if (!(cvartype == CVAR_Bool || cvartype == CVAR_Int)) + { + sc.ScriptMessage("CVar '%s' is not an int or bool", cvarName.GetChars()); + } + } + else + { + sc.ScriptMessage("CVar '%s' does not exist", cvarName.GetChars()); + } + + if (parenthesized) sc.MustGetToken(')'); + } } if(value == INVENTORY) { @@ -1444,6 +1475,24 @@ class CommandDrawNumber : public CommandDrawString num++; } break; + case INTCVAR: + { + FBaseCVar *CVar = GetCVar(statusBar->CPlayer->mo, cvarName); + if (CVar != nullptr) + { + ECVarType cvartype = CVar->GetRealType(); + + if (cvartype == CVAR_Bool || cvartype == CVAR_Int) + { + num = CVar->GetGenericRep(CVAR_Int).Int; + break; + } + } + + // Fallback in case of bad cvar/type. Unset can remove a cvar at will. + num = 0; + break; + } default: break; } if(interpolationSpeed != 0 && (!hudChanged || level.time == 1)) @@ -1522,6 +1571,7 @@ class CommandDrawNumber : public CommandDrawString ACCURACY, STAMINA, KEYS, + INTCVAR, CONSTANT }; @@ -1544,6 +1594,7 @@ class CommandDrawNumber : public CommandDrawString PClassActor *inventoryItem; FString prefixPadding; + FString cvarName; friend class CommandDrawInventoryBar; }; diff --git a/src/gl/data/gl_matrix.cpp b/src/gl/data/gl_matrix.cpp index d8017ad40..115882857 100644 --- a/src/gl/data/gl_matrix.cpp +++ b/src/gl/data/gl_matrix.cpp @@ -442,7 +442,7 @@ VSMatrix::computeNormalMatrix(const FLOATTYPE *aMatrix) mMat3x3[1] * (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) + mMat3x3[2] * (mMat3x3[3] * mMat3x3[7] - mMat3x3[4] * mMat3x3[6]); - invDet = 1.0f/det; + invDet = 1.0/det; mMatrix[0] = (mMat3x3[4] * mMat3x3[8] - mMat3x3[5] * mMat3x3[7]) * invDet; mMatrix[1] = (mMat3x3[5] * mMat3x3[6] - mMat3x3[8] * mMat3x3[3]) * invDet; diff --git a/src/gl/data/gl_vertexbuffer.cpp b/src/gl/data/gl_vertexbuffer.cpp index b18b367c3..d14599eb8 100644 --- a/src/gl/data/gl_vertexbuffer.cpp +++ b/src/gl/data/gl_vertexbuffer.cpp @@ -71,6 +71,7 @@ void FSimpleVertexBuffer::BindVBO() glEnableVertexAttribArray(VATTR_TEXCOORD); glEnableVertexAttribArray(VATTR_COLOR); glDisableVertexAttribArray(VATTR_VERTEX2); + glDisableVertexAttribArray(VATTR_NORMAL); } else { @@ -221,7 +222,7 @@ void FFlatVertexBuffer::OutputResized(int width, int height) vbo_shadowdata[7].Set((float)width, (float)height, 0, 0, 0); Map(); - memcpy(map, &vbo_shadowdata[4], 4 * sizeof(FFlatVertex)); + memcpy(&map[4], &vbo_shadowdata[4], 4 * sizeof(FFlatVertex)); Unmap(); } @@ -236,6 +237,7 @@ void FFlatVertexBuffer::BindVBO() glEnableVertexAttribArray(VATTR_TEXCOORD); glDisableVertexAttribArray(VATTR_COLOR); glDisableVertexAttribArray(VATTR_VERTEX2); + glDisableVertexAttribArray(VATTR_NORMAL); } else { diff --git a/src/gl/data/gl_vertexbuffer.h b/src/gl/data/gl_vertexbuffer.h index 596de35d9..f777118fd 100644 --- a/src/gl/data/gl_vertexbuffer.h +++ b/src/gl/data/gl_vertexbuffer.h @@ -271,6 +271,7 @@ struct FModelVertex { float x, y, z; // world position float u, v; // texture coordinates + unsigned packedNormal; // normal vector as GL_INT_2_10_10_10_REV. void Set(float xx, float yy, float zz, float uu, float vv) { @@ -283,7 +284,13 @@ struct FModelVertex void SetNormal(float nx, float ny, float nz) { - // GZDoom currently doesn't use normals. This function is so that the high level code can pretend it does. + /* + int inx = int(nx * 512); + int iny = int(ny * 512); + int inz = int(nz * 512); + packedNormal = 0x40000000 | ((inx & 1023) << 20) | ((iny & 1023) << 10) | (inz & 1023); + */ + packedNormal = 0; // Per-pixel lighting for models isn't implemented yet so leave this at 0 for now. } }; diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp index 20d0d6078..0e52eedd8 100644 --- a/src/gl/dynlights/a_dynlight.cpp +++ b/src/gl/dynlights/a_dynlight.cpp @@ -391,6 +391,16 @@ void ADynamicLight::UpdateLocation() Prev = target->Pos(); subsector = R_PointInSubsector(Prev); Sector = subsector->sector; + + // Some z-coordinate fudging to prevent the light from getting too close to the floor or ceiling planes. With proper attenuation this would render them invisible. + // A distance of 5 is needed so that the light's effect doesn't become too small. + if (Z() < target->floorz + 5.) SetZ(target->floorz + 5.); + else if (Z() > target->ceilingz - 5.) SetZ(target->ceilingz - 5.); + } + else + { + if (Z() < floorz + 5.) SetZ(floorz + 5.); + else if (Z() > ceilingz - 5.) SetZ(ceilingz - 5.); } diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index 91bd9131e..f3dfbd569 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -119,6 +119,7 @@ void FModelVertexBuffer::BindVBO() glEnableVertexAttribArray(VATTR_VERTEX); glEnableVertexAttribArray(VATTR_TEXCOORD); glEnableVertexAttribArray(VATTR_VERTEX2); + glEnableVertexAttribArray(VATTR_NORMAL); glDisableVertexAttribArray(VATTR_COLOR); } else @@ -245,6 +246,7 @@ unsigned int FModelVertexBuffer::SetupFrame(unsigned int frame1, unsigned int fr glVertexAttribPointer(VATTR_VERTEX, 3, GL_FLOAT, false, sizeof(FModelVertex), &VMO[frame1].x); glVertexAttribPointer(VATTR_TEXCOORD, 2, GL_FLOAT, false, sizeof(FModelVertex), &VMO[frame1].u); glVertexAttribPointer(VATTR_VERTEX2, 3, GL_FLOAT, false, sizeof(FModelVertex), &VMO[frame2].x); + glVertexAttribPointer(VATTR_NORMAL, 4, GL_UNSIGNED_INT_2_10_10_10_REV, false, sizeof(FModelVertex), &VMO[frame2].packedNormal); } else { diff --git a/src/gl/models/gl_voxels.cpp b/src/gl/models/gl_voxels.cpp index 6213eb006..288b1a9db 100644 --- a/src/gl/models/gl_voxels.cpp +++ b/src/gl/models/gl_voxels.cpp @@ -248,6 +248,7 @@ void FVoxelModel::AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3 FModelVertex vert; unsigned int indx[4]; + vert.packedNormal = 0; // currently this is not being used for voxels. vert.u = (((col & 15) * 255 / 16) + 7) / 255.f; vert.v = (((col / 16) * 255 / 16) + 7) / 255.f; @@ -271,6 +272,7 @@ void FVoxelModel::AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3 vert.y = -z3 + PivotZ; indx[3] = AddVertex(vert, check); + mIndices.Push(indx[0]); mIndices.Push(indx[1]); mIndices.Push(indx[3]); diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index ff65fee6d..fde8da353 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -61,6 +61,7 @@ #include "gl/shaders/gl_tonemapshader.h" #include "gl/shaders/gl_colormapshader.h" #include "gl/shaders/gl_lensshader.h" +#include "gl/shaders/gl_fxaashader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/renderer/gl_2ddrawer.h" #include "gl/stereo3d/gl_stereo3d.h" @@ -70,24 +71,24 @@ // CVARs // //========================================================================== -CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); -CUSTOM_CVAR(Float, gl_bloom_amount, 1.4f, 0) +CVAR(Bool, gl_bloom, false, CVAR_ARCHIVE); +CUSTOM_CVAR(Float, gl_bloom_amount, 1.4f, CVAR_ARCHIVE) { if (self < 0.1f) self = 0.1f; } -CVAR(Float, gl_exposure_scale, 1.3f, 0) -CVAR(Float, gl_exposure_min, 0.35f, 0) -CVAR(Float, gl_exposure_base, 0.35f, 0) -CVAR(Float, gl_exposure_speed, 0.05f, 0) +CVAR(Float, gl_exposure_scale, 1.3f, CVAR_ARCHIVE) +CVAR(Float, gl_exposure_min, 0.35f, CVAR_ARCHIVE) +CVAR(Float, gl_exposure_base, 0.35f, CVAR_ARCHIVE) +CVAR(Float, gl_exposure_speed, 0.05f, CVAR_ARCHIVE) -CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Int, gl_tonemap, 0, CVAR_ARCHIVE) { if (self < 0 || self > 5) self = 0; } -CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, 0) +CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, CVAR_ARCHIVE) { if (self < 3 || self > 15 || self % 2 == 0) self = 7; @@ -95,9 +96,17 @@ CUSTOM_CVAR(Int, gl_bloom_kernel_size, 7, 0) CVAR(Bool, gl_lens, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -CVAR(Float, gl_lens_k, -0.12f, 0) -CVAR(Float, gl_lens_kcube, 0.1f, 0) -CVAR(Float, gl_lens_chromatic, 1.12f, 0) +CVAR(Float, gl_lens_k, -0.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Float, gl_lens_kcube, 0.1f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Float, gl_lens_chromatic, 1.12f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +CUSTOM_CVAR(Int, gl_fxaa, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + if (self < 0 || self >= FFXAAShader::Count) + { + self = 0; + } +} CUSTOM_CVAR(Int, gl_ssao, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { @@ -139,6 +148,7 @@ void FGLRenderer::PostProcessScene() TonemapScene(); ColormapScene(); LensDistortScene(); + ApplyFXAA(); } //----------------------------------------------------------------------------- @@ -599,6 +609,51 @@ void FGLRenderer::LensDistortScene() FGLDebug::PopGroup(); } +//----------------------------------------------------------------------------- +// +// Apply FXAA and place the result in the HUD/2D texture +// +//----------------------------------------------------------------------------- + +void FGLRenderer::ApplyFXAA() +{ + if (0 == gl_fxaa) + { + return; + } + + FGLDebug::PushGroup("ApplyFXAA"); + + const GLfloat rpcRes[2] = + { + 1.0f / mBuffers->GetWidth(), + 1.0f / mBuffers->GetHeight() + }; + + FGLPostProcessState savedState; + + mBuffers->BindNextFB(); + mBuffers->BindCurrentTexture(0); + mFXAALumaShader->Bind(); + mFXAALumaShader->InputTexture.Set(0); + RenderScreenQuad(); + mBuffers->NextTexture(); + + mBuffers->BindNextFB(); + mBuffers->BindCurrentTexture(0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + mFXAAShader->Bind(); + mFXAAShader->InputTexture.Set(0); + mFXAAShader->ReciprocalResolution.Set(rpcRes); + RenderScreenQuad(); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + mBuffers->NextTexture(); + + FGLDebug::PopGroup(); +} + //----------------------------------------------------------------------------- // // Copies the rendered screen to its final destination diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index 080593881..2ca58149f 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -2,6 +2,7 @@ #define __GL_RENDERBUFFERS_H #include "gl/shaders/gl_shader.h" +#include "gl/renderer/gl_renderer.h" class FGLBloomTextureLevel { diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index 368db63d2..2895341cc 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -57,7 +57,9 @@ #include "gl/shaders/gl_tonemapshader.h" #include "gl/shaders/gl_colormapshader.h" #include "gl/shaders/gl_lensshader.h" +#include "gl/shaders/gl_fxaashader.h" #include "gl/shaders/gl_presentshader.h" +#include "gl/shaders/gl_present3dRowshader.h" #include "gl/stereo3d/gl_stereo3d.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" @@ -104,6 +106,7 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mTonemapPalette = nullptr; mBuffers = nullptr; mPresentShader = nullptr; + mPresent3dRowShader = nullptr; mBloomExtractShader = nullptr; mBloomCombineShader = nullptr; mExposureExtractShader = nullptr; @@ -118,6 +121,8 @@ FGLRenderer::FGLRenderer(OpenGLFrameBuffer *fb) mDepthBlurShader = nullptr; mSSAOShader = nullptr; mSSAOCombineShader = nullptr; + mFXAAShader = nullptr; + mFXAALumaShader = nullptr; } void gl_LoadModels(); @@ -140,7 +145,10 @@ void FGLRenderer::Initialize(int width, int height) mColormapShader = new FColormapShader(); mTonemapPalette = nullptr; mLensShader = new FLensShader(); + mFXAAShader = new FFXAAShader; + mFXAALumaShader = new FFXAALumaShader; mPresentShader = new FPresentShader(); + mPresent3dRowShader = new FPresent3DRowShader(); m2DDrawer = new F2DDrawer; // needed for the core profile, because someone decided it was a good idea to remove the default VAO. @@ -197,6 +205,7 @@ FGLRenderer::~FGLRenderer() if (mDepthBlurShader) delete mDepthBlurShader; if (mSSAOShader) delete mSSAOShader; if (mSSAOCombineShader) delete mSSAOCombineShader; + if (mPresent3dRowShader) delete mPresent3dRowShader; if (mBloomExtractShader) delete mBloomExtractShader; if (mBloomCombineShader) delete mBloomCombineShader; if (mExposureExtractShader) delete mExposureExtractShader; @@ -207,6 +216,8 @@ FGLRenderer::~FGLRenderer() if (mTonemapPalette) delete mTonemapPalette; if (mColormapShader) delete mColormapShader; if (mLensShader) delete mLensShader; + delete mFXAAShader; + delete mFXAALumaShader; } //========================================================================== diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index a285c9ef7..264aa1d04 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -32,7 +32,10 @@ class FBlurShader; class FTonemapShader; class FColormapShader; class FLensShader; +class FFXAALumaShader; +class FFXAAShader; class FPresentShader; +class FPresent3DRowShader; class F2DDrawer; class FHardwareTexture; @@ -111,7 +114,10 @@ public: FColormapShader *mColormapShader; FHardwareTexture *mTonemapPalette; FLensShader *mLensShader; + FFXAALumaShader *mFXAALumaShader; + FFXAAShader *mFXAAShader; FPresentShader *mPresentShader; + FPresent3DRowShader *mPresent3dRowShader; FTexture *gllight; FTexture *glpart2; @@ -188,6 +194,7 @@ public: void BindTonemapPalette(int texunit); void ClearTonemapPalette(); void LensDistortScene(); + void ApplyFXAA(); void CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma); void DrawPresentTexture(const GL_IRECT &box, bool applyGamma); void Flush(); diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 17f19455e..fa8237cca 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -142,6 +142,7 @@ bool FRenderState::ApplyShader() } glVertexAttrib4fv(VATTR_COLOR, mColor.vec); + glVertexAttrib4fv(VATTR_NORMAL, mNormal.vec); activeShader->muDesaturation.Set(mDesaturation / 255.f); activeShader->muFogEnabled.Set(fogset); @@ -270,12 +271,16 @@ bool FRenderState::ApplyShader() if (mModelMatrixEnabled) { mModelMatrix.matrixToGL(activeShader->modelmatrix_index); + VSMatrix norm; + norm.computeNormalMatrix(mModelMatrix); + mNormalModelMatrix.matrixToGL(activeShader->normalmodelmatrix_index); activeShader->currentModelMatrixState = true; } else if (activeShader->currentModelMatrixState) { activeShader->currentModelMatrixState = false; identityMatrix.matrixToGL(activeShader->modelmatrix_index); + identityMatrix.matrixToGL(activeShader->normalmodelmatrix_index); } return true; } diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index 7bec51be0..3276502f1 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -97,6 +97,7 @@ class FRenderState float mShaderTimer; FVertexBuffer *mVertexBuffer, *mCurrentVertexBuffer; + FStateVec4 mNormal; FStateVec4 mColor; FStateVec4 mCameraPos; FStateVec4 mGlowTop, mGlowBottom; @@ -128,6 +129,8 @@ public: VSMatrix mViewMatrix; VSMatrix mModelMatrix; VSMatrix mTextureMatrix; + VSMatrix mNormalViewMatrix; + VSMatrix mNormalModelMatrix; FRenderState() { @@ -189,6 +192,16 @@ public: void SetClipHeight(float height, float direction); + void SetNormal(FVector3 norm) + { + mNormal.Set(norm.X, norm.Y, norm.Z, 0.f); + } + + void SetNormal(float x, float y, float z) + { + mNormal.Set(x, y, z, 0.f); + } + void SetColor(float r, float g, float b, float a = 1.f, int desat = 0) { mColor.Set(r, g, b, a); diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index 90f3be66f..394ca1626 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -374,6 +374,7 @@ void GLFlat::Draw(int pass, bool trans) // trans only has meaning for GLPASS_LIG } #endif + gl_RenderState.SetNormal(plane.plane.Normal().X, plane.plane.Normal().Z, plane.plane.Normal().Y); switch (pass) { @@ -502,6 +503,11 @@ inline void GLFlat::PutFlat(bool fog) void GLFlat::Process(sector_t * model, int whichplane, bool fog) { plane.GetFromSector(model, whichplane); + if (whichplane != int(ceiling)) + { + // Flip the normal if the source plane has a different orientation than what we are about to render. + plane.plane.FlipVert(); + } if (!fog) { @@ -641,7 +647,7 @@ void GLFlat::ProcessSector(sector_t * frontsector) Colormap.CopyFrom3DLight(light); } renderstyle = STYLE_Translucent; - Process(frontsector, false, false); + Process(frontsector, sector_t::floor, false); } } @@ -700,7 +706,7 @@ void GLFlat::ProcessSector(sector_t * frontsector) Colormap.CopyFrom3DLight(light); } renderstyle = STYLE_Translucent; - Process(frontsector, true, false); + Process(frontsector, sector_t::ceiling, false); } } diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 575ef968d..02fd9b452 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -176,12 +176,12 @@ void GLPortal::DrawPortalStencil() bool GLPortal::Start(bool usestencil, bool doquery) { rendered_portals++; - PortalAll.Clock(); +// PortalAll.Clock(); if (usestencil) { if (!gl_portals) { - PortalAll.Unclock(); +// PortalAll.Unclock(); return false; } @@ -297,7 +297,7 @@ bool GLPortal::Start(bool usestencil, bool doquery) GLRenderer->mCurrentPortal = this; if (PrevPortal != NULL) PrevPortal->PushState(); - PortalAll.Unclock(); +// PortalAll.Unclock(); return true; } @@ -1219,7 +1219,7 @@ void GLEEHorizonPortal::DrawContents() if (sector->GetTexture(sector_t::ceiling) != skyflatnum) { GLHorizonInfo horz; - horz.plane.GetFromSector(sector, true); + horz.plane.GetFromSector(sector, sector_t::ceiling); horz.lightlevel = gl_ClampLight(sector->GetCeilingLight()); horz.colormap = sector->ColorMap; if (portal->mType == PORTS_PLANE) @@ -1232,7 +1232,7 @@ void GLEEHorizonPortal::DrawContents() if (sector->GetTexture(sector_t::floor) != skyflatnum) { GLHorizonInfo horz; - horz.plane.GetFromSector(sector, false); + horz.plane.GetFromSector(sector, sector_t::floor); horz.lightlevel = gl_ClampLight(sector->GetFloorLight()); horz.colormap = sector->ColorMap; if (portal->mType == PORTS_PLANE) diff --git a/src/gl/scene/gl_skydome.cpp b/src/gl/scene/gl_skydome.cpp index 1653d2bb8..015b42016 100644 --- a/src/gl/scene/gl_skydome.cpp +++ b/src/gl/scene/gl_skydome.cpp @@ -114,6 +114,7 @@ void FSkyVertexBuffer::BindVBO() glEnableVertexAttribArray(VATTR_TEXCOORD); glEnableVertexAttribArray(VATTR_COLOR); glDisableVertexAttribArray(VATTR_VERTEX2); + glDisableVertexAttribArray(VATTR_NORMAL); } else { diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 19753f742..ab88f2f9c 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -406,6 +406,7 @@ void GLSprite::Draw(int pass) gl_RenderState.Apply(); FVector3 v[4]; + gl_RenderState.SetNormal(0, 0, 0); CalculateVertices(v); FQuadDrawer qd; diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h index 32dc6ee5b..143029ba7 100644 --- a/src/gl/scene/gl_wall.h +++ b/src/gl/scene/gl_wall.h @@ -60,6 +60,15 @@ struct GLSeg float x1,x2; float y1,y2; float fracleft, fracright; // fractional offset of the 2 vertices on the linedef + + FVector3 Normal() const + { + // we do not use the vector math inlines here because they are not optimized for speed but accuracy in the playsim + float x = y2 - y1; + float y = x1 - x2; + float length = sqrt(x*x + y*y); + return FVector3(x / length, 0, y / length); + } }; struct texcoord diff --git a/src/gl/scene/gl_walls.cpp b/src/gl/scene/gl_walls.cpp index 3a7baec33..db16a941a 100644 --- a/src/gl/scene/gl_walls.cpp +++ b/src/gl/scene/gl_walls.cpp @@ -470,7 +470,7 @@ bool GLWall::DoHorizon(seg_t * seg,sector_t * fs, vertex_t * v1,vertex_t * v2) } else { - hi.plane.GetFromSector(fs, true); + hi.plane.GetFromSector(fs, sector_t::ceiling); hi.lightlevel = gl_ClampLight(fs->GetCeilingLight()); hi.colormap = fs->ColorMap; @@ -498,7 +498,7 @@ bool GLWall::DoHorizon(seg_t * seg,sector_t * fs, vertex_t * v1,vertex_t * v2) } else { - hi.plane.GetFromSector(fs, false); + hi.plane.GetFromSector(fs, sector_t::floor); hi.lightlevel = gl_ClampLight(fs->GetFloorLight()); hi.colormap = fs->ColorMap; diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index a7884b51a..dbc43fd22 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -254,14 +254,13 @@ void GLWall::RenderMirrorSurface() if (GLRenderer->mirrortexture == NULL) return; // For the sphere map effect we need a normal of the mirror surface, - Vector v(glseg.y2-glseg.y1, 0 ,-glseg.x2+glseg.x1); - v.Normalize(); + FVector3 v = glseg.Normal(); if (!gl.legacyMode) { // we use texture coordinates and texture matrix to pass the normal stuff to the shader so that the default vertex buffer format can be used as is. - tcs[LOLFT].u = tcs[LORGT].u = tcs[UPLFT].u = tcs[UPRGT].u = v.X(); - tcs[LOLFT].v = tcs[LORGT].v = tcs[UPLFT].v = tcs[UPRGT].v = v.Z(); + tcs[LOLFT].u = tcs[LORGT].u = tcs[UPLFT].u = tcs[UPRGT].u = v.X; + tcs[LOLFT].v = tcs[LORGT].v = tcs[UPLFT].v = tcs[UPRGT].v = v.Z; gl_RenderState.EnableTextureMatrix(true); gl_RenderState.mTextureMatrix.computeNormalMatrix(gl_RenderState.mViewMatrix); @@ -414,6 +413,7 @@ void GLWall::RenderTranslucentWall() //========================================================================== void GLWall::Draw(int pass) { + gl_RenderState.SetNormal(glseg.Normal()); switch (pass) { case GLPASS_LIGHTSONLY: diff --git a/src/gl/shaders/gl_fxaashader.cpp b/src/gl/shaders/gl_fxaashader.cpp new file mode 100644 index 000000000..277c7fbb3 --- /dev/null +++ b/src/gl/shaders/gl_fxaashader.cpp @@ -0,0 +1,99 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Alexey Lysiuk +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +// +// Fast approXimate Anti-Aliasing (FXAA) post-processing +// + +#include "gl/system/gl_system.h" +#include "gl/shaders/gl_fxaashader.h" + +EXTERN_CVAR(Int, gl_fxaa) + +void FFXAALumaShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/fxaa.fp", "#define FXAA_LUMA_PASS\n", 330); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/fxaa"); + mShader.SetAttribLocation(0, "PositionInProjection"); + InputTexture.Init(mShader, "InputTexture"); + } + + mShader.Bind(); +} + +static int GetMaxVersion() +{ + return gl.glslversion >= 4.f ? 400 : 330; +} + +static FString GetDefines() +{ + int quality; + + switch (gl_fxaa) + { + default: + case FFXAAShader::Low: quality = 10; break; + case FFXAAShader::Medium: quality = 12; break; + case FFXAAShader::High: quality = 29; break; + case FFXAAShader::Extreme: quality = 39; break; + } + + const int gatherAlpha = GetMaxVersion() >= 400 ? 1 : 0; + + // TODO: enable FXAA_GATHER4_ALPHA on OpenGL earlier than 4.0 + // when GL_ARB_gpu_shader5/GL_NV_gpu_shader5 extensions are supported + + FString result; + result.Format( + "#define FXAA_QUALITY__PRESET %i\n" + "#define FXAA_GATHER4_ALPHA %i\n", + quality, gatherAlpha); + + return result; +} + +void FFXAAShader::Bind() +{ + assert(gl_fxaa > 0 && gl_fxaa < Count); + FShaderProgram &shader = mShaders[gl_fxaa]; + + if (!shader) + { + const FString defines = GetDefines(); + const int maxVersion = GetMaxVersion(); + + shader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquad.vp", "", 330); + shader.Compile(FShaderProgram::Fragment, "shaders/glsl/fxaa.fp", defines, maxVersion); + shader.SetFragDataLocation(0, "FragColor"); + shader.Link("shaders/glsl/fxaa"); + shader.SetAttribLocation(0, "PositionInProjection"); + InputTexture.Init(shader, "InputTexture"); + ReciprocalResolution.Init(shader, "ReciprocalResolution"); + } + + shader.Bind(); +} diff --git a/src/gl/shaders/gl_fxaashader.h b/src/gl/shaders/gl_fxaashader.h new file mode 100644 index 000000000..5ebcd232e --- /dev/null +++ b/src/gl/shaders/gl_fxaashader.h @@ -0,0 +1,66 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Alexey Lysiuk +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +// +// Fast approXimate Anti-Aliasing (FXAA) post-processing +// + +#ifndef __GL_FXAASHADER_H__ +#define __GL_FXAASHADER_H__ + +#include "gl_shaderprogram.h" + +class FFXAALumaShader +{ +public: + void Bind(); + + FBufferedUniform1i InputTexture; + +private: + FShaderProgram mShader; +}; + + +class FFXAAShader +{ +public: + enum Quality + { + None, + Low, + Medium, + High, + Extreme, + Count + }; + + void Bind(); + + FBufferedUniform1i InputTexture; + FBufferedUniform2f ReciprocalResolution; + +private: + FShaderProgram mShaders[Count]; +}; + +#endif // __GL_FXAASHADER_H__ diff --git a/src/gl/shaders/gl_present3dRowshader.cpp b/src/gl/shaders/gl_present3dRowshader.cpp new file mode 100644 index 000000000..607e4ede6 --- /dev/null +++ b/src/gl/shaders/gl_present3dRowshader.cpp @@ -0,0 +1,60 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016 Christopher Bruns +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_3dRowshader.cpp +** Copy rendered texture to back buffer, possibly with gamma correction +** while interleaving rows from two independent viewpoint textures, +** representing the left-eye and right-eye views. +** +*/ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "gl/gl_functions.h" +#include "vectors.h" +#include "gl/system/gl_interface.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_cvars.h" +#include "gl/shaders/gl_present3dRowshader.h" + +void FPresent3DRowShader::Bind() +{ + if (!mShader) + { + mShader.Compile(FShaderProgram::Vertex, "shaders/glsl/screenquadscale.vp", "", 330); + mShader.Compile(FShaderProgram::Fragment, "shaders/glsl/present_row3d.fp", "", 330); + mShader.SetFragDataLocation(0, "FragColor"); + mShader.Link("shaders/glsl/presentRow3d"); + mShader.SetAttribLocation(0, "PositionInProjection"); + mShader.SetAttribLocation(1, "UV"); + LeftEyeTexture.Init(mShader, "LeftEyeTexture"); + RightEyeTexture.Init(mShader, "RightEyeTexture"); + InvGamma.Init(mShader, "InvGamma"); + Contrast.Init(mShader, "Contrast"); + Brightness.Init(mShader, "Brightness"); + Scale.Init(mShader, "UVScale"); + VerticalPixelOffset.Init(mShader, "VerticalPixelOffset"); + } + mShader.Bind(); +} diff --git a/src/gl/shaders/gl_present3dRowshader.h b/src/gl/shaders/gl_present3dRowshader.h new file mode 100644 index 000000000..8e3bd772d --- /dev/null +++ b/src/gl/shaders/gl_present3dRowshader.h @@ -0,0 +1,51 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2015 Christopher Bruns +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_present3dRowshader.h +** Final composition and present shader for row-interleaved stereoscopic 3D mode +** +*/ + +#ifndef GL_PRESENT3DROWSHADER_H_ +#define GL_PRESENT3DROWSHADER_H_ + +#include "gl_shaderprogram.h" + +class FPresent3DRowShader +{ +public: + void Bind(); + + FBufferedUniformSampler LeftEyeTexture; + FBufferedUniformSampler RightEyeTexture; + FBufferedUniform1f InvGamma; + FBufferedUniform1f Contrast; + FBufferedUniform1f Brightness; + FBufferedUniform2f Scale; + FBufferedUniform1i VerticalPixelOffset; + +private: + FShaderProgram mShader; +}; + +// GL_PRESENT3DROWSHADER_H_ +#endif \ No newline at end of file diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 59b941e86..9cbd69ce2 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -179,6 +179,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * glBindAttribLocation(hShader, VATTR_TEXCOORD, "aTexCoord"); glBindAttribLocation(hShader, VATTR_COLOR, "aColor"); glBindAttribLocation(hShader, VATTR_VERTEX2, "aVertex2"); + glBindAttribLocation(hShader, VATTR_NORMAL, "aNormal"); glBindFragDataLocation(hShader, 0, "FragColor"); glBindFragDataLocation(hShader, 1, "FragData"); @@ -244,6 +245,8 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * texturematrix_index = glGetUniformLocation(hShader, "TextureMatrix"); vertexmatrix_index = glGetUniformLocation(hShader, "uQuadVertices"); texcoordmatrix_index = glGetUniformLocation(hShader, "uQuadTexCoords"); + normalviewmatrix_index = glGetUniformLocation(hShader, "NormalViewMatrix"); + normalmodelmatrix_index = glGetUniformLocation(hShader, "NormalModelMatrix"); quadmode_index = glGetUniformLocation(hShader, "uQuadMode"); if (!gl.legacyMode && !(gl.flags & RFL_SHADER_STORAGE_BUFFER)) @@ -332,14 +335,14 @@ FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderP // //========================================================================== -void FShader::ApplyMatrices(VSMatrix *proj, VSMatrix *view) +void FShader::ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm) { Bind(); glUniformMatrix4fv(projectionmatrix_index, 1, false, proj->get()); glUniformMatrix4fv(viewmatrix_index, 1, false, view->get()); + glUniformMatrix4fv(normalviewmatrix_index, 1, false, norm->get()); } - //========================================================================== // // @@ -601,23 +604,26 @@ EXTERN_CVAR(Int, gl_fuzztype) void FShaderCollection::ApplyMatrices(VSMatrix *proj, VSMatrix *view) { + VSMatrix norm; + norm.computeNormalMatrix(*view); + for (int i = 0; i < 4; i++) { - mTextureEffects[i]->ApplyMatrices(proj, view); - mTextureEffectsNAT[i]->ApplyMatrices(proj, view); + mTextureEffects[i]->ApplyMatrices(proj, view, &norm); + mTextureEffectsNAT[i]->ApplyMatrices(proj, view, &norm); } - mTextureEffects[4]->ApplyMatrices(proj, view); + mTextureEffects[4]->ApplyMatrices(proj, view, &norm); if (gl_fuzztype != 0) { - mTextureEffects[4 + gl_fuzztype]->ApplyMatrices(proj, view); + mTextureEffects[4 + gl_fuzztype]->ApplyMatrices(proj, view, &norm); } for (unsigned i = 12; i < mTextureEffects.Size(); i++) { - mTextureEffects[i]->ApplyMatrices(proj, view); + mTextureEffects[i]->ApplyMatrices(proj, view, &norm); } for (int i = 0; i < MAX_EFFECTS; i++) { - mEffectShaders[i]->ApplyMatrices(proj, view); + mEffectShaders[i]->ApplyMatrices(proj, view, &norm); } } diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index ce285c3ee..acdd530aa 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -286,7 +286,9 @@ class FShader int lights_index; int projectionmatrix_index; int viewmatrix_index; + int normalviewmatrix_index; int modelmatrix_index; + int normalmodelmatrix_index; int texturematrix_index; public: int vertexmatrix_index; @@ -319,7 +321,7 @@ public: bool Bind(); unsigned int GetHandle() const { return hShader; } - void ApplyMatrices(VSMatrix *proj, VSMatrix *view); + void ApplyMatrices(VSMatrix *proj, VSMatrix *view, VSMatrix *norm); }; diff --git a/src/gl/stereo3d/gl_interleaved3d.cpp b/src/gl/stereo3d/gl_interleaved3d.cpp new file mode 100644 index 000000000..036a84b0b --- /dev/null +++ b/src/gl/stereo3d/gl_interleaved3d.cpp @@ -0,0 +1,124 @@ +/* +** gl_interleaved3d.cpp +** Interleaved image stereoscopic 3D modes for GZDoom +** +**--------------------------------------------------------------------------- +** Copyright 2016 Christopher Bruns +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include "gl_interleaved3d.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderbuffers.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/system/gl_framebuffer.h" +#include "gl/shaders/gl_present3dRowshader.h" + +EXTERN_CVAR(Float, vid_brightness) +EXTERN_CVAR(Float, vid_contrast) +EXTERN_CVAR(Bool, fullscreen) +EXTERN_CVAR(Int, win_y) // pixel position of top of display window + +namespace s3d { + +/* static */ +const RowInterleaved3D& RowInterleaved3D::getInstance(float ipd) +{ + static RowInterleaved3D instance(ipd); + return instance; +} + +RowInterleaved3D::RowInterleaved3D(double ipdMeters) + : TopBottom3D(ipdMeters) +{} + +void RowInterleaved3D::Present() const +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + + + // Bind each eye texture, for composition in the shader + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->mBuffers->BindEyeTexture(1, 1); + + glActiveTexture(GL_TEXTURE0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glActiveTexture(GL_TEXTURE1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + const GL_IRECT& box = GLRenderer->mOutputLetterbox; + glViewport(box.left, box.top, box.width, box.height); + + bool applyGamma = true; + + GLRenderer->mPresent3dRowShader->Bind(); + GLRenderer->mPresent3dRowShader->LeftEyeTexture.Set(0); + GLRenderer->mPresent3dRowShader->RightEyeTexture.Set(1); + + if (!applyGamma || GLRenderer->framebuffer->IsHWGammaActive()) + { + GLRenderer->mPresent3dRowShader->InvGamma.Set(1.0f); + GLRenderer->mPresent3dRowShader->Contrast.Set(1.0f); + GLRenderer->mPresent3dRowShader->Brightness.Set(0.0f); + } + else + { + GLRenderer->mPresent3dRowShader->InvGamma.Set(1.0f / clamp(Gamma, 0.1f, 4.f)); + GLRenderer->mPresent3dRowShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); + GLRenderer->mPresent3dRowShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); + } + GLRenderer->mPresent3dRowShader->Scale.Set( + GLRenderer->mScreenViewport.width / (float)GLRenderer->mBuffers->GetWidth(), + GLRenderer->mScreenViewport.height / (float)GLRenderer->mBuffers->GetHeight()); + + // Compute absolute offset from top of screen to top of current display window + // because we need screen-relative, not window-relative, scan line parity + int windowVOffset = 0; + +#ifdef _WIN32 + if (! fullscreen) { + I_SaveWindowedPos(); // update win_y CVAR + windowVOffset = win_y; + } +#endif // _WIN32 + + GLRenderer->mPresent3dRowShader->VerticalPixelOffset.Set( + windowVOffset // fixme: vary with window location + + box.height % 2 // because we want the top pixel offset, but gl_FragCoord.y is the bottom pixel offset + ); + + GLRenderer->RenderScreenQuad(); +} + + +} /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_interleaved3d.h b/src/gl/stereo3d/gl_interleaved3d.h new file mode 100644 index 000000000..30aef7d5a --- /dev/null +++ b/src/gl/stereo3d/gl_interleaved3d.h @@ -0,0 +1,60 @@ +/* +** gl_interleaved3d.h +** Interleaved stereoscopic 3D modes for GZDoom +** +**--------------------------------------------------------------------------- +** Copyright 2016 Christopher Bruns +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#ifndef GL_INTERLEAVED3D_H_ +#define GL_INTERLEAVED3D_H_ + +#include "gl_stereo3d.h" +#include "gl_stereo_leftright.h" +#include "gl_sidebyside3d.h" +#include "gl/system/gl_system.h" +#include "gl/renderer/gl_renderstate.h" + +class FPresent3DRowShader; + +namespace s3d { + +class RowInterleaved3D : public TopBottom3D +{ +public: + static const RowInterleaved3D& getInstance(float ipd); + RowInterleaved3D(double ipdMeters); + void Present() const override; +}; + +} /* namespace s3d */ + + +#endif /* GL_INTERLEAVED3D_H_ */ diff --git a/src/gl/stereo3d/gl_sidebyside3d.cpp b/src/gl/stereo3d/gl_sidebyside3d.cpp index c6463d2c8..ecb3ec1cc 100644 --- a/src/gl/stereo3d/gl_sidebyside3d.cpp +++ b/src/gl/stereo3d/gl_sidebyside3d.cpp @@ -108,4 +108,43 @@ void SideBySideFull::AdjustPlayerSprites() const /* override */ gl_RenderState.ApplyMatrices(); } +/* static */ +const TopBottom3D& TopBottom3D::getInstance(float ipd) +{ + static TopBottom3D instance(ipd); + return instance; +} + +void TopBottom3D::Present() const +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + + // Compute screen regions to use for left and right eye views + int topHeight = GLRenderer->mOutputLetterbox.height / 2; + int bottomHeight = GLRenderer->mOutputLetterbox.height - topHeight; + GL_IRECT topHalfScreen = GLRenderer->mOutputLetterbox; + topHalfScreen.height = topHeight; + topHalfScreen.top = topHeight; + GL_IRECT bottomHalfScreen = GLRenderer->mOutputLetterbox; + bottomHalfScreen.height = bottomHeight; + bottomHalfScreen.top = 0; + + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(topHalfScreen, true); + + GLRenderer->mBuffers->BindEyeTexture(1, 0); + GLRenderer->DrawPresentTexture(bottomHalfScreen, true); +} + +// AdjustViewports() is called from within FLGRenderer::SetOutputViewport(...) +void TopBottom3D::AdjustViewports() const +{ + // Change size of renderbuffer, and align to screen + GLRenderer->mSceneViewport.height /= 2; + GLRenderer->mSceneViewport.top /= 2; + GLRenderer->mScreenViewport.height /= 2; + GLRenderer->mScreenViewport.top /= 2; +} + } /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_sidebyside3d.h b/src/gl/stereo3d/gl_sidebyside3d.h index 374f255c5..c98f748a0 100644 --- a/src/gl/stereo3d/gl_sidebyside3d.h +++ b/src/gl/stereo3d/gl_sidebyside3d.h @@ -88,6 +88,14 @@ private: SBSFRightEyePose rightEye; }; +class TopBottom3D : public SideBySideSquished +{ +public: + static const TopBottom3D& getInstance(float ipd); + TopBottom3D(double ipdMeters) : SideBySideSquished(ipdMeters) {} + void Present() const override; + virtual void AdjustViewports() const override; +}; } /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_stereo_cvars.cpp b/src/gl/stereo3d/gl_stereo_cvars.cpp index c09a7a974..16296393d 100644 --- a/src/gl/stereo3d/gl_stereo_cvars.cpp +++ b/src/gl/stereo3d/gl_stereo_cvars.cpp @@ -30,6 +30,7 @@ #include "gl/stereo3d/gl_anaglyph.h" #include "gl/stereo3d/gl_quadstereo.h" #include "gl/stereo3d/gl_sidebyside3d.h" +#include "gl/stereo3d/gl_interleaved3d.h" #include "gl/system/gl_cvars.h" // Set up 3D-specific console variables: @@ -100,6 +101,12 @@ const Stereo3DMode& Stereo3DMode::getCurrentMode() setCurrentMode(AmberBlue::getInstance(vr_ipd)); break; // TODO: 10: HTC Vive/OpenVR + case 11: + setCurrentMode(TopBottom3D::getInstance(vr_ipd)); + break; + case 12: + setCurrentMode(RowInterleaved3D::getInstance(vr_ipd)); + break; case 0: default: setCurrentMode(MonoView::getInstance()); diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 94eba0817..6264352f6 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -189,7 +189,9 @@ void OpenGLFrameBuffer::Update() int clientHeight = GetClientHeight(); if (clientWidth > 0 && clientHeight > 0 && (Width != clientWidth || Height != clientHeight)) { - Resize(clientWidth, clientHeight); + // Do not call Resize here because it's only for software canvases + Pitch = Width = clientWidth; + Height = clientHeight; V_OutputResized(Width, Height); GLRenderer->mVBO->OutputResized(Width, Height); } @@ -209,13 +211,13 @@ void OpenGLFrameBuffer::Swap() { Finish.Reset(); Finish.Clock(); - glFinish(); if (needsetgamma) { //DoSetGamma(); needsetgamma = false; } SwapBuffers(); + glFinish(); Finish.Unclock(); swapped = true; FHardwareTexture::UnbindAll(); diff --git a/src/m_swap.h b/src/m_swap.h index f5606e59c..ddcf8eaf9 100644 --- a/src/m_swap.h +++ b/src/m_swap.h @@ -46,6 +46,11 @@ inline short LittleShort(int x) return OSSwapLittleToHostInt16((uint16_t)x); } +inline unsigned short LittleShort(unsigned int x) +{ + return OSSwapLittleToHostInt16((uint16_t)x); +} + inline int LittleLong(int x) { return OSSwapLittleToHostInt32((uint32_t)x); @@ -56,6 +61,16 @@ inline unsigned int LittleLong(unsigned int x) return OSSwapLittleToHostInt32(x); } +inline int LittleLong(long x) +{ + return OSSwapLittleToHostInt32((uint32_t)x); +} + +inline unsigned int LittleLong(unsigned long x) +{ + return OSSwapLittleToHostInt32((uint32_t)x); +} + inline short BigShort(short x) { return (short)OSSwapBigToHostInt16((uint16_t)x); diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 5e5124b2a..7e938230e 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -717,7 +717,11 @@ void M_Drawer (void) if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off) { - if (DMenu::CurrentMenu->DimAllowed()) screen->Dim(fade); + if (DMenu::CurrentMenu->DimAllowed()) + { + screen->Dim(fade); + V_SetBorderNeedRefresh(); + } DMenu::CurrentMenu->Drawer(); } } diff --git a/src/namedef.h b/src/namedef.h index c8b0d1524..36953e385 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -507,6 +507,21 @@ xx(Alphaceiling) xx(Renderstylefloor) xx(Renderstyleceiling) xx(Waterzone) +xx(portal_ceil_alpha) +xx(portal_ceil_blocksound) +xx(portal_ceil_disabled) +xx(portal_ceil_nopass) +xx(portal_ceil_norender) +xx(portal_ceil_overlaytype) +xx(portal_ceil_useglobaltex) +xx(portal_floor_alpha) +xx(portal_floor_blocksound) +xx(portal_floor_disabled) +xx(portal_floor_nopass) +xx(portal_floor_norender) +xx(portal_floor_overlaytype) +xx(portal_floor_useglobaltex) + xx(offsetx_top) xx(offsety_top) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index fefe5cd8f..0a2ccddac 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -754,7 +754,7 @@ void ACSStringPool::ReadStrings(FSerializer &file, const char *key) unsigned bucketnum = h % NUM_BUCKETS; Pool[ii].Hash = h; Pool[ii].Next = PoolBuckets[bucketnum]; - PoolBuckets[bucketnum] = i; + PoolBuckets[bucketnum] = ii; } file.EndObject(); } @@ -4379,10 +4379,11 @@ enum EACSFunctions ACSF_CheckClass = 200, ACSF_DamageActor, // [arookas] ACSF_SetActorFlag, + ACSF_SetTranslation, // ZDaemon ACSF_GetTeamScore = 19620, // (int team) - ACSF_SetTeamScore, // (int team, int value) + ACSF_SetTeamScore, // (int team, int value }; int DLevelScript::SideFromID(int id, int side) @@ -6002,6 +6003,26 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return count; } + case ACSF_SetTranslation: + { + int tid = args[0]; + const char *trname = FBehavior::StaticLookupString(args[1]); + if (tid == 0) + { + if (activator != nullptr) + activator->SetTranslation(trname); + } + else + { + FActorIterator it(tid); + while ((actor = it.Next()) != nullptr) + { + actor->SetTranslation(trname); + } + } + return 1; + } + default: break; } diff --git a/src/p_floor.cpp b/src/p_floor.cpp index 8d09ea2d5..62479e340 100644 --- a/src/p_floor.cpp +++ b/src/p_floor.cpp @@ -342,7 +342,7 @@ bool P_CreateFloor(sector_t *sec, DFloor::EFloor floortype, line_t *line, ceilingheight = sec->FindLowestCeilingPoint(&spot2); floor->m_FloorDestDist = sec->floorplane.PointToDist(spot, newheight); if (sec->floorplane.ZatPointDist(spot2, floor->m_FloorDestDist) > ceilingheight) - floor->m_FloorDestDist = sec->floorplane.PointToDist(spot2, floortype == ceilingheight - height); + floor->m_FloorDestDist = sec->floorplane.PointToDist(spot2, ceilingheight - height); break; case DFloor::floorRaiseToHighest: diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 8d6d75237..496f52e86 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -70,6 +70,7 @@ #include "p_spec.h" #include "p_checkposition.h" #include "serializer.h" +#include "r_utility.h" // MACROS ------------------------------------------------------------------ @@ -1040,11 +1041,11 @@ bool AActor::IsInsideVisibleAngles() const if (players[consoleplayer].camera == nullptr) return true; - DAngle anglestart = VisibleStartAngle.Normalized180(); - DAngle angleend = VisibleEndAngle.Normalized180(); - DAngle pitchstart = VisibleStartPitch.Normalized180(); - DAngle pitchend = VisibleEndPitch.Normalized180(); - + DAngle anglestart = VisibleStartAngle; + DAngle angleend = VisibleEndAngle; + DAngle pitchstart = VisibleStartPitch; + DAngle pitchend = VisibleEndPitch; + if (anglestart > angleend) { DAngle temp = anglestart; @@ -1052,28 +1053,26 @@ bool AActor::IsInsideVisibleAngles() const angleend = temp; } - if (pitchstart > angleend) + if (pitchstart > pitchend) { DAngle temp = pitchstart; pitchstart = pitchend; pitchend = temp; } + - player_t* pPlayer = players[consoleplayer].camera->player; + AActor *mo = players[consoleplayer].camera; - if (pPlayer && pPlayer->mo) + if (mo != nullptr) { - AActor *mo = pPlayer->mo; - DVector3 diffang = Vec3To(mo); + + DVector3 diffang = ViewPos - Pos(); DAngle to = diffang.Angle(); if (!(renderflags & RF_ABSMASKANGLE)) to = deltaangle(Angles.Yaw, to); - // Note that this check is inversed due to only being able to vectorize - // from one way (this actor to the player). It still means to pass - // if the player is within the visible angles. - if ((to <= anglestart || to >= angleend)) + if ((to >= anglestart && to <= angleend)) { to = diffang.Pitch(); if (!(renderflags & RF_ABSMASKPITCH)) @@ -1324,6 +1323,9 @@ bool AActor::Massacre () if (health > 0) { + auto f = flags; + auto f2 = flags2; + flags |= MF_SHOOTABLE; flags2 &= ~(MF2_DORMANT|MF2_INVULNERABLE); do @@ -1332,6 +1334,12 @@ bool AActor::Massacre () P_DamageMobj (this, NULL, NULL, TELEFRAG_DAMAGE, NAME_Massacre); } while (health != prevhealth && health > 0); //abort if the actor wasn't hurt. + if (health > 0) + { + // restore flags if this did not kill the monster. + flags = f; + flags2 = f2; + } return health <= 0; } return false; @@ -3518,7 +3526,7 @@ void AActor::Tick () sector_t *sec = node->m_sector; DVector2 scrollv; - if (level.Scrolls.Size() > (sec-sectors)) + if (level.Scrolls.Size() > unsigned(sec-sectors)) { scrollv = level.Scrolls[sec - sectors]; } @@ -6517,6 +6525,23 @@ int AActor::ApplyDamageFactor(FName damagetype, int damage) const } +void AActor::SetTranslation(const char *trname) +{ + if (*trname == 0) + { + // an empty string resets to the default + Translation = GetDefault()->Translation; + return; + } + + int tnum = R_FindCustomTranslation(trname); + if (tnum >= 0) + { + Translation = tnum; + } + // silently ignore if the name does not exist, this would create some insane message spam otherwise. +} + //---------------------------------------------------------------------------- // // DropItem handling diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 67a447f61..fab7e38d8 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -1049,6 +1049,25 @@ DEFINE_ACTION_FUNCTION(AActor, A_OverlayFlags) return 0; } +//--------------------------------------------------------------------------- +// +// PROC OverlayID +// Because non-action functions cannot acquire the ID of the overlay... +//--------------------------------------------------------------------------- + +DEFINE_ACTION_FUNCTION(AActor, OverlayID) +{ + PARAM_ACTION_PROLOGUE; + + if (ACTION_CALL_FROM_PSPRITE()) + { + ACTION_RETURN_INT(stateinfo->mPSPIndex); + } + ACTION_RETURN_INT(0); +} + + + //--------------------------------------------------------------------------- // // PROC A_Lower diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 170273b43..24e5ff9ee 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -642,6 +642,11 @@ static void ReadOnePlayer(FSerializer &arc, bool skipload) { CopyPlayer(&players[i], &playerTemp, name); } + else + { + // we need the player actor, so that G_FinishTravel can destroy it later. + players[i].mo = playerTemp.mo; + } } else { @@ -755,6 +760,13 @@ static void ReadMultiplePlayers(FSerializer &arc, int numPlayers, int numPlayers } } } + else + { + for (i = 0; i < MAXPLAYERS; ++i) + { + players[i].mo = playertemp[i].mo; + } + } delete[] tempPlayerUsed; delete[] playertemp; diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp index 2c7333a21..f2ece8b3d 100644 --- a/src/p_scroll.cpp +++ b/src/p_scroll.cpp @@ -259,6 +259,7 @@ DScroller::DScroller (EScroll type, double dx, double dy, m_Accel = accel; m_Parts = scrollpos; m_vdx = m_vdy = 0; + m_LastHeight = 0; if ((m_Control = control) != -1) m_LastHeight = sectors[control].CenterFloor () + sectors[control].CenterCeiling (); @@ -342,6 +343,7 @@ DScroller::DScroller (double dx, double dy, const line_t *l, m_vdx = m_vdy = 0; m_Accel = accel; m_Parts = scrollpos; + m_LastHeight = 0; if ((m_Control = control) != -1) m_LastHeight = sectors[control].CenterFloor() + sectors[control].CenterCeiling(); m_Affectee = int(l->sidedef[0] - sides); diff --git a/src/p_udmf.cpp b/src/p_udmf.cpp index c09debc51..c1a64f84b 100644 --- a/src/p_udmf.cpp +++ b/src/p_udmf.cpp @@ -1568,6 +1568,60 @@ public: tagstring = CheckString(key); break; + case NAME_portal_ceil_alpha: + sec->planes[sector_t::ceiling].alpha = CheckFloat(key); + break; + + case NAME_portal_ceil_blocksound: + Flag(sec->planes[sector_t::ceiling].Flags, PLANEF_BLOCKSOUND, key); + break; + + case NAME_portal_ceil_disabled: + Flag(sec->planes[sector_t::ceiling].Flags, PLANEF_DISABLED, key); + break; + + case NAME_portal_ceil_nopass: + Flag(sec->planes[sector_t::ceiling].Flags, PLANEF_NOPASS, key); + break; + + case NAME_portal_ceil_norender: + Flag(sec->planes[sector_t::ceiling].Flags, PLANEF_NORENDER, key); + break; + + case NAME_portal_ceil_overlaytype: + if (!stricmp(CheckString(key), "translucent")) sec->planes[sector_t::ceiling].Flags &= ~PLANEF_ADDITIVE; + else if (!stricmp(CheckString(key), "additive")) sec->planes[sector_t::ceiling].Flags |= PLANEF_ADDITIVE; + break; + + case NAME_portal_floor_alpha: + sec->planes[sector_t::floor].alpha = CheckFloat(key); + break; + + case NAME_portal_floor_blocksound: + Flag(sec->planes[sector_t::floor].Flags, PLANEF_BLOCKSOUND, key); + break; + + case NAME_portal_floor_disabled: + Flag(sec->planes[sector_t::floor].Flags, PLANEF_DISABLED, key); + break; + + case NAME_portal_floor_nopass: + Flag(sec->planes[sector_t::floor].Flags, PLANEF_NOPASS, key); + break; + + case NAME_portal_floor_norender: + Flag(sec->planes[sector_t::floor].Flags, PLANEF_NORENDER, key); + break; + + case NAME_portal_floor_overlaytype: + if (!stricmp(CheckString(key), "translucent")) sec->planes[sector_t::floor].Flags &= ~PLANEF_ADDITIVE; + else if (!stricmp(CheckString(key), "additive")) sec->planes[sector_t::floor].Flags |= PLANEF_ADDITIVE; + break; + + // These two are used by Eternity for something I do not understand. + //case NAME_portal_ceil_useglobaltex: + //case NAME_portal_floor_useglobaltex: + default: break; } diff --git a/src/r_data/r_translate.cpp b/src/r_data/r_translate.cpp index 1ee5e0b06..59dc00e32 100644 --- a/src/r_data/r_translate.cpp +++ b/src/r_data/r_translate.cpp @@ -609,26 +609,26 @@ void FRemapTable::AddToTranslation(const char *range) // //---------------------------------------------------------------------------- -int FRemapTable::StoreTranslation() +int FRemapTable::StoreTranslation(int slot) { unsigned int i; - for (i = 0; i < translationtables[TRANSLATION_Decorate].Size(); i++) + for (i = 0; i < translationtables[slot].Size(); i++) { - if (*this == *translationtables[TRANSLATION_Decorate][i]) + if (*this == *translationtables[slot][i]) { // A duplicate of this translation already exists - return TRANSLATION(TRANSLATION_Decorate, i); + return TRANSLATION(slot, i); } } - if (translationtables[TRANSLATION_Decorate].Size() >= MAX_DECORATE_TRANSLATIONS) + if (translationtables[slot].Size() >= MAX_DECORATE_TRANSLATIONS) { I_Error("Too many DECORATE translations"); } FRemapTable *newtrans = new FRemapTable; *newtrans = *this; - i = translationtables[TRANSLATION_Decorate].Push(newtrans); - return TRANSLATION(TRANSLATION_Decorate, i); + i = translationtables[slot].Push(newtrans); + return TRANSLATION(slot, i); } @@ -1197,3 +1197,90 @@ void R_GetPlayerTranslation (int color, const FPlayerColorSet *colorset, FPlayer R_CreatePlayerTranslation (h, s, v, colorset, skin, table, NULL, NULL); } + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- +static TMap customTranslationMap; + +int R_FindCustomTranslation(const char *name) +{ + if (name == nullptr) + { + return -1; + } + // Ice is a special case which will remain in its original slot. + if (!stricmp(name, "Ice")) + { + return TRANSLATION(TRANSLATION_Standard, 7); + } + int *t = customTranslationMap.CheckKey(FName(name, true)); + return (t != nullptr)? *t : -1; +} + +//---------------------------------------------------------------------------- +// +// +// +//---------------------------------------------------------------------------- + +void R_ParseTrnslate() +{ + customTranslationMap.Clear(); + translationtables[TRANSLATION_Custom].Clear(); + + int lump; + int lastlump = 0; + while (-1 != (lump = Wads.FindLump("TRNSLATE", &lastlump))) + { + FScanner sc(lump); + while (sc.GetToken()) + { + sc.TokenMustBe(TK_Identifier); + + FName newtrans = sc.String; + FRemapTable *base = nullptr; + if (sc.CheckToken(':')) + { + sc.MustGetAnyToken(); + if (sc.TokenType == TK_IntConst) + { + int max = 6; + if (sc.Number < 0 || sc.Number > max) + { + sc.ScriptError("Translation must be in the range [0,%d]", max); + } + base = translationtables[TRANSLATION_Standard][sc.Number]; + } + else if (sc.TokenType == TK_Identifier) + { + int tnum = R_FindCustomTranslation(sc.String); + if (tnum == -1) + { + sc.ScriptError("Base translation '%s' not found in '%s'", sc.String, newtrans.GetChars()); + } + base = translationtables[GetTranslationType(tnum)][GetTranslationIndex(tnum)]; + } + else + { + // error out. + sc.TokenMustBe(TK_Identifier); + } + } + sc.MustGetToken('='); + FRemapTable NewTranslation; + if (base != nullptr) NewTranslation = *base; + else NewTranslation.MakeIdentity(); + do + { + sc.MustGetToken(TK_StringConst); + NewTranslation.AddToTranslation(sc.String); + } while (sc.CheckToken(',')); + + int trans = NewTranslation.StoreTranslation(TRANSLATION_Custom); + customTranslationMap[newtrans] = trans; + } + } +} diff --git a/src/r_data/r_translate.h b/src/r_data/r_translate.h index a549f0759..4ca2f203f 100644 --- a/src/r_data/r_translate.h +++ b/src/r_data/r_translate.h @@ -19,6 +19,7 @@ enum TRANSLATION_Decorate, TRANSLATION_Blood, TRANSLATION_RainPillar, + TRANSLATION_Custom, NUM_TRANSLATION_TABLES }; @@ -42,7 +43,7 @@ struct FRemapTable void AddColorRange(int start, int end, int r1,int g1, int b1, int r2, int g2, int b2); void AddDesaturation(int start, int end, double r1, double g1, double b1, double r2, double g2, double b2); void AddToTranslation(const char * range); - int StoreTranslation(); + int StoreTranslation(int slot); BYTE *Remap; // For the software renderer PalEntry *Palette; // The ideal palette this maps to @@ -109,6 +110,9 @@ extern TArray BloodTranslationColors; int CreateBloodTranslation(PalEntry color); +int R_FindCustomTranslation(const char *name); +void R_ParseTrnslate(); + #endif // __R_TRANSLATE_H diff --git a/src/rapidjson/writer.h b/src/rapidjson/writer.h index c5a3b98a9..a8eeab66d 100644 --- a/src/rapidjson/writer.h +++ b/src/rapidjson/writer.h @@ -321,23 +321,31 @@ protected: } bool WriteDouble(double d) { - if (internal::Double(d).IsNanOrInf()) { - if (!(writeFlags & kWriteNanAndInfFlag)) - return false; - if (internal::Double(d).IsNan()) { - PutReserve(*os_, 3); - PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); - return true; - } - if (internal::Double(d).Sign()) { - PutReserve(*os_, 9); - PutUnsafe(*os_, '-'); - } - else - PutReserve(*os_, 8); - PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); - PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); - return true; + bool ret = true; + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag)) + { + // if we abort here, the writer is left in a broken state, unable to recover, so better write a 0 in addition to returning an error. + ret = false; + d = 0; + } + else + { + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } } char buffer[25]; @@ -345,7 +353,7 @@ protected: PutReserve(*os_, static_cast(end - buffer)); for (char* p = buffer; p != end; ++p) PutUnsafe(*os_, static_cast(*p)); - return true; + return ret; } bool WriteString(const Ch* str, SizeType length) { diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index b073070fd..0db9365c2 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -51,6 +51,7 @@ #include "i_system.h" #include "d_player.h" #include "serializer.h" +#include "v_text.h" // MACROS ------------------------------------------------------------------ @@ -326,6 +327,99 @@ void S_HashSounds () } } +//========================================================================== +// +// S_CheckIntegrity +// +// Scans the entire sound list and looks for recursive definitions. +//========================================================================== + +static bool S_CheckSound(sfxinfo_t *startsfx, sfxinfo_t *sfx, TArray &chain) +{ + sfxinfo_t *me = sfx; + bool success = true; + unsigned siz = chain.Size(); + + if (sfx->bPlayerReserve) + { + return true; + } + + // There is a bad link in here, but let's report it only for the sound that contains the broken definition. + // Once that sound has been disabled this one will work again. + if (chain.Find(sfx) < chain.Size()) + { + return true; + } + chain.Push(sfx); + + if (me->bRandomHeader) + { + const FRandomSoundList *list = &S_rnd[me->link]; + for (int i = 0; i < list->NumSounds; ++i) + { + auto rsfx = &S_sfx[list->Sounds[i]]; + if (rsfx == startsfx) + { + Printf(TEXTCOLOR_RED "recursive sound $random found for %s:\n", startsfx->name.GetChars()); + success = false; + for (unsigned i = 1; i %s\n", chain[i]->name.GetChars()); + } + } + else + { + success &= S_CheckSound(startsfx, rsfx, chain); + } + } + } + else if (me->link != sfxinfo_t::NO_LINK) + { + me = &S_sfx[me->link]; + if (me == startsfx) + { + Printf(TEXTCOLOR_RED "recursive sound $alias found for %s:\n", startsfx->name.GetChars()); + success = false; + for (unsigned i = 1; i %s\n", chain[i]->name.GetChars()); + } + chain.Resize(siz); + } + else + { + success &= S_CheckSound(startsfx, me, chain); + } + } + chain.Pop(); + return success; +} + +void S_CheckIntegrity() +{ + TArray chain; + TArray broken; + + broken.Resize(S_sfx.Size()); + memset(&broken[0], 0, sizeof(bool)*S_sfx.Size()); + for (unsigned i = 0; i < S_sfx.Size(); i++) + { + auto &sfx = S_sfx[i]; + broken[i] = !S_CheckSound(&sfx, &sfx, chain); + } + for (unsigned i = 0; i < S_sfx.Size(); i++) + { + if (broken[i]) + { + auto &sfx = S_sfx[i]; + Printf(TEXTCOLOR_RED "Sound %s has been disabled\n", sfx.name.GetChars()); + sfx.bRandomHeader = false; + sfx.link = 0; // link to the empty sound. + } + } +} + //========================================================================== // // S_PickReplacement @@ -334,13 +428,12 @@ void S_HashSounds () // is not the head of a random list, then the sound passed is returned. //========================================================================== -int S_PickReplacement (int refid) +int S_PickReplacement(int refid) { - if (S_sfx[refid].bRandomHeader) + while (S_sfx[refid].bRandomHeader) { const FRandomSoundList *list = &S_rnd[S_sfx[refid].link]; - - return list->Sounds[pr_randsound() % list->NumSounds]; + refid = list->Sounds[pr_randsound() % list->NumSounds]; } return refid; } @@ -941,6 +1034,7 @@ void S_ParseSndInfo (bool redefine) S_ShrinkPlayerSoundLists (); sfx_empty = Wads.CheckNumForName ("dsempty", ns_sounds); + S_CheckIntegrity(); } //========================================================================== @@ -961,6 +1055,7 @@ void S_AddLocalSndInfo(int lump) } S_ShrinkPlayerSoundLists (); + S_CheckIntegrity(); } //========================================================================== diff --git a/src/s_sound.cpp b/src/s_sound.cpp index 866af965a..524b12175 100644 --- a/src/s_sound.cpp +++ b/src/s_sound.cpp @@ -522,18 +522,19 @@ void S_CacheSound (sfxinfo_t *sfx) { return; } - else if (sfx->bRandomHeader) + sfxinfo_t *orig = sfx; + while (!sfx->bRandomHeader && sfx->link != sfxinfo_t::NO_LINK) { - S_CacheRandomSound (sfx); + sfx = &S_sfx[sfx->link]; + } + if (sfx->bRandomHeader) + { + S_CacheRandomSound(sfx); } else { - while (sfx->link != sfxinfo_t::NO_LINK) - { - sfx = &S_sfx[sfx->link]; - } + S_LoadSound(sfx); sfx->bUsed = true; - S_LoadSound (sfx); } } } @@ -753,8 +754,8 @@ static void CalcPosVel(int type, const AActor *actor, const sector_t *sector, if (type == SOURCE_Actor && actor != NULL) { vel->X = float(actor->Vel.X * TICRATE); - vel->Y = float(actor->Vel.Y * TICRATE); - vel->Z = float(actor->Vel.Z * TICRATE); + vel->Y = float(actor->Vel.Z * TICRATE); + vel->Z = float(actor->Vel.Y * TICRATE); } else { diff --git a/src/serializer.cpp b/src/serializer.cpp index a23459ced..7861701c0 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -215,8 +215,14 @@ struct FWriter void Double(double k) { - if (mWriter1) mWriter1->Double(k); - else if (mWriter2) mWriter2->Double(k); + if (mWriter1) + { + mWriter1->Double(k); + } + else if (mWriter2) + { + mWriter2->Double(k); + } } }; @@ -815,7 +821,6 @@ void FSerializer::WriteObjects() for (unsigned i = 0; i < w->mDObjects.Size(); i++) { auto obj = w->mDObjects[i]; - player_t *player; BeginObject(nullptr); w->Key("classtype"); @@ -1294,7 +1299,7 @@ FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T ** assert(base != nullptr); if (arc.isReading() || !arc.w->inObject() || defval == nullptr || value != *defval) { - ptrdiff_t vv = value == nullptr ? -1 : value - base; + int64_t vv = value == nullptr ? -1 : value - base; Serialize(arc, key, vv, nullptr); value = vv < 0 ? nullptr : base + vv; } diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index 17426a798..58b86201a 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -211,11 +211,11 @@ bool MUSSong2::CheckDone() void MUSSong2::Precache() { - TArray work(MusHeader->NumInstruments); + TArray work(LittleShort(MusHeader->NumInstruments)); const BYTE *used = (BYTE *)MusHeader + sizeof(MUSHeader) / sizeof(BYTE); int i, k; - for (i = k = 0; i < MusHeader->NumInstruments; ++i) + for (i = k = 0; i < LittleShort(MusHeader->NumInstruments); ++i) { BYTE instr = used[k++]; WORD val; diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 635637149..32d4f1ce0 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -288,7 +288,7 @@ static void FinishThingdef() if (func == nullptr) { - VMFunctionBuilder buildit; + VMFunctionBuilder buildit(true); assert(tcall->Proto != nullptr); diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 153c3b597..3b4e6203e 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -3322,6 +3322,22 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) return 0; } +//=========================================================================== +// +// A_SetRenderStyle +// +//=========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetRenderStyle) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_FLOAT(alpha); + PARAM_INT_OPT(mode) { mode = 0; } + + self->Alpha = clamp(alpha, 0., 1.); + self->RenderStyle = ERenderStyle(mode); + return 0; +} + //=========================================================================== // // A_FadeIn @@ -7396,3 +7412,18 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetVisibleRotation) ACTION_RETURN_BOOL(true); } + +//========================================================================== +// +// +// +//========================================================================== + +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslation) +{ + PARAM_SELF_PROLOGUE(AActor); + PARAM_STRING(trname); + + self->SetTranslation(trname); + return 0; +} diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 695b12799..700ac94bb 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3373,7 +3373,7 @@ FxExpression *FxClassMember::Resolve(FCompileContext &ctx) ExpEmit FxClassMember::Emit(VMFunctionBuilder *build) { - if (~membervar->Flags & VARF_Native) + if (build->IsActionFunc && ~membervar->Flags & VARF_Native) { // Check if this is a user-defined variable. // As of right now, FxClassMember is only ever used with FxSelf. // This very user variable was defined in stateowner so if @@ -3948,15 +3948,16 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) //========================================================================== // -// Assumption: This call is being made to generate code inside an action -// method, so the first three address registers are all set up for such a -// function. (self, stateowner, callingstate) +// Assumption: This call is being generated inside a function whose a0 +// register is a self pointer. For action functions, a1 maps to stateowner +// and a2 maps to callingstate. (self, stateowner, callingstate) // //========================================================================== ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) { - assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3); + assert((build->IsActionFunc && build->Registers[REGT_POINTER].GetMostUsed() >= NAP) || + (!build->IsActionFunc && build->Registers[REGT_POINTER].GetMostUsed() >= 1)); int count = (ArgList ? ArgList->Size() : 0); if (count == 1) @@ -3975,8 +3976,18 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) } if (Function->Flags & VARF_Action) { - build->Emit(OP_PARAM, 0, REGT_POINTER, 1); - build->Emit(OP_PARAM, 0, REGT_POINTER, 2); + static_assert(NAP == 3, "This code needs to be updated if NAP changes"); + if (build->IsActionFunc) + { + build->Emit(OP_PARAM, 0, REGT_POINTER, 1); + build->Emit(OP_PARAM, 0, REGT_POINTER, 2); + } + else + { + int null = build->GetConstantAddress(nullptr, ATAG_GENERIC); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, null); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, null); + } count += 2; } // Emit code to pass explicit parameters @@ -4967,7 +4978,8 @@ static int DecoHandleRuntimeState(VMFrameStack *stack, VMValue *param, int numpa ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build) { - assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3); + assert(build->IsActionFunc && build->Registers[REGT_POINTER].GetMostUsed() >= 3 && + "FxRuntimeStateIndex is only valid inside action functions"); ExpEmit out(build, REGT_POINTER); @@ -5139,7 +5151,14 @@ int DecoFindSingleNameState(VMFrameStack *stack, VMValue *param, int numparam, V ExpEmit FxMultiNameState::Emit(VMFunctionBuilder *build) { ExpEmit dest(build, REGT_POINTER); - build->Emit(OP_PARAM, 0, REGT_POINTER, 1); // pass stateowner + if (build->IsActionFunc) + { + build->Emit(OP_PARAM, 0, REGT_POINTER, 1); // pass stateowner + } + else + { + build->Emit(OP_PARAM, 0, REGT_POINTER, 0); // pass self + } for (unsigned i = 0; i < names.Size(); ++i) { build->EmitParamInt(names[i]); diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 8ada05aa6..d60cda823 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -1100,9 +1100,10 @@ DEFINE_PROPERTY(translation, L, Actor) for(int i = 1; i < PROP_PARM_COUNT; i++) { PROP_STRING_PARM(str, i); - if (i== 1 && PROP_PARM_COUNT == 2 && !stricmp(str, "Ice")) + int tnum; + if (i== 1 && PROP_PARM_COUNT == 2 && (tnum = R_FindCustomTranslation(str)) != -1) { - defaults->Translation = TRANSLATION(TRANSLATION_Standard, 7); + defaults->Translation = tnum; return; } else @@ -1110,7 +1111,7 @@ DEFINE_PROPERTY(translation, L, Actor) CurrentTranslation.AddToTranslation(str); } } - defaults->Translation = CurrentTranslation.StoreTranslation (); + defaults->Translation = CurrentTranslation.StoreTranslation (TRANSLATION_Decorate); } } @@ -1437,7 +1438,7 @@ DEFINE_PROPERTY(spriterotation, F, Actor) DEFINE_PROPERTY(visibleangles, Ff, Actor) { PROP_DOUBLE_PARM(visstart, 0); - PROP_DOUBLE_PARM(visend, 0); + PROP_DOUBLE_PARM(visend, 1); defaults->VisibleStartAngle = visstart; defaults->VisibleEndAngle = visend; } @@ -1448,7 +1449,7 @@ DEFINE_PROPERTY(visibleangles, Ff, Actor) DEFINE_PROPERTY(visiblepitch, Ff, Actor) { PROP_DOUBLE_PARM(visstart, 0); - PROP_DOUBLE_PARM(visend, 0); + PROP_DOUBLE_PARM(visend, 1); defaults->VisibleStartPitch = visstart; defaults->VisibleEndPitch = visend; } diff --git a/src/vectors.h b/src/vectors.h index 00a65df50..1852609f2 100644 --- a/src/vectors.h +++ b/src/vectors.h @@ -556,17 +556,29 @@ struct TVector3 // Resizes this vector to be the specified length (if it is not 0) TVector3 &MakeResize(double len) { - double scale = len / Length(); - X = vec_t(X * scale); - Y = vec_t(Y * scale); - Z = vec_t(Z * scale); + double vlen = Length(); + if (vlen != 0.) + { + double scale = len / vlen; + X = vec_t(X * scale); + Y = vec_t(Y * scale); + Z = vec_t(Z * scale); + } return *this; } TVector3 Resized(double len) { - double scale = len / Length(); - return{ vec_t(X * scale), vec_t(Y * scale), vec_t(Z * scale) }; + double vlen = Length(); + if (vlen != 0.) + { + double scale = len / vlen; + return{ vec_t(X * scale), vec_t(Y * scale), vec_t(Z * scale) }; + } + else + { + return *this; + } } // Dot product diff --git a/src/win32/i_xinput.cpp b/src/win32/i_xinput.cpp index b00b4c510..1180f9f32 100644 --- a/src/win32/i_xinput.cpp +++ b/src/win32/i_xinput.cpp @@ -27,6 +27,12 @@ // MACROS ------------------------------------------------------------------ +// This macro is defined by newer versions of xinput.h. In case we are +// compiling with an older version, define it here. +#ifndef XUSER_MAX_COUNT +#define XUSER_MAX_COUNT 4 +#endif + // TYPES ------------------------------------------------------------------- typedef DWORD (WINAPI *XInputGetStateType)(DWORD index, XINPUT_STATE *state); diff --git a/src/win32/win32gliface.cpp b/src/win32/win32gliface.cpp index 7ca001e1e..5d68d8427 100644 --- a/src/win32/win32gliface.cpp +++ b/src/win32/win32gliface.cpp @@ -37,7 +37,9 @@ extern int NewWidth, NewHeight, NewBits, DisplayBits; // these get used before GLEW is initialized so we have to use separate pointers with different names PFNWGLCHOOSEPIXELFORMATARBPROC myWglChoosePixelFormatARB; // = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB"); PFNWGLCREATECONTEXTATTRIBSARBPROC myWglCreateContextAttribsARB; -PFNWGLSWAPINTERVALEXTPROC vsyncfunc; +PFNWGLSWAPINTERVALEXTPROC myWglSwapIntervalExtProc; + + CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) @@ -933,10 +935,32 @@ Win32GLFrameBuffer::Win32GLFrameBuffer(void *hMonitor, int width, int height, in vid_renderer = 0; return; } - - vsyncfunc = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); - HDC hDC = GetDC(Window); + const char *wglext = nullptr; + + myWglSwapIntervalExtProc = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT"); + auto myWglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB"); + if (myWglGetExtensionsStringARB) + { + wglext = myWglGetExtensionsStringARB(hDC); + } + else + { + auto myWglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT"); + if (myWglGetExtensionsStringEXT) + { + wglext = myWglGetExtensionsStringEXT(); + } + } + SwapInterval = 1; + if (wglext != nullptr) + { + if (strstr(wglext, "WGL_EXT_swap_control_tear")) + { + SwapInterval = -1; + } + } + m_supportsGamma = !!GetDeviceGammaRamp(hDC, (void *)m_origGamma); ReleaseDC(Window, hDC); } @@ -1091,7 +1115,7 @@ void Win32GLFrameBuffer::ReleaseResources () void Win32GLFrameBuffer::SetVSync (bool vsync) { - if (vsyncfunc != NULL) vsyncfunc(vsync ? 1 : 0); + if (myWglSwapIntervalExtProc != NULL) myWglSwapIntervalExtProc(vsync ? SwapInterval : 0); } void Win32GLFrameBuffer::SwapBuffers() diff --git a/src/win32/win32gliface.h b/src/win32/win32gliface.h index 6320e2903..e767073c4 100644 --- a/src/win32/win32gliface.h +++ b/src/win32/win32gliface.h @@ -148,6 +148,7 @@ protected: int m_Lock; char m_displayDeviceNameBuffer[CCHDEVICENAME]; char *m_displayDeviceName; + int SwapInterval; friend class Win32GLVideo; diff --git a/src/zscript/vmbuilder.cpp b/src/zscript/vmbuilder.cpp index eb49ffb14..8d7b5a8c1 100644 --- a/src/zscript/vmbuilder.cpp +++ b/src/zscript/vmbuilder.cpp @@ -6,7 +6,7 @@ // //========================================================================== -VMFunctionBuilder::VMFunctionBuilder() +VMFunctionBuilder::VMFunctionBuilder(bool selfcheck) { NumIntConstants = 0; NumFloatConstants = 0; @@ -14,6 +14,7 @@ VMFunctionBuilder::VMFunctionBuilder() NumStringConstants = 0; MaxParam = 0; ActiveParam = 0; + IsActionFunc = selfcheck; } //========================================================================== diff --git a/src/zscript/vmbuilder.h b/src/zscript/vmbuilder.h index 2d8721acc..ed2516c2a 100644 --- a/src/zscript/vmbuilder.h +++ b/src/zscript/vmbuilder.h @@ -23,7 +23,7 @@ public: friend class VMFunctionBuilder; }; - VMFunctionBuilder(); + VMFunctionBuilder(bool checkself = false); ~VMFunctionBuilder(); VMScriptFunction *MakeFunction(); @@ -60,6 +60,9 @@ public: // Track available registers. RegAvailability Registers[4]; + // For use by DECORATE's self/stateowner sanitizer. + bool IsActionFunc; + private: struct AddrKonst { diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 3354b91d6..44b801f51 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -57,6 +57,7 @@ ACTOR Actor native //: Thinker native float GetSpriteAngle(int ptr = AAPTR_DEFAULT); native float GetSpriteRotation(int ptr = AAPTR_DEFAULT); native int GetMissileDamage(int mask, int add, int ptr = AAPTR_DEFAULT); + action native int OverlayID(); // Action functions // Meh, MBF redundant functions. Only for DeHackEd support. @@ -218,6 +219,7 @@ ACTOR Actor native //: Thinker native void A_LogInt(int whattoprint); native void A_LogFloat(float whattoprint); native void A_SetTranslucent(float alpha, int style = 0); + native void A_SetRenderStyle(float alpha, int style); action native A_FadeIn(float reduce = 0.1, int flags = 0); action native A_FadeOut(float reduce = 0.1, int flags = 1); //bool remove == true native void A_FadeTo(float target, float amount = 0.1, int flags = 0); @@ -343,6 +345,7 @@ ACTOR Actor native //: Thinker action native bool A_SetSpriteAngle(float angle = 0, int ptr = AAPTR_DEFAULT); action native bool A_SetSpriteRotation(float angle = 0, int ptr = AAPTR_DEFAULT); action native bool A_SetVisibleRotation(float anglestart = 0, float angleend = 0, float pitchstart = 0, float pitchend = 0, int flags = 0, int ptr = AAPTR_DEFAULT); + native void A_SetTranslation(string transname); native void A_RearrangePointers(int newtarget, int newmaster = AAPTR_DEFAULT, int newtracer = AAPTR_DEFAULT, int flags=0); native void A_TransferPointer(int ptr_source, int ptr_recepient, int sourcefield, int recepientfield=AAPTR_DEFAULT, int flags=0); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index bd6d65643..dce2488c2 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -689,4 +689,22 @@ enum VRF_NOANGLE = VRF_NOANGLESTART|VRF_NOANGLEEND, VRF_NOPITCH = VRF_NOPITCHSTART|VRF_NOPITCHEND, -}; \ No newline at end of file +}; + +enum +{ + STYLE_None, + STYLE_Normal, + STYLE_Fuzzy, + STYLE_SoulTrans, + STYLE_OptFuzzy, + STYLE_Stencil, + STYLE_Translucent, + STYLE_Add, + STYLE_Shaded, + STYLE_TranslucentStencil, + STYLE_Shadow, + STYLE_Subtract, + STYLE_AddStencil, + STYLE_AddShaded, +}; diff --git a/wadsrc/static/filter/game-heretic/sndinfo.txt b/wadsrc/static/filter/game-heretic/sndinfo.txt index b9f1fd7c3..53017a49a 100644 --- a/wadsrc/static/filter/game-heretic/sndinfo.txt +++ b/wadsrc/static/filter/game-heretic/sndinfo.txt @@ -151,6 +151,7 @@ $alias switches/exitbutn switches/normbutn // Heretic has no special exit button plats/pt1_strt pstart plats/pt1_stop pstop plats/pt1_mid dormov +plats/pt2_mid stnmov // // Door Sounds diff --git a/wadsrc/static/filter/game-heretic/sndseq.txt b/wadsrc/static/filter/game-heretic/sndseq.txt new file mode 100644 index 000000000..405ebc8fb --- /dev/null +++ b/wadsrc/static/filter/game-heretic/sndseq.txt @@ -0,0 +1,5 @@ +:Platform + playuntildone plats/pt1_strt + playrepeat plats/pt2_mid + stopsound plats/pt1_stop +end diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index cb3af51aa..547b53218 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -2638,6 +2638,7 @@ GLPREFMNU_BLOOM = "Bloom effect"; GLPREFMNU_LENS = "Lens distortion effect"; GLPREFMNU_SSAO = "Ambient occlusion quality"; GLPREFMNU_SSAO_PORTALS = "Portals with AO"; +GLPREFMNU_FXAA = "FXAA Quality"; // Option Values OPTVAL_SMART = "Smart"; @@ -2708,8 +2709,14 @@ OPTVAL_LEFTEYE = "Left Eye"; OPTVAL_RIGHTEYE = "Right Eye"; OPTVAL_SBSFULL = "Side-by-side Full"; OPTVAL_SBSNARROW = "Side-by-side Narrow"; +OPTVAL_TOPBOTTOM = "Top/Bottom"; +OPTVAL_ROWINTERLEAVED = "Row Interleaved"; OPTVAL_QUADBUFFERED = "Quad-buffered"; OPTVAL_UNCHARTED2 = "Uncharted 2"; OPTVAL_HEJLDAWSON = "Hejl Dawson"; OPTVAL_REINHARD = "Reinhard"; -OPTVAL_PALETTE = "Palette"; \ No newline at end of file +OPTVAL_PALETTE = "Palette"; +OPTVAL_LOW = "Low"; +OPTVAL_MEDIUM = "Medium"; +OPTVAL_HIGH = "High"; +OPTVAL_EXTREME = "Extreme"; diff --git a/wadsrc/static/menudef.zz b/wadsrc/static/menudef.zz index 4784baa93..d5c2bbc5c 100644 --- a/wadsrc/static/menudef.zz +++ b/wadsrc/static/menudef.zz @@ -50,6 +50,15 @@ OptionValue "SSAOModes" 3, "$OPTVAL_HIGH" } +OptionValue "FXAAQuality" +{ + 0, "$OPTVAL_OFF" + 1, "$OPTVAL_LOW" + 2, "$OPTVAL_MEDIUM" + 3, "$OPTVAL_HIGH" + 4, "$OPTVAL_EXTREME" +} + OptionValue "TextureFormats" { 0, "$OPTVAL_RGBA8" @@ -177,6 +186,8 @@ OptionValue VRMode 9, "$OPTVAL_AMBERBLUE" 3, "$OPTVAL_SBSFULL" 4, "$OPTVAL_SBSNARROW" + 11, "$OPTVAL_TOPBOTTOM" + 12, "$OPTVAL_ROWINTERLEAVED" 5, "$OPTVAL_LEFTEYE" 6, "$OPTVAL_RIGHTEYE" 7, "$OPTVAL_QUADBUFFERED" @@ -236,4 +247,5 @@ OptionMenu "GLPrefOptions" Option "$GLPREFMNU_LENS", gl_lens, "OnOff" Option "$GLPREFMNU_SSAO", gl_ssao, "SSAOModes" Slider "$GLPREFMNU_SSAO_PORTALS", gl_ssao_portals, 0.0, 4.0, 1.0, 0 + Option "$GLPREFMNU_FXAA", gl_fxaa, "FXAAQuality" } diff --git a/wadsrc/static/shaders/glsl/fxaa.fp b/wadsrc/static/shaders/glsl/fxaa.fp new file mode 100644 index 000000000..7fcdf8c81 --- /dev/null +++ b/wadsrc/static/shaders/glsl/fxaa.fp @@ -0,0 +1,615 @@ +//---------------------------------------------------------------------------------- +// File: es3-kepler\FXAA/FXAA3_11.h +// SDK Version: v3.00 +// Email: gameworks@nvidia.com +// Site: http://developer.nvidia.com/ +// +// Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of NVIDIA CORPORATION nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//---------------------------------------------------------------------------------- + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D InputTexture; + +#ifdef FXAA_LUMA_PASS + +void main() +{ + vec3 tex = texture(InputTexture, TexCoord).rgb; + vec3 luma = vec3(0.299, 0.587, 0.114); + FragColor = vec4(tex, dot(tex, luma)); +} + +#else // FXAA itself + +//============================================================================ +// NVIDIA FXAA 3.11 by TIMOTHY LOTTES +//============================================================================ + +#define FXAA_DISCARD 1 + +#define FXAA_GREEN_AS_LUMA 0 + +#define FxaaBool bool +#define FxaaDiscard discard +#define FxaaFloat float +#define FxaaFloat2 vec2 +#define FxaaFloat3 vec3 +#define FxaaFloat4 vec4 +#define FxaaHalf float +#define FxaaHalf2 vec2 +#define FxaaHalf3 vec3 +#define FxaaHalf4 vec4 +#define FxaaInt2 ivec2 +#define FxaaSat(x) clamp(x, 0.0, 1.0) +#define FxaaTex sampler2D + +#define FxaaTexTop(t, p) textureLod(t, p, 0.0) +#define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + +#if (FXAA_GATHER4_ALPHA == 1) + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) +#endif + +#if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } +#else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } +#endif + +#if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 +#elif (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 +#elif (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 +#elif (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 +#elif (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 +#elif (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 +#elif (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 +#elif (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 +#elif (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 +#elif (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 +#elif (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 +#elif (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 +#elif (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 +#elif (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 +#elif (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 +#elif (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#elif (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 +#endif + +FxaaFloat4 FxaaPixelShader(FxaaFloat2 pos, FxaaTex tex, FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat fxaaQualitySubpix, FxaaFloat fxaaQualityEdgeThreshold, FxaaFloat fxaaQualityEdgeThresholdMin) +{ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; +/*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif +/*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; +/*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; +/*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; +/*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; +/*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; +/*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); +/*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; +/*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; +/*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; +/*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } + #endif +/*--------------------------------------------------------------------------*/ + } +/*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; +/*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; +/*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat dst = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (dst * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; +/*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif +} + +uniform vec2 ReciprocalResolution; + +void main() +{ + FragColor = FxaaPixelShader(TexCoord, InputTexture, ReciprocalResolution, 0.75f, 0.166f, 0.0833f); +} + +#endif // FXAA_LUMA_PASS diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 0d2b419c1..1f270728d 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -1,6 +1,8 @@ in vec4 pixelpos; in vec2 glowdist; +in vec4 vWorldNormal; +in vec4 vEyeNormal; in vec4 vTexCoord; in vec4 vColor; @@ -124,6 +126,37 @@ float R_DoomLightingEquation(float light) return lightscale; } +//=========================================================================== +// +// Standard lambertian diffuse light calculation +// +//=========================================================================== + +float diffuseContribution(vec3 lightDirection, vec3 normal) +{ + return max(dot(normal, lightDirection), 0.0f); +} + +//=========================================================================== +// +// Calculates the brightness of a dynamic point light +// Todo: Find a better way to define which lighting model to use. +// (Specular mode has been removed for now.) +// +//=========================================================================== + +float pointLightAttenuation(vec4 lightpos) +{ + float attenuation = max(lightpos.w - distance(pixelpos.xyz, lightpos.xyz),0.0) / lightpos.w; + #if 0 + return attenuation; + #else + vec3 lightDirection = normalize(lightpos.xyz - pixelpos.xyz); + float diffuseAmount = diffuseContribution(lightDirection, normalize(vWorldNormal.xyz)); + return attenuation * diffuseAmount; + #endif +} + //=========================================================================== // // Calculate light @@ -199,7 +232,7 @@ vec4 getLightColor(float fogdist, float fogfactor) vec4 lightpos = lights[i]; vec4 lightcolor = lights[i+1]; - lightcolor.rgb *= max(lightpos.w - distance(pixelpos.xyz, lightpos.xyz),0.0) / lightpos.w; + lightcolor.rgb *= pointLightAttenuation(lightpos); dynlight.rgb += lightcolor.rgb; } // @@ -210,7 +243,7 @@ vec4 getLightColor(float fogdist, float fogfactor) vec4 lightpos = lights[i]; vec4 lightcolor = lights[i+1]; - lightcolor.rgb *= max(lightpos.w - distance(pixelpos.xyz, lightpos.xyz),0.0) / lightpos.w; + lightcolor.rgb *= pointLightAttenuation(lightpos); dynlight.rgb -= lightcolor.rgb; } } @@ -318,7 +351,7 @@ void main() vec4 lightpos = lights[i]; vec4 lightcolor = lights[i+1]; - lightcolor.rgb *= max(lightpos.w - distance(pixelpos.xyz, lightpos.xyz),0.0) / lightpos.w; + lightcolor.rgb *= pointLightAttenuation(lightpos); addlight.rgb += lightcolor.rgb; } frag.rgb = clamp(frag.rgb + desaturate(addlight).rgb, 0.0, 1.0); diff --git a/wadsrc/static/shaders/glsl/main.vp b/wadsrc/static/shaders/glsl/main.vp index a2c1bac5b..1de8854c0 100644 --- a/wadsrc/static/shaders/glsl/main.vp +++ b/wadsrc/static/shaders/glsl/main.vp @@ -4,8 +4,12 @@ in vec2 aTexCoord; in vec4 aColor; #ifndef SIMPLE // we do not need these for simple shaders in vec4 aVertex2; +in vec4 aNormal; out vec4 pixelpos; out vec2 glowdist; + +out vec4 vWorldNormal; +out vec4 vEyeNormal; #endif out vec4 vTexCoord; @@ -54,6 +58,9 @@ void main() gl_ClipDistance[3] = -((uSplitTopPlane.w + uSplitTopPlane.x * worldcoord.x + uSplitTopPlane.y * worldcoord.z) * uSplitTopPlane.z) - worldcoord.y; gl_ClipDistance[4] = worldcoord.y + ((uSplitBottomPlane.w + uSplitBottomPlane.x * worldcoord.x + uSplitBottomPlane.y * worldcoord.z) * uSplitBottomPlane.z); } + + vWorldNormal = NormalModelMatrix * aNormal; + vEyeNormal = NormalViewMatrix * vWorldNormal; #endif #ifdef SPHEREMAP diff --git a/wadsrc/static/shaders/glsl/present_row3d.fp b/wadsrc/static/shaders/glsl/present_row3d.fp new file mode 100644 index 000000000..8ae72d1e0 --- /dev/null +++ b/wadsrc/static/shaders/glsl/present_row3d.fp @@ -0,0 +1,35 @@ + +in vec2 TexCoord; +out vec4 FragColor; + +uniform sampler2D LeftEyeTexture; +uniform sampler2D RightEyeTexture; +uniform float InvGamma; +uniform float Contrast; +uniform float Brightness; +uniform int VerticalPixelOffset; // top-of-window might not be top-of-screen + +vec4 ApplyGamma(vec4 c) +{ + vec3 val = c.rgb * Contrast - (Contrast - 1.0) * 0.5; + val += Brightness * 0.5; + val = pow(max(val, vec3(0.0)), vec3(InvGamma)); + return vec4(val, c.a); +} + +void main() +{ + int thisVerticalPixel = int(gl_FragCoord.y + 1.0); // Bottom row is typically the right eye, when WindowHeight is even + bool isLeftEye = (thisVerticalPixel // because we want to alternate eye view on each row + + VerticalPixelOffset // because the window might not be aligned to the screen + ) % 2 == 0; + vec4 inputColor; + if (isLeftEye) { + inputColor = texture(LeftEyeTexture, TexCoord); + } + else { + // inputColor = vec4(0, 1, 0, 1); + inputColor = texture(RightEyeTexture, TexCoord); + } + FragColor = ApplyGamma(inputColor); +} diff --git a/wadsrc/static/shaders/glsl/shaderdefs.i b/wadsrc/static/shaders/glsl/shaderdefs.i index 3701694bc..8c5697a66 100644 --- a/wadsrc/static/shaders/glsl/shaderdefs.i +++ b/wadsrc/static/shaders/glsl/shaderdefs.i @@ -56,5 +56,7 @@ uniform int uQuadMode; uniform mat4 ProjectionMatrix; uniform mat4 ViewMatrix; uniform mat4 ModelMatrix; +uniform mat4 NormalViewMatrix; +uniform mat4 NormalModelMatrix; uniform mat4 TextureMatrix;