diff --git a/docs/licenses/xBRZ.jpg b/docs/licenses/xBRZ.jpg new file mode 100644 index 000000000..ab8335391 Binary files /dev/null and b/docs/licenses/xBRZ.jpg differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d78903973..a4791cb7f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -115,49 +115,59 @@ if( WIN32 ) set( FMOD_LIB_PATH_SUFFIXES PATH_SUFFIXES lib ) set( NASM_NAMES nasmw nasm ) - find_path( D3D_INCLUDE_DIR d3d9.h - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Include ) - if( NOT D3D_INCLUDE_DIR ) - message( SEND_ERROR "Could not find DirectX 9 header files" ) - else() - include_directories( ${D3D_INCLUDE_DIR} ) - endif() + if( NOT MSVC ) + find_path( D3D_INCLUDE_DIR d3d9.h + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Include ) + if( NOT D3D_INCLUDE_DIR ) + message( SEND_ERROR "Could not find DirectX 9 header files" ) + else() + include_directories( ${D3D_INCLUDE_DIR} ) + endif() - find_path( XINPUT_INCLUDE_DIR xinput.h - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Include ) - if( NOT XINPUT_INCLUDE_DIR ) - message( WARNING "Could not find xinput.h. XInput will be disabled." ) - add_definitions( -DNO_XINPUT ) + find_path( XINPUT_INCLUDE_DIR xinput.h + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Include ) + if( NOT XINPUT_INCLUDE_DIR ) + message( WARNING "Could not find xinput.h. XInput will be disabled." ) + add_definitions( -DNO_XINPUT ) + else() + include_directories( ${XINPUT_INCLUDE_DIR} ) + endif() + + find_library( DX_dxguid_LIBRARY dxguid + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Lib Lib/${XBITS} ) + find_library( DX_dinput8_LIBRARY dinput8 + PATHS ENV DXSDK_DIR + PATH_SUFFIXES Lib Lib/${XBITS} ) + + set( DX_LIBS_FOUND YES ) + if( NOT DX_dxguid_LIBRARY ) + set( DX_LIBS_FOUND NO ) + endif() + if( NOT DX_dinput8_LIBRARY ) + set( DX_LIBS_FOUND NO ) + endif() + + if( NOT DX_LIBS_FOUND ) + message( FATAL_ERROR "Could not find DirectX 9 libraries" ) + endif() + + set( DX_LIBS + "${DX_dxguid_LIBRARY}" + "${DX_dinput8_LIBRARY}" + ) else() - include_directories( ${XINPUT_INCLUDE_DIR} ) + set( DX_LIBS + dxguid + dinput8 + ) endif() - find_library( DX_dxguid_LIBRARY dxguid - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Lib Lib/${XBITS} ) - find_library( DX_dinput8_LIBRARY dinput8 - PATHS ENV DXSDK_DIR - PATH_SUFFIXES Lib Lib/${XBITS} ) - - set( DX_LIBS_FOUND YES ) - if( NOT DX_dxguid_LIBRARY ) - set( DX_LIBS_FOUND NO ) - endif() - if( NOT DX_dinput8_LIBRARY ) - set( DX_LIBS_FOUND NO ) - endif() - - if( NOT DX_LIBS_FOUND ) - message( FATAL_ERROR "Could not find DirectX 9 libraries" ) - endif() - - set( ZDOOM_LIBS + set( ZDOOM_LIBS ${DX_LIBS} wsock32 winmm - "${DX_dxguid_LIBRARY}" - "${DX_dinput8_LIBRARY}" ole32 user32 gdi32 @@ -660,6 +670,7 @@ set( PLAT_WIN32_SOURCES win32/i_main.cpp win32/i_movie.cpp win32/i_system.cpp + win32/i_specialpaths.cpp win32/st_start.cpp win32/win32gliface.cpp win32/win32video.cpp ) @@ -679,8 +690,11 @@ set( PLAT_SDL_SOURCES posix/sdl/sdlvideo.cpp posix/sdl/sdlglvideo.cpp posix/sdl/st_start.cpp ) +set( PLAT_UNIX_SOURCES + posix/unix/i_specialpaths.cpp ) set( PLAT_OSX_SOURCES posix/osx/iwadpicker_cocoa.mm + posix/osx/i_specialpaths.mm posix/osx/zdoom.icns ) set( PLAT_COCOA_SOURCES posix/cocoa/critsec.cpp @@ -697,7 +711,7 @@ set( PLAT_COCOA_SOURCES if( WIN32 ) set( SYSTEM_SOURCES_DIR win32 ) set( SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ${PLAT_UNIX_SOURCES} ) if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) # CMake is not set up to compile and link rc files with GCC. :( @@ -712,12 +726,12 @@ elseif( APPLE ) if( OSX_COCOA_BACKEND ) set( SYSTEM_SOURCES_DIR posix posix/cocoa ) set( SYSTEM_SOURCES ${PLAT_COCOA_SOURCES} ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_UNIX_SOURCES} ) else() set( SYSTEM_SOURCES_DIR posix posix/sdl ) set( SYSTEM_SOURCES ${PLAT_SDL_SOURCES} ) set( PLAT_OSX_SOURCES ${PLAT_OSX_SOURCES} posix/sdl/i_system.mm ) - set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ) + set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_COCOA_SOURCES} ${PLAT_UNIX_SOURCES} ) endif() set( SYSTEM_SOURCES ${SYSTEM_SOURCES} ${PLAT_POSIX_SOURCES} ${PLAT_OSX_SOURCES} "${FMOD_LIBRARY}" ) @@ -727,7 +741,7 @@ elseif( APPLE ) set_source_files_properties( posix/osx/iwadpicker_cocoa.mm PROPERTIES COMPILE_FLAGS -fobjc-exceptions ) else() set( SYSTEM_SOURCES_DIR posix posix/sdl ) - set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ) + set( SYSTEM_SOURCES ${PLAT_POSIX_SOURCES} ${PLAT_SDL_SOURCES} ${PLAT_UNIX_SOURCES} ) set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} ) endif() @@ -849,6 +863,7 @@ file( GLOB HEADER_FILES gl/dynlights/*.h gl/hqnx/*.h gl/hqnx_asm/*.h + gl/xbr/*.h gl/models/*.h gl/renderer/*.h gl/scene/*.h @@ -1072,6 +1087,8 @@ set( FASTMATH_SOURCES gl/hqnx/hq2x.cpp gl/hqnx/hq3x.cpp gl/hqnx/hq4x.cpp + gl/xbr/xbrz.cpp + gl/xbr/xbrz_old.cpp gl/textures/gl_hwtexture.cpp gl/textures/gl_texture.cpp gl/textures/gl_material.cpp @@ -1385,7 +1402,6 @@ add_executable( zdoom WIN32 MACOSX_BUNDLE ${FASTMATH_SOURCES} ${PCH_SOURCES} x86.cpp - m_specialpaths.cpp strnatcmp.c zstring.cpp math/asin.c @@ -1543,7 +1559,8 @@ source_group("OpenGL Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/ source_group("OpenGL Renderer\\Data" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/data/.+") source_group("OpenGL Renderer\\Dynamic Lights" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/dynlights/.+") source_group("OpenGL Renderer\\HQ Resize" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx/.+") -source_group("OpenGL Renderer\\HQ Resize Assembly version" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx_asm/.+") +source_group("OpenGL Renderer\\HQ Resize MMX version" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx_asm/.+") +source_group("OpenGL Renderer\\XBRZ" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/hqnx_asm/.+") source_group("OpenGL Renderer\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/models/.+") source_group("OpenGL Renderer\\Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/renderer/.+") source_group("OpenGL Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/scene/.+") @@ -1562,6 +1579,7 @@ source_group("Resource Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r source_group("POSIX Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/.+") source_group("Cocoa Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/cocoa/.+") source_group("OS X Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/osx/.+") +source_group("Unix Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/unix/.+") source_group("SDL Files" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/posix/sdl/.+") source_group("SFMT" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/sfmt/.+") source_group("Shared Game" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_shared/.+") diff --git a/src/actor.h b/src/actor.h index 60a00582f..28e68b64a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -283,7 +283,7 @@ enum ActorFlag4 enum ActorFlag5 { MF5_DONTDRAIN = 0x00000001, // cannot be drained health from. - /* FREE SLOT 0x00000002*/ + MF5_GETOWNER = 0x00000002, MF5_NODROPOFF = 0x00000004, // cannot drop off under any circumstances. MF5_NOFORWARDFALL = 0x00000008, // Does not make any actor fall forward by being damaged by this MF5_COUNTSECRET = 0x00000010, // From Doom 64: actor acts like a secret diff --git a/src/c_console.cpp b/src/c_console.cpp index 850a29455..08f850675 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -159,12 +159,37 @@ static int HistSize; CVAR (Float, con_notifytime, 3.f, CVAR_ARCHIVE) CVAR (Bool, con_centernotify, false, CVAR_ARCHIVE) -CUSTOM_CVAR (Int, con_scaletext, 0, CVAR_ARCHIVE) // Scale notify text at high resolutions? +CUSTOM_CVAR (Int, con_scaletext, 1, CVAR_ARCHIVE) // Scale notify text at high resolutions? { if (self < 0) self = 0; if (self > 3) self = 3; } +CUSTOM_CVAR(Int, con_scale, 0, CVAR_ARCHIVE) +{ + if (self < 0) self = 0; +} + +int active_con_scale() +{ + if (con_scale == 0) + return uiscale; + else + return con_scale; +} + +int active_con_scaletext() +{ + switch (con_scaletext) + { + default: + case 0: return 1; + case 1: return uiscale; + case 2: return 2; + case 3: return 4; + } +} + CUSTOM_CVAR(Float, con_alpha, 0.75f, CVAR_ARCHIVE) { if (self < 0.f) self = 0.f; @@ -493,13 +518,13 @@ void C_AddNotifyString (int printlevel, const char *source) return; } - switch (con_scaletext) + if (active_con_scaletext() == 0) { - default: - case 0: width = DisplayWidth; break; - case 1: width = DisplayWidth / CleanXfac; break; - case 2: width = DisplayWidth / 2; break; - case 3: width = DisplayWidth / 4; break; + width = DisplayWidth / CleanXfac; + } + else + { + width = DisplayWidth / active_con_scaletext(); } if (addtype == APPENDLINE && NotifyStrings[NUMNOTIFIES-1].PrintLevel == printlevel) @@ -721,7 +746,7 @@ static void C_DrawNotifyText () canskip = true; lineadv = SmallFont->GetHeight (); - if (con_scaletext == 1) + if (active_con_scaletext() == 0) { lineadv *= CleanYfac; } @@ -755,7 +780,7 @@ static void C_DrawNotifyText () else color = PrintColors[NotifyStrings[i].PrintLevel]; - if (con_scaletext == 1) + if (active_con_scaletext() == 0) { if (!center) screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, @@ -766,7 +791,7 @@ static void C_DrawNotifyText () line, NotifyStrings[i].Text, DTA_CleanNoMove, true, DTA_AlphaF, alpha, TAG_DONE); } - else if (con_scaletext == 0) + else if (active_con_scaletext() == 1) { if (!center) screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, @@ -777,37 +802,20 @@ static void C_DrawNotifyText () line, NotifyStrings[i].Text, DTA_AlphaF, alpha, TAG_DONE); } - else if (con_scaletext == 3) - { - if (!center) - screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, - DTA_VirtualWidth, screen->GetWidth() / 4, - DTA_VirtualHeight, screen->GetHeight() / 4, - DTA_KeepRatio, true, - DTA_AlphaF, alpha, TAG_DONE); - else - screen->DrawText (SmallFont, color, (screen->GetWidth() / 4 - - SmallFont->StringWidth (NotifyStrings[i].Text))/4, - line, NotifyStrings[i].Text, - DTA_VirtualWidth, screen->GetWidth() / 4, - DTA_VirtualHeight, screen->GetHeight() / 4, - DTA_KeepRatio, true, - DTA_AlphaF, alpha, TAG_DONE); - } else { if (!center) screen->DrawText (SmallFont, color, 0, line, NotifyStrings[i].Text, - DTA_VirtualWidth, screen->GetWidth() / 2, - DTA_VirtualHeight, screen->GetHeight() / 2, + DTA_VirtualWidth, screen->GetWidth() / active_con_scaletext(), + DTA_VirtualHeight, screen->GetHeight() / active_con_scaletext(), DTA_KeepRatio, true, DTA_AlphaF, alpha, TAG_DONE); else - screen->DrawText (SmallFont, color, (screen->GetWidth() / 2 - - SmallFont->StringWidth (NotifyStrings[i].Text))/2, + screen->DrawText (SmallFont, color, (screen->GetWidth() / active_con_scaletext() - + SmallFont->StringWidth (NotifyStrings[i].Text))/ active_con_scaletext(), line, NotifyStrings[i].Text, - DTA_VirtualWidth, screen->GetWidth() / 2, - DTA_VirtualHeight, screen->GetHeight() / 2, + DTA_VirtualWidth, screen->GetWidth() / active_con_scaletext(), + DTA_VirtualHeight, screen->GetHeight() / active_con_scaletext(), DTA_KeepRatio, true, DTA_AlphaF, alpha, TAG_DONE); } @@ -851,9 +859,13 @@ void C_DrawConsole (bool hw2d) static int oldbottom = 0; int lines, left, offset; + int textScale = active_con_scale(); + if (textScale == 0) + textScale = CleanXfac; + left = LEFTMARGIN; - lines = (ConBottom-ConFont->GetHeight()*2)/ConFont->GetHeight(); - if (-ConFont->GetHeight() + lines*ConFont->GetHeight() > ConBottom - ConFont->GetHeight()*7/2) + lines = (ConBottom/textScale-ConFont->GetHeight()*2)/ConFont->GetHeight(); + if (-ConFont->GetHeight() + lines*ConFont->GetHeight() > ConBottom/textScale - ConFont->GetHeight()*7/2) { offset = -ConFont->GetHeight()/2; lines--; @@ -899,16 +911,26 @@ void C_DrawConsole (bool hw2d) if (ConBottom >= 12) { - screen->DrawText (ConFont, CR_ORANGE, SCREENWIDTH - 8 - - ConFont->StringWidth (GetVersionString()), - ConBottom - ConFont->GetHeight() - 4, - GetVersionString(), TAG_DONE); + if (textScale == 1) + screen->DrawText (ConFont, CR_ORANGE, SCREENWIDTH - 8 - + ConFont->StringWidth (GetVersionString()), + ConBottom / textScale - ConFont->GetHeight() - 4, + GetVersionString(), TAG_DONE); + else + screen->DrawText(ConFont, CR_ORANGE, SCREENWIDTH / textScale - 8 - + ConFont->StringWidth(GetVersionString()), + ConBottom / textScale - ConFont->GetHeight() - 4, + GetVersionString(), + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + if (TickerMax) { char tickstr[256]; - const int tickerY = ConBottom - ConFont->GetHeight() - 4; + const int tickerY = ConBottom / textScale - ConFont->GetHeight() - 4; size_t i; - int tickend = ConCols - SCREENWIDTH / 90 - 6; + int tickend = ConCols / textScale - SCREENWIDTH / textScale / 90 - 6; int tickbegin = 0; if (TickerLabel) @@ -931,11 +953,23 @@ void C_DrawConsole (bool hw2d) { tickstr[tickend+3] = 0; } - screen->DrawText (ConFont, CR_BROWN, LEFTMARGIN, tickerY, tickstr, TAG_DONE); + if (textScale == 1) + screen->DrawText (ConFont, CR_BROWN, LEFTMARGIN, tickerY, tickstr, TAG_DONE); + else + screen->DrawText (ConFont, CR_BROWN, LEFTMARGIN, tickerY, tickstr, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); // Draw the marker i = LEFTMARGIN+5+tickbegin*8 + Scale (TickerAt, (SDWORD)(tickend - tickbegin)*8, TickerMax); - screen->DrawChar (ConFont, CR_ORANGE, (int)i, tickerY, 0x13, TAG_DONE); + if (textScale == 1) + screen->DrawChar (ConFont, CR_ORANGE, (int)i, tickerY, 0x13, TAG_DONE); + else + screen->DrawChar(ConFont, CR_ORANGE, (int)i, tickerY, 0x13, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); TickerVisible = true; } @@ -971,18 +1005,28 @@ void C_DrawConsole (bool hw2d) if (lines > 0) { // No more enqueuing because adding new text to the console won't touch the actual print data. - conbuffer->FormatText(ConFont, ConWidth); + conbuffer->FormatText(ConFont, ConWidth / textScale); unsigned int consolelines = conbuffer->GetFormattedLineCount(); FBrokenLines **blines = conbuffer->GetLines(); FBrokenLines **printline = blines + consolelines - 1 - RowAdjust; - int bottomline = ConBottom - ConFont->GetHeight()*2 - 4; + int bottomline = ConBottom / textScale - ConFont->GetHeight()*2 - 4; ConsoleDrawing = true; for(FBrokenLines **p = printline; p >= blines && lines > 0; p--, lines--) { - screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, TAG_DONE); + if (textScale == 1) + { + screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, TAG_DONE); + } + else + { + screen->DrawText(ConFont, CR_TAN, LEFTMARGIN, offset + lines * ConFont->GetHeight(), (*p)->Text, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + } } ConsoleDrawing = false; @@ -997,21 +1041,52 @@ void C_DrawConsole (bool hw2d) FString command((char *)&CmdLine[2+CmdLine[259]]); int cursorpos = CmdLine[1] - CmdLine[259]; - screen->DrawChar (ConFont, CR_ORANGE, left, bottomline, '\x1c', TAG_DONE); - screen->DrawText (ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline, - command, TAG_DONE); - - if (cursoron) + if (textScale == 1) { - screen->DrawChar (ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb), - bottomline, '\xb', TAG_DONE); + screen->DrawChar(ConFont, CR_ORANGE, left, bottomline, '\x1c', TAG_DONE); + screen->DrawText(ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline, + command, TAG_DONE); + + if (cursoron) + { + screen->DrawChar(ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb), + bottomline, '\xb', TAG_DONE); + } + } + else + { + screen->DrawChar(ConFont, CR_ORANGE, left, bottomline, '\x1c', + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + + screen->DrawText(ConFont, CR_ORANGE, left + ConFont->GetCharWidth(0x1c), bottomline, + command, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + + if (cursoron) + { + screen->DrawChar(ConFont, CR_YELLOW, left + ConFont->GetCharWidth(0x1c) + cursorpos * ConFont->GetCharWidth(0xb), + bottomline, '\xb', + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); + } } } if (RowAdjust && ConBottom >= ConFont->GetHeight()*7/2) { // Indicate that the view has been scrolled up (10) // and if we can scroll no further (12) - screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, TAG_DONE); + if (textScale == 1) + screen->DrawChar (ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, TAG_DONE); + else + screen->DrawChar(ConFont, CR_GREEN, 0, bottomline, RowAdjust == conbuffer->GetFormattedLineCount() ? 12 : 10, + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); } } } diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index b053d8bd8..3c0994a3b 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -46,6 +46,8 @@ EXTERN_CVAR (Bool, sb_cooperative_enable) EXTERN_CVAR (Bool, sb_deathmatch_enable) EXTERN_CVAR (Bool, sb_teamdeathmatch_enable) +int active_con_scaletext(); + // Public data void CT_Init (); @@ -224,7 +226,7 @@ void CT_Drawer (void) int i, x, scalex, y, promptwidth; y = (viewactive || gamestate != GS_LEVEL) ? -10 : -30; - if (con_scaletext == 1) + if (active_con_scaletext() == 0) { scalex = CleanXfac; y *= CleanYfac; @@ -235,25 +237,17 @@ void CT_Drawer (void) } int screen_width, screen_height, st_y; - switch (con_scaletext) + if (active_con_scaletext() == 0) { - default: - case 0: - case 1: screen_width = SCREENWIDTH; screen_height = SCREENHEIGHT; st_y = ST_Y; - break; - case 2: - screen_width = SCREENWIDTH / 2; - screen_height = SCREENHEIGHT / 2; - st_y = ST_Y / 2; - break; - case 3: - screen_width = SCREENWIDTH / 4; - screen_height = SCREENHEIGHT / 4; - st_y = ST_Y / 4; - break; + } + else + { + screen_width = SCREENWIDTH / active_con_scaletext(); + screen_height = SCREENHEIGHT / active_con_scaletext(); + st_y = ST_Y / active_con_scaletext(); } y += ((SCREENHEIGHT == viewheight && viewactive) || gamestate != GS_LEVEL) ? screen_height : st_y; @@ -280,10 +274,10 @@ void CT_Drawer (void) // draw the prompt, text, and cursor ChatQueue[len] = SmallFont->GetCursor(); ChatQueue[len+1] = '\0'; - if (con_scaletext < 2) + if (active_con_scaletext() < 2) { - screen->DrawText (SmallFont, CR_GREEN, 0, y, prompt, DTA_CleanNoMove, *con_scaletext, TAG_DONE); - screen->DrawText (SmallFont, CR_GREY, promptwidth, y, (char *)(ChatQueue + i), DTA_CleanNoMove, *con_scaletext, TAG_DONE); + screen->DrawText (SmallFont, CR_GREEN, 0, y, prompt, DTA_CleanNoMove, active_con_scaletext() == 0, TAG_DONE); + screen->DrawText (SmallFont, CR_GREY, promptwidth, y, (char *)(ChatQueue + i), DTA_CleanNoMove, active_con_scaletext() == 0, TAG_DONE); } else { diff --git a/src/g_shared/a_fastprojectile.cpp b/src/g_shared/a_fastprojectile.cpp index 8c7d63b74..c1253f966 100644 --- a/src/g_shared/a_fastprojectile.cpp +++ b/src/g_shared/a_fastprojectile.cpp @@ -166,8 +166,14 @@ void AFastProjectile::Effect() if (trail != NULL) { AActor *act = Spawn (trail, PosAtZ(hitz), ALLOW_REPLACE); - if (act != NULL) + if (act != nullptr) { + if ((flags5 & MF5_GETOWNER) && (target != nullptr)) + act->target = target; + else + act->target = this; + + act->Angles.Pitch = Angles.Pitch; act->Angles.Yaw = Angles.Yaw; } } diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index 74c416468..aae006ca7 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -161,7 +161,7 @@ double DEarthquake::GetModWave(double waveMultiplier) const // //========================================================================== -double DEarthquake::GetModIntensity(double intensity) const +double DEarthquake::GetModIntensity(double intensity, bool fake) const { assert(m_CountdownStart >= m_Countdown); @@ -195,7 +195,7 @@ double DEarthquake::GetModIntensity(double intensity) const } scalar = (scalar > divider) ? divider : scalar; - if (m_Flags & QF_FULLINTENSITY) + if (!fake && (m_Flags & QF_FULLINTENSITY)) { scalar *= 2; } @@ -273,64 +273,69 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger DEarthquake *quake; int count = 0; - while ( (quake = iterator.Next()) != NULL) + while ( (quake = iterator.Next()) != nullptr) { - if (quake->m_Spot != NULL) + if (quake->m_Spot != nullptr) { - double dist = quake->m_Spot->Distance2D (victim, true); + const double dist = quake->m_Spot->Distance2D(victim, true); if (dist < quake->m_TremorRadius) { - const double falloff = quake->GetFalloff(dist); - const double rfalloff = (quake->m_RollIntensity != 0) ? falloff : 0.; ++count; - double x = quake->GetModIntensity(quake->m_Intensity.X); - double y = quake->GetModIntensity(quake->m_Intensity.Y); - double z = quake->GetModIntensity(quake->m_Intensity.Z); - double r = quake->GetModIntensity(quake->m_RollIntensity); + const double falloff = quake->GetFalloff(dist); + const double r = quake->GetModIntensity(quake->m_RollIntensity); + const double strength = quake->GetModIntensity(1.0, true); + DVector3 intensity; + intensity.X = quake->GetModIntensity(quake->m_Intensity.X); + intensity.Y = quake->GetModIntensity(quake->m_Intensity.Y); + intensity.Z = quake->GetModIntensity(quake->m_Intensity.Z); if (!(quake->m_Flags & QF_WAVE)) { jiggers.Falloff = MAX(falloff, jiggers.Falloff); - jiggers.RFalloff = MAX(rfalloff, jiggers.RFalloff); - jiggers.RollIntensity = MAX(r, jiggers.RollIntensity); + jiggers.RollIntensity = MAX(r, jiggers.RollIntensity) * jiggers.Falloff; + + intensity *= jiggers.Falloff; if (quake->m_Flags & QF_RELATIVE) { - jiggers.RelIntensity.X = MAX(x, jiggers.RelIntensity.X); - jiggers.RelIntensity.Y = MAX(y, jiggers.RelIntensity.Y); - jiggers.RelIntensity.Z = MAX(z, jiggers.RelIntensity.Z); + jiggers.RelIntensity.X = MAX(intensity.X, jiggers.RelIntensity.X); + jiggers.RelIntensity.Y = MAX(intensity.Y, jiggers.RelIntensity.Y); + jiggers.RelIntensity.Z = MAX(intensity.Z, jiggers.RelIntensity.Z); } else { - jiggers.Intensity.X = MAX(x, jiggers.Intensity.X); - jiggers.Intensity.Y = MAX(y, jiggers.Intensity.Y); - jiggers.Intensity.Z = MAX(z, jiggers.Intensity.Z); + jiggers.Intensity.X = MAX(intensity.X, jiggers.Intensity.X); + jiggers.Intensity.Y = MAX(intensity.Y, jiggers.Intensity.Y); + jiggers.Intensity.Z = MAX(intensity.Z, jiggers.Intensity.Z); } } else { - jiggers.WFalloff = MAX(falloff, jiggers.WFalloff); - jiggers.RWFalloff = MAX(rfalloff, jiggers.RWFalloff); - jiggers.RollWave = r * quake->GetModWave(quake->m_RollWave); - double mx = x * quake->GetModWave(quake->m_WaveSpeed.X); - double my = y * quake->GetModWave(quake->m_WaveSpeed.Y); - double mz = z * quake->GetModWave(quake->m_WaveSpeed.Z); + jiggers.Falloff = MAX(falloff, jiggers.Falloff); + jiggers.RollWave = r * quake->GetModWave(quake->m_RollWave) * jiggers.Falloff * strength; + + + intensity.X *= quake->GetModWave(quake->m_WaveSpeed.X); + intensity.Y *= quake->GetModWave(quake->m_WaveSpeed.Y); + intensity.Z *= quake->GetModWave(quake->m_WaveSpeed.Z); + intensity *= strength * jiggers.Falloff; // [RH] This only gives effect to the last sine quake. I would // prefer if some way was found to make multiples coexist // peacefully, but just summing them together is undesirable // because they could cancel each other out depending on their // relative phases. + + // [MC] Now does so. And they stack rather well. I'm a little + // surprised at how easy it was. + + if (quake->m_Flags & QF_RELATIVE) { - jiggers.RelOffset.X = mx; - jiggers.RelOffset.Y = my; - jiggers.RelOffset.Z = mz; + jiggers.RelOffset += intensity; } else { - jiggers.Offset.X = mx; - jiggers.Offset.Y = my; - jiggers.Offset.Z = mz; + jiggers.Offset += intensity; } } } diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index f873ab0b7..314061f04 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -153,7 +153,7 @@ struct FQuakeJiggers DVector3 RelIntensity; DVector3 Offset; DVector3 RelOffset; - double Falloff, WFalloff, RFalloff, RWFalloff; + double Falloff; double RollIntensity, RollWave; }; @@ -180,8 +180,7 @@ public: int m_Highpoint, m_MiniCount; double m_RollIntensity, m_RollWave; - - double GetModIntensity(double intensity) const; + double GetModIntensity(double intensity, bool fake = false) const; double GetModWave(double waveMultiplier) const; double GetFalloff(double dist) const; diff --git a/src/g_shared/hudmessages.cpp b/src/g_shared/hudmessages.cpp index 9f13d3d9e..32a8d2de5 100644 --- a/src/g_shared/hudmessages.cpp +++ b/src/g_shared/hudmessages.cpp @@ -41,7 +41,8 @@ #include "doomstat.h" #include "farchive.h" -EXTERN_CVAR (Int, con_scaletext) +EXTERN_CVAR(Int, con_scaletext) +int active_con_scaletext(); IMPLEMENT_POINTY_CLASS (DHUDMessage) DECLARE_POINTER(Next) @@ -260,13 +261,10 @@ void DHUDMessage::ResetText (const char *text) } else { - switch (con_scaletext) + switch (active_con_scaletext()) { - default: - case 0: width = SCREENWIDTH; break; - case 1: width = SCREENWIDTH / CleanXfac; break; - case 2: width = SCREENWIDTH / 2; break; - case 3: width = SCREENWIDTH / 4; break; + case 0: width = SCREENWIDTH / CleanXfac; break; + default: width = SCREENWIDTH / active_con_scaletext(); break; } } @@ -332,7 +330,7 @@ void DHUDMessage::Draw (int bottom, int visibility) int screen_width = SCREENWIDTH; int screen_height = SCREENHEIGHT; - if (HUDWidth == 0 && con_scaletext==1) + if (HUDWidth == 0 && active_con_scaletext() == 0) { clean = true; xscale = CleanXfac; @@ -341,17 +339,11 @@ void DHUDMessage::Draw (int bottom, int visibility) else { xscale = yscale = 1; - if (HUDWidth==0 && con_scaletext==2) + if (HUDWidth == 0) { - screen_width/=2; - screen_height/=2; - bottom/=2; - } - else if (HUDWidth==0 && con_scaletext==3) - { - screen_width/=4; - screen_height/=4; - bottom/=4; + screen_width /= active_con_scaletext(); + screen_height /= active_con_scaletext(); + bottom /= active_con_scaletext(); } } @@ -453,7 +445,7 @@ void DHUDMessage::DoDraw (int linenum, int x, int y, bool clean, int hudheight) { if (hudheight == 0) { - if (con_scaletext <= 1) + if (active_con_scaletext() <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, @@ -461,21 +453,11 @@ void DHUDMessage::DoDraw (int linenum, int x, int y, bool clean, int hudheight) DTA_RenderStyle, Style, TAG_DONE); } - else if (con_scaletext == 3) - { - screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/4, - DTA_VirtualHeight, SCREENHEIGHT/4, - DTA_AlphaF, Alpha, - DTA_RenderStyle, Style, - DTA_KeepRatio, true, - TAG_DONE); - } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/2, - DTA_VirtualHeight, SCREENHEIGHT/2, + DTA_VirtualWidth, SCREENWIDTH / active_con_scaletext(), + DTA_VirtualHeight, SCREENHEIGHT / active_con_scaletext(), DTA_AlphaF, Alpha, DTA_RenderStyle, Style, DTA_KeepRatio, true, @@ -566,7 +548,7 @@ void DHUDMessageFadeOut::DoDraw (int linenum, int x, int y, bool clean, int hudh float trans = float(Alpha * -(Tics - FadeOutTics) / FadeOutTics); if (hudheight == 0) { - if (con_scaletext <= 1) + if (active_con_scaletext() <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, @@ -574,21 +556,11 @@ void DHUDMessageFadeOut::DoDraw (int linenum, int x, int y, bool clean, int hudh DTA_RenderStyle, Style, TAG_DONE); } - else if (con_scaletext == 3) - { - screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/4, - DTA_VirtualHeight, SCREENHEIGHT/4, - DTA_AlphaF, trans, - DTA_RenderStyle, Style, - DTA_KeepRatio, true, - TAG_DONE); - } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/2, - DTA_VirtualHeight, SCREENHEIGHT/2, + DTA_VirtualWidth, SCREENWIDTH / active_con_scaletext(), + DTA_VirtualHeight, SCREENHEIGHT / active_con_scaletext(), DTA_AlphaF, trans, DTA_RenderStyle, Style, DTA_KeepRatio, true, @@ -676,7 +648,7 @@ void DHUDMessageFadeInOut::DoDraw (int linenum, int x, int y, bool clean, int hu float trans = float(Alpha * Tics / FadeInTics); if (hudheight == 0) { - if (con_scaletext <= 1) + if (active_con_scaletext() <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, @@ -684,21 +656,11 @@ void DHUDMessageFadeInOut::DoDraw (int linenum, int x, int y, bool clean, int hu DTA_RenderStyle, Style, TAG_DONE); } - else if (con_scaletext == 3) - { - screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/4, - DTA_VirtualHeight, SCREENHEIGHT/4, - DTA_AlphaF, trans, - DTA_RenderStyle, Style, - DTA_KeepRatio, true, - TAG_DONE); - } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/2, - DTA_VirtualHeight, SCREENHEIGHT/2, + DTA_VirtualWidth, SCREENWIDTH / active_con_scaletext(), + DTA_VirtualHeight, SCREENHEIGHT / active_con_scaletext(), DTA_AlphaF, trans, DTA_RenderStyle, Style, DTA_KeepRatio, true, @@ -864,7 +826,7 @@ void DHUDMessageTypeOnFadeOut::DoDraw (int linenum, int x, int y, bool clean, in { if (hudheight == 0) { - if (con_scaletext <= 1) + if (active_con_scaletext() <= 1) { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, DTA_CleanNoMove, clean, @@ -873,22 +835,11 @@ void DHUDMessageTypeOnFadeOut::DoDraw (int linenum, int x, int y, bool clean, in DTA_RenderStyle, Style, TAG_DONE); } - else if (con_scaletext == 3) - { - screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/4, - DTA_VirtualHeight, SCREENHEIGHT/4, - DTA_KeepRatio, true, - DTA_TextLen, LineVisible, - DTA_AlphaF, Alpha, - DTA_RenderStyle, Style, - TAG_DONE); - } else { screen->DrawText (Font, TextColor, x, y, Lines[linenum].Text, - DTA_VirtualWidth, SCREENWIDTH/2, - DTA_VirtualHeight, SCREENHEIGHT/2, + DTA_VirtualWidth, SCREENWIDTH / active_con_scaletext(), + DTA_VirtualHeight, SCREENHEIGHT / active_con_scaletext(), DTA_KeepRatio, true, DTA_TextLen, LineVisible, DTA_AlphaF, Alpha, diff --git a/src/g_shared/sbarinfo.cpp b/src/g_shared/sbarinfo.cpp index 0136b4f25..42f28173c 100644 --- a/src/g_shared/sbarinfo.cpp +++ b/src/g_shared/sbarinfo.cpp @@ -1013,7 +1013,15 @@ public: void ScreenSizeChanged() { Super::ScreenSizeChanged(); - V_CalcCleanFacs(script->resW, script->resH, SCREENWIDTH, SCREENHEIGHT, &script->cleanX, &script->cleanY); + if (uiscale > 0) + { + script->cleanX = uiscale; + script->cleanY = uiscale; + } + else + { + V_CalcCleanFacs(script->resW, script->resH, SCREENWIDTH, SCREENHEIGHT, &script->cleanX, &script->cleanY); + } } void Draw (EHudState state) diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index b4a011510..a9fac2ee1 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -3464,6 +3464,78 @@ class CommandIfWaterLevel : public SBarInfoNegatableFlowControl //////////////////////////////////////////////////////////////////////////////// +class CommandIfCVarInt : public SBarInfoNegatableFlowControl +{ + public: + CommandIfCVarInt(SBarInfo *script) : SBarInfoNegatableFlowControl(script), + equalcomp(false) + { + } + + void ParseNegatable(FScanner &sc, bool fullScreenOffsets) + { + if(!sc.CheckToken(TK_StringConst)) + { + sc.MustGetToken(TK_Identifier); + } + + cvarname = sc.String; + cvar = FindCVar(cvarname, nullptr); + + if (cvar != nullptr) + { + ECVarType cvartype = cvar->GetRealType(); + + if (cvartype == CVAR_Bool || cvartype == CVAR_Int) + { + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + value = sc.Number; + + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_Identifier); + + if(sc.Compare("equal")) + { + equalcomp = true; + } + } + } + else + { + sc.ScriptError("Type mismatch: console variable '%s' is not of type 'bool' or 'int'.", cvarname.GetChars()); + } + } + else + { + sc.ScriptError("Unknown console variable '%s'.", cvarname.GetChars()); + } + } + void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) + { + SBarInfoNegatableFlowControl::Tick(block, statusBar, hudChanged); + + bool result = false; + cvar = GetCVar(statusBar->CPlayer->mo, cvarname); + + if (cvar != nullptr) + { + int cvarvalue = cvar->GetGenericRep(CVAR_Int).Int; + result = equalcomp ? cvarvalue == value : cvarvalue >= value; + } + + SetTruth(result, block, statusBar); + } + protected: + FString cvarname; + FBaseCVar *cvar; + int value; + bool equalcomp; +}; + +//////////////////////////////////////////////////////////////////////////////// + static const char *SBarInfoCommandNames[] = { "drawimage", "drawnumber", "drawswitchableimage", @@ -3474,7 +3546,7 @@ static const char *SBarInfoCommandNames[] = "isselected", "usesammo", "usessecondaryammo", "hasweaponpiece", "inventorybarnotvisible", "weaponammo", "ininventory", "alpha", "ifhealth", - "ifinvulnerable", "ifwaterlevel", + "ifinvulnerable", "ifwaterlevel", "ifcvarint", NULL }; @@ -3488,7 +3560,7 @@ enum SBarInfoCommands SBARINFO_ISSELECTED, SBARINFO_USESAMMO, SBARINFO_USESSECONDARYAMMO, SBARINFO_HASWEAPONPIECE, SBARINFO_INVENTORYBARNOTVISIBLE, SBARINFO_WEAPONAMMO, SBARINFO_ININVENTORY, SBARINFO_ALPHA, SBARINFO_IFHEALTH, - SBARINFO_IFINVULNERABLE, SBARINFO_IFWATERLEVEL, + SBARINFO_IFINVULNERABLE, SBARINFO_IFWATERLEVEL, SBARINFO_IFCVARINT, }; SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) @@ -3524,6 +3596,7 @@ SBarInfoCommand *SBarInfoCommandFlowControl::NextCommand(FScanner &sc) case SBARINFO_IFHEALTH: return new CommandIfHealth(script); case SBARINFO_IFINVULNERABLE: return new CommandIfInvulnerable(script); case SBARINFO_IFWATERLEVEL: return new CommandIfWaterLevel(script); + case SBARINFO_IFCVARINT: return new CommandIfCVarInt(script); } sc.ScriptError("Unknown command '%s'.\n", sc.String); diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index cddc86ee4..25a88cb35 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -66,7 +66,7 @@ EXTERN_CVAR (Int, screenblocks) EXTERN_CVAR (Bool, am_showtime) EXTERN_CVAR (Bool, am_showtotaltime) -CVAR(Int,hud_althudscale, 2, CVAR_ARCHIVE) // Scale the hud to 640x400? +CVAR(Int,hud_althudscale, 4, CVAR_ARCHIVE) // Scale the hud to 640x400? CVAR(Bool,hud_althud, false, CVAR_ARCHIVE) // Enable/Disable the alternate HUD // These are intentionally not the same as in the automap! @@ -118,7 +118,7 @@ static int hudwidth, hudheight; // current width/height for HUD display static int statspace; DVector2 AM_GetPosition(); - +int active_con_scaletext(); FTextureID GetHUDIcon(PClassInventory *cls) { @@ -886,22 +886,15 @@ static void DrawCoordinates(player_t * CPlayer) } int vwidth, vheight; - switch (con_scaletext) + if (active_con_scaletext() == 0) { - default: - case 0: - vwidth = SCREENWIDTH; - vheight = SCREENHEIGHT; - break; - case 1: - case 2: - vwidth = SCREENWIDTH/2; - vheight = SCREENHEIGHT/2; - break; - case 3: - vwidth = SCREENWIDTH/4; - vheight = SCREENHEIGHT/4; - break; + vwidth = SCREENWIDTH / 2; + vheight = SCREENHEIGHT / 2; + } + else + { + vwidth = SCREENWIDTH / active_con_scaletext(); + vheight = SCREENHEIGHT / active_con_scaletext(); } int xpos = vwidth - SmallFont->StringWidth("X: -00000")-6; @@ -1090,7 +1083,20 @@ void DrawHUD() if (hud_althudscale && SCREENWIDTH>640) { hudwidth=SCREENWIDTH/2; - if (hud_althudscale == 3) + if (hud_althudscale == 4) + { + if (uiscale == 0) + { + hudwidth = CleanWidth; + hudheight = CleanHeight; + } + else + { + hudwidth = SCREENWIDTH / uiscale; + hudheight = SCREENHEIGHT / uiscale; + } + } + else if (hud_althudscale == 3) { hudwidth = SCREENWIDTH / 4; hudheight = SCREENHEIGHT / 4; diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index c3b1fd262..4631a99aa 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -74,6 +74,8 @@ EXTERN_CVAR (Bool, am_showtotaltime) EXTERN_CVAR (Bool, noisedebug) EXTERN_CVAR (Int, con_scaletext) +int active_con_scaletext(); + DBaseStatusBar *StatusBar; extern int setblocks; @@ -1240,17 +1242,17 @@ void DBaseStatusBar::Draw (EHudState state) int xpos; int y; - if (con_scaletext == 0) + if (active_con_scaletext() == 1) { vwidth = SCREENWIDTH; vheight = SCREENHEIGHT; xpos = vwidth - 80; y = ::ST_Y - height; } - else if (con_scaletext == 3) + else if (active_con_scaletext() > 1) { - vwidth = SCREENWIDTH/4; - vheight = SCREENHEIGHT/4; + vwidth = SCREENWIDTH / active_con_scaletext(); + vheight = SCREENHEIGHT / active_con_scaletext(); xpos = vwidth - SmallFont->StringWidth("X: -00000")-6; y = ::ST_Y/4 - height; } @@ -1264,9 +1266,9 @@ void DBaseStatusBar::Draw (EHudState state) if (gameinfo.gametype == GAME_Strife) { - if (con_scaletext == 0) + if (active_con_scaletext() == 1) y -= height * 4; - else if (con_scaletext == 3) + else if (active_con_scaletext() > 3) y -= height; else y -= height * 2; @@ -1400,27 +1402,15 @@ void DBaseStatusBar::DrawLog () if (CPlayer->LogText.IsNotEmpty()) { // This uses the same scaling as regular HUD messages - switch (con_scaletext) + if (active_con_scaletext() == 0) { - default: - hudwidth = SCREENWIDTH; - hudheight = SCREENHEIGHT; - break; - - case 1: hudwidth = SCREENWIDTH / CleanXfac; hudheight = SCREENHEIGHT / CleanYfac; - break; - - case 2: - hudwidth = SCREENWIDTH / 2; - hudheight = SCREENHEIGHT / 2; - break; - - case 3: - hudwidth = SCREENWIDTH / 4; - hudheight = SCREENHEIGHT / 4; - break; + } + else + { + hudwidth = SCREENWIDTH / active_con_scaletext(); + hudheight = SCREENHEIGHT / active_con_scaletext(); } int linelen = hudwidth<640? Scale(hudwidth,9,10)-40 : 560; diff --git a/src/g_strife/a_strifeweapons.cpp b/src/g_strife/a_strifeweapons.cpp index f81ca79f7..24e7fb5c7 100644 --- a/src/g_strife/a_strifeweapons.cpp +++ b/src/g_strife/a_strifeweapons.cpp @@ -552,7 +552,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_MaulerTorpedoWave) // If the torpedo hit the ceiling, it should still spawn the wave savedz = self->Z(); - if (wavedef && self->ceilingz < wavedef->Top()) + if (wavedef && self->ceilingz < self->Z() + wavedef->Height) { self->SetZ(self->ceilingz - wavedef->Height); } diff --git a/src/gl/compatibility/gl_20.cpp b/src/gl/compatibility/gl_20.cpp index fdda99130..e192b213e 100644 --- a/src/gl/compatibility/gl_20.cpp +++ b/src/gl/compatibility/gl_20.cpp @@ -57,6 +57,8 @@ #include "gl/data/gl_vertexbuffer.h" +CVAR(Bool, gl_lights_additive, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + //========================================================================== // // Do some tinkering with the menus so that certain options only appear @@ -384,7 +386,7 @@ bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, DVector3 lpos = light->PosRelative(group); float dist = fabsf(p.DistToPoint(lpos.X, lpos.Z, lpos.Y)); - float radius = (light->GetRadius() * gl_lights_size); + float radius = light->GetRadius(); if (radius <= 0.f) return false; if (dist > radius) return false; @@ -415,9 +417,9 @@ bool gl_SetupLight(int group, Plane & p, ADynamicLight * light, Vector & nearPt, float cs = 1.0f - (dist / radius); if (additive) cs *= 0.2f; // otherwise the light gets too strong. - float r = light->GetRed() / 255.0f * cs * gl_lights_intensity; - float g = light->GetGreen() / 255.0f * cs * gl_lights_intensity; - float b = light->GetBlue() / 255.0f * cs * gl_lights_intensity; + float r = light->GetRed() / 255.0f * cs; + float g = light->GetGreen() / 255.0f * cs; + float b = light->GetBlue() / 255.0f * cs; if (light->IsSubtractive()) { @@ -478,7 +480,7 @@ bool GLWall::PutWallCompat(int passflag) if (sub->lighthead == nullptr) return false; } - bool foggy = !gl_isBlack(Colormap.FadeColor) || (level.flags&LEVEL_HASFADETABLE) || gl_lights_additive; + bool foggy = gl_CheckFog(&Colormap, lightlevel) || (level.flags&LEVEL_HASFADETABLE) || gl_lights_additive; bool masked = passflag == 2 && gltexture->isMasked(); int list = list_indices[masked][foggy]; diff --git a/src/gl/data/gl_vertexbuffer.h b/src/gl/data/gl_vertexbuffer.h index 88a1de2b8..0efff5508 100644 --- a/src/gl/data/gl_vertexbuffer.h +++ b/src/gl/data/gl_vertexbuffer.h @@ -10,6 +10,15 @@ struct secplane_t; struct subsector_t; struct sector_t; +enum +{ + VATTR_VERTEX_BIT, + VATTR_TEXCOORD_BIT, + VATTR_COLOR_BIT, + VATTR_VERTEX2_BIT, + VATTR_NORMAL_BIT +}; + class FVertexBuffer { @@ -20,6 +29,7 @@ public: FVertexBuffer(bool wantbuffer = true); virtual ~FVertexBuffer(); virtual void BindVBO() = 0; + void EnableBufferArrays(int enable, int disable); }; struct FFlatVertex diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp index ef96fdd3e..ac929e7c0 100644 --- a/src/gl/dynlights/a_dynlight.cpp +++ b/src/gl/dynlights/a_dynlight.cpp @@ -57,8 +57,6 @@ #include "gl/utility/gl_convert.h" #include "gl/utility/gl_templates.h" -EXTERN_CVAR (Float, gl_lights_size); -EXTERN_CVAR (Bool, gl_lights_additive); EXTERN_CVAR(Int, vid_renderer) @@ -379,7 +377,7 @@ void ADynamicLight::UpdateLocation() { intensity = m_currentRadius; } - radius = intensity * 2.0f * gl_lights_size; + radius = intensity * 2.0f; if (X() != oldx || Y() != oldy || radius != oldradius) { diff --git a/src/gl/dynlights/gl_dynlight.cpp b/src/gl/dynlights/gl_dynlight.cpp index e7d876b2a..b12290ab6 100644 --- a/src/gl/dynlights/gl_dynlight.cpp +++ b/src/gl/dynlights/gl_dynlight.cpp @@ -61,8 +61,6 @@ #include "gl/utility/gl_clock.h" #include "gl/utility/gl_convert.h" -EXTERN_CVAR (Float, gl_lights_intensity); -EXTERN_CVAR (Float, gl_lights_size); int ScriptDepth; void gl_InitGlow(FScanner &sc); void gl_ParseBrightmap(FScanner &sc, int); @@ -175,7 +173,7 @@ void FLightDefaults::ApplyProperties(ADynamicLight * light) const light->Angles.Yaw.Degrees = m_Param; light->SetOffset(m_Pos); light->halo = m_halo; - for (int a = 0; a < 3; a++) light->args[a] = clamp((int)(m_Args[a] * gl_lights_intensity), 0, 255); + for (int a = 0; a < 3; a++) light->args[a] = clamp((int)(m_Args[a]), 0, 255); light->m_Radius[0] = int(m_Args[LIGHT_INTENSITY]); light->m_Radius[1] = int(m_Args[LIGHT_SECONDARY_INTENSITY]); light->flags4 &= ~(MF4_ADDITIVE | MF4_SUBTRACTIVE | MF4_DONTLIGHTSELF); diff --git a/src/gl/dynlights/gl_dynlight.h b/src/gl/dynlights/gl_dynlight.h index 513245be5..6b6e40c2d 100644 --- a/src/gl/dynlights/gl_dynlight.h +++ b/src/gl/dynlights/gl_dynlight.h @@ -184,7 +184,7 @@ struct FDynLightData -bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, bool forceadditive, FDynLightData &data); +bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FDynLightData &data); void gl_UploadLights(FDynLightData &data); diff --git a/src/gl/dynlights/gl_dynlight1.cpp b/src/gl/dynlights/gl_dynlight1.cpp index 858bcfd09..55dfd306c 100644 --- a/src/gl/dynlights/gl_dynlight1.cpp +++ b/src/gl/dynlights/gl_dynlight1.cpp @@ -70,15 +70,8 @@ CUSTOM_CVAR (Bool, gl_lights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOIN CVAR (Bool, gl_attachedlights, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_lights_checkside, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -CVAR (Float, gl_lights_intensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -CVAR (Float, gl_lights_size, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_light_sprites, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); CVAR (Bool, gl_light_particles, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -CUSTOM_CVAR (Bool, gl_lights_additive, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) -{ - gl_DeleteAllAttachedLights(); - gl_RecreateAllAttachedLights(); -} CUSTOM_CVAR(Int, gl_light_math, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { @@ -90,14 +83,14 @@ CUSTOM_CVAR(Int, gl_light_math, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) // Sets up the parameters to render one dynamic light onto one plane // //========================================================================== -bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, bool forceadditive, FDynLightData &ldata) +bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, FDynLightData &ldata) { int i = 0; DVector3 pos = light->PosRelative(group); float dist = fabsf(p.DistToPoint(pos.X, pos.Z, pos.Y)); - float radius = (light->GetRadius() * gl_lights_size); + float radius = (light->GetRadius()); if (radius <= 0.f) return false; if (dist > radius) return false; @@ -108,7 +101,7 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, bo float cs; - if (gl_lights_additive || light->flags4&MF4_ADDITIVE || forceadditive) + if (light->IsAdditive()) { cs = 0.2f; i = 2; @@ -118,9 +111,9 @@ bool gl_GetLight(int group, Plane & p, ADynamicLight * light, bool checkside, bo cs = 1.0f; } - float r = light->GetRed() / 255.0f * cs * gl_lights_intensity; - float g = light->GetGreen() / 255.0f * cs * gl_lights_intensity; - float b = light->GetBlue() / 255.0f * cs * gl_lights_intensity; + float r = light->GetRed() / 255.0f * cs; + float g = light->GetGreen() / 255.0f * cs; + float b = light->GetBlue() / 255.0f * cs; if (light->IsSubtractive()) { diff --git a/src/gl/renderer/gl_2ddrawer.cpp b/src/gl/renderer/gl_2ddrawer.cpp index ac7d35397..e0f67c713 100644 --- a/src/gl/renderer/gl_2ddrawer.cpp +++ b/src/gl/renderer/gl_2ddrawer.cpp @@ -378,7 +378,7 @@ void F2DDrawer::AddPixel(int x1, int y1, int palcolor, uint32 color) // //========================================================================== -void F2DDrawer::Flush() +void F2DDrawer::Draw() { F2DDrawer::EDrawType lasttype = DrawTypeTexture; @@ -490,10 +490,12 @@ void F2DDrawer::Flush() } i += dg->mLen; } - mVertices.Clear(); - mData.Clear(); gl_RenderState.SetVertexBuffer(GLRenderer->mVBO); glset.lightmode = savedlightmode; } - +void F2DDrawer::Clear() +{ + mVertices.Clear(); + mData.Clear(); +} diff --git a/src/gl/renderer/gl_2ddrawer.h b/src/gl/renderer/gl_2ddrawer.h index 32d2ad1f5..8c27f13d8 100644 --- a/src/gl/renderer/gl_2ddrawer.h +++ b/src/gl/renderer/gl_2ddrawer.h @@ -66,7 +66,8 @@ public: void AddLine(int x1, int y1, int x2, int y2, int palcolor, uint32 color); void AddPixel(int x1, int y1, int palcolor, uint32 color); - void Flush(); + void Draw(); + void Clear(); }; diff --git a/src/gl/renderer/gl_postprocess.cpp b/src/gl/renderer/gl_postprocess.cpp index 512209454..b4fd4d05f 100644 --- a/src/gl/renderer/gl_postprocess.cpp +++ b/src/gl/renderer/gl_postprocess.cpp @@ -76,6 +76,7 @@ #include "gl/shaders/gl_lensshader.h" #include "gl/shaders/gl_presentshader.h" #include "gl/renderer/gl_2ddrawer.h" +#include "gl/stereo3d/gl_stereo3d.h" //========================================================================== // @@ -395,7 +396,7 @@ void FGLRenderer::BindTonemapPalette(int texunit) { for (int b = 0; b < 64; b++) { - PalEntry color = GPalette.BaseColors[ColorMatcher.Pick((r << 2) | (r >> 1), (g << 2) | (g >> 1), (b << 2) | (b >> 1))]; + PalEntry color = GPalette.BaseColors[(BYTE)PTM_BestColor((uint32 *)GPalette.BaseColors, (r << 2) | (r >> 4), (g << 2) | (g >> 4), (b << 2) | (b >> 4), 0, 256)]; int index = ((r * 64 + g) * 64 + b) * 4; lut[index] = color.r; lut[index + 1] = color.g; @@ -513,6 +514,41 @@ void FGLRenderer::LensDistortScene() FGLDebug::PopGroup(); } +//----------------------------------------------------------------------------- +// +// Copies the rendered screen to its final destination +// +//----------------------------------------------------------------------------- + +void FGLRenderer::Flush() +{ + const s3d::Stereo3DMode& stereo3dMode = s3d::Stereo3DMode::getCurrentMode(); + + if (stereo3dMode.IsMono() || !FGLRenderBuffers::IsEnabled()) + { + CopyToBackbuffer(nullptr, true); + } + else + { + // Render 2D to eye textures + for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix) + { + FGLDebug::PushGroup("Eye2D"); + mBuffers->BindEyeFB(eye_ix); + glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + glScissor(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + m2DDrawer->Draw(); + FGLDebug::PopGroup(); + } + m2DDrawer->Clear(); + + FGLPostProcessState savedState; + FGLDebug::PushGroup("PresentEyes"); + stereo3dMode.Present(); + FGLDebug::PopGroup(); + } +} + //----------------------------------------------------------------------------- // // Gamma correct while copying to frame buffer @@ -521,7 +557,9 @@ void FGLRenderer::LensDistortScene() void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma) { - m2DDrawer->Flush(); // draw all pending 2D stuff before copying the buffer + m2DDrawer->Draw(); // draw all pending 2D stuff before copying the buffer + m2DDrawer->Clear(); + FGLDebug::PushGroup("CopyToBackbuffer"); if (FGLRenderBuffers::IsEnabled()) { @@ -539,28 +577,8 @@ void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma) box = mOutputLetterbox; } - // Present what was rendered: - glViewport(box.left, box.top, box.width, box.height); - - mPresentShader->Bind(); - mPresentShader->InputTexture.Set(0); - if (!applyGamma || framebuffer->IsHWGammaActive()) - { - mPresentShader->InvGamma.Set(1.0f); - mPresentShader->Contrast.Set(1.0f); - mPresentShader->Brightness.Set(0.0f); - } - else - { - mPresentShader->InvGamma.Set(1.0f / clamp(Gamma, 0.1f, 4.f)); - mPresentShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); - mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); - } - mPresentShader->Scale.Set(mScreenViewport.width / (float)mBuffers->GetWidth(), mScreenViewport.height / (float)mBuffers->GetHeight()); mBuffers->BindCurrentTexture(0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - RenderScreenQuad(); + DrawPresentTexture(box, applyGamma); } else if (!bounds) { @@ -570,6 +588,32 @@ void FGLRenderer::CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma) FGLDebug::PopGroup(); } +void FGLRenderer::DrawPresentTexture(const GL_IRECT &box, bool applyGamma) +{ + glViewport(box.left, box.top, box.width, box.height); + + glActiveTexture(GL_TEXTURE0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + mPresentShader->Bind(); + mPresentShader->InputTexture.Set(0); + if (!applyGamma || framebuffer->IsHWGammaActive()) + { + mPresentShader->InvGamma.Set(1.0f); + mPresentShader->Contrast.Set(1.0f); + mPresentShader->Brightness.Set(0.0f); + } + else + { + mPresentShader->InvGamma.Set(1.0f / clamp(Gamma, 0.1f, 4.f)); + mPresentShader->Contrast.Set(clamp(vid_contrast, 0.1f, 3.f)); + mPresentShader->Brightness.Set(clamp(vid_brightness, -0.8f, 0.8f)); + } + mPresentShader->Scale.Set(mScreenViewport.width / (float)mBuffers->GetWidth(), mScreenViewport.height / (float)mBuffers->GetHeight()); + RenderScreenQuad(); +} + //----------------------------------------------------------------------------- // // Fills the black bars around the screen letterbox @@ -582,6 +626,8 @@ void FGLRenderer::ClearBorders() int clientWidth = framebuffer->GetClientWidth(); int clientHeight = framebuffer->GetClientHeight(); + if (clientWidth == 0 || clientHeight == 0) + return; glViewport(0, 0, clientWidth, clientHeight); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); @@ -608,3 +654,40 @@ void FGLRenderer::ClearBorders() } glDisable(GL_SCISSOR_TEST); } + + +// [SP] Re-implemented BestColor for more precision rather than speed. This function is only ever called once until the game palette is changed. + +int FGLRenderer::PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num) +{ + const PalEntry *pal = (const PalEntry *)pal_in; + static double powtable[256]; + static bool firstTime = true; + + double fbestdist, fdist; + int bestcolor; + + if (firstTime) + { + firstTime = false; + for (int x = 0; x < 256; x++) powtable[x] = pow((double)x/255,1.2); + } + + for (int color = first; color < num; color++) + { + double x = powtable[abs(r-pal[color].r)]; + double y = powtable[abs(g-pal[color].g)]; + double z = powtable[abs(b-pal[color].b)]; + fdist = x + y + z; + if (color == first || fdist < fbestdist) + { + if (fdist == 0) + return color; + + fbestdist = fdist; + bestcolor = color; + } + } + return bestcolor; +} + diff --git a/src/gl/renderer/gl_renderbuffers.cpp b/src/gl/renderer/gl_renderbuffers.cpp index b0ea123a0..9c442ce4e 100644 --- a/src/gl/renderer/gl_renderbuffers.cpp +++ b/src/gl/renderer/gl_renderbuffers.cpp @@ -86,6 +86,7 @@ FGLRenderBuffers::~FGLRenderBuffers() { ClearScene(); ClearPipeline(); + ClearEyeBuffers(); ClearBloom(); ClearAmbientOcclusion(); } @@ -128,6 +129,18 @@ void FGLRenderBuffers::ClearAmbientOcclusion() DeleteTexture(AmbientRandomTexture); } +void FGLRenderBuffers::ClearEyeBuffers() +{ + for (auto handle : mEyeFBs) + DeleteFrameBuffer(handle); + + for (auto handle : mEyeTextures) + DeleteTexture(handle); + + mEyeTextures.Clear(); + mEyeFBs.Clear(); +} + void FGLRenderBuffers::DeleteTexture(GLuint &handle) { if (handle != 0) @@ -212,6 +225,7 @@ bool FGLRenderBuffers::Setup(int width, int height, int sceneWidth, int sceneHei { ClearScene(); ClearPipeline(); + ClearEyeBuffers(); ClearBloom(); mWidth = 0; mHeight = 0; @@ -255,6 +269,7 @@ void FGLRenderBuffers::CreateScene(int width, int height, int samples) void FGLRenderBuffers::CreatePipeline(int width, int height) { ClearPipeline(); + ClearEyeBuffers(); for (int i = 0; i < NumPipelineTextures; i++) { @@ -336,6 +351,35 @@ void FGLRenderBuffers::CreateAmbientOcclusion(int width, int height) AmbientRandomTexture = Create2DTexture("AmbientRandomTexture", GL_RGBA16_SNORM, 4, 4, randomValues); } +//========================================================================== +// +// Creates eye buffers if needed +// +//========================================================================== + +void FGLRenderBuffers::CreateEyeBuffers(int eye) +{ + if (mEyeFBs.Size() > eye) + return; + + GLint activeTex, textureBinding, frameBufferBinding; + glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTex); + glActiveTexture(GL_TEXTURE0); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &frameBufferBinding); + + while (mEyeFBs.Size() <= eye) + { + GLuint texture = Create2DTexture("EyeTexture", GL_RGBA16F, mWidth, mHeight); + mEyeTextures.Push(texture); + mEyeFBs.Push(CreateFrameBuffer("EyeFB", texture)); + } + + glBindTexture(GL_TEXTURE_2D, textureBinding); + glActiveTexture(activeTex); + glBindFramebuffer(GL_FRAMEBUFFER, frameBufferBinding); +} + //========================================================================== // // Creates a 2D texture defaulting to linear filtering and clamp to edge @@ -544,6 +588,43 @@ void FGLRenderBuffers::BlitSceneToTexture() glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } +//========================================================================== +// +// Eye textures and their frame buffers +// +//========================================================================== + +void FGLRenderBuffers::BlitToEyeTexture(int eye) +{ + CreateEyeBuffers(eye); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, mPipelineFB[mCurrentPipelineTexture]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mEyeFBs[eye]); + glBlitFramebuffer(0, 0, mWidth, mHeight, 0, 0, mWidth, mHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + if ((gl.flags & RFL_INVALIDATE_BUFFER) != 0) + { + GLenum attachments[2] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT }; + glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 2, attachments); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +} + +void FGLRenderBuffers::BindEyeTexture(int eye, int texunit) +{ + CreateEyeBuffers(eye); + glActiveTexture(GL_TEXTURE0 + texunit); + glBindTexture(GL_TEXTURE_2D, mEyeTextures[eye]); +} + +void FGLRenderBuffers::BindEyeFB(int eye, bool readBuffer) +{ + CreateEyeBuffers(eye); + glBindFramebuffer(readBuffer ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER, mEyeFBs[eye]); +} + //========================================================================== // // Makes the scene frame buffer active (multisample, depth, stecil, etc.) diff --git a/src/gl/renderer/gl_renderbuffers.h b/src/gl/renderer/gl_renderbuffers.h index 2661908e3..ecfbbeb74 100644 --- a/src/gl/renderer/gl_renderbuffers.h +++ b/src/gl/renderer/gl_renderbuffers.h @@ -34,6 +34,10 @@ public: void BindOutputFB(); + void BlitToEyeTexture(int eye); + void BindEyeTexture(int eye, int texunit); + void BindEyeFB(int eye, bool readBuffer = false); + enum { NumBloomLevels = 4 }; FGLBloomTextureLevel BloomLevels[NumBloomLevels]; @@ -57,12 +61,14 @@ public: private: void ClearScene(); void ClearPipeline(); + void ClearEyeBuffers(); void ClearBloom(); void ClearAmbientOcclusion(); void CreateScene(int width, int height, int samples); void CreatePipeline(int width, int height); void CreateBloom(int width, int height); void CreateAmbientOcclusion(int width, int height); + void CreateEyeBuffers(int eye); GLuint Create2DTexture(const FString &name, GLuint format, int width, int height, const void *data = nullptr); GLuint Create2DMultisampleTexture(const FString &name, GLuint format, int width, int height, int samples, bool fixedSampleLocations); GLuint CreateRenderBuffer(const FString &name, GLuint format, int width, int height); @@ -97,6 +103,10 @@ private: // Back buffer frame buffer GLuint mOutputFB = 0; + // Eye buffers + TArray mEyeTextures; + TArray mEyeFBs; + static bool FailedCreate; static bool BuffersActive; }; diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index ed45ec0f3..e825baf48 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -244,6 +244,13 @@ void FGLRenderer::SetOutputViewport(GL_IRECT *bounds) // Back buffer letterbox for the final output int clientWidth = framebuffer->GetClientWidth(); int clientHeight = framebuffer->GetClientHeight(); + if (clientWidth == 0 || clientHeight == 0) + { + // When window is minimized there may not be any client area. + // Pretend to the rest of the render code that we just have a very small window. + clientWidth = 160; + clientHeight = 120; + } int screenWidth = framebuffer->GetWidth(); int screenHeight = framebuffer->GetHeight(); float scale = MIN(clientWidth / (float)screenWidth, clientHeight / (float)screenHeight); diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 8955acfd6..deb76485e 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -183,7 +183,8 @@ public: void ClearTonemapPalette(); void LensDistortScene(); void CopyToBackbuffer(const GL_IRECT *bounds, bool applyGamma); - void Flush() { CopyToBackbuffer(nullptr, true); } + void DrawPresentTexture(const GL_IRECT &box, bool applyGamma); + void Flush(); void SetProjection(float fov, float ratio, float fovratio); void SetProjection(VSMatrix matrix); // raw matrix input from stereo 3d modes @@ -200,6 +201,8 @@ public: double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, int lightlevel); + int PTM_BestColor (const uint32 *pal_in, int r, int g, int b, int first, int num); + static float GetZNear() { return 5.f; } static float GetZFar() { return 65536.f; } }; diff --git a/src/gl/scene/gl_flats.cpp b/src/gl/scene/gl_flats.cpp index 1a3fe994b..48ad30786 100644 --- a/src/gl/scene/gl_flats.cpp +++ b/src/gl/scene/gl_flats.cpp @@ -154,7 +154,7 @@ void GLFlat::SetupSubsectorLights(int pass, subsector_t * sub, int *dli) } p.Set(plane.plane); - gl_GetLight(sub->sector->PortalGroup, p, light, false, false, lightdata); + gl_GetLight(sub->sector->PortalGroup, p, light, false, lightdata); node = node->nextLight; } @@ -419,15 +419,17 @@ void GLFlat::Draw(int pass, bool trans) // trans only has meaning for GLPASS_LIG if (renderstyle==STYLE_Add) gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE); gl_SetColor(lightlevel, rel, Colormap, alpha); gl_SetFog(lightlevel, rel, &Colormap, false); - gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold); if (!gltexture) { + gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f); gl_RenderState.EnableTexture(false); DrawSubsectors(pass, false, true); gl_RenderState.EnableTexture(true); } else { + if (!gltexture->GetTransparent()) gl_RenderState.AlphaFunc(GL_GEQUAL, gl_mask_threshold); + else gl_RenderState.AlphaFunc(GL_GEQUAL, 0.f); gl_RenderState.SetMaterial(gltexture, CLAMP_NONE, 0, -1, false); gl_SetPlaneTextureRotation(&plane, gltexture); DrawSubsectors(pass, !gl.legacyMode, true); @@ -477,6 +479,21 @@ inline void GLFlat::PutFlat(bool fog) // translucent 3D floors go into the regular translucent list, translucent portals go into the translucent border list. list = (renderflags&SSRF_RENDER3DPLANES) ? GLDL_TRANSLUCENT : GLDL_TRANSLUCENTBORDER; } + else if (gltexture->GetTransparent()) + { + if (stack) + { + list = GLDL_TRANSLUCENTBORDER; + } + else if ((renderflags&SSRF_RENDER3DPLANES) && !plane.plane.isSlope()) + { + list = GLDL_TRANSLUCENT; + } + else + { + list = GLDL_PLAINFLATS; + } + } else { bool masked = gltexture->isMasked() && ((renderflags&SSRF_RENDER3DPLANES) || stack); diff --git a/src/gl/scene/gl_portal.cpp b/src/gl/scene/gl_portal.cpp index 20ded84aa..c7c1db48f 100644 --- a/src/gl/scene/gl_portal.cpp +++ b/src/gl/scene/gl_portal.cpp @@ -56,6 +56,7 @@ #include "gl/renderer/gl_lightdata.h" #include "gl/renderer/gl_renderer.h" #include "gl/renderer/gl_renderstate.h" +#include "gl/renderer/gl_quaddrawer.h" #include "gl/dynlights/gl_glow.h" #include "gl/data/gl_data.h" #include "gl/data/gl_vertexbuffer.h" @@ -128,8 +129,16 @@ void GLPortal::ClearScreen() bool multi = !!glIsEnabled(GL_MULTISAMPLE); gl_MatrixStack.Push(gl_RenderState.mViewMatrix); gl_MatrixStack.Push(gl_RenderState.mProjectionMatrix); - screen->Begin2D(false); - screen->Dim(0, 1.f, 0, 0, SCREENWIDTH, SCREENHEIGHT); + + gl_RenderState.mViewMatrix.loadIdentity(); + gl_RenderState.mProjectionMatrix.ortho(0, SCREENWIDTH, SCREENHEIGHT, 0, -1.0f, 1.0f); + gl_RenderState.ApplyMatrices(); + + glDisable(GL_MULTISAMPLE); + glDisable(GL_DEPTH_TEST); + + glDrawArrays(GL_TRIANGLE_STRIP, FFlatVertexBuffer::FULLSCREEN_INDEX, 4); + glEnable(GL_DEPTH_TEST); gl_MatrixStack.Pop(gl_RenderState.mProjectionMatrix); gl_MatrixStack.Pop(gl_RenderState.mViewMatrix); @@ -137,7 +146,6 @@ void GLPortal::ClearScreen() if (multi) glEnable(GL_MULTISAMPLE); } - //----------------------------------------------------------------------------- // // DrawPortalStencil diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 087bd9f62..0564addc8 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -682,8 +682,6 @@ void FGLRenderer::EndDrawScene(sector_t * viewsector) } if (gl.legacyMode) { - int cm = gl_RenderState.GetFixedColormap(); - gl_RenderState.SetFixedColormap(cm); gl_RenderState.DrawColormapOverlay(); } @@ -775,7 +773,7 @@ void FGLRenderer::SetFixedColormap (player_t *player) sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) { - sector_t * retval; + sector_t * lviewsector; mSceneClearColor[0] = 0.0f; mSceneClearColor[1] = 0.0f; mSceneClearColor[2] = 0.0f; @@ -822,11 +820,11 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo } // 'viewsector' will not survive the rendering so it cannot be used anymore below. - retval = viewsector; + lviewsector = viewsector; // Render (potentially) multiple views for stereo 3d float viewShift[3]; - const s3d::Stereo3DMode& stereo3dMode = s3d::Stereo3DMode::getCurrentMode(); + const s3d::Stereo3DMode& stereo3dMode = mainview && toscreen? s3d::Stereo3DMode::getCurrentMode() : s3d::Stereo3DMode::getMonoMode(); stereo3dMode.SetUp(); for (int eye_ix = 0; eye_ix < stereo3dMode.eye_count(); ++eye_ix) { @@ -852,20 +850,26 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo clipper.SafeAddClipRangeRealAngles(ViewAngle.BAMs() + a1, ViewAngle.BAMs() - a1); ProcessScene(toscreen); - if (mainview && toscreen) EndDrawScene(retval); // do not call this for camera textures. + if (mainview && toscreen) EndDrawScene(lviewsector); // do not call this for camera textures. if (mainview && FGLRenderBuffers::IsEnabled()) { PostProcessScene(); - DrawBlend(viewsector); // This should be done after postprocessing, not before. + + // This should be done after postprocessing, not before. + mBuffers->BindCurrentFB(); + glViewport(mScreenViewport.left, mScreenViewport.top, mScreenViewport.width, mScreenViewport.height); + DrawBlend(lviewsector); } mDrawingScene2D = false; + if (!stereo3dMode.IsMono() && FGLRenderBuffers::IsEnabled()) + mBuffers->BlitToEyeTexture(eye_ix); eye->TearDown(); } stereo3dMode.TearDown(); gl_frameCount++; // This counter must be increased right before the interpolations are restored. interpolator.RestoreInterpolations (); - return retval; + return lviewsector; } //----------------------------------------------------------------------------- @@ -1313,29 +1317,16 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in gl_fixedcolormap=CM_DEFAULT; gl_RenderState.SetFixedColormap(CM_DEFAULT); - bool usefb = gl_usefb || gltex->GetWidth() > screen->GetWidth() || gltex->GetHeight() > screen->GetHeight(); - if (!usefb) + if (gl.legacyMode) { + // In legacy mode, fail if the requested texture is too large. + if (gltex->GetWidth() > screen->GetWidth() || gltex->GetHeight() > screen->GetHeight()) return; glFlush(); } else { -#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) - __try -#endif - { - GLRenderer->StartOffscreen(); - gltex->BindToFrameBuffer(); - } -#if defined(_WIN32) && (defined(_MSC_VER) || defined(__INTEL_COMPILER)) - __except(1) - { - usefb = false; - gl_usefb = false; - GLRenderer->EndOffscreen(); - glFlush(); - } -#endif + GLRenderer->StartOffscreen(); + gltex->BindToFrameBuffer(); } GL_IRECT bounds; @@ -1345,7 +1336,7 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in GLRenderer->RenderViewpoint(Viewpoint, &bounds, FOV, (float)width/height, (float)width/height, false, false); - if (!usefb) + if (gl.legacyMode) { glFlush(); gl_RenderState.SetMaterial(gltex, 0, 0, -1, false); diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 8a8c89c80..07f397d6d 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -110,17 +110,155 @@ CVAR(Bool, gl_nolayer, false, 0) static const float LARGE_VALUE = 1e19f; + //========================================================================== // // // //========================================================================== + +void GLSprite::CalculateVertices(FVector3 *v) +{ + // [BB] Billboard stuff + const bool drawWithXYBillboard = ((particle && gl_billboard_particles) || (!(actor && actor->renderflags & RF_FORCEYBILLBOARD) + //&& GLRenderer->mViewActor != NULL + && (gl_billboard_mode == 1 || (actor && actor->renderflags & RF_FORCEXYBILLBOARD)))); + + const bool drawBillboardFacingCamera = gl_billboard_faces_camera; + // [Nash] has +ROLLSPRITE + const bool drawRollSpriteActor = (actor != nullptr && actor->renderflags & RF_ROLLSPRITE); + + + // [fgsfds] check sprite type mask + DWORD spritetype = (DWORD)-1; + if (actor != nullptr) spritetype = actor->renderflags & RF_SPRITETYPEMASK; + + // [Nash] is a flat sprite + const bool isFlatSprite = (actor != nullptr) && (spritetype == RF_WALLSPRITE || spritetype == RF_FLATSPRITE); + const bool dontFlip = (actor != nullptr) && (actor->renderflags & RF_DONTFLIP); + const bool useOffsets = (actor != nullptr) && !(actor->renderflags & RF_ROLLCENTER); + + // [Nash] check for special sprite drawing modes + if (drawWithXYBillboard || drawBillboardFacingCamera || drawRollSpriteActor || isFlatSprite) + { + // Compute center of sprite + float xcenter = (x1 + x2)*0.5; + float ycenter = (y1 + y2)*0.5; + float zcenter = (z1 + z2)*0.5; + float xx = -xcenter + x; + float zz = -zcenter + z; + float yy = -ycenter + y; + Matrix3x4 mat; + mat.MakeIdentity(); + mat.Translate(xcenter, zcenter, ycenter); // move to sprite center + + // Order of rotations matters. Perform yaw rotation (Y, face camera) before pitch (X, tilt up/down). + if (drawBillboardFacingCamera && !isFlatSprite) + { + // [CMB] Rotate relative to camera XY position, not just camera direction, + // which is nicer in VR + float xrel = xcenter - ViewPos.X; + float yrel = ycenter - ViewPos.Y; + float absAngleDeg = RAD2DEG(atan2(-yrel, xrel)); + float counterRotationDeg = 270. - GLRenderer->mAngles.Yaw.Degrees; // counteracts existing sprite rotation + float relAngleDeg = counterRotationDeg + absAngleDeg; + + mat.Rotate(0, 1, 0, relAngleDeg); + } + + // [fgsfds] calculate yaw vectors + float yawvecX = 0, yawvecY = 0, rollDegrees = 0; + float angleRad = (270. - GLRenderer->mAngles.Yaw).Radians(); + if (actor) rollDegrees = actor->Angles.Roll.Degrees; + if (isFlatSprite) + { + yawvecX = actor->Angles.Yaw.Cos(); + yawvecY = actor->Angles.Yaw.Sin(); + } + + // [MC] This is the only thing that I changed in Nash's submission which + // was constantly applying roll to everything. That was wrong. Flat sprites + // with roll literally look like paper thing space ships trying to swerve. + // However, it does well with wall sprites. + // Also, renamed FLOORSPRITE to FLATSPRITE because that's technically incorrect. + // I plan on adding proper FLOORSPRITEs which can actually curve along sloped + // 3D floors later... if possible. + + // Here we need some form of priority in order to work. + if (spritetype == RF_FLATSPRITE) + { + float pitchDegrees = -actor->Angles.Pitch.Degrees; + DVector3 apos = { x, y, z }; + DVector3 diff = ViewPos - apos; + DAngle angto = diff.Angle(); + + angto = deltaangle(actor->Angles.Yaw, angto); + + bool noFlipSprite = (!dontFlip || (fabs(angto) < 90.)); + mat.Rotate(0, 1, 0, (noFlipSprite) ? 0 : 180); + + mat.Rotate(-yawvecY, 0, yawvecX, (noFlipSprite) ? -pitchDegrees : pitchDegrees); + if (drawRollSpriteActor) + { + if (useOffsets) mat.Translate(xx, zz, yy); + mat.Rotate(yawvecX, 0, yawvecY, (noFlipSprite) ? -rollDegrees : rollDegrees); + if (useOffsets) mat.Translate(-xx, -zz, -yy); + } + } + // [fgsfds] Rotate the sprite about the sight vector (roll) + else if (spritetype == RF_WALLSPRITE) + { + mat.Rotate(0, 1, 0, 0); + if (drawRollSpriteActor) + { + if (useOffsets) mat.Translate(xx, zz, yy); + mat.Rotate(yawvecX, 0, yawvecY, rollDegrees); + if (useOffsets) mat.Translate(-xx, -zz, -yy); + } + } + else if (drawRollSpriteActor) + { + if (useOffsets) mat.Translate(xx, zz, yy); + if (drawWithXYBillboard) + { + mat.Rotate(-sin(angleRad), 0, cos(angleRad), -GLRenderer->mAngles.Pitch.Degrees); + } + mat.Rotate(cos(angleRad), 0, sin(angleRad), rollDegrees); + if (useOffsets) mat.Translate(-xx, -zz, -yy); + } + else if (drawWithXYBillboard) + { + // Rotate the sprite about the vector starting at the center of the sprite + // triangle strip and with direction orthogonal to where the player is looking + // in the x/y plane. + mat.Rotate(-sin(angleRad), 0, cos(angleRad), -GLRenderer->mAngles.Pitch.Degrees); + } + + mat.Translate(-xcenter, -zcenter, -ycenter); // retreat from sprite center + v[0] = mat * FVector3(x1, z1, y1); + v[1] = mat * FVector3(x2, z1, y2); + v[2] = mat * FVector3(x1, z2, y1); + v[3] = mat * FVector3(x2, z2, y2); + } + else // traditional "Y" billboard mode + { + v[0] = FVector3(x1, z1, y1); + v[1] = FVector3(x2, z1, y2); + v[2] = FVector3(x1, z2, y1); + v[3] = FVector3(x2, z2, y2); + } +} + +//========================================================================== +// +// +// +//========================================================================== + void GLSprite::Draw(int pass) { if (pass == GLPASS_DECALS || pass == GLPASS_LIGHTSONLY) return; - - bool additivefog = false; bool foglayer = false; int rel = fullbright? 0 : getExtraLight(); @@ -264,147 +402,17 @@ void GLSprite::Draw(int pass) if (!modelframe) { - // [BB] Billboard stuff - const bool drawWithXYBillboard = ((particle && gl_billboard_particles) || (!(actor && actor->renderflags & RF_FORCEYBILLBOARD) - //&& GLRenderer->mViewActor != NULL - && (gl_billboard_mode == 1 || (actor && actor->renderflags & RF_FORCEXYBILLBOARD)))); - - const bool drawBillboardFacingCamera = gl_billboard_faces_camera; - // [Nash] has +ROLLSPRITE - const bool drawRollSpriteActor = (actor != nullptr && actor->renderflags & RF_ROLLSPRITE); gl_RenderState.Apply(); - FVector3 v1; - FVector3 v2; - FVector3 v3; - FVector3 v4; + FVector3 v[4]; + CalculateVertices(v); - // [fgsfds] check sprite type mask - DWORD spritetype = (DWORD)-1; - if (actor != nullptr) spritetype = actor->renderflags & RF_SPRITETYPEMASK; - - // [Nash] is a flat sprite - const bool isFlatSprite = (actor != nullptr) && (spritetype == RF_WALLSPRITE || spritetype == RF_FLATSPRITE); - const bool dontFlip = (actor != nullptr) && (actor->renderflags & RF_DONTFLIP); - const bool useOffsets = (actor != nullptr) && !(actor->renderflags & RF_ROLLCENTER); - - // [Nash] check for special sprite drawing modes - if (drawWithXYBillboard || drawBillboardFacingCamera || drawRollSpriteActor || isFlatSprite) - { - // Compute center of sprite - float xcenter = (x1 + x2)*0.5; - float ycenter = (y1 + y2)*0.5; - float zcenter = (z1 + z2)*0.5; - float xx = -xcenter + x; - float zz = -zcenter + z; - float yy = -ycenter + y; - Matrix3x4 mat; - mat.MakeIdentity(); - mat.Translate(xcenter, zcenter, ycenter); // move to sprite center - - // Order of rotations matters. Perform yaw rotation (Y, face camera) before pitch (X, tilt up/down). - if (drawBillboardFacingCamera && !isFlatSprite) - { - // [CMB] Rotate relative to camera XY position, not just camera direction, - // which is nicer in VR - float xrel = xcenter - ViewPos.X; - float yrel = ycenter - ViewPos.Y; - float absAngleDeg = RAD2DEG(atan2(-yrel, xrel)); - float counterRotationDeg = 270. - GLRenderer->mAngles.Yaw.Degrees; // counteracts existing sprite rotation - float relAngleDeg = counterRotationDeg + absAngleDeg; - - mat.Rotate(0, 1, 0, relAngleDeg); - } - - // [fgsfds] calculate yaw vectors - float yawvecX = 0, yawvecY = 0, rollDegrees = 0; - float angleRad = (270. - GLRenderer->mAngles.Yaw).Radians(); - if (actor) rollDegrees = actor->Angles.Roll.Degrees; - if (isFlatSprite) - { - yawvecX = actor->Angles.Yaw.Cos(); - yawvecY = actor->Angles.Yaw.Sin(); - } - - // [MC] This is the only thing that I changed in Nash's submission which - // was constantly applying roll to everything. That was wrong. Flat sprites - // with roll literally look like paper thing space ships trying to swerve. - // However, it does well with wall sprites. - // Also, renamed FLOORSPRITE to FLATSPRITE because that's technically incorrect. - // I plan on adding proper FLOORSPRITEs which can actually curve along sloped - // 3D floors later... if possible. - - // Here we need some form of priority in order to work. - if (spritetype == RF_FLATSPRITE) - { - float pitchDegrees = -actor->Angles.Pitch.Degrees; - DVector3 apos = { x, y, z }; - DVector3 diff = ViewPos - apos; - DAngle angto = diff.Angle(); - - angto = deltaangle(actor->Angles.Yaw, angto); - - bool noFlipSprite = (!dontFlip || (fabs(angto) < 90.)); - mat.Rotate(0, 1, 0, (noFlipSprite) ? 0 : 180); - - mat.Rotate(-yawvecY, 0, yawvecX, (noFlipSprite) ? -pitchDegrees : pitchDegrees); - if (drawRollSpriteActor) - { - if (useOffsets) mat.Translate(xx, zz, yy); - mat.Rotate(yawvecX, 0, yawvecY, (noFlipSprite) ? -rollDegrees : rollDegrees); - if (useOffsets) mat.Translate(-xx, -zz, -yy); - } - } - // [fgsfds] Rotate the sprite about the sight vector (roll) - else if (spritetype == RF_WALLSPRITE) - { - mat.Rotate(0, 1, 0, 0); - if (drawRollSpriteActor) - { - if (useOffsets) mat.Translate(xx, zz, yy); - mat.Rotate(yawvecX, 0, yawvecY, rollDegrees); - if (useOffsets) mat.Translate(-xx, -zz, -yy); - } - } - else if (drawRollSpriteActor) - { - if (useOffsets) mat.Translate(xx, zz, yy); - if (drawWithXYBillboard) - { - mat.Rotate(-sin(angleRad), 0, cos(angleRad), -GLRenderer->mAngles.Pitch.Degrees); - } - mat.Rotate(cos(angleRad), 0, sin(angleRad), rollDegrees); - if (useOffsets) mat.Translate(-xx, -zz, -yy); - } - - // apply the transform - else if (drawWithXYBillboard) - { - // Rotate the sprite about the vector starting at the center of the sprite - // triangle strip and with direction orthogonal to where the player is looking - // in the x/y plane. - mat.Rotate(-sin(angleRad), 0, cos(angleRad), -GLRenderer->mAngles.Pitch.Degrees); - } - - mat.Translate(-xcenter, -zcenter, -ycenter); // retreat from sprite center - v1 = mat * FVector3(x1, z1, y1); - v2 = mat * FVector3(x2, z1, y2); - v3 = mat * FVector3(x1, z2, y1); - v4 = mat * FVector3(x2, z2, y2); - } - else // traditional "Y" billboard mode - { - v1 = FVector3(x1, z1, y1); - v2 = FVector3(x2, z1, y2); - v3 = FVector3(x1, z2, y1); - v4 = FVector3(x2, z2, y2); - } FQuadDrawer qd; - qd.Set(0, v1[0], v1[1], v1[2], ul, vt); - qd.Set(1, v2[0], v2[1], v2[2], ur, vt); - qd.Set(2, v3[0], v3[1], v3[2], ul, vb); - qd.Set(3, v4[0], v4[1], v4[2], ur, vb); + qd.Set(0, v[0][0], v[0][1], v[0][2], ul, vt); + qd.Set(1, v[1][0], v[1][1], v[1][2], ur, vt); + qd.Set(2, v[2][0], v[2][1], v[2][2], ul, vb); + qd.Set(3, v[3][0], v[3][1], v[3][2], ur, vb); qd.Render(GL_TRIANGLE_STRIP); if (foglayer) @@ -703,7 +711,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) x = thingpos.X; z = thingpos.Z; y = thingpos.Y; - if (spritetype == RF_FLATSPRITE) z -= thing->Floorclip; + if (spritetype != RF_FLATSPRITE) z -= thing->Floorclip; // [RH] Make floatbobbing a renderer-only effect. if (thing->flags2 & MF2_FLOATBOB) diff --git a/src/gl/scene/gl_spritelight.cpp b/src/gl/scene/gl_spritelight.cpp index b25fc30f0..329459bd1 100644 --- a/src/gl/scene/gl_spritelight.cpp +++ b/src/gl/scene/gl_spritelight.cpp @@ -96,7 +96,7 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * dist = FVector3(x - light->X(), y - light->Y(), z - light->Z()).LengthSquared(); } - radius = light->GetRadius() * gl_lights_size; + radius = light->GetRadius(); if (dist < radius * radius) { @@ -106,9 +106,9 @@ void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t * if (frac > 0) { - lr = light->GetRed() / 255.0f * gl_lights_intensity; - lg = light->GetGreen() / 255.0f * gl_lights_intensity; - lb = light->GetBlue() / 255.0f * gl_lights_intensity; + lr = light->GetRed() / 255.0f; + lg = light->GetGreen() / 255.0f; + lb = light->GetBlue() / 255.0f; if (light->IsSubtractive()) { float bright = FVector3(lr, lg, lb).Length(); diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h index ae7014c7d..32dc6ee5b 100644 --- a/src/gl/scene/gl_wall.h +++ b/src/gl/scene/gl_wall.h @@ -371,6 +371,7 @@ public: void SplitSprite(sector_t * frontsector, bool translucent); void SetLowerParam(); void PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, float spriteheight); + void CalculateVertices(FVector3 *v); public: diff --git a/src/gl/scene/gl_walls_draw.cpp b/src/gl/scene/gl_walls_draw.cpp index 40145abc4..9f521ad11 100644 --- a/src/gl/scene/gl_walls_draw.cpp +++ b/src/gl/scene/gl_walls_draw.cpp @@ -124,7 +124,7 @@ void GLWall::SetupLights() float y = node->lightsource->Y(); float z = node->lightsource->Z(); float dist = fabsf(p.DistToPoint(x, z, y)); - float radius = (node->lightsource->GetRadius() * gl_lights_size); + float radius = node->lightsource->GetRadius(); float scale = 1.0f / ((2.f * radius) - dist); if (radius > 0.f && dist < radius) @@ -158,7 +158,7 @@ void GLWall::SetupLights() } if (outcnt[0]!=4 && outcnt[1]!=4 && outcnt[2]!=4 && outcnt[3]!=4) { - gl_GetLight(seg->frontsector->PortalGroup, p, node->lightsource, true, false, lightdata); + gl_GetLight(seg->frontsector->PortalGroup, p, node->lightsource, true, lightdata); } } } diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 9262efe7d..624b65be7 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -12,7 +12,8 @@ enum VATTR_VERTEX = 0, VATTR_TEXCOORD = 1, VATTR_COLOR = 2, - VATTR_VERTEX2 = 3 + VATTR_VERTEX2 = 3, + VATTR_NORMAL = 4 }; diff --git a/src/gl/stereo3d/gl_anaglyph.cpp b/src/gl/stereo3d/gl_anaglyph.cpp index c84c35091..3c051d390 100644 --- a/src/gl/stereo3d/gl_anaglyph.cpp +++ b/src/gl/stereo3d/gl_anaglyph.cpp @@ -34,6 +34,8 @@ */ #include "gl_anaglyph.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderbuffers.h" namespace s3d { @@ -44,6 +46,25 @@ MaskAnaglyph::MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters) eye_ptrs.Push(&rightEye); } +void MaskAnaglyph::Present() const +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + + gl_RenderState.SetColorMask(leftEye.GetColorMask().r, leftEye.GetColorMask().g, leftEye.GetColorMask().b, true); + gl_RenderState.ApplyColorMask(); + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(GLRenderer->mOutputLetterbox, true); + + gl_RenderState.SetColorMask(rightEye.GetColorMask().r, rightEye.GetColorMask().g, rightEye.GetColorMask().b, true); + gl_RenderState.ApplyColorMask(); + GLRenderer->mBuffers->BindEyeTexture(1, 0); + GLRenderer->DrawPresentTexture(GLRenderer->mOutputLetterbox, true); + + gl_RenderState.ResetColorMask(); + gl_RenderState.ApplyColorMask(); +} + /* static */ const GreenMagenta& GreenMagenta::getInstance(float ipd) diff --git a/src/gl/stereo3d/gl_anaglyph.h b/src/gl/stereo3d/gl_anaglyph.h index a82ce0f3b..e3383fe26 100644 --- a/src/gl/stereo3d/gl_anaglyph.h +++ b/src/gl/stereo3d/gl_anaglyph.h @@ -61,14 +61,8 @@ class AnaglyphLeftPose : public LeftEyePose { public: AnaglyphLeftPose(const ColorMask& colorMask, float ipd) : LeftEyePose(ipd), colorMask(colorMask) {} - virtual void SetUp() const { - gl_RenderState.SetColorMask(colorMask.r, colorMask.g, colorMask.b, true); - gl_RenderState.ApplyColorMask(); - } - virtual void TearDown() const { - gl_RenderState.ResetColorMask(); - gl_RenderState.ApplyColorMask(); - } + ColorMask GetColorMask() const { return colorMask; } + private: ColorMask colorMask; }; @@ -77,14 +71,8 @@ class AnaglyphRightPose : public RightEyePose { public: AnaglyphRightPose(const ColorMask& colorMask, float ipd) : RightEyePose(ipd), colorMask(colorMask) {} - virtual void SetUp() const { - gl_RenderState.SetColorMask(colorMask.r, colorMask.g, colorMask.b, true); - gl_RenderState.ApplyColorMask(); - } - virtual void TearDown() const { - gl_RenderState.ResetColorMask(); - gl_RenderState.ApplyColorMask(); - } + ColorMask GetColorMask() const { return colorMask; } + private: ColorMask colorMask; }; @@ -93,6 +81,7 @@ class MaskAnaglyph : public Stereo3DMode { public: MaskAnaglyph(const ColorMask& leftColorMask, double ipdMeters); + void Present() const override; private: AnaglyphLeftPose leftEye; AnaglyphRightPose rightEye; diff --git a/src/gl/stereo3d/gl_quadstereo.cpp b/src/gl/stereo3d/gl_quadstereo.cpp index 63bbbedc1..cd689d3b5 100644 --- a/src/gl/stereo3d/gl_quadstereo.cpp +++ b/src/gl/stereo3d/gl_quadstereo.cpp @@ -34,6 +34,8 @@ */ #include "gl_quadstereo.h" +#include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderbuffers.h" namespace s3d { @@ -46,7 +48,7 @@ QuadStereo::QuadStereo(double ipdMeters) GLboolean supportsStereo, supportsBuffered; glGetBooleanv(GL_STEREO, &supportsStereo); glGetBooleanv(GL_DOUBLEBUFFER, &supportsBuffered); - bool bQuadStereoSupported = supportsStereo && supportsBuffered; + bQuadStereoSupported = supportsStereo && supportsBuffered; leftEye.bQuadStereoSupported = bQuadStereoSupported; rightEye.bQuadStereoSupported = bQuadStereoSupported; @@ -57,6 +59,33 @@ QuadStereo::QuadStereo(double ipdMeters) } } +void QuadStereo::Present() const +{ + if (bQuadStereoSupported) + { + GLRenderer->mBuffers->BindOutputFB(); + + glDrawBuffer(GL_BACK_LEFT); + GLRenderer->ClearBorders(); + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(GLRenderer->mOutputLetterbox, true); + + glDrawBuffer(GL_BACK_RIGHT); + GLRenderer->ClearBorders(); + GLRenderer->mBuffers->BindEyeTexture(1, 0); + GLRenderer->DrawPresentTexture(GLRenderer->mOutputLetterbox, true); + + glDrawBuffer(GL_BACK); + } + else + { + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + GLRenderer->mBuffers->BindEyeTexture(1, 0); + GLRenderer->DrawPresentTexture(GLRenderer->mOutputLetterbox, true); + } +} + /* static */ const QuadStereo& QuadStereo::getInstance(float ipd) { diff --git a/src/gl/stereo3d/gl_quadstereo.h b/src/gl/stereo3d/gl_quadstereo.h index f27fb78f8..d9aa4f435 100644 --- a/src/gl/stereo3d/gl_quadstereo.h +++ b/src/gl/stereo3d/gl_quadstereo.h @@ -47,14 +47,6 @@ class QuadStereoLeftPose : public LeftEyePose { public: QuadStereoLeftPose(float ipd) : LeftEyePose(ipd), bQuadStereoSupported(false) {} - virtual void SetUp() const { - if (bQuadStereoSupported) - glDrawBuffer(GL_BACK_LEFT); - } - virtual void TearDown() const { - if (bQuadStereoSupported) - glDrawBuffer(GL_BACK); - } bool bQuadStereoSupported; }; @@ -62,14 +54,6 @@ class QuadStereoRightPose : public RightEyePose { public: QuadStereoRightPose(float ipd) : RightEyePose(ipd), bQuadStereoSupported(false){} - virtual void SetUp() const { - if (bQuadStereoSupported) - glDrawBuffer(GL_BACK_RIGHT); - } - virtual void TearDown() const { - if (bQuadStereoSupported) - glDrawBuffer(GL_BACK); - } bool bQuadStereoSupported; }; @@ -84,10 +68,12 @@ class QuadStereo : public Stereo3DMode { public: QuadStereo(double ipdMeters); + void Present() const override; static const QuadStereo& getInstance(float ipd); private: QuadStereoLeftPose leftEye; QuadStereoRightPose rightEye; + bool bQuadStereoSupported; }; diff --git a/src/gl/stereo3d/gl_stereo3d.h b/src/gl/stereo3d/gl_stereo3d.h index 303f18825..2cb9417d2 100644 --- a/src/gl/stereo3d/gl_stereo3d.h +++ b/src/gl/stereo3d/gl_stereo3d.h @@ -74,6 +74,7 @@ class Stereo3DMode public: /* static methods for managing the selected stereoscopic view state */ static const Stereo3DMode& getCurrentMode(); + static const Stereo3DMode& getMonoMode(); Stereo3DMode(); virtual ~Stereo3DMode(); @@ -84,6 +85,9 @@ public: virtual void SetUp() const {}; virtual void TearDown() const {}; + virtual bool IsMono() const { return false; } + virtual void Present() const = 0; + protected: TArray eye_ptrs; @@ -101,6 +105,9 @@ class MonoView : public Stereo3DMode public: static const MonoView& getInstance(); + bool IsMono() const override { return true; } + void Present() const override { } + protected: MonoView() { eye_ptrs.Push(¢ralEye); } EyePose centralEye; diff --git a/src/gl/stereo3d/gl_stereo_cvars.cpp b/src/gl/stereo3d/gl_stereo_cvars.cpp index e7d08df41..897b28088 100644 --- a/src/gl/stereo3d/gl_stereo_cvars.cpp +++ b/src/gl/stereo3d/gl_stereo_cvars.cpp @@ -105,5 +105,12 @@ const Stereo3DMode& Stereo3DMode::getCurrentMode() return *currentStereo3DMode; } +const Stereo3DMode& Stereo3DMode::getMonoMode() +{ + setCurrentMode(MonoView::getInstance()); + return *currentStereo3DMode; +} + + } /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_stereo_leftright.cpp b/src/gl/stereo3d/gl_stereo_leftright.cpp index 354185792..8605c9b53 100644 --- a/src/gl/stereo3d/gl_stereo_leftright.cpp +++ b/src/gl/stereo3d/gl_stereo_leftright.cpp @@ -37,7 +37,10 @@ #include "vectors.h" // RAD2DEG #include "doomtype.h" // M_PI #include "gl/system/gl_cvars.h" +#include "gl/system/gl_system.h" +#include "gl/renderer/gl_renderstate.h" #include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderbuffers.h" #include EXTERN_CVAR(Float, vr_screendist) @@ -89,6 +92,13 @@ const LeftEyeView& LeftEyeView::getInstance(float ipd) return instance; } +void LeftEyeView::Present() const +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(GLRenderer->mOutputLetterbox, true); +} /* static */ const RightEyeView& RightEyeView::getInstance(float ipd) @@ -98,5 +108,13 @@ const RightEyeView& RightEyeView::getInstance(float ipd) return instance; } +void RightEyeView::Present() const +{ + GLRenderer->mBuffers->BindOutputFB(); + GLRenderer->ClearBorders(); + GLRenderer->mBuffers->BindEyeTexture(0, 0); + GLRenderer->DrawPresentTexture(GLRenderer->mOutputLetterbox, true); +} + } /* namespace s3d */ diff --git a/src/gl/stereo3d/gl_stereo_leftright.h b/src/gl/stereo3d/gl_stereo_leftright.h index de13a32cb..250846651 100644 --- a/src/gl/stereo3d/gl_stereo_leftright.h +++ b/src/gl/stereo3d/gl_stereo_leftright.h @@ -83,6 +83,7 @@ public: LeftEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); } float getIpd() const { return eye.getIpd(); } void setIpd(float ipd) { eye.setIpd(ipd); } + void Present() const override; protected: LeftEyePose eye; }; @@ -96,6 +97,7 @@ public: RightEyeView(float ipd) : eye(ipd) { eye_ptrs.Push(&eye); } float getIpd() const { return eye.getIpd(); } void setIpd(float ipd) { eye.setIpd(ipd); } + void Present() const override; protected: RightEyePose eye; }; diff --git a/src/gl/system/gl_cvars.h b/src/gl/system/gl_cvars.h index 290f3ea8c..f6dc61472 100644 --- a/src/gl/system/gl_cvars.h +++ b/src/gl/system/gl_cvars.h @@ -24,9 +24,6 @@ EXTERN_CVAR(Int, gl_weaponlight) EXTERN_CVAR (Bool, gl_lights); EXTERN_CVAR (Bool, gl_attachedlights); EXTERN_CVAR (Bool, gl_lights_checkside); -EXTERN_CVAR (Float, gl_lights_intensity); -EXTERN_CVAR (Float, gl_lights_size); -EXTERN_CVAR (Bool, gl_lights_additive); EXTERN_CVAR (Bool, gl_light_sprites); EXTERN_CVAR (Bool, gl_light_particles); EXTERN_CVAR (Int, gl_light_math); diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index 05d63f65d..577bc12db 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -189,7 +189,8 @@ bool OpenGLFrameBuffer::WipeStartScreen(int type) void OpenGLFrameBuffer::WipeEndScreen() { - GLRenderer->m2DDrawer->Flush(); + GLRenderer->m2DDrawer->Draw(); + GLRenderer->m2DDrawer->Clear(); const auto &viewport = GLRenderer->mScreenViewport; wipeendscreen = new FHardwareTexture(viewport.width, viewport.height, true); diff --git a/src/gl/textures/gl_hqresize.cpp b/src/gl/textures/gl_hqresize.cpp index 0af793e58..fe523c79d 100644 --- a/src/gl/textures/gl_hqresize.cpp +++ b/src/gl/textures/gl_hqresize.cpp @@ -43,15 +43,21 @@ #ifdef HAVE_MMX #include "gl/hqnx_asm/hqnx_asm.h" #endif +#include "gl/xbr/xbrz.h" +#include "gl/xbr/xbrz_old.h" CUSTOM_CVAR(Int, gl_texture_hqresize, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { -#ifdef HAVE_MMX - if (self < 0 || self > 9) -#else - if (self < 0 || self > 6) -#endif + if (self < 0 || self > 16) + { self = 0; + } + #ifndef HAVE_MMX + // This is to allow the menu option to work properly so that these filters can be skipped while cycling through them. + if (self == 7) self = 10; + if (self == 8) self = 10; + if (self == 9) self = 6; + #endif GLRenderer->FlushTextures(); } @@ -242,6 +248,42 @@ static unsigned char *hqNxHelper( void (*hqNxFunction) ( unsigned*, unsigned*, i } + +static unsigned char *xbrzHelper( void (*xbrzFunction) ( size_t, const uint32_t*, uint32_t*, int, int, xbrz::ColorFormat, const xbrz::ScalerCfg&, int, int ), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + outWidth = N * inWidth; + outHeight = N *inHeight; + + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + xbrzFunction(N, reinterpret_cast(inputBuffer), reinterpret_cast(newBuffer), inWidth, inHeight, xbrz::ARGB, xbrz::ScalerCfg(), 0, std::numeric_limits::max()); + delete[] inputBuffer; + return newBuffer; +} + +static unsigned char *xbrzoldHelper( void (*xbrzFunction) ( size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz_old::ScalerCfg& cfg, int yFirst, int yLast ), + const int N, + unsigned char *inputBuffer, + const int inWidth, + const int inHeight, + int &outWidth, + int &outHeight ) +{ + outWidth = N * inWidth; + outHeight = N *inHeight; + + unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4]; + xbrzFunction(N, reinterpret_cast(inputBuffer), reinterpret_cast(newBuffer), inWidth, inHeight, xbrz_old::ScalerCfg(), 0, std::numeric_limits::max()); + delete[] inputBuffer; + return newBuffer; +} + + //=========================================================================== // // [BB] Upsamples the texture in inputBuffer, frees inputBuffer and returns @@ -322,6 +364,16 @@ unsigned char *gl_CreateUpsampledTextureBuffer ( const FTexture *inputTexture, u case 9: return hqNxAsmHelper( &HQnX_asm::hq4x_32, 4, inputBuffer, inWidth, inHeight, outWidth, outHeight ); #endif + case 10: + case 11: + case 12: + return xbrzHelper(xbrz::scale, type - 8, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + + case 13: + case 14: + case 15: + return xbrzoldHelper(xbrz_old::scale, type - 11, inputBuffer, inWidth, inHeight, outWidth, outHeight ); + } } return inputBuffer; diff --git a/src/gl/textures/gl_hwtexture.cpp b/src/gl/textures/gl_hwtexture.cpp index 73c4e7b36..25dc989e7 100644 --- a/src/gl/textures/gl_hwtexture.cpp +++ b/src/gl/textures/gl_hwtexture.cpp @@ -64,18 +64,6 @@ extern int TexFormat[]; //=========================================================================== unsigned int FHardwareTexture::lastbound[FHardwareTexture::MAX_TEXTURES]; -//=========================================================================== -// -// STATIC - Gets the maximum size of hardware textures -// -//=========================================================================== -int FHardwareTexture::GetTexDimension(int value) -{ - if (value > gl.max_texturesize) return gl.max_texturesize; - return value; -} - - //=========================================================================== // // Quick'n dirty image rescaling. diff --git a/src/gl/textures/gl_hwtexture.h b/src/gl/textures/gl_hwtexture.h index 4c00af272..96eff0264 100644 --- a/src/gl/textures/gl_hwtexture.h +++ b/src/gl/textures/gl_hwtexture.h @@ -10,6 +10,7 @@ #define DIRECT_PALETTE -2 #include "tarray.h" +#include "gl/system/gl_interface.h" class FCanvasTexture; class AActor; @@ -49,7 +50,11 @@ public: static unsigned int lastbound[MAX_TEXTURES]; - static int GetTexDimension(int value); + static int GetTexDimension(int value) + { + if (value > gl.max_texturesize) return gl.max_texturesize; + return value; + } static void InitGlobalState() { for (int i = 0; i < MAX_TEXTURES; i++) lastbound[i] = 0; } diff --git a/src/gl/textures/gl_material.h b/src/gl/textures/gl_material.h index d44a845f6..ee42a66d0 100644 --- a/src/gl/textures/gl_material.h +++ b/src/gl/textures/gl_material.h @@ -267,3 +267,5 @@ public: }; #endif + + diff --git a/src/gl/xbr/xbrz.cpp b/src/gl/xbr/xbrz.cpp new file mode 100644 index 000000000..b26d4bbd3 --- /dev/null +++ b/src/gl/xbr/xbrz.cpp @@ -0,0 +1,1229 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#include "xbrz.h" + +#include +#include +#include +#include + +#if __cplusplus <= 199711 +#define static_assert(VAL, MSG) static_assertion(); +template struct static_assertion; +template<> struct static_assertion {}; +#endif // __cplusplus <= 199711 + +namespace +{ +template inline +unsigned char getByte(uint32_t val) { return static_cast((val >> (8 * N)) & 0xff); } + +inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); } +inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); } +inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); } +inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); } + +inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; } +inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; } + + +template inline +uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending +{ + static_assert(0 < M && M < N && N <= 1000, ""); + +#define calcColor(colFront, colBack) \ + (((colFront) * M + (colBack) * (N - M)) / N) + + return makePixel(calcColor(getRed (pixFront), getRed (pixBack)), + calcColor(getGreen(pixFront), getGreen(pixBack)), + calcColor(getBlue (pixFront), getBlue (pixBack))); + +#undef calcColor +} + + +template inline +uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!) +{ + static_assert(0 < M && M < N && N <= 1000, ""); + + const unsigned int weightFront = getAlpha(pixFront) * M; + const unsigned int weightBack = getAlpha(pixBack) * (N - M); + const unsigned int weightSum = weightFront + weightBack; + if (weightSum == 0) + return 0; + +#define calcColor(colFront, colBack) \ + static_cast(((colFront) * weightFront + (colBack) * weightBack) / weightSum) + + return makePixel(static_cast(weightSum / N), + calcColor(getRed (pixFront), getRed (pixBack)), + calcColor(getGreen(pixFront), getGreen(pixBack)), + calcColor(getBlue (pixFront), getBlue (pixBack))); + +#undef calcColor +} + + +//inline +//double fastSqrt(double n) +//{ +// __asm //speeds up xBRZ by about 9% compared to std::sqrt which internally uses the same assembler instructions but adds some "fluff" +// { +// fld n +// fsqrt +// } +//} +// + + +uint32_t* byteAdvance( uint32_t* ptr, int bytes) { return reinterpret_cast< uint32_t*>(reinterpret_cast< char*>(ptr) + bytes); } +const uint32_t* byteAdvance(const uint32_t* ptr, int bytes) { return reinterpret_cast(reinterpret_cast(ptr) + bytes); } + + +//fill block with the given color +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int blockWidth, int blockHeight) +{ + //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + // std::fill(trg, trg + blockWidth, col); + + for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + for (int x = 0; x < blockWidth; ++x) + trg[x] = col; +} + +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int n) { fillBlock(trg, pitch, col, n, n); } + + +#ifdef _MSC_VER + #define FORCE_INLINE __forceinline +#elif defined __GNUC__ + #define FORCE_INLINE __attribute__((always_inline)) inline +#else + #define FORCE_INLINE inline +#endif + + +enum RotationDegree //clock-wise +{ + ROT_0, + ROT_90, + ROT_180, + ROT_270 +}; + +//calculate input matrix coordinates after rotation at compile time +template +struct MatrixRotation; + +template +struct MatrixRotation +{ + static const size_t I_old = I; + static const size_t J_old = J; +}; + +template //(i, j) = (row, col) indices, N = size of (square) matrix +struct MatrixRotation +{ + static const size_t I_old = N - 1 - MatrixRotation(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation! + static const size_t J_old = MatrixRotation(rotDeg - 1), I, J, N>::I_old; // +}; + + +template +class OutputMatrix +{ +public: + OutputMatrix(uint32_t* out, int outWidth) : //access matrix area, top-left at position "out" for image with given width + out_(out), + outWidth_(outWidth) {} + + template + uint32_t& ref() const + { + static const size_t I_old = MatrixRotation::I_old; + static const size_t J_old = MatrixRotation::J_old; + return *(out_ + J_old + I_old * outWidth_); + } + +private: + uint32_t* out_; + const int outWidth_; +}; + + +template inline +T square(T value) { return value * value; } + + + +inline +double distRGB(uint32_t pix1, uint32_t pix2) +{ + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //euklidean RGB distance + return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff)); +} + + +inline +double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight) +{ + //http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion + //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first! + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); // + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double! + + //const double k_b = 0.0722; //ITU-R BT.709 conversion + //const double k_r = 0.2126; // + const double k_b = 0.0593; //ITU-R BT.2020 conversion + const double k_r = 0.2627; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + //we skip division by 255 to have similar range like other distance functions + return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r)); +} + + +struct DistYCbCrBuffer //30% perf boost compared to distYCbCr()! +{ +public: + static double dist(uint32_t pix1, uint32_t pix2) + { +#if defined _MSC_VER && _MSC_VER < 1900 +#error function scope static initialization is not yet thread-safe! +#endif + static const DistYCbCrBuffer inst; + return inst.distImpl(pix1, pix2); + } + +private: + DistYCbCrBuffer() : buffer(256 * 256 * 256) + { + for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores) + { + const int r_diff = getByte<2>(i) * 2 - 255; + const int g_diff = getByte<1>(i) * 2 - 255; + const int b_diff = getByte<0>(i) * 2 - 255; + + const double k_b = 0.0593; //ITU-R BT.2020 conversion + const double k_r = 0.2627; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + buffer[i] = static_cast(std::sqrt(square(y) + square(c_b) + square(c_r))); + } + } + + double distImpl(uint32_t pix1, uint32_t pix2) const + { + //if (pix1 == pix2) -> 8% perf degradation! + // return 0; + //if (pix1 > pix2) + // std::swap(pix1, pix2); -> 30% perf degradation!!! + + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + return buffer[(((r_diff + 255) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte + (((g_diff + 255) / 2) << 8) | + (( b_diff + 255) / 2)]; + } + + std::vector buffer; //consumes 64 MB memory; using double is only 2% faster, but takes 128 MB +}; + + +enum BlendType +{ + BLEND_NONE = 0, + BLEND_NORMAL, //a normal indication to blend + BLEND_DOMINANT, //a strong indication to blend + //attention: BlendType must fit into the value range of 2 bit!!! +}; + +struct BlendResult +{ + BlendType + /**/blend_f, blend_g, + /**/blend_j, blend_k; +}; + + +struct Kernel_4x4 //kernel for preprocessing step +{ + uint32_t + /**/a, b, c, d, + /**/e, f, g, h, + /**/i, j, k, l, + /**/m, n, o, p; +}; + +/* +input kernel area naming convention: +----------------- +| A | B | C | D | +----|---|---|---| +| E | F | G | H | //evaluate the four corners between F, G, J, K +----|---|---|---| //input pixel is at position F +| I | J | K | L | +----|---|---|---| +| M | N | O | P | +----------------- +*/ +template +FORCE_INLINE //detect blend direction +BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType" +{ + BlendResult result = {}; + + if ((ker.f == ker.g && + ker.j == ker.k) || + (ker.f == ker.j && + ker.g == ker.k)) + return result; + +#define dist(pix1, pix2) \ + ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) + + const int weight = 4; + double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g); + double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k); + +#undef dist + + if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8 + { + const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk; + if (ker.f != ker.g && ker.f != ker.j) + result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.k != ker.j && ker.k != ker.g) + result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + else if (fk < jg) + { + const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg; + if (ker.j != ker.f && ker.j != ker.k) + result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.g != ker.f && ker.g != ker.k) + result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + return result; +} + +struct Kernel_3x3 +{ + uint32_t + /**/a, b, c, + /**/d, e, f, + /**/g, h, i; +}; + +#define DEF_GETTER(x) template uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; } +//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token +DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c) +DEF_GETTER(d) DEF_GETTER(e) DEF_GETTER(f) +DEF_GETTER(g) DEF_GETTER(h) DEF_GETTER(i) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, g) DEF_GETTER(b, d) DEF_GETTER(c, a) +DEF_GETTER(d, h) DEF_GETTER(e, e) DEF_GETTER(f, b) +DEF_GETTER(g, i) DEF_GETTER(h, f) DEF_GETTER(i, c) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, i) DEF_GETTER(b, h) DEF_GETTER(c, g) +DEF_GETTER(d, f) DEF_GETTER(e, e) DEF_GETTER(f, d) +DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i) +DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h) +DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g) +#undef DEF_GETTER + + +//compress four blend types into a single byte +inline BlendType getTopL (unsigned char b) { return static_cast(0x3 & b); } +inline BlendType getTopR (unsigned char b) { return static_cast(0x3 & (b >> 2)); } +inline BlendType getBottomR(unsigned char b) { return static_cast(0x3 & (b >> 4)); } +inline BlendType getBottomL(unsigned char b) { return static_cast(0x3 & (b >> 6)); } + +inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing! +inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } +inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); } +inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); } + +inline bool blendingNeeded(unsigned char b) { return b != 0; } + +template inline +unsigned char rotateBlendInfo(unsigned char b) { return b; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 2) | (b >> 6)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 4) | (b >> 4)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; } + + +/* +input kernel area naming convention: +------------- +| A | B | C | +----|---|---| +| D | E | F | //input pixel is at position E +----|---|---| +| G | H | I | +------------- +*/ +template +FORCE_INLINE //perf: quite worth it! +void blendPixel(const Kernel_3x3& ker, + uint32_t* target, int trgWidth, + unsigned char blendInfo, //result of preprocessing all four corners of pixel "e" + const xbrz::ScalerCfg& cfg) +{ +#define a get_a(ker) +#define b get_b(ker) +#define c get_c(ker) +#define d get_d(ker) +#define e get_e(ker) +#define f get_f(ker) +#define g get_g(ker) +#define h get_h(ker) +#define i get_i(ker) + + const unsigned char blend = rotateBlendInfo(blendInfo); + + if (getBottomR(blend) >= BLEND_NORMAL) + { + struct LineBlend + { + static bool Eval(const Kernel_3x3& ker, const xbrz::ScalerCfg& cfg, const unsigned char blend) + { + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + +#define eq(pix1, pix2) \ + (ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) < cfg.equalColorTolerance) + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90 degree corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (!eq(e, i) && eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c)) + return false; + +#undef eq + + return true; + } + }; + + const bool doLineBlend = LineBlend::Eval(ker, cfg, blend); + +#define dist(pix1, pix2) \ + ColorDistance::dist((pix1), (pix2), cfg.luminanceWeight) + + const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color + + OutputMatrix out(target, trgWidth); + + if (doLineBlend) + { + const double fg = dist(f, g); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9 + const double hc = dist(h, c); // + + const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g; + const bool haveSteepLine = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c; + + if (haveShallowLine) + { + if (haveSteepLine) + Scaler::blendLineSteepAndShallow(px, out); + else + Scaler::blendLineShallow(px, out); + } + else + { + if (haveSteepLine) + Scaler::blendLineSteep(px, out); + else + Scaler::blendLineDiagonal(px,out); + } + } + else + Scaler::blendCorner(px, out); + } + +#undef dist + +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h +#undef i +} + + +template //scaler policy: see "Scaler2x" reference implementation +void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) +{ + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || srcWidth <= 0) + return; + + const int trgWidth = srcWidth * Scaler::scale; + + //"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of + //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing + const int bufferSize = srcWidth; + unsigned char* preProcBuffer = reinterpret_cast(trg + yLast * Scaler::scale * trgWidth) - bufferSize; + std::fill(preProcBuffer, preProcBuffer + bufferSize, 0); + static_assert(BLEND_NONE == 0, ""); + + //initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending + //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition! + if (yFirst > 0) + { + const int y = yFirst - 1; + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + for (int x = 0; x < srcWidth; ++x) + { + const int x_m1 = std::max(x - 1, 0); + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker = {}; //perf: initialization is negligible + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //input pixel is at position F + | J | K | + --------- + */ + setTopR(preProcBuffer[x], res.blend_j); + + if (x + 1 < bufferSize) + setTopL(preProcBuffer[x + 1], res.blend_k); + } + } + //------------------------------------------------------------------------------------ + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position + + for (int x = 0; x < srcWidth; ++x, out += Scaler::scale) + { + //all those bounds checks have only insignificant impact on performance! + const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers! + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker4 = {}; //perf: initialization is negligible + + ker4.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker4.b = s_m1[x]; + ker4.c = s_m1[x_p1]; + ker4.d = s_m1[x_p2]; + + ker4.e = s_0[x_m1]; + ker4.f = s_0[x]; + ker4.g = s_0[x_p1]; + ker4.h = s_0[x_p2]; + + ker4.i = s_p1[x_m1]; + ker4.j = s_p1[x]; + ker4.k = s_p1[x_p1]; + ker4.l = s_p1[x_p2]; + + ker4.m = s_p2[x_m1]; + ker4.n = s_p2[x]; + ker4.o = s_p2[x_p1]; + ker4.p = s_p2[x_p2]; + + //evaluate the four corners on bottom-right of current pixel + unsigned char blend_xy = 0; //for current (x, y) position + { + const BlendResult res = preProcessCorners(ker4, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //current input pixel is at position F + | J | K | + --------- + */ + blend_xy = preProcBuffer[x]; + setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence! + + setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1) + preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row + + blend_xy1 = 0; + setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column + + if (x + 1 < bufferSize) //set 3rd known corner for (x + 1, y) + setBottomL(preProcBuffer[x + 1], res.blend_g); + } + + //fill block of size scale * scale with the given color + fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale); //place *after* preprocessing step, to not overwrite the results while processing the the last pixel! + + //blend four corners of current pixel + if (blendingNeeded(blend_xy)) //good 5% perf-improvement + { + Kernel_3x3 ker3 = {}; //perf: initialization is negligible + + ker3.a = ker4.a; + ker3.b = ker4.b; + ker3.c = ker4.c; + + ker3.d = ker4.e; + ker3.e = ker4.f; + ker3.f = ker4.g; + + ker3.g = ker4.i; + ker3.h = ker4.j; + ker3.i = ker4.k; + + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + blendPixel(ker3, out, trgWidth, blend_xy, cfg); + } + } + } +} + +//------------------------------------------------------------------------------------ + +template +struct Scaler2x : public ColorGradient +{ + static const int scale = 2; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<1, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 1>(), col); + alphaGrad<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref<1, 1>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366 + } +}; + + +template +struct Scaler3x : public ColorGradient +{ + static const int scale = 3; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + out.template ref<2, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<2, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 2>(), col); + alphaGrad<3, 4>(out.template ref<2, 1>(), col); + alphaGrad<3, 4>(out.template ref<1, 2>(), col); + out.template ref<2, 2>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 8>(out.template ref<1, 2>(), col); //conflict with other rotations for this odd scale + alphaGrad<1, 8>(out.template ref<2, 1>(), col); + alphaGrad<7, 8>(out.template ref<2, 2>(), col); // + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598 + //alphaGrad<7, 256>(out.template ref<2, 1>(), col); //0.02826017254 -> negligible + avoid conflicts with other rotations for this odd scale + //alphaGrad<7, 256>(out.template ref<1, 2>(), col); //0.02826017254 + } +}; + + +template +struct Scaler4x : public ColorGradient +{ + static const int scale = 4; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<3, 4>(out.template ref<3, 1>(), col); + alphaGrad<3, 4>(out.template ref<1, 3>(), col); + alphaGrad<1, 4>(out.template ref<3, 0>(), col); + alphaGrad<1, 4>(out.template ref<0, 3>(), col); + + alphaGrad<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR + + out.template ref<3, 3>() = col; + out.template ref<3, 2>() = col; + out.template ref<2, 3>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563 + alphaGrad< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501 + alphaGrad< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501 + } +}; + + +template +struct Scaler5x : public ColorGradient +{ + static const int scale = 5; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<4, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + alphaGrad<2, 3>(out.template ref<3, 3>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 8>(out.template ref(), col); //conflict with other rotations for this odd scale + alphaGrad<1, 8>(out.template ref(), col); + alphaGrad<1, 8>(out.template ref(), col); // + + alphaGrad<7, 8>(out.template ref<4, 3>(), col); + alphaGrad<7, 8>(out.template ref<3, 4>(), col); + + out.template ref<4, 4>() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088 + alphaGrad<23, 100>(out.template ref<4, 3>(), col); //0.2306749731 + alphaGrad<23, 100>(out.template ref<3, 4>(), col); //0.2306749731 + //alphaGrad<1, 64>(out.template ref<4, 2>(), col); //0.01676812367 -> negligible + avoid conflicts with other rotations for this odd scale + //alphaGrad<1, 64>(out.template ref<2, 4>(), col); //0.01676812367 + } +}; + + +template +struct Scaler6x : public ColorGradient +{ + static const int scale = 6; + + template //bring template function into scope for GCC + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) { ColorGradient::template alphaGrad(pixBack, pixFront); } + + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<5, scale - 3>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 4>(out.template ref<0, scale - 1>(), col); + alphaGrad<1, 4>(out.template ref<2, scale - 2>(), col); + alphaGrad<3, 4>(out.template ref<1, scale - 1>(), col); + alphaGrad<3, 4>(out.template ref<3, scale - 2>(), col); + + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<1, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + alphaGrad<3, 4>(out.template ref(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + alphaGrad<1, 2>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaGrad<97, 100>(out.template ref<5, 5>(), col); //exact: 0.9711013910 + alphaGrad<42, 100>(out.template ref<4, 5>(), col); //0.4236372243 + alphaGrad<42, 100>(out.template ref<5, 4>(), col); //0.4236372243 + alphaGrad< 6, 100>(out.template ref<5, 3>(), col); //0.05652034508 + alphaGrad< 6, 100>(out.template ref<3, 5>(), col); //0.05652034508 + } +}; + +//------------------------------------------------------------------------------------ + +struct ColorDistanceRGB +{ + static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) + { + return DistYCbCrBuffer::dist(pix1, pix2); + + //if (pix1 == pix2) //about 4% perf boost + // return 0; + //return distYCbCr(pix1, pix2, luminanceWeight); + } +}; + +struct ColorDistanceARGB +{ + static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight) + { + const double a1 = getAlpha(pix1) / 255.0 ; + const double a2 = getAlpha(pix2) / 255.0 ; + /* + Requirements for a color distance handling alpha channel: with a1, a2 in [0, 1] + + 1. if a1 = a2, distance should be: a1 * distYCbCr() + 2. if a1 = 0, distance should be: a2 * distYCbCr(black, white) = a2 * 255 + 3. if a1 = 1, ??? maybe: 255 * (1 - a2) + a2 * distYCbCr() + */ + + //return std::min(a1, a2) * DistYCbCrBuffer::dist(pix1, pix2) + 255 * abs(a1 - a2); + //=> following code is 15% faster: + const double d = DistYCbCrBuffer::dist(pix1, pix2); + if (a1 < a2) + return a1 * d + 255 * (a2 - a1); + else + return a2 * d + 255 * (a1 - a2); + + //alternative? return std::sqrt(a1 * a2 * square(DistYCbCrBuffer::dist(pix1, pix2)) + square(255 * (a1 - a2))); + } +}; + + +struct ColorGradientRGB +{ + template + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) + { + pixBack = gradientRGB(pixFront, pixBack); + } +}; + +struct ColorGradientARGB +{ + template + static void alphaGrad(uint32_t& pixBack, uint32_t pixFront) + { + pixBack = gradientARGB(pixFront, pixBack); + } +}; +} + + +void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast) +{ + switch (colFmt) + { + case ARGB: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + + case RGB: + switch (factor) + { + case 2: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + break; + } + assert(false); +} + + +bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance) +{ + switch (colFmt) + { + case ARGB: + return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + + case RGB: + return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance; + } + assert(false); + return false; +} + + +void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast) +{ + if (srcPitch < srcWidth * static_cast(sizeof(uint32_t)) || + trgPitch < trgWidth * static_cast(sizeof(uint32_t))) + { + assert(false); + return; + } + + switch (st) + { + case NN_SCALE_SLICE_SOURCE: + //nearest-neighbor (going over source image - fast for upscaling, since source is read only once + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) + // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight + + //keep within for loop to support MT input slices! + const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) + const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) + const int blockHeight = yTrg_last - yTrg_first; + + if (blockHeight > 0) + { + const uint32_t* srcLine = byteAdvance(src, y * srcPitch); + uint32_t* trgLine = byteAdvance(trg, yTrg_first * trgPitch); + int xTrg_first = 0; + + for (int x = 0; x < srcWidth; ++x) + { + int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; + const int blockWidth = xTrg_last - xTrg_first; + if (blockWidth > 0) + { + xTrg_first = xTrg_last; + fillBlock(trgLine, trgPitch, srcLine[x], blockWidth, blockHeight); + trgLine += blockWidth; + } + } + } + } + break; + + case NN_SCALE_SLICE_TARGET: + //nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* trgLine = byteAdvance(trg, y * trgPitch); + const int ySrc = srcHeight * y / trgHeight; + const uint32_t* srcLine = byteAdvance(src, ySrc * srcPitch); + for (int x = 0; x < trgWidth; ++x) + { + const int xSrc = srcWidth * x / trgWidth; + trgLine[x] = srcLine[xSrc]; + } + } + break; + } +} diff --git a/src/gl/xbr/xbrz.h b/src/gl/xbr/xbrz.h new file mode 100644 index 000000000..c641429e5 --- /dev/null +++ b/src/gl/xbr/xbrz.h @@ -0,0 +1,102 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#ifndef XBRZ_HEADER_3847894708239054 +#define XBRZ_HEADER_3847894708239054 + +#include //size_t +#include //uint32_t +#include +#include "xbrz_config.h" + +namespace xbrz +{ +/* +------------------------------------------------------------------------- +| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju | +------------------------------------------------------------------------- +using a modified approach of xBR: +http://board.byuu.org/viewtopic.php?f=10&t=2248 +- new rule set preserving small image features +- highly optimized for performance +- support alpha channel +- support multithreading +- support 64-bit architectures +- support processing image slices +- support scaling up to 6xBRZ +*/ + +enum ColorFormat //from high bits -> low bits, 8 bit per channel +{ + RGB, //8 bit for each red, green, blue, upper 8 bits unused + ARGB, //including alpha channel, BGRA byte order on little-endian machines +}; + +/* +-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only +-> support for source/target pitch in bytes! +-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image: + Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis) + Caveat: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition + in the target image data if you are using multiple threads for processing each enlarged slice! + +THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap! + - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process 8-16 rows at least +*/ +#ifdef max +#undef max +#endif +void scale(size_t factor, //valid range: 2 - 6 + const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, + ColorFormat colFmt, + const ScalerCfg& cfg = ScalerCfg(), + int yFirst = 0, int yLast = std::numeric_limits::max()); //slice of source image + +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight); + +enum SliceType +{ + NN_SCALE_SLICE_SOURCE, + NN_SCALE_SLICE_TARGET, +}; +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, //pitch in bytes! + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast); + +//parameter tuning +bool equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance); + + + + + +//########################### implementation ########################### +inline +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight) +{ + nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + NN_SCALE_SLICE_TARGET, 0, trgHeight); +} +} + +#endif diff --git a/src/gl/xbr/xbrz_config.h b/src/gl/xbr/xbrz_config.h new file mode 100644 index 000000000..28e9e9044 --- /dev/null +++ b/src/gl/xbr/xbrz_config.h @@ -0,0 +1,45 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#ifndef XBRZ_CONFIG_HEADER_284578425345 +#define XBRZ_CONFIG_HEADER_284578425345 + +//do NOT include any headers here! used by xBRZ_dll!!! + +namespace xbrz +{ +struct ScalerCfg +{ + ScalerCfg() : + luminanceWeight(1), + equalColorTolerance(30), + dominantDirectionThreshold(3.6), + steepDirectionThreshold(2.2), + newTestAttribute(0) {} + + double luminanceWeight; + double equalColorTolerance; + double dominantDirectionThreshold; + double steepDirectionThreshold; + double newTestAttribute; //unused; test new parameters +}; +} + +#endif \ No newline at end of file diff --git a/src/gl/xbr/xbrz_config_old.h b/src/gl/xbr/xbrz_config_old.h new file mode 100644 index 000000000..480af7976 --- /dev/null +++ b/src/gl/xbr/xbrz_config_old.h @@ -0,0 +1,45 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#ifndef __XBRZ_CONFIG_OLD_HEADER_INCLUDED__ +#define __XBRZ_CONFIG_OLD_HEADER_INCLUDED__ + +//do NOT include any headers here! used by xBRZ_dll!!! + +namespace xbrz_old +{ +struct ScalerCfg +{ + ScalerCfg() : + luminanceWeight_(1), + equalColorTolerance_(30), + dominantDirectionThreshold(3.6), + steepDirectionThreshold(2.2), + newTestAttribute_(0) {} + + double luminanceWeight_; + double equalColorTolerance_; + double dominantDirectionThreshold; + double steepDirectionThreshold; + double newTestAttribute_; //unused; test new parameters +}; +} + +#endif \ No newline at end of file diff --git a/src/gl/xbr/xbrz_old.cpp b/src/gl/xbr/xbrz_old.cpp new file mode 100644 index 000000000..07527cb95 --- /dev/null +++ b/src/gl/xbr/xbrz_old.cpp @@ -0,0 +1,1365 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#include "xbrz_old.h" + +#include +#include +#include + +#if __cplusplus > 199711 +#define XBRZ_CXX11 +#endif // __cplusplus > 199711 + +namespace +{ +template inline +unsigned char getByte(uint32_t val) { return static_cast((val >> (8 * N)) & 0xff); } + +inline unsigned char getRed (uint32_t val) { return getByte<2>(val); } +inline unsigned char getGreen(uint32_t val) { return getByte<1>(val); } +inline unsigned char getBlue (uint32_t val) { return getByte<0>(val); } + +template inline +T abs(T value) +{ +#ifdef XBRZ_CXX11 + static_assert(std::numeric_limits::is_signed, ""); +#endif // XBRZ_CXX11 + return value < 0 ? -value : value; +} + +const uint32_t redMask = 0xff0000; +const uint32_t greenMask = 0x00ff00; +const uint32_t blueMask = 0x0000ff; + +template inline +void alphaBlend(uint32_t& dst, uint32_t col) //blend color over destination with opacity N / M +{ +#ifdef XBRZ_CXX11 + static_assert(N < 256, "possible overflow of (col & redMask) * N"); + static_assert(M < 256, "possible overflow of (col & redMask ) * N + (dst & redMask ) * (M - N)"); + static_assert(0 < N && N < M, ""); +#endif // XBRZ_CXX11 + + static const uint32_t ALPHA_MASK = 0xFF000000; + static const uint32_t ALPHA_SHIFT = 24; + + static const uint32_t FULL_OPAQUE = 0xFF; + + const uint32_t colAlpha = col >> ALPHA_SHIFT; + const uint32_t dstAlpha = dst >> ALPHA_SHIFT; + + // Overflow is ignored intentionally! + + const uint32_t alpha = (FULL_OPAQUE == colAlpha && FULL_OPAQUE == dstAlpha) + ? ALPHA_MASK + : ALPHA_MASK & ((colAlpha * N + dstAlpha * (M - N)) / M << ALPHA_SHIFT); + + dst = (redMask & ((col & redMask ) * N + (dst & redMask ) * (M - N)) / M) | //this works because 8 upper bits are free + (greenMask & ((col & greenMask) * N + (dst & greenMask) * (M - N)) / M) | + (blueMask & ((col & blueMask ) * N + (dst & blueMask ) * (M - N)) / M) | + alpha; +} + + +//inline +//double fastSqrt(double n) +//{ +// __asm //speeds up xBRZ by about 9% compared to std::sqrt +// { +// fld n +// fsqrt +// } +//} +// + + +inline +uint32_t alphaBlend2(uint32_t pix1, uint32_t pix2, double alpha) +{ + return (redMask & static_cast((pix1 & redMask ) * alpha + (pix2 & redMask ) * (1 - alpha))) | + (greenMask & static_cast((pix1 & greenMask) * alpha + (pix2 & greenMask) * (1 - alpha))) | + (blueMask & static_cast((pix1 & blueMask ) * alpha + (pix2 & blueMask ) * (1 - alpha))); +} + + +uint32_t* byteAdvance( uint32_t* ptr, int bytes) { return reinterpret_cast< uint32_t*>(reinterpret_cast< char*>(ptr) + bytes); } +const uint32_t* byteAdvance(const uint32_t* ptr, int bytes) { return reinterpret_cast(reinterpret_cast(ptr) + bytes); } + + +//fill block with the given color +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int blockWidth, int blockHeight) +{ + //for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + // std::fill(trg, trg + blockWidth, col); + + for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch)) + for (int x = 0; x < blockWidth; ++x) + trg[x] = col; +} + +inline +void fillBlock(uint32_t* trg, int pitch, uint32_t col, int n) { fillBlock(trg, pitch, col, n, n); } + + +#ifdef _MSC_VER +#define FORCE_INLINE __forceinline +#elif defined __GNUC__ +#define FORCE_INLINE __attribute__((always_inline)) inline +#else +#define FORCE_INLINE inline +#endif + + +enum RotationDegree //clock-wise +{ + ROT_0, + ROT_90, + ROT_180, + ROT_270 +}; + +//calculate input matrix coordinates after rotation at compile time +template +struct MatrixRotation; + +template +struct MatrixRotation +{ + static const size_t I_old = I; + static const size_t J_old = J; +}; + +template //(i, j) = (row, col) indices, N = size of (square) matrix +struct MatrixRotation +{ + static const size_t I_old = N - 1 - MatrixRotation(rotDeg - 1), I, J, N>::J_old; //old coordinates before rotation! + static const size_t J_old = MatrixRotation(rotDeg - 1), I, J, N>::I_old; // +}; + + +template +class OutputMatrix +{ +public: + OutputMatrix(uint32_t* out, int outWidth) : //access matrix area, top-left at position "out" for image with given width + out_(out), + outWidth_(outWidth) {} + + template + uint32_t& ref() const + { + static const size_t I_old = MatrixRotation::I_old; + static const size_t J_old = MatrixRotation::J_old; + return *(out_ + J_old + I_old * outWidth_); + } + +private: + uint32_t* out_; + const int outWidth_; +}; + + +template inline +T square(T value) { return value * value; } + + +/* +inline +void rgbtoLuv(uint32_t c, double& L, double& u, double& v) +{ + //http://www.easyrgb.com/index.php?X=MATH&H=02#text2 + double r = getRed (c) / 255.0; + double g = getGreen(c) / 255.0; + double b = getBlue (c) / 255.0; + + if ( r > 0.04045 ) + r = std::pow(( ( r + 0.055 ) / 1.055 ) , 2.4); + else + r /= 12.92; + if ( g > 0.04045 ) + g = std::pow(( ( g + 0.055 ) / 1.055 ) , 2.4); + else + g /= 12.92; + if ( b > 0.04045 ) + b = std::pow(( ( b + 0.055 ) / 1.055 ) , 2.4); + else + b /= 12.92; + + r *= 100; + g *= 100; + b *= 100; + + double x = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b; + double y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b; + double z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b; + //--------------------- + double var_U = 4 * x / ( x + 15 * y + 3 * z ); + double var_V = 9 * y / ( x + 15 * y + 3 * z ); + double var_Y = y / 100; + + if ( var_Y > 0.008856 ) var_Y = std::pow(var_Y , 1.0/3 ); + else var_Y = 7.787 * var_Y + 16.0 / 116; + + const double ref_X = 95.047; //Observer= 2 degree, Illuminant= D65 + const double ref_Y = 100.000; + const double ref_Z = 108.883; + + const double ref_U = ( 4 * ref_X ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) ); + const double ref_V = ( 9 * ref_Y ) / ( ref_X + ( 15 * ref_Y ) + ( 3 * ref_Z ) ); + + L = ( 116 * var_Y ) - 16; + u = 13 * L * ( var_U - ref_U ); + v = 13 * L * ( var_V - ref_V ); +} +*/ + +inline +void rgbtoLab(uint32_t c, unsigned char& L, signed char& A, signed char& B) +{ + //code: http://www.easyrgb.com/index.php?X=MATH + //test: http://www.workwithcolor.com/color-converter-01.htm + //------RGB to XYZ------ + double r = getRed (c) / 255.0; + double g = getGreen(c) / 255.0; + double b = getBlue (c) / 255.0; + + r = r > 0.04045 ? std::pow(( r + 0.055 ) / 1.055, 2.4) : r / 12.92; + r = g > 0.04045 ? std::pow(( g + 0.055 ) / 1.055, 2.4) : g / 12.92; + r = b > 0.04045 ? std::pow(( b + 0.055 ) / 1.055, 2.4) : b / 12.92; + + r *= 100; + g *= 100; + b *= 100; + + double x = 0.4124564 * r + 0.3575761 * g + 0.1804375 * b; + double y = 0.2126729 * r + 0.7151522 * g + 0.0721750 * b; + double z = 0.0193339 * r + 0.1191920 * g + 0.9503041 * b; + //------XYZ to Lab------ + const double refX = 95.047; // + const double refY = 100.000; //Observer= 2 degree, Illuminant= D65 + const double refZ = 108.883; // + double var_X = x / refX; + double var_Y = y / refY; + double var_Z = z / refZ; + + var_X = var_X > 0.008856 ? std::pow(var_X, 1.0 / 3) : 7.787 * var_X + 4.0 / 29; + var_Y = var_Y > 0.008856 ? std::pow(var_Y, 1.0 / 3) : 7.787 * var_Y + 4.0 / 29; + var_Z = var_Z > 0.008856 ? std::pow(var_Z, 1.0 / 3) : 7.787 * var_Z + 4.0 / 29; + + L = static_cast(116 * var_Y - 16); + A = static_cast< signed char>(500 * (var_X - var_Y)); + B = static_cast< signed char>(200 * (var_Y - var_Z)); +}; + + +inline +double distLAB(uint32_t pix1, uint32_t pix2) +{ + unsigned char L1 = 0; //[0, 100] + signed char a1 = 0; //[-128, 127] + signed char b1 = 0; //[-128, 127] + rgbtoLab(pix1, L1, a1, b1); + + unsigned char L2 = 0; + signed char a2 = 0; + signed char b2 = 0; + rgbtoLab(pix2, L2, a2, b2); + + //----------------------------- + //http://www.easyrgb.com/index.php?X=DELT + + //Delta E/CIE76 + return std::sqrt(square(1.0 * L1 - L2) + + square(1.0 * a1 - a2) + + square(1.0 * b1 - b2)); +} + + +/* +inline +void rgbtoHsl(uint32_t c, double& h, double& s, double& l) +{ + //http://www.easyrgb.com/index.php?X=MATH&H=18#text18 + const int r = getRed (c); + const int g = getGreen(c); + const int b = getBlue (c); + + const int varMin = numeric::min(r, g, b); + const int varMax = numeric::max(r, g, b); + const int delMax = varMax - varMin; + + l = (varMax + varMin) / 2.0 / 255.0; + + if (delMax == 0) //gray, no chroma... + { + h = 0; + s = 0; + } + else + { + s = l < 0.5 ? + delMax / (1.0 * varMax + varMin) : + delMax / (2.0 * 255 - varMax - varMin); + + double delR = ((varMax - r) / 6.0 + delMax / 2.0) / delMax; + double delG = ((varMax - g) / 6.0 + delMax / 2.0) / delMax; + double delB = ((varMax - b) / 6.0 + delMax / 2.0) / delMax; + + if (r == varMax) + h = delB - delG; + else if (g == varMax) + h = 1 / 3.0 + delR - delB; + else if (b == varMax) + h = 2 / 3.0 + delG - delR; + + if (h < 0) + h += 1; + if (h > 1) + h -= 1; + } +} + +inline +double distHSL(uint32_t pix1, uint32_t pix2, double lightningWeight) +{ + double h1 = 0; + double s1 = 0; + double l1 = 0; + rgbtoHsl(pix1, h1, s1, l1); + double h2 = 0; + double s2 = 0; + double l2 = 0; + rgbtoHsl(pix2, h2, s2, l2); + + //HSL is in cylindric coordinatates where L represents height, S radius, H angle, + //however we interpret the cylinder as a bi-conic solid with top/bottom radius 0, middle radius 1 + assert(0 <= h1 && h1 <= 1); + assert(0 <= h2 && h2 <= 1); + + double r1 = l1 < 0.5 ? + l1 * 2 : + 2 - l1 * 2; + + double x1 = r1 * s1 * std::cos(h1 * 2 * numeric::pi); + double y1 = r1 * s1 * std::sin(h1 * 2 * numeric::pi); + double z1 = l1; + + double r2 = l2 < 0.5 ? + l2 * 2 : + 2 - l2 * 2; + + double x2 = r2 * s2 * std::cos(h2 * 2 * numeric::pi); + double y2 = r2 * s2 * std::sin(h2 * 2 * numeric::pi); + double z2 = l2; + + return 255 * std::sqrt(square(x1 - x2) + square(y1 - y2) + square(lightningWeight * (z1 - z2))); +} +*/ + + +inline +double distRGB(uint32_t pix1, uint32_t pix2) +{ + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //euklidean RGB distance + return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff)); +} + + +inline +double distNonLinearRGB(uint32_t pix1, uint32_t pix2) +{ + //non-linear rgb: http://www.compuphase.com/cmetric.htm + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + const double r_avg = (static_cast(getRed(pix1)) + getRed(pix2)) / 2; + return std::sqrt((2 + r_avg / 255) * square(r_diff) + 4 * square(g_diff) + (2 + (255 - r_avg) / 255) * square(b_diff)); +} + + +inline +double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight) +{ + //http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion + //YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first! + const int r_diff = static_cast(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication + const int g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); // + const int b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); //substraction for int is noticeable faster than for double! + + const double k_b = 0.0722; //ITU-R BT.709 conversion + const double k_r = 0.2126; // + const double k_g = 1 - k_b - k_r; + + const double scale_b = 0.5 / (1 - k_b); + const double scale_r = 0.5 / (1 - k_r); + + const double y = k_r * r_diff + k_g * g_diff + k_b * b_diff; //[!], analog YCbCr! + const double c_b = scale_b * (b_diff - y); + const double c_r = scale_r * (r_diff - y); + + //we skip division by 255 to have similar range like other distance functions + return std::sqrt(square(lumaWeight * y) + square(c_b) + square(c_r)); +} + + +inline +double distYUV(uint32_t pix1, uint32_t pix2, double luminanceWeight) +{ + //perf: it's not worthwhile to buffer the YUV-conversion, the direct code is faster by ~ 6% + //since RGB -> YUV conversion is essentially a matrix multiplication, we can calculate the RGB diff before the conversion (distributive property) + const double r_diff = static_cast(getRed (pix1)) - getRed (pix2); + const double g_diff = static_cast(getGreen(pix1)) - getGreen(pix2); + const double b_diff = static_cast(getBlue (pix1)) - getBlue (pix2); + + //http://en.wikipedia.org/wiki/YUV#Conversion_to.2Ffrom_RGB + const double w_b = 0.114; + const double w_r = 0.299; + const double w_g = 1 - w_r - w_b; + + const double u_max = 0.436; + const double v_max = 0.615; + + const double scale_u = u_max / (1 - w_b); + const double scale_v = v_max / (1 - w_r); + + double y = w_r * r_diff + w_g * g_diff + w_b * b_diff;//value range: 255 * [-1, 1] + double u = scale_u * (b_diff - y); //value range: 255 * 2 * u_max * [-1, 1] + double v = scale_v * (r_diff - y); //value range: 255 * 2 * v_max * [-1, 1] + +#ifndef NDEBUG + const double eps = 0.5; +#endif + assert(std::abs(y) <= 255 + eps); + assert(std::abs(u) <= 255 * 2 * u_max + eps); + assert(std::abs(v) <= 255 * 2 * v_max + eps); + + return std::sqrt(square(luminanceWeight * y) + square(u) + square(v)); +} + + +inline +double colorDist(uint32_t pix1, uint32_t pix2, double luminanceWeight) +{ + if (pix1 == pix2) //about 8% perf boost + return 0; + + //return distHSL(pix1, pix2, luminanceWeight); + //return distRGB(pix1, pix2); + //return distLAB(pix1, pix2); + //return distNonLinearRGB(pix1, pix2); + //return distYUV(pix1, pix2, luminanceWeight); + + return distYCbCr(pix1, pix2, luminanceWeight); +} + + +enum BlendType +{ + BLEND_NONE = 0, + BLEND_NORMAL, //a normal indication to blend + BLEND_DOMINANT, //a strong indication to blend + //attention: BlendType must fit into the value range of 2 bit!!! +}; + +struct BlendResult +{ + BlendType + /**/blend_f, blend_g, + /**/blend_j, blend_k; +}; + + +struct Kernel_4x4 //kernel for preprocessing step +{ + uint32_t + /**/a, b, c, d, + /**/e, f, g, h, + /**/i, j, k, l, + /**/m, n, o, p; +}; + +/* +input kernel area naming convention: +----------------- +| A | B | C | D | +----|---|---|---| +| E | F | G | H | //evalute the four corners between F, G, J, K +----|---|---|---| //input pixel is at position F +| I | J | K | L | +----|---|---|---| +| M | N | O | P | +----------------- +*/ +FORCE_INLINE //detect blend direction +BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz_old::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType" +{ + BlendResult result = {}; + + if ((ker.f == ker.g && + ker.j == ker.k) || + (ker.f == ker.j && + ker.g == ker.k)) + return result; + +#ifdef XBRZ_CXX11 + auto dist = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_); }; +#else // !XBRZ_CXX11 +#define dist(C1, C2) colorDist((C1), (C2), cfg.luminanceWeight_) +#endif // XBRZ_CXX11 + + const int weight = 4; + double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g); + double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k); + +#ifndef XBRZ_CXX11 +#undef dist +#endif // !XBRZ_CXX11 + + if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8 + { + const bool dominantGradient = cfg.dominantDirectionThreshold * jg < fk; + if (ker.f != ker.g && ker.f != ker.j) + result.blend_f = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.k != ker.j && ker.k != ker.g) + result.blend_k = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + else if (fk < jg) + { + const bool dominantGradient = cfg.dominantDirectionThreshold * fk < jg; + if (ker.j != ker.f && ker.j != ker.k) + result.blend_j = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + + if (ker.g != ker.f && ker.g != ker.k) + result.blend_g = dominantGradient ? BLEND_DOMINANT : BLEND_NORMAL; + } + return result; +} + +struct Kernel_3x3 +{ + uint32_t + /**/a, b, c, + /**/d, e, f, + /**/g, h, i; +}; + +#define DEF_GETTER(x) template uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; } +//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token +DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c) +DEF_GETTER(d) DEF_GETTER(e) DEF_GETTER(f) +DEF_GETTER(g) DEF_GETTER(h) DEF_GETTER(i) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, g) DEF_GETTER(b, d) DEF_GETTER(c, a) +DEF_GETTER(d, h) DEF_GETTER(e, e) DEF_GETTER(f, b) +DEF_GETTER(g, i) DEF_GETTER(h, f) DEF_GETTER(i, c) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, i) DEF_GETTER(b, h) DEF_GETTER(c, g) +DEF_GETTER(d, f) DEF_GETTER(e, e) DEF_GETTER(f, d) +DEF_GETTER(g, c) DEF_GETTER(h, b) DEF_GETTER(i, a) +#undef DEF_GETTER + +#define DEF_GETTER(x, y) template <> inline uint32_t get_##x(const Kernel_3x3& ker) { return ker.y; } +DEF_GETTER(a, c) DEF_GETTER(b, f) DEF_GETTER(c, i) +DEF_GETTER(d, b) DEF_GETTER(e, e) DEF_GETTER(f, h) +DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g) +#undef DEF_GETTER + + +//compress four blend types into a single byte +inline BlendType getTopL (unsigned char b) { return static_cast(0x3 & b); } +inline BlendType getTopR (unsigned char b) { return static_cast(0x3 & (b >> 2)); } +inline BlendType getBottomR(unsigned char b) { return static_cast(0x3 & (b >> 4)); } +inline BlendType getBottomL(unsigned char b) { return static_cast(0x3 & (b >> 6)); } + +inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing! +inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } +inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); } +inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); } + +inline bool blendingNeeded(unsigned char b) { return b != 0; } + +template inline +unsigned char rotateBlendInfo(unsigned char b) { return b; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 2) | (b >> 6)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 4) | (b >> 4)) & 0xff; } +template <> inline unsigned char rotateBlendInfo(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; } + + +#ifndef NDEBUG +int debugPixelX = -1; +int debugPixelY = 84; +bool breakIntoDebugger = false; +#endif + +#define a get_a(ker) +#define b get_b(ker) +#define c get_c(ker) +#define d get_d(ker) +#define e get_e(ker) +#define f get_f(ker) +#define g get_g(ker) +#define h get_h(ker) +#define i get_i(ker) + +#ifndef XBRZ_CXX11 + +template +bool doLineBlend(const Kernel_3x3& ker, const xbrz_old::ScalerCfg& cfg, const unsigned char blend) +{ + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + +#define eq(C1, C2) (colorDist((C1), (C2), cfg.luminanceWeight_) < cfg.equalColorTolerance_) + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90 degree corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c) && !eq(e, i)) + return false; + +#undef eq + + return true; +}; + +#endif // !XBRZ_CXX11 + +/* +input kernel area naming convention: +------------- +| A | B | C | +----|---|---| +| D | E | F | //input pixel is at position E +----|---|---| +| G | H | I | +------------- +*/ +template +FORCE_INLINE //perf: quite worth it! +void scalePixel(const Kernel_3x3& ker, + uint32_t* target, int trgWidth, + unsigned char blendInfo, //result of preprocessing all four corners of pixel "e" + const xbrz_old::ScalerCfg& cfg) +{ +#ifndef NDEBUG + if (breakIntoDebugger) +#ifdef _MSC_VER + __debugbreak(); //__asm int 3; +#else // !_MSC_VER + __builtin_trap(); +#endif // _MSC_VER +#endif + + const unsigned char blend = rotateBlendInfo(blendInfo); + + if (getBottomR(blend) >= BLEND_NORMAL) + { +#ifdef XBRZ_CXX11 + auto eq = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_) < cfg.equalColorTolerance_; }; + auto dist = [&](uint32_t col1, uint32_t col2) { return colorDist(col1, col2, cfg.luminanceWeight_); }; + + const bool doLineBlend = [&]() -> bool + { + if (getBottomR(blend) >= BLEND_DOMINANT) + return true; + + //make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes + if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90 degree corners + return false; + if (getBottomL(blend) != BLEND_NONE && !eq(e, c)) + return false; + + //no full blending for L-shapes; blend corner only (handles "mario mushroom eyes") + if (eq(g, h) && eq(h , i) && eq(i, f) && eq(f, c) && !eq(e, i)) + return false; + + return true; + }(); +#else // !XBRZ_CXX11 +#define dist(C1, C2) colorDist((C1), (C2), cfg.luminanceWeight_) +#endif // XBRZ_CXX11 + + const uint32_t px = dist(e, f) <= dist(e, h) ? f : h; //choose most similar color + + OutputMatrix out(target, trgWidth); + +#ifdef XBRZ_CXX11 + if (doLineBlend) +#else // !XBRZ_CXX11 + if (doLineBlend(ker, cfg, blend)) +#endif // XBRZ_CXX11 + { + const double fg = dist(f, g); //test sample: 70% of values max(fg, hc) / min(fg, hc) are between 1.1 and 3.7 with median being 1.9 + const double hc = dist(h, c); // + + const bool haveShallowLine = cfg.steepDirectionThreshold * fg <= hc && e != g && d != g; + const bool haveSteepLine = cfg.steepDirectionThreshold * hc <= fg && e != c && b != c; + + if (haveShallowLine) + { + if (haveSteepLine) + Scaler::blendLineSteepAndShallow(px, out); + else + Scaler::blendLineShallow(px, out); + } + else + { + if (haveSteepLine) + Scaler::blendLineSteep(px, out); + else + Scaler::blendLineDiagonal(px,out); + } + } + else + Scaler::blendCorner(px, out); + } + +#ifndef XBRZ_CXX11 +#undef dist +#endif // XBRZ_CXX11 + +#undef a +#undef b +#undef c +#undef d +#undef e +#undef f +#undef g +#undef h +#undef i +} + + +template //scaler policy: see "Scaler2x" reference implementation +void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz_old::ScalerCfg& cfg, int yFirst, int yLast) +{ + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || srcWidth <= 0) + return; + + const int trgWidth = srcWidth * Scaler::scale; + + //"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of + //"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing + const int bufferSize = srcWidth; + unsigned char* preProcBuffer = reinterpret_cast(trg + yLast * Scaler::scale * trgWidth) - bufferSize; + std::fill(preProcBuffer, preProcBuffer + bufferSize, 0); +#ifdef XBRZ_CXX11 + static_assert(BLEND_NONE == 0, ""); +#endif // XBRZ_CXX11 + + //initialize preprocessing buffer for first row: detect upper left and right corner blending + //this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition! + if (yFirst > 0) + { + const int y = yFirst - 1; + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + for (int x = 0; x < srcWidth; ++x) + { + const int x_m1 = std::max(x - 1, 0); + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + Kernel_4x4 ker = {}; //perf: initialization is negligable + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //input pixel is at position F + | J | K | + --------- + */ + setTopR(preProcBuffer[x], res.blend_j); + + if (x + 1 < srcWidth) + setTopL(preProcBuffer[x + 1], res.blend_k); + } + } + //------------------------------------------------------------------------------------ + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access + + const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0); + const uint32_t* s_0 = src + srcWidth * y; //center line + const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1); + const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1); + + unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position + + for (int x = 0; x < srcWidth; ++x, out += Scaler::scale) + { +#ifndef NDEBUG + breakIntoDebugger = debugPixelX == x && debugPixelY == y; +#endif + //all those bounds checks have only insignificant impact on performance! + const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers! + const int x_p1 = std::min(x + 1, srcWidth - 1); + const int x_p2 = std::min(x + 2, srcWidth - 1); + + //evaluate the four corners on bottom-right of current pixel + unsigned char blend_xy = 0; //for current (x, y) position + { + Kernel_4x4 ker = {}; //perf: initialization is negligable + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + ker.d = s_m1[x_p2]; + + ker.e = s_0[x_m1]; + ker.f = s_0[x]; + ker.g = s_0[x_p1]; + ker.h = s_0[x_p2]; + + ker.i = s_p1[x_m1]; + ker.j = s_p1[x]; + ker.k = s_p1[x_p1]; + ker.l = s_p1[x_p2]; + + ker.m = s_p2[x_m1]; + ker.n = s_p2[x]; + ker.o = s_p2[x_p1]; + ker.p = s_p2[x_p2]; + + const BlendResult res = preProcessCorners(ker, cfg); + /* + preprocessing blend result: + --------- + | F | G | //evalute corner between F, G, J, K + ----|---| //current input pixel is at position F + | J | K | + --------- + */ + blend_xy = preProcBuffer[x]; + setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence! + + setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1) + preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row + + blend_xy1 = 0; + setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column + + if (x + 1 < srcWidth) //set 3rd known corner for (x + 1, y) + setBottomL(preProcBuffer[x + 1], res.blend_g); + } + + //fill block of size scale * scale with the given color + fillBlock(out, trgWidth * sizeof(uint32_t), s_0[x], Scaler::scale); //place *after* preprocessing step, to not overwrite the results while processing the the last pixel! + + //blend four corners of current pixel + if (blendingNeeded(blend_xy)) //good 20% perf-improvement + { + Kernel_3x3 ker = {}; //perf: initialization is negligable + + ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible + ker.b = s_m1[x]; + ker.c = s_m1[x_p1]; + + ker.d = s_0[x_m1]; + ker.e = s_0[x]; + ker.f = s_0[x_p1]; + + ker.g = s_p1[x_m1]; + ker.h = s_p1[x]; + ker.i = s_p1[x_p1]; + + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + scalePixel(ker, out, trgWidth, blend_xy, cfg); + } + } + } +} + + +struct Scaler2x +{ + static const int scale = 2; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<1, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 1>(), col); + alphaBlend<5, 6>(out.template ref<1, 1>(), col); //[!] fixes 7/8 used in xBR + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 2>(out.template ref<1, 1>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<21, 100>(out.template ref<1, 1>(), col); //exact: 1 - pi/4 = 0.2146018366 + } +}; + + +struct Scaler3x +{ + static const int scale = 3; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + out.template ref<2, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<2, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 2>(), col); + alphaBlend<3, 4>(out.template ref<2, 1>(), col); + alphaBlend<3, 4>(out.template ref<1, 2>(), col); + out.template ref<2, 2>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 8>(out.template ref<1, 2>(), col); + alphaBlend<1, 8>(out.template ref<2, 1>(), col); + alphaBlend<7, 8>(out.template ref<2, 2>(), col); + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<45, 100>(out.template ref<2, 2>(), col); //exact: 0.4545939598 + //alphaBlend<14, 1000>(out.template ref<2, 1>(), col); //0.01413008627 -> negligable + //alphaBlend<14, 1000>(out.template ref<1, 2>(), col); //0.01413008627 + } +}; + + +struct Scaler4x +{ + static const int scale = 4; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<3, 4>(out.template ref<3, 1>(), col); + alphaBlend<3, 4>(out.template ref<1, 3>(), col); + alphaBlend<1, 4>(out.template ref<3, 0>(), col); + alphaBlend<1, 4>(out.template ref<0, 3>(), col); + alphaBlend<1, 3>(out.template ref<2, 2>(), col); //[!] fixes 1/4 used in xBR + out.template ref<3, 3>() = out.template ref<3, 2>() = out.template ref<2, 3>() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 2>(out.template ref(), col); + alphaBlend<1, 2>(out.template ref(), col); + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<68, 100>(out.template ref<3, 3>(), col); //exact: 0.6848532563 + alphaBlend< 9, 100>(out.template ref<3, 2>(), col); //0.08677704501 + alphaBlend< 9, 100>(out.template ref<2, 3>(), col); //0.08677704501 + } +}; + + +struct Scaler5x +{ + static const int scale = 5; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<4, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + + out.template ref() = col; + out.template ref() = col; + + out.template ref<4, scale - 1>() = col; + + alphaBlend<2, 3>(out.template ref<3, 3>(), col); + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 8>(out.template ref(), col); + alphaBlend<1, 8>(out.template ref(), col); + alphaBlend<1, 8>(out.template ref(), col); + + alphaBlend<7, 8>(out.template ref<4, 3>(), col); + alphaBlend<7, 8>(out.template ref<3, 4>(), col); + + out.template ref<4, 4>() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<86, 100>(out.template ref<4, 4>(), col); //exact: 0.8631434088 + alphaBlend<23, 100>(out.template ref<4, 3>(), col); //0.2306749731 + alphaBlend<23, 100>(out.template ref<3, 4>(), col); //0.2306749731 + //alphaBlend<8, 1000>(out.template ref<4, 2>(), col); //0.008384061834 -> negligable + //alphaBlend<8, 1000>(out.template ref<2, 4>(), col); //0.008384061834 + } +}; +} + + +struct Scaler6x +{ + static const int scale = 6; + + template + static void blendLineShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineSteep(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<1, 4>(out.template ref<4, scale - 3>(), col); + + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + alphaBlend<3, 4>(out.template ref<5, scale - 3>(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + } + + template + static void blendLineSteepAndShallow(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 4>(out.template ref<0, scale - 1>(), col); + alphaBlend<1, 4>(out.template ref<2, scale - 2>(), col); + alphaBlend<3, 4>(out.template ref<1, scale - 1>(), col); + alphaBlend<3, 4>(out.template ref<3, scale - 2>(), col); + + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<1, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + alphaBlend<3, 4>(out.template ref(), col); + + out.template ref<2, scale - 1>() = col; + out.template ref<3, scale - 1>() = col; + out.template ref<4, scale - 1>() = col; + out.template ref<5, scale - 1>() = col; + + out.template ref<4, scale - 2>() = col; + out.template ref<5, scale - 2>() = col; + + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendLineDiagonal(uint32_t col, OutputMatrix& out) + { + alphaBlend<1, 2>(out.template ref(), col); + alphaBlend<1, 2>(out.template ref(), col); + alphaBlend<1, 2>(out.template ref(), col); + + out.template ref() = col; + out.template ref() = col; + out.template ref() = col; + } + + template + static void blendCorner(uint32_t col, OutputMatrix& out) + { + //model a round corner + alphaBlend<97, 100>(out.template ref<5, 5>(), col); //exact: 0.9711013910 + alphaBlend<42, 100>(out.template ref<4, 5>(), col); //0.4236372243 + alphaBlend<42, 100>(out.template ref<5, 4>(), col); //0.4236372243 + alphaBlend< 6, 100>(out.template ref<5, 3>(), col); //0.05652034508 + alphaBlend< 6, 100>(out.template ref<3, 5>(), col); //0.05652034508 + } +}; + + +void xbrz_old::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz_old::ScalerCfg& cfg, int yFirst, int yLast) +{ + switch (factor) + { + case 2: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 3: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 4: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 5: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + case 6: + return scaleImage(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast); + } + assert(false); +} + + +bool xbrz_old::equalColor(uint32_t col1, uint32_t col2, double luminanceWeight, double equalColorTolerance) +{ + return colorDist(col1, col2, luminanceWeight) < equalColorTolerance; +} + + +void xbrz_old::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast) +{ + if (srcPitch < srcWidth * static_cast(sizeof(uint32_t)) || + trgPitch < trgWidth * static_cast(sizeof(uint32_t))) + { + assert(false); + return; + } + + switch (st) + { + case NN_SCALE_SLICE_SOURCE: + //nearest-neighbor (going over source image - fast for upscaling, since source is read only once + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, srcHeight); + if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + //mathematically: ySrc = floor(srcHeight * yTrg / trgHeight) + // => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight + + //keep within for loop to support MT input slices! + const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight) + const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight) + const int blockHeight = yTrg_last - yTrg_first; + + if (blockHeight > 0) + { + const uint32_t* srcLine = byteAdvance(src, y * srcPitch); + uint32_t* trgLine = byteAdvance(trg, yTrg_first * trgPitch); + int xTrg_first = 0; + + for (int x = 0; x < srcWidth; ++x) + { + int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth; + const int blockWidth = xTrg_last - xTrg_first; + if (blockWidth > 0) + { + xTrg_first = xTrg_last; + fillBlock(trgLine, trgPitch, srcLine[x], blockWidth, blockHeight); + trgLine += blockWidth; + } + } + } + } + break; + + case NN_SCALE_SLICE_TARGET: + //nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!) + yFirst = std::max(yFirst, 0); + yLast = std::min(yLast, trgHeight); + if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return; + + for (int y = yFirst; y < yLast; ++y) + { + uint32_t* trgLine = byteAdvance(trg, y * trgPitch); + const int ySrc = srcHeight * y / trgHeight; + const uint32_t* srcLine = byteAdvance(src, ySrc * srcPitch); + for (int x = 0; x < trgWidth; ++x) + { + const int xSrc = srcWidth * x / trgWidth; + trgLine[x] = srcLine[xSrc]; + } + } + break; + } +} diff --git a/src/gl/xbr/xbrz_old.h b/src/gl/xbr/xbrz_old.h new file mode 100644 index 000000000..c93a1480a --- /dev/null +++ b/src/gl/xbr/xbrz_old.h @@ -0,0 +1,92 @@ +// **************************************************************************** +// * This file is part of the HqMAME project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// * * +// * Additionally and as a special exception, the author gives permission * +// * to link the code of this program with the MAME library (or with modified * +// * versions of MAME that use the same license as MAME), and distribute * +// * linked combinations including the two. You must obey the GNU General * +// * Public License in all respects for all of the code used other than MAME. * +// * If you modify this file, you may extend this exception to your version * +// * of the file, but you are not obligated to do so. If you do not wish to * +// * do so, delete this exception statement from your version. * +// * * +// * An explicit permission was granted to use xBRZ in combination with ZDoom * +// * and derived projects as long as it is used for non-commercial purposes. * +// * * +// * Backported to C++98 by Alexey Lysiuk * +// **************************************************************************** + +#ifndef __XBRZ_OLD_HEADER_INCLUDED__ +#define __XBRZ_OLD_HEADER_INCLUDED__ + +#include //size_t +#include //uint32_t +#include +#include "xbrz_config_old.h" + +namespace xbrz_old +{ +/* +------------------------------------------------------------------------- +| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju | +------------------------------------------------------------------------- +using a modified approach of xBR: +http://board.byuu.org/viewtopic.php?f=10&t=2248 +- new rule set preserving small image features +- support multithreading +- support 64 bit architectures +- support processing image slices +*/ + +/* +-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only +-> color format: ARGB (BGRA byte order), alpha channel unused +-> support for source/target pitch in bytes! +-> if your emulator changes only a few image slices during each cycle (e.g. Dosbox) then there's no need to run xBRZ on the complete image: + Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis) + Caveat: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition + if you are using multiple threads for processing each enlarged slice! + +THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap! + - there is a minor inefficiency for the first row of a slice, so avoid processing single rows only + + +*/ +void scale(size_t factor, //valid range: 2 - 5 + const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, + const ScalerCfg& cfg = ScalerCfg(), + int yFirst = 0, int yLast = std::numeric_limits::max()); //slice of source image + +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight); + +enum SliceType +{ + NN_SCALE_SLICE_SOURCE, + NN_SCALE_SLICE_TARGET, +}; +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, //pitch in bytes! + uint32_t* trg, int trgWidth, int trgHeight, int trgPitch, + SliceType st, int yFirst, int yLast); + +//parameter tuning +bool equalColor(uint32_t col1, uint32_t col2, double luminanceWeight, double equalColorTolerance); + + + + + +//########################### implementation ########################### +inline +void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, + uint32_t* trg, int trgWidth, int trgHeight) +{ + nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t), + trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t), + NN_SCALE_SLICE_TARGET, 0, trgHeight); +} +} + +#endif diff --git a/src/p_acs.cpp b/src/p_acs.cpp index a2b5275cb..67ae37771 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4450,6 +4450,8 @@ enum EACSFunctions */ ACSF_CheckClass = 200, + ACSF_DamageActor, // [arookas] + ACSF_SetActorFlag, // ZDaemon ACSF_GetTeamScore = 19620, // (int team) @@ -6035,6 +6037,43 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) const char *clsname = FBehavior::StaticLookupString(args[0]); return !!PClass::FindActor(clsname); } + + case ACSF_DamageActor: // [arookas] wrapper around P_DamageMobj + { + // (target, ptr_select1, inflictor, ptr_select2, amount, damagetype) + AActor* target = COPY_AAPTR(SingleActorFromTID(args[0], activator), args[1]); + AActor* inflictor = COPY_AAPTR(SingleActorFromTID(args[2], activator), args[3]); + FName damagetype(FBehavior::StaticLookupString(args[5])); + return P_DamageMobj(target, inflictor, inflictor, args[4], damagetype); + } + + case ACSF_SetActorFlag: + { + int tid = args[0]; + FString flagname = FBehavior::StaticLookupString(args[1]); + bool flagvalue = !!args[2]; + int count = 0; // Return value; number of actors affected + if (tid == 0) + { + if (ModActorFlag(activator, flagname, flagvalue)) + { + ++count; + } + } + else + { + FActorIterator it(tid); + while ((actor = it.Next()) != nullptr) + { + // Don't log errors when affecting many actors because things might share a TID but not share the flag + if (ModActorFlag(actor, flagname, flagvalue, false)) + { + ++count; + } + } + } + return count; + } default: break; diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index f455a5f9f..624e8ad07 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -459,23 +459,14 @@ CocoaWindow* CreateCocoaWindow(const NSUInteger styleMask) return window; } -} // unnamed namespace - - -// --------------------------------------------------------------------------- - - -CocoaVideo::CocoaVideo() -: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) -, m_width(-1) -, m_height(-1) -, m_fullscreen(false) -, m_hiDPI(false) +enum OpenGLProfile { - memset(&m_modeIterator, 0, sizeof m_modeIterator); - - // Set attributes for OpenGL context + Core, + Legacy +}; +NSOpenGLPixelFormat* CreatePixelFormat(const OpenGLProfile profile) +{ NSOpenGLPixelFormatAttribute attributes[16]; size_t i = 0; @@ -492,17 +483,59 @@ CocoaVideo::CocoaVideo() attributes[i++] = NSOpenGLPFAAllowOfflineRenderers; } - if (NSAppKitVersionNumber >= AppKit10_7) + if (NSAppKitVersionNumber >= AppKit10_7 && OpenGLProfile::Core == profile) { + NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersion3_2Core; + const char* const glversion = Args->CheckValue("-glversion"); + + if (nullptr != glversion) + { + const double version = strtod(glversion, nullptr) + 0.01; + if (version < 3.2) + { + profile = NSOpenGLProfileVersionLegacy; + } + } + attributes[i++] = NSOpenGLPFAOpenGLProfile; - attributes[i++] = NSOpenGLProfileVersion3_2Core; + attributes[i++] = profile; } attributes[i] = NSOpenGLPixelFormatAttribute(0); - // Create OpenGL context and view + return [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; +} - NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; +} // unnamed namespace + + +// --------------------------------------------------------------------------- + + +CocoaVideo::CocoaVideo() +: m_window(CreateCocoaWindow(STYLE_MASK_WINDOWED)) +, m_width(-1) +, m_height(-1) +, m_fullscreen(false) +, m_hiDPI(false) +{ + memset(&m_modeIterator, 0, sizeof m_modeIterator); + + // Create OpenGL pixel format + + NSOpenGLPixelFormat* pixelFormat = CreatePixelFormat(OpenGLProfile::Core); + + if (nil == pixelFormat) + { + pixelFormat = CreatePixelFormat(OpenGLProfile::Legacy); + + if (nil == pixelFormat) + { + I_FatalError("Cannot OpenGL create pixel format, graphics hardware is not supported"); + } + } + + // Create OpenGL context and view const NSRect contentRect = [m_window contentRectForFrameRect:[m_window frame]]; NSOpenGLView* glView = [[CocoaView alloc] initWithFrame:contentRect diff --git a/src/posix/osx/i_specialpaths.mm b/src/posix/osx/i_specialpaths.mm new file mode 100644 index 000000000..843fb82d6 --- /dev/null +++ b/src/posix/osx/i_specialpaths.mm @@ -0,0 +1,187 @@ +/* +** i_specialpaths.mm +** Gets special system folders where data should be stored. (macOS version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include + +#include "cmdlib.h" +#include "m_misc.h" +#include "version.h" // for GAMENAME + +//=========================================================================== +// +// M_GetCachePath macOS +// +// Returns the path for cache GL nodes. +// +//=========================================================================== + +FString M_GetCachePath(bool create) +{ + FString path; + + char pathstr[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX)) + { + path = pathstr; + } + else + { + path = progdir; + } + path += "/zdoom/cache"; + return path; +} + +//=========================================================================== +// +// M_GetAutoexecPath macOS +// +// Returns the expected location of autoexec.cfg. +// +//=========================================================================== + +FString M_GetAutoexecPath() +{ + FString path; + + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/autoexec.cfg"; + } + return path; +} + +//=========================================================================== +// +// M_GetCajunPath macOS +// +// Returns the location of the Cajun Bot definitions. +// +//=========================================================================== + +FString M_GetCajunPath(const char *botfilename) +{ + FString path; + + // Just copies the Windows code. Should this be more Mac-specific? + path << progdir << "zcajun/" << botfilename; + if (!FileExists(path)) + { + path = ""; + } + return path; +} + +//=========================================================================== +// +// M_GetConfigPath macOS +// +// Returns the path to the config file. On Windows, this can vary for reading +// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try +// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. +// +//=========================================================================== + +FString M_GetConfigPath(bool for_reading) +{ + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kPreferencesFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + FString path; + path << cpath << "/" GAMENAMELOWERCASE ".ini"; + return path; + } + // Ungh. + return GAMENAMELOWERCASE ".ini"; +} + +//=========================================================================== +// +// M_GetScreenshotsPath macOS +// +// Returns the path to the default screenshots directory. +// +//=========================================================================== + +FString M_GetScreenshotsPath() +{ + FString path; + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/Screenshots/"; + } + else + { + path = "~/"; + } + return path; +} + +//=========================================================================== +// +// M_GetSavegamesPath macOS +// +// Returns the path to the default save games directory. +// +//=========================================================================== + +FString M_GetSavegamesPath() +{ + FString path; + char cpath[PATH_MAX]; + FSRef folder; + + if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && + noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) + { + path << cpath << "/" GAME_DIR "/Savegames/"; + } + return path; +} + diff --git a/src/posix/unix/i_specialpaths.cpp b/src/posix/unix/i_specialpaths.cpp new file mode 100644 index 000000000..5dedba057 --- /dev/null +++ b/src/posix/unix/i_specialpaths.cpp @@ -0,0 +1,200 @@ +/* +** i_specialpaths.cpp +** Gets special system folders where data should be stored. (Unix version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include +#include +#include "i_system.h" +#include "cmdlib.h" + +#include "version.h" // for GAMENAME + + +FString GetUserFile (const char *file) +{ + FString path; + struct stat info; + + path = NicePath("~/" GAME_DIR "/"); + + if (stat (path, &info) == -1) + { + struct stat extrainfo; + + // Sanity check for ~/.config + FString configPath = NicePath("~/.config/"); + if (stat (configPath, &extrainfo) == -1) + { + if (mkdir (configPath, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + I_FatalError ("Failed to create ~/.config directory:\n%s", strerror(errno)); + } + } + else if (!S_ISDIR(extrainfo.st_mode)) + { + I_FatalError ("~/.config must be a directory"); + } + + // This can be removed after a release or two + // Transfer the old zdoom directory to the new location + bool moved = false; + FString oldpath = NicePath("~/." GAMENAMELOWERCASE "/"); + if (stat (oldpath, &extrainfo) != -1) + { + if (rename(oldpath, path) == -1) + { + I_Error ("Failed to move old " GAMENAMELOWERCASE " directory (%s) to new location (%s).", + oldpath.GetChars(), path.GetChars()); + } + else + moved = true; + } + + if (!moved && mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) + { + I_FatalError ("Failed to create %s directory:\n%s", + path.GetChars(), strerror (errno)); + } + } + else + { + if (!S_ISDIR(info.st_mode)) + { + I_FatalError ("%s must be a directory", path.GetChars()); + } + } + path += file; + return path; +} + +//=========================================================================== +// +// M_GetCachePath Unix +// +// Returns the path for cache GL nodes. +// +//=========================================================================== + +FString M_GetCachePath(bool create) +{ + // Don't use GAME_DIR and such so that ZDoom and its child ports can + // share the node cache. + FString path = NicePath("~/.config/zdoom/cache"); + if (create) + { + CreatePath(path); + } + return path; +} + +//=========================================================================== +// +// M_GetAutoexecPath Unix +// +// Returns the expected location of autoexec.cfg. +// +//=========================================================================== + +FString M_GetAutoexecPath() +{ + return GetUserFile("autoexec.cfg"); +} + +//=========================================================================== +// +// M_GetCajunPath Unix +// +// Returns the location of the Cajun Bot definitions. +// +//=========================================================================== + +FString M_GetCajunPath(const char *botfilename) +{ + FString path; + + // Check first in ~/.config/zdoom/botfilename. + path = GetUserFile(botfilename); + if (!FileExists(path)) + { + // Then check in SHARE_DIR/botfilename. + path = SHARE_DIR; + path << botfilename; + if (!FileExists(path)) + { + path = ""; + } + } + return path; +} + +//=========================================================================== +// +// M_GetConfigPath Unix +// +// Returns the path to the config file. On Windows, this can vary for reading +// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try +// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. +// +//=========================================================================== + +FString M_GetConfigPath(bool for_reading) +{ + return GetUserFile(GAMENAMELOWERCASE ".ini"); +} + +//=========================================================================== +// +// M_GetScreenshotsPath Unix +// +// Returns the path to the default screenshots directory. +// +//=========================================================================== + +FString M_GetScreenshotsPath() +{ + return NicePath("~/" GAME_DIR "/screenshots/"); +} + +//=========================================================================== +// +// M_GetSavegamesPath Unix +// +// Returns the path to the default save games directory. +// +//=========================================================================== + +FString M_GetSavegamesPath() +{ + return NicePath("~/" GAME_DIR); +} diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 71d3f2376..00ac91c97 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -656,7 +656,7 @@ void R_AddInterpolationPoint(const DVector3a &vec) // //========================================================================== -static double QuakePower(double factor, double intensity, double offset, double falloff, double wfalloff) +static double QuakePower(double factor, double intensity, double offset) { double randumb; if (intensity == 0) @@ -667,7 +667,7 @@ static double QuakePower(double factor, double intensity, double offset, double { randumb = pr_torchflicker.GenRand_Real2() * (intensity * 2) - intensity; } - return factor * (wfalloff * offset + falloff * randumb); + return factor * (offset + randumb); } //========================================================================== @@ -797,36 +797,36 @@ void R_SetupFrame (AActor *actor) if (jiggers.RollIntensity != 0 || jiggers.RollWave != 0) { - ViewRoll += QuakePower(quakefactor, jiggers.RollIntensity, jiggers.RollWave, jiggers.RFalloff, jiggers.RWFalloff); + ViewRoll += QuakePower(quakefactor, jiggers.RollIntensity, jiggers.RollWave); } if (jiggers.RelIntensity.X != 0 || jiggers.RelOffset.X != 0) { an = camera->Angles.Yaw; - double power = QuakePower(quakefactor, jiggers.RelIntensity.X, jiggers.RelOffset.X, jiggers.Falloff, jiggers.WFalloff); + double power = QuakePower(quakefactor, jiggers.RelIntensity.X, jiggers.RelOffset.X); ViewPos += an.ToVector(power); } if (jiggers.RelIntensity.Y != 0 || jiggers.RelOffset.Y != 0) { an = camera->Angles.Yaw + 90; - double power = QuakePower(quakefactor, jiggers.RelIntensity.Y, jiggers.RelOffset.Y, jiggers.Falloff, jiggers.WFalloff); + double power = QuakePower(quakefactor, jiggers.RelIntensity.Y, jiggers.RelOffset.Y); ViewPos += an.ToVector(power); } // FIXME: Relative Z is not relative if (jiggers.RelIntensity.Z != 0 || jiggers.RelOffset.Z != 0) { - ViewPos.Z += QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z, jiggers.Falloff, jiggers.WFalloff); + ViewPos.Z += QuakePower(quakefactor, jiggers.RelIntensity.Z, jiggers.RelOffset.Z); } if (jiggers.Intensity.X != 0 || jiggers.Offset.X != 0) { - ViewPos.X += QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X, jiggers.Falloff, jiggers.WFalloff); + ViewPos.X += QuakePower(quakefactor, jiggers.Intensity.X, jiggers.Offset.X); } if (jiggers.Intensity.Y != 0 || jiggers.Offset.Y != 0) { - ViewPos.Y += QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y, jiggers.Falloff, jiggers.WFalloff); + ViewPos.Y += QuakePower(quakefactor, jiggers.Intensity.Y, jiggers.Offset.Y); } if (jiggers.Intensity.Z != 0 || jiggers.Offset.Z != 0) { - ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z, jiggers.Falloff, jiggers.WFalloff); + ViewPos.Z += QuakePower(quakefactor, jiggers.Intensity.Z, jiggers.Offset.Z); } } } diff --git a/src/textures/bitmap.h b/src/textures/bitmap.h index 27d3c0b4b..a27d1ff98 100644 --- a/src/textures/bitmap.h +++ b/src/textures/bitmap.h @@ -380,7 +380,7 @@ struct bCopyAlpha }; struct bOverlay -{ +{ static __forceinline void OpC(BYTE &d, BYTE s, BYTE a, FCopyInfo *i) { d = (s*a + d*(255-a))/255; } static __forceinline void OpA(BYTE &d, BYTE s, FCopyInfo *i) { d = MAX(s,d); } static __forceinline bool ProcessAlpha0() { return false; } diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index b0db481a8..41ba5f0f2 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -628,8 +628,9 @@ int FMultiPatchTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rota { ret = Parts[i].Texture->CopyTrueColorPixels(bmp, x+Parts[i].OriginX, y+Parts[i].OriginY, Parts[i].Rotate, &info); } - - if (ret > retv) retv = ret; + // treat -1 (i.e. unknown) as absolute. We have no idea if this may have overwritten previous info so a real check needs to be done. + if (ret == -1) retv = ret; + else if (retv != -1 && ret > retv) retv = ret; } // Restore previous clipping rectangle. bmp->SetClipRect(saved_cr); diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 296383a3d..3a419b112 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -30,6 +30,7 @@ void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int in bool CheckDeprecatedFlags(const AActor *actor, PClassActor *info, int index); const char *GetFlagName(unsigned int flagnum, int flagoffset); void ModActorFlag(AActor *actor, FFlagDef *fd, bool set); +bool ModActorFlag(AActor *actor, FString &flagname, bool set, bool printerror = true); INTBOOL CheckActorFlag(const AActor *actor, FFlagDef *fd); INTBOOL CheckActorFlag(const AActor *owner, const char *flagname, bool printerror = true); diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 1f24a35c7..642f132af 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -76,6 +76,7 @@ #include "d_player.h" #include "p_maputl.h" #include "p_spec.h" +#include "templates.h" #include "math/cmath.h" AActor *SingleActorFromTID(int tid, AActor *defactor); @@ -1421,7 +1422,11 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_Explode) damagetype = self->DamageType; } - int count = P_RadiusAttack (self, self->target, damage, distance, damagetype, flags, fulldmgdistance); + int pflags = 0; + if (flags & XF_HURTSOURCE) pflags |= RADF_HURTSOURCE; + if (flags & XF_NOTMISSILE) pflags |= RADF_SOURCEISSPOT; + + int count = P_RadiusAttack (self, self->target, damage, distance, damagetype, pflags, fulldmgdistance); P_CheckSplash(self, distance); if (alert && self->target != NULL && self->target->player != NULL) { @@ -4501,7 +4506,9 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_JumpIfTargetInLOS) else { target = viewport; viewport = self; } } - if (fov > 0 && (fov < 360.)) + fov = MIN(fov, 360.); + + if (fov > 0) { DAngle an = absangle(viewport->AngleTo(target), viewport->Angles.Yaw); @@ -4678,90 +4685,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_ChangeFlag) PARAM_STRING (flagname); PARAM_BOOL (value); - const char *dot = strchr(flagname, '.'); - FFlagDef *fd; - PClassActor *cls = self->GetClass(); - - if (dot != NULL) - { - FString part1(flagname.GetChars(), dot - flagname); - fd = FindFlag(cls, part1, dot + 1); - } - else - { - fd = FindFlag(cls, flagname, NULL); - } - - if (fd != NULL) - { - bool kill_before, kill_after; - INTBOOL item_before, item_after; - INTBOOL secret_before, secret_after; - - kill_before = self->CountsAsKill(); - item_before = self->flags & MF_COUNTITEM; - secret_before = self->flags5 & MF5_COUNTSECRET; - - if (fd->structoffset == -1) - { - HandleDeprecatedFlags(self, cls, value, fd->flagbit); - } - else - { - ActorFlags *flagp = (ActorFlags*) (((char*)self) + fd->structoffset); - - // If these 2 flags get changed we need to update the blockmap and sector links. - bool linkchange = flagp == &self->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); - - if (linkchange) self->UnlinkFromWorld(); - ModActorFlag(self, fd, value); - if (linkchange) self->LinkToWorld(); - } - kill_after = self->CountsAsKill(); - item_after = self->flags & MF_COUNTITEM; - secret_after = self->flags5 & MF5_COUNTSECRET; - // Was this monster previously worth a kill but no longer is? - // Or vice versa? - if (kill_before != kill_after) - { - if (kill_after) - { // It counts as a kill now. - level.total_monsters++; - } - else - { // It no longer counts as a kill. - level.total_monsters--; - } - } - // same for items - if (item_before != item_after) - { - if (item_after) - { // It counts as an item now. - level.total_items++; - } - else - { // It no longer counts as an item - level.total_items--; - } - } - // and secretd - if (secret_before != secret_after) - { - if (secret_after) - { // It counts as an secret now. - level.total_secrets++; - } - else - { // It no longer counts as an secret - level.total_secrets--; - } - } - } - else - { - Printf("Unknown flag '%s' in '%s'\n", flagname.GetChars(), cls->TypeName.GetChars()); - } + ModActorFlag(self, flagname, value); return 0; } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 8fceca8ee..756c56a1a 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -182,6 +182,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF4, BOSSDEATH, AActor, flags4), DEFINE_FLAG(MF5, DONTDRAIN, AActor, flags5), + DEFINE_FLAG(MF5, GETOWNER, AActor, flags5), DEFINE_FLAG(MF5, NODROPOFF, AActor, flags5), DEFINE_FLAG(MF5, NOFORWARDFALL, AActor, flags5), DEFINE_FLAG(MF5, COUNTSECRET, AActor, flags5), diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 991e9b947..cad6198f0 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -309,7 +309,8 @@ static FxExpression *ParseExpressionC (FScanner &sc, PClassActor *cls) static FxExpression *ParseExpressionB (FScanner &sc, PClassActor *cls) { sc.GetToken(); - switch(sc.TokenType) + int token = sc.TokenType; + switch(token) { case '~': return new FxUnaryNotBitwise(ParseExpressionA (sc, cls)); @@ -325,7 +326,7 @@ static FxExpression *ParseExpressionB (FScanner &sc, PClassActor *cls) case TK_Incr: case TK_Decr: - return new FxPreIncrDecr(ParseExpressionA(sc, cls), sc.TokenType); + return new FxPreIncrDecr(ParseExpressionA(sc, cls), token); default: sc.UnGet(); diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index 372f444cd..f6ad511af 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -162,6 +162,71 @@ void ModActorFlag(AActor *actor, FFlagDef *fd, bool set) #endif } +//========================================================================== +// +// Finds a flag by name and sets or clears it +// +// Returns true if the flag was found for the actor; else returns false +// +//========================================================================== + +bool ModActorFlag(AActor *actor, FString &flagname, bool set, bool printerror) +{ + bool found = false; + + if (actor != NULL) + { + const char *dot = strchr(flagname, '.'); + FFlagDef *fd; + PClassActor *cls = actor->GetClass(); + + if (dot != NULL) + { + FString part1(flagname.GetChars(), dot - flagname); + fd = FindFlag(cls, part1, dot + 1); + } + else + { + fd = FindFlag(cls, flagname, NULL); + } + + if (fd != NULL) + { + found = true; + + if (actor->CountsAsKill() && actor->health > 0) --level.total_monsters; + if (actor->flags & MF_COUNTITEM) --level.total_items; + if (actor->flags5 & MF5_COUNTSECRET) --level.total_secrets; + + if (fd->structoffset == -1) + { + HandleDeprecatedFlags(actor, cls, set, fd->flagbit); + } + else + { + ActorFlags *flagp = (ActorFlags*)(((char*)actor) + fd->structoffset); + + // If these 2 flags get changed we need to update the blockmap and sector links. + bool linkchange = flagp == &actor->flags && (fd->flagbit == MF_NOBLOCKMAP || fd->flagbit == MF_NOSECTOR); + + if (linkchange) actor->UnlinkFromWorld(); + ModActorFlag(actor, fd, set); + if (linkchange) actor->LinkToWorld(); + } + + if (actor->CountsAsKill() && actor->health > 0) ++level.total_monsters; + if (actor->flags & MF_COUNTITEM) ++level.total_items; + if (actor->flags5 & MF5_COUNTSECRET) ++level.total_secrets; + } + else if (printerror) + { + DPrintf(DMSG_ERROR, "ACS/DECORATE: '%s' is not a flag in '%s'\n", flagname.GetChars(), cls->TypeName.GetChars()); + } + } + + return found; +} + //========================================================================== // // Returns whether an actor flag is true or not. diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 89023d4bd..dada0cd57 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -62,6 +62,14 @@ #include "colormatcher.h" #include "r_data/colormaps.h" +CUSTOM_CVAR(Int, uiscale, 2, CVAR_ARCHIVE | CVAR_NOINITCALL) +{ + if (StatusBar != NULL) + { + StatusBar->ScreenSizeChanged(); + } +} + // [RH] Stretch values to make a 320x200 image best fit the screen // without using fractional steppings int CleanXfac, CleanYfac; @@ -75,7 +83,7 @@ int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1; // FillSimplePoly uses this extern "C" short spanend[MAXHEIGHT]; -CVAR (Bool, hud_scale, false, CVAR_ARCHIVE); +CVAR (Bool, hud_scale, true, CVAR_ARCHIVE); // For routines that take RGB colors, cache the previous lookup in case there // are several repetitions with the same color. diff --git a/src/v_video.cpp b/src/v_video.cpp index 0ee065bbe..b9917a1cb 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -65,6 +65,7 @@ #include "menu/menu.h" #include "r_data/voxels.h" +int active_con_scale(); FRenderer *Renderer; @@ -857,10 +858,20 @@ void DFrameBuffer::DrawRateStuff () int chars; int rate_x; + int textScale = active_con_scale(); + if (textScale == 0) + textScale = CleanXfac; + chars = mysnprintf (fpsbuff, countof(fpsbuff), "%2u ms (%3u fps)", howlong, LastCount); - rate_x = Width - ConFont->StringWidth(&fpsbuff[0]); - Clear (rate_x, 0, Width, ConFont->GetHeight(), GPalette.BlackIndex, 0); - DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], TAG_DONE); + rate_x = Width / textScale - ConFont->StringWidth(&fpsbuff[0]); + Clear (rate_x * textScale, 0, Width, ConFont->GetHeight() * textScale, GPalette.BlackIndex, 0); + if (textScale == 1) + DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], TAG_DONE); + else + DrawText (ConFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0], + DTA_VirtualWidth, screen->GetWidth() / textScale, + DTA_VirtualHeight, screen->GetHeight() / textScale, + DTA_KeepRatio, true, TAG_DONE); DWORD thisSec = ms/1000; if (LastSec < thisSec) diff --git a/src/v_video.h b/src/v_video.h index 7091c518d..4f0a78675 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -522,4 +522,6 @@ inline bool Is54Aspect(int ratio) { return ratio == 4; } +EXTERN_CVAR(Int, uiscale); + #endif // __V_VIDEO_H__ diff --git a/src/m_specialpaths.cpp b/src/win32/i_specialpaths.cpp similarity index 50% rename from src/m_specialpaths.cpp rename to src/win32/i_specialpaths.cpp index abfb5db8f..ed8dc2ee6 100644 --- a/src/m_specialpaths.cpp +++ b/src/win32/i_specialpaths.cpp @@ -1,27 +1,46 @@ -#ifdef __APPLE__ -#include -#endif +/* +** i_specialpaths.cpp +** Gets special system folders where data should be stored. (Windows version) +** +**--------------------------------------------------------------------------- +** Copyright 2013-2016 Randy Heit +** Copyright 2016 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ -#ifdef _WIN32 #include #include #include #define USE_WINDOWS_DWORD -#endif #include "cmdlib.h" #include "m_misc.h" - -#if !defined(__APPLE__) && !defined(_WIN32) -#include -#include -#include "i_system.h" -#endif - #include "version.h" // for GAMENAME - -#if defined(_WIN32) - #include "i_system.h" typedef HRESULT (WINAPI *GKFP)(REFKNOWNFOLDERID, DWORD, HANDLE, PWSTR *); @@ -323,314 +342,3 @@ FString M_GetSavegamesPath() } return path; } - -#elif defined(__APPLE__) - -//=========================================================================== -// -// M_GetCachePath Mac OS X -// -// Returns the path for cache GL nodes. -// -//=========================================================================== - -FString M_GetCachePath(bool create) -{ - FString path; - - char pathstr[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, create ? kCreateFolder : 0, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)pathstr, PATH_MAX)) - { - path = pathstr; - } - else - { - path = progdir; - } - path += "/zdoom/cache"; - return path; -} - -//=========================================================================== -// -// M_GetAutoexecPath Mac OS X -// -// Returns the expected location of autoexec.cfg. -// -//=========================================================================== - -FString M_GetAutoexecPath() -{ - FString path; - - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/autoexec.cfg"; - } - return path; -} - -//=========================================================================== -// -// M_GetCajunPath Mac OS X -// -// Returns the location of the Cajun Bot definitions. -// -//=========================================================================== - -FString M_GetCajunPath(const char *botfilename) -{ - FString path; - - // Just copies the Windows code. Should this be more Mac-specific? - path << progdir << "zcajun/" << botfilename; - if (!FileExists(path)) - { - path = ""; - } - return path; -} - -//=========================================================================== -// -// M_GetConfigPath Mac OS X -// -// Returns the path to the config file. On Windows, this can vary for reading -// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try -// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. -// -//=========================================================================== - -FString M_GetConfigPath(bool for_reading) -{ - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kPreferencesFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - FString path; - path << cpath << "/" GAMENAMELOWERCASE ".ini"; - return path; - } - // Ungh. - return GAMENAMELOWERCASE ".ini"; -} - -//=========================================================================== -// -// M_GetScreenshotsPath Mac OS X -// -// Returns the path to the default screenshots directory. -// -//=========================================================================== - -FString M_GetScreenshotsPath() -{ - FString path; - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/Screenshots/"; - } - else - { - path = "~/"; - } - return path; -} - -//=========================================================================== -// -// M_GetSavegamesPath Mac OS X -// -// Returns the path to the default save games directory. -// -//=========================================================================== - -FString M_GetSavegamesPath() -{ - FString path; - char cpath[PATH_MAX]; - FSRef folder; - - if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) && - noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX)) - { - path << cpath << "/" GAME_DIR "/Savegames/"; - } - return path; -} - -#else // Linux, et al. - - -FString GetUserFile (const char *file) -{ - FString path; - struct stat info; - - path = NicePath("~/" GAME_DIR "/"); - - if (stat (path, &info) == -1) - { - struct stat extrainfo; - - // Sanity check for ~/.config - FString configPath = NicePath("~/.config/"); - if (stat (configPath, &extrainfo) == -1) - { - if (mkdir (configPath, S_IRUSR | S_IWUSR | S_IXUSR) == -1) - { - I_FatalError ("Failed to create ~/.config directory:\n%s", strerror(errno)); - } - } - else if (!S_ISDIR(extrainfo.st_mode)) - { - I_FatalError ("~/.config must be a directory"); - } - - // This can be removed after a release or two - // Transfer the old zdoom directory to the new location - bool moved = false; - FString oldpath = NicePath("~/." GAMENAMELOWERCASE "/"); - if (stat (oldpath, &extrainfo) != -1) - { - if (rename(oldpath, path) == -1) - { - I_Error ("Failed to move old " GAMENAMELOWERCASE " directory (%s) to new location (%s).", - oldpath.GetChars(), path.GetChars()); - } - else - moved = true; - } - - if (!moved && mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR) == -1) - { - I_FatalError ("Failed to create %s directory:\n%s", - path.GetChars(), strerror (errno)); - } - } - else - { - if (!S_ISDIR(info.st_mode)) - { - I_FatalError ("%s must be a directory", path.GetChars()); - } - } - path += file; - return path; -} - -//=========================================================================== -// -// M_GetCachePath Unix -// -// Returns the path for cache GL nodes. -// -//=========================================================================== - -FString M_GetCachePath(bool create) -{ - // Don't use GAME_DIR and such so that ZDoom and its child ports can - // share the node cache. - FString path = NicePath("~/.config/zdoom/cache"); - if (create) - { - CreatePath(path); - } - return path; -} - -//=========================================================================== -// -// M_GetAutoexecPath Unix -// -// Returns the expected location of autoexec.cfg. -// -//=========================================================================== - -FString M_GetAutoexecPath() -{ - return GetUserFile("autoexec.cfg"); -} - -//=========================================================================== -// -// M_GetCajunPath Unix -// -// Returns the location of the Cajun Bot definitions. -// -//=========================================================================== - -FString M_GetCajunPath(const char *botfilename) -{ - FString path; - - // Check first in ~/.config/zdoom/botfilename. - path = GetUserFile(botfilename); - if (!FileExists(path)) - { - // Then check in SHARE_DIR/botfilename. - path = SHARE_DIR; - path << botfilename; - if (!FileExists(path)) - { - path = ""; - } - } - return path; -} - -//=========================================================================== -// -// M_GetConfigPath Unix -// -// Returns the path to the config file. On Windows, this can vary for reading -// vs writing. i.e. If $PROGDIR/zdoom-.ini does not exist, it will try -// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini. -// -//=========================================================================== - -FString M_GetConfigPath(bool for_reading) -{ - return GetUserFile(GAMENAMELOWERCASE ".ini"); -} - -//=========================================================================== -// -// M_GetScreenshotsPath Unix -// -// Returns the path to the default screenshots directory. -// -//=========================================================================== - -FString M_GetScreenshotsPath() -{ - return NicePath("~/" GAME_DIR "/screenshots/"); -} - -//=========================================================================== -// -// M_GetSavegamesPath Unix -// -// Returns the path to the default save games directory. -// -//=========================================================================== - -FString M_GetSavegamesPath() -{ - return NicePath("~/" GAME_DIR); -} - -#endif diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 6c455b94c..109c94dfd 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1807,6 +1807,7 @@ DSPLYMNU_STILLBOB = "View bob amount while not moving"; HUDMNU_TITLE = "HUD Options"; HUDMNU_ALTHUD = "Alternative HUD"; HUDMNU_MESSAGE = "Message Options"; +HUDMNU_UISCALE = "User interface scale"; HUDMNU_CROSSHAIR = "Default Crosshair"; HUDMNU_FORCECROSSHAIR = "Force default crosshair"; HUDMNU_GROWCROSSHAIR = "Grow crosshair when picking up items"; @@ -1946,6 +1947,7 @@ MSGMNU_SHOWMESSAGES = "Show messages"; MSGMNU_SHOWOBITUARIES = "Show obituaries"; MSGMNU_SHOWSECRETS = "Show secret notifications"; MSGMNU_SCALETEXT = "Scale text in high res"; +MSGMNU_SCALECONSOLE = "Scale console"; MSGMNU_MESSAGELEVEL = "Minimum message level"; MSGMNU_CENTERMESSAGES = "Center messages"; MSGMNU_MESSAGECOLORS = "Message Colors"; @@ -2237,6 +2239,7 @@ OPTVAL_ANIMATED = "Animated"; OPTVAL_ROTATED = "Rotated"; OPTVAL_MAPDEFINEDCOLORSONLY = "Map defined colors only"; OPTVAL_DOUBLE = "Double"; +OPTVAL_TRIPLE = "Triple"; OPTVAL_QUADRUPLE = "Quadruple"; OPTVAL_ITEMPICKUP = "Item Pickup"; OPTVAL_OBITUARIES = "Obituaries"; @@ -2594,7 +2597,6 @@ GLTEXMNU_RESIZETEX = "Resize textures"; GLTEXMNU_RESIZESPR = "Resize sprites"; GLTEXMNU_RESIZEFNT = "Resize fonts"; GLTEXMNU_PRECACHETEX = "Precache GL textures"; -GLTEXMNU_CAMTEXOFFSCR = "Camera textures offscreen"; GLTEXMNU_TRIMSPREDGE = "Trim sprite edges"; GLTEXMNU_SORTDRAWLIST = "Sort draw lists by texture"; @@ -2605,9 +2607,6 @@ GLLIGHTMNU_LIGHTDEFS = "Enable light definitions"; GLLIGHTMNU_CLIPLIGHTS = "Clip lights"; GLLIGHTMNU_LIGHTSPRITES = "Lights affect sprites"; GLLIGHTMNU_LIGHTPARTICLES = "Lights affect particles"; -GLLIGHTMNU_FORCEADDITIVE = "Force additive lighting"; -GLLIGHTMNU_LIGHTINTENSITY = "Light intensity"; -GLLIGHTMNU_LIGHTSIZE = "Light size"; GLLIGHTMNU_LIGHTMATH = "Light quality"; // OpenGL Preferences diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 33cfd95ad..dcdaa90b3 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -768,6 +768,8 @@ OptionMenu "HUDOptions" Submenu "$HUDMNU_ALTHUD", "AltHudOptions" Submenu "$HUDMNU_MESSAGE", "MessageOptions" StaticText " " + Slider "$HUDMNU_UISCALE", "uiscale", 0.0, 8.0, 1.0, 0 + StaticText " " Option "$HUDMNU_CROSSHAIR", "crosshair", "Crosshairs" Option "$HUDMNU_FORCECROSSHAIR", "crosshairforce", "OnOff" Option "$HUDMNU_GROWCROSSHAIR", "crosshairgrow", "OnOff" @@ -802,6 +804,7 @@ OptionValue "AMCoordinates" OptionValue "AltHUDScale" { 0, "$OPTVAL_OFF" + 4, "$OPTVAL_ON" 1, "$OPTVAL_SCALETO640X400" 2, "$OPTVAL_PIXELDOUBLE" 3, "$OPTVAL_PIXELQUADRUPLE" @@ -1124,6 +1127,15 @@ OptionValue ScaleValues 3, "$OPTVAL_QUADRUPLE" } +OptionValue ConsoleScaleValues +{ + 1, "$OPTVAL_OFF" + 0, "$OPTVAL_ON" + 2, "$OPTVAL_DOUBLE" + 3, "$OPTVAL_TRIPLE" + 4, "$OPTVAL_QUADRUPLE" +} + OptionValue MessageLevels { 0.0, "$OPTVAL_ITEMPICKUP" @@ -1147,6 +1159,7 @@ OptionMenu MessageOptions Option "$MSGMNU_SHOWOBITUARIES", "show_obituaries", "OnOff" Option "$MSGMNU_SHOWSECRETS", "cl_showsecretmessage", "OnOff" Option "$MSGMNU_SCALETEXT", "con_scaletext", "ScaleValues" + Option "$MSGMNU_SCALECONSOLE", "con_scale", "ConsoleScaleValues" Option "$MSGMNU_MESSAGELEVEL", "msg", "MessageLevels" Option "$MSGMNU_DEVELOPER", "developer", "DevMessageLevels" Option "$MSGMNU_CENTERMESSAGES", "con_centernotify", "OnOff" diff --git a/wadsrc/static/menudef.z b/wadsrc/static/menudef.z index aae2ae76d..7a4f3b7fe 100644 --- a/wadsrc/static/menudef.z +++ b/wadsrc/static/menudef.z @@ -141,6 +141,12 @@ OptionValue "HqResizeModes" 7, "$OPTVAL_HQ2XMMX" 8, "$OPTVAL_HQ3XMMX" 9, "$OPTVAL_HQ4XMMX" + 10, "xBRZ 2x" + 11, "xBRZ 3x" + 12, "xBRZ 4x" + 13, "xBRZ_old 2x" + 14, "xBRZ_old 3x" + 15, "xBRZ_old 4x" } OptionValue "FogMode" @@ -186,7 +192,6 @@ OptionMenu "GLTextureGLOptions" Option "$GLTEXMNU_RESIZESPR", gl_texture_hqresize_sprites, "OnOff" Option "$GLTEXMNU_RESIZEFNT", gl_texture_hqresize_fonts, "OnOff" Option "$GLTEXMNU_PRECACHETEX", gl_precache, "YesNo" - Option "$GLTEXMNU_CAMTEXOFFSCR", gl_usefb, "OnOff" Option "$GLTEXMNU_TRIMSPREDGE", gl_trimsprites, "OnOff" Option "$GLTEXMNU_SORTDRAWLIST", gl_sort_textures, "YesNo" } @@ -199,10 +204,7 @@ OptionMenu "GLLightOptions" Option "$GLLIGHTMNU_CLIPLIGHTS", gl_lights_checkside, "YesNo" Option "$GLLIGHTMNU_LIGHTSPRITES", gl_light_sprites, "YesNo" Option "$GLLIGHTMNU_LIGHTPARTICLES", gl_light_particles, "YesNo" - Option "$GLLIGHTMNU_FORCEADDITIVE", gl_lights_additive, "YesNo" Option "$GLLIGHTMNU_LIGHTMATH", gl_light_math, "LightMathModes" - Slider "$GLLIGHTMNU_LIGHTINTENSITY", gl_lights_intensity, 0.0, 1.0, 0.1 - Slider "$GLLIGHTMNU_LIGHTSIZE", gl_lights_size, 0.0, 2.0, 0.1 } OptionMenu "GLPrefOptions" diff --git a/wadsrc/static/shaders/glsl/tonemap.fp b/wadsrc/static/shaders/glsl/tonemap.fp index c33349a38..d6574b295 100644 --- a/wadsrc/static/shaders/glsl/tonemap.fp +++ b/wadsrc/static/shaders/glsl/tonemap.fp @@ -69,15 +69,15 @@ uniform sampler2D PaletteLUT; vec3 Tonemap(vec3 color) { - ivec3 c = ivec3(clamp(color.rgb, vec3(0.0), vec3(1.0)) * 255.0 + 0.5); - int index = ((c.r >> 2) * 64 + (c.g >> 2)) * 64 + (c.b >> 2); + ivec3 c = ivec3(clamp(color.rgb, vec3(0.0), vec3(1.0)) * 63.0 + 0.5); + int index = (c.r * 64 + c.g) * 64 + c.b; int tx = index % 512; int ty = index / 512; return texelFetch(PaletteLUT, ivec2(tx, ty), 0).rgb; } #else -#error "Tonemap mode define is missing" +#error Tonemap mode define is missing #endif void main()