diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e26d8746..c90031d13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,6 +155,7 @@ target_architecture(ZDOOM_TARGET_ARCH) if( ${ZDOOM_TARGET_ARCH} MATCHES "x86_64" ) set( HAVE_VM_JIT ON ) + set( HAVE_VULKAN ON ) endif() # no, we're not using external asmjit for now, we made too many modifications to our's. @@ -180,7 +181,11 @@ if( MSVC ) # String pooling # Function-level linking # Disable run-time type information - set( ALL_C_FLAGS "/GF /Gy /GR-" ) + if ( HAVE_VULKAN ) + set( ALL_C_FLAGS "/GF /Gy /GR- /DHAVE_VULKAN" ) + else() + set( ALL_C_FLAGS "/GF /Gy /GR-" ) + endif() # Use SSE 2 as minimum always as the true color drawers needs it for __vectorcall #set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") # This is already the default @@ -229,7 +234,12 @@ if( MSVC ) string(REPLACE " /GR" " " CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} ) else() set( REL_LINKER_FLAGS "" ) - set( ALL_C_FLAGS "-ffp-contract=off" ) + if ( HAVE_VULKAN ) + set( ALL_C_FLAGS "-ffp-contract=off -DHAVE_VULKAN" ) + else() + set( ALL_C_FLAGS "-ffp-contract=off" ) + endif() + set( REL_C_FLAGS "" ) set( DEB_C_FLAGS "" ) @@ -275,9 +285,11 @@ mark_as_advanced( FORCE_INTERNAL_GME ) option(FORCE_INTERNAL_ASMJIT "Use internal asmjit" ON) mark_as_advanced( FORCE_INTERNAL_ASMJIT ) -add_subdirectory( glslang/glslang) -add_subdirectory( glslang/spirv ) -add_subdirectory( glslang/OGLCompilersDLL ) +if (HAVE_VULKAN) + add_subdirectory( glslang/glslang) + add_subdirectory( glslang/spirv ) + add_subdirectory( glslang/OGLCompilersDLL ) +endif() # Fast math flags, required by some subprojects set( ZD_FASTMATH_FLAG "" ) diff --git a/glslang/OGLCompilersDLL/CMakeLists.txt b/glslang/OGLCompilersDLL/CMakeLists.txt index 08d8cefd6..db97e565f 100644 --- a/glslang/OGLCompilersDLL/CMakeLists.txt +++ b/glslang/OGLCompilersDLL/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 2.8.12) +make_release_only() +set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZD_FASTMATH_FLAG}" ) + # Request C++11 if(${CMAKE_VERSION} VERSION_LESS 3.1) # CMake versions before 3.1 do not understand CMAKE_CXX_STANDARD diff --git a/glslang/glslang/CMakeLists.txt b/glslang/glslang/CMakeLists.txt index f3fba3be5..4ac7159b3 100644 --- a/glslang/glslang/CMakeLists.txt +++ b/glslang/glslang/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 2.8.12) +make_release_only() +set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZD_FASTMATH_FLAG}" ) + if(WIN32) add_subdirectory(OSDependent/Windows) elseif(UNIX) diff --git a/glslang/spirv/CMakeLists.txt b/glslang/spirv/CMakeLists.txt index 95439bf5b..c7fc8f983 100644 --- a/glslang/spirv/CMakeLists.txt +++ b/glslang/spirv/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 2.8.12) +make_release_only() +set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${ZD_FASTMATH_FLAG}" ) + # Request C++11 if(${CMAKE_VERSION} VERSION_LESS 3.1) # CMake versions before 3.1 do not understand CMAKE_CXX_STANDARD diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5dadc08c..25c480738 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -460,7 +460,10 @@ add_custom_target( revision_check ALL # Libraries ZDoom needs message( STATUS "Fluid synth libs: ${FLUIDSYNTH_LIBRARIES}" ) -set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${GME_LIBRARIES}" "${CMAKE_DL_LIBS}" "glslang" "SPIRV" "OGLCompiler" ) +set( ZDOOM_LIBS ${ZDOOM_LIBS} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${GME_LIBRARIES}" "${CMAKE_DL_LIBS}" ) +if (HAVE_VULKAN) + set( ZDOOM_LIBS ${ZDOOM_LIBS} "glslang" "SPIRV" "OGLCompiler") +endif() include_directories( "${ZLIB_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GME_INCLUDE_DIR}" ) if( ${HAVE_VM_JIT} ) @@ -504,8 +507,12 @@ set( PLAT_WIN32_SOURCES win32/gl_sysfb.cpp win32/base_sysfb.cpp win32/win32basevideo.cpp - win32/win32glvideo.cpp - win32/win32vulkanvideo.cpp ) + win32/win32glvideo.cpp) + +if (HAVE_VULKAN) + set (PLAT_WIN32_SOURCES ${PLAT_WIN32_SOURCES} win32/win32vulkanvideo.cpp ) +endif() + set( PLAT_POSIX_SOURCES posix/i_cd.cpp posix/i_steam.cpp ) @@ -892,9 +899,10 @@ set( FASTMATH_SOURCES sound/opnmidi/opnmidi_midiplay.cpp sound/opnmidi/opnmidi_opn2.cpp sound/opnmidi/opnmidi_private.cpp - sound/opnmidi/wopn/wopn_file.c - - #Vulkan stuff must go into a separate list later because it needs to be disabled for some platforms + sound/opnmidi/wopn/wopn_file.c) + +#Vulkan stuff must go into a separate list later because it needs to be disabled for some platforms +set (VULKAN_SOURCES rendering/vulkan/system/vk_device.cpp rendering/vulkan/system/vk_swapchain.cpp rendering/vulkan/system/vk_builders.cpp @@ -911,6 +919,10 @@ set( FASTMATH_SOURCES rendering/vulkan/thirdparty/vk_mem_alloc/vk_mem_alloc.cpp ) +if (HAVE_VULKAN) + set (FASTMATH_SOURCES ${FASTMATH_SOURCES} ${VULKAN_SOURCES}) +endif() + set (PCH_SOURCES am_map.cpp b_bot.cpp diff --git a/src/d_main.cpp b/src/d_main.cpp index bda6b3e97..4d2b735ff 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -563,22 +563,23 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) break; case 1: // Doom2.exe compatible with a few relaxed settings - v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT|COMPATF_SPRITESORT| - COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_DEHHEALTH|COMPATF_CROSSDROPOFF| - COMPATF_LIGHT|COMPATF_MASKEDMIDTEX; - w= COMPATF2_FLOORMOVE; + v = COMPATF_SHORTTEX | COMPATF_STAIRINDEX | COMPATF_USEBLOCKING | COMPATF_NODOORLIGHT | COMPATF_SPRITESORT | + COMPATF_TRACE | COMPATF_MISSILECLIP | COMPATF_SOUNDTARGET | COMPATF_DEHHEALTH | COMPATF_CROSSDROPOFF | + COMPATF_LIGHT | COMPATF_MASKEDMIDTEX; + w = COMPATF2_FLOORMOVE | COMPATF2_EXPLODE1; break; case 2: // same as 1 but stricter (NO_PASSMOBJ and INVISIBILITY are also set) - v = COMPATF_SHORTTEX|COMPATF_STAIRINDEX|COMPATF_USEBLOCKING|COMPATF_NODOORLIGHT|COMPATF_SPRITESORT| - COMPATF_TRACE|COMPATF_MISSILECLIP|COMPATF_SOUNDTARGET|COMPATF_NO_PASSMOBJ|COMPATF_LIMITPAIN| - COMPATF_DEHHEALTH|COMPATF_INVISIBILITY|COMPATF_CROSSDROPOFF|COMPATF_CORPSEGIBS|COMPATF_HITSCAN| - COMPATF_WALLRUN|COMPATF_NOTOSSDROPS|COMPATF_LIGHT|COMPATF_MASKEDMIDTEX; - w = COMPATF2_BADANGLES|COMPATF2_FLOORMOVE|COMPATF2_POINTONLINE; + v = COMPATF_SHORTTEX | COMPATF_STAIRINDEX | COMPATF_USEBLOCKING | COMPATF_NODOORLIGHT | COMPATF_SPRITESORT | + COMPATF_TRACE | COMPATF_MISSILECLIP | COMPATF_SOUNDTARGET | COMPATF_NO_PASSMOBJ | COMPATF_LIMITPAIN | + COMPATF_DEHHEALTH | COMPATF_INVISIBILITY | COMPATF_CROSSDROPOFF | COMPATF_CORPSEGIBS | COMPATF_HITSCAN | + COMPATF_WALLRUN | COMPATF_NOTOSSDROPS | COMPATF_LIGHT | COMPATF_MASKEDMIDTEX; + w = COMPATF2_BADANGLES | COMPATF2_FLOORMOVE | COMPATF2_POINTONLINE | COMPATF2_EXPLODE2; break; case 3: // Boom compat mode v = COMPATF_TRACE|COMPATF_SOUNDTARGET|COMPATF_BOOMSCROLL|COMPATF_MISSILECLIP|COMPATF_MASKEDMIDTEX; + w = COMPATF2_EXPLODE1; break; case 4: // Old ZDoom compat mode @@ -587,14 +588,15 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) break; case 5: // MBF compat mode - v = COMPATF_TRACE|COMPATF_SOUNDTARGET|COMPATF_BOOMSCROLL|COMPATF_MISSILECLIP|COMPATF_MUSHROOM| - COMPATF_MBFMONSTERMOVE|COMPATF_NOBLOCKFRIENDS|COMPATF_MASKEDMIDTEX; + v = COMPATF_TRACE | COMPATF_SOUNDTARGET | COMPATF_BOOMSCROLL | COMPATF_MISSILECLIP | COMPATF_MUSHROOM | + COMPATF_MBFMONSTERMOVE | COMPATF_NOBLOCKFRIENDS | COMPATF_MASKEDMIDTEX; + w = COMPATF2_EXPLODE1; break; case 6: // Boom with some added settings to reenable some 'broken' behavior - v = COMPATF_TRACE|COMPATF_SOUNDTARGET|COMPATF_BOOMSCROLL|COMPATF_MISSILECLIP|COMPATF_NO_PASSMOBJ| - COMPATF_INVISIBILITY|COMPATF_CORPSEGIBS|COMPATF_HITSCAN|COMPATF_WALLRUN|COMPATF_NOTOSSDROPS|COMPATF_MASKEDMIDTEX; - w = COMPATF2_POINTONLINE; + v = COMPATF_TRACE | COMPATF_SOUNDTARGET | COMPATF_BOOMSCROLL | COMPATF_MISSILECLIP | COMPATF_NO_PASSMOBJ | + COMPATF_INVISIBILITY | COMPATF_CORPSEGIBS | COMPATF_HITSCAN | COMPATF_WALLRUN | COMPATF_NOTOSSDROPS | COMPATF_MASKEDMIDTEX; + w = COMPATF2_POINTONLINE | COMPATF2_EXPLODE2; break; } @@ -642,6 +644,8 @@ CVAR (Flag, compat_multiexit, compatflags2, COMPATF2_MULTIEXIT); CVAR (Flag, compat_teleport, compatflags2, COMPATF2_TELEPORT); CVAR (Flag, compat_pushwindow, compatflags2, COMPATF2_PUSHWINDOW); CVAR (Flag, compat_checkswitchrange, compatflags2, COMPATF2_CHECKSWITCHRANGE); +CVAR (Flag, compat_explode1, compatflags2, COMPATF2_EXPLODE1); +CVAR (Flag, compat_explode2, compatflags2, COMPATF2_EXPLODE2); CVAR(Bool, vid_activeinbackground, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) diff --git a/src/d_net.cpp b/src/d_net.cpp index ec0a90deb..5b9121a51 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -145,18 +145,7 @@ static int oldentertics; extern bool advancedemo; -CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - // Do not use the separate FPS limit timer if we are limiting FPS with this. - if (self) - { - I_SetFPSLimit(0); - } - else - { - I_SetFPSLimit(-1); - } -} +CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR(Bool, net_ticbalance, false, CVAR_SERVERINFO | CVAR_NOSAVE) CUSTOM_CVAR(Int, net_extratic, 0, CVAR_SERVERINFO | CVAR_NOSAVE) diff --git a/src/doomdef.h b/src/doomdef.h index 14d029f8b..42b516850 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -329,6 +329,8 @@ enum : unsigned int COMPATF2_TELEPORT = 1 << 5, // Don't let indirect teleports trigger sector actions COMPATF2_PUSHWINDOW = 1 << 6, // Disable the window check in CheckForPushSpecial() COMPATF2_CHECKSWITCHRANGE = 1 << 7, // Enable buggy CheckSwitchRange behavior + COMPATF2_EXPLODE1 = 1 << 8, // No vertical explosion thrust + COMPATF2_EXPLODE2 = 1 << 9 // Use original explosion code throughout. }; // Emulate old bugs for select maps. These are not exposed by a cvar diff --git a/src/gamedata/g_mapinfo.cpp b/src/gamedata/g_mapinfo.cpp index c69430b09..969affe21 100644 --- a/src/gamedata/g_mapinfo.cpp +++ b/src/gamedata/g_mapinfo.cpp @@ -1642,6 +1642,8 @@ MapFlagHandlers[] = { "compat_teleport", MITYPE_COMPATFLAG, 0, COMPATF2_TELEPORT }, { "compat_pushwindow", MITYPE_COMPATFLAG, 0, COMPATF2_PUSHWINDOW }, { "compat_checkswitchrange", MITYPE_COMPATFLAG, 0, COMPATF2_CHECKSWITCHRANGE }, + { "compat_explode1", MITYPE_COMPATFLAG, 0, COMPATF2_EXPLODE1 }, + { "compat_explode2", MITYPE_COMPATFLAG, 0, COMPATF2_EXPLODE2 }, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, diff --git a/src/maploader/compatibility.cpp b/src/maploader/compatibility.cpp index a978ec47e..11283dbe1 100644 --- a/src/maploader/compatibility.cpp +++ b/src/maploader/compatibility.cpp @@ -165,6 +165,8 @@ static FCompatOption Options[] = { "teleport", COMPATF2_TELEPORT, SLOT_COMPAT2 }, { "disablepushwindowcheck", COMPATF2_PUSHWINDOW, SLOT_COMPAT2 }, { "checkswitchrange", COMPATF2_CHECKSWITCHRANGE, SLOT_COMPAT2 }, + { "explode1", COMPATF2_EXPLODE1, SLOT_COMPAT2 }, + { "explode2", COMPATF2_EXPLODE2, SLOT_COMPAT2 }, { NULL, 0, 0 } }; diff --git a/src/maploader/strifedialogue.cpp b/src/maploader/strifedialogue.cpp index ea018b6d5..837e90243 100644 --- a/src/maploader/strifedialogue.cpp +++ b/src/maploader/strifedialogue.cpp @@ -157,7 +157,6 @@ bool MapLoader::LoadScriptFile (const char *name, bool include, int type) auto fn = Wads.GetLumpFile(lumpnum); auto wadname = Wads.GetWadName(fn); if (stricmp(wadname, "STRIFE0.WAD") && stricmp(wadname, "STRIFE1.WAD") && stricmp(wadname, "SVE.WAD")) name = nullptr; // Only localize IWAD content. - if (name && !stricmp(name, "SCRIPT00")) name = nullptr; // This only contains random string references which already use the string table. bool res = LoadScriptFile(name, lumpnum, lump, Wads.LumpLength(lumpnum), include, type); return res; @@ -285,7 +284,7 @@ FStrifeDialogueNode *MapLoader::ReadRetailNode (const char *name, FileReader &lu // Convert the rest of the data to our own internal format. - if (name) + if (name && strncmp(speech.Dialogue, "RANDOM_", 7)) { FStringf label("$TXT_DLG_%s_d%d_%s", name, int(pos), TokenFromString(speech.Dialogue).GetChars()); node->Dialogue = GStrings.exists(label.GetChars()+1)? label : FString(speech.Dialogue); @@ -381,7 +380,7 @@ FStrifeDialogueNode *MapLoader::ReadTeaserNode (const char *name, FileReader &lu } // Convert the rest of the data to our own internal format. - if (name) + if (name && strncmp(speech.Dialogue, "RANDOM_", 7)) { FStringf label("$TXT_DLG_%s_d%d_%s", name, pos, TokenFromString(speech.Dialogue).GetChars()); node->Dialogue = GStrings.exists(label.GetChars() + 1)? label : FString(speech.Dialogue); @@ -407,7 +406,7 @@ FStrifeDialogueNode *MapLoader::ReadTeaserNode (const char *name, FileReader &lu // The speaker's name, if any. speech.Dialogue[0] = 0; //speech.Name[16] = 0; - if (name && speech.Name[0]) + if ((name && speech.Name[0])) { FString label = speech.Name; label.ReplaceChars(' ', '_'); diff --git a/src/p_map.cpp b/src/p_map.cpp index 6b7888674..00183fd10 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -5889,7 +5889,8 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom // them far too "active." BossBrains also use the old code // because some user levels require they have a height of 16, // which can make them near impossible to hit with the new code. - if (((flags & RADF_NODAMAGE) || !((bombspot->flags5 | thing->flags5) & MF5_OLDRADIUSDMG)) && !(flags & RADF_OLDRADIUSDAMAGE)) + if ((flags & RADF_NODAMAGE) || (!((bombspot->flags5 | thing->flags5) & MF5_OLDRADIUSDMG) && + !(flags & RADF_OLDRADIUSDAMAGE) && !(thing->Level->i_compatflags2 & COMPATF2_EXPLODE2))) { double points = GetRadiusDamage(false, bombspot, thing, bombdamage, bombdistance, fulldamagedistance, bombsource == thing); double check = int(points) * bombdamage; @@ -5939,7 +5940,10 @@ int P_RadiusAttack(AActor *bombspot, AActor *bombsource, int bombdamage, int bom } thing->Thrust(bombspot->AngleTo(thing), thrust); if (!(flags & RADF_NODAMAGE) || (flags & RADF_THRUSTZ)) - thing->Vel.Z += vz; // this really doesn't work well + { + if (!(thing->Level->i_compatflags2 & COMPATF2_EXPLODE1) || (flags & RADF_THRUSTZ)) + thing->Vel.Z += vz; // this really doesn't work well + } } } } diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 682b2239c..076c3c8df 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -33,8 +33,10 @@ #include "gl_load/gl_load.h" +#ifdef HAVE_VULKAN #define VK_USE_PLATFORM_MACOS_MVK #include "volk/volk.h" +#endif #include "i_common.h" @@ -93,6 +95,7 @@ EXTERN_CVAR(Bool, vid_hidpi) EXTERN_CVAR(Int, vid_defwidth) EXTERN_CVAR(Int, vid_defheight) EXTERN_CVAR(Int, vid_backend) +EXTERN_CVAR(Bool, vk_debug) CUSTOM_CVAR(Bool, vid_autoswitch, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { @@ -365,6 +368,18 @@ public: [ms_window setContentView:vulkanView]; + if (!vk_debug) + { + // Limit MoltenVK logging to errors only + setenv("MVK_CONFIG_LOG_LEVEL", "1", 0); + } + + if (!vid_autoswitch) + { + // CVAR from pre-Vulkan era has a priority over vk_device selection + setenv("MVK_CONFIG_FORCE_LOW_POWER_GPU", "1", 0); + } + try { m_vulkanDevice = new VulkanDevice(); @@ -561,7 +576,7 @@ void SystemBaseFrameBuffer::SetMode(const bool fullscreen, const bool hiDPI) { assert(m_window.screen != nil); assert(m_window.contentView.layer != nil); - m_window.contentView.layer.contentsScale = hiDPI ? m_window.screen.backingScaleFactor : 1.0; + [m_window.contentView layer].contentsScale = hiDPI ? m_window.screen.backingScaleFactor : 1.0; if (fullscreen) { @@ -712,16 +727,6 @@ void I_InitGraphics() // --------------------------------------------------------------------------- - -EXTERN_CVAR(Int, vid_maxfps); -EXTERN_CVAR(Bool, cl_capfps); - -// So Apple doesn't support POSIX timers and I can't find a good substitute short of -// having Objective-C Cocoa events or something like that. -void I_SetFPSLimit(int limit) -{ -} - CUSTOM_CVAR(Bool, vid_hidpi, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { SystemBaseFrameBuffer::UseHiDPI(self); @@ -811,6 +816,7 @@ void I_SetWindowTitle(const char* title) } +#ifdef HAVE_VULKAN void I_GetVulkanDrawableSize(int *width, int *height) { NSWindow* const window = CocoaVideo::GetWindow(); @@ -872,3 +878,4 @@ bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface) const VkResult result = vkCreateMacOSSurfaceMVK(instance, &windowCreateInfo, nullptr, surface); return result == VK_SUCCESS; } +#endif diff --git a/src/posix/hardware.h b/src/posix/hardware.h index 5853d3432..a7918935f 100644 --- a/src/posix/hardware.h +++ b/src/posix/hardware.h @@ -37,37 +37,4 @@ #include "i_video.h" #include "v_video.h" -// Semaphores -#ifdef __APPLE__ -#include -#include -#include -typedef semaphore_t Semaphore; -#define SEMAPHORE_WAIT(sem) \ - while(semaphore_wait(sem) != KERN_SUCCESS){} -#define SEMAPHORE_SIGNAL(sem) \ - semaphore_signal(sem); -#define SEMAPHORE_INIT(sem, shared, value) \ - semaphore_create(mach_task_self(), &sem, shared, value); -#else -#include -typedef sem_t Semaphore; -#define SEMAPHORE_WAIT(sem) \ - do { \ - while(sem_wait(&sem) != 0); \ - int semValue; \ - sem_getvalue(&sem, &semValue); \ - if(semValue < 1) \ - break; \ - } while(true); -#define SEMAPHORE_SIGNAL(sem) \ - sem_post(&sem); -#define SEMAPHORE_INIT(sem, shared, value) \ - sem_init(&sem, shared, value); -#endif - -extern Semaphore FPSLimitSemaphore; -void I_SetFPSLimit(int limit); - - #endif // __HARDWARE_H__ diff --git a/src/posix/sdl/hardware.cpp b/src/posix/sdl/hardware.cpp index 4ea04f4e0..e631fa313 100644 --- a/src/posix/sdl/hardware.cpp +++ b/src/posix/sdl/hardware.cpp @@ -85,82 +85,3 @@ void I_InitGraphics () atterm (I_ShutdownGraphics); } - -/** Remaining code is common to Win32 and Linux **/ - -// VIDEO WRAPPERS --------------------------------------------------------- - -//========================================================================== -// -// SetFPSLimit -// -// Initializes an event timer to fire at a rate of /sec. The video -// update will wait for this timer to trigger before updating. -// -// Pass 0 as the limit for unlimited. -// Pass a negative value for the limit to use the value of vid_maxfps. -// -//========================================================================== - -EXTERN_CVAR(Int, vid_maxfps); -EXTERN_CVAR(Bool, cl_capfps); - -#if !defined(__APPLE__) && !defined(__OpenBSD__) -Semaphore FPSLimitSemaphore; - -static void FPSLimitNotify(sigval val) -{ - SEMAPHORE_SIGNAL(FPSLimitSemaphore) -} - -void I_SetFPSLimit(int limit) -{ - static sigevent FPSLimitEvent; - static timer_t FPSLimitTimer; - static bool FPSLimitTimerEnabled = false; - static bool EventSetup = false; - if(!EventSetup) - { - EventSetup = true; - FPSLimitEvent.sigev_notify = SIGEV_THREAD; - FPSLimitEvent.sigev_signo = 0; - FPSLimitEvent.sigev_value.sival_int = 0; - FPSLimitEvent.sigev_notify_function = FPSLimitNotify; - FPSLimitEvent.sigev_notify_attributes = NULL; - - SEMAPHORE_INIT(FPSLimitSemaphore, 0, 0) - } - - if (limit < 0) - { - limit = vid_maxfps; - } - // Kill any leftover timer. - if (FPSLimitTimerEnabled) - { - timer_delete(FPSLimitTimer); - FPSLimitTimerEnabled = false; - } - if (limit == 0) - { // no limit - DPrintf(DMSG_NOTIFY, "FPS timer disabled\n"); - } - else - { - FPSLimitTimerEnabled = true; - if(timer_create(CLOCK_REALTIME, &FPSLimitEvent, &FPSLimitTimer) == -1) - Printf(DMSG_WARNING, "Failed to create FPS limiter event\n"); - itimerspec period = { {0, 0}, {0, 0} }; - period.it_value.tv_nsec = period.it_interval.tv_nsec = 1000000000 / limit; - if(timer_settime(FPSLimitTimer, 0, &period, NULL) == -1) - Printf(DMSG_WARNING, "Failed to set FPS limiter timer\n"); - DPrintf(DMSG_NOTIFY, "FPS timer set to %u ms\n", (unsigned int) period.it_interval.tv_nsec / 1000000); - } -} -#else -// So Apple doesn't support POSIX timers and I can't find a good substitute short of -// having Objective-C Cocoa events or something like that. -void I_SetFPSLimit(int limit) -{ -} -#endif diff --git a/src/posix/sdl/sdlglvideo.cpp b/src/posix/sdl/sdlglvideo.cpp index 188b3c265..9389dfce8 100644 --- a/src/posix/sdl/sdlglvideo.cpp +++ b/src/posix/sdl/sdlglvideo.cpp @@ -53,7 +53,9 @@ #include "gl/system/gl_framebuffer.h" #include "gl/shaders/gl_shader.h" +#ifdef HAVE_VULKAN #include "rendering/vulkan/system/vk_framebuffer.h" +#endif // MACROS ------------------------------------------------------------------ @@ -69,7 +71,6 @@ extern IVideo *Video; EXTERN_CVAR (Int, vid_adapter) EXTERN_CVAR (Int, vid_displaybits) -EXTERN_CVAR (Int, vid_maxfps) EXTERN_CVAR (Int, vid_defwidth) EXTERN_CVAR (Int, vid_defheight) EXTERN_CVAR (Int, vid_backend) @@ -100,9 +101,11 @@ namespace Priv static TOptProc NAME("SDL_" #NAME) SDL2_OPTIONAL_FUNCTION(int, GetWindowBordersSize, SDL_Window *window, int *top, int *left, int *bottom, int *right); +#ifdef HAVE_VULKAN SDL2_OPTIONAL_FUNCTION(void, Vulkan_GetDrawableSize, SDL_Window *window, int *width, int *height); SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_GetInstanceExtensions, SDL_Window *window, unsigned int *count, const char **names); SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_CreateSurface, SDL_Window *window, VkInstance instance, VkSurfaceKHR *surface); +#endif #undef SDL2_OPTIONAL_FUNCTION @@ -191,11 +194,14 @@ public: DFrameBuffer *CreateFrameBuffer (); private: +#ifdef HAVE_VULKAN VulkanDevice *device = nullptr; +#endif }; // CODE -------------------------------------------------------------------- +#ifdef HAVE_VULKAN void I_GetVulkanDrawableSize(int *width, int *height) { assert(Priv::vulkanEnabled); @@ -217,6 +223,7 @@ bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface) assert(Priv::window != nullptr); return Priv::Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE; } +#endif SDLVideo::SDLVideo () @@ -233,6 +240,7 @@ SDLVideo::SDLVideo () Priv::library.Load({ "libSDL2.so", "libSDL2-2.0.so" }); } +#ifdef HAVE_VULKAN Priv::vulkanEnabled = vid_backend == 0 && Priv::Vulkan_GetDrawableSize && Priv::Vulkan_GetInstanceExtensions && Priv::Vulkan_CreateSurface; @@ -245,6 +253,7 @@ SDLVideo::SDLVideo () Priv::vulkanEnabled = false; } } +#endif } SDLVideo::~SDLVideo () @@ -257,6 +266,7 @@ DFrameBuffer *SDLVideo::CreateFrameBuffer () SystemBaseFrameBuffer *fb = nullptr; // first try Vulkan, if that fails OpenGL +#ifdef HAVE_VULKAN if (Priv::vulkanEnabled) { try @@ -270,6 +280,7 @@ DFrameBuffer *SDLVideo::CreateFrameBuffer () Priv::vulkanEnabled = false; } } +#endif if (fb == nullptr) { @@ -302,8 +313,10 @@ int SystemBaseFrameBuffer::GetClientWidth() { int width = 0; +#ifdef HAVE_VULKAN assert(Priv::vulkanEnabled); Priv::Vulkan_GetDrawableSize(Priv::window, &width, nullptr); +#endif return width; } @@ -312,8 +325,10 @@ int SystemBaseFrameBuffer::GetClientHeight() { int height = 0; +#ifdef HAVE_VULKAN assert(Priv::vulkanEnabled); Priv::Vulkan_GetDrawableSize(Priv::window, nullptr, &height); +#endif return height; } @@ -472,13 +487,6 @@ void SystemGLFrameBuffer::SetVSync( bool vsync ) void SystemGLFrameBuffer::SwapBuffers() { -#if !defined(__APPLE__) && !defined(__OpenBSD__) - if (vid_maxfps && !cl_capfps) - { - SEMAPHORE_WAIT(FPSLimitSemaphore) - } -#endif - SDL_GL_SwapWindow(Priv::window); } diff --git a/src/rendering/gl/system/gl_framebuffer.cpp b/src/rendering/gl/system/gl_framebuffer.cpp index 482338e63..d5a00b72a 100644 --- a/src/rendering/gl/system/gl_framebuffer.cpp +++ b/src/rendering/gl/system/gl_framebuffer.cpp @@ -250,6 +250,7 @@ void OpenGLFrameBuffer::Swap() Finish.Reset(); Finish.Clock(); if (swapbefore) glFinish(); + FPSLimit(); SwapBuffers(); if (!swapbefore) glFinish(); Finish.Unclock(); diff --git a/src/rendering/vulkan/renderer/vk_postprocess.cpp b/src/rendering/vulkan/renderer/vk_postprocess.cpp index 8d5c8a9d7..1304efe39 100644 --- a/src/rendering/vulkan/renderer/vk_postprocess.cpp +++ b/src/rendering/vulkan/renderer/vk_postprocess.cpp @@ -195,6 +195,11 @@ void VkPostprocess::DrawPresentTexture(const IntRect &box, bool applyGamma, bool uniforms.Scale = { screen->mScreenViewport.width / (float)fb->GetBuffers()->GetWidth(), -screen->mScreenViewport.height / (float)fb->GetBuffers()->GetHeight() }; uniforms.Offset = { 0.0f, 1.0f }; + if (applyGamma && fb->swapChain->swapChainFormat.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT) + { + uniforms.InvGamma *= 2.2f; + } + VkPPRenderState renderstate; renderstate.Shader = &hw_postprocess.present.Present; renderstate.Uniforms.Set(uniforms); @@ -347,7 +352,7 @@ VkPPTexture::VkPPTexture(PPTexture *texture) PipelineBarrier barrier0; barrier0.addImage(Image.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT); - barrier0.execute(fb->GetUploadCommands(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); + barrier0.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); void *data = Staging->Map(0, totalsize); memcpy(data, texture->Data.get(), totalsize); @@ -359,18 +364,26 @@ VkPPTexture::VkPPTexture(PPTexture *texture) region.imageExtent.depth = 1; region.imageExtent.width = texture->Width; region.imageExtent.height = texture->Height; - fb->GetUploadCommands()->copyBufferToImage(Staging->buffer, Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + fb->GetTransferCommands()->copyBufferToImage(Staging->buffer, Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); PipelineBarrier barrier1; barrier1.addImage(Image.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); - barrier1.execute(fb->GetUploadCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + barrier1.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); Layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + if (fb->device->transferFamily != fb->device->graphicsFamily) + { + PipelineBarrier transfer; + transfer.addQueueTransfer(fb->device->transferFamily, fb->device->graphicsFamily, Image.get(), Layout); + transfer.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + transfer.execute(fb->GetPreDrawCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + } } else { PipelineBarrier barrier; barrier.addImage(Image.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT); - barrier.execute(fb->GetUploadCommands(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + barrier.execute(fb->GetPreDrawCommands(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); Layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } } diff --git a/src/rendering/vulkan/renderer/vk_renderbuffers.h b/src/rendering/vulkan/renderer/vk_renderbuffers.h index 9e783bb48..63b84f996 100644 --- a/src/rendering/vulkan/renderer/vk_renderbuffers.h +++ b/src/rendering/vulkan/renderer/vk_renderbuffers.h @@ -35,7 +35,7 @@ public: static const int NumPipelineImages = 2; std::unique_ptr PipelineImage[NumPipelineImages]; std::unique_ptr PipelineView[NumPipelineImages]; - VkImageLayout PipelineLayout[NumPipelineImages]; + VkImageLayout PipelineLayout[NumPipelineImages] = { VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; std::unique_ptr Shadowmap; std::unique_ptr ShadowmapView; diff --git a/src/rendering/vulkan/shaders/vk_shader.cpp b/src/rendering/vulkan/shaders/vk_shader.cpp index af9de97ce..c0fee6dbd 100644 --- a/src/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/rendering/vulkan/shaders/vk_shader.cpp @@ -220,6 +220,7 @@ std::unique_ptr VkShaderManager::LoadVertShader(FString shadername { FString code = GetTargetGlslVersion(); code << defines << shaderBindings; + if (!device->UsedDeviceFeatures.shaderClipDistance) code << "#define NO_CLIPDISTANCE_SUPPORT\n"; code << "#line 1\n"; code << LoadPrivateShaderLump(vert_lump).GetChars() << "\n"; @@ -233,6 +234,7 @@ std::unique_ptr VkShaderManager::LoadFragShader(FString shadername FString code = GetTargetGlslVersion(); code << defines << shaderBindings; + if (!device->UsedDeviceFeatures.shaderClipDistance) code << "#define NO_CLIPDISTANCE_SUPPORT\n"; if (!alphatest) code << "#define NO_ALPHATEST\n"; if (gbufferpass) code << "#define GBUFFER_PASS\n"; diff --git a/src/rendering/vulkan/system/vk_buffers.cpp b/src/rendering/vulkan/system/vk_buffers.cpp index c5a3d780d..493bd8762 100644 --- a/src/rendering/vulkan/system/vk_buffers.cpp +++ b/src/rendering/vulkan/system/vk_buffers.cpp @@ -42,7 +42,15 @@ void VKBuffer::SetData(size_t size, const void *data, bool staticdata) memcpy(dst, data, size); mStaging->Unmap(); - fb->GetUploadCommands()->copyBuffer(mStaging.get(), mBuffer.get()); + fb->GetTransferCommands()->copyBuffer(mStaging.get(), mBuffer.get()); + + if (fb->device->transferFamily != fb->device->graphicsFamily) + { + PipelineBarrier transfer; + transfer.addQueueTransfer(fb->device->transferFamily, fb->device->graphicsFamily, mBuffer.get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_SHADER_READ_BIT); + transfer.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + transfer.execute(fb->GetPreDrawCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + } } else { @@ -78,7 +86,22 @@ void VKBuffer::SetSubData(size_t offset, size_t size, const void *data) memcpy(dst, data, size); mStaging->Unmap(); - fb->GetUploadCommands()->copyBuffer(mStaging.get(), mBuffer.get(), offset, offset, size); + if (fb->device->transferFamily != fb->device->graphicsFamily) + { + PipelineBarrier transfer; + transfer.addQueueTransfer(fb->device->graphicsFamily, fb->device->transferFamily, mBuffer.get(), VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_SHADER_READ_BIT, 0); + transfer.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + } + + fb->GetTransferCommands()->copyBuffer(mStaging.get(), mBuffer.get(), offset, offset, size); + + if (fb->device->transferFamily != fb->device->graphicsFamily) + { + PipelineBarrier transfer; + transfer.addQueueTransfer(fb->device->transferFamily, fb->device->graphicsFamily, mBuffer.get(), VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_SHADER_READ_BIT); + transfer.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + transfer.execute(fb->GetPreDrawCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + } } else { diff --git a/src/rendering/vulkan/system/vk_builders.h b/src/rendering/vulkan/system/vk_builders.h index 49fc0a8b7..3d9201697 100644 --- a/src/rendering/vulkan/system/vk_builders.h +++ b/src/rendering/vulkan/system/vk_builders.h @@ -292,6 +292,8 @@ public: void addBuffer(VulkanBuffer *buffer, VkDeviceSize offset, VkDeviceSize size, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); void addImage(VulkanImage *image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, int baseMipLevel = 0, int levelCount = 1); void addImage(VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, int baseMipLevel = 0, int levelCount = 1); + void addQueueTransfer(int srcFamily, int dstFamily, VulkanBuffer *buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask); + void addQueueTransfer(int srcFamily, int dstFamily, VulkanImage *image, VkImageLayout layout, VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, int baseMipLevel = 0, int levelCount = 1); void execute(VulkanCommandBuffer *commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags = 0); @@ -301,6 +303,24 @@ private: FixedSizeVector imageMemoryBarriers; }; +class QueueSubmit +{ +public: + QueueSubmit(); + + void addCommandBuffer(VulkanCommandBuffer *buffer); + void addWait(VkPipelineStageFlags waitStageMask, VulkanSemaphore *semaphore); + void addSignal(VulkanSemaphore *semaphore); + void execute(VulkanDevice *device, VkQueue queue, VulkanFence *fence = nullptr); + +private: + VkSubmitInfo submitInfo = {}; + FixedSizeVector waitSemaphores; + FixedSizeVector waitStages; + FixedSizeVector signalSemaphores; + FixedSizeVector commandBuffers; +}; + class WriteDescriptors { public: @@ -1140,6 +1160,37 @@ inline void PipelineBarrier::addImage(VkImage image, VkImageLayout oldLayout, Vk imageMemoryBarriers.push_back(barrier); } +inline void PipelineBarrier::addQueueTransfer(int srcFamily, int dstFamily, VulkanBuffer *buffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask) +{ + VkBufferMemoryBarrier barrier = { }; + barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; + barrier.srcAccessMask = srcAccessMask; + barrier.dstAccessMask = dstAccessMask; + barrier.srcQueueFamilyIndex = srcFamily; + barrier.dstQueueFamilyIndex = dstFamily; + barrier.buffer = buffer->buffer; + barrier.offset = 0; + barrier.size = buffer->size; + bufferMemoryBarriers.push_back(barrier); +} + +inline void PipelineBarrier::addQueueTransfer(int srcFamily, int dstFamily, VulkanImage *image, VkImageLayout layout, VkImageAspectFlags aspectMask, int baseMipLevel, int levelCount) +{ + VkImageMemoryBarrier barrier = { }; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.oldLayout = layout; + barrier.newLayout = layout; + barrier.srcQueueFamilyIndex = srcFamily; + barrier.dstQueueFamilyIndex = dstFamily; + barrier.image = image->image; + barrier.subresourceRange.aspectMask = aspectMask; + barrier.subresourceRange.baseMipLevel = baseMipLevel; + barrier.subresourceRange.levelCount = levelCount; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + imageMemoryBarriers.push_back(barrier); +} + inline void PipelineBarrier::execute(VulkanCommandBuffer *commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags) { commandBuffer->pipelineBarrier( @@ -1151,6 +1202,44 @@ inline void PipelineBarrier::execute(VulkanCommandBuffer *commandBuffer, VkPipel ///////////////////////////////////////////////////////////////////////////// +inline QueueSubmit::QueueSubmit() +{ + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; +} + +inline void QueueSubmit::addCommandBuffer(VulkanCommandBuffer *buffer) +{ + commandBuffers.push_back(buffer->buffer); + submitInfo.pCommandBuffers = commandBuffers.data(); + submitInfo.commandBufferCount = (uint32_t)commandBuffers.size(); +} + +inline void QueueSubmit::addWait(VkPipelineStageFlags waitStageMask, VulkanSemaphore *semaphore) +{ + waitStages.push_back(waitStageMask); + waitSemaphores.push_back(semaphore->semaphore); + + submitInfo.pWaitDstStageMask = waitStages.data(); + submitInfo.pWaitSemaphores = waitSemaphores.data(); + submitInfo.waitSemaphoreCount = (uint32_t)waitSemaphores.size(); +} + +inline void QueueSubmit::addSignal(VulkanSemaphore *semaphore) +{ + signalSemaphores.push_back(semaphore->semaphore); + submitInfo.pSignalSemaphores = signalSemaphores.data(); + submitInfo.signalSemaphoreCount = (uint32_t)signalSemaphores.size(); +} + +inline void QueueSubmit::execute(VulkanDevice *device, VkQueue queue, VulkanFence *fence) +{ + VkResult result = vkQueueSubmit(device->graphicsQueue, 1, &submitInfo, fence ? fence->fence : VK_NULL_HANDLE); + if (result < VK_SUCCESS) + throw std::runtime_error("Failed to submit command buffer"); +} + +///////////////////////////////////////////////////////////////////////////// + inline void WriteDescriptors::addBuffer(VulkanDescriptorSet *descriptorSet, int binding, VkDescriptorType type, VulkanBuffer *buffer) { addBuffer(descriptorSet, binding, type, buffer, 0, buffer->size); diff --git a/src/rendering/vulkan/system/vk_device.cpp b/src/rendering/vulkan/system/vk_device.cpp index 30aeeda7b..26e45ef2f 100644 --- a/src/rendering/vulkan/system/vk_device.cpp +++ b/src/rendering/vulkan/system/vk_device.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include "vk_device.h" #include "vk_swapchain.h" @@ -75,14 +76,8 @@ VulkanDevice::VulkanDevice() InitVolk(); CreateInstance(); CreateSurface(); - - UsedDeviceFeatures.samplerAnisotropy = VK_TRUE; - UsedDeviceFeatures.shaderClipDistance = VK_TRUE; - UsedDeviceFeatures.fragmentStoresAndAtomics = VK_TRUE; - UsedDeviceFeatures.depthClamp = VK_TRUE; - UsedDeviceFeatures.shaderClipDistance = VK_TRUE; - SelectPhysicalDevice(); + SelectFeatures(); CreateDevice(); CreateAllocator(); } @@ -98,14 +93,20 @@ VulkanDevice::~VulkanDevice() ReleaseResources(); } -bool VulkanDevice::CheckFeatures(const VkPhysicalDeviceFeatures &f) +void VulkanDevice::SelectFeatures() +{ + UsedDeviceFeatures.samplerAnisotropy = PhysicalDevice.Features.samplerAnisotropy; + UsedDeviceFeatures.fragmentStoresAndAtomics = PhysicalDevice.Features.fragmentStoresAndAtomics; + UsedDeviceFeatures.depthClamp = PhysicalDevice.Features.depthClamp; + UsedDeviceFeatures.shaderClipDistance = PhysicalDevice.Features.shaderClipDistance; +} + +bool VulkanDevice::CheckRequiredFeatures(const VkPhysicalDeviceFeatures &f) { return f.samplerAnisotropy == VK_TRUE && - f.shaderClipDistance == VK_TRUE && f.fragmentStoresAndAtomics == VK_TRUE && - f.depthClamp == VK_TRUE && - f.shaderClipDistance == VK_TRUE; + f.depthClamp == VK_TRUE; } void VulkanDevice::SelectPhysicalDevice() @@ -116,29 +117,51 @@ void VulkanDevice::SelectPhysicalDevice() { const auto &info = AvailableDevices[idx]; - if (!CheckFeatures(info.Features)) + if (!CheckRequiredFeatures(info.Features)) continue; VulkanCompatibleDevice dev; dev.device = &AvailableDevices[idx]; - int i = 0; - for (const auto& queueFamily : info.QueueFamilies) + // Figure out what can present + for (int i = 0; i < (int)info.QueueFamilies.size(); i++) { - // Only accept a decent GPU for now.. + VkBool32 presentSupport = false; + VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(info.Device, i, surface, &presentSupport); + if (result == VK_SUCCESS && info.QueueFamilies[i].queueCount > 0 && presentSupport) + { + dev.presentFamily = i; + break; + } + } + + // Look for family that can do both graphics and transfer + for (int i = 0; i < (int)info.QueueFamilies.size(); i++) + { + const auto &queueFamily = info.QueueFamilies[i]; VkQueueFlags gpuFlags = (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT); if (queueFamily.queueCount > 0 && (queueFamily.queueFlags & gpuFlags) == gpuFlags) { dev.graphicsFamily = i; dev.transferFamily = i; + break; } + } - VkBool32 presentSupport = false; - VkResult result = vkGetPhysicalDeviceSurfaceSupportKHR(info.Device, i, surface, &presentSupport); - if (result == VK_SUCCESS && queueFamily.queueCount > 0 && presentSupport) - dev.presentFamily = i; - - i++; + // OK we didn't find any. Look for any match now. + if (dev.graphicsFamily == -1) + { + for (int i = 0; i < (int)info.QueueFamilies.size(); i++) + { + const auto &queueFamily = info.QueueFamilies[i]; + if (queueFamily.queueCount > 0) + { + if (dev.graphicsFamily == -1 && (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT)) + dev.graphicsFamily = i; + if (dev.transferFamily == -1 && (queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT)) + dev.transferFamily = i; + } + } } std::set requiredExtensionSearch(EnabledDeviceExtensions.begin(), EnabledDeviceExtensions.end()); @@ -163,8 +186,8 @@ void VulkanDevice::SelectPhysicalDevice() static const int typeSort[] = { 4, 1, 0, 2, 3 }; int sortA = a.device->Properties.deviceType < 5 ? typeSort[a.device->Properties.deviceType] : (int)a.device->Properties.deviceType; int sortB = b.device->Properties.deviceType < 5 ? typeSort[b.device->Properties.deviceType] : (int)b.device->Properties.deviceType; - if (sortA < sortB) - return true; + if (sortA != sortB) + return sortA < sortB; // Then sort by the device's unique ID so that vk_device uses a consistent order int sortUUID = memcmp(a.device->Properties.pipelineCacheUUID, b.device->Properties.pipelineCacheUUID, VK_UUID_SIZE); @@ -193,10 +216,16 @@ void VulkanDevice::SelectPhysicalDevice() transferFamily = SupportedDevices[selected].transferFamily; } +bool VulkanDevice::SupportsDeviceExtension(const char *ext) const +{ + return std::find(EnabledDeviceExtensions.begin(), EnabledDeviceExtensions.end(), ext) != EnabledDeviceExtensions.end(); +} + void VulkanDevice::CreateAllocator() { VmaAllocatorCreateInfo allocinfo = {}; - // allocinfo.flags = VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; // To do: enable this for better performance + if (SupportsDeviceExtension(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME) && SupportsDeviceExtension(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME)) + allocinfo.flags = VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; allocinfo.physicalDevice = PhysicalDevice.Device; allocinfo.device = device; allocinfo.preferredLargeHeapBlockSize = 64 * 1024 * 1024; @@ -346,7 +375,7 @@ VkBool32 VulkanDevice::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT mess if (callbackData->pObjects[i].pObjectName) { FString hexname; - hexname.Format("0x%x", callbackData->pObjects[i].objectHandle); + hexname.Format("0x%llx", callbackData->pObjects[i].objectHandle); msg.Substitute(hexname.GetChars(), callbackData->pObjects[i].pObjectName); } } diff --git a/src/rendering/vulkan/system/vk_device.h b/src/rendering/vulkan/system/vk_device.h index ce158332e..231f1692e 100644 --- a/src/rendering/vulkan/system/vk_device.h +++ b/src/rendering/vulkan/system/vk_device.h @@ -62,7 +62,7 @@ public: // Device setup VkPhysicalDeviceFeatures UsedDeviceFeatures = {}; std::vector EnabledDeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; - std::vector OptionalDeviceExtensions = { VK_EXT_HDR_METADATA_EXTENSION_NAME }; + std::vector OptionalDeviceExtensions = { VK_EXT_HDR_METADATA_EXTENSION_NAME, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME }; VulkanPhysicalDevice PhysicalDevice; bool DebugLayerActive = false; @@ -83,11 +83,14 @@ private: void CreateInstance(); void CreateSurface(); void SelectPhysicalDevice(); + void SelectFeatures(); void CreateDevice(); void CreateAllocator(); void ReleaseResources(); - static bool CheckFeatures(const VkPhysicalDeviceFeatures &f); + bool SupportsDeviceExtension(const char *ext) const; + + static bool CheckRequiredFeatures(const VkPhysicalDeviceFeatures &f); VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE; diff --git a/src/rendering/vulkan/system/vk_framebuffer.cpp b/src/rendering/vulkan/system/vk_framebuffer.cpp index 1ad33cebe..30ba16f45 100644 --- a/src/rendering/vulkan/system/vk_framebuffer.cpp +++ b/src/rendering/vulkan/system/vk_framebuffer.cpp @@ -62,7 +62,6 @@ void Draw2D(F2DDrawer *drawer, FRenderState &state); void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown); -EXTERN_CVAR(Bool, vid_vsync) EXTERN_CVAR(Bool, r_drawvoxels) EXTERN_CVAR(Int, gl_tonemap) EXTERN_CVAR(Int, screenblocks) @@ -118,8 +117,10 @@ void VulkanFrameBuffer::InitializeState() uniformblockalignment = (unsigned int)device->PhysicalDevice.Properties.limits.minUniformBufferOffsetAlignment; maxuniformblock = device->PhysicalDevice.Properties.limits.maxUniformBufferRange; - mUploadSemaphore.reset(new VulkanSemaphore(device)); + mTransferSemaphore.reset(new VulkanSemaphore(device)); + mPreDrawSemaphore.reset(new VulkanSemaphore(device)); mGraphicsCommandPool.reset(new VulkanCommandPool(device, device->graphicsFamily)); + mTransferCommandPool.reset(new VulkanCommandPool(device, device->transferFamily)); mScreenBuffers.reset(new VkRenderBuffers()); mSaveBuffers.reset(new VkRenderBuffers()); @@ -187,6 +188,8 @@ void VulkanFrameBuffer::Update() Flush3D.Unclock(); + FPSLimit(); + Finish.Reset(); Finish.Clock(); @@ -206,7 +209,8 @@ void VulkanFrameBuffer::Update() vkResetFences(device->device, 1, &mRenderFinishedFence->fence); mDrawCommands.reset(); - mUploadCommands.reset(); + mTransferCommands.reset(); + mPreDrawCommands.reset(); DeleteFrameObjects(); Finish.Unclock(); @@ -224,62 +228,50 @@ void VulkanFrameBuffer::DeleteFrameObjects() void VulkanFrameBuffer::SubmitCommands(bool finish) { + if (mTransferCommands) + { + mTransferCommands->end(); + + QueueSubmit submit; + submit.addCommandBuffer(mTransferCommands.get()); + submit.addSignal(mTransferSemaphore.get()); + submit.execute(device, device->transferQueue); + } + + if (mPreDrawCommands) + { + mPreDrawCommands->end(); + + QueueSubmit submit; + submit.addCommandBuffer(mPreDrawCommands.get()); + if (mTransferCommands) + submit.addWait(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, mTransferSemaphore.get()); + submit.addSignal(mPreDrawSemaphore.get()); + submit.execute(device, device->graphicsQueue); + } + mDrawCommands->end(); - if (mUploadCommands) + QueueSubmit submit; + submit.addCommandBuffer(mDrawCommands.get()); + if (mPreDrawCommands) + submit.addWait(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, mPreDrawSemaphore.get()); + else if (mTransferCommands) + submit.addWait(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, mTransferSemaphore.get()); + if (finish) { - mUploadCommands->end(); - - // Submit upload commands immediately - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &mUploadCommands->buffer; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &mUploadSemaphore->semaphore; - VkResult result = vkQueueSubmit(device->graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); - if (result < VK_SUCCESS) - I_FatalError("Failed to submit command buffer! Error %d\n", result); - - // Wait for upload commands to finish, then submit render commands - VkSemaphore waitSemaphores[] = { mUploadSemaphore->semaphore, mSwapChainImageAvailableSemaphore->semaphore }; - VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - submitInfo.waitSemaphoreCount = finish ? 2 : 1; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &mDrawCommands->buffer; - submitInfo.signalSemaphoreCount = finish ? 1 : 0; - submitInfo.pSignalSemaphores = &mRenderFinishedSemaphore->semaphore; - result = vkQueueSubmit(device->graphicsQueue, 1, &submitInfo, mRenderFinishedFence->fence); - if (result < VK_SUCCESS) - I_FatalError("Failed to submit command buffer! Error %d\n", result); - } - else - { - VkSemaphore waitSemaphores[] = { mSwapChainImageAvailableSemaphore->semaphore }; - VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; - - VkSubmitInfo submitInfo = {}; - submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.waitSemaphoreCount = finish ? 1 : 0; - submitInfo.pWaitSemaphores = waitSemaphores; - submitInfo.pWaitDstStageMask = waitStages; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &mDrawCommands->buffer; - submitInfo.signalSemaphoreCount = finish ? 1 : 0; - submitInfo.pSignalSemaphores = &mRenderFinishedSemaphore->semaphore; - VkResult result = vkQueueSubmit(device->graphicsQueue, 1, &submitInfo, mRenderFinishedFence->fence); - if (result < VK_SUCCESS) - I_FatalError("Failed to submit command buffer! Error %d\n", result); + submit.addWait(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mSwapChainImageAvailableSemaphore.get()); + submit.addSignal(mRenderFinishedSemaphore.get()); } + submit.execute(device, device->graphicsQueue, mRenderFinishedFence.get()); if (!finish) { vkWaitForFences(device->device, 1, &mRenderFinishedFence->fence, VK_TRUE, std::numeric_limits::max()); vkResetFences(device->device, 1, &mRenderFinishedFence->fence); mDrawCommands.reset(); - mUploadCommands.reset(); + mTransferCommands.reset(); + mPreDrawCommands.reset(); DeleteFrameObjects(); } } @@ -794,15 +786,26 @@ void VulkanFrameBuffer::Draw2D() ::Draw2D(&m2DDrawer, *mRenderState); } -VulkanCommandBuffer *VulkanFrameBuffer::GetUploadCommands() +VulkanCommandBuffer *VulkanFrameBuffer::GetTransferCommands() { - if (!mUploadCommands) + if (!mTransferCommands) { - mUploadCommands = mGraphicsCommandPool->createBuffer(); - mUploadCommands->SetDebugName("VulkanFrameBuffer.mUploadCommands"); - mUploadCommands->begin(); + mTransferCommands = mTransferCommandPool->createBuffer(); + mTransferCommands->SetDebugName("VulkanFrameBuffer.mTransferCommands"); + mTransferCommands->begin(); } - return mUploadCommands.get(); + return mTransferCommands.get(); +} + +VulkanCommandBuffer *VulkanFrameBuffer::GetPreDrawCommands() +{ + if (!mPreDrawCommands) + { + mPreDrawCommands = mGraphicsCommandPool->createBuffer(); + mPreDrawCommands->SetDebugName("VulkanFrameBuffer.mPreDrawCommands"); + mPreDrawCommands->begin(); + } + return mPreDrawCommands.get(); } VulkanCommandBuffer *VulkanFrameBuffer::GetDrawCommands() diff --git a/src/rendering/vulkan/system/vk_framebuffer.h b/src/rendering/vulkan/system/vk_framebuffer.h index 6a169ec48..692606305 100644 --- a/src/rendering/vulkan/system/vk_framebuffer.h +++ b/src/rendering/vulkan/system/vk_framebuffer.h @@ -25,7 +25,8 @@ public: std::unique_ptr swapChain; uint32_t presentImageIndex = 0; - VulkanCommandBuffer *GetUploadCommands(); + VulkanCommandBuffer *GetTransferCommands(); + VulkanCommandBuffer *GetPreDrawCommands(); VulkanCommandBuffer *GetDrawCommands(); VkShaderManager *GetShaderManager() { return mShaderManager.get(); } VkSamplerManager *GetSamplerManager() { return mSamplerManager.get(); } @@ -114,9 +115,12 @@ private: std::unique_ptr mPostprocess; std::unique_ptr mRenderPassManager; std::unique_ptr mGraphicsCommandPool; - std::unique_ptr mUploadCommands; + std::unique_ptr mTransferCommandPool; + std::unique_ptr mTransferCommands; + std::unique_ptr mPreDrawCommands; std::unique_ptr mDrawCommands; - std::unique_ptr mUploadSemaphore; + std::unique_ptr mTransferSemaphore; + std::unique_ptr mPreDrawSemaphore; std::unique_ptr mRenderState; std::unique_ptr mSwapChainImageAvailableSemaphore; diff --git a/src/rendering/vulkan/system/vk_swapchain.cpp b/src/rendering/vulkan/system/vk_swapchain.cpp index 22473b9ee..b9ffe154f 100644 --- a/src/rendering/vulkan/system/vk_swapchain.cpp +++ b/src/rendering/vulkan/system/vk_swapchain.cpp @@ -5,7 +5,7 @@ EXTERN_CVAR(Bool, vid_vsync); -CUSTOM_CVAR(Bool, vk_hdr, false, /*CVAR_ARCHIVE | CVAR_GLOBALCONFIG |*/ CVAR_NOINITCALL) +CUSTOM_CVAR(Bool, vk_hdr, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { Printf("This won't take effect until " GAMENAME " is restarted.\n"); } @@ -176,20 +176,21 @@ VulkanSwapChain::VulkanSwapChain(VulkanDevice *device) : vsync(vid_vsync), devic device->SetDebugObjectName("SwapChainImageView", (uint64_t)swapChainImageViews[i], VK_OBJECT_TYPE_IMAGE_VIEW); } +#if 0 // This isn't required it seems if (swapChainFormat.colorSpace == VK_COLOR_SPACE_HDR10_ST2084_EXT) { - // Mastering display with DCI-P3 color primaries and D65 white point, + // Mastering display with HDR10_ST2084 color primaries and D65 white point, // maximum luminance of 1000 nits and minimum luminance of 0.001 nits; // content has maximum luminance of 2000 nits and maximum frame average light level (MaxFALL) of 500 nits. VkHdrMetadataEXT metadata = {}; metadata.sType = VK_STRUCTURE_TYPE_HDR_METADATA_EXT; - metadata.displayPrimaryRed.x = 0.680f; - metadata.displayPrimaryRed.y = 0.320f; - metadata.displayPrimaryGreen.x = 0.265f; - metadata.displayPrimaryGreen.y = 0.690f; - metadata.displayPrimaryBlue.x = 0.150f; - metadata.displayPrimaryBlue.y = 0.060f; + metadata.displayPrimaryRed.x = 0.708f; + metadata.displayPrimaryRed.y = 0.292f; + metadata.displayPrimaryGreen.x = 0.170f; + metadata.displayPrimaryGreen.y = 0.797f; + metadata.displayPrimaryBlue.x = 0.131f; + metadata.displayPrimaryBlue.y = 0.046f; metadata.whitePoint.x = 0.3127f; metadata.whitePoint.y = 0.3290f; metadata.maxLuminance = 1000.0f; @@ -199,6 +200,7 @@ VulkanSwapChain::VulkanSwapChain(VulkanDevice *device) : vsync(vid_vsync), devic vkSetHdrMetadataEXT(device->device, 1, &swapChain, &metadata); } +#endif } catch (...) { diff --git a/src/rendering/vulkan/textures/vk_hwtexture.cpp b/src/rendering/vulkan/textures/vk_hwtexture.cpp index 45591e9c4..10dacecc4 100644 --- a/src/rendering/vulkan/textures/vk_hwtexture.cpp +++ b/src/rendering/vulkan/textures/vk_hwtexture.cpp @@ -195,7 +195,7 @@ void VkHardwareTexture::CreateImage(FTexture *tex, int translation, int flags) mImageView = viewbuilder.create(fb->device); mImageView->SetDebugName("VkHardwareTexture.mImageView"); - auto cmdbuffer = fb->GetUploadCommands(); + auto cmdbuffer = fb->GetPreDrawCommands(); PipelineBarrier imageTransition; imageTransition.addImage(mImage.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, VK_ACCESS_SHADER_READ_BIT); @@ -231,7 +231,7 @@ void VkHardwareTexture::CreateTexture(int w, int h, int pixelsize, VkFormat form mImageView = viewbuilder.create(fb->device); mImageView->SetDebugName("VkHardwareTexture.mImageView"); - auto cmdbuffer = fb->GetUploadCommands(); + auto cmdbuffer = fb->GetTransferCommands(); PipelineBarrier imageTransition0; imageTransition0.addImage(mImage.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT); @@ -289,6 +289,15 @@ void VkHardwareTexture::GenerateMipmaps(VulkanImage *image, VulkanCommandBuffer PipelineBarrier barrier2; barrier2.addImage(image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT, i - 1); barrier2.execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + + auto fb = GetVulkanFrameBuffer(); + if (fb->device->transferFamily != fb->device->graphicsFamily) + { + PipelineBarrier transfer; + transfer.addQueueTransfer(fb->device->transferFamily, fb->device->graphicsFamily, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT, 0, GetMipLevels(image->width, image->height)); + transfer.execute(fb->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + transfer.execute(fb->GetPreDrawCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + } } int VkHardwareTexture::GetMipLevels(int w, int h) @@ -326,7 +335,7 @@ void VkHardwareTexture::AllocateBuffer(int w, int h, int texelsize) mImageView = viewbuilder.create(fb->device); mImageView->SetDebugName("VkHardwareTexture.mImageView"); - auto cmdbuffer = fb->GetUploadCommands(); + auto cmdbuffer = fb->GetPreDrawCommands(); PipelineBarrier imageTransition; imageTransition.addImage(mImage.get(), VK_IMAGE_LAYOUT_UNDEFINED, mImageLayout, 0, VK_ACCESS_SHADER_READ_BIT); diff --git a/src/v_framebuffer.cpp b/src/v_framebuffer.cpp index ec53fc7e8..93545b2e9 100644 --- a/src/v_framebuffer.cpp +++ b/src/v_framebuffer.cpp @@ -52,6 +52,9 @@ #include "hwrenderer/utility/hw_clock.h" #include "hwrenderer/data/flatvertices.h" +#include +#include + CVAR(Bool, gl_scale_viewport, true, CVAR_ARCHIVE); CVAR(Bool, vid_fps, false, 0) @@ -60,6 +63,9 @@ CVAR(Int, vid_showpalette, 0, 0) EXTERN_CVAR(Bool, ticker) EXTERN_CVAR(Float, vid_brightness) EXTERN_CVAR(Float, vid_contrast) +EXTERN_CVAR(Bool, vid_vsync) +EXTERN_CVAR(Int, vid_maxfps) +EXTERN_CVAR(Bool, cl_capfps) EXTERN_CVAR(Int, screenblocks) //========================================================================== @@ -415,3 +421,37 @@ void DFrameBuffer::ScaleCoordsFromWindow(int16_t &x, int16_t &y) x = int16_t((x - letterboxX) * Width / letterboxWidth); y = int16_t((y - letterboxY) * Height / letterboxHeight); } + +void DFrameBuffer::FPSLimit() +{ + using namespace std::chrono; + using namespace std::this_thread; + + if (vid_maxfps <= 0 || vid_vsync || cl_capfps) + return; + + uint64_t targetWakeTime = fpsLimitTime + 1'000'000 / vid_maxfps; + + while (true) + { + fpsLimitTime = duration_cast(steady_clock::now().time_since_epoch()).count(); + int64_t timeToWait = targetWakeTime - fpsLimitTime; + + if (timeToWait > 1'000'000 || timeToWait <= 0) + { + break; + } + + if (timeToWait <= 2'000) + { + // We are too close to the deadline. OS sleep is not precise enough to wake us before it elapses. + // Yield execution and check time again. + sleep_for(nanoseconds(0)); + } + else + { + // Sleep, but try to wake before deadline. + sleep_for(microseconds(timeToWait - 2'000)); + } + } +} diff --git a/src/v_video.cpp b/src/v_video.cpp index 6e043383b..91a6fb2b4 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -70,7 +70,6 @@ #include "g_levellocals.h" #include "am_map.h" -EXTERN_CVAR(Bool, cl_capfps) EXTERN_CVAR(Int, menu_resolution_custom_width) EXTERN_CVAR(Int, menu_resolution_custom_height) @@ -90,10 +89,6 @@ CUSTOM_CVAR(Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { vid_maxfps = 1000; } - else if (cl_capfps == 0) - { - I_SetFPSLimit(vid_maxfps); - } } CUSTOM_CVAR(Int, vid_rendermode, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) diff --git a/src/v_video.h b/src/v_video.h index 38ed46366..365f27d85 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -540,6 +540,7 @@ public: int ScreenToWindowX(int x); int ScreenToWindowY(int y); + void FPSLimit(); // Retrieves a buffer containing image data for a screenshot. // Hint: Pitch can be negative for upside-down images, in which case buffer @@ -556,6 +557,7 @@ protected: void DrawRateStuff (); private: + uint64_t fpsLimitTime = 0; uint64_t LastMS = 0, LastSec = 0, FrameCount = 0, LastCount = 0, LastTic = 0; diff --git a/src/win32/gl_sysfb.cpp b/src/win32/gl_sysfb.cpp index 3ac1d7cf7..c8a6e3ea7 100644 --- a/src/win32/gl_sysfb.cpp +++ b/src/win32/gl_sysfb.cpp @@ -123,8 +123,6 @@ void SystemGLFrameBuffer::SetVSync (bool vsync) void SystemGLFrameBuffer::SwapBuffers() { - // Limiting the frame rate is as simple as waiting for the timer to signal this event. - I_FPSLimit(); ::SwapBuffers(static_cast(Video)->m_hDC); } diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index 22ccf999e..f19491033 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -44,12 +44,13 @@ #include "m_argv.h" #include "version.h" #include "win32glvideo.h" +#ifdef HAVE_VULKAN #include "win32vulkanvideo.h" +#endif #include "doomerrors.h" #include "i_system.h" #include "swrenderer/r_swrenderer.h" -EXTERN_CVAR(Int, vid_maxfps) EXTERN_CVAR(Int, vid_backend) extern HWND Window; @@ -128,6 +129,7 @@ void I_InitGraphics () // are the active app. Huh? } +#ifdef HAVE_VULKAN if (vid_backend == 0) { // first try Vulkan, if that fails OpenGL @@ -141,6 +143,7 @@ void I_InitGraphics () } } else +#endif { Video = new Win32GLVideo(); } @@ -150,88 +153,3 @@ void I_InitGraphics () atterm (I_ShutdownGraphics); } - - -static UINT FPSLimitTimer; -HANDLE FPSLimitEvent; - -//========================================================================== -// -// SetFPSLimit -// -// Initializes an event timer to fire at a rate of /sec. The video -// update will wait for this timer to trigger before updating. -// -// Pass 0 as the limit for unlimited. -// Pass a negative value for the limit to use the value of vid_maxfps. -// -//========================================================================== - -static void StopFPSLimit() -{ - I_SetFPSLimit(0); -} - -void I_SetFPSLimit(int limit) -{ - if (limit < 0) - { - limit = vid_maxfps; - } - // Kill any leftover timer. - if (FPSLimitTimer != 0) - { - timeKillEvent(FPSLimitTimer); - FPSLimitTimer = 0; - } - if (limit == 0) - { // no limit - if (FPSLimitEvent != NULL) - { - CloseHandle(FPSLimitEvent); - FPSLimitEvent = NULL; - } - DPrintf(DMSG_NOTIFY, "FPS timer disabled\n"); - } - else - { - if (FPSLimitEvent == NULL) - { - FPSLimitEvent = CreateEvent(NULL, FALSE, TRUE, NULL); - if (FPSLimitEvent == NULL) - { // Could not create event, so cannot use timer. - Printf(DMSG_WARNING, "Failed to create FPS limitter event\n"); - return; - } - } - atterm(StopFPSLimit); - // Set timer event as close as we can to limit/sec, in milliseconds. - UINT period = 1000 / limit; - FPSLimitTimer = timeSetEvent(period, 0, (LPTIMECALLBACK)FPSLimitEvent, 0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET); - if (FPSLimitTimer == 0) - { - CloseHandle(FPSLimitEvent); - FPSLimitEvent = NULL; - Printf("Failed to create FPS limiter timer\n"); - return; - } - DPrintf(DMSG_NOTIFY, "FPS timer set to %u ms\n", period); - } -} - -//========================================================================== -// -// StopFPSLimit -// -// Used for cleanup during application shutdown. -// -//========================================================================== - -void I_FPSLimit() -{ - if (FPSLimitEvent != NULL) - { - WaitForSingleObject(FPSLimitEvent, 1000); - } -} - diff --git a/src/win32/hardware.h b/src/win32/hardware.h index a4ab3bc00..a7918935f 100644 --- a/src/win32/hardware.h +++ b/src/win32/hardware.h @@ -37,8 +37,4 @@ #include "i_video.h" #include "v_video.h" -void I_SetFPSLimit(int limit); -void I_FPSLimit(); - - #endif // __HARDWARE_H__ diff --git a/wadsrc/static/language.csv b/wadsrc/static/language.csv index ced4b5acd..7e5d17cd5 100644 --- a/wadsrc/static/language.csv +++ b/wadsrc/static/language.csv @@ -1543,7 +1543,9 @@ appuyez sur une touche.",,"Non puoi caricare una partita durante una net quest! Premi un tasto.","impossivel carregar, porque esta em partida online! -aperte uma tecla.",, +aperte uma tecla.","Невозможно загрузить сохранение в сетевой игре! + +Нажмите любую клавишу.", "Quicksave over your quest named '%s'? @@ -1560,7 +1562,11 @@ Appuyez sur Y ou N.",,"Sovrascrivere il salvataggio '%s'? -Premi Y oppure N.",,, +Premi Y oppure N.",,"Перезаписать быстрое сохранение + +«%s»? + +Нажмите Y или N.", "You can't quickload during a netquest! Press a key.",QLOADNET,chex,,,"Du kannst während eines Netzwerkspiels nicht schnellladen. @@ -1572,7 +1578,9 @@ vous êtes en ligne! appuyez sur une touche.",,"Non puoi fare un quickload durante una netquest! -Premi un tasto.",,, +Premi un tasto.",,"Невозможно загрузить быстрое сохранение в сетевой игре! + +Нажмите любую клавишу.", "Do you want to quickload the quest named '%s'? @@ -1589,7 +1597,11 @@ Appuyez sur Y ou N.",,"Vuoi fare un quickload della quest '%s'? -Premi Y oppure N.",,, +Premi Y oppure N.",,"""Желите брзо учитавање за игру под именом + +„%s“? + +Притисните Y или N.""", "You can't start a new quest while in a network quest. @@ -1606,7 +1618,10 @@ durante una network quest. Premi un tasto.","impossivel iniciar um novo jogo enquanto se esta online. -aperte uma tecla.",, +aperte uma tecla.","You can't start a new game +while in a network game. + +Press a key.", "Careful, this will be tough. Do you wish to continue? @@ -1779,7 +1794,7 @@ Go to console,OPTMNU_CONSOLE,,,,Öffne Konsole,Ir a la consola,,Ouvrir la consol Reverb environment editor,OPTMNU_REVERB,,,,Hall-Editor,Editor de Amb. de Reverb.,,Editeur environement de révérb.,Visszhang-környezet szerkesztő,Editor ambiente reverb,,Редактор реверберации,Уредник одјека у околини Language,OPTMNU_LANGUAGE,,,,Sprache,Idioma,,Langage,Nyelv,Lingua,,Язык,Језик Customize Controls,CNTRLMNU_TITLE,,,,Steuerung einstellen,Personalizar Controles ,,Modifier contrôles,Irányítás Testreszabása,Personalizza i controlli,,Настройки управления,Подешавање контрола -"ENTER to change, BACKSPACE to clear",CNTRLMNU_SWITCHTEXT1,,,,ENTER: Editieren BACKSPACE: Löschen,"ENTER para cambiar, BACKSPACE para limpiar",,"ENTREE pour changer, RET. ARRIERE pour effacer.","ENTER a változtatáshoz, BACKSPACE a törléshez","INVIO per cambiare, BACKSPACE per pulire",,"ENTER — изменить, BACKSPACE — очистить","ENTER за промену, BACKSPACE за чишћење" +"ENTER to change, BACKSPACE to clear",CNTRLMNU_SWITCHTEXT1,,,,ENTER: Editieren BACKSPACE: Löschen,"ENTER para cambiar, BACKSPACE para limpiar",,"ENTREE pour changer, RET. ARRIERE pour effacer.","ENTER a változtatáshoz, BACKSPACE a törléshez","INVIO per modificare, BACKSPACE per ripulire",,"ENTER — изменить, BACKSPACE — очистить","ENTER за промену, BACKSPACE за чишћење" "Press new key for control, ESC to cancel",CNTRLMNU_SWITCHTEXT2,,,,Drücke eine Taste oder ESC um abzubrechen,"Presiona una tecla para el control, ESC para cancelar",,"Appuyez sur la nouvelle touche pour l'assigner, Appuyez sur ECHAP pour annuler.","Nyomj meg egy gombot, ESC a törléshez","Premi un nuovo tasto per il controllo, ESC per cancellare",,"Нажмите клавишу управления, ESC для отмены","Притисните ново дугме за одређивање контроле, ESC за отказивање" Controls,CNTRLMNU_CONTROLS,,,,Steuerung,Controles,,Contrôles,Irányítás Testreszabása,Controlli,,Управление,Контроле @@ -1903,7 +1918,7 @@ Gender,PLYRMNU_PLAYERGENDER,,,,Geschlecht,Género,,Genre,Nem,Sesso,,Пол,По Autoaim,PLYRMNU_AUTOAIM,,,,Automatisch zielen,Autoapuntar,,Auto-visée,Auto célzás,Mira automatica,,Автоприцеливание,Аутоматско циљање Switch on pickup,PLYRMNU_SWITCHONPICKUP,,,,Beim aufnehmen Waffe selektieren,Cambiar arma al recoger,,Dernière arme,Felvételkor váltás,Cambia arma al momento della raccolta,,Перекл. оружия при подборе,Пребаци на покупљ. Always Run,PLYRMNU_ALWAYSRUN,,,,Immer Laufen,Siempre correr,,Toujours courir,Mindig fusson,Corri sempre,,Постоянный бег,Увек трчи -PRESS \cjSPACE,PLYRMNU_PRESSSPACE,,,,\cjLeertaste,Presiona \cjEspacio,,\cjEspace,,PREMI \cjSPAZIO,,Нажмите \cjSPACE,ПРИТИСНИТЕ \cjSPACE +PRESS \cjSPACE,PLYRMNU_PRESSSPACE,,,,\cjLeertaste,Presiona \cjEspacio,,\cjEspace,,PREMI \cjSPAZIO,,Нажмите \cjПРОБЕЛ,ПРИТИСНИТЕ \cjSPACE TO SEE FRONT,PLYRMNU_SEEFRONT,,,,für Frontansicht,Para ver de Frente,,Pour L'Avant,,PER VEDERE AVANTI,,Повернуть,ДА ВИДИТЕ СПРЕДА TO SEE BACK,PLYRMNU_SEEBACK,,"Same as SEEFRONT in Russian. Intentional? @@ -2009,7 +2024,7 @@ Miscellaneous Options,MISCMNU_TITLE,,,,Verschiedene Optionen,Opciones Misceláne Merge left+right Alt/Ctrl/Shift,MISCMNU_MERGEKEYS,,,,Linke und rechte Umschalt/Strg/Alt zusammenfassen,Combinar izq.+der. Alt/Ctrl/Mayús,,Combiner Alt/Ctrl/maj gauche & droite,,Unisci sinistra+destra Alt/Control/Maiusc,,Не разделять левый/правый ALT/CTRL/SHIFT, Alt-Enter toggles fullscreen,MISCMNU_WINFULLSCREENTOGGLE,,,,Alt-Enter schaltet Vollbild an/aus,Alt+Enter alterna pantalla completa,,Alt-Entrée alterne plein écran,,Alt-Invio attiva/disattiva lo schermo pieno,,Переключение полного экрана по ALT+ENTER, Command-F toggles fullscreen,MISCMNU_MACFULLSCREENTOGGLE,,,,Cmd-F schaltet Vollbild an/aus,Cmd-F alterna pantalla completa,,Command-F alterne plein écran,,Command-F attiva/disattiva lo schermo pieno,,Переключение полного экрана по Command+F, -Show IWAD selection dialog,MISCMNU_QUERYIWAD,,,,Zeige IWAD-Auswahl,Mostrar diálogo de selección de IWAD,,Afficher la séléction d'IWAD,,Mostra la schermata della selezione IWAD,,Выбор IWAD при запуске, +Show IWAD selection dialog,MISCMNU_QUERYIWAD,,,,Zeige IWAD-Auswahl,Mostrar diálogo de selección de IWAD,,Afficher la séléction d'IWAD,,Mostra la schermata della selezione IWAD,,Выбор IWAD-файла при запуске, Enable cheats from all games,MISCMNU_ALLCHEATS,,,,Ermögliche Cheats aus allen Spielen,Activar trucos de todos los juegos,,Activer cheats de tous les jeux,,Abilita tutti i cheat da tutti i giochi,,Читы из всех игр, Enable autosaves,MISCMNU_ENABLEAUTOSAVES,,,,Automatisches Speichern,Activar autoguardado,,Activer Sauvegardes auto,,Abilita i salvataggi automatici,,Автосохранения, Number of autosaves,MISCMNU_AUTOSAVECOUNT,,,,Anzahl von automatischen Speicherständen,Número de autoguardados,,Total de sauvegardes auto,,Numero di salvataggi automatici,,Количество автосохранений, @@ -2056,7 +2071,7 @@ Toggle zoom,MAPCNTRLMNU_TOGGLEZOOM,,,,Zoom an/aus,Alternar zoom,,Alterner zoom,, Toggle follow,MAPCNTRLMNU_TOGGLEFOLLOW,,,,Folgen an/aus,Alternar seguimiento,,Alterner suivi,,Abilita/disabilita scorrimento mappa,,Переключить слежение, Toggle grid,MAPCNTRLMNU_TOGGLEGRID,,,,Gitter an/aus,Alternar cuadrícula,Alternar rejilla,Alterner grille,,Abilita/disabilita la griglia,,Переключить сетку, Toggle texture,MAPCNTRLMNU_TOGGLETEXTURE,,,,Texturen an/aus,Alternar textura,,Alterner texture,,Abilita/disabilita le texture,,Переключить текстуры, -Set mark,MAPCNTRLMNU_SETMARK,,,,Setze Markierung,Fijar marca,,Placer repère,,Segna il posto,,Поставить отметку, +Set mark,MAPCNTRLMNU_SETMARK,,,,Setze Markierung,Fijar marca,,Placer repère,,Aggiungi segnaposto,,Поставить отметку, Clear mark,MAPCNTRLMNU_CLEARMARK,,,,Lösche Markierung,Limpiar marca,,Enlever repères,,Pulisci il segnaposto,,Убрать отметку, Customize Map Colors,MAPCOLORMNU_TITLE,,,Customize Map Colours,Automapfarben einstellen,Personalizar colores (mapa),,Couleurs Carte Personnalisées,,Personalizza i colori della mappa,,Настройки цветов автокарты, Restore default custom colors,MAPCOLORMNU_DEFAULTMAPCOLORS,,,Restore default custom colours,Standardfarben wiederherstellen,Restaurar colores personalizados,,Couleurs par défaut,,Reimposta i colori personalizzati al default,,Вернуть стандартные цвета, @@ -2065,7 +2080,7 @@ You,MAPCOLORMNU_YOURCOLOR,,,,Du,Tú,,Votre couleur,,Tu,,Игрок, 1-sided walls,MAPCOLORMNU_WALLCOLOR,,,,Einseitige Wände,Paredes de 1 lado,,Murs à 1 côté,,Muri unilaterali,,Односторонние стены, 2-sided walls with different floors,MAPCOLORMNU_FDWALLCOLOR,,,,Zweiseitige Wände mit unterschiedlicher Bodenhöhe,Paredes de 2 lados con pisos distintos,,Murs à 2 côtés avec différents sols,,Muri bilaterali con pavimenti diversi,,Двусторонние стены, 2-sided walls with different ceilings,MAPCOLORMNU_CDWALLCOLOR,,,,Zweiseitige Wände mit unterschiedlicher Deckenhöhe,Paredes de 2 lados con techos distintos,,Murs à 2 côtés avec différents plafonds,,Muri bilaterali con soffitti diversi,,Двусторонние стены с разными потолками, -2-sided walls with 3D floors,MAPCOLORMNU_EFWALLCOLOR,,,,Zweiseitige Wände mit 3D-Ebenen,Paredes de 2 lados con suelos 3D,,Murs à 2 côtés avec sols 3d,,Muri bilaterali con floor 3D,,Двусторонние стены с разными полами, +2-sided walls with 3D floors,MAPCOLORMNU_EFWALLCOLOR,,,,Zweiseitige Wände mit 3D-Ebenen,Paredes de 2 lados con suelos 3D,,Murs à 2 côtés avec sols 3d,,Muri bilaterali con 3D floor,,Двусторонние стены с разными полами, Map grid,MAPCOLORMNU_GRIDCOLOR,,,,Kartengitter,Cuadrícula del mapa,Rejilla del mapa,Quadrillage,,Griglia mappa,,Цвет сетки, Center point,MAPCOLORMNU_XHAIRCOLOR,,,Centre point,Mittelpunkt,Punto central,,Point Central,,Punto centrale,,Курсор, Not-yet-seen walls,MAPCOLORMNU_NOTSEENCOLOR,,,,Ungesehene Wände,Paredes sin ver,,Murs non découvers,,Muri ancora da scoprire,,Необнаруженные стены, @@ -2333,8 +2348,8 @@ Axis Configuration,JOYMNU_AXIS,,,,Achsenkonfiguration,Configuración del eje,,Co Invert,JOYMNU_INVERT,,,,Invertieren,Invertir,,Inverser,,Inverti,,Инвертировать, Dead zone,JOYMNU_DEADZONE,,,,Totzone,Zona muerta,,Zone neutre,,Zona cieca,,Мёртвая зона, No configurable axes,JOYMNU_NOAXES,,,,Keine konfigurierbaren Achsen,No hay ejes configurables,,Aucun axe à configurer,,Nessun asse configurabile,,Нет настраиваемых осей, -Off,OPTVAL_OFF,,,,Aus,Desactivado,,,,Spento,,Откл., -On,OPTVAL_ON,,,,An,Activado,,,,Acceso,,Вкл., +Off,OPTVAL_OFF,,,,Aus,Desactivado,,,,Disattivo,,Откл., +On,OPTVAL_ON,,,,An,Activado,,,,Attivo,,Вкл., Auto,OPTVAL_AUTO,,,,,,,,,Automatico,,Авто, Male,OPTVAL_MALE,,,,Männlich,Masculino,,Masculin,,Maschio,,Мужской, Female,OPTVAL_FEMALE,,,,Weiblich,Femenino,,Féminin,,Femmina,,Женский, @@ -2358,8 +2373,8 @@ Optimized,OPTVAL_OPTIMIZED,,,,Optimiert,Optimizado,,Optimisé,,Ottimizzato,,Оп Classic (Faster),OPTVAL_CLASSIC,,,,Klassisch (schneller),Clásico (Más rápido),,Classique (+ Rapide),,Classico (più veloce),,Классический (Быстрее), Precise,OPTVAL_PRECISE,,,,Genau,Preciso,,Précis,,Preciso,,Точный, Normal,OPTVAL_NORMAL,,,,,,,,,Normale,,Обычный, -Stretch,OPTVAL_STRETCH,,,,Strecken,Estrechado,Estrecho,Etirer,,,,Растянутый, -Capped,OPTVAL_CAPPED,,,,Limitiert,Limitado,,Limité,,,,Ограниченный, +Stretch,OPTVAL_STRETCH,,,,Strecken,Estrechado,Estrecho,Etirer,,Disteso,,Растянутый, +Capped,OPTVAL_CAPPED,,,,Limitiert,Limitado,,Limité,,Limitato,,Ограниченный, Particles,OPTVAL_PARTICLES,,,,Partikel,Partículas,,Particules,,Particelle,,Частицы, Sprites,OPTVAL_SPRITES,,,,Sprites,,,,,Sprite,,Спрайты, Sprites & Particles,OPTVAL_SPRITESPARTICLES,,,,Sprites und Partikel,Sprites y partículas,,Sprites & Particules,,Sprite e particelle,,Спрайты и частицы, @@ -2484,7 +2499,7 @@ Errors,OPTVAL_ERRORS,,,,Fehler,Errores,,Erreurs,,Errori,,Ошибки, Warnings,OPTVAL_WARNINGS,,,,Warnungen,Advertencias,,Avertissements,,Avvisi,,Предупреждения, Notifications,OPTVAL_NOTIFICATIONS,,,,Benachrichtigungen,Notificaciones,,,,Notifiche,,Уведомления, Everything,OPTVAL_EVERYTHING,,,,Alles,Todo,,Tout,,Tutti,,Все, -OpenGL-Accelerated,OPTVAL_HWPOLY,,,,OpenGL hardwarebeschleunigt,Acelerado por OpenGL,,Accéléré par OpenGL,,Accelerazione OpenGL,,Аппаратный (OpenGL), +Hardware accelerated,OPTVAL_HWPOLY,,,,Hardwarebeschleunigt,Acelerado por Hardware,,Accéléré par Hardware,,Accelerazione Hardware,,Аппаратный, Doom Software Renderer,OPTVAL_SWDOOM,,,,,Renderizado por Software de Doom,,Rendu Software Doom,,Motore grafico Doom software,,Программный, True Color SW Renderer,OPTVAL_SWDOOMTC,,,,,Renderizado SW Color Verdadero,Renderizado SW True Color,Rendu Software Couleurs Réeles,,Motore grafico software a pieni colori,,Полноцветный програмный, Softpoly Renderer,OPTVAL_SWPOLY,,,,,Renderizado Softpoly,,Rendu Softpoly,,Motore grafico Softpoly,,Полирендер, @@ -2544,12 +2559,12 @@ Quad,OPTSTR_QUAD,,,,,,,,,,,Четырёхместный, No interpolation,OPTSTR_NOINTERPOLATION,,,,Keine Interpolation,Sin interpolación,,Pas d'interpolation,,No interpolazione,,Без интерполяции, Spline,OPTSTR_SPLINE,,,,,,,,,,,Сплайн, OpenAL,OPTSTR_OPENAL,,,,,,,,,,,, -OpenGL Renderer,DSPLYMNU_GLOPT,,,,,Renderizado OpenGL,,Moteur de Rendu OpenGL,,Motore grafico OpenGL,,Рендерер OpenGL, +Hardware Renderer,DSPLYMNU_GLOPT,,,,,Renderizado por Hardware,,Moteur de Rendu Hardware,,Motore grafico Hardware,,Рендерер OpenGL, Software Renderer,DSPLYMNU_SWOPT,,,,,Renderizado por Software,,Moteur de Rendu Software,,Motore grafico Software,,Программный рендерер, Gamma correction,DSPLYMNU_GAMMA,,,,Gammakorrektur,Corrección gamma,,Correction Gamma,,Correzione gamma,,Гамма-коррекция, Contrast,DSPLYMNU_CONTRAST,,,,Kontrast,Contraste,,Contraste,,Contrasto,,Контраст, Saturation,DSPLYMNU_SATURATION,,,,Sättigung,Saturación,,,,Saturazione,,Насыщенность, -OpenGL Options,GLMNU_TITLE,,,,OpenGL Optionen,Opciones de OpenGL,,Options OpenGL,,Opzioni OpenGL,,Настройки OpenGL, +Hardware Rendering Options,GLMNU_TITLE,,,,Hardware-Renderer Optionen,Opciones de OpenGL,,Options OpenGL,,Opzioni OpenGL,,Настройки OpenGL, Dynamic Light Options,GLMNU_DYNLIGHT,,,,Dynamisches-Licht-Optionen,Opc. de luz dinámica,,Options Lumières Dynamiques,,Opzioni Luci Dinamiche,,Динамическое освещение, Texture Options,GLMNU_TEXOPT,,,,Texturoptionen,Opc. de texturas,,Options Textures,,Opzioni Texture,,Настройки текстур, Preferences,GLMNU_PREFS,,,,Einstellungen,Preferencias,,Préférences,,Preferenze,,Предпочтения, @@ -2569,14 +2584,14 @@ Precache GL textures,GLTEXMNU_PRECACHETEX,,,,GL Texturen zwischenspeichern,Preca Trim sprite edges,GLTEXMNU_TRIMSPREDGE,,,,Leerraum in Sprites wegschneiden,Recortar líneas de sprite,,Nettoyer le bord des sprites,,Taglia gli spigoli agli sprite,,Обрезание краёв спрайтов, Sort draw lists by texture,GLTEXMNU_SORTDRAWLIST,,,,Renderlisten nach Textur sortieren,Ordenar tablas por textura,,Ordonner liste de rendu par texture,,Ordina la lista draw per texture,,Сортировать списки текстур, Dynamic Lights,GLLIGHTMNU_TITLE,,,,Dynamische Lichter,Luces dinámicas,,Lumières Dynamiques,,Luci Dinamiche,,Динамическое освещение, -Dynamic Lights (OpenGL),GLLIGHTMNU_LIGHTSENABLED,,,,Dynamische Lichter (OpenGL),Luces dinámicas (OpenGL),,Lumières Dynamiques (OpenGL),,Luci Dinamiche (OpenGL),,Динамическое освещение (OpenGL), -Enable light definitions,GLLIGHTMNU_LIGHTDEFS,,,,Lichtdefinitionen an,Activar definiciones de luz,,Activer les définitions GLDEFS,,Abilita le definizioni GLDEFS,,Включить определения света, +Dynamic Lights (Hardware),GLLIGHTMNU_LIGHTSENABLED,,,,Dynamische Lichter (Hardware),Luces dinámicas (Hardware),,Lumières Dynamiques (Hardware),,Luci Dinamiche (Hardware),,Динамическое освещение (OpenGL), +Enable light definitions,GLLIGHTMNU_LIGHTDEFS,,,Activer les définitions GLDEFS,Lichtdefinitionen an,Activar definiciones de luz,,,,Abilita le definizioni GLDEFS,,Включить определения света, Lights affect sprites,GLLIGHTMNU_LIGHTSPRITES,,,,Sprites werden beleuchtet,Las luces afectan a los sprites,,Lumières affectent les sprites,,Le luci influiscono sugli sprite,,Освещение спрайтов, Lights affect particles,GLLIGHTMNU_LIGHTPARTICLES,,,,Partikel werden beleuchtet,Las luces afectan a las partículas,,Lumières affectent les particules,,Le luci influiscono sulle particelle,,Освещение частиц, Light shadowmaps,GLLIGHTMNU_LIGHTSHADOWMAP,,,,Shadowmaps für Lichter,Mapeo de sombra de Luz,,Shadowmaps,,Mappe shadow,,Свет на теневых картах, Shadowmap quality,GLLIGHTMNU_LIGHTSHADOWMAPQUALITY,,,,Shadowmap Qualität,Calidad de Mapeo de Sombrad,,Qualité Shadowmap,,Qualità mappe shadow,,Качество теневых карт, Shadowmap filter,GLLIGHTMNU_LIGHTSHADOWMAPFILTER,,,,Shadowmap Filter,Filtro de Mapeo de Sombras,,Filtre de Shadowmaps,,Filtro mappe shadow,,Фильтр теневых карт, -OpenGL Preferences,GLPREFMNU_TITLE,,,,OpenGL Einstellungen,Preferencias de OpenGL,,Préférences OpenGL,,Preferenze OpenGL,,Настройки OpenGL, +Hardware Rendering Options,GLPREFMNU_TITLE,,UNUSED!,,,,,,,,,, Sector light mode,GLPREFMNU_SECLIGHTMODE,,,,Sektorlichtmodus,Modo de luz de sector,,Mode de lumière Secteur,,Modalità luce di settore,,Режим освещения секторов, Fog mode,GLPREFMNU_FOGMODE,,,,Nebelmodus,Modo de niebla,,Mode du broullard,,Modalità nebbia,,Режим тумана, Fog forces fullbright,GLPREFMNU_FOGFORCEFULLBRIGHT,,,,Nebel erzwingt volle Helligkeit,Forzar brillo completo en niebla,,Brouillard force fullbright,,La nebbia forza piena luce,,Туман увеличивает яркость, @@ -2694,7 +2709,7 @@ Linear filter when downscaling,TCMNU_MINFILTER,,,,Linearer Filter beim Herunters Linear filter when upscaling,TCMNU_MAGFILTER,,,,Linearer Filter beim Hochskalieren,Filtro lineal por escalado,,Filtrage linéaire en agrandissement,,Filtro lineare per l'aumento,,Линейная фильтрация при увеличении, Use mipmapped textures,TCMNU_MIPMAP,,,,Benutze Textur-Mipmaps,Usar texturas con mipmap,,Utiliser textures mipmappées,,Usa le texture mipmapped,,Мип-маппинг для текстур, Dynamic lights (Software),TCMNU_DYNLIGHTS,,,,Dynamisches Licht (Software),Luces dinámicas (Software),,Lumières Dynamiques (Software),,Luci Dinamiche (Software),,Динамическое освещение (Программное), -Option Search,OS_TITLE,,,,Optionssuche,Buscar Opciones,,Recherche Option,,Opzioni di ricerca,,, +Option Search,OS_TITLE,,,,Optionssuche,Buscar Opciones,,Recherche Option,,Opzioni di ricerca,,Поиск настройки:, Search for any term,OS_ANY,,,,Suche nach beliebigem Begriff,Buscar cualquier término,,N'importe quel mot,,Cerca per ciascun termine,,Искать любое из слов, Search for all terms,OS_ALL,,,,Suche nach allen Begriffen,Buscar todos los términos,,Tous les mots,,Cerca per tutti i termini,,Искать все слова, No results found.,OS_NO_RESULTS,,,,Keine Resultate,Ningun resultado.,,Pas de résultat trouvé,,Nessun risultato trovato.,,, @@ -2751,4 +2766,9 @@ High Dynamic Range,VKMNU_HDR,,,,,Alto Rango Dinámico,,,,,,, Warning: The Vulkan renderer is highly experimental!,VK_WARNING,,,,Achtung: Der Vulkan Renderer ist noch sehr experimentell!,Advertencia: ¡El renderizado por Vulkan es altamente experimental!,,Attention: Le moteur de rendu Vulkan est très expérimental!,,Attenzione: il motore grafico Vulkan è altamente sperimentale!,,, These options will require a restart to take effect.,VK_RESTART,,,,Diese Option erfordert einen Neustart!,Estas opciones requieren reiniciar para tener efecto.,,Ces options nécessitent un redémarrage.,,Queste opzioni richiedono il riavvio per avere effetto.,,, Select Vulkan Device,VKMNU_DEVICESELECT,,This is not yet implemented - but it is planned,,Vulkan Gerät auswählen,Seleccionar Dispositivo Vulkan,,Sélectionner périphérique Vulkan,,Seleziona il Dispositivo Vulkan,,, -Press any key or click anywhere in the window to quit.,TXT_QUITENDOOM,,,,Drücke eine Taste oder klicke mit der Maus ins Fenster zum Beenden.,Presiona una tecla o haz click en cualquier lugar de la ventana para salir.,Presiona una tecla o da click en cualquier lugar de la ventana para salir.,Appuyez sur une touche ou cliquez pour quitter.,,,,, \ No newline at end of file +Press any key or click anywhere in the window to quit.,TXT_QUITENDOOM,,,,Drücke eine Taste oder klicke mit der Maus ins Fenster zum Beenden.,Presiona una tecla o haz click en cualquier lugar de la ventana para salir.,Presiona una tecla o da click en cualquier lugar de la ventana para salir.,Appuyez sur une touche ou cliquez pour quitter.,,Premi un qualunque tasto o fai un click ovunque nella finestra per uscire.,,, +No vertical thrust from explosions,CMPTMNU_EXPLODE1,,,,Keine vertikale Bewegung durch Explosionen,,,,,Nessuna spinta verticale dalle esplosioni,,, +Use original Doom explosion behavior,CMPTMNU_EXPLODE2,,,Use original Doom explosion behaviour,Benutze Original Doom Explosionsverhalten,,,,,Usa l'originale comportamento dell'esplosione di Doom,,, +Screen Blend Options,HUDMNU_FLASH,,,,Überblendoptionen,,,,,,,, +Postprocessing,GLMNU_POSTPROCESS,,,,,,,,,,,, +3D mode,GLMNU_3DMODE,,,,3D Modus,Modo 3D,,Mode 3D,,Modalità 3D,,, \ No newline at end of file diff --git a/wadsrc/static/mapinfo/doom2.txt b/wadsrc/static/mapinfo/doom2.txt index e9853c8c4..032518aa7 100644 --- a/wadsrc/static/mapinfo/doom2.txt +++ b/wadsrc/static/mapinfo/doom2.txt @@ -10,7 +10,7 @@ episode map01 episode level01 { - name = "$TXT_D1E2" + name = "$TXT_D2E2" key = "n" optional } diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index cc89dd2c7..d625c05bb 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -862,8 +862,8 @@ OptionMenu "VideoOptions" protected Submenu "$DSPLYMNU_GLOPT", "OpenGLOptions" Submenu "$DSPLYMNU_SWOPT", "SWROptions" Submenu "$DSPLYMNU_VKOPT", "VKOptions" + Submenu "$GLMNU_TEXOPT", "GLTextureGLOptions" Submenu "$GLMNU_DYNLIGHT", "GLLightOptions" - Submenu "$DSPLYMNU_SCOREBOARD", "ScoreboardOptions" StaticText " " Slider "$DSPLYMNU_SCREENSIZE", "screenblocks", 3.0, 12.0, 1.0, 0 @@ -872,20 +872,6 @@ OptionMenu "VideoOptions" protected Slider "$DSPLYMNU_CONTRAST", "vid_contrast", 0.1, 3.0, 0.1 Slider "$DSPLYMNU_SATURATION", "vid_saturation", -3.0, 3.0, 0.25, 2 StaticText " " - Option "$DSPLYMNU_VSYNC", "vid_vsync", "OnOff" - Option "$DSPLYMNU_CAPFPS", "cl_capfps", "OffOn" - Slider "$DSPLYMNU_BLOODFADE", "blood_fade_scalar", 0.0, 1.0, 0.05, 2 - Slider "$DSPLYMNU_PICKUPFADE", "pickup_fade_scalar", 0.0, 1.0, 0.05, 2 - Slider "$DSPLYMNU_WATERFADE", "underwater_fade_scalar", 0.0, 1.0, 0.05, 2 - - StaticText " " - Option "$DSPLYMNU_WIPETYPE", "wipetype", "Wipes" - - IfOption(Windows) - { - Option "$DSPLYMNU_SHOWENDOOM", "showendoom", "Endoom" - } - Option "$DSPLYMNU_DRAWFUZZ", "r_drawfuzz", "Fuzziness" Option "$DSPLYMNU_OLDTRANS", "r_vanillatrans", "VanillaTrans" Slider "$DSPLYMNU_TRANSSOUL", "transsouls", 0.25, 1.0, 0.05, 2 @@ -900,15 +886,9 @@ OptionMenu "VideoOptions" protected Option "$DSPLYMNU_TELEZOOM", "telezoom", "OnOff" Slider "$DSPLYMNU_QUAKEINTENSITY", "r_quakeintensity", 0.0, 1.0, 0.05, 2 Option "$DSPLYMNU_NOMONSTERINTERPOLATION", "nomonsterinterpolation", "NoYes" - Slider "$DSPLYMNU_MENUDIM", "dimamount", 0, 1.0, 0.05, 2 - ColorPicker "$DSPLYMNU_DIMCOLOR", "dimcolor" - IfOption(Windows) - { - StaticText " " - Option "$DSPLYMNU_GPUSWITCH", vid_gpuswitch, "GPUSwitch" - } } + //------------------------------------------------------------------------------------------- // // HUD menu @@ -976,6 +956,8 @@ OptionMenu "HUDOptions" protected Submenu "$HUDMNU_SCALEOPT", "ScalingOptions" Submenu "$HUDMNU_ALTHUD", "AltHudOptions" Submenu "$HUDMNU_MESSAGE", "MessageOptions" + Submenu "$HUDMNU_FLASH", "FlashOptions" + Submenu "$DSPLYMNU_SCOREBOARD", "ScoreboardOptions" StaticText " " Option "$HUDMNU_CROSSHAIR", "crosshair", "Crosshairs" Option "$HUDMNU_FORCECROSSHAIR", "crosshairforce", "OnOff" @@ -988,15 +970,26 @@ OptionMenu "HUDOptions" protected Option "$HUDMNU_NAMETAGCOLOR", "nametagcolor", "TextColors", "displaynametags" Option "$HUDMNU_OLDOUCH", "st_oldouch", "OnOff" StaticText " " + Slider "$DSPLYMNU_MOVEBOB", "movebob", 0, 1.0, 0.05, 2 + Slider "$DSPLYMNU_STILLBOB", "stillbob", 0, 1.0, 0.05, 2 + Slider "$DSPLYMNU_BOBSPEED", "wbobspeed", 0, 2.0, 0.1 + StaticText " " + Slider "$DSPLYMNU_MENUDIM", "dimamount", 0, 1.0, 0.05, 2 + ColorPicker "$DSPLYMNU_DIMCOLOR", "dimcolor" + +} + +OptionMenu "FlashOptions" protected +{ + Title "$HUDMNU_FLASH" Option "$HUDMNU_HEXENFLASHES", "pf_hexenweaps", "ZDoomHexen" Option "$HUDMNU_POISONFLASHES", "pf_poison", "ZDoomHexen" Option "$HUDMNU_ICEFLASHES", "pf_ice", "ZDoomHexen" Option "$HUDMNU_HAZARDFLASHES", "pf_hazard", "ZDoomStrife" StaticText " " - Slider "$DSPLYMNU_MOVEBOB", "movebob", 0, 1.0, 0.05, 2 - Slider "$DSPLYMNU_STILLBOB", "stillbob", 0, 1.0, 0.05, 2 - Slider "$DSPLYMNU_BOBSPEED", "wbobspeed", 0, 2.0, 0.1 - + Slider "$DSPLYMNU_BLOODFADE", "blood_fade_scalar", 0.0, 1.0, 0.05, 2 + Slider "$DSPLYMNU_PICKUPFADE", "pickup_fade_scalar", 0.0, 1.0, 0.05, 2 + Slider "$DSPLYMNU_WATERFADE", "underwater_fade_scalar", 0.0, 1.0, 0.05, 2 } OptionMenu "ScalingOptions" protected @@ -1144,6 +1137,13 @@ OptionMenu "MiscOptions" protected SafeCommand "$MISCMNU_CLEARNODECACHE", "clearnodecache" StaticText " " Option "$OPTMNU_LANGUAGE", "language", "LanguageOptions" + IfOption(Windows) + { + StaticText " " + Option "$DSPLYMNU_SHOWENDOOM", "showendoom", "Endoom" + } + + } //------------------------------------------------------------------------------------------- @@ -1660,6 +1660,8 @@ OptionMenu "CompatPhysicsMenu" protected Option "$CMPTMNU_TRACE", "compat_TRACE", "YesNo" Option "$CMPTMNU_HITSCAN", "compat_HITSCAN", "YesNo" Option "$CMPTMNU_MISSILECLIP", "compat_MISSILECLIP", "YesNo" + Option "$CMPTMNU_EXPLODE1", "compat_explode1", "YesNo" + Option "$CMPTMNU_EXPLODE2", "compat_explode2", "YesNo" Class "CompatibilityMenu" } @@ -1813,7 +1815,7 @@ OptionValue MidiDevices OptionMenu SoundOptions protected { Title "$SNDMNU_TITLE" - Slider "$SNDMNU_MASTERVOLUME", "snd_mastervolume", 0, 1, 0.05, 2 + Slider "$MODMNU_MASTERVOLUME", "snd_mastervolume", 0, 1, 0.05, 2 StaticText " " Slider "$SNDMNU_SFXVOLUME", "snd_sfxvolume", 0, 1, 0.05, 2 Slider "$SNDMNU_MUSICVOLUME", "snd_musicvolume", 0, 1, 0.05, 2 @@ -2152,12 +2154,20 @@ OptionMenu VideoModeMenu protected { Option "$VIDMNU_HIDPI", "vid_hidpi", "YesNo" } + IfOption(Windows) + { + Option "$DSPLYMNU_GPUSWITCH", vid_gpuswitch, "GPUSwitch" + } Option "$VIDMNU_FORCEASPECT", "vid_aspect", "ForceRatios" Option "$VIDMNU_CROPASPECT", "vid_cropaspect", "CropAspect" Option "$VIDMNU_SCALEMODE", "vid_scalemode", "ScaleModes" Slider "$VIDMNU_SCALEFACTOR", "vid_scalefactor", 0.25, 2.0, 0.25, 2 + StaticText " " + Option "$DSPLYMNU_VSYNC", "vid_vsync", "OnOff" + Option "$DSPLYMNU_CAPFPS", "cl_capfps", "OffOn" + StaticText "" StaticText "$VIDMNU_CUSTOMRES" TextField "$VIDMNU_CUSTOMX", menu_resolution_custom_width @@ -2456,7 +2466,6 @@ OptionValue ShadowMapFilter OptionMenu "GLTextureGLOptions" protected { Title "$GLTEXMNU_TITLE" - Option "$GLTEXMNU_TEXENABLED", gl_texture, "YesNo" Option "$GLTEXMNU_TEXFILTER", gl_texture_filter, "FilterModes" Option "$GLTEXMNU_ANISOTROPIC", gl_texture_filter_anisotropic, "Anisotropy" Option "$GLTEXMNU_ENABLEHIRES", gl_texture_usehires, "YesNo" @@ -2496,7 +2505,8 @@ OptionMenu "GLLightOptions" protected OptionMenu "OpenGLOptions" protected { Title "$GLMNU_TITLE" - Submenu "$GLMNU_TEXOPT", "GLTextureGLOptions" + Submenu "$GLPREFMNU_VRMODE", "VR3DMenu" + Submenu "$GLMNU_POSTPROCESS", "PostProcessMenu" StaticText " " Option "$GLPREFMNU_SECLIGHTMODE", gl_lightmode, "LightingModes" Option "$GLPREFMNU_SWLMBANDED", gl_bandedswlight, "OnOff" @@ -2516,22 +2526,31 @@ OptionMenu "OpenGLOptions" protected StaticText " " Slider "$GLPREFMNU_MENUBLUR", gl_menu_blur, 0, 5.0, 0.5, 2 StaticText " " - Option "$GLPREFMNU_VRMODE", vr_mode, "VRMode" + Option "$GLPREFMNU_MULTISAMPLE", gl_multisample, "Multisample" +} + +OptionMenu "VR3DMenu" protected +{ + Title "GLPREFMNU_VRMODE" + Option "$GLMNU_3DMODE", vr_mode, "VRMode" IfOption(Windows) { Option "$GLPREFMNU_VRQUADSTEREO", vr_enable_quadbuffered, "OnOff" } Slider "$GLPREFMNU_VRIPD", vr_ipd, 0.041, 0.073, 0.001, 3 Slider "$GLPREFMNU_VRSCREENDIST", vr_screendist, 0.2, 10.0, 0.1, 1 - StaticText " " - Option "$GLPREFMNU_MULTISAMPLE", gl_multisample, "Multisample" - Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes" +} + +OptionMenu "PostProcessMenu" protected +{ + Title "$GLMNU_POSTPROCESS" Option "$GLPREFMNU_BLOOM", gl_bloom, "OnOff" 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" Option "$GLPREFMNU_DITHER", gl_dither_bpc, "DitherModes" + Option "$GLPREFMNU_TONEMAP", gl_tonemap, "TonemapModes" StaticText " " Slider "$GLPREFMNU_PALTONEMAPPOWER", gl_paltonemap_powtable, 0.2, 3.0, 0.1, 1 Option "$GLPREFMNU_PALTONEMAPORDER", gl_paltonemap_reverselookup, "LookupOrder" diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 64a914461..80094c456 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -10,6 +10,11 @@ layout(location = 5) in vec4 vWorldNormal; layout(location = 6) in vec4 vEyeNormal; #endif +#ifdef NO_CLIPDISTANCE_SUPPORT +layout(location = 7) in vec4 ClipDistanceA; +layout(location = 8) in vec4 ClipDistanceB; +#endif + layout(location=0) out vec4 FragColor; #ifdef GBUFFER_PASS layout(location=1) out vec4 FragFog; @@ -542,6 +547,10 @@ vec3 AmbientOcclusionColor() void main() { +#ifdef NO_CLIPDISTANCE_SUPPORT + if (ClipDistanceA.x < 0 || ClipDistanceA.y < 0 || ClipDistanceA.z < 0 || ClipDistanceA.w < 0 || ClipDistanceB.x < 0) discard; +#endif + Material material = ProcessMaterial(); vec4 frag = material.Base; diff --git a/wadsrc/static/shaders/glsl/main.vp b/wadsrc/static/shaders/glsl/main.vp index f3e1c3d22..cc332ad95 100644 --- a/wadsrc/static/shaders/glsl/main.vp +++ b/wadsrc/static/shaders/glsl/main.vp @@ -18,6 +18,22 @@ layout(location = 5) out vec4 vWorldNormal; layout(location = 6) out vec4 vEyeNormal; #endif +#ifdef NO_CLIPDISTANCE_SUPPORT +layout(location = 7) out vec4 ClipDistanceA; +layout(location = 8) out vec4 ClipDistanceB; +#define ClipDistance0 ClipDistanceA.x +#define ClipDistance1 ClipDistanceA.y +#define ClipDistance2 ClipDistanceA.z +#define ClipDistance3 ClipDistanceA.w +#define ClipDistance4 ClipDistanceB.x +#else +#define ClipDistance0 gl_ClipDistance[0] +#define ClipDistance1 gl_ClipDistance[1] +#define ClipDistance2 gl_ClipDistance[2] +#define ClipDistance3 gl_ClipDistance[3] +#define ClipDistance4 gl_ClipDistance[4] +#endif + void main() { vec2 parmTexCoord; @@ -67,8 +83,8 @@ void main() if (uSplitBottomPlane.z != 0.0) { - 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); + ClipDistance3 = ((uSplitTopPlane.w + uSplitTopPlane.x * worldcoord.x + uSplitTopPlane.y * worldcoord.z) * uSplitTopPlane.z) - worldcoord.y; + ClipDistance4 = worldcoord.y - ((uSplitBottomPlane.w + uSplitBottomPlane.x * worldcoord.x + uSplitBottomPlane.y * worldcoord.z) * uSplitBottomPlane.z); } #ifdef HAS_UNIFORM_VERTEX_DATA @@ -101,25 +117,25 @@ void main() if (uClipHeightDirection != 0.0) // clip planes used for reflective flats { - gl_ClipDistance[0] = (worldcoord.y - uClipHeight) * uClipHeightDirection; + ClipDistance0 = (worldcoord.y - uClipHeight) * uClipHeightDirection; } else if (uClipLine.x > -1000000.0) // and for line portals - this will never be active at the same time as the reflective planes clipping so it can use the same hardware clip plane. { - gl_ClipDistance[0] = -( (worldcoord.z - uClipLine.y) * uClipLine.z + (uClipLine.x - worldcoord.x) * uClipLine.w ) + 1.0/32768.0; // allow a tiny bit of imprecisions for colinear linedefs. + ClipDistance0 = -( (worldcoord.z - uClipLine.y) * uClipLine.z + (uClipLine.x - worldcoord.x) * uClipLine.w ) + 1.0/32768.0; // allow a tiny bit of imprecisions for colinear linedefs. } else { - gl_ClipDistance[0] = 1; + ClipDistance0 = 1; } // clip planes used for translucency splitting - gl_ClipDistance[1] = worldcoord.y - uClipSplit.x; - gl_ClipDistance[2] = uClipSplit.y - worldcoord.y; + ClipDistance1 = worldcoord.y - uClipSplit.x; + ClipDistance2 = uClipSplit.y - worldcoord.y; if (uSplitTopPlane == vec4(0.0)) { - gl_ClipDistance[3] = 1; - gl_ClipDistance[4] = 1; + ClipDistance3 = 1.0; + ClipDistance4 = 1.0; } gl_PointSize = 1.0; diff --git a/wadsrc/static/zscript/actors/attacks.zs b/wadsrc/static/zscript/actors/attacks.zs index d32485dfd..f2c038f87 100644 --- a/wadsrc/static/zscript/actors/attacks.zs +++ b/wadsrc/static/zscript/actors/attacks.zs @@ -596,6 +596,7 @@ extend class Actor int pflags = 0; if (flags & XF_HURTSOURCE) pflags |= RADF_HURTSOURCE; if (flags & XF_NOTMISSILE) pflags |= RADF_SOURCEISSPOT; + if (flags & XF_THRUSTZ) pflags |= RADF_THRUSTZ; int count = RadiusAttack (target, damage, distance, damagetype, pflags, fulldamagedistance); if (!(flags & XF_NOSPLASH)) CheckSplash(distance); diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index 6ac2ddc27..4fdc29fda 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -258,6 +258,8 @@ enum EExplodeFlags XF_NOTMISSILE = 4, XF_EXPLICITDAMAGETYPE = 8, XF_NOSPLASH = 16, + XF_THRUSTZ = 32, + }; // Flags for A_RadiusThrust @@ -1338,4 +1340,6 @@ enum ECompatFlags COMPATF2_TELEPORT = 1 << 5, // Don't let indirect teleports trigger sector actions COMPATF2_PUSHWINDOW = 1 << 6, // Disable the window check in CheckForPushSpecial() COMPATF2_CHECKSWITCHRANGE = 1 << 7, // Enable buggy CheckSwitchRange behavior + COMPATF2_EXPLODE1 = 1 << 8, // No vertical explosion thrust + COMPATF2_EXPLODE2 = 1 << 9 // Use original explosion code throughout. }; diff --git a/wadsrc_extra/static/filter/game-strife/fonts/defsmallfont2/00AB.lmp b/wadsrc_extra/static/filter/game-strife/fonts/defsmallfont2/00AB.lmp new file mode 100644 index 000000000..eafcf6c50 Binary files /dev/null and b/wadsrc_extra/static/filter/game-strife/fonts/defsmallfont2/00AB.lmp differ diff --git a/wadsrc_extra/static/filter/game-strife/fonts/defsmallfont2/00BB.lmp b/wadsrc_extra/static/filter/game-strife/fonts/defsmallfont2/00BB.lmp new file mode 100644 index 000000000..90e45fd57 Binary files /dev/null and b/wadsrc_extra/static/filter/game-strife/fonts/defsmallfont2/00BB.lmp differ diff --git a/wadsrc_extra/static/language.csv b/wadsrc_extra/static/language.csv index e106c2601..554832e56 100644 --- a/wadsrc_extra/static/language.csv +++ b/wadsrc_extra/static/language.csv @@ -245,64 +245,64 @@ AREA 36: Castle Clash,TXT_STRIFE_MAP36,,,,ZONE 36: Burg-Konflikt,ÁREA 36: Ataqu AREA 37: Killing Grounds,TXT_STRIFE_MAP37,,,,ZONE 37: Kampfgelände,ÁREA 37: Campo de Matanza,,ZONE 37: Zone de Chasse,,AREA 37: Campi di Uccisione,Área 37: Campo de Matança,Местность 37: Секторы обстрела, AREA 38: Ordered Chaos,TXT_STRIFE_MAP38,,,,ZONE 38: Geordnetes Chaos,ÁREA 38: Caos Ordenado,,ZONE 38: Chaos Ordonné,,AREA 38: Caos Ordinato,Área 38: Caos Ordenado,Местность 38: Управляемый хаос, ,,,Hexen,,,,,,,,,, -Winnowing Hall,TXT_HEXEN_MAP01,,,,Trennungshalle,Sala de los Vientos,,Chambre de la Séparation,,,,Зал отсеивания,Овејан хол -Seven Portals,TXT_HEXEN_MAP02,,,,Sieben Portale,Siete Portales,,Les Sept Portails,,,Sete Portais,Семь порталов,Седам портала -Guardian of Ice,TXT_HEXEN_MAP03,,,,Wächter des Eises,Guardián de Hielo,,Gardien de Glace,,,Guardião de Gelo,Страж льда,Чувар леда -Guardian of Fire,TXT_HEXEN_MAP04,,,,Wächter des Feuers,Guardián de Fuego,,Gardien de Feu,,,Guardião de Fogo,Страж огня,Чувар ватре -Guardian of Steel,TXT_HEXEN_MAP05,,,,Wächter des Stahls,Guardián de Acero,,Gardien d'Acier,,,Guardião de Aço,Страж стали,Чувар челика -Bright Crucible,TXT_HEXEN_MAP06,,,,Glänzende Feuerprobe,Crisol de Luz,,Creuset Radiant,,,,Испытание света,Светлосни лонац -Shadow Wood,TXT_HEXEN_MAP13,,,,Schattenwald,Bosque de la Sombra,,Forêt des Ombres,,,Floresta das Sombras,Лес теней,Шума сена -Darkmere,TXT_HEXEN_MAP08,,,,Düstersee,Pantano,,Marais Noir,,,Pantâno Negro,Болотная топь,Мочваре -Caves of Circe,TXT_HEXEN_MAP09,,,,Circes Höhle,Cuevas de Circe,,Caves de Circé,,,Carvernas de Circe,Пещеры цирцеи,Пећине цирке -Wastelands,TXT_HEXEN_MAP10,,,,Ödland,Yermo,,Terres Ruinées,,,,Пустоши,Пустаре -Sacred Grove,TXT_HEXEN_MAP11,,,,Heiliger Hain,Arboleda Sagrada,,Bosquet Sacré,,,Bosque Sagrado,Священная роща,Свети шумарак -Hypostyle,TXT_HEXEN_MAP12,,,,Säulenhalle,Hipóstila,,Hypostyle,,,Hipostilo,Гипостильный зал,Хипостил -Heresiarch's Seminary,TXT_HEXEN_MAP27,,,,Seminar des Erzketzers,Seminario del Heresiarca,,Séminaire de l'Hérésiarche,,,Seminário do Heresiarca,Семинария Ересиарха,Јересијархов семинар -Dragon Chapel,TXT_HEXEN_MAP28,,,,Drachenkapelle,Capilla Dragón,,Chapelle du Dragon,,,Capela do Dragão,Часовня дракона,Змајева капела -Griffin Chapel,TXT_HEXEN_MAP30,,,,Greifenkapelle,Capilla Grifo,,Chapelle du Grifon,,,Capela do Grifo,Часовня грифона,Грифинова капела -Deathwind Chapel,TXT_HEXEN_MAP31,,,,Todeswindkapelle,Capilla Viento de Muerte,,Chapelle du Vent Tueur,,,Capela do Vento da Morte,Часовня ветра смерти,Капела ветра смрти -Orchard of Lamentations,TXT_HEXEN_MAP32,,,,Garten der Wehklagen,Huerto de los Lamentos,,Verger des Lamentations,,,Pomar das Lamentações,Сады плача,Воћњак тужбалица -Silent Refectory,TXT_HEXEN_MAP33,,,,Stilles Refektorium,Refectorio Silencioso,,Réfectoire Silencieux,,,Refeitório Silencioso,Безмолвная трапезная,Тиха трпезарија -Wolf Chapel,TXT_HEXEN_MAP34,,,,Wolfskapelle,Capilla Lobo,,Chapelle du Loup,,,Capela do Lobo,Часовня волка,Вучја капела -Forsaken Outpost,TXT_HEXEN_MAP21,,,,Verlassener Vorposten,Puesto de Avanzada Abandonado,,Base Abandonnée,,,Posto Abandonado,Покинутая застава,Напуштена постаја -Castle of Grief,TXT_HEXEN_MAP22,,,,Leidensburg,Castillo del Pesar,,Château de la Souffrance,,,Castelo da Aflição,Замок скорби,Замак жалости -Gibbet,TXT_HEXEN_MAP23,,,,Richtstätte,Horca,,Gibet,,,Forca,Виселица,Вешала -Effluvium,TXT_HEXEN_MAP24,,,,Effluvium,Efluvio,,Effluvium,,,Eflúvio,Зловонный сток,Ефлувиум -Dungeons,TXT_HEXEN_MAP25,,,,Kerker,Calabozos,,Donjons,,,Masmorras,Подземелья,Тамнице -Desolate Garden,TXT_HEXEN_MAP26,,,,Verwildeter Garten,Jardín Desolado,,Jardin de la Désolation,,,Jardim Desolado,Заброшенный сад,Пуста башта -Necropolis,TXT_HEXEN_MAP35,,,,Nekropole,Necrópolis,,Nécropole,,,Necrópole,Некрополь,Некрополис -Zedek's Tomb,TXT_HEXEN_MAP36,,,,Zedeks Gruft,Tumba de Zedek,,Tombeau de Zedek,,,Tumba de Zedek,Гробница зедека,Зедеков гроб -Menelkir's Tomb,TXT_HEXEN_MAP37,,,,Menelkirs Gruft,Tumba de Menelkir,,Tombeau de Menelkir,,,Tumba de Menelkir,Гробница менелкира,Менелкиров гроб -Traductus' Tomb,TXT_HEXEN_MAP38,,,,Traductus' Gruft,Tumba de Traductus,,Tombeau de Traductus,,,Tumba de Traductus,Гробница традактуса,Традуктусов гроб -Vivarium,TXT_HEXEN_MAP39,,,,Vivarium,Vivario,,Vivarium,,,Viveiro,Виварий,Зверињак -Dark Crucible,TXT_HEXEN_MAP40,,,,Dunkle Feuerprobe,Crisol de Oscuridad,,Creuset Sombre,,,,Испытание тьмы,Мрачни лонац +Winnowing Hall,TXT_HEXEN_MAP01,,,,Trennungshalle,Sala de los Vientos,,Chambre de la Séparation,,Camera di separazione,,Зал отсеивания,Овејан хол +Seven Portals,TXT_HEXEN_MAP02,,,,Sieben Portale,Siete Portales,,Les Sept Portails,,I Sette Portali,Sete Portais,Семь порталов,Седам портала +Guardian of Ice,TXT_HEXEN_MAP03,,,,Wächter des Eises,Guardián de Hielo,,Gardien de Glace,,Guardiano del Ghiaccio,Guardião de Gelo,Страж льда,Чувар леда +Guardian of Fire,TXT_HEXEN_MAP04,,,,Wächter des Feuers,Guardián de Fuego,,Gardien de Feu,,Guardiano del Fuoco,Guardião de Fogo,Страж огня,Чувар ватре +Guardian of Steel,TXT_HEXEN_MAP05,,,,Wächter des Stahls,Guardián de Acero,,Gardien d'Acier,,Guardiano dell'Acciaio,Guardião de Aço,Страж стали,Чувар челика +Bright Crucible,TXT_HEXEN_MAP06,,,,Glänzende Feuerprobe,Crisol de Luz,,Creuset Radiant,,Crogiolo Luminoso,,Испытание света,Светлосни лонац +Shadow Wood,TXT_HEXEN_MAP13,,,,Schattenwald,Bosque de la Sombra,,Forêt des Ombres,,Foresta Oscura,Floresta das Sombras,Лес теней,Шума сена +Darkmere,TXT_HEXEN_MAP08,,,,Düstersee,Pantano,,Marais Noir,,Palude Nera,Pantâno Negro,Болотная топь,Мочваре +Caves of Circe,TXT_HEXEN_MAP09,,,,Circes Höhle,Cuevas de Circe,,Caves de Circé,,Grotte di Circe,Carvernas de Circe,Пещеры цирцеи,Пећине цирке +Wastelands,TXT_HEXEN_MAP10,,,,Ödland,Yermo,,Terres Ruinées,,Terre in rovina,,Пустоши,Пустаре +Sacred Grove,TXT_HEXEN_MAP11,,,,Heiliger Hain,Arboleda Sagrada,,Bosquet Sacré,,Boschetto Sacro,Bosque Sagrado,Священная роща,Свети шумарак +Hypostyle,TXT_HEXEN_MAP12,,,,Säulenhalle,Hipóstila,,Hypostyle,,Ipostilo,Hipostilo,Гипостильный зал,Хипостил +Heresiarch's Seminary,TXT_HEXEN_MAP27,,,,Seminar des Erzketzers,Seminario del Heresiarca,,Séminaire de l'Hérésiarche,,Seminario dell'Heresiarch,Seminário do Heresiarca,Семинария Ересиарха,Јересијархов семинар +Dragon Chapel,TXT_HEXEN_MAP28,,,,Drachenkapelle,Capilla Dragón,,Chapelle du Dragon,,Cappella del Drago,Capela do Dragão,Часовня дракона,Змајева капела +Griffin Chapel,TXT_HEXEN_MAP30,,,,Greifenkapelle,Capilla Grifo,,Chapelle du Grifon,,Cappella del Grifone,Capela do Grifo,Часовня грифона,Грифинова капела +Deathwind Chapel,TXT_HEXEN_MAP31,,,,Todeswindkapelle,Capilla Viento de Muerte,,Chapelle du Vent Tueur,,Cappella del Vento Assassino,Capela do Vento da Morte,Часовня ветра смерти,Капела ветра смрти +Orchard of Lamentations,TXT_HEXEN_MAP32,,,,Garten der Wehklagen,Huerto de los Lamentos,,Verger des Lamentations,,Frutteto delle Lamentazioni,Pomar das Lamentações,Сады плача,Воћњак тужбалица +Silent Refectory,TXT_HEXEN_MAP33,,,,Stilles Refektorium,Refectorio Silencioso,,Réfectoire Silencieux,,Refettorio silenzioso,Refeitório Silencioso,Безмолвная трапезная,Тиха трпезарија +Wolf Chapel,TXT_HEXEN_MAP34,,,,Wolfskapelle,Capilla Lobo,,Chapelle du Loup,,Cappella del Lupo,Capela do Lobo,Часовня волка,Вучја капела +Forsaken Outpost,TXT_HEXEN_MAP21,,,,Verlassener Vorposten,Puesto de Avanzada Abandonado,,Base Abandonnée,,Avamposto Abbandonato,Posto Abandonado,Покинутая застава,Напуштена постаја +Castle of Grief,TXT_HEXEN_MAP22,,,,Leidensburg,Castillo del Pesar,,Château de la Souffrance,,Castello della Sofferenza,Castelo da Aflição,Замок скорби,Замак жалости +Gibbet,TXT_HEXEN_MAP23,,,,Richtstätte,Horca,,Gibet,,Forca,Forca,Виселица,Вешала +Effluvium,TXT_HEXEN_MAP24,,,,Effluvium,Efluvio,,Effluvium,,Effluvio,Eflúvio,Зловонный сток,Ефлувиум +Dungeons,TXT_HEXEN_MAP25,,,,Kerker,Calabozos,,Donjons,,I Dungeon,Masmorras,Подземелья,Тамнице +Desolate Garden,TXT_HEXEN_MAP26,,,,Verwildeter Garten,Jardín Desolado,,Jardin de la Désolation,,Giardino Desolato,Jardim Desolado,Заброшенный сад,Пуста башта +Necropolis,TXT_HEXEN_MAP35,,,,Nekropole,Necrópolis,,Nécropole,,Necropoli,Necrópole,Некрополь,Некрополис +Zedek's Tomb,TXT_HEXEN_MAP36,,,,Zedeks Gruft,Tumba de Zedek,,Tombeau de Zedek,,Tomba di Zedek,Tumba de Zedek,Гробница зедека,Зедеков гроб +Menelkir's Tomb,TXT_HEXEN_MAP37,,,,Menelkirs Gruft,Tumba de Menelkir,,Tombeau de Menelkir,,Tomba di Menelkir,Tumba de Menelkir,Гробница менелкира,Менелкиров гроб +Traductus' Tomb,TXT_HEXEN_MAP38,,,,Traductus' Gruft,Tumba de Traductus,,Tombeau de Traductus,,Tomba di Traductus,Tumba de Traductus,Гробница традактуса,Традуктусов гроб +Vivarium,TXT_HEXEN_MAP39,,,,Vivarium,Vivario,,Vivarium,,Vivario,Viveiro,Виварий,Зверињак +Dark Crucible,TXT_HEXEN_MAP40,,,,Dunkle Feuerprobe,Crisol de Oscuridad,,Creuset Sombre,,Crogiolo Buio,,Испытание тьмы,Мрачни лонац ,,,Hexen Deathkings,,,,,,,,,, -Ruined Village,TXT_HEXDD_MAP41,,,,Zerstörtes Dorf,Pueblo en Ruinas,,Village en Ruines,,,Aldeia Destruída,Разрушенная деревня, -Blight,TXT_HEXDD_MAP42,,,,Fäule,Decaimiento,,Peste,,,Peste,Упадок, -Sump,TXT_HEXDD_MAP43,,,,Sumpf,Pozo de Barro,,Puisard,,,,Грязеотстойник, -Catacomb,TXT_HEXDD_MAP44,,,,Katakombe,Catacumba,,Catacombe,,,Catacumba,Катакомба, -Badlands,TXT_HEXDD_MAP45,,,,Ödland,Páramos,,Terres Sauvages,,,Ermo,Бесплодные земли, -Brackenwood,TXT_HEXDD_MAP46,,,,Moorwald,Brackenwood,,Forêt de Bracken,,,,Брекенвуд, -Pyre,TXT_HEXDD_MAP47,,,,Scheiterhaufen,Pira Funeraria,,Brasier,,,Pira,Погребальный костёр, -Constable's Gate,TXT_HEXDD_MAP48,,,,Wachtor,Puerta del Alguacil,,Portail du Constable,,,Portão do Condestável,Комендантские врата, -Treasury,TXT_HEXDD_MAP49,,,,Schatzkammer,Tesorería,,Trésorerie,,,Tesouraria,Сокровищница, -Market Place,TXT_HEXDD_MAP50,,,,Marktplatz,Plaza del Mercado,,Place du Marché,,,Feira,Рыночная площадь, +Ruined Village,TXT_HEXDD_MAP41,,,,Zerstörtes Dorf,Pueblo en Ruinas,,Village en Ruines,,Villagio in Rovine,Aldeia Destruída,Разрушенная деревня, +Blight,TXT_HEXDD_MAP42,,,,Fäule,Decaimiento,,Peste,,Peste,Peste,Упадок, +Sump,TXT_HEXDD_MAP43,,,,Sumpf,Pozo de Barro,,Puisard,,Pozzetto,,Грязеотстойник, +Catacomb,TXT_HEXDD_MAP44,,,,Katakombe,Catacumba,,Catacombe,,Catacombe,Catacumba,Катакомба, +Badlands,TXT_HEXDD_MAP45,,,,Ödland,Páramos,,Terres Sauvages,,Terre Selvagge,Ermo,Бесплодные земли, +Brackenwood,TXT_HEXDD_MAP46,,,,Moorwald,Brackenwood,,Forêt de Bracken,,Foresta di Felce,,Брекенвуд, +Pyre,TXT_HEXDD_MAP47,,,,Scheiterhaufen,Pira Funeraria,,Brasier,,Pira,Pira,Погребальный костёр, +Constable's Gate,TXT_HEXDD_MAP48,,,,Wachtor,Puerta del Alguacil,,Portail du Constable,,Porta del Conestabile,Portão do Condestável,Комендантские врата, +Treasury,TXT_HEXDD_MAP49,,,,Schatzkammer,Tesorería,,Trésorerie,,Tesoreria,Tesouraria,Сокровищница, +Market Place,TXT_HEXDD_MAP50,,,,Marktplatz,Plaza del Mercado,,Place du Marché,,Piazza del Mercato,Feira,Рыночная площадь, Locus Requiescat,TXT_HEXDD_MAP51,,,,Locus Requiescat,Lugar de Descanso Eterno,,Locus Requiescat,,,,Место вечного упокоения, -Ordeal,TXT_HEXDD_MAP52,,,,Heimsuchung,Prueba,,Supplice,,,Ordália,Испытание, -Armory,TXT_HEXDD_MAP53,,,Armoury,Waffenkammer,Armería,,Amurerie,,,Arsenal,Оружейная, -Nave,TXT_HEXDD_MAP54,,,,Kirchenschiff,La Nave,,Nef,,,,Неф, -Chantry,TXT_HEXDD_MAP55,,,,Kantorei,Capilla,,Chapelle,,,Capela,Часовня, -Abattoir,TXT_HEXDD_MAP56,,,,Schlachthaus,Matadero,,Abbatoire,,,Matadouro,Бойня, -Dark Watch,TXT_HEXDD_MAP57,,,,Dunkle Wache,Guardia Oscura,,Garde Noire,,,Guarda Negra,Тёмный страж, +Ordeal,TXT_HEXDD_MAP52,,,,Heimsuchung,Prueba,,Supplice,,Supplizio,Ordália,Испытание, +Armory,TXT_HEXDD_MAP53,,,Armoury,Waffenkammer,Armería,,Amurerie,,Armeria,Arsenal,Оружейная, +Nave,TXT_HEXDD_MAP54,,,,Kirchenschiff,La Nave,,Nef,,Navata,,Неф, +Chantry,TXT_HEXDD_MAP55,,,,Kantorei,Capilla,,Chapelle,,Cappella,Capela,Часовня, +Abattoir,TXT_HEXDD_MAP56,,,,Schlachthaus,Matadero,,Abbatoire,,Mattatoio,Matadouro,Бойня, +Dark Watch,TXT_HEXDD_MAP57,,,,Dunkle Wache,Guardia Oscura,,Garde Noire,,Guardia Oscura,Guarda Negra,Тёмный страж, Cloaca,TXT_HEXDD_MAP58,,,,Kloake,Cloaca,,Cloaque,,,,Клоака, -Ice Hold,TXT_HEXDD_MAP59,,,,Eiskammer,Tierra de Hielo,,Fort de Glace,,,Fortaleza de Gelo,Ледяная твердь, -Dark Citadel,TXT_HEXDD_MAP60,,,,Dunkle Festung,Fortaleza Oscura,,Citadelle Sombre,,,Citadela Negra,Тёмная цитадель, -Transit,TXT_HEXDD_MAP33,,,,,Sala de Espera,,Transit,,,Trânsito,Комната ожидания, -Over'n Under,TXT_HEXDD_MAP34,,,,Drunter und Drüber,Arriba y Abajo,,Sens Dessus Dessous,,,Sobre e Sob,Выше и ниже, -Deathfog,TXT_HEXDD_MAP35,,,,Todesnebel,Niebla de Muerte,,Brume Mortelle,,,Névoa da Morte,Туман смерти, -Castle of Pain,TXT_HEXDD_MAP36,,,,Burg der Angst,Castillo del Dolor,,Château de la Douleur,,,Castelo da Dor,Замок боли, -Sewer Pit,TXT_HEXDD_MAP37,,,,Kanalgrube,Pozo de Residuos,,Trou d'Egout,,,Fosso de Esgoto,Сточная яма, -The Rose,TXT_HEXDD_MAP38,,,,Die Rose,La Rosa,,La Rose,,,A Rosa,Роза, +Ice Hold,TXT_HEXDD_MAP59,,,,Eiskammer,Tierra de Hielo,,Fort de Glace,,Fortezza di Ghiaccio,Fortaleza de Gelo,Ледяная твердь, +Dark Citadel,TXT_HEXDD_MAP60,,,,Dunkle Festung,Fortaleza Oscura,,Citadelle Sombre,,Cittadella Oscura,Citadela Negra,Тёмная цитадель, +Transit,TXT_HEXDD_MAP33,,,,,Sala de Espera,,Transit,,Passaggio,Trânsito,Комната ожидания, +Over'n Under,TXT_HEXDD_MAP34,,,,Drunter und Drüber,Arriba y Abajo,,Sens Dessus Dessous,,Sopra e Sotto,Sobre e Sob,Выше и ниже, +Deathfog,TXT_HEXDD_MAP35,,,,Todesnebel,Niebla de Muerte,,Brume Mortelle,,Nebbia di Morte,Névoa da Morte,Туман смерти, +Castle of Pain,TXT_HEXDD_MAP36,,,,Burg der Angst,Castillo del Dolor,,Château de la Douleur,,Castello del Dolore,Castelo da Dor,Замок боли, +Sewer Pit,TXT_HEXDD_MAP37,,,,Kanalgrube,Pozo de Residuos,,Trou d'Egout,,Fossa della Fogna,Fosso de Esgoto,Сточная яма, +The Rose,TXT_HEXDD_MAP38,,,,Die Rose,La Rosa,,La Rose,,La Rosa,A Rosa,Роза, ,,,Intermission texts,,,,,,,,,, "Once you beat the big badasses and clean out the moon base you're supposed @@ -3992,7 +3992,7 @@ personne à avoir peur d'un défi..",,,,"Когда-то Вы уже владе искушением и примёте вызов...", "\n...and other players await. -",TXT_HEXDD_WIN3MSG,,,,\n...und andere Spieler warten.,\n...y otros jugadores esperan.,,\n...Et d'autres joueurs attendent.,,,,"\n...В то время как другие игроки будут +",TXT_HEXDD_WIN3MSG,,,,\n...und andere Spieler warten.,\n...y otros jugadores esperan.,,\n...Et d'autres joueurs attendent.,,\n...e altri giocatori aspettano.,,"\n...В то время как другие игроки будут ждать своего часа, чтобы совершить свой ход.", ,,,Hexen script texts,,,,,,,,,, @@ -4258,7 +4258,7 @@ More info.,TXT_RPLY0_SCRIPT02_D28804_MOREI,,,,Mehr Informationen.,Más informaci There's more to the Order than meets the eye.,TXT_RYES0_SCRIPT02_D28804_THERE,,,,An dem Orden ist mehr dran als man auf den ersten Blick denkt.,Hay más de la Orden de lo que parece.,Hay más de La Orden de lo que parece.,L'Ordre cache bien son jeu.,,,,"Орден — нечто большее, чем кажется.", We'll talk when you get gold!,TXT_RNO0_SCRIPT02_D28804_WELLT,,,,"Wir werden reden, wenn du Gold hast!",¡Hablaremos cuando traigas oro!,,On parlera quand tu auras des sous!,,,,"Поговорим, когда принесешь золото!", "That's it friend, the well of knowledge has run dry. I've told you more than I should have anyway. Good luck... And don't come back.",TXT_DLG_SCRIPT02_D30320_THATS,,,,"Das war's mein Freund, der Brunnen der Weisheit ist versiegt. Ich habe dir sowieso mehr erzählt als ich sollte. Viel Glück... und komm nicht zurück.",Ya está amigo. El pozo del conocimiento se ha secado. De todas formas te he dicho más de lo que debería. Buena suerte ... y no vuelvas.,,"Eh bien voilà, mon ami, le puits de la connaîssance s'est enfin asséché.. Je t'en ai dit plus que je n'aurai du, de toutes manières! Bonne chance.. Et ne reviens pas!",,,,"Это всё, дружище. Кладезь мудрости исчерпан. Я и так рассказал тебе больше, чем следовало. Удачи... и не возвращайся.", -"Hey, I'm only here in case of an emergency. If the core breaches, then I make sure no one gets in... Or out.",TXT_DLG_SCRIPT02_D31836_HEYIM,,,,"Hey, ich bin nur für den Fall eines Notfalls hier. Im Falle eines Kernbruchs sorge ich dafür das niemand rein kommt... oder raus.","Oye, sólo estoy aquí en caso de una emergencia. Si el núcleo se rompe, entonces me aseguro de que nadie entre... o salga.",,"Hé, je ne suis là qu'en cas d'urgence. Si le coeur a une brèche, il faut que je fasse en sorte que rien ne rentre.. Ou ne sorte.",,,,"Эй, я тут только на случай аварии. Если реактор даст течь, я должен быть уверен, что никто не войдёт... и не выйдет.", +"Hey, I'm only here in case of an emergency. If the core breaches, then I make sure no one gets in... or out.",TXT_DLG_SCRIPT02_D31836_HEYIM,,,,"Hey, ich bin nur für den Fall eines Notfalls hier. Im Falle eines Kernbruchs sorge ich dafür das niemand rein kommt... oder raus.","Oye, sólo estoy aquí en caso de una emergencia. Si el núcleo se rompe, entonces me aseguro de que nadie entre... o salga.",,"Hé, je ne suis là qu'en cas d'urgence. Si le coeur a une brèche, il faut que je fasse en sorte que rien ne rentre.. Ou ne sorte.",,,,"Эй, я тут только на случай аварии. Если реактор даст течь, я должен быть уверен, что никто не войдёт... и не выйдет.", "Watch your step, peasant!",TXT_DLG_SCRIPT02_D33352_WATCH,,"Not sure with the translation of ""peasant"" in this case. [GZ] As long as it is meant as an insult, ""Bauer"" is fine.",,"Pass auf wo du hintrittst, Bauer!","¡Cuida por donde caminas, campesino!",,"Regarde où tu va, paysan!",,,,"Смотри, куда прёшь, рабочий!", We're going to kill you! ,TXT_DLG_SCRIPT02_D34868_WEREG,,,,Wir werden dich umbringen!,"¡Vamos a matarte! @@ -4337,7 +4337,7 @@ What are you doing here?,TXT_DLG_SCRIPT02_D87928_WHATA,,,,Was machst du hier?,¿ "Blackbird told you the code, huh? Let me shut off the alarm. Macil is one flight down.",TXT_DLG_SCRIPT02_D89444_BLACK,,,,"Blackbird hat dir den Code gegeben, wie? Lass mich mal eben den Alarm abschalten. Macil ist eine Etage tiefer.","Blackbird te dijo el código, ¿eh? Déjame apagar la alarma. Macil está un vuelo hacia abajo.",,"Blackbird t'as donné le code, hein? Bon, laisse moi désactiver l'alarme. Macil est en bas des escaliers.",,,,"Чёрный дрозд сказал тебе пароль, так? Давай я отключу сигнализацию. Мэйсил этажом ниже.", Thanks.,TXT_RPLY0_SCRIPT02_D89444_THANK,,,,Danke.,Gracias.,,Merci.,,,,Спасибо., "Walk away, boy, just walk away.",TXT_DLG_SCRIPT02_D90960_WALKA,,,,"Geh weg Jungchen, geh einfach weg.","Aléjate, muchacho, sólo aléjate.",,"Pars, gamin, pars.",,,,"Проходи, парень. Просто проходи.", -Do you have an appointment with the Governor? ,TXT_DLG_SCRIPT02_D92476_DOYOU,,,,Hast du einen Termin bei dem Gouverneur?,¿Tienes una cita con el gobernador?,,Avez-vous un rendez-vous avec le gouverneur?,,,,У вас назначена встреча с губернатором?, +Do you have an appointment with the Governor? ,TXT_DLG_SCRIPT02_D92476_DOYOU,,,,Hast du einen Termin bei dem Gouverneur?,¿Tienes una cita con el gobernador?,,Avez-vous un rendez-vous avec le gouverneur?,,,,У Вас назначена встреча с губернатором?, Of course!,TXT_RPLY0_SCRIPT02_D92476_OFCOU,,,,Natürlich!,¡Claro que si!,,Bien sûr!,,,,Конечно!, "No, and I don't need one!",TXT_RPLY1_SCRIPT02_D92476_NOAND,,,,"Nein, und ich brauche auch keinen!","¡No, y no la necesito!",,"Non, et j'en ai pas besoin!",,,,"Нет, и я в ней не нуждаюсь!", Sorry! I didn't mean... Please go right up.,TXT_DLG_SCRIPT02_D93992_SORRY,,,,Entschuldigung! Das war nicht so gemeint... Bitte geh durch.,"¡Lo siento! No quise decir ... Por favor, suba.",,"Désolé, je.. Vous pouvez monter, bien sûr.",,,,"Простите! Я не хотел... Пожалуйста, пройдите наверх.", @@ -5190,67 +5190,67 @@ Find the chalice in the sanctuary chapel and bring it to Harris upstairs in the Find the Governor's mansion and talk to the Governor to get your reward,TXT_ILOG1102,,,,Finde die Villa des Gouverneurs und rede mit ihm über deine Belohnung.,Encuentra la mansión del Gobernador y habla con el Gobernador para obtener tu recompensa,Encuentra la mansión del Gobernador y habla con él para obtener tú recompensa.,Trouve le manoir du Gouverneur et parle-lui pour réclamer ta récompense.,,,,Пройди в особняк губернатора и обсуди с Морелом своё вознаграждение., Congratulations! You have earned our gratitude. Visit the medic and weapons trainer and they will get you ready for what lies ahead. Feel free to wander around within the base.,TXT_ILOG1201,,,,"Gratulation. Du hast dir unsere Dankbarkeit verdient. Suche den Sanitäter und den Waffentrainer auf, sie werden dich auf das was vor dir liegt, vorbereiten. Wenn du willst, schau dich in der Basis um.",¡Enhorabuena! Te has ganado nuestra gratitud. Visita al médico y al entrenador de armas y te prepararán para lo que viene en adelante. Sé libre de dar un paseo por la base.,¡Felicitaciones! Te haz ganado nuestra gratitud. Visita al médico y al entrenador de armas y ellos te dejarán listo para lo que sigue. Sientete libre de andar através de la base.,Félicitations! Vous méritez notre gratitude. Allez voir le médecin et le maître d'armes et ils pourront vous préparer pour ce qu'il va suivre. Vous pouvez faire le tour de la base si vous le souhaitez.,,,,"Поздравляем! Ты заслужил нашу благодарность. Зайди к медику и инструктору по стрельбе, и они подготовят тебя к тому, что ждёт тебя впереди. Теперь ты можешь свободно передвигаться по нашей базе.", ,,,Strife characters,,,,,,,,,, -Order Sergeant,TXT_SPEAKER_ORDER_SERGEANT,,,,Soldat des Ordens,Sargento de La Orden,Sargento de La Orden,Sergeant de l'Ordre,,,,Сержант Ордена, +Order Sergeant,TXT_SPEAKER_ORDER_SERGEANT,,,,Soldat des Ordens,Sargento de La Orden,Sargento de La Orden,Sergeant de l'Ordre,,Sergente dell'Ordine,,Сержант Ордена, Rowan,TXT_SPEAKER_ROWAN,,,,,,,,,,,Роуэн, Feris,TXT_SPEAKER_FERIS,,,,,,,,,,,Ферис, -Prison Guard,TXT_SPEAKER_PRISON_GUARD,,,,Gefängniswache,Guardia de la Prisión,Guardia de la Prisión,Garde de la Prison,,,,"Страж тюрьмы +Prison Guard,TXT_SPEAKER_PRISON_GUARD,,,,Gefängniswache,Guardia de la Prisión,Guardia de la Prisión,Garde de la Prison,,Guardia della Prigione,,"Страж тюрьмы ", Justin,TXT_SPEAKER_JUSTIN,,,,,,,,,,,Джастин, Macil,TXT_SPEAKER_MACIL,,,,,,,,,,,Мэйсил, -Assistant,TXT_SPEAKER_ASSISTANT,,,,Assistent,Asistente,Asistente,,,,,Ассистент, -Key Master,TXT_SPEAKER_KEY_MASTER,,,,Schlüsselmeister,Amo de Llaves,Maestro de Llaves,Maître des Clés,,,,Ключник, -Bodyguard,TXT_SPEAKER_BODYGUARD,,,,Leibwächter,Guardaespaldas,Guardaespaldas,Garde du corps,,,,Телохранитель, -Interrogator,TXT_SPEAKER_INTERROGATOR,,,,Befrager,Interrogador,Interrogador,Interrogateur,,,,Дознаватель, -Warden Montag,TXT_SPEAKER_WARDEN_MONTAG,,,,Direktor Montag,Carcelero Montag,Director Montag,Gardien Montag,,,,Тюремщик Монтаг, +Assistant,TXT_SPEAKER_ASSISTANT,,,,Assistent,Asistente,Asistente,,,Assistente,,Ассистент, +Key Master,TXT_SPEAKER_KEY_MASTER,,,,Schlüsselmeister,Amo de Llaves,Maestro de Llaves,Maître des Clés,,Maestro di chiavi,,Ключник, +Bodyguard,TXT_SPEAKER_BODYGUARD,,,,Leibwächter,Guardaespaldas,Guardaespaldas,Garde du corps,,Guardia del corpo,,Телохранитель, +Interrogator,TXT_SPEAKER_INTERROGATOR,,,,Befrager,Interrogador,Interrogador,Interrogateur,,Interrogatore,,Дознаватель, +Warden Montag,TXT_SPEAKER_WARDEN_MONTAG,,,,Direktor Montag,Carcelero Montag,Director Montag,Gardien Montag,,Guardiano Montag,,Тюремщик Монтаг, Richter,TXT_SPEAKER_RICHTER,,,,,,,,,,,Рихтер, -Macil's Advisor,TXT_SPEAKER_MACIL_S_ADVISOR,,,,Macils Berater,Consejero de Macil,Consejero de Macil,Conseiller de Macil,,,,Советник Мэйсила, -Judge Wolenick,TXT_SPEAKER_JUDGE_WOLENICK,,,,Richter Wolenick,Juez Wolenick,Juez Wolenick,Juge Wolenick,,,,Судья Уолник, +Macil's Advisor,TXT_SPEAKER_MACIL_S_ADVISOR,,,,Macils Berater,Consejero de Macil,Consejero de Macil,Conseiller de Macil,,Consigliere di Macil,,Советник Мэйсила, +Judge Wolenick,TXT_SPEAKER_JUDGE_WOLENICK,,,,Richter Wolenick,Juez Wolenick,Juez Wolenick,Juge Wolenick,,Giudice Wolenick,,Судья Уолник, Tevick,TXT_SPEAKER_TEVICK,,,,,,,,,,,Тевик, Harris,TXT_SPEAKER_HARRIS,,,,,,,,,,,Харрис, -Foreman,TXT_SPEAKER_FOREMAN,,,,Vormann,Capataz,Capataz,Contremaître,,,,Бригадир, -Prisoner,TXT_SPEAKER_PRISONER,,,,Gefangener,Prisionero,Prisionero,Prisonnier,,,,Пленник, +Foreman,TXT_SPEAKER_FOREMAN,,,,Vormann,Capataz,Capataz,Contremaître,,Caposquadra,,Бригадир, +Prisoner,TXT_SPEAKER_PRISONER,,,,Gefangener,Prisionero,Prisionero,Prisonnier,,Prigioniero,,Пленник, Sammis,TXT_SPEAKER_SAMMIS,,,,,,,,,,,Сэммис, -Weapon Smith,TXT_SPEAKER_WEAPON_SMITH,,,,Waffenschmied,Forjador de Armas,Forjador de Armas,Armurier,,,,Оружейник, -Reactor Guard,TXT_SPEAKER_REACTOR_GUARD,,,,Reaktorwache,Guardia del Reactor,Guardia del Reactor,Garde du réacteur,,,,Страж реактора, -Apprentice,TXT_SPEAKER_APPRENTICE,,,,Lehrling,Aprendiz,Aprendiz,Apprenti,,,,Подмастерье, -Door Guard,TXT_SPEAKER_DOOR_GUARD,,,,Türwache,Guarda de la Puerta,Guarda de la Puerta,Garde de la porte,,,,Страж входа, -Master Smithy,TXT_SPEAKER_MASTER_SMITHY,,,,Meister Smithy,Maestro Herrero,Maestro Herrero,Maîre Forgeron,,,,Мастер-кузнец, -Warehouse Guard,TXT_SPEAKER_WAREHOUSE_GUARD,,,,Lagerhaus-Wache,Guardia del Almacén,Guardia del Almacén,Garde de l'entrepôt,,,,Охранник склада, -Barkeep,TXT_SPEAKER_BARKEEP,,,,Wirt,Tabernero,Barman,Barman,,,,Хозяин таверны, +Weapon Smith,TXT_SPEAKER_WEAPON_SMITH,,,,Waffenschmied,Forjador de Armas,Forjador de Armas,Armurier,,Fabbro d'Armi,,Оружейник, +Reactor Guard,TXT_SPEAKER_REACTOR_GUARD,,,,Reaktorwache,Guardia del Reactor,Guardia del Reactor,Garde du réacteur,,Guardia del Reattore,,Страж реактора, +Apprentice,TXT_SPEAKER_APPRENTICE,,,,Lehrling,Aprendiz,Aprendiz,Apprenti,,Apprendista,,Подмастерье, +Door Guard,TXT_SPEAKER_DOOR_GUARD,,,,Türwache,Guarda de la Puerta,Guarda de la Puerta,Garde de la porte,,Guardia della Porta,,Страж входа, +Master Smithy,TXT_SPEAKER_MASTER_SMITHY,,,,Meister Smithy,Maestro Herrero,Maestro Herrero,Maîre Forgeron,,Maestro della Fucina,,Мастер-кузнец, +Warehouse Guard,TXT_SPEAKER_WAREHOUSE_GUARD,,,,Lagerhaus-Wache,Guardia del Almacén,Guardia del Almacén,Garde de l'entrepôt,,Guardia del Magazzino,,Охранник склада, +Barkeep,TXT_SPEAKER_BARKEEP,,,,Wirt,Tabernero,Barman,Barman,,Barman,,Хозяин таверны, Timothy,TXT_SPEAKER_TIMOTHY,,,,,,,,,,,Тимоти, James,TXT_SPEAKER_JAMES,,,,,,,,,,,Джеймс, Worner,TXT_SPEAKER_WORNER,,,,,,,,,,,Уорнэр, -Bailey Guard,TXT_SPEAKER_BAILEY_GUARD,,,,Vorhof-Wache,Guardia de la Muralla,Guardia Bailey,Garde d'enceinte,,,,Страж крепости, +Bailey Guard,TXT_SPEAKER_BAILEY_GUARD,,,,Vorhof-Wache,Guardia de la Muralla,Guardia Bailey,Garde d'enceinte,,Guardia del Bastione,,Страж крепости, Drone,TXT_SPEAKER_DRONE,,,,Drohne,Dron,Dron,,,,,Дрон, -Front Guard,TXT_SPEAKER_FRONT_GUARD,,,,Frontwache,Guardia del Frente,Guardia del Frente,Garde du Front,,,,Повстанец, +Front Guard,TXT_SPEAKER_FRONT_GUARD,,,,Frontwache,Guardia del Frente,Guardia del Frente,Garde du Front,,Guardia del Fronte,,Повстанец, Quincy,TXT_SPEAKER_QUINCY,,,,,,,,,,,Куинси, -Sergeant,TXT_SPEAKER_SERGEANT,,,,Feldwebel,Sargento,Sargento,Sergeant,,,,Сержант, -Temple Guard,TXT_SPEAKER_TEMPLE_GUARD,,,,Tempelwache,Guardia del Templo,Guardia del Templo,Garde du Temple,,,,Страж храма, -Oracle,TXT_SPEAKER_ORACLE,,,,Orakel,Oráculo,Oráculo,,,,,Оракул, +Sergeant,TXT_SPEAKER_SERGEANT,,,,Feldwebel,Sargento,Sargento,Sergeant,,Sergente,,Сержант, +Temple Guard,TXT_SPEAKER_TEMPLE_GUARD,,,,Tempelwache,Guardia del Templo,Guardia del Templo,Garde du Temple,,Guardia del Tempio,,Страж храма, +Oracle,TXT_SPEAKER_ORACLE,,,,Orakel,Oráculo,Oráculo,,,Oracolo,,Оракул, Ulaine,TXT_SPEAKER_ULAINE,,,,,,,,,,,Улейн, -Front Soldier,TXT_SPEAKER_FRONT_SOLDIER,,,,Front-Soldat,Soldado del Frente,Soldado del Frente,Soldat du Front,,,,Повстанец, -Programmer,TXT_SPEAKER_PROGRAMMER,,,,Programmierer,Programador,Programador,Programmeur,,,,Программист, -Medic,TXT_SPEAKER_MEDIC,,,,Sanitäter,Médico,Médico,Médecin,,,,Медик, -Watchman,TXT_SPEAKER_WATCHMAN,,,,Wächter,Celador,Celador,Vigie,,,,Караульный, +Front Soldier,TXT_SPEAKER_FRONT_SOLDIER,,,,Front-Soldat,Soldado del Frente,Soldado del Frente,Soldat du Front,,Soldato del Fronte,,Повстанец, +Programmer,TXT_SPEAKER_PROGRAMMER,,,,Programmierer,Programador,Programador,Programmeur,,Programmatore,,Программист, +Medic,TXT_SPEAKER_MEDIC,,,,Sanitäter,Médico,Médico,Médecin,,Medico,,Медик, +Watchman,TXT_SPEAKER_WATCHMAN,,,,Wächter,Celador,Celador,Vigie,,Vigilante,,Караульный, Ketrick,TXT_SPEAKER_KETRICK,,,,,,,,,,,Кетрик, Weran,TXT_SPEAKER_WERAN,,,,,,,,,,,Уэран, -Advisor,TXT_SPEAKER_ADVISOR,,,,Berater,Consejero,Consejero,Conseiller,,,,Советник, +Advisor,TXT_SPEAKER_ADVISOR,,,,Berater,Consejero,Consejero,Conseiller,,Consigliere,,Советник, Geoff,TXT_SPEAKER_GEOFF,,,,,,,,,,,Джефф, -Overseer,TXT_SPEAKER_OVERSEER,,,,Aufseher,Superintendente,Superintendente,Superviseur,,,,Надсмотрщик, -Security Complex Guard,TXT_SPEAKER_SECURITY_COMPLE,,,,Wache,Vigía,Vigía,Sécurité,,,,Комплекс охраны, -Computer Tech,TXT_SPEAKER_COMPUTER_TECH,,,,Computertechniker,Ingeniero Informático,Ingeniero Informático,Technicien Informatique,,,,Компьютерный техник, +Overseer,TXT_SPEAKER_OVERSEER,,,,Aufseher,Superintendente,Superintendente,Superviseur,,Supervisore,,Надсмотрщик, +Security Complex Guard,TXT_SPEAKER_SECURITY_COMPLE,,,,Wache,Vigía,Vigía,Sécurité,,Guardia del Complesso di Sicurezza,,Комплекс охраны, +Computer Tech,TXT_SPEAKER_COMPUTER_TECH,,,,Computertechniker,Ingeniero Informático,Ingeniero Informático,Technicien Informatique,,Tecnico Informatico,,Компьютерный техник, MacGuffin,TXT_SPEAKER_MACGUFFIN,,,,,,,,,,,МакГаффин, Arion,TXT_SPEAKER_ARION,,,,,,,,,,,Арион, -Dock Worker,TXT_SPEAKER_DOCK_WORKER,,,,Dockarbeiter,Trabajador del Muelle,Trabajador del Muelle,Docker,,,,Рабочий дока, +Dock Worker,TXT_SPEAKER_DOCK_WORKER,,,,Dockarbeiter,Trabajador del Muelle,Trabajador del Muelle,Docker,,Operaio Portuale,,Рабочий дока, Irale,TXT_SPEAKER_IRALE,,,,,,,,,,,Ирэйл, -Core Guard,TXT_SPEAKER_CORE_GUARD,,,,Wache,Guardia del Núcleo,Guardia del Núcleo,Garde du cœur,,,,Страж ядра, -Sewer Guard,TXT_SPEAKER_SEWER_GUARD,,,,Kanalisationswächter,Guardia de la Alcantarilla,Guardia de la Alcantarilla,Garde des égouts,,,,Страж канализации, -Technician,TXT_SPEAKER_TECHNICIAN,,,,Techniker,Técnico,Técnico,Technicien,,,,Техник, -Guard,TXT_SPEAKER_GUARD,,,,Wache,Guardia,Guardia,Garde,,,,Стражник, -Peasant,TXT_SPEAKER_PEASANT,,,,Einwohner,Campesino,Campesino,Paysan,,,,Работник, -Armorer,TXT_SPEAKER_ARMORER,,,Armourer,Waffenmeister,Armero,Armero,Armurier,,,,Бронник, +Core Guard,TXT_SPEAKER_CORE_GUARD,,,,Wache,Guardia del Núcleo,Guardia del Núcleo,Garde du cœur,,Guardia del Cuore,,Страж ядра, +Sewer Guard,TXT_SPEAKER_SEWER_GUARD,,,,Kanalisationswächter,Guardia de la Alcantarilla,Guardia de la Alcantarilla,Garde des égouts,,Guardia della Fogna,,Страж канализации, +Technician,TXT_SPEAKER_TECHNICIAN,,,,Techniker,Técnico,Técnico,Technicien,,Tecnico,,Техник, +Guard,TXT_SPEAKER_GUARD,,,,Wache,Guardia,Guardia,Garde,,Guardia,,Стражник, +Peasant,TXT_SPEAKER_PEASANT,,,,Einwohner,Campesino,Campesino,Paysan,,Contadino,,Работник, +Armorer,TXT_SPEAKER_ARMORER,,,Armourer,Waffenmeister,Armero,Armero,Armurier,,Armiere,,Бронник, Beldin,TXT_SPEAKER_BELDIN,,,,,,,,,,,Белдин, Gerard,TXT_SPEAKER_GERARD,,,,,,,,,,,Джерард, -Governor Mourel,TXT_SPEAKER_GOVERNOR_MOUREL,,,,Gouverneur Mourel,Gobernador Mourel,Gobernador Mourel,Gouverneur Mourel,,,,Губернатор Морел, +Governor Mourel,TXT_SPEAKER_GOVERNOR_MOUREL,,,,Gouverneur Mourel,Gobernador Mourel,Gobernador Mourel,Gouverneur Mourel,,Governatore Mourel,,Губернатор Морел, Bowyer,TXT_SPEAKER_BOWYER,,,,,,,,,,,Лучник, Derwin,TXT_SPEAKER_DERWIN,,,,,,,,,,,Дервин, \ No newline at end of file