diff --git a/.gitignore b/.gitignore index 1b078ed63..a001e38de 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ /build_vc2015-32 /build_vc2015-64 /build +/llvm +/src/r_drawersasm.obj +/src/r_drawersasm.o diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..61a770769 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,73 @@ +language: c++ +dist: trusty +sudo: required + +matrix: + include: + - os: osx + osx_image: xcode8.2 + env: + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Debug -DCMAKE_OSX_DEPLOYMENT_TARGET=10.7" + + - os: osx + osx_image: xcode8.2 + env: + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_DEPLOYMENT_TARGET=10.7 -DFORCE_INTERNAL_ZLIB=YES -DFORCE_INTERNAL_JPEG=YES -DFORCE_INTERNAL_BZIP2=YES -DFORCE_INTERNAL_GME=YES" + + - os: linux + compiler: gcc + env: + - GCC_VERSION=5 + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=Release" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - kubuntu-backports + packages: + - g++-5 + - libsdl2-dev + - libgme-dev + - libopenal-dev + - libmpg123-dev + - libsndfile-dev + - libfluidsynth-dev + - libgtk-3-dev + + - os: linux + compiler: clang + env: + - CLANG_VERSION=3.9 + - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=RelWithDebInfo -DDYN_OPENAL=NO -DDYN_FLUIDSYNTH=NO" + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-3.9 + - kubuntu-backports + packages: + - clang-3.9 + - libstdc++-5-dev + - libsdl2-dev + - libgme-dev + - libopenal-dev + - libmpg123-dev + - libsndfile-dev + - libfluidsynth-dev + - libgtk-3-dev + +before_install: + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; brew install mpg123 libsndfile fluidsynth; fi + - if [ -n "$GCC_VERSION" ]; then export CC="gcc-${GCC_VERSION}" CXX="g++-${GCC_VERSION}"; fi + - if [ -n "$CLANG_VERSION" ]; then export CC="clang-${CLANG_VERSION}" CXX="clang++-${CLANG_VERSION}"; fi + - $CC --version + - $CXX --version + +script: + - mkdir build + - cd build + - cmake ${CMAKE_OPTIONS} .. + - make -j2 + +notifications: + email: false diff --git a/CMakeLists.txt b/CMakeLists.txt index d8aa8df99..e28a4de91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required( VERSION 2.8.7 ) -project(GZDoom) +project(QZDoom) if( COMMAND cmake_policy ) if( POLICY CMP0011 ) @@ -122,7 +122,7 @@ IF( NOT CMAKE_BUILD_TYPE ) ENDIF() set( ZDOOM_OUTPUT_DIR ${CMAKE_BINARY_DIR} CACHE PATH "Directory where zdoom.pk3 and the executable will be created." ) -set( ZDOOM_EXE_NAME "gzdoom" CACHE FILEPATH "Name of the executable to create" ) +set( ZDOOM_EXE_NAME "qzdoom" CACHE FILEPATH "Name of the executable to create" ) if( MSVC ) # Allow the user to use ZDOOM_OUTPUT_DIR as a single release point. # Use zdoom, zdoomd, zdoom64, and zdoomd64 for the binary names @@ -173,20 +173,23 @@ if( MSVC ) # Disable run-time type information set( ALL_C_FLAGS "/GF /Gy /GR-" ) - if( CMAKE_SIZEOF_VOID_P MATCHES "4") - # SSE2 option (to allow x87 in 32 bit and disallow extended feature sets which have not yet been checked for precision) - option (ZDOOM_USE_SSE2 "Use SSE2 instruction set") - if (ZDOOM_USE_SSE2) - set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") - else () - if (MSVC_VERSION GREATER 1699) - # On Visual C++ 2012 and later SSE2 is the default, so we need to switch it off explicitly - set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:IA32") - endif () - endif () - else() - set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") - endif() + # Use SSE 2 as minimum always as the true color drawers needs it for __vectorcall + set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") + +# if( CMAKE_SIZEOF_VOID_P MATCHES "4") +# # SSE2 option (to allow x87 in 32 bit and disallow extended feature sets which have not yet been checked for precision) +# option (ZDOOM_USE_SSE2 "Use SSE2 instruction set") +# if (ZDOOM_USE_SSE2) +# set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") +# else () +# if (MSVC_VERSION GREATER 1699) +# # On Visual C++ 2012 and later SSE2 is the default, so we need to switch it off explicitly +# set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:IA32") +# endif () +# endif () +# else() +# set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") +# endif() # Avoid CRT DLL dependancies in release builds, optionally generate assembly output for checking crash locations. option( ZDOOM_GENERATE_ASM "Generate assembly output." OFF ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d8e818d10..65cdc7e1e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -543,6 +543,7 @@ if( HAVE_MMX ) PROPERTIES COMPILE_FLAGS "-mmmx" ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) endif( HAVE_MMX ) + add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.c ${CMAKE_CURRENT_BINARY_DIR}/xlat_parser.h COMMAND lemon -C${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y DEPENDS lemon ${CMAKE_CURRENT_SOURCE_DIR}/xlat/xlat_parser.y ) @@ -605,6 +606,19 @@ file( GLOB HEADER_FILES scripting/zscript/*.h scripting/vm/*.h xlat/*.h + swrenderer/*.h + swrenderer/drawers/*.h + swrenderer/scene/*.h + swrenderer/segments/*.h + swrenderer/line/*.h + swrenderer/plane/*.h + swrenderer/things/*.h + swrenderer/viewport/*.h + polyrenderer/*.h + polyrenderer/math/*.h + polyrenderer/drawers/*.h + polyrenderer/drawers/*.php + polyrenderer/scene/*.h gl/*.h gl/api/*.h gl/data/*.h @@ -639,16 +653,67 @@ set( NOT_COMPILED_SOURCE_FILES ) set( FASTMATH_PCH_SOURCES - r_swrenderer.cpp - r_3dfloors.cpp - r_bsp.cpp - r_draw.cpp - r_drawt.cpp - r_main.cpp - r_plane.cpp - r_segs.cpp + swrenderer/r_swcanvas.cpp + swrenderer/r_swrenderer.cpp + swrenderer/r_memory.cpp + swrenderer/r_renderthread.cpp + swrenderer/drawers/r_draw.cpp + swrenderer/drawers/r_draw_pal.cpp + swrenderer/drawers/r_draw_rgba.cpp + swrenderer/drawers/r_thread.cpp + swrenderer/scene/r_3dfloors.cpp + swrenderer/scene/r_light.cpp + swrenderer/scene/r_opaque_pass.cpp + swrenderer/scene/r_portal.cpp + swrenderer/scene/r_scene.cpp + swrenderer/scene/r_translucent_pass.cpp + swrenderer/viewport/r_drawerargs.cpp + swrenderer/viewport/r_skydrawer.cpp + swrenderer/viewport/r_spandrawer.cpp + swrenderer/viewport/r_spritedrawer.cpp + swrenderer/viewport/r_viewport.cpp + swrenderer/viewport/r_walldrawer.cpp + swrenderer/line/r_line.cpp + swrenderer/line/r_walldraw.cpp + swrenderer/line/r_wallsetup.cpp + swrenderer/line/r_fogboundary.cpp + swrenderer/line/r_renderdrawsegment.cpp + swrenderer/segments/r_clipsegment.cpp + swrenderer/segments/r_drawsegment.cpp + swrenderer/segments/r_portalsegment.cpp + swrenderer/things/r_visiblesprite.cpp + swrenderer/things/r_visiblespritelist.cpp + swrenderer/things/r_voxel.cpp + swrenderer/things/r_particle.cpp + swrenderer/things/r_playersprite.cpp + swrenderer/things/r_sprite.cpp + swrenderer/things/r_wallsprite.cpp + swrenderer/things/r_decal.cpp + swrenderer/plane/r_visibleplane.cpp + swrenderer/plane/r_visibleplanelist.cpp + swrenderer/plane/r_skyplane.cpp + swrenderer/plane/r_planerenderer.cpp + swrenderer/plane/r_flatplane.cpp + swrenderer/plane/r_slopeplane.cpp + polyrenderer/poly_renderer.cpp + polyrenderer/scene/poly_scene.cpp + polyrenderer/scene/poly_portal.cpp + polyrenderer/scene/poly_cull.cpp + polyrenderer/scene/poly_decal.cpp + polyrenderer/scene/poly_particle.cpp + polyrenderer/scene/poly_plane.cpp + polyrenderer/scene/poly_playersprite.cpp + polyrenderer/scene/poly_wall.cpp + polyrenderer/scene/poly_wallsprite.cpp + polyrenderer/scene/poly_sprite.cpp + polyrenderer/scene/poly_sky.cpp + polyrenderer/drawers/poly_triangle.cpp + polyrenderer/drawers/poly_buffer.cpp + polyrenderer/drawers/poly_draw_args.cpp + polyrenderer/drawers/screen_triangle.cpp + polyrenderer/math/tri_matrix.cpp + polyrenderer/math/poly_intersection.cpp r_sky.cpp - r_things.cpp s_advsound.cpp s_environment.cpp s_playlist.cpp @@ -789,6 +854,8 @@ set( FASTMATH_SOURCES gl/shaders/gl_fxaashader.cpp gl/system/gl_interface.cpp gl/system/gl_framebuffer.cpp + gl/system/gl_swframebuffer.cpp + gl/system/gl_swwipe.cpp gl/system/gl_debug.cpp gl/system/gl_menu.cpp gl/system/gl_wipe.cpp @@ -1060,6 +1127,10 @@ set_source_files_properties( sc_man.cpp PROPERTIES OBJECT_DEPENDS "${CMAKE_CURRE set_source_files_properties( ${NOT_COMPILED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE ) if ( WIN32 ) set_source_files_properties( win32/fb_d3d9.cpp win32/fb_d3d9_wipe.cpp PROPERTIES COMPILE_FLAGS ${ZD_FASTMATH_FLAG} ) + + # Supress C4244: 'initializing': conversion from '__int64' to 'unsigned int', possible loss of data + # For some reason using #pragma warning(disable: 4244) is not working.. + set_source_files_properties( ${PCH_SOURCES} PROPERTIES COMPILE_FLAGS /wd4244 ) endif() @@ -1069,6 +1140,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") endif() target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma ) + include_directories( . g_statusbar g_shared @@ -1179,8 +1251,18 @@ source_group("OpenGL Renderer\\Shaders" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOU source_group("OpenGL Renderer\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/system/.+") source_group("OpenGL Renderer\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/textures/.+") source_group("OpenGL Renderer\\Utilities" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/utility/.+") -source_group("Render Core\\Render Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_.+\\.h$") -source_group("Render Core\\Render Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_.+\\.cpp$") +source_group("Software Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/.+") +source_group("Software Renderer\\Drawers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/drawers/.+") +source_group("Software Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/scene/.+") +source_group("Software Renderer\\Segments" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/segments/.+") +source_group("Software Renderer\\Line" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/line/.+") +source_group("Software Renderer\\Plane" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/plane/.+") +source_group("Software Renderer\\Things" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/things/.+") +source_group("Software Renderer\\Viewport" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/swrenderer/viewport/.+") +source_group("Poly Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/polyrenderer/.+") +source_group("Poly Renderer\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/polyrenderer/math/.+") +source_group("Poly Renderer\\Drawers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/polyrenderer/drawers/.+") +source_group("Poly Renderer\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/polyrenderer/scene/.+") source_group("Render Data\\Resource Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/.+\\.h$") source_group("Render Data\\Resource Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/r_data/.+\\.cpp$") source_group("Render Data\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/textures/.+") diff --git a/src/am_map.cpp b/src/am_map.cpp index 15f3b5586..2123c0b3d 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -1933,7 +1933,7 @@ void AM_drawSubsectors() points[j].Y = float(f_y + (f_h - (pt.y - m_y) * scale)); } // For lighting and texture determination - sector_t *sec = Renderer->FakeFlat(subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight, false); + sector_t *sec = Renderer->FakeFlat(subsectors[i].render_sector, &tempsec, &floorlight, &ceilinglight); // Find texture origin. originpt.x = -sec->GetXOffset(sector_t::floor); originpt.y = sec->GetYOffset(sector_t::floor); diff --git a/src/c_console.cpp b/src/c_console.cpp index 83e61d3fe..a28d6c19e 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -162,10 +162,14 @@ int active_con_scale() int scale = con_scale; if (scale <= 0) { - scale = CleanXfac - 1; - if (scale <= 0) + scale = uiscale; + if (scale == 0) { - scale = 1; + scale = CleanXfac - 1; + if (scale <= 0) + { + scale = 1; + } } } return scale; diff --git a/src/compatibility.cpp b/src/compatibility.cpp index 37531d40f..951f9e473 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -114,6 +114,7 @@ static FCompatOption Options[] = { "linkfrozenprops", BCOMPATF_LINKFROZENPROPS, SLOT_BCOMPAT }, { "floatbob", BCOMPATF_FLOATBOB, SLOT_BCOMPAT }, { "noslopeid", BCOMPATF_NOSLOPEID, SLOT_BCOMPAT }, + { "clipmidtex", BCOMPATF_CLIPMIDTEX, SLOT_BCOMPAT }, // list copied from g_mapinfo.cpp { "shorttex", COMPATF_SHORTTEX, SLOT_COMPAT }, @@ -152,7 +153,6 @@ static FCompatOption Options[] = { "multiexit", COMPATF2_MULTIEXIT, SLOT_COMPAT2 }, { "teleport", COMPATF2_TELEPORT, SLOT_COMPAT2 }, { "disablepushwindowcheck", COMPATF2_PUSHWINDOW, SLOT_COMPAT2 }, - { NULL, 0, 0 } }; diff --git a/src/d_main.cpp b/src/d_main.cpp index 44e9e565e..07f213e45 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -698,8 +698,6 @@ void D_Display () } } - RenderTarget = screen; - // change the view size if needed if (setsizeneeded && StatusBar != NULL) { @@ -974,7 +972,6 @@ void D_ErrorCleanup () menuactive = MENU_Off; } insave = false; - Renderer->ErrorCleanup(); } //========================================================================== diff --git a/src/d_net.cpp b/src/d_net.cpp index b78366cf4..5b9e7860a 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2677,7 +2677,8 @@ void Net_DoCommand (int type, BYTE **stream, int player) int arg[3] = { 0, 0, 0 }; for (int i = 0; i < 3; i++) arg[i] = ReadLong(stream); - E_Console(player, s, arg[0], arg[1], arg[2]); + bool manual = !!ReadByte(stream); + E_Console(player, s, arg[0], arg[1], arg[2], manual); } break; @@ -2728,7 +2729,7 @@ void Net_SkipCommand (int type, BYTE **stream) break; case DEM_NETEVENT: - skip = strlen((char *)(*stream)) + 14; + skip = strlen((char *)(*stream)) + 15; break; case DEM_SUMMON2: diff --git a/src/doomdef.h b/src/doomdef.h index ed7fe51f7..57a0c8f5c 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -360,6 +360,7 @@ enum BCOMPATF_LINKFROZENPROPS = 1 << 6, // Clearing PROP_TOTALLYFROZEN or PROP_FROZEN also clears the other BCOMPATF_FLOATBOB = 1 << 8, // Use Hexen's original method of preventing floatbobbing items from falling down BCOMPATF_NOSLOPEID = 1 << 9, // disable line IDs on slopes. + BCOMPATF_CLIPMIDTEX = 1 << 10, // Always Clip midtex's in the software renderer (required to run certain GZDoom maps) }; // phares 3/20/98: diff --git a/src/events.cpp b/src/events.cpp index 9946a1059..5faee810a 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -117,7 +117,7 @@ bool E_UnregisterHandler(DStaticEventHandler* handler) return true; } -bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3) +bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3, bool manual) { if (gamestate != GS_LEVEL) return false; @@ -128,6 +128,7 @@ bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3) Net_WriteLong(arg1); Net_WriteLong(arg2); Net_WriteLong(arg3); + Net_WriteByte(manual); return true; } @@ -433,10 +434,10 @@ bool E_Responder(event_t* ev) return false; } -void E_Console(int player, FString name, int arg1, int arg2, int arg3) +void E_Console(int player, FString name, int arg1, int arg2, int arg3, bool manual) { for (DStaticEventHandler* handler = E_FirstEventHandler; handler; handler = handler->next) - handler->ConsoleProcess(player, name, arg1, arg2, arg3); + handler->ConsoleProcess(player, name, arg1, arg2, arg3, manual); } bool E_CheckUiProcessors() @@ -472,57 +473,51 @@ IMPLEMENT_POINTER(prev) IMPLEMENT_POINTERS_END IMPLEMENT_CLASS(DEventHandler, false, false); -IMPLEMENT_CLASS(DBaseEvent, false, false) -IMPLEMENT_CLASS(DRenderEvent, false, false) -IMPLEMENT_CLASS(DWorldEvent, false, false) -IMPLEMENT_CLASS(DPlayerEvent, false, false) -IMPLEMENT_CLASS(DUiEvent, false, false) -IMPLEMENT_CLASS(DInputEvent, false, false) -IMPLEMENT_CLASS(DConsoleEvent, false, false) DEFINE_FIELD_X(StaticEventHandler, DStaticEventHandler, Order); DEFINE_FIELD_X(StaticEventHandler, DStaticEventHandler, IsUiProcessor); DEFINE_FIELD_X(StaticEventHandler, DStaticEventHandler, RequireMouse); -DEFINE_FIELD_X(RenderEvent, DRenderEvent, ViewPos); -DEFINE_FIELD_X(RenderEvent, DRenderEvent, ViewAngle); -DEFINE_FIELD_X(RenderEvent, DRenderEvent, ViewPitch); -DEFINE_FIELD_X(RenderEvent, DRenderEvent, ViewRoll); -DEFINE_FIELD_X(RenderEvent, DRenderEvent, FracTic); -DEFINE_FIELD_X(RenderEvent, DRenderEvent, Camera); +DEFINE_FIELD_X(RenderEvent, FRenderEvent, ViewPos); +DEFINE_FIELD_X(RenderEvent, FRenderEvent, ViewAngle); +DEFINE_FIELD_X(RenderEvent, FRenderEvent, ViewPitch); +DEFINE_FIELD_X(RenderEvent, FRenderEvent, ViewRoll); +DEFINE_FIELD_X(RenderEvent, FRenderEvent, FracTic); +DEFINE_FIELD_X(RenderEvent, FRenderEvent, Camera); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, IsSaveGame); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, IsReopen); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, Thing); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, Inflictor); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, Damage); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, DamageSource); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, DamageType); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, DamageFlags); -DEFINE_FIELD_X(WorldEvent, DWorldEvent, DamageAngle); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, IsSaveGame); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, IsReopen); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, Thing); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, Inflictor); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, Damage); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageSource); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageType); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageFlags); +DEFINE_FIELD_X(WorldEvent, FWorldEvent, DamageAngle); -DEFINE_FIELD_X(PlayerEvent, DPlayerEvent, PlayerNumber); -DEFINE_FIELD_X(PlayerEvent, DPlayerEvent, IsReturn); +DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, PlayerNumber); +DEFINE_FIELD_X(PlayerEvent, FPlayerEvent, IsReturn); -DEFINE_FIELD_X(UiEvent, DUiEvent, Type); -DEFINE_FIELD_X(UiEvent, DUiEvent, KeyString); -DEFINE_FIELD_X(UiEvent, DUiEvent, KeyChar); -DEFINE_FIELD_X(UiEvent, DUiEvent, MouseX); -DEFINE_FIELD_X(UiEvent, DUiEvent, MouseY); -DEFINE_FIELD_X(UiEvent, DUiEvent, IsShift); -DEFINE_FIELD_X(UiEvent, DUiEvent, IsAlt); -DEFINE_FIELD_X(UiEvent, DUiEvent, IsCtrl); +DEFINE_FIELD_X(UiEvent, FUiEvent, Type); +DEFINE_FIELD_X(UiEvent, FUiEvent, KeyString); +DEFINE_FIELD_X(UiEvent, FUiEvent, KeyChar); +DEFINE_FIELD_X(UiEvent, FUiEvent, MouseX); +DEFINE_FIELD_X(UiEvent, FUiEvent, MouseY); +DEFINE_FIELD_X(UiEvent, FUiEvent, IsShift); +DEFINE_FIELD_X(UiEvent, FUiEvent, IsAlt); +DEFINE_FIELD_X(UiEvent, FUiEvent, IsCtrl); -DEFINE_FIELD_X(InputEvent, DInputEvent, Type); -DEFINE_FIELD_X(InputEvent, DInputEvent, KeyScan); -DEFINE_FIELD_X(InputEvent, DInputEvent, KeyString); -DEFINE_FIELD_X(InputEvent, DInputEvent, KeyChar); -DEFINE_FIELD_X(InputEvent, DInputEvent, MouseX); -DEFINE_FIELD_X(InputEvent, DInputEvent, MouseY); +DEFINE_FIELD_X(InputEvent, FInputEvent, Type); +DEFINE_FIELD_X(InputEvent, FInputEvent, KeyScan); +DEFINE_FIELD_X(InputEvent, FInputEvent, KeyString); +DEFINE_FIELD_X(InputEvent, FInputEvent, KeyChar); +DEFINE_FIELD_X(InputEvent, FInputEvent, MouseX); +DEFINE_FIELD_X(InputEvent, FInputEvent, MouseY); -DEFINE_FIELD_X(ConsoleEvent, DConsoleEvent, Player) -DEFINE_FIELD_X(ConsoleEvent, DConsoleEvent, Name) -DEFINE_FIELD_X(ConsoleEvent, DConsoleEvent, Args) +DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, Player) +DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, Name) +DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, Args) +DEFINE_FIELD_X(ConsoleEvent, FConsoleEvent, IsManual) DEFINE_ACTION_FUNCTION(DStaticEventHandler, SetOrder) { @@ -545,7 +540,7 @@ DEFINE_ACTION_FUNCTION(DEventHandler, SendNetworkEvent) PARAM_INT(arg3); // - ACTION_RETURN_BOOL(E_SendNetworkEvent(name, arg1, arg2, arg3)); + ACTION_RETURN_BOOL(E_SendNetworkEvent(name, arg1, arg2, arg3, false)); } DEFINE_ACTION_FUNCTION(DEventHandler, Create) @@ -717,19 +712,12 @@ void DStaticEventHandler::OnUnregister() } } -static DWorldEvent* E_SetupWorldEvent() +static FWorldEvent E_SetupWorldEvent() { - static DWorldEvent* e = nullptr; - if (!e) e = (DWorldEvent*)RUNTIME_CLASS(DWorldEvent)->CreateNew(); - e->IsSaveGame = savegamerestore; - e->IsReopen = level.FromSnapshot && !savegamerestore; // each one by itself isnt helpful, but with hub load we have savegamerestore==0 and level.FromSnapshot==1. - e->Thing = nullptr; - e->Inflictor = nullptr; - e->Damage = 0; - e->DamageAngle = 0.0; - e->DamageFlags = 0; - e->DamageSource = 0; - e->DamageType = NAME_None; + FWorldEvent e; + e.IsSaveGame = savegamerestore; + e.IsReopen = level.FromSnapshot && !savegamerestore; // each one by itself isnt helpful, but with hub load we have savegamerestore==0 and level.FromSnapshot==1. + e.DamageAngle = 0.0; return e; } @@ -740,8 +728,8 @@ void DStaticEventHandler::WorldLoaded() // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldLoaded_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -753,8 +741,8 @@ void DStaticEventHandler::WorldUnloaded() // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldUnloaded_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -766,9 +754,9 @@ void DStaticEventHandler::WorldThingSpawned(AActor* actor) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldThingSpawned_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - e->Thing = actor; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + e.Thing = actor; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -780,10 +768,10 @@ void DStaticEventHandler::WorldThingDied(AActor* actor, AActor* inflictor) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldThingDied_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - e->Thing = actor; - e->Inflictor = inflictor; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + e.Thing = actor; + e.Inflictor = inflictor; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -795,9 +783,9 @@ void DStaticEventHandler::WorldThingRevived(AActor* actor) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldThingRevived_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - e->Thing = actor; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + e.Thing = actor; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -809,14 +797,14 @@ void DStaticEventHandler::WorldThingDamaged(AActor* actor, AActor* inflictor, AA // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldThingDamaged_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - e->Thing = actor; - e->Damage = damage; - e->DamageSource = source; - e->DamageType = mod; - e->DamageFlags = flags; - e->DamageAngle = angle; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + e.Thing = actor; + e.Damage = damage; + e.DamageSource = source; + e.DamageType = mod; + e.DamageFlags = flags; + e.DamageAngle = angle; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -828,9 +816,9 @@ void DStaticEventHandler::WorldThingDestroyed(AActor* actor) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldThingDestroyed_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - e->Thing = actor; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + e.Thing = actor; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -842,8 +830,8 @@ void DStaticEventHandler::WorldLightning() // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldLightning_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -855,22 +843,21 @@ void DStaticEventHandler::WorldTick() // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_WorldTick_VMPtr) return; - DWorldEvent* e = E_SetupWorldEvent(); - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FWorldEvent e = E_SetupWorldEvent(); + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } -static DRenderEvent* E_SetupRenderEvent() +static FRenderEvent E_SetupRenderEvent() { - static DRenderEvent* e = nullptr; - if (!e) e = (DRenderEvent*)RUNTIME_CLASS(DRenderEvent)->CreateNew(); - e->ViewPos = ::ViewPos; - e->ViewAngle = ::ViewAngle; - e->ViewPitch = ::ViewPitch; - e->ViewRoll = ::ViewRoll; - e->FracTic = ::r_TicFracF; - e->Camera = ::camera; + FRenderEvent e; + e.ViewPos = ::ViewPos; + e.ViewAngle = ::ViewAngle; + e.ViewPitch = ::ViewPitch; + e.ViewRoll = ::ViewRoll; + e.FracTic = ::r_TicFracF; + e.Camera = ::camera; return e; } @@ -881,8 +868,8 @@ void DStaticEventHandler::RenderFrame() // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_RenderFrame_VMPtr) return; - DRenderEvent* e = E_SetupRenderEvent(); - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FRenderEvent e = E_SetupRenderEvent(); + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -894,21 +881,12 @@ void DStaticEventHandler::RenderOverlay() // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_RenderOverlay_VMPtr) return; - DRenderEvent* e = E_SetupRenderEvent(); - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FRenderEvent e = E_SetupRenderEvent(); + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } -static DPlayerEvent* E_SetupPlayerEvent() -{ - static DPlayerEvent* e = nullptr; - if (!e) e = (DPlayerEvent*)RUNTIME_CLASS(DPlayerEvent)->CreateNew(); - e->PlayerNumber = -1; - e->IsReturn = false; - return e; -} - void DStaticEventHandler::PlayerEntered(int num, bool fromhub) { IFVIRTUAL(DStaticEventHandler, PlayerEntered) @@ -916,10 +894,8 @@ void DStaticEventHandler::PlayerEntered(int num, bool fromhub) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_PlayerEntered_VMPtr) return; - DPlayerEvent* e = E_SetupPlayerEvent(); - e->IsReturn = fromhub; - e->PlayerNumber = num; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FPlayerEvent e = { num, fromhub }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -931,9 +907,8 @@ void DStaticEventHandler::PlayerRespawned(int num) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_PlayerRespawned_VMPtr) return; - DPlayerEvent* e = E_SetupPlayerEvent(); - e->PlayerNumber = num; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FPlayerEvent e = { num, false }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -945,9 +920,8 @@ void DStaticEventHandler::PlayerDied(int num) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_PlayerDied_VMPtr) return; - DPlayerEvent* e = E_SetupPlayerEvent(); - e->PlayerNumber = num; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FPlayerEvent e = { num, false }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -959,25 +933,50 @@ void DStaticEventHandler::PlayerDisconnected(int num) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_PlayerDisconnected_VMPtr) return; - DPlayerEvent* e = E_SetupPlayerEvent(); - e->PlayerNumber = num; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + FPlayerEvent e = { num, false }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } -static DUiEvent* E_SetupUiEvent() +FUiEvent::FUiEvent(event_t *ev) { - static DUiEvent* e = nullptr; - if (!e) e = (DUiEvent*)RUNTIME_CLASS(DUiEvent)->CreateNew(); - e->Type = EV_GUI_None; - e->IsShift = false; - e->IsAlt = false; - e->IsCtrl = false; - e->MouseX = e->MouseY = 0; - e->KeyChar = 0; - e->KeyString = ""; - return e; + Type = (EGUIEvent)ev->subtype; + KeyChar = 0; + IsShift = false; + IsAlt = false; + IsCtrl = false; + MouseX = 0; + MouseY = 0; + // we don't want the modders to remember what weird fields mean what for what events. + switch (ev->subtype) + { + case EV_GUI_None: + break; + case EV_GUI_KeyDown: + case EV_GUI_KeyRepeat: + case EV_GUI_KeyUp: + KeyChar = ev->data1; + KeyString = FString(char(ev->data1)); + IsShift = !!(ev->data3 & GKM_SHIFT); + IsAlt = !!(ev->data3 & GKM_ALT); + IsCtrl = !!(ev->data3 & GKM_CTRL); + break; + case EV_GUI_Char: + KeyChar = ev->data1; + KeyString = FString(char(ev->data1)); + IsAlt = !!ev->data2; // only true for Win32, not sure about SDL + break; + default: // mouse event + // note: SDL input doesn't seem to provide these at all + //Printf("Mouse data: %d, %d, %d, %d\n", ev->x, ev->y, ev->data1, ev->data2); + MouseX = ev->data1; + MouseY = ev->data2; + IsShift = !!(ev->data3 & GKM_SHIFT); + IsAlt = !!(ev->data3 & GKM_ALT); + IsCtrl = !!(ev->data3 & GKM_CTRL); + break; + } } bool DStaticEventHandler::UiProcess(event_t* ev) @@ -987,43 +986,11 @@ bool DStaticEventHandler::UiProcess(event_t* ev) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_UiProcess_VMPtr) return false; - DUiEvent* e = E_SetupUiEvent(); - - // - e->Type = (EGUIEvent)ev->subtype; - // we don't want the modders to remember what weird fields mean what for what events. - switch (e->Type) - { - case EV_GUI_None: - break; - case EV_GUI_KeyDown: - case EV_GUI_KeyRepeat: - case EV_GUI_KeyUp: - e->KeyChar = ev->data1; - e->KeyString.Format("%c", e->KeyChar); - e->IsShift = !!(ev->data3 & GKM_SHIFT); - e->IsAlt = !!(ev->data3 & GKM_ALT); - e->IsCtrl = !!(ev->data3 & GKM_CTRL); - break; - case EV_GUI_Char: - e->KeyChar = ev->data1; - e->KeyString.Format("%c", e->KeyChar); - e->IsAlt = !!ev->data2; // only true for Win32, not sure about SDL - break; - default: // mouse event - // note: SDL input doesn't seem to provide these at all - //Printf("Mouse data: %d, %d, %d, %d\n", ev->x, ev->y, ev->data1, ev->data2); - e->MouseX = ev->data1; - e->MouseY = ev->data2; - e->IsShift = !!(ev->data3 & GKM_SHIFT); - e->IsAlt = !!(ev->data3 & GKM_ALT); - e->IsCtrl = !!(ev->data3 & GKM_CTRL); - break; - } + FUiEvent e = ev; int processed; VMReturn results[1] = { &processed }; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, results, 1, nullptr); return !!processed; } @@ -1031,16 +998,31 @@ bool DStaticEventHandler::UiProcess(event_t* ev) return false; } -static DInputEvent* E_SetupInputEvent() +FInputEvent::FInputEvent(event_t *ev) { - static DInputEvent* e = nullptr; - if (!e) e = (DInputEvent*)RUNTIME_CLASS(DInputEvent)->CreateNew(); - e->Type = EV_None; - e->KeyScan = 0; - e->KeyChar = 0; - e->KeyString = ""; - e->MouseX = e->MouseY = 0; - return e; + Type = (EGenericEvent)ev->type; + // we don't want the modders to remember what weird fields mean what for what events. + KeyScan = 0; + KeyChar = 0; + MouseX = 0; + MouseY = 0; + switch (Type) + { + case EV_None: + break; + case EV_KeyDown: + case EV_KeyUp: + KeyScan = ev->data1; + KeyChar = ev->data2; + KeyString = FString(char(ev->data1)); + break; + case EV_Mouse: + MouseX = ev->x; + MouseY = ev->y; + break; + default: + break; // EV_DeviceChange = wat? + } } bool DStaticEventHandler::InputProcess(event_t* ev) @@ -1050,32 +1032,13 @@ bool DStaticEventHandler::InputProcess(event_t* ev) // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_InputProcess_VMPtr) return false; - DInputEvent* e = E_SetupInputEvent(); + FInputEvent e = ev; // - e->Type = (EGenericEvent)ev->type; - // we don't want the modders to remember what weird fields mean what for what events. - switch (e->Type) - { - case EV_None: - break; - case EV_KeyDown: - case EV_KeyUp: - e->KeyScan = ev->data1; - e->KeyChar = ev->data2; - e->KeyString.Format("%c", e->KeyChar); - break; - case EV_Mouse: - e->MouseX = ev->x; - e->MouseY = ev->y; - break; - default: - break; // EV_DeviceChange = wat? - } int processed; VMReturn results[1] = { &processed }; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, results, 1, nullptr); return !!processed; } @@ -1083,18 +1046,7 @@ bool DStaticEventHandler::InputProcess(event_t* ev) return false; } -static DConsoleEvent* E_SetupConsoleEvent() -{ - static DConsoleEvent* e = nullptr; - if (!e) e = (DConsoleEvent*)RUNTIME_CLASS(DConsoleEvent)->CreateNew(); - e->Player = -1; - e->Name = ""; - for (size_t i = 0; i < countof(e->Args); i++) - e->Args[i] = 0; - return e; -} - -void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3) +void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3, bool manual) { if (player < 0) { @@ -1103,16 +1055,17 @@ void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_ConsoleProcess_VMPtr) return; - DConsoleEvent* e = E_SetupConsoleEvent(); + FConsoleEvent e; // - e->Player = player; - e->Name = name; - e->Args[0] = arg1; - e->Args[1] = arg2; - e->Args[2] = arg3; + e.Player = player; + e.Name = name; + e.Args[0] = arg1; + e.Args[1] = arg2; + e.Args[2] = arg3; + e.IsManual = manual; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -1123,16 +1076,17 @@ void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int // don't create excessive DObjects if not going to be processed anyway if (func == DStaticEventHandler_NetworkProcess_VMPtr) return; - DConsoleEvent* e = E_SetupConsoleEvent(); + FConsoleEvent e; // - e->Player = player; - e->Name = name; - e->Args[0] = arg1; - e->Args[1] = arg2; - e->Args[2] = arg3; + e.Player = player; + e.Name = name; + e.Args[0] = arg1; + e.Args[1] = arg2; + e.Args[2] = arg3; + e.IsManual = manual; - VMValue params[2] = { (DStaticEventHandler*)this, e }; + VMValue params[2] = { (DStaticEventHandler*)this, &e }; GlobalVMStack.Call(func, params, 2, nullptr, 0, nullptr); } } @@ -1162,7 +1116,7 @@ CCMD(event) for (int i = 0; i < argn; i++) arg[i] = atoi(argv[2 + i]); // call locally - E_Console(-1, argv[1], arg[0], arg[1], arg[2]); + E_Console(-1, argv[1], arg[0], arg[1], arg[2], true); } } @@ -1187,6 +1141,6 @@ CCMD(netevent) for (int i = 0; i < argn; i++) arg[i] = atoi(argv[2 + i]); // call networked - E_SendNetworkEvent(argv[1], arg[0], arg[1], arg[2]); + E_SendNetworkEvent(argv[1], arg[0], arg[1], arg[2], true); } } diff --git a/src/events.h b/src/events.h index b83890223..6a84da3f6 100755 --- a/src/events.h +++ b/src/events.h @@ -58,10 +58,10 @@ void E_PlayerDisconnected(int num); // this executes on events. bool E_Responder(event_t* ev); // splits events into InputProcess and UiProcess // this executes on console/net events. -void E_Console(int player, FString name, int arg1, int arg2, int arg3); +void E_Console(int player, FString name, int arg1, int arg2, int arg3, bool manual); // send networked event. unified function. -bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3); +bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3, bool manual); // check if there is anything that should receive GUI events bool E_CheckUiProcessors(); @@ -152,7 +152,7 @@ public: bool UiProcess(event_t* ev); // - void ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3); + void ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3, bool manual); }; class DEventHandler : public DStaticEventHandler { @@ -163,76 +163,34 @@ public: extern DStaticEventHandler* E_FirstEventHandler; extern DStaticEventHandler* E_LastEventHandler; -// we cannot call this DEvent because in ZScript, 'event' is a keyword -class DBaseEvent : public DObject +struct FRenderEvent { - DECLARE_CLASS(DBaseEvent, DObject) -public: - - DBaseEvent() - { - // each type of event is created only once to avoid new/delete hell - // since from what I remember object creation and deletion results in a lot of GC processing - // (and we aren't supposed to pass event objects around anyway) - this->ObjectFlags |= OF_Fixed; - // we don't want to store events into the savegames because they are global. - this->ObjectFlags |= OF_Transient; - } -}; - -class DRenderEvent : public DBaseEvent -{ - DECLARE_CLASS(DRenderEvent, DBaseEvent) -public: // these are for all render events DVector3 ViewPos; DAngle ViewAngle; DAngle ViewPitch; DAngle ViewRoll; - double FracTic; // 0..1 value that describes where we are inside the current gametic, render-wise. - AActor* Camera; - - DRenderEvent() - { - FracTic = 0; - Camera = nullptr; - } + double FracTic = 0; // 0..1 value that describes where we are inside the current gametic, render-wise. + AActor* Camera = nullptr; }; -class DWorldEvent : public DBaseEvent +struct FWorldEvent { - DECLARE_CLASS(DWorldEvent, DBaseEvent) -public: // for loaded/unloaded - bool IsSaveGame; - bool IsReopen; + bool IsSaveGame = false; + bool IsReopen = false; // for thingspawned, thingdied, thingdestroyed - AActor* Thing; - // for thingdied - AActor* Inflictor; // can be null - // for damagemobj - int Damage; - AActor* DamageSource; // can be null + AActor* Thing = nullptr; // for thingdied + AActor* Inflictor = nullptr; // can be null - for damagemobj + AActor* DamageSource = nullptr; // can be null + int Damage = 0; FName DamageType; - int DamageFlags; + int DamageFlags = 0; DAngle DamageAngle; - - DWorldEvent() - { - IsSaveGame = false; - IsReopen = false; - Thing = nullptr; - Inflictor = nullptr; - Damage = 0; - DamageSource = nullptr; - DamageFlags = 0; - } }; -class DPlayerEvent : public DBaseEvent +struct FPlayerEvent { - DECLARE_CLASS(DPlayerEvent, DBaseEvent) -public: // we currently have only one member: player index // in ZScript, we have global players[] array from which we can // get both the player itself and player's body, @@ -240,18 +198,10 @@ public: int PlayerNumber; // we set this to true if level was reopened (RETURN scripts) bool IsReturn; - - DPlayerEvent() - { - PlayerNumber = -1; - IsReturn = false; - } }; -class DUiEvent : public DBaseEvent +struct FUiEvent { - DECLARE_CLASS(DUiEvent, DBaseEvent) -public: // this essentially translates event_t UI events to ZScript. EGUIEvent Type; // for keys/chars/whatever @@ -265,18 +215,13 @@ public: bool IsCtrl; bool IsAlt; - DUiEvent() - { - Type = EV_GUI_None; - } + FUiEvent(event_t *ev); }; -class DInputEvent : public DBaseEvent +struct FInputEvent { - DECLARE_CLASS(DInputEvent, DBaseEvent) -public: // this translates regular event_t events to ZScript (not UI, UI events are sent via DUiEvent and only if requested!) - EGenericEvent Type; + EGenericEvent Type = EV_None; // for keys int KeyScan; FString KeyString; @@ -285,26 +230,18 @@ public: int MouseX; int MouseY; - DInputEvent() - { - Type = EV_None; - } + FInputEvent(event_t *ev); }; -class DConsoleEvent : public DBaseEvent +struct FConsoleEvent { - DECLARE_CLASS(DConsoleEvent, DBaseEvent) -public: // player that activated this event. note that it's always -1 for non-playsim events (i.e. these not called with netevent) int Player; // FString Name; int Args[3]; - - DConsoleEvent() - { - Player = -1; - } + // + bool IsManual; }; #endif \ No newline at end of file diff --git a/src/f_wipe.cpp b/src/f_wipe.cpp index 5f46233f4..2476209ab 100644 --- a/src/f_wipe.cpp +++ b/src/f_wipe.cpp @@ -29,6 +29,9 @@ #include "f_wipe.h" #include "c_cvars.h" #include "templates.h" +#include "v_palette.h" + +EXTERN_CVAR(Bool, r_blendmethod) // // SCREEN WIPE PACKAGE @@ -281,39 +284,80 @@ bool wipe_doBurn (int ticks) fromold = (BYTE *)wipe_scr_start; fromnew = (BYTE *)wipe_scr_end; - for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep) + if (!r_blendmethod) { - for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep) + for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep) { - int fglevel; + for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep) + { + int fglevel; - fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2; - if (fglevel >= 63) - { - to[x] = fromnew[x]; - } - else if (fglevel == 0) - { - to[x] = fromold[x]; - done = false; - } - else - { - int bglevel = 64-fglevel; - DWORD *fg2rgb = Col2RGB8[fglevel]; - DWORD *bg2rgb = Col2RGB8[bglevel]; - DWORD fg = fg2rgb[fromnew[x]]; - DWORD bg = bg2rgb[fromold[x]]; - fg = (fg+bg) | 0x1f07c1f; - to[x] = RGB32k.All[fg & (fg>>15)]; - done = false; + fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2; + if (fglevel >= 63) + { + to[x] = fromnew[x]; + } + else if (fglevel == 0) + { + to[x] = fromold[x]; + done = false; + } + else + { + int bglevel = 64-fglevel; + DWORD *fg2rgb = Col2RGB8[fglevel]; + DWORD *bg2rgb = Col2RGB8[bglevel]; + DWORD fg = fg2rgb[fromnew[x]]; + DWORD bg = bg2rgb[fromold[x]]; + fg = (fg+bg) | 0x1f07c1f; + to[x] = RGB32k.All[fg & (fg>>15)]; + done = false; + } } + fromold += SCREENWIDTH; + fromnew += SCREENWIDTH; + to += SCREENPITCH; } - fromold += SCREENWIDTH; - fromnew += SCREENWIDTH; - to += SCREENPITCH; - } + } + else + { + for (y = 0, firey = 0; y < SCREENHEIGHT; y++, firey += ystep) + { + for (x = 0, firex = 0; x < SCREENWIDTH; x++, firex += xstep) + { + int fglevel; + + fglevel = burnarray[(firex>>SHIFT)+(firey>>SHIFT)*FIREWIDTH] / 2; + if (fglevel >= 63) + { + to[x] = fromnew[x]; + } + else if (fglevel == 0) + { + to[x] = fromold[x]; + done = false; + } + else + { + int bglevel = 64-fglevel; + + const PalEntry* pal = GPalette.BaseColors; + + DWORD fg = fromnew[x]; + DWORD bg = fromold[x]; + int r = MIN((pal[fg].r * fglevel + pal[bg].r * bglevel) >> 8, 63); + int g = MIN((pal[fg].g * fglevel + pal[bg].g * bglevel) >> 8, 63); + int b = MIN((pal[fg].b * fglevel + pal[bg].b * bglevel) >> 8, 63); + to[x] = RGB256k.RGB[r][g][b]; + done = false; + } + } + fromold += SCREENWIDTH; + fromnew += SCREENWIDTH; + to += SCREENPITCH; + } + } return done || (burntime > 40); } @@ -348,19 +392,41 @@ bool wipe_doFade (int ticks) BYTE *fromnew = (BYTE *)wipe_scr_end; BYTE *fromold = (BYTE *)wipe_scr_start; BYTE *to = screen->GetBuffer(); + const PalEntry *pal = GPalette.BaseColors; - for (y = 0; y < SCREENHEIGHT; y++) + if (!r_blendmethod) { - for (x = 0; x < SCREENWIDTH; x++) + for (y = 0; y < SCREENHEIGHT; y++) { - DWORD fg = fg2rgb[fromnew[x]]; - DWORD bg = bg2rgb[fromold[x]]; - fg = (fg+bg) | 0x1f07c1f; - to[x] = RGB32k.All[fg & (fg>>15)]; + for (x = 0; x < SCREENWIDTH; x++) + { + DWORD fg = fg2rgb[fromnew[x]]; + DWORD bg = bg2rgb[fromold[x]]; + fg = (fg+bg) | 0x1f07c1f; + to[x] = RGB32k.All[fg & (fg>>15)]; + } + fromnew += SCREENWIDTH; + fromold += SCREENWIDTH; + to += SCREENPITCH; + } + } + else + { + for (y = 0; y < SCREENHEIGHT; y++) + { + for (x = 0; x < SCREENWIDTH; x++) + { + DWORD fg = fromnew[x]; + DWORD bg = fromold[x]; + int r = MIN((pal[fg].r * (64-bglevel) + pal[bg].r * bglevel) >> 8, 63); + int g = MIN((pal[fg].g * (64-bglevel) + pal[bg].g * bglevel) >> 8, 63); + int b = MIN((pal[fg].b * (64-bglevel) + pal[bg].b * bglevel) >> 8, 63); + to[x] = RGB256k.RGB[r][g][b]; + } + fromnew += SCREENWIDTH; + fromold += SCREENWIDTH; + to += SCREENPITCH; } - fromnew += SCREENWIDTH; - fromold += SCREENWIDTH; - to += SCREENPITCH; } } return false; @@ -383,6 +449,9 @@ static bool (*wipes[])(int) = // Returns true if the wipe should be performed. bool wipe_StartScreen (int type) { + if (screen->IsBgra()) + return false; + CurrentWipeType = clamp(type, 0, wipe_NUMWIPES - 1); if (CurrentWipeType) @@ -396,11 +465,15 @@ bool wipe_StartScreen (int type) void wipe_EndScreen (void) { + if (screen->IsBgra()) + return; + if (CurrentWipeType) { wipe_scr_end = new short[SCREENWIDTH * SCREENHEIGHT / 2]; screen->GetBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_end); screen->DrawBlock (0, 0, SCREENWIDTH, SCREENHEIGHT, (BYTE *)wipe_scr_start); // restore start scr. + // Initialize the wipe (*wipes[(CurrentWipeType-1)*3])(0); } @@ -411,6 +484,9 @@ bool wipe_ScreenWipe (int ticks) { bool rc; + if (screen->IsBgra()) + return true; + if (CurrentWipeType == wipe_None) return true; @@ -424,6 +500,9 @@ bool wipe_ScreenWipe (int ticks) // Final things for the wipe void wipe_Cleanup() { + if (screen->IsBgra()) + return; + if (wipe_scr_start != NULL) { delete[] wipe_scr_start; diff --git a/src/g_level.cpp b/src/g_level.cpp index 427e17278..734f03bb6 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1389,7 +1389,7 @@ void G_InitLevelLocals () level_info_t *info; BaseBlendA = 0.0f; // Remove underwater blend effect, if any - NormalLight.Maps = realcolormaps; + NormalLight.Maps = realcolormaps.Maps; // [BB] Instead of just setting the color, we also have to reset Desaturate and build the lights. NormalLight.ChangeColor (PalEntry (255, 255, 255), 0); @@ -1417,6 +1417,7 @@ void G_InitLevelLocals () R_SetDefaultColormap (info->FadeTable); if (strnicmp (info->FadeTable, "COLORMAP", 8) != 0) { + level.fadeto = 0xff939393; //[SP] Hexen True-color compatibility, just use gray. level.flags |= LEVEL_HASFADETABLE; } /* diff --git a/src/g_statusbar/strife_sbar.cpp b/src/g_statusbar/strife_sbar.cpp index 972657482..a09ac4301 100644 --- a/src/g_statusbar/strife_sbar.cpp +++ b/src/g_statusbar/strife_sbar.cpp @@ -36,7 +36,6 @@ public: const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetPixels (); bool CheckModified (); - void Unload (); void SetVial (int level); @@ -91,10 +90,6 @@ bool FHealthBar::CheckModified () return NeedRefresh; } -void FHealthBar::Unload () -{ -} - const BYTE *FHealthBar::GetColumn (unsigned int column, const Span **spans_out) { if (NeedRefresh) diff --git a/src/gl/dynlights/a_dynlight.cpp b/src/gl/dynlights/a_dynlight.cpp index bd2575216..d5a07f089 100644 --- a/src/gl/dynlights/a_dynlight.cpp +++ b/src/gl/dynlights/a_dynlight.cpp @@ -81,7 +81,6 @@ #include "gl/utility/gl_templates.h" #include "gl/system//gl_interface.h" -EXTERN_CVAR(Int, vid_renderer) //========================================================================== @@ -233,10 +232,6 @@ void ADynamicLight::Deactivate(AActor *activator) //========================================================================== void ADynamicLight::Tick() { - if (vid_renderer == 0) - { - return; - } if (IsOwned()) { if (!target || !target->state) diff --git a/src/gl/renderer/gl_colormap.h b/src/gl/renderer/gl_colormap.h index 2c9fb91a4..4c755856b 100644 --- a/src/gl/renderer/gl_colormap.h +++ b/src/gl/renderer/gl_colormap.h @@ -79,5 +79,4 @@ struct FColormap }; - #endif diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index ed2c523f2..1688cfc1a 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -189,9 +189,9 @@ public: void SetFixedColormap (player_t *player); void WriteSavePic (player_t *player, FileWriter *file, int width, int height); void EndDrawScene(sector_t * viewsector); - void UpdateCameraExposure(); void PostProcessScene(); void AmbientOccludeScene(); + void UpdateCameraExposure(); void BloomScene(); void TonemapScene(); void ColormapScene(); diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index b852e1380..a77fc641d 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -1017,14 +1017,14 @@ struct FGLInterface : public FRenderer void StartSerialize(FSerializer &arc) override; void EndSerialize(FSerializer &arc) override; void RenderTextureView (FCanvasTexture *self, AActor *viewpoint, int fov) override; - sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) override; + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) override; void SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog) override; void PreprocessLevel() override; void CleanLevelData() override; bool RequireGLNodes() override; int GetMaxViewPitch(bool down) override; - void ClearBuffer(int color) override; + void SetClearColor(int color) override; void Init() override; }; @@ -1279,7 +1279,7 @@ int FGLInterface::GetMaxViewPitch(bool down) // //=========================================================================== -void FGLInterface::ClearBuffer(int color) +void FGLInterface::SetClearColor(int color) { PalEntry pe = GPalette.BaseColors[color]; GLRenderer->mSceneClearColor[0] = pe.r / 255.f; @@ -1379,7 +1379,7 @@ void FGLInterface::RenderTextureView (FCanvasTexture *tex, AActor *Viewpoint, in // //========================================================================== -sector_t *FGLInterface::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) +sector_t *FGLInterface::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) { if (floorlightlevel != NULL) { @@ -1389,7 +1389,7 @@ sector_t *FGLInterface::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlig { *ceilinglightlevel = sec->GetCeilingLight (); } - return gl_FakeFlat(sec, tempsec, back); + return gl_FakeFlat(sec, tempsec, false); } //=========================================================================== diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 864d796a8..bc1fbce6d 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -79,7 +79,7 @@ CUSTOM_CVAR(Int, vid_hwgamma, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITC //========================================================================== OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : - Super(hMonitor, width, height, bits, refreshHz, fullscreen) + Super(hMonitor, width, height, bits, refreshHz, fullscreen, false) { // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! diff --git a/src/gl/system/gl_interface.cpp b/src/gl/system/gl_interface.cpp index 7f98c7f3c..485876e7a 100644 --- a/src/gl/system/gl_interface.cpp +++ b/src/gl/system/gl_interface.cpp @@ -130,8 +130,16 @@ void gl_LoadExtensions() InitContext(); CollectExtensions(); - const char *version = Args->CheckValue("-glversion"); const char *glversion = (const char*)glGetString(GL_VERSION); + gl.es = false; + + if (glversion && strlen(glversion) > 10 && memcmp(glversion, "OpenGL ES ", 10) == 0) + { + glversion += 10; + gl.es = true; + } + + const char *version = Args->CheckValue("-glversion"); if (version == NULL) { @@ -147,90 +155,117 @@ void gl_LoadExtensions() float gl_version = (float)strtod(version, NULL) + 0.01f; - // Don't even start if it's lower than 2.0 or no framebuffers are available (The framebuffer extension is needed for glGenerateMipmapsEXT!) - if ((gl_version < 2.0f || !CheckExtension("GL_EXT_framebuffer_object")) && gl_version < 3.0f) + if (gl.es) { - I_FatalError("Unsupported OpenGL version.\nAt least OpenGL 2.0 with framebuffer support is required to run " GAMENAME ".\n"); - } + if (gl_version < 2.0f) + { + I_FatalError("Unsupported OpenGL ES version.\nAt least OpenGL ES 2.0 is required to run " GAMENAME ".\n"); + } + + const char *glslversion = (const char*)glGetString(GL_SHADING_LANGUAGE_VERSION); + if (glslversion && strlen(glslversion) > 18 && memcmp(glslversion, "OpenGL ES GLSL ES ", 10) == 0) + { + glslversion += 18; + } + + // add 0.01 to account for roundoff errors making the number a tad smaller than the actual version + gl.glslversion = strtod(glslversion, NULL) + 0.01f; + gl.vendorstring = (char*)glGetString(GL_VENDOR); - // add 0.01 to account for roundoff errors making the number a tad smaller than the actual version - gl.glslversion = strtod((char*)glGetString(GL_SHADING_LANGUAGE_VERSION), NULL) + 0.01f; - - gl.vendorstring = (char*)glGetString(GL_VENDOR); - - // first test for optional features - if (CheckExtension("GL_ARB_texture_compression")) gl.flags |= RFL_TEXTURE_COMPRESSION; - if (CheckExtension("GL_EXT_texture_compression_s3tc")) gl.flags |= RFL_TEXTURE_COMPRESSION_S3TC; - - if ((gl_version >= 3.3f || CheckExtension("GL_ARB_sampler_objects")) && !Args->CheckParm("-nosampler")) - { - gl.flags |= RFL_SAMPLER_OBJECTS; - } - - // The minimum requirement for the modern render path are GL 3.0 + uniform buffers. Also exclude the Linux Mesa driver at GL 3.0 because it errors out on shader compilation. - if (gl_version < 3.0f || (gl_version < 3.1f && (!CheckExtension("GL_ARB_uniform_buffer_object") || strstr(gl.vendorstring, "X.Org") != nullptr))) - { - gl.legacyMode = true; - gl.lightmethod = LM_LEGACY; - gl.buffermethod = BM_LEGACY; - gl.glslversion = 0; - gl.flags |= RFL_NO_CLIP_PLANES; - } - else - { + // Use the slowest/oldest modern path for now gl.legacyMode = false; gl.lightmethod = LM_DEFERRED; gl.buffermethod = BM_DEFERRED; - if (gl_version < 4.f) + } + else + { + // Don't even start if it's lower than 2.0 or no framebuffers are available (The framebuffer extension is needed for glGenerateMipmapsEXT!) + if ((gl_version < 2.0f || !CheckExtension("GL_EXT_framebuffer_object")) && gl_version < 3.0f) { -#ifdef _WIN32 - if (strstr(gl.vendorstring, "ATI Tech")) - { - gl.flags |= RFL_NO_CLIP_PLANES; // gl_ClipDistance is horribly broken on ATI GL3 drivers for Windows. - } -#endif + I_FatalError("Unsupported OpenGL version.\nAt least OpenGL 2.0 with framebuffer support is required to run " GAMENAME ".\n"); } - else if (gl_version < 4.5f) + + gl.es = false; + + // add 0.01 to account for roundoff errors making the number a tad smaller than the actual version + gl.glslversion = strtod((char*)glGetString(GL_SHADING_LANGUAGE_VERSION), NULL) + 0.01f; + + gl.vendorstring = (char*)glGetString(GL_VENDOR); + + // first test for optional features + if (CheckExtension("GL_ARB_texture_compression")) gl.flags |= RFL_TEXTURE_COMPRESSION; + if (CheckExtension("GL_EXT_texture_compression_s3tc")) gl.flags |= RFL_TEXTURE_COMPRESSION_S3TC; + + if ((gl_version >= 3.3f || CheckExtension("GL_ARB_sampler_objects")) && !Args->CheckParm("-nosampler")) { - // don't use GL 4.x features when running a GL 3.x context. - if (CheckExtension("GL_ARB_buffer_storage")) - { - // work around a problem with older AMD drivers: Their implementation of shader storage buffer objects is piss-poor and does not match uniform buffers even closely. - // Recent drivers, GL 4.4 don't have this problem, these can easily be recognized by also supporting the GL_ARB_buffer_storage extension. - if (CheckExtension("GL_ARB_shader_storage_buffer_object")) - { - // Shader storage buffer objects are broken on current Intel drivers. - if (strstr(gl.vendorstring, "Intel") == NULL) - { - gl.flags |= RFL_SHADER_STORAGE_BUFFER; - } - } - gl.flags |= RFL_BUFFER_STORAGE; - gl.lightmethod = LM_DIRECT; - gl.buffermethod = BM_PERSISTENT; - } + gl.flags |= RFL_SAMPLER_OBJECTS; + } + + // The minimum requirement for the modern render path are GL 3.0 + uniform buffers. Also exclude the Linux Mesa driver at GL 3.0 because it errors out on shader compilation. + if (gl_version < 3.0f || (gl_version < 3.1f && (!CheckExtension("GL_ARB_uniform_buffer_object") || strstr(gl.vendorstring, "X.Org") != nullptr))) + { + gl.legacyMode = true; + gl.lightmethod = LM_LEGACY; + gl.buffermethod = BM_LEGACY; + gl.glslversion = 0; + gl.flags |= RFL_NO_CLIP_PLANES; } else { - // Assume that everything works without problems on GL 4.5 drivers where these things are core features. - gl.flags |= RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE; - gl.lightmethod = LM_DIRECT; - gl.buffermethod = BM_PERSISTENT; - } + gl.legacyMode = false; + gl.lightmethod = LM_DEFERRED; + gl.buffermethod = BM_DEFERRED; + if (gl_version < 4.f) + { +#ifdef _WIN32 + if (strstr(gl.vendorstring, "ATI Tech")) + { + gl.flags |= RFL_NO_CLIP_PLANES; // gl_ClipDistance is horribly broken on ATI GL3 drivers for Windows. + } +#endif + } + else if (gl_version < 4.5f) + { + // don't use GL 4.x features when running a GL 3.x context. + if (CheckExtension("GL_ARB_buffer_storage")) + { + // work around a problem with older AMD drivers: Their implementation of shader storage buffer objects is piss-poor and does not match uniform buffers even closely. + // Recent drivers, GL 4.4 don't have this problem, these can easily be recognized by also supporting the GL_ARB_buffer_storage extension. + if (CheckExtension("GL_ARB_shader_storage_buffer_object")) + { + // Shader storage buffer objects are broken on current Intel drivers. + if (strstr(gl.vendorstring, "Intel") == NULL) + { + gl.flags |= RFL_SHADER_STORAGE_BUFFER; + } + } + gl.flags |= RFL_BUFFER_STORAGE; + gl.lightmethod = LM_DIRECT; + gl.buffermethod = BM_PERSISTENT; + } + } + else + { + // Assume that everything works without problems on GL 4.5 drivers where these things are core features. + gl.flags |= RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE; + gl.lightmethod = LM_DIRECT; + gl.buffermethod = BM_PERSISTENT; + } - if (gl_version >= 4.3f || CheckExtension("GL_ARB_invalidate_subdata")) gl.flags |= RFL_INVALIDATE_BUFFER; - if (gl_version >= 4.3f || CheckExtension("GL_KHR_debug")) gl.flags |= RFL_DEBUG; + if (gl_version >= 4.3f || CheckExtension("GL_ARB_invalidate_subdata")) gl.flags |= RFL_INVALIDATE_BUFFER; + if (gl_version >= 4.3f || CheckExtension("GL_KHR_debug")) gl.flags |= RFL_DEBUG; - const char *lm = Args->CheckValue("-lightmethod"); - if (lm != NULL) - { - if (!stricmp(lm, "deferred") && gl.lightmethod == LM_DIRECT) gl.lightmethod = LM_DEFERRED; - } + const char *lm = Args->CheckValue("-lightmethod"); + if (lm != NULL) + { + if (!stricmp(lm, "deferred") && gl.lightmethod == LM_DIRECT) gl.lightmethod = LM_DEFERRED; + } - lm = Args->CheckValue("-buffermethod"); - if (lm != NULL) - { - if (!stricmp(lm, "deferred") && gl.buffermethod == BM_PERSISTENT) gl.buffermethod = BM_DEFERRED; + lm = Args->CheckValue("-buffermethod"); + if (lm != NULL) + { + if (!stricmp(lm, "deferred") && gl.buffermethod == BM_PERSISTENT) gl.buffermethod = BM_DEFERRED; + } } } diff --git a/src/gl/system/gl_interface.h b/src/gl/system/gl_interface.h index edaec58c0..a839e13b4 100644 --- a/src/gl/system/gl_interface.h +++ b/src/gl/system/gl_interface.h @@ -66,6 +66,7 @@ struct RenderContext int max_texturesize; char * vendorstring; bool legacyMode; + bool es; int MaxLights() const { diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp new file mode 100644 index 000000000..a0fc6e716 --- /dev/null +++ b/src/gl/system/gl_swframebuffer.cpp @@ -0,0 +1,3744 @@ +/* +** gl_swframebuffer.cpp +** Code to let ZDoom use OpenGL as a simple framebuffer +** +**--------------------------------------------------------------------------- +** Copyright 1998-2011 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +** This file does _not_ implement hardware-acclerated 3D rendering. It is +** just a means of getting the pixel data to the screen in a more reliable +** method on modern hardware by copying the entire frame to a texture, +** drawing that to the screen, and presenting. +** +** That said, it does implement hardware-accelerated 2D rendering. +*/ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "doomstat.h" +#include "m_png.h" +#include "m_crc32.h" +#include "vectors.h" +#include "v_palette.h" +#include "templates.h" + +#include "c_dispatch.h" +#include "templates.h" +#include "i_system.h" +#include "i_video.h" +#include "i_input.h" +#include "v_pfx.h" +#include "stats.h" +#include "doomerrors.h" +#include "r_data/r_translate.h" +#include "f_wipe.h" +#include "sbar.h" +#include "w_wad.h" +#include "r_data/colormaps.h" + +#include "gl/system/gl_interface.h" +#include "gl/system/gl_swframebuffer.h" +#include "gl/data/gl_data.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/gl_functions.h" +#include "gl_debug.h" + +#include "swrenderer/scene/r_light.h" + +CVAR(Int, gl_showpacks, 0, 0) +#ifndef WIN32 // Defined in fb_d3d9 for Windows +CVAR(Bool, vid_hwaalines, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CUSTOM_CVAR(Bool, vid_hw2d, true, CVAR_NOINITCALL) +{ + V_SetBorderNeedRefresh(); + ST_SetNeedRefresh(); +} +#else +EXTERN_CVAR(Bool, vid_hwaalines) +EXTERN_CVAR(Bool, vid_hw2d) +#endif + +EXTERN_CVAR(Bool, fullscreen) +EXTERN_CVAR(Float, Gamma) +EXTERN_CVAR(Bool, vid_vsync) +EXTERN_CVAR(Float, transsouls) +EXTERN_CVAR(Int, vid_refreshrate) + +CVAR(Int, vid_max_width, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, vid_max_height, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) + +namespace +{ + int ClampWidth(int width) { return (vid_max_width == 0 || width < vid_max_width) ? width : vid_max_width; } + int ClampHeight(int height) { return (vid_max_height == 0 || height < vid_max_height) ? height : vid_max_height; } +} + +#ifdef WIN32 +extern cycle_t BlitCycles; +#endif + +void gl_LoadExtensions(); +void gl_PrintStartupLog(); + +#ifndef WIN32 +// This has to be in this file because system headers conflict Doom headers +DFrameBuffer *CreateGLSWFrameBuffer(int width, int height, bool bgra, bool fullscreen) +{ + return new OpenGLSWFrameBuffer(NULL, width, height, 32, 60, fullscreen, bgra); +} +#endif + +IMPLEMENT_CLASS(OpenGLSWFrameBuffer, false, false) + +const char *const OpenGLSWFrameBuffer::ShaderDefines[OpenGLSWFrameBuffer::NUM_SHADERS] = +{ + "#define ENORMALCOLOR", // NormalColor + "#define ENORMALCOLOR\n#define PALTEX", // NormalColorPal + "#define ENORMALCOLOR\n#define INVERT", // NormalColorInv + "#define ENORMALCOLOR\n#define PALTEX\n#define INVERT", // NormalColorPalInv + + "#define EREDTOALPHA", // RedToAlpha + "#define EREDTOALPHA\n#define INVERT", // RedToAlphaInv + + "#define EVERTEXCOLOR", // VertexColor + + "#define ESPECIALCOLORMAP\n", // SpecialColormap + "#define ESPECIALCOLORMAP\n#define PALTEX", // SpecialColorMapPal + + "#define EINGAMECOLORMAP", // InGameColormap + "#define EINGAMECOLORMAP\n#define DESAT", // InGameColormapDesat + "#define EINGAMECOLORMAP\n#define INVERT", // InGameColormapInv + "#define EINGAMECOLORMAP\n#define INVERT\n#define DESAT", // InGameColormapInvDesat + "#define EINGAMECOLORMAP\n#define PALTEX\n", // InGameColormapPal + "#define EINGAMECOLORMAP\n#define PALTEX\n#define DESAT", // InGameColormapPalDesat + "#define EINGAMECOLORMAP\n#define PALTEX\n#define INVERT", // InGameColormapPalInv + "#define EINGAMECOLORMAP\n#define PALTEX\n#define INVERT\n#define DESAT", // InGameColormapPalInvDesat + + "#define EBURNWIPE", // BurnWipe + "#define EGAMMACORRECTION", // GammaCorrection +}; + +OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra) : + Super(hMonitor, ClampWidth(width), ClampHeight(height), bits, refreshHz, fullscreen, bgra) +{ + VertexBuffer = nullptr; + IndexBuffer = nullptr; + FBTexture = nullptr; + InitialWipeScreen = nullptr; + ScreenshotTexture = nullptr; + FinalWipeScreen = nullptr; + PaletteTexture = nullptr; + for (int i = 0; i < NUM_SHADERS; ++i) + { + Shaders[i] = nullptr; + } + + BlendingRect.left = 0; + BlendingRect.top = 0; + BlendingRect.right = Width; + BlendingRect.bottom = Height; + In2D = 0; + Palettes = nullptr; + Textures = nullptr; + Accel2D = true; + GatheringWipeScreen = false; + ScreenWipe = nullptr; + InScene = false; + QuadExtra = new BufferedTris[MAX_QUAD_BATCH]; + memset(QuadExtra, 0, sizeof(BufferedTris) * MAX_QUAD_BATCH); + Atlases = nullptr; + PixelDoubling = 0; + + Gamma = 1.0; + FlashColor0 = 0; + FlashColor1 = 0xFFFFFFFF; + FlashColor = 0; + FlashAmount = 0; + + NeedGammaUpdate = false; + NeedPalUpdate = false; + + if (MemBuffer == nullptr) + { + return; + } + + memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry) * 256); + + // To do: this needs to cooperate with the same static in OpenGLFrameBuffer::InitializeState + static bool first = true; + if (first) + { + ogl_LoadFunctions(); + } + gl_LoadExtensions(); + InitializeState(); + if (first) + { + gl_PrintStartupLog(); + first = false; + } + + if (!glGetString) + return; + + // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. + // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! + Super::SetVSync(vid_vsync); + + Debug = std::make_shared(); + Debug->Update(); + + //Windowed = !(static_cast(Video)->GoFullscreen(fullscreen)); + + TrueHeight = height; + + Valid = CreateResources(); + if (Valid) + SetInitialState(); +} + +OpenGLSWFrameBuffer::~OpenGLSWFrameBuffer() +{ + ReleaseResources(); + delete[] QuadExtra; +} + +void *OpenGLSWFrameBuffer::MapBuffer(int target, int size) +{ + if (glMapBufferRange) + { + return (FBVERTEX*)glMapBufferRange(target, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); + } + else + { + glBufferData(target, size, nullptr, GL_STREAM_DRAW); + return glMapBuffer(target, GL_WRITE_ONLY); + } +} + +OpenGLSWFrameBuffer::HWFrameBuffer::~HWFrameBuffer() +{ + if (Framebuffer != 0) glDeleteFramebuffers(1, (GLuint*)&Framebuffer); + delete Texture; +} + +OpenGLSWFrameBuffer::HWTexture::~HWTexture() +{ + if (Texture != 0) glDeleteTextures(1, (GLuint*)&Texture); + if (Buffers[0] != 0) glDeleteBuffers(2, (GLuint*)Buffers); +} + +OpenGLSWFrameBuffer::HWVertexBuffer::~HWVertexBuffer() +{ + if (VertexArray != 0) glDeleteVertexArrays(1, (GLuint*)&VertexArray); + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); +} + +OpenGLSWFrameBuffer::FBVERTEX *OpenGLSWFrameBuffer::HWVertexBuffer::Lock() +{ + glBindBuffer(GL_ARRAY_BUFFER, Buffer); + return (FBVERTEX*)MapBuffer(GL_ARRAY_BUFFER, Size); +} + +void OpenGLSWFrameBuffer::HWVertexBuffer::Unlock() +{ + glUnmapBuffer(GL_ARRAY_BUFFER); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +OpenGLSWFrameBuffer::HWIndexBuffer::~HWIndexBuffer() +{ + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); +} + +uint16_t *OpenGLSWFrameBuffer::HWIndexBuffer::Lock() +{ + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &LockedOldBinding); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Buffer); + return (uint16_t*)MapBuffer(GL_ELEMENT_ARRAY_BUFFER, Size); +} + +void OpenGLSWFrameBuffer::HWIndexBuffer::Unlock() +{ + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, LockedOldBinding); +} + +OpenGLSWFrameBuffer::HWPixelShader::~HWPixelShader() +{ + if (Program != 0) glDeleteProgram(Program); + if (VertexShader != 0) glDeleteShader(VertexShader); + if (FragmentShader != 0) glDeleteShader(FragmentShader); +} + +bool OpenGLSWFrameBuffer::CreateFrameBuffer(const FString &name, int width, int height, HWFrameBuffer **outFramebuffer) +{ + auto fb = std::make_unique(); + + GLint format = GL_RGBA16F; + if (gl.es) format = GL_RGB; + + if (!CreateTexture(name, width, height, 1, format, &fb->Texture)) + { + outFramebuffer = nullptr; + return false; + } + + glGenFramebuffers(1, (GLuint*)&fb->Framebuffer); + + GLint oldFramebufferBinding = 0, oldTextureBinding = 0; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFramebufferBinding); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureBinding); + + glBindFramebuffer(GL_FRAMEBUFFER, fb->Framebuffer); + FGLDebug::LabelObject(GL_FRAMEBUFFER, fb->Framebuffer, name); + + glBindTexture(GL_TEXTURE_2D, fb->Texture->Texture); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->Texture->Texture, 0); + + GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindFramebuffer(GL_FRAMEBUFFER, oldFramebufferBinding); + glBindTexture(GL_TEXTURE_2D, oldTextureBinding); + + if (result != GL_FRAMEBUFFER_COMPLETE) + { + Printf("Framebuffer is not complete\n"); + outFramebuffer = nullptr; + return false; + } + + *outFramebuffer = fb.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader) +{ + auto shader = std::make_unique(); + + shader->Program = glCreateProgram(); + shader->VertexShader = glCreateShader(GL_VERTEX_SHADER); + shader->FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + int maxGlslVersion = 330; + int shaderVersion = MIN((int)round(gl.glslversion * 10) * 10, maxGlslVersion); + + FString prefix; + prefix.AppendFormat("#version %d\n%s\n#line 0\n", shaderVersion, defines.GetChars()); + //Printf("Shader prefix: %s", prefix.GetChars()); + + vertexsrc = prefix + vertexsrc; + fragmentsrc = prefix + fragmentsrc; + + { + int lengths[1] = { (int)vertexsrc.Len() }; + const char *sources[1] = { vertexsrc.GetChars() }; + glShaderSource(shader->VertexShader, 1, sources, lengths); + glCompileShader(shader->VertexShader); + } + + { + int lengths[1] = { (int)fragmentsrc.Len() }; + const char *sources[1] = { fragmentsrc.GetChars() }; + glShaderSource(shader->FragmentShader, 1, sources, lengths); + glCompileShader(shader->FragmentShader); + } + + GLint status = 0; + int errorShader = shader->VertexShader; + glGetShaderiv(shader->VertexShader, GL_COMPILE_STATUS, &status); + if (status != GL_FALSE) { errorShader = shader->FragmentShader; glGetShaderiv(shader->FragmentShader, GL_COMPILE_STATUS, &status); } + if (status == GL_FALSE) + { + static char buffer[10000]; + GLsizei length = 0; + buffer[0] = 0; + glGetShaderInfoLog(errorShader, 10000, &length, buffer); + //Printf("Shader compile failed: %s", buffer); + + *outShader = nullptr; + return false; + } + + glAttachShader(shader->Program, shader->VertexShader); + glAttachShader(shader->Program, shader->FragmentShader); + glBindFragDataLocation(shader->Program, 0, "FragColor"); + glBindAttribLocation(shader->Program, 0, "AttrPosition"); + glBindAttribLocation(shader->Program, 1, "AttrColor0"); + glBindAttribLocation(shader->Program, 2, "AttrColor1"); + glBindAttribLocation(shader->Program, 3, "AttrTexCoord0"); + glLinkProgram(shader->Program); + glGetProgramiv(shader->Program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + { + static char buffer[10000]; + GLsizei length = 0; + buffer[0] = 0; + glGetProgramInfoLog(shader->Program, 10000, &length, buffer); + //Printf("Shader link failed: %s", buffer); + + *outShader = nullptr; + return false; + } + + shader->ConstantLocations[PSCONST_Desaturation] = glGetUniformLocation(shader->Program, "Desaturation"); + shader->ConstantLocations[PSCONST_PaletteMod] = glGetUniformLocation(shader->Program, "PaletteMod"); + shader->ConstantLocations[PSCONST_Weights] = glGetUniformLocation(shader->Program, "Weights"); + shader->ConstantLocations[PSCONST_Gamma] = glGetUniformLocation(shader->Program, "Gamma"); + shader->ConstantLocations[PSCONST_ScreenSize] = glGetUniformLocation(shader->Program, "ScreenSize"); + shader->ImageLocation = glGetUniformLocation(shader->Program, "Image"); + shader->PaletteLocation = glGetUniformLocation(shader->Program, "Palette"); + shader->NewScreenLocation = glGetUniformLocation(shader->Program, "NewScreen"); + shader->BurnLocation = glGetUniformLocation(shader->Program, "Burn"); + + *outShader = shader.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer) +{ + auto obj = std::make_unique(); + + obj->Size = size; + + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + glGenVertexArrays(1, (GLuint*)&obj->VertexArray); + glGenBuffers(1, (GLuint*)&obj->Buffer); + glBindVertexArray(obj->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, obj->Buffer); + FGLDebug::LabelObject(GL_BUFFER, obj->Buffer, "VertexBuffer"); + glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); + + *outVertexBuffer = obj.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer) +{ + auto obj = std::make_unique(); + + obj->Size = size; + + GLint oldBinding = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &oldBinding); + + glGenBuffers(1, (GLuint*)&obj->Buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, obj->Buffer); + FGLDebug::LabelObject(GL_BUFFER, obj->Buffer, "IndexBuffer"); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oldBinding); + + *outIndexBuffer = obj.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateTexture(const FString &name, int width, int height, int levels, int format, HWTexture **outTexture) +{ + auto obj = std::make_unique(); + + obj->Format = format; + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + + glGenTextures(1, (GLuint*)&obj->Texture); + glBindTexture(GL_TEXTURE_2D, obj->Texture); + GLenum srcformat; + switch (format) + { + case GL_RGB: srcformat = GL_RGB; break; + case GL_R8: srcformat = GL_RED; break; + case GL_RGBA8: srcformat = gl.es ? GL_RGBA : GL_BGRA; break; + case GL_RGBA16F: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: srcformat = GL_RGB; break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: srcformat = GL_RGBA; break; + default: + I_FatalError("Unknown format passed to CreateTexture"); + return false; + } + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, srcformat, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + FGLDebug::LabelObject(GL_TEXTURE, obj->Texture, name); + + glBindTexture(GL_TEXTURE_2D, oldBinding); + + *outTexture = obj.release(); + return true; +} + +OpenGLSWFrameBuffer::HWTexture *OpenGLSWFrameBuffer::CopyCurrentScreen() +{ + auto obj = std::make_unique(); + obj->Format = GL_RGBA16F; + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + + glGenTextures(1, (GLuint*)&obj->Texture); + glBindTexture(GL_TEXTURE_2D, obj->Texture); + + glCopyTexImage2D(GL_TEXTURE_2D, 0, obj->Format, 0, 0, Width, Height, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + FGLDebug::LabelObject(GL_TEXTURE, obj->Texture, "CopyCurrentScreen"); + + glBindTexture(GL_TEXTURE_2D, oldBinding); + + return obj.release(); +} + +void OpenGLSWFrameBuffer::SetGammaRamp(const GammaRamp *ramp) +{ +} + +void OpenGLSWFrameBuffer::SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount) +{ + assert(uniformIndex < NumPSCONST && vec4fcount == 1); // This emulation of d3d9 only works for very simple stuff + for (int i = 0; i < 4; i++) + ShaderConstants[uniformIndex * 4 + i] = data[i]; + if (CurrentShader && CurrentShader->ConstantLocations[uniformIndex] != -1) + glUniform4fv(CurrentShader->ConstantLocations[uniformIndex], vec4fcount, data); +} + +void OpenGLSWFrameBuffer::SetHWPixelShader(HWPixelShader *shader) +{ + if (shader != CurrentShader) + { + if (shader) + { + glUseProgram(shader->Program); + for (int i = 0; i < NumPSCONST; i++) + { + if (shader->ConstantLocations[i] != -1) + glUniform4fv(shader->ConstantLocations[i], 1, &ShaderConstants[i * 4]); + } + } + else + { + glUseProgram(0); + } + } + CurrentShader = shader; +} + +void OpenGLSWFrameBuffer::SetStreamSource(HWVertexBuffer *vertexBuffer) +{ + if (vertexBuffer) + glBindVertexArray(vertexBuffer->VertexArray); + else + glBindVertexArray(0); +} + +void OpenGLSWFrameBuffer::SetIndices(HWIndexBuffer *indexBuffer) +{ + if (indexBuffer) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->Buffer); + else + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void OpenGLSWFrameBuffer::DrawTriangleFans(int count, const FBVERTEX *vertices) +{ + count = 2 + count; + + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBuffer) + { + StreamVertexBuffer = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBuffer->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBuffer->Buffer); + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + } + else + { + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_TRIANGLE_FAN, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + +void OpenGLSWFrameBuffer::DrawTriangleFans(int count, const BURNVERTEX *vertices) +{ + count = 2 + count; + + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBufferBurn) + { + StreamVertexBufferBurn = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBufferBurn->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBufferBurn->Buffer); + glBindVertexArray(StreamVertexBufferBurn->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBufferBurn->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(BURNVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(BURNVERTEX), (const GLvoid*)offsetof(BURNVERTEX, x)); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(BURNVERTEX), (const GLvoid*)offsetof(BURNVERTEX, tu0)); + } + else + { + glBindVertexArray(StreamVertexBufferBurn->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBufferBurn->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(BURNVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_TRIANGLE_FAN, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + +void OpenGLSWFrameBuffer::DrawPoints(int count, const FBVERTEX *vertices) +{ + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBuffer) + { + StreamVertexBuffer = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBuffer->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBuffer->Buffer); + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + } + else + { + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_POINTS, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + +void OpenGLSWFrameBuffer::DrawLineList(int count) +{ + glDrawArrays(GL_LINES, 0, count * 2); +} + +void OpenGLSWFrameBuffer::DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount) +{ + glDrawRangeElements(GL_TRIANGLES, minIndex, minIndex + numVertices - 1, primitiveCount * 3, GL_UNSIGNED_SHORT, (const void*)(startIndex * sizeof(uint16_t))); +} + +void OpenGLSWFrameBuffer::Present() +{ + int clientWidth = GetClientWidth(); + int clientHeight = GetClientHeight(); + if (clientWidth > 0 && clientHeight > 0) + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, clientWidth, clientHeight); + + float scale = MIN(clientWidth / (float)Width, clientHeight / (float)Height); + int letterboxWidth = (int)round(Width * scale); + int letterboxHeight = (int)round(Height * scale); + int letterboxX = (clientWidth - letterboxWidth) / 2; + int letterboxY = (clientHeight - letterboxHeight) / 2; + + DrawLetterbox(letterboxX, letterboxY, letterboxWidth, letterboxHeight); + glViewport(letterboxX, letterboxY, letterboxWidth, letterboxHeight); + + FBVERTEX verts[4]; + CalcFullscreenCoords(verts, false, 0, 0xFFFFFFFF); + SetTexture(0, OutputFB->Texture); + SetPixelShader(Shaders[SHADER_GammaCorrection]); + SetAlphaBlend(0); + EnableAlphaTest(false); + DrawTriangleFans(2, verts); + } + + SwapBuffers(); + Debug->Update(); + + float screensize[4] = { (float)Width, (float)Height, 1.0f, 1.0f }; + SetPixelShaderConstantF(PSCONST_ScreenSize, screensize, 1); + + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); + glViewport(0, 0, Width, Height); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: SetInitialState +// +// Called after initial device creation and reset, when everything is set +// to OpenGL's defaults. +// +//========================================================================== + +void OpenGLSWFrameBuffer::SetInitialState() +{ + if (gl.es) UseMappedMemBuffer = false; + + AlphaBlendEnabled = false; + AlphaBlendOp = GL_FUNC_ADD; + AlphaSrcBlend = 0; + AlphaDestBlend = 0; + + CurPixelShader = nullptr; + memset(Constant, 0, sizeof(Constant)); + + for (unsigned i = 0; i < countof(Texture); ++i) + { + Texture[i] = nullptr; + SamplerWrapS[i] = GL_CLAMP_TO_EDGE; + SamplerWrapT[i] = GL_CLAMP_TO_EDGE; + } + + NeedGammaUpdate = true; + NeedPalUpdate = true; + + // This constant is used for grayscaling weights (.xyz) and color inversion (.w) + float weights[4] = { 77 / 256.f, 143 / 256.f, 37 / 256.f, 1 }; + SetPixelShaderConstantF(PSCONST_Weights, weights, 1); + + float screensize[4] = { (float)Width, (float)Height, 1.0f, 1.0f }; + SetPixelShaderConstantF(PSCONST_ScreenSize, screensize, 1); + + AlphaTestEnabled = false; + + CurBorderColor = 0; + + // Clear to black, just in case it wasn't done already. + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateResources +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreateResources() +{ + Atlases = nullptr; + if (!LoadShaders()) + return false; + + if (!CreateFrameBuffer("OutputFB", Width, Height, &OutputFB)) + return false; + + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); + + if (!CreateFBTexture() || + !CreatePaletteTexture()) + { + return false; + } + if (!CreateVertexes()) + { + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: LoadShaders +// +// Returns true if all required shaders were loaded. (Gamma and burn wipe +// are the only ones not considered "required".) +// +//========================================================================== + +bool OpenGLSWFrameBuffer::LoadShaders() +{ + int lumpvert = Wads.CheckNumForFullName("shaders/glsl/swshader.vp"); + int lumpfrag = Wads.CheckNumForFullName("shaders/glsl/swshader.fp"); + if (lumpvert < 0 || lumpfrag < 0) + return false; + + FString vertsource = Wads.ReadLump(lumpvert).GetString(); + FString fragsource = Wads.ReadLump(lumpfrag).GetString(); + + FString shaderdir, shaderpath; + unsigned int i; + + for (i = 0; i < NUM_SHADERS; ++i) + { + shaderpath = shaderdir; + if (!CreatePixelShader(vertsource, fragsource, ShaderDefines[i], &Shaders[i]) && i < SHADER_BurnWipe) + { + break; + } + + glUseProgram(Shaders[i]->Program); + if (Shaders[i]->ImageLocation != -1) glUniform1i(Shaders[i]->ImageLocation, 0); + if (Shaders[i]->PaletteLocation != -1) glUniform1i(Shaders[i]->PaletteLocation, 1); + if (Shaders[i]->NewScreenLocation != -1) glUniform1i(Shaders[i]->NewScreenLocation, 0); + if (Shaders[i]->BurnLocation != -1) glUniform1i(Shaders[i]->BurnLocation, 1); + glUseProgram(0); + } + if (i == NUM_SHADERS) + { // Success! + return true; + } + // Failure. Release whatever managed to load (which is probably nothing.) + for (i = 0; i < NUM_SHADERS; ++i) + { + SafeRelease(Shaders[i]); + } + return false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: ReleaseResources +// +//========================================================================== + +void OpenGLSWFrameBuffer::ReleaseResources() +{ +#ifdef WIN32 + I_SaveWindowedPos(); +#endif + KillNativeTexs(); + KillNativePals(); + ReleaseDefaultPoolItems(); + SafeRelease(ScreenshotTexture); + SafeRelease(PaletteTexture); + for (int i = 0; i < NUM_SHADERS; ++i) + { + SafeRelease(Shaders[i]); + } + if (ScreenWipe != nullptr) + { + delete ScreenWipe; + ScreenWipe = nullptr; + } + Atlas *pack, *next; + for (pack = Atlases; pack != nullptr; pack = next) + { + next = pack->Next; + delete pack; + } + GatheringWipeScreen = false; +} + +void OpenGLSWFrameBuffer::ReleaseDefaultPoolItems() +{ + SafeRelease(FBTexture); + SafeRelease(FinalWipeScreen); + SafeRelease(InitialWipeScreen); + SafeRelease(VertexBuffer); + SafeRelease(IndexBuffer); + SafeRelease(OutputFB); +} + +bool OpenGLSWFrameBuffer::Reset() +{ + ReleaseDefaultPoolItems(); + + if (!CreateFrameBuffer("OutputFB", Width, Height, &OutputFB) || !CreateFBTexture() || !CreateVertexes()) + { + return false; + } + + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); + glViewport(0, 0, Width, Height); + + SetInitialState(); + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: KillNativePals +// +// Frees all native palettes. +// +//========================================================================== + +void OpenGLSWFrameBuffer::KillNativePals() +{ + while (Palettes != nullptr) + { + delete Palettes; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: KillNativeTexs +// +// Frees all native textures. +// +//========================================================================== + +void OpenGLSWFrameBuffer::KillNativeTexs() +{ + while (Textures != nullptr) + { + delete Textures; + } +} + +bool OpenGLSWFrameBuffer::CreateFBTexture() +{ + return CreateTexture("FBTexture", Width, Height, 1, IsBgra() ? GL_RGBA8 : GL_R8, &FBTexture); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreatePaletteTexture +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreatePaletteTexture() +{ + return CreateTexture("PaletteTexture", 256, 1, 1, GL_RGBA8, &PaletteTexture); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateVertexes +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreateVertexes() +{ + VertexPos = -1; + IndexPos = -1; + QuadBatchPos = -1; + BatchType = BATCH_None; + if (!CreateVertexBuffer(sizeof(FBVERTEX)*NUM_VERTS, &VertexBuffer)) + { + return false; + } + if (!CreateIndexBuffer(sizeof(uint16_t)*NUM_INDEXES, &IndexBuffer)) + { + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CalcFullscreenCoords +// +//========================================================================== + +void OpenGLSWFrameBuffer::CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, uint32_t color0, uint32_t color1) const +{ + float mxl, mxr, myt, myb, tmxl, tmxr, tmyt, tmyb; + + if (viewarea_only) + { // Just calculate vertices for the viewarea/BlendingRect + mxl = float(BlendingRect.left); + mxr = float(BlendingRect.right); + myt = float(BlendingRect.top); + myb = float(BlendingRect.bottom); + tmxl = float(BlendingRect.left) / float(Width); + tmxr = float(BlendingRect.right) / float(Width); + tmyt = float(BlendingRect.top) / float(Height); + tmyb = float(BlendingRect.bottom) / float(Height); + } + else + { // Calculate vertices for the whole screen + mxl = 0.0f; + mxr = float(Width); + myt = 0.0f; + myb = float(Height); + tmxl = 0; + tmxr = 1.0f; + tmyt = 0; + tmyb = 1.0f; + } + + //{ mxl, myt, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyt }, + //{ mxr, myt, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyt }, + //{ mxr, myb, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyb }, + //{ mxl, myb, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyb }, + + verts[0].x = mxl; + verts[0].y = myt; + verts[0].z = 0; + verts[0].rhw = 1; + verts[0].color0 = color0; + verts[0].color1 = color1; + verts[0].tu = tmxl; + verts[0].tv = tmyt; + + verts[1].x = mxr; + verts[1].y = myt; + verts[1].z = 0; + verts[1].rhw = 1; + verts[1].color0 = color0; + verts[1].color1 = color1; + verts[1].tu = tmxr; + verts[1].tv = tmyt; + + verts[2].x = mxr; + verts[2].y = myb; + verts[2].z = 0; + verts[2].rhw = 1; + verts[2].color0 = color0; + verts[2].color1 = color1; + verts[2].tu = tmxr; + verts[2].tv = tmyb; + + verts[3].x = mxl; + verts[3].y = myb; + verts[3].z = 0; + verts[3].rhw = 1; + verts[3].color0 = color0; + verts[3].color1 = color1; + verts[3].tu = tmxl; + verts[3].tv = tmyb; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: GetPageCount +// +//========================================================================== + +int OpenGLSWFrameBuffer::GetPageCount() +{ + return 1; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: IsValid +// +//========================================================================== + +bool OpenGLSWFrameBuffer::IsValid() +{ + return Valid; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Lock +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Lock(bool buffered) +{ + if (LockCount++ > 0) + { + return false; + } + assert(!In2D); + Accel2D = vid_hw2d; + if (UseMappedMemBuffer) + { + if (!MappedMemBuffer) + { + BindFBBuffer(); + + MappedMemBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_WRITE); + Pitch = Width; + if (MappedMemBuffer == nullptr) + return true; + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + Buffer = (uint8_t*)MappedMemBuffer; + } + else + { + Buffer = MemBuffer; + } + return false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Unlock +// +//========================================================================== + +void OpenGLSWFrameBuffer::Unlock() +{ + if (LockCount == 0) + { + return; + } + + if (UpdatePending && LockCount == 1) + { + Update(); + } + else if (--LockCount == 0) + { + Buffer = nullptr; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Update +// +// When In2D == 0: Copy buffer to screen and present +// When In2D == 1: Copy buffer to screen but do not present +// When In2D == 2: Set up for 2D drawing but do not draw anything +// When In2D == 3: Present and set In2D to 0 +// +//========================================================================== + +void OpenGLSWFrameBuffer::Update() +{ + if (In2D == 3) + { + if (InScene) + { + DrawRateStuff(); + DrawPackedTextures(gl_showpacks); + EndBatch(); // Make sure all batched primitives are drawn. + Flip(); + } + In2D = 0; + return; + } + + if (LockCount != 1) + { + I_FatalError("Framebuffer must have exactly 1 lock to be updated"); + if (LockCount > 0) + { + UpdatePending = true; + --LockCount; + } + return; + } + + if (In2D == 0) + { + DrawRateStuff(); + } + + if (NeedGammaUpdate) + { + float psgamma[4]; + float igamma; + + NeedGammaUpdate = false; + igamma = 1 / Gamma; + if (IsFullscreen()) + { + GammaRamp ramp; + + for (int i = 0; i < 256; ++i) + { + ramp.blue[i] = ramp.green[i] = ramp.red[i] = uint16_t(65535.f * powf(i / 255.f, igamma)); + } + SetGammaRamp(&ramp); + } + psgamma[2] = psgamma[1] = psgamma[0] = igamma; + psgamma[3] = 0.5; // For SM14 version + SetPixelShaderConstantF(PSCONST_Gamma, psgamma, 1); + } + + if (NeedPalUpdate) + { + UploadPalette(); + NeedPalUpdate = false; + } + +#ifdef WIN32 + BlitCycles.Reset(); + BlitCycles.Clock(); +#endif + + LockCount = 0; + Draw3DPart(In2D <= 1); + if (In2D == 0) + { + Flip(); + } + +#ifdef WIN32 + BlitCycles.Unclock(); + //LOG1 ("cycles = %d\n", BlitCycles); +#endif + + Buffer = nullptr; + UpdatePending = false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Flip +// +//========================================================================== + +void OpenGLSWFrameBuffer::Flip() +{ + assert(InScene); + + Present(); + InScene = false; + + if (!IsFullscreen()) + { + int clientWidth = ClampWidth(GetClientWidth()); + int clientHeight = ClampHeight(GetClientHeight()); + if (clientWidth > 0 && clientHeight > 0 && (Width != clientWidth || Height != clientHeight)) + { + Resize(clientWidth, clientHeight); + + TrueHeight = Height; + PixelDoubling = 0; + Reset(); + + V_OutputResized(Width, Height); + } + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: PaintToWindow +// +//========================================================================== + +#ifdef WIN32 + +bool OpenGLSWFrameBuffer::PaintToWindow() +{ + if (LockCount != 0) + { + return false; + } + Draw3DPart(true); + return true; +} + +#endif + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Draw3DPart +// +// The software 3D part, to be exact. +// +//========================================================================== + +void OpenGLSWFrameBuffer::BindFBBuffer() +{ + int usage = UseMappedMemBuffer ? GL_DYNAMIC_DRAW : GL_STREAM_DRAW; + + int pixelsize = IsBgra() ? 4 : 1; + int size = Width * Height * pixelsize; + + if (FBTexture->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)FBTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, size, nullptr, usage); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, size, nullptr, usage); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[FBTexture->CurrentBuffer]); + } +} + +void OpenGLSWFrameBuffer::BgraToRgba(uint32_t *dest, const uint32_t *src, int width, int height, int srcpitch) +{ + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + uint32_t r = RPART(src[x]); + uint32_t g = GPART(src[x]); + uint32_t b = BPART(src[x]); + uint32_t a = APART(src[x]); + dest[x] = r | (g << 8) | (b << 16) | (a << 24); + } + dest += width; + src += srcpitch; + } +} + +void OpenGLSWFrameBuffer::Draw3DPart(bool copy3d) +{ + if (copy3d) + { + BindFBBuffer(); + FBTexture->CurrentBuffer = (FBTexture->CurrentBuffer + 1) & 1; + + if (!UseMappedMemBuffer) + { + int pixelsize = IsBgra() ? 4 : 1; + int size = Width * Height * pixelsize; + + uint8_t *dest = (uint8_t*)MapBuffer(GL_PIXEL_UNPACK_BUFFER, size); + if (dest) + { + if (gl.es && pixelsize == 4) + { + BgraToRgba((uint32_t*)dest, (const uint32_t *)MemBuffer, Width, Height, Pitch); + } + else if (Pitch == Width) + { + memcpy(dest, MemBuffer, Width * Height * pixelsize); + } + else + { + uint8_t *src = MemBuffer; + for (int y = 0; y < Height; y++) + { + memcpy(dest, src, Width * pixelsize); + dest += Width * pixelsize; + src += Pitch * pixelsize; + } + } + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + } + } + else if (MappedMemBuffer) + { + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + MappedMemBuffer = nullptr; + } + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, FBTexture->Texture); + if (IsBgra()) + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Width, Height, gl.es ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 0); + else + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Width, Height, GL_RED, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + InScene = true; + if (vid_hwaalines) + glEnable(GL_LINE_SMOOTH); + else + glDisable(GL_LINE_SMOOTH); + + SetTexture(0, FBTexture); + SetPaletteTexture(PaletteTexture, 256, BorderColor); + memset(Constant, 0, sizeof(Constant)); + SetAlphaBlend(0); + EnableAlphaTest(false); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_NormalColor]); + else + SetPixelShader(Shaders[SHADER_NormalColorPal]); + if (copy3d) + { + FBVERTEX verts[4]; + uint32_t color0, color1; + if (Accel2D) + { + auto map = swrenderer::CameraLight::Instance()->ShaderColormap(); + if (map == nullptr) + { + color0 = 0; + color1 = 0xFFFFFFF; + } + else + { + color0 = ColorValue(map->ColorizeStart[0] / 2, map->ColorizeStart[1] / 2, map->ColorizeStart[2] / 2, 0); + color1 = ColorValue(map->ColorizeEnd[0] / 2, map->ColorizeEnd[1] / 2, map->ColorizeEnd[2] / 2, 1); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_SpecialColormap]); + else + SetPixelShader(Shaders[SHADER_SpecialColormapPal]); + } + } + else + { + color0 = FlashColor0; + color1 = FlashColor1; + } + CalcFullscreenCoords(verts, Accel2D, color0, color1); + DrawTriangleFans(2, verts); + } + if (IsBgra()) + SetPixelShader(Shaders[SHADER_NormalColor]); + else + SetPixelShader(Shaders[SHADER_NormalColorPal]); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawLetterbox +// +// Draws the black bars at the top and bottom of the screen for letterboxed +// modes. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawLetterbox(int x, int y, int width, int height) +{ + int clientWidth = GetClientWidth(); + int clientHeight = GetClientHeight(); + if (clientWidth == 0 || clientHeight == 0) + return; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glEnable(GL_SCISSOR_TEST); + if (y > 0) + { + glScissor(0, 0, clientWidth, y); + glClear(GL_COLOR_BUFFER_BIT); + } + if (clientHeight - y - height > 0) + { + glScissor(0, y + height, clientWidth, clientHeight - y - height); + glClear(GL_COLOR_BUFFER_BIT); + } + if (x > 0) + { + glScissor(0, y, x, height); + glClear(GL_COLOR_BUFFER_BIT); + } + if (clientWidth - x - width > 0) + { + glScissor(x + width, y, clientWidth - x - width, height); + glClear(GL_COLOR_BUFFER_BIT); + } + glDisable(GL_SCISSOR_TEST); +} + +void OpenGLSWFrameBuffer::UploadPalette() +{ + if (PaletteTexture->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)PaletteTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 4, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 4, nullptr, GL_STREAM_DRAW); + + if (gl.es) PaletteTexture->MapBuffer.resize(256 * 4); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[PaletteTexture->CurrentBuffer]); + PaletteTexture->CurrentBuffer = (PaletteTexture->CurrentBuffer + 1) & 1; + } + + uint8_t *pix = gl.es ? PaletteTexture->MapBuffer.data() : (uint8_t*)MapBuffer(GL_PIXEL_UNPACK_BUFFER, 256 * 4); + if (pix) + { + int i; + + for (i = 0; i < 256; ++i, pix += 4) + { + pix[0] = SourcePalette[i].b; + pix[1] = SourcePalette[i].g; + pix[2] = SourcePalette[i].r; + pix[3] = (i == 0 ? 0 : 255); + // To let masked textures work, the first palette entry's alpha is 0. + } + pix += 4; + for (; i < 255; ++i, pix += 4) + { + pix[0] = SourcePalette[i].b; + pix[1] = SourcePalette[i].g; + pix[2] = SourcePalette[i].r; + pix[3] = 255; + } + if (gl.es) + { + uint8_t *tempbuffer = PaletteTexture->MapBuffer.data(); + BgraToRgba((uint32_t*)tempbuffer, (const uint32_t *)tempbuffer, 256, 1, 256); + glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 4, tempbuffer); + } + else + { + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + } + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, PaletteTexture->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 1, gl.es ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + BorderColor = ColorXRGB(SourcePalette[255].r, SourcePalette[255].g, SourcePalette[255].b); + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +PalEntry *OpenGLSWFrameBuffer::GetPalette() +{ + return SourcePalette; +} + +void OpenGLSWFrameBuffer::UpdatePalette() +{ + NeedPalUpdate = true; +} + +bool OpenGLSWFrameBuffer::SetGamma(float gamma) +{ + Gamma = gamma; + NeedGammaUpdate = true; + return true; +} + +bool OpenGLSWFrameBuffer::SetFlash(PalEntry rgb, int amount) +{ + FlashColor = rgb; + FlashAmount = amount; + + // Fill in the constants for the pixel shader to do linear interpolation between the palette and the flash: + float r = rgb.r / 255.f, g = rgb.g / 255.f, b = rgb.b / 255.f, a = amount / 256.f; + FlashColor0 = ColorValue(r * a, g * a, b * a, 0); + a = 1 - a; + FlashColor1 = ColorValue(a, a, a, 1); + return true; +} + +void OpenGLSWFrameBuffer::GetFlash(PalEntry &rgb, int &amount) +{ + rgb = FlashColor; + amount = FlashAmount; +} + +void OpenGLSWFrameBuffer::GetFlashedPalette(PalEntry pal[256]) +{ + memcpy(pal, SourcePalette, 256 * sizeof(PalEntry)); + if (FlashAmount) + { + DoBlending(pal, pal, 256, FlashColor.r, FlashColor.g, FlashColor.b, FlashAmount); + } +} + +void OpenGLSWFrameBuffer::SetVSync(bool vsync) +{ + // Switch to the default frame buffer because Nvidia's driver associates the vsync state with the bound FB object. + GLint oldDrawFramebufferBinding = 0, oldReadFramebufferBinding = 0; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &oldDrawFramebufferBinding); + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &oldReadFramebufferBinding); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + + Super::SetVSync(vsync); + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oldDrawFramebufferBinding); + glBindFramebuffer(GL_READ_FRAMEBUFFER, oldReadFramebufferBinding); +} + +void OpenGLSWFrameBuffer::NewRefreshRate() +{ +} + +void OpenGLSWFrameBuffer::SetBlendingRect(int x1, int y1, int x2, int y2) +{ + BlendingRect.left = x1; + BlendingRect.top = y1; + BlendingRect.right = x2; + BlendingRect.bottom = y2; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: GetScreenshotBuffer +// +// Returns a pointer into a surface holding the current screen data. +// +//========================================================================== + +void OpenGLSWFrameBuffer::GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) +{ + Super::GetScreenshotBuffer(buffer, pitch, color_type); + /* + LockedRect lrect; + + if (!Accel2D) + { + Super::GetScreenshotBuffer(buffer, pitch, color_type); + return; + } + buffer = nullptr; + if ((ScreenshotTexture = GetCurrentScreen()) != nullptr) + { + if (!ScreenshotTexture->GetSurfaceLevel(0, &ScreenshotSurface)) + { + delete ScreenshotTexture; + ScreenshotTexture = nullptr; + } + else if (!ScreenshotSurface->LockRect(&lrect, nullptr, false)) + { + delete ScreenshotSurface; + ScreenshotSurface = nullptr; + delete ScreenshotTexture; + ScreenshotTexture = nullptr; + } + else + { + buffer = (const uint8_t *)lrect.pBits; + pitch = lrect.Pitch; + color_type = SS_BGRA; + } + } + */ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: ReleaseScreenshotBuffer +// +//========================================================================== + +void OpenGLSWFrameBuffer::ReleaseScreenshotBuffer() +{ + if (LockCount > 0) + { + Super::ReleaseScreenshotBuffer(); + } + SafeRelease(ScreenshotTexture); +} + +/**************************************************************************/ +/* 2D Stuff */ +/**************************************************************************/ + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawPackedTextures +// +// DEBUG: Draws the texture atlases to the screen, starting with the +// 1-based packnum. Ignores atlases that are flagged for use by one +// texture only. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawPackedTextures(int packnum) +{ + uint32_t empty_colors[8] = + { + 0x50FF0000, 0x5000FF00, 0x500000FF, 0x50FFFF00, + 0x50FF00FF, 0x5000FFFF, 0x50FF8000, 0x500080FF + }; + Atlas *pack; + int x = 8, y = 8; + + if (packnum <= 0) + { + return; + } + pack = Atlases; + // Find the first texture atlas that is an actual atlas. + while (pack != nullptr && pack->OneUse) + { // Skip textures that aren't used as atlases + pack = pack->Next; + } + // Skip however many atlases we would have otherwise drawn + // until we've skipped of them. + while (pack != nullptr && packnum != 1) + { + if (!pack->OneUse) + { // Skip textures that aren't used as atlases + packnum--; + } + pack = pack->Next; + } + // Draw atlases until we run out of room on the screen. + while (pack != nullptr) + { + if (pack->OneUse) + { // Skip textures that aren't used as atlases + pack = pack->Next; + continue; + } + + AddColorOnlyRect(x - 1, y - 1, 258, 258, ColorXRGB(255, 255, 0)); + int back = 0; + for (PackedTexture *box = pack->UsedList; box != nullptr; box = box->Next) + { + AddColorOnlyQuad( + x + box->Area.left * 256 / pack->Width, + y + box->Area.top * 256 / pack->Height, + (box->Area.right - box->Area.left) * 256 / pack->Width, + (box->Area.bottom - box->Area.top) * 256 / pack->Height, empty_colors[back]); + back = (back + 1) & 7; + } + // AddColorOnlyQuad(x, y-LBOffsetI, 256, 256, ColorARGB(180,0,0,0)); + + CheckQuadBatch(); + + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + FBVERTEX *vert = &VertexData[VertexPos]; + + quad->ClearSetup(); + if (pack->Format == GL_R8/* && !tex->IsGray*/) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette/* | BQF_DisableAlphaTest*/; + quad->ShaderNum = BQS_PalTex; + } + else + { + quad->Flags = BQF_WrapUV/* | BQF_DisableAlphaTest*/; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = pack->Tex; + quad->NumVerts = 4; + quad->NumTris = 2; + + float x0 = float(x); + float y0 = float(y); + float x1 = x0 + 256.f; + float y1 = y0 + 256.f; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFFF; + vert[0].tu = 0; + vert[0].tv = 0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFFF; + vert[1].tu = 1; + vert[1].tv = 0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFFF; + vert[2].tu = 1; + vert[2].tv = 1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFFF; + vert[3].tu = 0; + vert[3].tv = 1; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; + + x += 256 + 8; + if (x > Width - 256) + { + x = 8; + y += 256 + 8; + if (y > Height - 256) + { + return; + } + } + pack = pack->Next; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AllocPackedTexture +// +// Finds space to pack an image inside a texture atlas and returns it. +// Large images and those that need to wrap always get their own textures. +// +//========================================================================== + +OpenGLSWFrameBuffer::PackedTexture *OpenGLSWFrameBuffer::AllocPackedTexture(int w, int h, bool wrapping, int format) +{ + Atlas *pack; + Rect box; + bool padded; + + // The - 2 to account for padding + if (w > 256 - 2 || h > 256 - 2 || wrapping) + { // Create a new texture atlas. + pack = new Atlas(this, w, h, format); + pack->OneUse = true; + box = pack->Packer.Insert(w, h); + padded = false; + } + else + { // Try to find space in an existing texture atlas. + w += 2; // Add padding + h += 2; + for (pack = Atlases; pack != nullptr; pack = pack->Next) + { + // Use the first atlas it fits in. + if (pack->Format == format) + { + box = pack->Packer.Insert(w, h); + if (box.width != 0) + { + break; + } + } + } + if (pack == nullptr) + { // Create a new texture atlas. + pack = new Atlas(this, DEF_ATLAS_WIDTH, DEF_ATLAS_HEIGHT, format); + box = pack->Packer.Insert(w, h); + } + padded = true; + } + assert(box.width != 0 && box.height != 0); + return pack->AllocateImage(box, padded); +} + +//========================================================================== +// +// Atlas Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Atlas::Atlas(OpenGLSWFrameBuffer *fb, int w, int h, int format) + : Packer(w, h, true) +{ + Tex = nullptr; + Format = format; + UsedList = nullptr; + OneUse = false; + Width = 0; + Height = 0; + Next = nullptr; + + // Attach to the end of the atlas list + Atlas **prev = &fb->Atlases; + while (*prev != nullptr) + { + prev = &((*prev)->Next); + } + *prev = this; + + fb->CreateTexture("Atlas", w, h, 1, format, &Tex); + Width = w; + Height = h; +} + +//========================================================================== +// +// Atlas Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Atlas::~Atlas() +{ + PackedTexture *box, *next; + + SafeRelease(Tex); + for (box = UsedList; box != nullptr; box = next) + { + next = box->Next; + delete box; + } +} + +//========================================================================== +// +// Atlas :: AllocateImage +// +// Moves the box from the empty list to the used list, sizing it to the +// requested dimensions and adding additional boxes to the empty list if +// needed. +// +// The passed box *MUST* be in this texture atlas's empty list. +// +//========================================================================== + +OpenGLSWFrameBuffer::PackedTexture *OpenGLSWFrameBuffer::Atlas::AllocateImage(const Rect &rect, bool padded) +{ + PackedTexture *box = new PackedTexture; + + box->Owner = this; + box->Area.left = rect.x; + box->Area.top = rect.y; + box->Area.right = rect.x + rect.width; + box->Area.bottom = rect.y + rect.height; + + box->Left = float(box->Area.left + padded) / Width; + box->Right = float(box->Area.right - padded) / Width; + box->Top = float(box->Area.top + padded) / Height; + box->Bottom = float(box->Area.bottom - padded) / Height; + + box->Padded = padded; + + // Add it to the used list. + box->Next = UsedList; + if (box->Next != nullptr) + { + box->Next->Prev = &box->Next; + } + UsedList = box; + box->Prev = &UsedList; + + return box; +} + +//========================================================================== +// +// Atlas :: FreeBox +// +// Removes a box from the used list and deletes it. Space is returned to the +// waste list. Once all boxes for this atlas are freed, the entire bin +// packer is reinitialized for maximum efficiency. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Atlas::FreeBox(OpenGLSWFrameBuffer::PackedTexture *box) +{ + *(box->Prev) = box->Next; + if (box->Next != nullptr) + { + box->Next->Prev = box->Prev; + } + Rect waste; + waste.x = box->Area.left; + waste.y = box->Area.top; + waste.width = box->Area.right - box->Area.left; + waste.height = box->Area.bottom - box->Area.top; + box->Owner->Packer.AddWaste(waste); + delete box; + if (UsedList == nullptr) + { + Packer.Init(Width, Height, true); + } +} + +//========================================================================== +// +// OpenGLTex Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLTex::OpenGLTex(FTexture *tex, OpenGLSWFrameBuffer *fb, bool wrapping) +{ + // Attach to the texture list for the OpenGLSWFrameBuffer + Next = fb->Textures; + if (Next != nullptr) + { + Next->Prev = &Next; + } + Prev = &fb->Textures; + fb->Textures = this; + + GameTex = tex; + Box = nullptr; + IsGray = false; + + Create(fb, wrapping); +} + +//========================================================================== +// +// OpenGLTex Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLTex::~OpenGLTex() +{ + if (Box != nullptr) + { + Box->Owner->FreeBox(Box); + Box = nullptr; + } + // Detach from the texture list + *Prev = Next; + if (Next != nullptr) + { + Next->Prev = Prev; + } + // Remove link from the game texture + if (GameTex != nullptr) + { + GameTex->Native = nullptr; + } +} + +//========================================================================== +// +// OpenGLTex :: CheckWrapping +// +// Returns true if the texture is compatible with the specified wrapping +// mode. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::CheckWrapping(bool wrapping) +{ + // If it doesn't need to wrap, then it works. + if (!wrapping) + { + return true; + } + // If it needs to wrap, then it can't be packed inside another texture. + return Box->Owner->OneUse; +} + +//========================================================================== +// +// OpenGLTex :: Create +// +// Creates an HWTexture for the texture and copies the image data +// to it. Note that unlike FTexture, this image is row-major. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::Create(OpenGLSWFrameBuffer *fb, bool wrapping) +{ + assert(Box == nullptr); + if (Box != nullptr) + { + Box->Owner->FreeBox(Box); + } + + Box = fb->AllocPackedTexture(GameTex->GetWidth(), GameTex->GetHeight(), wrapping, GetTexFormat()); + + if (Box == nullptr) + { + return false; + } + if (!Update()) + { + Box->Owner->FreeBox(Box); + Box = nullptr; + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLTex :: Update +// +// Copies image data from the underlying FTexture to the OpenGL texture. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::Update() +{ + LTRBRect rect; + uint8_t *dest; + + assert(Box != nullptr); + assert(Box->Owner != nullptr); + assert(Box->Owner->Tex != nullptr); + assert(GameTex != nullptr); + + int format = Box->Owner->Tex->Format; + + rect = Box->Area; + + if (Box->Owner->Tex->Buffers[0] == 0) + glGenBuffers(2, (GLuint*)Box->Owner->Tex->Buffers); + + int bytesPerPixel = 4; + switch (format) + { + case GL_R8: bytesPerPixel = 1; break; + case GL_RGBA8: bytesPerPixel = 4; break; + default: return false; + } + + int buffersize = (rect.right - rect.left) * (rect.bottom - rect.top) * bytesPerPixel; + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Box->Owner->Tex->Buffers[Box->Owner->Tex->CurrentBuffer]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, buffersize, nullptr, GL_STREAM_DRAW); + Box->Owner->Tex->CurrentBuffer = (Box->Owner->Tex->CurrentBuffer + 1) & 1; + + static std::vector tempbuffer; + if (gl.es) + tempbuffer.resize(buffersize); + + int pitch = (rect.right - rect.left) * bytesPerPixel; + uint8_t *bits = gl.es ? tempbuffer.data() : (uint8_t *)MapBuffer(GL_PIXEL_UNPACK_BUFFER, buffersize); + dest = bits; + if (!dest) + { + return false; + } + if (Box->Padded) + { + dest += pitch + (format == GL_R8 ? 1 : 4); + } + GameTex->FillBuffer(dest, pitch, GameTex->GetHeight(), ToTexFmt(format)); + if (Box->Padded) + { + // Clear top padding row. + dest = bits; + int numbytes = GameTex->GetWidth() + 2; + if (format != GL_R8) + { + numbytes <<= 2; + } + memset(dest, 0, numbytes); + dest += pitch; + // Clear left and right padding columns. + if (format == GL_R8) + { + for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y) + { + dest[0] = 0; + dest[numbytes - 1] = 0; + dest += pitch; + } + } + else + { + for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y) + { + *(uint32_t *)dest = 0; + *(uint32_t *)(dest + numbytes - 4) = 0; + dest += pitch; + } + } + // Clear bottom padding row. + memset(dest, 0, numbytes); + } + + if (gl.es && format == GL_RGBA8) + { + BgraToRgba((uint32_t*)bits, (const uint32_t *)bits, rect.right - rect.left, rect.bottom - rect.top, rect.right - rect.left); + } + + if (gl.es) + glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, buffersize, bits); + else + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, Box->Owner->Tex->Texture); + if (format == GL_RGBA8) + glTexSubImage2D(GL_TEXTURE_2D, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, gl.es ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 0); + else + glTexSubImage2D(GL_TEXTURE_2D, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, GL_RED, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + return true; +} + +//========================================================================== +// +// OpenGLTex :: GetTexFormat +// +// Returns the texture format that would best fit this texture. +// +//========================================================================== + +int OpenGLSWFrameBuffer::OpenGLTex::GetTexFormat() +{ + FTextureFormat fmt = GameTex->GetFormat(); + + IsGray = false; + + switch (fmt) + { + case TEX_Pal: return GL_R8; + case TEX_Gray: IsGray = true; return GL_R8; + case TEX_RGB: return GL_RGBA8; + case TEX_DXT1: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + case TEX_DXT2: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case TEX_DXT3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case TEX_DXT4: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; // Doesn't exist in OpenGL. Closest match is DXT5. + case TEX_DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + default: I_FatalError("GameTex->GetFormat() returned invalid format."); + } + return GL_R8; +} + +//========================================================================== +// +// OpenGLTex :: ToTexFmt +// +// Converts an OpenGL internal format constant to something the FTexture system +// understands. +// +//========================================================================== + +FTextureFormat OpenGLSWFrameBuffer::OpenGLTex::ToTexFmt(int fmt) +{ + switch (fmt) + { + case GL_R8: return IsGray ? TEX_Gray : TEX_Pal; + case GL_RGBA8: return TEX_RGB; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return TEX_DXT1; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return TEX_DXT2; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return TEX_DXT3; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return TEX_DXT5; + default: + assert(0); // LOL WUT? + return TEX_Pal; + } +} + +//========================================================================== +// +// OpenGLPal Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLPal::OpenGLPal(FRemapTable *remap, OpenGLSWFrameBuffer *fb) + : Tex(nullptr), Remap(remap) +{ + int count; + + // Attach to the palette list for the OpenGLSWFrameBuffer + Next = fb->Palettes; + if (Next != nullptr) + { + Next->Prev = &Next; + } + Prev = &fb->Palettes; + fb->Palettes = this; + + int pow2count; + + // Round up to the nearest power of 2. + for (pow2count = 1; pow2count < remap->NumEntries; pow2count <<= 1) + { + } + count = pow2count; + DoColorSkip = false; + + BorderColor = 0; + RoundedPaletteSize = count; + if (fb->CreateTexture("Pal", count, 1, 1, GL_RGBA8, &Tex)) + { + if (!Update()) + { + delete Tex; + Tex = nullptr; + } + } +} + +//========================================================================== +// +// OpenGLPal Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLPal::~OpenGLPal() +{ + SafeRelease(Tex); + // Detach from the palette list + *Prev = Next; + if (Next != nullptr) + { + Next->Prev = Prev; + } + // Remove link from the remap table + if (Remap != nullptr) + { + Remap->Native = nullptr; + } +} + +//========================================================================== +// +// OpenGLPal :: Update +// +// Copies the palette to the texture. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLPal::Update() +{ + uint32_t *buff; + const PalEntry *pal; + int skipat, i; + + assert(Tex != nullptr); + + if (Tex->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)Tex->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, RoundedPaletteSize * 4, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, RoundedPaletteSize * 4, nullptr, GL_STREAM_DRAW); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[Tex->CurrentBuffer]); + Tex->CurrentBuffer = (Tex->CurrentBuffer + 1) & 1; + } + + int numEntries = MIN(Remap->NumEntries, RoundedPaletteSize); + + std::vector &tempbuffer = Tex->MapBuffer; + if (gl.es) + tempbuffer.resize(numEntries * 4); + + buff = gl.es ? (uint32_t*)tempbuffer.data() : (uint32_t *)MapBuffer(GL_PIXEL_UNPACK_BUFFER, numEntries * 4); + if (buff == nullptr) + { + return false; + } + pal = Remap->Palette; + + // See explanation in UploadPalette() for skipat rationale. + skipat = MIN(numEntries, DoColorSkip ? 256 - 8 : 256); + + for (i = 0; i < skipat; ++i) + { + buff[i] = ColorARGB(pal[i].a, pal[i].r, pal[i].g, pal[i].b); + } + for (++i; i < numEntries; ++i) + { + buff[i] = ColorARGB(pal[i].a, pal[i - 1].r, pal[i - 1].g, pal[i - 1].b); + } + if (numEntries > 1) + { + i = numEntries - 1; + BorderColor = ColorARGB(pal[i].a, pal[i - 1].r, pal[i - 1].g, pal[i - 1].b); + } + + if (gl.es) + { + BgraToRgba((uint32_t*)buff, (const uint32_t *)buff, numEntries, 1, numEntries); + glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, numEntries * 4, buff); + } + else + { + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + } + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, Tex->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, numEntries, 1, gl.es ? GL_RGBA : GL_BGRA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Begin2D +// +// Begins 2D mode drawing operations. In particular, DrawTexture is +// rerouted to use Direct3D instead of the software renderer. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Begin2D(bool copy3d) +{ + if (!Accel2D) + { + return false; + } + if (In2D) + { + return true; + } + In2D = 2 - copy3d; + Update(); + In2D = 3; + + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawBlendingRect +// +// Call after Begin2D to blend the 3D view. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawBlendingRect() +{ + if (!In2D || !Accel2D) + { + return; + } + Dim(FlashColor, FlashAmount / 256.f, viewwindowx, viewwindowy, viewwidth, viewheight); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateTexture +// +// Returns a native texture that wraps a FTexture. +// +//========================================================================== + +FNativeTexture *OpenGLSWFrameBuffer::CreateTexture(FTexture *gametex, bool wrapping) +{ + OpenGLTex *tex = new OpenGLTex(gametex, this, wrapping); + if (tex->Box == nullptr) + { + delete tex; + return nullptr; + } + return tex; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreatePalette +// +// Returns a native texture that contains a palette. +// +//========================================================================== + +FNativePalette *OpenGLSWFrameBuffer::CreatePalette(FRemapTable *remap) +{ + OpenGLPal *tex = new OpenGLPal(remap, this); + if (tex->Tex == nullptr) + { + delete tex; + return nullptr; + } + return tex; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Clear +// +// Fills the specified region with a color. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Clear(int left, int top, int right, int bottom, int palcolor, uint32 color) +{ + if (In2D < 2) + { + Super::Clear(left, top, right, bottom, palcolor, color); + return; + } + if (!InScene) + { + return; + } + if (palcolor >= 0 && color == 0) + { + color = GPalette.BaseColors[palcolor]; + } + else if (APART(color) < 255) + { + Dim(color, APART(color) / 255.f, left, top, right - left, bottom - top); + return; + } + AddColorOnlyQuad(left, top, right - left, bottom - top, color | 0xFF000000); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Dim +// +//========================================================================== + +void OpenGLSWFrameBuffer::Dim(PalEntry color, float amount, int x1, int y1, int w, int h) +{ + if (amount <= 0) + { + return; + } + if (In2D < 2) + { + Super::Dim(color, amount, x1, y1, w, h); + return; + } + if (!InScene) + { + return; + } + if (amount > 1) + { + amount = 1; + } + AddColorOnlyQuad(x1, y1, w, h, color | (int(amount * 255) << 24)); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: BeginLineBatch +// +//========================================================================== + +void OpenGLSWFrameBuffer::BeginLineBatch() +{ + if (In2D < 2 || !InScene || BatchType == BATCH_Lines) + { + return; + } + EndQuadBatch(); // Make sure all quads have been drawn first. + VertexData = VertexBuffer->Lock(); + VertexPos = 0; + BatchType = BATCH_Lines; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndLineBatch +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndLineBatch() +{ + if (In2D < 2 || !InScene || BatchType != BATCH_Lines) + { + return; + } + VertexBuffer->Unlock(); + if (VertexPos > 0) + { + SetPixelShader(Shaders[SHADER_VertexColor]); + SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + SetStreamSource(VertexBuffer); + DrawLineList(VertexPos / 2); + } + VertexPos = -1; + BatchType = BATCH_None; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawLine +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawLine(int x0, int y0, int x1, int y1, int palcolor, uint32 color) +{ + if (In2D < 2) + { + Super::DrawLine(x0, y0, x1, y1, palcolor, color); + return; + } + if (!InScene) + { + return; + } + if (BatchType != BATCH_Lines) + { + BeginLineBatch(); + } + if (VertexPos == NUM_VERTS) + { // Flush the buffer and refill it. + EndLineBatch(); + BeginLineBatch(); + } + // Add the endpoints to the vertex buffer. + VertexData[VertexPos].x = float(x0); + VertexData[VertexPos].y = float(y0); + VertexData[VertexPos].z = 0; + VertexData[VertexPos].rhw = 1; + VertexData[VertexPos].color0 = color; + VertexData[VertexPos].color1 = 0; + VertexData[VertexPos].tu = 0; + VertexData[VertexPos].tv = 0; + + VertexData[VertexPos + 1].x = float(x1); + VertexData[VertexPos + 1].y = float(y1); + VertexData[VertexPos + 1].z = 0; + VertexData[VertexPos + 1].rhw = 1; + VertexData[VertexPos + 1].color0 = color; + VertexData[VertexPos + 1].color1 = 0; + VertexData[VertexPos + 1].tu = 0; + VertexData[VertexPos + 1].tv = 0; + + VertexPos += 2; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawPixel +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawPixel(int x, int y, int palcolor, uint32 color) +{ + if (In2D < 2) + { + Super::DrawPixel(x, y, palcolor, color); + return; + } + if (!InScene) + { + return; + } + FBVERTEX pt = + { + float(x), float(y), 0, 1, color + }; + EndBatch(); // Draw out any batched operations. + SetPixelShader(Shaders[SHADER_VertexColor]); + SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + DrawPoints(1, &pt); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawTextureV +// +// If not in 2D mode, just call the normal software version. +// If in 2D mode, then use Direct3D calls to perform the drawing. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawTextureParms(FTexture *img, DrawParms &parms) +{ + if (In2D < 2) + { + Super::DrawTextureParms(img, parms); + return; + } + if (!InScene) + { + return; + } + + OpenGLTex *tex = static_cast(img->GetNative(false)); + + if (tex == nullptr) + { + assert(tex != nullptr); + return; + } + + CheckQuadBatch(); + + double xscale = parms.destwidth / parms.texwidth; + double yscale = parms.destheight / parms.texheight; + double x0 = parms.x - parms.left * xscale; + double y0 = parms.y - parms.top * yscale; + double x1 = x0 + parms.destwidth; + double y1 = y0 + parms.destheight; + float u0 = tex->Box->Left; + float v0 = tex->Box->Top; + float u1 = tex->Box->Right; + float v1 = tex->Box->Bottom; + double uscale = 1.f / tex->Box->Owner->Width; + bool scissoring = false; + FBVERTEX *vert; + + if (parms.flipX) + { + swapvalues(u0, u1); + } + if (parms.windowleft > 0 || parms.windowright < parms.texwidth) + { + double wi = MIN(parms.windowright, parms.texwidth); + x0 += parms.windowleft * xscale; + u0 = float(u0 + parms.windowleft * uscale); + x1 -= (parms.texwidth - wi) * xscale; + u1 = float(u1 - (parms.texwidth - wi) * uscale); + } + +#if 0 + float vscale = 1.f / tex->Box->Owner->Height / yscale; + if (y0 < parms.uclip) + { + v0 += (float(parms.uclip) - y0) * vscale; + y0 = float(parms.uclip); + } + if (y1 > parms.dclip) + { + v1 -= (y1 - float(parms.dclip)) * vscale; + y1 = float(parms.dclip); + } + if (x0 < parms.lclip) + { + u0 += float(parms.lclip - x0) * uscale / xscale * 2; + x0 = float(parms.lclip); + } + if (x1 > parms.rclip) + { + u1 -= (x1 - parms.rclip) * uscale / xscale * 2; + x1 = float(parms.rclip); + } +#else + // Use a scissor test because the math above introduces some jitter + // that is noticeable at low resolutions. Unfortunately, this means this + // quad has to be in a batch by itself. + if (y0 < parms.uclip || y1 > parms.dclip || x0 < parms.lclip || x1 > parms.rclip) + { + scissoring = true; + if (QuadBatchPos > 0) + { + EndQuadBatch(); + BeginQuadBatch(); + } + glEnable(GL_SCISSOR_TEST); + glScissor(parms.lclip, parms.uclip, parms.rclip - parms.lclip, parms.dclip - parms.uclip); + } +#endif + parms.bilinear = false; + + uint32_t color0, color1; + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + + if (!SetStyle(tex, parms, color0, color1, *quad)) + { + goto done; + } + + quad->Texture = tex->Box->Owner->Tex; + if (parms.bilinear) + { + quad->Flags |= BQF_Bilinear; + } + quad->NumTris = 2; + quad->NumVerts = 4; + + vert = &VertexData[VertexPos]; + + // Fill the vertex buffer. + vert[0].x = float(x0); + vert[0].y = float(y0); + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = color0; + vert[0].color1 = color1; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = float(x1); + vert[1].y = float(y0); + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = color0; + vert[1].color1 = color1; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = float(x1); + vert[2].y = float(y1); + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = color0; + vert[2].color1 = color1; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = float(x0); + vert[3].y = float(y1); + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = color0; + vert[3].color1 = color1; + vert[3].tu = u0; + vert[3].tv = v1; + + // Fill the vertex index buffer. + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + // Batch the quad. + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +done: + if (scissoring) + { + EndQuadBatch(); + glDisable(GL_SCISSOR_TEST); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: FlatFill +// +// Fills an area with a repeating copy of the texture. +// +//========================================================================== + +void OpenGLSWFrameBuffer::FlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin) +{ + if (In2D < 2) + { + Super::FlatFill(left, top, right, bottom, src, local_origin); + return; + } + if (!InScene) + { + return; + } + OpenGLTex *tex = static_cast(src->GetNative(true)); + if (tex == nullptr) + { + return; + } + float x0 = float(left); + float y0 = float(top); + float x1 = float(right); + float y1 = float(bottom); + float itw = 1.f / float(src->GetWidth()); + float ith = 1.f / float(src->GetHeight()); + float xo = local_origin ? x0 : 0; + float yo = local_origin ? y0 : 0; + float u0 = (x0 - xo) * itw; + float v0 = (y0 - yo) * ith; + float u1 = (x1 - xo) * itw; + float v1 = (y1 - yo) * ith; + + CheckQuadBatch(); + + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + FBVERTEX *vert = &VertexData[VertexPos]; + + quad->ClearSetup(); + if (tex->GetTexFormat() == GL_R8 && !tex->IsGray) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette; // | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_PalTex; + } + else + { + quad->Flags = BQF_WrapUV; // | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = tex->Box->Owner->Tex; + quad->NumVerts = 4; + quad->NumTris = 2; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFFF; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFFF; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFFF; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFFF; + vert[3].tu = u0; + vert[3].tv = v1; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: FillSimplePoly +// +// Here, "simple" means that a simple triangle fan can draw it. +// +//========================================================================== + +void OpenGLSWFrameBuffer::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + DAngle rotation, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) +{ + // Use an equation similar to player sprites to determine shade + double fadelevel = clamp((swrenderer::LightVisibility::LightLevelToShade(lightlevel, true) / 65536. - 12) / NUMCOLORMAPS, 0.0, 1.0); + + BufferedTris *quad; + FBVERTEX *verts; + OpenGLTex *tex; + float uscale, vscale; + int i, ipos; + uint32_t color0, color1; + float ox, oy; + float cosrot, sinrot; + bool dorotate = rotation != 0; + + if (npoints < 3) + { // This is no polygon. + return; + } + if (In2D < 2) + { + Super::FillSimplePoly(texture, points, npoints, originx, originy, scalex, scaley, rotation, colormap, lightlevel, flatcolor, bottomclip); + return; + } + if (!InScene) + { + return; + } + tex = static_cast(texture->GetNative(true)); + if (tex == nullptr) + { + return; + } + + cosrot = (float)cos(rotation.Radians()); + sinrot = (float)sin(rotation.Radians()); + + CheckQuadBatch(npoints - 2, npoints); + quad = &QuadExtra[QuadBatchPos]; + verts = &VertexData[VertexPos]; + + color0 = 0; + color1 = 0xFFFFFFFF; + + quad->ClearSetup(); + if (tex->GetTexFormat() == GL_R8 && !tex->IsGray) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_PalTex; + if (colormap != nullptr) + { + if (colormap->Desaturate != 0) + { + quad->Flags |= BQF_Desaturated; + } + quad->ShaderNum = BQS_InGameColormap; + quad->Desat = colormap->Desaturate; + color0 = ColorARGB(255, colormap->Color.r, colormap->Color.g, colormap->Color.b); + color1 = ColorARGB(uint32_t((1 - fadelevel) * 255), + uint32_t(colormap->Fade.r * fadelevel), + uint32_t(colormap->Fade.g * fadelevel), + uint32_t(colormap->Fade.b * fadelevel)); + } + } + else + { + quad->Flags = BQF_WrapUV | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = tex->Box->Owner->Tex; + quad->NumVerts = npoints; + quad->NumTris = npoints - 2; + + uscale = float(1.f / (texture->GetScaledWidth() * scalex)); + vscale = float(1.f / (texture->GetScaledHeight() * scaley)); + ox = float(originx); + oy = float(originy); + + for (i = 0; i < npoints; ++i) + { + verts[i].x = points[i].X; + verts[i].y = points[i].Y; + verts[i].z = 0; + verts[i].rhw = 1; + verts[i].color0 = color0; + verts[i].color1 = color1; + float u = points[i].X - ox; + float v = points[i].Y - oy; + if (dorotate) + { + float t = u; + u = t * cosrot - v * sinrot; + v = v * cosrot + t * sinrot; + } + verts[i].tu = u * uscale; + verts[i].tv = v * vscale; + } + for (ipos = IndexPos, i = 2; i < npoints; ++i, ipos += 3) + { + IndexData[ipos] = VertexPos; + IndexData[ipos + 1] = VertexPos + i - 1; + IndexData[ipos + 2] = VertexPos + i; + } + + QuadBatchPos++; + VertexPos += npoints; + IndexPos = ipos; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AddColorOnlyQuad +// +// Adds a single-color, untextured quad to the batch. +// +//========================================================================== + +void OpenGLSWFrameBuffer::AddColorOnlyQuad(int left, int top, int width, int height, uint32_t color) +{ + BufferedTris *quad; + FBVERTEX *verts; + + CheckQuadBatch(); + quad = &QuadExtra[QuadBatchPos]; + verts = &VertexData[VertexPos]; + + float x = float(left); + float y = float(top); + + quad->ClearSetup(); + quad->ShaderNum = BQS_ColorOnly; + if ((color & 0xFF000000) != 0xFF000000) + { + quad->BlendOp = GL_FUNC_ADD; + quad->SrcBlend = GL_SRC_ALPHA; + quad->DestBlend = GL_ONE_MINUS_SRC_ALPHA; + } + quad->Palette = nullptr; + quad->Texture = nullptr; + quad->NumVerts = 4; + quad->NumTris = 2; + + verts[0].x = x; + verts[0].y = y; + verts[0].z = 0; + verts[0].rhw = 1; + verts[0].color0 = color; + verts[0].color1 = 0; + verts[0].tu = 0; + verts[0].tv = 0; + + verts[1].x = x + width; + verts[1].y = y; + verts[1].z = 0; + verts[1].rhw = 1; + verts[1].color0 = color; + verts[1].color1 = 0; + verts[1].tu = 0; + verts[1].tv = 0; + + verts[2].x = x + width; + verts[2].y = y + height; + verts[2].z = 0; + verts[2].rhw = 1; + verts[2].color0 = color; + verts[2].color1 = 0; + verts[2].tu = 0; + verts[2].tv = 0; + + verts[3].x = x; + verts[3].y = y + height; + verts[3].z = 0; + verts[3].rhw = 1; + verts[3].color0 = color; + verts[3].color1 = 0; + verts[3].tu = 0; + verts[3].tv = 0; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AddColorOnlyRect +// +// Like AddColorOnlyQuad, except it's hollow. +// +//========================================================================== + +void OpenGLSWFrameBuffer::AddColorOnlyRect(int left, int top, int width, int height, uint32_t color) +{ + AddColorOnlyQuad(left, top, width - 1, 1, color); // top + AddColorOnlyQuad(left + width - 1, top, 1, height - 1, color); // right + AddColorOnlyQuad(left + 1, top + height - 1, width - 1, 1, color); // bottom + AddColorOnlyQuad(left, top + 1, 1, height - 1, color); // left +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CheckQuadBatch +// +// Make sure there's enough room in the batch for one more set of triangles. +// +//========================================================================== + +void OpenGLSWFrameBuffer::CheckQuadBatch(int numtris, int numverts) +{ + if (BatchType == BATCH_Lines) + { + EndLineBatch(); + } + else if (QuadBatchPos == MAX_QUAD_BATCH || + VertexPos + numverts > NUM_VERTS || + IndexPos + numtris * 3 > NUM_INDEXES) + { + EndQuadBatch(); + } + if (QuadBatchPos < 0) + { + BeginQuadBatch(); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: BeginQuadBatch +// +// Locks the vertex buffer for quads and sets the cursor to 0. +// +//========================================================================== + +void OpenGLSWFrameBuffer::BeginQuadBatch() +{ + if (In2D < 2 || !InScene || QuadBatchPos >= 0) + { + return; + } + EndLineBatch(); // Make sure all lines have been drawn first. + VertexData = VertexBuffer->Lock(); + IndexData = IndexBuffer->Lock(); + VertexPos = 0; + IndexPos = 0; + QuadBatchPos = 0; + BatchType = BATCH_Quads; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndQuadBatch +// +// Draws all the quads that have been batched up. +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndQuadBatch() +{ + if (In2D < 2 || !InScene || BatchType != BATCH_Quads) + { + return; + } + BatchType = BATCH_None; + VertexBuffer->Unlock(); + IndexBuffer->Unlock(); + if (QuadBatchPos == 0) + { + QuadBatchPos = -1; + VertexPos = -1; + IndexPos = -1; + return; + } + SetStreamSource(VertexBuffer); + SetIndices(IndexBuffer); + bool uv_wrapped = false; + bool uv_should_wrap; + int indexpos, vertpos; + + indexpos = vertpos = 0; + for (int i = 0; i < QuadBatchPos; ) + { + const BufferedTris *quad = &QuadExtra[i]; + int j; + + int startindex = indexpos; + int startvertex = vertpos; + + indexpos += quad->NumTris * 3; + vertpos += quad->NumVerts; + + // Quads with matching parameters should be done with a single + // DrawPrimitive call. + for (j = i + 1; j < QuadBatchPos; ++j) + { + const BufferedTris *q2 = &QuadExtra[j]; + if (quad->Texture != q2->Texture || + !quad->IsSameSetup(*q2) || + quad->Palette != q2->Palette) + { + break; + } + if (quad->ShaderNum == BQS_InGameColormap && (quad->Flags & BQF_Desaturated) && quad->Desat != q2->Desat) + { + break; + } + indexpos += q2->NumTris * 3; + vertpos += q2->NumVerts; + } + + // Set the palette (if one) + if ((quad->Flags & BQF_Paletted) == BQF_GamePalette) + { + SetPaletteTexture(PaletteTexture, 256, BorderColor); + } + else if ((quad->Flags & BQF_Paletted) == BQF_CustomPalette) + { + assert(quad->Palette != nullptr); + SetPaletteTexture(quad->Palette->Tex, quad->Palette->RoundedPaletteSize, quad->Palette->BorderColor); + } + + // Set the alpha blending + SetAlphaBlend(quad->BlendOp, quad->SrcBlend, quad->DestBlend); + + // Set the alpha test + EnableAlphaTest(!(quad->Flags & BQF_DisableAlphaTest)); + + // Set the pixel shader + if (quad->ShaderNum == BQS_PalTex) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_NormalColorPalInv : SHADER_NormalColorPal]); + } + else if (quad->ShaderNum == BQS_Plain) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_NormalColorInv : SHADER_NormalColor]); + } + else if (quad->ShaderNum == BQS_RedToAlpha) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_RedToAlphaInv : SHADER_RedToAlpha]); + } + else if (quad->ShaderNum == BQS_ColorOnly) + { + SetPixelShader(Shaders[SHADER_VertexColor]); + } + else if (quad->ShaderNum == BQS_SpecialColormap) + { + int select; + + select = !!(quad->Flags & BQF_Paletted); + SetPixelShader(Shaders[SHADER_SpecialColormap + select]); + } + else if (quad->ShaderNum == BQS_InGameColormap) + { + int select; + + select = !!(quad->Flags & BQF_Desaturated); + select |= !!(quad->Flags & BQF_InvertSource) << 1; + select |= !!(quad->Flags & BQF_Paletted) << 2; + if (quad->Flags & BQF_Desaturated) + { + SetConstant(PSCONST_Desaturation, quad->Desat / 255.f, (255 - quad->Desat) / 255.f, 0, 0); + } + SetPixelShader(Shaders[SHADER_InGameColormap + select]); + } + + // Set the texture clamp addressing mode + uv_should_wrap = !!(quad->Flags & BQF_WrapUV); + if (uv_wrapped != uv_should_wrap) + { + uint32_t mode = uv_should_wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE; + uv_wrapped = uv_should_wrap; + SetSamplerWrapS(0, mode); + SetSamplerWrapT(0, mode); + } + + // Set the texture + if (quad->Texture != nullptr) + { + SetTexture(0, quad->Texture); + } + + // Draw the quad + DrawTriangleList( + startvertex, // MinIndex + vertpos - startvertex, // NumVertices + startindex, // StartIndex + (indexpos - startindex) / 3 // PrimitiveCount + /*4 * i, 4 * (j - i), 6 * i, 2 * (j - i)*/); + i = j; + } + if (uv_wrapped) + { + SetSamplerWrapS(0, GL_CLAMP_TO_EDGE); + SetSamplerWrapT(0, GL_CLAMP_TO_EDGE); + } + QuadBatchPos = -1; + VertexPos = -1; + IndexPos = -1; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndBatch +// +// Draws whichever type of primitive is currently being batched. +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndBatch() +{ + if (BatchType == BATCH_Quads) + { + EndQuadBatch(); + } + else if (BatchType == BATCH_Lines) + { + EndLineBatch(); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: SetStyle +// +// Patterned after R_SetPatchStyle. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::SetStyle(OpenGLTex *tex, DrawParms &parms, uint32_t &color0, uint32_t &color1, BufferedTris &quad) +{ + int fmt = tex->GetTexFormat(); + FRenderStyle style = parms.style; + float alpha; + bool stencilling; + + if (style.Flags & STYLEF_TransSoulsAlpha) + { + alpha = transsouls; + } + else if (style.Flags & STYLEF_Alpha1) + { + alpha = 1; + } + else + { + alpha = clamp(parms.Alpha, 0.f, 1.f); + } + + style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Shadow) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = 0.3f; + parms.fillcolor = 0; + } + + // FIXME: Fuzz effect is not written + if (style.BlendOp == STYLEOP_FuzzOrAdd || style.BlendOp == STYLEOP_Fuzz) + { + style.BlendOp = STYLEOP_Add; + } + else if (style.BlendOp == STYLEOP_FuzzOrSub) + { + style.BlendOp = STYLEOP_Sub; + } + else if (style.BlendOp == STYLEOP_FuzzOrRevSub) + { + style.BlendOp = STYLEOP_RevSub; + } + + stencilling = false; + quad.Palette = nullptr; + quad.Flags = 0; + quad.Desat = 0; + + switch (style.BlendOp) + { + default: + case STYLEOP_Add: quad.BlendOp = GL_FUNC_ADD; break; + case STYLEOP_Sub: quad.BlendOp = GL_FUNC_SUBTRACT; break; + case STYLEOP_RevSub: quad.BlendOp = GL_FUNC_REVERSE_SUBTRACT; break; + case STYLEOP_None: return false; + } + quad.SrcBlend = GetStyleAlpha(style.SrcAlpha); + quad.DestBlend = GetStyleAlpha(style.DestAlpha); + + if (style.Flags & STYLEF_InvertOverlay) + { + // Only the overlay color is inverted, not the overlay alpha. + parms.colorOverlay = ColorARGB(APART(parms.colorOverlay), + 255 - RPART(parms.colorOverlay), 255 - GPART(parms.colorOverlay), + 255 - BPART(parms.colorOverlay)); + } + + SetColorOverlay(parms.colorOverlay, alpha, color0, color1); + + if (style.Flags & STYLEF_ColorIsFixed) + { + if (style.Flags & STYLEF_InvertSource) + { // Since the source color is a constant, we can invert it now + // without spending time doing it in the shader. + parms.fillcolor = ColorXRGB(255 - RPART(parms.fillcolor), + 255 - GPART(parms.fillcolor), 255 - BPART(parms.fillcolor)); + } + // Set up the color mod to replace the color from the image data. + color0 = (color0 & ColorRGBA(0, 0, 0, 255)) | (parms.fillcolor & ColorRGBA(255, 255, 255, 0)); + color1 &= ColorRGBA(0, 0, 0, 255); + + if (style.Flags & STYLEF_RedIsAlpha) + { + // Note that if the source texture is paletted, the palette is ignored. + quad.Flags = 0; + quad.ShaderNum = BQS_RedToAlpha; + } + else if (fmt == GL_R8) + { + quad.Flags = BQF_GamePalette; + quad.ShaderNum = BQS_PalTex; + } + else + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + } + else + { + if (style.Flags & STYLEF_RedIsAlpha) + { + quad.Flags = 0; + quad.ShaderNum = BQS_RedToAlpha; + } + else if (fmt == GL_R8) + { + if (parms.remap != nullptr) + { + quad.Flags = BQF_CustomPalette; + quad.Palette = reinterpret_cast(parms.remap->GetNative()); + quad.ShaderNum = BQS_PalTex; + } + else if (tex->IsGray) + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + else + { + quad.Flags = BQF_GamePalette; + quad.ShaderNum = BQS_PalTex; + } + } + else + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + if (style.Flags & STYLEF_InvertSource) + { + quad.Flags |= BQF_InvertSource; + } + + if (parms.specialcolormap != nullptr) + { // Emulate an invulnerability or similar colormap. + float *start, *end; + start = parms.specialcolormap->ColorizeStart; + end = parms.specialcolormap->ColorizeEnd; + if (quad.Flags & BQF_InvertSource) + { + quad.Flags &= ~BQF_InvertSource; + swapvalues(start, end); + } + quad.ShaderNum = BQS_SpecialColormap; + color0 = ColorRGBA(uint32_t(start[0] / 2 * 255), uint32_t(start[1] / 2 * 255), uint32_t(start[2] / 2 * 255), color0 >> 24); + color1 = ColorRGBA(uint32_t(end[0] / 2 * 255), uint32_t(end[1] / 2 * 255), uint32_t(end[2] / 2 * 255), color1 >> 24); + } + else if (parms.colormapstyle != nullptr) + { // Emulate the fading from an in-game colormap (colorized, faded, and desaturated) + if (parms.colormapstyle->Desaturate != 0) + { + quad.Flags |= BQF_Desaturated; + } + quad.ShaderNum = BQS_InGameColormap; + quad.Desat = parms.colormapstyle->Desaturate; + color0 = ColorARGB(color1 >> 24, + parms.colormapstyle->Color.r, + parms.colormapstyle->Color.g, + parms.colormapstyle->Color.b); + double fadelevel = parms.colormapstyle->FadeLevel; + color1 = ColorARGB(uint32_t((1 - fadelevel) * 255), + uint32_t(parms.colormapstyle->Fade.r * fadelevel), + uint32_t(parms.colormapstyle->Fade.g * fadelevel), + uint32_t(parms.colormapstyle->Fade.b * fadelevel)); + } + } + + // For unmasked images, force the alpha from the image data to be ignored. + if (!parms.masked && quad.ShaderNum != BQS_InGameColormap) + { + color0 = (color0 & ColorRGBA(255, 255, 255, 0)) | ColorValue(0, 0, 0, alpha); + color1 &= ColorRGBA(255, 255, 255, 0); + + // If our alpha is one and we are doing normal adding, then we can turn the blend off completely. + if (quad.BlendOp == GL_FUNC_ADD && + ((alpha == 1 && quad.SrcBlend == GL_SRC_ALPHA) || quad.SrcBlend == GL_ONE) && + ((alpha == 1 && quad.DestBlend == GL_ONE_MINUS_SRC_ALPHA) || quad.DestBlend == GL_ZERO)) + { + quad.BlendOp = 0; + } + quad.Flags |= BQF_DisableAlphaTest; + } + return true; +} + +int OpenGLSWFrameBuffer::GetStyleAlpha(int type) +{ + switch (type) + { + case STYLEALPHA_Zero: return GL_ZERO; + case STYLEALPHA_One: return GL_ONE; + case STYLEALPHA_Src: return GL_SRC_ALPHA; + case STYLEALPHA_InvSrc: return GL_ONE_MINUS_SRC_ALPHA; + default: return GL_ZERO; + } +} + + +void OpenGLSWFrameBuffer::SetColorOverlay(uint32_t color, float alpha, uint32_t &color0, uint32_t &color1) +{ + if (APART(color) != 0) + { + int a = APART(color) * 256 / 255; + color0 = ColorRGBA( + (RPART(color) * a) >> 8, + (GPART(color) * a) >> 8, + (BPART(color) * a) >> 8, + 0); + a = 256 - a; + color1 = ColorRGBA(a, a, a, int(alpha * 255)); + } + else + { + color0 = 0; + color1 = ColorValue(1, 1, 1, alpha); + } +} + +void OpenGLSWFrameBuffer::EnableAlphaTest(bool enabled) +{ + if (enabled != AlphaTestEnabled) + { + AlphaTestEnabled = enabled; + //glEnable(GL_ALPHA_TEST); // To do: move to shader as this is only in the compatibility profile + } +} + +void OpenGLSWFrameBuffer::SetAlphaBlend(int op, int srcblend, int destblend) +{ + if (op == 0) + { // Disable alpha blend + if (AlphaBlendEnabled) + { + AlphaBlendEnabled = false; + glDisable(GL_BLEND); + } + } + else + { // Enable alpha blend + assert(srcblend != 0); + assert(destblend != 0); + + if (!AlphaBlendEnabled) + { + AlphaBlendEnabled = true; + glEnable(GL_BLEND); + } + if (AlphaBlendOp != op) + { + AlphaBlendOp = op; + glBlendEquation(op); + } + if (AlphaSrcBlend != srcblend || AlphaDestBlend != destblend) + { + AlphaSrcBlend = srcblend; + AlphaDestBlend = destblend; + glBlendFunc(srcblend, destblend); + } + } +} + +void OpenGLSWFrameBuffer::SetConstant(int cnum, float r, float g, float b, float a) +{ + if (Constant[cnum][0] != r || + Constant[cnum][1] != g || + Constant[cnum][2] != b || + Constant[cnum][3] != a) + { + Constant[cnum][0] = r; + Constant[cnum][1] = g; + Constant[cnum][2] = b; + Constant[cnum][3] = a; + SetPixelShaderConstantF(cnum, Constant[cnum], 1); + } +} + +void OpenGLSWFrameBuffer::SetPixelShader(HWPixelShader *shader) +{ + if (CurPixelShader != shader) + { + CurPixelShader = shader; + SetHWPixelShader(shader); + } +} + +void OpenGLSWFrameBuffer::SetTexture(int tnum, HWTexture *texture) +{ + assert(unsigned(tnum) < countof(Texture)); + if (texture) + { + if (Texture[tnum] != texture || SamplerWrapS[tnum] != texture->WrapS || SamplerWrapT[tnum] != texture->WrapT) + { + Texture[tnum] = texture; + glActiveTexture(GL_TEXTURE0 + tnum); + glBindTexture(GL_TEXTURE_2D, texture->Texture); + if (Texture[tnum]->WrapS != SamplerWrapS[tnum]) + { + Texture[tnum]->WrapS = SamplerWrapS[tnum]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SamplerWrapS[tnum]); + } + if (Texture[tnum]->WrapT != SamplerWrapT[tnum]) + { + Texture[tnum]->WrapT = SamplerWrapT[tnum]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SamplerWrapT[tnum]); + } + } + } + else if (Texture[tnum] != texture) + { + Texture[tnum] = texture; + glActiveTexture(GL_TEXTURE0 + tnum); + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +void OpenGLSWFrameBuffer::SetSamplerWrapS(int tnum, int mode) +{ + assert(unsigned(tnum) < countof(Texture)); + if (Texture[tnum] && SamplerWrapS[tnum] != mode) + { + SamplerWrapS[tnum] = mode; + Texture[tnum]->WrapS = mode; + glActiveTexture(GL_TEXTURE0 + tnum); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SamplerWrapS[tnum]); + } +} + +void OpenGLSWFrameBuffer::SetSamplerWrapT(int tnum, int mode) +{ + assert(unsigned(tnum) < countof(Texture)); + if (Texture[tnum] && SamplerWrapT[tnum] != mode) + { + SamplerWrapT[tnum] = mode; + Texture[tnum]->WrapT = mode; + glActiveTexture(GL_TEXTURE0 + tnum); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SamplerWrapT[tnum]); + } +} + +void OpenGLSWFrameBuffer::SetPaletteTexture(HWTexture *texture, int count, uint32_t border_color) +{ + // The pixel shader receives color indexes in the range [0.0,1.0]. + // The palette texture is also addressed in the range [0.0,1.0], + // HOWEVER the coordinate 1.0 is the right edge of the texture and + // not actually the texture itself. We need to scale and shift + // the palette indexes so they lie exactly in the center of each + // texel. For a normal palette with 256 entries, that means the + // range we use should be [0.5,255.5], adjusted so the coordinate + // is still within [0.0,1.0]. + // + // The constant register c2 is used to hold the multiplier in the + // x part and the adder in the y part. + float fcount = 1 / float(count); + SetConstant(PSCONST_PaletteMod, 255 * fcount, 0.5f * fcount, 0, 0); + SetTexture(1, texture); +} diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h new file mode 100644 index 000000000..a9e7aa888 --- /dev/null +++ b/src/gl/system/gl_swframebuffer.h @@ -0,0 +1,505 @@ +#ifndef __GL_SWFRAMEBUFFER +#define __GL_SWFRAMEBUFFER + +#ifdef _WIN32 +#include "win32iface.h" +#include "win32gliface.h" +#endif + +#include "SkylineBinPack.h" + +#include + +class FGLDebug; + +#ifdef _WIN32 +class OpenGLSWFrameBuffer : public Win32GLFrameBuffer +{ + typedef Win32GLFrameBuffer Super; + DECLARE_CLASS(OpenGLSWFrameBuffer, Win32GLFrameBuffer) +#else +#include "sdlglvideo.h" +class OpenGLSWFrameBuffer : public SDLGLFB +{ +// typedef SDLGLFB Super; //[C]commented, DECLARE_CLASS defines this in linux + DECLARE_CLASS(OpenGLSWFrameBuffer, SDLGLFB) +#endif + + +public: + + explicit OpenGLSWFrameBuffer() {} + OpenGLSWFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra); + ~OpenGLSWFrameBuffer(); + + + bool IsValid() override; + bool Lock(bool buffered) override; + void Unlock() override; + void Update() override; + PalEntry *GetPalette() override; + void GetFlashedPalette(PalEntry palette[256]) override; + void UpdatePalette() override; + bool SetGamma(float gamma) override; + bool SetFlash(PalEntry rgb, int amount) override; + void GetFlash(PalEntry &rgb, int &amount) override; + int GetPageCount() override; + void SetVSync(bool vsync) override; + void NewRefreshRate() override; + void GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) override; + void ReleaseScreenshotBuffer() override; + void SetBlendingRect(int x1, int y1, int x2, int y2) override; + bool Begin2D(bool copy3d) override; + void DrawBlendingRect() override; + FNativeTexture *CreateTexture(FTexture *gametex, bool wrapping) override; + FNativePalette *CreatePalette(FRemapTable *remap) override; + void DrawTextureParms(FTexture *img, DrawParms &parms) override; + void Clear(int left, int top, int right, int bottom, int palcolor, uint32 color) override; + void Dim(PalEntry color, float amount, int x1, int y1, int w, int h) override; + void FlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin) override; + void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor) override; + void DrawPixel(int x, int y, int palcolor, uint32 rgbcolor) override; + void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) override; + bool WipeStartScreen(int type) override; + void WipeEndScreen() override; + bool WipeDo(int ticks) override; + void WipeCleanup() override; + +#ifdef WIN32 + void PaletteChanged() override { } + int QueryNewPalette() override { return 0; } + void Blank() override { } + bool PaintToWindow() override; + bool Is8BitMode() override { return false; } + int GetTrueHeight() override { return TrueHeight; } +#endif + +private: + struct FBVERTEX + { + float x, y, z, rhw; + uint32_t color0, color1; + float tu, tv; + }; + + struct BURNVERTEX + { + float x, y, z, rhw; + float tu0, tv0; + float tu1, tv1; + }; + + enum + { + PSCONST_Desaturation, + PSCONST_PaletteMod, + PSCONST_Weights, + PSCONST_Gamma, + PSCONST_ScreenSize, + NumPSCONST + }; + + struct GammaRamp + { + uint16_t red[256], green[256], blue[256]; + }; + + struct LTRBRect + { + int left, top, right, bottom; + }; + + class HWTexture + { + public: + HWTexture() { Buffers[0] = 0; Buffers[1] = 0; } + ~HWTexture(); + + int Texture = 0; + int Buffers[2]; + int CurrentBuffer = 0; + int WrapS = 0; + int WrapT = 0; + int Format = 0; + + std::vector MapBuffer; + }; + + class HWFrameBuffer + { + public: + ~HWFrameBuffer(); + + int Framebuffer = 0; + HWTexture *Texture = nullptr; + }; + + + class HWVertexBuffer + { + public: + ~HWVertexBuffer(); + + FBVERTEX *Lock(); + void Unlock(); + + int VertexArray = 0; + int Buffer = 0; + int Size = 0; + }; + + class HWIndexBuffer + { + public: + ~HWIndexBuffer(); + + uint16_t *Lock(); + void Unlock(); + + int Buffer = 0; + int Size = 0; + + private: + int LockedOldBinding = 0; + }; + + class HWPixelShader + { + public: + ~HWPixelShader(); + + int Program = 0; + int VertexShader = 0; + int FragmentShader = 0; + + int ConstantLocations[NumPSCONST]; + int ImageLocation = -1; + int PaletteLocation = -1; + int NewScreenLocation = -1; + int BurnLocation = -1; + }; + + bool CreateFrameBuffer(const FString &name, int width, int height, HWFrameBuffer **outFramebuffer); + bool CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader); + bool CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer); + bool CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer); + bool CreateTexture(const FString &name, int width, int height, int levels, int format, HWTexture **outTexture); + void SetGammaRamp(const GammaRamp *ramp); + void SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount); + void SetHWPixelShader(HWPixelShader *shader); + void SetStreamSource(HWVertexBuffer *vertexBuffer); + void SetIndices(HWIndexBuffer *indexBuffer); + void DrawTriangleFans(int count, const FBVERTEX *vertices); + void DrawTriangleFans(int count, const BURNVERTEX *vertices); + void DrawPoints(int count, const FBVERTEX *vertices); + void DrawLineList(int count); + void DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount); + void Present(); + + static void BgraToRgba(uint32_t *dest, const uint32_t *src, int width, int height, int srcpitch); + + void BindFBBuffer(); + void *MappedMemBuffer = nullptr; + bool UseMappedMemBuffer = true; + + static uint32_t ColorARGB(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | ((b) & 0xff); } + static uint32_t ColorRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { return ColorARGB(a, r, g, b); } + static uint32_t ColorXRGB(uint32_t r, uint32_t g, uint32_t b) { return ColorARGB(0xff, r, g, b); } + static uint32_t ColorValue(float r, float g, float b, float a) { return ColorRGBA((uint32_t)(r * 255.0f), (uint32_t)(g * 255.0f), (uint32_t)(b * 255.0f), (uint32_t)(a * 255.0f)); } + + static void *MapBuffer(int target, int size); + + // The number of points for the vertex buffer. + enum { NUM_VERTS = 10240 }; + + // The number of indices for the index buffer. + enum { NUM_INDEXES = ((NUM_VERTS * 6) / 4) }; + + // The number of quads we can batch together. + enum { MAX_QUAD_BATCH = (NUM_INDEXES / 6) }; + + // The default size for a texture atlas. + enum { DEF_ATLAS_WIDTH = 512 }; + enum { DEF_ATLAS_HEIGHT = 512 }; + + // TYPES ------------------------------------------------------------------- + + struct Atlas; + + struct PackedTexture + { + Atlas *Owner; + + PackedTexture **Prev, *Next; + + // Pixels this image covers + LTRBRect Area; + + // Texture coordinates for this image + float Left, Top, Right, Bottom; + + // Texture has extra space on the border? + bool Padded; + }; + + struct Atlas + { + Atlas(OpenGLSWFrameBuffer *fb, int width, int height, int format); + ~Atlas(); + + PackedTexture *AllocateImage(const Rect &rect, bool padded); + void FreeBox(PackedTexture *box); + + SkylineBinPack Packer; + Atlas *Next; + HWTexture *Tex; + int Format; + PackedTexture *UsedList; // Boxes that contain images + int Width, Height; + bool OneUse; + }; + + class OpenGLTex : public FNativeTexture + { + public: + OpenGLTex(FTexture *tex, OpenGLSWFrameBuffer *fb, bool wrapping); + ~OpenGLTex(); + + FTexture *GameTex; + PackedTexture *Box; + + OpenGLTex **Prev; + OpenGLTex *Next; + + bool IsGray; + + bool Create(OpenGLSWFrameBuffer *fb, bool wrapping); + bool Update(); + bool CheckWrapping(bool wrapping); + int GetTexFormat(); + FTextureFormat ToTexFmt(int fmt); + }; + + class OpenGLPal : public FNativePalette + { + public: + OpenGLPal(FRemapTable *remap, OpenGLSWFrameBuffer *fb); + ~OpenGLPal(); + + OpenGLPal **Prev; + OpenGLPal *Next; + + HWTexture *Tex; + uint32_t BorderColor; + bool DoColorSkip; + + bool Update(); + + FRemapTable *Remap; + int RoundedPaletteSize; + }; + + // Flags for a buffered quad + enum + { + BQF_GamePalette = 1, + BQF_CustomPalette = 7, + BQF_Paletted = 7, + BQF_Bilinear = 8, + BQF_WrapUV = 16, + BQF_InvertSource = 32, + BQF_DisableAlphaTest = 64, + BQF_Desaturated = 128, + }; + + // Shaders for a buffered quad + enum + { + BQS_PalTex, + BQS_Plain, + BQS_RedToAlpha, + BQS_ColorOnly, + BQS_SpecialColormap, + BQS_InGameColormap, + }; + + struct BufferedTris + { + uint8_t Flags; + uint8_t ShaderNum; + int BlendOp; + int SrcBlend; + int DestBlend; + + uint8_t Desat; + OpenGLPal *Palette; + HWTexture *Texture; + uint16_t NumVerts; // Number of _unique_ vertices used by this set. + uint16_t NumTris; // Number of triangles used by this set. + + void ClearSetup() + { + Flags = 0; + ShaderNum = 0; + BlendOp = 0; + SrcBlend = 0; + DestBlend = 0; + } + + bool IsSameSetup(const BufferedTris &other) const + { + return Flags == other.Flags && ShaderNum == other.ShaderNum && BlendOp == other.BlendOp && SrcBlend == other.SrcBlend && DestBlend == other.DestBlend; + } + }; + + enum + { + SHADER_NormalColor, + SHADER_NormalColorPal, + SHADER_NormalColorInv, + SHADER_NormalColorPalInv, + + SHADER_RedToAlpha, + SHADER_RedToAlphaInv, + + SHADER_VertexColor, + + SHADER_SpecialColormap, + SHADER_SpecialColormapPal, + + SHADER_InGameColormap, + SHADER_InGameColormapDesat, + SHADER_InGameColormapInv, + SHADER_InGameColormapInvDesat, + SHADER_InGameColormapPal, + SHADER_InGameColormapPalDesat, + SHADER_InGameColormapPalInv, + SHADER_InGameColormapPalInvDesat, + + SHADER_BurnWipe, + SHADER_GammaCorrection, + + NUM_SHADERS + }; + static const char *const ShaderDefines[NUM_SHADERS]; + + void Flip(); + void SetInitialState(); + bool CreateResources(); + void ReleaseResources(); + bool LoadShaders(); + bool CreateFBTexture(); + bool CreatePaletteTexture(); + bool CreateVertexes(); + void UploadPalette(); + void CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, uint32_t color0, uint32_t color1) const; + bool Reset(); + HWTexture *CopyCurrentScreen(); + void ReleaseDefaultPoolItems(); + void KillNativePals(); + void KillNativeTexs(); + PackedTexture *AllocPackedTexture(int width, int height, bool wrapping, int format); + void DrawPackedTextures(int packnum); + void DrawLetterbox(int x, int y, int width, int height); + void Draw3DPart(bool copy3d); + bool SetStyle(OpenGLTex *tex, DrawParms &parms, uint32_t &color0, uint32_t &color1, BufferedTris &quad); + static int GetStyleAlpha(int type); + static void SetColorOverlay(uint32_t color, float alpha, uint32_t &color0, uint32_t &color1); + void AddColorOnlyQuad(int left, int top, int width, int height, uint32_t color); + void AddColorOnlyRect(int left, int top, int width, int height, uint32_t color); + void CheckQuadBatch(int numtris = 2, int numverts = 4); + void BeginQuadBatch(); + void EndQuadBatch(); + void BeginLineBatch(); + void EndLineBatch(); + void EndBatch(); + + // State + void EnableAlphaTest(bool enabled); + void SetAlphaBlend(int op, int srcblend = 0, int destblend = 0); + void SetConstant(int cnum, float r, float g, float b, float a); + void SetPixelShader(HWPixelShader *shader); + void SetTexture(int tnum, HWTexture *texture); + void SetSamplerWrapS(int tnum, int mode); + void SetSamplerWrapT(int tnum, int mode); + void SetPaletteTexture(HWTexture *texture, int count, uint32_t border_color); + + template static void SafeRelease(T &x) { if (x != nullptr) { delete x; x = nullptr; } } + + bool Valid = false; + std::shared_ptr Debug; + + std::unique_ptr StreamVertexBuffer, StreamVertexBufferBurn; + float ShaderConstants[NumPSCONST * 4]; + HWPixelShader *CurrentShader = nullptr; + + HWFrameBuffer *OutputFB = nullptr; + + bool AlphaTestEnabled = false; + bool AlphaBlendEnabled = false; + int AlphaBlendOp = 0; + int AlphaSrcBlend = 0; + int AlphaDestBlend = 0; + float Constant[3][4]; + uint32_t CurBorderColor; + HWPixelShader *CurPixelShader; + HWTexture *Texture[5]; + int SamplerWrapS[5], SamplerWrapT[5]; + + PalEntry SourcePalette[256]; + uint32_t BorderColor; + uint32_t FlashColor0, FlashColor1; + PalEntry FlashColor; + int FlashAmount; + int TrueHeight; + int PixelDoubling; + float Gamma; + bool UpdatePending; + bool NeedPalUpdate; + bool NeedGammaUpdate; + LTRBRect BlendingRect; + int In2D; + bool InScene; + bool GatheringWipeScreen; + bool AALines; + uint8_t BlockNum; + OpenGLPal *Palettes = nullptr; + OpenGLTex *Textures = nullptr; + Atlas *Atlases = nullptr; + + HWTexture *FBTexture = nullptr; + HWTexture *PaletteTexture = nullptr; + HWTexture *ScreenshotTexture = nullptr; + + HWVertexBuffer *VertexBuffer = nullptr; + FBVERTEX *VertexData = nullptr; + HWIndexBuffer *IndexBuffer = nullptr; + uint16_t *IndexData = nullptr; + BufferedTris *QuadExtra = nullptr; + int VertexPos; + int IndexPos; + int QuadBatchPos; + enum { BATCH_None, BATCH_Quads, BATCH_Lines } BatchType; + + HWPixelShader *Shaders[NUM_SHADERS]; + + HWTexture *InitialWipeScreen = nullptr, *FinalWipeScreen = nullptr; + + class Wiper + { + public: + virtual ~Wiper(); + virtual bool Run(int ticks, OpenGLSWFrameBuffer *fb) = 0; + + void DrawScreen(OpenGLSWFrameBuffer *fb, HWTexture *tex, int blendop = 0, uint32_t color0 = 0, uint32_t color1 = 0xFFFFFFF); + }; + + class Wiper_Melt; friend class Wiper_Melt; + class Wiper_Burn; friend class Wiper_Burn; + class Wiper_Crossfade; friend class Wiper_Crossfade; + + Wiper *ScreenWipe; +}; + + +#endif //__GL_SWFRAMEBUFFER diff --git a/src/gl/system/gl_swwipe.cpp b/src/gl/system/gl_swwipe.cpp new file mode 100644 index 000000000..7f8e44a66 --- /dev/null +++ b/src/gl/system/gl_swwipe.cpp @@ -0,0 +1,587 @@ +/* +** gl_swwipe.cpp +** Implements the different screen wipes using OpenGL calls. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** 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. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "doomstat.h" +#include "m_png.h" +#include "m_crc32.h" +#include "vectors.h" +#include "v_palette.h" +#include "templates.h" + +#include "c_dispatch.h" +#include "templates.h" +#include "i_system.h" +#include "i_video.h" +#include "i_input.h" +#include "v_pfx.h" +#include "stats.h" +#include "doomerrors.h" +#include "r_data/r_translate.h" +#include "f_wipe.h" +#include "sbar.h" +#include "w_wad.h" +#include "r_data/colormaps.h" + +#include "gl/system/gl_interface.h" +#include "gl/system/gl_swframebuffer.h" +#include "gl/data/gl_data.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/gl_functions.h" +#include "gl_debug.h" +#include "m_random.h" + +class OpenGLSWFrameBuffer::Wiper_Crossfade : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Crossfade(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + int Clock; +}; + +class OpenGLSWFrameBuffer::Wiper_Melt : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Melt(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + // Match the strip sizes that oldschool Doom used. + static const int WIDTH = 160, HEIGHT = 200; + int y[WIDTH]; +}; + +class OpenGLSWFrameBuffer::Wiper_Burn : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Burn(OpenGLSWFrameBuffer *fb); + ~Wiper_Burn(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + static const int WIDTH = 64, HEIGHT = 64; + uint8_t BurnArray[WIDTH * (HEIGHT + 5)]; + HWTexture *BurnTexture; + int Density; + int BurnTime; +}; + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeStartScreen +// +// Called before the current screen has started rendering. This needs to +// save what was drawn the previous frame so that it can be animated into +// what gets drawn this frame. +// +// In fullscreen mode, we use GetFrontBufferData() to grab the data that +// is visible on screen right now. +// +// In windowed mode, we can't do that because we'll get the whole desktop. +// Instead, we can conveniently use the TempRenderTexture, which is normally +// used for gamma-correcting copying the image to the back buffer. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::WipeStartScreen(int type) +{ + if (!Accel2D) + { + return Super::WipeStartScreen(type); + } + + switch (type) + { + case wipe_Melt: + ScreenWipe = new Wiper_Melt; + break; + + case wipe_Burn: + ScreenWipe = new Wiper_Burn(this); + break; + + case wipe_Fade: + ScreenWipe = new Wiper_Crossfade; + break; + + default: + return false; + } + + InitialWipeScreen = CopyCurrentScreen(); + + // Make even fullscreen model render to the TempRenderTexture, so + // we can have a copy of the new screen readily available. + GatheringWipeScreen = true; + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeEndScreen +// +// The screen we want to animate to has just been drawn. This function is +// called in place of Update(), so it has not been Presented yet. +// +//========================================================================== + +void OpenGLSWFrameBuffer::WipeEndScreen() +{ + if (!Accel2D) + { + Super::WipeEndScreen(); + return; + } + + // Don't do anything if there is no starting point. + if (InitialWipeScreen == NULL) + { + return; + } + + // If the whole screen was drawn without 2D accel, get it in to + // video memory now. + if (!In2D) + { + Begin2D(true); + } + + EndBatch(); // Make sure all batched primitives have been drawn. + + FinalWipeScreen = CopyCurrentScreen(); + + // At this point, InitialWipeScreen holds the screen we are wiping from. + // FinalWipeScreen holds the screen we are wiping to, which may be the + // same texture as TempRenderTexture. +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeDo +// +// Perform the actual wipe animation. The number of tics since the last +// time this function was called is passed in. Returns true when the wipe +// is over. The first time this function has been called, the screen is +// still locked from before and EndScene() still has not been called. +// Successive times need to call BeginScene(). +// +//========================================================================== + +bool OpenGLSWFrameBuffer::WipeDo(int ticks) +{ + if (!Accel2D) + { + return Super::WipeDo(ticks); + } + + // Sanity checks. + if (InitialWipeScreen == NULL || FinalWipeScreen == NULL) + { + return true; + } + if (GatheringWipeScreen) + { // This is the first time we've been called for this wipe. + GatheringWipeScreen = false; + } + else + { // This is the second or later time we've been called for this wipe. + InScene = true; + } + + In2D = 3; + + EnableAlphaTest(false); + bool done = ScreenWipe->Run(ticks, this); + return done; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeCleanup +// +// Release any resources that were specifically created for the wipe. +// +//========================================================================== + +void OpenGLSWFrameBuffer::WipeCleanup() +{ + if (ScreenWipe != NULL) + { + delete ScreenWipe; + ScreenWipe = NULL; + } + SafeRelease( InitialWipeScreen ); + SafeRelease( FinalWipeScreen ); + GatheringWipeScreen = false; + if (!Accel2D) + { + Super::WipeCleanup(); + return; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper::~Wiper() +{ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper :: DrawScreen +// +// Draw either the initial or target screen completely to the screen. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Wiper::DrawScreen(OpenGLSWFrameBuffer *fb, HWTexture *tex, + int blendop, uint32_t color0, uint32_t color1) +{ + FBVERTEX verts[4]; + + fb->CalcFullscreenCoords(verts, false, color0, color1); + fb->SetTexture(0, tex); + fb->SetAlphaBlend(blendop, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + fb->SetPixelShader(fb->Shaders[SHADER_NormalColor]); + fb->DrawTriangleFans(2, verts); +} + +// WIPE: CROSSFADE --------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Crossfade Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Crossfade::Wiper_Crossfade() +: Clock(0) +{ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Crossfade :: Run +// +// Fades the old screen into the new one over 32 ticks. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Crossfade::Run(int ticks, OpenGLSWFrameBuffer *fb) +{ + Clock += ticks; + + // Put the initial screen back to the buffer. + DrawScreen(fb, fb->InitialWipeScreen); + + // Draw the new screen on top of it. + DrawScreen(fb, fb->FinalWipeScreen, GL_FUNC_ADD, ColorValue(0,0,0,Clock / 32.f), ColorRGBA(255,255,255,0)); + + return Clock >= 32; +} + +// WIPE: MELT -------------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Melt Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Melt::Wiper_Melt() +{ + int i, r; + + // setup initial column positions + // (y<0 => not ready to scroll yet) + y[0] = -(M_Random() & 15); + for (i = 1; i < WIDTH; ++i) + { + r = (M_Random()%3) - 1; + y[i] = clamp(y[i-1] + r, -15, 0); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Melt :: Run +// +// Fades the old screen into the new one over 32 ticks. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Melt::Run(int ticks, OpenGLSWFrameBuffer *fb) +{ + // Draw the new screen on the bottom. + DrawScreen(fb, fb->FinalWipeScreen); + + int i, dy; + int fbwidth = fb->Width; + int fbheight = fb->Height; + bool done = true; + + // Copy the old screen in vertical strips on top of the new one. + while (ticks--) + { + done = true; + for (i = 0; i < WIDTH; i++) + { + if (y[i] < 0) + { + y[i]++; + done = false; + } + else if (y[i] < HEIGHT) + { + dy = (y[i] < 16) ? y[i]+1 : 8; + y[i] = MIN(y[i] + dy, HEIGHT); + done = false; + } + if (ticks == 0) + { // Only draw for the final tick. + LTRBRect rect; + struct Point { int x, y; } dpt; + + dpt.x = i * fbwidth / WIDTH; + dpt.y = MAX(0, y[i] * fbheight / HEIGHT); + rect.left = dpt.x; + rect.top = 0; + rect.right = (i + 1) * fbwidth / WIDTH; + rect.bottom = fbheight - dpt.y; + if (rect.bottom > rect.top) + { + fb->CheckQuadBatch(); + + BufferedTris *quad = &fb->QuadExtra[fb->QuadBatchPos]; + FBVERTEX *vert = &fb->VertexData[fb->VertexPos]; + WORD *index = &fb->IndexData[fb->IndexPos]; + + quad->ClearSetup(); + quad->Flags = BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + quad->Palette = NULL; + quad->Texture = fb->InitialWipeScreen; + quad->NumVerts = 4; + quad->NumTris = 2; + + // Fill the vertex buffer. + float u0 = rect.left / float(fb->Width); + float v0 = 0; + float u1 = rect.right / float(fb->Width); + float v1 = (rect.bottom - rect.top) / float(fb->Height); + + float x0 = float(rect.left); + float x1 = float(rect.right); + float y0 = float(dpt.y); + float y1 = float(fbheight); + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFF; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFF; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFF; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFF; + vert[3].tu = u0; + vert[3].tv = v1; + + // Fill the vertex index buffer. + index[0] = fb->VertexPos; + index[1] = fb->VertexPos + 1; + index[2] = fb->VertexPos + 2; + index[3] = fb->VertexPos; + index[4] = fb->VertexPos + 2; + index[5] = fb->VertexPos + 3; + + // Batch the quad. + fb->QuadBatchPos++; + fb->VertexPos += 4; + fb->IndexPos += 6; + } + } + } + } + fb->EndQuadBatch(); + return done; +} + +// WIPE: BURN -------------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Burn::Wiper_Burn(OpenGLSWFrameBuffer *fb) +{ + Density = 4; + BurnTime = 0; + memset(BurnArray, 0, sizeof(BurnArray)); + if (fb->Shaders[SHADER_BurnWipe] == NULL || !fb->CreateTexture("BurnWipe", WIDTH, HEIGHT, 1, GL_R8, &BurnTexture)) + { + BurnTexture = NULL; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Burn::~Wiper_Burn() +{ + SafeRelease( BurnTexture ); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn :: Run +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Burn::Run(int ticks, OpenGLSWFrameBuffer *fb) +{ + bool done; + + BurnTime += ticks; + ticks *= 2; + + // Make the fire burn + done = false; + while (!done && ticks--) + { + Density = wipe_CalcBurn(BurnArray, WIDTH, HEIGHT, Density); + done = (Density < 0); + } + + // Update the burn texture with the new burn data + + if (BurnTexture->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)BurnTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, WIDTH * HEIGHT, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, WIDTH * HEIGHT, nullptr, GL_STREAM_DRAW); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[BurnTexture->CurrentBuffer]); + BurnTexture->CurrentBuffer = (BurnTexture->CurrentBuffer + 1) & 1; + } + + uint8_t *dest = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, WIDTH * HEIGHT, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); + if (dest) + { + memcpy(dest, BurnArray, WIDTH * HEIGHT); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, BurnTexture->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_RED, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + + // Put the initial screen back to the buffer. + DrawScreen(fb, fb->InitialWipeScreen); + + // Burn the new screen on top of it. + float right = float(fb->Width); + float bot = float(fb->Height); + + BURNVERTEX verts[4] = + { + { 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0, 0 }, + { right, 0.f, 0.f, 1.f, 1.f, 0.f, 1, 0 }, + { right, bot, 0.f, 1.f, 1.f, 1.f, 1, 1 }, + { 0.f, bot, 0.f, 1.f, 0.f, 1.f, 0, 1 } + }; + + fb->SetTexture(0, fb->FinalWipeScreen); + fb->SetTexture(1, BurnTexture); + fb->SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + fb->SetPixelShader(fb->Shaders[SHADER_BurnWipe]); + glActiveTexture(GL_TEXTURE1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + fb->DrawTriangleFans(2, verts); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glActiveTexture(GL_TEXTURE0); + + // The fire may not always stabilize, so the wipe is forced to end + // after an arbitrary maximum time. + return done || (BurnTime > 40); +} diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index a481dd24b..15df785bf 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -56,6 +56,7 @@ #include "menu/menu.h" #include "textures/textures.h" #include "virtual.h" +#include "events.h" // // Todo: Move these elsewhere @@ -173,15 +174,31 @@ DMenu::DMenu(DMenu *parent) bool DMenu::CallResponder(event_t *ev) { - IFVIRTUAL(DMenu, Responder) + if (ev->type == EV_GUI_Event) { - VMValue params[] = { (DObject*)this, ev}; - int retval; - VMReturn ret(&retval); - GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); - return !!retval; + IFVIRTUAL(DMenu, OnUIEvent) + { + FUiEvent e = ev; + VMValue params[] = { (DObject*)this, &e }; + int retval; + VMReturn ret(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + return !!retval; + } } - else return false; + else + { + IFVIRTUAL(DMenu, OnInputEvent) + { + FInputEvent e = ev; + VMValue params[] = { (DObject*)this, &e }; + int retval; + VMReturn ret(&retval); + GlobalVMStack.Call(func, params, 2, &ret, 1, nullptr); + return !!retval; + } + } + return false; } //============================================================================= diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index b699fadbd..543ead302 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -129,8 +129,6 @@ static void P_Add3DFloor(sector_t* sec, sector_t* sec2, line_t* master, int flag ffloor->top.copied = ffloor->bottom.copied = false; ffloor->top.model = ffloor->bottom.model = ffloor->model = sec2; ffloor->target = sec; - ffloor->ceilingclip = ffloor->floorclip = NULL; - ffloor->validcount = 0; if (!(flags&FF_THINFLOOR)) { diff --git a/src/p_3dfloors.h b/src/p_3dfloors.h index 75b8656e4..2c0dbff64 100644 --- a/src/p_3dfloors.h +++ b/src/p_3dfloors.h @@ -95,11 +95,6 @@ struct F3DFloor int lastlight; int alpha; - // kg3D - for software - short *floorclip; - short *ceilingclip; - int validcount; - FDynamicColormap *GetColormap(); void UpdateColormap(FDynamicColormap *&map); PalEntry GetBlend(); diff --git a/src/p_effect.h b/src/p_effect.h index 6a9f5ccf9..ebac76603 100644 --- a/src/p_effect.h +++ b/src/p_effect.h @@ -31,6 +31,8 @@ ** */ +#pragma once + #include "vectors.h" #define FX_ROCKET 0x00000001 diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 47675699a..1f036d1ff 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -120,6 +120,8 @@ DEFINE_FIELD(DPSprite, oldx) DEFINE_FIELD(DPSprite, oldy) DEFINE_FIELD(DPSprite, firstTic) DEFINE_FIELD(DPSprite, Tics) +DEFINE_FIELD(DPSprite, alpha) +DEFINE_FIELD(DPSprite, RenderStyle) DEFINE_FIELD_BIT(DPSprite, Flags, bAddWeapon, PSPF_ADDWEAPON) DEFINE_FIELD_BIT(DPSprite, Flags, bAddBob, PSPF_ADDBOB) DEFINE_FIELD_BIT(DPSprite, Flags, bPowDouble, PSPF_POWDOUBLE) @@ -141,7 +143,9 @@ DPSprite::DPSprite(player_t *owner, AActor *caller, int id) Owner(owner), Sprite(0), ID(id), - processPending(true) + processPending(true), + alpha(1), + RenderStyle(STYLE_Normal) { DPSprite *prev = nullptr; DPSprite *next = Owner->psprites; @@ -1037,7 +1041,7 @@ void A_OverlayOffset(AActor *self, int layer, double wx, double wy, int flags) player_t *player = self->player; DPSprite *psp; - if (player && (player->playerstate != PST_DEAD)) + if (player) { psp = player->FindPSprite(layer); @@ -1184,7 +1188,69 @@ DEFINE_ACTION_FUNCTION(AActor, OverlayID) ACTION_RETURN_INT(0); } +//--------------------------------------------------------------------------- +// +// PROC A_OverlayAlpha +// Sets the alpha of an overlay. +//--------------------------------------------------------------------------- +DEFINE_ACTION_FUNCTION(AActor, A_OverlayAlpha) +{ + PARAM_ACTION_PROLOGUE(AActor); + PARAM_INT(layer); + PARAM_FLOAT(alph); + + if (ACTION_CALL_FROM_PSPRITE()) + { + DPSprite *pspr = self->player->FindPSprite((layer != 0) ? layer : stateinfo->mPSPIndex); + + if (pspr != nullptr) + pspr->alpha = clamp(alph, 0.0, 1.0); + } + return 0; +} + +// NON-ACTION function to get the overlay alpha of a layer. +DEFINE_ACTION_FUNCTION(AActor, OverlayAlpha) +{ + PARAM_ACTION_PROLOGUE(AActor); + PARAM_INT_DEF(layer); + + if (ACTION_CALL_FROM_PSPRITE()) + { + DPSprite *pspr = self->player->FindPSprite((layer != 0) ? layer : stateinfo->mPSPIndex); + + if (pspr != nullptr) + { + ACTION_RETURN_FLOAT(pspr->alpha); + } + } + ACTION_RETURN_FLOAT(0.0); +} + +//--------------------------------------------------------------------------- +// +// PROC A_OverlayRenderStyle +// +//--------------------------------------------------------------------------- + +DEFINE_ACTION_FUNCTION(AActor, A_OverlayRenderStyle) +{ + PARAM_ACTION_PROLOGUE(AActor); + PARAM_INT(layer); + PARAM_INT(style); + + if (ACTION_CALL_FROM_PSPRITE()) + { + DPSprite *pspr = self->player->FindPSprite((layer != 0) ? layer : stateinfo->mPSPIndex); + + if (pspr == nullptr || style >= STYLE_Count || style < 0) + return 0; + + pspr->RenderStyle = style; + } + return 0; +} //--------------------------------------------------------------------------- // @@ -1414,7 +1480,9 @@ void DPSprite::Serialize(FSerializer &arc) ("x", x) ("y", y) ("oldx", oldx) - ("oldy", oldy); + ("oldy", oldy) + ("alpha", alpha) + ("renderstyle", RenderStyle); } //------------------------------------------------------------------------ diff --git a/src/p_pspr.h b/src/p_pspr.h index 346179f96..c7688e1b8 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -50,11 +50,15 @@ enum PSPLayers enum PSPFlags { - PSPF_ADDWEAPON = 1 << 0, - PSPF_ADDBOB = 1 << 1, - PSPF_POWDOUBLE = 1 << 2, - PSPF_CVARFAST = 1 << 3, - PSPF_FLIP = 1 << 6, + PSPF_ADDWEAPON = 1 << 0, + PSPF_ADDBOB = 1 << 1, + PSPF_POWDOUBLE = 1 << 2, + PSPF_CVARFAST = 1 << 3, + PSPF_ALPHA = 1 << 4, + PSPF_RENDERSTYLE = 1 << 5, + PSPF_FLIP = 1 << 6, + PSPF_FORCEALPHA = 1 << 7, + PSPF_FORCESTYLE = 1 << 8, }; class DPSprite : public DObject @@ -78,11 +82,12 @@ public: void ResetInterpolation() { oldx = x; oldy = y; } void OnDestroy() override; - double x, y; + double x, y, alpha; double oldx, oldy; bool firstTic; int Tics; int Flags; + int RenderStyle; private: DPSprite () {} diff --git a/src/p_user.cpp b/src/p_user.cpp index 44cb9dcb9..d68d11919 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2039,11 +2039,12 @@ void P_CalcHeight (player_t *player) return; } + //[SP] Added (x*player->mo->ViewBob) to allow DECORATE changes to view bobbing speed. if (still) { if (player->health > 0) { - angle = level.time / (120 * TICRATE / 35.) * 360.; + angle = level.time / (120 * TICRATE / 35.) * 360. * player->mo->ViewBob; bob = player->userinfo.GetStillBob() * angle.Sin(); } else @@ -2053,7 +2054,7 @@ void P_CalcHeight (player_t *player) } else { - angle = level.time / (20 * TICRATE / 35.) * 360.; + angle = level.time / (20 * TICRATE / 35.) * 360. * player->mo->ViewBob; bob = player->bob * angle.Sin() * (player->mo->waterlevel > 1 ? 0.25f : 0.5f); } diff --git a/src/polyrenderer/drawers/poly_buffer.cpp b/src/polyrenderer/drawers/poly_buffer.cpp new file mode 100644 index 000000000..291e05acb --- /dev/null +++ b/src/polyrenderer/drawers/poly_buffer.cpp @@ -0,0 +1,100 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "poly_buffer.h" +#include "screen_triangle.h" + +///////////////////////////////////////////////////////////////////////////// + +PolySubsectorGBuffer *PolySubsectorGBuffer::Instance() +{ + static PolySubsectorGBuffer buffer; + return &buffer; +} + +void PolySubsectorGBuffer::Resize(int newwidth, int newheight) +{ + width = newwidth; + height = newheight; + values.resize(width * height); +} + +///////////////////////////////////////////////////////////////////////////// + +PolyStencilBuffer *PolyStencilBuffer::Instance() +{ + static PolyStencilBuffer buffer; + return &buffer; +} + +void PolyStencilBuffer::Clear(int newwidth, int newheight, uint8_t stencil_value) +{ + width = newwidth; + height = newheight; + int count = BlockWidth() * BlockHeight(); + values.resize(count * 64); + masks.resize(count); + + uint8_t *v = Values(); + uint32_t *m = Masks(); + for (int i = 0; i < count; i++) + { + m[i] = 0xffffff00 | stencil_value; + } +} + +///////////////////////////////////////////////////////////////////////////// + +namespace +{ + int NextBufferVertex = 0; +} + +TriVertex *PolyVertexBuffer::GetVertices(int count) +{ + enum { VertexBufferSize = 256 * 1024 }; + static TriVertex Vertex[VertexBufferSize]; + + if (NextBufferVertex + count > VertexBufferSize) + return nullptr; + TriVertex *v = Vertex + NextBufferVertex; + NextBufferVertex += count; + return v; +} + +void PolyVertexBuffer::Clear() +{ + NextBufferVertex = 0; +} diff --git a/src/polyrenderer/drawers/poly_buffer.h b/src/polyrenderer/drawers/poly_buffer.h new file mode 100644 index 000000000..4454f72fd --- /dev/null +++ b/src/polyrenderer/drawers/poly_buffer.h @@ -0,0 +1,68 @@ +/* +** Frame buffers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +struct TriVertex; + +class PolySubsectorGBuffer +{ +public: + static PolySubsectorGBuffer *Instance(); + void Resize(int newwidth, int newheight); + int Width() const { return width; } + int Height() const { return height; } + uint32_t *Values() { return values.data(); } + +private: + int width; + int height; + std::vector values; +}; + +class PolyStencilBuffer +{ +public: + static PolyStencilBuffer *Instance(); + void Clear(int newwidth, int newheight, uint8_t stencil_value = 0); + int Width() const { return width; } + int Height() const { return height; } + int BlockWidth() const { return (width + 7) / 8; } + int BlockHeight() const { return (height + 7) / 8; } + uint8_t *Values() { return values.data(); } + uint32_t *Masks() { return masks.data(); } + +private: + int width; + int height; + + // 8x8 blocks of stencil values, plus a mask for each block indicating if values are the same for early out stencil testing + std::vector values; + std::vector masks; +}; + +class PolyVertexBuffer +{ +public: + static TriVertex *GetVertices(int count); + static void Clear(); +}; diff --git a/src/polyrenderer/drawers/poly_draw_args.cpp b/src/polyrenderer/drawers/poly_draw_args.cpp new file mode 100644 index 000000000..1f3556064 --- /dev/null +++ b/src/polyrenderer/drawers/poly_draw_args.cpp @@ -0,0 +1,107 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "poly_draw_args.h" +#include "swrenderer/viewport/r_viewport.h" + +void PolyDrawArgs::SetClipPlane(float a, float b, float c, float d) +{ + clipPlane[0] = a; + clipPlane[1] = b; + clipPlane[2] = c; + clipPlane[3] = d; +} + +void PolyDrawArgs::SetTexture(FTexture *texture) +{ + textureWidth = texture->GetWidth(); + textureHeight = texture->GetHeight(); + auto viewport = swrenderer::RenderViewport::Instance(); + if (viewport->RenderTarget->IsBgra()) + texturePixels = (const uint8_t *)texture->GetPixelsBgra(); + else + texturePixels = texture->GetPixels(); + translation = nullptr; +} + +void PolyDrawArgs::SetTexture(FTexture *texture, uint32_t translationID, bool forcePal) +{ + if (translationID != 0xffffffff && translationID != 0) + { + FRemapTable *table = TranslationToTable(translationID); + if (table != nullptr && !table->Inactive) + { + if (swrenderer::RenderViewport::Instance()->RenderTarget->IsBgra()) + translation = (uint8_t*)table->Palette; + else + translation = table->Remap; + + textureWidth = texture->GetWidth(); + textureHeight = texture->GetHeight(); + texturePixels = texture->GetPixels(); + return; + } + } + + if (forcePal) + { + textureWidth = texture->GetWidth(); + textureHeight = texture->GetHeight(); + texturePixels = texture->GetPixels(); + } + else + { + SetTexture(texture); + } +} + +void PolyDrawArgs::SetColormap(FSWColormap *base_colormap) +{ + uniforms.light_red = base_colormap->Color.r * 256 / 255; + uniforms.light_green = base_colormap->Color.g * 256 / 255; + uniforms.light_blue = base_colormap->Color.b * 256 / 255; + uniforms.light_alpha = base_colormap->Color.a * 256 / 255; + uniforms.fade_red = base_colormap->Fade.r; + uniforms.fade_green = base_colormap->Fade.g; + uniforms.fade_blue = base_colormap->Fade.b; + uniforms.fade_alpha = base_colormap->Fade.a; + uniforms.desaturate = MIN(abs(base_colormap->Desaturate), 255) * 255 / 256; + bool simple_shade = (base_colormap->Color.d == 0x00ffffff && base_colormap->Fade.d == 0x00000000 && base_colormap->Desaturate == 0); + if (simple_shade) + uniforms.flags |= TriUniforms::simple_shade; + else + uniforms.flags &= ~TriUniforms::simple_shade; + colormaps = base_colormap->Maps; +} diff --git a/src/polyrenderer/drawers/poly_draw_args.h b/src/polyrenderer/drawers/poly_draw_args.h new file mode 100644 index 000000000..bf38ffab9 --- /dev/null +++ b/src/polyrenderer/drawers/poly_draw_args.h @@ -0,0 +1,69 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "screen_triangle.h" + +class FTexture; + +enum class TriangleDrawMode +{ + Normal, + Fan, + Strip +}; + +struct TriDrawTriangleArgs; +struct TriMatrix; + +class PolyDrawArgs +{ +public: + TriUniforms uniforms; + const TriMatrix *objectToClip = nullptr; + const TriVertex *vinput = nullptr; + int vcount = 0; + TriangleDrawMode mode = TriangleDrawMode::Normal; + bool ccw = false; + // bool stencilTest = true; // Always true for now + bool subsectorTest = false; + bool writeStencil = true; + bool writeColor = true; + bool writeSubsector = true; + const uint8_t *texturePixels = nullptr; + int textureWidth = 0; + int textureHeight = 0; + const uint8_t *translation = nullptr; + uint8_t stenciltestvalue = 0; + uint8_t stencilwritevalue = 0; + const uint8_t *colormaps = nullptr; + float clipPlane[4]; + TriBlendMode blendmode = TriBlendMode::Copy; + + void SetClipPlane(float a, float b, float c, float d); + void SetTexture(FTexture *texture); + void SetTexture(FTexture *texture, uint32_t translationID, bool forcePal = false); + void SetColormap(FSWColormap *base_colormap); +}; diff --git a/src/polyrenderer/drawers/poly_drawers.h b/src/polyrenderer/drawers/poly_drawers.h new file mode 100644 index 000000000..d17d570f2 --- /dev/null +++ b/src/polyrenderer/drawers/poly_drawers.h @@ -0,0 +1,15414 @@ +/* +** Projected triangle drawer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +/* + Warning: this C++ source file has been auto-generated. Please modify the original php script that generated it. +*/ + +#pragma once + +#include "screen_triangle.h" + +static float FindGradientX(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (y0 - y2) - (c0 - c2) * (y1 - y2); + float bottom = (x1 - x2) * (y0 - y2) - (x0 - x2) * (y1 - y2); + return top / bottom; +} + +static float FindGradientY(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (x0 - x2) - (c0 - c2) * (x1 - x2); + float bottom = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2); + return top / bottom; +} + +static void TriFill32Copy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32AlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32AddSolid(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Add(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Sub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32RevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Stencil(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Shaded(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateCopy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateAlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateAdd(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32TranslateRevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32AddSrcColorOneMinusSrcColor(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill32Skycap(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Copy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32AlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32AddSolid(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Add(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Sub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32RevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Stencil(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Shaded(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + uint32_t fg = color; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateCopy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateAlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateAdd(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32TranslateRevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint32_t * RESTRICT translation = (const uint32_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32AddSrcColorOneMinusSrcColor(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw32Skycap(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint32_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint32_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint32_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Copy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8AlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8AddSolid(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Add(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Sub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8RevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Stencil(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Shaded(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateCopy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateAlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateAdd(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8TranslateRevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8AddSrcColorOneMinusSrcColor(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriFill8Skycap(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Copy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8AlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8AddSolid(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Add(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Sub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8RevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Stencil(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Shaded(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + uint8_t fg = color; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateCopy(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateAlphaBlend(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateAdd(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8TranslateRevSub(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const uint8_t * RESTRICT translation = (const uint8_t *)args->translation; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + fg = translation[fg]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8AddSrcColorOneMinusSrcColor(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +static void TriDraw8Skycap(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint8_t * RESTRICT texPixels = (const uint8_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint8_t * RESTRICT destOrg = (uint8_t*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + uint8_t color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint8_t *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + uint8_t *destptr = dest + x * 8 + ix; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint8_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + uint8_t *destptr = dest + x; + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint8_t fg = texPixels[texelX * texHeight + texelY]; + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + *destptr = fg; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } +} + +std::vector ScreenTriangle::TriFill32 = +{ + &TriFill32Copy, + &TriFill32AlphaBlend, + &TriFill32AddSolid, + &TriFill32Add, + &TriFill32Sub, + &TriFill32RevSub, + &TriFill32Stencil, + &TriFill32Shaded, + &TriFill32TranslateCopy, + &TriFill32TranslateAlphaBlend, + &TriFill32TranslateAdd, + &TriFill32TranslateSub, + &TriFill32TranslateRevSub, + &TriFill32AddSrcColorOneMinusSrcColor, + &TriFill32Skycap, +}; + +std::vector ScreenTriangle::TriDraw32 = +{ + &TriDraw32Copy, + &TriDraw32AlphaBlend, + &TriDraw32AddSolid, + &TriDraw32Add, + &TriDraw32Sub, + &TriDraw32RevSub, + &TriDraw32Stencil, + &TriDraw32Shaded, + &TriDraw32TranslateCopy, + &TriDraw32TranslateAlphaBlend, + &TriDraw32TranslateAdd, + &TriDraw32TranslateSub, + &TriDraw32TranslateRevSub, + &TriDraw32AddSrcColorOneMinusSrcColor, + &TriDraw32Skycap, +}; + +std::vector ScreenTriangle::TriFill8 = +{ + &TriFill8Copy, + &TriFill8AlphaBlend, + &TriFill8AddSolid, + &TriFill8Add, + &TriFill8Sub, + &TriFill8RevSub, + &TriFill8Stencil, + &TriFill8Shaded, + &TriFill8TranslateCopy, + &TriFill8TranslateAlphaBlend, + &TriFill8TranslateAdd, + &TriFill8TranslateSub, + &TriFill8TranslateRevSub, + &TriFill8AddSrcColorOneMinusSrcColor, + &TriFill8Skycap, +}; + +std::vector ScreenTriangle::TriDraw8 = +{ + &TriDraw8Copy, + &TriDraw8AlphaBlend, + &TriDraw8AddSolid, + &TriDraw8Add, + &TriDraw8Sub, + &TriDraw8RevSub, + &TriDraw8Stencil, + &TriDraw8Shaded, + &TriDraw8TranslateCopy, + &TriDraw8TranslateAlphaBlend, + &TriDraw8TranslateAdd, + &TriDraw8TranslateSub, + &TriDraw8TranslateRevSub, + &TriDraw8AddSrcColorOneMinusSrcColor, + &TriDraw8Skycap, +}; + diff --git a/src/polyrenderer/drawers/poly_drawers.php b/src/polyrenderer/drawers/poly_drawers.php new file mode 100644 index 000000000..741fcf3c7 --- /dev/null +++ b/src/polyrenderer/drawers/poly_drawers.php @@ -0,0 +1,449 @@ +/* +** Projected triangle drawer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +/* + Warning: this C++ source file has been auto-generated. Please modify the original php script that generated it. +*/ + +#pragma once + +#include "screen_triangle.h" + +static float FindGradientX(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (y0 - y2) - (c0 - c2) * (y1 - y2); + float bottom = (x1 - x2) * (y0 - y2) - (x0 - x2) * (y1 - y2); + return top / bottom; +} + +static float FindGradientY(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (x0 - x2) - (c0 - c2) * (x1 - x2); + float bottom = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2); + return top / bottom; +} + + +std::vector ScreenTriangle:: = +{ + +}; + + + &, + +static void (const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + auto flags = args->uniforms->flags; + bool is_simple_shade = (flags & TriUniforms::simple_shade) == TriUniforms::simple_shade; + bool is_nearest_filter = (flags & TriUniforms::nearest_filter) == TriUniforms::nearest_filter; + bool is_fixed_light = (flags & TriUniforms::fixed_light) == TriUniforms::fixed_light; + uint32_t lightmask = is_fixed_light ? 0 : 0xffffffff; + auto colormaps = args->colormaps; + uint32_t srcalpha = args->uniforms->srcalpha; + uint32_t destalpha = args->uniforms->destalpha; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + + const uint8_t * RESTRICT texPixels = args->texturePixels; + const * RESTRICT translation = (const *)args->translation; + + const * RESTRICT texPixels = (const *)args->texturePixels; + + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + * RESTRICT destOrg = (*)args->dest; + int pitch = args->pitch; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = args->uniforms->globvis * (1.0f / 32.0f); + + color = args->uniforms->color; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + *dest = destOrg + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int ix = 0; ix < 8; ix++) + { + *destptr = dest + x * 8 + ix; + + *destptr = fg; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + *dest = destOrg + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosY.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + lightpos = (lightpos & lightmask) | ((light << 8) & ~lightmask); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = FRACUNIT - (int)(clamp(shade - MIN(24.0f / 32.0f, globVis * blockPosX.W), 0.0f, 31.0f / 32.0f) * (float)FRACUNIT); + int lightstep = (lightnext - lightpos) / 8; + lightstep = lightstep & lightmask; + + for (int x = 0; x < 8; x++) + { + if ( & (1 << 31)) + { + *destptr = dest + x; + + *destptr = fg; + } + <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + } + + } +} + + + fg = color; + + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + fg = texPixels[texelX * texHeight + texelY]; + + fg = translation[fg]; + + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = (r * lightpos) >> 16; + g = (g * lightpos) >> 16; + b = (b * lightpos) >> 16; + + fg = 0xff000000 | (r << 16) | (g << 8) | b; + + int colormapindex = MIN(((256 - (lightpos >> 8)) * 32) >> 8, 31) << 8; + fg = colormaps[colormapindex + fg]; + + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + + + + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + int sample = texPixels[texelX * texHeight + texelY]; + + uint32_t fgalpha = sample;//clamp(sample, 0, 64) * 4; + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + + uint32_t fgalpha = APART(fg); + uint32_t inv_fgalpha = 256 - fgalpha; + int a = (fgalpha * srcalpha + 128) >> 8; + int inv_a = (destalpha * fgalpha + 256 * inv_fgalpha + 128) >> 8; + + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + + uint32_t inv_r = 256 - (r + (r >> 7)); + uint32_t inv_g = 256 - (g + (r >> 7)); + uint32_t inv_b = 256 - (b + (r >> 7)); + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = r + ((bg_red * inv_r + 127) >> 8); + g = g + ((bg_green * inv_g + 127) >> 8); + b = b + ((bg_blue * inv_b + 127) >> 8); + + int start_fade = 2; // How fast it should fade out + + int alpha_top = clamp(varyingPos[1] >> (16 - start_fade), 0, 256); + int alpha_bottom = clamp(((2 << 24) - varyingPos[1]) >> (16 - start_fade), 0, 256); + int a = MIN(alpha_top, alpha_bottom); + int inv_a = 256 - a; + + uint32_t bg_red = RPART(color); + uint32_t bg_green = GPART(color); + uint32_t bg_blue = BPART(color); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + + uint32_t a = APART(fg); + a += a >> 7; + uint32_t inv_a = 256 - a; + uint32_t bg = *destptr; + uint32_t bg_red = RPART(bg); + uint32_t bg_green = GPART(bg); + uint32_t bg_blue = BPART(bg); + r = (r * a + bg_red * inv_a + 127) >> 8; + g = (g * a + bg_green * inv_a + 127) >> 8; + b = (b * a + bg_blue * inv_a + 127) >> 8; + \ No newline at end of file diff --git a/src/polyrenderer/drawers/poly_triangle.cpp b/src/polyrenderer/drawers/poly_triangle.cpp new file mode 100644 index 000000000..0a58c6e60 --- /dev/null +++ b/src/polyrenderer/drawers/poly_triangle.cpp @@ -0,0 +1,413 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "poly_triangle.h" +#include "polyrenderer/poly_renderer.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "screen_triangle.h" + +CVAR(Bool, r_debug_trisetup, false, 0); + +int PolyTriangleDrawer::viewport_x; +int PolyTriangleDrawer::viewport_y; +int PolyTriangleDrawer::viewport_width; +int PolyTriangleDrawer::viewport_height; +int PolyTriangleDrawer::dest_pitch; +int PolyTriangleDrawer::dest_width; +int PolyTriangleDrawer::dest_height; +uint8_t *PolyTriangleDrawer::dest; +bool PolyTriangleDrawer::dest_bgra; +bool PolyTriangleDrawer::mirror; + +void PolyTriangleDrawer::set_viewport(int x, int y, int width, int height, DCanvas *canvas) +{ + dest = (uint8_t*)canvas->GetBuffer(); + dest_width = canvas->GetWidth(); + dest_height = canvas->GetHeight(); + dest_pitch = canvas->GetPitch(); + dest_bgra = canvas->IsBgra(); + + int offsetx = clamp(x, 0, dest_width); + int offsety = clamp(y, 0, dest_height); + int pixelsize = dest_bgra ? 4 : 1; + + viewport_x = x - offsetx; + viewport_y = y - offsety; + viewport_width = width; + viewport_height = height; + + dest += (offsetx + offsety * dest_pitch) * pixelsize; + dest_width = clamp(viewport_x + viewport_width, 0, dest_width - offsetx); + dest_height = clamp(viewport_y + viewport_height, 0, dest_height - offsety); + + mirror = false; +} + +void PolyTriangleDrawer::toggle_mirror() +{ + mirror = !mirror; +} + +void PolyTriangleDrawer::draw(const PolyDrawArgs &args) +{ + PolyRenderer::Instance()->Thread.DrawQueue->Push(args, mirror); +} + +void PolyTriangleDrawer::draw_arrays(const PolyDrawArgs &drawargs, WorkerThreadData *thread) +{ + if (drawargs.vcount < 3) + return; + + PolyDrawFuncPtr drawfuncs[4]; + int num_drawfuncs = 0; + + drawfuncs[num_drawfuncs++] = drawargs.subsectorTest ? &ScreenTriangle::SetupSubsector : &ScreenTriangle::SetupNormal; + + if (!r_debug_trisetup) // For profiling how much time is spent in setup vs drawal + { + int bmode = (int)drawargs.blendmode; + + if (drawargs.writeColor && drawargs.texturePixels) + drawfuncs[num_drawfuncs++] = dest_bgra ? ScreenTriangle::TriDraw32[bmode] : ScreenTriangle::TriDraw8[bmode]; + else if (drawargs.writeColor) + drawfuncs[num_drawfuncs++] = dest_bgra ? ScreenTriangle::TriFill32[bmode] : ScreenTriangle::TriFill8[bmode]; + } + + if (drawargs.writeStencil) + drawfuncs[num_drawfuncs++] = &ScreenTriangle::StencilWrite; + + if (drawargs.writeSubsector) + drawfuncs[num_drawfuncs++] = &ScreenTriangle::SubsectorWrite; + + TriDrawTriangleArgs args; + args.dest = dest; + args.pitch = dest_pitch; + args.clipleft = 0; + args.clipright = dest_width; + args.cliptop = 0; + args.clipbottom = dest_height; + args.texturePixels = drawargs.texturePixels; + args.textureWidth = drawargs.textureWidth; + args.textureHeight = drawargs.textureHeight; + args.translation = drawargs.translation; + args.uniforms = &drawargs.uniforms; + args.stencilTestValue = drawargs.stenciltestvalue; + args.stencilWriteValue = drawargs.stencilwritevalue; + args.stencilPitch = PolyStencilBuffer::Instance()->BlockWidth(); + args.stencilValues = PolyStencilBuffer::Instance()->Values(); + args.stencilMasks = PolyStencilBuffer::Instance()->Masks(); + args.subsectorGBuffer = PolySubsectorGBuffer::Instance()->Values(); + args.colormaps = drawargs.colormaps; + args.RGB256k = RGB256k.All; + args.BaseColors = (const uint8_t *)GPalette.BaseColors; + + bool ccw = drawargs.ccw; + const TriVertex *vinput = drawargs.vinput; + int vcount = drawargs.vcount; + + ShadedTriVertex vert[3]; + if (drawargs.mode == TriangleDrawMode::Normal) + { + for (int i = 0; i < vcount / 3; i++) + { + for (int j = 0; j < 3; j++) + vert[j] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + draw_shaded_triangle(vert, ccw, &args, thread, drawfuncs, num_drawfuncs); + } + } + else if (drawargs.mode == TriangleDrawMode::Fan) + { + vert[0] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + vert[1] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + for (int i = 2; i < vcount; i++) + { + vert[2] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + draw_shaded_triangle(vert, ccw, &args, thread, drawfuncs, num_drawfuncs); + vert[1] = vert[2]; + } + } + else // TriangleDrawMode::Strip + { + vert[0] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + vert[1] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + for (int i = 2; i < vcount; i++) + { + vert[2] = shade_vertex(*drawargs.objectToClip, drawargs.clipPlane, *(vinput++)); + draw_shaded_triangle(vert, ccw, &args, thread, drawfuncs, num_drawfuncs); + vert[0] = vert[1]; + vert[1] = vert[2]; + ccw = !ccw; + } + } +} + +ShadedTriVertex PolyTriangleDrawer::shade_vertex(const TriMatrix &objectToClip, const float *clipPlane, const TriVertex &v) +{ + // Apply transform to get clip coordinates: + ShadedTriVertex sv = objectToClip * v; + + // Calculate gl_ClipDistance[0] + sv.clipDistance0 = v.x * clipPlane[0] + v.y * clipPlane[1] + v.z * clipPlane[2] + v.w * clipPlane[3]; + + return sv; +} + +void PolyTriangleDrawer::draw_shaded_triangle(const ShadedTriVertex *vert, bool ccw, TriDrawTriangleArgs *args, WorkerThreadData *thread, PolyDrawFuncPtr *drawfuncs, int num_drawfuncs) +{ + // Cull, clip and generate additional vertices as needed + TriVertex clippedvert[max_additional_vertices]; + int numclipvert; + clipedge(vert, clippedvert, numclipvert); + + // Map to 2D viewport: + for (int j = 0; j < numclipvert; j++) + { + auto &v = clippedvert[j]; + + // Calculate normalized device coordinates: + v.w = 1.0f / v.w; + v.x *= v.w; + v.y *= v.w; + v.z *= v.w; + + // Apply viewport scale to get screen coordinates: + v.x = viewport_x + viewport_width * (1.0f + v.x) * 0.5f; + v.y = viewport_y + viewport_height * (1.0f - v.y) * 0.5f; + } + + // Keep varyings in -128 to 128 range if possible + if (numclipvert > 0) + { + for (int j = 0; j < TriVertex::NumVarying; j++) + { + float newOrigin = floorf(clippedvert[0].varying[j] * 0.1f) * 10.0f; + for (int i = 0; i < numclipvert; i++) + { + clippedvert[i].varying[j] -= newOrigin; + } + } + } + + // Draw screen triangles + if (ccw) + { + for (int i = numclipvert; i > 1; i--) + { + args->v1 = &clippedvert[numclipvert - 1]; + args->v2 = &clippedvert[i - 1]; + args->v3 = &clippedvert[i - 2]; + + for (int j = 0; j < num_drawfuncs; j++) + drawfuncs[j](args, thread); + } + } + else + { + for (int i = 2; i < numclipvert; i++) + { + args->v1 = &clippedvert[0]; + args->v2 = &clippedvert[i - 1]; + args->v3 = &clippedvert[i]; + + for (int j = 0; j < num_drawfuncs; j++) + drawfuncs[j](args, thread); + } + } +} + +bool PolyTriangleDrawer::cullhalfspace(float clipdistance1, float clipdistance2, float &t1, float &t2) +{ + if (clipdistance1 < 0.0f && clipdistance2 < 0.0f) + return true; + + if (clipdistance1 < 0.0f) + t1 = MAX(-clipdistance1 / (clipdistance2 - clipdistance1), 0.0f); + else + t1 = 0.0f; + + if (clipdistance2 < 0.0f) + t2 = MIN(1.0f + clipdistance2 / (clipdistance1 - clipdistance2), 1.0f); + else + t2 = 1.0f; + + return false; +} + +void PolyTriangleDrawer::clipedge(const ShadedTriVertex *verts, TriVertex *clippedvert, int &numclipvert) +{ + // Clip and cull so that the following is true for all vertices: + // -v.w <= v.x <= v.w + // -v.w <= v.y <= v.w + // -v.w <= v.z <= v.w + + // use barycentric weights while clipping vertices + float weights[max_additional_vertices * 3 * 2]; + for (int i = 0; i < 3; i++) + { + weights[i * 3 + 0] = 0.0f; + weights[i * 3 + 1] = 0.0f; + weights[i * 3 + 2] = 0.0f; + weights[i * 3 + i] = 1.0f; + } + + // halfspace clip distances + static const int numclipdistances = 7; + float clipdistance[numclipdistances * 3]; + for (int i = 0; i < 3; i++) + { + const auto &v = verts[i]; + clipdistance[i * numclipdistances + 0] = v.x + v.w; + clipdistance[i * numclipdistances + 1] = v.w - v.x; + clipdistance[i * numclipdistances + 2] = v.y + v.w; + clipdistance[i * numclipdistances + 3] = v.w - v.y; + clipdistance[i * numclipdistances + 4] = v.z + v.w; + clipdistance[i * numclipdistances + 5] = v.w - v.z; + clipdistance[i * numclipdistances + 6] = v.clipDistance0; + } + + // Clip against each halfspace + float *input = weights; + float *output = weights + max_additional_vertices * 3; + int inputverts = 3; + int outputverts = 0; + for (int p = 0; p < numclipdistances; p++) + { + // Clip each edge + outputverts = 0; + for (int i = 0; i < inputverts; i++) + { + int j = (i + 1) % inputverts; + float clipdistance1 = + clipdistance[0 * numclipdistances + p] * input[i * 3 + 0] + + clipdistance[1 * numclipdistances + p] * input[i * 3 + 1] + + clipdistance[2 * numclipdistances + p] * input[i * 3 + 2]; + + float clipdistance2 = + clipdistance[0 * numclipdistances + p] * input[j * 3 + 0] + + clipdistance[1 * numclipdistances + p] * input[j * 3 + 1] + + clipdistance[2 * numclipdistances + p] * input[j * 3 + 2]; + + float t1, t2; + if (!cullhalfspace(clipdistance1, clipdistance2, t1, t2) && outputverts + 1 < max_additional_vertices) + { + // add t1 vertex + for (int k = 0; k < 3; k++) + output[outputverts * 3 + k] = input[i * 3 + k] * (1.0f - t1) + input[j * 3 + k] * t1; + outputverts++; + + if (t2 != 1.0f && t2 > t1) + { + // add t2 vertex + for (int k = 0; k < 3; k++) + output[outputverts * 3 + k] = input[i * 3 + k] * (1.0f - t2) + input[j * 3 + k] * t2; + outputverts++; + } + } + } + std::swap(input, output); + std::swap(inputverts, outputverts); + if (inputverts == 0) + break; + } + + // Convert barycentric weights to actual vertices + numclipvert = inputverts; + for (int i = 0; i < numclipvert; i++) + { + auto &v = clippedvert[i]; + memset(&v, 0, sizeof(TriVertex)); + for (int w = 0; w < 3; w++) + { + float weight = input[i * 3 + w]; + v.x += verts[w].x * weight; + v.y += verts[w].y * weight; + v.z += verts[w].z * weight; + v.w += verts[w].w * weight; + for (int iv = 0; iv < TriVertex::NumVarying; iv++) + v.varying[iv] += verts[w].varying[iv] * weight; + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +DrawPolyTrianglesCommand::DrawPolyTrianglesCommand(const PolyDrawArgs &args, bool mirror) + : args(args) +{ + if (mirror) + this->args.ccw = !this->args.ccw; +} + +void DrawPolyTrianglesCommand::Execute(DrawerThread *thread) +{ + WorkerThreadData thread_data; + thread_data.core = thread->core; + thread_data.num_cores = thread->num_cores; + thread_data.pass_start_y = thread->pass_start_y; + thread_data.pass_end_y = thread->pass_end_y; + thread_data.FullSpans = thread->FullSpansBuffer.data(); + thread_data.PartialBlocks = thread->PartialBlocksBuffer.data(); + + PolyTriangleDrawer::draw_arrays(args, &thread_data); +} + +FString DrawPolyTrianglesCommand::DebugInfo() +{ + FString blendmodestr; + switch (args.blendmode) + { + default: blendmodestr = "Unknown"; break; + case TriBlendMode::Copy: blendmodestr = "Copy"; break; + case TriBlendMode::AlphaBlend: blendmodestr = "AlphaBlend"; break; + case TriBlendMode::AddSolid: blendmodestr = "AddSolid"; break; + case TriBlendMode::Add: blendmodestr = "Add"; break; + case TriBlendMode::Sub: blendmodestr = "Sub"; break; + case TriBlendMode::RevSub: blendmodestr = "RevSub"; break; + case TriBlendMode::Stencil: blendmodestr = "Stencil"; break; + case TriBlendMode::Shaded: blendmodestr = "Shaded"; break; + case TriBlendMode::TranslateCopy: blendmodestr = "TranslateCopy"; break; + case TriBlendMode::TranslateAlphaBlend: blendmodestr = "TranslateAlphaBlend"; break; + case TriBlendMode::TranslateAdd: blendmodestr = "TranslateAdd"; break; + case TriBlendMode::TranslateSub: blendmodestr = "TranslateSub"; break; + case TriBlendMode::TranslateRevSub: blendmodestr = "TranslateRevSub"; break; + case TriBlendMode::AddSrcColorOneMinusSrcColor: blendmodestr = "AddSrcColorOneMinusSrcColor"; break; + } + + FString info; + info.Format("DrawPolyTriangles: blend mode = %s, color = %d, light = %d, textureWidth = %d, textureHeight = %d, texture = %s, translation = %s, colormaps = %s", + blendmodestr.GetChars(), args.uniforms.color, args.uniforms.light, args.textureWidth, args.textureHeight, + args.texturePixels ? "ptr" : "null", args.translation ? "ptr" : "null", args.colormaps ? "ptr" : "null"); + return info; +} diff --git a/src/polyrenderer/drawers/poly_triangle.h b/src/polyrenderer/drawers/poly_triangle.h new file mode 100644 index 000000000..8debeec8a --- /dev/null +++ b/src/polyrenderer/drawers/poly_triangle.h @@ -0,0 +1,73 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/drawers/r_thread.h" +#include "polyrenderer/drawers/screen_triangle.h" +#include "polyrenderer/math/tri_matrix.h" +#include "polyrenderer/drawers/poly_buffer.h" +#include "polyrenderer/drawers/poly_draw_args.h" + +struct ShadedTriVertex : public TriVertex +{ + float clipDistance0; +}; + +typedef void(*PolyDrawFuncPtr)(const TriDrawTriangleArgs *, WorkerThreadData *); + +class PolyTriangleDrawer +{ +public: + static void set_viewport(int x, int y, int width, int height, DCanvas *canvas); + static void draw(const PolyDrawArgs &args); + static void toggle_mirror(); + +private: + static ShadedTriVertex shade_vertex(const TriMatrix &objectToClip, const float *clipPlane, const TriVertex &v); + static void draw_arrays(const PolyDrawArgs &args, WorkerThreadData *thread); + static void draw_shaded_triangle(const ShadedTriVertex *vertices, bool ccw, TriDrawTriangleArgs *args, WorkerThreadData *thread, PolyDrawFuncPtr *drawfuncs, int num_drawfuncs); + static bool cullhalfspace(float clipdistance1, float clipdistance2, float &t1, float &t2); + static void clipedge(const ShadedTriVertex *verts, TriVertex *clippedvert, int &numclipvert); + + static int viewport_x, viewport_y, viewport_width, viewport_height, dest_pitch, dest_width, dest_height; + static bool dest_bgra; + static uint8_t *dest; + static bool mirror; + + enum { max_additional_vertices = 16 }; + + friend class DrawPolyTrianglesCommand; +}; + +class DrawPolyTrianglesCommand : public DrawerCommand +{ +public: + DrawPolyTrianglesCommand(const PolyDrawArgs &args, bool mirror); + + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + +private: + PolyDrawArgs args; +}; diff --git a/src/polyrenderer/drawers/screen_triangle.cpp b/src/polyrenderer/drawers/screen_triangle.cpp new file mode 100644 index 000000000..54af97f2c --- /dev/null +++ b/src/polyrenderer/drawers/screen_triangle.cpp @@ -0,0 +1,966 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "poly_triangle.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "screen_triangle.h" +#include "poly_drawers.h" + +void ScreenTriangle::SetupNormal(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + int clipright = args->clipright; + int clipbottom = args->clipbottom; + + int stencilPitch = args->stencilPitch; + uint8_t * RESTRICT stencilValues = args->stencilValues; + uint32_t * RESTRICT stencilMasks = args->stencilMasks; + uint8_t stencilTestValue = args->stencilTestValue; + + TriFullSpan * RESTRICT span = thread->FullSpans; + TriPartialBlock * RESTRICT partial = thread->PartialBlocks; + + // 28.4 fixed-point coordinates + const int Y1 = (int)round(16.0f * v1.y); + const int Y2 = (int)round(16.0f * v2.y); + const int Y3 = (int)round(16.0f * v3.y); + + const int X1 = (int)round(16.0f * v1.x); + const int X2 = (int)round(16.0f * v2.x); + const int X3 = (int)round(16.0f * v3.x); + + // Deltas + const int DX12 = X1 - X2; + const int DX23 = X2 - X3; + const int DX31 = X3 - X1; + + const int DY12 = Y1 - Y2; + const int DY23 = Y2 - Y3; + const int DY31 = Y3 - Y1; + + // Fixed-point deltas + const int FDX12 = DX12 << 4; + const int FDX23 = DX23 << 4; + const int FDX31 = DX31 << 4; + + const int FDY12 = DY12 << 4; + const int FDY23 = DY23 << 4; + const int FDY31 = DY31 << 4; + + // Bounding rectangle + int minx = MAX((MIN(MIN(X1, X2), X3) + 0xF) >> 4, 0); + int maxx = MIN((MAX(MAX(X1, X2), X3) + 0xF) >> 4, clipright - 1); + int miny = MAX((MIN(MIN(Y1, Y2), Y3) + 0xF) >> 4, 0); + int maxy = MIN((MAX(MAX(Y1, Y2), Y3) + 0xF) >> 4, clipbottom - 1); + if (minx >= maxx || miny >= maxy) + { + thread->NumFullSpans = 0; + thread->NumPartialBlocks = 0; + return; + } + + // Block size, standard 8x8 (must be power of two) + const int q = 8; + + // Start in corner of 8x8 block + minx &= ~(q - 1); + miny &= ~(q - 1); + + // Half-edge constants + int C1 = DY12 * X1 - DX12 * Y1; + int C2 = DY23 * X2 - DX23 * Y2; + int C3 = DY31 * X3 - DX31 * Y3; + + // Correct for fill convention + if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++; + if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++; + if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++; + + // First block line for this thread + int core = thread->core; + int num_cores = thread->num_cores; + int core_skip = (num_cores - ((miny / q) - core) % num_cores) % num_cores; + miny += core_skip * q; + + thread->StartX = minx; + thread->StartY = miny; + span->Length = 0; + + // Loop through blocks + for (int y = miny; y < maxy; y += q * num_cores) + { + for (int x = minx; x < maxx; x += q) + { + // Corners of block + int x0 = x << 4; + int x1 = (x + q - 1) << 4; + int y0 = y << 4; + int y1 = (y + q - 1) << 4; + + // Evaluate half-space functions + bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0; + bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0; + bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0; + bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0; + int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3); + + bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0; + bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0; + bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0; + bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0; + int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3); + + bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0; + bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0; + bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0; + bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0; + int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3); + + // Stencil test the whole block, if possible + int block = x / 8 + y / 8 * stencilPitch; + uint8_t *stencilBlock = &stencilValues[block * 64]; + uint32_t *stencilBlockMask = &stencilMasks[block]; + bool blockIsSingleStencil = ((*stencilBlockMask) & 0xffffff00) == 0xffffff00; + bool skipBlock = blockIsSingleStencil && ((*stencilBlockMask) & 0xff) != stencilTestValue; + + // Skip block when outside an edge + if (a == 0 || b == 0 || c == 0 || skipBlock) + { + if (span->Length != 0) + { + span++; + span->Length = 0; + } + continue; + } + + // Accept whole block when totally covered + if (a == 0xf && b == 0xf && c == 0xf && x + q <= clipright && y + q <= clipbottom && blockIsSingleStencil) + { + if (span->Length != 0) + { + span->Length++; + } + else + { + span->X = x; + span->Y = y; + span->Length = 1; + } + } + else // Partially covered block + { + x0 = x << 4; + x1 = (x + q - 1) << 4; + int CY1 = C1 + DX12 * y0 - DY12 * x0; + int CY2 = C2 + DX23 * y0 - DY23 * x0; + int CY3 = C3 + DX31 * y0 - DY31 * x0; + + uint32_t mask0 = 0; + uint32_t mask1 = 0; + + for (int iy = 0; iy < 4; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool passStencilTest = blockIsSingleStencil || stencilBlock[ix + iy * q] == stencilTestValue; + bool covered = (CX1 > 0 && CX2 > 0 && CX3 > 0 && (x + ix) < clipright && (y + iy) < clipbottom && passStencilTest); + mask0 <<= 1; + mask0 |= (uint32_t)covered; + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + } + + for (int iy = 4; iy < q; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool passStencilTest = blockIsSingleStencil || stencilBlock[ix + iy * q] == stencilTestValue; + bool covered = (CX1 > 0 && CX2 > 0 && CX3 > 0 && (x + ix) < clipright && (y + iy) < clipbottom && passStencilTest); + mask1 <<= 1; + mask1 |= (uint32_t)covered; + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + } + + if (mask0 != 0xffffffff || mask1 != 0xffffffff) + { + if (span->Length > 0) + { + span++; + span->Length = 0; + } + + if (mask0 == 0 && mask1 == 0) + continue; + + partial->X = x; + partial->Y = y; + partial->Mask0 = mask0; + partial->Mask1 = mask1; + partial++; + } + else if (span->Length != 0) + { + span->Length++; + } + else + { + span->X = x; + span->Y = y; + span->Length = 1; + } + } + } + + if (span->Length != 0) + { + span++; + span->Length = 0; + } + } + + thread->NumFullSpans = (int)(span - thread->FullSpans); + thread->NumPartialBlocks = (int)(partial - thread->PartialBlocks); +} + +void ScreenTriangle::SetupSubsector(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + int clipright = args->clipright; + int clipbottom = args->clipbottom; + + int stencilPitch = args->stencilPitch; + uint8_t * RESTRICT stencilValues = args->stencilValues; + uint32_t * RESTRICT stencilMasks = args->stencilMasks; + uint8_t stencilTestValue = args->stencilTestValue; + + uint32_t * RESTRICT subsectorGBuffer = args->subsectorGBuffer; + uint32_t subsectorDepth = args->uniforms->subsectorDepth; + int32_t pitch = args->pitch; + + TriFullSpan * RESTRICT span = thread->FullSpans; + TriPartialBlock * RESTRICT partial = thread->PartialBlocks; + + // 28.4 fixed-point coordinates + const int Y1 = (int)round(16.0f * v1.y); + const int Y2 = (int)round(16.0f * v2.y); + const int Y3 = (int)round(16.0f * v3.y); + + const int X1 = (int)round(16.0f * v1.x); + const int X2 = (int)round(16.0f * v2.x); + const int X3 = (int)round(16.0f * v3.x); + + // Deltas + const int DX12 = X1 - X2; + const int DX23 = X2 - X3; + const int DX31 = X3 - X1; + + const int DY12 = Y1 - Y2; + const int DY23 = Y2 - Y3; + const int DY31 = Y3 - Y1; + + // Fixed-point deltas + const int FDX12 = DX12 << 4; + const int FDX23 = DX23 << 4; + const int FDX31 = DX31 << 4; + + const int FDY12 = DY12 << 4; + const int FDY23 = DY23 << 4; + const int FDY31 = DY31 << 4; + + // Bounding rectangle + int minx = MAX((MIN(MIN(X1, X2), X3) + 0xF) >> 4, 0); + int maxx = MIN((MAX(MAX(X1, X2), X3) + 0xF) >> 4, clipright - 1); + int miny = MAX((MIN(MIN(Y1, Y2), Y3) + 0xF) >> 4, 0); + int maxy = MIN((MAX(MAX(Y1, Y2), Y3) + 0xF) >> 4, clipbottom - 1); + if (minx >= maxx || miny >= maxy) + { + thread->NumFullSpans = 0; + thread->NumPartialBlocks = 0; + return; + } + + // Block size, standard 8x8 (must be power of two) + const int q = 8; + + // Start in corner of 8x8 block + minx &= ~(q - 1); + miny &= ~(q - 1); + + // Half-edge constants + int C1 = DY12 * X1 - DX12 * Y1; + int C2 = DY23 * X2 - DX23 * Y2; + int C3 = DY31 * X3 - DX31 * Y3; + + // Correct for fill convention + if (DY12 < 0 || (DY12 == 0 && DX12 > 0)) C1++; + if (DY23 < 0 || (DY23 == 0 && DX23 > 0)) C2++; + if (DY31 < 0 || (DY31 == 0 && DX31 > 0)) C3++; + + // First block line for this thread + int core = thread->core; + int num_cores = thread->num_cores; + int core_skip = (num_cores - ((miny / q) - core) % num_cores) % num_cores; + miny += core_skip * q; + + thread->StartX = minx; + thread->StartY = miny; + span->Length = 0; + + // Loop through blocks + for (int y = miny; y < maxy; y += q * num_cores) + { + for (int x = minx; x < maxx; x += q) + { + // Corners of block + int x0 = x << 4; + int x1 = (x + q - 1) << 4; + int y0 = y << 4; + int y1 = (y + q - 1) << 4; + + // Evaluate half-space functions + bool a00 = C1 + DX12 * y0 - DY12 * x0 > 0; + bool a10 = C1 + DX12 * y0 - DY12 * x1 > 0; + bool a01 = C1 + DX12 * y1 - DY12 * x0 > 0; + bool a11 = C1 + DX12 * y1 - DY12 * x1 > 0; + int a = (a00 << 0) | (a10 << 1) | (a01 << 2) | (a11 << 3); + + bool b00 = C2 + DX23 * y0 - DY23 * x0 > 0; + bool b10 = C2 + DX23 * y0 - DY23 * x1 > 0; + bool b01 = C2 + DX23 * y1 - DY23 * x0 > 0; + bool b11 = C2 + DX23 * y1 - DY23 * x1 > 0; + int b = (b00 << 0) | (b10 << 1) | (b01 << 2) | (b11 << 3); + + bool c00 = C3 + DX31 * y0 - DY31 * x0 > 0; + bool c10 = C3 + DX31 * y0 - DY31 * x1 > 0; + bool c01 = C3 + DX31 * y1 - DY31 * x0 > 0; + bool c11 = C3 + DX31 * y1 - DY31 * x1 > 0; + int c = (c00 << 0) | (c10 << 1) | (c01 << 2) | (c11 << 3); + + // Stencil test the whole block, if possible + int block = x / 8 + y / 8 * stencilPitch; + uint8_t *stencilBlock = &stencilValues[block * 64]; + uint32_t *stencilBlockMask = &stencilMasks[block]; + bool blockIsSingleStencil = ((*stencilBlockMask) & 0xffffff00) == 0xffffff00; + bool skipBlock = blockIsSingleStencil && ((*stencilBlockMask) & 0xff) < stencilTestValue; + + // Skip block when outside an edge + if (a == 0 || b == 0 || c == 0 || skipBlock) + { + if (span->Length != 0) + { + span++; + span->Length = 0; + } + continue; + } + + // Accept whole block when totally covered + if (a == 0xf && b == 0xf && c == 0xf && x + q <= clipright && y + q <= clipbottom && blockIsSingleStencil) + { + // Totally covered block still needs a subsector coverage test: + + uint32_t *subsector = subsectorGBuffer + x + y * pitch; + + uint32_t mask0 = 0; + uint32_t mask1 = 0; + + for (int iy = 0; iy < 4; iy++) + { + for (int ix = 0; ix < q; ix++) + { + bool covered = subsector[ix] >= subsectorDepth; + mask0 <<= 1; + mask0 |= (uint32_t)covered; + } + subsector += pitch; + } + + for (int iy = 4; iy < q; iy++) + { + for (int ix = 0; ix < q; ix++) + { + bool covered = subsector[ix] >= subsectorDepth; + mask1 <<= 1; + mask1 |= (uint32_t)covered; + } + subsector += pitch; + } + + if (mask0 != 0xffffffff || mask1 != 0xffffffff) + { + if (span->Length > 0) + { + span++; + span->Length = 0; + } + + if (mask0 == 0 && mask1 == 0) + continue; + + partial->X = x; + partial->Y = y; + partial->Mask0 = mask0; + partial->Mask1 = mask1; + partial++; + } + else if (span->Length != 0) + { + span->Length++; + } + else + { + span->X = x; + span->Y = y; + span->Length = 1; + } + } + else // Partially covered block + { + x0 = x << 4; + x1 = (x + q - 1) << 4; + int CY1 = C1 + DX12 * y0 - DY12 * x0; + int CY2 = C2 + DX23 * y0 - DY23 * x0; + int CY3 = C3 + DX31 * y0 - DY31 * x0; + + uint32_t *subsector = subsectorGBuffer + x + y * pitch; + + uint32_t mask0 = 0; + uint32_t mask1 = 0; + + for (int iy = 0; iy < 4; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool passStencilTest = blockIsSingleStencil || stencilBlock[ix + iy * q] >= stencilTestValue; + bool covered = (CX1 > 0 && CX2 > 0 && CX3 > 0 && (x + ix) < clipright && (y + iy) < clipbottom && passStencilTest && subsector[ix] >= subsectorDepth); + mask0 <<= 1; + mask0 |= (uint32_t)covered; + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + subsector += pitch; + } + + for (int iy = 4; iy < q; iy++) + { + int CX1 = CY1; + int CX2 = CY2; + int CX3 = CY3; + + for (int ix = 0; ix < q; ix++) + { + bool passStencilTest = blockIsSingleStencil || stencilBlock[ix + iy * q] >= stencilTestValue; + bool covered = (CX1 > 0 && CX2 > 0 && CX3 > 0 && (x + ix) < clipright && (y + iy) < clipbottom && passStencilTest && subsector[ix] >= subsectorDepth); + mask1 <<= 1; + mask1 |= (uint32_t)covered; + + CX1 -= FDY12; + CX2 -= FDY23; + CX3 -= FDY31; + } + + CY1 += FDX12; + CY2 += FDX23; + CY3 += FDX31; + subsector += pitch; + } + + if (mask0 != 0xffffffff || mask1 != 0xffffffff) + { + if (span->Length > 0) + { + span++; + span->Length = 0; + } + + if (mask0 == 0 && mask1 == 0) + continue; + + partial->X = x; + partial->Y = y; + partial->Mask0 = mask0; + partial->Mask1 = mask1; + partial++; + } + else if (span->Length != 0) + { + span->Length++; + } + else + { + span->X = x; + span->Y = y; + span->Length = 1; + } + } + } + + if (span->Length != 0) + { + span++; + span->Length = 0; + } + } + + thread->NumFullSpans = (int)(span - thread->FullSpans); + thread->NumPartialBlocks = (int)(partial - thread->PartialBlocks); +} + +void ScreenTriangle::StencilWrite(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + uint8_t * RESTRICT stencilValues = args->stencilValues; + uint32_t * RESTRICT stencilMasks = args->stencilMasks; + uint32_t stencilWriteValue = args->stencilWriteValue; + uint32_t stencilPitch = args->stencilPitch; + + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + int block = span.X / 8 + span.Y / 8 * stencilPitch; + uint8_t *stencilBlock = &stencilValues[block * 64]; + uint32_t *stencilBlockMask = &stencilMasks[block]; + + int width = span.Length; + for (int x = 0; x < width; x++) + stencilBlockMask[x] = 0xffffff00 | stencilWriteValue; + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + + int sblock = block.X / 8 + block.Y / 8 * stencilPitch; + uint8_t *stencilBlock = &stencilValues[sblock * 64]; + uint32_t *stencilBlockMask = &stencilMasks[sblock]; + + bool isSingleValue = ((*stencilBlockMask) & 0xffffff00) == 0xffffff00; + if (isSingleValue) + { + uint8_t value = (*stencilBlockMask) & 0xff; + for (int v = 0; v < 64; v++) + stencilBlock[v] = value; + *stencilBlockMask = 0; + } + + int count = 0; + for (int v = 0; v < 32; v++) + { + if ((mask0 & (1 << 31)) || stencilBlock[v] == stencilWriteValue) + { + stencilBlock[v] = stencilWriteValue; + count++; + } + mask0 <<= 1; + } + for (int v = 32; v < 64; v++) + { + if ((mask1 & (1 << 31)) || stencilBlock[v] == stencilWriteValue) + { + stencilBlock[v] = stencilWriteValue; + count++; + } + mask1 <<= 1; + } + + if (count == 64) + *stencilBlockMask = 0xffffff00 | stencilWriteValue; + } +} + +void ScreenTriangle::SubsectorWrite(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + uint32_t * RESTRICT subsectorGBuffer = args->subsectorGBuffer; + uint32_t subsectorDepth = args->uniforms->subsectorDepth; + int pitch = args->pitch; + + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *subsector = subsectorGBuffer + span.X + span.Y * pitch; + int width = span.Length * 8; + int height = 8; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + subsector[x] = subsectorDepth; + subsector += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + uint32_t *subsector = subsectorGBuffer + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + subsector[x] = subsectorDepth; + mask0 <<= 1; + } + subsector += pitch; + } + for (int y = 4; y < 8; y++) + { + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + subsector[x] = subsectorDepth; + mask1 <<= 1; + } + subsector += pitch; + } + } +} + +#if 0 +float ScreenTriangle::FindGradientX(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (y0 - y2) - (c0 - c2) * (y1 - y2); + float bottom = (x1 - x2) * (y0 - y2) - (x0 - x2) * (y1 - y2); + return top / bottom; +} + +float ScreenTriangle::FindGradientY(float x0, float y0, float x1, float y1, float x2, float y2, float c0, float c1, float c2) +{ + float top = (c1 - c2) * (x0 - x2) - (c0 - c2) * (x1 - x2); + float bottom = (x0 - x2) * (y1 - y2) - (x1 - x2) * (y0 - y2); + return top / bottom; +} + +void ScreenTriangle::Draw(const TriDrawTriangleArgs *args, WorkerThreadData *thread) +{ + int numSpans = thread->NumFullSpans; + auto fullSpans = thread->FullSpans; + int numBlocks = thread->NumPartialBlocks; + auto partialBlocks = thread->PartialBlocks; + int startX = thread->StartX; + int startY = thread->StartY; + + // Calculate gradients + const TriVertex &v1 = *args->v1; + const TriVertex &v2 = *args->v2; + const TriVertex &v3 = *args->v3; + ScreenTriangleStepVariables gradientX; + ScreenTriangleStepVariables gradientY; + ScreenTriangleStepVariables start; + gradientX.W = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + gradientY.W = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.w, v2.w, v3.w); + start.W = v1.w + gradientX.W * (startX - v1.x) + gradientY.W * (startY - v1.y); + for (int i = 0; i < TriVertex::NumVarying; i++) + { + gradientX.Varying[i] = FindGradientX(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + gradientY.Varying[i] = FindGradientY(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v1.varying[i] * v1.w, v2.varying[i] * v2.w, v3.varying[i] * v3.w); + start.Varying[i] = v1.varying[i] * v1.w + gradientX.Varying[i] * (startX - v1.x) + gradientY.Varying[i] * (startY - v1.y); + } + + const uint32_t * RESTRICT texPixels = (const uint32_t *)args->texturePixels; + uint32_t texWidth = args->textureWidth; + uint32_t texHeight = args->textureHeight; + + uint32_t * RESTRICT destOrg = (uint32_t*)args->dest; + uint32_t * RESTRICT subsectorGBuffer = (uint32_t*)args->subsectorGBuffer; + int pitch = args->pitch; + + uint32_t subsectorDepth = args->uniforms->subsectorDepth; + + uint32_t light = args->uniforms->light; + float shade = (64.0f - (light * 255 / 256 + 12.0f) * 32.0f / 128.0f) / 32.0f; + float globVis = 1706.0f; + + for (int i = 0; i < numSpans; i++) + { + const auto &span = fullSpans[i]; + + uint32_t *dest = destOrg + span.X + span.Y * pitch; + uint32_t *subsector = subsectorGBuffer + span.X + span.Y * pitch; + int width = span.Length; + int height = 8; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (span.X - startX) + gradientY.W * (span.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (span.X - startX) + gradientY.Varying[j] * (span.Y - startY); + + for (int y = 0; y < height; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + int lightpos = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + + for (int x = 0; x < width; x++) + { + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + int lightstep = (lightnext - lightpos) / 8; + + for (int ix = 0; ix < 8; ix++) + { + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = r * lightpos / 256; + g = g * lightpos / 256; + b = b * lightpos / 256; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + + dest[x * 8 + ix] = fg; + subsector[x * 8 + ix] = subsectorDepth; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + subsector += pitch; + } + } + + for (int i = 0; i < numBlocks; i++) + { + const auto &block = partialBlocks[i]; + + ScreenTriangleStepVariables blockPosY; + blockPosY.W = start.W + gradientX.W * (block.X - startX) + gradientY.W * (block.Y - startY); + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] = start.Varying[j] + gradientX.Varying[j] * (block.X - startX) + gradientY.Varying[j] * (block.Y - startY); + + uint32_t *dest = destOrg + block.X + block.Y * pitch; + uint32_t *subsector = subsectorGBuffer + block.X + block.Y * pitch; + uint32_t mask0 = block.Mask0; + uint32_t mask1 = block.Mask1; + for (int y = 0; y < 4; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + int lightstep = (lightnext - lightpos) / 8; + + for (int x = 0; x < 8; x++) + { + if (mask0 & (1 << 31)) + { + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = r * lightpos / 256; + g = g * lightpos / 256; + b = b * lightpos / 256; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + + dest[x] = fg; + subsector[x] = subsectorDepth; + } + mask0 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + subsector += pitch; + } + for (int y = 4; y < 8; y++) + { + ScreenTriangleStepVariables blockPosX = blockPosY; + + float rcpW = 0x01000000 / blockPosX.W; + int32_t varyingPos[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] = (int32_t)(blockPosX.Varying[j] * rcpW); + + int lightpos = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + + blockPosX.W += gradientX.W * 8; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosX.Varying[j] += gradientX.Varying[j] * 8; + + rcpW = 0x01000000 / blockPosX.W; + int32_t varyingStep[TriVertex::NumVarying]; + for (int j = 0; j < TriVertex::NumVarying; j++) + { + int32_t nextPos = (int32_t)(blockPosX.Varying[j] * rcpW); + varyingStep[j] = (nextPos - varyingPos[j]) / 8; + } + + int lightnext = 256 - (int)(clamp(shade - MIN(24.0f, globVis * blockPosX.W) / 32.0f, 0.0f, 31.0f / 32.0f) * 256.0f); + int lightstep = (lightnext - lightpos) / 8; + + for (int x = 0; x < 8; x++) + { + if (mask1 & (1 << 31)) + { + int texelX = ((((uint32_t)varyingPos[0] << 8) >> 16) * texWidth) >> 16; + int texelY = ((((uint32_t)varyingPos[1] << 8) >> 16) * texHeight) >> 16; + uint32_t fg = texPixels[texelX * texHeight + texelY]; + + uint32_t r = RPART(fg); + uint32_t g = GPART(fg); + uint32_t b = BPART(fg); + r = r * lightpos / 256; + g = g * lightpos / 256; + b = b * lightpos / 256; + fg = 0xff000000 | (r << 16) | (g << 8) | b; + + dest[x] = fg; + subsector[x] = subsectorDepth; + } + mask1 <<= 1; + + for (int j = 0; j < TriVertex::NumVarying; j++) + varyingPos[j] += varyingStep[j]; + lightpos += lightstep; + } + + blockPosY.W += gradientY.W; + for (int j = 0; j < TriVertex::NumVarying; j++) + blockPosY.Varying[j] += gradientY.Varying[j]; + + dest += pitch; + subsector += pitch; + } + } +} +#endif diff --git a/src/polyrenderer/drawers/screen_triangle.h b/src/polyrenderer/drawers/screen_triangle.h new file mode 100644 index 000000000..cd2a6cbe0 --- /dev/null +++ b/src/polyrenderer/drawers/screen_triangle.h @@ -0,0 +1,164 @@ +/* +** Projected triangle drawer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include +#include + +class FString; + +struct TriFullSpan +{ + uint16_t X; + uint16_t Y; + uint32_t Length; +}; + +struct TriPartialBlock +{ + uint16_t X; + uint16_t Y; + uint32_t Mask0; + uint32_t Mask1; +}; + +struct WorkerThreadData +{ + int32_t core; + int32_t num_cores; + int32_t pass_start_y; + int32_t pass_end_y; + uint32_t *temp; + + // Triangle working data: + TriFullSpan *FullSpans; + TriPartialBlock *PartialBlocks; + uint32_t NumFullSpans; + uint32_t NumPartialBlocks; + int32_t StartX; + int32_t StartY; +}; + +struct TriVertex +{ + TriVertex() { } + TriVertex(float x, float y, float z, float w, float u, float v) : x(x), y(y), z(z), w(w) { varying[0] = u; varying[1] = v; } + + enum { NumVarying = 2 }; + float x, y, z, w; + float varying[NumVarying]; +}; + +struct TriUniforms +{ + uint32_t light; + uint32_t subsectorDepth; + uint32_t color; + uint32_t srcalpha; + uint32_t destalpha; + uint16_t light_alpha; + uint16_t light_red; + uint16_t light_green; + uint16_t light_blue; + uint16_t fade_alpha; + uint16_t fade_red; + uint16_t fade_green; + uint16_t fade_blue; + uint16_t desaturate; + float globvis; + uint32_t flags; + enum Flags + { + simple_shade = 1, + nearest_filter = 2, + fixed_light = 4 + }; +}; + +struct TriDrawTriangleArgs +{ + uint8_t *dest; + int32_t pitch; + TriVertex *v1; + TriVertex *v2; + TriVertex *v3; + int32_t clipleft; + int32_t clipright; + int32_t cliptop; + int32_t clipbottom; + const uint8_t *texturePixels; + uint32_t textureWidth; + uint32_t textureHeight; + const uint8_t *translation; + const TriUniforms *uniforms; + uint8_t *stencilValues; + uint32_t *stencilMasks; + int32_t stencilPitch; + uint8_t stencilTestValue; + uint8_t stencilWriteValue; + uint32_t *subsectorGBuffer; + const uint8_t *colormaps; + const uint8_t *RGB256k; + const uint8_t *BaseColors; +}; + +enum class TriBlendMode +{ + Copy, // blend_copy(shade(fg)) + AlphaBlend, // blend_alpha_blend(shade(fg), bg) + AddSolid, // blend_add(shade(fg), bg, srcalpha, destalpha) + Add, // blend_add(shade(fg), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + Sub, // blend_sub(shade(fg), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + RevSub, // blend_revsub(shade(fg), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + Stencil, // blend_stencil(shade(color), fg.a, bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + Shaded, // blend_stencil(shade(color), fg.index, bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + TranslateCopy, // blend_copy(shade(translate(fg))) + TranslateAlphaBlend, // blend_alpha_blend(shade(translate(fg)), bg) + TranslateAdd, // blend_add(shade(translate(fg)), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + TranslateSub, // blend_sub(shade(translate(fg)), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + TranslateRevSub,// blend_revsub(shade(translate(fg)), bg, srcalpha, calc_blend_bgalpha(fg, destalpha)) + AddSrcColorOneMinusSrcColor, // glBlendMode(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR) used by GZDoom's fullbright additive sprites + Skycap // Fade to sky color when the V texture coordinate go beyond the [-1, 1] range +}; + +inline int NumTriBlendModes() { return (int)TriBlendMode::Skycap + 1; } + +class ScreenTriangle +{ +public: + static void SetupNormal(const TriDrawTriangleArgs *args, WorkerThreadData *thread); + static void SetupSubsector(const TriDrawTriangleArgs *args, WorkerThreadData *thread); + static void StencilWrite(const TriDrawTriangleArgs *args, WorkerThreadData *thread); + static void SubsectorWrite(const TriDrawTriangleArgs *args, WorkerThreadData *thread); + + static std::vector TriDraw8; + static std::vector TriDraw32; + static std::vector TriFill8; + static std::vector TriFill32; +}; + +struct ScreenTriangleStepVariables +{ + float W; + float Varying[TriVertex::NumVarying]; +}; diff --git a/src/polyrenderer/math/poly_intersection.cpp b/src/polyrenderer/math/poly_intersection.cpp new file mode 100644 index 000000000..ed5e8ef43 --- /dev/null +++ b/src/polyrenderer/math/poly_intersection.cpp @@ -0,0 +1,235 @@ +/* +** Various 3D intersection tests +** Copyright (c) 1997-2015 The UICore Team +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "poly_intersection.h" + +IntersectionTest::Result IntersectionTest::plane_aabb(const Vec4f &plane, const AxisAlignedBoundingBox &aabb) +{ + Vec3f center = aabb.center(); + Vec3f extents = aabb.extents(); + float e = extents.x * std::abs(plane.x) + extents.y * std::abs(plane.y) + extents.z * std::abs(plane.z); + float s = center.x * plane.x + center.y * plane.y + center.z * plane.z + plane.w; + if (s - e > 0) + return inside; + else if (s + e < 0) + return outside; + else + return intersecting; +} + +IntersectionTest::Result IntersectionTest::plane_obb(const Vec4f &plane, const OrientedBoundingBox &obb) +{ + Vec3f n(plane); + float d = plane.w; + float e = obb.extents.x * std::abs(Vec3f::dot(obb.axis_x, n)) + obb.extents.y * std::abs(Vec3f::dot(obb.axis_y, n)) + obb.extents.z * std::abs(Vec3f::dot(obb.axis_z, n)); + float s = Vec3f::dot(obb.center, n) + d; + if (s - e > 0) + return inside; + else if (s + e < 0) + return outside; + else + return intersecting; +} + +IntersectionTest::OverlapResult IntersectionTest::sphere(const Vec3f ¢er1, float radius1, const Vec3f ¢er2, float radius2) +{ + Vec3f h = center1 - center2; + float square_distance = Vec3f::dot(h, h); + float radius_sum = radius1 + radius2; + if (square_distance > radius_sum * radius_sum) + return disjoint; + else + return overlap; +} + +IntersectionTest::OverlapResult IntersectionTest::sphere_aabb(const Vec3f ¢er, float radius, const AxisAlignedBoundingBox &aabb) +{ + Vec3f a = aabb.aabb_min - center; + Vec3f b = center - aabb.aabb_max; + a.x = std::max(a.x, 0.0f); + a.y = std::max(a.y, 0.0f); + a.z = std::max(a.z, 0.0f); + b.x = std::max(b.x, 0.0f); + b.y = std::max(b.y, 0.0f); + b.z = std::max(b.z, 0.0f); + Vec3f e = a + b; + float d = Vec3f::dot(e, e); + if (d > radius * radius) + return disjoint; + else + return overlap; +} + +IntersectionTest::OverlapResult IntersectionTest::aabb(const AxisAlignedBoundingBox &a, const AxisAlignedBoundingBox &b) +{ + if (a.aabb_min.x > b.aabb_max.x || b.aabb_min.x > a.aabb_max.x || + a.aabb_min.y > b.aabb_max.y || b.aabb_min.y > a.aabb_max.y || + a.aabb_min.z > b.aabb_max.z || b.aabb_min.z > a.aabb_max.z) + { + return disjoint; + } + else + { + return overlap; + } +} + +IntersectionTest::Result IntersectionTest::frustum_aabb(const FrustumPlanes &frustum, const AxisAlignedBoundingBox &box) +{ + bool is_intersecting = false; + for (int i = 0; i < 6; i++) + { + Result result = plane_aabb(frustum.planes[i], box); + if (result == outside) + return outside; + else if (result == intersecting) + is_intersecting = true; + break; + } + if (is_intersecting) + return intersecting; + else + return inside; +} + +IntersectionTest::Result IntersectionTest::frustum_obb(const FrustumPlanes &frustum, const OrientedBoundingBox &box) +{ + bool is_intersecting = false; + for (int i = 0; i < 6; i++) + { + Result result = plane_obb(frustum.planes[i], box); + if (result == outside) + return outside; + else if (result == intersecting) + is_intersecting = true; + } + if (is_intersecting) + return intersecting; + else + return inside; +} + +IntersectionTest::OverlapResult IntersectionTest::ray_aabb(const Vec3f &ray_start, const Vec3f &ray_end, const AxisAlignedBoundingBox &aabb) +{ + Vec3f c = (ray_start + ray_end) * 0.5f; + Vec3f w = ray_end - c; + Vec3f h = aabb.extents(); + + c -= aabb.center(); + + Vec3f v(std::abs(w.x), std::abs(w.y), std::abs(w.z)); + + if (std::abs(c.x) > v.x + h.x || std::abs(c.y) > v.y + h.y || std::abs(c.z) > v.z + h.z) + return disjoint; + + if (std::abs(c.y * w.z - c.z * w.y) > h.y * v.z + h.z * v.y || + std::abs(c.x * w.z - c.z * w.x) > h.x * v.z + h.z * v.x || + std::abs(c.x * w.y - c.y * w.x) > h.x * v.y + h.y * v.x) + return disjoint; + + return overlap; +} + +///////////////////////////////////////////////////////////////////////////// + +FrustumPlanes::FrustumPlanes() +{ +} + +FrustumPlanes::FrustumPlanes(const Mat4f &world_to_projection) +{ + planes[0] = near_frustum_plane(world_to_projection); + planes[1] = far_frustum_plane(world_to_projection); + planes[2] = left_frustum_plane(world_to_projection); + planes[3] = right_frustum_plane(world_to_projection); + planes[4] = top_frustum_plane(world_to_projection); + planes[5] = bottom_frustum_plane(world_to_projection); +} + +Vec4f FrustumPlanes::left_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] + m.matrix[0 + 0 * 4], + m.matrix[3 + 1 * 4] + m.matrix[0 + 1 * 4], + m.matrix[3 + 2 * 4] + m.matrix[0 + 2 * 4], + m.matrix[3 + 3 * 4] + m.matrix[0 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::right_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] - m.matrix[0 + 0 * 4], + m.matrix[3 + 1 * 4] - m.matrix[0 + 1 * 4], + m.matrix[3 + 2 * 4] - m.matrix[0 + 2 * 4], + m.matrix[3 + 3 * 4] - m.matrix[0 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::top_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] - m.matrix[1 + 0 * 4], + m.matrix[3 + 1 * 4] - m.matrix[1 + 1 * 4], + m.matrix[3 + 2 * 4] - m.matrix[1 + 2 * 4], + m.matrix[3 + 3 * 4] - m.matrix[1 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::bottom_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] + m.matrix[1 + 0 * 4], + m.matrix[3 + 1 * 4] + m.matrix[1 + 1 * 4], + m.matrix[3 + 2 * 4] + m.matrix[1 + 2 * 4], + m.matrix[3 + 3 * 4] + m.matrix[1 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::near_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] + m.matrix[2 + 0 * 4], + m.matrix[3 + 1 * 4] + m.matrix[2 + 1 * 4], + m.matrix[3 + 2 * 4] + m.matrix[2 + 2 * 4], + m.matrix[3 + 3 * 4] + m.matrix[2 + 3 * 4]); + plane /= plane.length3(); + return plane; +} + +Vec4f FrustumPlanes::far_frustum_plane(const Mat4f &m) +{ + Vec4f plane( + m.matrix[3 + 0 * 4] - m.matrix[2 + 0 * 4], + m.matrix[3 + 1 * 4] - m.matrix[2 + 1 * 4], + m.matrix[3 + 2 * 4] - m.matrix[2 + 2 * 4], + m.matrix[3 + 3 * 4] - m.matrix[2 + 3 * 4]); + plane /= plane.length3(); + return plane; +} diff --git a/src/polyrenderer/math/poly_intersection.h b/src/polyrenderer/math/poly_intersection.h new file mode 100644 index 000000000..438146fce --- /dev/null +++ b/src/polyrenderer/math/poly_intersection.h @@ -0,0 +1,179 @@ +/* +** Various 3D intersection tests +** Copyright (c) 1997-2015 The UICore Team +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" +#include +#include + +class Vec3f; + +class Vec4f +{ +public: + Vec4f() = default; + Vec4f(const Vec4f &) = default; + Vec4f(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) { } + Vec4f(float v) : x(v), y(v), z(v), w(v) { } + Vec4f(const Vec3f &xyz, float w); + + static float dot(const Vec4f &a, const Vec4f &b) { return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } + static float dot3(const Vec4f &a, const Vec4f &b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + float length3() const { return std::sqrt(dot3(*this, *this)); } + float magnitude() const { return std::sqrt(dot(*this, *this)); } + + Vec4f &operator+=(const Vec4f &b) { *this = Vec4f(x + b.x, y + b.y, z + b.z, w + b.w); return *this; } + Vec4f &operator-=(const Vec4f &b) { *this = Vec4f(x - b.x, y - b.y, z - b.z, w - b.w); return *this; } + Vec4f &operator*=(const Vec4f &b) { *this = Vec4f(x * b.x, y * b.y, z * b.z, w * b.w); return *this; } + Vec4f &operator/=(const Vec4f &b) { *this = Vec4f(x / b.x, y / b.y, z / b.z, w / b.w); return *this; } + Vec4f &operator+=(float b) { *this = Vec4f(x + b, y + b, z + b, w + b); return *this; } + Vec4f &operator-=(float b) { *this = Vec4f(x - b, y - b, z - b, w - b); return *this; } + Vec4f &operator*=(float b) { *this = Vec4f(x * b, y * b, z * b, w * b); return *this; } + Vec4f &operator/=(float b) { *this = Vec4f(x / b, y / b, z / b, w / b); return *this; } + + float x, y, z, w; +}; + +inline bool operator==(const Vec4f &a, const Vec4f &b) { return a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w; } +inline bool operator!=(const Vec4f &a, const Vec4f &b) { return a.x != b.x || a.y != b.y || a.z != b.z || a.w == b.w; } + +class Vec3f +{ +public: + Vec3f() = default; + Vec3f(const Vec3f &) = default; + Vec3f(const Vec4f &v) : x(v.x), y(v.y), z(v.z) { } + Vec3f(float x, float y, float z) : x(x), y(y), z(z) { } + Vec3f(float v) : x(v), y(v), z(v) { } + + static float dot(const Vec3f &a, const Vec3f &b) { return a.x * b.x + a.y * b.y + a.z * b.z; } + float length() const { return std::sqrt(dot(*this, *this)); } + + Vec3f &operator+=(const Vec3f &b) { *this = Vec3f(x + b.x, y + b.y, z + b.z); return *this; } + Vec3f &operator-=(const Vec3f &b) { *this = Vec3f(x - b.x, y - b.y, z - b.z); return *this; } + Vec3f &operator*=(const Vec3f &b) { *this = Vec3f(x * b.x, y * b.y, z * b.z); return *this; } + Vec3f &operator/=(const Vec3f &b) { *this = Vec3f(x / b.x, y / b.y, z / b.z); return *this; } + Vec3f &operator+=(float b) { *this = Vec3f(x + b, y + b, z + b); return *this; } + Vec3f &operator-=(float b) { *this = Vec3f(x - b, y - b, z - b); return *this; } + Vec3f &operator*=(float b) { *this = Vec3f(x * b, y * b, z * b); return *this; } + Vec3f &operator/=(float b) { *this = Vec3f(x / b, y / b, z / b); return *this; } + + float x, y, z; +}; + +inline bool operator==(const Vec3f &a, const Vec3f &b) { return a.x == b.x && a.y == b.y && a.z == b.z; } +inline bool operator!=(const Vec3f &a, const Vec3f &b) { return a.x != b.x || a.y != b.y || a.z != b.z; } + +inline Vec3f operator+(const Vec3f &a, const Vec3f &b) { return Vec3f(a.x + b.x, a.y + b.y, a.z + b.z); } +inline Vec3f operator-(const Vec3f &a, const Vec3f &b) { return Vec3f(a.x - b.x, a.y - b.y, a.z - b.z); } +inline Vec3f operator*(const Vec3f &a, const Vec3f &b) { return Vec3f(a.x * b.x, a.y * b.y, a.z * b.z); } +inline Vec3f operator/(const Vec3f &a, const Vec3f &b) { return Vec3f(a.x / b.x, a.y / b.y, a.z / b.z); } + +inline Vec3f operator+(const Vec3f &a, float b) { return Vec3f(a.x + b, a.y + b, a.z + b); } +inline Vec3f operator-(const Vec3f &a, float b) { return Vec3f(a.x - b, a.y - b, a.z - b); } +inline Vec3f operator*(const Vec3f &a, float b) { return Vec3f(a.x * b, a.y * b, a.z * b); } +inline Vec3f operator/(const Vec3f &a, float b) { return Vec3f(a.x / b, a.y / b, a.z / b); } + +inline Vec3f operator+(float a, const Vec3f &b) { return Vec3f(a + b.x, a + b.y, a + b.z); } +inline Vec3f operator-(float a, const Vec3f &b) { return Vec3f(a - b.x, a - b.y, a - b.z); } +inline Vec3f operator*(float a, const Vec3f &b) { return Vec3f(a * b.x, a * b.y, a * b.z); } +inline Vec3f operator/(float a, const Vec3f &b) { return Vec3f(a / b.x, a / b.y, a / b.z); } + +inline Vec4f::Vec4f(const Vec3f &xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) { } + +typedef TriMatrix Mat4f; + +class AxisAlignedBoundingBox +{ +public: + AxisAlignedBoundingBox() : aabb_min(), aabb_max() {} + AxisAlignedBoundingBox(const Vec3f &aabb_min, const Vec3f &aabb_max) : aabb_min(aabb_min), aabb_max(aabb_max) { } + AxisAlignedBoundingBox(const AxisAlignedBoundingBox &aabb, const Vec3f &barycentric_min, const Vec3f &barycentric_max) + : aabb_min(mix(aabb.aabb_min, aabb.aabb_max, barycentric_min)), aabb_max(mix(aabb.aabb_min, aabb.aabb_max, barycentric_max)) { } + + Vec3f center() const { return (aabb_max + aabb_min) * 0.5f; } + Vec3f extents() const { return (aabb_max - aabb_min) * 0.5f; } + + Vec3f aabb_min; + Vec3f aabb_max; + +private: + template + inline A mix(A a, B b, C mix) + { + return a * (C(1) - mix) + b * mix; + } +}; + +class OrientedBoundingBox +{ +public: + Vec3f center; + Vec3f extents; + Vec3f axis_x; + Vec3f axis_y; + Vec3f axis_z; +}; + +class FrustumPlanes +{ +public: + FrustumPlanes(); + explicit FrustumPlanes(const Mat4f &world_to_projection); + + Vec4f planes[6]; + +private: + static Vec4f left_frustum_plane(const Mat4f &matrix); + static Vec4f right_frustum_plane(const Mat4f &matrix); + static Vec4f top_frustum_plane(const Mat4f &matrix); + static Vec4f bottom_frustum_plane(const Mat4f &matrix); + static Vec4f near_frustum_plane(const Mat4f &matrix); + static Vec4f far_frustum_plane(const Mat4f &matrix); +}; + +class IntersectionTest +{ +public: + enum Result + { + outside, + inside, + intersecting, + }; + + enum OverlapResult + { + disjoint, + overlap + }; + + static Result plane_aabb(const Vec4f &plane, const AxisAlignedBoundingBox &aabb); + static Result plane_obb(const Vec4f &plane, const OrientedBoundingBox &obb); + static OverlapResult sphere(const Vec3f ¢er1, float radius1, const Vec3f ¢er2, float radius2); + static OverlapResult sphere_aabb(const Vec3f ¢er, float radius, const AxisAlignedBoundingBox &aabb); + static OverlapResult aabb(const AxisAlignedBoundingBox &a, const AxisAlignedBoundingBox &b); + static Result frustum_aabb(const FrustumPlanes &frustum, const AxisAlignedBoundingBox &box); + static Result frustum_obb(const FrustumPlanes &frustum, const OrientedBoundingBox &box); + static OverlapResult ray_aabb(const Vec3f &ray_start, const Vec3f &ray_end, const AxisAlignedBoundingBox &box); +}; diff --git a/src/polyrenderer/math/tri_matrix.cpp b/src/polyrenderer/math/tri_matrix.cpp new file mode 100644 index 000000000..daa549587 --- /dev/null +++ b/src/polyrenderer/math/tri_matrix.cpp @@ -0,0 +1,189 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_utility.h" +#include "tri_matrix.h" +#include "polyrenderer/drawers/poly_triangle.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_viewport.h" + +TriMatrix TriMatrix::null() +{ + TriMatrix m; + memset(m.matrix, 0, sizeof(m.matrix)); + return m; +} + +TriMatrix TriMatrix::identity() +{ + TriMatrix m = null(); + m.matrix[0] = 1.0f; + m.matrix[5] = 1.0f; + m.matrix[10] = 1.0f; + m.matrix[15] = 1.0f; + return m; +} + +TriMatrix TriMatrix::translate(float x, float y, float z) +{ + TriMatrix m = identity(); + m.matrix[0 + 3 * 4] = x; + m.matrix[1 + 3 * 4] = y; + m.matrix[2 + 3 * 4] = z; + return m; +} + +TriMatrix TriMatrix::scale(float x, float y, float z) +{ + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = x; + m.matrix[1 + 1 * 4] = y; + m.matrix[2 + 2 * 4] = z; + m.matrix[3 + 3 * 4] = 1; + return m; +} + +TriMatrix TriMatrix::rotate(float angle, float x, float y, float z) +{ + float c = cosf(angle); + float s = sinf(angle); + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = (x*x*(1.0f - c) + c); + m.matrix[0 + 1 * 4] = (x*y*(1.0f - c) - z*s); + m.matrix[0 + 2 * 4] = (x*z*(1.0f - c) + y*s); + m.matrix[1 + 0 * 4] = (y*x*(1.0f - c) + z*s); + m.matrix[1 + 1 * 4] = (y*y*(1.0f - c) + c); + m.matrix[1 + 2 * 4] = (y*z*(1.0f - c) - x*s); + m.matrix[2 + 0 * 4] = (x*z*(1.0f - c) - y*s); + m.matrix[2 + 1 * 4] = (y*z*(1.0f - c) + x*s); + m.matrix[2 + 2 * 4] = (z*z*(1.0f - c) + c); + m.matrix[3 + 3 * 4] = 1.0f; + return m; +} + +TriMatrix TriMatrix::swapYZ() +{ + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = 1.0f; + m.matrix[1 + 2 * 4] = 1.0f; + m.matrix[2 + 1 * 4] = -1.0f; + m.matrix[3 + 3 * 4] = 1.0f; + return m; +} + +TriMatrix TriMatrix::perspective(float fovy, float aspect, float z_near, float z_far) +{ + float f = (float)(1.0 / tan(fovy * M_PI / 360.0)); + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = f / aspect; + m.matrix[1 + 1 * 4] = f; + m.matrix[2 + 2 * 4] = (z_far + z_near) / (z_near - z_far); + m.matrix[2 + 3 * 4] = (2.0f * z_far * z_near) / (z_near - z_far); + m.matrix[3 + 2 * 4] = -1.0f; + return m; +} + +TriMatrix TriMatrix::frustum(float left, float right, float bottom, float top, float near, float far) +{ + float a = (right + left) / (right - left); + float b = (top + bottom) / (top - bottom); + float c = -(far + near) / (far - near); + float d = -(2.0f * far) / (far - near); + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = 2.0f * near / (right - left); + m.matrix[1 + 1 * 4] = 2.0f * near / (top - bottom); + m.matrix[0 + 2 * 4] = a; + m.matrix[1 + 2 * 4] = b; + m.matrix[2 + 2 * 4] = c; + m.matrix[2 + 3 * 4] = d; + m.matrix[3 + 2 * 4] = -1; + return m; +} + +TriMatrix TriMatrix::worldToView() +{ + TriMatrix m = null(); + m.matrix[0 + 0 * 4] = (float)ViewSin; + m.matrix[0 + 1 * 4] = (float)-ViewCos; + m.matrix[1 + 2 * 4] = 1.0f; + m.matrix[2 + 0 * 4] = (float)-ViewCos; + m.matrix[2 + 1 * 4] = (float)-ViewSin; + m.matrix[3 + 3 * 4] = 1.0f; + return m * translate((float)-ViewPos.X, (float)-ViewPos.Y, (float)-ViewPos.Z); +} + +TriMatrix TriMatrix::viewToClip() +{ + auto viewport = swrenderer::RenderViewport::Instance(); + float near = 5.0f; + float far = 65536.0f; + float width = (float)(FocalTangent * near); + float top = (float)(viewport->CenterY / viewport->InvZtoScale * near); + float bottom = (float)(top - viewheight / viewport->InvZtoScale * near); + return frustum(-width, width, bottom, top, near, far); +} + +TriMatrix TriMatrix::operator*(const TriMatrix &mult) const +{ + TriMatrix result; + for (int x = 0; x < 4; x++) + { + for (int y = 0; y < 4; y++) + { + result.matrix[x + y * 4] = + matrix[0 * 4 + x] * mult.matrix[y * 4 + 0] + + matrix[1 * 4 + x] * mult.matrix[y * 4 + 1] + + matrix[2 * 4 + x] * mult.matrix[y * 4 + 2] + + matrix[3 * 4 + x] * mult.matrix[y * 4 + 3]; + } + } + return result; +} + +ShadedTriVertex TriMatrix::operator*(TriVertex v) const +{ + float vx = matrix[0 * 4 + 0] * v.x + matrix[1 * 4 + 0] * v.y + matrix[2 * 4 + 0] * v.z + matrix[3 * 4 + 0] * v.w; + float vy = matrix[0 * 4 + 1] * v.x + matrix[1 * 4 + 1] * v.y + matrix[2 * 4 + 1] * v.z + matrix[3 * 4 + 1] * v.w; + float vz = matrix[0 * 4 + 2] * v.x + matrix[1 * 4 + 2] * v.y + matrix[2 * 4 + 2] * v.z + matrix[3 * 4 + 2] * v.w; + float vw = matrix[0 * 4 + 3] * v.x + matrix[1 * 4 + 3] * v.y + matrix[2 * 4 + 3] * v.z + matrix[3 * 4 + 3] * v.w; + ShadedTriVertex sv; + sv.x = vx; + sv.y = vy; + sv.z = vz; + sv.w = vw; + for (int i = 0; i < TriVertex::NumVarying; i++) + sv.varying[i] = v.varying[i]; + return sv; +} diff --git a/src/polyrenderer/math/tri_matrix.h b/src/polyrenderer/math/tri_matrix.h new file mode 100644 index 000000000..c1eabcce8 --- /dev/null +++ b/src/polyrenderer/math/tri_matrix.h @@ -0,0 +1,46 @@ +/* +** Triangle drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +struct TriVertex; +struct ShadedTriVertex; + +struct TriMatrix +{ + static TriMatrix null(); + static TriMatrix identity(); + static TriMatrix translate(float x, float y, float z); + static TriMatrix scale(float x, float y, float z); + static TriMatrix rotate(float angle, float x, float y, float z); + static TriMatrix swapYZ(); + static TriMatrix perspective(float fovy, float aspect, float near, float far); + static TriMatrix frustum(float left, float right, float bottom, float top, float near, float far); + + static TriMatrix worldToView(); // Software renderer world to view space transform + static TriMatrix viewToClip(); // Software renderer shearing projection + + ShadedTriVertex operator*(TriVertex v) const; + TriMatrix operator*(const TriMatrix &m) const; + + float matrix[16]; +}; diff --git a/src/polyrenderer/poly_renderer.cpp b/src/polyrenderer/poly_renderer.cpp new file mode 100644 index 000000000..77b59ac4b --- /dev/null +++ b/src/polyrenderer/poly_renderer.cpp @@ -0,0 +1,225 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "st_stuff.h" +#include "r_data/r_translate.h" +#include "r_data/r_interpolate.h" +#include "poly_renderer.h" +#include "gl/data/gl_data.h" +#include "d_net.h" +#include "po_man.h" +#include "st_stuff.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_viewport.h" + +EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Int, screenblocks) +void InitGLRMapinfoData(); +extern bool r_showviewer; + +///////////////////////////////////////////////////////////////////////////// + +PolyRenderer *PolyRenderer::Instance() +{ + static PolyRenderer scene; + return &scene; +} + +PolyRenderer::PolyRenderer() : Thread(nullptr) +{ +} + +void PolyRenderer::RenderView(player_t *player) +{ + using namespace swrenderer; + + auto viewport = RenderViewport::Instance(); + + viewport->RenderTarget = screen; + + int width = SCREENWIDTH; + int height = SCREENHEIGHT; + int stHeight = gST_Y; + float trueratio; + ActiveRatio(width, height, &trueratio); + viewport->SetViewport(width, height, trueratio); + + RenderActorView(player->mo, false); + + // Apply special colormap if the target cannot do it + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->ShaderColormap() && viewport->RenderTarget->IsBgra() && !(r_shadercolormaps && screen->Accel2D)) + { + Thread.DrawQueue->Push(cameraLight->ShaderColormap(), screen); + } + + DrawerThreads::Execute({ Thread.DrawQueue }); +} + +void PolyRenderer::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines) +{ + auto viewport = swrenderer::RenderViewport::Instance(); + + const bool savedviewactive = viewactive; + + viewwidth = width; + viewport->RenderTarget = canvas; + R_SetWindow(12, width, height, height, true); + viewport->SetViewport(width, height, WidescreenRatio); + viewwindowx = x; + viewwindowy = y; + viewactive = true; + + canvas->Lock(true); + + RenderActorView(actor, dontmaplines); + DrawerThreads::Execute({ Thread.DrawQueue }); + + canvas->Unlock(); + + viewport->RenderTarget = screen; + R_ExecuteSetViewSize(); + float trueratio; + ActiveRatio(width, height, &trueratio); + viewport->SetViewport(width, height, WidescreenRatio); + viewactive = savedviewactive; +} + +void PolyRenderer::RenderActorView(AActor *actor, bool dontmaplines) +{ + NetUpdate(); + + DontMapLines = dontmaplines; + + P_FindParticleSubsectors(); + PO_LinkToSubsectors(); + R_SetupFrame(actor); + swrenderer::CameraLight::Instance()->SetCamera(actor); + swrenderer::RenderViewport::Instance()->SetupFreelook(); + + ActorRenderFlags savedflags = camera->renderflags; + // Never draw the player unless in chasecam mode + if (!r_showviewer) + camera->renderflags |= RF_INVISIBLE; + + ClearBuffers(); + SetSceneViewport(); + SetupPerspectiveMatrix(); + MainPortal.SetViewpoint(WorldToClip, Vec4f(0.0f, 0.0f, 0.0f, 1.0f), GetNextStencilValue()); + MainPortal.Render(0); + Skydome.Render(WorldToClip); + MainPortal.RenderTranslucent(0); + PlayerSprites.Render(); + + camera->renderflags = savedflags; + interpolator.RestoreInterpolations (); + + NetUpdate(); +} + +void PolyRenderer::RenderRemainingPlayerSprites() +{ + PlayerSprites.RenderRemainingSprites(); +} + +void PolyRenderer::ClearBuffers() +{ + PolyVertexBuffer::Clear(); + auto viewport = swrenderer::RenderViewport::Instance(); + PolyStencilBuffer::Instance()->Clear(viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight(), 0); + PolySubsectorGBuffer::Instance()->Resize(viewport->RenderTarget->GetPitch(), viewport->RenderTarget->GetHeight()); + NextStencilValue = 0; + SeenLinePortals.clear(); + SeenMirrors.clear(); +} + +void PolyRenderer::SetSceneViewport() +{ + using namespace swrenderer; + + auto viewport = RenderViewport::Instance(); + + if (viewport->RenderTarget == screen) // Rendering to screen + { + int height; + if (screenblocks >= 10) + height = SCREENHEIGHT; + else + height = (screenblocks*SCREENHEIGHT / 10) & ~7; + + int bottom = SCREENHEIGHT - (height + viewwindowy - ((height - viewheight) / 2)); + PolyTriangleDrawer::set_viewport(viewwindowx, SCREENHEIGHT - bottom - height, viewwidth, height, viewport->RenderTarget); + } + else // Rendering to camera texture + { + PolyTriangleDrawer::set_viewport(0, 0, viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight(), viewport->RenderTarget); + } +} + +void PolyRenderer::SetupPerspectiveMatrix() +{ + static bool bDidSetup = false; + + if (!bDidSetup) + { + InitGLRMapinfoData(); + bDidSetup = true; + } + + // Code provided courtesy of Graf Zahl. Now we just have to plug it into the viewmatrix code... + // We have to scale the pitch to account for the pixel stretching, because the playsim doesn't know about this and treats it as 1:1. + double radPitch = ViewPitch.Normalized180().Radians(); + double angx = cos(radPitch); + double angy = sin(radPitch) * glset.pixelstretch; + double alen = sqrt(angx*angx + angy*angy); + float adjustedPitch = (float)asin(angy / alen); + float adjustedViewAngle = (float)(ViewAngle - 90).Radians(); + + float ratio = WidescreenRatio; + float fovratio = (WidescreenRatio >= 1.3f) ? 1.333333f : ratio; + float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(FieldOfView.Radians() / 2) / fovratio)).Degrees); + + TriMatrix worldToView = + TriMatrix::rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) * + TriMatrix::rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) * + TriMatrix::scale(1.0f, glset.pixelstretch, 1.0f) * + TriMatrix::swapYZ() * + TriMatrix::translate((float)-ViewPos.X, (float)-ViewPos.Y, (float)-ViewPos.Z); + + WorldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; +} + +bool PolyRenderer::InsertSeenLinePortal(FLinePortal *portal) +{ + return SeenLinePortals.insert(portal).second; +} + +bool PolyRenderer::InsertSeenMirror(line_t *mirrorLine) +{ + return SeenMirrors.insert(mirrorLine).second; +} diff --git a/src/polyrenderer/poly_renderer.h b/src/polyrenderer/poly_renderer.h new file mode 100644 index 000000000..565eec004 --- /dev/null +++ b/src/polyrenderer/poly_renderer.h @@ -0,0 +1,75 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include +#include +#include +#include +#include "doomdata.h" +#include "r_utility.h" +#include "scene/poly_portal.h" +#include "scene/poly_playersprite.h" +#include "scene/poly_sky.h" +#include "swrenderer/r_renderthread.h" + +class AActor; +class DCanvas; +class DrawerCommandQueue; +typedef std::shared_ptr DrawerCommandQueuePtr; + +class PolyRenderer +{ +public: + PolyRenderer(); + + void RenderView(player_t *player); + void RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines); + void RenderRemainingPlayerSprites(); + + static PolyRenderer *Instance(); + + uint32_t GetNextStencilValue() { uint32_t value = NextStencilValue; NextStencilValue += 2; return value; } + + bool InsertSeenLinePortal(FLinePortal *portal); + bool InsertSeenMirror(line_t *mirrorLine); + + bool DontMapLines = false; + + swrenderer::RenderThread Thread; + +private: + void RenderActorView(AActor *actor, bool dontmaplines); + void ClearBuffers(); + void SetSceneViewport(); + void SetupPerspectiveMatrix(); + + TriMatrix WorldToClip; + RenderPolyScene MainPortal; + PolySkyDome Skydome; + RenderPolyPlayerSprites PlayerSprites; + uint32_t NextStencilValue = 0; + + std::set SeenLinePortals; + std::set SeenMirrors; +}; diff --git a/src/polyrenderer/scene/poly_cull.cpp b/src/polyrenderer/scene/poly_cull.cpp new file mode 100644 index 000000000..38f150222 --- /dev/null +++ b/src/polyrenderer/scene/poly_cull.cpp @@ -0,0 +1,311 @@ +/* +** Potential visible set (PVS) handling +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_cull.h" +#include "polyrenderer/poly_renderer.h" + +void PolyCull::CullScene(const TriMatrix &worldToClip, const Vec4f &portalClipPlane) +{ + PvsSectors.clear(); + frustumPlanes = FrustumPlanes(worldToClip); + PortalClipPlane = portalClipPlane; + + // Cull front to back + MaxCeilingHeight = 0.0; + MinFloorHeight = 0.0; + if (numnodes == 0) + CullSubsector(subsectors); + else + CullNode(nodes + numnodes - 1); // The head node is the last node output. +} + +void PolyCull::CullNode(void *node) +{ + while (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + + // Decide which side the view point is on. + int side = PointOnSide(ViewPos, bsp); + + // Recursively divide front space (toward the viewer). + CullNode(bsp->children[side]); + + // Possibly divide back space (away from the viewer). + side ^= 1; + + if (!CheckBBox(bsp->bbox[side])) + return; + + node = bsp->children[side]; + } + + subsector_t *sub = (subsector_t *)((BYTE *)node - 1); + CullSubsector(sub); +} + +void PolyCull::CullSubsector(subsector_t *sub) +{ + // Update sky heights for the scene + MaxCeilingHeight = MAX(MaxCeilingHeight, sub->sector->ceilingplane.Zat0()); + MinFloorHeight = MIN(MinFloorHeight, sub->sector->floorplane.Zat0()); + + // Mark that we need to render this + PvsSectors.push_back(sub); + + // Update culling info for further bsp clipping + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + if ((line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) && line->backsector == nullptr) + { + // Skip lines not facing viewer + DVector2 pt1 = line->v1->fPos() - ViewPos; + DVector2 pt2 = line->v2->fPos() - ViewPos; + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + continue; + + int sx1, sx2; + if (GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2) == LineSegmentRange::HasSegment) + { + MarkSegmentCulled(sx1, sx2); + } + } + } +} + +void PolyCull::ClearSolidSegments() +{ + SolidSegments.clear(); + SolidSegments.reserve(SolidCullScale + 2); + SolidSegments.push_back({ -0x7fff, -SolidCullScale }); + SolidSegments.push_back({ SolidCullScale , 0x7fff }); +} + +void PolyCull::InvertSegments() +{ + TempInvertSolidSegments.swap(SolidSegments); + ClearSolidSegments(); + int x = -0x7fff; + for (const auto &segment : TempInvertSolidSegments) + { + MarkSegmentCulled(x, segment.X1 - 1); + x = segment.X2 + 1; + } +} + +bool PolyCull::IsSegmentCulled(int x1, int x2) const +{ + x1 = clamp(x1, -0x7ffe, 0x7ffd); + x2 = clamp(x2, -0x7ffd, 0x7ffe); + + int next = 0; + while (SolidSegments[next].X2 <= x2) + next++; + return (x1 >= SolidSegments[next].X1 && x2 <= SolidSegments[next].X2); +} + +void PolyCull::MarkSegmentCulled(int x1, int x2) +{ + if (x1 >= x2) + return; + + x1 = clamp(x1, -0x7ffe, 0x7ffd); + x2 = clamp(x2, -0x7ffd, 0x7ffe); + + int cur = 0; + while (true) + { + if (SolidSegments[cur].X1 <= x1 && SolidSegments[cur].X2 >= x2) // Already fully marked + { + break; + } + else if (SolidSegments[cur].X2 >= x1 && SolidSegments[cur].X1 <= x2) // Merge segments + { + // Find last segment + int merge = cur; + while (merge + 1 != (int)SolidSegments.size() && SolidSegments[merge + 1].X1 <= x2) + merge++; + + // Apply new merged range + SolidSegments[cur].X1 = MIN(SolidSegments[cur].X1, x1); + SolidSegments[cur].X2 = MAX(SolidSegments[merge].X2, x2); + + // Remove additional segments we merged with + if (merge > cur) + SolidSegments.erase(SolidSegments.begin() + (cur + 1), SolidSegments.begin() + (merge + 1)); + + break; + } + else if (SolidSegments[cur].X1 > x1) // Insert new segment + { + SolidSegments.insert(SolidSegments.begin() + cur, { x1, x2 }); + break; + } + cur++; + } +} + +int PolyCull::PointOnSide(const DVector2 &pos, const node_t *node) +{ + return DMulScale32(FLOAT2FIXED(pos.Y) - node->y, node->dx, node->x - FLOAT2FIXED(pos.X), node->dy) > 0; +} + +bool PolyCull::CheckBBox(float *bspcoord) +{ + // Start using a quick frustum AABB test: + + AxisAlignedBoundingBox aabb(Vec3f(bspcoord[BOXLEFT], bspcoord[BOXBOTTOM], (float)ViewPos.Z - 1000.0f), Vec3f(bspcoord[BOXRIGHT], bspcoord[BOXTOP], (float)ViewPos.Z + 1000.0f)); + auto result = IntersectionTest::frustum_aabb(frustumPlanes, aabb); + if (result == IntersectionTest::outside) + return false; + + // Skip if its in front of the portal: + + if (IntersectionTest::plane_aabb(PortalClipPlane, aabb) == IntersectionTest::outside) + return false; + + // Occlusion test using solid segments: + + static const int lines[4][4] = + { + { BOXLEFT, BOXBOTTOM, BOXRIGHT, BOXBOTTOM }, + { BOXRIGHT, BOXBOTTOM, BOXRIGHT, BOXTOP }, + { BOXRIGHT, BOXTOP, BOXLEFT, BOXTOP }, + { BOXLEFT, BOXTOP, BOXLEFT, BOXBOTTOM } + }; + + bool foundline = false; + int minsx1, maxsx2; + for (int i = 0; i < 4; i++) + { + int j = i < 3 ? i + 1 : 0; + float x1 = bspcoord[lines[i][0]]; + float y1 = bspcoord[lines[i][1]]; + float x2 = bspcoord[lines[i][2]]; + float y2 = bspcoord[lines[i][3]]; + int sx1, sx2; + LineSegmentRange result = GetSegmentRangeForLine(x1, y1, x2, y2, sx1, sx2); + if (result == LineSegmentRange::HasSegment) + { + if (foundline) + { + minsx1 = MIN(minsx1, sx1); + maxsx2 = MAX(maxsx2, sx2); + } + else + { + minsx1 = sx1; + maxsx2 = sx2; + foundline = true; + } + } + else if (result == LineSegmentRange::AlwaysVisible) + { + return true; + } + } + if (!foundline) + return false; + + return !IsSegmentCulled(minsx1, maxsx2); +} + +LineSegmentRange PolyCull::GetSegmentRangeForLine(double x1, double y1, double x2, double y2, int &sx1, int &sx2) const +{ + double znear = 5.0; + double updownnear = -400.0; + double sidenear = 400.0; + + // Clip line to the portal clip plane + float distance1 = Vec4f::dot(PortalClipPlane, Vec4f((float)x1, (float)y1, 0.0f, 1.0f)); + float distance2 = Vec4f::dot(PortalClipPlane, Vec4f((float)x2, (float)y2, 0.0f, 1.0f)); + if (distance1 < 0.0f && distance2 < 0.0f) + { + return LineSegmentRange::NotVisible; + } + else if (distance1 < 0.0f || distance2 < 0.0f) + { + double t1 = 0.0f, t2 = 1.0f; + if (distance1 < 0.0f) + t1 = clamp(distance1 / (distance1 - distance2), 0.0f, 1.0f); + else + t2 = clamp(distance2 / (distance1 - distance2), 0.0f, 1.0f); + double nx1 = x1 * (1.0 - t1) + x2 * t1; + double ny1 = y1 * (1.0 - t1) + y2 * t1; + double nx2 = x1 * (1.0 - t2) + x2 * t2; + double ny2 = y1 * (1.0 - t2) + y2 * t2; + x1 = nx1; + x2 = nx2; + y1 = ny1; + y2 = ny2; + } + + // Transform to 2D view space: + x1 = x1 - ViewPos.X; + y1 = y1 - ViewPos.Y; + x2 = x2 - ViewPos.X; + y2 = y2 - ViewPos.Y; + double rx1 = x1 * ViewSin - y1 * ViewCos; + double rx2 = x2 * ViewSin - y2 * ViewCos; + double ry1 = x1 * ViewCos + y1 * ViewSin; + double ry2 = x2 * ViewCos + y2 * ViewSin; + + // Is it potentially visible when looking straight up or down? + if (!(ry1 < updownnear && ry2 < updownnear) && !(ry1 > znear && ry2 > znear) && + !(rx1 < -sidenear && rx2 < -sidenear) && !(rx1 > sidenear && rx2 > sidenear)) + return LineSegmentRange::AlwaysVisible; + + // Cull if line is entirely behind view + if (ry1 < znear && ry2 < znear) + return LineSegmentRange::NotVisible; + + // Clip line, if needed + double t1 = 0.0f, t2 = 1.0f; + if (ry1 < znear) + t1 = clamp((znear - ry1) / (ry2 - ry1), 0.0, 1.0); + if (ry2 < znear) + t2 = clamp((znear - ry2) / (ry2 - ry1), 0.0, 1.0); + if (t1 != 0.0 || t2 != 1.0) + { + double nx1 = rx1 * (1.0 - t1) + rx2 * t1; + double ny1 = ry1 * (1.0 - t1) + ry2 * t1; + double nx2 = rx1 * (1.0 - t2) + rx2 * t2; + double ny2 = ry1 * (1.0 - t2) + ry2 * t2; + rx1 = nx1; + rx2 = nx2; + ry1 = ny1; + ry2 = ny2; + } + + sx1 = (int)floor(clamp(rx1 / ry1 * (SolidCullScale / 3), (double)-SolidCullScale, (double)SolidCullScale)); + sx2 = (int)floor(clamp(rx2 / ry2 * (SolidCullScale / 3), (double)-SolidCullScale, (double)SolidCullScale)); + + if (sx1 > sx2) + std::swap(sx1, sx2); + return (sx1 != sx2) ? LineSegmentRange::HasSegment : LineSegmentRange::AlwaysVisible; +} diff --git a/src/polyrenderer/scene/poly_cull.h b/src/polyrenderer/scene/poly_cull.h new file mode 100644 index 000000000..faa8a0740 --- /dev/null +++ b/src/polyrenderer/scene/poly_cull.h @@ -0,0 +1,71 @@ +/* +** Potential visible set (PVS) handling +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" +#include "polyrenderer/math/poly_intersection.h" + +enum class LineSegmentRange +{ + NotVisible, + HasSegment, + AlwaysVisible +}; + +class PolyCull +{ +public: + void ClearSolidSegments(); + void CullScene(const TriMatrix &worldToClip, const Vec4f &portalClipPlane); + + LineSegmentRange GetSegmentRangeForLine(double x1, double y1, double x2, double y2, int &sx1, int &sx2) const; + void MarkSegmentCulled(int x1, int x2); + bool IsSegmentCulled(int x1, int x2) const; + void InvertSegments(); + + std::vector PvsSectors; + double MaxCeilingHeight = 0.0; + double MinFloorHeight = 0.0; + +private: + struct SolidSegment + { + SolidSegment(int x1, int x2) : X1(x1), X2(x2) { } + int X1, X2; + }; + + void CullNode(void *node); + void CullSubsector(subsector_t *sub); + int PointOnSide(const DVector2 &pos, const node_t *node); + + // Checks BSP node/subtree bounding box. + // Returns true if some part of the bbox might be visible. + bool CheckBBox(float *bspcoord); + + std::vector SolidSegments; + std::vector TempInvertSolidSegments; + const int SolidCullScale = 3000; + + FrustumPlanes frustumPlanes; + Vec4f PortalClipPlane; +}; diff --git a/src/polyrenderer/scene/poly_decal.cpp b/src/polyrenderer/scene/poly_decal.cpp new file mode 100644 index 000000000..0812f3a79 --- /dev/null +++ b/src/polyrenderer/scene/poly_decal.cpp @@ -0,0 +1,180 @@ +/* +** Handling drawing a decal +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_decal.h" +#include "polyrenderer/poly_renderer.h" +#include "a_sharedglobal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" + +void RenderPolyDecal::RenderWallDecals(const TriMatrix &worldToClip, const Vec4f &clipPlane, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue) +{ + if (line->linedef == nullptr && line->sidedef == nullptr) + return; + + for (DBaseDecal *decal = line->sidedef->AttachedDecals; decal != nullptr; decal = decal->WallNext) + { + RenderPolyDecal render; + render.Render(worldToClip, clipPlane, decal, line, subsectorDepth, stencilValue); + } +} + +void RenderPolyDecal::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue) +{ + if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid()) + return; + + FTexture *tex = TexMan(decal->PicNum, true); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return; + + double edge_right = tex->GetWidth(); + double edge_left = tex->LeftOffset; + edge_right = (edge_right - edge_left) * decal->ScaleX; + edge_left *= decal->ScaleX; + + double dcx, dcy; + decal->GetXY(line->sidedef, dcx, dcy); + DVector2 decal_pos = { dcx, dcy }; + + DVector2 angvec = (line->v2->fPos() - line->v1->fPos()).Unit(); + DVector2 decal_left = decal_pos - edge_left * angvec; + DVector2 decal_right = decal_pos + edge_right * angvec; + + // Determine actor z + double zpos = decal->Z; + sector_t *front = line->frontsector; + sector_t *back = (line->backsector != nullptr) ? line->backsector : line->frontsector; + switch (decal->RenderFlags & RF_RELMASK) + { + default: + zpos = decal->Z; + break; + case RF_RELUPPER: + if (line->linedef->flags & ML_DONTPEGTOP) + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + else + zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling); + break; + case RF_RELLOWER: + if (line->linedef->flags & ML_DONTPEGBOTTOM) + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + else + zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor); + break; + case RF_RELMID: + if (line->linedef->flags & ML_DONTPEGBOTTOM) + zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor); + else + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + + DVector2 spriteScale = { decal->ScaleX, decal->ScaleY }; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + bool flipTextureX = (decal->RenderFlags & RF_XFLIP) == RF_XFLIP; + + DVector2 points[2] = { decal_left, decal_right }; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + + std::pair offsets[4] = + { + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 0.0f }, + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(zpos + spriteHeight * offsets[i].second - spriteHeight * 0.5); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first * tex->Scale.X); + vertices[i].varying[1] = (float)((1.0f - offsets[i].second) * tex->Scale.Y); + if (flipTextureX) + vertices[i].varying[0] = 1.0f - vertices[i].varying[0]; + } + + bool fullbrightSprite = (decal->RenderFlags & RF_FULLBRIGHT) == RF_FULLBRIGHT; + + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + args.uniforms.flags = 0; + args.SetColormap(front->ColorMap); + args.SetTexture(tex, decal->Translation, true); + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + if (fullbrightSprite || cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + args.uniforms.light = 256; + args.uniforms.flags |= TriUniforms::fixed_light; + } + else + { + args.uniforms.light = (uint32_t)((front->lightlevel + actualextralight) / 255.0f * 256.0f); + } + args.uniforms.subsectorDepth = subsectorDepth; + + if (swrenderer::RenderViewport::Instance()->RenderTarget->IsBgra()) + { + args.uniforms.color = 0xff000000 | decal->AlphaColor; + } + else + { + args.uniforms.color = ((uint32_t)decal->AlphaColor) >> 24; + } + + args.uniforms.srcalpha = (uint32_t)(decal->Alpha * 256.0 + 0.5); + args.uniforms.destalpha = 256 - args.uniforms.srcalpha; + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue; + //mode = R_SetPatchStyle (decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + args.blendmode = TriBlendMode::Shaded; + args.subsectorTest = true; + args.writeStencil = false; + args.writeSubsector = false; + PolyTriangleDrawer::draw(args); +} diff --git a/src/polyrenderer/scene/poly_decal.h b/src/polyrenderer/scene/poly_decal.h new file mode 100644 index 000000000..48907780a --- /dev/null +++ b/src/polyrenderer/scene/poly_decal.h @@ -0,0 +1,36 @@ +/* +** Handling drawing a decal +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class Vec4f; + +class RenderPolyDecal +{ +public: + static void RenderWallDecals(const TriMatrix &worldToClip, const Vec4f &clipPlane, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue); + +private: + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, DBaseDecal *decal, const seg_t *line, uint32_t subsectorDepth, uint32_t stencilValue); +}; diff --git a/src/polyrenderer/scene/poly_particle.cpp b/src/polyrenderer/scene/poly_particle.cpp new file mode 100644 index 000000000..fd9ee5c88 --- /dev/null +++ b/src/polyrenderer/scene/poly_particle.cpp @@ -0,0 +1,118 @@ +/* +** Particle drawing +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_particle.h" +#include "polyrenderer/poly_renderer.h" +#include "swrenderer/scene/r_light.h" + +void RenderPolyParticle::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue) +{ + DVector3 pos = particle->Pos; + double psize = particle->size / 8.0; + double zpos = pos.Z; + + DVector2 points[2] = + { + { pos.X - ViewSin * psize, pos.Y + ViewCos * psize }, + { pos.X + ViewSin * psize, pos.Y - ViewCos * psize } + }; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + + std::pair offsets[4] = + { + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 0.0f }, + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(zpos + psize * (2.0 * offsets[i].second - 1.0)); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first); + vertices[i].varying[1] = (float)(1.0f - offsets[i].second); + } + + // int color = (particle->color >> 24) & 0xff; // pal index, I think + bool fullbrightSprite = particle->bright != 0; + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->ParticleGlobVis(foggy); + + if (fullbrightSprite || cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + args.uniforms.light = 256; + args.uniforms.flags = TriUniforms::fixed_light; + } + else + { + args.uniforms.light = (uint32_t)((sub->sector->lightlevel + actualextralight) / 255.0f * 256.0f); + args.uniforms.flags = 0; + } + args.uniforms.subsectorDepth = subsectorDepth; + + uint32_t alpha = (uint32_t)clamp(particle->alpha * 255.0f + 0.5f, 0.0f, 255.0f); + + if (swrenderer::RenderViewport::Instance()->RenderTarget->IsBgra()) + { + args.uniforms.color = (alpha << 24) | (particle->color & 0xffffff); + } + else + { + args.uniforms.color = ((uint32_t)particle->color) >> 24; + args.uniforms.srcalpha = alpha; + args.uniforms.destalpha = 255 - alpha; + } + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue; + args.SetColormap(sub->sector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + args.subsectorTest = true; + args.writeStencil = false; + args.writeSubsector = false; + args.blendmode = TriBlendMode::AlphaBlend; + PolyTriangleDrawer::draw(args); +} diff --git a/src/polyrenderer/scene/poly_particle.h b/src/polyrenderer/scene/poly_particle.h new file mode 100644 index 000000000..b3b25b996 --- /dev/null +++ b/src/polyrenderer/scene/poly_particle.h @@ -0,0 +1,34 @@ +/* +** Handling drawing a particle +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" +#include "p_effect.h" + +class Vec4f; + +class RenderPolyParticle +{ +public: + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, particle_t *particle, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue); +}; diff --git a/src/polyrenderer/scene/poly_plane.cpp b/src/polyrenderer/scene/poly_plane.cpp new file mode 100644 index 000000000..00a283b0a --- /dev/null +++ b/src/polyrenderer/scene/poly_plane.cpp @@ -0,0 +1,505 @@ +/* +** Handling drawing a plane (ceiling, floor) +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_plane.h" +#include "poly_portal.h" +#include "polyrenderer/poly_renderer.h" +#include "r_sky.h" +#include "swrenderer/scene/r_light.h" + +EXTERN_CVAR(Int, r_3dfloors) + +void RenderPolyPlane::RenderPlanes(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector> §orPortals) +{ + RenderPolyPlane plane; + + if (r_3dfloors) + { + auto frontsector = sub->sector; + auto &ffloors = frontsector->e->XFloor.ffloors; + + // 3D floor floors + for (int i = 0; i < (int)ffloors.Size(); i++) + { + F3DFloor *fakeFloor = ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!fakeFloor->model) continue; + if (fakeFloor->bottom.plane->isSlope()) continue; + //if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + // R_3D_AddHeight(fakeFloor->top.plane, frontsector); + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (fakeFloor->alpha == 0) continue; + if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue; + //fakeFloor->alpha + + double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight < ViewPos.Z && fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)) + { + plane.Render3DFloor(worldToClip, clipPlane, sub, subsectorDepth, stencilValue, false, fakeFloor); + } + } + + // 3D floor ceilings + for (int i = 0; i < (int)ffloors.Size(); i++) + { + F3DFloor *fakeFloor = ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!fakeFloor->model) continue; + if (fakeFloor->top.plane->isSlope()) continue; + //if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + // R_3D_AddHeight(fakeFloor->bottom.plane, frontsector); + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (fakeFloor->alpha == 0) continue; + if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE | FF_INVERTSECTOR)) == (FF_SWIMMABLE | FF_INVERTSECTOR)) continue; + //fakeFloor->alpha + + double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight > ViewPos.Z && fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)) + { + plane.Render3DFloor(worldToClip, clipPlane, sub, subsectorDepth, stencilValue, true, fakeFloor); + } + } + } + + plane.Render(worldToClip, clipPlane, cull, sub, subsectorDepth, stencilValue, true, skyCeilingHeight, sectorPortals); + plane.Render(worldToClip, clipPlane, cull, sub, subsectorDepth, stencilValue, false, skyFloorHeight, sectorPortals); +} + +void RenderPolyPlane::Render3DFloor(const TriMatrix &worldToClip, const Vec4f &clipPlane, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, F3DFloor *fakeFloor) +{ + FTextureID picnum = ceiling ? *fakeFloor->bottom.texture : *fakeFloor->top.texture; + FTexture *tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + return; + + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + int lightlevel = 255; + bool foggy = false; + if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size()) + { + lightlist_t *light = P_GetPlaneLight(sub->sector, &sub->sector->ceilingplane, false); + //basecolormap = light->extra_colormap; + lightlevel = *light->p_lightlevel; + } + + UVTransform xform(ceiling ? fakeFloor->top.model->planes[sector_t::ceiling].xform : fakeFloor->top.model->planes[sector_t::floor].xform, tex); + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->SlopePlaneGlobVis(foggy) * 48.0f; + args.uniforms.light = (uint32_t)(lightlevel / 255.0f * 256.0f); + if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + args.uniforms.light = 256; + args.uniforms.flags = 0; + args.uniforms.subsectorDepth = subsectorDepth; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(sub->numlines); + if (!vertices) + return; + + if (ceiling) + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[sub->numlines - 1 - i] = PlaneVertex(line->v1, fakeFloor->bottom.plane->ZatPoint(line->v1), xform); + } + } + else + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[i] = PlaneVertex(line->v1, fakeFloor->top.plane->ZatPoint(line->v1), xform); + } + } + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = sub->numlines; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue + 1; + args.SetTexture(tex); + args.SetColormap(sub->sector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); +} + +void RenderPolyPlane::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector> §orPortals) +{ + std::vector portalSegments; + FSectorPortal *portal = nullptr;// sub->sector->ValidatePortal(ceiling ? sector_t::ceiling : sector_t::floor); + bool foggy = false; + PolyDrawSectorPortal *polyportal = nullptr; + if (portal && (portal->mFlags & PORTSF_INSKYBOX) == PORTSF_INSKYBOX) // Do not recurse into portals we already recursed into + portal = nullptr; + if (portal) + { + for (auto &p : sectorPortals) + { + if (p->Portal == portal) // To do: what other criteria do we need to check for? + { + polyportal = p.get(); + break; + } + } + if (!polyportal) + { + sectorPortals.push_back(std::make_unique(portal, ceiling)); + polyportal = sectorPortals.back().get(); + } + + // Calculate portal clipping + + DVector2 v; + bool inside = true; + double vdist = 1.0e10; + + portalSegments.reserve(sub->numlines); + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + + DVector2 pt1 = line->v1->fPos() - ViewPos; + DVector2 pt2 = line->v2->fPos() - ViewPos; + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + inside = false; + + double dist = pt1.LengthSquared(); + if (dist < vdist) + { + v = line->v1->fPos(); + vdist = dist; + } + dist = pt2.LengthSquared(); + if (dist < vdist) + { + v = line->v2->fPos(); + vdist = dist; + } + + int sx1, sx2; + LineSegmentRange range = cull.GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2); + if (range == LineSegmentRange::HasSegment) + portalSegments.push_back({ sx1, sx2 }); + } + + if (inside) + { + polyportal->PortalPlane = Vec4f(0.0f, 0.0f, 0.0f, 1.0f); + } + else if(polyportal->PortalPlane == Vec4f(0.0f) || Vec4f::dot(polyportal->PortalPlane, Vec4f((float)v.X, (float)v.Y, 0.0f, 1.0f)) > 0.0f) + { + DVector2 planePos = v; + DVector2 planeNormal = v - ViewPos; + planeNormal.MakeUnit(); + double planeD = -(planeNormal | (planePos + planeNormal * 0.001)); + polyportal->PortalPlane = Vec4f((float)planeNormal.X, (float)planeNormal.Y, 0.0f, (float)planeD); + } + } + + sector_t *fakesector = sub->sector->heightsec; + if (fakesector && (fakesector == sub->sector || (fakesector->MoreFlags & SECF_IGNOREHEIGHTSEC) == SECF_IGNOREHEIGHTSEC)) + fakesector = nullptr; + + bool fakeflooronly = fakesector && (fakesector->MoreFlags & SECF_FAKEFLOORONLY) == SECF_FAKEFLOORONLY; + + FTextureID picnum; + bool ccw; + sector_t *frontsector; + if (fakesector) + { + // Floor and ceiling texture needs to be swapped sometimes? Why?? :( + + if (ViewPos.Z < fakesector->floorplane.Zat0()) // In water + { + if (ceiling) + { + picnum = fakesector->GetTexture(sector_t::ceiling); + ceiling = false; + frontsector = fakesector; + ccw = false; + } + else + { + picnum = fakesector->GetTexture(sector_t::floor); + frontsector = sub->sector; + ccw = true; + } + } + else if (ViewPos.Z >= fakesector->ceilingplane.Zat0() && !fakeflooronly) // In ceiling water + { + if (ceiling) + { + picnum = fakesector->GetTexture(sector_t::ceiling); + frontsector = sub->sector; + ccw = true; + } + else + { + picnum = fakesector->GetTexture(sector_t::floor); + frontsector = fakesector; + ccw = false; + ceiling = true; + } + } + else if (!ceiling) // Water surface + { + picnum = fakesector->GetTexture(sector_t::ceiling); + frontsector = fakesector; + ccw = true; + } + else if (!fakeflooronly) // Ceiling water surface + { + picnum = fakesector->GetTexture(sector_t::floor); + frontsector = fakesector; + ccw = true; + } + else // Upper ceiling + { + picnum = sub->sector->GetTexture(sector_t::ceiling); + ccw = true; + frontsector = sub->sector; + } + } + else + { + picnum = sub->sector->GetTexture(ceiling ? sector_t::ceiling : sector_t::floor); + ccw = true; + frontsector = sub->sector; + } + + FTexture *tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + return; + + bool isSky = picnum == skyflatnum; + + UVTransform transform(ceiling ? frontsector->planes[sector_t::ceiling].xform : frontsector->planes[sector_t::floor].xform, tex); + + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->SlopePlaneGlobVis(foggy) * 48.0f; + args.uniforms.light = (uint32_t)(frontsector->lightlevel / 255.0f * 256.0f); + if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + args.uniforms.light = 256; + args.uniforms.flags = 0; + args.uniforms.subsectorDepth = isSky ? RenderPolyScene::SkySubsectorDepth : subsectorDepth; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(sub->numlines); + if (!vertices) + return; + + if (ceiling) + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[sub->numlines - 1 - i] = PlaneVertex(line->v1, isSky ? skyHeight : frontsector->ceilingplane.ZatPoint(line->v1), transform); + } + } + else + { + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + vertices[i] = PlaneVertex(line->v1, isSky ? skyHeight : frontsector->floorplane.ZatPoint(line->v1), transform); + } + } + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = sub->numlines; + args.mode = TriangleDrawMode::Fan; + args.ccw = ccw; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue + 1; + args.SetColormap(frontsector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + + if (!isSky) + { + if (!portal) + { + args.SetTexture(tex); + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); + } + else + { + args.stencilwritevalue = polyportal->StencilValue; + args.writeColor = false; + args.writeSubsector = false; + PolyTriangleDrawer::draw(args); + polyportal->Shape.push_back({ args.vinput, args.vcount, args.ccw, subsectorDepth }); + polyportal->Segments.insert(polyportal->Segments.end(), portalSegments.begin(), portalSegments.end()); + } + } + else + { + if (portal) + { + args.stencilwritevalue = polyportal->StencilValue; + polyportal->Shape.push_back({ args.vinput, args.vcount, args.ccw, subsectorDepth }); + polyportal->Segments.insert(polyportal->Segments.end(), portalSegments.begin(), portalSegments.end()); + } + else + { + args.stencilwritevalue = 255; + } + + args.writeColor = false; + args.writeSubsector = false; + PolyTriangleDrawer::draw(args); + + for (uint32_t i = 0; i < sub->numlines; i++) + { + TriVertex *wallvert = PolyVertexBuffer::GetVertices(4); + if (!wallvert) + return; + + seg_t *line = &sub->firstline[i]; + + double skyBottomz1 = frontsector->ceilingplane.ZatPoint(line->v1); + double skyBottomz2 = frontsector->ceilingplane.ZatPoint(line->v2); + if (line->backsector) + { + sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector; + + double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1); + double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1); + double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2); + double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2); + + double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1); + double backfloorz1 = backsector->floorplane.ZatPoint(line->v1); + double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2); + double backfloorz2 = backsector->floorplane.ZatPoint(line->v2); + + double topceilz1 = frontceilz1; + double topceilz2 = frontceilz2; + double topfloorz1 = MIN(backceilz1, frontceilz1); + double topfloorz2 = MIN(backceilz2, frontceilz2); + double bottomceilz1 = MAX(frontfloorz1, backfloorz1); + double bottomceilz2 = MAX(frontfloorz2, backfloorz2); + double middleceilz1 = topfloorz1; + double middleceilz2 = topfloorz2; + double middlefloorz1 = MIN(bottomceilz1, middleceilz1); + double middlefloorz2 = MIN(bottomceilz2, middleceilz2); + + bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; + + bool closedSector = backceilz1 == backfloorz1 && backceilz2 == backfloorz2; + if (ceiling && bothSkyCeiling && closedSector) + { + skyBottomz1 = middlefloorz1; + skyBottomz2 = middlefloorz2; + } + else if (bothSkyCeiling) + { + continue; + } + } + + if (ceiling) + { + wallvert[0] = PlaneVertex(line->v1, skyHeight, transform); + wallvert[1] = PlaneVertex(line->v2, skyHeight, transform); + wallvert[2] = PlaneVertex(line->v2, skyBottomz2, transform); + wallvert[3] = PlaneVertex(line->v1, skyBottomz1, transform); + } + else + { + wallvert[0] = PlaneVertex(line->v1, frontsector->floorplane.ZatPoint(line->v1), transform); + wallvert[1] = PlaneVertex(line->v2, frontsector->floorplane.ZatPoint(line->v2), transform); + wallvert[2] = PlaneVertex(line->v2, skyHeight, transform); + wallvert[3] = PlaneVertex(line->v1, skyHeight, transform); + } + + args.vinput = wallvert; + args.vcount = 4; + PolyTriangleDrawer::draw(args); + + if (portal) + { + polyportal->Shape.push_back({ args.vinput, args.vcount, args.ccw, subsectorDepth }); + polyportal->Segments.insert(polyportal->Segments.end(), portalSegments.begin(), portalSegments.end()); + } + } + } +} + +TriVertex RenderPolyPlane::PlaneVertex(vertex_t *v1, double height, const UVTransform &transform) +{ + TriVertex v; + v.x = (float)v1->fPos().X; + v.y = (float)v1->fPos().Y; + v.z = (float)height; + v.w = 1.0f; + v.varying[0] = transform.GetU(v.x, v.y); + v.varying[1] = transform.GetV(v.x, v.y); + return v; +} + +RenderPolyPlane::UVTransform::UVTransform(const FTransform &transform, FTexture *tex) +{ + if (tex) + { + xscale = (float)(transform.xScale * tex->Scale.X / tex->GetWidth()); + yscale = (float)(transform.yScale * tex->Scale.Y / tex->GetHeight()); + + double planeang = (transform.Angle + transform.baseAngle).Radians(); + cosine = (float)cos(planeang); + sine = (float)sin(planeang); + + xOffs = (float)transform.xOffs; + yOffs = (float)transform.yOffs; + } + else + { + xscale = 1.0f / 64.0f; + yscale = 1.0f / 64.0f; + cosine = 1.0f; + sine = 0.0f; + xOffs = 0.0f; + yOffs = 0.0f; + } +} + +float RenderPolyPlane::UVTransform::GetU(float x, float y) const +{ + return (xOffs + x * cosine - y * sine) * xscale; +} + +float RenderPolyPlane::UVTransform::GetV(float x, float y) const +{ + return (yOffs - x * sine - y * cosine) * yscale; +} diff --git a/src/polyrenderer/scene/poly_plane.h b/src/polyrenderer/scene/poly_plane.h new file mode 100644 index 000000000..bf844dcc2 --- /dev/null +++ b/src/polyrenderer/scene/poly_plane.h @@ -0,0 +1,54 @@ +/* +** Handling drawing a plane (ceiling, floor) +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class PolyDrawSectorPortal; +class PolyCull; +class Vec4f; + +class RenderPolyPlane +{ +public: + static void RenderPlanes(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, double skyCeilingHeight, double skyFloorHeight, std::vector> §orPortals); + +private: + struct UVTransform + { + UVTransform(const FTransform &transform, FTexture *tex); + + float GetU(float x, float y) const; + float GetV(float x, float y) const; + + float xscale; + float yscale; + float cosine; + float sine; + float xOffs, yOffs; + }; + + void Render3DFloor(const TriMatrix &worldToClip, const Vec4f &clipPlane, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, F3DFloor *fakefloor); + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, bool ceiling, double skyHeight, std::vector> §orPortals); + TriVertex PlaneVertex(vertex_t *v1, double height, const UVTransform &transform); +}; diff --git a/src/polyrenderer/scene/poly_playersprite.cpp b/src/polyrenderer/scene/poly_playersprite.cpp new file mode 100644 index 000000000..6ec53f325 --- /dev/null +++ b/src/polyrenderer/scene/poly_playersprite.cpp @@ -0,0 +1,445 @@ +/* +** Handling drawing a player sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_playersprite.h" +#include "polyrenderer/poly_renderer.h" +#include "d_player.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" + +EXTERN_CVAR(Bool, r_drawplayersprites) +EXTERN_CVAR(Bool, r_deathcamera) +EXTERN_CVAR(Bool, st_scale) +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) +EXTERN_CVAR(Bool, r_shadercolormaps) + +void RenderPolyPlayerSprites::Render() +{ + // This code cannot be moved directly to RenderRemainingSprites because the engine + // draws the canvas textures between this call and the final call to RenderRemainingSprites.. + + if (!r_drawplayersprites || + !camera || + !camera->player || + (players[consoleplayer].cheats & CF_CHASECAM) || + (r_deathcamera && camera->health <= 0)) + return; + + float bobx, boby; + P_BobWeapon(camera->player, &bobx, &boby, r_TicFracF); + + // Interpolate the main weapon layer once so as to be able to add it to other layers. + double wx, wy; + DPSprite *weapon = camera->player->FindPSprite(PSP_WEAPON); + if (weapon) + { + if (weapon->firstTic) + { + wx = weapon->x; + wy = weapon->y; + } + else + { + wx = weapon->oldx + (weapon->x - weapon->oldx) * r_TicFracF; + wy = weapon->oldy + (weapon->y - weapon->oldy) * r_TicFracF; + } + } + else + { + wx = 0; + wy = 0; + } + + for (DPSprite *sprite = camera->player->psprites; sprite != nullptr; sprite = sprite->GetNext()) + { + // [RH] Don't draw the targeter's crosshair if the player already has a crosshair set. + // It's possible this psprite's caller is now null but the layer itself hasn't been destroyed + // because it didn't tick yet (if we typed 'take all' while in the console for example). + // In this case let's simply not draw it to avoid crashing. + if ((sprite->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && sprite->GetCaller() != nullptr) + { + RenderSprite(sprite, camera, bobx, boby, wx, wy, r_TicFracF); + } + } +} + +void RenderPolyPlayerSprites::RenderRemainingSprites() +{ + for (auto &sprite : ScreenSprites) + sprite.Render(); + ScreenSprites.clear(); +} + +void RenderPolyPlayerSprites::RenderSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac) +{ + // decide which patch to use + if ((unsigned)sprite->GetSprite() >= (unsigned)sprites.Size()) + { + DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite number %i\n", sprite->GetSprite()); + return; + } + + spritedef_t *def = &sprites[sprite->GetSprite()]; + if (sprite->GetFrame() >= def->numframes) + { + DPrintf(DMSG_ERROR, "RenderPlayerSprite: invalid sprite frame %i : %i\n", sprite->GetSprite(), sprite->GetFrame()); + return; + } + + spriteframe_t *frame = &SpriteFrames[def->spriteframes + sprite->GetFrame()]; + FTextureID picnum = frame->Texture[0]; + bool flip = (frame->Flip & 1) != 0; + + FTexture *tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + return; + + // Can't interpolate the first tic. + if (sprite->firstTic) + { + sprite->firstTic = false; + sprite->oldx = sprite->x; + sprite->oldy = sprite->y; + } + + double sx = sprite->oldx + (sprite->x - sprite->oldx) * ticfrac; + double sy = sprite->oldy + (sprite->y - sprite->oldy) * ticfrac; + + if (sprite->Flags & PSPF_ADDBOB) + { + sx += bobx; + sy += boby; + } + + if (sprite->Flags & PSPF_ADDWEAPON && sprite->GetID() != PSP_WEAPON) + { + sx += wx; + sy += wy; + } + + auto viewport = swrenderer::RenderViewport::Instance(); + + double pspritexscale = centerxwide / 160.0; + double pspriteyscale = pspritexscale * viewport->YaspectMul; + double pspritexiscale = 1 / pspritexscale; + + // calculate edges of the shape + double tx = sx - BaseXCenter; + + tx -= tex->GetScaledLeftOffset(); + int x1 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); + + // off the right side + if (x1 > viewwidth) + return; + + tx += tex->GetScaledWidth(); + int x2 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); + + // off the left side + if (x2 <= 0) + return; + + double texturemid = (BaseYCenter - sy) * tex->Scale.Y + tex->TopOffset; + + // Adjust PSprite for fullscreen views + if (camera->player && (viewport->RenderTarget != screen || viewheight == viewport->RenderTarget->GetHeight() || (viewport->RenderTarget->GetWidth() > (BaseXCenter * 2) && !st_scale))) + { + AWeapon *weapon = dyn_cast(sprite->GetCaller()); + if (weapon != nullptr && weapon->YAdjust != 0) + { + if (viewport->RenderTarget != screen || viewheight == viewport->RenderTarget->GetHeight()) + { + texturemid -= weapon->YAdjust; + } + else + { + texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; + } + } + } + + // Move the weapon down for 1280x1024. + if (sprite->GetID() < PSP_TARGETCENTER) + { + texturemid -= AspectPspriteOffset(WidescreenRatio); + } + + int clipped_x1 = MAX(x1, 0); + int clipped_x2 = MIN(x2, viewwidth); + double xscale = pspritexscale / tex->Scale.X; + double yscale = pspriteyscale / tex->Scale.Y; + uint32_t translation = 0; // [RH] Use default colors + + double xiscale, startfrac; + if (flip) + { + xiscale = -pspritexiscale * tex->Scale.X; + startfrac = 1; + } + else + { + xiscale = pspritexiscale * tex->Scale.X; + startfrac = 0; + } + + if (clipped_x1 > x1) + startfrac += xiscale * (clipped_x1 - x1); + + bool noaccel = false; + + FDynamicColormap *basecolormap = viewsector->ColorMap; + FDynamicColormap *colormap_to_use = basecolormap; + + int ColormapNum = 0; + FSWColormap *BaseColormap = basecolormap; + float Alpha = 0; + FRenderStyle RenderStyle; + RenderStyle = STYLE_Normal; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + int spriteshade = swrenderer::LightVisibility::LightLevelToShade(owner->Sector->lightlevel + actualextralight, foggy); + double minz = double((2048 * 4) / double(1 << 20)); + ColormapNum = GETPALOOKUP(swrenderer::LightVisibility::Instance()->SpriteGlobVis(foggy) / minz, spriteshade); + + if (sprite->GetID() < PSP_TARGETCENTER) + { + Alpha = float(owner->Alpha); + RenderStyle = owner->RenderStyle; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + INTBOOL invertcolormap = (RenderStyle.Flags & STYLEF_InvertOverlay); + + if (RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + FDynamicColormap *mybasecolormap = basecolormap; + + if (RenderStyle.Flags & STYLEF_FadeToBlack) + { + if (invertcolormap) + { // Fade to white + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255, 255, 255), mybasecolormap->Desaturate); + invertcolormap = false; + } + else + { // Fade to black + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0, 0, 0), mybasecolormap->Desaturate); + } + } + + /* + if (swrenderer::realfixedcolormap != nullptr && (!swrenderer::r_swtruecolor || (r_shadercolormaps && screen->Accel2D))) + { // fixed color + BaseColormap = swrenderer::realfixedcolormap; + ColormapNum = 0; + } + else + { + if (invertcolormap) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); + } + if (swrenderer::fixedlightlev >= 0) + { + BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : mybasecolormap; + ColormapNum = swrenderer::fixedlightlev >> COLORMAPSHIFT; + } + else if (!foggy && sprite->GetState()->GetFullbright()) + { // full bright + BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : mybasecolormap; // [RH] use basecolormap + ColormapNum = 0; + } + else + { // local light + BaseColormap = mybasecolormap; + ColormapNum = GETPALOOKUP(0, spriteshade); + } + } + */ + + if (camera->Inventory != nullptr) + { + visstyle_t visstyle; + visstyle.Alpha = Alpha; + visstyle.RenderStyle = STYLE_Count; + visstyle.Invert = false; + + camera->Inventory->AlterWeaponSprite(&visstyle); + + Alpha = visstyle.Alpha; + + if (visstyle.RenderStyle != STYLE_Count) + { + RenderStyle = visstyle.RenderStyle; + } + + if (visstyle.Invert) + { + BaseColormap = &SpecialColormaps[INVERSECOLORMAP]; + ColormapNum = 0; + if (BaseColormap->Maps < mybasecolormap->Maps || BaseColormap->Maps >= mybasecolormap->Maps + NUMCOLORMAPS * 256) + { + noaccel = true; + } + } + } + + // If we're drawing with a special colormap, but shaders for them are disabled, do + // not accelerate. + if (!r_shadercolormaps && (BaseColormap >= &SpecialColormaps[0] && + BaseColormap <= &SpecialColormaps.Last())) + { + noaccel = true; + } + // If drawing with a BOOM colormap, disable acceleration. + if (mybasecolormap == &NormalLight && NormalLight.Maps != realcolormaps.Maps) + { + noaccel = true; + } + // If the main colormap has fixed lights, and this sprite is being drawn with that + // colormap, disable acceleration so that the lights can remain fixed. + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + if (!noaccel && cameraLight->ShaderColormap() == nullptr && + NormalLightHasFixedLights && mybasecolormap == &NormalLight && + tex->UseBasePalette()) + { + noaccel = true; + } + // [SP] If emulating GZDoom fullbright, disable acceleration + if (r_fullbrightignoresectorcolor && cameraLight->FixedLightLevel() >= 0) + mybasecolormap = &FullNormalLight; + if (r_fullbrightignoresectorcolor && !foggy && sprite->GetState()->GetFullbright()) + mybasecolormap = &FullNormalLight; + colormap_to_use = mybasecolormap; + } + + // Check for hardware-assisted 2D. If it's available, and this sprite is not + // fuzzy, don't draw it until after the switch to 2D mode. + if (!noaccel && swrenderer::RenderViewport::Instance()->RenderTarget == screen && (DFrameBuffer *)screen->Accel2D) + { + FRenderStyle style = RenderStyle; + style.CheckFuzz(); + if (style.BlendOp != STYLEOP_Fuzz) + { + PolyScreenSprite screenSprite; + screenSprite.Pic = tex; + screenSprite.X1 = viewwindowx + x1; + screenSprite.Y1 = viewwindowy + viewheight / 2 - texturemid * yscale - 0.5; + screenSprite.Width = tex->GetWidth() * xscale; + screenSprite.Height = tex->GetHeight() * yscale; + screenSprite.Translation = TranslationToTable(translation); + //screenSprite.Translation = translation; + screenSprite.Flip = xiscale < 0; + screenSprite.Alpha = Alpha; + screenSprite.RenderStyle = RenderStyle; + screenSprite.BaseColormap = BaseColormap; + screenSprite.ColormapNum = ColormapNum; + screenSprite.Colormap = colormap_to_use; + ScreenSprites.push_back(screenSprite); + return; + } + } + + // To do: draw sprite same way as R_DrawVisSprite(vis) here + + // Draw the fuzzy weapon: + FRenderStyle style = RenderStyle; + style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Fuzz) + { + RenderStyle = LegacyRenderStyles[STYLE_Shadow]; + + PolyScreenSprite screenSprite; + screenSprite.Pic = tex; + screenSprite.X1 = viewwindowx + x1; + screenSprite.Y1 = viewwindowy + viewheight / 2 - texturemid * yscale - 0.5; + screenSprite.Width = tex->GetWidth() * xscale; + screenSprite.Height = tex->GetHeight() * yscale; + screenSprite.Translation = TranslationToTable(translation); + screenSprite.Flip = xiscale < 0; + screenSprite.Alpha = Alpha; + screenSprite.RenderStyle = RenderStyle; + screenSprite.BaseColormap = BaseColormap; + screenSprite.ColormapNum = ColormapNum; + screenSprite.Colormap = colormap_to_use; + ScreenSprites.push_back(screenSprite); + } +} + +void PolyScreenSprite::Render() +{ + FSpecialColormap *special = nullptr; + FColormapStyle colormapstyle; + PalEntry overlay = 0; + bool usecolormapstyle = false; + if (BaseColormap >= &SpecialColormaps[0] && + BaseColormap < &SpecialColormaps[SpecialColormaps.Size()]) + { + special = static_cast(BaseColormap); + } + else if (Colormap->Color == PalEntry(255, 255, 255) && + Colormap->Desaturate == 0) + { + overlay = Colormap->Fade; + overlay.a = BYTE(ColormapNum * 255 / NUMCOLORMAPS); + } + else + { + usecolormapstyle = true; + colormapstyle.Color = Colormap->Color; + colormapstyle.Fade = Colormap->Fade; + colormapstyle.Desaturate = Colormap->Desaturate; + colormapstyle.FadeLevel = ColormapNum / float(NUMCOLORMAPS); + } + + screen->DrawTexture(Pic, + X1, + Y1, + DTA_DestWidthF, Width, + DTA_DestHeightF, Height, + DTA_TranslationIndex, Translation, + DTA_FlipX, Flip, + DTA_TopOffset, 0, + DTA_LeftOffset, 0, + DTA_ClipLeft, viewwindowx, + DTA_ClipTop, viewwindowy, + DTA_ClipRight, viewwindowx + viewwidth, + DTA_ClipBottom, viewwindowy + viewheight, + DTA_Alpha, Alpha, + DTA_RenderStyle, RenderStyle, + DTA_FillColor, FillColor, + DTA_SpecialColormap, special, + DTA_ColorOverlay, overlay.d, + DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : nullptr, + TAG_DONE); +} diff --git a/src/polyrenderer/scene/poly_playersprite.h b/src/polyrenderer/scene/poly_playersprite.h new file mode 100644 index 000000000..97c6e6118 --- /dev/null +++ b/src/polyrenderer/scene/poly_playersprite.h @@ -0,0 +1,65 @@ +/* +** Handling drawing a player sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_defs.h" + +class PolyScreenSprite; +class DPSprite; +struct FSWColormap; + +class RenderPolyPlayerSprites +{ +public: + void Render(); + void RenderRemainingSprites(); + +private: + void RenderSprite(DPSprite *sprite, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac); + + const int BaseXCenter = 160; + const int BaseYCenter = 100; + + std::vector ScreenSprites; +}; + +// DScreen accelerated sprite to be rendered +class PolyScreenSprite +{ +public: + void Render(); + + FTexture *Pic = nullptr; + double X1 = 0.0; + double Y1 = 0.0; + double Width = 0.0; + double Height = 0.0; + FRemapTable *Translation = nullptr; + bool Flip = false; + float Alpha = 1; + FRenderStyle RenderStyle; + FSWColormap *BaseColormap = nullptr; + int ColormapNum = 0; + uint32_t FillColor = 0; + FDynamicColormap *Colormap = nullptr; +}; diff --git a/src/polyrenderer/scene/poly_portal.cpp b/src/polyrenderer/scene/poly_portal.cpp new file mode 100644 index 000000000..9ef0f3bdd --- /dev/null +++ b/src/polyrenderer/scene/poly_portal.cpp @@ -0,0 +1,298 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "p_maputl.h" +#include "sbar.h" +#include "g_levellocals.h" +#include "r_data/r_translate.h" +#include "poly_portal.h" +#include "polyrenderer/poly_renderer.h" +#include "swrenderer/scene/r_light.h" +#include "gl/data/gl_data.h" + +extern bool r_showviewer; + +///////////////////////////////////////////////////////////////////////////// + +PolyDrawSectorPortal::PolyDrawSectorPortal(FSectorPortal *portal, bool ceiling) : Portal(portal), Ceiling(ceiling) +{ + StencilValue = PolyRenderer::Instance()->GetNextStencilValue(); +} + +void PolyDrawSectorPortal::Render(int portalDepth) +{ + if (Portal->mType == PORTS_HORIZON || Portal->mType == PORTS_PLANE) + return; + + SaveGlobals(); + + // To do: get this information from PolyRenderer instead of duplicating the code.. + double radPitch = ViewPitch.Normalized180().Radians(); + double angx = cos(radPitch); + double angy = sin(radPitch) * glset.pixelstretch; + double alen = sqrt(angx*angx + angy*angy); + float adjustedPitch = (float)asin(angy / alen); + float adjustedViewAngle = (float)(ViewAngle - 90).Radians(); + float ratio = WidescreenRatio; + float fovratio = (WidescreenRatio >= 1.3f) ? 1.333333f : ratio; + float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(FieldOfView.Radians() / 2) / fovratio)).Degrees); + TriMatrix worldToView = + TriMatrix::rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) * + TriMatrix::rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) * + TriMatrix::scale(1.0f, glset.pixelstretch, 1.0f) * + TriMatrix::swapYZ() * + TriMatrix::translate((float)-ViewPos.X, (float)-ViewPos.Y, (float)-ViewPos.Z); + TriMatrix worldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; + + RenderPortal.SetViewpoint(worldToClip, PortalPlane, StencilValue); + RenderPortal.SetPortalSegments(Segments); + RenderPortal.Render(portalDepth); + + RestoreGlobals(); +} + +void PolyDrawSectorPortal::RenderTranslucent(int portalDepth) +{ + if (Portal->mType == PORTS_HORIZON || Portal->mType == PORTS_PLANE) + return; + + SaveGlobals(); + + RenderPortal.RenderTranslucent(portalDepth); + + RestoreGlobals(); +} + +void PolyDrawSectorPortal::SaveGlobals() +{ + savedextralight = extralight; + savedpos = ViewPos; + savedangle = ViewAngle; + savedvisibility = swrenderer::LightVisibility::Instance()->GetVisibility(); + savedcamera = camera; + savedsector = viewsector; + + if (Portal->mType == PORTS_SKYVIEWPOINT) + { + // Don't let gun flashes brighten the sky box + AActor *sky = Portal->mSkybox; + extralight = 0; + swrenderer::LightVisibility::Instance()->SetVisibility(sky->args[0] * 0.25f); + ViewPos = sky->InterpolatedPosition(r_TicFracF); + ViewAngle = savedangle + (sky->PrevAngles.Yaw + deltaangle(sky->PrevAngles.Yaw, sky->Angles.Yaw) * r_TicFracF); + } + else //if (Portal->mType == PORTS_STACKEDSECTORTHING || Portal->mType == PORTS_PORTAL || Portal->mType == PORTS_LINKEDPORTAL) + { + //extralight = pl->extralight; + //swrenderer::R_SetVisibility(pl->visibility); + ViewPos.X += Portal->mDisplacement.X; + ViewPos.Y += Portal->mDisplacement.Y; + } + + camera = nullptr; + viewsector = Portal->mDestination; + R_SetViewAngle(); + + Portal->mFlags |= PORTSF_INSKYBOX; + if (Portal->mPartner > 0) level.sectorPortals[Portal->mPartner].mFlags |= PORTSF_INSKYBOX; +} + +void PolyDrawSectorPortal::RestoreGlobals() +{ + Portal->mFlags &= ~PORTSF_INSKYBOX; + if (Portal->mPartner > 0) level.sectorPortals[Portal->mPartner].mFlags &= ~PORTSF_INSKYBOX; + + camera = savedcamera; + viewsector = savedsector; + ViewPos = savedpos; + swrenderer::LightVisibility::Instance()->SetVisibility(savedvisibility); + extralight = savedextralight; + ViewAngle = savedangle; + R_SetViewAngle(); +} + +///////////////////////////////////////////////////////////////////////////// + +PolyDrawLinePortal::PolyDrawLinePortal(FLinePortal *portal) : Portal(portal) +{ + StencilValue = PolyRenderer::Instance()->GetNextStencilValue(); +} + +PolyDrawLinePortal::PolyDrawLinePortal(line_t *mirror) : Mirror(mirror) +{ + StencilValue = PolyRenderer::Instance()->GetNextStencilValue(); +} + +void PolyDrawLinePortal::Render(int portalDepth) +{ + SaveGlobals(); + + // To do: get this information from PolyRenderer instead of duplicating the code.. + double radPitch = ViewPitch.Normalized180().Radians(); + double angx = cos(radPitch); + double angy = sin(radPitch) * glset.pixelstretch; + double alen = sqrt(angx*angx + angy*angy); + float adjustedPitch = (float)asin(angy / alen); + float adjustedViewAngle = (float)(ViewAngle - 90).Radians(); + float ratio = WidescreenRatio; + float fovratio = (WidescreenRatio >= 1.3f) ? 1.333333f : ratio; + float fovy = (float)(2 * DAngle::ToDegrees(atan(tan(FieldOfView.Radians() / 2) / fovratio)).Degrees); + TriMatrix worldToView = + TriMatrix::rotate(adjustedPitch, 1.0f, 0.0f, 0.0f) * + TriMatrix::rotate(adjustedViewAngle, 0.0f, -1.0f, 0.0f) * + TriMatrix::scale(1.0f, glset.pixelstretch, 1.0f) * + TriMatrix::swapYZ() * + TriMatrix::translate((float)-ViewPos.X, (float)-ViewPos.Y, (float)-ViewPos.Z); + if (Mirror) + worldToView = TriMatrix::scale(-1.0f, 1.0f, 1.0f) * worldToView; + TriMatrix worldToClip = TriMatrix::perspective(fovy, ratio, 5.0f, 65535.0f) * worldToView; + + // Calculate plane clipping + line_t *clipLine = Portal ? Portal->mDestination : Mirror; + DVector2 planePos = clipLine->v1->fPos(); + DVector2 planeNormal = (clipLine->v2->fPos() - clipLine->v1->fPos()).Rotated90CW(); + planeNormal.MakeUnit(); + double planeD = -(planeNormal | (planePos + planeNormal * 0.001)); + Vec4f portalPlane((float)planeNormal.X, (float)planeNormal.Y, 0.0f, (float)planeD); + + RenderPortal.SetViewpoint(worldToClip, portalPlane, StencilValue); + RenderPortal.SetPortalSegments(Segments); + RenderPortal.Render(portalDepth); + + RestoreGlobals(); +} + +void PolyDrawLinePortal::RenderTranslucent(int portalDepth) +{ + SaveGlobals(); + RenderPortal.RenderTranslucent(portalDepth); + RestoreGlobals(); +} + +void PolyDrawLinePortal::SaveGlobals() +{ + savedextralight = extralight; + savedpos = ViewPos; + savedangle = ViewAngle; + savedcamera = camera; + savedsector = viewsector; + savedinvisibility = camera ? (camera->renderflags & RF_INVISIBLE) == RF_INVISIBLE : false; + savedViewPath[0] = ViewPath[0]; + savedViewPath[1] = ViewPath[1]; + + if (Mirror) + { + DAngle startang = ViewAngle; + DVector3 startpos = ViewPos; + + vertex_t *v1 = Mirror->v1; + + // Reflect the current view behind the mirror. + if (Mirror->Delta().X == 0) + { // vertical mirror + ViewPos.X = v1->fX() - startpos.X + v1->fX(); + } + else if (Mirror->Delta().Y == 0) + { // horizontal mirror + ViewPos.Y = v1->fY() - startpos.Y + v1->fY(); + } + else + { // any mirror + vertex_t *v2 = Mirror->v2; + + double dx = v2->fX() - v1->fX(); + double dy = v2->fY() - v1->fY(); + double x1 = v1->fX(); + double y1 = v1->fY(); + double x = startpos.X; + double y = startpos.Y; + + // the above two cases catch len == 0 + double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); + + ViewPos.X = (x1 + r * dx) * 2 - x; + ViewPos.Y = (y1 + r * dy) * 2 - y; + } + ViewAngle = Mirror->Delta().Angle() * 2 - startang; + + if (camera) + camera->renderflags &= ~RF_INVISIBLE; + } + else + { + auto src = Portal->mOrigin; + auto dst = Portal->mDestination; + + P_TranslatePortalXY(src, ViewPos.X, ViewPos.Y); + P_TranslatePortalZ(src, ViewPos.Z); + P_TranslatePortalAngle(src, ViewAngle); + P_TranslatePortalXY(src, ViewPath[0].X, ViewPath[0].Y); + P_TranslatePortalXY(src, ViewPath[1].X, ViewPath[1].Y); + + if (!r_showviewer && camera && P_PointOnLineSidePrecise(ViewPath[0], dst) != P_PointOnLineSidePrecise(ViewPath[1], dst)) + { + double distp = (ViewPath[0] - ViewPath[1]).Length(); + if (distp > EQUAL_EPSILON) + { + double dist1 = (ViewPos - ViewPath[0]).Length(); + double dist2 = (ViewPos - ViewPath[1]).Length(); + + if (dist1 + dist2 < distp + 1) + { + camera->renderflags |= RF_INVISIBLE; + } + } + } + } + + //camera = nullptr; + //viewsector = R_PointInSubsector(ViewPos)->sector; + R_SetViewAngle(); + + if (Mirror) + PolyTriangleDrawer::toggle_mirror(); +} + +void PolyDrawLinePortal::RestoreGlobals() +{ + if (camera) + { + if (savedinvisibility) + camera->renderflags |= RF_INVISIBLE; + else + camera->renderflags &= ~RF_INVISIBLE; + } + camera = savedcamera; + viewsector = savedsector; + ViewPos = savedpos; + extralight = savedextralight; + ViewAngle = savedangle; + ViewPath[0] = savedViewPath[0]; + ViewPath[1] = savedViewPath[1]; + R_SetViewAngle(); + + if (Mirror) + PolyTriangleDrawer::toggle_mirror(); +} diff --git a/src/polyrenderer/scene/poly_portal.h b/src/polyrenderer/scene/poly_portal.h new file mode 100644 index 000000000..24a812a4f --- /dev/null +++ b/src/polyrenderer/scene/poly_portal.h @@ -0,0 +1,100 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "poly_scene.h" + +struct PolyPortalVertexRange +{ + PolyPortalVertexRange(const TriVertex *vertices, int count, bool ccw, uint32_t subsectorDepth) : Vertices(vertices), Count(count), Ccw(ccw), SubsectorDepth(subsectorDepth) { } + const TriVertex *Vertices; + int Count; + bool Ccw; + uint32_t SubsectorDepth; +}; + +class PolyPortalSegment +{ +public: + PolyPortalSegment(int x1, int x2) : X1(x1), X2(x2) { } + int X1, X2; +}; + +class PolyDrawSectorPortal +{ +public: + PolyDrawSectorPortal(FSectorPortal *portal, bool ceiling); + + void Render(int portalDepth); + void RenderTranslucent(int portalDepth); + + FSectorPortal *Portal = nullptr; + uint32_t StencilValue = 0; + std::vector Shape; + std::vector Segments; + Vec4f PortalPlane = Vec4f(0.0f); + +private: + void SaveGlobals(); + void RestoreGlobals(); + + bool Ceiling; + RenderPolyScene RenderPortal; + + int savedextralight; + DVector3 savedpos; + DAngle savedangle; + double savedvisibility; + AActor *savedcamera; + sector_t *savedsector; +}; + +class PolyDrawLinePortal +{ +public: + PolyDrawLinePortal(FLinePortal *portal); + PolyDrawLinePortal(line_t *mirror); + + void Render(int portalDepth); + void RenderTranslucent(int portalDepth); + + FLinePortal *Portal = nullptr; + line_t *Mirror = nullptr; + uint32_t StencilValue = 0; + std::vector Shape; + std::vector Segments; + +private: + void SaveGlobals(); + void RestoreGlobals(); + + RenderPolyScene RenderPortal; + + int savedextralight; + DVector3 savedpos; + DAngle savedangle; + AActor *savedcamera; + sector_t *savedsector; + bool savedinvisibility; + DVector3 savedViewPath[2]; +}; diff --git a/src/polyrenderer/scene/poly_scene.cpp b/src/polyrenderer/scene/poly_scene.cpp new file mode 100644 index 000000000..125537678 --- /dev/null +++ b/src/polyrenderer/scene/poly_scene.cpp @@ -0,0 +1,368 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "p_maputl.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "polyrenderer/scene/poly_scene.h" +#include "polyrenderer/poly_renderer.h" +#include "gl/data/gl_data.h" +#include "swrenderer/scene/r_light.h" + +CVAR(Bool, r_debug_cull, 0, 0) +EXTERN_CVAR(Int, r_portal_recursions) + +///////////////////////////////////////////////////////////////////////////// + +RenderPolyScene::RenderPolyScene() +{ +} + +RenderPolyScene::~RenderPolyScene() +{ +} + +void RenderPolyScene::SetViewpoint(const TriMatrix &worldToClip, const Vec4f &portalPlane, uint32_t stencilValue) +{ + WorldToClip = worldToClip; + StencilValue = stencilValue; + PortalPlane = portalPlane; +} + +void RenderPolyScene::SetPortalSegments(const std::vector &segments) +{ + Cull.ClearSolidSegments(); + for (const auto &segment : segments) + { + Cull.MarkSegmentCulled(segment.X1, segment.X2); + } + Cull.InvertSegments(); + PortalSegmentsAdded = true; +} + +void RenderPolyScene::Render(int portalDepth) +{ + ClearBuffers(); + if (!PortalSegmentsAdded) + Cull.ClearSolidSegments(); + Cull.CullScene(WorldToClip, PortalPlane); + Cull.ClearSolidSegments(); + RenderSectors(); + RenderPortals(portalDepth); +} + +void RenderPolyScene::ClearBuffers() +{ + SeenSectors.clear(); + SubsectorDepths.clear(); + TranslucentObjects.clear(); + SectorPortals.clear(); + LinePortals.clear(); + NextSubsectorDepth = 0; +} + +void RenderPolyScene::RenderSectors() +{ + if (r_debug_cull) + { + for (auto it = Cull.PvsSectors.rbegin(); it != Cull.PvsSectors.rend(); ++it) + RenderSubsector(*it); + } + else + { + for (auto it = Cull.PvsSectors.begin(); it != Cull.PvsSectors.end(); ++it) + RenderSubsector(*it); + } +} + +void RenderPolyScene::RenderSubsector(subsector_t *sub) +{ + sector_t *frontsector = sub->sector; + frontsector->MoreFlags |= SECF_DRAWN; + + uint32_t subsectorDepth = NextSubsectorDepth++; + + if (sub->sector->CenterFloor() != sub->sector->CenterCeiling()) + { + RenderPolyPlane::RenderPlanes(WorldToClip, PortalPlane, Cull, sub, subsectorDepth, StencilValue, Cull.MaxCeilingHeight, Cull.MinFloorHeight, SectorPortals); + } + + for (uint32_t i = 0; i < sub->numlines; i++) + { + seg_t *line = &sub->firstline[i]; + if (line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) + { + RenderLine(sub, line, frontsector, subsectorDepth); + } + } + + bool mainBSP = ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors); + if (mainBSP) + { + int subsectorIndex = (int)(sub - subsectors); + for (int i = ParticlesInSubsec[subsectorIndex]; i != NO_PARTICLE; i = Particles[i].snext) + { + particle_t *particle = Particles + i; + TranslucentObjects.push_back({ particle, sub, subsectorDepth }); + } + } + + SeenSectors.insert(sub->sector); + SubsectorDepths[sub] = subsectorDepth; +} + +void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right) +{ + if (numnodes == 0) + { + subsector_t *sub = subsectors; + auto it = SubsectorDepths.find(sub); + if (it != SubsectorDepths.end()) + TranslucentObjects.push_back({ thing, sub, it->second, sortDistance, 0.0f, 1.0f }); + } + else + { + RenderSprite(thing, sortDistance, left, right, 0.0, 1.0, nodes + numnodes - 1); // The head node is the last node output. + } +} + +void RenderPolyScene::RenderSprite(AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node) +{ + while (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + + DVector2 planePos(FIXED2DBL(bsp->x), FIXED2DBL(bsp->y)); + DVector2 planeNormal = DVector2(FIXED2DBL(-bsp->dy), FIXED2DBL(bsp->dx)); + double planeD = planeNormal | planePos; + + int sideLeft = (left | planeNormal) > planeD; + int sideRight = (right | planeNormal) > planeD; + + if (sideLeft != sideRight) + { + double dotLeft = planeNormal | left; + double dotRight = planeNormal | right; + double t = (planeD - dotLeft) / (dotRight - dotLeft); + + DVector2 mid = left * (1.0 - t) + right * t; + double tmid = t1 * (1.0 - t) + t2 * t; + + RenderSprite(thing, sortDistance, mid, right, tmid, t2, bsp->children[sideRight]); + right = mid; + t2 = tmid; + } + node = bsp->children[sideLeft]; + } + + subsector_t *sub = (subsector_t *)((BYTE *)node - 1); + + auto it = SubsectorDepths.find(sub); + if (it != SubsectorDepths.end()) + TranslucentObjects.push_back({ thing, sub, it->second, sortDistance, (float)t1, (float)t2 }); +} + +void RenderPolyScene::RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth) +{ + // Reject lines not facing viewer + DVector2 pt1 = line->v1->fPos() - ViewPos; + DVector2 pt2 = line->v2->fPos() - ViewPos; + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + return; + + // Cull wall if not visible + int sx1, sx2; + LineSegmentRange segmentRange = Cull.GetSegmentRangeForLine(line->v1->fX(), line->v1->fY(), line->v2->fX(), line->v2->fY(), sx1, sx2); + if (segmentRange == LineSegmentRange::NotVisible || (segmentRange == LineSegmentRange::HasSegment && Cull.IsSegmentCulled(sx1, sx2))) + return; + + // Tell automap we saw this + if (!PolyRenderer::Instance()->DontMapLines && line->linedef && segmentRange != LineSegmentRange::AlwaysVisible) + { + line->linedef->flags |= ML_MAPPED; + sub->flags |= SSECF_DRAWN; + } + + // Render 3D floor sides + if (line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) + { + for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) + { + F3DFloor *fakeFloor = line->backsector->e->XFloor.ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (!fakeFloor->model) continue; + RenderPolyWall::Render3DFloorLine(WorldToClip, PortalPlane, Cull, line, frontsector, subsectorDepth, StencilValue, fakeFloor, TranslucentObjects); + } + } + + // Render wall, and update culling info if its an occlusion blocker + if (RenderPolyWall::RenderLine(WorldToClip, PortalPlane, Cull, line, frontsector, subsectorDepth, StencilValue, TranslucentObjects, LinePortals)) + { + if (segmentRange == LineSegmentRange::HasSegment) + Cull.MarkSegmentCulled(sx1, sx2); + } +} + +void RenderPolyScene::RenderPortals(int portalDepth) +{ + bool foggy = false; + if (portalDepth < r_portal_recursions) + { + for (auto &portal : SectorPortals) + portal->Render(portalDepth + 1); + + for (auto &portal : LinePortals) + portal->Render(portalDepth + 1); + } + else // Fill with black + { + PolyDrawArgs args; + args.objectToClip = &WorldToClip; + args.mode = TriangleDrawMode::Fan; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + args.uniforms.color = 0; + args.uniforms.light = 256; + args.uniforms.flags = TriUniforms::fixed_light; + args.SetClipPlane(PortalPlane.x, PortalPlane.y, PortalPlane.z, PortalPlane.w); + + for (auto &portal : SectorPortals) + { + args.stenciltestvalue = portal->StencilValue; + args.stencilwritevalue = portal->StencilValue + 1; + for (const auto &verts : portal->Shape) + { + args.vinput = verts.Vertices; + args.vcount = verts.Count; + args.ccw = verts.Ccw; + args.uniforms.subsectorDepth = verts.SubsectorDepth; + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); + } + } + + for (auto &portal : LinePortals) + { + args.stenciltestvalue = portal->StencilValue; + args.stencilwritevalue = portal->StencilValue + 1; + for (const auto &verts : portal->Shape) + { + args.vinput = verts.Vertices; + args.vcount = verts.Count; + args.ccw = verts.Ccw; + args.uniforms.subsectorDepth = verts.SubsectorDepth; + PolyTriangleDrawer::draw(args); + } + } + } +} + +void RenderPolyScene::RenderTranslucent(int portalDepth) +{ + if (portalDepth < r_portal_recursions) + { + for (auto it = SectorPortals.rbegin(); it != SectorPortals.rend(); ++it) + { + auto &portal = *it; + portal->RenderTranslucent(portalDepth + 1); + + PolyDrawArgs args; + args.objectToClip = &WorldToClip; + args.mode = TriangleDrawMode::Fan; + args.stenciltestvalue = portal->StencilValue + 1; + args.stencilwritevalue = StencilValue + 1; + args.SetClipPlane(PortalPlane.x, PortalPlane.y, PortalPlane.z, PortalPlane.w); + for (const auto &verts : portal->Shape) + { + args.vinput = verts.Vertices; + args.vcount = verts.Count; + args.ccw = verts.Ccw; + args.uniforms.subsectorDepth = verts.SubsectorDepth; + args.writeColor = false; + PolyTriangleDrawer::draw(args); + } + } + + for (auto it = LinePortals.rbegin(); it != LinePortals.rend(); ++it) + { + auto &portal = *it; + portal->RenderTranslucent(portalDepth + 1); + + PolyDrawArgs args; + args.objectToClip = &WorldToClip; + args.mode = TriangleDrawMode::Fan; + args.stenciltestvalue = portal->StencilValue + 1; + args.stencilwritevalue = StencilValue + 1; + args.SetClipPlane(PortalPlane.x, PortalPlane.y, PortalPlane.z, PortalPlane.w); + for (const auto &verts : portal->Shape) + { + args.vinput = verts.Vertices; + args.vcount = verts.Count; + args.ccw = verts.Ccw; + args.uniforms.subsectorDepth = verts.SubsectorDepth; + args.writeColor = false; + PolyTriangleDrawer::draw(args); + } + } + } + + for (sector_t *sector : SeenSectors) + { + for (AActor *thing = sector->thinglist; thing != nullptr; thing = thing->snext) + { + DVector2 left, right; + if (!RenderPolySprite::GetLine(thing, left, right)) + continue; + double distanceSquared = (thing->Pos() - ViewPos).LengthSquared(); + RenderSprite(thing, distanceSquared, left, right); + } + } + + std::stable_sort(TranslucentObjects.begin(), TranslucentObjects.end()); + + for (auto it = TranslucentObjects.rbegin(); it != TranslucentObjects.rend(); ++it) + { + auto &obj = *it; + if (obj.particle) + { + RenderPolyParticle spr; + spr.Render(WorldToClip, PortalPlane, obj.particle, obj.sub, obj.subsectorDepth, StencilValue + 1); + } + else if (!obj.thing) + { + obj.wall.Render(WorldToClip, PortalPlane, Cull); + } + else if ((obj.thing->renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) + { + RenderPolyWallSprite wallspr; + wallspr.Render(WorldToClip, PortalPlane, obj.thing, obj.sub, obj.subsectorDepth, StencilValue + 1); + } + else + { + RenderPolySprite spr; + spr.Render(WorldToClip, PortalPlane, obj.thing, obj.sub, obj.subsectorDepth, StencilValue + 1, obj.SpriteLeft, obj.SpriteRight); + } + } +} diff --git a/src/polyrenderer/scene/poly_scene.h b/src/polyrenderer/scene/poly_scene.h new file mode 100644 index 000000000..ba6fd5eea --- /dev/null +++ b/src/polyrenderer/scene/poly_scene.h @@ -0,0 +1,105 @@ +/* +** Polygon Doom software renderer +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include +#include +#include +#include +#include "doomdata.h" +#include "r_utility.h" +#include "polyrenderer/drawers/poly_triangle.h" +#include "polyrenderer/math/poly_intersection.h" +#include "poly_wall.h" +#include "poly_sprite.h" +#include "poly_wallsprite.h" +#include "poly_playersprite.h" +#include "poly_particle.h" +#include "poly_plane.h" +#include "poly_cull.h" +#include +#include + +class PolyTranslucentObject +{ +public: + PolyTranslucentObject(particle_t *particle, subsector_t *sub, uint32_t subsectorDepth) : particle(particle), sub(sub), subsectorDepth(subsectorDepth) { } + PolyTranslucentObject(AActor *thing, subsector_t *sub, uint32_t subsectorDepth, double dist, float t1, float t2) : thing(thing), sub(sub), subsectorDepth(subsectorDepth), DistanceSquared(dist), SpriteLeft(t1), SpriteRight(t2) { } + PolyTranslucentObject(RenderPolyWall wall) : wall(wall), subsectorDepth(wall.SubsectorDepth), DistanceSquared(1.e6) { } + + bool operator<(const PolyTranslucentObject &other) const + { + return subsectorDepth != other.subsectorDepth ? subsectorDepth < other.subsectorDepth : DistanceSquared < other.DistanceSquared; + } + + particle_t *particle = nullptr; + AActor *thing = nullptr; + subsector_t *sub = nullptr; + + RenderPolyWall wall; + + uint32_t subsectorDepth = 0; + double DistanceSquared = 0.0; + + float SpriteLeft = 0.0f, SpriteRight = 1.0f; +}; + +class PolyDrawSectorPortal; +class PolyDrawLinePortal; +class PolyPortalSegment; + +// Renders everything from a specific viewpoint +class RenderPolyScene +{ +public: + RenderPolyScene(); + ~RenderPolyScene(); + void SetViewpoint(const TriMatrix &worldToClip, const Vec4f &portalPlane, uint32_t stencilValue); + void SetPortalSegments(const std::vector &segments); + void Render(int portalDepth); + void RenderTranslucent(int portalDepth); + + static const uint32_t SkySubsectorDepth = 0x7fffffff; + +private: + void ClearBuffers(); + void RenderPortals(int portalDepth); + void RenderSectors(); + void RenderSubsector(subsector_t *sub); + void RenderLine(subsector_t *sub, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth); + void RenderSprite(AActor *thing, double sortDistance, const DVector2 &left, const DVector2 &right); + void RenderSprite(AActor *thing, double sortDistance, DVector2 left, DVector2 right, double t1, double t2, void *node); + + TriMatrix WorldToClip; + Vec4f PortalPlane; + uint32_t StencilValue = 0; + PolyCull Cull; + uint32_t NextSubsectorDepth = 0; + std::set SeenSectors; + std::unordered_map SubsectorDepths; + std::vector TranslucentObjects; + + std::vector> SectorPortals; + std::vector> LinePortals; + bool PortalSegmentsAdded = false; +}; diff --git a/src/polyrenderer/scene/poly_sky.cpp b/src/polyrenderer/scene/poly_sky.cpp new file mode 100644 index 000000000..507319e26 --- /dev/null +++ b/src/polyrenderer/scene/poly_sky.cpp @@ -0,0 +1,187 @@ +/* +** Sky dome rendering +** Copyright(C) 2003-2016 Christoph Oelckers +** All rights reserved. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program. If not, see http:**www.gnu.org/licenses/ +** +** Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky. +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_sky.h" +#include "poly_portal.h" +#include "r_sky.h" // for skyflatnum +#include "g_levellocals.h" +#include "swrenderer/scene/r_light.h" + +PolySkyDome::PolySkyDome() +{ + CreateDome(); +} + +void PolySkyDome::Render(const TriMatrix &worldToClip) +{ + FTextureID sky1tex, sky2tex; + bool foggy = false; + if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) + sky1tex = sky2texture; + else + sky1tex = sky1texture; + sky2tex = sky2texture; + + FTexture *frontskytex = TexMan(sky1tex, true); + FTexture *backskytex = nullptr; + if (level.flags & LEVEL_DOUBLESKY) + backskytex = TexMan(sky2tex, true); + + TriMatrix objectToWorld = TriMatrix::translate((float)ViewPos.X, (float)ViewPos.Y, (float)ViewPos.Z); + objectToClip = worldToClip * objectToWorld; + + int rc = mRows + 1; + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + args.uniforms.light = 256; + args.uniforms.flags = 0; + args.uniforms.subsectorDepth = RenderPolyScene::SkySubsectorDepth; + args.objectToClip = &objectToClip; + args.stenciltestvalue = 255; + args.stencilwritevalue = 1; + args.SetColormap(&NormalLight); + args.SetClipPlane(0.0f, 0.0f, 0.0f, 0.0f); + + RenderCapColorRow(args, frontskytex, 0, false); + RenderCapColorRow(args, frontskytex, rc, true); + + args.SetTexture(frontskytex); + + uint32_t topcapcolor = frontskytex->GetSkyCapColor(false); + uint32_t bottomcapcolor = frontskytex->GetSkyCapColor(true); + + for (int i = 1; i <= mRows; i++) + { + RenderRow(args, i, topcapcolor); + RenderRow(args, rc + i, bottomcapcolor); + } +} + +void PolySkyDome::RenderRow(PolyDrawArgs &args, int row, uint32_t capcolor) +{ + args.vinput = &mVertices[mPrimStart[row]]; + args.vcount = mPrimStart[row + 1] - mPrimStart[row]; + args.mode = TriangleDrawMode::Strip; + args.ccw = false; + args.uniforms.color = capcolor; + args.blendmode = TriBlendMode::Skycap; + PolyTriangleDrawer::draw(args); +} + +void PolySkyDome::RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap) +{ + uint32_t solid = skytex->GetSkyCapColor(bottomCap); + if (!swrenderer::RenderViewport::Instance()->RenderTarget->IsBgra()) + solid = RGB32k.RGB[(RPART(solid) >> 3)][(GPART(solid) >> 3)][(BPART(solid) >> 3)]; + + args.vinput = &mVertices[mPrimStart[row]]; + args.vcount = mPrimStart[row + 1] - mPrimStart[row]; + args.mode = TriangleDrawMode::Fan; + args.ccw = bottomCap; + args.uniforms.color = solid; + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); +} + +void PolySkyDome::CreateDome() +{ + mColumns = 16;// 128; + mRows = 4; + CreateSkyHemisphere(false); + CreateSkyHemisphere(true); + mPrimStart.Push(mVertices.Size()); +} + +void PolySkyDome::CreateSkyHemisphere(bool zflip) +{ + int r, c; + + mPrimStart.Push(mVertices.Size()); + + for (c = 0; c < mColumns; c++) + { + SkyVertex(1, c, zflip); + } + + // The total number of triangles per hemisphere can be calculated + // as follows: rows * columns * 2 + 2 (for the top cap). + for (r = 0; r < mRows; r++) + { + mPrimStart.Push(mVertices.Size()); + for (c = 0; c <= mColumns; c++) + { + SkyVertex(r + zflip, c, zflip); + SkyVertex(r + 1 - zflip, c, zflip); + } + } +} + +TriVertex PolySkyDome::SetVertexXYZ(float xx, float yy, float zz, float uu, float vv) +{ + TriVertex v; + v.x = xx; + v.y = zz; + v.z = yy; + v.w = 1.0f; + v.varying[0] = uu; + v.varying[1] = vv; + return v; +} + +void PolySkyDome::SkyVertex(int r, int c, bool zflip) +{ + static const FAngle maxSideAngle = 60.f; + static const float scale = 10000.; + + FAngle topAngle = (c / (float)mColumns * 360.f); + FAngle sideAngle = maxSideAngle * (float)(mRows - r) / (float)mRows; + float height = sideAngle.Sin(); + float realRadius = scale * sideAngle.Cos(); + FVector2 pos = topAngle.ToVector(realRadius); + float z = (!zflip) ? scale * height : -scale * height; + + float u, v; + + // And the texture coordinates. + if (!zflip) // Flipped Y is for the lower hemisphere. + { + u = (-c / (float)mColumns); + v = (r / (float)mRows); + } + else + { + u = (-c / (float)mColumns); + v = 1.0f + ((mRows - r) / (float)mRows); + } + + if (r != 4) z += 300; + + // And finally the vertex. + TriVertex vert; + vert = SetVertexXYZ(-pos.X, z - 1.f, pos.Y, u * 4.0f, v * 1.2f - 0.5f); + mVertices.Push(vert); +} diff --git a/src/polyrenderer/scene/poly_sky.h b/src/polyrenderer/scene/poly_sky.h new file mode 100644 index 000000000..1a8cd8ef2 --- /dev/null +++ b/src/polyrenderer/scene/poly_sky.h @@ -0,0 +1,45 @@ +/* +** Sky dome rendering +** Copyright(C) 2003-2016 Christoph Oelckers +** All rights reserved. +** +** This program is free software: you can redistribute it and/or modify +** it under the terms of the GNU Lesser General Public License as published by +** the Free Software Foundation, either version 3 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU Lesser General Public License for more details. +** +** You should have received a copy of the GNU Lesser General Public License +** along with this program. If not, see http:**www.gnu.org/licenses/ +** +** Loosely based on the JDoom sky and the ZDoomGL 0.66.2 sky. +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class PolySkyDome +{ +public: + PolySkyDome(); + void Render(const TriMatrix &worldToClip); + +private: + TArray mVertices; + TArray mPrimStart; + int mRows, mColumns; + TriMatrix objectToClip; + + void SkyVertex(int r, int c, bool yflip); + void CreateSkyHemisphere(bool zflip); + void CreateDome(); + void RenderRow(PolyDrawArgs &args, int row, uint32_t capcolor); + void RenderCapColorRow(PolyDrawArgs &args, FTexture *skytex, int row, bool bottomCap); + + TriVertex SetVertexXYZ(float xx, float yy, float zz, float uu = 0, float vv = 0); +}; diff --git a/src/polyrenderer/scene/poly_sprite.cpp b/src/polyrenderer/scene/poly_sprite.cpp new file mode 100644 index 000000000..e2ca59b24 --- /dev/null +++ b/src/polyrenderer/scene/poly_sprite.cpp @@ -0,0 +1,439 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_sprite.h" +#include "polyrenderer/poly_renderer.h" +#include "polyrenderer/math/poly_intersection.h" +#include "swrenderer/scene/r_light.h" + +EXTERN_CVAR(Float, transsouls) +EXTERN_CVAR(Int, r_drawfuzz) + +bool RenderPolySprite::GetLine(AActor *thing, DVector2 &left, DVector2 &right) +{ + if (IsThingCulled(thing)) + return false; + + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + + bool flipTextureX = false; + FTexture *tex = GetSpriteTexture(thing, flipTextureX); + if (tex == nullptr) + return false; + + DVector2 spriteScale = thing->Scale; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + + if (flipTextureX) + pos.X -= (tex->GetWidth() - tex->LeftOffset) * thingxscalemul; + else + pos.X -= tex->LeftOffset * thingxscalemul; + + double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + pos.X += spriteHalfWidth; + + left = DVector2(pos.X - ViewSin * spriteHalfWidth, pos.Y + ViewCos * spriteHalfWidth); + right = DVector2(pos.X + ViewSin * spriteHalfWidth, pos.Y - ViewCos * spriteHalfWidth); + return true; +} + +void RenderPolySprite::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, float t1, float t2) +{ + DVector2 line[2]; + if (!GetLine(thing, line[0], line[1])) + return; + + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + pos.Z += thing->GetBobOffset(r_TicFracF); + + bool flipTextureX = false; + FTexture *tex = GetSpriteTexture(thing, flipTextureX); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return; + + DVector2 spriteScale = thing->Scale; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + + if (flipTextureX) + pos.X -= (tex->GetWidth() - tex->LeftOffset) * thingxscalemul; + else + pos.X -= tex->LeftOffset * thingxscalemul; + + //pos.Z -= tex->TopOffset * thingyscalemul; + pos.Z -= (tex->GetHeight() - tex->TopOffset) * thingyscalemul + thing->Floorclip; + + double spriteHalfWidth = thingxscalemul * tex->GetWidth() * 0.5; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + pos.X += spriteHalfWidth; + + //double depth = 1.0; + //visstyle_t visstyle = GetSpriteVisStyle(thing, depth); + // Rumor has it that AlterWeaponSprite needs to be called with visstyle passed in somewhere around here.. + //R_SetColorMapLight(visstyle.BaseColormap, 0, visstyle.ColormapNum << FRACBITS); + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + + std::pair offsets[4] = + { + { t1, 1.0f }, + { t2, 1.0f }, + { t2, 0.0f }, + { t1, 0.0f }, + }; + + DVector2 points[2] = + { + line[0] * (1.0 - t1) + line[1] * t1, + line[0] * (1.0 - t2) + line[1] * t2 + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(pos.Z + spriteHeight * offsets[i].second); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first * tex->Scale.X); + vertices[i].varying[1] = (float)((1.0f - offsets[i].second) * tex->Scale.Y); + if (flipTextureX) + vertices[i].varying[0] = 1.0f - vertices[i].varying[0]; + } + + bool fullbrightSprite = ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)); + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->SpriteGlobVis(foggy); + args.uniforms.flags = 0; + if (fullbrightSprite || cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + args.uniforms.light = 256; + args.uniforms.flags |= TriUniforms::fixed_light; + } + else + { + args.uniforms.light = (uint32_t)((thing->Sector->lightlevel + actualextralight) / 255.0f * 256.0f); + } + args.uniforms.subsectorDepth = subsectorDepth; + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue; + args.SetTexture(tex, thing->Translation); + args.SetColormap(sub->sector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + + TriBlendMode blendmode; + + if (thing->RenderStyle == LegacyRenderStyles[STYLE_Normal] || + (r_drawfuzz == 0 && thing->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])) + { + args.uniforms.destalpha = 0; + args.uniforms.srcalpha = 256; + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Add] && fullbrightSprite && thing->Alpha == 1.0 && args.translation == nullptr) + { + args.uniforms.destalpha = 256; + args.uniforms.srcalpha = 256; + blendmode = TriBlendMode::AddSrcColorOneMinusSrcColor; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Add]) + { + args.uniforms.destalpha = (uint32_t)(1.0 * 256); + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Subtract]) + { + args.uniforms.destalpha = (uint32_t)(1.0 * 256); + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + blendmode = args.translation ? TriBlendMode::TranslateRevSub : TriBlendMode::RevSub; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_SoulTrans]) + { + args.uniforms.destalpha = (uint32_t)(256 - transsouls * 256); + args.uniforms.srcalpha = (uint32_t)(transsouls * 256); + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy] || + (r_drawfuzz == 2 && thing->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])) + { // NYI - Fuzzy - for now, just a copy of "Shadow" + args.uniforms.destalpha = 160; + args.uniforms.srcalpha = 0; + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Shadow] || + (r_drawfuzz == 1 && thing->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy])) + { + args.uniforms.destalpha = 160; + args.uniforms.srcalpha = 0; + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_TranslucentStencil]) + { + args.uniforms.destalpha = (uint32_t)(256 - thing->Alpha * 256); + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + args.uniforms.color = 0xff000000 | thing->fillcolor; + blendmode = TriBlendMode::Stencil; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_AddStencil]) + { + args.uniforms.destalpha = 256; + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + args.uniforms.color = 0xff000000 | thing->fillcolor; + blendmode = TriBlendMode::Stencil; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_Shaded]) + { + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + args.uniforms.destalpha = 256 - args.uniforms.srcalpha; + args.uniforms.color = 0; + blendmode = TriBlendMode::Shaded; + } + else if (thing->RenderStyle == LegacyRenderStyles[STYLE_AddShaded]) + { + args.uniforms.destalpha = 256; + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + args.uniforms.color = 0; + blendmode = TriBlendMode::Shaded; + } + else + { + args.uniforms.destalpha = (uint32_t)(256 - thing->Alpha * 256); + args.uniforms.srcalpha = (uint32_t)(thing->Alpha * 256); + blendmode = args.translation ? TriBlendMode::TranslateAdd : TriBlendMode::Add; + } + + if (blendmode == TriBlendMode::Shaded) + { + args.SetTexture(tex, thing->Translation, true); + } + + if (!swrenderer::RenderViewport::Instance()->RenderTarget->IsBgra()) + { + uint32_t r = (args.uniforms.color >> 16) & 0xff; + uint32_t g = (args.uniforms.color >> 8) & 0xff; + uint32_t b = args.uniforms.color & 0xff; + args.uniforms.color = RGB32k.RGB[r >> 3][g >> 3][b >> 3]; + + if (blendmode == TriBlendMode::Sub) // Sub crashes in pal mode for some weird reason. + blendmode = TriBlendMode::Add; + } + + args.subsectorTest = true; + args.writeSubsector = false; + args.writeStencil = false; + args.blendmode = blendmode; + PolyTriangleDrawer::draw(args); +} + +bool RenderPolySprite::IsThingCulled(AActor *thing) +{ + FIntCVar *cvar = thing->GetClass()->distancecheck; + if (cvar != nullptr && *cvar >= 0) + { + double dist = (thing->Pos() - ViewPos).LengthSquared(); + double check = (double)**cvar; + if (dist >= check * check) + return true; + } + + // Don't waste time projecting sprites that are definitely not visible. + if (thing == nullptr || + (thing->renderflags & RF_INVISIBLE) || + !thing->RenderStyle.IsVisible(thing->Alpha) || + !thing->IsVisibleToPlayer()) + { + return true; + } + + return false; +} + +#if 0 +visstyle_t RenderPolySprite::GetSpriteVisStyle(AActor *thing, double z) +{ + visstyle_t visstyle; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + int spriteshade = LIGHT2SHADE(thing->Sector->lightlevel + actualextralight); + + FRenderStyle RenderStyle; + RenderStyle = thing->RenderStyle; + float Alpha = float(thing->Alpha); + int ColormapNum = 0; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + + if (RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + FDynamicColormap *mybasecolormap = thing->Sector->ColorMap; + + // Sprites that are added to the scene must fade to black. + if (RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); + } + + if (RenderStyle.Flags & STYLEF_FadeToBlack) + { + if (invertcolormap) + { // Fade to white + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255, 255, 255), mybasecolormap->Desaturate); + invertcolormap = false; + } + else + { // Fade to black + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0, 0, 0), mybasecolormap->Desaturate); + } + } + + // get light level + if (swrenderer::fixedcolormap != nullptr) + { // fixed map + BaseColormap = swrenderer::fixedcolormap; + ColormapNum = 0; + } + else + { + if (invertcolormap) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); + } + if (swrenderer::fixedlightlev >= 0) + { + BaseColormap = mybasecolormap; + ColormapNum = swrenderer::fixedlightlev >> COLORMAPSHIFT; + } + else if (!foggy && ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) + { // full bright + BaseColormap = mybasecolormap; + ColormapNum = 0; + } + else + { // diminished light + double minz = double((2048 * 4) / double(1 << 20)); + ColormapNum = GETPALOOKUP(swrenderer::r_SpriteVisibility / MAX(z, minz), spriteshade); + BaseColormap = mybasecolormap; + } + } + + return visstyle; +} +#endif + +FTexture *RenderPolySprite::GetSpriteTexture(AActor *thing, /*out*/ bool &flipX) +{ + flipX = false; + if (thing->picnum.isValid()) + { + FTexture *tex = TexMan(thing->picnum); + if (tex->UseType == FTexture::TEX_Null) + { + return nullptr; + } + + if (tex->Rotations != 0xFFFF) + { + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[tex->Rotations]; + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + pos.Z += thing->GetBobOffset(r_TicFracF); + DAngle ang = (pos - ViewPos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + flipX = (sprframe->Flip & (1 << rot)) != 0; + tex = TexMan[sprframe->Texture[rot]]; // Do not animate the rotation + } + return tex; + } + else + { + // decide which texture to use for the sprite + int spritenum = thing->sprite; + if (spritenum >= (signed)sprites.Size() || spritenum < 0) + return nullptr; + + spritedef_t *sprdef = &sprites[spritenum]; + if (thing->frame >= sprdef->numframes) + { + // If there are no frames at all for this sprite, don't draw it. + return nullptr; + } + else + { + //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + pos.Z += thing->GetBobOffset(r_TicFracF); + DAngle ang = (pos - ViewPos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + flipX = (sprframe->Flip & (1 << rot)) != 0; + return TexMan[sprframe->Texture[rot]]; // Do not animate the rotation + } + } +} diff --git a/src/polyrenderer/scene/poly_sprite.h b/src/polyrenderer/scene/poly_sprite.h new file mode 100644 index 000000000..a61c7f4b5 --- /dev/null +++ b/src/polyrenderer/scene/poly_sprite.h @@ -0,0 +1,40 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class Vec4f; + +class RenderPolySprite +{ +public: + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue, float t1, float t2); + + static bool GetLine(AActor *thing, DVector2 &left, DVector2 &right); + static bool IsThingCulled(AActor *thing); + static FTexture *GetSpriteTexture(AActor *thing, /*out*/ bool &flipX); + +private: + //visstyle_t GetSpriteVisStyle(AActor *thing, double z); +}; diff --git a/src/polyrenderer/scene/poly_wall.cpp b/src/polyrenderer/scene/poly_wall.cpp new file mode 100644 index 000000000..e64d1f6c6 --- /dev/null +++ b/src/polyrenderer/scene/poly_wall.cpp @@ -0,0 +1,482 @@ +/* +** Handling drawing a wall +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_wall.h" +#include "poly_decal.h" +#include "polyrenderer/poly_renderer.h" +#include "r_sky.h" +#include "swrenderer/scene/r_light.h" + +EXTERN_CVAR(Bool, r_drawmirrors) + +bool RenderPolyWall::RenderLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals) +{ + PolyDrawLinePortal *polyportal = nullptr; + if (line->backsector == nullptr && line->linedef && line->sidedef == line->linedef->sidedef[0] && (line->linedef->special == Line_Mirror && r_drawmirrors)) + { + if (PolyRenderer::Instance()->InsertSeenMirror(line->linedef)) + { + linePortals.push_back(std::make_unique(line->linedef)); + polyportal = linePortals.back().get(); + } + } + else if (line->linedef && line->linedef->isVisualPortal()) + { + FLinePortal *portal = line->linedef->getPortal(); + if (PolyRenderer::Instance()->InsertSeenLinePortal(portal)) + { + for (auto &p : linePortals) + { + if (p->Portal == portal) // To do: what other criterias do we need to check for? + { + polyportal = p.get(); + break; + } + } + if (!polyportal) + { + linePortals.push_back(std::make_unique(portal)); + polyportal = linePortals.back().get(); + } + } + } + + RenderPolyWall wall; + wall.LineSeg = line; + wall.Line = line->linedef; + wall.Side = line->sidedef; + wall.Colormap = frontsector->ColorMap; + wall.Masked = false; + wall.SubsectorDepth = subsectorDepth; + wall.StencilValue = stencilValue; + + double frontceilz1 = frontsector->ceilingplane.ZatPoint(line->v1); + double frontfloorz1 = frontsector->floorplane.ZatPoint(line->v1); + double frontceilz2 = frontsector->ceilingplane.ZatPoint(line->v2); + double frontfloorz2 = frontsector->floorplane.ZatPoint(line->v2); + + if (line->backsector == nullptr) + { + if (line->sidedef) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2); + wall.TopZ = frontceilz1; + wall.BottomZ = frontfloorz1; + wall.UnpeggedCeil = frontceilz1; + wall.Texpart = side_t::mid; + wall.Polyportal = polyportal; + wall.Render(worldToClip, clipPlane, cull); + return true; + } + } + else + { + sector_t *backsector = (line->backsector != line->frontsector) ? line->backsector : line->frontsector; + + double backceilz1 = backsector->ceilingplane.ZatPoint(line->v1); + double backfloorz1 = backsector->floorplane.ZatPoint(line->v1); + double backceilz2 = backsector->ceilingplane.ZatPoint(line->v2); + double backfloorz2 = backsector->floorplane.ZatPoint(line->v2); + + double topceilz1 = frontceilz1; + double topceilz2 = frontceilz2; + double topfloorz1 = MIN(backceilz1, frontceilz1); + double topfloorz2 = MIN(backceilz2, frontceilz2); + double bottomceilz1 = MAX(frontfloorz1, backfloorz1); + double bottomceilz2 = MAX(frontfloorz2, backfloorz2); + double bottomfloorz1 = frontfloorz1; + double bottomfloorz2 = frontfloorz2; + double middleceilz1 = topfloorz1; + double middleceilz2 = topfloorz2; + double middlefloorz1 = MIN(bottomceilz1, middleceilz1); + double middlefloorz2 = MIN(bottomceilz2, middleceilz2); + + bool bothSkyCeiling = frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum; + + if ((topceilz1 > topfloorz1 || topceilz2 > topfloorz2) && line->sidedef && !bothSkyCeiling) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), topceilz1, topfloorz1, topceilz2, topfloorz2); + wall.TopZ = topceilz1; + wall.BottomZ = topfloorz1; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::top; + wall.Render(worldToClip, clipPlane, cull); + } + + if ((bottomfloorz1 < bottomceilz1 || bottomfloorz2 < bottomceilz2) && line->sidedef) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), bottomceilz1, bottomfloorz1, bottomceilz2, bottomfloorz2); + wall.TopZ = bottomceilz1; + wall.BottomZ = bottomfloorz2; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::bottom; + wall.Render(worldToClip, clipPlane, cull); + } + + if (line->sidedef) + { + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), middleceilz1, middlefloorz1, middleceilz2, middlefloorz2); + wall.TopZ = middleceilz1; + wall.BottomZ = middlefloorz1; + wall.UnpeggedCeil = topceilz1; + wall.Texpart = side_t::mid; + wall.Masked = true; + + FTexture *midtex = TexMan(line->sidedef->GetTexture(side_t::mid), true); + if (midtex && midtex->UseType != FTexture::TEX_Null) + translucentWallsOutput.push_back({ wall }); + + if (polyportal) + { + wall.Polyportal = polyportal; + wall.Render(worldToClip, clipPlane, cull); + } + } + } + return polyportal != nullptr; +} + +void RenderPolyWall::Render3DFloorLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput) +{ + double frontceilz1 = fakeFloor->top.plane->ZatPoint(line->v1); + double frontfloorz1 = fakeFloor->bottom.plane->ZatPoint(line->v1); + double frontceilz2 = fakeFloor->top.plane->ZatPoint(line->v2); + double frontfloorz2 = fakeFloor->bottom.plane->ZatPoint(line->v2); + + RenderPolyWall wall; + wall.LineSeg = line; + wall.Line = fakeFloor->master; + wall.Side = fakeFloor->master->sidedef[0]; + wall.Colormap = frontsector->ColorMap; + wall.Masked = false; + wall.SubsectorDepth = subsectorDepth; + wall.StencilValue = stencilValue; + wall.SetCoords(line->v1->fPos(), line->v2->fPos(), frontceilz1, frontfloorz1, frontceilz2, frontfloorz2); + wall.TopZ = frontceilz1; + wall.BottomZ = frontfloorz1; + wall.UnpeggedCeil = frontceilz1; + wall.Texpart = side_t::mid; + wall.Render(worldToClip, clipPlane, cull); +} + +void RenderPolyWall::SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2) +{ + this->v1 = v1; + this->v2 = v2; + this->ceil1 = ceil1; + this->floor1 = floor1; + this->ceil2 = ceil2; + this->floor2 = floor2; +} + +void RenderPolyWall::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull) +{ + bool foggy = false; + FTexture *tex = GetTexture(); + if (!tex && !Polyportal) + return; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + vertices[0].x = (float)v1.X; + vertices[0].y = (float)v1.Y; + vertices[0].z = (float)ceil1; + vertices[0].w = 1.0f; + + vertices[1].x = (float)v2.X; + vertices[1].y = (float)v2.Y; + vertices[1].z = (float)ceil2; + vertices[1].w = 1.0f; + + vertices[2].x = (float)v2.X; + vertices[2].y = (float)v2.Y; + vertices[2].z = (float)floor2; + vertices[2].w = 1.0f; + + vertices[3].x = (float)v1.X; + vertices[3].y = (float)v1.Y; + vertices[3].z = (float)floor1; + vertices[3].w = 1.0f; + + if (tex) + { + PolyWallTextureCoords texcoords(tex, LineSeg, Line, Side, Texpart, TopZ, BottomZ, UnpeggedCeil); + vertices[0].varying[0] = (float)texcoords.u1; + vertices[0].varying[1] = (float)texcoords.v1; + vertices[1].varying[0] = (float)texcoords.u2; + vertices[1].varying[1] = (float)texcoords.v1; + vertices[2].varying[0] = (float)texcoords.u2; + vertices[2].varying[1] = (float)texcoords.v2; + vertices[3].varying[0] = (float)texcoords.u1; + vertices[3].varying[1] = (float)texcoords.v2; + } + + // Masked walls clamp to the 0-1 range (no texture repeat) + if (Masked) + { + ClampHeight(vertices[0], vertices[3]); + ClampHeight(vertices[1], vertices[2]); + } + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + args.uniforms.light = (uint32_t)(GetLightLevel() / 255.0f * 256.0f); + args.uniforms.flags = 0; + args.uniforms.subsectorDepth = SubsectorDepth; + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = StencilValue; + args.stencilwritevalue = StencilValue + 1; + if (tex) + args.SetTexture(tex); + args.SetColormap(Line->frontsector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + + //if (Side && Side->lighthead) + // args.uniforms.light = 255; // Make walls touched by a light fullbright! + + if (Polyportal) + { + args.stencilwritevalue = Polyportal->StencilValue; + args.writeColor = false; + args.writeSubsector = false; + PolyTriangleDrawer::draw(args); + Polyportal->Shape.push_back({ args.vinput, args.vcount, args.ccw, args.uniforms.subsectorDepth }); + + int sx1, sx2; + LineSegmentRange range = cull.GetSegmentRangeForLine(v1.X, v1.Y, v2.X, v2.Y, sx1, sx2); + if (range == LineSegmentRange::HasSegment) + Polyportal->Segments.push_back({ sx1, sx2 }); + } + else if (!Masked) + { + args.blendmode = TriBlendMode::Copy; + PolyTriangleDrawer::draw(args); + } + else + { + args.uniforms.destalpha = (Line->flags & ML_ADDTRANS) ? 256 : (uint32_t)(256 - Line->alpha * 256); + args.uniforms.srcalpha = (uint32_t)(Line->alpha * 256); + args.subsectorTest = true; + args.writeSubsector = false; + args.writeStencil = false; + if (args.uniforms.destalpha == 0 && args.uniforms.srcalpha == 256) + args.blendmode = TriBlendMode::AlphaBlend; + else + args.blendmode = TriBlendMode::Add; + PolyTriangleDrawer::draw(args); + } + + RenderPolyDecal::RenderWallDecals(worldToClip, clipPlane, LineSeg, SubsectorDepth, StencilValue); +} + +void RenderPolyWall::ClampHeight(TriVertex &v1, TriVertex &v2) +{ + float top = v1.z; + float bottom = v2.z; + float texv1 = v1.varying[1]; + float texv2 = v2.varying[1]; + float delta = (texv2 - texv1); + + float t1 = texv1 < 0.0f ? -texv1 / delta : 0.0f; + float t2 = texv2 > 1.0f ? (1.0f - texv1) / delta : 1.0f; + float inv_t1 = 1.0f - t1; + float inv_t2 = 1.0f - t2; + + v1.z = top * inv_t1 + bottom * t1; + v1.varying[1] = texv1 * inv_t1 + texv2 * t1; + + v2.z = top * inv_t2 + bottom * t2; + v2.varying[1] = texv1 * inv_t2 + texv2 * t2; +} + +FTexture *RenderPolyWall::GetTexture() +{ + FTexture *tex = TexMan(Side->GetTexture(Texpart), true); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + { + // Mapping error. Doom floodfills this with a plane. + // This code doesn't do that, but at least it uses the "right" texture.. + + if (Line && Line->backsector && Line->sidedef[0] == Side) + { + if (Texpart == side_t::top) + tex = TexMan(Line->backsector->GetTexture(sector_t::ceiling), true); + else if (Texpart == side_t::bottom) + tex = TexMan(Line->backsector->GetTexture(sector_t::floor), true); + } + if (Line && Line->backsector && Line->sidedef[1] == Side) + { + if (Texpart == side_t::top) + tex = TexMan(Line->frontsector->GetTexture(sector_t::ceiling), true); + else if (Texpart == side_t::bottom) + tex = TexMan(Line->frontsector->GetTexture(sector_t::floor), true); + } + + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return nullptr; + } + return tex; +} + +int RenderPolyWall::GetLightLevel() +{ + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + return 255; + } + else + { + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + return clamp(Side->GetLightLevel(foggy, LineSeg->frontsector->lightlevel) + actualextralight, 0, 255); + } +} + +///////////////////////////////////////////////////////////////////////////// + +PolyWallTextureCoords::PolyWallTextureCoords(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil) +{ + CalcU(tex, lineseg, line, side, texpart); + CalcV(tex, line, side, texpart, topz, bottomz, unpeggedceil); +} + +void PolyWallTextureCoords::CalcU(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart) +{ + double lineLength = side->TexelLength; + double lineStart = 0.0; + + bool entireSegment = ((lineseg->v1 == line->v1) && (lineseg->v2 == line->v2)) || ((lineseg->v2 == line->v1) && (lineseg->v1 == line->v2)); + if (!entireSegment) + { + lineLength = (lineseg->v2->fPos() - lineseg->v1->fPos()).Length(); + lineStart = (lineseg->v1->fPos() - line->v1->fPos()).Length(); + } + + int texWidth = tex->GetWidth(); + double uscale = side->GetTextureXScale(texpart) * tex->Scale.X; + u1 = lineStart + side->GetTextureXOffset(texpart); + u2 = u1 + lineLength; + u1 *= uscale; + u2 *= uscale; + u1 /= texWidth; + u2 /= texWidth; +} + +void PolyWallTextureCoords::CalcV(FTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil) +{ + double vscale = side->GetTextureYScale(texpart) * tex->Scale.Y; + + double yoffset = side->GetTextureYOffset(texpart); + if (tex->bWorldPanning) + yoffset *= vscale; + + switch (texpart) + { + default: + case side_t::mid: + CalcVMidPart(tex, line, side, topz, bottomz, vscale, yoffset); + break; + case side_t::top: + CalcVTopPart(tex, line, side, topz, bottomz, vscale, yoffset); + break; + case side_t::bottom: + CalcVBottomPart(tex, line, side, topz, bottomz, unpeggedceil, vscale, yoffset); + break; + } + + int texHeight = tex->GetHeight(); + v1 /= texHeight; + v2 /= texHeight; +} + +void PolyWallTextureCoords::CalcVTopPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset) +{ + bool pegged = (line->flags & ML_DONTPEGTOP) == 0; + if (pegged) // bottom to top + { + int texHeight = tex->GetHeight(); + v1 = -yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + v1 = texHeight - v1; + v2 = texHeight - v2; + std::swap(v1, v2); + } + else // top to bottom + { + v1 = yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } +} + +void PolyWallTextureCoords::CalcVMidPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset) +{ + bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0; + if (pegged) // top to bottom + { + v1 = yoffset * vscale; + v2 = (yoffset + (topz - bottomz)) * vscale; + } + else // bottom to top + { + int texHeight = tex->GetHeight(); + v1 = texHeight - (-yoffset + (topz - bottomz)) * vscale; + v2 = texHeight + yoffset * vscale; + } +} + +void PolyWallTextureCoords::CalcVBottomPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset) +{ + bool pegged = (line->flags & ML_DONTPEGBOTTOM) == 0; + if (pegged) // top to bottom + { + v1 = yoffset; + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } + else + { + v1 = yoffset + (unpeggedceil - topz); + v2 = v1 + (topz - bottomz); + v1 *= vscale; + v2 *= vscale; + } +} diff --git a/src/polyrenderer/scene/poly_wall.h b/src/polyrenderer/scene/poly_wall.h new file mode 100644 index 000000000..014110a40 --- /dev/null +++ b/src/polyrenderer/scene/poly_wall.h @@ -0,0 +1,82 @@ +/* +** Handling drawing a wall +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class PolyTranslucentObject; +class PolyDrawLinePortal; +class PolyCull; +class Vec4f; + +class RenderPolyWall +{ +public: + static bool RenderLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, std::vector &translucentWallsOutput, std::vector> &linePortals); + static void Render3DFloorLine(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull, seg_t *line, sector_t *frontsector, uint32_t subsectorDepth, uint32_t stencilValue, F3DFloor *fakeFloor, std::vector &translucentWallsOutput); + + void SetCoords(const DVector2 &v1, const DVector2 &v2, double ceil1, double floor1, double ceil2, double floor2); + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, PolyCull &cull); + + DVector2 v1; + DVector2 v2; + double ceil1 = 0.0; + double floor1 = 0.0; + double ceil2 = 0.0; + double floor2 = 0.0; + + const seg_t *LineSeg = nullptr; + const line_t *Line = nullptr; + const side_t *Side = nullptr; + side_t::ETexpart Texpart = side_t::mid; + double TopZ = 0.0; + double BottomZ = 0.0; + double UnpeggedCeil = 0.0; + FSWColormap *Colormap = nullptr; + bool Masked = false; + uint32_t SubsectorDepth = 0; + uint32_t StencilValue = 0; + PolyDrawLinePortal *Polyportal = nullptr; + +private: + void ClampHeight(TriVertex &v1, TriVertex &v2); + FTexture *GetTexture(); + int GetLightLevel(); +}; + +// Texture coordinates for a wall +class PolyWallTextureCoords +{ +public: + PolyWallTextureCoords(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); + + double u1, u2; + double v1, v2; + +private: + void CalcU(FTexture *tex, const seg_t *lineseg, const line_t *line, const side_t *side, side_t::ETexpart texpart); + void CalcV(FTexture *tex, const line_t *line, const side_t *side, side_t::ETexpart texpart, double topz, double bottomz, double unpeggedceil); + void CalcVTopPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset); + void CalcVMidPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double vscale, double yoffset); + void CalcVBottomPart(FTexture *tex, const line_t *line, const side_t *side, double topz, double bottomz, double unpeggedceil, double vscale, double yoffset); +}; diff --git a/src/polyrenderer/scene/poly_wallsprite.cpp b/src/polyrenderer/scene/poly_wallsprite.cpp new file mode 100644 index 000000000..a563f026d --- /dev/null +++ b/src/polyrenderer/scene/poly_wallsprite.cpp @@ -0,0 +1,132 @@ +/* +** Handling drawing a sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "poly_wallsprite.h" +#include "polyrenderer/poly_renderer.h" +#include "swrenderer/scene/r_light.h" + +void RenderPolyWallSprite::Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue) +{ + if (RenderPolySprite::IsThingCulled(thing)) + return; + + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + pos.Z += thing->GetBobOffset(r_TicFracF); + + bool flipTextureX = false; + FTexture *tex = RenderPolySprite::GetSpriteTexture(thing, flipTextureX); + if (tex == nullptr || tex->UseType == FTexture::TEX_Null) + return; + + DVector2 spriteScale = thing->Scale; + double thingxscalemul = spriteScale.X / tex->Scale.X; + double thingyscalemul = spriteScale.Y / tex->Scale.Y; + double spriteHeight = thingyscalemul * tex->GetHeight(); + + DAngle ang = thing->Angles.Yaw + 90; + double angcos = ang.Cos(); + double angsin = ang.Sin(); + + // Determine left and right edges of sprite. The sprite's angle is its normal, + // so the edges are 90 degrees each side of it. + double x2 = tex->GetScaledWidth() * spriteScale.X; + double x1 = tex->GetScaledLeftOffset() * spriteScale.X; + DVector2 left, right; + left.X = pos.X - x1 * angcos; + left.Y = pos.Y - x1 * angsin; + right.X = left.X + x2 * angcos; + right.Y = right.Y + x2 * angsin; + + //int scaled_to = tex->GetScaledTopOffset(); + //int scaled_bo = scaled_to - tex->GetScaledHeight(); + //gzt = pos.Z + scale.Y * scaled_to; + //gzb = pos.Z + scale.Y * scaled_bo; + + DVector2 points[2] = { left, right }; + + TriVertex *vertices = PolyVertexBuffer::GetVertices(4); + if (!vertices) + return; + + bool foggy = false; + int actualextralight = foggy ? 0 : extralight << 4; + + std::pair offsets[4] = + { + { 0.0f, 1.0f }, + { 1.0f, 1.0f }, + { 1.0f, 0.0f }, + { 0.0f, 0.0f }, + }; + + for (int i = 0; i < 4; i++) + { + auto &p = (i == 0 || i == 3) ? points[0] : points[1]; + + vertices[i].x = (float)p.X; + vertices[i].y = (float)p.Y; + vertices[i].z = (float)(pos.Z + spriteHeight * offsets[i].second); + vertices[i].w = 1.0f; + vertices[i].varying[0] = (float)(offsets[i].first * tex->Scale.X); + vertices[i].varying[1] = (float)((1.0f - offsets[i].second) * tex->Scale.Y); + if (flipTextureX) + vertices[i].varying[0] = 1.0f - vertices[i].varying[0]; + } + + bool fullbrightSprite = ((thing->renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)); + swrenderer::CameraLight *cameraLight = swrenderer::CameraLight::Instance(); + + PolyDrawArgs args; + args.uniforms.globvis = (float)swrenderer::LightVisibility::Instance()->WallGlobVis(foggy); + if (fullbrightSprite || cameraLight->FixedLightLevel() >= 0 || cameraLight->FixedColormap()) + { + args.uniforms.light = 256; + args.uniforms.flags = TriUniforms::fixed_light; + } + else + { + args.uniforms.light = (uint32_t)((thing->Sector->lightlevel + actualextralight) / 255.0f * 256.0f); + args.uniforms.flags = 0; + } + args.uniforms.subsectorDepth = subsectorDepth; + + args.objectToClip = &worldToClip; + args.vinput = vertices; + args.vcount = 4; + args.mode = TriangleDrawMode::Fan; + args.ccw = true; + args.stenciltestvalue = stencilValue; + args.stencilwritevalue = stencilValue; + args.SetTexture(tex); + args.SetColormap(sub->sector->ColorMap); + args.SetClipPlane(clipPlane.x, clipPlane.y, clipPlane.z, clipPlane.w); + args.subsectorTest = true; + args.writeSubsector = false; + args.writeStencil = false; + args.blendmode = TriBlendMode::AlphaBlend; + PolyTriangleDrawer::draw(args); +} diff --git a/src/polyrenderer/scene/poly_wallsprite.h b/src/polyrenderer/scene/poly_wallsprite.h new file mode 100644 index 000000000..75a550748 --- /dev/null +++ b/src/polyrenderer/scene/poly_wallsprite.h @@ -0,0 +1,33 @@ +/* +** Handling drawing a wall sprite +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "polyrenderer/drawers/poly_triangle.h" + +class Vec4f; + +class RenderPolyWallSprite +{ +public: + void Render(const TriMatrix &worldToClip, const Vec4f &clipPlane, AActor *thing, subsector_t *sub, uint32_t subsectorDepth, uint32_t stencilValue); +}; diff --git a/src/posix/cocoa/i_video.mm b/src/posix/cocoa/i_video.mm index 637ca0d7f..d7791c43c 100644 --- a/src/posix/cocoa/i_video.mm +++ b/src/posix/cocoa/i_video.mm @@ -48,7 +48,7 @@ #include "m_argv.h" #include "m_png.h" #include "r_renderer.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" #include "st_console.h" #include "stats.h" #include "textures.h" @@ -114,11 +114,23 @@ @end +DFrameBuffer *CreateGLSWFrameBuffer(int width, int height, bool bgra, bool fullscreen); EXTERN_CVAR(Bool, ticker ) EXTERN_CVAR(Bool, vid_vsync) EXTERN_CVAR(Bool, vid_hidpi) +CUSTOM_CVAR(Bool, swtruecolor, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + // Strictly speaking this doesn't require a mode switch, but it is the easiest + // way to force a CreateFramebuffer call without a lot of refactoring. + extern int NewWidth, NewHeight, NewBits, DisplayBits; + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + CUSTOM_CVAR(Bool, fullscreen, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) { extern int NewWidth, NewHeight, NewBits, DisplayBits; @@ -136,7 +148,7 @@ CUSTOM_CVAR(Bool, vid_autoswitch, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_ static int s_currentRenderer; -CUSTOM_CVAR(Int, vid_renderer, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +CUSTOM_CVAR(Int, vid_renderer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { // 0: Software renderer // 1: OpenGL renderer @@ -251,7 +263,7 @@ public: virtual EDisplayType GetDisplayType() { return DISPLAY_Both; } virtual void SetWindowedScale(float scale); - virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer* old); + virtual DFrameBuffer* CreateFrameBuffer(int width, int height, bool bgra, bool fs, DFrameBuffer* old); virtual void StartModeIterator(int bits, bool fullscreen); virtual bool NextMode(int* width, int* height, bool* letterbox); @@ -293,7 +305,7 @@ private: class CocoaFrameBuffer : public DFrameBuffer { public: - CocoaFrameBuffer(int width, int height, bool fullscreen); + CocoaFrameBuffer(int width, int height, bool bgra, bool fullscreen); ~CocoaFrameBuffer(); virtual bool Lock(bool buffer); @@ -496,7 +508,7 @@ NSOpenGLPixelFormat* CreatePixelFormat(const OpenGLProfile profile) attributes[i++] = NSOpenGLPFAAllowOfflineRenderers; } - if (NSAppKitVersionNumber >= AppKit10_7 && OpenGLProfile::Core == profile && 1 == vid_renderer) + if (NSAppKitVersionNumber >= AppKit10_7 && OpenGLProfile::Core == profile) { NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersion3_2Core; const char* const glversion = Args->CheckValue("-glversion"); @@ -606,14 +618,14 @@ bool CocoaVideo::NextMode(int* const width, int* const height, bool* const lette return false; } -DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, const bool fullscreen, DFrameBuffer* const old) +DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, const bool bgra, const bool fullscreen, DFrameBuffer* const old) { PalEntry flashColor = 0; int flashAmount = 0; if (NULL != old) { - if (width == m_width && height == m_height) + if (width == m_width && height == m_height && bgra == old->IsBgra()) { SetMode(width, height, fullscreen, vid_hidpi); return old; @@ -638,7 +650,8 @@ DFrameBuffer* CocoaVideo::CreateFrameBuffer(const int width, const int height, c } else { - fb = new CocoaFrameBuffer(width, height, fullscreen); + //fb = new CocoaFrameBuffer(width, height, bgra, fullscreen); + fb = CreateGLSWFrameBuffer(width, height, bgra, fullscreen); } fb->SetFlash(flashColor, flashAmount); @@ -862,8 +875,8 @@ CocoaVideo* CocoaVideo::GetInstance() // --------------------------------------------------------------------------- -CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool fullscreen) -: DFrameBuffer(width, height) +CocoaFrameBuffer::CocoaFrameBuffer(int width, int height, bool bgra, bool fullscreen) +: DFrameBuffer(width, height, bgra) , m_needPaletteUpdate(false) , m_gamma(0.0f) , m_needGammaUpdate(false) @@ -965,8 +978,15 @@ void CocoaFrameBuffer::Update() FlipCycles.Reset(); BlitCycles.Clock(); - GPfx.Convert(MemBuffer, Pitch, m_pixelBuffer, Width * BYTES_PER_PIXEL, - Width, Height, FRACUNIT, FRACUNIT, 0, 0); + if (IsBgra()) + { + CopyWithGammaBgra(m_pixelBuffer, Width * BYTES_PER_PIXEL, m_gammaTable[0], m_gammaTable[1], m_gammaTable[2], m_flashColor, m_flashAmount); + } + else + { + GPfx.Convert(MemBuffer, Pitch, m_pixelBuffer, Width * BYTES_PER_PIXEL, + Width, Height, FRACUNIT, FRACUNIT, 0, 0); + } FlipCycles.Clock(); Flip(); @@ -1098,8 +1118,10 @@ void CocoaFrameBuffer::Flip() static const GLenum format = GL_ABGR_EXT; #endif // __LITTLE_ENDIAN__ - glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, - Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); + if (IsBgra()) + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, Width, Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, m_pixelBuffer); + else + glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, Width, Height, 0, format, GL_UNSIGNED_BYTE, m_pixelBuffer); glBegin(GL_QUADS); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); @@ -1122,8 +1144,8 @@ void CocoaFrameBuffer::Flip() // --------------------------------------------------------------------------- -SDLGLFB::SDLGLFB(void*, const int width, const int height, int, int, const bool fullscreen) -: DFrameBuffer(width, height) +SDLGLFB::SDLGLFB(void*, const int width, const int height, int, int, const bool fullscreen, bool bgra) +: DFrameBuffer(width, height, bgra) , m_lock(-1) , m_isUpdatePending(false) { @@ -1329,7 +1351,7 @@ void I_CreateRenderer() DFrameBuffer* I_SetMode(int &width, int &height, DFrameBuffer* old) { - return Video->CreateFrameBuffer(width, height, fullscreen, old); + return Video->CreateFrameBuffer(width, height, swtruecolor, fullscreen, old); } bool I_CheckResolution(const int width, const int height, const int bits) diff --git a/src/posix/cocoa/sdlglvideo.h b/src/posix/cocoa/sdlglvideo.h index fcbf23f2a..43f2c9c43 100644 --- a/src/posix/cocoa/sdlglvideo.h +++ b/src/posix/cocoa/sdlglvideo.h @@ -52,7 +52,7 @@ class SDLGLFB : public DFrameBuffer { public: // This must have the same parameters as the Windows version, even if they are not used! - SDLGLFB(void *hMonitor, int width, int height, int, int, bool fullscreen); + SDLGLFB(void *hMonitor, int width, int height, int, int, bool fullscreen, bool bgra); ~SDLGLFB(); virtual bool Lock(bool buffered = true); diff --git a/src/posix/hardware.h b/src/posix/hardware.h index 618941fe5..3c06cb6c6 100644 --- a/src/posix/hardware.h +++ b/src/posix/hardware.h @@ -74,7 +74,7 @@ class IVideo virtual EDisplayType GetDisplayType () = 0; virtual void SetWindowedScale (float scale) = 0; - virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old) = 0; + virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old) = 0; virtual void StartModeIterator (int bits, bool fs) = 0; virtual bool NextMode (int *width, int *height, bool *letterbox) = 0; diff --git a/src/posix/sdl/hardware.cpp b/src/posix/sdl/hardware.cpp index beb16576c..4dfc33847 100644 --- a/src/posix/sdl/hardware.cpp +++ b/src/posix/sdl/hardware.cpp @@ -49,10 +49,11 @@ #include "m_argv.h" #include "sdlglvideo.h" #include "r_renderer.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" EXTERN_CVAR (Bool, ticker) EXTERN_CVAR (Bool, fullscreen) +EXTERN_CVAR (Bool, swtruecolor) EXTERN_CVAR (Float, vid_winscale) IVideo *Video; @@ -64,7 +65,7 @@ void I_RestartRenderer(); int currentrenderer; // [ZDoomGL] -CUSTOM_CVAR (Int, vid_renderer, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +CUSTOM_CVAR (Int, vid_renderer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { // 0: Software renderer // 1: OpenGL renderer @@ -119,8 +120,7 @@ void I_InitGraphics () ticker.SetGenericRepDefault (val, CVAR_Bool); currentrenderer = vid_renderer; - if (currentrenderer==1) Video = new SDLGLVideo(0); - else Video = new SDLVideo (0); + Video = new SDLGLVideo(0); if (Video == NULL) I_FatalError ("Failed to initialize display"); @@ -166,7 +166,7 @@ DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) fs = fullscreen; break; } - DFrameBuffer *res = Video->CreateFrameBuffer (width, height, fs, old); + DFrameBuffer *res = Video->CreateFrameBuffer (width, height, swtruecolor, fs, old); /* Right now, CreateFrameBuffer cannot return NULL if (res == NULL) @@ -320,6 +320,16 @@ CUSTOM_CVAR (Int, vid_maxfps, 200, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) extern int NewWidth, NewHeight, NewBits, DisplayBits; +CUSTOM_CVAR(Bool, swtruecolor, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ + // Strictly speaking this doesn't require a mode switch, but it is the easiest + // way to force a CreateFramebuffer call without a lot of refactoring. + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + CUSTOM_CVAR (Bool, fullscreen, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { NewWidth = screen->GetWidth(); diff --git a/src/posix/sdl/sdlglvideo.cpp b/src/posix/sdl/sdlglvideo.cpp index d88c2bc6a..226600c64 100644 --- a/src/posix/sdl/sdlglvideo.cpp +++ b/src/posix/sdl/sdlglvideo.cpp @@ -13,6 +13,7 @@ #include "c_console.h" #include "sdlglvideo.h" +#include "sdlvideo.h" #include "gl/system/gl_system.h" #include "r_defs.h" #include "gl/gl_functions.h" @@ -29,6 +30,7 @@ // TYPES ------------------------------------------------------------------- +IMPLEMENT_CLASS(SDLBaseFB, true, false) IMPLEMENT_CLASS(SDLGLFB, true, false) struct MiniModeInfo @@ -52,12 +54,30 @@ EXTERN_CVAR (Int, vid_renderer) EXTERN_CVAR (Int, vid_maxfps) EXTERN_CVAR (Bool, cl_capfps) +DFrameBuffer *CreateGLSWFrameBuffer(int width, int height, bool bgra, bool fullscreen); + // PUBLIC DATA DEFINITIONS ------------------------------------------------- CUSTOM_CVAR(Bool, gl_debug, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { Printf("This won't take effect until " GAMENAME " is restarted.\n"); } +CUSTOM_CVAR(Bool, vid_glswfb, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("This won't take effect until " GAMENAME " is restarted.\n"); +} + +#ifdef __arm__ +CUSTOM_CVAR(Bool, gl_es, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("This won't take effect until " GAMENAME " is restarted.\n"); +} +#else +CUSTOM_CVAR(Bool, gl_es, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + Printf("This won't take effect until " GAMENAME " is restarted.\n"); +} +#endif // PRIVATE DATA DEFINITIONS ------------------------------------------------ @@ -159,7 +179,7 @@ bool SDLGLVideo::NextMode (int *width, int *height, bool *letterbox) return false; } -DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) +DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool bgra, bool fullscreen, DFrameBuffer *old) { static int retry = 0; static int owidth, oheight; @@ -169,15 +189,15 @@ DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscr if (old != NULL) { // Reuse the old framebuffer if its attributes are the same - SDLGLFB *fb = static_cast (old); + SDLBaseFB *fb = static_cast (old); if (fb->Width == width && fb->Height == height) { - bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; + bool fsnow = (SDL_GetWindowFlags (fb->GetSDLWindow()) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; if (fsnow != fullscreen) { - SDL_SetWindowFullscreen (fb->Screen, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + SDL_SetWindowFullscreen (fb->GetSDLWindow(), fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); } return old; } @@ -190,7 +210,22 @@ DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscr // flashAmount = 0; } - SDLGLFB *fb = new OpenGLFrameBuffer (0, width, height, 32, 60, fullscreen); + SDLBaseFB *fb; + if (vid_renderer == 1) + { + fb = new OpenGLFrameBuffer(0, width, height, 32, 60, fullscreen); + } + else if (vid_glswfb == 0) + { + fb = new SDLFB(width, height, bgra, fullscreen, nullptr); + } + else + { + fb = (SDLBaseFB*)CreateGLSWFrameBuffer(width, height, bgra, fullscreen); + if (!fb->IsValid()) + fb = new SDLFB(width, height, bgra, fullscreen, nullptr); + } + retry = 0; // If we could not create the framebuffer, try again with slightly @@ -233,7 +268,7 @@ DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscr } ++retry; - fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); + fb = static_cast(CreateFrameBuffer (width, height, false, fullscreen, NULL)); } // fb->SetFlash (flashColor, flashAmount); @@ -288,6 +323,14 @@ bool SDLGLVideo::SetupPixelFormat(bool allowsoftware, int multisample) } if (gl_debug) SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + + if (gl_es) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } + return true; } @@ -310,8 +353,8 @@ bool SDLGLVideo::InitHardware (bool allowsoftware, int multisample) // FrameBuffer implementation ----------------------------------------------- -SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen) - : DFrameBuffer (width, height) +SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen, bool bgra) + : SDLBaseFB (width, height, bgra) { int i; diff --git a/src/posix/sdl/sdlglvideo.h b/src/posix/sdl/sdlglvideo.h index d8ce9005d..c3fde256c 100644 --- a/src/posix/sdl/sdlglvideo.h +++ b/src/posix/sdl/sdlglvideo.h @@ -21,7 +21,7 @@ class SDLGLVideo : public IVideo EDisplayType GetDisplayType () { return DISPLAY_Both; } void SetWindowedScale (float scale); - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old); void StartModeIterator (int bits, bool fs); bool NextMode (int *width, int *height, bool *letterbox); @@ -34,12 +34,23 @@ private: int IteratorMode; int IteratorBits; }; -class SDLGLFB : public DFrameBuffer + +class SDLBaseFB : public DFrameBuffer { - DECLARE_CLASS(SDLGLFB, DFrameBuffer) + DECLARE_CLASS(SDLBaseFB, DFrameBuffer) +public: + using DFrameBuffer::DFrameBuffer; + virtual SDL_Window *GetSDLWindow() = 0; + + friend class SDLGLVideo; +}; + +class SDLGLFB : public SDLBaseFB +{ + DECLARE_CLASS(SDLGLFB, SDLBaseFB) public: // this must have the same parameters as the Windows version, even if they are not used! - SDLGLFB (void *hMonitor, int width, int height, int, int, bool fullscreen); + SDLGLFB (void *hMonitor, int width, int height, int, int, bool fullscreen, bool bgra); ~SDLGLFB (); void ForceBuffering (bool force); @@ -61,6 +72,8 @@ public: int GetClientWidth(); int GetClientHeight(); + SDL_Window *GetSDLWindow() override { return Screen; } + protected: bool CanUpdate(); void SetGammaTable(WORD *tbl); diff --git a/src/posix/sdl/sdlvideo.cpp b/src/posix/sdl/sdlvideo.cpp index 4a6833026..8594eb648 100644 --- a/src/posix/sdl/sdlvideo.cpp +++ b/src/posix/sdl/sdlvideo.cpp @@ -11,7 +11,7 @@ #include "stats.h" #include "v_palette.h" #include "sdlvideo.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" #include "version.h" #include @@ -24,61 +24,6 @@ // TYPES ------------------------------------------------------------------- -class SDLFB : public DFrameBuffer -{ - DECLARE_CLASS(SDLFB, DFrameBuffer) -public: - SDLFB (int width, int height, bool fullscreen, SDL_Window *oldwin); - ~SDLFB (); - - bool Lock (bool buffer); - void Unlock (); - bool Relock (); - void ForceBuffering (bool force); - bool IsValid (); - void Update (); - PalEntry *GetPalette (); - void GetFlashedPalette (PalEntry pal[256]); - void UpdatePalette (); - bool SetGamma (float gamma); - bool SetFlash (PalEntry rgb, int amount); - void GetFlash (PalEntry &rgb, int &amount); - void SetFullscreen (bool fullscreen); - int GetPageCount (); - bool IsFullscreen (); - - friend class SDLVideo; - - virtual void SetVSync (bool vsync); - virtual void ScaleCoordsFromWindow(SWORD &x, SWORD &y); - -private: - PalEntry SourcePalette[256]; - BYTE GammaTable[3][256]; - PalEntry Flash; - int FlashAmount; - float Gamma; - bool UpdatePending; - - SDL_Window *Screen; - SDL_Renderer *Renderer; - union - { - SDL_Texture *Texture; - SDL_Surface *Surface; - }; - - bool UsingRenderer; - bool NeedPalUpdate; - bool NeedGammaUpdate; - bool NotPaletted; - - void UpdateColors (); - void ResetSDLRenderer (); - - SDLFB () {} -}; - IMPLEMENT_CLASS(SDLFB, false, false) struct MiniModeInfo @@ -132,72 +77,6 @@ CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // PRIVATE DATA DEFINITIONS ------------------------------------------------ -// Dummy screen sizes to pass when windowed -static MiniModeInfo WinModes[] = -{ - { 320, 200 }, - { 320, 240 }, - { 400, 225 }, // 16:9 - { 400, 300 }, - { 480, 270 }, // 16:9 - { 480, 360 }, - { 512, 288 }, // 16:9 - { 512, 384 }, - { 640, 360 }, // 16:9 - { 640, 400 }, - { 640, 480 }, - { 720, 480 }, // 16:10 - { 720, 540 }, - { 800, 450 }, // 16:9 - { 800, 480 }, - { 800, 500 }, // 16:10 - { 800, 600 }, - { 848, 480 }, // 16:9 - { 960, 600 }, // 16:10 - { 960, 720 }, - { 1024, 576 }, // 16:9 - { 1024, 600 }, // 17:10 - { 1024, 640 }, // 16:10 - { 1024, 768 }, - { 1088, 612 }, // 16:9 - { 1152, 648 }, // 16:9 - { 1152, 720 }, // 16:10 - { 1152, 864 }, - { 1280, 540 }, // 21:9 - { 1280, 720 }, // 16:9 - { 1280, 854 }, - { 1280, 800 }, // 16:10 - { 1280, 960 }, - { 1280, 1024 }, // 5:4 - { 1360, 768 }, // 16:9 - { 1366, 768 }, - { 1400, 787 }, // 16:9 - { 1400, 875 }, // 16:10 - { 1400, 1050 }, - { 1440, 900 }, - { 1440, 960 }, - { 1440, 1080 }, - { 1600, 900 }, // 16:9 - { 1600, 1000 }, // 16:10 - { 1600, 1200 }, - { 1680, 1050 }, // 16:10 - { 1920, 1080 }, - { 1920, 1200 }, - { 2048, 1536 }, - { 2560, 1080 }, // 21:9 - { 2560, 1440 }, - { 2560, 1600 }, - { 2560, 2048 }, - { 2880, 1800 }, - { 3200, 1800 }, - { 3440, 1440 }, // 21:9 - { 3840, 2160 }, - { 3840, 2400 }, - { 4096, 2160 }, - { 5120, 2160 }, // 21:9 - { 5120, 2880 } -}; - static cycle_t BlitCycles; static cycle_t SDLFlipCycles; @@ -228,131 +107,10 @@ void ScaleWithAspect (int &w, int &h, int Width, int Height) h = y; } -SDLVideo::SDLVideo (int parm) -{ - IteratorBits = 0; -} - -SDLVideo::~SDLVideo () -{ -} - -void SDLVideo::StartModeIterator (int bits, bool fs) -{ - IteratorMode = 0; - IteratorBits = bits; -} - -bool SDLVideo::NextMode (int *width, int *height, bool *letterbox) -{ - if (IteratorBits != 8) - return false; - - if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) - { - *width = WinModes[IteratorMode].Width; - *height = WinModes[IteratorMode].Height; - ++IteratorMode; - return true; - } - return false; -} - -DFrameBuffer *SDLVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) -{ - static int retry = 0; - static int owidth, oheight; - - PalEntry flashColor; - int flashAmount; - - SDL_Window *oldwin = NULL; - - if (old != NULL) - { // Reuse the old framebuffer if its attributes are the same - SDLFB *fb = static_cast (old); - if (fb->Width == width && - fb->Height == height) - { - bool fsnow = (SDL_GetWindowFlags (fb->Screen) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; - - if (fsnow != fullscreen) - { - fb->SetFullscreen (fullscreen); - } - return old; - } - - oldwin = fb->Screen; - fb->Screen = NULL; - - old->GetFlash (flashColor, flashAmount); - old->ObjectFlags |= OF_YesReallyDelete; - if (screen == old) screen = NULL; - delete old; - } - else - { - flashColor = 0; - flashAmount = 0; - } - - SDLFB *fb = new SDLFB (width, height, fullscreen, oldwin); - - // If we could not create the framebuffer, try again with slightly - // different parameters in this order: - // 1. Try with the closest size - // 2. Try in the opposite screen mode with the original size - // 3. Try in the opposite screen mode with the closest size - // This is a somewhat confusing mass of recursion here. - - while (fb == NULL || !fb->IsValid ()) - { - if (fb != NULL) - { - delete fb; - } - - switch (retry) - { - case 0: - owidth = width; - oheight = height; - case 2: - // Try a different resolution. Hopefully that will work. - I_ClosestResolution (&width, &height, 8); - break; - - case 1: - // Try changing fullscreen mode. Maybe that will work. - width = owidth; - height = oheight; - fullscreen = !fullscreen; - break; - - default: - // I give up! - I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight); - } - - ++retry; - fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); - } - retry = 0; - - fb->SetFlash (flashColor, flashAmount); - - return fb; -} - -void SDLVideo::SetWindowedScale (float scale) -{ -} - // FrameBuffer implementation ----------------------------------------------- -SDLFB::SDLFB (int width, int height, bool fullscreen, SDL_Window *oldwin) - : DFrameBuffer (width, height) +SDLFB::SDLFB (int width, int height, bool bgra, bool fullscreen, SDL_Window *oldwin) + : SDLBaseFB (width, height, bgra) { int i; @@ -495,7 +253,11 @@ void SDLFB::Update () pitch = Surface->pitch; } - if (NotPaletted) + if (Bgra) + { + CopyWithGammaBgra(pixels, pitch, GammaTable[0], GammaTable[1], GammaTable[2], Flash, FlashAmount); + } + else if (NotPaletted) { GPfx.Convert (MemBuffer, Pitch, pixels, pitch, Width, Height, @@ -675,13 +437,20 @@ void SDLFB::ResetSDLRenderer () SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255); Uint32 fmt; - switch(vid_displaybits) + if (Bgra) { - default: fmt = SDL_PIXELFORMAT_ARGB8888; break; - case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; - case 24: fmt = SDL_PIXELFORMAT_RGB888; break; - case 16: fmt = SDL_PIXELFORMAT_RGB565; break; - case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; + fmt = SDL_PIXELFORMAT_ARGB8888; + } + else + { + switch (vid_displaybits) + { + default: fmt = SDL_PIXELFORMAT_ARGB8888; break; + case 30: fmt = SDL_PIXELFORMAT_ARGB2101010; break; + case 24: fmt = SDL_PIXELFORMAT_RGB888; break; + case 16: fmt = SDL_PIXELFORMAT_RGB565; break; + case 15: fmt = SDL_PIXELFORMAT_ARGB1555; break; + } } Texture = SDL_CreateTexture (Renderer, fmt, SDL_TEXTUREACCESS_STREAMING, Width, Height); diff --git a/src/posix/sdl/sdlvideo.h b/src/posix/sdl/sdlvideo.h index 072167b5a..33192800d 100644 --- a/src/posix/sdl/sdlvideo.h +++ b/src/posix/sdl/sdlvideo.h @@ -1,21 +1,60 @@ #include "hardware.h" #include "v_video.h" +#include "sdlglvideo.h" -class SDLVideo : public IVideo +class SDLFB : public SDLBaseFB { - public: - SDLVideo (int parm); - ~SDLVideo (); + DECLARE_CLASS(SDLFB, SDLBaseFB) +public: + SDLFB(int width, int height, bool bgra, bool fullscreen, SDL_Window *oldwin); + ~SDLFB(); - EDisplayType GetDisplayType () { return DISPLAY_Both; } - void SetWindowedScale (float scale); + bool Lock(bool buffer); + void Unlock(); + bool Relock(); + void ForceBuffering(bool force); + bool IsValid(); + void Update(); + PalEntry *GetPalette(); + void GetFlashedPalette(PalEntry pal[256]); + void UpdatePalette(); + bool SetGamma(float gamma); + bool SetFlash(PalEntry rgb, int amount); + void GetFlash(PalEntry &rgb, int &amount); + void SetFullscreen(bool fullscreen); + int GetPageCount(); + bool IsFullscreen(); - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + friend class SDLGLVideo; - void StartModeIterator (int bits, bool fs); - bool NextMode (int *width, int *height, bool *letterbox); + virtual void SetVSync(bool vsync); + virtual void ScaleCoordsFromWindow(SWORD &x, SWORD &y); + + SDL_Window *GetSDLWindow() override { return Screen; } private: - int IteratorMode; - int IteratorBits; + PalEntry SourcePalette[256]; + BYTE GammaTable[3][256]; + PalEntry Flash; + int FlashAmount; + float Gamma; + bool UpdatePending; + + SDL_Window *Screen; + SDL_Renderer *Renderer; + union + { + SDL_Texture *Texture; + SDL_Surface *Surface; + }; + + bool UsingRenderer; + bool NeedPalUpdate; + bool NeedGammaUpdate; + bool NotPaletted; + + void UpdateColors(); + void ResetSDLRenderer(); + + SDLFB() {} }; diff --git a/src/r_3dfloors.cpp b/src/r_3dfloors.cpp deleted file mode 100644 index ee7dc4c81..000000000 --- a/src/r_3dfloors.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* -** r_3dfloors.cpp -** software 3D floors addon -** -** by kgsws -*/ - -#include "templates.h" -#include "doomdef.h" -#include "p_local.h" -#include "c_dispatch.h" -#include "r_local.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "c_cvars.h" -#include "r_3dfloors.h" - -// external variables -int fake3D; -F3DFloor *fakeFloor; -fixed_t fakeHeight; -fixed_t fakeAlpha; -int fakeActive = 0; -double sclipBottom; -double sclipTop; -HeightLevel *height_top = NULL; -HeightLevel *height_cur = NULL; -int CurrentMirror = 0; -int CurrentSkybox = 0; - -CVAR(Int, r_3dfloors, true, 0); - -// private variables -int height_max = -1; -TArray toplist; -ClipStack *clip_top = NULL; -ClipStack *clip_cur = NULL; - -void R_3D_DeleteHeights() -{ - height_cur = height_top; - while(height_cur) { - height_top = height_cur; - height_cur = height_cur->next; - M_Free(height_top); - } - height_max = -1; - height_top = height_cur = NULL; -} - -void R_3D_AddHeight(secplane_t *add, sector_t *sec) -{ - HeightLevel *near; - HeightLevel *curr; - - double fheight = add->ZatPoint(ViewPos); - if(fheight >= sec->CenterCeiling()) return; - if(fheight <= sec->CenterFloor()) return; - - fixed_t height = FLOAT2FIXED(fheight); - fakeActive = 1; - - if(height_max >= 0) { - near = height_top; - while(near && near->height < height) near = near->next; - if(near) { - if(near->height == height) return; - curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); - curr->height = height; - curr->prev = near->prev; - curr->next = near; - if(near->prev) near->prev->next = curr; - else height_top = curr; - near->prev = curr; - } else { - curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); - curr->height = height; - curr->prev = height_cur; - curr->next = NULL; - height_cur->next = curr; - height_cur = curr; - } - } else { - height_top = height_cur = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); - height_top->height = height; - height_top->prev = NULL; - height_top->next = NULL; - } - height_max++; -} - -void R_3D_NewClip() -{ - ClipStack *curr; -// extern short floorclip[MAXWIDTH]; -// extern short ceilingclip[MAXWIDTH]; - - curr = (ClipStack*)M_Malloc(sizeof(ClipStack)); - curr->next = 0; - memcpy(curr->floorclip, floorclip, sizeof(short) * MAXWIDTH); - memcpy(curr->ceilingclip, ceilingclip, sizeof(short) * MAXWIDTH); - curr->ffloor = fakeFloor; - assert(fakeFloor->floorclip == NULL); - assert(fakeFloor->ceilingclip == NULL); - fakeFloor->floorclip = curr->floorclip; - fakeFloor->ceilingclip = curr->ceilingclip; - if(clip_top) { - clip_cur->next = curr; - clip_cur = curr; - } else { - clip_top = clip_cur = curr; - } -} - -void R_3D_ResetClip() -{ - clip_cur = clip_top; - while(clip_cur) - { - assert(clip_cur->ffloor->floorclip != NULL); - assert(clip_cur->ffloor->ceilingclip != NULL); - clip_cur->ffloor->ceilingclip = clip_cur->ffloor->floorclip = NULL; - clip_top = clip_cur; - clip_cur = clip_cur->next; - M_Free(clip_top); - } - clip_cur = clip_top = NULL; -} - -void R_3D_EnterSkybox() -{ - HeightStack current; - - current.height_top = height_top; - current.height_cur = height_cur; - current.height_max = height_max; - - toplist.Push(current); - - height_top = NULL; - height_cur = NULL; - height_max = -1; - - CurrentSkybox++; -} - -void R_3D_LeaveSkybox() -{ - HeightStack current; - - current.height_top = NULL; - current.height_cur = NULL; - current.height_max = -1; - - toplist.Pop(current); - - height_top = current.height_top; - height_cur = current.height_cur; - height_max = current.height_max; - - CurrentSkybox--; -} - diff --git a/src/r_3dfloors.h b/src/r_3dfloors.h deleted file mode 100644 index cacb97444..000000000 --- a/src/r_3dfloors.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef SOFT_FAKE3D_H -#define SOFT_FAKE3D_H - -#include "p_3dfloors.h" - -// special types - -struct HeightLevel -{ - double height; - struct HeightLevel *prev; - struct HeightLevel *next; -}; - -struct HeightStack -{ - HeightLevel *height_top; - HeightLevel *height_cur; - int height_max; -}; - -struct ClipStack -{ - short floorclip[MAXWIDTH]; - short ceilingclip[MAXWIDTH]; - F3DFloor *ffloor; - ClipStack *next; -}; - -// external varialbes - -// fake3D flags: -enum -{ - // BSP stage: - FAKE3D_FAKEFLOOR = 1, // fake floor, mark seg as FAKE - FAKE3D_FAKECEILING = 2, // fake ceiling, mark seg as FAKE - FAKE3D_FAKEBACK = 4, // R_AddLine with fake backsector, mark seg as FAKE - FAKE3D_FAKEMASK = 7, - FAKE3D_CLIPBOTFRONT = 8, // use front sector clipping info (bottom) - FAKE3D_CLIPTOPFRONT = 16, // use front sector clipping info (top) - - // sorting stage: - FAKE3D_CLIPBOTTOM = 1, // clip bottom - FAKE3D_CLIPTOP = 2, // clip top - FAKE3D_REFRESHCLIP = 4, // refresh clip info - FAKE3D_DOWN2UP = 8, // rendering from down to up (floors) -}; - -extern int fake3D; -extern F3DFloor *fakeFloor; -extern fixed_t fakeAlpha; -extern int fakeActive; -extern double sclipBottom; -extern double sclipTop; -extern HeightLevel *height_top; -extern HeightLevel *height_cur; -extern int CurrentMirror; -extern int CurrentSkybox; -EXTERN_CVAR(Int, r_3dfloors); - -// functions -void R_3D_DeleteHeights(); -void R_3D_AddHeight(secplane_t *add, sector_t *sec); -void R_3D_NewClip(); -void R_3D_ResetClip(); -void R_3D_EnterSkybox(); -void R_3D_LeaveSkybox(); - -#endif diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp deleted file mode 100644 index 88c907e2b..000000000 --- a/src/r_bsp.cpp +++ /dev/null @@ -1,1389 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// BSP traversal, handling of LineSegs for rendering. -// -// This file contains some code from the Build Engine. -// -// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman -// Ken Silverman's official web site: "http://www.advsys.net/ken" -// See the included license file "BUILDLIC.TXT" for license info. -// -//----------------------------------------------------------------------------- - - -#include - -#include "templates.h" - -#include "doomdef.h" - -#include "m_bbox.h" - -#include "i_system.h" -#include "p_lnspec.h" -#include "p_setup.h" - -#include "r_local.h" -#include "r_main.h" -#include "r_plane.h" -#include "r_draw.h" -#include "r_things.h" -#include "r_3dfloors.h" -#include "a_sharedglobal.h" -#include "g_level.h" -#include "p_effect.h" - -// State. -#include "doomstat.h" -#include "r_state.h" -#include "r_bsp.h" -#include "r_segs.h" -#include "v_palette.h" -#include "r_sky.h" -#include "po_man.h" -#include "r_data/colormaps.h" - -seg_t* curline; -side_t* sidedef; -line_t* linedef; -sector_t* frontsector; -sector_t* backsector; - -// killough 4/7/98: indicates doors closed wrt automap bugfix: -int doorclosed; - -bool r_fakingunderwater; - -extern bool rw_prepped; -extern bool rw_havehigh, rw_havelow; -extern int rw_floorstat, rw_ceilstat; -extern bool rw_mustmarkfloor, rw_mustmarkceiling; -extern short walltop[MAXWIDTH]; // [RH] record max extents of wall -extern short wallbottom[MAXWIDTH]; -extern short wallupper[MAXWIDTH]; -extern short walllower[MAXWIDTH]; - -double rw_backcz1, rw_backcz2; -double rw_backfz1, rw_backfz2; -double rw_frontcz1, rw_frontcz2; -double rw_frontfz1, rw_frontfz2; - - -size_t MaxDrawSegs; -drawseg_t *drawsegs; -drawseg_t* firstdrawseg; -drawseg_t* ds_p; - -size_t FirstInterestingDrawseg; -TArray InterestingDrawsegs; - -FWallCoords WallC; -FWallTmapVals WallT; - -static BYTE FakeSide; - -int WindowLeft, WindowRight; -WORD MirrorFlags; -TArray WallPortals(1000); // note: this array needs to go away as reallocation can cause crashes. - - -subsector_t *InSubsector; - -CVAR (Bool, r_drawflat, false, 0) // [RH] Don't texture segs? - - -void R_StoreWallRange (int start, int stop); - -// -// R_ClearDrawSegs -// -void R_ClearDrawSegs (void) -{ - if (drawsegs == NULL) - { - MaxDrawSegs = 256; // [RH] Default. Increased as needed. - firstdrawseg = drawsegs = (drawseg_t *)M_Malloc (MaxDrawSegs * sizeof(drawseg_t)); - } - FirstInterestingDrawseg = 0; - InterestingDrawsegs.Clear (); - ds_p = drawsegs; -} - - - -// -// ClipWallSegment -// Clips the given range of columns -// and includes it in the new clip list. -// -// -// 1/11/98 killough: Since a type "short" is sufficient, we -// should use it, since smaller arrays fit better in cache. -// - -struct cliprange_t -{ - short first, last; // killough -}; - - -// newend is one past the last valid seg -static cliprange_t *newend; -static cliprange_t solidsegs[MAXWIDTH/2+2]; - - - -//========================================================================== -// -// R_ClipWallSegment -// -// Clips the given range of columns, possibly including it in the clip list. -// Handles both windows (e.g. LineDefs with upper and lower textures) and -// solid walls (e.g. single sided LineDefs [middle texture]) that entirely -// block the view. -// -//========================================================================== - -bool R_ClipWallSegment (int first, int last, bool solid) -{ - cliprange_t *next, *start; - int i, j; - bool res = false; - - // Find the first range that touches the range - // (adjacent pixels are touching). - start = solidsegs; - while (start->last < first) - start++; - - if (first < start->first) - { - res = true; - if (last <= start->first) - { - // Post is entirely visible (above start). - R_StoreWallRange (first, last); - if (fake3D & FAKE3D_FAKEMASK) - { - return true; - } - - // Insert a new clippost for solid walls. - if (solid) - { - if (last == start->first) - { - start->first = first; - } - else - { - next = newend; - newend++; - while (next != start) - { - *next = *(next-1); - next--; - } - next->first = first; - next->last = last; - } - } - return true; - } - - // There is a fragment above *start. - R_StoreWallRange (first, start->first); - - // Adjust the clip size for solid walls - if (solid && !(fake3D & FAKE3D_FAKEMASK)) - { - start->first = first; - } - } - - // Bottom contained in start? - if (last <= start->last) - return res; - - next = start; - while (last >= (next+1)->first) - { - // There is a fragment between two posts. - R_StoreWallRange (next->last, (next+1)->first); - next++; - - if (last <= next->last) - { - // Bottom is contained in next. - last = next->last; - goto crunch; - } - } - - // There is a fragment after *next. - R_StoreWallRange (next->last, last); - -crunch: - if (fake3D & FAKE3D_FAKEMASK) - { - return true; - } - if (solid) - { - // Adjust the clip size. - start->last = last; - - if (next != start) - { - // Remove start+1 to next from the clip list, - // because start now covers their area. - for (i = 1, j = (int)(newend - next); j > 0; i++, j--) - { - start[i] = next[i]; - } - newend = start+i; - } - } - return true; -} - -bool R_CheckClipWallSegment (int first, int last) -{ - cliprange_t *start; - - // Find the first range that touches the range - // (adjacent pixels are touching). - start = solidsegs; - while (start->last < first) - start++; - - if (first < start->first) - { - return true; - } - - // Bottom contained in start? - if (last > start->last) - { - return true; - } - - return false; -} - - - -// -// R_ClearClipSegs -// -void R_ClearClipSegs (short left, short right) -{ - solidsegs[0].first = -0x7fff; // new short limit -- killough - solidsegs[0].last = left; - solidsegs[1].first = right; - solidsegs[1].last = 0x7fff; // new short limit -- killough - newend = solidsegs+2; -} - - -// -// killough 3/7/98: Hack floor/ceiling heights for deep water etc. -// -// If player's view height is underneath fake floor, lower the -// drawn ceiling to be just under the floor height, and replace -// the drawn floor and ceiling textures, and light level, with -// the control sector's. -// -// Similar for ceiling, only reflected. -// -// killough 4/11/98, 4/13/98: fix bugs, add 'back' parameter -// - -sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, - int *floorlightlevel, int *ceilinglightlevel, - bool back) -{ - // [RH] allow per-plane lighting - if (floorlightlevel != NULL) - { - *floorlightlevel = sec->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = sec->GetCeilingLight (); - } - - FakeSide = FAKED_Center; - - const sector_t *s = sec->GetHeightSec(); - if (s != NULL) - { - sector_t *heightsec = viewsector->heightsec; - bool underwater = r_fakingunderwater || - (heightsec && heightsec->floorplane.PointOnSide(ViewPos) <= 0); - bool doorunderwater = false; - int diffTex = (s->MoreFlags & SECF_CLIPFAKEPLANES); - - // Replace sector being drawn with a copy to be hacked - *tempsec = *sec; - - // Replace floor and ceiling height with control sector's heights. - if (diffTex) - { - if (s->floorplane.CopyPlaneIfValid (&tempsec->floorplane, &sec->ceilingplane)) - { - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); - } - else if (s->MoreFlags & SECF_FAKEFLOORONLY) - { - if (underwater) - { - tempsec->ColorMap = s->ColorMap; - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) - { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) - { - *floorlightlevel = s->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = s->GetCeilingLight (); - } - } - FakeSide = FAKED_BelowFloor; - return tempsec; - } - return sec; - } - } - else - { - tempsec->floorplane = s->floorplane; - } - - if (!(s->MoreFlags & SECF_FAKEFLOORONLY)) - { - if (diffTex) - { - if (s->ceilingplane.CopyPlaneIfValid (&tempsec->ceilingplane, &sec->floorplane)) - { - tempsec->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false); - } - } - else - { - tempsec->ceilingplane = s->ceilingplane; - } - } - - double refceilz = s->ceilingplane.ZatPoint(ViewPos); - double orgceilz = sec->ceilingplane.ZatPoint(ViewPos); - -#if 1 - // [RH] Allow viewing underwater areas through doors/windows that - // are underwater but not in a water sector themselves. - // Only works if you cannot see the top surface of any deep water - // sectors at the same time. - if (back && !r_fakingunderwater && curline->frontsector->heightsec == NULL) - { - if (rw_frontcz1 <= s->floorplane.ZatPoint(curline->v1) && - rw_frontcz2 <= s->floorplane.ZatPoint(curline->v2)) - { - // Check that the window is actually visible - for (int z = WallC.sx1; z < WallC.sx2; ++z) - { - if (floorclip[z] > ceilingclip[z]) - { - doorunderwater = true; - r_fakingunderwater = true; - break; - } - } - } - } -#endif - - if (underwater || doorunderwater) - { - tempsec->floorplane = sec->floorplane; - tempsec->ceilingplane = s->floorplane; - tempsec->ceilingplane.FlipVert (); - tempsec->ceilingplane.ChangeHeight(-1 / 65536.); - tempsec->ColorMap = s->ColorMap; - } - - // killough 11/98: prevent sudden light changes from non-water sectors: - if ((underwater && !back) || doorunderwater) - { // head-below-floor hack - tempsec->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; - - tempsec->ceilingplane = s->floorplane; - tempsec->ceilingplane.FlipVert (); - tempsec->ceilingplane.ChangeHeight (-1 / 65536.); - if (s->GetTexture(sector_t::ceiling) == skyflatnum) - { - tempsec->floorplane = tempsec->ceilingplane; - tempsec->floorplane.FlipVert (); - tempsec->floorplane.ChangeHeight (+1 / 65536.); - tempsec->SetTexture(sector_t::ceiling, tempsec->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform; - } - else - { - tempsec->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false); - tempsec->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform; - } - - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) - { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) - { - *floorlightlevel = s->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = s->GetCeilingLight (); - } - } - FakeSide = FAKED_BelowFloor; - } - else if (heightsec && heightsec->ceilingplane.PointOnSide(ViewPos) <= 0 && - orgceilz > refceilz && !(s->MoreFlags & SECF_FAKEFLOORONLY)) - { // Above-ceiling hack - tempsec->ceilingplane = s->ceilingplane; - tempsec->floorplane = s->ceilingplane; - tempsec->floorplane.FlipVert (); - tempsec->floorplane.ChangeHeight (+1 / 65536.); - tempsec->ColorMap = s->ColorMap; - tempsec->ColorMap = s->ColorMap; - - tempsec->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false); - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false); - tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform; - - if (s->GetTexture(sector_t::floor) != skyflatnum) - { - tempsec->ceilingplane = sec->ceilingplane; - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; - } - - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) - { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) - { - *floorlightlevel = s->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = s->GetCeilingLight (); - } - } - FakeSide = FAKED_AboveCeiling; - } - sec = tempsec; // Use other sector - } - return sec; -} - - -bool R_SkyboxCompare(sector_t *frontsector, sector_t *backsector) -{ - FSectorPortal *frontc = frontsector->GetPortal(sector_t::ceiling); - FSectorPortal *frontf = frontsector->GetPortal(sector_t::floor); - FSectorPortal *backc = backsector->GetPortal(sector_t::ceiling); - FSectorPortal *backf = backsector->GetPortal(sector_t::floor); - - // return true if any of the planes has a linedef-based portal (unless both sides have the same one. - // Ideally this should also check thing based portals but the omission of this check had been abused to hell and back for those. - // (Note: This may require a compatibility option if some maps ran into this for line based portals as well.) - if (!frontc->MergeAllowed()) return (frontc != backc); - if (!frontf->MergeAllowed()) return (frontf != backf); - if (!backc->MergeAllowed()) return true; - if (!backf->MergeAllowed()) return true; - return false; -} - -// -// R_AddLine -// Clips the given segment -// and adds any visible pieces to the line list. -// - -void R_AddLine (seg_t *line) -{ - static sector_t tempsec; // killough 3/8/98: ceiling/water hack - bool solid; - DVector2 pt1, pt2; - - curline = line; - - // [RH] Color if not texturing line - dc_color = (((int)(line - segs) * 8) + 4) & 255; - - pt1 = line->v1->fPos() - ViewPos; - pt2 = line->v2->fPos() - ViewPos; - - // Reject lines not facing viewer - if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) - return; - - if (WallC.Init(pt1, pt2, 32.0 / (1 << 12))) - return; - - if (WallC.sx1 >= WindowRight || WallC.sx2 <= WindowLeft) - return; - - if (line->linedef == NULL) - { - if (R_CheckClipWallSegment (WallC.sx1, WallC.sx2)) - { - InSubsector->flags |= SSECF_DRAWN; - } - return; - } - - // reject lines that aren't seen from the portal (if any) - // [ZZ] 10.01.2016: lines inside a skybox shouldn't be clipped, although this imposes some limitations on portals in skyboxes. - if (!CurrentPortalInSkybox && CurrentPortal && P_ClipLineToPortal(line->linedef, CurrentPortal->dst, ViewPos)) - return; - - vertex_t *v1, *v2; - - v1 = line->linedef->v1; - v2 = line->linedef->v2; - - if ((v1 == line->v1 && v2 == line->v2) || (v2 == line->v1 && v1 == line->v2)) - { // The seg is the entire wall. - WallT.InitFromWallCoords(&WallC); - } - else - { // The seg is only part of the wall. - if (line->linedef->sidedef[0] != line->sidedef) - { - swapvalues (v1, v2); - } - WallT.InitFromLine(v1->fPos() - ViewPos, v2->fPos() - ViewPos); - } - - if (!(fake3D & FAKE3D_FAKEBACK)) - { - backsector = line->backsector; - } - rw_frontcz1 = frontsector->ceilingplane.ZatPoint(line->v1); - rw_frontfz1 = frontsector->floorplane.ZatPoint(line->v1); - rw_frontcz2 = frontsector->ceilingplane.ZatPoint(line->v2); - rw_frontfz2 = frontsector->floorplane.ZatPoint(line->v2); - - rw_mustmarkfloor = rw_mustmarkceiling = false; - rw_havehigh = rw_havelow = false; - - // Single sided line? - if (backsector == NULL || (line->linedef->isVisualPortal() && line->sidedef == line->linedef->sidedef[0])) - { - solid = true; - } - else - { - // kg3D - its fake, no transfer_heights - if (!(fake3D & FAKE3D_FAKEBACK)) - { // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water - backsector = R_FakeFlat (backsector, &tempsec, NULL, NULL, true); - } - doorclosed = 0; // killough 4/16/98 - - rw_backcz1 = backsector->ceilingplane.ZatPoint(line->v1); - rw_backfz1 = backsector->floorplane.ZatPoint(line->v1); - rw_backcz2 = backsector->ceilingplane.ZatPoint(line->v2); - rw_backfz2 = backsector->floorplane.ZatPoint(line->v2); - - // Cannot make these walls solid, because it can result in - // sprite clipping problems for sprites near the wall - if (rw_frontcz1 > rw_backcz1 || rw_frontcz2 > rw_backcz2) - { - rw_havehigh = true; - WallMost (wallupper, backsector->ceilingplane, &WallC); - } - if (rw_frontfz1 < rw_backfz1 || rw_frontfz2 < rw_backfz2) - { - rw_havelow = true; - WallMost (walllower, backsector->floorplane, &WallC); - } - - // Closed door. - if ((rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2) || - (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2)) - { - solid = true; - } - else if ( - // properly render skies (consider door "open" if both ceilings are sky): - (backsector->GetTexture(sector_t::ceiling) != skyflatnum || frontsector->GetTexture(sector_t::ceiling) != skyflatnum) - - // if door is closed because back is shut: - && rw_backcz1 <= rw_backfz1 && rw_backcz2 <= rw_backfz2 - - // preserve a kind of transparent door/lift special effect: - && ((rw_backcz1 >= rw_frontcz1 && rw_backcz2 >= rw_frontcz2) || line->sidedef->GetTexture(side_t::top).isValid()) - && ((rw_backfz1 <= rw_frontfz1 && rw_backfz2 <= rw_frontfz2) || line->sidedef->GetTexture(side_t::bottom).isValid())) - { - // killough 1/18/98 -- This function is used to fix the automap bug which - // showed lines behind closed doors simply because the door had a dropoff. - // - // It assumes that Doom has already ruled out a door being closed because - // of front-back closure (e.g. front floor is taller than back ceiling). - - // This fixes the automap floor height bug -- killough 1/18/98: - // killough 4/7/98: optimize: save result in doorclosed for use in r_segs.c - doorclosed = true; - solid = true; - } - else if (frontsector->ceilingplane != backsector->ceilingplane || - frontsector->floorplane != backsector->floorplane) - { - // Window. - solid = false; - } - else if (R_SkyboxCompare(frontsector, backsector)) - { - solid = false; - } - else if (backsector->lightlevel != frontsector->lightlevel - || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) - || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) - || curline->sidedef->GetTexture(side_t::mid).isValid() - - // killough 3/7/98: Take flats offsets into account: - || backsector->planes[sector_t::floor].xform != frontsector->planes[sector_t::floor].xform - || backsector->planes[sector_t::ceiling].xform != frontsector->planes[sector_t::ceiling].xform - - || backsector->GetPlaneLight(sector_t::floor) != frontsector->GetPlaneLight(sector_t::floor) - || backsector->GetPlaneLight(sector_t::ceiling) != frontsector->GetPlaneLight(sector_t::ceiling) - || backsector->GetVisFlags(sector_t::floor) != frontsector->GetVisFlags(sector_t::floor) - || backsector->GetVisFlags(sector_t::ceiling) != frontsector->GetVisFlags(sector_t::ceiling) - - // [RH] Also consider colormaps - || backsector->ColorMap != frontsector->ColorMap - - - - // kg3D - and fake lights - || (frontsector->e && frontsector->e->XFloor.lightlist.Size()) - || (backsector->e && backsector->e->XFloor.lightlist.Size()) - ) - { - solid = false; - } - else - { - // Reject empty lines used for triggers and special events. - // Identical floor and ceiling on both sides, identical light levels - // on both sides, and no middle texture. - - // When using GL nodes, do a clipping test for these lines so we can - // mark their subsectors as visible for automap texturing. - if (hasglnodes && !(InSubsector->flags & SSECF_DRAWN)) - { - if (R_CheckClipWallSegment(WallC.sx1, WallC.sx2)) - { - InSubsector->flags |= SSECF_DRAWN; - } - } - return; - } - } - - rw_prepped = false; - - if (line->linedef->special == Line_Horizon) - { - // Be aware: Line_Horizon does not work properly with sloped planes - clearbufshort (walltop+WallC.sx1, WallC.sx2 - WallC.sx1, centery); - clearbufshort (wallbottom+WallC.sx1, WallC.sx2 - WallC.sx1, centery); - } - else - { - rw_ceilstat = WallMost (walltop, frontsector->ceilingplane, &WallC); - rw_floorstat = WallMost (wallbottom, frontsector->floorplane, &WallC); - - // [RH] treat off-screen walls as solid -#if 0 // Maybe later... - if (!solid) - { - if (rw_ceilstat == 12 && line->sidedef->GetTexture(side_t::top) != 0) - { - rw_mustmarkceiling = true; - solid = true; - } - if (rw_floorstat == 3 && line->sidedef->GetTexture(side_t::bottom) != 0) - { - rw_mustmarkfloor = true; - solid = true; - } - } -#endif - } - - if (R_ClipWallSegment (WallC.sx1, WallC.sx2, solid)) - { - InSubsector->flags |= SSECF_DRAWN; - } -} - -// -// FWallCoords :: Init -// -// Transform and clip coordinates. Returns true if it was clipped away -// -bool FWallCoords::Init(const DVector2 &pt1, const DVector2 &pt2, double too_close) -{ - tleft.X = float(pt1.X * ViewSin - pt1.Y * ViewCos); - tright.X = float(pt2.X * ViewSin - pt2.Y * ViewCos); - - tleft.Y = float(pt1.X * ViewTanCos + pt1.Y * ViewTanSin); - tright.Y = float(pt2.X * ViewTanCos + pt2.Y * ViewTanSin); - - if (MirrorFlags & RF_XFLIP) - { - float t = -tleft.X; - tleft.X = -tright.X; - tright.X = t; - swapvalues(tleft.Y, tright.Y); - } - - if (tleft.X >= -tleft.Y) - { - if (tleft.X > tleft.Y) return true; // left edge is off the right side - if (tleft.Y == 0) return true; - sx1 = xs_RoundToInt(CenterX + tleft.X * CenterX / tleft.Y); - sz1 = tleft.Y; - } - else - { - if (tright.X < -tright.Y) return true; // wall is off the left side - float den = tleft.X - tright.X - tright.Y + tleft.Y; - if (den == 0) return true; - sx1 = 0; - sz1 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X + tleft.Y) / den; - } - - if (sz1 < too_close) - return true; - - if (tright.X <= tright.Y) - { - if (tright.X < -tright.Y) return true; // right edge is off the left side - if (tright.Y == 0) return true; - sx2 = xs_RoundToInt(CenterX + tright.X * CenterX / tright.Y); - sz2 = tright.Y; - } - else - { - if (tleft.X > tleft.Y) return true; // wall is off the right side - float den = tright.Y - tleft.Y - tright.X + tleft.X; - if (den == 0) return true; - sx2 = viewwidth; - sz2 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X - tleft.Y) / den; - } - - if (sz2 < too_close || sx2 <= sx1) - return true; - - return false; -} - -void FWallTmapVals::InitFromWallCoords(const FWallCoords *wallc) -{ - const FVector2 *left = &wallc->tleft; - const FVector2 *right = &wallc->tright; - - if (MirrorFlags & RF_XFLIP) - { - swapvalues(left, right); - } - UoverZorg = left->X * centerx; - UoverZstep = -left->Y; - InvZorg = (left->X - right->X) * centerx; - InvZstep = right->Y - left->Y; -} - -void FWallTmapVals::InitFromLine(const DVector2 &left, const DVector2 &right) -{ // Coordinates should have already had viewx,viewy subtracted - double fullx1 = left.X * ViewSin - left.Y * ViewCos; - double fullx2 = right.X * ViewSin - right.Y * ViewCos; - double fully1 = left.X * ViewTanCos + left.Y * ViewTanSin; - double fully2 = right.X * ViewTanCos + right.Y * ViewTanSin; - - if (MirrorFlags & RF_XFLIP) - { - fullx1 = -fullx1; - fullx2 = -fullx2; - } - - UoverZorg = float(fullx1 * centerx); - UoverZstep = float(-fully1); - InvZorg = float((fullx1 - fullx2) * centerx); - InvZstep = float(fully2 - fully1); -} - -// -// R_CheckBBox -// Checks BSP node/subtree bounding box. -// Returns true if some part of the bbox might be visible. -// -extern "C" const int checkcoord[12][4] = -{ - {3,0,2,1}, - {3,0,2,0}, - {3,1,2,0}, - {0}, - {2,0,2,1}, - {0,0,0,0}, - {3,1,3,0}, - {0}, - {2,0,3,1}, - {2,1,3,1}, - {2,1,3,0} -}; - - -static bool R_CheckBBox (fixed_t *bspcoord) // killough 1/28/98: static -{ - int boxx; - int boxy; - int boxpos; - - double x1, y1, x2, y2; - double rx1, ry1, rx2, ry2; - int sx1, sx2; - - cliprange_t* start; - - // Find the corners of the box - // that define the edges from current viewpoint. - if (ViewPos.X <= FIXED2DBL(bspcoord[BOXLEFT])) - boxx = 0; - else if (ViewPos.X < FIXED2DBL(bspcoord[BOXRIGHT])) - boxx = 1; - else - boxx = 2; - - if (ViewPos.Y >= FIXED2DBL(bspcoord[BOXTOP])) - boxy = 0; - else if (ViewPos.Y > FIXED2DBL(bspcoord[BOXBOTTOM])) - boxy = 1; - else - boxy = 2; - - boxpos = (boxy<<2)+boxx; - if (boxpos == 5) - return true; - - x1 = FIXED2DBL(bspcoord[checkcoord[boxpos][0]]) - ViewPos.X; - y1 = FIXED2DBL(bspcoord[checkcoord[boxpos][1]]) - ViewPos.Y; - x2 = FIXED2DBL(bspcoord[checkcoord[boxpos][2]]) - ViewPos.X; - y2 = FIXED2DBL(bspcoord[checkcoord[boxpos][3]]) - ViewPos.Y; - - // check clip list for an open space - - // Sitting on a line? - if (y1 * (x1 - x2) + x1 * (y2 - y1) >= -EQUAL_EPSILON) - return true; - - rx1 = x1 * ViewSin - y1 * ViewCos; - rx2 = x2 * ViewSin - y2 * ViewCos; - ry1 = x1 * ViewTanCos + y1 * ViewTanSin; - ry2 = x2 * ViewTanCos + y2 * ViewTanSin; - - if (MirrorFlags & RF_XFLIP) - { - double t = -rx1; - rx1 = -rx2; - rx2 = t; - swapvalues(ry1, ry2); - } - - if (rx1 >= -ry1) - { - if (rx1 > ry1) return false; // left edge is off the right side - if (ry1 == 0) return false; - sx1 = xs_RoundToInt(CenterX + rx1 * CenterX / ry1); - } - else - { - if (rx2 < -ry2) return false; // wall is off the left side - if (rx1 - rx2 - ry2 + ry1 == 0) return false; // wall does not intersect view volume - sx1 = 0; - } - - if (rx2 <= ry2) - { - if (rx2 < -ry2) return false; // right edge is off the left side - if (ry2 == 0) return false; - sx2 = xs_RoundToInt(CenterX + rx2 * CenterX / ry2); - } - else - { - if (rx1 > ry1) return false; // wall is off the right side - if (ry2 - ry1 - rx2 + rx1 == 0) return false; // wall does not intersect view volume - sx2 = viewwidth; - } - - // Find the first clippost that touches the source post - // (adjacent pixels are touching). - - // Does not cross a pixel. - if (sx2 <= sx1) - return false; - - start = solidsegs; - while (start->last < sx2) - start++; - - if (sx1 >= start->first && sx2 <= start->last) - { - // The clippost contains the new span. - return false; - } - - return true; -} - - -void R_Subsector (subsector_t *sub); -static void R_AddPolyobjs(subsector_t *sub) -{ - if (sub->BSP == NULL || sub->BSP->bDirty) - { - sub->BuildPolyBSP(); - } - if (sub->BSP->Nodes.Size() == 0) - { - R_Subsector(&sub->BSP->Subsectors[0]); - } - else - { - R_RenderBSPNode(&sub->BSP->Nodes.Last()); - } -} - -// kg3D - add fake segs, never rendered -void R_FakeDrawLoop(subsector_t *sub) -{ - int count; - seg_t* line; - - count = sub->numlines; - line = sub->firstline; - - while (count--) - { - if ((line->sidedef) && !(line->sidedef->Flags & WALLF_POLYOBJ)) - { - R_AddLine (line); - } - line++; - } -} - -// -// R_Subsector -// Determine floor/ceiling planes. -// Add sprites of things in sector. -// Draw one or more line segments. -// -void R_Subsector (subsector_t *sub) -{ - int count; - seg_t* line; - sector_t tempsec; // killough 3/7/98: deep water hack - int floorlightlevel; // killough 3/16/98: set floor lightlevel - int ceilinglightlevel; // killough 4/11/98 - bool outersubsector; - int fll, cll, position; - FSectorPortal *portal; - - // kg3D - fake floor stuff - visplane_t *backupfp; - visplane_t *backupcp; - //secplane_t templane; - lightlist_t *light; - - if (InSubsector != NULL) - { // InSubsector is not NULL. This means we are rendering from a mini-BSP. - outersubsector = false; - } - else - { - outersubsector = true; - InSubsector = sub; - } - -#ifdef RANGECHECK - if (outersubsector && sub - subsectors >= (ptrdiff_t)numsubsectors) - I_Error ("R_Subsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); -#endif - - assert(sub->sector != NULL); - - if (sub->polys) - { // Render the polyobjs in the subsector first - R_AddPolyobjs(sub); - if (outersubsector) - { - InSubsector = NULL; - } - return; - } - - frontsector = sub->sector; - frontsector->MoreFlags |= SECF_DRAWN; - count = sub->numlines; - line = sub->firstline; - - // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect - frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, - &ceilinglightlevel, false); // killough 4/11/98 - - fll = floorlightlevel; - cll = ceilinglightlevel; - - // [RH] set foggy flag - foggy = level.fadeto || frontsector->ColorMap->Fade || (level.flags & LEVEL_HASFADETABLE); - r_actualextralight = foggy ? 0 : extralight << 4; - - // kg3D - fake lights - if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(frontsector, &frontsector->ceilingplane, false); - basecolormap = light->extra_colormap; - // If this is the real ceiling, don't discard plane lighting R_FakeFlat() - // accounted for. - if (light->p_lightlevel != &frontsector->lightlevel) - { - ceilinglightlevel = *light->p_lightlevel; - } - } - else - { - basecolormap = frontsector->ColorMap; - } - - portal = frontsector->ValidatePortal(sector_t::ceiling); - - ceilingplane = frontsector->ceilingplane.PointOnSide(ViewPos) > 0 || - frontsector->GetTexture(sector_t::ceiling) == skyflatnum || - portal != NULL || - (frontsector->heightsec && - !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && - frontsector->heightsec->GetTexture(sector_t::floor) == skyflatnum) ? - R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 - frontsector->GetTexture(sector_t::ceiling), - ceilinglightlevel + r_actualextralight, // killough 4/11/98 - frontsector->GetAlpha(sector_t::ceiling), - !!(frontsector->GetFlags(sector_t::ceiling) & PLANEF_ADDITIVE), - frontsector->planes[sector_t::ceiling].xform, - frontsector->sky, - portal - ) : NULL; - - if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(frontsector, &frontsector->floorplane, false); - basecolormap = light->extra_colormap; - // If this is the real floor, don't discard plane lighting R_FakeFlat() - // accounted for. - if (light->p_lightlevel != &frontsector->lightlevel) - { - floorlightlevel = *light->p_lightlevel; - } - } - else - { - basecolormap = frontsector->ColorMap; - } - - // killough 3/7/98: Add (x,y) offsets to flats, add deep water check - // killough 3/16/98: add floorlightlevel - // killough 10/98: add support for skies transferred from sidedefs - portal = frontsector->ValidatePortal(sector_t::floor); - - floorplane = frontsector->floorplane.PointOnSide(ViewPos) > 0 || // killough 3/7/98 - frontsector->GetTexture(sector_t::floor) == skyflatnum || - portal != NULL || - (frontsector->heightsec && - !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && - frontsector->heightsec->GetTexture(sector_t::ceiling) == skyflatnum) ? - R_FindPlane(frontsector->floorplane, - frontsector->GetTexture(sector_t::floor), - floorlightlevel + r_actualextralight, // killough 3/16/98 - frontsector->GetAlpha(sector_t::floor), - !!(frontsector->GetFlags(sector_t::floor) & PLANEF_ADDITIVE), - frontsector->planes[sector_t::floor].xform, - frontsector->sky, - portal - ) : NULL; - - // kg3D - fake planes rendering - if (r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) - { - backupfp = floorplane; - backupcp = ceilingplane; - // first check all floors - for (int i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = frontsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!fakeFloor->model) continue; - if (fakeFloor->bottom.plane->isSlope()) continue; - if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES|FF_RENDERSIDES))) - { - R_3D_AddHeight(fakeFloor->top.plane, frontsector); - } - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (fakeFloor->alpha == 0) continue; - if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue; - fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE); - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot); - if (fakeHeight < ViewPos.Z && - fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)) - { - fake3D = FAKE3D_FAKEFLOOR; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - if (!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) - { - tempsec.SetTexture(sector_t::floor, tempsec.GetTexture(sector_t::ceiling)); - position = sector_t::ceiling; - } else position = sector_t::floor; - frontsector = &tempsec; - - if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(sub->sector, &frontsector->floorplane, false); - basecolormap = light->extra_colormap; - floorlightlevel = *light->p_lightlevel; - } - - ceilingplane = NULL; - floorplane = R_FindPlane(frontsector->floorplane, - frontsector->GetTexture(sector_t::floor), - floorlightlevel + r_actualextralight, // killough 3/16/98 - frontsector->GetAlpha(sector_t::floor), - !!(fakeFloor->flags & FF_ADDITIVETRANS), - frontsector->planes[position].xform, - frontsector->sky, - NULL); - - R_FakeDrawLoop(sub); - fake3D = 0; - frontsector = sub->sector; - } - } - // and now ceilings - for (unsigned int i = 0; i < frontsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = frontsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!fakeFloor->model) continue; - if (fakeFloor->top.plane->isSlope()) continue; - if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES|FF_RENDERSIDES))) - { - R_3D_AddHeight(fakeFloor->bottom.plane, frontsector); - } - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (fakeFloor->alpha == 0) continue; - if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE|FF_INVERTSECTOR)) == (FF_SWIMMABLE|FF_INVERTSECTOR)) continue; - fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE); - - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot); - if (fakeHeight > ViewPos.Z && - fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)) - { - fake3D = FAKE3D_FAKECEILING; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - if ((!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) || - (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR)) - { - tempsec.SetTexture(sector_t::ceiling, tempsec.GetTexture(sector_t::floor)); - position = sector_t::floor; - } else position = sector_t::ceiling; - frontsector = &tempsec; - - tempsec.ceilingplane.ChangeHeight(-1 / 65536.); - if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(sub->sector, &frontsector->ceilingplane, false); - basecolormap = light->extra_colormap; - ceilinglightlevel = *light->p_lightlevel; - } - tempsec.ceilingplane.ChangeHeight(1 / 65536.); - - floorplane = NULL; - ceilingplane = R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 - frontsector->GetTexture(sector_t::ceiling), - ceilinglightlevel + r_actualextralight, // killough 4/11/98 - frontsector->GetAlpha(sector_t::ceiling), - !!(fakeFloor->flags & FF_ADDITIVETRANS), - frontsector->planes[position].xform, - frontsector->sky, - NULL); - - R_FakeDrawLoop(sub); - fake3D = 0; - frontsector = sub->sector; - } - } - fakeFloor = NULL; - floorplane = backupfp; - ceilingplane = backupcp; - } - - basecolormap = frontsector->ColorMap; - floorlightlevel = fll; - ceilinglightlevel = cll; - - // killough 9/18/98: Fix underwater slowdown, by passing real sector - // instead of fake one. Improve sprite lighting by basing sprite - // lightlevels on floor & ceiling lightlevels in the surrounding area. - // [RH] Handle sprite lighting like Duke 3D: If the ceiling is a sky, sprites are lit by - // it, otherwise they are lit by the floor. - R_AddSprites (sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ? - ceilinglightlevel : floorlightlevel, FakeSide); - - // [RH] Add particles - if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors) - { // Only do it for the main BSP. - int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight); - for (WORD i = ParticlesInSubsec[(unsigned int)(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) - { - R_ProjectParticle (Particles + i, subsectors[sub-subsectors].sector, shade, FakeSide); - } - } - - count = sub->numlines; - line = sub->firstline; - - while (count--) - { - if (!outersubsector || line->sidedef == NULL || !(line->sidedef->Flags & WALLF_POLYOBJ)) - { - // kg3D - fake planes bounding calculation - if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) - { - backupfp = floorplane; - backupcp = ceilingplane; - floorplane = NULL; - ceilingplane = NULL; - for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = line->backsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (!fakeFloor->model) continue; - fake3D = FAKE3D_FAKEBACK; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - backsector = &tempsec; - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - if (frontsector->CenterFloor() >= backsector->CenterFloor()) - { - fake3D |= FAKE3D_CLIPBOTFRONT; - } - if (frontsector->CenterCeiling() <= backsector->CenterCeiling()) - { - fake3D |= FAKE3D_CLIPTOPFRONT; - } - R_AddLine(line); // fake - } - fakeFloor = NULL; - fake3D = 0; - floorplane = backupfp; - ceilingplane = backupcp; - } - R_AddLine (line); // now real - } - line++; - } - if (outersubsector) - { - InSubsector = NULL; - } -} - -// -// RenderBSPNode -// Renders all subsectors below a given node, traversing subtree recursively. -// Just call with BSP root and -1. -// killough 5/2/98: reformatted, removed tail recursion - -void R_RenderBSPNode (void *node) -{ - if (numnodes == 0) - { - R_Subsector (subsectors); - return; - } - while (!((size_t)node & 1)) // Keep going until found a subsector - { - node_t *bsp = (node_t *)node; - - // Decide which side the view point is on. - int side = R_PointOnSide (ViewPos, bsp); - - // Recursively divide front space (toward the viewer). - R_RenderBSPNode (bsp->children[side]); - - // Possibly divide back space (away from the viewer). - side ^= 1; - if (!R_CheckBBox (bsp->bbox[side])) - return; - - node = bsp->children[side]; - } - R_Subsector ((subsector_t *)((BYTE *)node - 1)); -} diff --git a/src/r_bsp.h b/src/r_bsp.h deleted file mode 100644 index 48ca7565b..000000000 --- a/src/r_bsp.h +++ /dev/null @@ -1,123 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Refresh module, BSP traversal and handling. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_BSP__ -#define __R_BSP__ - -#include "tarray.h" -#include -#include "r_defs.h" - -// The 3072 below is just an arbitrary value picked to avoid -// drawing lines the player is too close to that would overflow -// the texture calculations. -#define TOO_CLOSE_Z (3072.0 / (1<<12)) - -struct FWallCoords -{ - FVector2 tleft; // coords at left of wall in view space rx1,ry1 - FVector2 tright; // coords at right of wall in view space rx2,ry2 - - float sz1, sz2; // depth at left, right of wall in screen space yb1,yb2 - short sx1, sx2; // x coords at left, right of wall in screen space xb1,xb2 - - bool Init(const DVector2 &pt1, const DVector2 &pt2, double too_close); -}; - -struct FWallTmapVals -{ - float UoverZorg, UoverZstep; - float InvZorg, InvZstep; - - void InitFromWallCoords(const FWallCoords *wallc); - void InitFromLine(const DVector2 &left, const DVector2 &right); -}; - -extern FWallCoords WallC; -extern FWallTmapVals WallT; - -enum -{ - FAKED_Center, - FAKED_BelowFloor, - FAKED_AboveCeiling -}; - -struct drawseg_t -{ - seg_t* curline; - float light, lightstep; - float iscale, iscalestep; - short x1, x2; // Same as sx1 and sx2, but clipped to the drawseg - short sx1, sx2; // left, right of parent seg on screen - float sz1, sz2; // z for left, right of parent seg on screen - float siz1, siz2; // 1/z for left, right of parent seg on screen - float cx, cy, cdx, cdy; - float yscale; - BYTE silhouette; // 0=none, 1=bottom, 2=top, 3=both - BYTE bFogBoundary; - BYTE bFakeBoundary; // for fake walls - int shade; -// Pointers to lists for sprite clipping, -// all three adjusted so [x1] is first value. - ptrdiff_t sprtopclip; // type short - ptrdiff_t sprbottomclip; // type short - ptrdiff_t maskedtexturecol; // type short - ptrdiff_t swall; // type float - int fake; // ident fake drawseg, don't draw and clip sprites -// backups - ptrdiff_t bkup; // sprtopclip backup, for mid and fake textures - FWallTmapVals tmapvals; - int CurrentPortalUniq; // [ZZ] to identify the portal that this drawseg is in. used for sprite clipping. -}; - - -extern seg_t* curline; -extern side_t* sidedef; -extern line_t* linedef; -extern sector_t* frontsector; -extern sector_t* backsector; - -extern drawseg_t *drawsegs; -extern drawseg_t *firstdrawseg; -extern drawseg_t* ds_p; - -extern TArray InterestingDrawsegs; // drawsegs that have something drawn on them -extern size_t FirstInterestingDrawseg; - -extern int WindowLeft, WindowRight; -extern WORD MirrorFlags; - -typedef void (*drawfunc_t) (int start, int stop); - -EXTERN_CVAR (Bool, r_drawflat) // [RH] Don't texture segs? - -// BSP? -void R_ClearClipSegs (short left, short right); -void R_ClearDrawSegs (); -void R_RenderBSPNode (void *node); - -// killough 4/13/98: fake floors/ceilings for deep water / fake ceilings: -sector_t *R_FakeFlat(sector_t *, sector_t *, int *, int *, bool); - - -#endif diff --git a/src/r_data/colormaps.cpp b/src/r_data/colormaps.cpp index 2d3c0e49d..659470a23 100644 --- a/src/r_data/colormaps.cpp +++ b/src/r_data/colormaps.cpp @@ -72,8 +72,8 @@ struct FakeCmap }; TArray fakecmaps; -BYTE *realcolormaps; -BYTE *realfbcolormaps; //[SP] For fullbright use +FSWColormap realcolormaps; +FSWColormap realfbcolormaps; //[SP] For fullbright use size_t numfakecmaps; @@ -410,7 +410,7 @@ void R_SetDefaultColormap (const char *name) foo.Color = 0xFFFFFF; foo.Fade = 0; - foo.Maps = realcolormaps; + foo.Maps = realcolormaps.Maps; foo.Desaturate = 0; foo.Next = NULL; foo.BuildLights (); @@ -432,7 +432,7 @@ void R_SetDefaultColormap (const char *name) remap[0] = 0; for (i = 0; i < NUMCOLORMAPS; ++i) { - BYTE *map2 = &realcolormaps[i*256]; + BYTE *map2 = &realcolormaps.Maps[i*256]; lumpr.Read (map, 256); for (j = 0; j < 256; ++j) { @@ -456,15 +456,11 @@ void R_DeinitColormaps () { SpecialColormaps.Clear(); fakecmaps.Clear(); - if (realcolormaps != NULL) + delete[] realcolormaps.Maps; + if (realfbcolormaps.Maps) { - delete[] realcolormaps; - realcolormaps = NULL; - } - if (realfbcolormaps != NULL) - { - delete[] realfbcolormaps; - realfbcolormaps = NULL; + delete[] realfbcolormaps.Maps; + realfbcolormaps.Maps = nullptr; } FreeSpecialLights(); } @@ -508,7 +504,7 @@ void R_InitColormaps () } } } - realcolormaps = new BYTE[256*NUMCOLORMAPS*fakecmaps.Size()]; + realcolormaps.Maps = new BYTE[256*NUMCOLORMAPS*fakecmaps.Size()]; R_SetDefaultColormap ("COLORMAP"); if (fakecmaps.Size() > 1) @@ -530,7 +526,7 @@ void R_InitColormaps () { int k, r, g, b; FWadLump lump = Wads.OpenLumpNum (fakecmaps[j].lump); - BYTE *const map = realcolormaps + NUMCOLORMAPS*256*j; + BYTE *const map = realcolormaps.Maps + NUMCOLORMAPS*256*j; for (k = 0; k < NUMCOLORMAPS; ++k) { @@ -557,19 +553,19 @@ void R_InitColormaps () } // [SP] Create a copy of the colormap - if (!realfbcolormaps) + if (!realfbcolormaps.Maps) { - realfbcolormaps = new BYTE[256*NUMCOLORMAPS*fakecmaps.Size()]; - memcpy(realfbcolormaps, realcolormaps, 256*NUMCOLORMAPS*fakecmaps.Size()); + realfbcolormaps.Maps = new BYTE[256*NUMCOLORMAPS*fakecmaps.Size()]; + memcpy(realfbcolormaps.Maps, realcolormaps.Maps, 256*NUMCOLORMAPS*fakecmaps.Size()); } NormalLight.Color = PalEntry (255, 255, 255); NormalLight.Fade = 0; - NormalLight.Maps = realcolormaps; + NormalLight.Maps = realcolormaps.Maps; FullNormalLight.Color = PalEntry (255, 255, 255); FullNormalLight.Fade = 0; - FullNormalLight.Maps = realfbcolormaps; - NormalLightHasFixedLights = R_CheckForFixedLights(realcolormaps); + FullNormalLight.Maps = realfbcolormaps.Maps; + NormalLightHasFixedLights = R_CheckForFixedLights(realcolormaps.Maps); numfakecmaps = fakecmaps.Size(); // build default special maps (e.g. invulnerability) diff --git a/src/r_data/colormaps.h b/src/r_data/colormaps.h index 03f705b2b..675f669b9 100644 --- a/src/r_data/colormaps.h +++ b/src/r_data/colormaps.h @@ -1,18 +1,26 @@ #ifndef __RES_CMAP_H #define __RES_CMAP_H +struct FSWColormap; + void R_InitColormaps (); void R_DeinitColormaps (); DWORD R_ColormapNumForName(const char *name); // killough 4/4/98 void R_SetDefaultColormap (const char *name); // [RH] change normal fadetable DWORD R_BlendForColormap (DWORD map); // [RH] return calculated blend for a colormap -extern BYTE *realcolormaps; // [RH] make the colormaps externally visible +extern FSWColormap realcolormaps; // [RH] make the colormaps externally visible extern size_t numfakecmaps; +struct FSWColormap +{ + BYTE *Maps = nullptr; + PalEntry Color = 0xffffffff; + PalEntry Fade = 0xff000000; + int Desaturate = 0; +}; - -struct FDynamicColormap +struct FDynamicColormap : FSWColormap { void ChangeFade (PalEntry fadecolor); void ChangeColor (PalEntry lightcolor, int desaturate); @@ -21,10 +29,6 @@ struct FDynamicColormap void BuildLights (); static void RebuildAllLights(); - BYTE *Maps; - PalEntry Color; - PalEntry Fade; - int Desaturate; FDynamicColormap *Next; }; @@ -44,8 +48,13 @@ enum }; -struct FSpecialColormap +struct FSpecialColormap : FSWColormap { + FSpecialColormap() + { + Maps = Colormap; + } + float ColorizeStart[3]; float ColorizeEnd[3]; BYTE Colormap[256]; diff --git a/src/r_data/renderstyle.h b/src/r_data/renderstyle.h index 947f9afed..364cf579e 100644 --- a/src/r_data/renderstyle.h +++ b/src/r_data/renderstyle.h @@ -121,6 +121,9 @@ enum ERenderFlags // Actors only: Ignore sector fade and fade to black. To fade to white, // combine this with STYLEF_InvertOverlay. STYLEF_FadeToBlack = 64, + + // Force alpha. + STYLEF_ForceAlpha = 128, }; union FRenderStyle diff --git a/src/r_data/voxels.cpp b/src/r_data/voxels.cpp index d81220ea8..4b8081288 100644 --- a/src/r_data/voxels.cpp +++ b/src/r_data/voxels.cpp @@ -392,6 +392,52 @@ FVoxel::~FVoxel() if (Palette != NULL) delete [] Palette; } +//========================================================================== +// +// Create true color version of the slab data +// +//========================================================================== + +void FVoxel::CreateBgraSlabData() +{ + assert(Palette != NULL); + + for (int i = 0; i < NumMips; ++i) + { + int size = Mips[i].OffsetX[Mips[i].SizeX]; + if (size <= 0) continue; + + Mips[i].SlabDataBgra.Resize(size); + + kvxslab_t *src = (kvxslab_t*)Mips[i].SlabData; + kvxslab_bgra_t *dest = (kvxslab_bgra_t*)&Mips[i].SlabDataBgra[0]; + + while (size >= 3) + { + dest->backfacecull = src->backfacecull; + dest->ztop = src->ztop; + dest->zleng = src->zleng; + + int slabzleng = src->zleng; + for (int j = 0; j < slabzleng; ++j) + { + int colorIndex = src->col[j]; + + uint32_t red = (Palette[colorIndex * 3 + 0] << 2) | (Palette[colorIndex * 3 + 0] >> 4); + uint32_t green = (Palette[colorIndex * 3 + 1] << 2) | (Palette[colorIndex * 3 + 1] >> 4); + uint32_t blue = (Palette[colorIndex * 3 + 2] << 2) | (Palette[colorIndex * 3 + 2] >> 4); + + dest->col[j] = 0xff000000 | (red << 16) | (green << 8) | blue; + } + slabzleng += 3; + + dest = (kvxslab_bgra_t *)((uint32_t *)dest + slabzleng); + src = (kvxslab_t *)((BYTE *)src + slabzleng); + size -= slabzleng; + } + } +} + //========================================================================== // // Remap the voxel to the game palette diff --git a/src/r_data/voxels.h b/src/r_data/voxels.h index 85095c52f..df5839836 100644 --- a/src/r_data/voxels.h +++ b/src/r_data/voxels.h @@ -15,6 +15,14 @@ struct kvxslab_t BYTE col[1/*zleng*/];// color data from top to bottom }; +struct kvxslab_bgra_t +{ + uint32_t ztop; // starting z coordinate of top of slab + uint32_t zleng; // # of bytes in the color array - slab height + uint32_t backfacecull; // low 6 bits tell which of 6 faces are exposed + uint32_t col[1/*zleng*/];// color data from top to bottom +}; + struct FVoxelMipLevel { FVoxelMipLevel(); @@ -27,6 +35,7 @@ struct FVoxelMipLevel int *OffsetX; short *OffsetXY; BYTE *SlabData; + TArray SlabDataBgra; }; struct FVoxel @@ -39,6 +48,7 @@ struct FVoxel FVoxel(); ~FVoxel(); + void CreateBgraSlabData(); void Remap(); void RemovePalette(); }; diff --git a/src/r_defs.h b/src/r_defs.h index f0d72e9f9..47e3ff3eb 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -60,7 +60,6 @@ enum SIL_BOTH }; -namespace swrenderer { extern size_t MaxDrawSegs; } struct FDisplacement; // diff --git a/src/r_draw.cpp b/src/r_draw.cpp deleted file mode 100644 index c58175912..000000000 --- a/src/r_draw.cpp +++ /dev/null @@ -1,2489 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// The actual span/column drawing functions. -// Here find the main potential for optimization, -// e.g. inline assembly, different algorithms. -// -//----------------------------------------------------------------------------- - -#include - -#include "templates.h" -#include "doomdef.h" -#include "i_system.h" -#include "w_wad.h" -#include "r_local.h" -#include "v_video.h" -#include "doomstat.h" -#include "st_stuff.h" -#include "g_game.h" -#include "g_level.h" -#include "r_data/r_translate.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - -#include "gi.h" -#include "stats.h" -#include "x86.h" - -#undef RANGECHECK - -// status bar height at bottom of screen -// [RH] status bar position at bottom of screen -extern int ST_Y; - -// -// All drawing to the view buffer is accomplished in this file. -// The other refresh files only know about ccordinates, -// not the architecture of the frame buffer. -// Conveniently, the frame buffer is a linear one, -// and we need only the base address, -// and the total size == width*height*depth/8., -// - -BYTE* viewimage; -extern "C" { -int halfviewwidth; -int ylookup[MAXHEIGHT]; -BYTE *dc_destorg; -} -int scaledviewwidth; - -// [RH] Pointers to the different column drawers. -// These get changed depending on the current -// screen depth and asm/no asm. -void (*R_DrawColumnHoriz)(void); -void (*R_DrawColumn)(void); -void (*R_DrawFuzzColumn)(void); -void (*R_DrawTranslatedColumn)(void); -void (*R_DrawShadedColumn)(void); -void (*R_DrawSpan)(void); -void (*R_DrawSpanMasked)(void); -void (*R_DrawSpanTranslucent)(void); -void (*R_DrawSpanMaskedTranslucent)(void); -void (*R_DrawSpanAddClamp)(void); -void (*R_DrawSpanMaskedAddClamp)(void); -void (*rt_map4cols)(int,int,int); - -// -// R_DrawColumn -// Source is the top of the column to scale. -// -double dc_texturemid; -extern "C" { -int dc_pitch=0xABadCafe; // [RH] Distance between rows - -lighttable_t* dc_colormap; -int dc_x; -int dc_yl; -int dc_yh; -fixed_t dc_iscale; -fixed_t dc_texturefrac; -int dc_color; // [RH] Color for column filler -DWORD dc_srccolor; -DWORD *dc_srcblend; // [RH] Source and destination -DWORD *dc_destblend; // blending lookups - -// first pixel in a column (possibly virtual) -const BYTE* dc_source; - -BYTE* dc_dest; -int dc_count; - -DWORD vplce[4]; -DWORD vince[4]; -BYTE* palookupoffse[4]; -const BYTE* bufplce[4]; - -// just for profiling -int dccount; -} - -int dc_fillcolor; -BYTE *dc_translation; -BYTE shadetables[NUMCOLORMAPS*16*256]; -FDynamicColormap ShadeFakeColormap[16]; -BYTE identitymap[256]; - -EXTERN_CVAR (Int, r_columnmethod) - - -void R_InitShadeMaps() -{ - int i,j; - // set up shading tables for shaded columns - // 16 colormap sets, progressing from full alpha to minimum visible alpha - - BYTE *table = shadetables; - - // Full alpha - for (i = 0; i < 16; ++i) - { - ShadeFakeColormap[i].Color = ~0u; - ShadeFakeColormap[i].Desaturate = ~0u; - ShadeFakeColormap[i].Next = NULL; - ShadeFakeColormap[i].Maps = table; - - for (j = 0; j < NUMCOLORMAPS; ++j) - { - int a = (NUMCOLORMAPS - j) * 256 / NUMCOLORMAPS * (16-i); - for (int k = 0; k < 256; ++k) - { - BYTE v = (((k+2) * a) + 256) >> 14; - table[k] = MIN (v, 64); - } - table += 256; - } - } - for (i = 0; i < NUMCOLORMAPS*16*256; ++i) - { - assert(shadetables[i] <= 64); - } - - // Set up a guaranteed identity map - for (i = 0; i < 256; ++i) - { - identitymap[i] = i; - } -} - -/************************************/ -/* */ -/* Palettized drawers (C versions) */ -/* */ -/************************************/ - -#ifndef X86_ASM -// -// A column is a vertical slice/span from a wall texture that, -// given the DOOM style restrictions on the view orientation, -// will always have constant z depth. -// Thus a special case loop for very fast rendering can -// be used. It has also been used with Wolfenstein 3D. -// -void R_DrawColumnP_C (void) -{ - int count; - BYTE* dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - - // Zero length, column does not exceed a pixel. - if (count <= 0) - return; - - // Framebuffer destination address. - dest = dc_dest; - - // Determine scaling, - // which is the only mapping to be done. - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - // [RH] Get local copies of these variables so that the compiler - // has a better chance of optimizing this well. - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - - // Inner loop that does the actual texture mapping, - // e.g. a DDA-lile scaling. - // This is as fast as it gets. - do - { - // Re-map color indices from wall texture column - // using a lighting/special effects LUT. - *dest = colormap[source[frac>>FRACBITS]]; - - dest += pitch; - frac += fracstep; - - } while (--count); - } -} -#endif - -// [RH] Just fills a column with a color -void R_FillColumnP (void) -{ - int count; - BYTE* dest; - - count = dc_count; - - if (count <= 0) - return; - - dest = dc_dest; - - { - int pitch = dc_pitch; - BYTE color = dc_color; - - do - { - *dest = color; - dest += pitch; - } while (--count); - } -} - -void R_FillAddColumn (void) -{ - int count; - BYTE *dest; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - DWORD *bg2rgb; - DWORD fg; - - bg2rgb = dc_destblend; - fg = dc_srccolor; - int pitch = dc_pitch; - - do - { - DWORD bg; - bg = (fg + bg2rgb[*dest]) | 0x1f07c1f; - *dest = RGB32k.All[bg & (bg>>15)]; - dest += pitch; - } while (--count); - -} - -void R_FillAddClampColumn (void) -{ - int count; - BYTE *dest; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - DWORD *bg2rgb; - DWORD fg; - - bg2rgb = dc_destblend; - fg = dc_srccolor; - int pitch = dc_pitch; - - do - { - DWORD a = fg + bg2rgb[*dest]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a>>15)]; - dest += pitch; - } while (--count); - -} - -void R_FillSubClampColumn (void) -{ - int count; - BYTE *dest; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - DWORD *bg2rgb; - DWORD fg; - - bg2rgb = dc_destblend; - fg = dc_srccolor | 0x40100400; - int pitch = dc_pitch; - - do - { - DWORD a = fg - bg2rgb[*dest]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a>>15)]; - dest += pitch; - } while (--count); - -} - -void R_FillRevSubClampColumn (void) -{ - int count; - BYTE *dest; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - DWORD *bg2rgb; - DWORD fg; - - bg2rgb = dc_destblend; - fg = dc_srccolor; - int pitch = dc_pitch; - - do - { - DWORD a = (bg2rgb[*dest] | 0x40100400) - fg; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a>>15)]; - dest += pitch; - } while (--count); - -} - -// -// Spectre/Invisibility. -// -#define FUZZTABLE 50 - -extern "C" -{ -int fuzzoffset[FUZZTABLE+1]; // [RH] +1 for the assembly routine -int fuzzpos = 0; -int fuzzviewheight; -} -/* - FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, - FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, - FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, - FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, - FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, - FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, - FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF -*/ - -static const signed char fuzzinit[FUZZTABLE] = { - 1,-1, 1,-1, 1, 1,-1, - 1, 1,-1, 1, 1, 1,-1, - 1, 1, 1,-1,-1,-1,-1, - 1,-1,-1, 1, 1, 1, 1,-1, - 1,-1, 1, 1,-1,-1, 1, - 1,-1,-1,-1,-1, 1, 1, - 1, 1,-1, 1, 1,-1, 1 -}; - -void R_InitFuzzTable (int fuzzoff) -{ - int i; - - for (i = 0; i < FUZZTABLE; i++) - { - fuzzoffset[i] = fuzzinit[i] * fuzzoff; - } -} - -#ifndef X86_ASM -// -// Creates a fuzzy image by copying pixels from adjacent ones above and below. -// Used with an all black colormap, this could create the SHADOW effect, -// i.e. spectres and invisible players. -// -void R_DrawFuzzColumnP_C (void) -{ - int count; - BYTE *dest; - - // Adjust borders. Low... - if (dc_yl == 0) - dc_yl = 1; - - // .. and high. - if (dc_yh > fuzzviewheight) - dc_yh = fuzzviewheight; - - count = dc_yh - dc_yl; - - // Zero length. - if (count < 0) - return; - - count++; - - dest = ylookup[dc_yl] + dc_x + dc_destorg; - - // colormap #6 is used for shading (of 0-31, a bit brighter than average) - { - // [RH] Make local copies of global vars to try and improve - // the optimizations made by the compiler. - int pitch = dc_pitch; - int fuzz = fuzzpos; - int cnt; - BYTE *map = &NormalLight.Maps[6*256]; - - // [RH] Split this into three separate loops to minimize - // the number of times fuzzpos needs to be clamped. - if (fuzz) - { - cnt = MIN(FUZZTABLE-fuzz,count); - count -= cnt; - do - { - *dest = map[dest[fuzzoffset[fuzz++]]]; - dest += pitch; - } while (--cnt); - } - if (fuzz == FUZZTABLE || count > 0) - { - while (count >= FUZZTABLE) - { - fuzz = 0; - cnt = FUZZTABLE; - count -= FUZZTABLE; - do - { - *dest = map[dest[fuzzoffset[fuzz++]]]; - dest += pitch; - } while (--cnt); - } - fuzz = 0; - if (count > 0) - { - do - { - *dest = map[dest[fuzzoffset[fuzz++]]]; - dest += pitch; - } while (--count); - } - } - fuzzpos = fuzz; - } -} -#endif - -// -// R_DrawTranlucentColumn -// - -/* -[RH] This translucency algorithm is based on DOSDoom 0.65's, but uses -a 32k RGB table instead of an 8k one. At least on my machine, it's -slightly faster (probably because it uses only one shift instead of -two), and it looks considerably less green at the ends of the -translucency range. The extra size doesn't appear to be an issue. - -The following note is from DOSDoom 0.65: - -New translucency algorithm, by Erik Sandberg: - -Basically, we compute the red, green and blue values for each pixel, and -then use a RGB table to check which one of the palette colours that best -represents those RGB values. The RGB table is 8k big, with 4 R-bits, -5 G-bits and 4 B-bits. A 4k table gives a bit too bad precision, and a 32k -table takes up more memory and results in more cache misses, so an 8k -table seemed to be quite ultimate. - -The computation of the RGB for each pixel is accelerated by using two -1k tables for each translucency level. -The xth element of one of these tables contains the r, g and b values for -the colour x, weighted for the current translucency level (for example, -the weighted rgb values for background colour at 75% translucency are 1/4 -of the original rgb values). The rgb values are stored as three -low-precision fixed point values, packed into one long per colour: -Bit 0-4: Frac part of blue (5 bits) -Bit 5-8: Int part of blue (4 bits) -Bit 9-13: Frac part of red (5 bits) -Bit 14-17: Int part of red (4 bits) -Bit 18-22: Frac part of green (5 bits) -Bit 23-27: Int part of green (5 bits) -Bit 28-31: All zeros (4 bits) - -The point of this format is that the two colours now can be added, and -then be converted to a RGB table index very easily: First, we just set -all the frac bits and the four upper zero bits to 1. It's now possible -to get the RGB table index by anding the current value >> 5 with the -current value >> 19. When asm-optimised, this should be the fastest -algorithm that uses RGB tables. - -*/ - -void R_DrawAddColumnP_C (void) -{ - int count; - BYTE *dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - - do - { - DWORD fg = colormap[source[frac>>FRACBITS]]; - DWORD bg = *dest; - - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg>>15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } -} - -// -// R_DrawTranslatedColumn -// Used to draw player sprites with the green colorramp mapped to others. -// Could be used with different translation tables, e.g. the lighter colored -// version of the BaronOfHell, the HellKnight, uses identical sprites, kinda -// brightened up. -// - -void R_DrawTranslatedColumnP_C (void) -{ - int count; - BYTE* dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - // [RH] Local copies of global vars to improve compiler optimizations - BYTE *colormap = dc_colormap; - BYTE *translation = dc_translation; - const BYTE *source = dc_source; - int pitch = dc_pitch; - - do - { - *dest = colormap[translation[source[frac>>FRACBITS]]]; - dest += pitch; - - frac += fracstep; - } while (--count); - } -} - -// Draw a column that is both translated and translucent -void R_DrawTlatedAddColumnP_C (void) -{ - int count; - BYTE *dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - BYTE *translation = dc_translation; - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - - do - { - DWORD fg = colormap[translation[source[frac>>FRACBITS]]]; - DWORD bg = *dest; - - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg>>15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } -} - -// Draw a column whose "color" values are actually translucency -// levels for a base color stored in dc_color. -void R_DrawShadedColumnP_C (void) -{ - int count; - BYTE *dest; - fixed_t frac, fracstep; - - count = dc_count; - - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - const BYTE *source = dc_source; - BYTE *colormap = dc_colormap; - int pitch = dc_pitch; - DWORD *fgstart = &Col2RGB8[0][dc_color]; - - do - { - DWORD val = colormap[source[frac>>FRACBITS]]; - DWORD fg = fgstart[val<<8]; - val = (Col2RGB8[64-val][*dest] + fg) | 0x1f07c1f; - *dest = RGB32k.All[val & (val>>15)]; - - dest += pitch; - frac += fracstep; - } while (--count); - } -} - -// Add source to destination, clamping it to white -void R_DrawAddClampColumnP_C () -{ - int count; - BYTE *dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - DWORD a = fg2rgb[colormap[source[frac>>FRACBITS]]] + bg2rgb[*dest]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a>>15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } -} - -// Add translated source to destination, clamping it to white -void R_DrawAddClampTranslatedColumnP_C () -{ - int count; - BYTE *dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - BYTE *translation = dc_translation; - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - DWORD a = fg2rgb[colormap[translation[source[frac>>FRACBITS]]]] + bg2rgb[*dest]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[(a>>15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } -} - -// Subtract destination from source, clamping it to black -void R_DrawSubClampColumnP_C () -{ - int count; - BYTE *dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - DWORD a = (fg2rgb[colormap[source[frac>>FRACBITS]]] | 0x40100400) - bg2rgb[*dest]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a>>15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } -} - -// Subtract destination from source, clamping it to black -void R_DrawSubClampTranslatedColumnP_C () -{ - int count; - BYTE *dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - BYTE *translation = dc_translation; - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - DWORD a = (fg2rgb[colormap[translation[source[frac>>FRACBITS]]]] | 0x40100400) - bg2rgb[*dest]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a>>15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } -} - -// Subtract source from destination, clamping it to black -void R_DrawRevSubClampColumnP_C () -{ - int count; - BYTE *dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - DWORD a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[source[frac>>FRACBITS]]]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a>>15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } -} - -// Subtract source from destination, clamping it to black -void R_DrawRevSubClampTranslatedColumnP_C () -{ - int count; - BYTE *dest; - fixed_t frac; - fixed_t fracstep; - - count = dc_count; - if (count <= 0) - return; - - dest = dc_dest; - - fracstep = dc_iscale; - frac = dc_texturefrac; - - { - BYTE *translation = dc_translation; - BYTE *colormap = dc_colormap; - const BYTE *source = dc_source; - int pitch = dc_pitch; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - DWORD a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[translation[source[frac>>FRACBITS]]]]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a>>15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } -} - - - -// -// R_DrawSpan -// With DOOM style restrictions on view orientation, -// the floors and ceilings consist of horizontal slices -// or spans with constant z depth. -// However, rotation around the world z axis is possible, -// thus this mapping, while simpler and faster than -// perspective correct texture mapping, has to traverse -// the texture at an angle in all but a few cases. -// In consequence, flats are not stored by column (like walls), -// and the inner loop has to step in texture space u and v. -// -// [RH] I'm not sure who wrote this, but floor/ceiling mapping -// *is* perspective correct for spans of constant z depth, which -// Doom guarantees because it does not let you change your pitch. -// Also, because of the new texture system, flats *are* stored by -// column to make it easy to use them on walls too. To accomodate -// this, the use of x/u and y/v in R_DrawSpan just needs to be -// swapped. -// -extern "C" { -int ds_color; // [RH] color for non-textured spans - -int ds_y; -int ds_x1; -int ds_x2; - -lighttable_t* ds_colormap; - -dsfixed_t ds_xfrac; -dsfixed_t ds_yfrac; -dsfixed_t ds_xstep; -dsfixed_t ds_ystep; -int ds_xbits; -int ds_ybits; - -// start of a floor/ceiling tile image -const BYTE* ds_source; - -// just for profiling -int dscount; - -#ifdef X86_ASM -extern "C" void R_SetSpanSource_ASM (const BYTE *flat); -extern "C" void R_SetSpanSize_ASM (int xbits, int ybits); -extern "C" void R_SetSpanColormap_ASM (BYTE *colormap); -extern "C" BYTE *ds_curcolormap, *ds_cursource, *ds_curtiltedsource; -#endif -} - -//========================================================================== -// -// R_SetSpanSource -// -// Sets the source bitmap for the span drawing routines. -// -//========================================================================== - -void R_SetSpanSource(const BYTE *pixels) -{ - ds_source = pixels; -#ifdef X86_ASM - if (ds_cursource != ds_source) - { - R_SetSpanSource_ASM(pixels); - } -#endif -} - -//========================================================================== -// -// R_SetSpanColormap -// -// Sets the colormap for the span drawing routines. -// -//========================================================================== - -void R_SetSpanColormap(BYTE *colormap) -{ - ds_colormap = colormap; -#ifdef X86_ASM - if (ds_colormap != ds_curcolormap) - { - R_SetSpanColormap_ASM (ds_colormap); - } -#endif -} - -//========================================================================== -// -// R_SetupSpanBits -// -// Sets the texture size for the span drawing routines. -// -//========================================================================== - -void R_SetupSpanBits(FTexture *tex) -{ - tex->GetWidth (); - ds_xbits = tex->WidthBits; - ds_ybits = tex->HeightBits; - if ((1 << ds_xbits) > tex->GetWidth()) - { - ds_xbits--; - } - if ((1 << ds_ybits) > tex->GetHeight()) - { - ds_ybits--; - } -#ifdef X86_ASM - R_SetSpanSize_ASM (ds_xbits, ds_ybits); -#endif -} - -// -// Draws the actual span. -#ifndef X86_ASM -void R_DrawSpanP_C (void) -{ - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - BYTE* dest; - const BYTE* source = ds_source; - const BYTE* colormap = ds_colormap; - int count; - int spot; - -#ifdef RANGECHECK - if (ds_x2 < ds_x1 || ds_x1 < 0 - || ds_x2 >= screen->width || ds_y > screen->height) - { - I_Error ("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y); - } -// dscount++; -#endif - - xfrac = ds_xfrac; - yfrac = ds_yfrac; - - dest = ylookup[ds_y] + ds_x1 + dc_destorg; - - count = ds_x2 - ds_x1 + 1; - - xstep = ds_xstep; - ystep = ds_ystep; - - if (ds_xbits == 6 && ds_ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - // Current texture index in u,v. - spot = ((xfrac>>(32-6-6))&(63*64)) + (yfrac>>(32-6)); - - // Lookup pixel from flat texture tile, - // re-index using light/colormap. - *dest++ = colormap[source[spot]]; - - // Next step in u,v. - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - BYTE yshift = 32 - ds_ybits; - BYTE xshift = yshift - ds_xbits; - int xmask = ((1 << ds_xbits) - 1) << ds_ybits; - - do - { - // Current texture index in u,v. - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - - // Lookup pixel from flat texture tile, - // re-index using light/colormap. - *dest++ = colormap[source[spot]]; - - // Next step in u,v. - xfrac += xstep; - yfrac += ystep; - } while (--count); - } -} - -// [RH] Draw a span with holes -void R_DrawSpanMaskedP_C (void) -{ - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - BYTE* dest; - const BYTE* source = ds_source; - const BYTE* colormap = ds_colormap; - int count; - int spot; - - xfrac = ds_xfrac; - yfrac = ds_yfrac; - - dest = ylookup[ds_y] + ds_x1 + dc_destorg; - - count = ds_x2 - ds_x1 + 1; - - xstep = ds_xstep; - ystep = ds_ystep; - - if (ds_xbits == 6 && ds_ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - BYTE texdata; - - spot = ((xfrac>>(32-6-6))&(63*64)) + (yfrac>>(32-6)); - texdata = source[spot]; - if (texdata != 0) - { - *dest = colormap[texdata]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - BYTE yshift = 32 - ds_ybits; - BYTE xshift = yshift - ds_xbits; - int xmask = ((1 << ds_xbits) - 1) << ds_ybits; - do - { - BYTE texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - *dest = colormap[texdata]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } -} -#endif - -void R_DrawSpanTranslucentP_C (void) -{ - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - BYTE* dest; - const BYTE* source = ds_source; - const BYTE* colormap = ds_colormap; - int count; - int spot; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - xfrac = ds_xfrac; - yfrac = ds_yfrac; - - dest = ylookup[ds_y] + ds_x1 + dc_destorg; - - count = ds_x2 - ds_x1 + 1; - - xstep = ds_xstep; - ystep = ds_ystep; - - if (ds_xbits == 6 && ds_ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - spot = ((xfrac>>(32-6-6))&(63*64)) + (yfrac>>(32-6)); - DWORD fg = colormap[source[spot]]; - DWORD bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - *dest++ = RGB32k.All[fg & (fg>>15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - BYTE yshift = 32 - ds_ybits; - BYTE xshift = yshift - ds_xbits; - int xmask = ((1 << ds_xbits) - 1) << ds_ybits; - do - { - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - DWORD fg = colormap[source[spot]]; - DWORD bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - *dest++ = RGB32k.All[fg & (fg>>15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } -} - -void R_DrawSpanMaskedTranslucentP_C (void) -{ - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - BYTE* dest; - const BYTE* source = ds_source; - const BYTE* colormap = ds_colormap; - int count; - int spot; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - xfrac = ds_xfrac; - yfrac = ds_yfrac; - - dest = ylookup[ds_y] + ds_x1 + dc_destorg; - - count = ds_x2 - ds_x1 + 1; - - xstep = ds_xstep; - ystep = ds_ystep; - - if (ds_xbits == 6 && ds_ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - BYTE texdata; - - spot = ((xfrac>>(32-6-6))&(63*64)) + (yfrac>>(32-6)); - texdata = source[spot]; - if (texdata != 0) - { - DWORD fg = colormap[texdata]; - DWORD bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg>>15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - BYTE yshift = 32 - ds_ybits; - BYTE xshift = yshift - ds_xbits; - int xmask = ((1 << ds_xbits) - 1) << ds_ybits; - do - { - BYTE texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - DWORD fg = colormap[texdata]; - DWORD bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg>>15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } -} - -void R_DrawSpanAddClampP_C (void) -{ - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - BYTE* dest; - const BYTE* source = ds_source; - const BYTE* colormap = ds_colormap; - int count; - int spot; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - xfrac = ds_xfrac; - yfrac = ds_yfrac; - - dest = ylookup[ds_y] + ds_x1 + dc_destorg; - - count = ds_x2 - ds_x1 + 1; - - xstep = ds_xstep; - ystep = ds_ystep; - - if (ds_xbits == 6 && ds_ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - spot = ((xfrac>>(32-6-6))&(63*64)) + (yfrac>>(32-6)); - DWORD a = fg2rgb[colormap[source[spot]]] + bg2rgb[*dest]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest++ = RGB32k.All[a & (a>>15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - BYTE yshift = 32 - ds_ybits; - BYTE xshift = yshift - ds_xbits; - int xmask = ((1 << ds_xbits) - 1) << ds_ybits; - do - { - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - DWORD a = fg2rgb[colormap[source[spot]]] + bg2rgb[*dest]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest++ = RGB32k.All[a & (a>>15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } -} - -void R_DrawSpanMaskedAddClampP_C (void) -{ - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - BYTE* dest; - const BYTE* source = ds_source; - const BYTE* colormap = ds_colormap; - int count; - int spot; - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - xfrac = ds_xfrac; - yfrac = ds_yfrac; - - dest = ylookup[ds_y] + ds_x1 + dc_destorg; - - count = ds_x2 - ds_x1 + 1; - - xstep = ds_xstep; - ystep = ds_ystep; - - if (ds_xbits == 6 && ds_ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - BYTE texdata; - - spot = ((xfrac>>(32-6-6))&(63*64)) + (yfrac>>(32-6)); - texdata = source[spot]; - if (texdata != 0) - { - DWORD a = fg2rgb[colormap[texdata]] + bg2rgb[*dest]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a>>15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - BYTE yshift = 32 - ds_ybits; - BYTE xshift = yshift - ds_xbits; - int xmask = ((1 << ds_xbits) - 1) << ds_ybits; - do - { - BYTE texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - DWORD a = fg2rgb[colormap[texdata]] + bg2rgb[*dest]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a>>15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } -} - -// [RH] Just fill a span with a color -void R_FillSpan (void) -{ - memset (ylookup[ds_y] + ds_x1 + dc_destorg, ds_color, ds_x2 - ds_x1 + 1); -} - -// Draw a voxel slab -// -// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman -// Ken Silverman's official web site: "http://www.advsys.net/ken" -// See the included license file "BUILDLIC.TXT" for license info. - -// Actually, this is just R_DrawColumn with an extra width parameter. - -#ifndef X86_ASM -static const BYTE *slabcolormap; - -extern "C" void R_SetupDrawSlabC(const BYTE *colormap) -{ - slabcolormap = colormap; -} - -extern "C" void R_DrawSlabC(int dx, fixed_t v, int dy, fixed_t vi, const BYTE *vptr, BYTE *p) -{ - int x; - const BYTE *colormap = slabcolormap; - int pitch = dc_pitch; - - assert(dx > 0); - - if (dx == 1) - { - while (dy > 0) - { - *p = colormap[vptr[v >> FRACBITS]]; - p += pitch; - v += vi; - dy--; - } - } - else if (dx == 2) - { - while (dy > 0) - { - BYTE color = colormap[vptr[v >> FRACBITS]]; - p[0] = color; - p[1] = color; - p += pitch; - v += vi; - dy--; - } - } - else if (dx == 3) - { - while (dy > 0) - { - BYTE color = colormap[vptr[v >> FRACBITS]]; - p[0] = color; - p[1] = color; - p[2] = color; - p += pitch; - v += vi; - dy--; - } - } - else if (dx == 4) - { - while (dy > 0) - { - BYTE color = colormap[vptr[v >> FRACBITS]]; - p[0] = color; - p[1] = color; - p[2] = color; - p[3] = color; - p += pitch; - v += vi; - dy--; - } - } - else while (dy > 0) - { - BYTE color = colormap[vptr[v >> FRACBITS]]; - // The optimizer will probably turn this into a memset call. - // Since dx is not likely to be large, I'm not sure that's a good thing, - // hence the alternatives above. - for (x = 0; x < dx; x++) - { - p[x] = color; - } - p += pitch; - v += vi; - dy--; - } -} -#endif - - -/****************************************************/ -/****************************************************/ - -// wallscan stuff, in C - -#ifndef X86_ASM -static DWORD vlinec1 (); -static int vlinebits; - -DWORD (*dovline1)() = vlinec1; -DWORD (*doprevline1)() = vlinec1; - -#ifdef X64_ASM -extern "C" void vlinetallasm4(); -#define dovline4 vlinetallasm4 -extern "C" void setupvlinetallasm (int); -#else -static void vlinec4 (); -void (*dovline4)() = vlinec4; -#endif - -static DWORD mvlinec1(); -static void mvlinec4(); -static int mvlinebits; - -DWORD (*domvline1)() = mvlinec1; -void (*domvline4)() = mvlinec4; - -#else - -extern "C" -{ -DWORD vlineasm1 (); -DWORD prevlineasm1 (); -DWORD vlinetallasm1 (); -DWORD prevlinetallasm1 (); -void vlineasm4 (); -void vlinetallasmathlon4 (); -void vlinetallasm4 (); -void setupvlineasm (int); -void setupvlinetallasm (int); - -DWORD mvlineasm1(); -void mvlineasm4(); -void setupmvlineasm (int); -} - -DWORD (*dovline1)() = vlinetallasm1; -DWORD (*doprevline1)() = prevlinetallasm1; -void (*dovline4)() = vlinetallasm4; - -DWORD (*domvline1)() = mvlineasm1; -void (*domvline4)() = mvlineasm4; -#endif - -void setupvline (int fracbits) -{ -#ifdef X86_ASM - if (CPU.Family <= 5) - { - if (fracbits >= 24) - { - setupvlineasm (fracbits); - dovline4 = vlineasm4; - dovline1 = vlineasm1; - doprevline1 = prevlineasm1; - } - else - { - setupvlinetallasm (fracbits); - dovline1 = vlinetallasm1; - doprevline1 = prevlinetallasm1; - dovline4 = vlinetallasm4; - } - } - else - { - setupvlinetallasm (fracbits); - if (CPU.bIsAMD && CPU.AMDFamily >= 7) - { - dovline4 = vlinetallasmathlon4; - } - } -#else - vlinebits = fracbits; -#ifdef X64_ASM - setupvlinetallasm(fracbits); -#endif -#endif -} - -#if !defined(X86_ASM) -DWORD vlinec1 () -{ - DWORD fracstep = dc_iscale; - DWORD frac = dc_texturefrac; - BYTE *colormap = dc_colormap; - int count = dc_count; - const BYTE *source = dc_source; - BYTE *dest = dc_dest; - int bits = vlinebits; - int pitch = dc_pitch; - - do - { - *dest = colormap[source[frac>>bits]]; - frac += fracstep; - dest += pitch; - } while (--count); - - return frac; -} - -void vlinec4 () -{ - BYTE *dest = dc_dest; - int count = dc_count; - int bits = vlinebits; - DWORD place; - - do - { - dest[0] = palookupoffse[0][bufplce[0][(place=vplce[0])>>bits]]; vplce[0] = place+vince[0]; - dest[1] = palookupoffse[1][bufplce[1][(place=vplce[1])>>bits]]; vplce[1] = place+vince[1]; - dest[2] = palookupoffse[2][bufplce[2][(place=vplce[2])>>bits]]; vplce[2] = place+vince[2]; - dest[3] = palookupoffse[3][bufplce[3][(place=vplce[3])>>bits]]; vplce[3] = place+vince[3]; - dest += dc_pitch; - } while (--count); -} -#endif - -void setupmvline (int fracbits) -{ -#if defined(X86_ASM) - setupmvlineasm (fracbits); - domvline1 = mvlineasm1; - domvline4 = mvlineasm4; -#else - mvlinebits = fracbits; -#endif -} - -#if !defined(X86_ASM) -DWORD mvlinec1 () -{ - DWORD fracstep = dc_iscale; - DWORD frac = dc_texturefrac; - BYTE *colormap = dc_colormap; - int count = dc_count; - const BYTE *source = dc_source; - BYTE *dest = dc_dest; - int bits = mvlinebits; - int pitch = dc_pitch; - - do - { - BYTE pix = source[frac>>bits]; - if (pix != 0) - { - *dest = colormap[pix]; - } - frac += fracstep; - dest += pitch; - } while (--count); - - return frac; -} - -void mvlinec4 () -{ - BYTE *dest = dc_dest; - int count = dc_count; - int bits = mvlinebits; - DWORD place; - - do - { - BYTE pix; - - pix = bufplce[0][(place=vplce[0])>>bits]; if(pix) dest[0] = palookupoffse[0][pix]; vplce[0] = place+vince[0]; - pix = bufplce[1][(place=vplce[1])>>bits]; if(pix) dest[1] = palookupoffse[1][pix]; vplce[1] = place+vince[1]; - pix = bufplce[2][(place=vplce[2])>>bits]; if(pix) dest[2] = palookupoffse[2][pix]; vplce[2] = place+vince[2]; - pix = bufplce[3][(place=vplce[3])>>bits]; if(pix) dest[3] = palookupoffse[3][pix]; vplce[3] = place+vince[3]; - dest += dc_pitch; - } while (--count); -} -#endif - -extern "C" short spanend[MAXHEIGHT]; -extern float rw_light; -extern float rw_lightstep; -extern int wallshade; - -static void R_DrawFogBoundarySection (int y, int y2, int x1) -{ - BYTE *colormap = dc_colormap; - BYTE *dest = ylookup[y] + dc_destorg; - - for (; y < y2; ++y) - { - int x2 = spanend[y]; - int x = x1; - do - { - dest[x] = colormap[dest[x]]; - } while (++x <= x2); - dest += dc_pitch; - } -} - -static void R_DrawFogBoundaryLine (int y, int x) -{ - int x2 = spanend[y]; - BYTE *colormap = dc_colormap; - BYTE *dest = ylookup[y] + dc_destorg; - do - { - dest[x] = colormap[dest[x]]; - } while (++x <= x2); -} - -void R_DrawFogBoundary (int x1, int x2, short *uclip, short *dclip) -{ - // This is essentially the same as R_MapVisPlane but with an extra step - // to create new horizontal spans whenever the light changes enough that - // we need to use a new colormap. - - double lightstep = rw_lightstep; - double light = rw_light + rw_lightstep*(x2-x1-1); - int x = x2-1; - int t2 = uclip[x]; - int b2 = dclip[x]; - int rcolormap = GETPALOOKUP(light, wallshade); - int lcolormap; - BYTE *basecolormapdata = basecolormap->Maps; - - if (b2 > t2) - { - clearbufshort (spanend+t2, b2-t2, x); - } - - dc_colormap = basecolormapdata + (rcolormap << COLORMAPSHIFT); - - for (--x; x >= x1; --x) - { - int t1 = uclip[x]; - int b1 = dclip[x]; - const int xr = x+1; - int stop; - - light -= rw_lightstep; - lcolormap = GETPALOOKUP(light, wallshade); - if (lcolormap != rcolormap) - { - if (t2 < b2 && rcolormap != 0) - { // Colormap 0 is always the identity map, so rendering it is - // just a waste of time. - R_DrawFogBoundarySection (t2, b2, xr); - } - if (t1 < t2) t2 = t1; - if (b1 > b2) b2 = b1; - if (t2 < b2) - { - clearbufshort (spanend+t2, b2-t2, x); - } - rcolormap = lcolormap; - dc_colormap = basecolormapdata + (lcolormap << COLORMAPSHIFT); - } - else - { - if (dc_colormap != basecolormapdata) - { - stop = MIN (t1, b2); - while (t2 < stop) - { - R_DrawFogBoundaryLine (t2++, xr); - } - stop = MAX (b1, t2); - while (b2 > stop) - { - R_DrawFogBoundaryLine (--b2, xr); - } - } - else - { - t2 = MAX (t2, MIN (t1, b2)); - b2 = MIN (b2, MAX (b1, t2)); - } - - stop = MIN (t2, b1); - while (t1 < stop) - { - spanend[t1++] = x; - } - stop = MAX (b2, t2); - while (b1 > stop) - { - spanend[--b1] = x; - } - } - - t2 = uclip[x]; - b2 = dclip[x]; - } - if (t2 < b2 && rcolormap != 0) - { - R_DrawFogBoundarySection (t2, b2, x1); - } -} - -int tmvlinebits; - -void setuptmvline (int bits) -{ - tmvlinebits = bits; -} - -fixed_t tmvline1_add () -{ - DWORD fracstep = dc_iscale; - DWORD frac = dc_texturefrac; - BYTE *colormap = dc_colormap; - int count = dc_count; - const BYTE *source = dc_source; - BYTE *dest = dc_dest; - int bits = tmvlinebits; - int pitch = dc_pitch; - - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - BYTE pix = source[frac>>bits]; - if (pix != 0) - { - DWORD fg = fg2rgb[colormap[pix]]; - DWORD bg = bg2rgb[*dest]; - fg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg>>15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - - return frac; -} - -void tmvline4_add () -{ - BYTE *dest = dc_dest; - int count = dc_count; - int bits = tmvlinebits; - - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - for (int i = 0; i < 4; ++i) - { - BYTE pix = bufplce[i][vplce[i] >> bits]; - if (pix != 0) - { - DWORD fg = fg2rgb[palookupoffse[i][pix]]; - DWORD bg = bg2rgb[dest[i]]; - fg = (fg+bg) | 0x1f07c1f; - dest[i] = RGB32k.All[fg & (fg>>15)]; - } - vplce[i] += vince[i]; - } - dest += dc_pitch; - } while (--count); -} - -fixed_t tmvline1_addclamp () -{ - DWORD fracstep = dc_iscale; - DWORD frac = dc_texturefrac; - BYTE *colormap = dc_colormap; - int count = dc_count; - const BYTE *source = dc_source; - BYTE *dest = dc_dest; - int bits = tmvlinebits; - int pitch = dc_pitch; - - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - BYTE pix = source[frac>>bits]; - if (pix != 0) - { - DWORD a = fg2rgb[colormap[pix]] + bg2rgb[*dest]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a>>15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - - return frac; -} - -void tmvline4_addclamp () -{ - BYTE *dest = dc_dest; - int count = dc_count; - int bits = tmvlinebits; - - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - for (int i = 0; i < 4; ++i) - { - BYTE pix = bufplce[i][vplce[i] >> bits]; - if (pix != 0) - { - DWORD a = fg2rgb[palookupoffse[i][pix]] + bg2rgb[dest[i]]; - DWORD b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[i] = RGB32k.All[a & (a>>15)]; - } - vplce[i] += vince[i]; - } - dest += dc_pitch; - } while (--count); -} - -fixed_t tmvline1_subclamp () -{ - DWORD fracstep = dc_iscale; - DWORD frac = dc_texturefrac; - BYTE *colormap = dc_colormap; - int count = dc_count; - const BYTE *source = dc_source; - BYTE *dest = dc_dest; - int bits = tmvlinebits; - int pitch = dc_pitch; - - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - BYTE pix = source[frac>>bits]; - if (pix != 0) - { - DWORD a = (fg2rgb[colormap[pix]] | 0x40100400) - bg2rgb[*dest]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a>>15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - - return frac; -} - -void tmvline4_subclamp () -{ - BYTE *dest = dc_dest; - int count = dc_count; - int bits = tmvlinebits; - - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - for (int i = 0; i < 4; ++i) - { - BYTE pix = bufplce[i][vplce[i] >> bits]; - if (pix != 0) - { - DWORD a = (fg2rgb[palookupoffse[i][pix]] | 0x40100400) - bg2rgb[dest[i]]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[i] = RGB32k.All[a & (a>>15)]; - } - vplce[i] += vince[i]; - } - dest += dc_pitch; - } while (--count); -} - -fixed_t tmvline1_revsubclamp () -{ - DWORD fracstep = dc_iscale; - DWORD frac = dc_texturefrac; - BYTE *colormap = dc_colormap; - int count = dc_count; - const BYTE *source = dc_source; - BYTE *dest = dc_dest; - int bits = tmvlinebits; - int pitch = dc_pitch; - - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - BYTE pix = source[frac>>bits]; - if (pix != 0) - { - DWORD a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[pix]]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a>>15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - - return frac; -} - -void tmvline4_revsubclamp () -{ - BYTE *dest = dc_dest; - int count = dc_count; - int bits = tmvlinebits; - - DWORD *fg2rgb = dc_srcblend; - DWORD *bg2rgb = dc_destblend; - - do - { - for (int i = 0; i < 4; ++i) - { - BYTE pix = bufplce[i][vplce[i] >> bits]; - if (pix != 0) - { - DWORD a = (bg2rgb[dest[i]] | 0x40100400) - fg2rgb[palookupoffse[i][pix]]; - DWORD b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[i] = RGB32k.All[a & (a>>15)]; - } - vplce[i] += vince[i]; - } - dest += dc_pitch; - } while (--count); -} - - -//========================================================================== -// -// R_GetColumn -// -//========================================================================== - -const BYTE *R_GetColumn (FTexture *tex, int col) -{ - int width; - - // If the texture's width isn't a power of 2, then we need to make it a - // positive offset for proper clamping. - if (col < 0 && (width = tex->GetWidth()) != (1 << tex->WidthBits)) - { - col = width + (col % width); - } - return tex->GetColumn (col, NULL); -} - - -// [RH] Initialize the column drawer pointers -void R_InitColumnDrawers () -{ -#ifdef X86_ASM - R_DrawColumn = R_DrawColumnP_ASM; - R_DrawColumnHoriz = R_DrawColumnHorizP_ASM; - R_DrawFuzzColumn = R_DrawFuzzColumnP_ASM; - R_DrawTranslatedColumn = R_DrawTranslatedColumnP_C; - R_DrawShadedColumn = R_DrawShadedColumnP_C; - R_DrawSpan = R_DrawSpanP_ASM; - R_DrawSpanMasked = R_DrawSpanMaskedP_ASM; - if (CPU.Family <= 5) - { - rt_map4cols = rt_map4cols_asm2; - } - else - { - rt_map4cols = rt_map4cols_asm1; - } -#else - R_DrawColumnHoriz = R_DrawColumnHorizP_C; - R_DrawColumn = R_DrawColumnP_C; - R_DrawFuzzColumn = R_DrawFuzzColumnP_C; - R_DrawTranslatedColumn = R_DrawTranslatedColumnP_C; - R_DrawShadedColumn = R_DrawShadedColumnP_C; - R_DrawSpan = R_DrawSpanP_C; - R_DrawSpanMasked = R_DrawSpanMaskedP_C; - rt_map4cols = rt_map4cols_c; -#endif - R_DrawSpanTranslucent = R_DrawSpanTranslucentP_C; - R_DrawSpanMaskedTranslucent = R_DrawSpanMaskedTranslucentP_C; - R_DrawSpanAddClamp = R_DrawSpanAddClampP_C; - R_DrawSpanMaskedAddClamp = R_DrawSpanMaskedAddClampP_C; -} - -// [RH] Choose column drawers in a single place -EXTERN_CVAR (Int, r_drawfuzz) -EXTERN_CVAR (Bool, r_drawtrans) -EXTERN_CVAR (Float, transsouls) - -static FDynamicColormap *basecolormapsave; - -static bool R_SetBlendFunc (int op, fixed_t fglevel, fixed_t bglevel, int flags) -{ - // r_drawtrans is a seriously bad thing to turn off. I wonder if I should - // just remove it completely. - if (!r_drawtrans || (op == STYLEOP_Add && fglevel == FRACUNIT && bglevel == 0 && !(flags & STYLEF_InvertSource))) - { - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillColumnP; - hcolfunc_post1 = rt_copy1col; - hcolfunc_post4 = rt_copy4cols; - } - else if (dc_translation == NULL) - { - colfunc = basecolfunc; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; - } - else - { - colfunc = transcolfunc; - hcolfunc_post1 = rt_tlate1col; - hcolfunc_post4 = rt_tlate4cols; - } - return true; - } - if (flags & STYLEF_InvertSource) - { - dc_srcblend = Col2RGB8_Inverse[fglevel>>10]; - dc_destblend = Col2RGB8_LessPrecision[bglevel>>10]; - } - else if (op == STYLEOP_Add && fglevel + bglevel <= FRACUNIT) - { - dc_srcblend = Col2RGB8[fglevel>>10]; - dc_destblend = Col2RGB8[bglevel>>10]; - } - else - { - dc_srcblend = Col2RGB8_LessPrecision[fglevel>>10]; - dc_destblend = Col2RGB8_LessPrecision[bglevel>>10]; - } - switch (op) - { - case STYLEOP_Add: - if (fglevel == 0 && bglevel == FRACUNIT) - { - return false; - } - if (fglevel + bglevel <= FRACUNIT) - { // Colors won't overflow when added - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillAddColumn; - hcolfunc_post1 = rt_add1col; - hcolfunc_post4 = rt_add4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawAddColumnP_C; - hcolfunc_post1 = rt_add1col; - hcolfunc_post4 = rt_add4cols; - } - else - { - colfunc = R_DrawTlatedAddColumnP_C; - hcolfunc_post1 = rt_tlateadd1col; - hcolfunc_post4 = rt_tlateadd4cols; - } - } - else - { // Colors might overflow when added - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillAddClampColumn; - hcolfunc_post1 = rt_addclamp1col; - hcolfunc_post4 = rt_addclamp4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawAddClampColumnP_C; - hcolfunc_post1 = rt_addclamp1col; - hcolfunc_post4 = rt_addclamp4cols; - } - else - { - colfunc = R_DrawAddClampTranslatedColumnP_C; - hcolfunc_post1 = rt_tlateaddclamp1col; - hcolfunc_post4 = rt_tlateaddclamp4cols; - } - } - return true; - - case STYLEOP_Sub: - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillSubClampColumn; - hcolfunc_post1 = rt_subclamp1col; - hcolfunc_post4 = rt_subclamp4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawSubClampColumnP_C; - hcolfunc_post1 = rt_subclamp1col; - hcolfunc_post4 = rt_subclamp4cols; - } - else - { - colfunc = R_DrawSubClampTranslatedColumnP_C; - hcolfunc_post1 = rt_tlatesubclamp1col; - hcolfunc_post4 = rt_tlatesubclamp4cols; - } - return true; - - case STYLEOP_RevSub: - if (fglevel == 0 && bglevel == FRACUNIT) - { - return false; - } - if (flags & STYLEF_ColorIsFixed) - { - colfunc = R_FillRevSubClampColumn; - hcolfunc_post1 = rt_subclamp1col; - hcolfunc_post4 = rt_subclamp4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawRevSubClampColumnP_C; - hcolfunc_post1 = rt_revsubclamp1col; - hcolfunc_post4 = rt_revsubclamp4cols; - } - else - { - colfunc = R_DrawRevSubClampTranslatedColumnP_C; - hcolfunc_post1 = rt_tlaterevsubclamp1col; - hcolfunc_post4 = rt_tlaterevsubclamp4cols; - } - return true; - - default: - return false; - } -} - -static fixed_t GetAlpha(int type, fixed_t alpha) -{ - switch (type) - { - case STYLEALPHA_Zero: return 0; - case STYLEALPHA_One: return OPAQUE; - case STYLEALPHA_Src: return alpha; - case STYLEALPHA_InvSrc: return OPAQUE - alpha; - default: return 0; - } -} - -ESPSResult R_SetPatchStyle (FRenderStyle style, fixed_t alpha, int translation, DWORD color) -{ - fixed_t fglevel, bglevel; - - style.CheckFuzz(); - - if (style.BlendOp == STYLEOP_Shadow) - { - style = LegacyRenderStyles[STYLE_TranslucentStencil]; - alpha = TRANSLUC33; - color = 0; - } - - if (style.Flags & STYLEF_TransSoulsAlpha) - { - alpha = fixed_t(transsouls * OPAQUE); - } - else if (style.Flags & STYLEF_Alpha1) - { - alpha = FRACUNIT; - } - else - { - alpha = clamp (alpha, 0, OPAQUE); - } - - dc_translation = NULL; - if (translation != 0) - { - FRemapTable *table = TranslationToTable(translation); - if (table != NULL && !table->Inactive) - { - dc_translation = table->Remap; - } - } - basecolormapsave = basecolormap; - hcolfunc_pre = R_DrawColumnHoriz; - - // Check for special modes - if (style.BlendOp == STYLEOP_Fuzz) - { - colfunc = fuzzcolfunc; - return DoDraw0; - } - else if (style == LegacyRenderStyles[STYLE_Shaded]) - { - // Shaded drawer only gets 16 levels of alpha because it saves memory. - if ((alpha >>= 12) == 0) - return DontDraw; - colfunc = R_DrawShadedColumn; - hcolfunc_post1 = rt_shaded1col; - hcolfunc_post4 = rt_shaded4cols; - dc_color = fixedcolormap ? fixedcolormap[APART(color)] : basecolormap->Maps[APART(color)]; - dc_colormap = (basecolormap = &ShadeFakeColormap[16-alpha])->Maps; - if (fixedlightlev >= 0 && fixedcolormap == NULL) - { - dc_colormap += fixedlightlev; - } - return r_columnmethod ? DoDraw1 : DoDraw0; - } - - fglevel = GetAlpha(style.SrcAlpha, alpha); - bglevel = GetAlpha(style.DestAlpha, alpha); - - if (style.Flags & STYLEF_ColorIsFixed) - { - int x = fglevel >> 10; - int r = RPART(color); - int g = GPART(color); - int b = BPART(color); - // dc_color is used by the rt_* routines. It is indexed into dc_srcblend. - dc_color = RGB32k.RGB[r>>3][g>>3][b>>3]; - if (style.Flags & STYLEF_InvertSource) - { - r = 255 - r; - g = 255 - g; - b = 255 - b; - } - // dc_srccolor is used by the R_Fill* routines. It is premultiplied - // with the alpha. - dc_srccolor = ((((r*x)>>4)<<20) | ((g*x)>>4) | ((((b)*x)>>4)<<10)) & 0x3feffbff; - hcolfunc_pre = R_FillColumnHorizP; - dc_colormap = identitymap; - } - - if (!R_SetBlendFunc (style.BlendOp, fglevel, bglevel, style.Flags)) - { - return DontDraw; - } - return r_columnmethod ? DoDraw1 : DoDraw0; -} - -void R_FinishSetPatchStyle () -{ - basecolormap = basecolormapsave; -} - -bool R_GetTransMaskDrawers (fixed_t (**tmvline1)(), void (**tmvline4)()) -{ - if (colfunc == R_DrawAddColumnP_C) - { - *tmvline1 = tmvline1_add; - *tmvline4 = tmvline4_add; - return true; - } - if (colfunc == R_DrawAddClampColumnP_C) - { - *tmvline1 = tmvline1_addclamp; - *tmvline4 = tmvline4_addclamp; - return true; - } - if (colfunc == R_DrawSubClampColumnP_C) - { - *tmvline1 = tmvline1_subclamp; - *tmvline4 = tmvline4_subclamp; - return true; - } - if (colfunc == R_DrawRevSubClampColumnP_C) - { - *tmvline1 = tmvline1_revsubclamp; - *tmvline4 = tmvline4_revsubclamp; - return true; - } - return false; -} - diff --git a/src/r_draw.h b/src/r_draw.h deleted file mode 100644 index cb2f68f33..000000000 --- a/src/r_draw.h +++ /dev/null @@ -1,296 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// System specific interface stuff. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_DRAW__ -#define __R_DRAW__ - -#include "r_defs.h" - -extern "C" int ylookup[MAXHEIGHT]; - -extern "C" int dc_pitch; // [RH] Distance between rows - -extern "C" lighttable_t*dc_colormap; -extern "C" int dc_x; -extern "C" int dc_yl; -extern "C" int dc_yh; -extern "C" fixed_t dc_iscale; -extern double dc_texturemid; -extern "C" fixed_t dc_texturefrac; -extern "C" int dc_color; // [RH] For flat colors (no texturing) -extern "C" DWORD dc_srccolor; -extern "C" DWORD *dc_srcblend; -extern "C" DWORD *dc_destblend; - -// first pixel in a column -extern "C" const BYTE* dc_source; - -extern "C" BYTE *dc_dest, *dc_destorg; -extern "C" int dc_count; - -extern "C" DWORD vplce[4]; -extern "C" DWORD vince[4]; -extern "C" BYTE* palookupoffse[4]; -extern "C" const BYTE* bufplce[4]; - -// [RH] Temporary buffer for column drawing -extern "C" BYTE *dc_temp; -extern "C" unsigned int dc_tspans[4][MAXHEIGHT]; -extern "C" unsigned int *dc_ctspan[4]; -extern "C" unsigned int horizspans[4]; - - -// [RH] Pointers to the different column and span drawers... - -// The span blitting interface. -// Hook in assembler or system specific BLT here. -extern void (*R_DrawColumn)(void); - -extern DWORD (*dovline1) (); -extern DWORD (*doprevline1) (); -#ifdef X64_ASM -#define dovline4 vlinetallasm4 -extern "C" void vlinetallasm4(); -#else -extern void (*dovline4) (); -#endif -extern void setupvline (int); - -extern DWORD (*domvline1) (); -extern void (*domvline4) (); -extern void setupmvline (int); - -extern void setuptmvline (int); - -// The Spectre/Invisibility effect. -extern void (*R_DrawFuzzColumn)(void); - -// [RH] Draw shaded column -extern void (*R_DrawShadedColumn)(void); - -// Draw with color translation tables, for player sprite rendering, -// Green/Red/Blue/Indigo shirts. -extern void (*R_DrawTranslatedColumn)(void); - -// Span drawing for rows, floor/ceiling. No Spectre effect needed. -extern void (*R_DrawSpan)(void); -void R_SetupSpanBits(FTexture *tex); -void R_SetSpanColormap(BYTE *colormap); -void R_SetSpanSource(const BYTE *pixels); - -// Span drawing for masked textures. -extern void (*R_DrawSpanMasked)(void); - -// Span drawing for translucent textures. -extern void (*R_DrawSpanTranslucent)(void); - -// Span drawing for masked, translucent textures. -extern void (*R_DrawSpanMaskedTranslucent)(void); - -// Span drawing for translucent, additive textures. -extern void (*R_DrawSpanAddClamp)(void); - -// Span drawing for masked, translucent, additive textures. -extern void (*R_DrawSpanMaskedAddClamp)(void); - -// [RH] Span blit into an interleaved intermediate buffer -extern void (*R_DrawColumnHoriz)(void); -void R_DrawMaskedColumnHoriz (const BYTE *column, const FTexture::Span *spans); - -// [RH] Initialize the above pointers -void R_InitColumnDrawers (); - -// [RH] Moves data from the temporary buffer to the screen. -extern "C" -{ -void rt_copy1col_c (int hx, int sx, int yl, int yh); -void rt_copy4cols_c (int sx, int yl, int yh); - -void rt_shaded1col (int hx, int sx, int yl, int yh); -void rt_shaded4cols_c (int sx, int yl, int yh); -void rt_shaded4cols_asm (int sx, int yl, int yh); - -void rt_map1col_c (int hx, int sx, int yl, int yh); -void rt_add1col (int hx, int sx, int yl, int yh); -void rt_addclamp1col (int hx, int sx, int yl, int yh); -void rt_subclamp1col (int hx, int sx, int yl, int yh); -void rt_revsubclamp1col (int hx, int sx, int yl, int yh); - -void rt_tlate1col (int hx, int sx, int yl, int yh); -void rt_tlateadd1col (int hx, int sx, int yl, int yh); -void rt_tlateaddclamp1col (int hx, int sx, int yl, int yh); -void rt_tlatesubclamp1col (int hx, int sx, int yl, int yh); -void rt_tlaterevsubclamp1col (int hx, int sx, int yl, int yh); - -void rt_map4cols_c (int sx, int yl, int yh); -void rt_add4cols_c (int sx, int yl, int yh); -void rt_addclamp4cols_c (int sx, int yl, int yh); -void rt_subclamp4cols (int sx, int yl, int yh); -void rt_revsubclamp4cols (int sx, int yl, int yh); - -void rt_tlate4cols (int sx, int yl, int yh); -void rt_tlateadd4cols (int sx, int yl, int yh); -void rt_tlateaddclamp4cols (int sx, int yl, int yh); -void rt_tlatesubclamp4cols (int sx, int yl, int yh); -void rt_tlaterevsubclamp4cols (int sx, int yl, int yh); - -void rt_copy1col_asm (int hx, int sx, int yl, int yh); -void rt_map1col_asm (int hx, int sx, int yl, int yh); - -void rt_copy4cols_asm (int sx, int yl, int yh); -void rt_map4cols_asm1 (int sx, int yl, int yh); -void rt_map4cols_asm2 (int sx, int yl, int yh); -void rt_add4cols_asm (int sx, int yl, int yh); -void rt_addclamp4cols_asm (int sx, int yl, int yh); -} - -extern void (*rt_map4cols)(int sx, int yl, int yh); - -#ifdef X86_ASM -#define rt_copy1col rt_copy1col_asm -#define rt_copy4cols rt_copy4cols_asm -#define rt_map1col rt_map1col_asm -#define rt_shaded4cols rt_shaded4cols_asm -#define rt_add4cols rt_add4cols_asm -#define rt_addclamp4cols rt_addclamp4cols_asm -#else -#define rt_copy1col rt_copy1col_c -#define rt_copy4cols rt_copy4cols_c -#define rt_map1col rt_map1col_c -#define rt_shaded4cols rt_shaded4cols_c -#define rt_add4cols rt_add4cols_c -#define rt_addclamp4cols rt_addclamp4cols_c -#endif - -void rt_draw4cols (int sx); - -// [RH] Preps the temporary horizontal buffer. -void rt_initcols (BYTE *buffer=NULL); - -void R_DrawFogBoundary (int x1, int x2, short *uclip, short *dclip); - - -#ifdef X86_ASM - -extern "C" void R_DrawColumnP_Unrolled (void); -extern "C" void R_DrawColumnHorizP_ASM (void); -extern "C" void R_DrawColumnP_ASM (void); -extern "C" void R_DrawFuzzColumnP_ASM (void); - void R_DrawTranslatedColumnP_C (void); - void R_DrawShadedColumnP_C (void); -extern "C" void R_DrawSpanP_ASM (void); -extern "C" void R_DrawSpanMaskedP_ASM (void); - -#else - -void R_DrawColumnHorizP_C (void); -void R_DrawColumnP_C (void); -void R_DrawFuzzColumnP_C (void); -void R_DrawTranslatedColumnP_C (void); -void R_DrawShadedColumnP_C (void); -void R_DrawSpanP_C (void); -void R_DrawSpanMaskedP_C (void); - -#endif - -void R_DrawSpanTranslucentP_C (void); -void R_DrawSpanMaskedTranslucentP_C (void); - -void R_DrawTlatedLucentColumnP_C (void); -#define R_DrawTlatedLucentColumn R_DrawTlatedLucentColumnP_C - -void R_FillColumnP (void); -void R_FillColumnHorizP (void); -void R_FillSpan (void); - -#ifdef X86_ASM -#define R_SetupDrawSlab R_SetupDrawSlabA -#define R_DrawSlab R_DrawSlabA -#else -#define R_SetupDrawSlab R_SetupDrawSlabC -#define R_DrawSlab R_DrawSlabC -#endif - -extern "C" void R_SetupDrawSlab(const BYTE *colormap); -extern "C" void R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const BYTE *vptr, BYTE *p); - -extern "C" int ds_y; -extern "C" int ds_x1; -extern "C" int ds_x2; - -extern "C" lighttable_t* ds_colormap; - -extern "C" dsfixed_t ds_xfrac; -extern "C" dsfixed_t ds_yfrac; -extern "C" dsfixed_t ds_xstep; -extern "C" dsfixed_t ds_ystep; -extern "C" int ds_xbits; -extern "C" int ds_ybits; -extern "C" fixed_t ds_alpha; - -// start of a 64*64 tile image -extern "C" const BYTE* ds_source; - -extern "C" int ds_color; // [RH] For flat color (no texturing) - -extern BYTE shadetables[/*NUMCOLORMAPS*16*256*/]; -extern FDynamicColormap ShadeFakeColormap[16]; -extern BYTE identitymap[256]; -extern BYTE *dc_translation; - -// [RH] Added for muliresolution support -void R_InitShadeMaps(); -void R_InitFuzzTable (int fuzzoff); - -// [RH] Consolidate column drawer selection -enum ESPSResult -{ - DontDraw, // not useful to draw this - DoDraw0, // draw this as if r_columnmethod is 0 - DoDraw1, // draw this as if r_columnmethod is 1 -}; -ESPSResult R_SetPatchStyle (FRenderStyle style, fixed_t alpha, int translation, DWORD color); -inline ESPSResult R_SetPatchStyle(FRenderStyle style, float alpha, int translation, DWORD color) -{ - return R_SetPatchStyle(style, FLOAT2FIXED(alpha), translation, color); -} - -// Call this after finished drawing the current thing, in case its -// style was STYLE_Shade -void R_FinishSetPatchStyle (); - -// transmaskwallscan calls this to find out what column drawers to use -bool R_GetTransMaskDrawers (fixed_t (**tmvline1)(), void (**tmvline4)()); - -// Retrieve column data for wallscan. Should probably be removed -// to just use the texture's GetColumn() method. It just exists -// for double-layer skies. -const BYTE *R_GetColumn (FTexture *tex, int col); -void wallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int col)=R_GetColumn); - -// maskwallscan is exactly like wallscan but does not draw anything where the texture is color 0. -void maskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int col)=R_GetColumn); - -// transmaskwallscan is like maskwallscan, but it can also blend to the background -void transmaskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int col)=R_GetColumn); - -#endif diff --git a/src/r_draw_pal.cpp b/src/r_draw_pal.cpp deleted file mode 100644 index 2cdeba314..000000000 --- a/src/r_draw_pal.cpp +++ /dev/null @@ -1,2622 +0,0 @@ -/* -** r_draw_pal.cpp -** -**--------------------------------------------------------------------------- -** Copyright 1998-2016 Randy Heit -** Copyright 2016 Magnus Norddahl -** 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 "templates.h" -#include "doomtype.h" -#include "doomdef.h" -#include "r_defs.h" -#include "r_draw.h" -#include "r_main.h" -#include "r_things.h" -#include "v_video.h" -#include "r_draw_pal.h" - -/* - [RH] This translucency algorithm is based on DOSDoom 0.65's, but uses - a 32k RGB table instead of an 8k one. At least on my machine, it's - slightly faster (probably because it uses only one shift instead of - two), and it looks considerably less green at the ends of the - translucency range. The extra size doesn't appear to be an issue. - - The following note is from DOSDoom 0.65: - - New translucency algorithm, by Erik Sandberg: - - Basically, we compute the red, green and blue values for each pixel, and - then use a RGB table to check which one of the palette colours that best - represents those RGB values. The RGB table is 8k big, with 4 R-bits, - 5 G-bits and 4 B-bits. A 4k table gives a bit too bad precision, and a 32k - table takes up more memory and results in more cache misses, so an 8k - table seemed to be quite ultimate. - - The computation of the RGB for each pixel is accelerated by using two - 1k tables for each translucency level. - The xth element of one of these tables contains the r, g and b values for - the colour x, weighted for the current translucency level (for example, - the weighted rgb values for background colour at 75% translucency are 1/4 - of the original rgb values). The rgb values are stored as three - low-precision fixed point values, packed into one long per colour: - Bit 0-4: Frac part of blue (5 bits) - Bit 5-8: Int part of blue (4 bits) - Bit 9-13: Frac part of red (5 bits) - Bit 14-17: Int part of red (4 bits) - Bit 18-22: Frac part of green (5 bits) - Bit 23-27: Int part of green (5 bits) - Bit 28-31: All zeros (4 bits) - - The point of this format is that the two colours now can be added, and - then be converted to a RGB table index very easily: First, we just set - all the frac bits and the four upper zero bits to 1. It's now possible - to get the RGB table index by anding the current value >> 5 with the - current value >> 19. When asm-optimised, this should be the fastest - algorithm that uses RGB tables. -*/ - -namespace swrenderer -{ - PalWall1Command::PalWall1Command() - { - using namespace drawerargs; - - _iscale = dc_iscale; - _texturefrac = dc_texturefrac; - _colormap = dc_colormap; - _count = dc_count; - _source = dc_source; - _dest = dc_dest; - _fracbits = dc_wall_fracbits; - _pitch = dc_pitch; - _srcblend = dc_srcblend; - _destblend = dc_destblend; - } - - PalWall4Command::PalWall4Command() - { - using namespace drawerargs; - - _dest = dc_dest; - _count = dc_count; - _pitch = dc_pitch; - _fracbits = dc_wall_fracbits; - for (int col = 0; col < 4; col++) - { - _colormap[col] = dc_wall_colormap[col]; - _source[col] = dc_wall_source[col]; - _iscale[col] = dc_wall_iscale[col]; - _texturefrac[col] = dc_wall_texturefrac[col]; - } - _srcblend = dc_srcblend; - _destblend = dc_destblend; - } - - void DrawWall1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - *dest = colormap[source[frac >> bits]]; - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWall4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - uint32_t place; - auto pal0 = _colormap[0]; - auto pal1 = _colormap[1]; - auto pal2 = _colormap[2]; - auto pal3 = _colormap[3]; - auto buf0 = _source[0]; - auto buf1 = _source[1]; - auto buf2 = _source[2]; - auto buf3 = _source[3]; - auto dc_wall_iscale0 = _iscale[0]; - auto dc_wall_iscale1 = _iscale[1]; - auto dc_wall_iscale2 = _iscale[2]; - auto dc_wall_iscale3 = _iscale[3]; - auto dc_wall_texturefrac0 = _texturefrac[0]; - auto dc_wall_texturefrac1 = _texturefrac[1]; - auto dc_wall_texturefrac2 = _texturefrac[2]; - auto dc_wall_texturefrac3 = _texturefrac[3]; - auto pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - dc_wall_texturefrac0 += dc_wall_iscale0 * skipped; - dc_wall_texturefrac1 += dc_wall_iscale1 * skipped; - dc_wall_texturefrac2 += dc_wall_iscale2 * skipped; - dc_wall_texturefrac3 += dc_wall_iscale3 * skipped; - dc_wall_iscale0 *= thread->num_cores; - dc_wall_iscale1 *= thread->num_cores; - dc_wall_iscale2 *= thread->num_cores; - dc_wall_iscale3 *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - dest[0] = pal0[buf0[(place = dc_wall_texturefrac0) >> bits]]; dc_wall_texturefrac0 = place + dc_wall_iscale0; - dest[1] = pal1[buf1[(place = dc_wall_texturefrac1) >> bits]]; dc_wall_texturefrac1 = place + dc_wall_iscale1; - dest[2] = pal2[buf2[(place = dc_wall_texturefrac2) >> bits]]; dc_wall_texturefrac2 = place + dc_wall_iscale2; - dest[3] = pal3[buf3[(place = dc_wall_texturefrac3) >> bits]]; dc_wall_texturefrac3 = place + dc_wall_iscale3; - dest += pitch; - } while (--count); - } - - void DrawWallMasked1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - *dest = colormap[pix]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallMasked4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - uint32_t place; - auto pal0 = _colormap[0]; - auto pal1 = _colormap[1]; - auto pal2 = _colormap[2]; - auto pal3 = _colormap[3]; - auto buf0 = _source[0]; - auto buf1 = _source[1]; - auto buf2 = _source[2]; - auto buf3 = _source[3]; - auto dc_wall_iscale0 = _iscale[0]; - auto dc_wall_iscale1 = _iscale[1]; - auto dc_wall_iscale2 = _iscale[2]; - auto dc_wall_iscale3 = _iscale[3]; - auto dc_wall_texturefrac0 = _texturefrac[0]; - auto dc_wall_texturefrac1 = _texturefrac[1]; - auto dc_wall_texturefrac2 = _texturefrac[2]; - auto dc_wall_texturefrac3 = _texturefrac[3]; - auto pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - dc_wall_texturefrac0 += dc_wall_iscale0 * skipped; - dc_wall_texturefrac1 += dc_wall_iscale1 * skipped; - dc_wall_texturefrac2 += dc_wall_iscale2 * skipped; - dc_wall_texturefrac3 += dc_wall_iscale3 * skipped; - dc_wall_iscale0 *= thread->num_cores; - dc_wall_iscale1 *= thread->num_cores; - dc_wall_iscale2 *= thread->num_cores; - dc_wall_iscale3 *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix; - - pix = buf0[(place = dc_wall_texturefrac0) >> bits]; if (pix) dest[0] = pal0[pix]; dc_wall_texturefrac0 = place + dc_wall_iscale0; - pix = buf1[(place = dc_wall_texturefrac1) >> bits]; if (pix) dest[1] = pal1[pix]; dc_wall_texturefrac1 = place + dc_wall_iscale1; - pix = buf2[(place = dc_wall_texturefrac2) >> bits]; if (pix) dest[2] = pal2[pix]; dc_wall_texturefrac2 = place + dc_wall_iscale2; - pix = buf3[(place = dc_wall_texturefrac3) >> bits]; if (pix) dest[3] = pal3[pix]; dc_wall_texturefrac3 = place + dc_wall_iscale3; - dest += pitch; - } while (--count); - } - - void DrawWallAdd1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - uint32_t fg = fg2rgb[colormap[pix]]; - uint32_t bg = bg2rgb[*dest]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallAdd4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - uint32_t dc_wall_texturefrac[4] = { _texturefrac[0], _texturefrac[1], _texturefrac[2], _texturefrac[3] }; - uint32_t dc_wall_iscale[4] = { _iscale[0], _iscale[1], _iscale[2], _iscale[3] }; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] += dc_wall_iscale[i] * skipped; - dc_wall_iscale[i] *= thread->num_cores; - } - pitch *= thread->num_cores; - - do - { - for (int i = 0; i < 4; ++i) - { - uint8_t pix = _source[i][dc_wall_texturefrac[i] >> bits]; - if (pix != 0) - { - uint32_t fg = fg2rgb[_colormap[i][pix]]; - uint32_t bg = bg2rgb[dest[i]]; - fg = (fg + bg) | 0x1f07c1f; - dest[i] = RGB32k.All[fg & (fg >> 15)]; - } - dc_wall_texturefrac[i] += dc_wall_iscale[i]; - } - dest += pitch; - } while (--count); - } - - void DrawWallAddClamp1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - uint32_t a = fg2rgb[colormap[pix]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallAddClamp4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - uint32_t dc_wall_texturefrac[4] = { _texturefrac[0], _texturefrac[1], _texturefrac[2], _texturefrac[3] }; - uint32_t dc_wall_iscale[4] = { _iscale[0], _iscale[1], _iscale[2], _iscale[3] }; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] += dc_wall_iscale[i] * skipped; - dc_wall_iscale[i] *= thread->num_cores; - } - pitch *= thread->num_cores; - - do - { - for (int i = 0; i < 4; ++i) - { - uint8_t pix = _source[i][dc_wall_texturefrac[i] >> bits]; - if (pix != 0) - { - uint32_t a = fg2rgb[_colormap[i][pix]] + bg2rgb[dest[i]]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[i] = RGB32k.All[a & (a >> 15)]; - } - dc_wall_texturefrac[i] += dc_wall_iscale[i]; - } - dest += pitch; - } while (--count); - } - - void DrawWallSubClamp1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - uint32_t a = (fg2rgb[colormap[pix]] | 0x40100400) - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallSubClamp4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - uint32_t dc_wall_texturefrac[4] = { _texturefrac[0], _texturefrac[1], _texturefrac[2], _texturefrac[3] }; - uint32_t dc_wall_iscale[4] = { _iscale[0], _iscale[1], _iscale[2], _iscale[3] }; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] += dc_wall_iscale[i] * skipped; - dc_wall_iscale[i] *= thread->num_cores; - } - pitch *= thread->num_cores; - - do - { - for (int i = 0; i < 4; ++i) - { - uint8_t pix = _source[i][dc_wall_texturefrac[i] >> bits]; - if (pix != 0) - { - uint32_t a = (fg2rgb[_colormap[i][pix]] | 0x40100400) - bg2rgb[dest[i]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[i] = RGB32k.All[a & (a >> 15)]; - } - dc_wall_texturefrac[i] += dc_wall_iscale[i]; - } - dest += pitch; - } while (--count); - } - - void DrawWallRevSubClamp1PalCommand::Execute(DrawerThread *thread) - { - uint32_t fracstep = _iscale; - uint32_t frac = _texturefrac; - uint8_t *colormap = _colormap; - int count = _count; - const uint8_t *source = _source; - uint8_t *dest = _dest; - int bits = _fracbits; - int pitch = _pitch; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - do - { - uint8_t pix = source[frac >> bits]; - if (pix != 0) - { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[pix]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - } - frac += fracstep; - dest += pitch; - } while (--count); - } - - void DrawWallRevSubClamp4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int bits = _fracbits; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - uint32_t dc_wall_texturefrac[4] = { _texturefrac[0], _texturefrac[1], _texturefrac[2], _texturefrac[3] }; - uint32_t dc_wall_iscale[4] = { _iscale[0], _iscale[1], _iscale[2], _iscale[3] }; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] += dc_wall_iscale[i] * skipped; - dc_wall_iscale[i] *= thread->num_cores; - } - pitch *= thread->num_cores; - - do - { - for (int i = 0; i < 4; ++i) - { - uint8_t pix = _source[i][dc_wall_texturefrac[i] >> bits]; - if (pix != 0) - { - uint32_t a = (bg2rgb[dest[i]] | 0x40100400) - fg2rgb[_colormap[i][pix]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[i] = RGB32k.All[a & (a >> 15)]; - } - dc_wall_texturefrac[i] += dc_wall_iscale[i]; - } - dest += _pitch; - } while (--count); - } - - ///////////////////////////////////////////////////////////////////////// - - PalSkyCommand::PalSkyCommand(uint32_t solid_top, uint32_t solid_bottom) : solid_top(solid_top), solid_bottom(solid_bottom) - { - using namespace drawerargs; - - _dest = dc_dest; - _count = dc_count; - _pitch = dc_pitch; - for (int col = 0; col < 4; col++) - { - _source[col] = dc_wall_source[col]; - _source2[col] = dc_wall_source2[col]; - _sourceheight[col] = dc_wall_sourceheight[col]; - _iscale[col] = dc_wall_iscale[col]; - _texturefrac[col] = dc_wall_texturefrac[col]; - } - } - - void DrawSingleSky1PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int pitch = _pitch; - const uint8_t *source0 = _source[0]; - int textureheight0 = _sourceheight[0]; - - int32_t frac = _texturefrac[0]; - int32_t fracstep = _iscale[0]; - - int start_fade = 2; // How fast it should fade out - - int solid_top_r = RPART(solid_top); - int solid_top_g = GPART(solid_top); - int solid_top_b = BPART(solid_top); - int solid_bottom_r = RPART(solid_bottom); - int solid_bottom_g = GPART(solid_bottom); - int solid_bottom_b = BPART(solid_bottom); - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * skipped; - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - for (int index = 0; index < count; index++) - { - uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[sample_index]; - - int alpha_top = MAX(MIN(frac >> (16 - start_fade), 256), 0); - int alpha_bottom = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); - - if (alpha_top == 256 && alpha_bottom == 256) - { - *dest = fg; - } - else - { - int inv_alpha_top = 256 - alpha_top; - int inv_alpha_bottom = 256 - alpha_bottom; - - const auto &c = GPalette.BaseColors[fg]; - int c_red = c.r; - int c_green = c.g; - int c_blue = c.b; - c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; - c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; - c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; - c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; - c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; - c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; - *dest = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - } - - frac += fracstep; - dest += pitch; - } - } - - void DrawSingleSky4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int pitch = _pitch; - const uint8_t *source0[4] = { _source[0], _source[1], _source[2], _source[3] }; - int textureheight0 = _sourceheight[0]; - const uint32_t *palette = (const uint32_t *)GPalette.BaseColors; - int32_t frac[4] = { (int32_t)_texturefrac[0], (int32_t)_texturefrac[1], (int32_t)_texturefrac[2], (int32_t)_texturefrac[3] }; - int32_t fracstep[4] = { (int32_t)_iscale[0], (int32_t)_iscale[1], (int32_t)_iscale[2], (int32_t)_iscale[3] }; - uint8_t output[4]; - - int start_fade = 2; // How fast it should fade out - - int solid_top_r = RPART(solid_top); - int solid_top_g = GPART(solid_top); - int solid_top_b = BPART(solid_top); - int solid_bottom_r = RPART(solid_bottom); - int solid_bottom_g = GPART(solid_bottom); - int solid_bottom_b = BPART(solid_bottom); - uint32_t solid_top_fill = RGB32k.RGB[(solid_top_r >> 3)][(solid_top_g >> 3)][(solid_top_b >> 3)]; - uint32_t solid_bottom_fill = RGB32k.RGB[(solid_bottom_r >> 3)][(solid_bottom_g >> 3)][(solid_bottom_b >> 3)]; - solid_top_fill = (solid_top_fill << 24) | (solid_top_fill << 16) | (solid_top_fill << 8) | solid_top_fill; - solid_bottom_fill = (solid_bottom_fill << 24) | (solid_bottom_fill << 16) | (solid_bottom_fill << 8) | solid_bottom_fill; - - // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: - int fade_length = (1 << (24 - start_fade)); - int start_fadetop_y = (-frac[0]) / fracstep[0]; - int end_fadetop_y = (fade_length - frac[0]) / fracstep[0]; - int start_fadebottom_y = ((2 << 24) - fade_length - frac[0]) / fracstep[0]; - int end_fadebottom_y = ((2 << 24) - frac[0]) / fracstep[0]; - for (int col = 1; col < 4; col++) - { - start_fadetop_y = MIN(start_fadetop_y, (-frac[0]) / fracstep[0]); - end_fadetop_y = MAX(end_fadetop_y, (fade_length - frac[0]) / fracstep[0]); - start_fadebottom_y = MIN(start_fadebottom_y, ((2 << 24) - fade_length - frac[0]) / fracstep[0]); - end_fadebottom_y = MAX(end_fadebottom_y, ((2 << 24) - frac[0]) / fracstep[0]); - } - start_fadetop_y = clamp(start_fadetop_y, 0, count); - end_fadetop_y = clamp(end_fadetop_y, 0, count); - start_fadebottom_y = clamp(start_fadebottom_y, 0, count); - end_fadebottom_y = clamp(end_fadebottom_y, 0, count); - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int col = 0; col < 4; col++) - { - frac[col] += fracstep[col] * skipped; - fracstep[col] *= thread->num_cores; - } - pitch *= thread->num_cores; - int num_cores = thread->num_cores; - int index = skipped; - - // Top solid color: - while (index < start_fadetop_y) - { - *((uint32_t*)dest) = solid_top_fill; - dest += pitch; - for (int col = 0; col < 4; col++) - frac[col] += fracstep[col]; - index += num_cores; - } - - // Top fade: - while (index < end_fadetop_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - - uint32_t c = palette[fg]; - int alpha_top = MAX(MIN(frac[col] >> (16 - start_fade), 256), 0); - int inv_alpha_top = 256 - alpha_top; - int c_red = RPART(c); - int c_green = GPART(c); - int c_blue = BPART(c); - c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; - c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; - c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; - output[col] = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - - frac[col] += fracstep[col]; - } - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Textured center: - while (index < start_fadebottom_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - output[col] = source0[col][sample_index]; - - frac[col] += fracstep[col]; - } - - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Fade bottom: - while (index < end_fadebottom_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - - uint32_t c = palette[fg]; - int alpha_bottom = MAX(MIN(((2 << 24) - frac[col]) >> (16 - start_fade), 256), 0); - int inv_alpha_bottom = 256 - alpha_bottom; - int c_red = RPART(c); - int c_green = GPART(c); - int c_blue = BPART(c); - c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; - c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; - c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; - output[col] = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - - frac[col] += fracstep[col]; - } - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Bottom solid color: - while (index < count) - { - *((uint32_t*)dest) = solid_bottom_fill; - dest += pitch; - index += num_cores; - } - } - - void DrawDoubleSky1PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int pitch = _pitch; - const uint8_t *source0 = _source[0]; - const uint8_t *source1 = _source2[0]; - int textureheight0 = _sourceheight[0]; - uint32_t maxtextureheight1 = _sourceheight[1] - 1; - - int32_t frac = _texturefrac[0]; - int32_t fracstep = _iscale[0]; - - int start_fade = 2; // How fast it should fade out - - int solid_top_r = RPART(solid_top); - int solid_top_g = GPART(solid_top); - int solid_top_b = BPART(solid_top); - int solid_bottom_r = RPART(solid_bottom); - int solid_bottom_g = GPART(solid_bottom); - int solid_bottom_b = BPART(solid_bottom); - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * skipped; - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - for (int index = 0; index < count; index++) - { - uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[sample_index]; - if (fg == 0) - { - uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); - fg = source1[sample_index2]; - } - - int alpha_top = MAX(MIN(frac >> (16 - start_fade), 256), 0); - int alpha_bottom = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); - - if (alpha_top == 256 && alpha_bottom == 256) - { - *dest = fg; - } - else - { - int inv_alpha_top = 256 - alpha_top; - int inv_alpha_bottom = 256 - alpha_bottom; - - const auto &c = GPalette.BaseColors[fg]; - int c_red = c.r; - int c_green = c.g; - int c_blue = c.b; - c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; - c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; - c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; - c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; - c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; - c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; - *dest = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - } - - frac += fracstep; - dest += pitch; - } - } - - void DrawDoubleSky4PalCommand::Execute(DrawerThread *thread) - { - uint8_t *dest = _dest; - int count = _count; - int pitch = _pitch; - const uint8_t *source0[4] = { _source[0], _source[1], _source[2], _source[3] }; - const uint8_t *source1[4] = { _source2[0], _source2[1], _source2[2], _source2[3] }; - int textureheight0 = _sourceheight[0]; - uint32_t maxtextureheight1 = _sourceheight[1] - 1; - const uint32_t *palette = (const uint32_t *)GPalette.BaseColors; - int32_t frac[4] = { (int32_t)_texturefrac[0], (int32_t)_texturefrac[1], (int32_t)_texturefrac[2], (int32_t)_texturefrac[3] }; - int32_t fracstep[4] = { (int32_t)_iscale[0], (int32_t)_iscale[1], (int32_t)_iscale[2], (int32_t)_iscale[3] }; - uint8_t output[4]; - - int start_fade = 2; // How fast it should fade out - - int solid_top_r = RPART(solid_top); - int solid_top_g = GPART(solid_top); - int solid_top_b = BPART(solid_top); - int solid_bottom_r = RPART(solid_bottom); - int solid_bottom_g = GPART(solid_bottom); - int solid_bottom_b = BPART(solid_bottom); - uint32_t solid_top_fill = RGB32k.RGB[(solid_top_r >> 3)][(solid_top_g >> 3)][(solid_top_b >> 3)]; - uint32_t solid_bottom_fill = RGB32k.RGB[(solid_bottom_r >> 3)][(solid_bottom_g >> 3)][(solid_bottom_b >> 3)]; - solid_top_fill = (solid_top_fill << 24) | (solid_top_fill << 16) | (solid_top_fill << 8) | solid_top_fill; - solid_bottom_fill = (solid_bottom_fill << 24) | (solid_bottom_fill << 16) | (solid_bottom_fill << 8) | solid_bottom_fill; - - // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: - int fade_length = (1 << (24 - start_fade)); - int start_fadetop_y = (-frac[0]) / fracstep[0]; - int end_fadetop_y = (fade_length - frac[0]) / fracstep[0]; - int start_fadebottom_y = ((2 << 24) - fade_length - frac[0]) / fracstep[0]; - int end_fadebottom_y = ((2 << 24) - frac[0]) / fracstep[0]; - for (int col = 1; col < 4; col++) - { - start_fadetop_y = MIN(start_fadetop_y, (-frac[0]) / fracstep[0]); - end_fadetop_y = MAX(end_fadetop_y, (fade_length - frac[0]) / fracstep[0]); - start_fadebottom_y = MIN(start_fadebottom_y, ((2 << 24) - fade_length - frac[0]) / fracstep[0]); - end_fadebottom_y = MAX(end_fadebottom_y, ((2 << 24) - frac[0]) / fracstep[0]); - } - start_fadetop_y = clamp(start_fadetop_y, 0, count); - end_fadetop_y = clamp(end_fadetop_y, 0, count); - start_fadebottom_y = clamp(start_fadebottom_y, 0, count); - end_fadebottom_y = clamp(end_fadebottom_y, 0, count); - - int skipped = thread->skipped_by_thread(_dest_y); - dest = thread->dest_for_thread(_dest_y, pitch, dest); - for (int col = 0; col < 4; col++) - { - frac[col] += fracstep[col] * skipped; - fracstep[col] *= thread->num_cores; - } - pitch *= thread->num_cores; - int num_cores = thread->num_cores; - int index = skipped; - - // Top solid color: - while (index < start_fadetop_y) - { - *((uint32_t*)dest) = solid_top_fill; - dest += pitch; - for (int col = 0; col < 4; col++) - frac[col] += fracstep[col]; - index += num_cores; - } - - // Top fade: - while (index < end_fadetop_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - if (fg == 0) - { - uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); - fg = source1[col][sample_index2]; - } - output[col] = fg; - - uint32_t c = palette[fg]; - int alpha_top = MAX(MIN(frac[col] >> (16 - start_fade), 256), 0); - int inv_alpha_top = 256 - alpha_top; - int c_red = RPART(c); - int c_green = GPART(c); - int c_blue = BPART(c); - c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; - c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; - c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; - output[col] = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - - frac[col] += fracstep[col]; - } - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Textured center: - while (index < start_fadebottom_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - if (fg == 0) - { - uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); - fg = source1[col][sample_index2]; - } - output[col] = fg; - - frac[col] += fracstep[col]; - } - - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Fade bottom: - while (index < end_fadebottom_y) - { - for (int col = 0; col < 4; col++) - { - uint32_t sample_index = (((((uint32_t)frac[col]) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; - uint8_t fg = source0[col][sample_index]; - if (fg == 0) - { - uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); - fg = source1[col][sample_index2]; - } - output[col] = fg; - - uint32_t c = palette[fg]; - int alpha_bottom = MAX(MIN(((2 << 24) - frac[col]) >> (16 - start_fade), 256), 0); - int inv_alpha_bottom = 256 - alpha_bottom; - int c_red = RPART(c); - int c_green = GPART(c); - int c_blue = BPART(c); - c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; - c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; - c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; - output[col] = RGB32k.RGB[(c_red >> 3)][(c_green >> 3)][(c_blue >> 3)]; - - frac[col] += fracstep[col]; - } - *((uint32_t*)dest) = *((uint32_t*)output); - dest += pitch; - index += num_cores; - } - - // Bottom solid color: - while (index < count) - { - *((uint32_t*)dest) = solid_bottom_fill; - dest += pitch; - index += num_cores; - } - } - - ///////////////////////////////////////////////////////////////////////// - - PalColumnCommand::PalColumnCommand() - { - using namespace drawerargs; - - _count = dc_count; - _dest = dc_dest; - _pitch = dc_pitch; - _iscale = dc_iscale; - _texturefrac = dc_texturefrac; - _colormap = dc_colormap; - _source = dc_source; - _translation = dc_translation; - _color = dc_color; - _srcblend = dc_srcblend; - _destblend = dc_destblend; - _srccolor = dc_srccolor; - } - - void DrawColumnPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - - // Framebuffer destination address. - dest = _dest; - - // Determine scaling, - // which is the only mapping to be done. - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - // [RH] Get local copies of these variables so that the compiler - // has a better chance of optimizing this well. - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - - // Inner loop that does the actual texture mapping, - // e.g. a DDA-lile scaling. - // This is as fast as it gets. - do - { - // Re-map color indices from wall texture column - // using a lighting/special effects LUT. - *dest = colormap[source[frac >> FRACBITS]]; - - dest += pitch; - frac += fracstep; - - } while (--count); - } - - void FillColumnPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - dest = _dest; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - uint8_t color = _color; - do - { - *dest = color; - dest += pitch; - } while (--count); - } - - void FillColumnAddPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - dest = _dest; - uint32_t *bg2rgb; - uint32_t fg; - - bg2rgb = _destblend; - fg = _srccolor; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - do - { - uint32_t bg; - bg = (fg + bg2rgb[*dest]) | 0x1f07c1f; - *dest = RGB32k.All[bg & (bg >> 15)]; - dest += pitch; - } while (--count); - - } - - void FillColumnAddClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - - dest = _dest; - uint32_t *bg2rgb; - uint32_t fg; - - bg2rgb = _destblend; - fg = _srccolor; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - do - { - uint32_t a = fg + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - } while (--count); - } - - void FillColumnSubClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - - dest = _dest; - uint32_t *bg2rgb; - uint32_t fg; - - bg2rgb = _destblend; - fg = _srccolor | 0x40100400; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - do - { - uint32_t a = fg - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - } while (--count); - } - - void FillColumnRevSubClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - - count = _count; - if (count <= 0) - return; - - dest = _dest; - uint32_t *bg2rgb; - uint32_t fg; - - bg2rgb = _destblend; - fg = _srccolor; - int pitch = _pitch; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - dest = thread->dest_for_thread(_dest_y, pitch, dest); - pitch *= thread->num_cores; - - do - { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - } while (--count); - } - - void DrawColumnAddPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - - do - { - uint32_t fg = colormap[source[frac >> FRACBITS]]; - uint32_t bg = *dest; - - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnTranslatedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t* dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - // [RH] Local copies of global vars to improve compiler optimizations - const uint8_t *colormap = _colormap; - const uint8_t *translation = _translation; - const uint8_t *source = _source; - - do - { - *dest = colormap[translation[source[frac >> FRACBITS]]]; - dest += pitch; - - frac += fracstep; - } while (--count); - } - - void DrawColumnTlatedAddPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - const uint8_t *translation = _translation; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - - do - { - uint32_t fg = colormap[translation[source[frac >> FRACBITS]]]; - uint32_t bg = *dest; - - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnShadedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac, fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - uint32_t *fgstart = &Col2RGB8[0][_color]; - - do - { - uint32_t val = colormap[source[frac >> FRACBITS]]; - uint32_t fg = fgstart[val << 8]; - val = (Col2RGB8[64 - val][*dest] + fg) | 0x1f07c1f; - *dest = RGB32k.All[val & (val >> 15)]; - - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnAddClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = fg2rgb[colormap[source[frac >> FRACBITS]]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnAddClampTranslatedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *translation = _translation; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = fg2rgb[colormap[translation[source[frac >> FRACBITS]]]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[(a >> 15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnSubClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = (fg2rgb[colormap[source[frac >> FRACBITS]]] | 0x40100400) - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnSubClampTranslatedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *translation = _translation; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = (fg2rgb[colormap[translation[source[frac >> FRACBITS]]]] | 0x40100400) - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a >> 15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnRevSubClampPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[source[frac >> FRACBITS]]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[a & (a >> 15)]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - void DrawColumnRevSubClampTranslatedPalCommand::Execute(DrawerThread *thread) - { - int count; - uint8_t *dest; - fixed_t frac; - fixed_t fracstep; - - count = _count; - dest = _dest; - - fracstep = _iscale; - frac = _texturefrac; - - count = thread->count_for_thread(_dest_y, count); - if (count <= 0) - return; - - int pitch = _pitch; - dest = thread->dest_for_thread(_dest_y, pitch, dest); - frac += fracstep * thread->skipped_by_thread(_dest_y); - fracstep *= thread->num_cores; - pitch *= thread->num_cores; - - const uint8_t *translation = _translation; - const uint8_t *colormap = _colormap; - const uint8_t *source = _source; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - do - { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[translation[source[frac >> FRACBITS]]]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a >> 15) & a]; - dest += pitch; - frac += fracstep; - } while (--count); - } - - ///////////////////////////////////////////////////////////////////////// - - DrawFuzzColumnPalCommand::DrawFuzzColumnPalCommand() - { - using namespace drawerargs; - - _yl = dc_yl; - _yh = dc_yh; - _x = dc_x; - _destorg = dc_destorg; - _pitch = dc_pitch; - _fuzzpos = fuzzpos; - _fuzzviewheight = fuzzviewheight; - } - - void DrawFuzzColumnPalCommand::Execute(DrawerThread *thread) - { - int yl = MAX(_yl, 1); - int yh = MIN(_yh, _fuzzviewheight); - - int count = thread->count_for_thread(yl, yh - yl + 1); - - // Zero length. - if (count <= 0) - return; - - uint8_t *map = &NormalLight.Maps[6 * 256]; - - uint8_t *dest = thread->dest_for_thread(yl, _pitch, ylookup[yl] + _x + _destorg); - - int pitch = _pitch * thread->num_cores; - int fuzzstep = thread->num_cores; - int fuzz = (_fuzzpos + thread->skipped_by_thread(yl)) % FUZZTABLE; - - yl += thread->skipped_by_thread(yl); - - // Handle the case where we would go out of bounds at the top: - if (yl < fuzzstep) - { - uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep + pitch; - //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (_pitch)) < viewheight); - - *dest = map[*srcdest]; - dest += pitch; - fuzz += fuzzstep; - fuzz %= FUZZTABLE; - - count--; - if (count == 0) - return; - } - - bool lowerbounds = (yl + (count + fuzzstep - 1) * fuzzstep > _fuzzviewheight); - if (lowerbounds) - count--; - - // Fuzz where fuzzoffset stays within bounds - while (count > 0) - { - int available = (FUZZTABLE - fuzz); - int next_wrap = available / fuzzstep; - if (available % fuzzstep != 0) - next_wrap++; - - int cnt = MIN(count, next_wrap); - count -= cnt; - do - { - uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep; - //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (_pitch)) < viewheight); - - *dest = map[*srcdest]; - dest += pitch; - fuzz += fuzzstep; - } while (--cnt); - - fuzz %= FUZZTABLE; - } - - // Handle the case where we would go out of bounds at the bottom - if (lowerbounds) - { - uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep - pitch; - //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (_pitch)) < viewheight); - - *dest = map[*srcdest]; - } - } - - ///////////////////////////////////////////////////////////////////////// - - PalSpanCommand::PalSpanCommand() - { - using namespace drawerargs; - - _source = ds_source; - _colormap = ds_colormap; - _xfrac = ds_xfrac; - _yfrac = ds_yfrac; - _y = ds_y; - _x1 = ds_x1; - _x2 = ds_x2; - _destorg = dc_destorg; - _xstep = ds_xstep; - _ystep = ds_ystep; - _xbits = ds_xbits; - _ybits = ds_ybits; - _srcblend = dc_srcblend; - _destblend = dc_destblend; - _color = ds_color; - } - - void DrawSpanPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - // Current texture index in u,v. - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - - // Lookup pixel from flat texture tile, - // re-index using light/colormap. - *dest++ = colormap[source[spot]]; - - // Next step in u,v. - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - - do - { - // Current texture index in u,v. - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - - // Lookup pixel from flat texture tile, - // re-index using light/colormap. - *dest++ = colormap[source[spot]]; - - // Next step in u,v. - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanMaskedPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - int texdata; - - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - texdata = source[spot]; - if (texdata != 0) - { - *dest = colormap[texdata]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - int texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - *dest = colormap[texdata]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanTranslucentPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - uint32_t fg = colormap[source[spot]]; - uint32_t bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest++ = RGB32k.All[fg & (fg >> 15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - uint32_t fg = colormap[source[spot]]; - uint32_t bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest++ = RGB32k.All[fg & (fg >> 15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanMaskedTranslucentPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - uint8_t texdata; - - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - texdata = source[spot]; - if (texdata != 0) - { - uint32_t fg = colormap[texdata]; - uint32_t bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - uint8_t texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - uint32_t fg = colormap[texdata]; - uint32_t bg = *dest; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg + bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg >> 15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanAddClampPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - uint32_t a = fg2rgb[colormap[source[spot]]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest++ = RGB32k.All[a & (a >> 15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - uint32_t a = fg2rgb[colormap[source[spot]]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest++ = RGB32k.All[a & (a >> 15)]; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void DrawSpanMaskedAddClampPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - dsfixed_t xfrac; - dsfixed_t yfrac; - dsfixed_t xstep; - dsfixed_t ystep; - uint8_t *dest; - const uint8_t *source = _source; - const uint8_t *colormap = _colormap; - int count; - int spot; - uint32_t *fg2rgb = _srcblend; - uint32_t *bg2rgb = _destblend; - - xfrac = _xfrac; - yfrac = _yfrac; - - dest = ylookup[_y] + _x1 + _destorg; - - count = _x2 - _x1 + 1; - - xstep = _xstep; - ystep = _ystep; - - if (_xbits == 6 && _ybits == 6) - { - // 64x64 is the most common case by far, so special case it. - do - { - uint8_t texdata; - - spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); - texdata = source[spot]; - if (texdata != 0) - { - uint32_t a = fg2rgb[colormap[texdata]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - else - { - uint8_t yshift = 32 - _ybits; - uint8_t xshift = yshift - _xbits; - int xmask = ((1 << _xbits) - 1) << _ybits; - do - { - uint8_t texdata; - - spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); - texdata = source[spot]; - if (texdata != 0) - { - uint32_t a = fg2rgb[colormap[texdata]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[a & (a >> 15)]; - } - dest++; - xfrac += xstep; - yfrac += ystep; - } while (--count); - } - } - - void FillSpanPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(_y)) - return; - - memset(ylookup[_y] + _x1 + _destorg, _color, _x2 - _x1 + 1); - } - - ///////////////////////////////////////////////////////////////////////// - - DrawTiltedSpanPalCommand::DrawTiltedSpanPalCommand(int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy) - : y(y), x1(x1), x2(x2), plane_sz(plane_sz), plane_su(plane_su), plane_sv(plane_sv), plane_shade(plane_shade), planeshade(planeshade), planelightfloat(planelightfloat), pviewx(pviewx), pviewy(pviewy) - { - using namespace drawerargs; - - _colormap = ds_colormap; - _destorg = dc_destorg; - _ybits = ds_ybits; - _xbits = ds_xbits; - _source = ds_source; - basecolormapdata = basecolormap->Maps; - } - - void DrawTiltedSpanPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(y)) - return; - - const uint8_t **tiltlighting = thread->tiltlighting; - - int width = x2 - x1; - double iz, uz, vz; - uint8_t *fb; - uint32_t u, v; - int i; - - iz = plane_sz[2] + plane_sz[1] * (centery - y) + plane_sz[0] * (x1 - centerx); - - // Lighting is simple. It's just linear interpolation from start to end - if (plane_shade) - { - uz = (iz + plane_sz[0] * width) * planelightfloat; - vz = iz * planelightfloat; - CalcTiltedLighting(vz, uz, width, thread); - } - else - { - for (int i = 0; i < width; ++i) - { - tiltlighting[i] = _colormap; - } - } - - uz = plane_su[2] + plane_su[1] * (centery - y) + plane_su[0] * (x1 - centerx); - vz = plane_sv[2] + plane_sv[1] * (centery - y) + plane_sv[0] * (x1 - centerx); - - fb = ylookup[y] + x1 + _destorg; - - uint8_t vshift = 32 - _ybits; - uint8_t ushift = vshift - _xbits; - int umask = ((1 << _xbits) - 1) << _ybits; - - #if 0 - // The "perfect" reference version of this routine. Pretty slow. - // Use it only to see how things are supposed to look. - i = 0; - do - { - double z = 1.f / iz; - - u = int64_t(uz*z) + pviewx; - v = int64_t(vz*z) + pviewy; - R_SetDSColorMapLight(tiltlighting[i], 0, 0); - fb[i++] = ds_colormap[ds_source[(v >> vshift) | ((u >> ushift) & umask)]]; - iz += plane_sz[0]; - uz += plane_su[0]; - vz += plane_sv[0]; - } while (--width >= 0); - #else - //#define SPANSIZE 32 - //#define INVSPAN 0.03125f - //#define SPANSIZE 8 - //#define INVSPAN 0.125f - #define SPANSIZE 16 - #define INVSPAN 0.0625f - - double startz = 1.f / iz; - double startu = uz*startz; - double startv = vz*startz; - double izstep, uzstep, vzstep; - - izstep = plane_sz[0] * SPANSIZE; - uzstep = plane_su[0] * SPANSIZE; - vzstep = plane_sv[0] * SPANSIZE; - x1 = 0; - width++; - - while (width >= SPANSIZE) - { - iz += izstep; - uz += uzstep; - vz += vzstep; - - double endz = 1.f / iz; - double endu = uz*endz; - double endv = vz*endz; - uint32_t stepu = (uint32_t)int64_t((endu - startu) * INVSPAN); - uint32_t stepv = (uint32_t)int64_t((endv - startv) * INVSPAN); - u = (uint32_t)(int64_t(startu) + pviewx); - v = (uint32_t)(int64_t(startv) + pviewy); - - for (i = SPANSIZE - 1; i >= 0; i--) - { - fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); - x1++; - u += stepu; - v += stepv; - } - startu = endu; - startv = endv; - width -= SPANSIZE; - } - if (width > 0) - { - if (width == 1) - { - u = (uint32_t)int64_t(startu); - v = (uint32_t)int64_t(startv); - fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); - } - else - { - double left = width; - iz += plane_sz[0] * left; - uz += plane_su[0] * left; - vz += plane_sv[0] * left; - - double endz = 1.f / iz; - double endu = uz*endz; - double endv = vz*endz; - left = 1.f / left; - uint32_t stepu = (uint32_t)int64_t((endu - startu) * left); - uint32_t stepv = (uint32_t)int64_t((endv - startv) * left); - u = (uint32_t)(int64_t(startu) + pviewx); - v = (uint32_t)(int64_t(startv) + pviewy); - - for (; width != 0; width--) - { - fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); - x1++; - u += stepu; - v += stepv; - } - } - } - #endif - } - - // Calculates the lighting for one row of a tilted plane. If the definition - // of GETPALOOKUP changes, this needs to change, too. - void DrawTiltedSpanPalCommand::CalcTiltedLighting(double lval, double lend, int width, DrawerThread *thread) - { - const uint8_t **tiltlighting = thread->tiltlighting; - - double lstep; - uint8_t *lightfiller; - int i = 0; - - if (width == 0 || lval == lend) - { // Constant lighting - lightfiller = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); - } - else - { - lstep = (lend - lval) / width; - if (lval >= MAXLIGHTVIS) - { // lval starts "too bright". - lightfiller = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); - for (; i <= width && lval >= MAXLIGHTVIS; ++i) - { - tiltlighting[i] = lightfiller; - lval += lstep; - } - } - if (lend >= MAXLIGHTVIS) - { // lend ends "too bright". - lightfiller = basecolormapdata + (GETPALOOKUP(lend, planeshade) << COLORMAPSHIFT); - for (; width > i && lend >= MAXLIGHTVIS; --width) - { - tiltlighting[width] = lightfiller; - lend -= lstep; - } - } - if (width > 0) - { - lval = FIXED2DBL(planeshade) - lval; - lend = FIXED2DBL(planeshade) - lend; - lstep = (lend - lval) / width; - if (lstep < 0) - { // Going from dark to light - if (lval < 1.) - { // All bright - lightfiller = basecolormapdata; - } - else - { - if (lval >= NUMCOLORMAPS) - { // Starts beyond the dark end - uint8_t *clight = basecolormapdata + ((NUMCOLORMAPS - 1) << COLORMAPSHIFT); - while (lval >= NUMCOLORMAPS && i <= width) - { - tiltlighting[i++] = clight; - lval += lstep; - } - if (i > width) - return; - } - while (i <= width && lval >= 0) - { - tiltlighting[i++] = basecolormapdata + (xs_ToInt(lval) << COLORMAPSHIFT); - lval += lstep; - } - lightfiller = basecolormapdata; - } - } - else - { // Going from light to dark - if (lval >= (NUMCOLORMAPS - 1)) - { // All dark - lightfiller = basecolormapdata + ((NUMCOLORMAPS - 1) << COLORMAPSHIFT); - } - else - { - while (lval < 0 && i <= width) - { - tiltlighting[i++] = basecolormapdata; - lval += lstep; - } - if (i > width) - return; - while (i <= width && lval < (NUMCOLORMAPS - 1)) - { - tiltlighting[i++] = basecolormapdata + (xs_ToInt(lval) << COLORMAPSHIFT); - lval += lstep; - } - lightfiller = basecolormapdata + ((NUMCOLORMAPS - 1) << COLORMAPSHIFT); - } - } - } - } - for (; i <= width; i++) - { - tiltlighting[i] = lightfiller; - } - } - - ///////////////////////////////////////////////////////////////////////// - - DrawColoredSpanPalCommand::DrawColoredSpanPalCommand(int y, int x1, int x2) : y(y), x1(x1), x2(x2) - { - using namespace drawerargs; - color = ds_color; - destorg = dc_destorg; - } - - void DrawColoredSpanPalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(y)) - return; - - memset(ylookup[y] + x1 + destorg, color, x2 - x1 + 1); - } - - ///////////////////////////////////////////////////////////////////////// - - DrawSlabPalCommand::DrawSlabPalCommand(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p, const uint8_t *colormap) - : _dx(dx), _v(v), _dy(dy), _vi(vi), _vvptr(vptr), _p(p), _colormap(colormap) - { - using namespace drawerargs; - _pitch = dc_pitch; - _start_y = static_cast((p - dc_destorg) / dc_pitch); - } - - void DrawSlabPalCommand::Execute(DrawerThread *thread) - { - int count = _dy; - uint8_t *dest = _p; - int pitch = _pitch; - int width = _dx; - const uint8_t *colormap = _colormap; - const uint8_t *source = _vvptr; - fixed_t fracpos = _v; - fixed_t iscale = _vi; - - count = thread->count_for_thread(_start_y, count); - dest = thread->dest_for_thread(_start_y, pitch, dest); - fracpos += iscale * thread->skipped_by_thread(_start_y); - iscale *= thread->num_cores; - pitch *= thread->num_cores; - - while (count > 0) - { - uint8_t color = colormap[source[fracpos >> FRACBITS]]; - - for (int x = 0; x < width; x++) - dest[x] = color; - - dest += pitch; - fracpos += iscale; - count--; - } - } - - ///////////////////////////////////////////////////////////////////////// - - DrawFogBoundaryLinePalCommand::DrawFogBoundaryLinePalCommand(int y, int x1, int x2) : y(y), x1(x1), x2(x2) - { - using namespace drawerargs; - _colormap = dc_colormap; - _destorg = dc_destorg; - } - - void DrawFogBoundaryLinePalCommand::Execute(DrawerThread *thread) - { - if (thread->line_skipped_by_thread(y)) - return; - - const uint8_t *colormap = _colormap; - uint8_t *dest = ylookup[y] + _destorg; - int x = x1; - do - { - dest[x] = colormap[dest[x]]; - } while (++x <= x2); - } -} diff --git a/src/r_draw_pal.h b/src/r_draw_pal.h deleted file mode 100644 index ab3c98327..000000000 --- a/src/r_draw_pal.h +++ /dev/null @@ -1,329 +0,0 @@ - -#pragma once - -#include "r_draw.h" -#include "v_palette.h" -#include "r_thread.h" - -namespace swrenderer -{ - class PalWall1Command : public DrawerCommand - { - public: - PalWall1Command(); - FString DebugInfo() override { return "PalWallCommand"; } - - protected: - uint32_t _iscale; - uint32_t _texturefrac; - uint8_t *_colormap; - int _count; - const uint8_t *_source; - uint8_t *_dest; - int _fracbits; - int _pitch; - uint32_t *_srcblend; - uint32_t *_destblend; - }; - - class PalWall4Command : public DrawerCommand - { - public: - PalWall4Command(); - FString DebugInfo() override { return "PalWallCommand"; } - - protected: - uint8_t *_dest; - int _count; - int _pitch; - int _fracbits; - uint8_t *_colormap[4]; - const uint8_t *_source[4]; - uint32_t _iscale[4]; - uint32_t _texturefrac[4]; - uint32_t *_srcblend; - uint32_t *_destblend; - }; - - class DrawWall1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWall4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallMasked1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallMasked4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallAdd1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallAdd4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallAddClamp1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallAddClamp4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallSubClamp1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallSubClamp4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallRevSubClamp1PalCommand : public PalWall1Command { public: void Execute(DrawerThread *thread) override; }; - class DrawWallRevSubClamp4PalCommand : public PalWall4Command { public: void Execute(DrawerThread *thread) override; }; - - class PalSkyCommand : public DrawerCommand - { - public: - PalSkyCommand(uint32_t solid_top, uint32_t solid_bottom); - FString DebugInfo() override { return "PalSkyCommand"; } - - protected: - uint32_t solid_top; - uint32_t solid_bottom; - - uint8_t *_dest; - int _count; - int _pitch; - const uint8_t *_source[4]; - const uint8_t *_source2[4]; - int _sourceheight[4]; - uint32_t _iscale[4]; - uint32_t _texturefrac[4]; - }; - - class DrawSingleSky1PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; - class DrawSingleSky4PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; - class DrawDoubleSky1PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; - class DrawDoubleSky4PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; - - class PalColumnCommand : public DrawerCommand - { - public: - PalColumnCommand(); - FString DebugInfo() override { return "PalColumnCommand"; } - - protected: - int _count; - uint8_t *_dest; - int _pitch; - fixed_t _iscale; - fixed_t _texturefrac; - const uint8_t *_colormap; - const uint8_t *_source; - const uint8_t *_translation; - int _color; - uint32_t *_srcblend; - uint32_t *_destblend; - uint32_t _srccolor; - }; - - class DrawColumnPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnAddPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnAddClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnSubClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class FillColumnRevSubClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnAddPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnTranslatedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnTlatedAddPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnShadedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnAddClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnAddClampTranslatedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnSubClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnSubClampTranslatedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnRevSubClampPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawColumnRevSubClampTranslatedPalCommand : public PalColumnCommand { public: void Execute(DrawerThread *thread) override; }; - - class DrawFuzzColumnPalCommand : public DrawerCommand - { - public: - DrawFuzzColumnPalCommand(); - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "DrawFuzzColumnPalCommand"; } - - private: - int _yl; - int _yh; - int _x; - uint8_t *_destorg; - int _pitch; - int _fuzzpos; - int _fuzzviewheight; - }; - - class PalSpanCommand : public DrawerCommand - { - public: - PalSpanCommand(); - FString DebugInfo() override { return "PalSpanCommand"; } - - protected: - const uint8_t *_source; - const uint8_t *_colormap; - dsfixed_t _xfrac; - dsfixed_t _yfrac; - int _y; - int _x1; - int _x2; - uint8_t *_destorg; - dsfixed_t _xstep; - dsfixed_t _ystep; - int _xbits; - int _ybits; - uint32_t *_srcblend; - uint32_t *_destblend; - int _color; - }; - - class DrawSpanPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanMaskedPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanTranslucentPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanMaskedTranslucentPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanAddClampPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class DrawSpanMaskedAddClampPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - class FillSpanPalCommand : public PalSpanCommand { public: void Execute(DrawerThread *thread) override; }; - - class DrawTiltedSpanPalCommand : public DrawerCommand - { - public: - DrawTiltedSpanPalCommand(int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy); - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "DrawTiltedSpanPalCommand"; } - - private: - void CalcTiltedLighting(double lval, double lend, int width, DrawerThread *thread); - - int y; - int x1; - int x2; - FVector3 plane_sz; - FVector3 plane_su; - FVector3 plane_sv; - bool plane_shade; - int planeshade; - float planelightfloat; - fixed_t pviewx; - fixed_t pviewy; - - const uint8_t *_colormap; - uint8_t *_destorg; - int _ybits; - int _xbits; - const uint8_t *_source; - uint8_t *basecolormapdata; - }; - - class DrawColoredSpanPalCommand : public PalSpanCommand - { - public: - DrawColoredSpanPalCommand(int y, int x1, int x2); - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "DrawColoredSpanPalCommand"; } - - private: - int y; - int x1; - int x2; - int color; - uint8_t *destorg; - }; - - class DrawSlabPalCommand : public PalSpanCommand - { - public: - DrawSlabPalCommand(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p, const uint8_t *colormap); - void Execute(DrawerThread *thread) override; - - private: - int _dx; - fixed_t _v; - int _dy; - fixed_t _vi; - const uint8_t *_vvptr; - uint8_t *_p; - const uint8_t *_colormap; - int _pitch; - int _start_y; - }; - - class DrawFogBoundaryLinePalCommand : public PalSpanCommand - { - public: - DrawFogBoundaryLinePalCommand(int y, int x1, int x2); - void Execute(DrawerThread *thread) override; - - private: - int y, x1, x2; - const uint8_t *_colormap; - uint8_t *_destorg; - }; - - class RtInitColsPalCommand : public DrawerCommand - { - public: - RtInitColsPalCommand(uint8_t *buff); - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "RtInitColsPalCommand"; } - - private: - uint8_t *buff; - }; - - class PalColumnHorizCommand : public DrawerCommand - { - public: - PalColumnHorizCommand(); - - protected: - const uint8_t *_source; - fixed_t _iscale; - fixed_t _texturefrac; - int _count; - int _color; - int _x; - int _yl; - }; - - class DrawColumnHorizPalCommand : public PalColumnHorizCommand - { - public: - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "DrawColumnHorizPalCommand"; } - }; - - class FillColumnHorizPalCommand : public PalColumnHorizCommand - { - public: - void Execute(DrawerThread *thread) override; - FString DebugInfo() override { return "FillColumnHorizPalCommand"; } - }; - - class PalRtCommand : public DrawerCommand - { - public: - PalRtCommand(int hx, int sx, int yl, int yh); - FString DebugInfo() override { return "PalRtCommand"; } - - protected: - int hx, sx, yl, yh; - uint8_t *_destorg; - int _pitch; - const uint8_t *_colormap; - const uint32_t *_srcblend; - const uint32_t *_destblend; - const uint8_t *_translation; - int _color; - }; - - class DrawColumnRt1CopyPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4CopyPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1PalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4PalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1TranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4TranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1AddPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4AddPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt1AddTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt4AddTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1ShadedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4ShadedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1AddClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4AddClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt1AddClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt4AddClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1SubClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4SubClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt1SubClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt4SubClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt1RevSubClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - class DrawColumnRt4RevSubClampPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt1RevSubClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; - //class DrawColumnRt4RevSubClampTranslatedPalCommand : public PalRtCommand { public: using PalRtCommand::PalRtCommand; void Execute(DrawerThread *thread) override; }; -} diff --git a/src/r_drawt_pal.cpp b/src/r_drawt_pal.cpp deleted file mode 100644 index 3356592d2..000000000 --- a/src/r_drawt_pal.cpp +++ /dev/null @@ -1,867 +0,0 @@ -/* -** r_drawt.cpp -** Faster column drawers for modern processors -** -**--------------------------------------------------------------------------- -** Copyright 1998-2006 Randy Heit -** 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. -**--------------------------------------------------------------------------- -** -** These functions stretch columns into a temporary buffer and then -** map them to the screen. On modern machines, this is faster than drawing -** them directly to the screen. -** -** Will I be able to even understand any of this if I come back to it later? -** Let's hope so. :-) -*/ - -#include "templates.h" -#include "doomtype.h" -#include "doomdef.h" -#include "r_defs.h" -#include "r_draw.h" -#include "r_main.h" -#include "r_things.h" -#include "v_video.h" -#include "r_draw_pal.h" - -// I should have commented this stuff better. -// -// dc_temp is the buffer R_DrawColumnHoriz writes into. -// dc_tspans points into it. -// dc_ctspan points into dc_tspans. -// horizspan also points into dc_tspans. - -// dc_ctspan is advanced while drawing into dc_temp. -// horizspan is advanced up to dc_ctspan when drawing from dc_temp to the screen. - -namespace swrenderer -{ - RtInitColsPalCommand::RtInitColsPalCommand(uint8_t *buff) : buff(buff) - { - } - - void RtInitColsPalCommand::Execute(DrawerThread *thread) - { - thread->dc_temp = buff == nullptr ? thread->dc_temp_buff : buff; - } - - ///////////////////////////////////////////////////////////////////// - - PalColumnHorizCommand::PalColumnHorizCommand() - { - using namespace drawerargs; - - _source = dc_source; - _iscale = dc_iscale; - _texturefrac = dc_texturefrac; - _count = dc_count; - _color = dc_color; - _x = dc_x; - _yl = dc_yl; - } - - void DrawColumnHorizPalCommand::Execute(DrawerThread *thread) - { - int count = _count; - uint8_t *dest; - fixed_t fracstep; - fixed_t frac; - - count = thread->count_for_thread(_yl, count); - if (count <= 0) - return; - - fracstep = _iscale; - frac = _texturefrac; - - const uint8_t *source = _source; - - int x = _x & 3; - dest = &thread->dc_temp[x + thread->temp_line_for_thread(_yl) * 4]; - frac += fracstep * thread->skipped_by_thread(_yl); - fracstep *= thread->num_cores; - - if (count & 1) { - *dest = source[frac >> FRACBITS]; dest += 4; frac += fracstep; - } - if (count & 2) { - dest[0] = source[frac >> FRACBITS]; frac += fracstep; - dest[4] = source[frac >> FRACBITS]; frac += fracstep; - dest += 8; - } - if (count & 4) { - dest[0] = source[frac >> FRACBITS]; frac += fracstep; - dest[4] = source[frac >> FRACBITS]; frac += fracstep; - dest[8] = source[frac >> FRACBITS]; frac += fracstep; - dest[12] = source[frac >> FRACBITS]; frac += fracstep; - dest += 16; - } - count >>= 3; - if (!count) return; - - do - { - dest[0] = source[frac >> FRACBITS]; frac += fracstep; - dest[4] = source[frac >> FRACBITS]; frac += fracstep; - dest[8] = source[frac >> FRACBITS]; frac += fracstep; - dest[12] = source[frac >> FRACBITS]; frac += fracstep; - dest[16] = source[frac >> FRACBITS]; frac += fracstep; - dest[20] = source[frac >> FRACBITS]; frac += fracstep; - dest[24] = source[frac >> FRACBITS]; frac += fracstep; - dest[28] = source[frac >> FRACBITS]; frac += fracstep; - dest += 32; - } while (--count); - } - - void FillColumnHorizPalCommand::Execute(DrawerThread *thread) - { - int count = _count; - uint8_t color = _color; - uint8_t *dest; - - count = thread->count_for_thread(_yl, count); - if (count <= 0) - return; - - int x = _x & 3; - dest = &thread->dc_temp[x + thread->temp_line_for_thread(_yl) * 4]; - - if (count & 1) { - *dest = color; - dest += 4; - } - if (!(count >>= 1)) - return; - do { - dest[0] = color; dest[4] = color; - dest += 8; - } while (--count); - } - - ///////////////////////////////////////////////////////////////////// - - PalRtCommand::PalRtCommand(int hx, int sx, int yl, int yh) : hx(hx), sx(sx), yl(yl), yh(yh) - { - using namespace drawerargs; - - _destorg = dc_destorg; - _pitch = dc_pitch; - _colormap = dc_colormap; - _srcblend = dc_srcblend; - _destblend = dc_destblend; - _translation = dc_translation; - _color = dc_color; - } - - void DrawColumnRt1CopyPalCommand::Execute(DrawerThread *thread) - { - uint8_t *source; - uint8_t *dest; - int count; - int pitch; - - count = yh - yl + 1; - - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - - if (count & 1) { - *dest = *source; - source += 4; - dest += pitch; - } - if (count & 2) { - dest[0] = source[0]; - dest[pitch] = source[4]; - source += 8; - dest += pitch*2; - } - if (!(count >>= 2)) - return; - - do { - dest[0] = source[0]; - dest[pitch] = source[4]; - dest[pitch*2] = source[8]; - dest[pitch*3] = source[12]; - source += 16; - dest += pitch*4; - } while (--count); - } - - void DrawColumnRt4CopyPalCommand::Execute(DrawerThread *thread) - { - int *source; - int *dest; - int count; - int pitch; - - count = yh - yl + 1; - - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - dest = (int *)(ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg); - source = (int *)(&thread->dc_temp[thread->temp_line_for_thread(yl)*4]); - pitch = _pitch*thread->num_cores/sizeof(int); - - if (count & 1) { - *dest = *source; - source += 4/sizeof(int); - dest += pitch; - } - if (!(count >>= 1)) - return; - - do { - dest[0] = source[0]; - dest[pitch] = source[4/sizeof(int)]; - source += 8/sizeof(int); - dest += pitch*2; - } while (--count); - } - - void DrawColumnRt1PalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int count; - int pitch; - - count = yh - yl + 1; - - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - colormap = _colormap; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl) *4 + hx]; - pitch = _pitch*thread->num_cores; - - if (count & 1) { - *dest = colormap[*source]; - source += 4; - dest += pitch; - } - if (!(count >>= 1)) - return; - - do { - dest[0] = colormap[source[0]]; - dest[pitch] = colormap[source[4]]; - source += 8; - dest += pitch*2; - } while (--count); - } - - void DrawColumnRt4PalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int count; - int pitch; - - count = yh - yl + 1; - - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - colormap = _colormap; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch*thread->num_cores; - - if (count & 1) { - dest[0] = colormap[source[0]]; - dest[1] = colormap[source[1]]; - dest[2] = colormap[source[2]]; - dest[3] = colormap[source[3]]; - source += 4; - dest += pitch; - } - if (!(count >>= 1)) - return; - - do { - dest[0] = colormap[source[0]]; - dest[1] = colormap[source[1]]; - dest[2] = colormap[source[2]]; - dest[3] = colormap[source[3]]; - dest[pitch] = colormap[source[4]]; - dest[pitch+1] = colormap[source[5]]; - dest[pitch+2] = colormap[source[6]]; - dest[pitch+3] = colormap[source[7]]; - source += 8; - dest += pitch*2; - } while (--count); - } - - void DrawColumnRt1TranslatedPalCommand::Execute(DrawerThread *thread) - { - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - uint8_t *source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - const uint8_t *translation = _translation; - - // Things we do to hit the compiler's optimizer with a clue bat: - // 1. Parallelism is explicitly spelled out by using a separate - // C instruction for each assembly instruction. GCC lets me - // have four temporaries, but VC++ spills to the stack with - // more than two. Two is probably optimal, anyway. - // 2. The results of the translation lookups are explicitly - // stored in byte-sized variables. This causes the VC++ code - // to use byte mov instructions in most cases; for apparently - // random reasons, it will use movzx for some places. GCC - // ignores this and uses movzx always. - - // Do 8 rows at a time. - for (int count8 = count >> 3; count8; --count8) - { - int c0, c1; - uint8_t b0, b1; - - c0 = source[0]; c1 = source[4]; - b0 = translation[c0]; b1 = translation[c1]; - source[0] = b0; source[4] = b1; - - c0 = source[8]; c1 = source[12]; - b0 = translation[c0]; b1 = translation[c1]; - source[8] = b0; source[12] = b1; - - c0 = source[16]; c1 = source[20]; - b0 = translation[c0]; b1 = translation[c1]; - source[16] = b0; source[20] = b1; - - c0 = source[24]; c1 = source[28]; - b0 = translation[c0]; b1 = translation[c1]; - source[24] = b0; source[28] = b1; - - source += 32; - } - // Finish by doing 1 row at a time. - for (count &= 7; count; --count, source += 4) - { - source[0] = translation[source[0]]; - } - } - - void DrawColumnRt4TranslatedPalCommand::Execute(DrawerThread *thread) - { - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - uint8_t *source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - const uint8_t *translation = _translation; - int c0, c1; - uint8_t b0, b1; - - // Do 2 rows at a time. - for (int count8 = count >> 1; count8; --count8) - { - c0 = source[0]; c1 = source[1]; - b0 = translation[c0]; b1 = translation[c1]; - source[0] = b0; source[1] = b1; - - c0 = source[2]; c1 = source[3]; - b0 = translation[c0]; b1 = translation[c1]; - source[2] = b0; source[3] = b1; - - c0 = source[4]; c1 = source[5]; - b0 = translation[c0]; b1 = translation[c1]; - source[4] = b0; source[5] = b1; - - c0 = source[6]; c1 = source[7]; - b0 = translation[c0]; b1 = translation[c1]; - source[6] = b0; source[7] = b1; - - source += 8; - } - // Do the final row if count was odd. - if (count & 1) - { - c0 = source[0]; c1 = source[1]; - b0 = translation[c0]; b1 = translation[c1]; - source[0] = b0; source[1] = b1; - - c0 = source[2]; c1 = source[3]; - b0 = translation[c0]; b1 = translation[c1]; - source[2] = b0; source[3] = b1; - } - } - - void DrawColumnRt1AddPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t fg = colormap[*source]; - uint32_t bg = *dest; - - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[fg & (fg>>15)]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4AddPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t fg = colormap[source[0]]; - uint32_t bg = dest[0]; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - dest[0] = RGB32k.All[fg & (fg>>15)]; - - fg = colormap[source[1]]; - bg = dest[1]; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - dest[1] = RGB32k.All[fg & (fg>>15)]; - - - fg = colormap[source[2]]; - bg = dest[2]; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - dest[2] = RGB32k.All[fg & (fg>>15)]; - - fg = colormap[source[3]]; - bg = dest[3]; - fg = fg2rgb[fg]; - bg = bg2rgb[bg]; - fg = (fg+bg) | 0x1f07c1f; - dest[3] = RGB32k.All[fg & (fg>>15)]; - - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt1ShadedPalCommand::Execute(DrawerThread *thread) - { - uint32_t *fgstart; - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - fgstart = &Col2RGB8[0][_color]; - colormap = _colormap; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - - do { - uint32_t val = colormap[*source]; - uint32_t fg = fgstart[val<<8]; - val = (Col2RGB8[64-val][*dest] + fg) | 0x1f07c1f; - *dest = RGB32k.All[val & (val>>15)]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4ShadedPalCommand::Execute(DrawerThread *thread) - { - uint32_t *fgstart; - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - fgstart = &Col2RGB8[0][_color]; - colormap = _colormap; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - - do { - uint32_t val; - - val = colormap[source[0]]; - val = (Col2RGB8[64-val][dest[0]] + fgstart[val<<8]) | 0x1f07c1f; - dest[0] = RGB32k.All[val & (val>>15)]; - - val = colormap[source[1]]; - val = (Col2RGB8[64-val][dest[1]] + fgstart[val<<8]) | 0x1f07c1f; - dest[1] = RGB32k.All[val & (val>>15)]; - - val = colormap[source[2]]; - val = (Col2RGB8[64-val][dest[2]] + fgstart[val<<8]) | 0x1f07c1f; - dest[2] = RGB32k.All[val & (val>>15)]; - - val = colormap[source[3]]; - val = (Col2RGB8[64-val][dest[3]] + fgstart[val<<8]) | 0x1f07c1f; - dest[3] = RGB32k.All[val & (val>>15)]; - - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt1AddClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = fg2rgb[colormap[*source]] + bg2rgb[*dest]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - *dest = RGB32k.All[(a>>15) & a]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4AddClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - - do { - uint32_t a = fg2rgb[colormap[source[0]]] + bg2rgb[dest[0]]; - uint32_t b = a; - - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[0] = RGB32k.All[(a>>15) & a]; - - a = fg2rgb[colormap[source[1]]] + bg2rgb[dest[1]]; - b = a; - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[1] = RGB32k.All[(a>>15) & a]; - - a = fg2rgb[colormap[source[2]]] + bg2rgb[dest[2]]; - b = a; - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[2] = RGB32k.All[(a>>15) & a]; - - a = fg2rgb[colormap[source[3]]] + bg2rgb[dest[3]]; - b = a; - a |= 0x01f07c1f; - b &= 0x40100400; - a &= 0x3fffffff; - b = b - (b >> 5); - a |= b; - dest[3] = RGB32k.All[(a>>15) & a]; - - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt1SubClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = (fg2rgb[colormap[*source]] | 0x40100400) - bg2rgb[*dest]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a>>15) & a]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4SubClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = (fg2rgb[colormap[source[0]]] | 0x40100400) - bg2rgb[dest[0]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[0] = RGB32k.All[(a>>15) & a]; - - a = (fg2rgb[colormap[source[1]]] | 0x40100400) - bg2rgb[dest[1]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[1] = RGB32k.All[(a>>15) & a]; - - a = (fg2rgb[colormap[source[2]]] | 0x40100400) - bg2rgb[dest[2]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[2] = RGB32k.All[(a>>15) & a]; - - a = (fg2rgb[colormap[source[3]]] | 0x40100400) - bg2rgb[dest[3]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[3] = RGB32k.All[(a>>15) & a]; - - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt1RevSubClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4 + hx]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[*source]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - *dest = RGB32k.All[(a>>15) & a]; - source += 4; - dest += pitch; - } while (--count); - } - - void DrawColumnRt4RevSubClampPalCommand::Execute(DrawerThread *thread) - { - const uint8_t *colormap; - uint8_t *source; - uint8_t *dest; - int pitch; - - int count = yh - yl + 1; - count = thread->count_for_thread(yl, count); - if (count <= 0) - return; - - const uint32_t *fg2rgb = _srcblend; - const uint32_t *bg2rgb = _destblend; - dest = ylookup[yl + thread->skipped_by_thread(yl)] + sx + _destorg; - source = &thread->dc_temp[thread->temp_line_for_thread(yl)*4]; - pitch = _pitch * thread->num_cores; - colormap = _colormap; - - do { - uint32_t a = (bg2rgb[dest[0]] | 0x40100400) - fg2rgb[colormap[source[0]]]; - uint32_t b = a; - - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[0] = RGB32k.All[(a>>15) & a]; - - a = (bg2rgb[dest[1]] | 0x40100400) - fg2rgb[colormap[source[1]]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[1] = RGB32k.All[(a>>15) & a]; - - a = (bg2rgb[dest[2]] | 0x40100400) - fg2rgb[colormap[source[2]]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[2] = RGB32k.All[(a>>15) & a]; - - a = (bg2rgb[dest[3]] | 0x40100400) - fg2rgb[colormap[source[3]]]; - b = a; - b &= 0x40100400; - b = b - (b >> 5); - a &= b; - a |= 0x01f07c1f; - dest[3] = RGB32k.All[(a>>15) & a]; - - source += 4; - dest += pitch; - } while (--count); - } -} diff --git a/src/r_local.h b/src/r_local.h deleted file mode 100644 index 7977e6923..000000000 --- a/src/r_local.h +++ /dev/null @@ -1,41 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Refresh (R_*) module, global header. -// All the rendering/drawing stuff is here. -// -//----------------------------------------------------------------------------- - -#ifndef __R_LOCAL_H__ -#define __R_LOCAL_H__ - -// Binary Angles, sine/cosine/atan lookups. -#include "tables.h" - -// Screen size related parameters. -#include "doomdef.h" - -// Include the refresh/render data structs. - -// -// Separate header file for each module. -// -#include "r_main.h" -#include "r_things.h" -#include "r_draw.h" - -#endif // __R_LOCAL_H__ diff --git a/src/r_main.cpp b/src/r_main.cpp deleted file mode 100644 index b87119b88..000000000 --- a/src/r_main.cpp +++ /dev/null @@ -1,1112 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// Rendering main loop and setup functions, -// utility functions (BSP, geometry, trigonometry). -// See tables.c, too. -// -//----------------------------------------------------------------------------- - -// HEADER FILES ------------------------------------------------------------ - -#include -#include - -#include "templates.h" -#include "doomdef.h" -#include "d_net.h" -#include "doomstat.h" -#include "m_random.h" -#include "m_bbox.h" -#include "r_local.h" -#include "r_plane.h" -#include "r_bsp.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "r_sky.h" -#include "st_stuff.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "v_video.h" -#include "stats.h" -#include "i_video.h" -#include "i_system.h" -#include "a_sharedglobal.h" -#include "r_data/r_translate.h" -#include "p_3dmidtex.h" -#include "r_data/r_interpolate.h" -#include "v_palette.h" -#include "po_man.h" -#include "p_effect.h" -#include "st_start.h" -#include "v_font.h" -#include "r_data/colormaps.h" -#include "farchive.h" - -// MACROS ------------------------------------------------------------------ - -#if 0 -#define TEST_X 32343794 -#define TEST_Y 111387517 -#define TEST_Z 2164524 -#define TEST_ANGLE 2468347904 -#endif - - -// TYPES ------------------------------------------------------------------- - -// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- - -void R_SpanInitData (); -void R_DeinitSprites(); - -// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- - -// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static void R_ShutdownRenderer(); - -// EXTERNAL DATA DECLARATIONS ---------------------------------------------- - -extern short *openings; -extern bool r_fakingunderwater; -extern "C" int fuzzviewheight; -extern subsector_t *InSubsector; -extern bool r_showviewer; - - -// PRIVATE DATA DECLARATIONS ----------------------------------------------- - -static double CurrentVisibility = 8.f; -static double MaxVisForWall; -static double MaxVisForFloor; -bool r_dontmaplines; - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -CVAR (String, r_viewsize, "", CVAR_NOSET) -CVAR (Bool, r_shadercolormaps, true, CVAR_ARCHIVE) - -double r_BaseVisibility; -double r_WallVisibility; -double r_FloorVisibility; -float r_TiltVisibility; -double r_SpriteVisibility; -double r_ParticleVisibility; - -double GlobVis; -fixed_t viewingrangerecip; -double FocalLengthX; -double FocalLengthY; -FDynamicColormap*basecolormap; // [RH] colormap currently drawing with -int fixedlightlev; -lighttable_t *fixedcolormap; -FSpecialColormap *realfixedcolormap; -double WallTMapScale2; - - -bool bRenderingToCanvas; // [RH] True if rendering to a special canvas -double globaluclip, globaldclip; -double CenterX, CenterY; -double YaspectMul; -double BaseYaspectMul; // yaspectmul without a forced aspect ratio -double IYaspectMul; -double InvZtoScale; - -// just for profiling purposes -int linecount; -int loopcount; - - -// -// precalculated math tables -// - -// The xtoviewangleangle[] table maps a screen pixel -// to the lowest viewangle that maps back to x ranges -// from clipangle to -clipangle. -angle_t xtoviewangle[MAXWIDTH+1]; - -bool foggy; // [RH] ignore extralight and fullbright? -int r_actualextralight; - -void (*colfunc) (void); -void (*basecolfunc) (void); -void (*fuzzcolfunc) (void); -void (*transcolfunc) (void); -void (*spanfunc) (void); - -void (*hcolfunc_pre) (void); -void (*hcolfunc_post1) (int hx, int sx, int yl, int yh); -void (*hcolfunc_post2) (int hx, int sx, int yl, int yh); -void (*hcolfunc_post4) (int sx, int yl, int yh); - -cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static int lastcenteryfrac; - -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// viewangletox -// -// Used solely for construction the xtoviewangle table. -// -//========================================================================== - -static inline int viewangletox(int i) -{ - if (finetangent[i] > FRACUNIT*2) - { - return -1; - } - else if (finetangent[i] < -FRACUNIT*2) - { - return viewwidth+1; - } - else - { - double t = FIXED2DBL(finetangent[i]) * FocalLengthX; - return clamp(xs_CeilToInt(CenterX - t), -1, viewwidth+1); - } -} - -//========================================================================== -// -// R_InitTextureMapping -// -//========================================================================== - -void R_InitTextureMapping () -{ - int i, x; - - // Calc focallength so FieldOfView fineangles covers viewwidth. - FocalLengthX = CenterX / FocalTangent; - FocalLengthY = FocalLengthX * YaspectMul; - - // This is 1/FocalTangent before the widescreen extension of FOV. - viewingrangerecip = DivScale32(1, finetangent[FINEANGLES/4+(FieldOfView/2)]); - - // [RH] Do not generate viewangletox, because texture mapping is no - // longer done with trig, so it's not needed. - - // Now generate xtoviewangle for sky texture mapping. - // We do this with a hybrid approach: The center 90 degree span is - // constructed as per the original code: - // Scan xtoviewangle to find the smallest view angle that maps to x. - // (viewangletox is sorted in non-increasing order.) - // This reduces the chances of "doubling-up" of texture columns in - // the drawn sky texture. - // The remaining arcs are done with tantoangle instead. - - const int t1 = MAX(int(CenterX - FocalLengthX), 0); - const int t2 = MIN(int(CenterX + FocalLengthX), viewwidth); - const fixed_t dfocus = FLOAT2FIXED(FocalLengthX) >> DBITS; - - for (i = 0, x = t2; x >= t1; --x) - { - while (viewangletox(i) > x) - { - ++i; - } - xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANGLE_90; - } - for (x = t2 + 1; x <= viewwidth; ++x) - { - xtoviewangle[x] = ANGLE_270 + tantoangle[dfocus / (x - centerx)]; - } - for (x = 0; x < t1; ++x) - { - xtoviewangle[x] = (angle_t)(-(signed)xtoviewangle[viewwidth - x]); - } -} - -//========================================================================== -// -// R_SetVisibility -// -// Changes how rapidly things get dark with distance -// -//========================================================================== - -void R_SetVisibility(double vis) -{ - // Allow negative visibilities, just for novelty's sake - vis = clamp(vis, -204.7, 204.7); // (205 and larger do not work in 5:4 aspect ratio) - - CurrentVisibility = vis; - - if (FocalTangent == 0 || FocalLengthY == 0) - { // If r_visibility is called before the renderer is all set up, don't - // divide by zero. This will be called again later, and the proper - // values can be initialized then. - return; - } - - r_BaseVisibility = vis; - - // Prevent overflow on walls - if (r_BaseVisibility < 0 && r_BaseVisibility < -MaxVisForWall) - r_WallVisibility = -MaxVisForWall; - else if (r_BaseVisibility > 0 && r_BaseVisibility > MaxVisForWall) - r_WallVisibility = MaxVisForWall; - else - r_WallVisibility = r_BaseVisibility; - - r_WallVisibility = (InvZtoScale * SCREENWIDTH*BaseRatioSizes[WidescreenRatio][1] / - (viewwidth*SCREENHEIGHT*3)) * (r_WallVisibility * FocalTangent); - - // Prevent overflow on floors/ceilings. Note that the calculation of - // MaxVisForFloor means that planes less than two units from the player's - // view could still overflow, but there is no way to totally eliminate - // that while still using fixed point math. - if (r_BaseVisibility < 0 && r_BaseVisibility < -MaxVisForFloor) - r_FloorVisibility = -MaxVisForFloor; - else if (r_BaseVisibility > 0 && r_BaseVisibility > MaxVisForFloor) - r_FloorVisibility = MaxVisForFloor; - else - r_FloorVisibility = r_BaseVisibility; - - r_FloorVisibility = 160.0 * r_FloorVisibility / FocalLengthY; - - r_TiltVisibility = float(vis * FocalTangent * (16.f * 320.f) / viewwidth); - r_SpriteVisibility = r_WallVisibility; -} - -//========================================================================== -// -// R_GetVisibility -// -//========================================================================== - -double R_GetVisibility() -{ - return CurrentVisibility; -} - -//========================================================================== -// -// CCMD r_visibility -// -// Controls how quickly light ramps across a 1/z range. Set this, and it -// sets all the r_*Visibility variables (except r_SkyVisibilily, which is -// currently unused). -// -//========================================================================== - -CCMD (r_visibility) -{ - if (argv.argc() < 2) - { - Printf ("Visibility is %g\n", R_GetVisibility()); - } - else if (!netgame) - { - R_SetVisibility(atof(argv[1])); - } - else - { - Printf ("Visibility cannot be changed in net games.\n"); - } -} - -//========================================================================== -// -// R_SetWindow -// -//========================================================================== - -void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio) -{ - int virtheight, virtwidth, virtwidth2, virtheight2; - - if (!bRenderingToCanvas) - { // Set r_viewsize cvar to reflect the current view size - UCVarValue value; - char temp[16]; - - mysnprintf (temp, countof(temp), "%d x %d", viewwidth, viewheight); - value.String = temp; - r_viewsize.ForceSet (value, CVAR_String); - } - - fuzzviewheight = viewheight - 2; // Maximum row the fuzzer can draw to - halfviewwidth = (viewwidth >> 1) - 1; - - lastcenteryfrac = 1<<30; - CenterX = centerx; - CenterY = centery; - - virtwidth = virtwidth2 = fullWidth; - virtheight = virtheight2 = fullHeight; - - if (Is54Aspect(trueratio)) - { - virtheight2 = virtheight2 * BaseRatioSizes[trueratio][3] / 48; - } - else - { - virtwidth2 = virtwidth2 * BaseRatioSizes[trueratio][3] / 48; - } - - if (Is54Aspect(WidescreenRatio)) - { - virtheight = virtheight * BaseRatioSizes[WidescreenRatio][3] / 48; - } - else - { - virtwidth = virtwidth * BaseRatioSizes[WidescreenRatio][3] / 48; - } - - BaseYaspectMul = 320.0 * virtheight2 / (r_Yaspect * virtwidth2); - YaspectMul = 320.0 * virtheight / (r_Yaspect * virtwidth); - IYaspectMul = (double)virtwidth * r_Yaspect / 320.0 / virtheight; - InvZtoScale = YaspectMul * CenterX; - - WallTMapScale2 = IYaspectMul / CenterX; - - // psprite scales - pspritexscale = centerxwide / 160.0; - pspriteyscale = pspritexscale * YaspectMul; - pspritexiscale = 1 / pspritexscale; - - // thing clipping - clearbufshort (screenheightarray, viewwidth, (short)viewheight); - - R_InitTextureMapping (); - - MaxVisForWall = (InvZtoScale * (SCREENWIDTH*r_Yaspect) / - (viewwidth*SCREENHEIGHT * FocalTangent)); - MaxVisForWall = 32767.0 / MaxVisForWall; - MaxVisForFloor = 32767.0 / (viewheight * FocalLengthY / 160); - - // Reset r_*Visibility vars - R_SetVisibility(R_GetVisibility()); -} - -//========================================================================== -// -// CVAR r_columnmethod -// -// Selects which version of the seg renderers to use. -// -//========================================================================== - -CUSTOM_CVAR (Int, r_columnmethod, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self != 0 && self != 1) - { - self = 1; - } - else - { // Trigger the change - setsizeneeded = true; - } -} - -//========================================================================== -// -// R_Init -// -//========================================================================== - -void R_InitRenderer() -{ - atterm(R_ShutdownRenderer); - // viewwidth / viewheight are set by the defaults - clearbufshort (zeroarray, MAXWIDTH, 0); - - R_InitPlanes (); - R_InitShadeMaps(); - R_InitColumnDrawers (); - - colfunc = basecolfunc = R_DrawColumn; - fuzzcolfunc = R_DrawFuzzColumn; - transcolfunc = R_DrawTranslatedColumn; - spanfunc = R_DrawSpan; - - // [RH] Horizontal column drawers - hcolfunc_pre = R_DrawColumnHoriz; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; -} - -//========================================================================== -// -// R_ShutdownRenderer -// -//========================================================================== - -static void R_ShutdownRenderer() -{ - R_DeinitSprites(); - R_DeinitPlanes(); - // Free openings - if (openings != NULL) - { - M_Free (openings); - openings = NULL; - } - - // Free drawsegs - if (drawsegs != NULL) - { - M_Free (drawsegs); - drawsegs = NULL; - } -} - -//========================================================================== -// -// R_CopyStackedViewParameters -// -//========================================================================== - -void R_CopyStackedViewParameters() -{ - stacked_viewpos = ViewPos; - stacked_angle = ViewAngle; - stacked_extralight = extralight; - stacked_visibility = R_GetVisibility(); -} - -//========================================================================== -// -// R_SetupColormap -// -// Sets up special fixed colormaps -// -//========================================================================== - -void R_SetupColormap(player_t *player) -{ - realfixedcolormap = NULL; - fixedcolormap = NULL; - fixedlightlev = -1; - - if (player != NULL && camera == player->mo) - { - if (player->fixedcolormap >= 0 && player->fixedcolormap < (int)SpecialColormaps.Size()) - { - realfixedcolormap = &SpecialColormaps[player->fixedcolormap]; - if (RenderTarget == screen && (DFrameBuffer *)screen->Accel2D && r_shadercolormaps) - { - // Render everything fullbright. The copy to video memory will - // apply the special colormap, so it won't be restricted to the - // palette. - fixedcolormap = realcolormaps; - } - else - { - fixedcolormap = SpecialColormaps[player->fixedcolormap].Colormap; - } - } - else if (player->fixedlightlevel >= 0 && player->fixedlightlevel < NUMCOLORMAPS) - { - fixedlightlev = player->fixedlightlevel * 256; - } - } - // [RH] Inverse light for shooting the Sigil - if (fixedcolormap == NULL && extralight == INT_MIN) - { - fixedcolormap = SpecialColormaps[INVERSECOLORMAP].Colormap; - extralight = 0; - } -} - -//========================================================================== -// -// R_SetupFreelook -// -// [RH] freelook stuff -// -//========================================================================== - -void R_SetupFreelook() -{ - { - double dy; - - if (camera != NULL) - { - dy = FocalLengthY * (-ViewPitch).Tan(); - } - else - { - dy = 0; - } - - CenterY = (viewheight / 2.0) + dy; - centery = xs_ToInt(CenterY); - globaluclip = -CenterY / InvZtoScale; - globaldclip = (viewheight - CenterY) / InvZtoScale; - - //centeryfrac &= 0xffff0000; - int e, i; - - i = 0; - e = viewheight; - float focus = float(FocalLengthY); - float den; - float cy = float(CenterY); - if (i < centery) - { - den = cy - i - 0.5f; - if (e <= centery) - { - do { - yslope[i] = FLOAT2FIXED(focus / den); - den -= 1; - } while (++i < e); - } - else - { - do { - yslope[i] = FLOAT2FIXED(focus / den); - den -= 1; - } while (++i < centery); - den = i - cy + 0.5f; - do { - yslope[i] = FLOAT2FIXED(focus / den); - den += 1; - } while (++i < e); - } - } - else - { - den = i - cy + 0.5f; - do { - yslope[i] = FLOAT2FIXED(focus / den); - den += 1; - } while (++i < e); - } - } -} - -//========================================================================== -// -// R_EnterPortal -// -// [RH] Draw the reflection inside a mirror -// [ZZ] Merged with portal code, originally called R_EnterMirror -// -//========================================================================== - -CVAR(Int, r_portal_recursions, 4, CVAR_ARCHIVE) -CVAR(Bool, r_highlight_portals, false, CVAR_ARCHIVE) - -void R_HighlightPortal (PortalDrawseg* pds) -{ - // [ZZ] NO OVERFLOW CHECKS HERE - // I believe it won't break. if it does, blame me. :( - - BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 255, 0, 0, 0, 255); - - BYTE* pixels = RenderTarget->GetBuffer(); - // top edge - for (int x = pds->x1; x < pds->x2; x++) - { - if (x < 0 || x >= RenderTarget->GetWidth()) - continue; - - int p = x - pds->x1; - int Ytop = pds->ceilingclip[p]; - int Ybottom = pds->floorclip[p]; - - if (x == pds->x1 || x == pds->x2-1) - { - RenderTarget->DrawLine(x, Ytop, x, Ybottom+1, color, 0); - continue; - } - - int YtopPrev = pds->ceilingclip[p-1]; - int YbottomPrev = pds->floorclip[p-1]; - - if (abs(Ytop-YtopPrev) > 1) - RenderTarget->DrawLine(x, YtopPrev, x, Ytop, color, 0); - else *(pixels + Ytop * RenderTarget->GetPitch() + x) = color; - - if (abs(Ybottom-YbottomPrev) > 1) - RenderTarget->DrawLine(x, YbottomPrev, x, Ybottom, color, 0); - else *(pixels + Ybottom * RenderTarget->GetPitch() + x) = color; - } -} - -void R_EnterPortal (PortalDrawseg* pds, int depth) -{ - // [ZZ] check depth. fill portal with black if it's exceeding the visual recursion limit, and continue like nothing happened. - if (depth >= r_portal_recursions) - { - BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 0, 0, 0, 0, 255); - int spacing = RenderTarget->GetPitch(); - for (int x = pds->x1; x < pds->x2; x++) - { - if (x < 0 || x >= RenderTarget->GetWidth()) - continue; - - int Ytop = pds->ceilingclip[x-pds->x1]; - int Ybottom = pds->floorclip[x-pds->x1]; - - BYTE *dest = RenderTarget->GetBuffer() + x + Ytop * spacing; - - for (int y = Ytop; y <= Ybottom; y++) - { - *dest = color; - dest += spacing; - } - } - - if (r_highlight_portals) - R_HighlightPortal(pds); - - return; - } - - DAngle startang = ViewAngle; - DVector3 startpos = ViewPos; - DVector3 savedpath[2] = { ViewPath[0], ViewPath[1] }; - ActorRenderFlags savedvisibility = camera? camera->renderflags & RF_INVISIBLE : ActorRenderFlags::FromInt(0); - - CurrentPortalUniq++; - - unsigned int portalsAtStart = WallPortals.Size (); - - if (pds->mirror) - { - //vertex_t *v1 = ds->curline->v1; - vertex_t *v1 = pds->src->v1; - - // Reflect the current view behind the mirror. - if (pds->src->Delta().X == 0) - { // vertical mirror - ViewPos.X = v1->fX() - startpos.X + v1->fX(); - } - else if (pds->src->Delta().Y == 0) - { // horizontal mirror - ViewPos.Y = v1->fY() - startpos.Y + v1->fY(); - } - else - { // any mirror - vertex_t *v2 = pds->src->v2; - - double dx = v2->fX() - v1->fX(); - double dy = v2->fY() - v1->fY(); - double x1 = v1->fX(); - double y1 = v1->fY(); - double x = startpos.X; - double y = startpos.Y; - - // the above two cases catch len == 0 - double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); - - ViewPos.X = (x1 + r * dx)*2 - x; - ViewPos.Y = (y1 + r * dy)*2 - y; - } - ViewAngle = pds->src->Delta().Angle() - startang; - } - else - { - P_TranslatePortalXY(pds->src, ViewPos.X, ViewPos.Y); - P_TranslatePortalZ(pds->src, ViewPos.Z); - P_TranslatePortalAngle(pds->src, ViewAngle); - P_TranslatePortalXY(pds->src, ViewPath[0].X, ViewPath[0].Y); - P_TranslatePortalXY(pds->src, ViewPath[1].X, ViewPath[1].Y); - - if (!r_showviewer && camera) - { - double distp = (ViewPath[0] - ViewPath[1]).Length(); - if (distp > EQUAL_EPSILON) - { - double dist1 = (ViewPos - ViewPath[0]).Length(); - double dist2 = (ViewPos - ViewPath[1]).Length(); - - if (dist1 + dist2 < distp + 1) - { - camera->renderflags |= RF_INVISIBLE; - } - } - } - } - - ViewSin = ViewAngle.Sin(); - ViewCos = ViewAngle.Cos(); - - ViewTanSin = FocalTangent * ViewSin; - ViewTanCos = FocalTangent * ViewCos; - - R_CopyStackedViewParameters(); - - validcount++; - PortalDrawseg* prevpds = CurrentPortal; - CurrentPortal = pds; - - R_ClearPlanes (false); - R_ClearClipSegs (pds->x1, pds->x2); - - WindowLeft = pds->x1; - WindowRight = pds->x2; - - // RF_XFLIP should be removed before calling the root function - int prevmf = MirrorFlags; - if (pds->mirror) - { - if (MirrorFlags & RF_XFLIP) - MirrorFlags &= ~RF_XFLIP; - else MirrorFlags |= RF_XFLIP; - } - - // some portals have height differences, account for this here - R_3D_EnterSkybox(); // push 3D floor height map - CurrentPortalInSkybox = false; // first portal in a skybox should set this variable to false for proper clipping in skyboxes. - - // first pass, set clipping - memcpy (ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len*sizeof(*ceilingclip)); - memcpy (floorclip + pds->x1, &pds->floorclip[0], pds->len*sizeof(*floorclip)); - - InSubsector = NULL; - R_RenderBSPNode (nodes + numnodes - 1); - R_3D_ResetClip(); // reset clips (floor/ceiling) - if (!savedvisibility && camera) camera->renderflags &= ~RF_INVISIBLE; - - PlaneCycles.Clock(); - R_DrawPlanes (); - R_DrawPortals (); - PlaneCycles.Unclock(); - - double vzp = ViewPos.Z; - - int prevuniq = CurrentPortalUniq; - // depth check is in another place right now - unsigned int portalsAtEnd = WallPortals.Size (); - for (; portalsAtStart < portalsAtEnd; portalsAtStart++) - { - R_EnterPortal (&WallPortals[portalsAtStart], depth + 1); - } - int prevuniq2 = CurrentPortalUniq; - CurrentPortalUniq = prevuniq; - - NetUpdate(); - - MaskedCycles.Clock(); // [ZZ] count sprites in portals/mirrors along with normal ones. - R_DrawMasked (); // this is required since with portals there often will be cases when more than 80% of the view is inside a portal. - MaskedCycles.Unclock(); - - NetUpdate(); - - R_3D_LeaveSkybox(); // pop 3D floor height map - CurrentPortalUniq = prevuniq2; - - // draw a red line around a portal if it's being highlighted - if (r_highlight_portals) - R_HighlightPortal(pds); - - CurrentPortal = prevpds; - MirrorFlags = prevmf; - ViewAngle = startang; - ViewPos = startpos; - ViewPath[0] = savedpath[0]; - ViewPath[1] = savedpath[1]; -} - -//========================================================================== -// -// R_SetupBuffer -// -// Precalculate all row offsets and fuzz table. -// -//========================================================================== - -void R_SetupBuffer () -{ - static BYTE *lastbuff = NULL; - - int pitch = RenderTarget->GetPitch(); - BYTE *lineptr = RenderTarget->GetBuffer() + viewwindowy*pitch + viewwindowx; - - if (dc_pitch != pitch || lineptr != lastbuff) - { - if (dc_pitch != pitch) - { - dc_pitch = pitch; - R_InitFuzzTable (pitch); -#if defined(X86_ASM) || defined(X64_ASM) - ASM_PatchPitch (); -#endif - } - dc_destorg = lineptr; - for (int i = 0; i < RenderTarget->GetHeight(); i++) - { - ylookup[i] = i * pitch; - } - } -} - -//========================================================================== -// -// R_RenderActorView -// -//========================================================================== - -void R_RenderActorView (AActor *actor, bool dontmaplines) -{ - WallCycles.Reset(); - PlaneCycles.Reset(); - MaskedCycles.Reset(); - WallScanCycles.Reset(); - - fakeActive = 0; // kg3D - reset fake floor indicator - R_3D_ResetClip(); // reset clips (floor/ceiling) - - R_SetupBuffer (); - R_SetupFrame (actor); - - // Clear buffers. - R_ClearClipSegs (0, viewwidth); - R_ClearDrawSegs (); - R_ClearPlanes (true); - R_ClearSprites (); - - NetUpdate (); - - // [RH] Show off segs if r_drawflat is 1 - if (r_drawflat) - { - hcolfunc_pre = R_FillColumnHorizP; - hcolfunc_post1 = rt_copy1col; - hcolfunc_post4 = rt_copy4cols; - colfunc = R_FillColumnP; - spanfunc = R_FillSpan; - } - else - { - hcolfunc_pre = R_DrawColumnHoriz; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; - colfunc = basecolfunc; - spanfunc = R_DrawSpan; - } - - WindowLeft = 0; - WindowRight = viewwidth; - MirrorFlags = 0; - CurrentPortal = NULL; - CurrentPortalUniq = 0; - - r_dontmaplines = dontmaplines; - - // [RH] Hack to make windows into underwater areas possible - r_fakingunderwater = false; - - // [RH] Setup particles for this frame - P_FindParticleSubsectors (); - - WallCycles.Clock(); - ActorRenderFlags savedflags = camera->renderflags; - // Never draw the player unless in chasecam mode - if (!r_showviewer) - { - camera->renderflags |= RF_INVISIBLE; - } - // Link the polyobjects right before drawing the scene to reduce the amounts of calls to this function - PO_LinkToSubsectors(); - InSubsector = NULL; - R_RenderBSPNode (nodes + numnodes - 1); // The head node is the last node output. - R_3D_ResetClip(); // reset clips (floor/ceiling) - camera->renderflags = savedflags; - WallCycles.Unclock(); - - NetUpdate (); - - if (viewactive) - { - PlaneCycles.Clock(); - R_DrawPlanes (); - R_DrawPortals (); - PlaneCycles.Unclock(); - - // [RH] Walk through mirrors - // [ZZ] Merged with portals - size_t lastportal = WallPortals.Size(); - for (unsigned int i = 0; i < lastportal; i++) - { - R_EnterPortal(&WallPortals[i], 0); - } - - CurrentPortal = NULL; - CurrentPortalUniq = 0; - - NetUpdate (); - - MaskedCycles.Clock(); - R_DrawMasked (); - MaskedCycles.Unclock(); - - NetUpdate (); - } - WallPortals.Clear (); - interpolator.RestoreInterpolations (); - R_SetupBuffer (); - - // If we don't want shadered colormaps, NULL it now so that the - // copy to the screen does not use a special colormap shader. - if (!r_shadercolormaps) - { - realfixedcolormap = NULL; - } -} - -//========================================================================== -// -// R_RenderViewToCanvas -// -// Pre: Canvas is already locked. -// -//========================================================================== - -void R_RenderViewToCanvas (AActor *actor, DCanvas *canvas, - int x, int y, int width, int height, bool dontmaplines) -{ - const bool savedviewactive = viewactive; - - viewwidth = width; - RenderTarget = canvas; - bRenderingToCanvas = true; - - R_SetWindow (12, width, height, height); - viewwindowx = x; - viewwindowy = y; - viewactive = true; - - R_RenderActorView (actor, dontmaplines); - - RenderTarget = screen; - bRenderingToCanvas = false; - R_ExecuteSetViewSize (); - screen->Lock (true); - R_SetupBuffer (); - screen->Unlock (); - viewactive = savedviewactive; -} - -//========================================================================== -// -// R_MultiresInit -// -// Called from V_SetResolution() -// -//========================================================================== - -void R_MultiresInit () -{ - R_PlaneInitData (); -} - - -//========================================================================== -// -// STAT fps -// -// Displays statistics about rendering times -// -//========================================================================== -extern cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; -extern cycle_t FrameCycles; - -ADD_STAT (fps) -{ - FString out; - out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms", - FrameCycles.TimeMS(), WallCycles.TimeMS(), PlaneCycles.TimeMS(), MaskedCycles.TimeMS()); - return out; -} - - -static double f_acc, w_acc,p_acc,m_acc; -static int acc_c; - -ADD_STAT (fps_accumulated) -{ - f_acc += FrameCycles.TimeMS(); - w_acc += WallCycles.TimeMS(); - p_acc += PlaneCycles.TimeMS(); - m_acc += MaskedCycles.TimeMS(); - acc_c++; - FString out; - out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms %d counts", - f_acc/acc_c, w_acc/acc_c, p_acc/acc_c, m_acc/acc_c, acc_c); - Printf(PRINT_LOG, "%s\n", out.GetChars()); - return out; -} - -//========================================================================== -// -// STAT wallcycles -// -// Displays the minimum number of cycles spent drawing walls -// -//========================================================================== - -static double bestwallcycles = HUGE_VAL; - -ADD_STAT (wallcycles) -{ - FString out; - double cycles = WallCycles.Time(); - if (cycles && cycles < bestwallcycles) - bestwallcycles = cycles; - out.Format ("%g", bestwallcycles); - return out; -} - -//========================================================================== -// -// CCMD clearwallcycles -// -// Resets the count of minimum wall drawing cycles -// -//========================================================================== - -CCMD (clearwallcycles) -{ - bestwallcycles = HUGE_VAL; -} - -#if 1 -// To use these, also uncomment the clock/unclock in wallscan -static double bestscancycles = HUGE_VAL; - -ADD_STAT (scancycles) -{ - FString out; - double scancycles = WallScanCycles.Time(); - if (scancycles && scancycles < bestscancycles) - bestscancycles = scancycles; - out.Format ("%g", bestscancycles); - return out; -} - -CCMD (clearscancycles) -{ - bestscancycles = HUGE_VAL; -} -#endif diff --git a/src/r_main.h b/src/r_main.h deleted file mode 100644 index 24103393d..000000000 --- a/src/r_main.h +++ /dev/null @@ -1,146 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// System specific interface stuff. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_MAIN_H__ -#define __R_MAIN_H__ - -#include "r_utility.h" -#include "d_player.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - - -typedef BYTE lighttable_t; // This could be wider for >8 bit display. - -// -// POV related. -// -extern bool bRenderingToCanvas; -extern double ViewCos; -extern double ViewSin; -extern fixed_t viewingrangerecip; -extern double FocalLengthX, FocalLengthY; -extern double InvZtoScale; - -extern double WallTMapScale2; - -extern int viewwindowx; -extern int viewwindowy; - -extern double CenterX; -extern double CenterY; -extern double YaspectMul; -extern double IYaspectMul; - -extern FDynamicColormap*basecolormap; // [RH] Colormap for sector currently being drawn - -extern int linecount; -extern int loopcount; - -extern bool r_dontmaplines; - -// -// Lighting. -// -// [RH] This has changed significantly from Doom, which used lookup -// tables based on 1/z for walls and z for flats and only recognized -// 16 discrete light levels. The terminology I use is borrowed from Build. -// - -// The size of a single colormap, in bits -#define COLORMAPSHIFT 8 - -// Convert a light level into an unbounded colormap index (shade). Result is -// fixed point. Why the +12? I wish I knew, but experimentation indicates it -// is necessary in order to best reproduce Doom's original lighting. -#define LIGHT2SHADE(l) ((NUMCOLORMAPS*2*FRACUNIT)-(((l)+12)*(FRACUNIT*NUMCOLORMAPS/128))) - -// MAXLIGHTSCALE from original DOOM, divided by 2. -#define MAXLIGHTVIS (24.0) - -// Convert a shade and visibility to a clamped colormap index. -// Result is not fixed point. -// Change R_CalcTiltedLighting() when this changes. -#define GETPALOOKUP(vis,shade) (clamp (((shade)-FLOAT2FIXED(MIN(MAXLIGHTVIS,double(vis))))>>FRACBITS, 0, NUMCOLORMAPS-1)) - -extern double GlobVis; - -void R_SetVisibility(double visibility); -double R_GetVisibility(); - -extern double r_BaseVisibility; -extern double r_WallVisibility; -extern double r_FloorVisibility; -extern float r_TiltVisibility; -extern double r_SpriteVisibility; - -extern int r_actualextralight; -extern bool foggy; -extern int fixedlightlev; -extern lighttable_t* fixedcolormap; -extern FSpecialColormap*realfixedcolormap; - - -// -// Function pointers to switch refresh/drawing functions. -// Used to select shadow mode etc. -// -extern void (*colfunc) (void); -extern void (*basecolfunc) (void); -extern void (*fuzzcolfunc) (void); -extern void (*transcolfunc) (void); -// No shadow effects on floors. -extern void (*spanfunc) (void); - -// [RH] Function pointers for the horizontal column drawers. -extern void (*hcolfunc_pre) (void); -extern void (*hcolfunc_post1) (int hx, int sx, int yl, int yh); -extern void (*hcolfunc_post2) (int hx, int sx, int yl, int yh); -extern void (*hcolfunc_post4) (int sx, int yl, int yh); - - -void R_InitTextureMapping (); - - -// -// REFRESH - the actual rendering functions. -// - -// Called by G_Drawer. -void R_RenderActorView (AActor *actor, bool dontmaplines = false); -void R_SetupBuffer (); - -void R_RenderViewToCanvas (AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines = false); - -// [RH] Initialize multires stuff for renderer -void R_MultiresInit (void); - - -extern int stacked_extralight; -extern double stacked_visibility; -extern DVector3 stacked_viewpos; -extern DAngle stacked_angle; - -extern void R_CopyStackedViewParameters(); - - -#endif // __R_MAIN_H__ diff --git a/src/r_plane.cpp b/src/r_plane.cpp deleted file mode 100644 index 790bb9309..000000000 --- a/src/r_plane.cpp +++ /dev/null @@ -1,1836 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// Here is a core component: drawing the floors and ceilings, -// while maintaining a per column clipping list only. -// Moreover, the sky areas have to be determined. -// -// MAXVISPLANES is no longer a limit on the number of visplanes, -// but a limit on the number of hash slots; larger numbers mean -// better performance usually but after a point they are wasted, -// and memory and time overheads creep in. -// -// Lee Killough -// -// [RH] Further modified to significantly increase accuracy and add slopes. -// -//----------------------------------------------------------------------------- - -#include -#include - -#include "templates.h" -#include "i_system.h" -#include "w_wad.h" - -#include "doomdef.h" -#include "doomstat.h" - -#include "r_local.h" -#include "r_sky.h" -#include "stats.h" - -#include "v_video.h" -#include "a_sharedglobal.h" -#include "c_console.h" -#include "cmdlib.h" -#include "d_net.h" -#include "g_level.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - -#ifdef _MSC_VER -#pragma warning(disable:4244) -#endif - -//EXTERN_CVAR (Int, tx) -//EXTERN_CVAR (Int, ty) - -extern subsector_t *InSubsector; - -static void R_DrawSkyStriped (visplane_t *pl); - -planefunction_t floorfunc; -planefunction_t ceilingfunc; - -// Here comes the obnoxious "visplane". -#define MAXVISPLANES 128 /* must be a power of 2 */ - -// Avoid infinite recursion with stacked sectors by limiting them. -#define MAX_SKYBOX_PLANES 1000 - -// [RH] Allocate one extra for sky box planes. -static visplane_t *visplanes[MAXVISPLANES+1]; // killough -static visplane_t *freetail; // killough -static visplane_t **freehead = &freetail; // killough - -visplane_t *floorplane; -visplane_t *ceilingplane; - -// killough -- hash function for visplanes -// Empirically verified to be fairly uniform: - -#define visplane_hash(picnum,lightlevel,height) \ - ((unsigned)((picnum)*3+(lightlevel)+(FLOAT2FIXED((height).fD()))*7) & (MAXVISPLANES-1)) - -// These are copies of the main parameters used when drawing stacked sectors. -// When you change the main parameters, you should copy them here too *unless* -// you are changing them to draw a stacked sector. Otherwise, stacked sectors -// won't draw in skyboxes properly. -int stacked_extralight; -double stacked_visibility; -DVector3 stacked_viewpos; -DAngle stacked_angle; - - -// -// opening -// - -size_t maxopenings; -short *openings; -ptrdiff_t lastopening; - -// -// Clip values are the solid pixel bounding the range. -// floorclip starts out SCREENHEIGHT and is just outside the range -// ceilingclip starts out 0 and is just inside the range -// -short floorclip[MAXWIDTH]; -short ceilingclip[MAXWIDTH]; - -// -// texture mapping -// - -static double planeheight; - -extern "C" { -// -// spanend holds the end of a plane span in each screen row -// -short spanend[MAXHEIGHT]; -BYTE *tiltlighting[MAXWIDTH]; - -int planeshade; -FVector3 plane_sz, plane_su, plane_sv; -float planelightfloat; -bool plane_shade; -fixed_t pviewx, pviewy; - -void R_DrawTiltedPlane_ASM (int y, int x1); -} - -fixed_t yslope[MAXHEIGHT]; -static fixed_t xscale, yscale; -static DWORD xstepscale, ystepscale; -static DWORD basexfrac, baseyfrac; - -#ifdef X86_ASM -extern "C" void R_SetSpanSource_ASM (const BYTE *flat); -extern "C" void R_SetSpanSize_ASM (int xbits, int ybits); -extern "C" void R_SetSpanColormap_ASM (BYTE *colormap); -extern "C" void R_SetTiltedSpanSource_ASM (const BYTE *flat); -extern "C" BYTE *ds_curcolormap, *ds_cursource, *ds_curtiltedsource; -#endif -void R_DrawSinglePlane (visplane_t *, fixed_t alpha, bool additive, bool masked); - -//========================================================================== -// -// R_InitPlanes -// -// Called at game startup. -// -//========================================================================== - -void R_InitPlanes () -{ -} - -//========================================================================== -// -// R_DeinitPlanes -// -//========================================================================== - -void R_DeinitPlanes () -{ - fakeActive = 0; - - // do not use R_ClearPlanes because at this point the screen pointer is no longer valid. - for (int i = 0; i <= MAXVISPLANES; i++) // new code -- killough - { - for (*freehead = visplanes[i], visplanes[i] = NULL; *freehead; ) - { - freehead = &(*freehead)->next; - } - } - for (visplane_t *pl = freetail; pl != NULL; ) - { - visplane_t *next = pl->next; - free (pl); - pl = next; - } -} - -//========================================================================== -// -// R_MapPlane -// -// Globals used: planeheight, ds_source, basexscale, baseyscale, -// pviewx, pviewy, xoffs, yoffs, basecolormap, xscale, yscale. -// -//========================================================================== - -void R_MapPlane (int y, int x1) -{ - int x2 = spanend[y]; - fixed_t distance; - -#ifdef RANGECHECK - if (x2 < x1 || x1<0 || x2>=viewwidth || (unsigned)y>=(unsigned)viewheight) - { - I_FatalError ("R_MapPlane: %i, %i at %i", x1, x2, y); - } -#endif - - // [RH] Notice that I dumped the caching scheme used by Doom. - // It did not offer any appreciable speedup. - - distance = xs_ToInt(planeheight * yslope[y]); - - ds_xstep = FixedMul (distance, xstepscale); - ds_ystep = FixedMul (distance, ystepscale); - ds_xfrac = FixedMul (distance, basexfrac) + pviewx; - ds_yfrac = FixedMul (distance, baseyfrac) + pviewy; - - if (plane_shade) - { - // Determine lighting based on the span's distance from the viewer. - ds_colormap = basecolormap->Maps + (GETPALOOKUP ( - GlobVis * fabs(CenterY - y), planeshade) << COLORMAPSHIFT); - } - -#ifdef X86_ASM - if (ds_colormap != ds_curcolormap) - R_SetSpanColormap_ASM (ds_colormap); -#endif - - ds_y = y; - ds_x1 = x1; - ds_x2 = x2; - - spanfunc (); -} - -//========================================================================== -// -// R_CalcTiltedLighting -// -// Calculates the lighting for one row of a tilted plane. If the definition -// of GETPALOOKUP changes, this needs to change, too. -// -//========================================================================== - -extern "C" { -void R_CalcTiltedLighting (double lval, double lend, int width) -{ - double lstep; - BYTE *lightfiller; - BYTE *basecolormapdata = basecolormap->Maps; - int i = 0; - - if (width == 0 || lval == lend) - { // Constant lighting - lightfiller = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); - } - else - { - lstep = (lend - lval) / width; - if (lval >= MAXLIGHTVIS) - { // lval starts "too bright". - lightfiller = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); - for (; i <= width && lval >= MAXLIGHTVIS; ++i) - { - tiltlighting[i] = lightfiller; - lval += lstep; - } - } - if (lend >= MAXLIGHTVIS) - { // lend ends "too bright". - lightfiller = basecolormapdata + (GETPALOOKUP(lend, planeshade) << COLORMAPSHIFT); - for (; width > i && lend >= MAXLIGHTVIS; --width) - { - tiltlighting[width] = lightfiller; - lend -= lstep; - } - } - if (width > 0) - { - lval = planeshade - lval; - lend = planeshade - lend; - lstep = (lend - lval) / width; - if (lstep < 0) - { // Going from dark to light - if (lval < FRACUNIT) - { // All bright - lightfiller = basecolormapdata; - } - else - { - if (lval >= NUMCOLORMAPS*FRACUNIT) - { // Starts beyond the dark end - BYTE *clight = basecolormapdata + ((NUMCOLORMAPS-1) << COLORMAPSHIFT); - while (lval >= NUMCOLORMAPS*FRACUNIT && i <= width) - { - tiltlighting[i++] = clight; - lval += lstep; - } - if (i > width) - return; - } - while (i <= width && lval >= 0) - { - tiltlighting[i++] = basecolormapdata + (xs_ToInt(lval) << COLORMAPSHIFT); - lval += lstep; - } - lightfiller = basecolormapdata; - } - } - else - { // Going from light to dark - if (lval >= (NUMCOLORMAPS-1)*FRACUNIT) - { // All dark - lightfiller = basecolormapdata + ((NUMCOLORMAPS-1) << COLORMAPSHIFT); - } - else - { - while (lval < 0 && i <= width) - { - tiltlighting[i++] = basecolormapdata; - lval += lstep; - } - if (i > width) - return; - while (i <= width && lval < (NUMCOLORMAPS-1)*FRACUNIT) - { - tiltlighting[i++] = basecolormapdata + (xs_ToInt(lval) << COLORMAPSHIFT); - lval += lstep; - } - lightfiller = basecolormapdata + ((NUMCOLORMAPS-1) << COLORMAPSHIFT); - } - } - } - } - for (; i <= width; i++) - { - tiltlighting[i] = lightfiller; - } -} -} // extern "C" - -//========================================================================== -// -// R_MapTiltedPlane -// -//========================================================================== - -void R_MapTiltedPlane (int y, int x1) -{ - int x2 = spanend[y]; - int width = x2 - x1; - double iz, uz, vz; - BYTE *fb; - DWORD u, v; - int i; - - iz = plane_sz[2] + plane_sz[1]*(centery-y) + plane_sz[0]*(x1-centerx); - - // Lighting is simple. It's just linear interpolation from start to end - if (plane_shade) - { - uz = (iz + plane_sz[0]*width) * planelightfloat; - vz = iz * planelightfloat; - R_CalcTiltedLighting (vz, uz, width); - } - - uz = plane_su[2] + plane_su[1]*(centery-y) + plane_su[0]*(x1-centerx); - vz = plane_sv[2] + plane_sv[1]*(centery-y) + plane_sv[0]*(x1-centerx); - - fb = ylookup[y] + x1 + dc_destorg; - - BYTE vshift = 32 - ds_ybits; - BYTE ushift = vshift - ds_xbits; - int umask = ((1 << ds_xbits) - 1) << ds_ybits; - -#if 0 // The "perfect" reference version of this routine. Pretty slow. - // Use it only to see how things are supposed to look. - i = 0; - do - { - double z = 1.f/iz; - - u = SQWORD(uz*z) + pviewx; - v = SQWORD(vz*z) + pviewy; - ds_colormap = tiltlighting[i]; - fb[i++] = ds_colormap[ds_source[(v >> vshift) | ((u >> ushift) & umask)]]; - iz += plane_sz[0]; - uz += plane_su[0]; - vz += plane_sv[0]; - } while (--width >= 0); -#else -//#define SPANSIZE 32 -//#define INVSPAN 0.03125f -//#define SPANSIZE 8 -//#define INVSPAN 0.125f -#define SPANSIZE 16 -#define INVSPAN 0.0625f - - double startz = 1.f/iz; - double startu = uz*startz; - double startv = vz*startz; - double izstep, uzstep, vzstep; - - izstep = plane_sz[0] * SPANSIZE; - uzstep = plane_su[0] * SPANSIZE; - vzstep = plane_sv[0] * SPANSIZE; - x1 = 0; - width++; - - while (width >= SPANSIZE) - { - iz += izstep; - uz += uzstep; - vz += vzstep; - - double endz = 1.f/iz; - double endu = uz*endz; - double endv = vz*endz; - DWORD stepu = SQWORD((endu - startu) * INVSPAN); - DWORD stepv = SQWORD((endv - startv) * INVSPAN); - u = SQWORD(startu) + pviewx; - v = SQWORD(startv) + pviewy; - - for (i = SPANSIZE-1; i >= 0; i--) - { - fb[x1] = *(tiltlighting[x1] + ds_source[(v >> vshift) | ((u >> ushift) & umask)]); - x1++; - u += stepu; - v += stepv; - } - startu = endu; - startv = endv; - width -= SPANSIZE; - } - if (width > 0) - { - if (width == 1) - { - u = SQWORD(startu); - v = SQWORD(startv); - fb[x1] = *(tiltlighting[x1] + ds_source[(v >> vshift) | ((u >> ushift) & umask)]); - } - else - { - double left = width; - iz += plane_sz[0] * left; - uz += plane_su[0] * left; - vz += plane_sv[0] * left; - - double endz = 1.f/iz; - double endu = uz*endz; - double endv = vz*endz; - left = 1.f/left; - DWORD stepu = SQWORD((endu - startu) * left); - DWORD stepv = SQWORD((endv - startv) * left); - u = SQWORD(startu) + pviewx; - v = SQWORD(startv) + pviewy; - - for (; width != 0; width--) - { - fb[x1] = *(tiltlighting[x1] + ds_source[(v >> vshift) | ((u >> ushift) & umask)]); - x1++; - u += stepu; - v += stepv; - } - } - } -#endif -} - -//========================================================================== -// -// R_MapColoredPlane -// -//========================================================================== - -void R_MapColoredPlane (int y, int x1) -{ - memset (ylookup[y] + x1 + dc_destorg, ds_color, spanend[y] - x1 + 1); -} - -//========================================================================== -// -// R_ClearPlanes -// -// Called at the beginning of each frame. -// -//========================================================================== - -void R_ClearPlanes (bool fullclear) -{ - int i; - - // Don't clear fake planes if not doing a full clear. - if (!fullclear) - { - for (i = 0; i <= MAXVISPLANES-1; i++) // new code -- killough - { - for (visplane_t **probe = &visplanes[i]; *probe != NULL; ) - { - if ((*probe)->sky < 0) - { // fake: move past it - probe = &(*probe)->next; - } - else - { // not fake: move to freelist - visplane_t *vis = *probe; - *freehead = vis; - *probe = vis->next; - vis->next = NULL; - freehead = &vis->next; - } - } - } - } - else - { - for (i = 0; i <= MAXVISPLANES; i++) // new code -- killough - { - for (*freehead = visplanes[i], visplanes[i] = NULL; *freehead; ) - { - freehead = &(*freehead)->next; - } - } - - // opening / clipping determination - clearbufshort (floorclip, viewwidth, viewheight); - // [RH] clip ceiling to console bottom - clearbufshort (ceilingclip, viewwidth, - !screen->Accel2D && ConBottom > viewwindowy && !bRenderingToCanvas - ? (ConBottom - viewwindowy) : 0); - - lastopening = 0; - } -} - -//========================================================================== -// -// new_visplane -// -// New function, by Lee Killough -// [RH] top and bottom buffers get allocated immediately after the visplane. -// -//========================================================================== - -static visplane_t *new_visplane (unsigned hash) -{ - visplane_t *check = freetail; - - if (check == NULL) - { - check = (visplane_t *)M_Malloc (sizeof(*check) + 3 + sizeof(*check->top)*(MAXWIDTH*2)); - memset(check, 0, sizeof(*check) + 3 + sizeof(*check->top)*(MAXWIDTH*2)); - check->bottom = check->top + MAXWIDTH+2; - } - else if (NULL == (freetail = freetail->next)) - { - freehead = &freetail; - } - - check->next = visplanes[hash]; - visplanes[hash] = check; - return check; -} - - -//========================================================================== -// -// R_FindPlane -// -// killough 2/28/98: Add offsets -//========================================================================== - -visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightlevel, double Alpha, bool additive, - const FTransform &xform, - int sky, FSectorPortal *portal) -{ - secplane_t plane; - visplane_t *check; - unsigned hash; // killough - bool isskybox; - fixed_t xoffs = FLOAT2FIXED(xform.xOffs); - fixed_t yoffs = FLOAT2FIXED(xform.yOffs + xform.baseyOffs); - fixed_t xscale = FLOAT2FIXED(xform.xScale); - fixed_t yscale = FLOAT2FIXED(xform.yScale); - fixed_t alpha = FLOAT2FIXED(Alpha); - angle_t angle = (xform.Angle + xform.baseAngle).BAMs(); - - if (picnum == skyflatnum) // killough 10/98 - { // most skies map together - lightlevel = 0; - xoffs = 0; - yoffs = 0; - xscale = 0; - yscale = 0; - angle = 0; - alpha = 0; - additive = false; - // [RH] Map floor skies and ceiling skies to separate visplanes. This isn't - // always necessary, but it is needed if a floor and ceiling sky are in the - // same column but separated by a wall. If they both try to reside in the - // same visplane, then only the floor sky will be drawn. - plane.set(0., 0., height.fC(), 0.); - isskybox = portal != NULL && !(portal->mFlags & PORTSF_INSKYBOX); - } - else if (portal != NULL && !(portal->mFlags & PORTSF_INSKYBOX)) - { - plane = height; - isskybox = true; - } - else - { - plane = height; - isskybox = false; - // kg3D - hack, store alpha in sky - // i know there is ->alpha, but this also allows to identify fake plane - // and ->alpha is for stacked sectors - if (fake3D & (FAKE3D_FAKEFLOOR|FAKE3D_FAKECEILING)) sky = 0x80000000 | fakeAlpha; - else sky = 0; // not skyflatnum so it can't be a sky - portal = NULL; - alpha = OPAQUE; - } - - // New visplane algorithm uses hash table -- killough - hash = isskybox ? MAXVISPLANES : visplane_hash (picnum.GetIndex(), lightlevel, height); - - for (check = visplanes[hash]; check; check = check->next) // killough - { - if (isskybox) - { - if (portal == check->portal && plane == check->height) - { - if (portal->mType != PORTS_SKYVIEWPOINT) - { // This skybox is really a stacked sector, so we need to - // check even more. - if (check->extralight == stacked_extralight && - check->visibility == stacked_visibility && - check->viewpos == stacked_viewpos && - ( - // headache inducing logic... :( - (portal->mType != PORTS_STACKEDSECTORTHING) || - ( - check->Alpha == alpha && - check->Additive == additive && - (alpha == 0 || // if alpha is > 0 everything needs to be checked - (plane == check->height && - picnum == check->picnum && - lightlevel == check->lightlevel && - - xoffs == check->xoffs && // killough 2/28/98: Add offset checks - yoffs == check->yoffs && - basecolormap == check->colormap && // [RH] Add more checks - xscale == check->xscale && - yscale == check->yscale && - angle == check->angle - ) - ) && - check->viewangle == stacked_angle - ) - ) - ) - { - return check; - } - } - else - { - return check; - } - } - } - else - if (plane == check->height && - picnum == check->picnum && - lightlevel == check->lightlevel && - xoffs == check->xoffs && // killough 2/28/98: Add offset checks - yoffs == check->yoffs && - basecolormap == check->colormap && // [RH] Add more checks - xscale == check->xscale && - yscale == check->yscale && - angle == check->angle && - sky == check->sky && - CurrentPortalUniq == check->CurrentPortalUniq && - MirrorFlags == check->MirrorFlags && - CurrentSkybox == check->CurrentSkybox && - ViewPos == check->viewpos - ) - { - return check; - } - } - - check = new_visplane (hash); // killough - - check->height = plane; - check->picnum = picnum; - check->lightlevel = lightlevel; - check->xoffs = xoffs; // killough 2/28/98: Save offsets - check->yoffs = yoffs; - check->xscale = xscale; - check->yscale = yscale; - check->angle = angle; - check->colormap = basecolormap; // [RH] Save colormap - check->sky = sky; - check->portal = portal; - check->left = viewwidth; // Was SCREENWIDTH -- killough 11/98 - check->right = 0; - check->extralight = stacked_extralight; - check->visibility = stacked_visibility; - check->viewpos = stacked_viewpos; - check->viewangle = stacked_angle; - check->Alpha = alpha; - check->Additive = additive; - check->CurrentPortalUniq = CurrentPortalUniq; - check->MirrorFlags = MirrorFlags; - check->CurrentSkybox = CurrentSkybox; - - clearbufshort (check->top, viewwidth, 0x7fff); - - return check; -} - -//========================================================================== -// -// R_CheckPlane -// -//========================================================================== - -visplane_t *R_CheckPlane (visplane_t *pl, int start, int stop) -{ - int intrl, intrh; - int unionl, unionh; - int x; - - assert (start >= 0 && start < viewwidth); - assert (stop > start && stop <= viewwidth); - - if (start < pl->left) - { - intrl = pl->left; - unionl = start; - } - else - { - unionl = pl->left; - intrl = start; - } - - if (stop > pl->right) - { - intrh = pl->right; - unionh = stop; - } - else - { - unionh = pl->right; - intrh = stop; - } - - for (x = intrl; x < intrh && pl->top[x] == 0x7fff; x++) - ; - - if (x >= intrh) - { - // use the same visplane - pl->left = unionl; - pl->right = unionh; - } - else - { - // make a new visplane - unsigned hash; - - if (pl->portal != NULL && !(pl->portal->mFlags & PORTSF_INSKYBOX) && viewactive) - { - hash = MAXVISPLANES; - } - else - { - hash = visplane_hash (pl->picnum.GetIndex(), pl->lightlevel, pl->height); - } - visplane_t *new_pl = new_visplane (hash); - - new_pl->height = pl->height; - new_pl->picnum = pl->picnum; - new_pl->lightlevel = pl->lightlevel; - new_pl->xoffs = pl->xoffs; // killough 2/28/98 - new_pl->yoffs = pl->yoffs; - new_pl->xscale = pl->xscale; // [RH] copy these, too - new_pl->yscale = pl->yscale; - new_pl->angle = pl->angle; - new_pl->colormap = pl->colormap; - new_pl->portal = pl->portal; - new_pl->extralight = pl->extralight; - new_pl->visibility = pl->visibility; - new_pl->viewpos = pl->viewpos; - new_pl->viewangle = pl->viewangle; - new_pl->sky = pl->sky; - new_pl->Alpha = pl->Alpha; - new_pl->Additive = pl->Additive; - new_pl->CurrentPortalUniq = pl->CurrentPortalUniq; - new_pl->MirrorFlags = pl->MirrorFlags; - new_pl->CurrentSkybox = pl->CurrentSkybox; - pl = new_pl; - pl->left = start; - pl->right = stop; - clearbufshort (pl->top, viewwidth, 0x7fff); - } - return pl; -} - - -//========================================================================== -// -// R_MakeSpans -// -// -//========================================================================== - -inline void R_MakeSpans (int x, int t1, int b1, int t2, int b2, void (*mapfunc)(int y, int x1)) -{ -} - -//========================================================================== -// -// R_DrawSky -// -// Can handle overlapped skies. Note that the front sky is *not* masked in -// in the normal convention for patches, but uses color 0 as a transparent -// color instead. -// -// Note that since ZDoom now uses color 0 as transparent for other purposes, -// you can use normal texture transparency, so the distinction isn't so -// important anymore, but you should still be aware of it. -// -//========================================================================== - -static FTexture *frontskytex, *backskytex; -static angle_t skyflip; -static int frontpos, backpos; -static double frontyScale; -static fixed_t frontcyl, backcyl; -static double skymid; -static angle_t skyangle; -static double frontiScale; - -extern float swall[MAXWIDTH]; -extern fixed_t lwall[MAXWIDTH]; -extern fixed_t rw_offset; -extern FTexture *rw_pic; - -// Allow for layer skies up to 512 pixels tall. This is overkill, -// since the most anyone can ever see of the sky is 500 pixels. -// We need 4 skybufs because wallscan can draw up to 4 columns at a time. -static BYTE skybuf[4][512]; -static DWORD lastskycol[4]; -static int skycolplace; - -// Get a column of sky when there is only one sky texture. -static const BYTE *R_GetOneSkyColumn (FTexture *fronttex, int x) -{ - angle_t column = (skyangle + xtoviewangle[x]) ^ skyflip; - return fronttex->GetColumn((UMulScale16(column, frontcyl) + frontpos) >> FRACBITS, NULL); -} - -// Get a column of sky when there are two overlapping sky textures -static const BYTE *R_GetTwoSkyColumns (FTexture *fronttex, int x) -{ - DWORD ang = (skyangle + xtoviewangle[x]) ^ skyflip; - DWORD angle1 = (DWORD)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); - DWORD angle2 = (DWORD)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); - - // Check if this column has already been built. If so, there's - // no reason to waste time building it again. - DWORD skycol = (angle1 << 16) | angle2; - int i; - - for (i = 0; i < 4; ++i) - { - if (lastskycol[i] == skycol) - { - return skybuf[i]; - } - } - - lastskycol[skycolplace] = skycol; - BYTE *composite = skybuf[skycolplace]; - skycolplace = (skycolplace + 1) & 3; - - // The ordering of the following code has been tuned to allow VC++ to optimize - // it well. In particular, this arrangement lets it keep count in a register - // instead of on the stack. - const BYTE *front = fronttex->GetColumn (angle1, NULL); - const BYTE *back = backskytex->GetColumn (angle2, NULL); - - int count = MIN (512, MIN (backskytex->GetHeight(), fronttex->GetHeight())); - i = 0; - do - { - if (front[i]) - { - composite[i] = front[i]; - } - else - { - composite[i] = back[i]; - } - } while (++i, --count); - return composite; -} - -static void R_DrawSky (visplane_t *pl) -{ - int x; - float swal; - - if (pl->left >= pl->right) - return; - - swal = skyiscale; - for (x = pl->left; x < pl->right; ++x) - { - swall[x] = swal; - } - - if (MirrorFlags & RF_XFLIP) - { - for (x = pl->left; x < pl->right; ++x) - { - lwall[x] = (viewwidth - x) << FRACBITS; - } - } - else - { - for (x = pl->left; x < pl->right; ++x) - { - lwall[x] = x << FRACBITS; - } - } - - for (x = 0; x < 4; ++x) - { - lastskycol[x] = 0xffffffff; - } - - rw_pic = frontskytex; - rw_offset = 0; - - frontyScale = rw_pic->Scale.Y; - dc_texturemid = skymid * frontyScale; - - if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) - { // The texture tiles nicely - for (x = 0; x < 4; ++x) - { - lastskycol[x] = 0xffffffff; - } - wallscan (pl->left, pl->right, (short *)pl->top, (short *)pl->bottom, swall, lwall, - frontyScale, backskytex == NULL ? R_GetOneSkyColumn : R_GetTwoSkyColumns); - } - else - { // The texture does not tile nicely - frontyScale *= skyscale; - frontiScale = 1 / frontyScale; - R_DrawSkyStriped (pl); - } -} - -static void R_DrawSkyStriped (visplane_t *pl) -{ - short drawheight = short(frontskytex->GetHeight() * frontyScale); - double topfrac; - double iscale = frontiScale; - short top[MAXWIDTH], bot[MAXWIDTH]; - short yl, yh; - int x; - - topfrac = fmod(skymid + iscale * (1 - CenterY), frontskytex->GetHeight()); - if (topfrac < 0) topfrac += frontskytex->GetHeight(); - yl = 0; - yh = short((frontskytex->GetHeight() - topfrac) * frontyScale); - dc_texturemid = topfrac - iscale * (1 - CenterY); - - while (yl < viewheight) - { - for (x = pl->left; x < pl->right; ++x) - { - top[x] = MAX (yl, (short)pl->top[x]); - bot[x] = MIN (yh, (short)pl->bottom[x]); - } - for (x = 0; x < 4; ++x) - { - lastskycol[x] = 0xffffffff; - } - wallscan (pl->left, pl->right, top, bot, swall, lwall, rw_pic->Scale.Y, - backskytex == NULL ? R_GetOneSkyColumn : R_GetTwoSkyColumns); - yl = yh; - yh += drawheight; - dc_texturemid = iscale * (centery-yl-1); - } -} - -//========================================================================== -// -// R_DrawPlanes -// -// At the end of each frame. -// -//========================================================================== - -CVAR (Bool, tilt, false, 0); -//CVAR (Int, pa, 0, 0) - -int R_DrawPlanes () -{ - visplane_t *pl; - int i; - int vpcount = 0; - - ds_color = 3; - - for (i = 0; i < MAXVISPLANES; i++) - { - for (pl = visplanes[i]; pl; pl = pl->next) - { - // kg3D - draw only correct planes - if(pl->CurrentPortalUniq != CurrentPortalUniq || pl->CurrentSkybox != CurrentSkybox) - continue; - // kg3D - draw only real planes now - if(pl->sky >= 0) { - vpcount++; - R_DrawSinglePlane (pl, OPAQUE, false, false); - } - } - } - return vpcount; -} - -// kg3D - draw all visplanes with "height" -void R_DrawHeightPlanes(double height) -{ - visplane_t *pl; - int i; - - ds_color = 3; - - DVector3 oViewPos = ViewPos; - DAngle oViewAngle = ViewAngle; - - for (i = 0; i < MAXVISPLANES; i++) - { - for (pl = visplanes[i]; pl; pl = pl->next) - { - // kg3D - draw only correct planes - if(pl->CurrentSkybox != CurrentSkybox || pl->CurrentPortalUniq != CurrentPortalUniq) - continue; - if(pl->sky < 0 && pl->height.Zat0() == height) { - ViewPos = pl->viewpos; - ViewAngle = pl->viewangle; - MirrorFlags = pl->MirrorFlags; - R_DrawSinglePlane (pl, pl->sky & 0x7FFFFFFF, pl->Additive, true); - } - } - } - ViewPos = oViewPos; - ViewAngle = oViewAngle; -} - - -//========================================================================== -// -// R_DrawSinglePlane -// -// Draws a single visplane. -// -//========================================================================== - -void R_DrawSinglePlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked) -{ - if (pl->left >= pl->right) - return; - - if (r_drawflat) - { // [RH] no texture mapping - ds_color += 4; - R_MapVisPlane (pl, R_MapColoredPlane); - } - else if (pl->picnum == skyflatnum) - { // sky flat - R_DrawSkyPlane (pl); - } - else - { // regular flat - FTexture *tex = TexMan(pl->picnum, true); - - if (tex->UseType == FTexture::TEX_Null) - { - return; - } - - if (!masked && !additive) - { // If we're not supposed to see through this plane, draw it opaque. - alpha = OPAQUE; - } - else if (!tex->bMasked) - { // Don't waste time on a masked texture if it isn't really masked. - masked = false; - } - R_SetupSpanBits(tex); - pl->xscale = fixed_t(pl->xscale * tex->Scale.X); - pl->yscale = fixed_t(pl->yscale * tex->Scale.Y); - ds_source = tex->GetPixels (); - - basecolormap = pl->colormap; - planeshade = LIGHT2SHADE(pl->lightlevel); - - if (r_drawflat || (!pl->height.isSlope() && !tilt)) - { - R_DrawNormalPlane (pl, alpha, additive, masked); - } - else - { - R_DrawTiltedPlane (pl, alpha, additive, masked); - } - } - NetUpdate (); -} - -//========================================================================== -// -// R_DrawPortals -// -// Draws any recorded sky boxes and then frees them. -// -// The process: -// 1. Move the camera to coincide with the SkyViewpoint. -// 2. Clear out the old planes. (They have already been drawn.) -// 3. Clear a window out of the ClipSegs just large enough for the plane. -// 4. Pretend the existing vissprites and drawsegs aren't there. -// 5. Create a drawseg at 0 distance to clip sprites to the visplane. It -// doesn't need to be associated with a line in the map, since there -// will never be any sprites in front of it. -// 6. Render the BSP, then planes, then masked stuff. -// 7. Restore the previous vissprites and drawsegs. -// 8. Repeat for any other sky boxes. -// 9. Put the camera back where it was to begin with. -// -//========================================================================== -CVAR (Bool, r_skyboxes, true, 0) -static int numskyboxes; - -void R_DrawPortals () -{ - static TArray interestingStack; - static TArray drawsegStack; - static TArray visspriteStack; - static TArray viewposStack; - static TArray visplaneStack; - - numskyboxes = 0; - - if (visplanes[MAXVISPLANES] == NULL) - return; - - R_3D_EnterSkybox(); - CurrentPortalInSkybox = true; - - int savedextralight = extralight; - DVector3 savedpos = ViewPos; - DAngle savedangle = ViewAngle; - ptrdiff_t savedvissprite_p = vissprite_p - vissprites; - ptrdiff_t savedds_p = ds_p - drawsegs; - ptrdiff_t savedlastopening = lastopening; - size_t savedinteresting = FirstInterestingDrawseg; - double savedvisibility = R_GetVisibility(); - AActor *savedcamera = camera; - sector_t *savedsector = viewsector; - - int i; - visplane_t *pl; - - for (pl = visplanes[MAXVISPLANES]; pl != NULL; pl = visplanes[MAXVISPLANES]) - { - // Pop the visplane off the list now so that if this skybox adds more - // skyboxes to the list, they will be drawn instead of skipped (because - // new skyboxes go to the beginning of the list instead of the end). - visplanes[MAXVISPLANES] = pl->next; - pl->next = NULL; - - if (pl->right < pl->left || !r_skyboxes || numskyboxes == MAX_SKYBOX_PLANES || pl->portal == NULL) - { - R_DrawSinglePlane (pl, OPAQUE, false, false); - *freehead = pl; - freehead = &pl->next; - continue; - } - - numskyboxes++; - - FSectorPortal *port = pl->portal; - switch (port->mType) - { - case PORTS_SKYVIEWPOINT: - { - // Don't let gun flashes brighten the sky box - ASkyViewpoint *sky = barrier_cast(port->mSkybox); - extralight = 0; - R_SetVisibility(sky->args[0] * 0.25f); - - ViewPos = sky->InterpolatedPosition(r_TicFracF); - ViewAngle = savedangle + (sky->PrevAngles.Yaw + deltaangle(sky->PrevAngles.Yaw, sky->Angles.Yaw) * r_TicFracF); - - R_CopyStackedViewParameters(); - break; - } - - case PORTS_STACKEDSECTORTHING: - case PORTS_PORTAL: - case PORTS_LINKEDPORTAL: - extralight = pl->extralight; - R_SetVisibility (pl->visibility); - ViewPos.X = pl->viewpos.X + port->mDisplacement.X; - ViewPos.Y = pl->viewpos.Y + port->mDisplacement.Y; - ViewPos.Z = pl->viewpos.Z; - ViewAngle = pl->viewangle; - break; - - case PORTS_HORIZON: - case PORTS_PLANE: - // not implemented yet - - default: - R_DrawSinglePlane(pl, OPAQUE, false, false); - *freehead = pl; - freehead = &pl->next; - numskyboxes--; - continue; - } - - port->mFlags |= PORTSF_INSKYBOX; - if (port->mPartner > 0) sectorPortals[port->mPartner].mFlags |= PORTSF_INSKYBOX; - camera = NULL; - viewsector = port->mDestination; - assert(viewsector != NULL); - R_SetViewAngle (); - validcount++; // Make sure we see all sprites - - R_ClearPlanes (false); - R_ClearClipSegs (pl->left, pl->right); - WindowLeft = pl->left; - WindowRight = pl->right; - - for (i = pl->left; i < pl->right; i++) - { - if (pl->top[i] == 0x7fff) - { - ceilingclip[i] = viewheight; - floorclip[i] = -1; - } - else - { - ceilingclip[i] = pl->top[i]; - floorclip[i] = pl->bottom[i]; - } - } - - // Create a drawseg to clip sprites to the sky plane - R_CheckDrawSegs (); - ds_p->CurrentPortalUniq = CurrentPortalUniq; - ds_p->siz1 = INT_MAX; - ds_p->siz2 = INT_MAX; - ds_p->sz1 = 0; - ds_p->sz2 = 0; - ds_p->x1 = pl->left; - ds_p->x2 = pl->right; - ds_p->silhouette = SIL_BOTH; - ds_p->sprbottomclip = R_NewOpening (pl->right - pl->left); - ds_p->sprtopclip = R_NewOpening (pl->right - pl->left); - ds_p->maskedtexturecol = ds_p->swall = -1; - ds_p->bFogBoundary = false; - ds_p->curline = NULL; - ds_p->fake = 0; - memcpy (openings + ds_p->sprbottomclip, floorclip + pl->left, (pl->right - pl->left)*sizeof(short)); - memcpy (openings + ds_p->sprtopclip, ceilingclip + pl->left, (pl->right - pl->left)*sizeof(short)); - - firstvissprite = vissprite_p; - firstdrawseg = ds_p++; - FirstInterestingDrawseg = InterestingDrawsegs.Size(); - - interestingStack.Push (FirstInterestingDrawseg); - ptrdiff_t diffnum = firstdrawseg - drawsegs; - drawsegStack.Push (diffnum); - diffnum = firstvissprite - vissprites; - visspriteStack.Push (diffnum); - viewposStack.Push(ViewPos); - visplaneStack.Push (pl); - - InSubsector = NULL; - R_RenderBSPNode (nodes + numnodes - 1); - R_3D_ResetClip(); // reset clips (floor/ceiling) - R_DrawPlanes (); - - port->mFlags &= ~PORTSF_INSKYBOX; - if (port->mPartner > 0) sectorPortals[port->mPartner].mFlags &= ~PORTSF_INSKYBOX; - } - - // Draw all the masked textures in a second pass, in the reverse order they - // were added. This must be done separately from the previous step for the - // sake of nested skyboxes. - while (interestingStack.Pop (FirstInterestingDrawseg)) - { - ptrdiff_t pd = 0; - - drawsegStack.Pop (pd); - firstdrawseg = drawsegs + pd; - visspriteStack.Pop (pd); - firstvissprite = vissprites + pd; - - // Masked textures and planes need the view coordinates restored for proper positioning. - viewposStack.Pop(ViewPos); - - R_DrawMasked (); - - ds_p = firstdrawseg; - vissprite_p = firstvissprite; - - visplaneStack.Pop (pl); - if (pl->Alpha > 0) - { - R_DrawSinglePlane (pl, pl->Alpha, pl->Additive, true); - } - *freehead = pl; - freehead = &pl->next; - } - firstvissprite = vissprites; - vissprite_p = vissprites + savedvissprite_p; - firstdrawseg = drawsegs; - ds_p = drawsegs + savedds_p; - InterestingDrawsegs.Resize ((unsigned int)FirstInterestingDrawseg); - FirstInterestingDrawseg = savedinteresting; - - lastopening = savedlastopening; - - camera = savedcamera; - viewsector = savedsector; - ViewPos = savedpos; - R_SetVisibility(savedvisibility); - extralight = savedextralight; - ViewAngle = savedangle; - R_SetViewAngle (); - - CurrentPortalInSkybox = false; - R_3D_LeaveSkybox(); - - if(fakeActive) return; - - for (*freehead = visplanes[MAXVISPLANES], visplanes[MAXVISPLANES] = NULL; *freehead; ) - freehead = &(*freehead)->next; -} - -ADD_STAT(skyboxes) -{ - FString out; - out.Format ("%d skybox planes", numskyboxes); - return out; -} - -//========================================================================== -// -// R_DrawSkyPlane -// -//========================================================================== - -void R_DrawSkyPlane (visplane_t *pl) -{ - FTextureID sky1tex, sky2tex; - double frontdpos = 0, backdpos = 0; - - if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) - { - sky1tex = sky2texture; - } - else - { - sky1tex = sky1texture; - } - sky2tex = sky2texture; - skymid = skytexturemid; - skyangle = ViewAngle.BAMs(); - - if (pl->picnum == skyflatnum) - { - if (!(pl->sky & PL_SKYFLAT)) - { // use sky1 - sky1: - frontskytex = TexMan(sky1tex, true); - if (level.flags & LEVEL_DOUBLESKY) - backskytex = TexMan(sky2tex, true); - else - backskytex = NULL; - skyflip = 0; - frontdpos = sky1pos; - backdpos = sky2pos; - frontcyl = sky1cyl; - backcyl = sky2cyl; - } - else if (pl->sky == PL_SKYFLAT) - { // use sky2 - frontskytex = TexMan(sky2tex, true); - backskytex = NULL; - frontcyl = sky2cyl; - skyflip = 0; - frontdpos = sky2pos; - } - else - { // MBF's linedef-controlled skies - // Sky Linedef - const line_t *l = &lines[(pl->sky & ~PL_SKYFLAT)-1]; - - // Sky transferred from first sidedef - const side_t *s = l->sidedef[0]; - int pos; - - // Texture comes from upper texture of reference sidedef - // [RH] If swapping skies, then use the lower sidedef - if (level.flags & LEVEL_SWAPSKIES && s->GetTexture(side_t::bottom).isValid()) - { - pos = side_t::bottom; - } - else - { - pos = side_t::top; - } - - frontskytex = TexMan(s->GetTexture(pos), true); - if (frontskytex == NULL || frontskytex->UseType == FTexture::TEX_Null) - { // [RH] The blank texture: Use normal sky instead. - goto sky1; - } - backskytex = NULL; - - // Horizontal offset is turned into an angle offset, - // to allow sky rotation as well as careful positioning. - // However, the offset is scaled very small, so that it - // allows a long-period of sky rotation. - skyangle += FLOAT2FIXED(s->GetTextureXOffset(pos)); - - // Vertical offset allows careful sky positioning. - skymid = s->GetTextureYOffset(pos) - 28; - - // We sometimes flip the picture horizontally. - // - // Doom always flipped the picture, so we make it optional, - // to make it easier to use the new feature, while to still - // allow old sky textures to be used. - skyflip = l->args[2] ? 0u : ~0u; - - int frontxscale = int(frontskytex->Scale.X * 1024); - frontcyl = MAX(frontskytex->GetWidth(), frontxscale); - if (skystretch) - { - skymid = skymid * frontskytex->GetScaledHeightDouble() / SKYSTRETCH_HEIGHT; - } - } - } - frontpos = int(fmod(frontdpos, sky1cyl * 65536.0)); - if (backskytex != NULL) - { - backpos = int(fmod(backdpos, sky2cyl * 65536.0)); - } - - bool fakefixed = false; - if (fixedcolormap) - { - dc_colormap = fixedcolormap; - } - else - { - fakefixed = true; - fixedcolormap = dc_colormap = NormalLight.Maps; - } - - R_DrawSky (pl); - - if (fakefixed) - fixedcolormap = NULL; -} - -//========================================================================== -// -// R_DrawNormalPlane -// -//========================================================================== - -void R_DrawNormalPlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked) -{ -#ifdef X86_ASM - if (ds_source != ds_cursource) - { - R_SetSpanSource_ASM (ds_source); - } -#endif - - if (alpha <= 0) - { - return; - } - - angle_t planeang = pl->angle; - xscale = pl->xscale << (16 - ds_xbits); - yscale = pl->yscale << (16 - ds_ybits); - if (planeang != 0) - { - double rad = planeang * (M_PI / ANGLE_180); - double cosine = cos(rad), sine = sin(rad); - - pviewx = xs_RoundToInt(pl->xoffs + FLOAT2FIXED(ViewPos.X * cosine - ViewPos.Y * sine)); - pviewy = xs_RoundToInt(pl->yoffs - FLOAT2FIXED(ViewPos.X * sine - ViewPos.Y * cosine)); - } - else - { - pviewx = pl->xoffs + FLOAT2FIXED(ViewPos.X); - pviewy = pl->yoffs - FLOAT2FIXED(ViewPos.Y); - } - - pviewx = FixedMul (xscale, pviewx); - pviewy = FixedMul (yscale, pviewy); - - // left to right mapping - planeang = (ViewAngle.BAMs() - ANG90 + planeang) >> ANGLETOFINESHIFT; - // Scale will be unit scale at FocalLengthX (normally SCREENWIDTH/2) distance - xstepscale = fixed_t(FixedMul(xscale, finecosine[planeang]) / FocalLengthX); - ystepscale = fixed_t(FixedMul(yscale, -finesine[planeang]) / FocalLengthX); - - // [RH] flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - xstepscale = (DWORD)(-(SDWORD)xstepscale); - ystepscale = (DWORD)(-(SDWORD)ystepscale); - } - - int x = pl->right - halfviewwidth - 1; - planeang = (planeang + (ANG90 >> ANGLETOFINESHIFT)) & FINEMASK; - basexfrac = FixedMul (xscale, finecosine[planeang]) + x*xstepscale; - baseyfrac = FixedMul (yscale, -finesine[planeang]) + x*ystepscale; - - planeheight = fabs(pl->height.Zat0() - ViewPos.Z); - - GlobVis = r_FloorVisibility / planeheight; - if (fixedlightlev >= 0) - ds_colormap = basecolormap->Maps + fixedlightlev, plane_shade = false; - else if (fixedcolormap) - ds_colormap = fixedcolormap, plane_shade = false; - else - plane_shade = true; - - if (spanfunc != R_FillSpan) - { - if (masked) - { - if (alpha < OPAQUE || additive) - { - if (!additive) - { - spanfunc = R_DrawSpanMaskedTranslucent; - dc_srcblend = Col2RGB8[alpha>>10]; - dc_destblend = Col2RGB8[(OPAQUE-alpha)>>10]; - } - else - { - spanfunc = R_DrawSpanMaskedAddClamp; - dc_srcblend = Col2RGB8_LessPrecision[alpha>>10]; - dc_destblend = Col2RGB8_LessPrecision[FRACUNIT>>10]; - } - } - else - { - spanfunc = R_DrawSpanMasked; - } - } - else - { - if (alpha < OPAQUE || additive) - { - if (!additive) - { - spanfunc = R_DrawSpanTranslucent; - dc_srcblend = Col2RGB8[alpha>>10]; - dc_destblend = Col2RGB8[(OPAQUE-alpha)>>10]; - } - else - { - spanfunc = R_DrawSpanAddClamp; - dc_srcblend = Col2RGB8_LessPrecision[alpha>>10]; - dc_destblend = Col2RGB8_LessPrecision[FRACUNIT>>10]; - } - } - else - { - spanfunc = R_DrawSpan; - } - } - } - R_MapVisPlane (pl, R_MapPlane); -} - -//========================================================================== -// -// R_DrawTiltedPlane -// -//========================================================================== - -void R_DrawTiltedPlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked) -{ - static const float ifloatpow2[16] = - { - // ifloatpow2[i] = 1 / (1 << i) - 64.f, 32.f, 16.f, 8.f, 4.f, 2.f, 1.f, 0.5f, - 0.25f, 0.125f, 0.0625f, 0.03125f, 0.015625f, 0.0078125f, - 0.00390625f, 0.001953125f - /*, 0.0009765625f, 0.00048828125f, 0.000244140625f, - 1.220703125e-4f, 6.103515625e-5, 3.0517578125e-5*/ - }; - double lxscale, lyscale; - double xscale, yscale; - FVector3 p, m, n; - double ang; - double zeroheight; - - if (alpha <= 0) - { - return; - } - - lxscale = FIXED2DBL(pl->xscale) * ifloatpow2[ds_xbits]; - lyscale = FIXED2DBL(pl->yscale) * ifloatpow2[ds_ybits]; - xscale = 64.f / lxscale; - yscale = 64.f / lyscale; - zeroheight = pl->height.ZatPoint(ViewPos); - - pviewx = MulScale (pl->xoffs, pl->xscale, ds_xbits); - pviewy = MulScale (pl->yoffs, pl->yscale, ds_ybits); - - // p is the texture origin in view space - // Don't add in the offsets at this stage, because doing so can result in - // errors if the flat is rotated. - ang = (DAngle(270.) - ViewAngle).Radians(); - p[0] = ViewPos.X * cos(ang) - ViewPos.Y * sin(ang); - p[2] = ViewPos.X * sin(ang) + ViewPos.Y * cos(ang); - p[1] = pl->height.ZatPoint(0.0, 0.0) - ViewPos.Z; - - // m is the v direction vector in view space - ang = (DAngle(180.) - ViewAngle).Radians(); - m[0] = yscale * cos(ang); - m[2] = yscale * sin(ang); -// m[1] = pl->height.ZatPointF (0, iyscale) - pl->height.ZatPointF (0,0)); -// VectorScale2 (m, 64.f/VectorLength(m)); - - // n is the u direction vector in view space - ang += PI/2; - n[0] = -xscale * cos(ang); - n[2] = -xscale * sin(ang); -// n[1] = pl->height.ZatPointF (ixscale, 0) - pl->height.ZatPointF (0,0)); -// VectorScale2 (n, 64.f/VectorLength(n)); - - // This code keeps the texture coordinates constant across the x,y plane no matter - // how much you slope the surface. Use the commented-out code above instead to keep - // the textures a constant size across the surface's plane instead. - ang = pl->angle * (M_PI / ANGLE_180); - m[1] = pl->height.ZatPoint(ViewPos.X + yscale * sin(ang), ViewPos.Y + yscale * cos(ang)) - zeroheight; - ang += PI/2; - n[1] = pl->height.ZatPoint(ViewPos.X + xscale * sin(ang), ViewPos.Y + xscale * cos(ang)) - zeroheight; - - plane_su = p ^ m; - plane_sv = p ^ n; - plane_sz = m ^ n; - - plane_su.Z *= FocalLengthX; - plane_sv.Z *= FocalLengthX; - plane_sz.Z *= FocalLengthX; - - plane_su.Y *= IYaspectMul; - plane_sv.Y *= IYaspectMul; - plane_sz.Y *= IYaspectMul; - - // Premultiply the texture vectors with the scale factors - plane_su *= 4294967296.f; - plane_sv *= 4294967296.f; - - if (MirrorFlags & RF_XFLIP) - { - plane_su[0] = -plane_su[0]; - plane_sv[0] = -plane_sv[0]; - plane_sz[0] = -plane_sz[0]; - } - - planelightfloat = (r_TiltVisibility * lxscale * lyscale) / (fabs(pl->height.ZatPoint(ViewPos) - ViewPos.Z)) / 65536.f; - - if (pl->height.fC() > 0) - planelightfloat = -planelightfloat; - - if (fixedlightlev >= 0) - ds_colormap = basecolormap->Maps + fixedlightlev, plane_shade = false; - else if (fixedcolormap) - ds_colormap = fixedcolormap, plane_shade = false; - else - ds_colormap = basecolormap->Maps, plane_shade = true; - - if (!plane_shade) - { - for (int i = 0; i < viewwidth; ++i) - { - tiltlighting[i] = ds_colormap; - } - } - -#if defined(X86_ASM) - if (ds_source != ds_curtiltedsource) - R_SetTiltedSpanSource_ASM (ds_source); - R_MapVisPlane (pl, R_DrawTiltedPlane_ASM); -#else - R_MapVisPlane (pl, R_MapTiltedPlane); -#endif -} - -//========================================================================== -// -// R_MapVisPlane -// -// t1/b1 are at x -// t2/b2 are at x+1 -// spanend[y] is at the right edge -// -//========================================================================== - -void R_MapVisPlane (visplane_t *pl, void (*mapfunc)(int y, int x1)) -{ - int x = pl->right - 1; - int t2 = pl->top[x]; - int b2 = pl->bottom[x]; - - if (b2 > t2) - { - clearbufshort (spanend+t2, b2-t2, x); - } - - for (--x; x >= pl->left; --x) - { - int t1 = pl->top[x]; - int b1 = pl->bottom[x]; - const int xr = x+1; - int stop; - - // Draw any spans that have just closed - stop = MIN (t1, b2); - while (t2 < stop) - { - mapfunc (t2++, xr); - } - stop = MAX (b1, t2); - while (b2 > stop) - { - mapfunc (--b2, xr); - } - - // Mark any spans that have just opened - stop = MIN (t2, b1); - while (t1 < stop) - { - spanend[t1++] = x; - } - stop = MAX (b2, t2); - while (b1 > stop) - { - spanend[--b1] = x; - } - - t2 = pl->top[x]; - b2 = pl->bottom[x]; - basexfrac -= xstepscale; - baseyfrac -= ystepscale; - } - // Draw any spans that are still open - while (t2 < b2) - { - mapfunc (--b2, pl->left); - } -} - -//========================================================================== -// -// R_PlaneInitData -// -//========================================================================== - -bool R_PlaneInitData () -{ - int i; - visplane_t *pl; - - // Free all visplanes and let them be re-allocated as needed. - pl = freetail; - - while (pl) - { - visplane_t *next = pl->next; - M_Free (pl); - pl = next; - } - freetail = NULL; - freehead = &freetail; - - for (i = 0; i < MAXVISPLANES; i++) - { - pl = visplanes[i]; - visplanes[i] = NULL; - while (pl) - { - visplane_t *next = pl->next; - M_Free (pl); - pl = next; - } - } - - return true; -} diff --git a/src/r_plane.h b/src/r_plane.h deleted file mode 100644 index 25e46de98..000000000 --- a/src/r_plane.h +++ /dev/null @@ -1,117 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Refresh, visplane stuff (floor, ceilings). -// -//----------------------------------------------------------------------------- - - -#ifndef __R_PLANE_H__ -#define __R_PLANE_H__ - -#include - -class ASkyViewpoint; - -// -// The infamous visplane -// -struct visplane_s -{ - struct visplane_s *next; // Next visplane in hash chain -- killough - - secplane_t height; - FTextureID picnum; - int lightlevel; - fixed_t xoffs, yoffs; // killough 2/28/98: Support scrolling flats - int left, right; - FDynamicColormap *colormap; // [RH] Support multiple colormaps - fixed_t xscale, yscale; // [RH] Support flat scaling - angle_t angle; // [RH] Support flat rotation - int sky; - FSectorPortal *portal; // [RH] Support sky boxes - - // [RH] This set of variables copies information from the time when the - // visplane is created. They are only used by stacks so that you can - // have stacked sectors inside a skybox. If the visplane is not for a - // stack, then they are unused. - int extralight; - double visibility; - DVector3 viewpos; - DAngle viewangle; - fixed_t Alpha; - bool Additive; - - // kg3D - keep track of mirror and skybox owner - int CurrentSkybox; - int CurrentPortalUniq; // mirror counter, counts all of them - int MirrorFlags; // this is not related to CurrentMirror - - unsigned short *bottom; // [RH] bottom and top arrays are dynamically - unsigned short pad; // allocated immediately after the - unsigned short top[]; // visplane. -}; -typedef struct visplane_s visplane_t; - - - -// Visplane related. -extern ptrdiff_t lastopening; // type short - - -typedef void (*planefunction_t) (int top, int bottom); - -extern planefunction_t floorfunc; -extern planefunction_t ceilingfunc_t; - -extern short floorclip[MAXWIDTH]; -extern short ceilingclip[MAXWIDTH]; - -extern fixed_t yslope[MAXHEIGHT]; - -void R_InitPlanes (); -void R_DeinitPlanes (); -void R_ClearPlanes (bool fullclear); - -int R_DrawPlanes (); -void R_DrawPortals (); -void R_DrawSkyPlane (visplane_t *pl); -void R_DrawNormalPlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked); -void R_DrawTiltedPlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked); -void R_MapVisPlane (visplane_t *pl, void (*mapfunc)(int y, int x1)); - -visplane_t *R_FindPlane -( const secplane_t &height, - FTextureID picnum, - int lightlevel, - double alpha, - bool additive, - const FTransform &xform, - int sky, - FSectorPortal *portal); - -visplane_t *R_CheckPlane (visplane_t *pl, int start, int stop); - - -// [RH] Added for multires support -bool R_PlaneInitData (void); - - -extern visplane_t* floorplane; -extern visplane_t* ceilingplane; - -#endif // __R_PLANE_H__ diff --git a/src/r_renderer.h b/src/r_renderer.h index 4236993d6..9f1e94428 100644 --- a/src/r_renderer.h +++ b/src/r_renderer.h @@ -53,14 +53,10 @@ struct FRenderer virtual int GetMaxViewPitch(bool down) = 0; // return value is in plain degrees virtual void OnModeSet () {} - virtual void ErrorCleanup () {} - virtual void ClearBuffer(int color) = 0; + virtual void SetClearColor(int color) = 0; virtual void Init() = 0; - virtual void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) {} - virtual void SetupFrame(player_t *player) {} - virtual void CopyStackedViewParameters() {} virtual void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) = 0; - virtual sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) = 0; + virtual sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) = 0; virtual void SetFogParams(int _fogdensity, PalEntry _outsidefogcolor, int _outsidefogdensity, int _skyfog) {} virtual void PreprocessLevel() {} virtual void CleanLevelData() {} diff --git a/src/r_segs.cpp b/src/r_segs.cpp deleted file mode 100644 index 696e30b7c..000000000 --- a/src/r_segs.cpp +++ /dev/null @@ -1,3311 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// All the clipping: columns, horizontal spans, sky columns. -// -// This file contains some code from the Build Engine. -// -// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman -// Ken Silverman's official web site: "http://www.advsys.net/ken" -// See the included license file "BUILDLIC.TXT" for license info. -// -//----------------------------------------------------------------------------- - -#include -#include - -#include "templates.h" -#include "i_system.h" - -#include "doomdef.h" -#include "doomstat.h" -#include "doomdata.h" -#include "p_lnspec.h" - -#include "r_local.h" -#include "r_sky.h" -#include "v_video.h" - -#include "m_swap.h" -#include "w_wad.h" -#include "stats.h" -#include "a_sharedglobal.h" -#include "d_net.h" -#include "g_level.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - -#define WALLYREPEAT 8 - - -CVAR(Bool, r_np2, true, 0) - -//CVAR (Int, ty, 8, 0) -//CVAR (Int, tx, 8, 0) - -#define HEIGHTBITS 12 -#define HEIGHTSHIFT (FRACBITS-HEIGHTBITS) - -extern double globaluclip, globaldclip; - -PortalDrawseg* CurrentPortal = NULL; -int CurrentPortalUniq = 0; -bool CurrentPortalInSkybox = false; - -// OPTIMIZE: closed two sided lines as single sided - -// killough 1/6/98: replaced globals with statics where appropriate - -static bool segtextured; // True if any of the segs textures might be visible. -bool markfloor; // False if the back side is the same plane. -bool markceiling; -FTexture *toptexture; -FTexture *bottomtexture; -FTexture *midtexture; -fixed_t rw_offset_top; -fixed_t rw_offset_mid; -fixed_t rw_offset_bottom; - - -int wallshade; - -short walltop[MAXWIDTH]; // [RH] record max extents of wall -short wallbottom[MAXWIDTH]; -short wallupper[MAXWIDTH]; -short walllower[MAXWIDTH]; -float swall[MAXWIDTH]; -fixed_t lwall[MAXWIDTH]; -double lwallscale; - -// -// regular wall -// -extern double rw_backcz1, rw_backcz2; -extern double rw_backfz1, rw_backfz2; -extern double rw_frontcz1, rw_frontcz2; -extern double rw_frontfz1, rw_frontfz2; - -int rw_ceilstat, rw_floorstat; -bool rw_mustmarkfloor, rw_mustmarkceiling; -bool rw_prepped; -bool rw_markportal; -bool rw_havehigh; -bool rw_havelow; - -float rw_light; // [RH] Scale lights with viewsize adjustments -float rw_lightstep; -float rw_lightleft; - -static double rw_frontlowertop; - -static int rw_x; -static int rw_stopx; -fixed_t rw_offset; -static double rw_scalestep; -static double rw_midtexturemid; -static double rw_toptexturemid; -static double rw_bottomtexturemid; -static double rw_midtexturescalex; -static double rw_midtexturescaley; -static double rw_toptexturescalex; -static double rw_toptexturescaley; -static double rw_bottomtexturescalex; -static double rw_bottomtexturescaley; - -FTexture *rw_pic; - -static fixed_t *maskedtexturecol; - -static void R_RenderDecal (side_t *wall, DBaseDecal *first, drawseg_t *clipper, int pass); -static void WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans)); -void wallscan_np2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask); -static void wallscan_np2_ds(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat); -static void call_wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask); - -//============================================================================= -// -// CVAR r_fogboundary -// -// If true, makes fog look more "real" by shading the walls separating two -// sectors with different fog. -//============================================================================= - -CVAR(Bool, r_fogboundary, true, 0) - -inline bool IsFogBoundary (sector_t *front, sector_t *back) -{ - return r_fogboundary && fixedcolormap == NULL && front->ColorMap->Fade && - front->ColorMap->Fade != back->ColorMap->Fade && - (front->GetTexture(sector_t::ceiling) != skyflatnum || back->GetTexture(sector_t::ceiling) != skyflatnum); -} - -//============================================================================= -// -// CVAR r_drawmirrors -// -// Set to false to disable rendering of mirrors -//============================================================================= - -CVAR(Bool, r_drawmirrors, true, 0) - -// -// R_RenderMaskedSegRange -// -float *MaskedSWall; -float MaskedScaleY; - -static void BlastMaskedColumn (void (*blastfunc)(const BYTE *pixels, const FTexture::Span *spans), FTexture *tex) -{ - // calculate lighting - if (fixedcolormap == NULL && fixedlightlev < 0) - { - dc_colormap = basecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); - } - - dc_iscale = xs_Fix<16>::ToFix(MaskedSWall[dc_x] * MaskedScaleY); - if (sprflipvert) - sprtopscreen = CenterY + dc_texturemid * spryscale; - else - sprtopscreen = CenterY - dc_texturemid * spryscale; - - // killough 1/25/98: here's where Medusa came in, because - // it implicitly assumed that the column was all one patch. - // Originally, Doom did not construct complete columns for - // multipatched textures, so there were no header or trailer - // bytes in the column referred to below, which explains - // the Medusa effect. The fix is to construct true columns - // when forming multipatched textures (see r_data.c). - - // draw the texture - const FTexture::Span *spans; - const BYTE *pixels = tex->GetColumn (maskedtexturecol[dc_x] >> FRACBITS, &spans); - blastfunc (pixels, spans); - rw_light += rw_lightstep; - spryscale += rw_scalestep; -} - -// Clip a midtexture to the floor and ceiling of the sector in front of it. -void ClipMidtex(int x1, int x2) -{ - short most[MAXWIDTH]; - - WallMost(most, curline->frontsector->ceilingplane, &WallC); - for (int i = x1; i < x2; ++i) - { - if (wallupper[i] < most[i]) - wallupper[i] = most[i]; - } - WallMost(most, curline->frontsector->floorplane, &WallC); - for (int i = x1; i < x2; ++i) - { - if (walllower[i] > most[i]) - walllower[i] = most[i]; - } -} - -void R_RenderFakeWallRange(drawseg_t *ds, int x1, int x2); - -void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) -{ - FTexture *tex; - int i; - sector_t tempsec; // killough 4/13/98 - double texheight, texheightscale; - bool notrelevant = false; - double rowoffset; - - const sector_t *sec; - - sprflipvert = false; - - curline = ds->curline; - - // killough 4/11/98: draw translucent 2s normal textures - // [RH] modified because we don't use user-definable translucency maps - ESPSResult drawmode; - - drawmode = R_SetPatchStyle (LegacyRenderStyles[curline->linedef->flags & ML_ADDTRANS ? STYLE_Add : STYLE_Translucent], - (float)MIN(curline->linedef->alpha, 1.), 0, 0); - - if ((drawmode == DontDraw && !ds->bFogBoundary && !ds->bFakeBoundary)) - { - return; - } - - NetUpdate (); - - frontsector = curline->frontsector; - backsector = curline->backsector; - - tex = TexMan(curline->sidedef->GetTexture(side_t::mid), true); - if (i_compatflags & COMPATF_MASKEDMIDTEX) - { - tex = tex->GetRawTexture(); - } - - // killough 4/13/98: get correct lightlevel for 2s normal textures - sec = R_FakeFlat (frontsector, &tempsec, NULL, NULL, false); - - basecolormap = sec->ColorMap; // [RH] Set basecolormap - - wallshade = ds->shade; - rw_lightstep = ds->lightstep; - rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; - - if (fixedlightlev < 0) - { - if (!(fake3D & FAKE3D_CLIPTOP)) - { - sclipTop = sec->ceilingplane.ZatPoint(ViewPos); - } - for (i = frontsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) - { - if (sclipTop <= frontsector->e->XFloor.lightlist[i].plane.Zat0()) - { - lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - - mfloorclip = openings + ds->sprbottomclip - ds->x1; - mceilingclip = openings + ds->sprtopclip - ds->x1; - - // [RH] Draw fog partition - if (ds->bFogBoundary) - { - R_DrawFogBoundary (x1, x2, mceilingclip, mfloorclip); - if (ds->maskedtexturecol == -1) - { - goto clearfog; - } - } - if ((ds->bFakeBoundary && !(ds->bFakeBoundary & 4)) || drawmode == DontDraw) - { - goto clearfog; - } - - MaskedSWall = (float *)(openings + ds->swall) - ds->x1; - MaskedScaleY = ds->yscale; - maskedtexturecol = (fixed_t *)(openings + ds->maskedtexturecol) - ds->x1; - spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); - rw_scalestep = ds->iscalestep; - - if (fixedlightlev >= 0) - dc_colormap = basecolormap->Maps + fixedlightlev; - else if (fixedcolormap != NULL) - dc_colormap = fixedcolormap; - - // find positioning - texheight = tex->GetScaledHeightDouble(); - texheightscale = fabs(curline->sidedef->GetTextureYScale(side_t::mid)); - if (texheightscale != 1) - { - texheight = texheight / texheightscale; - } - if (curline->linedef->flags & ML_DONTPEGBOTTOM) - { - dc_texturemid = MAX(frontsector->GetPlaneTexZ(sector_t::floor), backsector->GetPlaneTexZ(sector_t::floor)) + texheight; - } - else - { - dc_texturemid = MIN(frontsector->GetPlaneTexZ(sector_t::ceiling), backsector->GetPlaneTexZ(sector_t::ceiling)); - } - - rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid); - - if (!(curline->linedef->flags & ML_WRAP_MIDTEX) && - !(curline->sidedef->Flags & WALLF_WRAP_MIDTEX)) - { // Texture does not wrap vertically. - double textop; - - if (MaskedScaleY < 0) - { - MaskedScaleY = -MaskedScaleY; - sprflipvert = true; - } - if (tex->bWorldPanning) - { - // rowoffset is added before the multiply so that the masked texture will - // still be positioned in world units rather than texels. - dc_texturemid += rowoffset - ViewPos.Z; - textop = dc_texturemid; - dc_texturemid *= MaskedScaleY; - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - textop = dc_texturemid + rowoffset / MaskedScaleY - ViewPos.Z; - dc_texturemid = (dc_texturemid - ViewPos.Z) * MaskedScaleY + rowoffset; - } - if (sprflipvert) - { - MaskedScaleY = -MaskedScaleY; - dc_texturemid -= tex->GetHeight() << FRACBITS; - } - - // [RH] Don't bother drawing segs that are completely offscreen - if (globaldclip * ds->sz1 < -textop && globaldclip * ds->sz2 < -textop) - { // Texture top is below the bottom of the screen - goto clearfog; - } - - if (globaluclip * ds->sz1 > texheight - textop && globaluclip * ds->sz2 > texheight - textop) - { // Texture bottom is above the top of the screen - goto clearfog; - } - - if ((fake3D & FAKE3D_CLIPBOTTOM) && textop < sclipBottom - ViewPos.Z) - { - notrelevant = true; - goto clearfog; - } - if ((fake3D & FAKE3D_CLIPTOP) && textop - texheight > sclipTop - ViewPos.Z) - { - notrelevant = true; - goto clearfog; - } - - WallC.sz1 = ds->sz1; - WallC.sz2 = ds->sz2; - WallC.sx1 = ds->sx1; - WallC.sx2 = ds->sx2; - - if (fake3D & FAKE3D_CLIPTOP) - { - OWallMost(wallupper, textop < sclipTop - ViewPos.Z ? textop : sclipTop - ViewPos.Z, &WallC); - } - else - { - OWallMost(wallupper, textop, &WallC); - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - OWallMost(walllower, textop - texheight > sclipBottom - ViewPos.Z ? textop - texheight : sclipBottom - ViewPos.Z, &WallC); - } - else - { - OWallMost(walllower, textop - texheight, &WallC); - } - - for (i = x1; i < x2; i++) - { - if (wallupper[i] < mceilingclip[i]) - wallupper[i] = mceilingclip[i]; - } - for (i = x1; i < x2; i++) - { - if (walllower[i] > mfloorclip[i]) - walllower[i] = mfloorclip[i]; - } - - if (CurrentSkybox) - { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor - // or above the ceiling, so the appropriate end won't be clipped automatically when adding - // this drawseg. - if ((curline->linedef->flags & ML_CLIP_MIDTEX) || - (curline->sidedef->Flags & WALLF_CLIP_MIDTEX)) - { - ClipMidtex(x1, x2); - } - } - - mfloorclip = walllower; - mceilingclip = wallupper; - - // draw the columns one at a time - if (drawmode == DoDraw0) - { - for (dc_x = x1; dc_x < x2; ++dc_x) - { - BlastMaskedColumn (R_DrawMaskedColumn, tex); - } - } - else - { - // [RH] Draw up to four columns at once - int stop = x2 & ~3; - - if (x1 >= x2) - goto clearfog; - - dc_x = x1; - - while ((dc_x < stop) && (dc_x & 3)) - { - BlastMaskedColumn (R_DrawMaskedColumn, tex); - dc_x++; - } - - while (dc_x < stop) - { - rt_initcols(); - BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++; - BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++; - BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++; - BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); - rt_draw4cols (dc_x - 3); - dc_x++; - } - - while (dc_x < x2) - { - BlastMaskedColumn (R_DrawMaskedColumn, tex); - dc_x++; - } - } - } - else - { // Texture does wrap vertically. - if (tex->bWorldPanning) - { - // rowoffset is added before the multiply so that the masked texture will - // still be positioned in world units rather than texels. - dc_texturemid = (dc_texturemid + rowoffset - ViewPos.Z) * MaskedScaleY; - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - dc_texturemid = (dc_texturemid - ViewPos.Z) * MaskedScaleY + rowoffset; - } - - WallC.sz1 = ds->sz1; - WallC.sz2 = ds->sz2; - WallC.sx1 = ds->sx1; - WallC.sx2 = ds->sx2; - - if (CurrentSkybox) - { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor - // or above the ceiling, so the appropriate end won't be clipped automatically when adding - // this drawseg. - if ((curline->linedef->flags & ML_CLIP_MIDTEX) || - (curline->sidedef->Flags & WALLF_CLIP_MIDTEX)) - { - ClipMidtex(x1, x2); - } - } - - if (fake3D & FAKE3D_CLIPTOP) - { - OWallMost(wallupper, sclipTop - ViewPos.Z, &WallC); - for (i = x1; i < x2; i++) - { - if (wallupper[i] < mceilingclip[i]) - wallupper[i] = mceilingclip[i]; - } - mceilingclip = wallupper; - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - OWallMost(walllower, sclipBottom - ViewPos.Z, &WallC); - for (i = x1; i < x2; i++) - { - if (walllower[i] > mfloorclip[i]) - walllower[i] = mfloorclip[i]; - } - mfloorclip = walllower; - } - - rw_offset = 0; - rw_pic = tex; - wallscan_np2_ds(ds, x1, x2, mceilingclip, mfloorclip, MaskedSWall, maskedtexturecol, ds->yscale); - } - -clearfog: - R_FinishSetPatchStyle (); - if (ds->bFakeBoundary & 3) - { - R_RenderFakeWallRange(ds, x1, x2); - } - if (!notrelevant) - { - if (fake3D & FAKE3D_REFRESHCLIP) - { - assert(ds->bkup >= 0); - memcpy(openings + ds->sprtopclip, openings + ds->bkup, (ds->x2 - ds->x1) * 2); - } - else - { - clearbufshort(openings + ds->sprtopclip - ds->x1 + x1, x2 - x1, viewheight); - } - } - return; -} - -// kg3D - render one fake wall -void R_RenderFakeWall(drawseg_t *ds, int x1, int x2, F3DFloor *rover) -{ - int i; - fixed_t xscale; - double yscale; - - fixed_t Alpha = Scale(rover->alpha, OPAQUE, 255); - ESPSResult drawmode; - drawmode = R_SetPatchStyle (LegacyRenderStyles[rover->flags & FF_ADDITIVETRANS ? STYLE_Add : STYLE_Translucent], - Alpha, 0, 0); - - if(drawmode == DontDraw) { - R_FinishSetPatchStyle(); - return; - } - - rw_lightstep = ds->lightstep; - rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; - - mfloorclip = openings + ds->sprbottomclip - ds->x1; - mceilingclip = openings + ds->sprtopclip - ds->x1; - - spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); - rw_scalestep = ds->iscalestep; - MaskedSWall = (float *)(openings + ds->swall) - ds->x1; - - // find positioning - side_t *scaledside; - side_t::ETexpart scaledpart; - if (rover->flags & FF_UPPERTEXTURE) - { - scaledside = curline->sidedef; - scaledpart = side_t::top; - } - else if (rover->flags & FF_LOWERTEXTURE) - { - scaledside = curline->sidedef; - scaledpart = side_t::bottom; - } - else - { - scaledside = rover->master->sidedef[0]; - scaledpart = side_t::mid; - } - xscale = FLOAT2FIXED(rw_pic->Scale.X * scaledside->GetTextureXScale(scaledpart)); - yscale = rw_pic->Scale.Y * scaledside->GetTextureYScale(scaledpart); - - double rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureYOffset(side_t::mid); - double planez = rover->model->GetPlaneTexZ(sector_t::ceiling); - rw_offset = FLOAT2FIXED(curline->sidedef->GetTextureXOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureXOffset(side_t::mid)); - if (rowoffset < 0) - { - rowoffset += rw_pic->GetHeight(); - } - dc_texturemid = (planez - ViewPos.Z) * yscale; - if (rw_pic->bWorldPanning) - { - // rowoffset is added before the multiply so that the masked texture will - // still be positioned in world units rather than texels. - - dc_texturemid = dc_texturemid + rowoffset * yscale; - rw_offset = MulScale16 (rw_offset, xscale); - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - dc_texturemid += rowoffset; - } - - if (fixedlightlev >= 0) - dc_colormap = basecolormap->Maps + fixedlightlev; - else if (fixedcolormap != NULL) - dc_colormap = fixedcolormap; - - WallC.sz1 = ds->sz1; - WallC.sz2 = ds->sz2; - WallC.sx1 = ds->sx1; - WallC.sx2 = ds->sx2; - WallC.tleft.X = ds->cx; - WallC.tleft.Y = ds->cy; - WallC.tright.X = ds->cx + ds->cdx; - WallC.tright.Y = ds->cy + ds->cdy; - WallT = ds->tmapvals; - - OWallMost(wallupper, sclipTop - ViewPos.Z, &WallC); - OWallMost(walllower, sclipBottom - ViewPos.Z, &WallC); - - for (i = x1; i < x2; i++) - { - if (wallupper[i] < mceilingclip[i]) - wallupper[i] = mceilingclip[i]; - } - for (i = x1; i < x2; i++) - { - if (walllower[i] > mfloorclip[i]) - walllower[i] = mfloorclip[i]; - } - - PrepLWall (lwall, curline->sidedef->TexelLength*xscale, ds->sx1, ds->sx2); - wallscan_np2_ds(ds, x1, x2, wallupper, walllower, MaskedSWall, lwall, yscale); - R_FinishSetPatchStyle(); -} - -// kg3D - walls of fake floors -void R_RenderFakeWallRange (drawseg_t *ds, int x1, int x2) -{ - FTexture *const DONT_DRAW = ((FTexture*)(intptr_t)-1); - int i,j; - F3DFloor *rover, *fover = NULL; - int passed, last; - fixed_t floorheight; - fixed_t ceilingheight; - - sprflipvert = false; - curline = ds->curline; - - frontsector = curline->frontsector; - backsector = curline->backsector; - - if (backsector == NULL) - { - return; - } - if ((ds->bFakeBoundary & 3) == 2) - { - sector_t *sec = backsector; - backsector = frontsector; - frontsector = sec; - } - - floorheight = FLOAT2FIXED(backsector->CenterFloor()); - ceilingheight = FLOAT2FIXED(backsector->CenterCeiling()); - - // maybe fix clipheights - if (!(fake3D & FAKE3D_CLIPBOTTOM)) sclipBottom = floorheight; - if (!(fake3D & FAKE3D_CLIPTOP)) sclipTop = ceilingheight; - - // maybe not visible - if (sclipBottom >= FLOAT2FIXED(frontsector->CenterCeiling())) return; - if (sclipTop <= FLOAT2FIXED(frontsector->CenterFloor())) return; - - if (fake3D & FAKE3D_DOWN2UP) - { // bottom to viewz - last = 0; - for (i = backsector->e->XFloor.ffloors.Size() - 1; i >= 0; i--) - { - rover = backsector->e->XFloor.ffloors[i]; - if (!(rover->flags & FF_EXISTS)) continue; - - // visible? - passed = 0; - if (!(rover->flags & FF_RENDERSIDES) || rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || - rover->top.plane->Zat0() <= sclipBottom || - rover->bottom.plane->Zat0() >= ceilingheight || - rover->top.plane->Zat0() <= floorheight) - { - if (!i) - { - passed = 1; - } - else - { - continue; - } - } - - rw_pic = NULL; - if (rover->bottom.plane->Zat0() >= sclipTop || passed) - { - if (last) - { - break; - } - // maybe wall from inside rendering? - fover = NULL; - for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) - { - fover = frontsector->e->XFloor.ffloors[j]; - if (fover->model == rover->model) - { // never - fover = NULL; - break; - } - if (!(fover->flags & FF_EXISTS)) continue; - if (!(fover->flags & FF_RENDERSIDES)) continue; - // no sloped walls, it's bugged - if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; - - // visible? - if (fover->top.plane->Zat0() <= sclipBottom) continue; // no - if (fover->bottom.plane->Zat0() >= sclipTop) - { // no, last possible - fover = NULL; - break; - } - // it is, render inside? - if (!(fover->flags & (FF_BOTHPLANES|FF_INVERTPLANES))) - { // no - fover = NULL; - } - break; - } - // nothing - if (!fover || j == -1) - { - break; - } - // correct texture - if (fover->flags & rover->flags & FF_SWIMMABLE) - { // don't ever draw (but treat as something has been found) - rw_pic = DONT_DRAW; - } - else if(fover->flags & FF_UPPERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); - } - else if(fover->flags & FF_LOWERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); - } - else - { - rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); - } - } - else if (frontsector->e->XFloor.ffloors.Size()) - { - // maybe not visible? - fover = NULL; - for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) - { - fover = frontsector->e->XFloor.ffloors[j]; - if (fover->model == rover->model) // never - { - break; - } - if (!(fover->flags & FF_EXISTS)) continue; - if (!(fover->flags & FF_RENDERSIDES)) continue; - // no sloped walls, it's bugged - if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; - - // visible? - if (fover->top.plane->Zat0() <= sclipBottom) continue; // no - if (fover->bottom.plane->Zat0() >= sclipTop) - { // visible, last possible - fover = NULL; - break; - } - if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && - !(!(fover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) - ) - { - break; - } - if (fover->flags & rover->flags & FF_SWIMMABLE) - { // don't ever draw (but treat as something has been found) - rw_pic = DONT_DRAW; - } - fover = NULL; // visible - break; - } - if (fover && j != -1) - { - fover = NULL; - last = 1; - continue; // not visible - } - } - if (!rw_pic) - { - fover = NULL; - if (rover->flags & FF_UPPERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); - } - else if(rover->flags & FF_LOWERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); - } - else - { - rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); - } - } - // correct colors now - basecolormap = frontsector->ColorMap; - wallshade = ds->shade; - if (fixedlightlev < 0) - { - if ((ds->bFakeBoundary & 3) == 2) - { - for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) - { - if (sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) - { - lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - else - { - for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) - { - if (sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) - { - lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - } - if (rw_pic != DONT_DRAW) - { - R_RenderFakeWall(ds, x1, x2, fover ? fover : rover); - } - else rw_pic = NULL; - break; - } - } - else - { // top to viewz - for (i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) - { - rover = backsector->e->XFloor.ffloors[i]; - if (!(rover->flags & FF_EXISTS)) continue; - - // visible? - passed = 0; - if (!(rover->flags & FF_RENDERSIDES) || - rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || - rover->bottom.plane->Zat0() >= sclipTop || - rover->top.plane->Zat0() <= floorheight || - rover->bottom.plane->Zat0() >= ceilingheight) - { - if ((unsigned)i == backsector->e->XFloor.ffloors.Size() - 1) - { - passed = 1; - } - else - { - continue; - } - } - rw_pic = NULL; - if (rover->top.plane->Zat0() <= sclipBottom || passed) - { // maybe wall from inside rendering? - fover = NULL; - for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) - { - fover = frontsector->e->XFloor.ffloors[j]; - if (fover->model == rover->model) - { // never - fover = NULL; - break; - } - if (!(fover->flags & FF_EXISTS)) continue; - if (!(fover->flags & FF_RENDERSIDES)) continue; - // no sloped walls, it's bugged - if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; - - // visible? - if (fover->bottom.plane->Zat0() >= sclipTop) continue; // no - if (fover->top.plane->Zat0() <= sclipBottom) - { // no, last possible - fover = NULL; - break; - } - // it is, render inside? - if (!(fover->flags & (FF_BOTHPLANES|FF_INVERTPLANES))) - { // no - fover = NULL; - } - break; - } - // nothing - if (!fover || (unsigned)j == frontsector->e->XFloor.ffloors.Size()) - { - break; - } - // correct texture - if (fover->flags & rover->flags & FF_SWIMMABLE) - { - rw_pic = DONT_DRAW; // don't ever draw (but treat as something has been found) - } - else if (fover->flags & FF_UPPERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); - } - else if (fover->flags & FF_LOWERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); - } - else - { - rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); - } - } - else if (frontsector->e->XFloor.ffloors.Size()) - { // maybe not visible? - fover = NULL; - for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) - { - fover = frontsector->e->XFloor.ffloors[j]; - if (fover->model == rover->model) - { // never - break; - } - if (!(fover->flags & FF_EXISTS)) continue; - if (!(fover->flags & FF_RENDERSIDES)) continue; - // no sloped walls, its bugged - if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; - - // visible? - if (fover->bottom.plane->Zat0() >= sclipTop) continue; // no - if (fover->top.plane->Zat0() <= sclipBottom) - { // visible, last possible - fover = NULL; - break; - } - if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && - !(!(rover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) - ) - { - break; - } - if (fover->flags & rover->flags & FF_SWIMMABLE) - { // don't ever draw (but treat as something has been found) - rw_pic = DONT_DRAW; - } - fover = NULL; // visible - break; - } - if (fover && (unsigned)j != frontsector->e->XFloor.ffloors.Size()) - { // not visible - break; - } - } - if (rw_pic == NULL) - { - fover = NULL; - if (rover->flags & FF_UPPERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); - } - else if (rover->flags & FF_LOWERTEXTURE) - { - rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); - } - else - { - rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); - } - } - // correct colors now - basecolormap = frontsector->ColorMap; - wallshade = ds->shade; - if (fixedlightlev < 0) - { - if ((ds->bFakeBoundary & 3) == 2) - { - for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) - { - if (sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) - { - lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - else - { - for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) - { - if(sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) - { - lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - break; - } - } - } - } - - if (rw_pic != DONT_DRAW) - { - R_RenderFakeWall(ds, x1, x2, fover ? fover : rover); - } - else - { - rw_pic = NULL; - } - break; - } - } - return; -} - -// prevlineasm1 is like vlineasm1 but skips the loop if only drawing one pixel -inline fixed_t prevline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) -{ - dc_iscale = vince; - dc_colormap = colormap; - dc_count = count; - dc_texturefrac = vplce; - dc_source = bufplce; - dc_dest = dest; - return doprevline1 (); -} - -void wallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, - double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - int x, fracbits; - int y1ve[4], y2ve[4], u4, d4, z; - char bad; - float light = rw_light - rw_lightstep; - SDWORD xoffset; - BYTE *basecolormapdata; - double iscale; - - // This function also gets used to draw skies. Unlike BUILD, skies are - // drawn by visplane instead of by bunch, so these checks are invalid. - //if ((uwal[x1] > viewheight) && (uwal[x2] > viewheight)) return; - //if ((dwal[x1] < 0) && (dwal[x2] < 0)) return; - - if (rw_pic->UseType == FTexture::TEX_Null) - { - return; - } - -//extern cycle_t WallScanCycles; -//clock (WallScanCycles); - - rw_pic->GetHeight(); // Make sure texture size is loaded - fracbits = 32 - rw_pic->HeightBits; - setupvline(fracbits); - xoffset = rw_offset; - basecolormapdata = basecolormap->Maps; - - x = x1; - //while ((umost[x] > dmost[x]) && (x < x2)) x++; - - bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); - if (fixed) - { - palookupoffse[0] = dc_colormap; - palookupoffse[1] = dc_colormap; - palookupoffse[2] = dc_colormap; - palookupoffse[3] = dc_colormap; - } - - for(; (x < x2) && (x & 3); ++x) - { - light += rw_lightstep; - y1ve[0] = uwal[x];//max(uwal[x],umost[x]); - y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); - if (y2ve[0] <= y1ve[0]) continue; - assert (y1ve[0] < viewheight); - assert (y2ve[0] <= viewheight); - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + x + dc_destorg; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); - - dovline1(); - } - - for(; x < x2-3; x += 4) - { - bad = 0; - for (z = 3; z>= 0; --z) - { - y1ve[z] = uwal[x+z];//max(uwal[x+z],umost[x+z]); - y2ve[z] = dwal[x+z];//min(dwal[x+z],dmost[x+z])-1; - if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); - iscale = swal[x + z] * yrepeat; - vince[z] = xs_ToFixed(fracbits, iscale); - vplce[z] = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[z] - CenterY + 1)); - } - if (bad == 15) - { - light += rw_lightstep * 4; - continue; - } - - if (!fixed) - { - for (z = 0; z < 4; ++z) - { - light += rw_lightstep; - palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - } - - u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); - d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); - - if ((bad != 0) || (u4 >= d4)) - { - for (z = 0; z < 4; ++z) - { - if (!(bad & 1)) - { - prevline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+x+z+dc_destorg); - } - bad >>= 1; - } - continue; - } - - for (z = 0; z < 4; ++z) - { - if (u4 > y1ve[z]) - { - vplce[z] = prevline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+x+z+dc_destorg); - } - } - - if (d4 > u4) - { - dc_count = d4-u4; - dc_dest = ylookup[u4]+x+dc_destorg; - dovline4(); - } - - BYTE *i = x+ylookup[d4]+dc_destorg; - for (z = 0; z < 4; ++z) - { - if (y2ve[z] > d4) - { - prevline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); - } - } - } - for(;x> FRACBITS); - dc_dest = ylookup[y1ve[0]] + x + dc_destorg; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); - - dovline1(); - } - -//unclock (WallScanCycles); - - NetUpdate (); -} - -void wallscan_striped (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) -{ - FDynamicColormap *startcolormap = basecolormap; - int startshade = wallshade; - bool fogginess = foggy; - - short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; - short *up, *down; - - up = uwal; - down = most1; - - assert(WallC.sx1 <= x1); - assert(WallC.sx2 >= x2); - - // kg3D - fake floors instead of zdoom light list - for (unsigned int i = 0; i < frontsector->e->XFloor.lightlist.Size(); i++) - { - int j = WallMost (most3, frontsector->e->XFloor.lightlist[i].plane, &WallC); - if (j != 3) - { - for (int j = x1; j < x2; ++j) - { - down[j] = clamp (most3[j], up[j], dwal[j]); - } - wallscan (x1, x2, up, down, swal, lwal, yrepeat); - up = down; - down = (down == most1) ? most2 : most1; - } - - lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(fogginess, - *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - } - - wallscan (x1, x2, up, dwal, swal, lwal, yrepeat); - basecolormap = startcolormap; - wallshade = startshade; -} - -static void call_wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask) -{ - if (mask) - { - if (colfunc == basecolfunc) - { - maskwallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - else - { - transmaskwallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - } - else - { - if (fixedcolormap != NULL || fixedlightlev >= 0 || !(frontsector->e && frontsector->e->XFloor.lightlist.Size())) - { - wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - else - { - wallscan_striped(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - } -} - -//============================================================================= -// -// wallscan_np2 -// -// This is a wrapper around wallscan that helps it tile textures whose heights -// are not powers of 2. It divides the wall into texture-sized strips and calls -// wallscan for each of those. Since only one repetition of the texture fits -// in each strip, wallscan will not tile. -// -//============================================================================= - -void wallscan_np2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask) -{ - if (!r_np2) - { - call_wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, mask); - } - else - { - short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; - short *up, *down; - double texheight = rw_pic->GetHeight(); - double partition; - double scaledtexheight = texheight / yrepeat; - - if (yrepeat >= 0) - { // normal orientation: draw strips from top to bottom - partition = top - fmod(top - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); - if (partition == top) - { - partition -= scaledtexheight; - } - up = uwal; - down = most1; - dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; - while (partition > bot) - { - int j = OWallMost(most3, partition - ViewPos.Z, &WallC); - if (j != 3) - { - for (int j = x1; j < x2; ++j) - { - down[j] = clamp(most3[j], up[j], dwal[j]); - } - call_wallscan(x1, x2, up, down, swal, lwal, yrepeat, mask); - up = down; - down = (down == most1) ? most2 : most1; - } - partition -= scaledtexheight; - dc_texturemid -= texheight; - } - call_wallscan(x1, x2, up, dwal, swal, lwal, yrepeat, mask); - } - else - { // upside down: draw strips from bottom to top - partition = bot - fmod(bot - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); - up = most1; - down = dwal; - dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; - while (partition < top) - { - int j = OWallMost(most3, partition - ViewPos.Z, &WallC); - if (j != 12) - { - for (int j = x1; j < x2; ++j) - { - up[j] = clamp(most3[j], uwal[j], down[j]); - } - call_wallscan(x1, x2, up, down, swal, lwal, yrepeat, mask); - down = up; - up = (up == most1) ? most2 : most1; - } - partition -= scaledtexheight; - dc_texturemid -= texheight; - } - call_wallscan(x1, x2, uwal, down, swal, lwal, yrepeat, mask); - } - } -} - -static void wallscan_np2_ds(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) -{ - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - double frontcz1 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v1); - double frontfz1 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v1); - double frontcz2 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v2); - double frontfz2 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v2); - double top = MAX(frontcz1, frontcz2); - double bot = MIN(frontfz1, frontfz2); - if (fake3D & FAKE3D_CLIPTOP) - { - top = MIN(top, sclipTop); - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - bot = MAX(bot, sclipBottom); - } - wallscan_np2(x1, x2, uwal, dwal, swal, lwal, yrepeat, top, bot, true); - } - else - { - call_wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, true); - } -} - -inline fixed_t mvline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) -{ - dc_iscale = vince; - dc_colormap = colormap; - dc_count = count; - dc_texturefrac = vplce; - dc_source = bufplce; - dc_dest = dest; - return domvline1 (); -} - -void maskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, - double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - int x, fracbits; - BYTE *p; - int y1ve[4], y2ve[4], u4, d4, startx, dax, z; - char bad; - float light = rw_light - rw_lightstep; - SDWORD xoffset; - BYTE *basecolormapdata; - double iscale; - - if (rw_pic->UseType == FTexture::TEX_Null) - { - return; - } - - if (!rw_pic->bMasked) - { // Textures that aren't masked can use the faster wallscan. - wallscan (x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - return; - } - -//extern cycle_t WallScanCycles; -//clock (WallScanCycles); - - rw_pic->GetHeight(); // Make sure texture size is loaded - fracbits = 32- rw_pic->HeightBits; - setupmvline(fracbits); - xoffset = rw_offset; - basecolormapdata = basecolormap->Maps; - - x = startx = x1; - p = x + dc_destorg; - - bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); - if (fixed) - { - palookupoffse[0] = dc_colormap; - palookupoffse[1] = dc_colormap; - palookupoffse[2] = dc_colormap; - palookupoffse[3] = dc_colormap; - } - - for(; (x < x2) && ((size_t)p & 3); ++x, ++p) - { - light += rw_lightstep; - y1ve[0] = uwal[x];//max(uwal[x],umost[x]); - y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); - if (y2ve[0] <= y1ve[0]) continue; - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + p; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); - - domvline1(); - } - - for(; x < x2-3; x += 4, p+= 4) - { - bad = 0; - for (z = 3, dax = x+3; z >= 0; --z, --dax) - { - y1ve[z] = uwal[dax]; - y2ve[z] = dwal[dax]; - if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); - iscale = swal[dax] * yrepeat; - vince[z] = xs_ToFixed(fracbits, iscale); - vplce[z] = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[z] - CenterY + 1)); - } - if (bad == 15) - { - light += rw_lightstep * 4; - continue; - } - - if (!fixed) - { - for (z = 0; z < 4; ++z) - { - light += rw_lightstep; - palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - } - - u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); - d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); - - if ((bad != 0) || (u4 >= d4)) - { - for (z = 0; z < 4; ++z) - { - if (!(bad & 1)) - { - mvline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); - } - bad >>= 1; - } - continue; - } - - for (z = 0; z < 4; ++z) - { - if (u4 > y1ve[z]) - { - vplce[z] = mvline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); - } - } - - if (d4 > u4) - { - dc_count = d4-u4; - dc_dest = ylookup[u4]+p; - domvline4(); - } - - BYTE *i = p+ylookup[d4]; - for (z = 0; z < 4; ++z) - { - if (y2ve[z] > d4) - { - mvline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); - } - } - } - for(; x < x2; ++x, ++p) - { - light += rw_lightstep; - y1ve[0] = uwal[x]; - y2ve[0] = dwal[x]; - if (y2ve[0] <= y1ve[0]) continue; - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + p; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); - - domvline1(); - } - -//unclock(WallScanCycles); - - NetUpdate (); -} - -inline void preptmvline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) -{ - dc_iscale = vince; - dc_colormap = colormap; - dc_count = count; - dc_texturefrac = vplce; - dc_source = bufplce; - dc_dest = dest; -} - -void transmaskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, - double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - fixed_t (*tmvline1)(); - void (*tmvline4)(); - int x, fracbits; - BYTE *p; - int y1ve[4], y2ve[4], u4, d4, startx, dax, z; - char bad; - float light = rw_light - rw_lightstep; - SDWORD xoffset; - BYTE *basecolormapdata; - double iscale; - - if (rw_pic->UseType == FTexture::TEX_Null) - { - return; - } - - if (!R_GetTransMaskDrawers (&tmvline1, &tmvline4)) - { - // The current translucency is unsupported, so draw with regular maskwallscan instead. - maskwallscan (x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - return; - } - -//extern cycle_t WallScanCycles; -//clock (WallScanCycles); - - rw_pic->GetHeight(); // Make sure texture size is loaded - fracbits = 32 - rw_pic->HeightBits; - setuptmvline(fracbits); - xoffset = rw_offset; - basecolormapdata = basecolormap->Maps; - fixed_t centeryfrac = FLOAT2FIXED(CenterY); - - x = startx = x1; - p = x + dc_destorg; - - bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); - if (fixed) - { - palookupoffse[0] = dc_colormap; - palookupoffse[1] = dc_colormap; - palookupoffse[2] = dc_colormap; - palookupoffse[3] = dc_colormap; - } - - for(; (x < x2) && ((size_t)p & 3); ++x, ++p) - { - light += rw_lightstep; - y1ve[0] = uwal[x];//max(uwal[x],umost[x]); - y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); - if (y2ve[0] <= y1ve[0]) continue; - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + p; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); - - tmvline1(); - } - - for(; x < x2-3; x += 4, p+= 4) - { - bad = 0; - for (z = 3, dax = x+3; z >= 0; --z, --dax) - { - y1ve[z] = uwal[dax]; - y2ve[z] = dwal[dax]; - if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); - iscale = swal[dax] * yrepeat; - vince[z] = xs_ToFixed(fracbits, iscale); - vplce[z] = xs_ToFixed(fracbits, dc_texturemid + vince[z] * (y1ve[z] - CenterY + 1)); - } - if (bad == 15) - { - light += rw_lightstep * 4; - continue; - } - - if (!fixed) - { - for (z = 0; z < 4; ++z) - { - light += rw_lightstep; - palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - } - - u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); - d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); - - if ((bad != 0) || (u4 >= d4)) - { - for (z = 0; z < 4; ++z) - { - if (!(bad & 1)) - { - preptmvline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); - tmvline1(); - } - bad >>= 1; - } - continue; - } - - for (z = 0; z < 4; ++z) - { - if (u4 > y1ve[z]) - { - preptmvline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); - vplce[z] = tmvline1(); - } - } - - if (d4 > u4) - { - dc_count = d4-u4; - dc_dest = ylookup[u4]+p; - tmvline4(); - } - - BYTE *i = p+ylookup[d4]; - for (z = 0; z < 4; ++z) - { - if (y2ve[z] > d4) - { - preptmvline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); - tmvline1(); - } - } - } - for(; x < x2; ++x, ++p) - { - light += rw_lightstep; - y1ve[0] = uwal[x]; - y2ve[0] = dwal[x]; - if (y2ve[0] <= y1ve[0]) continue; - - if (!fixed) - { // calculate lighting - dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); - } - - dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); - dc_dest = ylookup[y1ve[0]] + p; - dc_count = y2ve[0] - y1ve[0]; - iscale = swal[x] * yrepeat; - dc_iscale = xs_ToFixed(fracbits, iscale); - dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); - - tmvline1(); - } - -//unclock(WallScanCycles); - - NetUpdate (); -} - -// -// R_RenderSegLoop -// Draws zero, one, or two textures for walls. -// Can draw or mark the starting pixel of floor and ceiling textures. -// CALLED: CORE LOOPING ROUTINE. -// -// [RH] Rewrote this to use Build's wallscan, so it's quite far -// removed from the original Doom routine. -// - -void R_RenderSegLoop () -{ - int x1 = rw_x; - int x2 = rw_stopx; - int x; - double xscale; - double yscale; - fixed_t xoffset = rw_offset; - - if (fixedlightlev >= 0) - dc_colormap = basecolormap->Maps + fixedlightlev; - else if (fixedcolormap != NULL) - dc_colormap = fixedcolormap; - - // clip wall to the floor and ceiling - for (x = x1; x < x2; ++x) - { - if (walltop[x] < ceilingclip[x]) - { - walltop[x] = ceilingclip[x]; - } - if (wallbottom[x] > floorclip[x]) - { - wallbottom[x] = floorclip[x]; - } - } - - // mark ceiling areas - if (markceiling) - { - for (x = x1; x < x2; ++x) - { - short top = (fakeFloor && fake3D & 2) ? fakeFloor->ceilingclip[x] : ceilingclip[x]; - short bottom = MIN (walltop[x], floorclip[x]); - if (top < bottom) - { - ceilingplane->top[x] = top; - ceilingplane->bottom[x] = bottom; - } - } - } - - // mark floor areas - if (markfloor) - { - for (x = x1; x < x2; ++x) - { - short top = MAX (wallbottom[x], ceilingclip[x]); - short bottom = (fakeFloor && fake3D & 1) ? fakeFloor->floorclip[x] : floorclip[x]; - if (top < bottom) - { - assert (bottom <= viewheight); - floorplane->top[x] = top; - floorplane->bottom[x] = bottom; - } - } - } - - // kg3D - fake planes clipping - if (fake3D & FAKE3D_REFRESHCLIP) - { - if (fake3D & FAKE3D_CLIPBOTFRONT) - { - memcpy (fakeFloor->floorclip+x1, wallbottom+x1, (x2-x1)*sizeof(short)); - } - else - { - for (x = x1; x < x2; ++x) - { - walllower[x] = MIN (MAX (walllower[x], ceilingclip[x]), wallbottom[x]); - } - memcpy (fakeFloor->floorclip+x1, walllower+x1, (x2-x1)*sizeof(short)); - } - if (fake3D & FAKE3D_CLIPTOPFRONT) - { - memcpy (fakeFloor->ceilingclip+x1, walltop+x1, (x2-x1)*sizeof(short)); - } - else - { - for (x = x1; x < x2; ++x) - { - wallupper[x] = MAX (MIN (wallupper[x], floorclip[x]), walltop[x]); - } - memcpy (fakeFloor->ceilingclip+x1, wallupper+x1, (x2-x1)*sizeof(short)); - } - } - if(fake3D & 7) return; - - // draw the wall tiers - if (midtexture) - { // one sided line - if (midtexture->UseType != FTexture::TEX_Null && viewactive) - { - dc_texturemid = rw_midtexturemid; - rw_pic = midtexture; - xscale = rw_pic->Scale.X * rw_midtexturescalex; - yscale = rw_pic->Scale.Y * rw_midtexturescaley; - if (xscale != lwallscale) - { - PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); - lwallscale = xscale; - } - if (midtexture->bWorldPanning) - { - rw_offset = xs_RoundToInt(rw_offset_mid * xscale); - } - else - { - rw_offset = rw_offset_mid; - } - if (xscale < 0) - { - rw_offset = -rw_offset; - } - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - wallscan_np2(x1, x2, walltop, wallbottom, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_frontfz1, rw_frontfz2), false); - } - else - { - call_wallscan(x1, x2, walltop, wallbottom, swall, lwall, yscale, false); - } - } - clearbufshort (ceilingclip+x1, x2-x1, viewheight); - clearbufshort (floorclip+x1, x2-x1, 0xffff); - } - else - { // two sided line - if (toptexture != NULL && toptexture->UseType != FTexture::TEX_Null) - { // top wall - for (x = x1; x < x2; ++x) - { - wallupper[x] = MAX (MIN (wallupper[x], floorclip[x]), walltop[x]); - } - if (viewactive) - { - dc_texturemid = rw_toptexturemid; - rw_pic = toptexture; - xscale = rw_pic->Scale.X * rw_toptexturescalex; - yscale = rw_pic->Scale.Y * rw_toptexturescaley; - if (xscale != lwallscale) - { - PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); - lwallscale = xscale; - } - if (toptexture->bWorldPanning) - { - rw_offset = xs_RoundToInt(rw_offset_top * xscale); - } - else - { - rw_offset = rw_offset_top; - } - if (xscale < 0) - { - rw_offset = -rw_offset; - } - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - wallscan_np2(x1, x2, walltop, wallupper, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_backcz1, rw_backcz2), false); - } - else - { - call_wallscan(x1, x2, walltop, wallupper, swall, lwall, yscale, false); - } - } - memcpy (ceilingclip+x1, wallupper+x1, (x2-x1)*sizeof(short)); - } - else if (markceiling) - { // no top wall - memcpy (ceilingclip+x1, walltop+x1, (x2-x1)*sizeof(short)); - } - - - if (bottomtexture != NULL && bottomtexture->UseType != FTexture::TEX_Null) - { // bottom wall - for (x = x1; x < x2; ++x) - { - walllower[x] = MIN (MAX (walllower[x], ceilingclip[x]), wallbottom[x]); - } - if (viewactive) - { - dc_texturemid = rw_bottomtexturemid; - rw_pic = bottomtexture; - xscale = rw_pic->Scale.X * rw_bottomtexturescalex; - yscale = rw_pic->Scale.Y * rw_bottomtexturescaley; - if (xscale != lwallscale) - { - PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); - lwallscale = xscale; - } - if (bottomtexture->bWorldPanning) - { - rw_offset = xs_RoundToInt(rw_offset_bottom * xscale); - } - else - { - rw_offset = rw_offset_bottom; - } - if (xscale < 0) - { - rw_offset = -rw_offset; - } - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - wallscan_np2(x1, x2, walllower, wallbottom, swall, lwall, yscale, MAX(rw_backfz1, rw_backfz2), MIN(rw_frontfz1, rw_frontfz2), false); - } - else - { - call_wallscan(x1, x2, walllower, wallbottom, swall, lwall, yscale, false); - } - } - memcpy (floorclip+x1, walllower+x1, (x2-x1)*sizeof(short)); - } - else if (markfloor) - { // no bottom wall - memcpy (floorclip+x1, wallbottom+x1, (x2-x1)*sizeof(short)); - } - } - rw_offset = xoffset; -} - -void R_NewWall (bool needlights) -{ - double rowoffset; - double yrepeat; - - rw_markportal = false; - - sidedef = curline->sidedef; - linedef = curline->linedef; - - // mark the segment as visible for auto map - if (!r_dontmaplines) linedef->flags |= ML_MAPPED; - - midtexture = toptexture = bottomtexture = 0; - - if (sidedef == linedef->sidedef[0] && - (linedef->isVisualPortal() || (linedef->special == Line_Mirror && r_drawmirrors))) // [ZZ] compatibility with r_drawmirrors cvar that existed way before portals - { - markfloor = markceiling = true; // act like an one-sided wall here (todo: check how does this work with transparency) - rw_markportal = true; - } - else if (backsector == NULL) - { - // single sided line - // a single sided line is terminal, so it must mark ends - markfloor = markceiling = true; - // [RH] Horizon lines do not need to be textured - if (linedef->special != Line_Horizon) - { - midtexture = TexMan(sidedef->GetTexture(side_t::mid), true); - rw_offset_mid = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); - rowoffset = sidedef->GetTextureYOffset(side_t::mid); - rw_midtexturescalex = sidedef->GetTextureXScale(side_t::mid); - rw_midtexturescaley = sidedef->GetTextureYScale(side_t::mid); - yrepeat = midtexture->Scale.Y * rw_midtexturescaley; - if (yrepeat >= 0) - { // normal orientation - if (linedef->flags & ML_DONTPEGBOTTOM) - { // bottom of texture at bottom - rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + midtexture->GetHeight(); - } - else - { // top of texture at top - rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; - if (rowoffset < 0 && midtexture != NULL) - { - rowoffset += midtexture->GetHeight(); - } - } - } - else - { // upside down - rowoffset = -rowoffset; - if (linedef->flags & ML_DONTPEGBOTTOM) - { // top of texture at bottom - rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat; - } - else - { // bottom of texture at top - rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + midtexture->GetHeight(); - } - } - if (midtexture->bWorldPanning) - { - rw_midtexturemid += rowoffset * yrepeat; - } - else - { - // rowoffset is added outside the multiply so that it positions the texture - // by texels instead of world units. - rw_midtexturemid += rowoffset; - } - } - } - else - { // two-sided line - // hack to allow height changes in outdoor areas - - rw_frontlowertop = frontsector->GetPlaneTexZ(sector_t::ceiling); - - if (frontsector->GetTexture(sector_t::ceiling) == skyflatnum && - backsector->GetTexture(sector_t::ceiling) == skyflatnum) - { - if (rw_havehigh) - { // front ceiling is above back ceiling - memcpy (&walltop[WallC.sx1], &wallupper[WallC.sx1], (WallC.sx2 - WallC.sx1)*sizeof(walltop[0])); - rw_havehigh = false; - } - else if (rw_havelow && frontsector->ceilingplane != backsector->ceilingplane) - { // back ceiling is above front ceiling - // The check for rw_havelow is not Doom-compliant, but it avoids HoM that - // would otherwise occur because there is space made available for this - // wall but nothing to draw for it. - // Recalculate walltop so that the wall is clipped by the back sector's - // ceiling instead of the front sector's ceiling. - WallMost (walltop, backsector->ceilingplane, &WallC); - } - // Putting sky ceilings on the front and back of a line alters the way unpegged - // positioning works. - rw_frontlowertop = backsector->GetPlaneTexZ(sector_t::ceiling); - } - - if ((rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2) || - (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2)) - { - // closed door - markceiling = markfloor = true; - } - else - { - markfloor = rw_mustmarkfloor - || backsector->floorplane != frontsector->floorplane - || backsector->lightlevel != frontsector->lightlevel - || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) - || backsector->GetPlaneLight(sector_t::floor) != frontsector->GetPlaneLight(sector_t::floor) - - // killough 3/7/98: Add checks for (x,y) offsets - || backsector->planes[sector_t::floor].xform != frontsector->planes[sector_t::floor].xform - || backsector->GetAlpha(sector_t::floor) != frontsector->GetAlpha(sector_t::floor) - - // killough 4/15/98: prevent 2s normals - // from bleeding through deep water - || frontsector->heightsec - - || backsector->GetVisFlags(sector_t::floor) != frontsector->GetVisFlags(sector_t::floor) - - // [RH] Add checks for colormaps - || backsector->ColorMap != frontsector->ColorMap - - - // kg3D - add fake lights - || (frontsector->e && frontsector->e->XFloor.lightlist.Size()) - || (backsector->e && backsector->e->XFloor.lightlist.Size()) - - || (sidedef->GetTexture(side_t::mid).isValid() && - ((linedef->flags & (ML_CLIP_MIDTEX|ML_WRAP_MIDTEX)) || - (sidedef->Flags & (WALLF_CLIP_MIDTEX|WALLF_WRAP_MIDTEX)))) - ; - - markceiling = (frontsector->GetTexture(sector_t::ceiling) != skyflatnum || - backsector->GetTexture(sector_t::ceiling) != skyflatnum) && - (rw_mustmarkceiling - || backsector->ceilingplane != frontsector->ceilingplane - || backsector->lightlevel != frontsector->lightlevel - || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) - - // killough 3/7/98: Add checks for (x,y) offsets - || backsector->planes[sector_t::ceiling].xform != frontsector->planes[sector_t::ceiling].xform - || backsector->GetAlpha(sector_t::ceiling) != frontsector->GetAlpha(sector_t::ceiling) - - // killough 4/15/98: prevent 2s normals - // from bleeding through fake ceilings - || (frontsector->heightsec && frontsector->GetTexture(sector_t::ceiling) != skyflatnum) - - || backsector->GetPlaneLight(sector_t::ceiling) != frontsector->GetPlaneLight(sector_t::ceiling) - || backsector->GetFlags(sector_t::ceiling) != frontsector->GetFlags(sector_t::ceiling) - - // [RH] Add check for colormaps - || backsector->ColorMap != frontsector->ColorMap - - // kg3D - add fake lights - || (frontsector->e && frontsector->e->XFloor.lightlist.Size()) - || (backsector->e && backsector->e->XFloor.lightlist.Size()) - - || (sidedef->GetTexture(side_t::mid).isValid() && - ((linedef->flags & (ML_CLIP_MIDTEX|ML_WRAP_MIDTEX)) || - (sidedef->Flags & (WALLF_CLIP_MIDTEX|WALLF_WRAP_MIDTEX)))) - ); - } - - if (rw_havehigh) - { // top texture - toptexture = TexMan(sidedef->GetTexture(side_t::top), true); - - rw_offset_top = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::top)); - rowoffset = sidedef->GetTextureYOffset(side_t::top); - rw_toptexturescalex =sidedef->GetTextureXScale(side_t::top); - rw_toptexturescaley =sidedef->GetTextureYScale(side_t::top); - yrepeat = toptexture->Scale.Y * rw_toptexturescaley; - if (yrepeat >= 0) - { // normal orientation - if (linedef->flags & ML_DONTPEGTOP) - { // top of texture at top - rw_toptexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; - if (rowoffset < 0 && toptexture != NULL) - { - rowoffset += toptexture->GetHeight(); - } - } - else - { // bottom of texture at bottom - rw_toptexturemid = (backsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + toptexture->GetHeight(); - } - } - else - { // upside down - rowoffset = -rowoffset; - if (linedef->flags & ML_DONTPEGTOP) - { // bottom of texture at top - rw_toptexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + toptexture->GetHeight(); - } - else - { // top of texture at bottom - rw_toptexturemid = (backsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; - } - } - if (toptexture->bWorldPanning) - { - rw_toptexturemid += rowoffset * yrepeat; - } - else - { - rw_toptexturemid += rowoffset; - } - } - if (rw_havelow) - { // bottom texture - bottomtexture = TexMan(sidedef->GetTexture(side_t::bottom), true); - - rw_offset_bottom = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::bottom)); - rowoffset = sidedef->GetTextureYOffset(side_t::bottom); - rw_bottomtexturescalex = sidedef->GetTextureXScale(side_t::bottom); - rw_bottomtexturescaley = sidedef->GetTextureYScale(side_t::bottom); - yrepeat = bottomtexture->Scale.Y * rw_bottomtexturescaley; - if (yrepeat >= 0) - { // normal orientation - if (linedef->flags & ML_DONTPEGBOTTOM) - { // bottom of texture at bottom - rw_bottomtexturemid = (rw_frontlowertop - ViewPos.Z) * yrepeat; - } - else - { // top of texture at top - rw_bottomtexturemid = (backsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat; - if (rowoffset < 0 && bottomtexture != NULL) - { - rowoffset += bottomtexture->GetHeight(); - } - } - } - else - { // upside down - rowoffset = -rowoffset; - if (linedef->flags & ML_DONTPEGBOTTOM) - { // top of texture at bottom - rw_bottomtexturemid = (rw_frontlowertop - ViewPos.Z) * yrepeat; - } - else - { // bottom of texture at top - rw_bottomtexturemid = (backsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + bottomtexture->GetHeight(); - } - } - if (bottomtexture->bWorldPanning) - { - rw_bottomtexturemid += rowoffset * yrepeat; - } - else - { - rw_bottomtexturemid += rowoffset; - } - } - } - - // if a floor / ceiling plane is on the wrong side of the view plane, - // it is definitely invisible and doesn't need to be marked. - - // killough 3/7/98: add deep water check - if (frontsector->GetHeightSec() == NULL) - { - int planeside; - - planeside = frontsector->floorplane.PointOnSide(ViewPos); - if (frontsector->floorplane.fC() < 0) // 3D floors have the floor backwards - planeside = -planeside; - if (planeside <= 0) // above view plane - markfloor = false; - - if (frontsector->GetTexture(sector_t::ceiling) != skyflatnum) - { - planeside = frontsector->ceilingplane.PointOnSide(ViewPos); - if (frontsector->ceilingplane.fC() > 0) // 3D floors have the ceiling backwards - planeside = -planeside; - if (planeside <= 0) // below view plane - markceiling = false; - } - } - - FTexture *midtex = TexMan(sidedef->GetTexture(side_t::mid), true); - - segtextured = midtex != NULL || toptexture != NULL || bottomtexture != NULL; - - // calculate light table - if (needlights && (segtextured || (backsector && IsFogBoundary(frontsector, backsector)))) - { - lwallscale = - midtex ? (midtex->Scale.X * sidedef->GetTextureXScale(side_t::mid)) : - toptexture ? (toptexture->Scale.X * sidedef->GetTextureXScale(side_t::top)) : - bottomtexture ? (bottomtexture->Scale.X * sidedef->GetTextureXScale(side_t::bottom)) : - 1.; - - PrepWall (swall, lwall, sidedef->TexelLength * lwallscale, WallC.sx1, WallC.sx2); - - if (fixedcolormap == NULL && fixedlightlev < 0) - { - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, frontsector->lightlevel) - + r_actualextralight); - GlobVis = r_WallVisibility; - rw_lightleft = float (GlobVis / WallC.sz1); - rw_lightstep = float((GlobVis / WallC.sz2 - rw_lightleft) / (WallC.sx2 - WallC.sx1)); - } - else - { - rw_lightleft = 1; - rw_lightstep = 0; - } - } -} - - -// -// R_CheckDrawSegs -// - -void R_CheckDrawSegs () -{ - if (ds_p == &drawsegs[MaxDrawSegs]) - { // [RH] Grab some more drawsegs - size_t newdrawsegs = MaxDrawSegs ? MaxDrawSegs*2 : 32; - ptrdiff_t firstofs = firstdrawseg - drawsegs; - drawsegs = (drawseg_t *)M_Realloc (drawsegs, newdrawsegs * sizeof(drawseg_t)); - firstdrawseg = drawsegs + firstofs; - ds_p = drawsegs + MaxDrawSegs; - MaxDrawSegs = newdrawsegs; - DPrintf ("MaxDrawSegs increased to %zu\n", MaxDrawSegs); - } -} - -// -// R_CheckOpenings -// - -ptrdiff_t R_NewOpening (ptrdiff_t len) -{ - ptrdiff_t res = lastopening; - lastopening += len; - if ((size_t)lastopening > maxopenings) - { - do - maxopenings = maxopenings ? maxopenings*2 : 16384; - while ((size_t)lastopening > maxopenings); - openings = (short *)M_Realloc (openings, maxopenings * sizeof(*openings)); - DPrintf ("MaxOpenings increased to %zu\n", maxopenings); - } - return res; -} - - -// -// R_StoreWallRange -// A wall segment will be drawn between start and stop pixels (inclusive). -// - -void R_StoreWallRange (int start, int stop) -{ - int i; - bool maskedtexture = false; - -#ifdef RANGECHECK - if (start >= viewwidth || start >= stop) - I_FatalError ("Bad R_StoreWallRange: %i to %i", start , stop); -#endif - - // don't overflow and crash - R_CheckDrawSegs (); - - if (!rw_prepped) - { - rw_prepped = true; - R_NewWall (true); - } - - rw_offset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); - rw_light = rw_lightleft + rw_lightstep * (start - WallC.sx1); - - ds_p->CurrentPortalUniq = CurrentPortalUniq; - ds_p->sx1 = WallC.sx1; - ds_p->sx2 = WallC.sx2; - ds_p->sz1 = WallC.sz1; - ds_p->sz2 = WallC.sz2; - ds_p->cx = WallC.tleft.X;; - ds_p->cy = WallC.tleft.Y; - ds_p->cdx = WallC.tright.X - WallC.tleft.X; - ds_p->cdy = WallC.tright.Y - WallC.tleft.Y; - ds_p->tmapvals = WallT; - ds_p->siz1 = 1 / WallC.sz1; - ds_p->siz2 = 1 / WallC.sz2; - ds_p->x1 = rw_x = start; - ds_p->x2 = stop; - ds_p->curline = curline; - rw_stopx = stop; - ds_p->bFogBoundary = false; - ds_p->bFakeBoundary = false; - if(fake3D & 7) ds_p->fake = 1; - else ds_p->fake = 0; - - // killough 1/6/98, 2/1/98: remove limit on openings - ds_p->sprtopclip = ds_p->sprbottomclip = ds_p->maskedtexturecol = ds_p->bkup = ds_p->swall = -1; - - if (rw_markportal) - { - ds_p->silhouette = SIL_BOTH; - } - else if (backsector == NULL) - { - ds_p->sprtopclip = R_NewOpening (stop - start); - ds_p->sprbottomclip = R_NewOpening (stop - start); - clearbufshort (openings + ds_p->sprtopclip, stop-start, viewheight); - memset (openings + ds_p->sprbottomclip, -1, (stop-start)*sizeof(short)); - ds_p->silhouette = SIL_BOTH; - } - else - { - // two sided line - ds_p->silhouette = 0; - - if (rw_frontfz1 > rw_backfz1 || rw_frontfz2 > rw_backfz2 || - backsector->floorplane.PointOnSide(ViewPos) < 0) - { - ds_p->silhouette = SIL_BOTTOM; - } - - if (rw_frontcz1 < rw_backcz1 || rw_frontcz2 < rw_backcz2 || - backsector->ceilingplane.PointOnSide(ViewPos) < 0) - { - ds_p->silhouette |= SIL_TOP; - } - - // killough 1/17/98: this test is required if the fix - // for the automap bug (r_bsp.c) is used, or else some - // sprites will be displayed behind closed doors. That - // fix prevents lines behind closed doors with dropoffs - // from being displayed on the automap. - // - // killough 4/7/98: make doorclosed external variable - - { - extern int doorclosed; // killough 1/17/98, 2/8/98, 4/7/98 - if (doorclosed || (rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2)) - { - ds_p->sprbottomclip = R_NewOpening (stop - start); - memset (openings + ds_p->sprbottomclip, -1, (stop-start)*sizeof(short)); - ds_p->silhouette |= SIL_BOTTOM; - } - if (doorclosed || (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2)) - { // killough 1/17/98, 2/8/98 - ds_p->sprtopclip = R_NewOpening (stop - start); - clearbufshort (openings + ds_p->sprtopclip, stop - start, viewheight); - ds_p->silhouette |= SIL_TOP; - } - } - - if(!ds_p->fake && r_3dfloors && backsector->e && backsector->e->XFloor.ffloors.Size()) { - for(i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) { - F3DFloor *rover = backsector->e->XFloor.ffloors[i]; - if(rover->flags & FF_RENDERSIDES && (!(rover->flags & FF_INVERTSIDES) || rover->flags & FF_ALLSIDES)) { - ds_p->bFakeBoundary |= 1; - break; - } - } - } - if(!ds_p->fake && r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) { - for(i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) { - F3DFloor *rover = frontsector->e->XFloor.ffloors[i]; - if(rover->flags & FF_RENDERSIDES && (rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) { - ds_p->bFakeBoundary |= 2; - break; - } - } - } - // kg3D - no for fakes - if(!ds_p->fake) - // allocate space for masked texture tables, if needed - // [RH] Don't just allocate the space; fill it in too. - if ((TexMan(sidedef->GetTexture(side_t::mid), true)->UseType != FTexture::TEX_Null || ds_p->bFakeBoundary || IsFogBoundary (frontsector, backsector)) && - (rw_ceilstat != 12 || !sidedef->GetTexture(side_t::top).isValid()) && - (rw_floorstat != 3 || !sidedef->GetTexture(side_t::bottom).isValid()) && - (WallC.sz1 >= TOO_CLOSE_Z && WallC.sz2 >= TOO_CLOSE_Z)) - { - float *swal; - fixed_t *lwal; - int i; - - maskedtexture = true; - - // kg3D - backup for mid and fake walls - ds_p->bkup = R_NewOpening(stop - start); - memcpy(openings + ds_p->bkup, &ceilingclip[start], sizeof(short)*(stop - start)); - - ds_p->bFogBoundary = IsFogBoundary (frontsector, backsector); - if (sidedef->GetTexture(side_t::mid).isValid() || ds_p->bFakeBoundary) - { - if(sidedef->GetTexture(side_t::mid).isValid()) - ds_p->bFakeBoundary |= 4; // it is also mid texture - - ds_p->maskedtexturecol = R_NewOpening ((stop - start) * 2); - ds_p->swall = R_NewOpening ((stop - start) * 2); - - lwal = (fixed_t *)(openings + ds_p->maskedtexturecol); - swal = (float *)(openings + ds_p->swall); - FTexture *pic = TexMan(sidedef->GetTexture(side_t::mid), true); - double yscale = pic->Scale.X * sidedef->GetTextureYScale(side_t::mid); - fixed_t xoffset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); - - if (pic->bWorldPanning) - { - xoffset = xs_RoundToInt(xoffset * lwallscale); - } - - for (i = start; i < stop; i++) - { - *lwal++ = lwall[i] + xoffset; - *swal++ = swall[i]; - } - - double istart = *((float *)(openings + ds_p->swall)) * yscale; - double iend = *(swal - 1) * yscale; -#if 0 - ///This was for avoiding overflow when using fixed point. It might not be needed anymore. - const double mini = 3 / 65536.0; - if (istart < mini && istart >= 0) istart = mini; - if (istart > -mini && istart < 0) istart = -mini; - if (iend < mini && iend >= 0) iend = mini; - if (iend > -mini && iend < 0) iend = -mini; -#endif - istart = 1 / istart; - iend = 1 / iend; - ds_p->yscale = (float)yscale; - ds_p->iscale = (float)istart; - if (stop - start > 0) - { - ds_p->iscalestep = float((iend - istart) / (stop - start)); - } - else - { - ds_p->iscalestep = 0; - } - } - ds_p->light = rw_light; - ds_p->lightstep = rw_lightstep; - - // Masked midtextures should get the light level from the sector they reference, - // not from the current subsector, which is what the current wallshade value - // comes from. We make an exeption for polyobjects, however, since their "home" - // sector should be whichever one they move into. - if (curline->sidedef->Flags & WALLF_POLYOBJ) - { - ds_p->shade = wallshade; - } - else - { - ds_p->shade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, curline->frontsector->lightlevel) - + r_actualextralight); - } - - if (ds_p->bFogBoundary || ds_p->maskedtexturecol != -1) - { - size_t drawsegnum = ds_p - drawsegs; - InterestingDrawsegs.Push (drawsegnum); - } - } - } - - // render it - if (markceiling) - { - if (ceilingplane) - { // killough 4/11/98: add NULL ptr checks - ceilingplane = R_CheckPlane (ceilingplane, start, stop); - } - else - { - markceiling = false; - } - } - - if (markfloor) - { - if (floorplane) - { // killough 4/11/98: add NULL ptr checks - floorplane = R_CheckPlane (floorplane, start, stop); - } - else - { - markfloor = false; - } - } - - R_RenderSegLoop (); - - if(fake3D & 7) { - ds_p++; - return; - } - - // save sprite clipping info - if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture) && ds_p->sprtopclip == -1) - { - ds_p->sprtopclip = R_NewOpening (stop - start); - memcpy (openings + ds_p->sprtopclip, &ceilingclip[start], sizeof(short)*(stop-start)); - } - - if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && ds_p->sprbottomclip == -1) - { - ds_p->sprbottomclip = R_NewOpening (stop - start); - memcpy (openings + ds_p->sprbottomclip, &floorclip[start], sizeof(short)*(stop-start)); - } - - if (maskedtexture && curline->sidedef->GetTexture(side_t::mid).isValid()) - { - ds_p->silhouette |= SIL_TOP | SIL_BOTTOM; - } - - // [RH] Draw any decals bound to the seg - // [ZZ] Only if not an active mirror - if (!rw_markportal) - { - for (DBaseDecal *decal = curline->sidedef->AttachedDecals; decal != NULL; decal = decal->WallNext) - { - R_RenderDecal (curline->sidedef, decal, ds_p, 0); - } - } - - if (rw_markportal) - { - PortalDrawseg pds; - pds.src = curline->linedef; - pds.dst = curline->linedef->special == Line_Mirror? curline->linedef : curline->linedef->getPortalDestination(); - pds.x1 = ds_p->x1; - pds.x2 = ds_p->x2; - pds.len = pds.x2 - pds.x1; - pds.ceilingclip.Resize(pds.len); - memcpy(&pds.ceilingclip[0], openings + ds_p->sprtopclip, pds.len*sizeof(*openings)); - pds.floorclip.Resize(pds.len); - memcpy(&pds.floorclip[0], openings + ds_p->sprbottomclip, pds.len*sizeof(*openings)); - - for (int i = 0; i < pds.x2-pds.x1; i++) - { - if (pds.ceilingclip[i] < 0) - pds.ceilingclip[i] = 0; - if (pds.ceilingclip[i] >= viewheight) - pds.ceilingclip[i] = viewheight-1; - if (pds.floorclip[i] < 0) - pds.floorclip[i] = 0; - if (pds.floorclip[i] >= viewheight) - pds.floorclip[i] = viewheight-1; - } - - pds.mirror = curline->linedef->special == Line_Mirror; - WallPortals.Push(pds); - } - - ds_p++; -} - -int OWallMost (short *mostbuf, double z, const FWallCoords *wallc) -{ - int bad, ix1, ix2; - double y, iy1, iy2; - double s1, s2, s3, s4; - - z = -z; - s1 = globaluclip * wallc->sz1; s2 = globaluclip * wallc->sz2; - s3 = globaldclip * wallc->sz1; s4 = globaldclip * wallc->sz2; - bad = (zs3)<<2)+((z>s4)<<3); - -#if 1 - if ((bad&3) == 3) - { - memset (&mostbuf[wallc->sx1], 0, (wallc->sx2 - wallc->sx1)*sizeof(mostbuf[0])); - return bad; - } - - if ((bad&12) == 12) - { - clearbufshort (&mostbuf[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); - return bad; - } -#endif - ix1 = wallc->sx1; iy1 = wallc->sz1; - ix2 = wallc->sx2; iy2 = wallc->sz2; -#if 1 - if (bad & 3) - { - double t = (z-s1) / (s2-s1); - double inty = wallc->sz1 + t * (wallc->sz2 - wallc->sz1); - int xcross = xs_RoundToInt(wallc->sx1 + (t * wallc->sz2 * (wallc->sx2 - wallc->sx1)) / inty); - - if ((bad & 3) == 2) - { - if (wallc->sx1 <= xcross) { iy2 = inty; ix2 = xcross; } - if (wallc->sx2 > xcross) memset (&mostbuf[xcross], 0, (wallc->sx2-xcross)*sizeof(mostbuf[0])); - } - else - { - if (xcross <= wallc->sx2) { iy1 = inty; ix1 = xcross; } - if (xcross > wallc->sx1) memset (&mostbuf[wallc->sx1], 0, (xcross-wallc->sx1)*sizeof(mostbuf[0])); - } - } - - if (bad & 12) - { - double t = (z-s3) / (s4-s3); - double inty = wallc->sz1 + t * (wallc->sz2 - wallc->sz1); - int xcross = xs_RoundToInt(wallc->sx1 + (t * wallc->sz2 * (wallc->sx2 - wallc->sx1)) / inty); - - if ((bad & 12) == 8) - { - if (wallc->sx1 <= xcross) { iy2 = inty; ix2 = xcross; } - if (wallc->sx2 > xcross) clearbufshort (&mostbuf[xcross], wallc->sx2 - xcross, viewheight); - } - else - { - if (xcross <= wallc->sx2) { iy1 = inty; ix1 = xcross; } - if (xcross > wallc->sx1) clearbufshort (&mostbuf[wallc->sx1], xcross - wallc->sx1, viewheight); - } - } - - y = z * InvZtoScale / iy1; - if (ix2 == ix1) - { - mostbuf[ix1] = (short)xs_RoundToInt(y + CenterY); - } - else - { - fixed_t yinc = FLOAT2FIXED(((z * InvZtoScale / iy2) - y) / (ix2 - ix1)); - qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, FLOAT2FIXED(y + CenterY), yinc); - } -#else - double max = viewheight; - double zz = z / 65536.0; -#if 0 - double z1 = zz * InvZtoScale / wallc->sz1; - double z2 = zz * InvZtoScale / wallc->sz2 - z1; - z2 /= (wallc->sx2 - wallc->sx1); - z1 += centeryfrac / 65536.0; - - for (int x = wallc->sx1; x < wallc->sx2; ++x) - { - mostbuf[x] = xs_RoundToInt(clamp(z1, 0.0, max)); - z1 += z2; - } -#else - double top, bot, i; - - i = wallc->sx1 - centerx; - top = WallT.UoverZorg + WallT.UoverZstep * i; - bot = WallT.InvZorg + WallT.InvZstep * i; - double cy = centeryfrac / 65536.0; - - for (int x = wallc->sx1; x < wallc->sx2; x++) - { - double frac = top / bot; - double scale = frac * WallT.DepthScale + WallT.DepthOrg; - mostbuf[x] = xs_RoundToInt(clamp(zz / scale + cy, 0.0, max)); - top += WallT.UoverZstep; - bot += WallT.InvZstep; - } -#endif -#endif - return bad; -} - -int WallMost (short *mostbuf, const secplane_t &plane, const FWallCoords *wallc) -{ - if (!plane.isSlope()) - { - return OWallMost(mostbuf, plane.Zat0() - ViewPos.Z, wallc); - } - - double x, y, den, z1, z2, oz1, oz2; - double s1, s2, s3, s4; - int bad, ix1, ix2; - double iy1, iy2; - - if (MirrorFlags & RF_XFLIP) - { - x = curline->v2->fX(); - y = curline->v2->fY(); - if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) - { - double frac = (wallc->tleft.Y + wallc->tleft.X) / den; - x -= frac * (x - curline->v1->fX()); - y -= frac * (y - curline->v1->fY()); - } - z1 = ViewPos.Z - plane.ZatPoint(x, y); - - if (wallc->sx2 > wallc->sx1 + 1) - { - x = curline->v1->fX(); - y = curline->v1->fY(); - if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) - { - double frac = (wallc->tright.Y - wallc->tright.X) / den; - x += frac * (curline->v2->fX() - x); - y += frac * (curline->v2->fY() - y); - } - z2 = ViewPos.Z - plane.ZatPoint(x, y); - } - else - { - z2 = z1; - } - } - else - { - x = curline->v1->fX(); - y = curline->v1->fY(); - if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) - { - double frac = (wallc->tleft.Y + wallc->tleft.X) / den; - x += frac * (curline->v2->fX() - x); - y += frac * (curline->v2->fY() - y); - } - z1 = ViewPos.Z - plane.ZatPoint(x, y); - - if (wallc->sx2 > wallc->sx1 + 1) - { - x = curline->v2->fX(); - y = curline->v2->fY(); - if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) - { - double frac = (wallc->tright.Y - wallc->tright.X) / den; - x -= frac * (x - curline->v1->fX()); - y -= frac * (y - curline->v1->fY()); - } - z2 = ViewPos.Z - plane.ZatPoint(x, y); - } - else - { - z2 = z1; - } - } - - s1 = globaluclip * wallc->sz1; s2 = globaluclip * wallc->sz2; - s3 = globaldclip * wallc->sz1; s4 = globaldclip * wallc->sz2; - bad = (z1s3)<<2)+((z2>s4)<<3); - - ix1 = wallc->sx1; ix2 = wallc->sx2; - iy1 = wallc->sz1; iy2 = wallc->sz2; - oz1 = z1; oz2 = z2; - - if ((bad&3) == 3) - { - memset (&mostbuf[ix1], -1, (ix2-ix1)*sizeof(mostbuf[0])); - return bad; - } - - if ((bad&12) == 12) - { - clearbufshort (&mostbuf[ix1], ix2-ix1, viewheight); - return bad; - - } - - if (bad&3) - { - //inty = intz / (globaluclip>>16) - double t = (oz1-s1) / (s2-s1+oz1-oz2); - double inty = wallc->sz1 + t * (wallc->sz2-wallc->sz1); - double intz = oz1 + t * (oz2-oz1); - int xcross = wallc->sx1 + xs_RoundToInt((t * wallc->sz2 * (wallc->sx2-wallc->sx1)) / inty); - - //t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4)); - //inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t); - //intz = z1 + mulscale30(z2-z1,t); - - if ((bad&3) == 2) - { - if (wallc->sx1 <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; } - memset (&mostbuf[xcross], 0, (wallc->sx2-xcross)*sizeof(mostbuf[0])); - } - else - { - if (xcross <= wallc->sx2) { z1 = intz; iy1 = inty; ix1 = xcross; } - memset (&mostbuf[wallc->sx1], 0, (xcross-wallc->sx1)*sizeof(mostbuf[0])); - } - } - - if (bad&12) - { - //inty = intz / (globaldclip>>16) - double t = (oz1-s3) / (s4-s3+oz1-oz2); - double inty = wallc->sz1 + t * (wallc->sz2-wallc->sz1); - double intz = oz1 + t * (oz2-oz1); - int xcross = wallc->sx1 + xs_RoundToInt((t * wallc->sz2 * (wallc->sx2-wallc->sx1)) / inty); - - //t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4)); - //inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t); - //intz = z1 + mulscale30(z2-z1,t); - - if ((bad&12) == 8) - { - if (wallc->sx1 <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; } - if (wallc->sx2 > xcross) clearbufshort (&mostbuf[xcross], wallc->sx2-xcross, viewheight); - } - else - { - if (xcross <= wallc->sx2) { z1 = intz; iy1 = inty; ix1 = xcross; } - if (xcross > wallc->sx1) clearbufshort (&mostbuf[wallc->sx1], xcross-wallc->sx1, viewheight); - } - } - - y = z1 * InvZtoScale / iy1; - if (ix2 == ix1) - { - mostbuf[ix1] = (short)xs_RoundToInt(y/65536.0 + CenterY); - } - else - { - fixed_t yinc = FLOAT2FIXED(((z2 * InvZtoScale / iy2) - y) / (ix2-ix1)); - qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, FLOAT2FIXED(y/65536.0 + CenterY), yinc); - } - - return bad; -} - -static void PrepWallRoundFix(fixed_t *lwall, fixed_t walxrepeat, int x1, int x2) -{ - // fix for rounding errors - walxrepeat = abs(walxrepeat); - fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat-1 : 0; - int x; - - if (x1 > 0) - { - for (x = x1; x < x2; x++) - { - if ((unsigned)lwall[x] >= (unsigned)walxrepeat) - { - lwall[x] = fix; - } - else - { - break; - } - } - } - fix = walxrepeat - 1 - fix; - for (x = x2-1; x >= x1; x--) - { - if ((unsigned)lwall[x] >= (unsigned)walxrepeat) - { - lwall[x] = fix; - } - else - { - break; - } - } -} - -void PrepWall (float *swall, fixed_t *lwall, double walxrepeat, int x1, int x2) -{ // swall = scale, lwall = texturecolumn - double top, bot, i; - double xrepeat = fabs(walxrepeat * 65536); - double depth_scale = WallT.InvZstep * WallTMapScale2; - double depth_org = -WallT.UoverZstep * WallTMapScale2; - - i = x1 - centerx; - top = WallT.UoverZorg + WallT.UoverZstep * i; - bot = WallT.InvZorg + WallT.InvZstep * i; - - for (int x = x1; x < x2; x++) - { - double frac = top / bot; - if (walxrepeat < 0) - { - lwall[x] = xs_RoundToInt(xrepeat - frac * xrepeat); - } - else - { - lwall[x] = xs_RoundToInt(frac * xrepeat); - } - swall[x] = float(frac * depth_scale + depth_org); - top += WallT.UoverZstep; - bot += WallT.InvZstep; - } - PrepWallRoundFix(lwall, FLOAT2FIXED(walxrepeat), x1, x2); -} - -void PrepLWall (fixed_t *lwall, double walxrepeat, int x1, int x2) -{ // lwall = texturecolumn - double top, bot, i; - double xrepeat = fabs(walxrepeat * 65536); - double topstep, botstep; - - i = x1 - centerx; - top = WallT.UoverZorg + WallT.UoverZstep * i; - bot = WallT.InvZorg + WallT.InvZstep * i; - - top *= xrepeat; - topstep = WallT.UoverZstep * xrepeat; - botstep = WallT.InvZstep; - - for (int x = x1; x < x2; x++) - { - if (walxrepeat < 0) - { - lwall[x] = xs_RoundToInt(xrepeat - top / bot); - } - else - { - lwall[x] = xs_RoundToInt(top / bot); - } - top += topstep; - bot += botstep; - } - PrepWallRoundFix(lwall, FLOAT2FIXED(walxrepeat), x1, x2); -} - -// pass = 0: when seg is first drawn -// = 1: drawing masked textures (including sprites) -// Currently, only pass = 0 is done or used - -static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, int pass) -{ - DVector2 decal_left, decal_right, decal_pos; - int x1, x2; - double yscale; - BYTE flipx; - double zpos; - int needrepeat = 0; - sector_t *front, *back; - bool calclighting; - bool rereadcolormap; - FDynamicColormap *usecolormap; - - if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid()) - return; - - // Determine actor z - zpos = decal->Z; - front = curline->frontsector; - back = (curline->backsector != NULL) ? curline->backsector : curline->frontsector; - switch (decal->RenderFlags & RF_RELMASK) - { - default: - zpos = decal->Z; - break; - case RF_RELUPPER: - if (curline->linedef->flags & ML_DONTPEGTOP) - { - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); - } - else - { - zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling); - } - break; - case RF_RELLOWER: - if (curline->linedef->flags & ML_DONTPEGBOTTOM) - { - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); - } - else - { - zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor); - } - break; - case RF_RELMID: - if (curline->linedef->flags & ML_DONTPEGBOTTOM) - { - zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor); - } - else - { - zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); - } - } - - WallSpriteTile = TexMan(decal->PicNum, true); - flipx = (BYTE)(decal->RenderFlags & RF_XFLIP); - - if (WallSpriteTile == NULL || WallSpriteTile->UseType == FTexture::TEX_Null) - { - return; - } - - // Determine left and right edges of sprite. Since this sprite is bound - // to a wall, we use the wall's angle instead of the decal's. This is - // pretty much the same as what R_AddLine() does. - - FWallCoords savecoord = WallC; - - double edge_right = WallSpriteTile->GetWidth(); - double edge_left = WallSpriteTile->LeftOffset; - edge_right = (edge_right - edge_left) * decal->ScaleX; - edge_left *= decal->ScaleX; - - double dcx, dcy; - decal->GetXY(wall, dcx, dcy); - decal_pos = { dcx, dcy }; - - DVector2 angvec = (curline->v2->fPos() - curline->v1->fPos()).Unit(); - - decal_left = decal_pos - edge_left * angvec - ViewPos; - decal_right = decal_pos + edge_right * angvec - ViewPos; - - if (WallC.Init(decal_left, decal_right, TOO_CLOSE_Z)) - goto done; - - x1 = WallC.sx1; - x2 = WallC.sx2; - - if (x1 >= clipper->x2 || x2 <= clipper->x1) - goto done; - - WallT.InitFromWallCoords(&WallC); - - // Get the top and bottom clipping arrays - switch (decal->RenderFlags & RF_CLIPMASK) - { - case RF_CLIPFULL: - if (curline->backsector == NULL) - { - if (pass != 0) - { - goto done; - } - mceilingclip = walltop; - mfloorclip = wallbottom; - } - else if (pass == 0) - { - mceilingclip = walltop; - mfloorclip = ceilingclip; - needrepeat = 1; - } - else - { - mceilingclip = openings + clipper->sprtopclip - clipper->x1; - mfloorclip = openings + clipper->sprbottomclip - clipper->x1; - } - break; - - case RF_CLIPUPPER: - if (pass != 0) - { - goto done; - } - mceilingclip = walltop; - mfloorclip = ceilingclip; - break; - - case RF_CLIPMID: - if (curline->backsector != NULL && pass != 2) - { - goto done; - } - mceilingclip = openings + clipper->sprtopclip - clipper->x1; - mfloorclip = openings + clipper->sprbottomclip - clipper->x1; - break; - - case RF_CLIPLOWER: - if (pass != 0) - { - goto done; - } - mceilingclip = floorclip; - mfloorclip = wallbottom; - break; - } - - yscale = decal->ScaleY; - dc_texturemid = WallSpriteTile->TopOffset + (zpos - ViewPos.Z) / yscale; - - // Clip sprite to drawseg - x1 = MAX(clipper->x1, x1); - x2 = MIN(clipper->x2, x2); - if (x1 >= x2) - { - goto done; - } - - PrepWall (swall, lwall, WallSpriteTile->GetWidth(), x1, x2); - - if (flipx) - { - int i; - int right = (WallSpriteTile->GetWidth() << FRACBITS) - 1; - - for (i = x1; i < x2; i++) - { - lwall[i] = right - lwall[i]; - } - } - - // Prepare lighting - calclighting = false; - usecolormap = basecolormap; - rereadcolormap = true; - - // Decals that are added to the scene must fade to black. - if (decal->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) - { - usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); - rereadcolormap = false; - } - - rw_light = rw_lightleft + (x1 - WallC.sx1) * rw_lightstep; - if (fixedlightlev >= 0) - dc_colormap = usecolormap->Maps + fixedlightlev; - else if (fixedcolormap != NULL) - dc_colormap = fixedcolormap; - else if (!foggy && (decal->RenderFlags & RF_FULLBRIGHT)) - dc_colormap = usecolormap->Maps; - else - calclighting = true; - - // Draw it - if (decal->RenderFlags & RF_YFLIP) - { - sprflipvert = true; - yscale = -yscale; - dc_texturemid -= WallSpriteTile->GetHeight(); - } - else - { - sprflipvert = false; - } - - MaskedScaleY = float(1 / yscale); - do - { - dc_x = x1; - ESPSResult mode; - - mode = R_SetPatchStyle (decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor); - - // R_SetPatchStyle can modify basecolormap. - if (rereadcolormap) - { - usecolormap = basecolormap; - } - - if (mode == DontDraw) - { - needrepeat = 0; - } - else - { - int stop4; - - if (mode == DoDraw0) - { // 1 column at a time - stop4 = dc_x; - } - else // DoDraw1 - { // up to 4 columns at a time - stop4 = x2 & ~3; - } - - while ((dc_x < stop4) && (dc_x & 3)) - { - if (calclighting) - { // calculate lighting - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); - } - R_WallSpriteColumn (R_DrawMaskedColumn); - dc_x++; - } - - while (dc_x < stop4) - { - if (calclighting) - { // calculate lighting - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); - } - rt_initcols(); - for (int zz = 4; zz; --zz) - { - R_WallSpriteColumn (R_DrawMaskedColumnHoriz); - dc_x++; - } - rt_draw4cols (dc_x - 4); - } - - while (dc_x < x2) - { - if (calclighting) - { // calculate lighting - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); - } - R_WallSpriteColumn (R_DrawMaskedColumn); - dc_x++; - } - } - - // If this sprite is RF_CLIPFULL on a two-sided line, needrepeat will - // be set 1 if we need to draw on the lower wall. In all other cases, - // needrepeat will be 0, and the while will fail. - mceilingclip = floorclip; - mfloorclip = wallbottom; - R_FinishSetPatchStyle (); - } while (needrepeat--); - - colfunc = basecolfunc; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; - - R_FinishSetPatchStyle (); -done: - WallC = savecoord; -} diff --git a/src/r_segs.h b/src/r_segs.h deleted file mode 100644 index 1fc428c96..000000000 --- a/src/r_segs.h +++ /dev/null @@ -1,73 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Refresh module, drawing LineSegs from BSP. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_SEGS_H__ -#define __R_SEGS_H__ - -struct drawseg_t; - -void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2); - -extern short *openings; -extern ptrdiff_t lastopening; -extern size_t maxopenings; - -int OWallMost (short *mostbuf, double z, const FWallCoords *wallc); -int WallMost (short *mostbuf, const secplane_t &plane, const FWallCoords *wallc); -void PrepWall (float *swall, fixed_t *lwall, double walxrepeat, int x1, int x2); -void PrepLWall (fixed_t *lwall, double walxrepeat, int x1, int x2); - -ptrdiff_t R_NewOpening (ptrdiff_t len); - -void R_CheckDrawSegs (); - -void R_RenderSegLoop (); - -extern float swall[MAXWIDTH]; -extern fixed_t lwall[MAXWIDTH]; -extern float rw_light; // [RH] Scale lights with viewsize adjustments -extern float rw_lightstep; -extern float rw_lightleft; -extern fixed_t rw_offset; - -/* portal structure, this is used in r_ code in order to store drawsegs with portals (and mirrors) */ -struct PortalDrawseg -{ - line_t* src; // source line (the one drawn) this doesn't change over render loops - line_t* dst; // destination line (the one that the portal is linked with, equals 'src' for mirrors) - - int x1; // drawseg x1 - int x2; // drawseg x2 - - int len; - TArray ceilingclip; - TArray floorclip; - - bool mirror; // true if this is a mirror (src should equal dst) -}; - -extern PortalDrawseg* CurrentPortal; -extern int CurrentPortalUniq; -extern bool CurrentPortalInSkybox; -extern TArray WallPortals; - -#endif diff --git a/src/r_state.h b/src/r_state.h index bacbe135c..0fad90e1d 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -69,7 +69,6 @@ extern int numgamesubsectors; extern AActor* camera; // [RH] camera instead of viewplayer extern sector_t* viewsector; // [RH] keep track of sector viewing from -namespace swrenderer { extern angle_t xtoviewangle[MAXWIDTH+1]; } extern DAngle FieldOfView; int R_FindSkin (const char *name, int pclass); // [RH] Find a skin diff --git a/src/r_swrenderer.cpp b/src/r_swrenderer.cpp deleted file mode 100644 index 7eb1e13e9..000000000 --- a/src/r_swrenderer.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* -** r_swrender.cpp -** Software renderer interface -** -**--------------------------------------------------------------------------- -** Copyright 2011 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 "r_local.h" -#include "v_palette.h" -#include "v_video.h" -#include "m_png.h" -#include "r_bsp.h" -#include "r_swrenderer.h" -#include "r_3dfloors.h" -#include "textures/textures.h" -#include "r_data/voxels.h" - - -class FArchive; -void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio); -void R_SetupColormap(player_t *); -void R_SetupFreelook(); -void R_InitRenderer(); - -extern float LastFOV; - -//========================================================================== -// -// DCanvas :: Init -// -//========================================================================== - -void FSoftwareRenderer::Init() -{ - R_InitRenderer(); -} - -//========================================================================== -// -// DCanvas :: UsesColormap -// -//========================================================================== - -bool FSoftwareRenderer::UsesColormap() const -{ - return true; -} - -//=========================================================================== -// -// Texture precaching -// -//=========================================================================== - -void FSoftwareRenderer::PrecacheTexture(FTexture *tex, int cache) -{ - if (tex != NULL) - { - if (cache & FTextureManager::HIT_Columnmode) - { - const FTexture::Span *spanp; - tex->GetColumn(0, &spanp); - } - else if (cache != 0) - { - tex->GetPixels (); - } - else - { - tex->Unload (); - } - } -} - -//=========================================================================== -// -// Render the view -// -//=========================================================================== - -void FSoftwareRenderer::RenderView(player_t *player) -{ - R_RenderActorView (player->mo); - // [RH] Let cameras draw onto textures that were visible this frame. - FCanvasTextureInfo::UpdateAll (); -} - -//========================================================================== -// -// -// -//========================================================================== - -void FSoftwareRenderer::RemapVoxels() -{ - for (unsigned i=0; iRemap(); - } -} - -//=========================================================================== -// -// Render the view to a savegame picture -// -//=========================================================================== - -void FSoftwareRenderer::WriteSavePic (player_t *player, FILE *file, int width, int height) -{ - DCanvas *pic = new DSimpleCanvas (width, height); - PalEntry palette[256]; - - // Take a snapshot of the player's view - pic->ObjectFlags |= OF_Fixed; - pic->Lock (); - R_RenderViewToCanvas (player->mo, pic, 0, 0, width, height); - screen->GetFlashedPalette (palette); - M_CreatePNG (file, pic->GetBuffer(), palette, SS_PAL, width, height, pic->GetPitch()); - pic->Unlock (); - pic->Destroy(); - pic->ObjectFlags |= OF_YesReallyDelete; - delete pic; -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::DrawRemainingPlayerSprites() -{ - R_DrawRemainingPlayerSprites(); -} - -//=========================================================================== -// -// Get max. view angle (renderer specific information so it goes here now) -// -//=========================================================================== -#define MAX_DN_ANGLE 56 // Max looking down angle -#define MAX_UP_ANGLE 32 // Max looking up angle - -int FSoftwareRenderer::GetMaxViewPitch(bool down) -{ - return down ? MAX_DN_ANGLE : MAX_UP_ANGLE; -} - -//========================================================================== -// -// OnModeSet -// -// Called from V_SetResolution() -// -//========================================================================== - -void FSoftwareRenderer::OnModeSet () -{ - R_MultiresInit (); - - RenderTarget = screen; - screen->Lock (true); - R_SetupBuffer (); - screen->Unlock (); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::ErrorCleanup () -{ - fakeActive = 0; - fake3D = 0; - while (CurrentSkybox) - { - R_3D_DeleteHeights(); - R_3D_LeaveSkybox(); - } - R_3D_ResetClip(); - R_3D_DeleteHeights(); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::ClearBuffer(int color) -{ - memset(RenderTarget->GetBuffer(), color, RenderTarget->GetPitch() * RenderTarget->GetHeight()); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio) -{ - R_SWRSetWindow(windowSize, fullWidth, fullHeight, stHeight, trueratio); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FSoftwareRenderer::SetupFrame(player_t *player) -{ - R_SetupColormap(player); - R_SetupFreelook(); -} - -//========================================================================== -// -// R_CopyStackedViewParameters -// -//========================================================================== - -void FSoftwareRenderer::CopyStackedViewParameters() -{ - R_CopyStackedViewParameters(); -} - -//========================================================================== -// -// -// -//========================================================================== - -void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) -{ - BYTE *Pixels = const_cast(tex->GetPixels()); - DSimpleCanvas *Canvas = tex->GetCanvas(); - - // curse Doom's overuse of global variables in the renderer. - // These get clobbered by rendering to a camera texture but they need to be preserved so the final rendering can be done with the correct palette. - unsigned char *savecolormap = fixedcolormap; - FSpecialColormap *savecm = realfixedcolormap; - - float savedfov = LastFOV; - R_SetFOV ((float)fov); - R_RenderViewToCanvas (viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate); - R_SetFOV (savedfov); - if (Pixels == Canvas->GetBuffer()) - { - FTexture::FlipSquareBlockRemap (Pixels, tex->GetWidth(), tex->GetHeight(), GPalette.Remap); - } - else - { - FTexture::FlipNonSquareBlockRemap (Pixels, Canvas->GetBuffer(), tex->GetWidth(), tex->GetHeight(), Canvas->GetPitch(), GPalette.Remap); - } - tex->SetUpdated(); - fixedcolormap = savecolormap; - realfixedcolormap = savecm; -} - -//========================================================================== -// -// -// -//========================================================================== - -sector_t *FSoftwareRenderer::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) -{ - return R_FakeFlat(sec, tempsec, floorlightlevel, ceilinglightlevel, back); -} - diff --git a/src/r_swrenderer.h b/src/r_swrenderer.h deleted file mode 100644 index 939547797..000000000 --- a/src/r_swrenderer.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef __R_SWRENDERER_H -#define __R_SWRENDERER_H - -#include "r_renderer.h" - -struct FSoftwareRenderer : public FRenderer -{ - // Can be overridden so that the colormaps for sector color/fade won't be built. - virtual bool UsesColormap() const; - - // precache one texture - virtual void PrecacheTexture(FTexture *tex, int cache); - - // render 3D view - virtual void RenderView(player_t *player); - - // Remap voxel palette - virtual void RemapVoxels(); - - // renders view to a savegame picture - virtual void WriteSavePic (player_t *player, FILE *file, int width, int height); - - // draws player sprites with hardware acceleration (only useful for software rendering) - virtual void DrawRemainingPlayerSprites(); - - virtual int GetMaxViewPitch(bool down); - - void OnModeSet (); - void ErrorCleanup (); - void ClearBuffer(int color); - void Init(); - void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, int trueratio); - void SetupFrame(player_t *player); - void CopyStackedViewParameters(); - void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov); - sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back); - -}; - - -#endif diff --git a/src/r_things.cpp b/src/r_things.cpp deleted file mode 100644 index b930a1e92..000000000 --- a/src/r_things.cpp +++ /dev/null @@ -1,3202 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// $Log:$ -// -// DESCRIPTION: -// Refresh of things, i.e. objects represented by sprites. -// -// This file contains some code from the Build Engine. -// -// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman -// Ken Silverman's official web site: "http://www.advsys.net/ken" -// See the included license file "BUILDLIC.TXT" for license info. -// -//----------------------------------------------------------------------------- - -#include -#include -#include - -#include "p_lnspec.h" -#include "templates.h" -#include "doomdef.h" -#include "m_swap.h" -#include "i_system.h" -#include "w_wad.h" -#include "r_local.h" -#include "c_console.h" -#include "c_cvars.h" -#include "c_dispatch.h" -#include "doomstat.h" -#include "v_video.h" -#include "sc_man.h" -#include "s_sound.h" -#include "sbar.h" -#include "gi.h" -#include "r_sky.h" -#include "cmdlib.h" -#include "g_level.h" -#include "d_net.h" -#include "colormatcher.h" -#include "d_netinf.h" -#include "p_effect.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "v_palette.h" -#include "r_data/r_translate.h" -#include "r_data/colormaps.h" -#include "r_data/voxels.h" -#include "p_local.h" -#include "p_maputl.h" - -// [RH] A c-buffer. Used for keeping track of offscreen voxel spans. - -struct FCoverageBuffer -{ - struct Span - { - Span *NextSpan; - short Start, Stop; - }; - - FCoverageBuffer(int size); - ~FCoverageBuffer(); - - void Clear(); - void InsertSpan(int listnum, int start, int stop); - Span *AllocSpan(); - - FMemArena SpanArena; - Span **Spans; // [0..NumLists-1] span lists - Span *FreeSpans; - unsigned int NumLists; -}; - -extern double globaluclip, globaldclip; -extern float MaskedScaleY; - -#define MINZ double((2048*4) / double(1 << 20)) -#define BASEYCENTER (100) - -EXTERN_CVAR (Bool, st_scale) -EXTERN_CVAR(Bool, r_shadercolormaps) -EXTERN_CVAR(Int, r_drawfuzz) -EXTERN_CVAR(Bool, r_deathcamera); - -// -// Sprite rotation 0 is facing the viewer, -// rotation 1 is one angle turn CLOCKWISE around the axis. -// This is not the same as the angle, -// which increases counter clockwise (protractor). -// -double pspritexscale; -double pspritexiscale; -double pspriteyscale; -fixed_t sky1scale; // [RH] Sky 1 scale factor -fixed_t sky2scale; // [RH] Sky 2 scale factor - -vissprite_t *VisPSprites[NUMPSPRITES]; -int VisPSpritesX1[NUMPSPRITES]; -FDynamicColormap *VisPSpritesBaseColormap[NUMPSPRITES]; - -static int spriteshade; - -FTexture *WallSpriteTile; - -// constant arrays -// used for psprite clipping and initializing clipping -short zeroarray[MAXWIDTH]; -short screenheightarray[MAXWIDTH]; - -EXTERN_CVAR (Bool, r_drawplayersprites) -EXTERN_CVAR (Bool, r_drawvoxels) - -// -// INITIALIZATION FUNCTIONS -// - -int OffscreenBufferWidth, OffscreenBufferHeight; -BYTE *OffscreenColorBuffer; -FCoverageBuffer *OffscreenCoverageBuffer; - -// - -// GAME FUNCTIONS -// -int MaxVisSprites; -vissprite_t **vissprites; -vissprite_t **firstvissprite; -vissprite_t **vissprite_p; -vissprite_t **lastvissprite; -int newvissprite; -bool DrewAVoxel; - -static vissprite_t **spritesorter; -static int spritesortersize = 0; -static int vsprcount; - -static void R_ProjectWallSprite(AActor *thing, const DVector3 &pos, FTextureID picnum, const DVector2 &scale, INTBOOL flip); - - - -void R_DeinitSprites() -{ - // Free vissprites - for (int i = 0; i < MaxVisSprites; ++i) - { - delete vissprites[i]; - } - free (vissprites); - vissprites = NULL; - vissprite_p = lastvissprite = NULL; - MaxVisSprites = 0; - - // Free vissprites sorter - if (spritesorter != NULL) - { - delete[] spritesorter; - spritesortersize = 0; - spritesorter = NULL; - } - - // Free offscreen buffer - if (OffscreenColorBuffer != NULL) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = NULL; - } - if (OffscreenCoverageBuffer != NULL) - { - delete OffscreenCoverageBuffer; - OffscreenCoverageBuffer = NULL; - } - OffscreenBufferHeight = OffscreenBufferWidth = 0; -} - -// -// R_ClearSprites -// Called at frame start. -// -void R_ClearSprites (void) -{ - vissprite_p = firstvissprite; - DrewAVoxel = false; -} - - -// -// R_NewVisSprite -// -vissprite_t *R_NewVisSprite (void) -{ - if (vissprite_p == lastvissprite) - { - ptrdiff_t firstvisspritenum = firstvissprite - vissprites; - ptrdiff_t prevvisspritenum = vissprite_p - vissprites; - - MaxVisSprites = MaxVisSprites ? MaxVisSprites * 2 : 128; - vissprites = (vissprite_t **)M_Realloc (vissprites, MaxVisSprites * sizeof(vissprite_t)); - lastvissprite = &vissprites[MaxVisSprites]; - firstvissprite = &vissprites[firstvisspritenum]; - vissprite_p = &vissprites[prevvisspritenum]; - DPrintf ("MaxVisSprites increased to %d\n", MaxVisSprites); - - // Allocate sprites from the new pile - for (vissprite_t **p = vissprite_p; p < lastvissprite; ++p) - { - *p = new vissprite_t; - } - } - - vissprite_p++; - return *(vissprite_p-1); -} - -// -// R_DrawMaskedColumn -// Used for sprites and masked mid textures. -// Masked means: partly transparent, i.e. stored -// in posts/runs of opaque pixels. -// -short* mfloorclip; -short* mceilingclip; - -double spryscale; -double sprtopscreen; - -bool sprflipvert; - -void R_DrawMaskedColumn (const BYTE *column, const FTexture::Span *span) -{ - const fixed_t centeryfrac = FLOAT2FIXED(CenterY); - const fixed_t texturemid = FLOAT2FIXED(dc_texturemid); - while (span->Length != 0) - { - const int length = span->Length; - const int top = span->TopOffset; - - // calculate unclipped screen coordinates for post - dc_yl = xs_RoundToInt(sprtopscreen + spryscale * top); - dc_yh = xs_RoundToInt(sprtopscreen + spryscale * (top + length)) - 1; - - if (sprflipvert) - { - swapvalues (dc_yl, dc_yh); - } - - if (dc_yh >= mfloorclip[dc_x]) - { - dc_yh = mfloorclip[dc_x] - 1; - } - if (dc_yl < mceilingclip[dc_x]) - { - dc_yl = mceilingclip[dc_x]; - } - - if (dc_yl <= dc_yh) - { - if (sprflipvert) - { - dc_texturefrac = (dc_yl*dc_iscale) - (top << FRACBITS) - - FixedMul (centeryfrac, dc_iscale) - texturemid; - const fixed_t maxfrac = length << FRACBITS; - while (dc_texturefrac >= maxfrac) - { - if (++dc_yl > dc_yh) - goto nextpost; - dc_texturefrac += dc_iscale; - } - fixed_t endfrac = dc_texturefrac + (dc_yh-dc_yl)*dc_iscale; - while (endfrac < 0) - { - if (--dc_yh < dc_yl) - goto nextpost; - endfrac -= dc_iscale; - } - } - else - { - dc_texturefrac = texturemid - (top << FRACBITS) - + (dc_yl*dc_iscale) - FixedMul (centeryfrac-FRACUNIT, dc_iscale); - while (dc_texturefrac < 0) - { - if (++dc_yl > dc_yh) - goto nextpost; - dc_texturefrac += dc_iscale; - } - fixed_t endfrac = dc_texturefrac + (dc_yh-dc_yl)*dc_iscale; - const fixed_t maxfrac = length << FRACBITS; - if (dc_yh < mfloorclip[dc_x]-1 && endfrac < maxfrac - dc_iscale) - { - dc_yh++; - } - else while (endfrac >= maxfrac) - { - if (--dc_yh < dc_yl) - goto nextpost; - endfrac -= dc_iscale; - } - } - dc_source = column + top; - dc_dest = ylookup[dc_yl] + dc_x + dc_destorg; - dc_count = dc_yh - dc_yl + 1; - colfunc (); - } -nextpost: - span++; - } -} - -// [ZZ] -// R_ClipSpriteColumnWithPortals -// - -static TArray portaldrawsegs; - -static inline void R_CollectPortals() -{ - // This function collects all drawsegs that may be of interest to R_ClipSpriteColumnWithPortals - // Having that function over the entire list of drawsegs can break down performance quite drastically. - // This is doing the costly stuff only once so that R_ClipSpriteColumnWithPortals can - // a) exit early if no relevant info is found and - // b) skip most of the collected drawsegs which have no portal attached. - portaldrawsegs.Clear(); - for (drawseg_t* seg = ds_p; seg-- > firstdrawseg; ) // copied code from killough below - { - // I don't know what makes this happen (some old top-down portal code or possibly skybox code? something adds null lines...) - // crashes at the first frame of the first map of Action2.wad - if (!seg->curline) continue; - - line_t* line = seg->curline->linedef; - // ignore minisegs from GL nodes. - if (!line) continue; - - // check if this line will clip sprites to itself - if (!line->isVisualPortal() && line->special != Line_Mirror) - continue; - - // don't clip sprites with portal's back side (it's transparent) - if (seg->curline->sidedef != line->sidedef[0]) - continue; - - portaldrawsegs.Push(seg); - } -} - -static inline bool R_ClipSpriteColumnWithPortals(vissprite_t* spr) -{ - // [ZZ] 10.01.2016: don't clip sprites from the root of a skybox. - if (CurrentPortalInSkybox) - return false; - - for (drawseg_t *seg : portaldrawsegs) - { - // ignore segs from other portals - if (seg->CurrentPortalUniq != CurrentPortalUniq) - continue; - - // (all checks that are already done in R_CollectPortals have been removed for performance reasons.) - - // don't clip if the sprite is in front of the portal - if (!P_PointOnLineSidePrecise(spr->gpos.X, spr->gpos.Y, seg->curline->linedef)) - continue; - - // now if current column is covered by this drawseg, we clip it away - if ((dc_x >= seg->x1) && (dc_x < seg->x2)) - return true; - } - - return false; -} - - -// -// R_DrawVisSprite -// mfloorclip and mceilingclip should also be set. -// -void R_DrawVisSprite (vissprite_t *vis) -{ - const BYTE *pixels; - const FTexture::Span *spans; - fixed_t frac; - FTexture *tex; - int x2, stop4; - fixed_t xiscale; - ESPSResult mode; - bool ispsprite = (!vis->sector && vis->gpos != FVector3(0, 0, 0)); - - if (vis->xscale == 0 || vis->yscale == 0) - { // scaled to 0; can't see - return; - } - - fixed_t centeryfrac = FLOAT2FIXED(CenterY); - dc_colormap = vis->Style.colormap; - - mode = R_SetPatchStyle (vis->Style.RenderStyle, vis->Style.Alpha, vis->Translation, vis->FillColor); - - if (vis->Style.RenderStyle == LegacyRenderStyles[STYLE_Shaded]) - { // For shaded sprites, R_SetPatchStyle sets a dc_colormap to an alpha table, but - // it is the brightest one. We need to get back to the proper light level for - // this sprite. - dc_colormap += vis->ColormapNum << COLORMAPSHIFT; - } - - if (mode != DontDraw) - { - if (mode == DoDraw0) - { - // One column at a time - stop4 = vis->x1; - } - else // DoDraw1 - { - // Up to four columns at a time - stop4 = vis->x2 & ~3; - } - - tex = vis->pic; - spryscale = vis->yscale; - sprflipvert = false; - dc_iscale = FLOAT2FIXED(1 / vis->yscale); - frac = vis->startfrac; - xiscale = vis->xiscale; - dc_texturemid = vis->texturemid; - - if (vis->renderflags & RF_YFLIP) - { - sprflipvert = true; - spryscale = -spryscale; - dc_iscale = -dc_iscale; - dc_texturemid -= vis->pic->GetHeight(); - sprtopscreen = CenterY + dc_texturemid * spryscale; - } - else - { - sprflipvert = false; - sprtopscreen = CenterY - dc_texturemid * spryscale; - } - - dc_x = vis->x1; - x2 = vis->x2; - - if (dc_x < x2) - { - while ((dc_x < stop4) && (dc_x & 3)) - { - pixels = tex->GetColumn (frac >> FRACBITS, &spans); - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumn (pixels, spans); - dc_x++; - frac += xiscale; - } - - while (dc_x < stop4) - { - rt_initcols(); - for (int zz = 4; zz; --zz) - { - pixels = tex->GetColumn (frac >> FRACBITS, &spans); - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumnHoriz (pixels, spans); - dc_x++; - frac += xiscale; - } - rt_draw4cols (dc_x - 4); - } - - while (dc_x < x2) - { - pixels = tex->GetColumn (frac >> FRACBITS, &spans); - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumn (pixels, spans); - dc_x++; - frac += xiscale; - } - } - } - - R_FinishSetPatchStyle (); - - NetUpdate (); -} - -void R_DrawWallSprite(vissprite_t *spr) -{ - int x1, x2; - double iyscale; - - x1 = MAX(spr->x1, spr->wallc.sx1); - x2 = MIN(spr->x2, spr->wallc.sx2); - if (x1 >= x2) - return; - WallT.InitFromWallCoords(&spr->wallc); - PrepWall(swall, lwall, spr->pic->GetWidth() << FRACBITS, x1, x2); - iyscale = 1 / spr->yscale; - dc_texturemid = (spr->gzt - ViewPos.Z) * iyscale; - if (spr->renderflags & RF_XFLIP) - { - int right = (spr->pic->GetWidth() << FRACBITS) - 1; - - for (int i = x1; i < x2; i++) - { - lwall[i] = right - lwall[i]; - } - } - // Prepare lighting - bool calclighting = false; - FDynamicColormap *usecolormap = basecolormap; - bool rereadcolormap = true; - - // Decals that are added to the scene must fade to black. - if (spr->Style.RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) - { - usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); - rereadcolormap = false; - } - - int shade = LIGHT2SHADE(spr->sector->lightlevel + r_actualextralight); - GlobVis = r_WallVisibility; - rw_lightleft = float (GlobVis / spr->wallc.sz1); - rw_lightstep = float((GlobVis / spr->wallc.sz2 - rw_lightleft) / (spr->wallc.sx2 - spr->wallc.sx1)); - rw_light = rw_lightleft + (x1 - spr->wallc.sx1) * rw_lightstep; - if (fixedlightlev >= 0) - dc_colormap = usecolormap->Maps + fixedlightlev; - else if (fixedcolormap != NULL) - dc_colormap = fixedcolormap; - else if (!foggy && (spr->renderflags & RF_FULLBRIGHT)) - dc_colormap = usecolormap->Maps; - else - calclighting = true; - - // Draw it - WallSpriteTile = spr->pic; - if (spr->renderflags & RF_YFLIP) - { - sprflipvert = true; - iyscale = -iyscale; - dc_texturemid -= spr->pic->GetHeight(); - } - else - { - sprflipvert = false; - } - - MaskedScaleY = (float)iyscale; - - dc_x = x1; - ESPSResult mode; - - mode = R_SetPatchStyle (spr->Style.RenderStyle, spr->Style.Alpha, spr->Translation, spr->FillColor); - - // R_SetPatchStyle can modify basecolormap. - if (rereadcolormap) - { - usecolormap = basecolormap; - } - - if (mode == DontDraw) - { - return; - } - else - { - int stop4; - - if (mode == DoDraw0) - { // 1 column at a time - stop4 = dc_x; - } - else // DoDraw1 - { // up to 4 columns at a time - stop4 = x2 & ~3; - } - - while ((dc_x < stop4) && (dc_x & 3)) - { - if (calclighting) - { // calculate lighting - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, shade) << COLORMAPSHIFT); - } - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(R_DrawMaskedColumn); - dc_x++; - } - - while (dc_x < stop4) - { - if (calclighting) - { // calculate lighting - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, shade) << COLORMAPSHIFT); - } - rt_initcols(); - for (int zz = 4; zz; --zz) - { - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(R_DrawMaskedColumnHoriz); - dc_x++; - } - rt_draw4cols(dc_x - 4); - } - - while (dc_x < x2) - { - if (calclighting) - { // calculate lighting - dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, shade) << COLORMAPSHIFT); - } - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(R_DrawMaskedColumn); - dc_x++; - } - } - R_FinishSetPatchStyle(); -} - -void R_WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans)) -{ - float iscale = swall[dc_x] * MaskedScaleY; - dc_iscale = FLOAT2FIXED(iscale); - spryscale = 1 / iscale; - if (sprflipvert) - sprtopscreen = CenterY + dc_texturemid * spryscale; - else - sprtopscreen = CenterY - dc_texturemid * spryscale; - - const BYTE *column; - const FTexture::Span *spans; - column = WallSpriteTile->GetColumn (lwall[dc_x] >> FRACBITS, &spans); - dc_texturefrac = 0; - drawfunc (column, spans); - rw_light += rw_lightstep; -} - -void R_DrawVisVoxel(vissprite_t *spr, int minslabz, int maxslabz, short *cliptop, short *clipbot) -{ - ESPSResult mode; - int flags = 0; - - // Do setup for blending. - dc_colormap = spr->Style.colormap; - mode = R_SetPatchStyle(spr->Style.RenderStyle, spr->Style.Alpha, spr->Translation, spr->FillColor); - - if (mode == DontDraw) - { - return; - } - if (colfunc == fuzzcolfunc || colfunc == R_FillColumnP) - { - flags = DVF_OFFSCREEN | DVF_SPANSONLY; - } - else if (colfunc != basecolfunc) - { - flags = DVF_OFFSCREEN; - } - if (flags != 0) - { - R_CheckOffscreenBuffer(RenderTarget->GetWidth(), RenderTarget->GetHeight(), !!(flags & DVF_SPANSONLY)); - } - if (spr->bInMirror) - { - flags |= DVF_MIRRORED; - } - - // Render the voxel, either directly to the screen or offscreen. - R_DrawVoxel(spr->pa.vpos, spr->pa.vang, spr->gpos, spr->angle, - spr->xscale, FLOAT2FIXED(spr->yscale), spr->voxel, spr->Style.colormap, cliptop, clipbot, - minslabz, maxslabz, flags); - - // Blend the voxel, if that's what we need to do. - if ((flags & ~DVF_MIRRORED) != 0) - { - for (int x = 0; x < viewwidth; ++x) - { - if (!(flags & DVF_SPANSONLY) && (x & 3) == 0) - { - rt_initcols(OffscreenColorBuffer + x * OffscreenBufferHeight); - } - for (FCoverageBuffer::Span *span = OffscreenCoverageBuffer->Spans[x]; span != NULL; span = span->NextSpan) - { - if (flags & DVF_SPANSONLY) - { - dc_x = x; - dc_yl = span->Start; - dc_yh = span->Stop - 1; - dc_count = span->Stop - span->Start; - dc_dest = ylookup[span->Start] + x + dc_destorg; - colfunc(); - } - else - { - unsigned int **tspan = &dc_ctspan[x & 3]; - (*tspan)[0] = span->Start; - (*tspan)[1] = span->Stop - 1; - *tspan += 2; - } - } - if (!(flags & DVF_SPANSONLY) && (x & 3) == 3) - { - rt_draw4cols(x - 3); - } - } - } - - R_FinishSetPatchStyle(); - NetUpdate(); -} - -// -// R_ProjectSprite -// Generates a vissprite for a thing if it might be visible. -// -void R_ProjectSprite (AActor *thing, int fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling) -{ - double tr_x; - double tr_y; - - double gzt; // killough 3/27/98 - double gzb; // [RH] use bottom of sprite, not actor - double tx;// , tx2; - double tz; - - double xscale = 1, yscale = 1; - - int x1; - int x2; - - FTextureID picnum; - FTexture *tex; - FVoxelDef *voxel; - - vissprite_t* vis; - - fixed_t iscale; - - sector_t* heightsec; // killough 3/27/98 - - // Don't waste time projecting sprites that are definitely not visible. - if (thing == NULL || - (thing->renderflags & RF_INVISIBLE) || - !thing->RenderStyle.IsVisible(thing->Alpha) || - !thing->IsVisibleToPlayer()) - { - return; - } - - // [ZZ] Or less definitely not visible (hue) - // [ZZ] 10.01.2016: don't try to clip stuff inside a skybox against the current portal. - if (!CurrentPortalInSkybox && CurrentPortal && !!P_PointOnLineSidePrecise(thing->Pos(), CurrentPortal->dst)) - return; - - // [RH] Interpolate the sprite's position to make it look smooth - DVector3 pos = thing->InterpolatedPosition(r_TicFracF); - pos.Z += thing->GetBobOffset(r_TicFracF); - - tex = NULL; - voxel = NULL; - - int spritenum = thing->sprite; - DVector2 spriteScale = thing->Scale; - int renderflags = thing->renderflags; - if (spriteScale.Y < 0) - { - spriteScale.Y = -spriteScale.Y; - renderflags ^= RF_YFLIP; - } - if (thing->player != NULL) - { - P_CheckPlayerSprite(thing, spritenum, spriteScale); - } - - if (thing->picnum.isValid()) - { - picnum = thing->picnum; - - tex = TexMan(picnum); - if (tex->UseType == FTexture::TEX_Null) - { - return; - } - - if (tex->Rotations != 0xFFFF) - { - // choose a different rotation based on player view - spriteframe_t *sprframe = &SpriteFrames[tex->Rotations]; - DAngle ang = (pos - ViewPos).Angle(); - angle_t rot; - if (sprframe->Texture[0] == sprframe->Texture[1]) - { - rot = (ang - thing->Angles.Yaw + 45.0/2*9).BAMs() >> 28; - } - else - { - rot = (ang - thing->Angles.Yaw + (45.0/2*9-180.0/16)).BAMs() >> 28; - } - picnum = sprframe->Texture[rot]; - if (sprframe->Flip & (1 << rot)) - { - renderflags ^= RF_XFLIP; - } - tex = TexMan[picnum]; // Do not animate the rotation - } - } - else - { - // decide which texture to use for the sprite -#ifdef RANGECHECK - if (spritenum >= (signed)sprites.Size () || spritenum < 0) - { - DPrintf ("R_ProjectSprite: invalid sprite number %u\n", spritenum); - return; - } -#endif - spritedef_t *sprdef = &sprites[spritenum]; - if (thing->frame >= sprdef->numframes) - { - // If there are no frames at all for this sprite, don't draw it. - return; - } - else - { - //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; - // choose a different rotation based on player view - spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; - DAngle ang = (pos - ViewPos).Angle(); - angle_t rot; - if (sprframe->Texture[0] == sprframe->Texture[1]) - { - rot = (ang - thing->Angles.Yaw + 45.0 / 2 * 9).BAMs() >> 28; - } - else - { - rot = (ang - thing->Angles.Yaw + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - } - picnum = sprframe->Texture[rot]; - if (sprframe->Flip & (1 << rot)) - { - renderflags ^= RF_XFLIP; - } - tex = TexMan[picnum]; // Do not animate the rotation - if (r_drawvoxels) - { - voxel = sprframe->Voxel; - } - } - } - if (spriteScale.X < 0) - { - spriteScale.X = -spriteScale.X; - renderflags ^= RF_XFLIP; - } - if (voxel == NULL && (tex == NULL || tex->UseType == FTexture::TEX_Null)) - { - return; - } - - if ((renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) - { - R_ProjectWallSprite(thing, pos, picnum, spriteScale, renderflags); - return; - } - - // transform the origin point - tr_x = pos.X - ViewPos.X; - tr_y = pos.Y - ViewPos.Y; - - tz = tr_x * ViewTanCos + tr_y * ViewTanSin; - - // thing is behind view plane? - if (voxel == NULL && tz < MINZ) - return; - - tx = tr_x * ViewSin - tr_y * ViewCos; - - // [RH] Flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - tx = -tx; - } - //tx2 = tx >> 4; - - // too far off the side? - // if it's a voxel, it can be further off the side - if ((voxel == NULL && (fabs(tx / 64) > fabs(tz))) || - (voxel != NULL && (fabs(tx / 128) > abs(tz)))) - { - return; - } - - if (voxel == NULL) - { - // [RH] Added scaling - int scaled_to = tex->GetScaledTopOffset(); - int scaled_bo = scaled_to - tex->GetScaledHeight(); - gzt = pos.Z + spriteScale.Y * scaled_to; - gzb = pos.Z + spriteScale.Y * scaled_bo; - } - else - { - xscale = spriteScale.X * voxel->Scale; - yscale = spriteScale.Y * voxel->Scale; - double piv = voxel->Voxel->Mips[0].Pivot.Z; - gzt = pos.Z + yscale * piv - thing->Floorclip; - gzb = pos.Z + yscale * (piv - voxel->Voxel->Mips[0].SizeZ); - if (gzt <= gzb) - return; - } - - // killough 3/27/98: exclude things totally separated - // from the viewer, by either water or fake ceilings - // killough 4/11/98: improve sprite clipping for underwater/fake ceilings - - heightsec = thing->Sector->GetHeightSec(); - - if (heightsec != NULL) // only clip things which are in special sectors - { - if (fakeside == FAKED_AboveCeiling) - { - if (gzt < heightsec->ceilingplane.ZatPoint(pos)) - return; - } - else if (fakeside == FAKED_BelowFloor) - { - if (gzb >= heightsec->floorplane.ZatPoint(pos)) - return; - } - else - { - if (gzt < heightsec->floorplane.ZatPoint(pos)) - return; - if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint(pos)) - return; - } - } - - if (voxel == NULL) - { - xscale = CenterX / tz; - - // [RH] Reject sprites that are off the top or bottom of the screen - if (globaluclip * tz > ViewPos.Z - gzb || globaldclip * tz < ViewPos.Z - gzt) - { - return; - } - - // [RH] Flip for mirrors - renderflags ^= MirrorFlags & RF_XFLIP; - - // calculate edges of the shape - const double thingxscalemul = spriteScale.X / tex->Scale.X; - - tx -= ((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; - x1 = centerx + xs_RoundToInt(tx * xscale); - - // off the right side? - if (x1 >= WindowRight) - return; - - tx += tex->GetWidth() * thingxscalemul; - x2 = centerx + xs_RoundToInt(tx * xscale); - - // off the left side or too small? - if ((x2 < WindowLeft || x2 <= x1)) - return; - - xscale = spriteScale.X * xscale / tex->Scale.X; - iscale = (tex->GetWidth() << FRACBITS) / (x2 - x1); - - double yscale = spriteScale.Y / tex->Scale.Y; - - // store information in a vissprite - vis = R_NewVisSprite(); - - vis->CurrentPortalUniq = CurrentPortalUniq; - vis->xscale = FLOAT2FIXED(xscale); - vis->yscale = float(InvZtoScale * yscale / tz); - vis->idepth = float(1 / tz); - vis->floorclip = thing->Floorclip / yscale; - vis->texturemid = tex->TopOffset - (ViewPos.Z - pos.Z + thing->Floorclip) / yscale; - vis->x1 = x1 < WindowLeft ? WindowLeft : x1; - vis->x2 = x2 > WindowRight ? WindowRight : x2; - vis->angle = thing->Angles.Yaw.BAMs(); - - if (renderflags & RF_XFLIP) - { - vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; - vis->xiscale = -iscale; - } - else - { - vis->startfrac = 0; - vis->xiscale = iscale; - } - - if (vis->x1 > x1) - vis->startfrac += vis->xiscale * (vis->x1 - x1); - } - else - { - vis = R_NewVisSprite(); - - vis->CurrentPortalUniq = CurrentPortalUniq; - vis->xscale = FLOAT2FIXED(xscale); - vis->yscale = (float)yscale; - vis->x1 = WindowLeft; - vis->x2 = WindowRight; - vis->idepth = 1 / MINZ; - vis->floorclip = thing->Floorclip; - - pos.Z -= thing->Floorclip; - - vis->angle = thing->Angles.Yaw.BAMs() + voxel->AngleOffset.BAMs(); - - int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; - if (voxelspin != 0) - { - DAngle ang = double(I_FPSTime()) * voxelspin / 1000; - vis->angle -= ang.BAMs(); - } - - vis->pa.vpos = { (float)ViewPos.X, (float)ViewPos.Y, (float)ViewPos.Z }; - vis->pa.vang = FAngle((float)ViewAngle.Degrees); - } - - // killough 3/27/98: save sector for special clipping later - vis->heightsec = heightsec; - vis->sector = thing->Sector; - - vis->depth = (float)tz; - vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; - vis->gzb = (float)gzb; // [RH] use gzb, not thing->z - vis->gzt = (float)gzt; // killough 3/27/98 - vis->deltax = float(pos.X - ViewPos.X); - vis->deltay = float(pos.Y - ViewPos.Y); - vis->renderflags = renderflags; - if(thing->flags5 & MF5_BRIGHT) vis->renderflags |= RF_FULLBRIGHT; // kg3D - vis->Style.RenderStyle = thing->RenderStyle; - vis->FillColor = thing->fillcolor; - vis->Translation = thing->Translation; // [RH] thing translation table - vis->FakeFlatStat = fakeside; - vis->Style.Alpha = float(thing->Alpha); - vis->fakefloor = fakefloor; - vis->fakeceiling = fakeceiling; - vis->ColormapNum = 0; - vis->bInMirror = MirrorFlags & RF_XFLIP; - vis->bSplitSprite = false; - - if (voxel != NULL) - { - vis->voxel = voxel->Voxel; - vis->bIsVoxel = true; - vis->bWallSprite = false; - DrewAVoxel = true; - } - else - { - vis->pic = tex; - vis->bIsVoxel = false; - vis->bWallSprite = false; - } - - // The software renderer cannot invert the source without inverting the overlay - // too. That means if the source is inverted, we need to do the reverse of what - // the invert overlay flag says to do. - INTBOOL invertcolormap = (vis->Style.RenderStyle.Flags & STYLEF_InvertOverlay); - - if (vis->Style.RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - FDynamicColormap *mybasecolormap = basecolormap; - - // Sprites that are added to the scene must fade to black. - if (vis->Style.RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); - } - - if (vis->Style.RenderStyle.Flags & STYLEF_FadeToBlack) - { - if (invertcolormap) - { // Fade to white - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255,255,255), mybasecolormap->Desaturate); - invertcolormap = false; - } - else - { // Fade to black - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0,0,0), mybasecolormap->Desaturate); - } - } - - // get light level - if (fixedcolormap != NULL) - { // fixed map - vis->Style.colormap = fixedcolormap; - } - else - { - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - vis->Style.colormap = mybasecolormap->Maps + fixedlightlev; - } - else if (!foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) - { // full bright - vis->Style.colormap = mybasecolormap->Maps; - } - else - { // diminished light - vis->ColormapNum = GETPALOOKUP( - r_SpriteVisibility / MAX(tz, MINZ), spriteshade); - vis->Style.colormap = mybasecolormap->Maps + (vis->ColormapNum << COLORMAPSHIFT); - } - } -} - -static void R_ProjectWallSprite(AActor *thing, const DVector3 &pos, FTextureID picnum, const DVector2 &scale, int renderflags) -{ - FWallCoords wallc; - double x1, x2; - DVector2 left, right; - double gzb, gzt, tz; - FTexture *pic = TexMan(picnum, true); - DAngle ang = thing->Angles.Yaw + 90; - double angcos = ang.Cos(); - double angsin = ang.Sin(); - vissprite_t *vis; - - // Determine left and right edges of sprite. The sprite's angle is its normal, - // so the edges are 90 degrees each side of it. - x2 = pic->GetScaledWidth(); - x1 = pic->GetScaledLeftOffset(); - - x1 *= scale.X; - x2 *= scale.X; - - left.X = pos.X - x1 * angcos - ViewPos.X; - left.Y = pos.Y - x1 * angsin - ViewPos.Y; - right.X = left.X + x2 * angcos; - right.Y = right.Y + x2 * angsin; - - // Is it off-screen? - if (wallc.Init(left, right, TOO_CLOSE_Z)) - return; - - if (wallc.sx1 >= WindowRight || wallc.sx2 <= WindowLeft) - return; - - // Sprite sorting should probably treat these as walls, not sprites, - // but right now, I just want to get them drawing. - tz = (pos.X - ViewPos.X) * ViewTanCos + (pos.Y - ViewPos.Y) * ViewTanSin; - - int scaled_to = pic->GetScaledTopOffset(); - int scaled_bo = scaled_to - pic->GetScaledHeight(); - gzt = pos.Z + scale.Y * scaled_to; - gzb = pos.Z + scale.Y * scaled_bo; - - vis = R_NewVisSprite(); - vis->CurrentPortalUniq = CurrentPortalUniq; - vis->x1 = wallc.sx1 < WindowLeft ? WindowLeft : wallc.sx1; - vis->x2 = wallc.sx2 >= WindowRight ? WindowRight : wallc.sx2; - vis->yscale = (float)scale.Y; - vis->idepth = float(1 / tz); - vis->depth = (float)tz; - vis->sector = thing->Sector; - vis->heightsec = NULL; - vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; - vis->gzb = (float)gzb; - vis->gzt = (float)gzt; - vis->deltax = float(pos.X - ViewPos.X); - vis->deltay = float(pos.Y - ViewPos.Y); - vis->renderflags = renderflags; - if(thing->flags5 & MF5_BRIGHT) vis->renderflags |= RF_FULLBRIGHT; // kg3D - vis->Style.RenderStyle = thing->RenderStyle; - vis->FillColor = thing->fillcolor; - vis->Translation = thing->Translation; - vis->FakeFlatStat = 0; - vis->Style.Alpha = float(thing->Alpha); - vis->fakefloor = NULL; - vis->fakeceiling = NULL; - vis->ColormapNum = 0; - vis->bInMirror = MirrorFlags & RF_XFLIP; - vis->pic = pic; - vis->bIsVoxel = false; - vis->bWallSprite = true; - vis->ColormapNum = GETPALOOKUP( - r_SpriteVisibility / MAX(tz, MINZ), spriteshade); - vis->Style.colormap = basecolormap->Maps + (vis->ColormapNum << COLORMAPSHIFT); - vis->wallc = wallc; -} - -// -// R_AddSprites -// During BSP traversal, this adds sprites by sector. -// -// killough 9/18/98: add lightlevel as parameter, fixing underwater lighting -// [RH] Save which side of heightsec sprite is on here. -void R_AddSprites (sector_t *sec, int lightlevel, int fakeside) -{ - AActor *thing; - F3DFloor *fakeceiling = NULL; - F3DFloor *fakefloor = NULL; - - // BSP is traversed by subsector. - // A sector might have been split into several - // subsectors during BSP building. - // Thus we check whether it was already added. - if (sec->thinglist == NULL || sec->validcount == validcount) - return; - - // Well, now it will be done. - sec->validcount = validcount; - - spriteshade = LIGHT2SHADE(lightlevel + r_actualextralight); - - // Handle all things in sector. - for (thing = sec->thinglist; thing; thing = thing->snext) - { - FIntCVar *cvar = thing->GetClass()->distancecheck; - if (cvar != NULL && *cvar >= 0) - { - double dist = (thing->Pos() - ViewPos).LengthSquared(); - double check = (double)**cvar; - if (dist >= check * check) - { - continue; - } - } - - // find fake level - for(auto rover : frontsector->e->XFloor.ffloors) - { - if(!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES)) continue; - if(!(rover->flags & FF_SOLID) || rover->alpha != 255) continue; - if(!fakefloor) - { - if(!rover->top.plane->isSlope()) - { - if(rover->top.plane->ZatPoint(0., 0.) <= thing->Z()) fakefloor = rover; - } - } - if(!rover->bottom.plane->isSlope()) - { - if(rover->bottom.plane->ZatPoint(0., 0.) >= thing->Top()) fakeceiling = rover; - } - } - R_ProjectSprite (thing, fakeside, fakefloor, fakeceiling); - fakeceiling = NULL; - fakefloor = NULL; - } -} - - -// -// R_DrawPSprite -// -void R_DrawPSprite (pspdef_t* psp, int pspnum, AActor *owner, double sx, double sy) -{ - double tx; - int x1; - int x2; - spritedef_t* sprdef; - spriteframe_t* sprframe; - FTextureID picnum; - WORD flip; - FTexture* tex; - vissprite_t* vis; - static vissprite_t avis[NUMPSPRITES + 1]; - static vissprite_t *avisp[countof(avis)]; - bool noaccel; - - assert(pspnum >= 0 && pspnum < NUMPSPRITES); - - if (avisp[0] == NULL) - { - for (unsigned i = 0; i < countof(avis); ++i) - { - avisp[i] = &avis[i]; - } - } - - // decide which patch to use - if ( (unsigned)psp->sprite >= (unsigned)sprites.Size ()) - { - DPrintf ("R_DrawPSprite: invalid sprite number %i\n", psp->sprite); - return; - } - sprdef = &sprites[psp->sprite]; - if (psp->frame >= sprdef->numframes) - { - DPrintf ("R_DrawPSprite: invalid sprite frame %i : %i\n", psp->sprite, psp->frame); - return; - } - sprframe = &SpriteFrames[sprdef->spriteframes + psp->frame]; - - picnum = sprframe->Texture[0]; - flip = sprframe->Flip & 1; - tex = TexMan(picnum); - - if (tex->UseType == FTexture::TEX_Null) - return; - - // calculate edges of the shape - tx = sx - (320 / 2); - - tx -= tex->GetScaledLeftOffset(); - x1 = xs_RoundToInt(CenterX + tx * pspritexscale); - - // off the right side - if (x1 > viewwidth) - return; - - tx += tex->GetScaledWidth(); - x2 = xs_RoundToInt(CenterX + tx * pspritexscale); - - // off the left side - if (x2 <= 0) - return; - - // store information in a vissprite - vis = avisp[NUMPSPRITES]; - vis->renderflags = owner->renderflags; - vis->floorclip = 0; - - vis->texturemid = (BASEYCENTER - sy) * tex->Scale.Y + tex->TopOffset; - - if (camera->player && (RenderTarget != screen || - viewheight == RenderTarget->GetHeight() || - (RenderTarget->GetWidth() > 320 && !st_scale))) - { // Adjust PSprite for fullscreen views - AWeapon *weapon = NULL; - if (camera->player != NULL) - { - weapon = camera->player->ReadyWeapon; - } - if (pspnum <= ps_flash && weapon != NULL && weapon->YAdjust != 0) - { - if (RenderTarget != screen || viewheight == RenderTarget->GetHeight()) - { - vis->texturemid -= weapon->YAdjust; - } - else - { - vis->texturemid -= StatusBar->GetDisplacement () * weapon->YAdjust; - } - } - } - if (pspnum <= ps_flash) - { // Move the weapon down for 1280x1024. - vis->texturemid -= BaseRatioSizes[WidescreenRatio][2]; - } - vis->x1 = x1 < 0 ? 0 : x1; - vis->x2 = x2 >= viewwidth ? viewwidth : x2; - vis->xscale = FLOAT2FIXED(pspritexscale / tex->Scale.X); - vis->yscale = float(pspriteyscale / tex->Scale.Y); - vis->Translation = 0; // [RH] Use default colors - vis->pic = tex; - vis->ColormapNum = 0; - - if (flip) - { - vis->xiscale = -FLOAT2FIXED(pspritexiscale * tex->Scale.X); - vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; - } - else - { - vis->xiscale = FLOAT2FIXED(pspritexiscale * tex->Scale.X); - vis->startfrac = 0; - } - - if (vis->x1 > x1) - vis->startfrac += vis->xiscale*(vis->x1-x1); - - noaccel = false; - FDynamicColormap *colormap_to_use = NULL; - if (pspnum <= ps_flash) - { - vis->Style.Alpha = float(owner->Alpha); - vis->Style.RenderStyle = owner->RenderStyle; - - // The software renderer cannot invert the source without inverting the overlay - // too. That means if the source is inverted, we need to do the reverse of what - // the invert overlay flag says to do. - INTBOOL invertcolormap = (vis->Style.RenderStyle.Flags & STYLEF_InvertOverlay); - - if (vis->Style.RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - FDynamicColormap *mybasecolormap = basecolormap; - - if (vis->Style.RenderStyle.Flags & STYLEF_FadeToBlack) - { - if (invertcolormap) - { // Fade to white - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255,255,255), mybasecolormap->Desaturate); - invertcolormap = false; - } - else - { // Fade to black - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0,0,0), mybasecolormap->Desaturate); - } - } - - if (realfixedcolormap != NULL) - { // fixed color - vis->Style.colormap = realfixedcolormap->Colormap; - } - else - { - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - vis->Style.colormap = mybasecolormap->Maps + fixedlightlev; - } - else if (!foggy && psp->state->GetFullbright()) - { // full bright - vis->Style.colormap = mybasecolormap->Maps; // [RH] use basecolormap - } - else - { // local light - vis->Style.colormap = mybasecolormap->Maps + (GETPALOOKUP (0, spriteshade) << COLORMAPSHIFT); - } - } - if (camera->Inventory != NULL) - { - lighttable_t *oldcolormap = vis->Style.colormap; - camera->Inventory->AlterWeaponSprite (&vis->Style); - if (vis->Style.colormap != oldcolormap) - { - // The colormap has changed. Is it one we can easily identify? - // If not, then don't bother trying to identify it for - // hardware accelerated drawing. - if (vis->Style.colormap < SpecialColormaps[0].Colormap || - vis->Style.colormap > SpecialColormaps.Last().Colormap) - { - noaccel = true; - } - // Has the basecolormap changed? If so, we can't hardware accelerate it, - // since we don't know what it is anymore. - else if (vis->Style.colormap < mybasecolormap->Maps || - vis->Style.colormap >= mybasecolormap->Maps + NUMCOLORMAPS*256) - { - noaccel = true; - } - } - } - // If we're drawing with a special colormap, but shaders for them are disabled, do - // not accelerate. - if (!r_shadercolormaps && (vis->Style.colormap >= SpecialColormaps[0].Colormap && - vis->Style.colormap <= SpecialColormaps.Last().Colormap)) - { - noaccel = true; - } - // If drawing with a BOOM colormap, disable acceleration. - if (mybasecolormap == &NormalLight && NormalLight.Maps != realcolormaps) - { - noaccel = true; - } - // If the main colormap has fixed lights, and this sprite is being drawn with that - // colormap, disable acceleration so that the lights can remain fixed. - if (!noaccel && realfixedcolormap == NULL && - NormalLightHasFixedLights && mybasecolormap == &NormalLight && - vis->pic->UseBasePalette()) - { - noaccel = true; - } - colormap_to_use = mybasecolormap; - } - else - { - colormap_to_use = basecolormap; - vis->Style.colormap = basecolormap->Maps; - vis->Style.RenderStyle = STYLE_Normal; - } - - // Check for hardware-assisted 2D. If it's available, and this sprite is not - // fuzzy, don't draw it until after the switch to 2D mode. - if (!noaccel && RenderTarget == screen && (DFrameBuffer *)screen->Accel2D) - { - FRenderStyle style = vis->Style.RenderStyle; - style.CheckFuzz(); - if (style.BlendOp != STYLEOP_Fuzz) - { - VisPSpritesX1[pspnum] = x1; - VisPSpritesBaseColormap[pspnum] = colormap_to_use; - VisPSprites[pspnum] = vis; - swapvalues(avisp[pspnum], avisp[NUMPSPRITES]); - return; - } - } - R_DrawVisSprite (vis); -} - - - -//========================================================================== -// -// R_DrawPlayerSprites -// -//========================================================================== - -void R_DrawPlayerSprites () -{ - int i; - int lightnum; - pspdef_t* psp; - sector_t* sec = NULL; - static sector_t tempsec; - int floorlight, ceilinglight; - F3DFloor *rover; - - if (!r_drawplayersprites || - !camera || - !camera->player || - (players[consoleplayer].cheats & CF_CHASECAM) || - (r_deathcamera && camera->health <= 0)) - return; - - if(fixedlightlev < 0 && viewsector->e && viewsector->e->XFloor.lightlist.Size()) { - for(i = viewsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) - if(ViewPos.Z <= viewsector->e->XFloor.lightlist[i].plane.Zat0()) { - rover = viewsector->e->XFloor.lightlist[i].caster; - if(rover) { - if(rover->flags & FF_DOUBLESHADOW && ViewPos.Z <= rover->bottom.plane->Zat0()) - break; - sec = rover->model; - if(rover->flags & FF_FADEWALLS) - basecolormap = sec->ColorMap; - else - basecolormap = viewsector->e->XFloor.lightlist[i].extra_colormap; - } - break; - } - if(!sec) { - sec = viewsector; - basecolormap = sec->ColorMap; - } - floorlight = ceilinglight = sec->lightlevel; - } else { - // This used to use camera->Sector but due to interpolation that can be incorrect - // when the interpolated viewpoint is in a different sector than the camera. - sec = R_FakeFlat (viewsector, &tempsec, &floorlight, - &ceilinglight, false); - - // [RH] set basecolormap - basecolormap = sec->ColorMap; - } - - // [RH] set foggy flag - foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); - r_actualextralight = foggy ? 0 : extralight << 4; - - // get light level - lightnum = ((floorlight + ceilinglight) >> 1) + r_actualextralight; - spriteshade = LIGHT2SHADE(lightnum) - 24*FRACUNIT; - - // clip to screen bounds - mfloorclip = screenheightarray; - mceilingclip = zeroarray; - - if (camera->player != NULL) - { - double centerhack = CenterY; - float ofsx, ofsy; - - CenterY = viewheight / 2; - - P_BobWeapon (camera->player, &camera->player->psprites[ps_weapon], &ofsx, &ofsy, r_TicFracF); - - // add all active psprites - for (i = 0, psp = camera->player->psprites; - i < NUMPSPRITES; - i++, psp++) - { - // [RH] Don't draw the targeter's crosshair if the player already has a crosshair set. - if (psp->state && (i != ps_targetcenter || CrosshairImage == NULL)) - { - R_DrawPSprite (psp, i, camera, psp->sx + ofsx, psp->sy + ofsy); - } - // [RH] Don't bob the targeter. - if (i == ps_flash) - { - ofsx = ofsy = 0; - } - } - - CenterY = centerhack; - } -} - -//========================================================================== -// -// R_DrawRemainingPlayerSprites -// -// Called from D_Display to draw sprites that were not drawn by -// R_DrawPlayerSprites(). -// -//========================================================================== - -void R_DrawRemainingPlayerSprites() -{ - for (int i = 0; i < NUMPSPRITES; ++i) - { - vissprite_t *vis; - - vis = VisPSprites[i]; - VisPSprites[i] = NULL; - - if (vis != NULL) - { - FDynamicColormap *colormap = VisPSpritesBaseColormap[i]; - bool flip = vis->xiscale < 0; - FSpecialColormap *special = NULL; - PalEntry overlay = 0; - FColormapStyle colormapstyle; - bool usecolormapstyle = false; - - if (vis->Style.colormap >= SpecialColormaps[0].Colormap && - vis->Style.colormap < SpecialColormaps[SpecialColormaps.Size()].Colormap) - { - // Yuck! There needs to be a better way to store colormaps in the vissprite... :( - ptrdiff_t specialmap = (vis->Style.colormap - SpecialColormaps[0].Colormap) / sizeof(FSpecialColormap); - special = &SpecialColormaps[specialmap]; - } - else if (colormap->Color == PalEntry(255,255,255) && - colormap->Desaturate == 0) - { - overlay = colormap->Fade; - overlay.a = BYTE(((vis->Style.colormap - colormap->Maps) >> 8) * 255 / NUMCOLORMAPS); - } - else - { - usecolormapstyle = true; - colormapstyle.Color = colormap->Color; - colormapstyle.Fade = colormap->Fade; - colormapstyle.Desaturate = colormap->Desaturate; - colormapstyle.FadeLevel = ((vis->Style.colormap - colormap->Maps) >> 8) / float(NUMCOLORMAPS); - } - screen->DrawTexture(vis->pic, - viewwindowx + VisPSpritesX1[i], - viewwindowy + viewheight/2 - vis->texturemid * vis->yscale - 0.5, - DTA_DestWidthF, FIXED2DBL(vis->pic->GetWidth() * vis->xscale), - DTA_DestHeightF, vis->pic->GetHeight() * vis->yscale, - DTA_Translation, TranslationToTable(vis->Translation), - DTA_FlipX, flip, - DTA_TopOffset, 0, - DTA_LeftOffset, 0, - DTA_ClipLeft, viewwindowx, - DTA_ClipTop, viewwindowy, - DTA_ClipRight, viewwindowx + viewwidth, - DTA_ClipBottom, viewwindowy + viewheight, - DTA_AlphaF, vis->Style.Alpha, - DTA_RenderStyle, vis->Style.RenderStyle, - DTA_FillColor, vis->FillColor, - DTA_SpecialColormap, special, - DTA_ColorOverlay, overlay.d, - DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : NULL, - TAG_DONE); - } - } -} - -// -// R_SortVisSprites -// -// [RH] The old code for this function used a bubble sort, which was far less -// than optimal with large numbers of sprites. I changed it to use the -// stdlib qsort() function instead, and now it is a *lot* faster; the -// more vissprites that need to be sorted, the better the performance -// gain compared to the old function. -// -// Sort vissprites by depth, far to near - -// This is the standard version, which does a simple test based on depth. -static bool sv_compare(vissprite_t *a, vissprite_t *b) -{ - return a->idepth > b->idepth; -} - -// This is an alternate version, for when one or more voxel is in view. -// It does a 2D distance test based on whichever one is furthest from -// the viewpoint. -static bool sv_compare2d(vissprite_t *a, vissprite_t *b) -{ - return DVector2(a->deltax, a->deltay).LengthSquared() < - DVector2(b->deltax, b->deltay).LengthSquared(); -} - -#if 0 -static drawseg_t **drawsegsorter; -static int drawsegsortersize = 0; - -// Sort vissprites by leftmost column, left to right -static int sv_comparex (const void *arg1, const void *arg2) -{ - return (*(vissprite_t **)arg2)->x1 - (*(vissprite_t **)arg1)->x1; -} - -// Sort drawsegs by rightmost column, left to right -static int sd_comparex (const void *arg1, const void *arg2) -{ - return (*(drawseg_t **)arg2)->x2 - (*(drawseg_t **)arg1)->x2; -} - -CVAR (Bool, r_splitsprites, true, CVAR_ARCHIVE) - -// Split up vissprites that intersect drawsegs -void R_SplitVisSprites () -{ - size_t start, stop; - size_t numdrawsegs = ds_p - firstdrawseg; - size_t numsprites; - size_t spr, dseg, dseg2; - - if (!r_splitsprites) - return; - - if (numdrawsegs == 0 || vissprite_p - firstvissprite == 0) - return; - - // Sort drawsegs from left to right - if (numdrawsegs > drawsegsortersize) - { - if (drawsegsorter != NULL) - delete[] drawsegsorter; - drawsegsortersize = numdrawsegs * 2; - drawsegsorter = new drawseg_t *[drawsegsortersize]; - } - for (dseg = dseg2 = 0; dseg < numdrawsegs; ++dseg) - { - // Drawsegs that don't clip any sprites don't need to be considered. - if (firstdrawseg[dseg].silhouette) - { - drawsegsorter[dseg2++] = &firstdrawseg[dseg]; - } - } - numdrawsegs = dseg2; - if (numdrawsegs == 0) - { - return; - } - qsort (drawsegsorter, numdrawsegs, sizeof(drawseg_t *), sd_comparex); - - // Now sort vissprites from left to right, and walk them simultaneously - // with the drawsegs, splitting any that intersect. - start = firstvissprite - vissprites; - - int p = 0; - do - { - p++; - R_SortVisSprites (sv_comparex, start); - stop = vissprite_p - vissprites; - numsprites = stop - start; - - spr = dseg = 0; - do - { - vissprite_t *vis = spritesorter[spr], *vis2; - - // Skip drawsegs until we get to one that doesn't end before the sprite - // begins. - while (dseg < numdrawsegs && drawsegsorter[dseg]->x2 <= vis->x1) - { - dseg++; - } - // Now split the sprite against any drawsegs it intersects - for (dseg2 = dseg; dseg2 < numdrawsegs; dseg2++) - { - drawseg_t *ds = drawsegsorter[dseg2]; - - if (ds->x1 > vis->x2 || ds->x2 < vis->x1) - continue; - - if ((vis->idepth < ds->siz1) != (vis->idepth < ds->siz2)) - { // The drawseg is crossed; find the x where the intersection occurs - int cross = Scale (vis->idepth - ds->siz1, ds->sx2 - ds->sx1, ds->siz2 - ds->siz1) + ds->sx1 + 1; - -/* if (cross < ds->x1 || cross > ds->x2) - { // The original seg is crossed, but the drawseg is not - continue; - } -*/ if (cross <= vis->x1 || cross >= vis->x2) - { // Don't create 0-sized sprites - continue; - } - - vis->bSplitSprite = true; - - // Create a new vissprite for the right part of the sprite - vis2 = R_NewVisSprite (); - *vis2 = *vis; - vis2->startfrac += vis2->xiscale * (cross - vis2->x1); - vis->x2 = cross-1; - vis2->x1 = cross; - //vis2->alpha /= 2; - //vis2->RenderStyle = STYLE_Add; - - if (vis->idepth < ds->siz1) - { // Left is in back, right is in front - vis->sector = ds->curline->backsector; - vis2->sector = ds->curline->frontsector; - } - else - { // Right is in front, left is in back - vis->sector = ds->curline->frontsector; - vis2->sector = ds->curline->backsector; - } - } - } - } - while (dseg < numdrawsegs && ++spr < numsprites); - - // Repeat for any new sprites that were added. - } - while (start = stop, stop != vissprite_p - vissprites); -} -#endif - -#ifdef __GNUC__ -static void swap(vissprite_t *&a, vissprite_t *&b) -{ - vissprite_t *t = a; - a = b; - b = t; -} -#endif - -void R_SortVisSprites (bool (*compare)(vissprite_t *, vissprite_t *), size_t first) -{ - int i; - vissprite_t **spr; - - vsprcount = int(vissprite_p - &vissprites[first]); - - if (vsprcount == 0) - return; - - if (spritesortersize < MaxVisSprites) - { - if (spritesorter != NULL) - delete[] spritesorter; - spritesorter = new vissprite_t *[MaxVisSprites]; - spritesortersize = MaxVisSprites; - } - - if (!(i_compatflags & COMPATF_SPRITESORT)) - { - for (i = 0, spr = firstvissprite; i < vsprcount; i++, spr++) - { - spritesorter[i] = *spr; - } - } - else - { - // If the compatibility option is on sprites of equal distance need to - // be sorted in inverse order. This is most easily achieved by - // filling the sort array backwards before the sort. - for (i = 0, spr = firstvissprite + vsprcount-1; i < vsprcount; i++, spr--) - { - spritesorter[i] = *spr; - } - } - - std::stable_sort(&spritesorter[0], &spritesorter[vsprcount], compare); -} - -// -// R_DrawSprite -// -void R_DrawSprite (vissprite_t *spr) -{ - static short clipbot[MAXWIDTH]; - static short cliptop[MAXWIDTH]; - drawseg_t *ds; - int i; - int x1, x2; - int r1, r2; - short topclip, botclip; - short *clip1, *clip2; - lighttable_t *colormap = spr->Style.colormap; - F3DFloor *rover; - FDynamicColormap *mybasecolormap; - - // [RH] Check for particles - if (!spr->bIsVoxel && spr->pic == NULL) - { - // kg3D - reject invisible parts - if ((fake3D & FAKE3D_CLIPBOTTOM) && spr->gpos.Z <= sclipBottom) return; - if ((fake3D & FAKE3D_CLIPTOP) && spr->gpos.Z >= sclipTop) return; - R_DrawParticle (spr); - return; - } - - x1 = spr->x1; - x2 = spr->x2; - - // [RH] Quickly reject sprites with bad x ranges. - if (x1 >= x2) - return; - - // [RH] Sprites split behind a one-sided line can also be discarded. - if (spr->sector == NULL) - return; - - // kg3D - reject invisible parts - if ((fake3D & FAKE3D_CLIPBOTTOM) && spr->gzt <= sclipBottom) return; - if ((fake3D & FAKE3D_CLIPTOP) && spr->gzb >= sclipTop) return; - - // kg3D - correct colors now - if (!fixedcolormap && fixedlightlev < 0 && spr->sector->e && spr->sector->e->XFloor.lightlist.Size()) - { - if (!(fake3D & FAKE3D_CLIPTOP)) - { - sclipTop = spr->sector->ceilingplane.ZatPoint(ViewPos); - } - sector_t *sec = NULL; - for (i = spr->sector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) - { - if (sclipTop <= spr->sector->e->XFloor.lightlist[i].plane.Zat0()) - { - rover = spr->sector->e->XFloor.lightlist[i].caster; - if (rover) - { - if (rover->flags & FF_DOUBLESHADOW && sclipTop <= rover->bottom.plane->Zat0()) - { - break; - } - sec = rover->model; - if (rover->flags & FF_FADEWALLS) - { - mybasecolormap = sec->ColorMap; - } - else - { - mybasecolormap = spr->sector->e->XFloor.lightlist[i].extra_colormap; - } - } - break; - } - } - // found new values, recalculate - if (sec) - { - INTBOOL invertcolormap = (spr->Style.RenderStyle.Flags & STYLEF_InvertOverlay); - - if (spr->Style.RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - // Sprites that are added to the scene must fade to black. - if (spr->Style.RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); - } - - if (spr->Style.RenderStyle.Flags & STYLEF_FadeToBlack) - { - if (invertcolormap) - { // Fade to white - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255,255,255), mybasecolormap->Desaturate); - invertcolormap = false; - } - else - { // Fade to black - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0,0,0), mybasecolormap->Desaturate); - } - } - - // get light level - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - spr->Style.colormap = mybasecolormap->Maps + fixedlightlev; - } - else if (!foggy && (spr->renderflags & RF_FULLBRIGHT)) - { // full bright - spr->Style.colormap = mybasecolormap->Maps; - } - else - { // diminished light - spriteshade = LIGHT2SHADE(sec->lightlevel + r_actualextralight); - spr->Style.colormap = mybasecolormap->Maps + (GETPALOOKUP ( - r_SpriteVisibility / MAX(MINZ, (double)spr->depth), spriteshade) << COLORMAPSHIFT); - } - } - } - - // [RH] Initialize the clipping arrays to their largest possible range - // instead of using a special "not clipped" value. This eliminates - // visual anomalies when looking down and should be faster, too. - topclip = 0; - botclip = viewheight; - - // killough 3/27/98: - // Clip the sprite against deep water and/or fake ceilings. - // [RH] rewrote this to be based on which part of the sector is really visible - - double scale = InvZtoScale * spr->idepth; - double hzb = DBL_MIN, hzt = DBL_MAX; - - if (spr->bIsVoxel && spr->floorclip != 0) - { - hzb = spr->gzb; - } - - if (spr->heightsec && !(spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) - { // only things in specially marked sectors - if (spr->FakeFlatStat != FAKED_AboveCeiling) - { - double hz = spr->heightsec->floorplane.ZatPoint(spr->gpos); - int h = xs_RoundToInt(CenterY - (hz - ViewPos.Z) * scale); - - if (spr->FakeFlatStat == FAKED_BelowFloor) - { // seen below floor: clip top - if (!spr->bIsVoxel && h > topclip) - { - topclip = MIN (h, viewheight); - } - hzt = MIN(hzt, hz); - } - else - { // seen in the middle: clip bottom - if (!spr->bIsVoxel && h < botclip) - { - botclip = MAX (0, h); - } - hzb = MAX(hzb, hz); - } - } - if (spr->FakeFlatStat != FAKED_BelowFloor && !(spr->heightsec->MoreFlags & SECF_FAKEFLOORONLY)) - { - double hz = spr->heightsec->ceilingplane.ZatPoint(spr->gpos); - int h = xs_RoundToInt(CenterY - (hz - ViewPos.Z) * scale); - - if (spr->FakeFlatStat == FAKED_AboveCeiling) - { // seen above ceiling: clip bottom - if (!spr->bIsVoxel && h < botclip) - { - botclip = MAX (0, h); - } - hzb = MAX(hzb, hz); - } - else - { // seen in the middle: clip top - if (!spr->bIsVoxel && h > topclip) - { - topclip = MIN (h, viewheight); - } - hzt = MIN(hzt, hz); - } - } - } - // killough 3/27/98: end special clipping for deep water / fake ceilings - else if (!spr->bIsVoxel && spr->floorclip) - { // [RH] Move floorclip stuff from R_DrawVisSprite to here - //int clip = ((FLOAT2FIXED(CenterY) - FixedMul (spr->texturemid - (spr->pic->GetHeight() << FRACBITS) + spr->floorclip, spr->yscale)) >> FRACBITS); - int clip = xs_RoundToInt(CenterY - (spr->texturemid - spr->pic->GetHeight() + spr->floorclip) * spr->yscale); - if (clip < botclip) - { - botclip = MAX(0, clip); - } - } - - if (fake3D & FAKE3D_CLIPBOTTOM) - { - if (!spr->bIsVoxel) - { - double hz = sclipBottom; - if (spr->fakefloor) - { - double floorz = spr->fakefloor->top.plane->Zat0(); - if (ViewPos.Z > floorz && floorz == sclipBottom ) - { - hz = spr->fakefloor->bottom.plane->Zat0(); - } - } - int h = xs_RoundToInt(CenterY - (hz - ViewPos.Z) * scale); - if (h < botclip) - { - botclip = MAX(0, h); - } - } - hzb = MAX(hzb, sclipBottom); - } - if (fake3D & FAKE3D_CLIPTOP) - { - if (!spr->bIsVoxel) - { - double hz = sclipTop; - if (spr->fakeceiling != NULL) - { - double ceilingZ = spr->fakeceiling->bottom.plane->Zat0(); - if (ViewPos.Z < ceilingZ && ceilingZ == sclipTop) - { - hz = spr->fakeceiling->top.plane->Zat0(); - } - } - int h = xs_RoundToInt(CenterY - (hz - ViewPos.Z) * scale); - if (h > topclip) - { - topclip = MIN(h, viewheight); - } - } - hzt = MIN(hzt, sclipTop); - } - -#if 0 - // [RH] Sprites that were split by a drawseg should also be clipped - // by the sector's floor and ceiling. (Not sure how/if to handle this - // with fake floors, since those already do clipping.) - if (spr->bSplitSprite && - (spr->heightsec == NULL || (spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC))) - { - fixed_t h = spr->sector->floorplane.ZatPoint (spr->gx, spr->gy); - h = (centeryfrac - FixedMul (h-viewz, scale)) >> FRACBITS; - if (h < botclip) - { - botclip = MAX (0, h); - } - h = spr->sector->ceilingplane.ZatPoint (spr->gx, spr->gy); - h = (centeryfrac - FixedMul (h-viewz, scale)) >> FRACBITS; - if (h > topclip) - { - topclip = MIN (h, viewheight); - } - } -#endif - - if (topclip >= botclip) - { - spr->Style.colormap = colormap; - return; - } - - i = x2 - x1; - clip1 = clipbot + x1; - clip2 = cliptop + x1; - do - { - *clip1++ = botclip; - *clip2++ = topclip; - } while (--i); - - // Scan drawsegs from end to start for obscuring segs. - // The first drawseg that is closer than the sprite is the clip seg. - - // Modified by Lee Killough: - // (pointer check was originally nonportable - // and buggy, by going past LEFT end of array): - - // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code - - for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough - { - // [ZZ] portal handling here - //if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) - // continue; - // [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping - - // kg3D - no clipping on fake segs - if (ds->fake) continue; - // determine if the drawseg obscures the sprite - if (ds->x1 >= x2 || ds->x2 <= x1 || - (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == -1 && - !ds->bFogBoundary) ) - { - // does not cover sprite - continue; - } - - r1 = MAX (ds->x1, x1); - r2 = MIN (ds->x2, x2); - - float neardepth, fardepth; - if (!spr->bWallSprite) - { - if (ds->sz1 < ds->sz2) - { - neardepth = ds->sz1, fardepth = ds->sz2; - } - else - { - neardepth = ds->sz2, fardepth = ds->sz1; - } - } - // Check if sprite is in front of draw seg: - if ((!spr->bWallSprite && neardepth > spr->depth) || ((spr->bWallSprite || fardepth > spr->depth) && - (spr->gpos.Y - ds->curline->v1->fY()) * (ds->curline->v2->fX() - ds->curline->v1->fX()) - - (spr->gpos.X - ds->curline->v1->fX()) * (ds->curline->v2->fY() - ds->curline->v1->fY()) <= 0)) - { - // seg is behind sprite, so draw the mid texture if it has one - if (ds->CurrentPortalUniq == CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here - (ds->maskedtexturecol != -1 || ds->bFogBoundary)) - R_RenderMaskedSegRange (ds, r1, r2); - continue; - } - - // clip this piece of the sprite - // killough 3/27/98: optimized and made much shorter - // [RH] Optimized further (at least for VC++; - // other compilers should be at least as good as before) - - if (ds->silhouette & SIL_BOTTOM) //bottom sil - { - clip1 = clipbot + r1; - clip2 = openings + ds->sprbottomclip + r1 - ds->x1; - i = r2 - r1; - do - { - if (*clip1 > *clip2) - *clip1 = *clip2; - clip1++; - clip2++; - } while (--i); - } - - if (ds->silhouette & SIL_TOP) // top sil - { - clip1 = cliptop + r1; - clip2 = openings + ds->sprtopclip + r1 - ds->x1; - i = r2 - r1; - do - { - if (*clip1 < *clip2) - *clip1 = *clip2; - clip1++; - clip2++; - } while (--i); - } - } - - // all clipping has been performed, so draw the sprite - - if (!spr->bIsVoxel) - { - mfloorclip = clipbot; - mceilingclip = cliptop; - if (!spr->bWallSprite) - { - R_DrawVisSprite(spr); - } - else - { - R_DrawWallSprite(spr); - } - } - else - { - // If it is completely clipped away, don't bother drawing it. - if (cliptop[x2] >= clipbot[x2]) - { - for (i = x1; i < x2; ++i) - { - if (cliptop[i] < clipbot[i]) - { - break; - } - } - if (i == x2) - { - spr->Style.colormap = colormap; - return; - } - } - // Add everything outside the left and right edges to the clipping array - // for R_DrawVisVoxel(). - if (x1 > 0) - { - clearbufshort(cliptop, x1, viewheight); - } - if (x2 < viewwidth - 1) - { - clearbufshort(cliptop + x2, viewwidth - x2, viewheight); - } - int minvoxely = spr->gzt <= hzt ? 0 : xs_RoundToInt((spr->gzt - hzt) / spr->yscale); - int maxvoxely = spr->gzb > hzb ? INT_MAX : xs_RoundToInt((spr->gzt - hzb) / spr->yscale); - R_DrawVisVoxel(spr, minvoxely, maxvoxely, cliptop, clipbot); - } - spr->Style.colormap = colormap; -} - -// kg3D: -// R_DrawMasked contains sorting -// original renamed to R_DrawMaskedSingle - -void R_DrawMaskedSingle (bool renew) -{ - drawseg_t *ds; - int i; - -#if 0 - R_SplitVisSprites (); -#endif - - for (i = vsprcount; i > 0; i--) - { - if (spritesorter[i-1]->CurrentPortalUniq != CurrentPortalUniq) - continue; // probably another time - R_DrawSprite (spritesorter[i-1]); - } - - // render any remaining masked mid textures - - // Modified by Lee Killough: - // (pointer check was originally nonportable - // and buggy, by going past LEFT end of array): - - // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code - - if (renew) - { - fake3D |= FAKE3D_REFRESHCLIP; - } - for (ds = ds_p; ds-- > firstdrawseg; ) // new -- killough - { - // [ZZ] the same as above - if (ds->CurrentPortalUniq != CurrentPortalUniq) - continue; - // kg3D - no fake segs - if (ds->fake) continue; - if (ds->maskedtexturecol != -1 || ds->bFogBoundary) - { - R_RenderMaskedSegRange (ds, ds->x1, ds->x2); - } - } -} - -void R_DrawHeightPlanes(double height); // kg3D - fake planes - -void R_DrawMasked (void) -{ - R_CollectPortals(); - R_SortVisSprites (DrewAVoxel ? sv_compare2d : sv_compare, firstvissprite - vissprites); - - if (height_top == NULL) - { // kg3D - no visible 3D floors, normal rendering - R_DrawMaskedSingle(false); - } - else - { // kg3D - correct sorting - HeightLevel *hl; - - // ceilings - for (hl = height_cur; hl != NULL && hl->height >= ViewPos.Z; hl = hl->prev) - { - if (hl->next) - { - fake3D = FAKE3D_CLIPBOTTOM | FAKE3D_CLIPTOP; - sclipTop = hl->next->height; - } - else - { - fake3D = FAKE3D_CLIPBOTTOM; - } - sclipBottom = hl->height; - R_DrawMaskedSingle(true); - R_DrawHeightPlanes(hl->height); - } - - // floors - fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPTOP; - sclipTop = height_top->height; - R_DrawMaskedSingle(true); - hl = height_top; - for (hl = height_top; hl != NULL && hl->height < ViewPos.Z; hl = hl->next) - { - R_DrawHeightPlanes(hl->height); - if (hl->next) - { - fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPTOP | FAKE3D_CLIPBOTTOM; - sclipTop = hl->next->height; - } - else - { - fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPBOTTOM; - } - sclipBottom = hl->height; - R_DrawMaskedSingle(true); - } - R_3D_DeleteHeights(); - fake3D = 0; - } - R_DrawPlayerSprites (); -} - - -void R_ProjectParticle (particle_t *particle, const sector_t *sector, int shade, int fakeside) -{ - double tr_x, tr_y; - double tx, ty; - double tz, tiz; - double xscale, yscale; - int x1, x2, y1, y2; - vissprite_t* vis; - sector_t* heightsec = NULL; - BYTE* map; - - // [ZZ] Particle not visible through the portal plane - if (CurrentPortal && !!P_PointOnLineSide(particle->Pos, CurrentPortal->dst)) - return; - - // transform the origin point - tr_x = particle->Pos.X - ViewPos.X; - tr_y = particle->Pos.Y - ViewPos.Y; - - tz = tr_x * ViewTanCos + tr_y * ViewTanSin; - - // particle is behind view plane? - if (tz < MINZ) - return; - - tx = tr_x * ViewSin - tr_y * ViewCos; - - // Flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - tx = viewwidth - tx - 1; - } - - // too far off the side? - if (tz <= fabs(tx)) - return; - - tiz = 1 / tz; - xscale = centerx * tiz; - - // calculate edges of the shape - double psize = particle->size / 8.0; - - x1 = MAX(WindowLeft, centerx + xs_RoundToInt((tx - psize) * xscale)); - x2 = MIN(WindowRight, centerx + xs_RoundToInt((tx + psize) * xscale)); - - if (x1 >= x2) - return; - - yscale = xs_RoundToInt(YaspectMul * xscale); - ty = FLOAT2FIXED(particle->Pos.Z - ViewPos.Z); - y1 = xs_RoundToInt(CenterY - (ty + psize) * yscale); - y2 = xs_RoundToInt(CenterY - (ty - psize) * yscale); - - // Clip the particle now. Because it's a point and projected as its subsector is - // entered, we don't need to clip it to drawsegs like a normal sprite. - - // Clip particles behind walls. - if (y1 < ceilingclip[x1]) y1 = ceilingclip[x1]; - if (y1 < ceilingclip[x2-1]) y1 = ceilingclip[x2-1]; - if (y2 >= floorclip[x1]) y2 = floorclip[x1] - 1; - if (y2 >= floorclip[x2-1]) y2 = floorclip[x2-1] - 1; - - if (y1 > y2) - return; - - // Clip particles above the ceiling or below the floor. - heightsec = sector->GetHeightSec(); - - const secplane_t *topplane; - const secplane_t *botplane; - FTextureID toppic; - FTextureID botpic; - - if (heightsec) // only clip things which are in special sectors - { - if (fakeside == FAKED_AboveCeiling) - { - topplane = §or->ceilingplane; - botplane = &heightsec->ceilingplane; - toppic = sector->GetTexture(sector_t::ceiling); - botpic = heightsec->GetTexture(sector_t::ceiling); - map = heightsec->ColorMap->Maps; - } - else if (fakeside == FAKED_BelowFloor) - { - topplane = &heightsec->floorplane; - botplane = §or->floorplane; - toppic = heightsec->GetTexture(sector_t::floor); - botpic = sector->GetTexture(sector_t::floor); - map = heightsec->ColorMap->Maps; - } - else - { - topplane = &heightsec->ceilingplane; - botplane = &heightsec->floorplane; - toppic = heightsec->GetTexture(sector_t::ceiling); - botpic = heightsec->GetTexture(sector_t::floor); - map = sector->ColorMap->Maps; - } - } - else - { - topplane = §or->ceilingplane; - botplane = §or->floorplane; - toppic = sector->GetTexture(sector_t::ceiling); - botpic = sector->GetTexture(sector_t::floor); - map = sector->ColorMap->Maps; - } - - if (botpic != skyflatnum && particle->Pos.Z < botplane->ZatPoint (particle->Pos)) - return; - if (toppic != skyflatnum && particle->Pos.Z >= topplane->ZatPoint (particle->Pos)) - return; - - // store information in a vissprite - vis = R_NewVisSprite (); - vis->CurrentPortalUniq = CurrentPortalUniq; - vis->heightsec = heightsec; - vis->xscale = FLOAT2FIXED(xscale); - vis->yscale = (float)xscale; -// vis->yscale *= InvZtoScale; - vis->depth = (float)tz; - vis->idepth = float(1 / tz); - vis->gpos = { (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z }; - vis->y1 = y1; - vis->y2 = y2; - vis->x1 = x1; - vis->x2 = x2; - vis->Translation = 0; - vis->startfrac = 255 & (particle->color >>24); - vis->pic = NULL; - vis->bIsVoxel = false; - vis->renderflags = particle->trans; - vis->FakeFlatStat = fakeside; - vis->floorclip = 0; - vis->ColormapNum = 0; - - if (fixedlightlev >= 0) - { - vis->Style.colormap = map + fixedlightlev; - } - else if (fixedcolormap) - { - vis->Style.colormap = fixedcolormap; - } - else if (particle->bright) - { - vis->Style.colormap = map; - } - else - { - // Particles are slightly more visible than regular sprites. - vis->ColormapNum = GETPALOOKUP(tiz * r_SpriteVisibility * 0.5, shade); - vis->Style.colormap = map + (vis->ColormapNum << COLORMAPSHIFT); - } -} - -static void R_DrawMaskedSegsBehindParticle (const vissprite_t *vis) -{ - const int x1 = vis->x1; - const int x2 = vis->x2; - - // Draw any masked textures behind this particle so that when the - // particle is drawn, it will be in front of them. - for (unsigned int p = InterestingDrawsegs.Size(); p-- > FirstInterestingDrawseg; ) - { - drawseg_t *ds = &drawsegs[InterestingDrawsegs[p]]; - // kg3D - no fake segs - if(ds->fake) continue; - if (ds->x1 >= x2 || ds->x2 <= x1) - { - continue; - } - if ((ds->siz2 - ds->siz1) * ((x2 + x1)/2 - ds->sx1) / (ds->sx2 - ds->sx1) + ds->siz1 < vis->idepth) - { - // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves - if (ds->CurrentPortalUniq == vis->CurrentPortalUniq) - R_RenderMaskedSegRange (ds, MAX(ds->x1, x1), MIN(ds->x2, x2)); - } - } -} - -void R_DrawParticle (vissprite_t *vis) -{ - DWORD *bg2rgb; - int spacing; - BYTE *dest; - DWORD fg; - BYTE color = vis->Style.colormap[vis->startfrac]; - int yl = vis->y1; - int ycount = vis->y2 - yl + 1; - int x1 = vis->x1; - int countbase = vis->x2 - x1; - - R_DrawMaskedSegsBehindParticle (vis); - - // vis->renderflags holds translucency level (0-255) - { - fixed_t fglevel, bglevel; - DWORD *fg2rgb; - - fglevel = ((vis->renderflags + 1) << 8) & ~0x3ff; - bglevel = FRACUNIT-fglevel; - fg2rgb = Col2RGB8[fglevel>>10]; - bg2rgb = Col2RGB8[bglevel>>10]; - fg = fg2rgb[color]; - } - - /* - - spacing = RenderTarget->GetPitch() - countbase; - dest = ylookup[yl] + x1 + dc_destorg; - - do - { - int count = countbase; - do - { - DWORD bg = bg2rgb[*dest]; - bg = (fg+bg) | 0x1f07c1f; - *dest++ = RGB32k.All[bg & (bg>>15)]; - } while (--count); - dest += spacing; - } while (--ycount);*/ - - // original was row-wise - // width = countbase - // height = ycount - - spacing = RenderTarget->GetPitch(); - - for (int x = x1; x < (x1+countbase); x++) - { - dc_x = x; - if (R_ClipSpriteColumnWithPortals(vis)) - continue; - dest = ylookup[yl] + x + dc_destorg; - for (int y = 0; y < ycount; y++) - { - DWORD bg = bg2rgb[*dest]; - bg = (fg+bg) | 0x1f07c1f; - *dest = RGB32k.All[bg & (bg>>15)]; - dest += spacing; - } - } -} - -extern double BaseYaspectMul;; - -void R_DrawVoxel(const FVector3 &globalpos, FAngle viewangle, - const FVector3 &dasprpos, angle_t dasprang, - fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, - lighttable_t *colormap, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags) -{ - int i, j, k, x, y, syoff, ggxstart, ggystart, nxoff; - fixed_t cosang, sinang, sprcosang, sprsinang; - int backx, backy, gxinc, gyinc; - int daxscalerecip, dayscalerecip, cnt, gxstart, gystart, dazscale; - int lx, rx, nx, ny, x1=0, y1=0, x2=0, y2=0, yinc=0; - int yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc; - kvxslab_t *voxptr, *voxend; - FVoxelMipLevel *mip; - int z1a[64], z2a[64], yplc[64]; - - const int nytooclose = centerxwide * 2100, nytoofar = 32768*32768 - 1048576; - const int xdimenscale = FLOAT2FIXED(centerxwide * YaspectMul / 160); - const double centerxwide_f = centerxwide; - const double centerxwidebig_f = centerxwide_f * 65536*65536*8; - - // Convert to Build's coordinate system. - fixed_t globalposx = xs_Fix<4>::ToFix(globalpos.X); - fixed_t globalposy = xs_Fix<4>::ToFix(-globalpos.Y); - fixed_t globalposz = xs_Fix<8>::ToFix(-globalpos.Z); - - fixed_t dasprx = xs_Fix<4>::ToFix(dasprpos.X); - fixed_t daspry = xs_Fix<4>::ToFix(-dasprpos.Y); - fixed_t dasprz = xs_Fix<8>::ToFix(-dasprpos.Z); - - // Shift the scales from 16 bits of fractional precision to 6. - // Also do some magic voodoo scaling to make them the right size. - daxscale = daxscale / (0xC000 >> 6); - dayscale = dayscale / (0xC000 >> 6); - - angle_t viewang = viewangle.BAMs(); - cosang = finecosine[viewang >> ANGLETOFINESHIFT] >> 2; - sinang = -finesine[viewang >> ANGLETOFINESHIFT] >> 2; - sprcosang = finecosine[dasprang >> ANGLETOFINESHIFT] >> 2; - sprsinang = -finesine[dasprang >> ANGLETOFINESHIFT] >> 2; - - R_SetupDrawSlab(colormap); - - // Select mip level - i = abs(DMulScale6(dasprx - globalposx, cosang, daspry - globalposy, sinang)); - i = DivScale6(i, MIN(daxscale, dayscale)); - j = xs_Fix<13>::ToFix(FocalLengthX); - for (k = 0; i >= j && k < voxobj->NumMips; ++k) - { - i >>= 1; - } - if (k >= voxobj->NumMips) k = voxobj->NumMips - 1; - - mip = &voxobj->Mips[k]; if (mip->SlabData == NULL) return; - - minslabz >>= k; - maxslabz >>= k; - - daxscale <<= (k+8); dayscale <<= (k+8); - dazscale = FixedDiv(dayscale, FLOAT2FIXED(BaseYaspectMul)); - daxscale = fixed_t(daxscale / YaspectMul); - daxscale = Scale(daxscale, xdimenscale, centerxwide << 9); - dayscale = Scale(dayscale, FixedMul(xdimenscale, viewingrangerecip), centerxwide << 9); - - daxscalerecip = (1<<30) / daxscale; - dayscalerecip = (1<<30) / dayscale; - - fixed_t piv_x = fixed_t(mip->Pivot.X*256.); - fixed_t piv_y = fixed_t(mip->Pivot.Y*256.); - fixed_t piv_z = fixed_t(mip->Pivot.Z*256.); - - x = FixedMul(globalposx - dasprx, daxscalerecip); - y = FixedMul(globalposy - daspry, daxscalerecip); - backx = (DMulScale10(x, sprcosang, y, sprsinang) + piv_x) >> 8; - backy = (DMulScale10(y, sprcosang, x, -sprsinang) + piv_y) >> 8; - cbackx = clamp(backx, 0, mip->SizeX - 1); - cbacky = clamp(backy, 0, mip->SizeY - 1); - - sprcosang = MulScale14(daxscale, sprcosang); - sprsinang = MulScale14(daxscale, sprsinang); - - x = (dasprx - globalposx) - DMulScale18(piv_x, sprcosang, piv_y, -sprsinang); - y = (daspry - globalposy) - DMulScale18(piv_y, sprcosang, piv_x, sprsinang); - - cosang = FixedMul(cosang, dayscalerecip); - sinang = FixedMul(sinang, dayscalerecip); - - gxstart = y*cosang - x*sinang; - gystart = x*cosang + y*sinang; - gxinc = DMulScale10(sprsinang, cosang, sprcosang, -sinang); - gyinc = DMulScale10(sprcosang, cosang, sprsinang, sinang); - if ((abs(globalposz - dasprz) >> 10) >= abs(dazscale)) return; - - x = 0; y = 0; j = MAX(mip->SizeX, mip->SizeY); - fixed_t *ggxinc = (fixed_t *)alloca((j + 1) * sizeof(fixed_t) * 2); - fixed_t *ggyinc = ggxinc + (j + 1); - for (i = 0; i <= j; i++) - { - ggxinc[i] = x; x += gxinc; - ggyinc[i] = y; y += gyinc; - } - - syoff = DivScale21(globalposz - dasprz, FixedMul(dazscale, 0xE800)) + (piv_z << 7); - yoff = (abs(gxinc) + abs(gyinc)) >> 1; - - for (cnt = 0; cnt < 8; cnt++) - { - switch (cnt) - { - case 0: xs = 0; ys = 0; xi = 1; yi = 1; break; - case 1: xs = mip->SizeX-1; ys = 0; xi = -1; yi = 1; break; - case 2: xs = 0; ys = mip->SizeY-1; xi = 1; yi = -1; break; - case 3: xs = mip->SizeX-1; ys = mip->SizeY-1; xi = -1; yi = -1; break; - case 4: xs = 0; ys = cbacky; xi = 1; yi = 2; break; - case 5: xs = mip->SizeX-1; ys = cbacky; xi = -1; yi = 2; break; - case 6: xs = cbackx; ys = 0; xi = 2; yi = 1; break; - case 7: xs = cbackx; ys = mip->SizeY-1; xi = 2; yi = -1; break; - } - xe = cbackx; ye = cbacky; - if (cnt < 4) - { - if ((xi < 0) && (xe >= xs)) continue; - if ((xi > 0) && (xe <= xs)) continue; - if ((yi < 0) && (ye >= ys)) continue; - if ((yi > 0) && (ye <= ys)) continue; - } - else - { - if ((xi < 0) && (xe > xs)) continue; - if ((xi > 0) && (xe < xs)) continue; - if ((yi < 0) && (ye > ys)) continue; - if ((yi > 0) && (ye < ys)) continue; - xe += xi; ye += yi; - } - - i = ksgn(ys-backy)+ksgn(xs-backx)*3+4; - switch(i) - { - case 6: case 7: x1 = 0; y1 = 0; break; - case 8: case 5: x1 = gxinc; y1 = gyinc; break; - case 0: case 3: x1 = gyinc; y1 = -gxinc; break; - case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break; - } - switch(i) - { - case 2: case 5: x2 = 0; y2 = 0; break; - case 0: case 1: x2 = gxinc; y2 = gyinc; break; - case 8: case 7: x2 = gyinc; y2 = -gxinc; break; - case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break; - } - BYTE oand = (1 << int(xs 0) { dagxinc = gxinc; dagyinc = FixedMul(gyinc, viewingrangerecip); } - else { dagxinc = -gxinc; dagyinc = -FixedMul(gyinc, viewingrangerecip); } - - /* Fix for non 90 degree viewing ranges */ - nxoff = FixedMul(x2 - x1, viewingrangerecip); - x1 = FixedMul(x1, viewingrangerecip); - - ggxstart = gxstart + ggyinc[ys]; - ggystart = gystart - ggxinc[ys]; - - for (x = xs; x != xe; x += xi) - { - BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; - short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; - - nx = FixedMul(ggxstart + ggxinc[x], viewingrangerecip) + x1; - ny = ggystart + ggyinc[x]; - for (y = ys; y != ye; y += yi, nx += dagyinc, ny -= dagxinc) - { - if ((ny <= nytooclose) || (ny >= nytoofar)) continue; - voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); - voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); - if (voxptr >= voxend) continue; - - lx = xs_RoundToInt(nx * centerxwide_f / (ny + y1)) + centerx; - if (lx < 0) lx = 0; - rx = xs_RoundToInt((nx + nxoff) * centerxwide_f / (ny + y2)) + centerx; - if (rx > viewwidth) rx = viewwidth; - if (rx <= lx) continue; - - if (flags & DVF_MIRRORED) - { - int t = viewwidth - lx; - lx = viewwidth - rx; - rx = t; - } - - fixed_t l1 = xs_RoundToInt(centerxwidebig_f / (ny - yoff)); - fixed_t l2 = xs_RoundToInt(centerxwidebig_f / (ny + yoff)); - for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3)) - { - const BYTE *col = voxptr->col; - int zleng = voxptr->zleng; - int ztop = voxptr->ztop; - fixed_t z1, z2; - - if (ztop < minslabz) - { - int diff = minslabz - ztop; - ztop = minslabz; - col += diff; - zleng -= diff; - } - if (ztop + zleng > maxslabz) - { - int diff = ztop + zleng - maxslabz; - zleng -= diff; - } - if (zleng <= 0) continue; - - j = (ztop << 15) - syoff; - if (j < 0) - { - k = j + (zleng << 15); - if (k < 0) - { - if ((voxptr->backfacecull & oand32) == 0) continue; - z2 = MulScale32(l2, k) + centery; /* Below slab */ - } - else - { - if ((voxptr->backfacecull & oand) == 0) continue; /* Middle of slab */ - z2 = MulScale32(l1, k) + centery; - } - z1 = MulScale32(l1, j) + centery; - } - else - { - if ((voxptr->backfacecull & oand16) == 0) continue; - z1 = MulScale32(l2, j) + centery; /* Above slab */ - z2 = MulScale32(l1, j + (zleng << 15)) + centery; - } - - if (z2 <= z1) continue; - - if (zleng == 1) - { - yinc = 0; - } - else - { - if (z2-z1 >= 1024) yinc = FixedDiv(zleng, z2 - z1); - else yinc = (((1 << 24) - 1) / (z2 - z1)) * zleng >> 8; - } - // [RH] Clip each column separately, not just by the first one. - for (int stripwidth = MIN(countof(z1a), rx - lx), lxt = lx; - lxt < rx; - (lxt += countof(z1a)), stripwidth = MIN(countof(z1a), rx - lxt)) - { - // Calculate top and bottom pixels locations - for (int xxx = 0; xxx < stripwidth; ++xxx) - { - if (zleng == 1) - { - yplc[xxx] = 0; - z1a[xxx] = MAX(z1, daumost[lxt + xxx]); - } - else - { - if (z1 < daumost[lxt + xxx]) - { - yplc[xxx] = yinc * (daumost[lxt + xxx] - z1); - z1a[xxx] = daumost[lxt + xxx]; - } - else - { - yplc[xxx] = 0; - z1a[xxx] = z1; - } - } - z2a[xxx] = MIN(z2, dadmost[lxt + xxx]); - } - // Find top and bottom pixels that match and draw them as one strip - for (int xxl = 0, xxr; xxl < stripwidth; ) - { - if (z1a[xxl] >= z2a[xxl]) - { // No column here - xxl++; - continue; - } - int z1 = z1a[xxl]; - int z2 = z2a[xxl]; - // How many columns share the same extents? - for (xxr = xxl + 1; xxr < stripwidth; ++xxr) - { - if (z1a[xxr] != z1 || z2a[xxr] != z2) - break; - } - - if (!(flags & DVF_OFFSCREEN)) - { - // Draw directly to the screen. - R_DrawSlab(xxr - xxl, yplc[xxl], z2 - z1, yinc, col, ylookup[z1] + lxt + xxl + dc_destorg); - } - else - { - // Record the area covered and possibly draw to an offscreen buffer. - dc_yl = z1; - dc_yh = z2 - 1; - dc_count = z2 - z1; - dc_iscale = yinc; - for (int x = xxl; x < xxr; ++x) - { - OffscreenCoverageBuffer->InsertSpan(lxt + x, z1, z2); - if (!(flags & DVF_SPANSONLY)) - { - dc_x = lxt + x; - rt_initcols(OffscreenColorBuffer + (dc_x & ~3) * OffscreenBufferHeight); - dc_source = col; - dc_texturefrac = yplc[xxl]; - hcolfunc_pre(); - } - } - } - xxl = xxr; - } - } - } - } - } - } -} - -//========================================================================== -// -// FCoverageBuffer Constructor -// -//========================================================================== - -FCoverageBuffer::FCoverageBuffer(int lists) - : Spans(NULL), FreeSpans(NULL) -{ - NumLists = lists; - Spans = new Span *[lists]; - memset(Spans, 0, sizeof(Span*)*lists); -} - -//========================================================================== -// -// FCoverageBuffer Destructor -// -//========================================================================== - -FCoverageBuffer::~FCoverageBuffer() -{ - if (Spans != NULL) - { - delete[] Spans; - } -} - -//========================================================================== -// -// FCoverageBuffer :: Clear -// -//========================================================================== - -void FCoverageBuffer::Clear() -{ - SpanArena.FreeAll(); - memset(Spans, 0, sizeof(Span*)*NumLists); - FreeSpans = NULL; -} - -//========================================================================== -// -// FCoverageBuffer :: InsertSpan -// -// start is inclusive. -// stop is exclusive. -// -//========================================================================== - -void FCoverageBuffer::InsertSpan(int listnum, int start, int stop) -{ - assert(unsigned(listnum) < NumLists); - assert(start < stop); - - Span **span_p = &Spans[listnum]; - Span *span; - - if (*span_p == NULL || (*span_p)->Start > stop) - { // This list is empty or the first entry is after this one, so we can just insert the span. - goto addspan; - } - - // Insert the new span in order, merging with existing ones. - while (*span_p != NULL) - { - if ((*span_p)->Stop < start) // ===== (existing span) - { // Span ends before this one starts. // ++++ (new span) - span_p = &(*span_p)->NextSpan; - continue; - } - - // Does the new span overlap or abut the existing one? - if ((*span_p)->Start <= start) - { - if ((*span_p)->Stop >= stop) // ============= - { // The existing span completely covers this one. // +++++ - return; - } -extend: // Extend the existing span with the new one. // ====== - span = *span_p; // +++++++ - span->Stop = stop; // (or) +++++ - - // Free up any spans we just covered up. - span_p = &(*span_p)->NextSpan; - while (*span_p != NULL && (*span_p)->Start <= stop && (*span_p)->Stop <= stop) - { - Span *span = *span_p; // ====== ====== - *span_p = span->NextSpan; // +++++++++++++ - span->NextSpan = FreeSpans; - FreeSpans = span; - } - if (*span_p != NULL && (*span_p)->Start <= stop) // ======= ======== - { // Our new span connects two existing spans. // ++++++++++++++ - // They should all be collapsed into a single span. - span->Stop = (*span_p)->Stop; - span = *span_p; - *span_p = span->NextSpan; - span->NextSpan = FreeSpans; - FreeSpans = span; - } - goto check; - } - else if ((*span_p)->Start <= stop) // ===== - { // The new span extends the existing span from // ++++ - // the beginning. // (or) ++++ - (*span_p)->Start = start; - if ((*span_p)->Stop < stop) - { // The new span also extends the existing span // ====== - // at the bottom // ++++++++++++++ - goto extend; - } - goto check; - } - else // ====== - { // No overlap, so insert a new span. // +++++ - goto addspan; - } - } - // Append a new span to the end of the list. -addspan: - span = AllocSpan(); - span->NextSpan = *span_p; - span->Start = start; - span->Stop = stop; - *span_p = span; -check: -#ifdef _DEBUG - // Validate the span list: Spans must be in order, and there must be - // at least one pixel between spans. - for (span = Spans[listnum]; span != NULL; span = span->NextSpan) - { - assert(span->Start < span->Stop); - if (span->NextSpan != NULL) - { - assert(span->Stop < span->NextSpan->Start); - } - } -#endif - ; -} - -//========================================================================== -// -// FCoverageBuffer :: AllocSpan -// -//========================================================================== - -FCoverageBuffer::Span *FCoverageBuffer::AllocSpan() -{ - Span *span; - - if (FreeSpans != NULL) - { - span = FreeSpans; - FreeSpans = span->NextSpan; - } - else - { - span = (Span *)SpanArena.Alloc(sizeof(Span)); - } - return span; -} - -//========================================================================== -// -// R_CheckOffscreenBuffer -// -// Allocates the offscreen coverage buffer and optionally the offscreen -// color buffer. If they already exist but are the wrong size, they will -// be reallocated. -// -//========================================================================== - -void R_CheckOffscreenBuffer(int width, int height, bool spansonly) -{ - if (OffscreenCoverageBuffer == NULL) - { - assert(OffscreenColorBuffer == NULL && "The color buffer cannot exist without the coverage buffer"); - OffscreenCoverageBuffer = new FCoverageBuffer(width); - } - else if (OffscreenCoverageBuffer->NumLists != (unsigned)width) - { - delete OffscreenCoverageBuffer; - OffscreenCoverageBuffer = new FCoverageBuffer(width); - if (OffscreenColorBuffer != NULL) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = NULL; - } - } - else - { - OffscreenCoverageBuffer->Clear(); - } - - if (!spansonly) - { - if (OffscreenColorBuffer == NULL) - { - OffscreenColorBuffer = new BYTE[width * height]; - } - else if (OffscreenBufferWidth != width || OffscreenBufferHeight != height) - { - delete[] OffscreenColorBuffer; - OffscreenColorBuffer = new BYTE[width * height]; - } - } - OffscreenBufferWidth = width; - OffscreenBufferHeight = height; -} diff --git a/src/r_things.h b/src/r_things.h deleted file mode 100644 index 97010713a..000000000 --- a/src/r_things.h +++ /dev/null @@ -1,145 +0,0 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ -// -// Copyright (C) 1993-1996 by id Software, Inc. -// -// This source is available for distribution and/or modification -// only under the terms of the DOOM Source Code License as -// published by id Software. All rights reserved. -// -// The source is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License -// for more details. -// -// DESCRIPTION: -// Rendering of moving objects, sprites. -// -//----------------------------------------------------------------------------- - - -#ifndef __R_THINGS__ -#define __R_THINGS__ - -#include "r_bsp.h" - -// A vissprite_t is a thing -// that will be drawn during a refresh. -// I.e. a sprite object that is partly visible. - -struct vissprite_t -{ - struct posang - { - FVector3 vpos; // view origin - FAngle vang; // view angle - }; - - short x1, x2; - FVector3 gpos; // origin in world coordinates - union - { - float gzb, gzt; // global bottom / top for silhouette clipping - int y1, y2; // top / bottom of particle on screen - }; - angle_t angle; - fixed_t xscale; - float yscale; - float depth; - float idepth; // 1/z - float deltax, deltay; - DWORD FillColor; - double floorclip; - union - { - FTexture *pic; - struct FVoxel *voxel; - }; - union - { - // Used by face sprites - struct - { - double texturemid; - fixed_t startfrac; // horizontal position of x1 - fixed_t xiscale; // negative if flipped - }; - // Used by wall sprites - FWallCoords wallc; - // Used by voxels - posang pa; - }; - sector_t *heightsec; // killough 3/27/98: height sector for underwater/fake ceiling - sector_t *sector; // [RH] sector this sprite is in - F3DFloor *fakefloor; - F3DFloor *fakeceiling; - BYTE bIsVoxel:1; // [RH] Use voxel instead of pic - BYTE bWallSprite:1; // [RH] This is a wall sprite - BYTE bSplitSprite:1; // [RH] Sprite was split by a drawseg - BYTE bInMirror:1; // [RH] Sprite is "inside" a mirror - BYTE FakeFlatStat; // [RH] which side of fake/floor ceiling sprite is on - BYTE ColormapNum; // Which colormap is rendered (needed for shaded drawer) - short renderflags; - DWORD Translation; // [RH] for color translation - visstyle_t Style; - int CurrentPortalUniq; // [ZZ] to identify the portal that this thing is in. used for clipping. - - vissprite_t() {} -}; - -struct particle_t; - -void R_DrawParticle (vissprite_t *); -void R_ProjectParticle (particle_t *, const sector_t *sector, int shade, int fakeside); - -extern int MaxVisSprites; - -extern vissprite_t **vissprites, **firstvissprite; -extern vissprite_t **vissprite_p; - -// Constant arrays used for psprite clipping -// and initializing clipping. -extern short zeroarray[MAXWIDTH]; -extern short screenheightarray[MAXWIDTH]; - -// vars for R_DrawMaskedColumn -extern short* mfloorclip; -extern short* mceilingclip; -extern double spryscale; -extern double sprtopscreen; -extern bool sprflipvert; - -extern double pspritexscale; -extern double pspritexiscale; -extern double pspriteyscale; - -extern FTexture *WallSpriteTile; - - -void R_DrawMaskedColumn (const BYTE *column, const FTexture::Span *spans); -void R_WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans)); - -void R_CacheSprite (spritedef_t *sprite); -void R_SortVisSprites (int (*compare)(const void *, const void *), size_t first); -void R_AddSprites (sector_t *sec, int lightlevel, int fakeside); -void R_AddPSprites (); -void R_DrawSprites (); -void R_ClearSprites (); -void R_DrawMasked (); -void R_DrawRemainingPlayerSprites (); - -void R_CheckOffscreenBuffer(int width, int height, bool spansonly); - -enum { DVF_OFFSCREEN = 1, DVF_SPANSONLY = 2, DVF_MIRRORED = 4 }; - -void R_DrawVoxel(const FVector3 &viewpos, FAngle viewangle, - const FVector3 &sprpos, angle_t dasprang, - fixed_t daxscale, fixed_t dayscale, struct FVoxel *voxobj, - lighttable_t *colormap, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags); - -void R_ClipVisSprite (vissprite_t *vis, int xl, int xh); - - -#endif diff --git a/src/r_utility.cpp b/src/r_utility.cpp index 15c68c3ab..4d8e1d891 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -102,8 +102,6 @@ CUSTOM_CVAR(Float, r_quakeintensity, 1.0f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) else if (self > 1.f) self = 1.f; } -DCanvas *RenderTarget; // [RH] canvas to render to - int viewwindowx; int viewwindowy; @@ -203,8 +201,6 @@ void R_SetViewSize (int blocks) void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, bool renderingToCanvas) { - float trueratio; - if (windowSize >= 11) { viewwidth = fullWidth; @@ -226,11 +222,10 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, b if (renderingToCanvas) { WidescreenRatio = fullWidth / (float)fullHeight; - trueratio = WidescreenRatio; } else { - WidescreenRatio = ActiveRatio(fullWidth, fullHeight, &trueratio); + WidescreenRatio = ActiveRatio(fullWidth, fullHeight); } DrawFSHUD = (windowSize == 11); @@ -260,7 +255,6 @@ void R_SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, b if (fov > 170.) fov = 170.; } FocalTangent = tan(fov.Radians() / 2); - Renderer->SetWindow(windowSize, fullWidth, fullHeight, stHeight, trueratio); } //========================================================================== @@ -900,22 +894,19 @@ void R_SetupFrame (AActor *actor) BaseBlendG = GPART(newblend); BaseBlendB = BPART(newblend); BaseBlendA = APART(newblend) / 255.f; - NormalLight.Maps = realcolormaps; + NormalLight.Maps = realcolormaps.Maps; } else { - NormalLight.Maps = realcolormaps + NUMCOLORMAPS*256*newblend; + NormalLight.Maps = realcolormaps.Maps + NUMCOLORMAPS*256*newblend; BaseBlendR = BaseBlendG = BaseBlendB = 0; BaseBlendA = 0.f; } } - Renderer->CopyStackedViewParameters(); - Renderer->SetupFrame(player); - validcount++; - if (RenderTarget == screen && r_clearbuffer != 0) + if (r_clearbuffer != 0) { int color; int hom = r_clearbuffer; @@ -940,7 +931,7 @@ void R_SetupFrame (AActor *actor) { color = pr_hom(); } - Renderer->ClearBuffer(color); + Renderer->SetClearColor(color); } } diff --git a/src/r_utility.h b/src/r_utility.h index 71414df44..ac31a6f5b 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -12,8 +12,6 @@ class FSerializer; // There a 0-31, i.e. 32 LUT in the COLORMAP lump. #define NUMCOLORMAPS 32 -extern DCanvas *RenderTarget; - extern DVector3 ViewPos; extern DVector3 ViewActorPos; extern DAngle ViewAngle; @@ -21,6 +19,11 @@ extern DAngle ViewPitch; extern DAngle ViewRoll; extern DVector3 ViewPath[2]; +extern double ViewCos; +extern double ViewSin; +extern int viewwindowx; +extern int viewwindowy; + extern "C" int centerx, centerxwide; extern "C" int centery; @@ -45,6 +48,8 @@ extern unsigned int R_OldBlend; const double r_Yaspect = 200.0; // Why did I make this a variable? It's never set anywhere. +extern bool r_showviewer; + //========================================================================== // // R_PointOnSide diff --git a/src/r_walldraw.cpp b/src/r_walldraw.cpp deleted file mode 100644 index c13786bba..000000000 --- a/src/r_walldraw.cpp +++ /dev/null @@ -1,582 +0,0 @@ -/* -** Wall drawing stuff free of Build pollution -** Copyright (c) 2016 Magnus Norddahl -** -** This software is provided 'as-is', without any express or implied -** warranty. In no event will the authors be held liable for any damages -** arising from the use of this software. -** -** Permission is granted to anyone to use this software for any purpose, -** including commercial applications, and to alter it and redistribute it -** freely, subject to the following restrictions: -** -** 1. The origin of this software must not be misrepresented; you must not -** claim that you wrote the original software. If you use this software -** in a product, an acknowledgment in the product documentation would be -** appreciated but is not required. -** 2. Altered source versions must be plainly marked as such, and must not be -** misrepresented as being the original software. -** 3. This notice may not be removed or altered from any source distribution. -** -*/ - -#include -#include - -#include "doomdef.h" -#include "doomstat.h" -#include "doomdata.h" - -#include "r_local.h" -#include "r_sky.h" -#include "v_video.h" - -#include "m_swap.h" -#include "a_sharedglobal.h" -#include "d_net.h" -#include "g_level.h" -#include "r_draw.h" -#include "r_bsp.h" -#include "r_plane.h" -#include "r_segs.h" -#include "r_3dfloors.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - -namespace swrenderer -{ - using namespace drawerargs; - - extern FTexture *rw_pic; - extern int wallshade; - -struct WallSampler -{ - WallSampler() { } - WallSampler(int y1, float swal, double yrepeat, fixed_t xoffset, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)); - - uint32_t uv_pos; - uint32_t uv_step; - uint32_t uv_max; - - const BYTE *source; - uint32_t height; -}; - -WallSampler::WallSampler(int y1, float swal, double yrepeat, fixed_t xoffset, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)) -{ - height = texture->GetHeight(); - - int uv_fracbits = 32 - texture->HeightBits; - if (uv_fracbits != 32) - { - uv_max = height << uv_fracbits; - - // Find start uv in [0-base_height[ range. - // Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range. - double uv_stepd = swal * yrepeat; - double v = (dc_texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; - v = v - floor(v); - v *= height; - v *= (1 << uv_fracbits); - - uv_pos = (uint32_t)v; - uv_step = xs_ToFixed(uv_fracbits, uv_stepd); - if (uv_step == 0) // To prevent divide by zero elsewhere - uv_step = 1; - } - else - { // Hack for one pixel tall textures - uv_pos = 0; - uv_step = 0; - uv_max = 1; - } - - source = getcol(texture, xoffset >> FRACBITS); -} - -// Draw a column with support for non-power-of-two ranges -static void Draw1Column(int x, int y1, int y2, WallSampler &sampler, void(*draw1column)()) -{ - if (sampler.uv_max == 0 || sampler.uv_step == 0) // power of two - { - int count = y2 - y1; - - dc_source = sampler.source; - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - dc_iscale = sampler.uv_step; - dc_texturefrac = sampler.uv_pos; - draw1column(); - - uint64_t step64 = sampler.uv_step; - uint64_t pos64 = sampler.uv_pos; - sampler.uv_pos = (uint32_t)(pos64 + step64 * count); - } - else - { - uint32_t uv_pos = sampler.uv_pos; - - uint32_t left = y2 - y1; - while (left > 0) - { - uint32_t available = sampler.uv_max - uv_pos; - uint32_t next_uv_wrap = available / sampler.uv_step; - if (available % sampler.uv_step != 0) - next_uv_wrap++; - uint32_t count = MIN(left, next_uv_wrap); - - dc_source = sampler.source; - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - dc_iscale = sampler.uv_step; - dc_texturefrac = uv_pos; - draw1column(); - - left -= count; - uv_pos += sampler.uv_step * count; - if (uv_pos >= sampler.uv_max) - uv_pos -= sampler.uv_max; - } - - sampler.uv_pos = uv_pos; - } -} - -// Draw four columns with support for non-power-of-two ranges -static void Draw4Columns(int x, int y1, int y2, WallSampler *sampler, void(*draw4columns)()) -{ - if (sampler[0].uv_max == 0 || sampler[0].uv_step == 0) // power of two, no wrap handling needed - { - int count = y2 - y1; - for (int i = 0; i < 4; i++) - { - dc_wall_source[i] = sampler[i].source; - dc_wall_texturefrac[i] = sampler[i].uv_pos; - dc_wall_iscale[i] = sampler[i].uv_step; - - uint64_t step64 = sampler[i].uv_step; - uint64_t pos64 = sampler[i].uv_pos; - sampler[i].uv_pos = (uint32_t)(pos64 + step64 * count); - } - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - draw4columns(); - } - else - { - dc_dest = (ylookup[y1] + x) + dc_destorg; - for (int i = 0; i < 4; i++) - { - dc_wall_source[i] = sampler[i].source; - } - - uint32_t left = y2 - y1; - while (left > 0) - { - // Find which column wraps first - uint32_t count = left; - for (int i = 0; i < 4; i++) - { - uint32_t available = sampler[i].uv_max - sampler[i].uv_pos; - uint32_t next_uv_wrap = available / sampler[i].uv_step; - if (available % sampler[i].uv_step != 0) - next_uv_wrap++; - count = MIN(next_uv_wrap, count); - } - - // Draw until that column wraps - for (int i = 0; i < 4; i++) - { - dc_wall_texturefrac[i] = sampler[i].uv_pos; - dc_wall_iscale[i] = sampler[i].uv_step; - } - dc_count = count; - draw4columns(); - - // Wrap the uv position - for (int i = 0; i < 4; i++) - { - sampler[i].uv_pos += sampler[i].uv_step * count; - if (sampler[i].uv_pos >= sampler[i].uv_max) - sampler[i].uv_pos -= sampler[i].uv_max; - } - - left -= count; - } - } -} - -typedef void(*DrawColumnFuncPtr)(); - -static void ProcessWallWorker( - int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, - const BYTE *(*getcol)(FTexture *tex, int x), DrawColumnFuncPtr draw1column, DrawColumnFuncPtr draw4columns) -{ - if (rw_pic->UseType == FTexture::TEX_Null) - return; - - fixed_t xoffset = rw_offset; - - int fracbits = 32 - rw_pic->HeightBits; - if (fracbits == 32) - { // Hack for one pixel tall textures - fracbits = 0; - yrepeat = 0; - dc_texturemid = 0; - } - - dc_wall_fracbits = fracbits; - - bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); - if (fixed) - { - dc_wall_colormap[0] = dc_colormap; - dc_wall_colormap[1] = dc_colormap; - dc_wall_colormap[2] = dc_colormap; - dc_wall_colormap[3] = dc_colormap; - } - - if (fixedcolormap) - dc_colormap = fixedcolormap; - else - dc_colormap = basecolormap->Maps; - - float light = rw_light; - - // Calculate where 4 column alignment begins and ends: - int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); - int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); - - // First unaligned columns: - for (int x = x1; x < aligned_x1; x++, light += rw_lightstep) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - - WallSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, rw_pic, getcol); - Draw1Column(x, y1, y2, sampler, draw1column); - } - - // The aligned columns - for (int x = aligned_x1; x < aligned_x2; x += 4) - { - // Find y1, y2, light and uv values for four columns: - int y1[4] = { uwal[x], uwal[x + 1], uwal[x + 2], uwal[x + 3] }; - int y2[4] = { dwal[x], dwal[x + 1], dwal[x + 2], dwal[x + 3] }; - - float lights[4]; - for (int i = 0; i < 4; i++) - { - lights[i] = light; - light += rw_lightstep; - } - - WallSampler sampler[4]; - for (int i = 0; i < 4; i++) - sampler[i] = WallSampler(y1[i], swal[x + i], yrepeat, lwal[x + i] + xoffset, rw_pic, getcol); - - // Figure out where we vertically can start and stop drawing 4 columns in one go - int middle_y1 = y1[0]; - int middle_y2 = y2[0]; - for (int i = 1; i < 4; i++) - { - middle_y1 = MAX(y1[i], middle_y1); - middle_y2 = MIN(y2[i], middle_y2); - } - - // If we got an empty column in our set we cannot draw 4 columns in one go: - bool empty_column_in_set = false; - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - empty_column_in_set = true; - } - - if (empty_column_in_set || middle_y2 <= middle_y1) - { - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - continue; - - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - Draw1Column(x + i, y1[i], y2[i], sampler[i], draw1column); - } - continue; - } - - // Draw the first rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - - if (y1[i] < middle_y1) - Draw1Column(x + i, y1[i], middle_y1, sampler[i], draw1column); - } - - // Draw the area where all 4 columns are active - if (!fixed) - { - for (int i = 0; i < 4; i++) - { - dc_wall_colormap[i] = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - } - } - Draw4Columns(x, middle_y1, middle_y2, sampler, draw4columns); - - // Draw the last rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - - if (middle_y2 < y2[i]) - Draw1Column(x + i, middle_y2, y2[i], sampler[i], draw1column); - } - } - - // The last unaligned columns: - for (int x = aligned_x2; x < x2; x++, light += rw_lightstep) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - if (!fixed) - dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); - - WallSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, rw_pic, getcol); - Draw1Column(x, y1, y2, sampler, draw1column); - } - - NetUpdate(); -} - -static void ProcessNormalWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) -{ - ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, R_DrawWallCol1, R_DrawWallCol4); -} - -static void ProcessMaskedWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) -{ - if (!rw_pic->bMasked) // Textures that aren't masked can use the faster ProcessNormalWall. - { - ProcessNormalWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - } - else - { - ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, R_DrawWallMaskedCol1, R_DrawWallMaskedCol4); - } -} - -static void ProcessTranslucentWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) -{ - void (*drawcol1)(); - void (*drawcol4)(); - if (!R_GetTransMaskDrawers(&drawcol1, &drawcol4)) - { - // The current translucency is unsupported, so draw with regular ProcessMaskedWall instead. - ProcessMaskedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - } - else - { - ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, drawcol1, drawcol4); - } -} - -static void ProcessStripedWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) -{ - FDynamicColormap *startcolormap = basecolormap; - int startshade = wallshade; - bool fogginess = foggy; - - short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; - short *up, *down; - - up = uwal; - down = most1; - - assert(WallC.sx1 <= x1); - assert(WallC.sx2 >= x2); - - // kg3D - fake floors instead of zdoom light list - for (unsigned int i = 0; i < frontsector->e->XFloor.lightlist.Size(); i++) - { - int j = R_CreateWallSegmentYSloped (most3, frontsector->e->XFloor.lightlist[i].plane, &WallC); - if (j != 3) - { - for (int j = x1; j < x2; ++j) - { - down[j] = clamp (most3[j], up[j], dwal[j]); - } - ProcessNormalWall (x1, x2, up, down, swal, lwal, yrepeat); - up = down; - down = (down == most1) ? most2 : most1; - } - - lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(fogginess, - *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - } - - ProcessNormalWall (x1, x2, up, dwal, swal, lwal, yrepeat); - basecolormap = startcolormap; - wallshade = startshade; -} - -static void ProcessWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask) -{ - if (mask) - { - if (colfunc == basecolfunc) - { - ProcessMaskedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - else - { - ProcessTranslucentWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - } - else - { - if (fixedcolormap != NULL || fixedlightlev >= 0 || !(frontsector->e && frontsector->e->XFloor.lightlist.Size())) - { - ProcessNormalWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - else - { - ProcessStripedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - } -} - -//============================================================================= -// -// ProcessWallNP2 -// -// This is a wrapper around ProcessWall that helps it tile textures whose heights -// are not powers of 2. It divides the wall into texture-sized strips and calls -// ProcessNormalWall for each of those. Since only one repetition of the texture fits -// in each strip, ProcessWall will not tile. -// -//============================================================================= - -static void ProcessWallNP2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask) -{ - short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; - short *up, *down; - double texheight = rw_pic->GetHeight(); - double partition; - double scaledtexheight = texheight / yrepeat; - - if (yrepeat >= 0) - { // normal orientation: draw strips from top to bottom - partition = top - fmod(top - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); - if (partition == top) - { - partition -= scaledtexheight; - } - up = uwal; - down = most1; - dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; - while (partition > bot) - { - int j = R_CreateWallSegmentY(most3, partition - ViewPos.Z, &WallC); - if (j != 3) - { - for (int j = x1; j < x2; ++j) - { - down[j] = clamp(most3[j], up[j], dwal[j]); - } - ProcessWall(x1, x2, up, down, swal, lwal, yrepeat, mask); - up = down; - down = (down == most1) ? most2 : most1; - } - partition -= scaledtexheight; - dc_texturemid -= texheight; - } - ProcessWall(x1, x2, up, dwal, swal, lwal, yrepeat, mask); - } - else - { // upside down: draw strips from bottom to top - partition = bot - fmod(bot - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); - up = most1; - down = dwal; - dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; - while (partition < top) - { - int j = R_CreateWallSegmentY(most3, partition - ViewPos.Z, &WallC); - if (j != 12) - { - for (int j = x1; j < x2; ++j) - { - up[j] = clamp(most3[j], uwal[j], down[j]); - } - ProcessWall(x1, x2, up, down, swal, lwal, yrepeat, mask); - down = up; - up = (up == most1) ? most2 : most1; - } - partition -= scaledtexheight; - dc_texturemid -= texheight; - } - ProcessWall(x1, x2, uwal, down, swal, lwal, yrepeat, mask); - } -} - -void R_DrawDrawSeg(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) -{ - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - double frontcz1 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v1); - double frontfz1 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v1); - double frontcz2 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v2); - double frontfz2 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v2); - double top = MAX(frontcz1, frontcz2); - double bot = MIN(frontfz1, frontfz2); - if (fake3D & FAKE3D_CLIPTOP) - { - top = MIN(top, sclipTop); - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - bot = MAX(bot, sclipBottom); - } - ProcessWallNP2(x1, x2, uwal, dwal, swal, lwal, yrepeat, top, bot, true); - } - else - { - ProcessWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, true); - } -} - - -void R_DrawWallSegment(FTexture *rw_pic, int x1, int x2, short *walltop, short *wallbottom, float *swall, fixed_t *lwall, double yscale, double top, double bottom, bool mask) -{ - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - ProcessWallNP2(x1, x2, walltop, wallbottom, swall, lwall, yscale, top, bottom, false); - } - else - { - ProcessWall(x1, x2, walltop, wallbottom, swall, lwall, yscale, false); - } -} - -void R_DrawSkySegment(visplane_t *pl, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - ProcessNormalWall(pl->left, pl->right, uwal, dwal, swal, lwal, yrepeat, getcol); -} - - - -} \ No newline at end of file diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp index 06f8757c3..12a0c44e0 100644 --- a/src/scripting/backend/codegen.cpp +++ b/src/scripting/backend/codegen.cpp @@ -6063,10 +6063,9 @@ FxExpression *FxIdentifier::ResolveMember(FCompileContext &ctx, PStruct *classct object = nullptr; return nullptr; } - if ((vsym->Flags & VARF_Deprecated) && sym->mVersion >= ctx.Version) + if ((vsym->Flags & VARF_Deprecated) && sym->mVersion <= ctx.Version) { ScriptPosition.Message(MSG_WARNING, "Accessing deprecated member variable %s - deprecated since %d.%d.%d", sym->SymbolName.GetChars(), vsym->mVersion.major, vsym->mVersion.minor, vsym->mVersion.revision); - ScriptPosition.Message(MSG_WARNING, "Accessing deprecated member variable %s", vsym->SymbolName.GetChars()); } // We have 4 cases to consider here: @@ -8513,10 +8512,9 @@ bool FxVMFunctionCall::CheckAccessibility(const VersionInfo &ver) ScriptPosition.Message(MSG_ERROR, "%s not accessible to %s", Function->SymbolName.GetChars(), VersionString.GetChars()); return false; } - if ((Function->Variants[0].Flags & VARF_Deprecated) && Function->mVersion >= ver) + if ((Function->Variants[0].Flags & VARF_Deprecated) && Function->mVersion <= ver) { ScriptPosition.Message(MSG_WARNING, "Accessing deprecated function %s - deprecated since %d.%d.%d", Function->SymbolName.GetChars(), Function->mVersion.major, Function->mVersion.minor, Function->mVersion.revision); - return false; } return true; } @@ -8565,7 +8563,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) if (!CheckAccessibility(ctx.Version)) { delete this; - return false; + return nullptr; } // This should never happen. if (Self == nullptr && (Function->Variants[0].Flags & VARF_Method)) diff --git a/src/sound/mpg123_decoder.h b/src/sound/mpg123_decoder.h index 051473abb..59e1df2ca 100644 --- a/src/sound/mpg123_decoder.h +++ b/src/sound/mpg123_decoder.h @@ -6,7 +6,8 @@ #ifdef HAVE_MPG123 #ifdef _MSC_VER -typedef int ssize_t; +#include +typedef ptrdiff_t ssize_t; #endif #include "mpg123.h" diff --git a/src/swrenderer/drawers/r_draw.cpp b/src/swrenderer/drawers/r_draw.cpp new file mode 100644 index 000000000..d7b4237ce --- /dev/null +++ b/src/swrenderer/drawers/r_draw.cpp @@ -0,0 +1,164 @@ +/* +** r_draw.cpp +** +**--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2016 Magnus Norddahl +** 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 "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_draw.h" +#include "r_draw_rgba.h" +#include "r_draw_pal.h" +#include "r_thread.h" +#include "swrenderer/scene/r_light.h" + +CVAR(Bool, r_dynlights, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +namespace swrenderer +{ + uint8_t shadetables[NUMCOLORMAPS * 16 * 256]; + FDynamicColormap ShadeFakeColormap[16]; + uint8_t identitymap[256]; + FDynamicColormap identitycolormap; + int fuzzoffset[FUZZTABLE + 1]; + int fuzzpos; + int fuzzviewheight; + + uint32_t particle_texture[PARTICLE_TEXTURE_SIZE * PARTICLE_TEXTURE_SIZE]; + + short zeroarray[MAXWIDTH]; + short screenheightarray[MAXWIDTH]; + + void R_InitShadeMaps() + { + int i, j; + // set up shading tables for shaded columns + // 16 colormap sets, progressing from full alpha to minimum visible alpha + + uint8_t *table = shadetables; + + // Full alpha + for (i = 0; i < 16; ++i) + { + ShadeFakeColormap[i].Color = ~0u; + ShadeFakeColormap[i].Desaturate = ~0u; + ShadeFakeColormap[i].Next = NULL; + ShadeFakeColormap[i].Maps = table; + + for (j = 0; j < NUMCOLORMAPS; ++j) + { + int a = (NUMCOLORMAPS - j) * 256 / NUMCOLORMAPS * (16 - i); + for (int k = 0; k < 256; ++k) + { + uint8_t v = (((k + 2) * a) + 256) >> 14; + table[k] = MIN(v, 64); + } + table += 256; + } + } + for (i = 0; i < NUMCOLORMAPS * 16 * 256; ++i) + { + assert(shadetables[i] <= 64); + } + + // Set up a guaranteed identity map + for (i = 0; i < 256; ++i) + { + identitymap[i] = i; + } + identitycolormap.Maps = identitymap; + } + + void R_InitFuzzTable(int fuzzoff) + { + /* + FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF + */ + + static const int8_t fuzzinit[FUZZTABLE] = { + 1,-1, 1,-1, 1, 1,-1, + 1, 1,-1, 1, 1, 1,-1, + 1, 1, 1,-1,-1,-1,-1, + 1,-1,-1, 1, 1, 1, 1,-1, + 1,-1, 1, 1,-1,-1, 1, + 1,-1,-1,-1,-1, 1, 1, + 1, 1,-1, 1, 1,-1, 1 + }; + + for (int i = 0; i < FUZZTABLE; i++) + { + fuzzoffset[i] = fuzzinit[i] * fuzzoff; + } + } + + void R_InitParticleTexture() + { + float center = PARTICLE_TEXTURE_SIZE * 0.5f; + for (int y = 0; y < PARTICLE_TEXTURE_SIZE; y++) + { + for (int x = 0; x < PARTICLE_TEXTURE_SIZE; x++) + { + float dx = (center - x - 0.5f) / center; + float dy = (center - y - 0.5f) / center; + float dist2 = dx * dx + dy * dy; + float alpha = clamp(1.1f - dist2 * 1.1f, 0.0f, 1.0f); + + particle_texture[x + y * PARTICLE_TEXTURE_SIZE] = (int)(alpha * 128.0f + 0.5f); + } + } + } + + void R_UpdateFuzzPos(const SpriteDrawerArgs &args) + { + int yl = MAX(args.FuzzY1(), 1); + int yh = MIN(args.FuzzY2(), fuzzviewheight); + if (yl <= yh) + fuzzpos = (fuzzpos + yh - yl + 1) % FUZZTABLE; + } +} diff --git a/src/swrenderer/drawers/r_draw.h b/src/swrenderer/drawers/r_draw.h new file mode 100644 index 000000000..29b908398 --- /dev/null +++ b/src/swrenderer/drawers/r_draw.h @@ -0,0 +1,98 @@ + +#pragma once + +#include "r_defs.h" +#include + +struct FSWColormap; +struct FLightNode; + +EXTERN_CVAR(Bool, r_multithreaded); +EXTERN_CVAR(Bool, r_magfilter); +EXTERN_CVAR(Bool, r_minfilter); +EXTERN_CVAR(Bool, r_mipmap); +EXTERN_CVAR(Float, r_lod_bias); +EXTERN_CVAR(Int, r_drawfuzz); +EXTERN_CVAR(Bool, r_drawtrans); +EXTERN_CVAR(Float, transsouls); +EXTERN_CVAR(Bool, r_dynlights); + +class DrawerCommandQueue; +typedef std::shared_ptr DrawerCommandQueuePtr; + +namespace swrenderer +{ + class DrawerArgs; + class SkyDrawerArgs; + class WallDrawerArgs; + class SpanDrawerArgs; + class SpriteDrawerArgs; + + extern uint8_t shadetables[/*NUMCOLORMAPS*16*256*/]; + extern FDynamicColormap ShadeFakeColormap[16]; + extern uint8_t identitymap[256]; + extern FDynamicColormap identitycolormap; + + // Constant arrays used for psprite clipping and initializing clipping. + extern short zeroarray[MAXWIDTH]; + extern short screenheightarray[MAXWIDTH]; + + // Spectre/Invisibility. + #define FUZZTABLE 50 + extern int fuzzoffset[FUZZTABLE + 1]; + extern int fuzzpos; + extern int fuzzviewheight; + + #define PARTICLE_TEXTURE_SIZE 64 + extern uint32_t particle_texture[PARTICLE_TEXTURE_SIZE * PARTICLE_TEXTURE_SIZE]; + + class SWPixelFormatDrawers + { + public: + SWPixelFormatDrawers(DrawerCommandQueuePtr queue) : Queue(queue) { } + virtual ~SWPixelFormatDrawers() { } + virtual void DrawWallColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallMaskedColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallAddColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallAddClampColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallSubClampColumn(const WallDrawerArgs &args) = 0; + virtual void DrawWallRevSubClampColumn(const WallDrawerArgs &args) = 0; + virtual void DrawSingleSkyColumn(const SkyDrawerArgs &args) = 0; + virtual void DrawDoubleSkyColumn(const SkyDrawerArgs &args) = 0; + virtual void DrawColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillAddColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillAddClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillSubClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void FillRevSubClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawFuzzColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawAddColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawTranslatedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawTranslatedAddColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawShadedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawAddClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawAddClampTranslatedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawSubClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawSubClampTranslatedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawRevSubClampColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawRevSubClampTranslatedColumn(const SpriteDrawerArgs &args) = 0; + virtual void DrawSpan(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanMasked(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanTranslucent(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanMaskedTranslucent(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanAddClamp(const SpanDrawerArgs &args) = 0; + virtual void DrawSpanMaskedAddClamp(const SpanDrawerArgs &args) = 0; + virtual void FillSpan(const SpanDrawerArgs &args) = 0; + virtual void DrawTiltedSpan(const SpanDrawerArgs &args, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) = 0; + virtual void DrawColoredSpan(const SpanDrawerArgs &args, int y, int x1, int x2) = 0; + virtual void DrawFogBoundaryLine(const SpanDrawerArgs &args, int y, int x1, int x2) = 0; + + DrawerCommandQueuePtr Queue; + }; + + void R_InitShadeMaps(); + void R_InitFuzzTable(int fuzzoff); + void R_InitParticleTexture(); + + void R_UpdateFuzzPos(const SpriteDrawerArgs &args); +} diff --git a/src/swrenderer/drawers/r_draw_pal.cpp b/src/swrenderer/drawers/r_draw_pal.cpp new file mode 100644 index 000000000..0e08427cf --- /dev/null +++ b/src/swrenderer/drawers/r_draw_pal.cpp @@ -0,0 +1,2935 @@ +/* +** r_draw_pal.cpp +** +**--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2016 Magnus Norddahl +** Copyright 2016 Rachael Alexanderson +** 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 __arm__ +#define NO_SSE +#endif + +#ifndef NO_SSE +#include +#endif +#include "templates.h" +#include "doomtype.h" +#include "doomdef.h" +#include "r_defs.h" +#include "r_draw.h" +#include "v_video.h" +#include "r_draw_pal.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" + +// [SP] r_blendmethod - false = rgb555 matching (ZDoom classic), true = rgb666 (refactored) +CVAR(Bool, r_blendmethod, false, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) + +/* + [RH] This translucency algorithm is based on DOSDoom 0.65's, but uses + a 32k RGB table instead of an 8k one. At least on my machine, it's + slightly faster (probably because it uses only one shift instead of + two), and it looks considerably less green at the ends of the + translucency range. The extra size doesn't appear to be an issue. + + The following note is from DOSDoom 0.65: + + New translucency algorithm, by Erik Sandberg: + + Basically, we compute the red, green and blue values for each pixel, and + then use a RGB table to check which one of the palette colours that best + represents those RGB values. The RGB table is 8k big, with 4 R-bits, + 5 G-bits and 4 B-bits. A 4k table gives a bit too bad precision, and a 32k + table takes up more memory and results in more cache misses, so an 8k + table seemed to be quite ultimate. + + The computation of the RGB for each pixel is accelerated by using two + 1k tables for each translucency level. + The xth element of one of these tables contains the r, g and b values for + the colour x, weighted for the current translucency level (for example, + the weighted rgb values for background colour at 75% translucency are 1/4 + of the original rgb values). The rgb values are stored as three + low-precision fixed point values, packed into one long per colour: + Bit 0-4: Frac part of blue (5 bits) + Bit 5-8: Int part of blue (4 bits) + Bit 9-13: Frac part of red (5 bits) + Bit 14-17: Int part of red (4 bits) + Bit 18-22: Frac part of green (5 bits) + Bit 23-27: Int part of green (5 bits) + Bit 28-31: All zeros (4 bits) + + The point of this format is that the two colours now can be added, and + then be converted to a RGB table index very easily: First, we just set + all the frac bits and the four upper zero bits to 1. It's now possible + to get the RGB table index by anding the current value >> 5 with the + current value >> 19. When asm-optimised, this should be the fastest + algorithm that uses RGB tables. +*/ + +namespace swrenderer +{ + PalWall1Command::PalWall1Command(const WallDrawerArgs &args) : args(args) + { + } + + uint8_t PalWall1Command::AddLights(const DrawerLight *lights, int num_lights, float viewpos_z, uint8_t fg, uint8_t material) + { + uint32_t lit_r = 0; + uint32_t lit_g = 0; + uint32_t lit_b = 0; + + for (int i = 0; i < num_lights; i++) + { + uint32_t light_color_r = RPART(lights[i].color); + uint32_t light_color_g = GPART(lights[i].color); + uint32_t light_color_b = BPART(lights[i].color); + + // L = light-pos + // dist = sqrt(dot(L, L)) + // distance_attenuation = 1 - MIN(dist * (1/radius), 1) + float Lxy2 = lights[i].x; // L.x*L.x + L.y*L.y + float Lz = lights[i].z - viewpos_z; + float dist2 = Lxy2 + Lz * Lz; +#ifdef NO_SSE + float rcp_dist = 1.0f / (dist2 * 0.01f); +#else + float rcp_dist = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_load_ss(&dist2))); +#endif + float dist = dist2 * rcp_dist; + float distance_attenuation = (256.0f - MIN(dist * lights[i].radius, 256.0f)); + + // The simple light type + float simple_attenuation = distance_attenuation; + + // The point light type + // diffuse = dot(N,L) * attenuation + float point_attenuation = lights[i].y * rcp_dist * distance_attenuation; + uint32_t attenuation = (uint32_t)(lights[i].y == 0.0f ? simple_attenuation : point_attenuation); + + lit_r += (light_color_r * attenuation) >> 8; + lit_g += (light_color_g * attenuation) >> 8; + lit_b += (light_color_b * attenuation) >> 8; + } + + if (lit_r == 0 && lit_g == 0 && lit_b == 0) + return fg; + + uint32_t material_r = GPalette.BaseColors[material].r; + uint32_t material_g = GPalette.BaseColors[material].g; + uint32_t material_b = GPalette.BaseColors[material].b; + + lit_r = MIN(GPalette.BaseColors[fg].r + ((lit_r * material_r) >> 8), 255); + lit_g = MIN(GPalette.BaseColors[fg].g + ((lit_g * material_g) >> 8), 255); + lit_b = MIN(GPalette.BaseColors[fg].b + ((lit_b * material_b) >> 8), 255); + + return RGB256k.All[((lit_r >> 2) << 12) | ((lit_g >> 2) << 6) | (lit_b >> 2)]; + } + + void DrawWall1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (num_dynlights == 0) + { + do + { + *dest = colormap[source[frac >> bits]]; + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + do + { + *dest = AddLights(dynlights, num_dynlights, viewpos_z, colormap[source[frac >> bits]], source[frac >> bits]); + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallMasked1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (num_dynlights == 0) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + *dest = colormap[pix]; + } + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + *dest = AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix); + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallAdd1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (!r_blendmethod) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = colormap[pix]; + + uint32_t fg = fg2rgb[lit]; + uint32_t bg = bg2rgb[*dest]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + } + frac += fracstep; + dest += pitch; + } while (--count); + + } + else + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = colormap[pix]; + + uint32_t r = MIN(GPalette.BaseColors[lit].r + GPalette.BaseColors[*dest].r, 255); + uint32_t g = MIN(GPalette.BaseColors[lit].g + GPalette.BaseColors[*dest].g, 255); + uint32_t b = MIN(GPalette.BaseColors[lit].b + GPalette.BaseColors[*dest].b, 255); + *dest = RGB256k.RGB[r>>2][g>>2][b>>2]; + } + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallAddClamp1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + if (!r_blendmethod) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + uint32_t a = fg2rgb[lit] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + uint32_t r = MIN(GPalette.BaseColors[lit].r + GPalette.BaseColors[*dest].r, 255); + uint32_t g = MIN(GPalette.BaseColors[lit].g + GPalette.BaseColors[*dest].g, 255); + uint32_t b = MIN(GPalette.BaseColors[lit].b + GPalette.BaseColors[*dest].b, 255); + *dest = RGB256k.RGB[r>>2][g>>2][b>>2]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallSubClamp1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + if (!r_blendmethod) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + uint32_t a = (fg2rgb[lit] | 0x40100400) - bg2rgb[*dest]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + int r = clamp(-GPalette.BaseColors[lit].r + GPalette.BaseColors[*dest].r, 0, 255); + int g = clamp(-GPalette.BaseColors[lit].g + GPalette.BaseColors[*dest].g, 0, 255); + int b = clamp(-GPalette.BaseColors[lit].b + GPalette.BaseColors[*dest].b, 0, 255); + *dest = RGB256k.RGB[r>>2][g>>2][b>>2]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + void DrawWallRevSubClamp1PalCommand::Execute(DrawerThread *thread) + { + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint8_t *colormap = args.Colormap(); + int count = args.Count(); + const uint8_t *source = args.TexturePixels(); + uint8_t *dest = args.Dest(); + int bits = args.TextureFracBits(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + DrawerLight *dynlights = args.dc_lights; + int num_dynlights = args.dc_num_lights; + float viewpos_z = args.dc_viewpos.Z; + float step_viewpos_z = args.dc_viewpos_step.Z; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + viewpos_z += step_viewpos_z * thread->skipped_by_thread(args.DestY()); + step_viewpos_z *= thread->num_cores; + + if (!r_blendmethod) + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[lit]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + else + { + do + { + uint8_t pix = source[frac >> bits]; + if (pix != 0) + { + uint8_t lit = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_z, colormap[pix], pix) : colormap[pix]; + + int r = clamp(GPalette.BaseColors[lit].r - GPalette.BaseColors[*dest].r, 0, 255); + int g = clamp(GPalette.BaseColors[lit].g - GPalette.BaseColors[*dest].g, 0, 255); + int b = clamp(GPalette.BaseColors[lit].b - GPalette.BaseColors[*dest].b, 0, 255); + *dest = RGB256k.RGB[r>>2][g>>2][b>>2]; + } + viewpos_z += step_viewpos_z; + frac += fracstep; + dest += pitch; + } while (--count); + } + } + + ///////////////////////////////////////////////////////////////////////// + + PalSkyCommand::PalSkyCommand(const SkyDrawerArgs &args) : args(args) + { + } + + void DrawSingleSky1PalCommand::Execute(DrawerThread *thread) + { + uint8_t *dest = args.Dest(); + int count = args.Count(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + const uint8_t *source0 = args.FrontTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!args.FadeSky()) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + dest += pitch; + frac += fracstep; + } + + return; + } + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + + int solid_top_r = RPART(solid_top); + int solid_top_g = GPART(solid_top); + int solid_top_b = BPART(solid_top); + int solid_bottom_r = RPART(solid_bottom); + int solid_bottom_g = GPART(solid_bottom); + int solid_bottom_b = BPART(solid_bottom); + uint8_t solid_top_fill = RGB32k.RGB[(solid_top_r >> 3)][(solid_top_g >> 3)][(solid_top_b >> 3)]; + uint8_t solid_bottom_fill = RGB32k.RGB[(solid_bottom_r >> 3)][(solid_bottom_g >> 3)][(solid_bottom_b >> 3)]; + + const uint32_t *palette = (const uint32_t *)GPalette.BaseColors; + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top_fill; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + + uint32_t c = palette[fg]; + int alpha_top = MAX(MIN(frac >> (16 - start_fade), 256), 0); + int inv_alpha_top = 256 - alpha_top; + int c_red = RPART(c); + int c_green = GPART(c); + int c_blue = BPART(c); + c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; + c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; + c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; + *dest = RGB256k.RGB[(c_red >> 2)][(c_green >> 2)][(c_blue >> 2)]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + + uint32_t c = palette[fg]; + int alpha_bottom = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); + int inv_alpha_bottom = 256 - alpha_bottom; + int c_red = RPART(c); + int c_green = GPART(c); + int c_blue = BPART(c); + c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; + c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; + c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; + *dest = RGB256k.RGB[(c_red >> 2)][(c_green >> 2)][(c_blue >> 2)]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom_fill; + dest += pitch; + index += num_cores; + } + } + + void DrawDoubleSky1PalCommand::Execute(DrawerThread *thread) + { + uint8_t *dest = args.Dest(); + int count = args.Count(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + const uint8_t *source0 = args.FrontTexturePixels(); + const uint8_t *source1 = args.BackTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + uint32_t maxtextureheight1 = args.BackTextureHeight() - 1; + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!args.FadeSky()) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + *dest = fg; + dest += pitch; + frac += fracstep; + } + + return; + } + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + + int solid_top_r = RPART(solid_top); + int solid_top_g = GPART(solid_top); + int solid_top_b = BPART(solid_top); + int solid_bottom_r = RPART(solid_bottom); + int solid_bottom_g = GPART(solid_bottom); + int solid_bottom_b = BPART(solid_bottom); + uint8_t solid_top_fill = RGB32k.RGB[(solid_top_r >> 3)][(solid_top_g >> 3)][(solid_top_b >> 3)]; + uint8_t solid_bottom_fill = RGB32k.RGB[(solid_bottom_r >> 3)][(solid_bottom_g >> 3)][(solid_bottom_b >> 3)]; + + const uint32_t *palette = (const uint32_t *)GPalette.BaseColors; + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top_fill; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + uint32_t c = palette[fg]; + int alpha_top = MAX(MIN(frac >> (16 - start_fade), 256), 0); + int inv_alpha_top = 256 - alpha_top; + int c_red = RPART(c); + int c_green = GPART(c); + int c_blue = BPART(c); + c_red = (c_red * alpha_top + solid_top_r * inv_alpha_top) >> 8; + c_green = (c_green * alpha_top + solid_top_g * inv_alpha_top) >> 8; + c_blue = (c_blue * alpha_top + solid_top_b * inv_alpha_top) >> 8; + *dest = RGB256k.RGB[(c_red >> 2)][(c_green >> 2)][(c_blue >> 2)]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + *dest = fg; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint8_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + uint32_t c = palette[fg]; + int alpha_bottom = MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0); + int inv_alpha_bottom = 256 - alpha_bottom; + int c_red = RPART(c); + int c_green = GPART(c); + int c_blue = BPART(c); + c_red = (c_red * alpha_bottom + solid_bottom_r * inv_alpha_bottom) >> 8; + c_green = (c_green * alpha_bottom + solid_bottom_g * inv_alpha_bottom) >> 8; + c_blue = (c_blue * alpha_bottom + solid_bottom_b * inv_alpha_bottom) >> 8; + *dest = RGB256k.RGB[(c_red >> 2)][(c_green >> 2)][(c_blue >> 2)]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom_fill; + dest += pitch; + index += num_cores; + } + } + + ///////////////////////////////////////////////////////////////////////// + + PalColumnCommand::PalColumnCommand(const SpriteDrawerArgs &args) : args(args) + { + } + + uint8_t PalColumnCommand::AddLights(uint8_t fg, uint8_t material, uint32_t lit_r, uint32_t lit_g, uint32_t lit_b) + { + if (lit_r == 0 && lit_g == 0 && lit_b == 0) + return fg; + + uint32_t material_r = GPalette.BaseColors[material].r; + uint32_t material_g = GPalette.BaseColors[material].g; + uint32_t material_b = GPalette.BaseColors[material].b; + + lit_r = MIN(GPalette.BaseColors[fg].r + ((lit_r * material_r) >> 8), 255); + lit_g = MIN(GPalette.BaseColors[fg].g + ((lit_g * material_g) >> 8), 255); + lit_b = MIN(GPalette.BaseColors[fg].b + ((lit_b * material_b) >> 8), 255); + + return RGB256k.All[((lit_r >> 2) << 12) | ((lit_g >> 2) << 6) | (lit_b >> 2)]; + } + + void DrawColumnPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + + // Framebuffer destination address. + dest = args.Dest(); + + // Determine scaling, + // which is the only mapping to be done. + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + // [RH] Get local copies of these variables so that the compiler + // has a better chance of optimizing this well. + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + + uint32_t dynlight = args.DynamicLight(); + if (dynlight == 0) + { + do + { + *dest = colormap[source[frac >> FRACBITS]]; + + dest += pitch; + frac += fracstep; + + } while (--count); + } + else + { + uint32_t lit_r = RPART(dynlight); + uint32_t lit_g = GPART(dynlight); + uint32_t lit_b = BPART(dynlight); + uint32_t light = 256 - (args.Light() >> (FRACBITS - 8)); + lit_r = MIN(light + lit_r, 256); + lit_g = MIN(light + lit_g, 256); + lit_b = MIN(light + lit_b, 256); + lit_r = lit_r - light; + lit_g = lit_g - light; + lit_b = lit_b - light; + + do + { + auto material = source[frac >> FRACBITS]; + auto fg = colormap[material]; + *dest = AddLights(fg, material, lit_r, lit_g, lit_b); + + dest += pitch; + frac += fracstep; + + } while (--count); + } + } + + void FillColumnPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + dest = args.Dest(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + uint8_t color = args.SolidColor(); + do + { + *dest = color; + dest += pitch; + } while (--count); + } + + void FillColumnAddPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + dest = args.Dest(); + uint32_t *bg2rgb; + uint32_t fg; + + bg2rgb = args.DestBlend(); + fg = args.SrcColorIndex(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + const PalEntry* pal = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t bg; + bg = (fg + bg2rgb[*dest]) | 0x1f07c1f; + *dest = RGB32k.All[bg & (bg >> 15)]; + dest += pitch; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + int src_r = ((srccolor >> 16) & 0xff) * srcalpha; + int src_g = ((srccolor >> 0) & 0xff) * srcalpha; + int src_b = ((srccolor >> 8) & 0xff) * srcalpha; + do + { + int r = clamp((src_r + pal[*dest].r * destalpha)>>18, 0, 255); + int g = clamp((src_g + pal[*dest].g * destalpha)>>18, 0, 255); + int b = clamp((src_b + pal[*dest].b * destalpha)>>18, 0, 255); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + } while (--count); + } + } + + void FillColumnAddClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + + dest = args.Dest(); + uint32_t *bg2rgb; + uint32_t fg; + + bg2rgb = args.DestBlend(); + fg = args.SrcColorIndex(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + const PalEntry* pal = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = fg + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + int src_r = ((srccolor >> 16) & 0xff) * srcalpha; + int src_g = ((srccolor >> 0) & 0xff) * srcalpha; + int src_b = ((srccolor >> 8) & 0xff) * srcalpha; + do + { + int r = clamp((src_r + pal[*dest].r * destalpha)>>18, 0, 255); + int g = clamp((src_g + pal[*dest].g * destalpha)>>18, 0, 255); + int b = clamp((src_b + pal[*dest].b * destalpha)>>18, 0, 255); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + } while (--count); + } + } + + void FillColumnSubClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + + dest = args.Dest(); + uint32_t *bg2rgb = args.DestBlend(); + uint32_t fg = args.SrcColorIndex(); + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + const PalEntry* palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = fg - bg2rgb[*dest]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int src_r = ((srccolor >> 16) & 0xff) * srcalpha; + int src_g = ((srccolor >> 0) & 0xff) * srcalpha; + int src_b = ((srccolor >> 8) & 0xff) * srcalpha; + int bg = *dest; + int r = MAX((-src_r + palette[bg].r * destalpha)>>18, 0); + int g = MAX((-src_g + palette[bg].g * destalpha)>>18, 0); + int b = MAX((-src_b + palette[bg].b * destalpha)>>18, 0); + + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + } while (--count); + } + } + + void FillColumnRevSubClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + + count = args.Count(); + if (count <= 0) + return; + + dest = args.Dest(); + uint32_t *bg2rgb = args.DestBlend(); + uint32_t fg = args.SrcColorIndex(); + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + pitch *= thread->num_cores; + + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int src_r = ((srccolor >> 16) & 0xff) * srcalpha; + int src_g = ((srccolor >> 0) & 0xff) * srcalpha; + int src_b = ((srccolor >> 8) & 0xff) * srcalpha; + int bg = *dest; + int r = MAX((src_r - palette[bg].r * destalpha)>>18, 0); + int g = MAX((src_g - palette[bg].g * destalpha)>>18, 0); + int b = MAX((src_b - palette[bg].b * destalpha)>>18, 0); + + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + } while (--count); + } + } + + void DrawColumnAddPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t fg = colormap[source[frac >> FRACBITS]]; + uint32_t bg = *dest; + + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + uint32_t srccolor = args.SrcColorIndex(); + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + uint32_t fg = colormap[source[frac >> FRACBITS]]; + uint32_t bg = *dest; + uint32_t r = MIN((palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 63); + uint32_t g = MIN((palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 63); + uint32_t b = MIN((palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 63); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnTranslatedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t* dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + // [RH] Local copies of global vars to improve compiler optimizations + const uint8_t *colormap = args.Colormap(); + const uint8_t *translation = args.TranslationMap(); + const uint8_t *source = args.TexturePixels(); + + do + { + *dest = colormap[translation[source[frac >> FRACBITS]]]; + dest += pitch; + + frac += fracstep; + } while (--count); + } + + void DrawColumnTlatedAddPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const uint8_t *translation = args.TranslationMap(); + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t fg = colormap[translation[source[frac >> FRACBITS]]]; + uint32_t bg = *dest; + + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + uint32_t fg = colormap[translation[source[frac >> FRACBITS]]]; + uint32_t bg = *dest; + uint32_t r = MIN((palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 63); + uint32_t g = MIN((palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 63); + uint32_t b = MIN((palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 63); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnShadedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac, fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *source = args.TexturePixels(); + const uint8_t *colormap = args.Colormap(); + uint32_t *fgstart = &Col2RGB8[0][args.SolidColor()]; + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t val = colormap[source[frac >> FRACBITS]]; + uint32_t fg = fgstart[val << 8]; + val = (Col2RGB8[64 - val][*dest] + fg) | 0x1f07c1f; + *dest = RGB32k.All[val & (val >> 15)]; + + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + int color = args.SolidColor(); + do + { + uint32_t val = source[frac >> FRACBITS]; + + int r = (palette[*dest].r * (255-val) + palette[color].r * val) >> 10; + int g = (palette[*dest].g * (255-val) + palette[color].g * val) >> 10; + int b = (palette[*dest].b * (255-val) + palette[color].b * val) >> 10; + *dest = RGB256k.RGB[clamp(r,0,63)][clamp(g,0,63)][clamp(b,0,63)]; + + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnAddClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = fg2rgb[colormap[source[frac >> FRACBITS]]] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[source[frac >> FRACBITS]]; + int bg = *dest; + int r = MIN((palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 63); + int g = MIN((palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 63); + int b = MIN((palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 63); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnAddClampTranslatedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *translation = args.TranslationMap(); + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = fg2rgb[colormap[translation[source[frac >> FRACBITS]]]] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[(a >> 15) & a]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[translation[source[frac >> FRACBITS]]]; + int bg = *dest; + int r = MIN((palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 63); + int g = MIN((palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 63); + int b = MIN((palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 63); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnSubClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (fg2rgb[colormap[source[frac >> FRACBITS]]] | 0x40100400) - bg2rgb[*dest]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[source[frac >> FRACBITS]]; + int bg = *dest; + int r = MAX((palette[fg].r * srcalpha - palette[bg].r * destalpha)>>18, 0); + int g = MAX((palette[fg].g * srcalpha - palette[bg].g * destalpha)>>18, 0); + int b = MAX((palette[fg].b * srcalpha - palette[bg].b * destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnSubClampTranslatedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *translation = args.TranslationMap(); + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (fg2rgb[colormap[translation[source[frac >> FRACBITS]]]] | 0x40100400) - bg2rgb[*dest]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[(a >> 15) & a]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[translation[source[frac >> FRACBITS]]]; + int bg = *dest; + int r = MAX((palette[fg].r * srcalpha - palette[bg].r * destalpha)>>18, 0); + int g = MAX((palette[fg].g * srcalpha - palette[bg].g * destalpha)>>18, 0); + int b = MAX((palette[fg].b * srcalpha - palette[bg].b * destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnRevSubClampPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[source[frac >> FRACBITS]]]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[a & (a >> 15)]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[source[frac >> FRACBITS]]; + int bg = *dest; + int r = MAX((-palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 0); + int g = MAX((-palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 0); + int b = MAX((-palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + void DrawColumnRevSubClampTranslatedPalCommand::Execute(DrawerThread *thread) + { + int count; + uint8_t *dest; + fixed_t frac; + fixed_t fracstep; + + count = args.Count(); + dest = args.Dest(); + + fracstep = args.TextureVStep(); + frac = args.TextureVPos(); + + count = thread->count_for_thread(args.DestY(), count); + if (count <= 0) + return; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * thread->skipped_by_thread(args.DestY()); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + const uint8_t *translation = args.TranslationMap(); + const uint8_t *colormap = args.Colormap(); + const uint8_t *source = args.TexturePixels(); + uint32_t *fg2rgb = args.SrcBlend(); + uint32_t *bg2rgb = args.DestBlend(); + const PalEntry *palette = GPalette.BaseColors; + + if (!r_blendmethod) + { + do + { + uint32_t a = (bg2rgb[*dest] | 0x40100400) - fg2rgb[colormap[translation[source[frac >> FRACBITS]]]]; + uint32_t b = a; + + b &= 0x40100400; + b = b - (b >> 5); + a &= b; + a |= 0x01f07c1f; + *dest = RGB32k.All[(a >> 15) & a]; + dest += pitch; + frac += fracstep; + } while (--count); + } + else + { + fixed_t srcalpha = args.SrcAlpha(); + fixed_t destalpha = args.DestAlpha(); + do + { + int fg = colormap[translation[source[frac >> FRACBITS]]]; + int bg = *dest; + int r = MAX((-palette[fg].r * srcalpha + palette[bg].r * destalpha)>>18, 0); + int g = MAX((-palette[fg].g * srcalpha + palette[bg].g * destalpha)>>18, 0); + int b = MAX((-palette[fg].b * srcalpha + palette[bg].b * destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + dest += pitch; + frac += fracstep; + } while (--count); + } + } + + ///////////////////////////////////////////////////////////////////////// + + DrawFuzzColumnPalCommand::DrawFuzzColumnPalCommand(const SpriteDrawerArgs &args) + { + _yl = args.FuzzY1(); + _yh = args.FuzzY2(); + _x = args.FuzzX(); + _destorg = RenderViewport::Instance()->GetDest(0, 0); + _fuzzpos = fuzzpos; + _fuzzviewheight = fuzzviewheight; + } + + void DrawFuzzColumnPalCommand::Execute(DrawerThread *thread) + { + int yl = MAX(_yl, 1); + int yh = MIN(_yh, _fuzzviewheight); + + int count = thread->count_for_thread(yl, yh - yl + 1); + + // Zero length. + if (count <= 0) + return; + + uint8_t *map = &NormalLight.Maps[6 * 256]; + + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + uint8_t *dest = thread->dest_for_thread(yl, pitch, yl * pitch + _x + _destorg); + + pitch = pitch * thread->num_cores; + int fuzzstep = thread->num_cores; + int fuzz = (_fuzzpos + thread->skipped_by_thread(yl)) % FUZZTABLE; + + yl += thread->skipped_by_thread(yl); + + // Handle the case where we would go out of bounds at the top: + if (yl < fuzzstep) + { + uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep + pitch; + //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (pitch)) < viewheight); + + *dest = map[*srcdest]; + dest += pitch; + fuzz += fuzzstep; + fuzz %= FUZZTABLE; + + count--; + if (count == 0) + return; + } + + bool lowerbounds = (yl + (count + fuzzstep - 1) * fuzzstep > _fuzzviewheight); + if (lowerbounds) + count--; + + // Fuzz where fuzzoffset stays within bounds + while (count > 0) + { + int available = (FUZZTABLE - fuzz); + int next_wrap = available / fuzzstep; + if (available % fuzzstep != 0) + next_wrap++; + + int cnt = MIN(count, next_wrap); + count -= cnt; + do + { + uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep; + //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (pitch)) < viewheight); + + *dest = map[*srcdest]; + dest += pitch; + fuzz += fuzzstep; + } while (--cnt); + + fuzz %= FUZZTABLE; + } + + // Handle the case where we would go out of bounds at the bottom + if (lowerbounds) + { + uint8_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep - pitch; + //assert(static_cast((srcdest - (uint8_t*)dc_destorg) / (pitch)) < viewheight); + + *dest = map[*srcdest]; + } + } + + ///////////////////////////////////////////////////////////////////////// + + PalSpanCommand::PalSpanCommand(const SpanDrawerArgs &args) + { + _source = args.TexturePixels(); + _colormap = args.Colormap(); + _xfrac = args.TextureUPos(); + _yfrac = args.TextureVPos(); + _y = args.DestY(); + _x1 = args.DestX1(); + _x2 = args.DestX2(); + _dest = RenderViewport::Instance()->GetDest(_x1, _y); + _xstep = args.TextureUStep(); + _ystep = args.TextureVStep(); + _xbits = args.TextureWidthBits(); + _ybits = args.TextureHeightBits(); + _srcblend = args.SrcBlend(); + _destblend = args.DestBlend(); + _color = args.SolidColor(); + _srcalpha = args.SrcAlpha(); + _destalpha = args.DestAlpha(); + _dynlights = args.dc_lights; + _num_dynlights = args.dc_num_lights; + _viewpos_x = args.dc_viewpos.X; + _step_viewpos_x = args.dc_viewpos_step.X; + } + + uint8_t PalSpanCommand::AddLights(const DrawerLight *lights, int num_lights, float viewpos_x, uint8_t fg, uint8_t material) + { + uint32_t lit_r = 0; + uint32_t lit_g = 0; + uint32_t lit_b = 0; + + for (int i = 0; i < num_lights; i++) + { + uint32_t light_color_r = RPART(lights[i].color); + uint32_t light_color_g = GPART(lights[i].color); + uint32_t light_color_b = BPART(lights[i].color); + + // L = light-pos + // dist = sqrt(dot(L, L)) + // attenuation = 1 - MIN(dist * (1/radius), 1) + float Lyz2 = lights[i].y; // L.y*L.y + L.z*L.z + float Lx = lights[i].x - viewpos_x; + float dist2 = Lyz2 + Lx * Lx; +#ifdef NO_SSE + float rcp_dist = 1.0f / (dist2 * 0.01f); +#else + float rcp_dist = _mm_cvtss_f32(_mm_rsqrt_ss(_mm_load_ss(&dist2))); +#endif + float dist = dist2 * rcp_dist; + float distance_attenuation = (256.0f - MIN(dist * lights[i].radius, 256.0f)); + + // The simple light type + float simple_attenuation = distance_attenuation; + + // The point light type + // diffuse = dot(N,L) * attenuation + float point_attenuation = lights[i].z * rcp_dist * distance_attenuation; + uint32_t attenuation = (uint32_t)(lights[i].z == 0.0f ? simple_attenuation : point_attenuation); + + lit_r += (light_color_r * attenuation) >> 8; + lit_g += (light_color_g * attenuation) >> 8; + lit_b += (light_color_b * attenuation) >> 8; + } + + if (lit_r == 0 && lit_g == 0 && lit_b == 0) + return fg; + + uint32_t material_r = GPalette.BaseColors[material].r; + uint32_t material_g = GPalette.BaseColors[material].g; + uint32_t material_b = GPalette.BaseColors[material].b; + + lit_r = MIN(GPalette.BaseColors[fg].r + ((lit_r * material_r) >> 8), 255); + lit_g = MIN(GPalette.BaseColors[fg].g + ((lit_g * material_g) >> 8), 255); + lit_b = MIN(GPalette.BaseColors[fg].b + ((lit_b * material_b) >> 8), 255); + + return RGB256k.All[((lit_r >> 2) << 12) | ((lit_g >> 2) << 6) | (lit_b >> 2)]; + } + + void DrawSpanPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + if (_xbits == 6 && _ybits == 6 && num_dynlights == 0) + { + // 64x64 is the most common case by far, so special case it. + do + { + // Current texture index in u,v. + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + + // Lookup pixel from flat texture tile, + // re-index using light/colormap. + *dest++ = colormap[source[spot]]; + + // Next step in u,v. + xfrac += xstep; + yfrac += ystep; + } while (--count); + } + else if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + // Current texture index in u,v. + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + + // Lookup pixel from flat texture tile, + // re-index using light/colormap. + *dest++ = AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]); + + // Next step in u,v. + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + + do + { + // Current texture index in u,v. + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + + // Lookup pixel from flat texture tile, + // re-index using light/colormap. + *dest++ = AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]); + + // Next step in u,v. + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + + void DrawSpanMaskedPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + int texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + *dest = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + int texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + *dest = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + + void DrawSpanTranslucentPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + uint32_t *fg2rgb = _srcblend; + uint32_t *bg2rgb = _destblend; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + const PalEntry *palette = GPalette.BaseColors; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + if (!r_blendmethod) + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest++ = RGB32k.All[fg & (fg >> 15)]; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest++ = RGB32k.All[fg & (fg >> 15)]; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + else + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest++ = RGB256k.RGB[r][g][b]; + + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest++ = RGB256k.RGB[r][g][b]; + + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + } + + void DrawSpanMaskedTranslucentPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + uint32_t *fg2rgb = _srcblend; + uint32_t *bg2rgb = _destblend; + + const PalEntry *palette = GPalette.BaseColors; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + if (!r_blendmethod) + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + uint8_t texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + uint8_t texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + fg = fg2rgb[fg]; + bg = bg2rgb[bg]; + fg = (fg + bg) | 0x1f07c1f; + *dest = RGB32k.All[fg & (fg >> 15)]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + else + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + uint8_t texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + uint8_t texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + } + + void DrawSpanAddClampPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + uint32_t *fg2rgb = _srcblend; + uint32_t *bg2rgb = _destblend; + const PalEntry *palette = GPalette.BaseColors; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + if (!r_blendmethod) + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t a = fg2rgb[fg] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest++ = RGB32k.All[a & (a >> 15)]; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t a = fg2rgb[fg] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest++ = RGB32k.All[a & (a >> 15)]; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + else + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest++ = RGB256k.RGB[r][g][b]; + + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[source[spot]], source[spot]) : colormap[source[spot]]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest++ = RGB256k.RGB[r][g][b]; + + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + } + + void DrawSpanMaskedAddClampPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + dsfixed_t xfrac; + dsfixed_t yfrac; + dsfixed_t xstep; + dsfixed_t ystep; + uint8_t *dest; + const uint8_t *source = _source; + const uint8_t *colormap = _colormap; + int count; + int spot; + uint32_t *fg2rgb = _srcblend; + uint32_t *bg2rgb = _destblend; + const PalEntry *palette = GPalette.BaseColors; + + const DrawerLight *dynlights = _dynlights; + int num_dynlights = _num_dynlights; + float viewpos_x = _viewpos_x; + float step_viewpos_x = _step_viewpos_x; + + xfrac = _xfrac; + yfrac = _yfrac; + + dest = _dest; + + count = _x2 - _x1 + 1; + + xstep = _xstep; + ystep = _ystep; + + if (!r_blendmethod) + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + uint8_t texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t a = fg2rgb[fg] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + uint8_t texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t a = fg2rgb[fg] + bg2rgb[*dest]; + uint32_t b = a; + + a |= 0x01f07c1f; + b &= 0x40100400; + a &= 0x3fffffff; + b = b - (b >> 5); + a |= b; + *dest = RGB32k.All[a & (a >> 15)]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + else + { + if (_xbits == 6 && _ybits == 6) + { + // 64x64 is the most common case by far, so special case it. + do + { + uint8_t texdata; + + spot = ((xfrac >> (32 - 6 - 6))&(63 * 64)) + (yfrac >> (32 - 6)); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + else + { + uint8_t yshift = 32 - _ybits; + uint8_t xshift = yshift - _xbits; + int xmask = ((1 << _xbits) - 1) << _ybits; + do + { + uint8_t texdata; + + spot = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + texdata = source[spot]; + if (texdata != 0) + { + uint32_t fg = num_dynlights != 0 ? AddLights(dynlights, num_dynlights, viewpos_x, colormap[texdata], texdata) : colormap[texdata]; + uint32_t bg = *dest; + int r = MAX((palette[fg].r * _srcalpha + palette[bg].r * _destalpha)>>18, 0); + int g = MAX((palette[fg].g * _srcalpha + palette[bg].g * _destalpha)>>18, 0); + int b = MAX((palette[fg].b * _srcalpha + palette[bg].b * _destalpha)>>18, 0); + *dest = RGB256k.RGB[r][g][b]; + } + dest++; + xfrac += xstep; + yfrac += ystep; + viewpos_x += step_viewpos_x; + } while (--count); + } + } + } + + void FillSpanPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + memset(_dest, _color, _x2 - _x1 + 1); + } + + ///////////////////////////////////////////////////////////////////////// + + DrawTiltedSpanPalCommand::DrawTiltedSpanPalCommand(const SpanDrawerArgs &args, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) + : y(y), x1(x1), x2(x2), plane_sz(plane_sz), plane_su(plane_su), plane_sv(plane_sv), plane_shade(plane_shade), planeshade(planeshade), planelightfloat(planelightfloat), pviewx(pviewx), pviewy(pviewy) + { + _colormap = args.Colormap(); + _dest = RenderViewport::Instance()->GetDest(x1, y); + _ybits = args.TextureHeightBits(); + _xbits = args.TextureWidthBits(); + _source = args.TexturePixels(); + basecolormapdata = basecolormap->Maps; + } + + void DrawTiltedSpanPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(y)) + return; + + const uint8_t **tiltlighting = thread->tiltlighting; + + int width = x2 - x1; + double iz, uz, vz; + uint8_t *fb; + uint32_t u, v; + int i; + + iz = plane_sz[2] + plane_sz[1] * (centery - y) + plane_sz[0] * (x1 - centerx); + + // Lighting is simple. It's just linear interpolation from start to end + if (plane_shade) + { + uz = (iz + plane_sz[0] * width) * planelightfloat; + vz = iz * planelightfloat; + CalcTiltedLighting(vz, uz, width, thread); + } + else + { + for (int i = 0; i < width; ++i) + { + tiltlighting[i] = _colormap; + } + } + + uz = plane_su[2] + plane_su[1] * (centery - y) + plane_su[0] * (x1 - centerx); + vz = plane_sv[2] + plane_sv[1] * (centery - y) + plane_sv[0] * (x1 - centerx); + + fb = _dest; + + uint8_t vshift = 32 - _ybits; + uint8_t ushift = vshift - _xbits; + int umask = ((1 << _xbits) - 1) << _ybits; + + #if 0 + // The "perfect" reference version of this routine. Pretty slow. + // Use it only to see how things are supposed to look. + i = 0; + do + { + double z = 1.f / iz; + + u = int64_t(uz*z) + pviewx; + v = int64_t(vz*z) + pviewy; + R_SetDSColorMapLight(tiltlighting[i], 0, 0); + fb[i++] = ds_colormap[ds_source[(v >> vshift) | ((u >> ushift) & umask)]]; + iz += plane_sz[0]; + uz += plane_su[0]; + vz += plane_sv[0]; + } while (--width >= 0); + #else + //#define SPANSIZE 32 + //#define INVSPAN 0.03125f + //#define SPANSIZE 8 + //#define INVSPAN 0.125f + #define SPANSIZE 16 + #define INVSPAN 0.0625f + + double startz = 1.f / iz; + double startu = uz*startz; + double startv = vz*startz; + double izstep, uzstep, vzstep; + + izstep = plane_sz[0] * SPANSIZE; + uzstep = plane_su[0] * SPANSIZE; + vzstep = plane_sv[0] * SPANSIZE; + x1 = 0; + width++; + + while (width >= SPANSIZE) + { + iz += izstep; + uz += uzstep; + vz += vzstep; + + double endz = 1.f / iz; + double endu = uz*endz; + double endv = vz*endz; + uint32_t stepu = (uint32_t)int64_t((endu - startu) * INVSPAN); + uint32_t stepv = (uint32_t)int64_t((endv - startv) * INVSPAN); + u = (uint32_t)(int64_t(startu) + pviewx); + v = (uint32_t)(int64_t(startv) + pviewy); + + for (i = SPANSIZE - 1; i >= 0; i--) + { + fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); + x1++; + u += stepu; + v += stepv; + } + startu = endu; + startv = endv; + width -= SPANSIZE; + } + if (width > 0) + { + if (width == 1) + { + u = (uint32_t)int64_t(startu); + v = (uint32_t)int64_t(startv); + fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); + } + else + { + double left = width; + iz += plane_sz[0] * left; + uz += plane_su[0] * left; + vz += plane_sv[0] * left; + + double endz = 1.f / iz; + double endu = uz*endz; + double endv = vz*endz; + left = 1.f / left; + uint32_t stepu = (uint32_t)int64_t((endu - startu) * left); + uint32_t stepv = (uint32_t)int64_t((endv - startv) * left); + u = (uint32_t)(int64_t(startu) + pviewx); + v = (uint32_t)(int64_t(startv) + pviewy); + + for (; width != 0; width--) + { + fb[x1] = *(tiltlighting[x1] + _source[(v >> vshift) | ((u >> ushift) & umask)]); + x1++; + u += stepu; + v += stepv; + } + } + } + #endif + } + + // Calculates the lighting for one row of a tilted plane. If the definition + // of GETPALOOKUP changes, this needs to change, too. + void DrawTiltedSpanPalCommand::CalcTiltedLighting(double lstart, double lend, int width, DrawerThread *thread) + { + const uint8_t **tiltlighting = thread->tiltlighting; + + uint8_t *lightstart = basecolormapdata + (GETPALOOKUP(lstart, planeshade) << COLORMAPSHIFT); + uint8_t *lightend = basecolormapdata + (GETPALOOKUP(lend, planeshade) << COLORMAPSHIFT); + + if (width == 0 || lightstart == lightend) + { + for (int i = 0; i <= width; i++) + { + tiltlighting[i] = lightstart; + } + } + else + { + double lstep = (lend - lstart) / width; + double lval = lstart; + for (int i = 0; i <= width; i++) + { + tiltlighting[i] = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); + lval += lstep; + } + } + } + + ///////////////////////////////////////////////////////////////////////// + + DrawColoredSpanPalCommand::DrawColoredSpanPalCommand(const SpanDrawerArgs &args, int y, int x1, int x2) : PalSpanCommand(args), y(y), x1(x1), x2(x2) + { + color = args.SolidColor(); + dest = RenderViewport::Instance()->GetDest(x1, y); + } + + void DrawColoredSpanPalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(y)) + return; + + memset(_dest, color, x2 - x1 + 1); + } + + ///////////////////////////////////////////////////////////////////////// + + DrawFogBoundaryLinePalCommand::DrawFogBoundaryLinePalCommand(const SpanDrawerArgs &args, int y, int x1, int x2) : PalSpanCommand(args), y(y), x1(x1), x2(x2) + { + _colormap = args.Colormap(); + _dest = RenderViewport::Instance()->GetDest(0, y); + } + + void DrawFogBoundaryLinePalCommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(y)) + return; + + const uint8_t *colormap = _colormap; + uint8_t *dest = _dest; + int x = x1; + do + { + dest[x] = colormap[dest[x]]; + } while (++x <= x2); + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawParticleColumnPalCommand::DrawParticleColumnPalCommand(uint8_t *dest, int dest_y, int pitch, int count, uint32_t fg, uint32_t alpha, uint32_t fracposx) + { + _dest = dest; + _pitch = pitch; + _count = count; + _fg = fg; + _alpha = alpha; + _fracposx = fracposx; + _dest_y = dest_y; + } + + void DrawParticleColumnPalCommand::Execute(DrawerThread *thread) + { + int count = thread->count_for_thread(_dest_y, _count); + if (count <= 0) + return; + + int pitch = _pitch; + uint8_t *dest = thread->dest_for_thread(_dest_y, pitch, _dest); + pitch = pitch * thread->num_cores; + + const uint32_t *source = &particle_texture[(_fracposx >> FRACBITS) * PARTICLE_TEXTURE_SIZE]; + uint32_t particle_alpha = _alpha; + + uint32_t fracstep = PARTICLE_TEXTURE_SIZE * FRACUNIT / _count; + uint32_t fracpos = fracstep * thread->skipped_by_thread(_dest_y) + fracstep / 2; + fracstep *= thread->num_cores; + + uint32_t fg_red = (_fg >> 16) & 0xff; + uint32_t fg_green = (_fg >> 8) & 0xff; + uint32_t fg_blue = _fg & 0xff; + + for (int y = 0; y < count; y++) + { + uint32_t alpha = (source[fracpos >> FRACBITS] * particle_alpha) >> 7; + uint32_t inv_alpha = 256 - alpha; + + int bg = *dest; + uint32_t bg_red = GPalette.BaseColors[bg].r; + uint32_t bg_green = GPalette.BaseColors[bg].g; + uint32_t bg_blue = GPalette.BaseColors[bg].b; + + uint32_t red = (fg_red * alpha + bg_red * inv_alpha) / 256; + uint32_t green = (fg_green * alpha + bg_green * inv_alpha) / 256; + uint32_t blue = (fg_blue * alpha + bg_blue * inv_alpha) / 256; + + *dest = RGB256k.All[((red >> 2) << 12) | ((green >> 2) << 6) | (blue >> 2)]; + dest += pitch; + fracpos += fracstep; + } + } + + FString DrawParticleColumnPalCommand::DebugInfo() + { + return "DrawParticle"; + } +} diff --git a/src/swrenderer/drawers/r_draw_pal.h b/src/swrenderer/drawers/r_draw_pal.h new file mode 100644 index 000000000..b57bfdf30 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_pal.h @@ -0,0 +1,259 @@ + +#pragma once + +#include "r_draw.h" +#include "v_palette.h" +#include "r_thread.h" +#include "swrenderer/viewport/r_skydrawer.h" +#include "swrenderer/viewport/r_spandrawer.h" +#include "swrenderer/viewport/r_walldrawer.h" +#include "swrenderer/viewport/r_spritedrawer.h" + +namespace swrenderer +{ + class PalWall1Command : public DrawerCommand + { + public: + PalWall1Command(const WallDrawerArgs &args); + FString DebugInfo() override { return "PalWallCommand"; } + + protected: + inline static uint8_t AddLights(const DrawerLight *lights, int num_lights, float viewpos_z, uint8_t fg, uint8_t material); + + WallDrawerArgs args; + }; + + class DrawWall1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallMasked1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallAdd1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallAddClamp1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallSubClamp1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + class DrawWallRevSubClamp1PalCommand : public PalWall1Command { public: using PalWall1Command::PalWall1Command; void Execute(DrawerThread *thread) override; }; + + class PalSkyCommand : public DrawerCommand + { + public: + PalSkyCommand(const SkyDrawerArgs &args); + FString DebugInfo() override { return "PalSkyCommand"; } + + protected: + SkyDrawerArgs args; + }; + + class DrawSingleSky1PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; + class DrawDoubleSky1PalCommand : public PalSkyCommand { public: using PalSkyCommand::PalSkyCommand; void Execute(DrawerThread *thread) override; }; + + class PalColumnCommand : public DrawerCommand + { + public: + PalColumnCommand(const SpriteDrawerArgs &args); + FString DebugInfo() override { return "PalColumnCommand"; } + + protected: + uint8_t AddLights(uint8_t fg, uint8_t material, uint32_t lit_r, uint32_t lit_g, uint32_t lit_b); + + SpriteDrawerArgs args; + }; + + class DrawColumnPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnAddPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnAddClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnSubClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class FillColumnRevSubClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnAddPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnTranslatedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnTlatedAddPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnShadedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnAddClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnAddClampTranslatedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnSubClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnSubClampTranslatedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnRevSubClampPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + class DrawColumnRevSubClampTranslatedPalCommand : public PalColumnCommand { public: using PalColumnCommand::PalColumnCommand; void Execute(DrawerThread *thread) override; }; + + class DrawFuzzColumnPalCommand : public DrawerCommand + { + public: + DrawFuzzColumnPalCommand(const SpriteDrawerArgs &args); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override { return "DrawFuzzColumnPalCommand"; } + + private: + int _yl; + int _yh; + int _x; + uint8_t *_destorg; + int _fuzzpos; + int _fuzzviewheight; + }; + + class PalSpanCommand : public DrawerCommand + { + public: + PalSpanCommand(const SpanDrawerArgs &args); + FString DebugInfo() override { return "PalSpanCommand"; } + + protected: + inline static uint8_t AddLights(const DrawerLight *lights, int num_lights, float viewpos_x, uint8_t fg, uint8_t material); + + const uint8_t *_source; + const uint8_t *_colormap; + dsfixed_t _xfrac; + dsfixed_t _yfrac; + int _y; + int _x1; + int _x2; + uint8_t *_dest; + dsfixed_t _xstep; + dsfixed_t _ystep; + int _xbits; + int _ybits; + uint32_t *_srcblend; + uint32_t *_destblend; + int _color; + fixed_t _srcalpha; + fixed_t _destalpha; + DrawerLight *_dynlights; + int _num_dynlights; + float _viewpos_x; + float _step_viewpos_x; + }; + + class DrawSpanPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanMaskedPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanTranslucentPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanMaskedTranslucentPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanAddClampPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class DrawSpanMaskedAddClampPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + class FillSpanPalCommand : public PalSpanCommand { public: using PalSpanCommand::PalSpanCommand; void Execute(DrawerThread *thread) override; }; + + class DrawTiltedSpanPalCommand : public DrawerCommand + { + public: + DrawTiltedSpanPalCommand(const SpanDrawerArgs &args, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override { return "DrawTiltedSpanPalCommand"; } + + private: + void CalcTiltedLighting(double lval, double lend, int width, DrawerThread *thread); + + int y; + int x1; + int x2; + FVector3 plane_sz; + FVector3 plane_su; + FVector3 plane_sv; + bool plane_shade; + int planeshade; + float planelightfloat; + fixed_t pviewx; + fixed_t pviewy; + + const uint8_t *_colormap; + uint8_t *_dest; + int _ybits; + int _xbits; + const uint8_t *_source; + uint8_t *basecolormapdata; + }; + + class DrawColoredSpanPalCommand : public PalSpanCommand + { + public: + DrawColoredSpanPalCommand(const SpanDrawerArgs &args, int y, int x1, int x2); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override { return "DrawColoredSpanPalCommand"; } + + private: + int y; + int x1; + int x2; + int color; + uint8_t *dest; + }; + + class DrawFogBoundaryLinePalCommand : public PalSpanCommand + { + public: + DrawFogBoundaryLinePalCommand(const SpanDrawerArgs &args, int y, int x1, int x2); + void Execute(DrawerThread *thread) override; + + private: + int y, x1, x2; + const uint8_t *_colormap; + uint8_t *_dest; + }; + + class DrawParticleColumnPalCommand : public DrawerCommand + { + public: + DrawParticleColumnPalCommand(uint8_t *dest, int dest_y, int pitch, int count, uint32_t fg, uint32_t alpha, uint32_t fracposx); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + + private: + uint8_t *_dest; + int _dest_y; + int _pitch; + int _count; + uint32_t _fg; + uint32_t _alpha; + uint32_t _fracposx; + }; + + class SWPalDrawers : public SWPixelFormatDrawers + { + public: + using SWPixelFormatDrawers::SWPixelFormatDrawers; + + void DrawWallColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + void DrawWallMaskedColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + + void DrawWallAddColumn(const WallDrawerArgs &args) override + { + if (args.dc_num_lights == 0) + Queue->Push(args); + else + Queue->Push(args); + } + + void DrawWallAddClampColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + void DrawWallSubClampColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + void DrawWallRevSubClampColumn(const WallDrawerArgs &args) override { Queue->Push(args); } + void DrawSingleSkyColumn(const SkyDrawerArgs &args) override { Queue->Push(args); } + void DrawDoubleSkyColumn(const SkyDrawerArgs &args) override { Queue->Push(args); } + void DrawColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillAddColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillAddClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillSubClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void FillRevSubClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawFuzzColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); R_UpdateFuzzPos(args); } + void DrawAddColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawTranslatedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawTranslatedAddColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawShadedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawAddClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawAddClampTranslatedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawSubClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawSubClampTranslatedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawRevSubClampColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawRevSubClampTranslatedColumn(const SpriteDrawerArgs &args) override { Queue->Push(args); } + void DrawSpan(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanMasked(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanTranslucent(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanMaskedTranslucent(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanAddClamp(const SpanDrawerArgs &args) override { Queue->Push(args); } + void DrawSpanMaskedAddClamp(const SpanDrawerArgs &args) override { Queue->Push(args); } + void FillSpan(const SpanDrawerArgs &args) override { Queue->Push(args); } + + void DrawTiltedSpan(const SpanDrawerArgs &args, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) override + { + Queue->Push(args, y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy, basecolormap); + } + + void DrawColoredSpan(const SpanDrawerArgs &args, int y, int x1, int x2) override { Queue->Push(args, y, x1, x2); } + void DrawFogBoundaryLine(const SpanDrawerArgs &args, int y, int x1, int x2) override { Queue->Push(args, y, x1, x2); } + }; +} diff --git a/src/swrenderer/drawers/r_draw_rgba.cpp b/src/swrenderer/drawers/r_draw_rgba.cpp new file mode 100644 index 000000000..48bbc2c38 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_rgba.cpp @@ -0,0 +1,869 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// True color span/column drawing functions. +// +//----------------------------------------------------------------------------- + +#include + +#include "templates.h" +#include "doomdef.h" +#include "i_system.h" +#include "w_wad.h" +#include "v_video.h" +#include "doomstat.h" +#include "st_stuff.h" +#include "g_game.h" +#include "g_level.h" +#include "r_data/r_translate.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_draw_rgba.h" +#include "gl/data/gl_matrix.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" +#include "r_draw_wall32_sse2.h" +#include "r_draw_sprite32_sse2.h" +#include "r_draw_span32_sse2.h" +#include "r_draw_sky32_sse2.h" + +#include "gi.h" +#include "stats.h" +#include "x86.h" +#include + +// Use linear filtering when scaling up +CVAR(Bool, r_magfilter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +// Use linear filtering when scaling down +CVAR(Bool, r_minfilter, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +// Use mipmapped textures +CVAR(Bool, r_mipmap, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +// Level of detail texture bias +CVAR(Float, r_lod_bias, -1.5, 0); // To do: add CVAR_ARCHIVE | CVAR_GLOBALCONFIG when a good default has been decided + +namespace swrenderer +{ + void SWTruecolorDrawers::DrawWallColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallMaskedColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallAddColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallAddClampColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallSubClampColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawWallRevSubClampColumn(const WallDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillAddColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillAddClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillSubClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::FillRevSubClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawFuzzColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + R_UpdateFuzzPos(args); + } + + void SWTruecolorDrawers::DrawAddColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawTranslatedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawTranslatedAddColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawShadedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawAddClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawAddClampTranslatedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSubClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSubClampTranslatedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawRevSubClampColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawRevSubClampTranslatedColumn(const SpriteDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpan(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanMasked(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanTranslucent(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanMaskedTranslucent(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanAddClamp(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSpanMaskedAddClamp(const SpanDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawSingleSkyColumn(const SkyDrawerArgs &args) + { + Queue->Push(args); + } + + void SWTruecolorDrawers::DrawDoubleSkyColumn(const SkyDrawerArgs &args) + { + Queue->Push(args); + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawFuzzColumnRGBACommand::DrawFuzzColumnRGBACommand(const SpriteDrawerArgs &drawerargs) + { + _x = drawerargs.FuzzX(); + _yl = drawerargs.FuzzY1(); + _yh = drawerargs.FuzzY2(); + _destorg = RenderViewport::Instance()->GetDest(0, 0); + _pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + _fuzzpos = fuzzpos; + _fuzzviewheight = fuzzviewheight; + } + + void DrawFuzzColumnRGBACommand::Execute(DrawerThread *thread) + { + int yl = MAX(_yl, 1); + int yh = MIN(_yh, _fuzzviewheight); + + int count = thread->count_for_thread(yl, yh - yl + 1); + + // Zero length. + if (count <= 0) + return; + + uint32_t *dest = thread->dest_for_thread(yl, _pitch, _pitch * yl + _x + (uint32_t*)_destorg); + + int pitch = _pitch * thread->num_cores; + int fuzzstep = thread->num_cores; + int fuzz = (_fuzzpos + thread->skipped_by_thread(yl)) % FUZZTABLE; + + yl += thread->skipped_by_thread(yl); + + // Handle the case where we would go out of bounds at the top: + if (yl < fuzzstep) + { + uint32_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep + pitch; + //assert(static_cast((srcdest - (uint32_t*)dc_destorg) / (_pitch)) < viewheight); + + uint32_t bg = *srcdest; + + uint32_t red = RPART(bg) * 3 / 4; + uint32_t green = GPART(bg) * 3 / 4; + uint32_t blue = BPART(bg) * 3 / 4; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + dest += pitch; + fuzz += fuzzstep; + fuzz %= FUZZTABLE; + + count--; + if (count == 0) + return; + } + + bool lowerbounds = (yl + (count + fuzzstep - 1) * fuzzstep > _fuzzviewheight); + if (lowerbounds) + count--; + + // Fuzz where fuzzoffset stays within bounds + while (count > 0) + { + int available = (FUZZTABLE - fuzz); + int next_wrap = available / fuzzstep; + if (available % fuzzstep != 0) + next_wrap++; + + int cnt = MIN(count, next_wrap); + count -= cnt; + do + { + uint32_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep; + //assert(static_cast((srcdest - (uint32_t*)dc_destorg) / (_pitch)) < viewheight); + + uint32_t bg = *srcdest; + + uint32_t red = RPART(bg) * 3 / 4; + uint32_t green = GPART(bg) * 3 / 4; + uint32_t blue = BPART(bg) * 3 / 4; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + dest += pitch; + fuzz += fuzzstep; + } while (--cnt); + + fuzz %= FUZZTABLE; + } + + // Handle the case where we would go out of bounds at the bottom + if (lowerbounds) + { + uint32_t *srcdest = dest + fuzzoffset[fuzz] * fuzzstep - pitch; + //assert(static_cast((srcdest - (uint32_t*)dc_destorg) / (_pitch)) < viewheight); + + uint32_t bg = *srcdest; + + uint32_t red = RPART(bg) * 3 / 4; + uint32_t green = GPART(bg) * 3 / 4; + uint32_t blue = BPART(bg) * 3 / 4; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + } + } + + FString DrawFuzzColumnRGBACommand::DebugInfo() + { + return "DrawFuzzColumn"; + } + + ///////////////////////////////////////////////////////////////////////////// + + FillSpanRGBACommand::FillSpanRGBACommand(const SpanDrawerArgs &drawerargs) + { + _x1 = drawerargs.DestX1(); + _x2 = drawerargs.DestX2(); + _y = drawerargs.DestY(); + _dest = RenderViewport::Instance()->GetDest(_x1, _y); + _light = drawerargs.Light(); + _color = drawerargs.SolidColor(); + } + + void FillSpanRGBACommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + uint32_t *dest = (uint32_t*)_dest; + int count = (_x2 - _x1 + 1); + uint32_t light = LightBgra::calc_light_multiplier(_light); + uint32_t color = LightBgra::shade_pal_index_simple(_color, light); + for (int i = 0; i < count; i++) + dest[i] = color; + } + + FString FillSpanRGBACommand::DebugInfo() + { + return "FillSpan"; + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawFogBoundaryLineRGBACommand::DrawFogBoundaryLineRGBACommand(const SpanDrawerArgs &drawerargs, int y, int x, int x2) + { + _y = y; + _x = x; + _x2 = x2; + + _line = RenderViewport::Instance()->GetDest(0, y); + _light = drawerargs.Light(); + _shade_constants = drawerargs.ColormapConstants(); + } + + void DrawFogBoundaryLineRGBACommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + int y = _y; + int x = _x; + int x2 = _x2; + + uint32_t *dest = (uint32_t*)_line; + + uint32_t light = LightBgra::calc_light_multiplier(_light); + ShadeConstants constants = _shade_constants; + + do + { + uint32_t red = (dest[x] >> 16) & 0xff; + uint32_t green = (dest[x] >> 8) & 0xff; + uint32_t blue = dest[x] & 0xff; + + if (constants.simple_shade) + { + red = red * light / 256; + green = green * light / 256; + blue = blue * light / 256; + } + else + { + uint32_t inv_light = 256 - light; + uint32_t inv_desaturate = 256 - constants.desaturate; + + uint32_t intensity = ((red * 77 + green * 143 + blue * 37) >> 8) * constants.desaturate; + + red = (red * inv_desaturate + intensity) / 256; + green = (green * inv_desaturate + intensity) / 256; + blue = (blue * inv_desaturate + intensity) / 256; + + red = (constants.fade_red * inv_light + red * light) / 256; + green = (constants.fade_green * inv_light + green * light) / 256; + blue = (constants.fade_blue * inv_light + blue * light) / 256; + + red = (red * constants.light_red) / 256; + green = (green * constants.light_green) / 256; + blue = (blue * constants.light_blue) / 256; + } + + dest[x] = 0xff000000 | (red << 16) | (green << 8) | blue; + } while (++x <= x2); + } + + FString DrawFogBoundaryLineRGBACommand::DebugInfo() + { + return "DrawFogBoundaryLine"; + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawTiltedSpanRGBACommand::DrawTiltedSpanRGBACommand(const SpanDrawerArgs &drawerargs, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy) + { + _x1 = x1; + _x2 = x2; + _y = y; + _dest = RenderViewport::Instance()->GetDest(_x1, _y); + _light = drawerargs.Light(); + _shade_constants = drawerargs.ColormapConstants(); + _plane_sz = plane_sz; + _plane_su = plane_su; + _plane_sv = plane_sv; + _plane_shade = plane_shade; + _planeshade = planeshade; + _planelightfloat = planelightfloat; + _pviewx = pviewx; + _pviewy = pviewy; + _source = (const uint32_t*)drawerargs.TexturePixels(); + _xbits = drawerargs.TextureWidthBits(); + _ybits = drawerargs.TextureHeightBits(); + } + + void DrawTiltedSpanRGBACommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + //#define SPANSIZE 32 + //#define INVSPAN 0.03125f + //#define SPANSIZE 8 + //#define INVSPAN 0.125f + #define SPANSIZE 16 + #define INVSPAN 0.0625f + + int source_width = 1 << _xbits; + int source_height = 1 << _ybits; + + uint32_t *dest = (uint32_t*)_dest; + int count = _x2 - _x1 + 1; + + // Depth (Z) change across the span + double iz = _plane_sz[2] + _plane_sz[1] * (centery - _y) + _plane_sz[0] * (_x1 - centerx); + + // Light change across the span + fixed_t lightstart = _light; + fixed_t lightend = lightstart; + if (_plane_shade) + { + double vis_start = iz * _planelightfloat; + double vis_end = (iz + _plane_sz[0] * count) * _planelightfloat; + + lightstart = LIGHTSCALE(vis_start, _planeshade); + lightend = LIGHTSCALE(vis_end, _planeshade); + } + fixed_t light = lightstart; + fixed_t steplight = (lightend - lightstart) / count; + + // Texture coordinates + double uz = _plane_su[2] + _plane_su[1] * (centery - _y) + _plane_su[0] * (_x1 - centerx); + double vz = _plane_sv[2] + _plane_sv[1] * (centery - _y) + _plane_sv[0] * (_x1 - centerx); + double startz = 1.f / iz; + double startu = uz*startz; + double startv = vz*startz; + double izstep = _plane_sz[0] * SPANSIZE; + double uzstep = _plane_su[0] * SPANSIZE; + double vzstep = _plane_sv[0] * SPANSIZE; + + // Linear interpolate in sizes of SPANSIZE to increase speed + while (count >= SPANSIZE) + { + iz += izstep; + uz += uzstep; + vz += vzstep; + + double endz = 1.f / iz; + double endu = uz*endz; + double endv = vz*endz; + uint32_t stepu = (uint32_t)(SQWORD((endu - startu) * INVSPAN)); + uint32_t stepv = (uint32_t)(SQWORD((endv - startv) * INVSPAN)); + uint32_t u = (uint32_t)(SQWORD(startu) + _pviewx); + uint32_t v = (uint32_t)(SQWORD(startv) + _pviewy); + + for (int i = 0; i < SPANSIZE; i++) + { + uint32_t sx = ((u >> 16) * source_width) >> 16; + uint32_t sy = ((v >> 16) * source_height) >> 16; + uint32_t fg = _source[sy + sx * source_height]; + + if (_shade_constants.simple_shade) + *(dest++) = LightBgra::shade_bgra_simple(fg, LightBgra::calc_light_multiplier(light)); + else + *(dest++) = LightBgra::shade_bgra(fg, LightBgra::calc_light_multiplier(light), _shade_constants); + + u += stepu; + v += stepv; + light += steplight; + } + startu = endu; + startv = endv; + count -= SPANSIZE; + } + + // The last few pixels at the end + while (count > 0) + { + double endz = 1.f / iz; + startu = uz*endz; + startv = vz*endz; + uint32_t u = (uint32_t)(SQWORD(startu) + _pviewx); + uint32_t v = (uint32_t)(SQWORD(startv) + _pviewy); + + uint32_t sx = ((u >> 16) * source_width) >> 16; + uint32_t sy = ((v >> 16) * source_height) >> 16; + uint32_t fg = _source[sy + sx * source_height]; + + if (_shade_constants.simple_shade) + *(dest++) = LightBgra::shade_bgra_simple(fg, LightBgra::calc_light_multiplier(light)); + else + *(dest++) = LightBgra::shade_bgra(fg, LightBgra::calc_light_multiplier(light), _shade_constants); + + iz += _plane_sz[0]; + uz += _plane_su[0]; + vz += _plane_sv[0]; + light += steplight; + count--; + } + } + + FString DrawTiltedSpanRGBACommand::DebugInfo() + { + return "DrawTiltedSpan"; + } + + ///////////////////////////////////////////////////////////////////////////// + + DrawColoredSpanRGBACommand::DrawColoredSpanRGBACommand(const SpanDrawerArgs &drawerargs, int y, int x1, int x2) + { + _y = y; + _x1 = x1; + _x2 = x2; + + _dest = RenderViewport::Instance()->GetDest(_x1, _y); + _light = drawerargs.Light(); + _color = drawerargs.SolidColor(); + } + + void DrawColoredSpanRGBACommand::Execute(DrawerThread *thread) + { + if (thread->line_skipped_by_thread(_y)) + return; + + int y = _y; + int x1 = _x1; + int x2 = _x2; + + uint32_t *dest = (uint32_t*)_dest; + int count = (x2 - x1 + 1); + uint32_t light = LightBgra::calc_light_multiplier(_light); + uint32_t color = LightBgra::shade_pal_index_simple(_color, light); + for (int i = 0; i < count; i++) + dest[i] = color; + } + + FString DrawColoredSpanRGBACommand::DebugInfo() + { + return "DrawColoredSpan"; + } + + ///////////////////////////////////////////////////////////////////////////// + + FillTransColumnRGBACommand::FillTransColumnRGBACommand(const DrawerArgs &drawerargs, int x, int y1, int y2, int color, int a) + { + _x = x; + _y1 = y1; + _y2 = y2; + _color = color; + _a = a; + + _destorg = RenderViewport::Instance()->GetDest(0, 0); + _pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + } + + void FillTransColumnRGBACommand::Execute(DrawerThread *thread) + { + int x = _x; + int y1 = _y1; + int y2 = _y2; + int color = _color; + int a = _a; + + int ycount = thread->count_for_thread(y1, y2 - y1 + 1); + if (ycount <= 0) + return; + + uint32_t fg = GPalette.BaseColors[color].d; + uint32_t fg_red = (fg >> 16) & 0xff; + uint32_t fg_green = (fg >> 8) & 0xff; + uint32_t fg_blue = fg & 0xff; + + uint32_t alpha = a + 1; + uint32_t inv_alpha = 256 - alpha; + + fg_red *= alpha; + fg_green *= alpha; + fg_blue *= alpha; + + int spacing = _pitch * thread->num_cores; + uint32_t *dest = thread->dest_for_thread(y1, _pitch, _pitch * y1 + x + (uint32_t*)_destorg); + + for (int y = 0; y < ycount; y++) + { + uint32_t bg_red = (*dest >> 16) & 0xff; + uint32_t bg_green = (*dest >> 8) & 0xff; + uint32_t bg_blue = (*dest) & 0xff; + + uint32_t red = (fg_red + bg_red * inv_alpha) / 256; + uint32_t green = (fg_green + bg_green * inv_alpha) / 256; + uint32_t blue = (fg_blue + bg_blue * inv_alpha) / 256; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + dest += spacing; + } + } + + FString FillTransColumnRGBACommand::DebugInfo() + { + return "FillTransColumn"; + } + + ///////////////////////////////////////////////////////////////////////////// + + ApplySpecialColormapRGBACommand::ApplySpecialColormapRGBACommand(FSpecialColormap *colormap, DFrameBuffer *screen) + { + buffer = screen->GetBuffer(); + pitch = screen->GetPitch(); + width = screen->GetWidth(); + height = screen->GetHeight(); + + start_red = (int)(colormap->ColorizeStart[0] * 255); + start_green = (int)(colormap->ColorizeStart[1] * 255); + start_blue = (int)(colormap->ColorizeStart[2] * 255); + end_red = (int)(colormap->ColorizeEnd[0] * 255); + end_green = (int)(colormap->ColorizeEnd[1] * 255); + end_blue = (int)(colormap->ColorizeEnd[2] * 255); + } + +#ifdef NO_SSE + void ApplySpecialColormapRGBACommand::Execute(DrawerThread *thread) + { + int y = thread->skipped_by_thread(0); + int count = thread->count_for_thread(0, height); + while (count > 0) + { + uint8_t *pixels = buffer + y * pitch * 4; + for (int x = 0; x < width; x++) + { + int fg_red = pixels[2]; + int fg_green = pixels[1]; + int fg_blue = pixels[0]; + + int gray = (fg_red * 77 + fg_green * 143 + fg_blue * 37) >> 8; + gray += (gray >> 7); // gray*=256/255 + int inv_gray = 256 - gray; + + int red = clamp((start_red * inv_gray + end_red * gray) >> 8, 0, 255); + int green = clamp((start_green * inv_gray + end_green * gray) >> 8, 0, 255); + int blue = clamp((start_blue * inv_gray + end_blue * gray) >> 8, 0, 255); + + pixels[0] = (uint8_t)blue; + pixels[1] = (uint8_t)green; + pixels[2] = (uint8_t)red; + pixels[3] = 0xff; + + pixels += 4; + } + y += thread->num_cores; + count--; + } + } +#else + void ApplySpecialColormapRGBACommand::Execute(DrawerThread *thread) + { + int y = thread->skipped_by_thread(0); + int count = thread->count_for_thread(0, height); + __m128i gray_weight = _mm_set_epi16(256, 77, 143, 37, 256, 77, 143, 37); + __m128i start_end = _mm_set_epi16(255, start_red, start_green, start_blue, 255, end_red, end_green, end_blue); + while (count > 0) + { + uint8_t *pixels = buffer + y * pitch * 4; + int sse_length = width / 4; + for (int x = 0; x < sse_length; x++) + { + // Unpack to integers: + __m128i p = _mm_loadu_si128((const __m128i*)pixels); + + __m128i p16_0 = _mm_unpacklo_epi8(p, _mm_setzero_si128()); + __m128i p16_1 = _mm_unpackhi_epi8(p, _mm_setzero_si128()); + + // Add gray weighting to colors + __m128i mullo0 = _mm_mullo_epi16(p16_0, gray_weight); + __m128i mullo1 = _mm_mullo_epi16(p16_1, gray_weight); + __m128i p32_0 = _mm_unpacklo_epi16(mullo0, _mm_setzero_si128()); + __m128i p32_1 = _mm_unpackhi_epi16(mullo0, _mm_setzero_si128()); + __m128i p32_2 = _mm_unpacklo_epi16(mullo1, _mm_setzero_si128()); + __m128i p32_3 = _mm_unpackhi_epi16(mullo1, _mm_setzero_si128()); + + // Transpose to get color components in individual vectors: + __m128 tmpx = _mm_castsi128_ps(p32_0); + __m128 tmpy = _mm_castsi128_ps(p32_1); + __m128 tmpz = _mm_castsi128_ps(p32_2); + __m128 tmpw = _mm_castsi128_ps(p32_3); + _MM_TRANSPOSE4_PS(tmpx, tmpy, tmpz, tmpw); + __m128i blue = _mm_castps_si128(tmpx); + __m128i green = _mm_castps_si128(tmpy); + __m128i red = _mm_castps_si128(tmpz); + __m128i alpha = _mm_castps_si128(tmpw); + + // Calculate gray and 256-gray values: + __m128i gray = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(red, green), blue), 8); + __m128i inv_gray = _mm_sub_epi32(_mm_set1_epi32(256), gray); + + // p32 = start * inv_gray + end * gray: + __m128i gray0 = _mm_shuffle_epi32(gray, _MM_SHUFFLE(0, 0, 0, 0)); + __m128i gray1 = _mm_shuffle_epi32(gray, _MM_SHUFFLE(1, 1, 1, 1)); + __m128i gray2 = _mm_shuffle_epi32(gray, _MM_SHUFFLE(2, 2, 2, 2)); + __m128i gray3 = _mm_shuffle_epi32(gray, _MM_SHUFFLE(3, 3, 3, 3)); + __m128i inv_gray0 = _mm_shuffle_epi32(inv_gray, _MM_SHUFFLE(0, 0, 0, 0)); + __m128i inv_gray1 = _mm_shuffle_epi32(inv_gray, _MM_SHUFFLE(1, 1, 1, 1)); + __m128i inv_gray2 = _mm_shuffle_epi32(inv_gray, _MM_SHUFFLE(2, 2, 2, 2)); + __m128i inv_gray3 = _mm_shuffle_epi32(inv_gray, _MM_SHUFFLE(3, 3, 3, 3)); + __m128i gray16_0 = _mm_packs_epi32(gray0, inv_gray0); + __m128i gray16_1 = _mm_packs_epi32(gray1, inv_gray1); + __m128i gray16_2 = _mm_packs_epi32(gray2, inv_gray2); + __m128i gray16_3 = _mm_packs_epi32(gray3, inv_gray3); + __m128i gray16_0_mullo = _mm_mullo_epi16(gray16_0, start_end); + __m128i gray16_1_mullo = _mm_mullo_epi16(gray16_1, start_end); + __m128i gray16_2_mullo = _mm_mullo_epi16(gray16_2, start_end); + __m128i gray16_3_mullo = _mm_mullo_epi16(gray16_3, start_end); + __m128i gray16_0_mulhi = _mm_mulhi_epi16(gray16_0, start_end); + __m128i gray16_1_mulhi = _mm_mulhi_epi16(gray16_1, start_end); + __m128i gray16_2_mulhi = _mm_mulhi_epi16(gray16_2, start_end); + __m128i gray16_3_mulhi = _mm_mulhi_epi16(gray16_3, start_end); + p32_0 = _mm_srli_epi32(_mm_add_epi32(_mm_unpacklo_epi16(gray16_0_mullo, gray16_0_mulhi), _mm_unpackhi_epi16(gray16_0_mullo, gray16_0_mulhi)), 8); + p32_1 = _mm_srli_epi32(_mm_add_epi32(_mm_unpacklo_epi16(gray16_1_mullo, gray16_1_mulhi), _mm_unpackhi_epi16(gray16_1_mullo, gray16_1_mulhi)), 8); + p32_2 = _mm_srli_epi32(_mm_add_epi32(_mm_unpacklo_epi16(gray16_2_mullo, gray16_2_mulhi), _mm_unpackhi_epi16(gray16_2_mullo, gray16_2_mulhi)), 8); + p32_3 = _mm_srli_epi32(_mm_add_epi32(_mm_unpacklo_epi16(gray16_3_mullo, gray16_3_mulhi), _mm_unpackhi_epi16(gray16_3_mullo, gray16_3_mulhi)), 8); + + p16_0 = _mm_packs_epi32(p32_0, p32_1); + p16_1 = _mm_packs_epi32(p32_2, p32_3); + p = _mm_packus_epi16(p16_0, p16_1); + + _mm_storeu_si128((__m128i*)pixels, p); + pixels += 16; + } + + for (int x = sse_length * 4; x < width; x++) + { + int fg_red = pixels[2]; + int fg_green = pixels[1]; + int fg_blue = pixels[0]; + + int gray = (fg_red * 77 + fg_green * 143 + fg_blue * 37) >> 8; + gray += (gray >> 7); // gray*=256/255 + int inv_gray = 256 - gray; + + int red = clamp((start_red * inv_gray + end_red * gray) >> 8, 0, 255); + int green = clamp((start_green * inv_gray + end_green * gray) >> 8, 0, 255); + int blue = clamp((start_blue * inv_gray + end_blue * gray) >> 8, 0, 255); + + pixels[0] = (uint8_t)blue; + pixels[1] = (uint8_t)green; + pixels[2] = (uint8_t)red; + pixels[3] = 0xff; + + pixels += 4; + } + + y += thread->num_cores; + count--; + } + } +#endif + + ///////////////////////////////////////////////////////////////////////////// + + DrawParticleColumnRGBACommand::DrawParticleColumnRGBACommand(uint32_t *dest, int dest_y, int pitch, int count, uint32_t fg, uint32_t alpha, uint32_t fracposx) + { + _dest = dest; + _pitch = pitch; + _count = count; + _fg = fg; + _alpha = alpha; + _fracposx = fracposx; + _dest_y = dest_y; + } + + void DrawParticleColumnRGBACommand::Execute(DrawerThread *thread) + { + int count = thread->count_for_thread(_dest_y, _count); + if (count <= 0) + return; + + uint32_t *dest = thread->dest_for_thread(_dest_y, _pitch, _dest); + int pitch = _pitch * thread->num_cores; + + const uint32_t *source = &particle_texture[(_fracposx >> FRACBITS) * PARTICLE_TEXTURE_SIZE]; + uint32_t particle_alpha = _alpha; + + uint32_t fracstep = PARTICLE_TEXTURE_SIZE * FRACUNIT / _count; + uint32_t fracpos = fracstep * thread->skipped_by_thread(_dest_y) + fracstep / 2; + fracstep *= thread->num_cores; + + uint32_t fg_red = (_fg >> 16) & 0xff; + uint32_t fg_green = (_fg >> 8) & 0xff; + uint32_t fg_blue = _fg & 0xff; + + for (int y = 0; y < count; y++) + { + uint32_t alpha = (source[fracpos >> FRACBITS] * particle_alpha) >> 7; + uint32_t inv_alpha = 256 - alpha; + + uint32_t bg_red = (*dest >> 16) & 0xff; + uint32_t bg_green = (*dest >> 8) & 0xff; + uint32_t bg_blue = (*dest) & 0xff; + + uint32_t red = (fg_red * alpha + bg_red * inv_alpha) / 256; + uint32_t green = (fg_green * alpha + bg_green * inv_alpha) / 256; + uint32_t blue = (fg_blue * alpha + bg_blue * inv_alpha) / 256; + + *dest = 0xff000000 | (red << 16) | (green << 8) | blue; + dest += pitch; + fracpos += fracstep; + } + } + + FString DrawParticleColumnRGBACommand::DebugInfo() + { + return "DrawParticle"; + } + +} diff --git a/src/swrenderer/drawers/r_draw_rgba.h b/src/swrenderer/drawers/r_draw_rgba.h new file mode 100644 index 000000000..3e95c8cb6 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_rgba.h @@ -0,0 +1,390 @@ +/* +** Drawer commands for the RT family of drawers +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "r_draw.h" +#include "v_palette.h" +#include "r_thread.h" +#include "swrenderer/viewport/r_skydrawer.h" +#include "swrenderer/viewport/r_spandrawer.h" +#include "swrenderer/viewport/r_walldrawer.h" +#include "swrenderer/viewport/r_spritedrawer.h" + +#ifdef __arm__ +#define NO_SSE +#endif + +#ifndef NO_SSE +#include +#endif + +struct FSpecialColormap; + +EXTERN_CVAR(Bool, r_mipmap) +EXTERN_CVAR(Float, r_lod_bias) + +namespace swrenderer +{ + // Give the compiler a strong hint we want these functions inlined: + #ifndef FORCEINLINE + #if defined(_MSC_VER) + #define FORCEINLINE __forceinline + #elif defined(__GNUC__) + #define FORCEINLINE __attribute__((always_inline)) inline + #else + #define FORCEINLINE inline + #endif + #endif + + // Promise compiler we have no aliasing of this pointer + #ifndef RESTRICT + #if defined(_MSC_VER) + #define RESTRICT __restrict + #elif defined(__GNUC__) + #define RESTRICT __restrict__ + #else + #define RESTRICT + #endif + #endif + + // Force the compiler to use a calling convention that works for vector types + #if defined(_MSC_VER) + #define VECTORCALL __vectorcall + #else + #define VECTORCALL + #endif + + class DrawFuzzColumnRGBACommand : public DrawerCommand + { + int _x; + int _yl; + int _yh; + uint8_t * RESTRICT _destorg; + int _pitch; + int _fuzzpos; + int _fuzzviewheight; + + public: + DrawFuzzColumnRGBACommand(const SpriteDrawerArgs &drawerargs); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class FillSpanRGBACommand : public DrawerCommand + { + int _x1; + int _x2; + int _y; + uint8_t * RESTRICT _dest; + fixed_t _light; + int _color; + + public: + FillSpanRGBACommand(const SpanDrawerArgs &drawerargs); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class DrawFogBoundaryLineRGBACommand : public DrawerCommand + { + int _y; + int _x; + int _x2; + uint8_t * RESTRICT _line; + fixed_t _light; + ShadeConstants _shade_constants; + + public: + DrawFogBoundaryLineRGBACommand(const SpanDrawerArgs &drawerargs, int y, int x, int x2); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class DrawTiltedSpanRGBACommand : public DrawerCommand + { + int _x1; + int _x2; + int _y; + uint8_t * RESTRICT _dest; + fixed_t _light; + ShadeConstants _shade_constants; + FVector3 _plane_sz; + FVector3 _plane_su; + FVector3 _plane_sv; + bool _plane_shade; + int _planeshade; + float _planelightfloat; + fixed_t _pviewx; + fixed_t _pviewy; + int _xbits; + int _ybits; + const uint32_t * RESTRICT _source; + + public: + DrawTiltedSpanRGBACommand(const SpanDrawerArgs &drawerargs, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class DrawColoredSpanRGBACommand : public DrawerCommand + { + int _y; + int _x1; + int _x2; + uint8_t * RESTRICT _dest; + fixed_t _light; + int _color; + + public: + DrawColoredSpanRGBACommand(const SpanDrawerArgs &drawerargs, int y, int x1, int x2); + + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class FillTransColumnRGBACommand : public DrawerCommand + { + int _x; + int _y1; + int _y2; + int _color; + int _a; + uint8_t * RESTRICT _destorg; + int _pitch; + fixed_t _light; + + public: + FillTransColumnRGBACommand(const DrawerArgs &drawerargs, int x, int y1, int y2, int color, int a); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + }; + + class ApplySpecialColormapRGBACommand : public DrawerCommand + { + uint8_t *buffer; + int pitch; + int width; + int height; + int start_red; + int start_green; + int start_blue; + int end_red; + int end_green; + int end_blue; + + public: + ApplySpecialColormapRGBACommand(FSpecialColormap *colormap, DFrameBuffer *screen); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override { return "ApplySpecialColormapRGBACommand"; } + }; + + template + class DrawerBlendCommand : public CommandType + { + public: + void Execute(DrawerThread *thread) override + { + typename CommandType::LoopIterator loop(this, thread); + if (!loop) return; + BlendMode blend(*this, loop); + do + { + blend.Blend(*this, loop); + } while (loop.next()); + } + }; + + ///////////////////////////////////////////////////////////////////////////// + + class DrawParticleColumnRGBACommand : public DrawerCommand + { + public: + DrawParticleColumnRGBACommand(uint32_t *dest, int dest_y, int pitch, int count, uint32_t fg, uint32_t alpha, uint32_t fracposx); + void Execute(DrawerThread *thread) override; + FString DebugInfo() override; + + private: + uint32_t *_dest; + int _dest_y; + int _pitch; + int _count; + uint32_t _fg; + uint32_t _alpha; + uint32_t _fracposx; + }; + + ///////////////////////////////////////////////////////////////////////////// + + class SWTruecolorDrawers : public SWPixelFormatDrawers + { + public: + using SWPixelFormatDrawers::SWPixelFormatDrawers; + + void DrawWallColumn(const WallDrawerArgs &args) override; + void DrawWallMaskedColumn(const WallDrawerArgs &args) override; + void DrawWallAddColumn(const WallDrawerArgs &args) override; + void DrawWallAddClampColumn(const WallDrawerArgs &args) override; + void DrawWallSubClampColumn(const WallDrawerArgs &args) override; + void DrawWallRevSubClampColumn(const WallDrawerArgs &args) override; + void DrawSingleSkyColumn(const SkyDrawerArgs &args) override; + void DrawDoubleSkyColumn(const SkyDrawerArgs &args) override; + void DrawColumn(const SpriteDrawerArgs &args) override; + void FillColumn(const SpriteDrawerArgs &args) override; + void FillAddColumn(const SpriteDrawerArgs &args) override; + void FillAddClampColumn(const SpriteDrawerArgs &args) override; + void FillSubClampColumn(const SpriteDrawerArgs &args) override; + void FillRevSubClampColumn(const SpriteDrawerArgs &args) override; + void DrawFuzzColumn(const SpriteDrawerArgs &args) override; + void DrawAddColumn(const SpriteDrawerArgs &args) override; + void DrawTranslatedColumn(const SpriteDrawerArgs &args) override; + void DrawTranslatedAddColumn(const SpriteDrawerArgs &args) override; + void DrawShadedColumn(const SpriteDrawerArgs &args) override; + void DrawAddClampColumn(const SpriteDrawerArgs &args) override; + void DrawAddClampTranslatedColumn(const SpriteDrawerArgs &args) override; + void DrawSubClampColumn(const SpriteDrawerArgs &args) override; + void DrawSubClampTranslatedColumn(const SpriteDrawerArgs &args) override; + void DrawRevSubClampColumn(const SpriteDrawerArgs &args) override; + void DrawRevSubClampTranslatedColumn(const SpriteDrawerArgs &args) override; + void DrawSpan(const SpanDrawerArgs &args) override; + void DrawSpanMasked(const SpanDrawerArgs &args) override; + void DrawSpanTranslucent(const SpanDrawerArgs &args) override; + void DrawSpanMaskedTranslucent(const SpanDrawerArgs &args) override; + void DrawSpanAddClamp(const SpanDrawerArgs &args) override; + void DrawSpanMaskedAddClamp(const SpanDrawerArgs &args) override; + void FillSpan(const SpanDrawerArgs &args) override { Queue->Push(args); } + + void DrawTiltedSpan(const SpanDrawerArgs &args, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) override + { + Queue->Push(args, y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy); + } + + void DrawColoredSpan(const SpanDrawerArgs &args, int y, int x1, int x2) override { Queue->Push(args, y, x1, x2); } + void DrawFogBoundaryLine(const SpanDrawerArgs &args, int y, int x1, int x2) override { Queue->Push(args, y, x1, x2); } + }; + + ///////////////////////////////////////////////////////////////////////////// + // Pixel shading inline functions: + + class LightBgra + { + public: + // calculates the light constant passed to the shade_pal_index function + FORCEINLINE static uint32_t calc_light_multiplier(dsfixed_t light) + { + return 256 - (light >> (FRACBITS - 8)); + } + + // Calculates a ARGB8 color for the given palette index and light multiplier + FORCEINLINE static uint32_t shade_pal_index_simple(uint32_t index, uint32_t light) + { + const PalEntry &color = GPalette.BaseColors[index]; + uint32_t red = color.r; + uint32_t green = color.g; + uint32_t blue = color.b; + + red = red * light / 256; + green = green * light / 256; + blue = blue * light / 256; + + return 0xff000000 | (red << 16) | (green << 8) | blue; + } + + // Calculates a ARGB8 color for the given palette index, light multiplier and dynamic colormap + FORCEINLINE static uint32_t shade_pal_index(uint32_t index, uint32_t light, const ShadeConstants &constants) + { + const PalEntry &color = GPalette.BaseColors[index]; + uint32_t alpha = color.d & 0xff000000; + uint32_t red = color.r; + uint32_t green = color.g; + uint32_t blue = color.b; + if (constants.simple_shade) + { + red = red * light / 256; + green = green * light / 256; + blue = blue * light / 256; + } + else + { + uint32_t inv_light = 256 - light; + uint32_t inv_desaturate = 256 - constants.desaturate; + + uint32_t intensity = ((red * 77 + green * 143 + blue * 37) >> 8) * constants.desaturate; + + red = (red * inv_desaturate + intensity) / 256; + green = (green * inv_desaturate + intensity) / 256; + blue = (blue * inv_desaturate + intensity) / 256; + + red = (constants.fade_red * inv_light + red * light) / 256; + green = (constants.fade_green * inv_light + green * light) / 256; + blue = (constants.fade_blue * inv_light + blue * light) / 256; + + red = (red * constants.light_red) / 256; + green = (green * constants.light_green) / 256; + blue = (blue * constants.light_blue) / 256; + } + return alpha | (red << 16) | (green << 8) | blue; + } + + FORCEINLINE static uint32_t shade_bgra_simple(uint32_t color, uint32_t light) + { + uint32_t red = RPART(color) * light / 256; + uint32_t green = GPART(color) * light / 256; + uint32_t blue = BPART(color) * light / 256; + return 0xff000000 | (red << 16) | (green << 8) | blue; + } + + FORCEINLINE static uint32_t shade_bgra(uint32_t color, uint32_t light, const ShadeConstants &constants) + { + uint32_t alpha = color & 0xff000000; + uint32_t red = (color >> 16) & 0xff; + uint32_t green = (color >> 8) & 0xff; + uint32_t blue = color & 0xff; + if (constants.simple_shade) + { + red = red * light / 256; + green = green * light / 256; + blue = blue * light / 256; + } + else + { + uint32_t inv_light = 256 - light; + uint32_t inv_desaturate = 256 - constants.desaturate; + + uint32_t intensity = ((red * 77 + green * 143 + blue * 37) >> 8) * constants.desaturate; + + red = (red * inv_desaturate + intensity) / 256; + green = (green * inv_desaturate + intensity) / 256; + blue = (blue * inv_desaturate + intensity) / 256; + + red = (constants.fade_red * inv_light + red * light) / 256; + green = (constants.fade_green * inv_light + green * light) / 256; + blue = (constants.fade_blue * inv_light + blue * light) / 256; + + red = (red * constants.light_red) / 256; + green = (green * constants.light_green) / 256; + blue = (blue * constants.light_blue) / 256; + } + return alpha | (red << 16) | (green << 8) | blue; + } + }; +} diff --git a/src/swrenderer/drawers/r_draw_sky32_sse2.h b/src/swrenderer/drawers/r_draw_sky32_sse2.h new file mode 100644 index 000000000..f047c7795 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_sky32_sse2.h @@ -0,0 +1,314 @@ +/* +** Drawer commands for spans +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_skydrawer.h" + +namespace swrenderer +{ + class DrawSkySingle32Command : public DrawerCommand + { + protected: + SkyDrawerArgs args; + + public: + DrawSkySingle32Command(const SkyDrawerArgs &args) : args(args) { } + + void Execute(DrawerThread *thread) override + { + uint32_t *dest = (uint32_t *)args.Dest(); + int count = args.Count(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + const uint32_t *source0 = (const uint32_t *)args.FrontTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + bool fadeSky = args.FadeSky(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!fadeSky) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + dest += pitch; + frac += fracstep; + } + + return; + } + + __m128i solid_top_fill = _mm_unpacklo_epi8(_mm_cvtsi32_si128(solid_top), _mm_setzero_si128()); + __m128i solid_bottom_fill = _mm_unpacklo_epi8(_mm_cvtsi32_si128(solid_bottom), _mm_setzero_si128()); + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + + __m128i alpha = _mm_set1_epi16(MAX(MIN(frac >> (16 - start_fade), 256), 0)); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + __m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(fg), _mm_setzero_si128()); + c = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(c, alpha), _mm_mullo_epi16(solid_top_fill, inv_alpha)), 8); + *dest = _mm_cvtsi128_si32(_mm_packus_epi16(c, _mm_setzero_si128())); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + *dest = source0[sample_index]; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + + __m128i alpha = _mm_set1_epi16(MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0)); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + __m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(fg), _mm_setzero_si128()); + c = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(c, alpha), _mm_mullo_epi16(solid_top_fill, inv_alpha)), 8); + *dest = _mm_cvtsi128_si32(_mm_packus_epi16(c, _mm_setzero_si128())); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom; + dest += pitch; + index += num_cores; + } + } + + FString DebugInfo() override { return "DrawSkySingle32Command"; } + }; + + class DrawSkyDouble32Command : public DrawerCommand + { + protected: + SkyDrawerArgs args; + + public: + DrawSkyDouble32Command(const SkyDrawerArgs &args) : args(args) { } + + void Execute(DrawerThread *thread) override + { + uint32_t *dest = (uint32_t *)args.Dest(); + int count = args.Count(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + const uint32_t *source0 = (const uint32_t *)args.FrontTexturePixels(); + const uint32_t *source1 = (const uint32_t *)args.BackTexturePixels(); + int textureheight0 = args.FrontTextureHeight(); + uint32_t maxtextureheight1 = args.BackTextureHeight() - 1; + + int32_t frac = args.TextureVPos(); + int32_t fracstep = args.TextureVStep(); + + uint32_t solid_top = args.SolidTopColor(); + uint32_t solid_bottom = args.SolidBottomColor(); + bool fadeSky = args.FadeSky(); + + // Find bands for top solid color, top fade, center textured, bottom fade, bottom solid color: + int start_fade = 2; // How fast it should fade out + int fade_length = (1 << (24 - start_fade)); + int start_fadetop_y = (-frac) / fracstep; + int end_fadetop_y = (fade_length - frac) / fracstep; + int start_fadebottom_y = ((2 << 24) - fade_length - frac) / fracstep; + int end_fadebottom_y = ((2 << 24) - frac) / fracstep; + start_fadetop_y = clamp(start_fadetop_y, 0, count); + end_fadetop_y = clamp(end_fadetop_y, 0, count); + start_fadebottom_y = clamp(start_fadebottom_y, 0, count); + end_fadebottom_y = clamp(end_fadebottom_y, 0, count); + + int num_cores = thread->num_cores; + int skipped = thread->skipped_by_thread(args.DestY()); + dest = thread->dest_for_thread(args.DestY(), pitch, dest); + frac += fracstep * skipped; + fracstep *= num_cores; + pitch *= num_cores; + + if (!fadeSky) + { + count = thread->count_for_thread(args.DestY(), count); + + for (int index = 0; index < count; index++) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + *dest = fg; + dest += pitch; + frac += fracstep; + } + + return; + } + + __m128i solid_top_fill = _mm_unpacklo_epi8(_mm_cvtsi32_si128(solid_top), _mm_setzero_si128()); + __m128i solid_bottom_fill = _mm_unpacklo_epi8(_mm_cvtsi32_si128(solid_bottom), _mm_setzero_si128()); + + int index = skipped; + + // Top solid color: + while (index < start_fadetop_y) + { + *dest = solid_top; + dest += pitch; + frac += fracstep; + index += num_cores; + } + + // Top fade: + while (index < end_fadetop_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + __m128i alpha = _mm_set1_epi16(MAX(MIN(frac >> (16 - start_fade), 256), 0)); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + __m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(fg), _mm_setzero_si128()); + c = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(c, alpha), _mm_mullo_epi16(solid_top_fill, inv_alpha)), 8); + *dest = _mm_cvtsi128_si32(_mm_packus_epi16(c, _mm_setzero_si128())); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Textured center: + while (index < start_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + *dest = fg; + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Fade bottom: + while (index < end_fadebottom_y) + { + uint32_t sample_index = (((((uint32_t)frac) << 8) >> FRACBITS) * textureheight0) >> FRACBITS; + uint32_t fg = source0[sample_index]; + if (fg == 0) + { + uint32_t sample_index2 = MIN(sample_index, maxtextureheight1); + fg = source1[sample_index2]; + } + + __m128i alpha = _mm_set1_epi16(MAX(MIN(((2 << 24) - frac) >> (16 - start_fade), 256), 0)); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + __m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(fg), _mm_setzero_si128()); + c = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(c, alpha), _mm_mullo_epi16(solid_top_fill, inv_alpha)), 8); + *dest = _mm_cvtsi128_si32(_mm_packus_epi16(c, _mm_setzero_si128())); + + frac += fracstep; + dest += pitch; + index += num_cores; + } + + // Bottom solid color: + while (index < count) + { + *dest = solid_bottom; + dest += pitch; + index += num_cores; + } + } + + FString DebugInfo() override { return "DrawSkyDouble32Command"; } + }; +} diff --git a/src/swrenderer/drawers/r_draw_span32_sse2.h b/src/swrenderer/drawers/r_draw_span32_sse2.h new file mode 100644 index 000000000..94d5b278c --- /dev/null +++ b/src/swrenderer/drawers/r_draw_span32_sse2.h @@ -0,0 +1,517 @@ +/* +** Drawer commands for spans +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_spandrawer.h" + +namespace swrenderer +{ + namespace DrawSpan32TModes + { + enum class SpanBlendModes { Opaque, Masked, Translucent, AddClamp, SubClamp, RevSubClamp }; + struct OpaqueSpan { static const int Mode = (int)SpanBlendModes::Opaque; }; + struct MaskedSpan { static const int Mode = (int)SpanBlendModes::Masked; }; + struct TranslucentSpan { static const int Mode = (int)SpanBlendModes::Translucent; }; + struct AddClampSpan { static const int Mode = (int)SpanBlendModes::AddClamp; }; + struct SubClampSpan { static const int Mode = (int)SpanBlendModes::SubClamp; }; + struct RevSubClampSpan { static const int Mode = (int)SpanBlendModes::RevSubClamp; }; + + enum class FilterModes { Nearest, Linear }; + struct NearestFilter { static const int Mode = (int)FilterModes::Nearest; }; + struct LinearFilter { static const int Mode = (int)FilterModes::Linear; }; + + enum class ShadeMode { Simple, Advanced }; + struct SimpleShade { static const int Mode = (int)ShadeMode::Simple; }; + struct AdvancedShade { static const int Mode = (int)ShadeMode::Advanced; }; + + enum class SpanTextureSize { SizeAny, Size64x64 }; + struct TextureSizeAny { static const int Mode = (int)SpanTextureSize::SizeAny; }; + struct TextureSize64x64 { static const int Mode = (int)SpanTextureSize::Size64x64; }; + } + + template + class DrawSpan32T : public DrawerCommand + { + protected: + SpanDrawerArgs args; + + public: + DrawSpan32T(const SpanDrawerArgs &drawerargs) : args(drawerargs) { } + + struct TextureData + { + uint32_t xbits; + uint32_t ybits; + uint32_t xstep; + uint32_t ystep; + uint32_t xfrac; + uint32_t yfrac; + uint32_t yshift; + uint32_t xshift; + uint32_t xmask; + const uint32_t *source; + }; + + void Execute(DrawerThread *thread) override + { + using namespace DrawSpan32TModes; + + if (thread->line_skipped_by_thread(args.DestY())) return; + + TextureData texdata; + texdata.xbits = args.TextureWidthBits(); + texdata.ybits = args.TextureHeightBits(); + texdata.xstep = args.TextureUStep(); + texdata.ystep = args.TextureVStep(); + texdata.xfrac = args.TextureUPos(); + texdata.yfrac = args.TextureVPos(); + texdata.yshift = 32 - texdata.ybits; + texdata.xshift = texdata.yshift - texdata.xbits; + texdata.xmask = ((1 << texdata.xbits) - 1) << texdata.ybits; + + texdata.source = (const uint32_t*)args.TexturePixels(); + + double lod = args.TextureLOD(); + bool mipmapped = args.MipmappedTexture(); + + bool magnifying = lod < 0.0; + if (r_mipmap && mipmapped) + { + int level = (int)lod; + while (level > 0) + { + if (texdata.xbits <= 2 || texdata.ybits <= 2) + break; + + texdata.source += (1 << (texdata.xbits)) * (1 << (texdata.ybits)); + texdata.xbits -= 1; + texdata.ybits -= 1; + level--; + } + } + + bool is_nearest_filter = !((magnifying && r_magfilter) || (!magnifying && r_minfilter)); + bool is_64x64 = texdata.xbits == 6 && texdata.ybits == 6; + + auto shade_constants = args.ColormapConstants(); + if (shade_constants.simple_shade) + { + if (is_nearest_filter) + { + if (is_64x64) + Loop(thread, texdata, shade_constants); + else + Loop(thread, texdata, shade_constants); + } + else + { + if (is_64x64) + Loop(thread, texdata, shade_constants); + else + Loop(thread, texdata, shade_constants); + } + } + else + { + if (is_nearest_filter) + { + if (is_64x64) + Loop(thread, texdata, shade_constants); + else + Loop(thread, texdata, shade_constants); + } + else + { + if (is_64x64) + Loop(thread, texdata, shade_constants); + else + Loop(thread, texdata, shade_constants); + } + } + } + + template + FORCEINLINE void VECTORCALL Loop(DrawerThread *thread, TextureData texdata, ShadeConstants shade_constants) + { + using namespace DrawSpan32TModes; + + // Shade constants + int light = 256 - (args.Light() >> (FRACBITS - 8)); + __m128i mlight = _mm_set_epi16(256, light, light, light, 256, light, light, light); + __m128i inv_light = _mm_set_epi16(0, 256 - light, 256 - light, 256 - light, 0, 256 - light, 256 - light, 256 - light); + + __m128i inv_desaturate, shade_fade, shade_light; + int desaturate; + if (ShadeModeT::Mode == (int)ShadeMode::Advanced) + { + inv_desaturate = _mm_setr_epi16(256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate); + shade_fade = _mm_set_epi16(shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue, shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue); + shade_fade = _mm_mullo_epi16(shade_fade, inv_light); + shade_light = _mm_set_epi16(shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue, shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue); + desaturate = shade_constants.desaturate; + } + else + { + inv_desaturate = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_light = _mm_setzero_si128(); + desaturate = 0; + } + + auto lights = args.dc_lights; + auto num_lights = args.dc_num_lights; + float vpx = args.dc_viewpos.X; + float stepvpx = args.dc_viewpos_step.X; + __m128 viewpos_x = _mm_setr_ps(vpx, vpx + stepvpx, 0.0f, 0.0f); + __m128 step_viewpos_x = _mm_set1_ps(stepvpx * 2.0f); + + int count = args.DestX2() - args.DestX1() + 1; + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + uint32_t *dest = (uint32_t*)RenderViewport::Instance()->GetDest(args.DestX1(), args.DestY()); + + if (FilterModeT::Mode == (int)FilterModes::Linear) + { + texdata.xfrac -= 1 << (31 - texdata.xbits); + texdata.yfrac -= 1 << (31 - texdata.ybits); + } + + uint32_t srcalpha = args.SrcAlpha() >> (FRACBITS - 8); + uint32_t destalpha = args.DestAlpha() >> (FRACBITS - 8); + + int ssecount = count / 2; + for (int index = 0; index < ssecount; index++) + { + int offset = index * 2; + + __m128i bgcolor; + if (BlendT::Mode != (int)SpanBlendModes::Opaque) + { + bgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)(dest + offset)), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + unsigned int ifgcolor[2]; + ifgcolor[0] = Sample(texdata.xbits, texdata.ybits, texdata.xstep, texdata.ystep, texdata.xfrac, texdata.yfrac, texdata.yshift, texdata.xshift, texdata.xmask, texdata.source); + texdata.xfrac += texdata.xstep; + texdata.yfrac += texdata.ystep; + + ifgcolor[1] = Sample(texdata.xbits, texdata.ybits, texdata.xstep, texdata.ystep, texdata.xfrac, texdata.yfrac, texdata.yshift, texdata.xshift, texdata.xmask, texdata.source); + texdata.xfrac += texdata.xstep; + texdata.yfrac += texdata.ystep; + + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lights, num_lights, viewpos_x); + __m128i outcolor = Blend(fgcolor, bgcolor, srcalpha, destalpha, ifgcolor[0], ifgcolor[1]); + + _mm_storel_epi64((__m128i*)(dest + offset), outcolor); + viewpos_x = _mm_add_ps(viewpos_x, step_viewpos_x); + } + + if (ssecount * 2 != count) + { + int index = ssecount * 2; + int offset = index; + + __m128i bgcolor; + if (BlendT::Mode != (int)SpanBlendModes::Opaque) + { + bgcolor = _mm_unpacklo_epi8(_mm_cvtsi32_si128(dest[offset]), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + // Sample + unsigned int ifgcolor[2]; + ifgcolor[0] = Sample(texdata.xbits, texdata.ybits, texdata.xstep, texdata.ystep, texdata.xfrac, texdata.yfrac, texdata.yshift, texdata.xshift, texdata.xmask, texdata.source); + ifgcolor[1] = 0; + + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lights, num_lights, viewpos_x); + __m128i outcolor = Blend(fgcolor, bgcolor, srcalpha, destalpha, ifgcolor[0], ifgcolor[1]); + + dest[offset] = _mm_cvtsi128_si32(outcolor); + } + + } + + template + FORCEINLINE unsigned int VECTORCALL Sample(uint32_t xbits, uint32_t ybits, uint32_t xstep, uint32_t ystep, uint32_t xfrac, uint32_t yfrac, uint32_t yshift, uint32_t xshift, uint32_t xmask, const uint32_t *source) + { + using namespace DrawSpan32TModes; + + if (FilterModeT::Mode == (int)FilterModes::Nearest && TextureSizeT::Mode == (int)SpanTextureSize::Size64x64) + { + int sample_index = ((xfrac >> (32 - 6 - 6)) & (63 * 64)) + (yfrac >> (32 - 6)); + return source[sample_index]; + } + else if (FilterModeT::Mode == (int)FilterModes::Nearest) + { + int sample_index = ((xfrac >> xshift) & xmask) + (yfrac >> yshift); + return source[sample_index]; + } + else + { + uint32_t xxbits, yybits; + if (TextureSizeT::Mode == (int)SpanTextureSize::Size64x64) + { + xxbits = 26; + yybits = 26; + } + else + { + xxbits = 32 - xbits; + yybits = 32 - ybits; + } + + uint32_t xxshift = (32 - xxbits); + uint32_t yyshift = (32 - yybits); + uint32_t xxmask = (1 << xxshift) - 1; + uint32_t yymask = (1 << yyshift) - 1; + uint32_t x = xfrac >> xxbits; + uint32_t y = yfrac >> yybits; + + uint32_t p00 = source[((y & yymask) + ((x & xxmask) << yyshift))]; + uint32_t p01 = source[(((y + 1) & yymask) + ((x & xxmask) << yyshift))]; + uint32_t p10 = source[((y & yymask) + (((x + 1) & xxmask) << yyshift))]; + uint32_t p11 = source[(((y + 1) & yymask) + (((x + 1) & xxmask) << yyshift))]; + + uint32_t inv_b = (xfrac >> (xxbits - 4)) & 15; + uint32_t inv_a = (yfrac >> (yybits - 4)) & 15; + uint32_t a = 16 - inv_a; + uint32_t b = 16 - inv_b; + + uint32_t sred = (RPART(p00) * (a * b) + RPART(p01) * (inv_a * b) + RPART(p10) * (a * inv_b) + RPART(p11) * (inv_a * inv_b) + 127) >> 8; + uint32_t sgreen = (GPART(p00) * (a * b) + GPART(p01) * (inv_a * b) + GPART(p10) * (a * inv_b) + GPART(p11) * (inv_a * inv_b) + 127) >> 8; + uint32_t sblue = (BPART(p00) * (a * b) + BPART(p01) * (inv_a * b) + BPART(p10) * (a * inv_b) + BPART(p11) * (inv_a * inv_b) + 127) >> 8; + uint32_t salpha = (APART(p00) * (a * b) + APART(p01) * (inv_a * b) + APART(p10) * (a * inv_b) + APART(p11) * (inv_a * inv_b) + 127) >> 8; + + return (salpha << 24) | (sred << 16) | (sgreen << 8) | sblue; + } + } + + template + FORCEINLINE __m128i VECTORCALL Shade(__m128i fgcolor, __m128i mlight, unsigned int ifgcolor0, unsigned int ifgcolor1, int desaturate, __m128i inv_desaturate, __m128i shade_fade, __m128i shade_light, const DrawerLight *lights, int num_lights, __m128 viewpos_x) + { + using namespace DrawSpan32TModes; + + __m128i material = fgcolor; + if (ShadeModeT::Mode == (int)ShadeMode::Simple) + { + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, mlight), 8); + } + else + { + int blue0 = BPART(ifgcolor0); + int green0 = GPART(ifgcolor0); + int red0 = RPART(ifgcolor0); + int intensity0 = ((red0 * 77 + green0 * 143 + blue0 * 37) >> 8) * desaturate; + + int blue1 = BPART(ifgcolor1); + int green1 = GPART(ifgcolor1); + int red1 = RPART(ifgcolor1); + int intensity1 = ((red1 * 77 + green1 * 143 + blue1 * 37) >> 8) * desaturate; + + __m128i intensity = _mm_set_epi16(0, intensity1, intensity1, intensity1, 0, intensity0, intensity0, intensity0); + + fgcolor = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(fgcolor, inv_desaturate), intensity), 8); + fgcolor = _mm_mullo_epi16(fgcolor, mlight); + fgcolor = _mm_srli_epi16(_mm_add_epi16(shade_fade, fgcolor), 8); + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, shade_light), 8); + } + + return AddLights(material, fgcolor, lights, num_lights, viewpos_x); + } + + FORCEINLINE __m128i VECTORCALL AddLights(__m128i material, __m128i fgcolor, const DrawerLight *lights, int num_lights, __m128 viewpos_x) + { + using namespace DrawSpan32TModes; + + __m128i lit = _mm_setzero_si128(); + + for (int i = 0; i != num_lights; i++) + { + __m128 light_x = _mm_set1_ps(lights[i].x); + __m128 light_y = _mm_set1_ps(lights[i].y); + __m128 light_z = _mm_set1_ps(lights[i].z); + __m128 light_radius = _mm_set1_ps(lights[i].radius); + __m128 m256 = _mm_set1_ps(256.0f); + + // L = light-pos + // dist = sqrt(dot(L, L)) + // distance_attenuation = 1 - MIN(dist * (1/radius), 1) + __m128 Lyz2 = light_y; // L.y*L.y + L.z*L.z + __m128 Lx = _mm_sub_ps(light_x, viewpos_x); + __m128 dist2 = _mm_add_ps(Lyz2, _mm_mul_ps(Lx, Lx)); + __m128 rcp_dist = _mm_rsqrt_ps(dist2); + __m128 dist = _mm_mul_ps(dist2, rcp_dist); + __m128 distance_attenuation = _mm_sub_ps(m256, _mm_min_ps(_mm_mul_ps(dist, light_radius), m256)); + + // The simple light type + __m128 simple_attenuation = distance_attenuation; + + // The point light type + // diffuse = dot(N,L) * attenuation + __m128 point_attenuation = _mm_mul_ps(_mm_mul_ps(light_z, rcp_dist), distance_attenuation); + + __m128 is_attenuated = _mm_cmpeq_ps(light_z, _mm_setzero_ps()); + __m128i attenuation = _mm_cvtps_epi32(_mm_or_ps(_mm_and_ps(is_attenuated, simple_attenuation), _mm_andnot_ps(is_attenuated, point_attenuation))); + attenuation = _mm_packs_epi32(_mm_shuffle_epi32(attenuation, _MM_SHUFFLE(0, 0, 0, 0)), _mm_shuffle_epi32(attenuation, _MM_SHUFFLE(1, 1, 1, 1))); + + __m128i light_color = _mm_cvtsi32_si128(lights[i].color); + light_color = _mm_unpacklo_epi8(light_color, _mm_setzero_si128()); + light_color = _mm_shuffle_epi32(light_color, _MM_SHUFFLE(1, 0, 1, 0)); + + lit = _mm_add_epi16(lit, _mm_srli_epi16(_mm_mullo_epi16(light_color, attenuation), 8)); + } + + fgcolor = _mm_add_epi16(fgcolor, _mm_srli_epi16(_mm_mullo_epi16(material, lit), 8)); + fgcolor = _mm_min_epi16(fgcolor, _mm_set1_epi16(255)); + return fgcolor; + } + + FORCEINLINE __m128i VECTORCALL Blend(__m128i fgcolor, __m128i bgcolor, uint32_t srcalpha, uint32_t destalpha, unsigned int ifgcolor0, unsigned int ifgcolor1) + { + using namespace DrawSpan32TModes; + + if (BlendT::Mode == (int)SpanBlendModes::Opaque) + { + __m128i outcolor = fgcolor; + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + return outcolor; + } + else if (BlendT::Mode == (int)SpanBlendModes::Masked) + { +#if 0 // leaving this in for alpha texture support (todo: fix in texture manager later?) + __m128i alpha = _mm_shufflelo_epi16(fgcolor, _MM_SHUFFLE(3, 3, 3, 3)); + alpha = _mm_shufflehi_epi16(alpha, _MM_SHUFFLE(3, 3, 3, 3)); + alpha = _mm_add_epi16(alpha, _mm_srli_epi16(alpha, 7)); // 255 -> 256 + + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + fgcolor = _mm_mullo_epi16(fgcolor, alpha); + bgcolor = _mm_mullo_epi16(bgcolor, inv_alpha); + __m128i outcolor = _mm_srli_epi16(_mm_add_epi16(fgcolor, bgcolor), 8); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; +#endif + __m128i mask = _mm_cmpeq_epi32(_mm_packus_epi16(fgcolor, _mm_setzero_si128()), _mm_setzero_si128()); + mask = _mm_unpacklo_epi8(mask, _mm_setzero_si128()); + __m128i outcolor = _mm_or_si128(_mm_and_si128(mask, bgcolor), _mm_andnot_si128(mask, fgcolor)); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + else if (BlendT::Mode == (int)SpanBlendModes::Translucent) + { + __m128i fgalpha = _mm_set1_epi16(srcalpha); + __m128i bgalpha = _mm_set1_epi16(destalpha); + + fgcolor = _mm_mullo_epi16(fgcolor, fgalpha); + bgcolor = _mm_mullo_epi16(bgcolor, bgalpha); + + __m128i fg_lo = _mm_unpacklo_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_lo = _mm_unpacklo_epi16(bgcolor, _mm_setzero_si128()); + __m128i fg_hi = _mm_unpackhi_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_hi = _mm_unpackhi_epi16(bgcolor, _mm_setzero_si128()); + + __m128i out_lo = _mm_add_epi32(fg_lo, bg_lo); + __m128i out_hi = _mm_add_epi32(fg_hi, bg_hi); + + out_lo = _mm_srai_epi32(out_lo, 8); + out_hi = _mm_srai_epi32(out_hi, 8); + __m128i outcolor = _mm_packs_epi32(out_lo, out_hi); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + else + { + uint32_t alpha0 = APART(ifgcolor0); + uint32_t alpha1 = APART(ifgcolor1); + alpha0 += alpha0 >> 7; // 255->256 + alpha1 += alpha1 >> 7; // 255->256 + uint32_t inv_alpha0 = 256 - alpha0; + uint32_t inv_alpha1 = 256 - alpha1; + + uint32_t bgalpha0 = (destalpha * alpha0 + (inv_alpha0 << 8) + 128) >> 8; + uint32_t bgalpha1 = (destalpha * alpha1 + (inv_alpha1 << 8) + 128) >> 8; + uint32_t fgalpha0 = (srcalpha * alpha0 + 128) >> 8; + uint32_t fgalpha1 = (srcalpha * alpha1 + 128) >> 8; + + __m128i bgalpha = _mm_set_epi16(bgalpha1, bgalpha1, bgalpha1, bgalpha1, bgalpha0, bgalpha0, bgalpha0, bgalpha0); + __m128i fgalpha = _mm_set_epi16(fgalpha1, fgalpha1, fgalpha1, fgalpha1, fgalpha0, fgalpha0, fgalpha0, fgalpha0); + + fgcolor = _mm_mullo_epi16(fgcolor, fgalpha); + bgcolor = _mm_mullo_epi16(bgcolor, bgalpha); + + __m128i fg_lo = _mm_unpacklo_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_lo = _mm_unpacklo_epi16(bgcolor, _mm_setzero_si128()); + __m128i fg_hi = _mm_unpackhi_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_hi = _mm_unpackhi_epi16(bgcolor, _mm_setzero_si128()); + + __m128i out_lo, out_hi; + if (BlendT::Mode == (int)SpanBlendModes::AddClamp) + { + out_lo = _mm_add_epi32(fg_lo, bg_lo); + out_hi = _mm_add_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)SpanBlendModes::SubClamp) + { + out_lo = _mm_sub_epi32(fg_lo, bg_lo); + out_hi = _mm_sub_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)SpanBlendModes::RevSubClamp) + { + out_lo = _mm_sub_epi32(bg_lo, fg_lo); + out_hi = _mm_sub_epi32(bg_hi, fg_hi); + } + + out_lo = _mm_srai_epi32(out_lo, 8); + out_hi = _mm_srai_epi32(out_hi, 8); + __m128i outcolor = _mm_packs_epi32(out_lo, out_hi); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + } + + FString DebugInfo() override { return "DrawSpan32T"; } + }; + + typedef DrawSpan32T DrawSpan32Command; + typedef DrawSpan32T DrawSpanMasked32Command; + typedef DrawSpan32T DrawSpanTranslucent32Command; + typedef DrawSpan32T DrawSpanAddClamp32Command; + typedef DrawSpan32T DrawSpanSubClamp32Command; + typedef DrawSpan32T DrawSpanRevSubClamp32Command; +} diff --git a/src/swrenderer/drawers/r_draw_sprite32_sse2.h b/src/swrenderer/drawers/r_draw_sprite32_sse2.h new file mode 100644 index 000000000..dc4a42172 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_sprite32_sse2.h @@ -0,0 +1,456 @@ +/* +** Drawer commands for sprites +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_walldrawer.h" + +namespace swrenderer +{ + namespace DrawSprite32TModes + { + enum class SpriteBlendModes { Copy, Opaque, Shaded, AddClamp, SubClamp, RevSubClamp }; + struct CopySprite { static const int Mode = (int)SpriteBlendModes::Copy; }; + struct OpaqueSprite { static const int Mode = (int)SpriteBlendModes::Opaque; }; + struct ShadedSprite { static const int Mode = (int)SpriteBlendModes::Shaded; }; + struct AddClampSprite { static const int Mode = (int)SpriteBlendModes::AddClamp; }; + struct SubClampSprite { static const int Mode = (int)SpriteBlendModes::SubClamp; }; + struct RevSubClampSprite { static const int Mode = (int)SpriteBlendModes::RevSubClamp; }; + + enum class FilterModes { Nearest, Linear }; + struct NearestFilter { static const int Mode = (int)FilterModes::Nearest; }; + struct LinearFilter { static const int Mode = (int)FilterModes::Linear; }; + + enum class ShadeMode { Simple, Advanced }; + struct SimpleShade { static const int Mode = (int)ShadeMode::Simple; }; + struct AdvancedShade { static const int Mode = (int)ShadeMode::Advanced; }; + + enum class SpriteSamplers { Texture, Fill, Shaded, Translated }; + struct TextureSampler { static const int Mode = (int)SpriteSamplers::Texture; }; + struct FillSampler { static const int Mode = (int)SpriteSamplers::Fill; }; + struct ShadedSampler { static const int Mode = (int)SpriteSamplers::Shaded; }; + struct TranslatedSampler { static const int Mode = (int)SpriteSamplers::Translated; }; + } + + template + class DrawSprite32T : public DrawerCommand + { + protected: + SpriteDrawerArgs args; + + public: + DrawSprite32T(const SpriteDrawerArgs &drawerargs) : args(drawerargs) { } + + void Execute(DrawerThread *thread) override + { + using namespace DrawSprite32TModes; + + auto shade_constants = args.ColormapConstants(); + if (SamplerT::Mode == (int)SpriteSamplers::Texture) + { + const uint32_t *source2 = (const uint32_t*)args.TexturePixels2(); + bool is_nearest_filter = (source2 == nullptr); + + if (shade_constants.simple_shade) + { + if (is_nearest_filter) + Loop(thread, shade_constants); + else + Loop(thread, shade_constants); + } + else + { + if (is_nearest_filter) + Loop(thread, shade_constants); + else + Loop(thread, shade_constants); + } + } + else // no linear filtering for translated, shaded or fill + { + if (shade_constants.simple_shade) + { + Loop(thread, shade_constants); + } + else + { + Loop(thread, shade_constants); + } + } + } + + template + FORCEINLINE void VECTORCALL Loop(DrawerThread *thread, ShadeConstants shade_constants) + { + using namespace DrawSprite32TModes; + + const uint32_t *source; + const uint32_t *source2; + const uint8_t *colormap; + const uint32_t *translation; + + if (SamplerT::Mode == (int)SpriteSamplers::Shaded || SamplerT::Mode == (int)SpriteSamplers::Translated) + { + source = (const uint32_t*)args.TexturePixels(); + source2 = nullptr; + colormap = args.Colormap(); + translation = (const uint32_t*)args.TranslationMap(); + } + else + { + source = (const uint32_t*)args.TexturePixels(); + source2 = (const uint32_t*)args.TexturePixels2(); + colormap = nullptr; + translation = nullptr; + } + + int textureheight = args.TextureHeight(); + uint32_t one = ((0x80000000 + textureheight - 1) / textureheight) * 2 + 1; + + // Shade constants + __m128i dynlight = _mm_cvtsi32_si128(args.DynamicLight()); + dynlight = _mm_unpacklo_epi8(dynlight, _mm_setzero_si128()); + dynlight = _mm_shuffle_epi32(dynlight, _MM_SHUFFLE(1, 0, 1, 0)); + int light = 256 - (args.Light() >> (FRACBITS - 8)); + __m128i mlight = _mm_set_epi16(256, light, light, light, 256, light, light, light); + + __m128i inv_desaturate, shade_fade, shade_light; + int desaturate; + __m128i lightcontrib; + if (ShadeModeT::Mode == (int)ShadeMode::Advanced) + { + __m128i inv_light = _mm_set_epi16(0, 256 - light, 256 - light, 256 - light, 0, 256 - light, 256 - light, 256 - light); + inv_desaturate = _mm_setr_epi16(256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate); + shade_fade = _mm_set_epi16(shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue, shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue); + shade_fade = _mm_mullo_epi16(shade_fade, inv_light); + shade_light = _mm_set_epi16(shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue, shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue); + desaturate = shade_constants.desaturate; + + lightcontrib = _mm_min_epi16(_mm_add_epi16(mlight, dynlight), _mm_set1_epi16(256)); + lightcontrib = _mm_sub_epi16(lightcontrib, mlight); + } + else + { + inv_desaturate = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_light = _mm_setzero_si128(); + desaturate = 0; + lightcontrib = _mm_setzero_si128(); + + mlight = _mm_min_epi16(_mm_add_epi16(mlight, dynlight), _mm_set1_epi16(256)); + } + + int count = args.Count(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint32_t texturefracx = args.TextureUPos(); + uint32_t *dest = (uint32_t*)args.Dest(); + int dest_y = args.DestY(); + + count = thread->count_for_thread(dest_y, count); + if (count <= 0) return; + frac += thread->skipped_by_thread(dest_y) * fracstep; + dest = thread->dest_for_thread(dest_y, pitch, dest); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (FilterModeT::Mode == (int)FilterModes::Linear) + { + frac -= one / 2; + } + + uint32_t srcalpha = args.SrcAlpha() >> (FRACBITS - 8); + uint32_t destalpha = args.DestAlpha() >> (FRACBITS - 8); + uint32_t srccolor = args.SrcColorBgra(); + uint32_t color = LightBgra::shade_pal_index_simple(args.SolidColor(), light); + + int ssecount = count / 2; + for (int index = 0; index < ssecount; index++) + { + int offset = index * pitch * 2; + uint32_t desttmp[2]; + desttmp[0] = dest[offset]; + desttmp[1] = dest[offset + pitch]; + + __m128i bgcolor; + if (BlendT::Mode != (int)SpriteBlendModes::Opaque && BlendT::Mode != (int)SpriteBlendModes::Copy) + { + bgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)desttmp), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + unsigned int ifgcolor[2], ifgshade[2]; + ifgcolor[0] = Sample(frac, source, source2, translation, textureheight, one, texturefracx, color, srccolor); + ifgshade[0] = SampleShade(frac, source, colormap); + frac += fracstep; + + ifgcolor[1] = Sample(frac, source, source2, translation, textureheight, one, texturefracx, color, srccolor); + ifgshade[1] = SampleShade(frac, source, colormap); + frac += fracstep; + + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lightcontrib); + __m128i outcolor = Blend(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha); + + _mm_storel_epi64((__m128i*)desttmp, outcolor); + dest[offset] = desttmp[0]; + dest[offset + pitch] = desttmp[1]; + } + + if (ssecount * 2 != count) + { + int index = ssecount * 2; + int offset = index * pitch; + + __m128i bgcolor; + if (BlendT::Mode != (int)SpriteBlendModes::Opaque && BlendT::Mode != (int)SpriteBlendModes::Copy) + { + bgcolor = _mm_unpacklo_epi8(_mm_cvtsi32_si128(dest[offset]), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + // Sample + unsigned int ifgcolor[2], ifgshade[2]; + ifgcolor[0] = Sample(frac, source, source2, translation, textureheight, one, texturefracx, color, srccolor); + ifgcolor[1] = 0; + ifgshade[0] = SampleShade(frac, source, colormap); + ifgshade[1] = 0; + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lightcontrib); + __m128i outcolor = Blend(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], ifgshade[0], ifgshade[1], srcalpha, destalpha); + + dest[offset] = _mm_cvtsi128_si32(outcolor); + } + } + + template + FORCEINLINE unsigned int VECTORCALL Sample(uint32_t frac, const uint32_t *source, const uint32_t *source2, const uint32_t *translation, int textureheight, uint32_t one, uint32_t texturefracx, uint32_t color, uint32_t srccolor) + { + using namespace DrawSprite32TModes; + + if (SamplerT::Mode == (int)SpriteSamplers::Shaded) + { + return color; + } + else if (SamplerT::Mode == (int)SpriteSamplers::Translated) + { + const uint8_t *sourcepal = (const uint8_t *)source; + return translation[sourcepal[frac >> FRACBITS]]; + } + else if (SamplerT::Mode == (int)SpriteSamplers::Fill) + { + return srccolor; + } + else if (FilterModeT::Mode == (int)FilterModes::Nearest) + { + int sample_index = (((frac << 2) >> FRACBITS) * textureheight) >> FRACBITS; + return source[sample_index]; + } + else + { + // Clamp to edge + unsigned int frac_y0 = (clamp(frac, 0, 1 << 30) >> (FRACBITS - 2)) * textureheight; + unsigned int frac_y1 = (clamp(frac + one, 0, 1 << 30) >> (FRACBITS - 2)) * textureheight; + unsigned int y0 = frac_y0 >> FRACBITS; + unsigned int y1 = frac_y1 >> FRACBITS; + + unsigned int p00 = source[y0]; + unsigned int p01 = source[y1]; + unsigned int p10 = source2[y0]; + unsigned int p11 = source2[y1]; + + unsigned int inv_b = texturefracx; + unsigned int inv_a = (frac_y1 >> (FRACBITS - 4)) & 15; + unsigned int a = 16 - inv_a; + unsigned int b = 16 - inv_b; + + unsigned int sred = (RPART(p00) * (a * b) + RPART(p01) * (inv_a * b) + RPART(p10) * (a * inv_b) + RPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int sgreen = (GPART(p00) * (a * b) + GPART(p01) * (inv_a * b) + GPART(p10) * (a * inv_b) + GPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int sblue = (BPART(p00) * (a * b) + BPART(p01) * (inv_a * b) + BPART(p10) * (a * inv_b) + BPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int salpha = (APART(p00) * (a * b) + APART(p01) * (inv_a * b) + APART(p10) * (a * inv_b) + APART(p11) * (inv_a * inv_b) + 127) >> 8; + + return (salpha << 24) | (sred << 16) | (sgreen << 8) | sblue; + } + } + + FORCEINLINE unsigned int VECTORCALL SampleShade(uint32_t frac, const uint32_t *source, const uint8_t *colormap) + { + using namespace DrawSprite32TModes; + + if (SamplerT::Mode == (int)SpriteSamplers::Shaded) + { + const uint8_t *sourcepal = (const uint8_t *)source; + unsigned int sampleshadeout = colormap[sourcepal[frac >> FRACBITS]]; + return clamp(sampleshadeout, 0, 64) * 4; + } + else + { + return 0; + } + } + + template + FORCEINLINE __m128i VECTORCALL Shade(__m128i fgcolor, __m128i mlight, unsigned int ifgcolor0, unsigned int ifgcolor1, int desaturate, __m128i inv_desaturate, __m128i shade_fade, __m128i shade_light, __m128i lightcontrib) + { + using namespace DrawSprite32TModes; + + if (BlendT::Mode == (int)SpriteBlendModes::Copy || BlendT::Mode == (int)SpriteBlendModes::Shaded) + return fgcolor; + + if (ShadeModeT::Mode == (int)ShadeMode::Simple) + { + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, mlight), 8); + return fgcolor; + } + else + { + __m128i lit_dynlight = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, lightcontrib), 8); + + int blue0 = BPART(ifgcolor0); + int green0 = GPART(ifgcolor0); + int red0 = RPART(ifgcolor0); + int intensity0 = ((red0 * 77 + green0 * 143 + blue0 * 37) >> 8) * desaturate; + + int blue1 = BPART(ifgcolor1); + int green1 = GPART(ifgcolor1); + int red1 = RPART(ifgcolor1); + int intensity1 = ((red1 * 77 + green1 * 143 + blue1 * 37) >> 8) * desaturate; + + __m128i intensity = _mm_set_epi16(0, intensity1, intensity1, intensity1, 0, intensity0, intensity0, intensity0); + + fgcolor = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(fgcolor, inv_desaturate), intensity), 8); + fgcolor = _mm_mullo_epi16(fgcolor, mlight); + fgcolor = _mm_srli_epi16(_mm_add_epi16(shade_fade, fgcolor), 8); + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, shade_light), 8); + + fgcolor = _mm_add_epi16(fgcolor, lit_dynlight); + fgcolor = _mm_min_epi16(fgcolor, _mm_set1_epi16(256)); + return fgcolor; + } + } + + FORCEINLINE __m128i VECTORCALL Blend(__m128i fgcolor, __m128i bgcolor, unsigned int ifgcolor0, unsigned int ifgcolor1, unsigned int ifgshade0, unsigned int ifgshade1, uint32_t srcalpha, uint32_t destalpha) + { + using namespace DrawSprite32TModes; + + if (BlendT::Mode == (int)SpriteBlendModes::Opaque) + { + __m128i outcolor = fgcolor; + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + return outcolor; + } + else if (BlendT::Mode == (int)SpriteBlendModes::Shaded) + { + __m128i alpha = _mm_set_epi16(ifgshade1, ifgshade1, ifgshade1, ifgshade1, ifgshade0, ifgshade0, ifgshade0, ifgshade0); + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + fgcolor = _mm_mullo_epi16(fgcolor, alpha); + bgcolor = _mm_mullo_epi16(bgcolor, inv_alpha); + __m128i outcolor = _mm_srli_epi16(_mm_add_epi16(fgcolor, bgcolor), 8); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + else + { + uint32_t alpha0 = APART(ifgcolor0); + uint32_t alpha1 = APART(ifgcolor1); + alpha0 += alpha0 >> 7; // 255->256 + alpha1 += alpha1 >> 7; // 255->256 + uint32_t inv_alpha0 = 256 - alpha0; + uint32_t inv_alpha1 = 256 - alpha1; + + uint32_t bgalpha0 = (destalpha * alpha0 + (inv_alpha0 << 8) + 128) >> 8; + uint32_t bgalpha1 = (destalpha * alpha1 + (inv_alpha1 << 8) + 128) >> 8; + uint32_t fgalpha0 = (srcalpha * alpha0 + 128) >> 8; + uint32_t fgalpha1 = (srcalpha * alpha1 + 128) >> 8; + + __m128i bgalpha = _mm_set_epi16(bgalpha1, bgalpha1, bgalpha1, bgalpha1, bgalpha0, bgalpha0, bgalpha0, bgalpha0); + __m128i fgalpha = _mm_set_epi16(fgalpha1, fgalpha1, fgalpha1, fgalpha1, fgalpha0, fgalpha0, fgalpha0, fgalpha0); + + fgcolor = _mm_mullo_epi16(fgcolor, fgalpha); + bgcolor = _mm_mullo_epi16(bgcolor, bgalpha); + + __m128i fg_lo = _mm_unpacklo_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_lo = _mm_unpacklo_epi16(bgcolor, _mm_setzero_si128()); + __m128i fg_hi = _mm_unpackhi_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_hi = _mm_unpackhi_epi16(bgcolor, _mm_setzero_si128()); + + __m128i out_lo, out_hi; + if (BlendT::Mode == (int)SpriteBlendModes::AddClamp) + { + out_lo = _mm_add_epi32(fg_lo, bg_lo); + out_hi = _mm_add_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)SpriteBlendModes::SubClamp) + { + out_lo = _mm_sub_epi32(fg_lo, bg_lo); + out_hi = _mm_sub_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)SpriteBlendModes::RevSubClamp) + { + out_lo = _mm_sub_epi32(bg_lo, fg_lo); + out_hi = _mm_sub_epi32(bg_hi, fg_hi); + } + + out_lo = _mm_srai_epi32(out_lo, 8); + out_hi = _mm_srai_epi32(out_hi, 8); + __m128i outcolor = _mm_packs_epi32(out_lo, out_hi); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + } + + FString DebugInfo() override { return "DrawSprite32T"; } + }; + + typedef DrawSprite32T DrawSpriteCopy32Command; + + typedef DrawSprite32T DrawSprite32Command; + typedef DrawSprite32T DrawSpriteAddClamp32Command; + typedef DrawSprite32T DrawSpriteSubClamp32Command; + typedef DrawSprite32T DrawSpriteRevSubClamp32Command; + + typedef DrawSprite32T FillSprite32Command; + typedef DrawSprite32T FillSpriteAddClamp32Command; + typedef DrawSprite32T FillSpriteSubClamp32Command; + typedef DrawSprite32T FillSpriteRevSubClamp32Command; + + typedef DrawSprite32T DrawSpriteShaded32Command; + + typedef DrawSprite32T DrawSpriteTranslated32Command; + typedef DrawSprite32T DrawSpriteTranslatedAddClamp32Command; + typedef DrawSprite32T DrawSpriteTranslatedSubClamp32Command; + typedef DrawSprite32T DrawSpriteTranslatedRevSubClamp32Command; +} diff --git a/src/swrenderer/drawers/r_draw_wall32_sse2.h b/src/swrenderer/drawers/r_draw_wall32_sse2.h new file mode 100644 index 000000000..31ab06e86 --- /dev/null +++ b/src/swrenderer/drawers/r_draw_wall32_sse2.h @@ -0,0 +1,415 @@ +/* +** Drawer commands for walls +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/viewport/r_walldrawer.h" + +namespace swrenderer +{ + namespace DrawWall32TModes + { + enum class WallBlendModes { Opaque, Masked, AddClamp, SubClamp, RevSubClamp }; + struct OpaqueWall { static const int Mode = (int)WallBlendModes::Opaque; }; + struct MaskedWall { static const int Mode = (int)WallBlendModes::Masked; }; + struct AddClampWall { static const int Mode = (int)WallBlendModes::AddClamp; }; + struct SubClampWall { static const int Mode = (int)WallBlendModes::SubClamp; }; + struct RevSubClampWall { static const int Mode = (int)WallBlendModes::RevSubClamp; }; + + enum class FilterModes { Nearest, Linear }; + struct NearestFilter { static const int Mode = (int)FilterModes::Nearest; }; + struct LinearFilter { static const int Mode = (int)FilterModes::Linear; }; + + enum class ShadeMode { Simple, Advanced }; + struct SimpleShade { static const int Mode = (int)ShadeMode::Simple; }; + struct AdvancedShade { static const int Mode = (int)ShadeMode::Advanced; }; + } + + template + class DrawWall32T : public DrawerCommand + { + protected: + WallDrawerArgs args; + + public: + DrawWall32T(const WallDrawerArgs &drawerargs) : args(drawerargs) { } + + void Execute(DrawerThread *thread) override + { + using namespace DrawWall32TModes; + + const uint32_t *source2 = (const uint32_t*)args.TexturePixels2(); + bool is_nearest_filter = (source2 == nullptr); + auto shade_constants = args.ColormapConstants(); + if (shade_constants.simple_shade) + { + if (is_nearest_filter) + Loop(thread, shade_constants); + else + Loop(thread, shade_constants); + } + else + { + if (is_nearest_filter) + Loop(thread, shade_constants); + else + Loop(thread, shade_constants); + } + } + + template + FORCEINLINE void VECTORCALL Loop(DrawerThread *thread, ShadeConstants shade_constants) + { + using namespace DrawWall32TModes; + + const uint32_t *source = (const uint32_t*)args.TexturePixels(); + const uint32_t *source2 = (const uint32_t*)args.TexturePixels2(); + int textureheight = args.TextureHeight(); + uint32_t one = ((0x80000000 + textureheight - 1) / textureheight) * 2 + 1; + + // Shade constants + int light = 256 - (args.Light() >> (FRACBITS - 8)); + __m128i mlight = _mm_set_epi16(256, light, light, light, 256, light, light, light); + __m128i inv_light = _mm_set_epi16(0, 256 - light, 256 - light, 256 - light, 0, 256 - light, 256 - light, 256 - light); + + __m128i inv_desaturate, shade_fade, shade_light; + int desaturate; + if (ShadeModeT::Mode == (int)ShadeMode::Advanced) + { + inv_desaturate = _mm_setr_epi16(256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate, 256 - shade_constants.desaturate); + shade_fade = _mm_set_epi16(shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue, shade_constants.fade_alpha, shade_constants.fade_red, shade_constants.fade_green, shade_constants.fade_blue); + shade_fade = _mm_mullo_epi16(shade_fade, inv_light); + shade_light = _mm_set_epi16(shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue, shade_constants.light_alpha, shade_constants.light_red, shade_constants.light_green, shade_constants.light_blue); + desaturate = shade_constants.desaturate; + } + else + { + inv_desaturate = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_fade = _mm_setzero_si128(); + shade_light = _mm_setzero_si128(); + desaturate = 0; + } + + int count = args.Count(); + int pitch = RenderViewport::Instance()->RenderTarget->GetPitch(); + uint32_t fracstep = args.TextureVStep(); + uint32_t frac = args.TextureVPos(); + uint32_t texturefracx = args.TextureUPos(); + uint32_t *dest = (uint32_t*)args.Dest(); + int dest_y = args.DestY(); + + auto lights = args.dc_lights; + auto num_lights = args.dc_num_lights; + float vpz = args.dc_viewpos.Z + args.dc_viewpos_step.Z * thread->skipped_by_thread(dest_y); + float stepvpz = args.dc_viewpos_step.Z * thread->num_cores; + __m128 viewpos_z = _mm_setr_ps(vpz, vpz + stepvpz, 0.0f, 0.0f); + __m128 step_viewpos_z = _mm_set1_ps(stepvpz * 2.0f); + + count = thread->count_for_thread(dest_y, count); + if (count <= 0) return; + frac += thread->skipped_by_thread(dest_y) * fracstep; + dest = thread->dest_for_thread(dest_y, pitch, dest); + fracstep *= thread->num_cores; + pitch *= thread->num_cores; + + if (FilterModeT::Mode == (int)FilterModes::Linear) + { + frac -= one / 2; + } + + uint32_t srcalpha = args.SrcAlpha() >> (FRACBITS - 8); + uint32_t destalpha = args.DestAlpha() >> (FRACBITS - 8); + + int ssecount = count / 2; + for (int index = 0; index < ssecount; index++) + { + int offset = index * pitch * 2; + uint32_t desttmp[2]; + desttmp[0] = dest[offset]; + desttmp[1] = dest[offset + pitch]; + + __m128i bgcolor; + if (BlendT::Mode != (int)WallBlendModes::Opaque) + { + bgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)desttmp), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + unsigned int ifgcolor[2]; + ifgcolor[0] = Sample(frac, source, source2, textureheight, one, texturefracx); + frac += fracstep; + + ifgcolor[1] = Sample(frac, source, source2, textureheight, one, texturefracx); + frac += fracstep; + + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lights, num_lights, viewpos_z); + __m128i outcolor = Blend(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], srcalpha, destalpha); + + _mm_storel_epi64((__m128i*)desttmp, outcolor); + dest[offset] = desttmp[0]; + dest[offset + pitch] = desttmp[1]; + viewpos_z = _mm_add_ps(viewpos_z, step_viewpos_z); + } + + if (ssecount * 2 != count) + { + int index = ssecount * 2; + int offset = index * pitch; + + __m128i bgcolor; + if (BlendT::Mode != (int)WallBlendModes::Opaque) + { + bgcolor = _mm_unpacklo_epi8(_mm_cvtsi32_si128(dest[offset]), _mm_setzero_si128()); + } + else + { + bgcolor = _mm_setzero_si128(); + } + + unsigned int ifgcolor[2]; + ifgcolor[0] = Sample(frac, source, source2, textureheight, one, texturefracx); + ifgcolor[1] = 0; + __m128i fgcolor = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i*)ifgcolor), _mm_setzero_si128()); + + fgcolor = Shade(fgcolor, mlight, ifgcolor[0], ifgcolor[1], desaturate, inv_desaturate, shade_fade, shade_light, lights, num_lights, viewpos_z); + __m128i outcolor = Blend(fgcolor, bgcolor, ifgcolor[0], ifgcolor[1], srcalpha, destalpha); + + dest[offset] = _mm_cvtsi128_si32(outcolor); + } + } + + template + FORCEINLINE unsigned int VECTORCALL Sample(uint32_t frac, const uint32_t *source, const uint32_t *source2, int textureheight, uint32_t one, uint32_t texturefracx) + { + using namespace DrawWall32TModes; + + if (FilterModeT::Mode == (int)FilterModes::Nearest) + { + int sample_index = ((frac >> FRACBITS) * textureheight) >> FRACBITS; + return source[sample_index]; + } + else + { + unsigned int frac_y0 = (frac >> FRACBITS) * textureheight; + unsigned int frac_y1 = ((frac + one) >> FRACBITS) * textureheight; + unsigned int y0 = frac_y0 >> FRACBITS; + unsigned int y1 = frac_y1 >> FRACBITS; + + unsigned int p00 = source[y0]; + unsigned int p01 = source[y1]; + unsigned int p10 = source2[y0]; + unsigned int p11 = source2[y1]; + + unsigned int inv_b = texturefracx; + unsigned int inv_a = (frac_y1 >> (FRACBITS - 4)) & 15; + unsigned int a = 16 - inv_a; + unsigned int b = 16 - inv_b; + + unsigned int sred = (RPART(p00) * (a * b) + RPART(p01) * (inv_a * b) + RPART(p10) * (a * inv_b) + RPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int sgreen = (GPART(p00) * (a * b) + GPART(p01) * (inv_a * b) + GPART(p10) * (a * inv_b) + GPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int sblue = (BPART(p00) * (a * b) + BPART(p01) * (inv_a * b) + BPART(p10) * (a * inv_b) + BPART(p11) * (inv_a * inv_b) + 127) >> 8; + unsigned int salpha = (APART(p00) * (a * b) + APART(p01) * (inv_a * b) + APART(p10) * (a * inv_b) + APART(p11) * (inv_a * inv_b) + 127) >> 8; + + return (salpha << 24) | (sred << 16) | (sgreen << 8) | sblue; + } + } + + template + FORCEINLINE __m128i VECTORCALL Shade(__m128i fgcolor, __m128i mlight, unsigned int ifgcolor0, unsigned int ifgcolor1, int desaturate, __m128i inv_desaturate, __m128i shade_fade, __m128i shade_light, const DrawerLight *lights, int num_lights, __m128 viewpos_z) + { + using namespace DrawWall32TModes; + + __m128i material = fgcolor; + if (ShadeModeT::Mode == (int)ShadeMode::Simple) + { + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, mlight), 8); + } + else + { + int blue0 = BPART(ifgcolor0); + int green0 = GPART(ifgcolor0); + int red0 = RPART(ifgcolor0); + int intensity0 = ((red0 * 77 + green0 * 143 + blue0 * 37) >> 8) * desaturate; + + int blue1 = BPART(ifgcolor1); + int green1 = GPART(ifgcolor1); + int red1 = RPART(ifgcolor1); + int intensity1 = ((red1 * 77 + green1 * 143 + blue1 * 37) >> 8) * desaturate; + + __m128i intensity = _mm_set_epi16(0, intensity1, intensity1, intensity1, 0, intensity0, intensity0, intensity0); + + fgcolor = _mm_srli_epi16(_mm_add_epi16(_mm_mullo_epi16(fgcolor, inv_desaturate), intensity), 8); + fgcolor = _mm_mullo_epi16(fgcolor, mlight); + fgcolor = _mm_srli_epi16(_mm_add_epi16(shade_fade, fgcolor), 8); + fgcolor = _mm_srli_epi16(_mm_mullo_epi16(fgcolor, shade_light), 8); + } + + return AddLights(material, fgcolor, lights, num_lights, viewpos_z); + } + + FORCEINLINE __m128i VECTORCALL AddLights(__m128i material, __m128i fgcolor, const DrawerLight *lights, int num_lights, __m128 viewpos_z) + { + using namespace DrawWall32TModes; + + __m128i lit = _mm_setzero_si128(); + + for (int i = 0; i != num_lights; i++) + { + __m128 light_x = _mm_set1_ps(lights[i].x); + __m128 light_y = _mm_set1_ps(lights[i].y); + __m128 light_z = _mm_set1_ps(lights[i].z); + __m128 light_radius = _mm_set1_ps(lights[i].radius); + __m128 m256 = _mm_set1_ps(256.0f); + + // L = light-pos + // dist = sqrt(dot(L, L)) + // distance_attenuation = 1 - MIN(dist * (1/radius), 1) + __m128 Lxy2 = light_x; // L.x*L.x + L.y*L.y + __m128 Lz = _mm_sub_ps(light_z, viewpos_z); + __m128 dist2 = _mm_add_ps(Lxy2, _mm_mul_ps(Lz, Lz)); + __m128 rcp_dist = _mm_rsqrt_ps(dist2); + __m128 dist = _mm_mul_ps(dist2, rcp_dist); + __m128 distance_attenuation = _mm_sub_ps(m256, _mm_min_ps(_mm_mul_ps(dist, light_radius), m256)); + + // The simple light type + __m128 simple_attenuation = distance_attenuation; + + // The point light type + // diffuse = dot(N,L) * attenuation + __m128 point_attenuation = _mm_mul_ps(_mm_mul_ps(light_y, rcp_dist), distance_attenuation); + + __m128 is_attenuated = _mm_cmpeq_ps(light_y, _mm_setzero_ps()); + __m128i attenuation = _mm_cvtps_epi32(_mm_or_ps(_mm_and_ps(is_attenuated, simple_attenuation), _mm_andnot_ps(is_attenuated, point_attenuation))); + attenuation = _mm_packs_epi32(_mm_shuffle_epi32(attenuation, _MM_SHUFFLE(0, 0, 0, 0)), _mm_shuffle_epi32(attenuation, _MM_SHUFFLE(1, 1, 1, 1))); + + __m128i light_color = _mm_cvtsi32_si128(lights[i].color); + light_color = _mm_unpacklo_epi8(light_color, _mm_setzero_si128()); + light_color = _mm_shuffle_epi32(light_color, _MM_SHUFFLE(1, 0, 1, 0)); + + lit = _mm_add_epi16(lit, _mm_srli_epi16(_mm_mullo_epi16(light_color, attenuation), 8)); + } + + fgcolor = _mm_add_epi16(fgcolor, _mm_srli_epi16(_mm_mullo_epi16(material, lit), 8)); + fgcolor = _mm_min_epi16(fgcolor, _mm_set1_epi16(255)); + return fgcolor; + } + + FORCEINLINE __m128i VECTORCALL Blend(__m128i fgcolor, __m128i bgcolor, unsigned int ifgcolor0, unsigned int ifgcolor1, uint32_t srcalpha, uint32_t destalpha) + { + using namespace DrawWall32TModes; + + if (BlendT::Mode == (int)WallBlendModes::Opaque) + { + __m128i outcolor = fgcolor; + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + return outcolor; + } + else if (BlendT::Mode == (int)WallBlendModes::Masked) + { +#if 0 // leaving this in for alpha texture support (todo: fix in texture manager later?) + __m128i alpha = _mm_shufflelo_epi16(fgcolor, _MM_SHUFFLE(3, 3, 3, 3)); + alpha = _mm_shufflehi_epi16(alpha, _MM_SHUFFLE(3, 3, 3, 3)); + alpha = _mm_add_epi16(alpha, _mm_srli_epi16(alpha, 7)); // 255 -> 256 + + __m128i inv_alpha = _mm_sub_epi16(_mm_set1_epi16(256), alpha); + + fgcolor = _mm_mullo_epi16(fgcolor, alpha); + bgcolor = _mm_mullo_epi16(bgcolor, inv_alpha); + __m128i outcolor = _mm_srli_epi16(_mm_add_epi16(fgcolor, bgcolor), 8); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; +#endif + __m128i mask = _mm_cmpeq_epi32(_mm_packus_epi16(fgcolor, _mm_setzero_si128()), _mm_setzero_si128()); + mask = _mm_unpacklo_epi8(mask, _mm_setzero_si128()); + __m128i outcolor = _mm_or_si128(_mm_and_si128(mask, bgcolor), _mm_andnot_si128(mask, fgcolor)); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + else + { + uint32_t alpha0 = APART(ifgcolor0); + uint32_t alpha1 = APART(ifgcolor1); + alpha0 += alpha0 >> 7; // 255->256 + alpha1 += alpha1 >> 7; // 255->256 + uint32_t inv_alpha0 = 256 - alpha0; + uint32_t inv_alpha1 = 256 - alpha1; + + uint32_t bgalpha0 = (destalpha * alpha0 + (inv_alpha0 << 8) + 128) >> 8; + uint32_t bgalpha1 = (destalpha * alpha1 + (inv_alpha1 << 8) + 128) >> 8; + uint32_t fgalpha0 = (srcalpha * alpha0 + 128) >> 8; + uint32_t fgalpha1 = (srcalpha * alpha1 + 128) >> 8; + + __m128i bgalpha = _mm_set_epi16(bgalpha1, bgalpha1, bgalpha1, bgalpha1, bgalpha0, bgalpha0, bgalpha0, bgalpha0); + __m128i fgalpha = _mm_set_epi16(fgalpha1, fgalpha1, fgalpha1, fgalpha1, fgalpha0, fgalpha0, fgalpha0, fgalpha0); + + fgcolor = _mm_mullo_epi16(fgcolor, fgalpha); + bgcolor = _mm_mullo_epi16(bgcolor, bgalpha); + + __m128i fg_lo = _mm_unpacklo_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_lo = _mm_unpacklo_epi16(bgcolor, _mm_setzero_si128()); + __m128i fg_hi = _mm_unpackhi_epi16(fgcolor, _mm_setzero_si128()); + __m128i bg_hi = _mm_unpackhi_epi16(bgcolor, _mm_setzero_si128()); + + __m128i out_lo, out_hi; + if (BlendT::Mode == (int)WallBlendModes::AddClamp) + { + out_lo = _mm_add_epi32(fg_lo, bg_lo); + out_hi = _mm_add_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)WallBlendModes::SubClamp) + { + out_lo = _mm_sub_epi32(fg_lo, bg_lo); + out_hi = _mm_sub_epi32(fg_hi, bg_hi); + } + else if (BlendT::Mode == (int)WallBlendModes::RevSubClamp) + { + out_lo = _mm_sub_epi32(bg_lo, fg_lo); + out_hi = _mm_sub_epi32(bg_hi, fg_hi); + } + + out_lo = _mm_srai_epi32(out_lo, 8); + out_hi = _mm_srai_epi32(out_hi, 8); + __m128i outcolor = _mm_packs_epi32(out_lo, out_hi); + outcolor = _mm_packus_epi16(outcolor, _mm_setzero_si128()); + outcolor = _mm_or_si128(outcolor, _mm_set1_epi32(0xff000000)); + return outcolor; + } + } + + FString DebugInfo() override { return "DrawWall32T"; } + }; + + typedef DrawWall32T DrawWall32Command; + typedef DrawWall32T DrawWallMasked32Command; + typedef DrawWall32T DrawWallAddClamp32Command; + typedef DrawWall32T DrawWallSubClamp32Command; + typedef DrawWall32T DrawWallRevSubClamp32Command; +} diff --git a/src/r_thread.cpp b/src/swrenderer/drawers/r_thread.cpp similarity index 71% rename from src/r_thread.cpp rename to src/swrenderer/drawers/r_thread.cpp index c96f14e74..adcee2768 100644 --- a/src/r_thread.cpp +++ b/src/swrenderer/drawers/r_thread.cpp @@ -25,87 +25,48 @@ #include "doomdef.h" #include "i_system.h" #include "w_wad.h" -#include "r_local.h" #include "v_video.h" #include "doomstat.h" #include "st_stuff.h" #include "g_game.h" #include "g_level.h" #include "r_thread.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" CVAR(Bool, r_multithreaded, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -void R_BeginDrawerCommands() -{ - DrawerCommandQueue::Begin(); -} - -void R_EndDrawerCommands() -{ - DrawerCommandQueue::End(); -} - ///////////////////////////////////////////////////////////////////////////// -DrawerCommandQueue *DrawerCommandQueue::Instance() +DrawerThreads *DrawerThreads::Instance() { - static DrawerCommandQueue queue; - return &queue; + static DrawerThreads threads; + return &threads; } -DrawerCommandQueue::DrawerCommandQueue() +DrawerThreads::DrawerThreads() { } -DrawerCommandQueue::~DrawerCommandQueue() +DrawerThreads::~DrawerThreads() { StopThreads(); } -void* DrawerCommandQueue::AllocMemory(size_t size) +void DrawerThreads::Execute(const std::vector &queues) { - // Make sure allocations remain 16-byte aligned - size = (size + 15) / 16 * 16; - - auto queue = Instance(); - if (queue->memorypool_pos + size > memorypool_size) - return nullptr; - - void *data = queue->memorypool + queue->memorypool_pos; - queue->memorypool_pos += size; - return data; -} - -void DrawerCommandQueue::Begin() -{ - auto queue = Instance(); - queue->Finish(); - queue->threaded_render++; -} - -void DrawerCommandQueue::End() -{ - auto queue = Instance(); - queue->Finish(); - if (queue->threaded_render > 0) - queue->threaded_render--; -} - -void DrawerCommandQueue::WaitForWorkers() -{ - Instance()->Finish(); -} - -void DrawerCommandQueue::Finish() -{ - auto queue = Instance(); - if (queue->commands.empty()) + bool hasWork = false; + for (const auto &queue : queues) + hasWork = hasWork || !queue->commands.empty(); + if (!hasWork) return; + + auto queue = Instance(); // Give worker threads something to do: std::unique_lock start_lock(queue->start_mutex); - queue->active_commands.swap(queue->commands); + queue->active_commands = queues; queue->run_id++; start_lock.unlock(); @@ -114,19 +75,21 @@ void DrawerCommandQueue::Finish() // Do one thread ourselves: - DrawerThread thread; + static DrawerThread thread; thread.core = 0; thread.num_cores = (int)(queue->threads.size() + 1); struct TryCatchData { - DrawerCommandQueue *queue; + DrawerThreads *queue; DrawerThread *thread; + size_t list_index; size_t command_index; } data; data.queue = queue; data.thread = &thread; + data.list_index = 0; data.command_index = 0; VectoredTryCatch(&data, [](void *data) @@ -140,18 +103,22 @@ void DrawerCommandQueue::Finish() if (pass + 1 == d->queue->num_passes) d->thread->pass_end_y = MAX(d->thread->pass_end_y, MAXHEIGHT); - size_t size = d->queue->active_commands.size(); - for (d->command_index = 0; d->command_index < size; d->command_index++) + for (auto &list : d->queue->active_commands) { - auto &command = d->queue->active_commands[d->command_index]; - command->Execute(d->thread); + size_t size = list->commands.size(); + for (d->command_index = 0; d->command_index < size; d->command_index++) + { + auto &command = list->commands[d->command_index]; + command->Execute(d->thread); + } + d->list_index++; } } }, [](void *data, const char *reason, bool fatal) { TryCatchData *d = (TryCatchData*)data; - ReportDrawerError(d->queue->active_commands[d->command_index], true, reason, fatal); + ReportDrawerError(d->queue->active_commands[d->list_index]->commands[d->command_index], true, reason, fatal); }); // Wait for everyone to finish: @@ -171,14 +138,17 @@ void DrawerCommandQueue::Finish() // Clean up batch: - for (auto &command : queue->active_commands) - command->~DrawerCommand(); + for (auto &list : queue->active_commands) + { + for (auto &command : list->commands) + command->~DrawerCommand(); + list->Clear(); + } queue->active_commands.clear(); - queue->memorypool_pos = 0; queue->finished_threads = 0; } -void DrawerCommandQueue::StartThreads() +void DrawerThreads::StartThreads() { if (!threads.empty()) return; @@ -191,7 +161,7 @@ void DrawerCommandQueue::StartThreads() for (int i = 0; i < num_threads - 1; i++) { - DrawerCommandQueue *queue = this; + DrawerThreads *queue = this; DrawerThread *thread = &threads[i]; thread->core = i + 1; thread->num_cores = num_threads; @@ -212,13 +182,15 @@ void DrawerCommandQueue::StartThreads() struct TryCatchData { - DrawerCommandQueue *queue; + DrawerThreads *queue; DrawerThread *thread; + size_t list_index; size_t command_index; } data; data.queue = queue; data.thread = thread; + data.list_index = 0; data.command_index = 0; VectoredTryCatch(&data, [](void *data) @@ -232,18 +204,22 @@ void DrawerCommandQueue::StartThreads() if (pass + 1 == d->queue->num_passes) d->thread->pass_end_y = MAX(d->thread->pass_end_y, MAXHEIGHT); - size_t size = d->queue->active_commands.size(); - for (d->command_index = 0; d->command_index < size; d->command_index++) + for (auto &list : d->queue->active_commands) { - auto &command = d->queue->active_commands[d->command_index]; - command->Execute(d->thread); + size_t size = list->commands.size(); + for (d->command_index = 0; d->command_index < size; d->command_index++) + { + auto &command = list->commands[d->command_index]; + command->Execute(d->thread); + } + d->list_index++; } } }, [](void *data, const char *reason, bool fatal) { TryCatchData *d = (TryCatchData*)data; - ReportDrawerError(d->queue->active_commands[d->command_index], true, reason, fatal); + ReportDrawerError(d->queue->active_commands[d->list_index]->commands[d->command_index], true, reason, fatal); }); // Notify main thread that we finished: @@ -256,7 +232,7 @@ void DrawerCommandQueue::StartThreads() } } -void DrawerCommandQueue::StopThreads() +void DrawerThreads::StopThreads() { std::unique_lock lock(start_mutex); shutdown_flag = true; @@ -269,7 +245,7 @@ void DrawerCommandQueue::StopThreads() shutdown_flag = false; } -void DrawerCommandQueue::ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal) +void DrawerThreads::ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal) { if (worker_thread) { @@ -291,7 +267,20 @@ void DrawerCommandQueue::ReportDrawerError(DrawerCommand *command, bool worker_t } } +#ifndef WIN32 + void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)) { tryBlock(data); } + +#endif + +DrawerCommandQueue::DrawerCommandQueue(swrenderer::RenderThread *renderthread) : renderthread(renderthread) +{ +} + +void *DrawerCommandQueue::AllocMemory(size_t size) +{ + return renderthread->FrameMemory->AllocMemory((int)size); +} diff --git a/src/r_thread.h b/src/swrenderer/drawers/r_thread.h similarity index 61% rename from src/r_thread.h rename to src/swrenderer/drawers/r_thread.h index e44d872c2..cc8d33eab 100644 --- a/src/r_thread.h +++ b/src/swrenderer/drawers/r_thread.h @@ -23,6 +23,7 @@ #pragma once #include "r_draw.h" +#include "polyrenderer/drawers/screen_triangle.h" #include #include #include @@ -32,20 +33,14 @@ // Use multiple threads when drawing EXTERN_CVAR(Bool, r_multithreaded) -// Redirect drawer commands to worker threads -void R_BeginDrawerCommands(); - -// Wait until all drawers finished executing -void R_EndDrawerCommands(); - // Worker data for each thread executing drawer commands class DrawerThread { public: DrawerThread() { - dc_temp = dc_temp_buff; - dc_temp_rgba = dc_temp_rgbabuff_rgba; + FullSpansBuffer.resize(MAXWIDTH / 8 * (MAXHEIGHT / 8)); + PartialBlocksBuffer.resize(MAXWIDTH / 8 * (MAXHEIGHT / 8)); } std::thread thread; @@ -60,17 +55,13 @@ public: int pass_start_y = 0; int pass_end_y = MAXHEIGHT; - // Working buffer used by Rt drawers - uint8_t dc_temp_buff[MAXHEIGHT * 4]; - uint8_t *dc_temp = nullptr; - - // Working buffer used by Rt drawers, true color edition - uint32_t dc_temp_rgbabuff_rgba[MAXHEIGHT * 4]; - uint32_t *dc_temp_rgba = nullptr; - // Working buffer used by the tilted (sloped) span drawer const uint8_t *tiltlighting[MAXWIDTH]; + // Working buffer used by the triangler drawer + std::vector FullSpansBuffer; + std::vector PartialBlocksBuffer; + // Checks if a line is rendered by this thread bool line_skipped_by_thread(int line) { @@ -111,39 +102,7 @@ public: // Task to be executed by each worker thread class DrawerCommand { -protected: - int _dest_y; - - void DetectRangeError(uint32_t *&dest, int &dest_y, int &count) - { -#if defined(_MSC_VER) && defined(_DEBUG) - if (dest_y < 0 || count < 0 || dest_y + count > swrenderer::drawerargs::dc_destheight) - __debugbreak(); // Buffer overrun detected! -#endif - - if (dest_y < 0) - { - count += dest_y; - dest_y = 0; - dest = (uint32_t*)swrenderer::drawerargs::dc_destorg; - } - else if (dest_y >= swrenderer::drawerargs::dc_destheight) - { - dest_y = 0; - count = 0; - } - - if (count < 0 || count > MAXHEIGHT) count = 0; - if (dest_y + count >= swrenderer::drawerargs::dc_destheight) - count = swrenderer::drawerargs::dc_destheight - dest_y; - } - public: - DrawerCommand() - { - _dest_y = static_cast((swrenderer::drawerargs::dc_dest - swrenderer::drawerargs::dc_destorg) / (swrenderer::drawerargs::dc_pitch)); - } - virtual ~DrawerCommand() { } virtual void Execute(DrawerThread *thread) = 0; @@ -152,20 +111,30 @@ public: void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)); -// Manages queueing up commands and executing them on worker threads -class DrawerCommandQueue +class DrawerCommandQueue; +typedef std::shared_ptr DrawerCommandQueuePtr; + +class DrawerThreads { - enum { memorypool_size = 16 * 1024 * 1024 }; - char memorypool[memorypool_size]; - size_t memorypool_pos = 0; - - std::vector commands; +public: + // Runs the collected commands on worker threads + static void Execute(const std::vector &queues); + +private: + DrawerThreads(); + ~DrawerThreads(); + + void StartThreads(); + void StopThreads(); + static DrawerThreads *Instance(); + static void ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal); + std::vector threads; std::mutex start_mutex; std::condition_variable start_condition; - std::vector active_commands; + std::vector active_commands; bool shutdown_flag = false; int run_id = 0; @@ -179,63 +148,45 @@ class DrawerCommandQueue DrawerThread single_core_thread; int num_passes = 1; int rows_in_pass = MAXHEIGHT; + + friend class DrawerCommandQueue; +}; - void StartThreads(); - void StopThreads(); - void Finish(); - - static DrawerCommandQueue *Instance(); - static void ReportDrawerError(DrawerCommand *command, bool worker_thread, const char *reason, bool fatal); - - DrawerCommandQueue(); - ~DrawerCommandQueue(); +namespace swrenderer { class RenderThread; } +class DrawerCommandQueue +{ public: - // Allocate memory valid for the duration of a command execution - static void* AllocMemory(size_t size); - + DrawerCommandQueue(swrenderer::RenderThread *renderthread); + + void Clear() { commands.clear(); } + // Queue command to be executed by drawer worker threads template - static void QueueCommand(Types &&... args) + void Push(Types &&... args) { - auto queue = Instance(); - if (queue->threaded_render == 0 || !r_multithreaded) + DrawerThreads *threads = DrawerThreads::Instance(); + if (ThreadedRender && r_multithreaded) { - T command(std::forward(args)...); - VectoredTryCatch(&command, - [](void *data) - { - T *c = (T*)data; - c->Execute(&Instance()->single_core_thread); - }, - [](void *data, const char *reason, bool fatal) - { - T *c = (T*)data; - ReportDrawerError(c, false, reason, fatal); - }); + void *ptr = AllocMemory(sizeof(T)); + T *command = new (ptr)T(std::forward(args)...); + commands.push_back(command); } else { - void *ptr = AllocMemory(sizeof(T)); - if (!ptr) // Out of memory - render what we got - { - queue->Finish(); - ptr = AllocMemory(sizeof(T)); - if (!ptr) - return; - } - T *command = new (ptr)T(std::forward(args)...); - queue->commands.push_back(command); + T command(std::forward(args)...); + command.Execute(&threads->single_core_thread); } } - - // Redirects all drawing commands to worker threads until End is called - // Begin/End blocks can be nested. - static void Begin(); - - // End redirection and wait until all worker threads finished executing - static void End(); - - // Waits until all worker threads finished executing - static void WaitForWorkers(); + + bool ThreadedRender = true; + +private: + // Allocate memory valid for the duration of a command execution + void *AllocMemory(size_t size); + + std::vector commands; + swrenderer::RenderThread *renderthread; + + friend class DrawerThreads; }; diff --git a/src/swrenderer/line/r_fogboundary.cpp b/src/swrenderer/line/r_fogboundary.cpp new file mode 100644 index 000000000..8bd907c70 --- /dev/null +++ b/src/swrenderer/line/r_fogboundary.cpp @@ -0,0 +1,148 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/line/r_fogboundary.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/scene/r_light.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +namespace swrenderer +{ + void RenderFogBoundary::Render(RenderThread *thread, int x1, int x2, short *uclip, short *dclip, int wallshade, float lightleft, float lightstep, FDynamicColormap *basecolormap) + { + // This is essentially the same as R_MapVisPlane but with an extra step + // to create new horizontal spans whenever the light changes enough that + // we need to use a new colormap. + + float light = lightleft + lightstep*(x2 - x1 - 1); + int x = x2 - 1; + int t2 = uclip[x]; + int b2 = dclip[x]; + int rcolormap = GETPALOOKUP(light, wallshade); + int lcolormap; + uint8_t *basecolormapdata = basecolormap->Maps; + + if (b2 > t2) + { + fillshort(spanend + t2, b2 - t2, x); + } + + drawerargs.SetLight(basecolormap, (float)light, wallshade); + + uint8_t *fake_dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); + + for (--x; x >= x1; --x) + { + int t1 = uclip[x]; + int b1 = dclip[x]; + const int xr = x + 1; + int stop; + + light -= lightstep; + lcolormap = GETPALOOKUP(light, wallshade); + if (lcolormap != rcolormap) + { + if (t2 < b2 && rcolormap != 0) + { // Colormap 0 is always the identity map, so rendering it is + // just a waste of time. + RenderSection(thread, t2, b2, xr); + } + if (t1 < t2) t2 = t1; + if (b1 > b2) b2 = b1; + if (t2 < b2) + { + fillshort(spanend + t2, b2 - t2, x); + } + rcolormap = lcolormap; + drawerargs.SetLight(basecolormap, (float)light, wallshade); + fake_dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); + } + else + { + if (fake_dc_colormap != basecolormapdata) + { + stop = MIN(t1, b2); + while (t2 < stop) + { + int y = t2++; + drawerargs.DrawFogBoundaryLine(thread, y, xr, spanend[y]); + } + stop = MAX(b1, t2); + while (b2 > stop) + { + int y = --b2; + drawerargs.DrawFogBoundaryLine(thread, y, xr, spanend[y]); + } + } + else + { + t2 = MAX(t2, MIN(t1, b2)); + b2 = MIN(b2, MAX(b1, t2)); + } + + stop = MIN(t2, b1); + while (t1 < stop) + { + spanend[t1++] = x; + } + stop = MAX(b2, t2); + while (b1 > stop) + { + spanend[--b1] = x; + } + } + + t2 = uclip[x]; + b2 = dclip[x]; + } + if (t2 < b2 && rcolormap != 0) + { + RenderSection(thread, t2, b2, x1); + } + } + + void RenderFogBoundary::RenderSection(RenderThread *thread, int y, int y2, int x1) + { + for (; y < y2; ++y) + { + drawerargs.DrawFogBoundaryLine(thread, y, x1, spanend[y]); + } + } +} diff --git a/src/swrenderer/line/r_fogboundary.h b/src/swrenderer/line/r_fogboundary.h new file mode 100644 index 000000000..c7df265a4 --- /dev/null +++ b/src/swrenderer/line/r_fogboundary.h @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/viewport/r_spandrawer.h" + +namespace swrenderer +{ + class RenderThread; + + class RenderFogBoundary + { + public: + void Render(RenderThread *thread, int x1, int x2, short *uclip, short *dclip, int wallshade, float lightleft, float lightstep, FDynamicColormap *basecolormap); + + private: + void RenderSection(RenderThread *thread, int y, int y2, int x1); + + short spanend[MAXHEIGHT]; + SpanDrawerArgs drawerargs; + }; +} diff --git a/src/swrenderer/line/r_line.cpp b/src/swrenderer/line/r_line.cpp new file mode 100644 index 000000000..963e3d17b --- /dev/null +++ b/src/swrenderer/line/r_line.cpp @@ -0,0 +1,1373 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "r_sky.h" +#include "v_video.h" +#include "m_swap.h" +#include "w_wad.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "r_wallsetup.h" +#include "v_palette.h" +#include "r_utility.h" +#include "r_data/colormaps.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/line/r_line.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/things/r_decal.h" +#include "swrenderer/r_renderthread.h" + +CVAR(Bool, r_fogboundary, true, 0) +CVAR(Bool, r_drawmirrors, true, 0) +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + SWRenderLine::SWRenderLine(RenderThread *thread) + { + Thread = thread; + } + + void SWRenderLine::Render(seg_t *line, subsector_t *subsector, sector_t *sector, sector_t *fakebacksector, VisiblePlane *linefloorplane, VisiblePlane *lineceilingplane, bool infog, FDynamicColormap *colormap) + { + mSubsector = subsector; + mFrontSector = sector; + mBackSector = fakebacksector; + mFloorPlane = linefloorplane; + mCeilingPlane = lineceilingplane; + foggy = infog; + basecolormap = colormap; + mLineSegment = line; + + DVector2 pt1 = line->v1->fPos() - ViewPos; + DVector2 pt2 = line->v2->fPos() - ViewPos; + + // Reject lines not facing viewer + if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0) + return; + + if (WallC.Init(Thread, pt1, pt2, 32.0 / (1 << 12))) + return; + + RenderPortal *renderportal = Thread->Portal.get(); + if (WallC.sx1 >= renderportal->WindowRight || WallC.sx2 <= renderportal->WindowLeft) + return; + + if (line->linedef == nullptr) + { + if (Thread->ClipSegments->Check(WallC.sx1, WallC.sx2)) + { + mSubsector->flags |= SSECF_DRAWN; + } + return; + } + + // reject lines that aren't seen from the portal (if any) + // [ZZ] 10.01.2016: lines inside a skybox shouldn't be clipped, although this imposes some limitations on portals in skyboxes. + if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && P_ClipLineToPortal(line->linedef, renderportal->CurrentPortal->dst, ViewPos)) + return; + + vertex_t *v1 = line->linedef->v1; + vertex_t *v2 = line->linedef->v2; + + if ((v1 == line->v1 && v2 == line->v2) || (v2 == line->v1 && v1 == line->v2)) + { // The seg is the entire wall. + WallT.InitFromWallCoords(Thread, &WallC); + } + else + { // The seg is only part of the wall. + if (line->linedef->sidedef[0] != line->sidedef) + { + swapvalues(v1, v2); + } + WallT.InitFromLine(Thread, v1->fPos() - ViewPos, v2->fPos() - ViewPos); + } + + mFrontCeilingZ1 = mFrontSector->ceilingplane.ZatPoint(line->v1); + mFrontFloorZ1 = mFrontSector->floorplane.ZatPoint(line->v1); + mFrontCeilingZ2 = mFrontSector->ceilingplane.ZatPoint(line->v2); + mFrontFloorZ2 = mFrontSector->floorplane.ZatPoint(line->v2); + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + if (!(clip3d->fake3D & FAKE3D_FAKEBACK)) + { + mBackSector = line->backsector; + } + + if (mBackSector) + { + // kg3D - its fake, no transfer_heights + if (!(clip3d->fake3D & FAKE3D_FAKEBACK)) + { // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water + mBackSector = Thread->OpaquePass->FakeFlat(mBackSector, &tempsec, nullptr, nullptr, mLineSegment, WallC.sx1, WallC.sx2, mFrontCeilingZ1, mFrontCeilingZ2); + } + + mBackCeilingZ1 = mBackSector->ceilingplane.ZatPoint(line->v1); + mBackFloorZ1 = mBackSector->floorplane.ZatPoint(line->v1); + mBackCeilingZ2 = mBackSector->ceilingplane.ZatPoint(line->v2); + mBackFloorZ2 = mBackSector->floorplane.ZatPoint(line->v2); + + if (clip3d->fake3D & FAKE3D_FAKEBACK) + { + if (mFrontFloorZ1 >= mBackFloorZ1 && mFrontFloorZ2 >= mBackFloorZ2) + { + clip3d->fake3D |= FAKE3D_CLIPBOTFRONT; + } + if (mFrontCeilingZ1 <= mBackCeilingZ1 && mFrontCeilingZ2 <= mBackCeilingZ2) + { + clip3d->fake3D |= FAKE3D_CLIPTOPFRONT; + } + } + } + + mDoorClosed = IsDoorClosed(); + + if (IsInvisibleLine()) + { + // When using GL nodes, do a clipping test for these lines so we can + // mark their subsectors as visible for automap texturing. + if (hasglnodes && !(mSubsector->flags & SSECF_DRAWN)) + { + if (Thread->ClipSegments->Check(WallC.sx1, WallC.sx2)) + { + mSubsector->flags |= SSECF_DRAWN; + } + } + return; + } + + rw_prepped = false; + + bool visible = Thread->ClipSegments->Clip(WallC.sx1, WallC.sx2, IsSolid(), this); + + if (visible) + { + mSubsector->flags |= SSECF_DRAWN; + } + } + + bool SWRenderLine::IsInvisibleLine() const + { + // Reject empty lines used for triggers and special events. + // Identical floor and ceiling on both sides, identical light levels + // on both sides, and no middle texture. + + if (!mBackSector) return false; + + // Portal + if (mLineSegment->linedef->isVisualPortal() && mLineSegment->sidedef == mLineSegment->linedef->sidedef[0]) return false; + + // Closed door. + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return false; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return false; + if (IsDoorClosed()) return false; + + // Window. + if (mFrontSector->ceilingplane != mBackSector->ceilingplane || mFrontSector->floorplane != mBackSector->floorplane) return false; + if (SkyboxCompare(mFrontSector, mBackSector)) return false; + + if (mBackSector->lightlevel != mFrontSector->lightlevel) return false; + if (mBackSector->GetTexture(sector_t::floor) != mFrontSector->GetTexture(sector_t::floor)) return false; + if (mBackSector->GetTexture(sector_t::ceiling) != mFrontSector->GetTexture(sector_t::ceiling)) return false; + if (mLineSegment->sidedef->GetTexture(side_t::mid).isValid()) return false; + + if (mBackSector->planes[sector_t::floor].xform != mFrontSector->planes[sector_t::floor].xform) return false; + if (mBackSector->planes[sector_t::ceiling].xform != mFrontSector->planes[sector_t::ceiling].xform) return false; + + if (mBackSector->GetPlaneLight(sector_t::floor) != mFrontSector->GetPlaneLight(sector_t::floor)) return false; + if (mBackSector->GetPlaneLight(sector_t::ceiling) != mFrontSector->GetPlaneLight(sector_t::ceiling)) return false; + if (mBackSector->GetVisFlags(sector_t::floor) != mFrontSector->GetVisFlags(sector_t::floor)) return false; + if (mBackSector->GetVisFlags(sector_t::ceiling) != mFrontSector->GetVisFlags(sector_t::ceiling)) return false; + + if (mBackSector->ColorMap != mFrontSector->ColorMap) return false; + + if (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size()) return false; + if (mBackSector->e && mBackSector->e->XFloor.lightlist.Size()) return false; + + return true; + } + + bool SWRenderLine::IsSolid() const + { + // One sided + if (mBackSector == nullptr) return true; + + // Portal + if (mLineSegment->linedef->isVisualPortal() && mLineSegment->sidedef == mLineSegment->linedef->sidedef[0]) return true; + + // Closed door + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return true; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return true; + if (IsDoorClosed()) return true; + + return false; + } + + bool SWRenderLine::IsDoorClosed() const + { + if (!mBackSector) return false; + + // Portal + if (mLineSegment->linedef->isVisualPortal() && mLineSegment->sidedef == mLineSegment->linedef->sidedef[0]) return false; + + // Closed door. + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return false; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return false; + + // properly render skies (consider door "open" if both ceilings are sky) + if (mBackSector->GetTexture(sector_t::ceiling) == skyflatnum && mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum) return false; + + // if door is closed because back is shut: + if (!(mBackCeilingZ1 <= mBackFloorZ1 && mBackCeilingZ2 <= mBackFloorZ2)) return false; + + // preserve a kind of transparent door/lift special effect: + if (((mBackCeilingZ1 >= mFrontCeilingZ1 && mBackCeilingZ2 >= mFrontCeilingZ2) || mLineSegment->sidedef->GetTexture(side_t::top).isValid()) + && ((mBackFloorZ1 <= mFrontFloorZ1 && mBackFloorZ2 <= mFrontFloorZ2) || mLineSegment->sidedef->GetTexture(side_t::bottom).isValid())) + { + // killough 1/18/98 -- This function is used to fix the automap bug which + // showed lines behind closed doors simply because the door had a dropoff. + // + // It assumes that Doom has already ruled out a door being closed because + // of front-back closure (e.g. front floor is taller than back ceiling). + + // This fixes the automap floor height bug -- killough 1/18/98: + // killough 4/7/98: optimize: save result in doorclosed for use in r_segs.c + return true; + } + + return false; + } + + bool SWRenderLine::SkyboxCompare(sector_t *frontsector, sector_t *backsector) const + { + FSectorPortal *frontc = frontsector->GetPortal(sector_t::ceiling); + FSectorPortal *frontf = frontsector->GetPortal(sector_t::floor); + FSectorPortal *backc = backsector->GetPortal(sector_t::ceiling); + FSectorPortal *backf = backsector->GetPortal(sector_t::floor); + + // return true if any of the planes has a linedef-based portal (unless both sides have the same one. + // Ideally this should also check thing based portals but the omission of this check had been abused to hell and back for those. + // (Note: This may require a compatibility option if some maps ran into this for line based portals as well.) + if (!frontc->MergeAllowed()) return (frontc != backc); + if (!frontf->MergeAllowed()) return (frontf != backf); + if (!backc->MergeAllowed()) return true; + if (!backf->MergeAllowed()) return true; + return false; + } + + // A wall segment will be drawn between start and stop pixels (inclusive). + bool SWRenderLine::RenderWallSegment(int start, int stop) + { + int i; + bool maskedtexture = false; + +#ifdef RANGECHECK + if (start >= viewwidth || start >= stop) + I_FatalError("Bad R_StoreWallRange: %i to %i", start, stop); +#endif + + if (!rw_prepped) + { + rw_prepped = true; + SetWallVariables(true); + } + + side_t *sidedef = mLineSegment->sidedef; + + RenderPortal *renderportal = Thread->Portal.get(); + + DrawSegment *draw_segment = Thread->FrameMemory->NewObject(); + Thread->DrawSegments->Push(draw_segment); + + draw_segment->CurrentPortalUniq = renderportal->CurrentPortalUniq; + draw_segment->sx1 = WallC.sx1; + draw_segment->sx2 = WallC.sx2; + draw_segment->sz1 = WallC.sz1; + draw_segment->sz2 = WallC.sz2; + draw_segment->cx = WallC.tleft.X;; + draw_segment->cy = WallC.tleft.Y; + draw_segment->cdx = WallC.tright.X - WallC.tleft.X; + draw_segment->cdy = WallC.tright.Y - WallC.tleft.Y; + draw_segment->tmapvals = WallT; + draw_segment->siz1 = 1 / WallC.sz1; + draw_segment->siz2 = 1 / WallC.sz2; + draw_segment->x1 = start; + draw_segment->x2 = stop; + draw_segment->curline = mLineSegment; + draw_segment->bFogBoundary = false; + draw_segment->bFakeBoundary = false; + draw_segment->foggy = foggy; + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + if (clip3d->fake3D & FAKE3D_FAKEMASK) draw_segment->fake = 1; + else draw_segment->fake = 0; + + draw_segment->sprtopclip = nullptr; + draw_segment->sprbottomclip = nullptr; + draw_segment->maskedtexturecol = nullptr; + draw_segment->bkup = nullptr; + draw_segment->swall = nullptr; + + bool markportal = ShouldMarkPortal(); + + if (markportal) + { + draw_segment->silhouette = SIL_BOTH; + } + else if (mBackSector == NULL) + { + draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory(stop - start); + draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory(stop - start); + fillshort(draw_segment->sprtopclip, stop - start, viewheight); + memset(draw_segment->sprbottomclip, -1, (stop - start) * sizeof(short)); + draw_segment->silhouette = SIL_BOTH; + } + else + { + // two sided line + draw_segment->silhouette = 0; + + if (mFrontFloorZ1 > mBackFloorZ1 || mFrontFloorZ2 > mBackFloorZ2 || + mBackSector->floorplane.PointOnSide(ViewPos) < 0) + { + draw_segment->silhouette = SIL_BOTTOM; + } + + if (mFrontCeilingZ1 < mBackCeilingZ1 || mFrontCeilingZ2 < mBackCeilingZ2 || + mBackSector->ceilingplane.PointOnSide(ViewPos) < 0) + { + draw_segment->silhouette |= SIL_TOP; + } + + // killough 1/17/98: this test is required if the fix + // for the automap bug (r_bsp.c) is used, or else some + // sprites will be displayed behind closed doors. That + // fix prevents lines behind closed doors with dropoffs + // from being displayed on the automap. + // + // killough 4/7/98: make doorclosed external variable + + { + if (mDoorClosed || (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2)) + { + draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory(stop - start); + memset(draw_segment->sprbottomclip, -1, (stop - start) * sizeof(short)); + draw_segment->silhouette |= SIL_BOTTOM; + } + if (mDoorClosed || (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2)) + { // killough 1/17/98, 2/8/98 + draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory(stop - start); + fillshort(draw_segment->sprtopclip, stop - start, viewheight); + draw_segment->silhouette |= SIL_TOP; + } + } + + if (!draw_segment->fake && r_3dfloors && mBackSector->e && mBackSector->e->XFloor.ffloors.Size()) { + for (i = 0; i < (int)mBackSector->e->XFloor.ffloors.Size(); i++) { + F3DFloor *rover = mBackSector->e->XFloor.ffloors[i]; + if (rover->flags & FF_RENDERSIDES && (!(rover->flags & FF_INVERTSIDES) || rover->flags & FF_ALLSIDES)) { + draw_segment->bFakeBoundary |= 1; + break; + } + } + } + if (!draw_segment->fake && r_3dfloors && mFrontSector->e && mFrontSector->e->XFloor.ffloors.Size()) { + for (i = 0; i < (int)mFrontSector->e->XFloor.ffloors.Size(); i++) { + F3DFloor *rover = mFrontSector->e->XFloor.ffloors[i]; + if (rover->flags & FF_RENDERSIDES && (rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) { + draw_segment->bFakeBoundary |= 2; + break; + } + } + } + // kg3D - no for fakes + if (!draw_segment->fake) + // allocate space for masked texture tables, if needed + // [RH] Don't just allocate the space; fill it in too. + if ((TexMan(sidedef->GetTexture(side_t::mid), true)->UseType != FTexture::TEX_Null || draw_segment->bFakeBoundary || IsFogBoundary(mFrontSector, mBackSector)) && + (mCeilingClipped != ProjectedWallCull::OutsideBelow || !sidedef->GetTexture(side_t::top).isValid()) && + (mFloorClipped != ProjectedWallCull::OutsideAbove || !sidedef->GetTexture(side_t::bottom).isValid()) && + (WallC.sz1 >= TOO_CLOSE_Z && WallC.sz2 >= TOO_CLOSE_Z)) + { + float *swal; + fixed_t *lwal; + int i; + + maskedtexture = true; + + // kg3D - backup for mid and fake walls + draw_segment->bkup = Thread->FrameMemory->AllocMemory(stop - start); + memcpy(draw_segment->bkup, &Thread->OpaquePass->ceilingclip[start], sizeof(short)*(stop - start)); + + draw_segment->bFogBoundary = IsFogBoundary(mFrontSector, mBackSector); + if (sidedef->GetTexture(side_t::mid).isValid() || draw_segment->bFakeBoundary) + { + if (sidedef->GetTexture(side_t::mid).isValid()) + draw_segment->bFakeBoundary |= 4; // it is also mid texture + + draw_segment->maskedtexturecol = Thread->FrameMemory->AllocMemory(stop - start); + draw_segment->swall = Thread->FrameMemory->AllocMemory(stop - start); + + lwal = draw_segment->maskedtexturecol; + swal = draw_segment->swall; + FTexture *pic = TexMan(sidedef->GetTexture(side_t::mid), true); + double yscale = pic->Scale.Y * sidedef->GetTextureYScale(side_t::mid); + fixed_t xoffset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); + + if (pic->bWorldPanning) + { + xoffset = xs_RoundToInt(xoffset * lwallscale); + } + + for (i = start; i < stop; i++) + { + *lwal++ = walltexcoords.UPos[i] + xoffset; + *swal++ = walltexcoords.VStep[i]; + } + + double istart = draw_segment->swall[0] * yscale; + double iend = *(swal - 1) * yscale; +#if 0 + ///This was for avoiding overflow when using fixed point. It might not be needed anymore. + const double mini = 3 / 65536.0; + if (istart < mini && istart >= 0) istart = mini; + if (istart > -mini && istart < 0) istart = -mini; + if (iend < mini && iend >= 0) iend = mini; + if (iend > -mini && iend < 0) iend = -mini; +#endif + istart = 1 / istart; + iend = 1 / iend; + draw_segment->yscale = (float)yscale; + draw_segment->iscale = (float)istart; + if (stop - start > 0) + { + draw_segment->iscalestep = float((iend - istart) / (stop - start)); + } + else + { + draw_segment->iscalestep = 0; + } + } + draw_segment->light = rw_lightleft + rw_lightstep * (start - WallC.sx1); + draw_segment->lightstep = rw_lightstep; + + // Masked mMiddlePart.Textures should get the light level from the sector they reference, + // not from the current subsector, which is what the current wallshade value + // comes from. We make an exeption for polyobjects, however, since their "home" + // sector should be whichever one they move into. + if (mLineSegment->sidedef->Flags & WALLF_POLYOBJ) + { + draw_segment->shade = wallshade; + } + else + { + draw_segment->shade = LightVisibility::LightLevelToShade(mLineSegment->sidedef->GetLightLevel(foggy, mLineSegment->frontsector->lightlevel) + LightVisibility::ActualExtraLight(foggy), foggy); + } + + if (draw_segment->bFogBoundary || draw_segment->maskedtexturecol != nullptr) + { + Thread->DrawSegments->PushInteresting(draw_segment); + } + } + } + + ClipSegmentTopBottom(start, stop); + + MarkCeilingPlane(start, stop); + MarkFloorPlane(start, stop); + Mark3DFloors(start, stop); + + if (clip3d->fake3D & FAKE3D_FAKEMASK) + { + return (clip3d->fake3D & FAKE3D_FAKEMASK) == 0; + } + + MarkOpaquePassClip(start, stop); + + // save sprite clipping info + if (((draw_segment->silhouette & SIL_TOP) || maskedtexture) && draw_segment->sprtopclip == nullptr) + { + draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory(stop - start); + memcpy(draw_segment->sprtopclip, &Thread->OpaquePass->ceilingclip[start], sizeof(short)*(stop - start)); + } + + if (((draw_segment->silhouette & SIL_BOTTOM) || maskedtexture) && draw_segment->sprbottomclip == nullptr) + { + draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory(stop - start); + memcpy(draw_segment->sprbottomclip, &Thread->OpaquePass->floorclip[start], sizeof(short)*(stop - start)); + } + + if (maskedtexture && mLineSegment->sidedef->GetTexture(side_t::mid).isValid()) + { + draw_segment->silhouette |= SIL_TOP | SIL_BOTTOM; + } + + RenderMiddleTexture(start, stop); + RenderTopTexture(start, stop); + RenderBottomTexture(start, stop); + + // [RH] Draw any decals bound to the seg + // [ZZ] Only if not an active mirror + if (!markportal) + { + RenderDecal::RenderDecals(Thread, mLineSegment->sidedef, draw_segment, wallshade, rw_lightleft, rw_lightstep, mLineSegment, WallC, foggy, basecolormap, walltop.ScreenY, wallbottom.ScreenY); + } + + if (markportal) + { + Thread->Portal->AddLinePortal(mLineSegment->linedef, draw_segment->x1, draw_segment->x2, draw_segment->sprtopclip, draw_segment->sprbottomclip); + } + + return (clip3d->fake3D & FAKE3D_FAKEMASK) == 0; + } + + bool SWRenderLine::ShouldMarkFloor() const + { + if (!mFloorPlane) + return false; + + // deep water check + if (mFrontSector->GetHeightSec() == nullptr) + { + int planeside = mFrontSector->floorplane.PointOnSide(ViewPos); + if (mFrontSector->floorplane.fC() < 0) // 3D floors have the floor backwards + planeside = -planeside; + if (planeside <= 0) // above view plane + return false; + } + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) + { + return true; + } + else if (mBackSector == nullptr) // single sided line + { + return true; + } + else // two-sided line + { + if (linedef->isVisualPortal()) return true; + + // closed door + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return true; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return true; + + if (mBackSector->floorplane != mFrontSector->floorplane) return true; + if (mBackSector->lightlevel != mFrontSector->lightlevel) return true; + if (mBackSector->GetTexture(sector_t::floor) != mFrontSector->GetTexture(sector_t::floor)) return true; + if (mBackSector->GetPlaneLight(sector_t::floor) != mFrontSector->GetPlaneLight(sector_t::floor)) return true; + + // Add checks for (x,y) offsets + if (mBackSector->planes[sector_t::floor].xform != mFrontSector->planes[sector_t::floor].xform) return true; + if (mBackSector->GetAlpha(sector_t::floor) != mFrontSector->GetAlpha(sector_t::floor)) return true; + + // prevent 2s normals from bleeding through deep water + if (mFrontSector->heightsec) return true; + + if (mBackSector->GetVisFlags(sector_t::floor) != mFrontSector->GetVisFlags(sector_t::floor)) return true; + if (mBackSector->ColorMap != mFrontSector->ColorMap) return true; + if (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size()) return true; + if (mBackSector->e && mBackSector->e->XFloor.lightlist.Size()) return true; + + if (sidedef->GetTexture(side_t::mid).isValid() && ((ib_compatflags & BCOMPATF_CLIPMIDTEX) || (linedef->flags & (ML_CLIP_MIDTEX | ML_WRAP_MIDTEX)) || sidedef->Flags & (WALLF_CLIP_MIDTEX | WALLF_WRAP_MIDTEX))) return true; + + return false; + } + } + + bool SWRenderLine::ShouldMarkCeiling() const + { + if (!mCeilingPlane) + return false; + + // deep water check + if (mFrontSector->GetHeightSec() == nullptr && mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum) + { + int planeside = mFrontSector->ceilingplane.PointOnSide(ViewPos); + if (mFrontSector->ceilingplane.fC() > 0) // 3D floors have the ceiling backwards + planeside = -planeside; + if (planeside <= 0) // below view plane + return false; + } + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) + { + return true; + } + else if (mBackSector == nullptr) // single sided line + { + return true; + } + else // two-sided line + { + if (linedef->isVisualPortal()) return true; + + // closed door + if (mBackCeilingZ1 <= mFrontFloorZ1 && mBackCeilingZ2 <= mFrontFloorZ2) return true; + if (mBackFloorZ1 >= mFrontCeilingZ1 && mBackFloorZ2 >= mFrontCeilingZ2) return true; + + if (mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum || mBackSector->GetTexture(sector_t::ceiling) != skyflatnum) + { + if (mBackSector->ceilingplane != mFrontSector->ceilingplane) return true; + if (mBackSector->lightlevel != mFrontSector->lightlevel) return true; + if (mBackSector->GetTexture(sector_t::ceiling) != mFrontSector->GetTexture(sector_t::ceiling)) return true; + + // Add checks for (x,y) offsets + if (mBackSector->planes[sector_t::ceiling].xform != mFrontSector->planes[sector_t::ceiling].xform) return true; + if (mBackSector->GetAlpha(sector_t::ceiling) != mFrontSector->GetAlpha(sector_t::ceiling)) return true; + + // prevent 2s normals from bleeding through fake ceilings + if (mFrontSector->heightsec && mFrontSector->GetTexture(sector_t::ceiling) != skyflatnum) return true; + + if (mBackSector->GetPlaneLight(sector_t::ceiling) != mFrontSector->GetPlaneLight(sector_t::ceiling)) return true; + if (mBackSector->GetFlags(sector_t::ceiling) != mFrontSector->GetFlags(sector_t::ceiling)) return true; + + if (mBackSector->ColorMap != mFrontSector->ColorMap) return true; + if (mFrontSector->e && mFrontSector->e->XFloor.lightlist.Size()) return true; + if (mBackSector->e && mBackSector->e->XFloor.lightlist.Size()) return true; + + if (sidedef->GetTexture(side_t::mid).isValid()) + { + if (ib_compatflags & BCOMPATF_CLIPMIDTEX) return true; + if (linedef->flags & (ML_CLIP_MIDTEX | ML_WRAP_MIDTEX)) return true; + if (sidedef->Flags & (WALLF_CLIP_MIDTEX | WALLF_WRAP_MIDTEX)) return true; + } + } + return false; + } + } + + bool SWRenderLine::ShouldMarkPortal() const + { + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) + { + return true; + } + else + { + return linedef->isVisualPortal(); + } + } + + void SWRenderLine::SetWallVariables(bool needlights) + { + RenderPortal *renderportal = Thread->Portal.get(); + + bool rw_havehigh = false; + bool rw_havelow = false; + if (mBackSector) + { + // Cannot make these walls solid, because it can result in + // sprite clipping problems for sprites near the wall + if (mFrontCeilingZ1 > mBackCeilingZ1 || mFrontCeilingZ2 > mBackCeilingZ2) + { + rw_havehigh = true; + wallupper.Project(mBackSector->ceilingplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP); + } + if (mFrontFloorZ1 < mBackFloorZ1 || mFrontFloorZ2 < mBackFloorZ2) + { + rw_havelow = true; + walllower.Project(mBackSector->floorplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP); + } + } + + if (mLineSegment->linedef->special == Line_Horizon) + { + // Be aware: Line_Horizon does not work properly with sloped planes + fillshort(walltop.ScreenY + WallC.sx1, WallC.sx2 - WallC.sx1, centery); + fillshort(wallbottom.ScreenY + WallC.sx1, WallC.sx2 - WallC.sx1, centery); + } + else + { + mCeilingClipped = walltop.Project(mFrontSector->ceilingplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP); + mFloorClipped = wallbottom.Project(mFrontSector->floorplane, &WallC, mLineSegment, renderportal->MirrorFlags & RF_XFLIP); + } + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + + // mark the segment as visible for auto map + if (!Thread->Scene->DontMapLines()) linedef->flags |= ML_MAPPED; + + markfloor = ShouldMarkFloor(); + markceiling = ShouldMarkCeiling(); + + SetTopTexture(); + SetMiddleTexture(); + SetBottomTexture(); + + if (mBackSector && !(sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors))) + { + // skyhack to allow height changes in outdoor areas + if (mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum && + mBackSector->GetTexture(sector_t::ceiling) == skyflatnum) + { + if (rw_havehigh) + { // front ceiling is above back ceiling + memcpy(&walltop.ScreenY[WallC.sx1], &wallupper.ScreenY[WallC.sx1], (WallC.sx2 - WallC.sx1) * sizeof(walltop.ScreenY[0])); + rw_havehigh = false; + } + else if (rw_havelow && mFrontSector->ceilingplane != mBackSector->ceilingplane) + { // back ceiling is above front ceiling + // The check for rw_havelow is not Doom-compliant, but it avoids HoM that + // would otherwise occur because there is space made available for this + // wall but nothing to draw for it. + // Recalculate walltop so that the wall is clipped by the back sector's + // ceiling instead of the front sector's ceiling. + walltop.Project(mBackSector->ceilingplane, &WallC, mLineSegment, Thread->Portal->MirrorFlags & RF_XFLIP); + } + } + } + + FTexture *midtex = TexMan(sidedef->GetTexture(side_t::mid), true); + + bool segtextured = midtex != NULL || mTopPart.Texture != NULL || mBottomPart.Texture != NULL; + + // calculate light table + if (needlights && (segtextured || (mBackSector && IsFogBoundary(mFrontSector, mBackSector)))) + { + lwallscale = + midtex ? (midtex->Scale.X * sidedef->GetTextureXScale(side_t::mid)) : + mTopPart.Texture ? (mTopPart.Texture->Scale.X * sidedef->GetTextureXScale(side_t::top)) : + mBottomPart.Texture ? (mBottomPart.Texture->Scale.X * sidedef->GetTextureXScale(side_t::bottom)) : + 1.; + + walltexcoords.Project(sidedef->TexelLength * lwallscale, WallC.sx1, WallC.sx2, WallT); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap() == nullptr && cameraLight->FixedLightLevel() < 0) + { + wallshade = LightVisibility::LightLevelToShade(mLineSegment->sidedef->GetLightLevel(foggy, mFrontSector->lightlevel) + LightVisibility::ActualExtraLight(foggy), foggy); + double GlobVis = LightVisibility::Instance()->WallGlobVis(foggy); + rw_lightleft = float(GlobVis / WallC.sz1); + rw_lightstep = float((GlobVis / WallC.sz2 - rw_lightleft) / (WallC.sx2 - WallC.sx1)); + } + else + { + rw_lightleft = 1; + rw_lightstep = 0; + } + } + } + + void SWRenderLine::SetTopTexture() + { + mTopPart.Texture = nullptr; + + if (!(mFrontCeilingZ1 > mBackCeilingZ1 || mFrontCeilingZ2 > mBackCeilingZ2)) return; + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) return; + if (!mBackSector) return; + + // No top texture for skyhack lines + if (mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum && mBackSector->GetTexture(sector_t::ceiling) == skyflatnum) return; + + mTopPart.Texture = TexMan(sidedef->GetTexture(side_t::top), true); + + mTopPart.TextureOffsetU = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::top)); + double rowoffset = sidedef->GetTextureYOffset(side_t::top); + mTopPart.TextureScaleU = sidedef->GetTextureXScale(side_t::top); + mTopPart.TextureScaleV = sidedef->GetTextureYScale(side_t::top); + double yrepeat = mTopPart.Texture->Scale.Y * mTopPart.TextureScaleV; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGTOP) + { // top of texture at top + mTopPart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; + if (rowoffset < 0 && mTopPart.Texture != NULL) + { + rowoffset += mTopPart.Texture->GetHeight(); + } + } + else + { // bottom of texture at bottom + mTopPart.TextureMid = (mBackSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + mTopPart.Texture->GetHeight(); + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGTOP) + { // bottom of texture at top + mTopPart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + mTopPart.Texture->GetHeight(); + } + else + { // top of texture at bottom + mTopPart.TextureMid = (mBackSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; + } + } + if (mTopPart.Texture->bWorldPanning) + { + mTopPart.TextureMid += rowoffset * yrepeat; + } + else + { + mTopPart.TextureMid += rowoffset; + } + } + + void SWRenderLine::SetMiddleTexture() + { + mMiddlePart.Texture = nullptr; + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) return; + if (mBackSector) return; + + // [RH] Horizon lines do not need to be textured + if (linedef->isVisualPortal()) return; + if (linedef->special == Line_Horizon) return; + + mMiddlePart.Texture = TexMan(sidedef->GetTexture(side_t::mid), true); + mMiddlePart.TextureOffsetU = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); + double rowoffset = sidedef->GetTextureYOffset(side_t::mid); + mMiddlePart.TextureScaleU = sidedef->GetTextureXScale(side_t::mid); + mMiddlePart.TextureScaleV = sidedef->GetTextureYScale(side_t::mid); + double yrepeat = mMiddlePart.Texture->Scale.Y * mMiddlePart.TextureScaleV; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + mMiddlePart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + mMiddlePart.Texture->GetHeight(); + } + else + { // top of texture at top + mMiddlePart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; + if (rowoffset < 0 && mMiddlePart.Texture != NULL) + { + rowoffset += mMiddlePart.Texture->GetHeight(); + } + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGBOTTOM) + { // top of texture at bottom + mMiddlePart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat; + } + else + { // bottom of texture at top + mMiddlePart.TextureMid = (mFrontSector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + mMiddlePart.Texture->GetHeight(); + } + } + if (mMiddlePart.Texture->bWorldPanning) + { + mMiddlePart.TextureMid += rowoffset * yrepeat; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + mMiddlePart.TextureMid += rowoffset; + } + } + + void SWRenderLine::SetBottomTexture() + { + mBottomPart.Texture = nullptr; + + if (!(mFrontFloorZ1 < mBackFloorZ1 || mFrontFloorZ2 < mBackFloorZ2)) return; + + side_t *sidedef = mLineSegment->sidedef; + line_t *linedef = mLineSegment->linedef; + if (sidedef == linedef->sidedef[0] && (linedef->special == Line_Mirror && r_drawmirrors)) return; + if (!mBackSector) return; + + double frontlowertop = mFrontSector->GetPlaneTexZ(sector_t::ceiling); + if (mFrontSector->GetTexture(sector_t::ceiling) == skyflatnum && mBackSector->GetTexture(sector_t::ceiling) == skyflatnum) + { + // Putting sky ceilings on the front and back of a line alters the way unpegged + // positioning works. + frontlowertop = mBackSector->GetPlaneTexZ(sector_t::ceiling); + } + + mBottomPart.Texture = TexMan(sidedef->GetTexture(side_t::bottom), true); + + mBottomPart.TextureOffsetU = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::bottom)); + double rowoffset = sidedef->GetTextureYOffset(side_t::bottom); + mBottomPart.TextureScaleU = sidedef->GetTextureXScale(side_t::bottom); + mBottomPart.TextureScaleV = sidedef->GetTextureYScale(side_t::bottom); + double yrepeat = mBottomPart.Texture->Scale.Y * mBottomPart.TextureScaleV; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + mBottomPart.TextureMid = (frontlowertop - ViewPos.Z) * yrepeat; + } + else + { // top of texture at top + mBottomPart.TextureMid = (mBackSector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat; + if (rowoffset < 0 && mBottomPart.Texture != NULL) + { + rowoffset += mBottomPart.Texture->GetHeight(); + } + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGBOTTOM) + { // top of texture at bottom + mBottomPart.TextureMid = (frontlowertop - ViewPos.Z) * yrepeat; + } + else + { // bottom of texture at top + mBottomPart.TextureMid = (mBackSector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + mBottomPart.Texture->GetHeight(); + } + } + if (mBottomPart.Texture->bWorldPanning) + { + mBottomPart.TextureMid += rowoffset * yrepeat; + } + else + { + mBottomPart.TextureMid += rowoffset; + } + } + + bool SWRenderLine::IsFogBoundary(sector_t *front, sector_t *back) const + { + return r_fogboundary && CameraLight::Instance()->FixedColormap() == nullptr && front->ColorMap->Fade && + front->ColorMap->Fade != back->ColorMap->Fade && + (front->GetTexture(sector_t::ceiling) != skyflatnum || back->GetTexture(sector_t::ceiling) != skyflatnum); + } + + void SWRenderLine::ClipSegmentTopBottom(int x1, int x2) + { + // clip wall to the floor and ceiling + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + for (int x = x1; x < x2; ++x) + { + if (walltop.ScreenY[x] < ceilingclip[x]) + { + walltop.ScreenY[x] = ceilingclip[x]; + } + if (wallbottom.ScreenY[x] > floorclip[x]) + { + wallbottom.ScreenY[x] = floorclip[x]; + } + } + } + + void SWRenderLine::MarkCeilingPlane(int x1, int x2) + { + // mark ceiling areas + if (markceiling) + { + mCeilingPlane = Thread->PlaneList->GetRange(mCeilingPlane, x1, x2); + + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + for (int x = x1; x < x2; ++x) + { + short top = (clip3d->fakeFloor && clip3d->fake3D & FAKE3D_FAKECEILING) ? clip3d->fakeFloor->ceilingclip[x] : ceilingclip[x]; + short bottom = MIN(walltop.ScreenY[x], floorclip[x]); + if (top < bottom) + { + mCeilingPlane->top[x] = top; + mCeilingPlane->bottom[x] = bottom; + } + } + } + } + + void SWRenderLine::MarkFloorPlane(int x1, int x2) + { + if (markfloor) + { + mFloorPlane = Thread->PlaneList->GetRange(mFloorPlane, x1, x2); + + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + for (int x = x1; x < x2; ++x) + { + short top = MAX(wallbottom.ScreenY[x], ceilingclip[x]); + short bottom = (clip3d->fakeFloor && clip3d->fake3D & FAKE3D_FAKEFLOOR) ? clip3d->fakeFloor->floorclip[x] : floorclip[x]; + if (top < bottom) + { + assert(bottom <= viewheight); + mFloorPlane->top[x] = top; + mFloorPlane->bottom[x] = bottom; + } + } + } + } + + void SWRenderLine::Mark3DFloors(int x1, int x2) + { + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + // kg3D - fake planes clipping + if (clip3d->fake3D & FAKE3D_REFRESHCLIP) + { + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + + if (clip3d->fake3D & FAKE3D_CLIPBOTFRONT) + { + memcpy(clip3d->fakeFloor->floorclip + x1, wallbottom.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + else + { + for (int x = x1; x < x2; ++x) + { + walllower.ScreenY[x] = MIN(MAX(walllower.ScreenY[x], ceilingclip[x]), wallbottom.ScreenY[x]); + } + memcpy(clip3d->fakeFloor->floorclip + x1, walllower.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + if (clip3d->fake3D & FAKE3D_CLIPTOPFRONT) + { + memcpy(clip3d->fakeFloor->ceilingclip + x1, walltop.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + else + { + for (int x = x1; x < x2; ++x) + { + wallupper.ScreenY[x] = MAX(MIN(wallupper.ScreenY[x], floorclip[x]), walltop.ScreenY[x]); + } + memcpy(clip3d->fakeFloor->ceilingclip + x1, wallupper.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + } + } + + void SWRenderLine::MarkOpaquePassClip(int x1, int x2) + { + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + + if (mMiddlePart.Texture) // one sided line + { + fillshort(ceilingclip + x1, x2 - x1, viewheight); + fillshort(floorclip + x1, x2 - x1, 0xffff); + } + else + { // two sided line + if (mTopPart.Texture != NULL && mTopPart.Texture->UseType != FTexture::TEX_Null) + { // top wall + for (int x = x1; x < x2; ++x) + { + wallupper.ScreenY[x] = MAX(MIN(wallupper.ScreenY[x], floorclip[x]), walltop.ScreenY[x]); + } + memcpy(ceilingclip + x1, wallupper.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + else if (markceiling) + { // no top wall + memcpy(ceilingclip + x1, walltop.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + + if (mBottomPart.Texture != NULL && mBottomPart.Texture->UseType != FTexture::TEX_Null) + { // bottom wall + for (int x = x1; x < x2; ++x) + { + walllower.ScreenY[x] = MIN(MAX(walllower.ScreenY[x], ceilingclip[x]), wallbottom.ScreenY[x]); + } + memcpy(floorclip + x1, walllower.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + else if (markfloor) + { // no bottom wall + memcpy(floorclip + x1, wallbottom.ScreenY + x1, (x2 - x1) * sizeof(short)); + } + } + } + + void SWRenderLine::RenderTopTexture(int x1, int x2) + { + if (mMiddlePart.Texture) return; + if (!mTopPart.Texture || mTopPart.Texture->UseType == FTexture::TEX_Null) return; + if (!viewactive) return; + + FTexture *rw_pic = mTopPart.Texture; + double xscale = rw_pic->Scale.X * mTopPart.TextureScaleU; + double yscale = rw_pic->Scale.Y * mTopPart.TextureScaleV; + if (xscale != lwallscale) + { + walltexcoords.ProjectPos(mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT); + lwallscale = xscale; + } + fixed_t offset; + if (mTopPart.Texture->bWorldPanning) + { + offset = xs_RoundToInt(mTopPart.TextureOffsetU * xscale); + } + else + { + offset = mTopPart.TextureOffsetU; + } + if (xscale < 0) + { + offset = -offset; + } + + WallDrawerArgs drawerargs; + drawerargs.SetStyle(false, false, OPAQUE); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != nullptr) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + + float rw_light = rw_lightleft + rw_lightstep * (x1 - WallC.sx1); + + FLightNode *light_list = (mLineSegment && mLineSegment->sidedef) ? mLineSegment->sidedef->lighthead : nullptr; + + if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr)) + light_list = nullptr; // [SP] Don't draw dynlights if invul/lightamp active + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walltop.ScreenY, wallupper.ScreenY, mTopPart.TextureMid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mFrontCeilingZ1, mFrontCeilingZ2), MIN(mBackCeilingZ1, mBackCeilingZ2), false, wallshade, offset, rw_light, rw_lightstep, light_list, foggy, basecolormap); + } + + void SWRenderLine::RenderMiddleTexture(int x1, int x2) + { + if (!mMiddlePart.Texture || mMiddlePart.Texture->UseType == FTexture::TEX_Null) return; + if (!viewactive) return; + + FTexture *rw_pic = mMiddlePart.Texture; + double xscale = rw_pic->Scale.X * mMiddlePart.TextureScaleU; + double yscale = rw_pic->Scale.Y * mMiddlePart.TextureScaleV; + if (xscale != lwallscale) + { + walltexcoords.ProjectPos(mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT); + lwallscale = xscale; + } + fixed_t offset; + if (mMiddlePart.Texture->bWorldPanning) + { + offset = xs_RoundToInt(mMiddlePart.TextureOffsetU * xscale); + } + else + { + offset = mMiddlePart.TextureOffsetU; + } + if (xscale < 0) + { + offset = -offset; + } + + WallDrawerArgs drawerargs; + drawerargs.SetStyle(false, false, OPAQUE); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != nullptr) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + + float rw_light = rw_lightleft + rw_lightstep * (x1 - WallC.sx1); + + FLightNode *light_list = (mLineSegment && mLineSegment->sidedef) ? mLineSegment->sidedef->lighthead : nullptr; + + if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr)) + light_list = nullptr; // [SP] Don't draw dynlights if invul/lightamp active + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walltop.ScreenY, wallbottom.ScreenY, mMiddlePart.TextureMid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mFrontCeilingZ1, mFrontCeilingZ2), MIN(mFrontFloorZ1, mFrontFloorZ2), false, wallshade, offset, rw_light, rw_lightstep, light_list, foggy, basecolormap); + } + + void SWRenderLine::RenderBottomTexture(int x1, int x2) + { + if (mMiddlePart.Texture) return; + if (!mBottomPart.Texture || mBottomPart.Texture->UseType == FTexture::TEX_Null) return; + if (!viewactive) return; + + FTexture *rw_pic = mBottomPart.Texture; + double xscale = rw_pic->Scale.X * mBottomPart.TextureScaleU; + double yscale = rw_pic->Scale.Y * mBottomPart.TextureScaleV; + if (xscale != lwallscale) + { + walltexcoords.ProjectPos(mLineSegment->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2, WallT); + lwallscale = xscale; + } + fixed_t offset; + if (mBottomPart.Texture->bWorldPanning) + { + offset = xs_RoundToInt(mBottomPart.TextureOffsetU * xscale); + } + else + { + offset = mBottomPart.TextureOffsetU; + } + if (xscale < 0) + { + offset = -offset; + } + + WallDrawerArgs drawerargs; + drawerargs.SetStyle(false, false, OPAQUE); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != nullptr) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + + float rw_light = rw_lightleft + rw_lightstep * (x1 - WallC.sx1); + + FLightNode *light_list = (mLineSegment && mLineSegment->sidedef) ? mLineSegment->sidedef->lighthead : nullptr; + + if ((cameraLight->FixedLightLevel() >= 0) || (cameraLight->FixedColormap() != nullptr)) + light_list = nullptr; // [SP] Don't draw dynlights if invul/lightamp active + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(drawerargs, mFrontSector, mLineSegment, WallC, rw_pic, x1, x2, walllower.ScreenY, wallbottom.ScreenY, mBottomPart.TextureMid, walltexcoords.VStep, walltexcoords.UPos, yscale, MAX(mBackFloorZ1, mBackFloorZ2), MIN(mFrontFloorZ1, mFrontFloorZ2), false, wallshade, offset, rw_light, rw_lightstep, light_list, foggy, basecolormap); + } + + //////////////////////////////////////////////////////////////////////////// + + // Transform and clip coordinates. Returns true if it was clipped away + bool FWallCoords::Init(RenderThread *thread, const DVector2 &pt1, const DVector2 &pt2, double too_close) + { + tleft.X = float(pt1.X * ViewSin - pt1.Y * ViewCos); + tright.X = float(pt2.X * ViewSin - pt2.Y * ViewCos); + + tleft.Y = float(pt1.X * ViewTanCos + pt1.Y * ViewTanSin); + tright.Y = float(pt2.X * ViewTanCos + pt2.Y * ViewTanSin); + + RenderPortal *renderportal = thread->Portal.get(); + auto viewport = RenderViewport::Instance(); + + if (renderportal->MirrorFlags & RF_XFLIP) + { + float t = -tleft.X; + tleft.X = -tright.X; + tright.X = t; + swapvalues(tleft.Y, tright.Y); + } + + if (tleft.X >= -tleft.Y) + { + if (tleft.X > tleft.Y) return true; // left edge is off the right side + if (tleft.Y == 0) return true; + sx1 = xs_RoundToInt(viewport->CenterX + tleft.X * viewport->CenterX / tleft.Y); + sz1 = tleft.Y; + } + else + { + if (tright.X < -tright.Y) return true; // wall is off the left side + float den = tleft.X - tright.X - tright.Y + tleft.Y; + if (den == 0) return true; + sx1 = 0; + sz1 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X + tleft.Y) / den; + } + + if (sz1 < too_close) + return true; + + if (tright.X <= tright.Y) + { + if (tright.X < -tright.Y) return true; // right edge is off the left side + if (tright.Y == 0) return true; + sx2 = xs_RoundToInt(viewport->CenterX + tright.X * viewport->CenterX / tright.Y); + sz2 = tright.Y; + } + else + { + if (tleft.X > tleft.Y) return true; // wall is off the right side + float den = tright.Y - tleft.Y - tright.X + tleft.X; + if (den == 0) return true; + sx2 = viewwidth; + sz2 = tleft.Y + (tright.Y - tleft.Y) * (tleft.X - tleft.Y) / den; + } + + if (sz2 < too_close || sx2 <= sx1) + return true; + + return false; + } + + ///////////////////////////////////////////////////////////////////////// + + void FWallTmapVals::InitFromWallCoords(RenderThread *thread, const FWallCoords *wallc) + { + const FVector2 *left = &wallc->tleft; + const FVector2 *right = &wallc->tright; + + RenderPortal *renderportal = thread->Portal.get(); + + if (renderportal->MirrorFlags & RF_XFLIP) + { + swapvalues(left, right); + } + UoverZorg = left->X * centerx; + UoverZstep = -left->Y; + InvZorg = (left->X - right->X) * centerx; + InvZstep = right->Y - left->Y; + } + + void FWallTmapVals::InitFromLine(RenderThread *thread, const DVector2 &left, const DVector2 &right) + { + // Coordinates should have already had viewx,viewy subtracted + + double fullx1 = left.X * ViewSin - left.Y * ViewCos; + double fullx2 = right.X * ViewSin - right.Y * ViewCos; + double fully1 = left.X * ViewTanCos + left.Y * ViewTanSin; + double fully2 = right.X * ViewTanCos + right.Y * ViewTanSin; + + RenderPortal *renderportal = thread->Portal.get(); + + if (renderportal->MirrorFlags & RF_XFLIP) + { + fullx1 = -fullx1; + fullx2 = -fullx2; + } + + UoverZorg = float(fullx1 * centerx); + UoverZstep = float(-fully1); + InvZorg = float((fullx1 - fullx2) * centerx); + InvZstep = float(fully2 - fully1); + } +} diff --git a/src/swrenderer/line/r_line.h b/src/swrenderer/line/r_line.h new file mode 100644 index 000000000..b0e3c416d --- /dev/null +++ b/src/swrenderer/line/r_line.h @@ -0,0 +1,149 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "vectors.h" +#include "r_wallsetup.h" +#include "swrenderer/segments/r_clipsegment.h" + +struct seg_t; +struct subsector_t; +struct sector_t; +struct side_t; +struct line_t; +struct FDynamicColormap; + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlane; + + struct FWallCoords + { + FVector2 tleft; // coords at left of wall in view space rx1,ry1 + FVector2 tright; // coords at right of wall in view space rx2,ry2 + + float sz1, sz2; // depth at left, right of wall in screen space yb1,yb2 + short sx1, sx2; // x coords at left, right of wall in screen space xb1,xb2 + + bool Init(RenderThread *thread, const DVector2 &pt1, const DVector2 &pt2, double too_close); + }; + + struct FWallTmapVals + { + float UoverZorg, UoverZstep; + float InvZorg, InvZstep; + + void InitFromWallCoords(RenderThread *thread, const FWallCoords *wallc); + void InitFromLine(RenderThread *thread, const DVector2 &left, const DVector2 &right); + }; + + struct WallPartTexture + { + fixed_t TextureOffsetU; + double TextureMid; + double TextureScaleU; + double TextureScaleV; + FTexture *Texture; + }; + + class SWRenderLine : VisibleSegmentRenderer + { + public: + SWRenderLine(RenderThread *thread); + void Render(seg_t *line, subsector_t *subsector, sector_t *sector, sector_t *fakebacksector, VisiblePlane *floorplane, VisiblePlane *ceilingplane, bool foggy, FDynamicColormap *basecolormap); + + RenderThread *Thread = nullptr; + + private: + bool RenderWallSegment(int x1, int x2) override; + void SetWallVariables(bool needlights); + void SetTopTexture(); + void SetMiddleTexture(); + void SetBottomTexture(); + void ClipSegmentTopBottom(int x1, int x2); + void MarkCeilingPlane(int x1, int x2); + void MarkFloorPlane(int x1, int x2); + void Mark3DFloors(int x1, int x2); + void MarkOpaquePassClip(int x1, int x2); + void RenderTopTexture(int x1, int x2); + void RenderMiddleTexture(int x1, int x2); + void RenderBottomTexture(int x1, int x2); + + bool IsFogBoundary(sector_t *front, sector_t *back) const; + bool SkyboxCompare(sector_t *frontsector, sector_t *backsector) const; + + bool IsInvisibleLine() const; + bool IsDoorClosed() const; + bool IsSolid() const; + + bool ShouldMarkFloor() const; + bool ShouldMarkCeiling() const; + bool ShouldMarkPortal() const; + + // Line variables: + + subsector_t *mSubsector; + sector_t *mFrontSector; + sector_t *mBackSector; + VisiblePlane *mFloorPlane; + VisiblePlane *mCeilingPlane; + seg_t *mLineSegment; + + double mBackCeilingZ1; + double mBackCeilingZ2; + double mBackFloorZ1; + double mBackFloorZ2; + double mFrontCeilingZ1; + double mFrontCeilingZ2; + double mFrontFloorZ1; + double mFrontFloorZ2; + + bool mDoorClosed; + + FWallCoords WallC; + FWallTmapVals WallT; + + bool foggy; + FDynamicColormap *basecolormap; + + // Wall segment variables: + + bool rw_prepped; + + int wallshade; + float rw_lightstep; + float rw_lightleft; + + double lwallscale; + + bool markfloor; // False if the back side is the same plane. + bool markceiling; + + WallPartTexture mTopPart; + WallPartTexture mMiddlePart; + WallPartTexture mBottomPart; + + ProjectedWallCull mCeilingClipped; + ProjectedWallCull mFloorClipped; + + ProjectedWallLine walltop; + ProjectedWallLine wallbottom; + ProjectedWallLine wallupper; + ProjectedWallLine walllower; + ProjectedWallTexcoords walltexcoords; + + sector_t tempsec; // killough 3/8/98: ceiling/water hack + }; +} diff --git a/src/swrenderer/line/r_renderdrawsegment.cpp b/src/swrenderer/line/r_renderdrawsegment.cpp new file mode 100644 index 000000000..f157a89b6 --- /dev/null +++ b/src/swrenderer/line/r_renderdrawsegment.cpp @@ -0,0 +1,959 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "d_net.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_fogboundary.h" +#include "swrenderer/line/r_renderdrawsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/viewport/r_spritedrawer.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + RenderDrawSegment::RenderDrawSegment(RenderThread *thread) + { + Thread = thread; + } + + void RenderDrawSegment::Render(DrawSegment *ds, int x1, int x2) + { + auto viewport = RenderViewport::Instance(); + RenderFogBoundary renderfog; + float *MaskedSWall = nullptr, MaskedScaleY = 0, rw_scalestep = 0; + fixed_t *maskedtexturecol = nullptr; + + FTexture *tex; + int i; + sector_t tempsec; // killough 4/13/98 + double texheight, texheightscale; + bool notrelevant = false; + double rowoffset; + bool wrap = false; + + const sector_t *sec; + + bool sprflipvert = false; + + curline = ds->curline; + + float alpha = (float)MIN(curline->linedef->alpha, 1.); + bool additive = (curline->linedef->flags & ML_ADDTRANS) != 0; + + WallDrawerArgs walldrawerargs; + walldrawerargs.SetStyle(true, additive, FLOAT2FIXED(alpha)); + + SpriteDrawerArgs columndrawerargs; + FDynamicColormap *patchstylecolormap = nullptr; + bool visible = columndrawerargs.SetStyle(LegacyRenderStyles[additive ? STYLE_Add : STYLE_Translucent], alpha, 0, 0, patchstylecolormap); + + if (!visible && !ds->bFogBoundary && !ds->bFakeBoundary) + { + return; + } + + if (Thread->MainThread) + NetUpdate(); + + frontsector = curline->frontsector; + backsector = curline->backsector; + + tex = TexMan(curline->sidedef->GetTexture(side_t::mid), true); + if (i_compatflags & COMPATF_MASKEDMIDTEX) + { + tex = tex->GetRawTexture(); + } + + // killough 4/13/98: get correct lightlevel for 2s normal textures + sec = Thread->OpaquePass->FakeFlat(frontsector, &tempsec, nullptr, nullptr, nullptr, 0, 0, 0, 0); + + FDynamicColormap *basecolormap = sec->ColorMap; // [RH] Set basecolormap + + int wallshade = ds->shade; + rw_lightstep = ds->lightstep; + rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() < 0) + { + if (!(clip3d->fake3D & FAKE3D_CLIPTOP)) + { + clip3d->sclipTop = sec->ceilingplane.ZatPoint(ViewPos); + } + for (i = frontsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) + { + if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[i].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy), foggy); + break; + } + } + } + + short *mfloorclip = ds->sprbottomclip - ds->x1; + short *mceilingclip = ds->sprtopclip - ds->x1; + double spryscale; + + // [RH] Draw fog partition + if (ds->bFogBoundary) + { + renderfog.Render(Thread, x1, x2, mceilingclip, mfloorclip, wallshade, rw_light, rw_lightstep, basecolormap); + if (ds->maskedtexturecol == nullptr) + { + goto clearfog; + } + } + if ((ds->bFakeBoundary && !(ds->bFakeBoundary & 4)) || !visible) + { + goto clearfog; + } + + MaskedSWall = ds->swall - ds->x1; + MaskedScaleY = ds->yscale; + maskedtexturecol = ds->maskedtexturecol - ds->x1; + spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); + rw_scalestep = ds->iscalestep; + + if (cameraLight->FixedLightLevel() >= 0) + { + walldrawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + columndrawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + } + else if (cameraLight->FixedColormap() != nullptr) + { + walldrawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + columndrawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + } + + // find positioning + texheight = tex->GetScaledHeightDouble(); + texheightscale = fabs(curline->sidedef->GetTextureYScale(side_t::mid)); + if (texheightscale != 1) + { + texheight = texheight / texheightscale; + } + + double texturemid; + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + texturemid = MAX(frontsector->GetPlaneTexZ(sector_t::floor), backsector->GetPlaneTexZ(sector_t::floor)) + texheight; + } + else + { + texturemid = MIN(frontsector->GetPlaneTexZ(sector_t::ceiling), backsector->GetPlaneTexZ(sector_t::ceiling)); + } + + rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid); + + wrap = (curline->linedef->flags & ML_WRAP_MIDTEX) || (curline->sidedef->Flags & WALLF_WRAP_MIDTEX); + if (!wrap) + { // Texture does not wrap vertically. + double textop; + + if (MaskedScaleY < 0) + { + MaskedScaleY = -MaskedScaleY; + sprflipvert = true; + } + if (tex->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + texturemid += rowoffset - ViewPos.Z; + textop = texturemid; + texturemid *= MaskedScaleY; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + textop = texturemid + rowoffset / MaskedScaleY - ViewPos.Z; + texturemid = (texturemid - ViewPos.Z) * MaskedScaleY + rowoffset; + } + if (sprflipvert) + { + MaskedScaleY = -MaskedScaleY; + texturemid -= tex->GetHeight() << FRACBITS; + } + + // [RH] Don't bother drawing segs that are completely offscreen + if (viewport->globaldclip * ds->sz1 < -textop && viewport->globaldclip * ds->sz2 < -textop) + { // Texture top is below the bottom of the screen + goto clearfog; + } + + if (viewport->globaluclip * ds->sz1 > texheight - textop && viewport->globaluclip * ds->sz2 > texheight - textop) + { // Texture bottom is above the top of the screen + goto clearfog; + } + + if ((clip3d->fake3D & FAKE3D_CLIPBOTTOM) && textop < clip3d->sclipBottom - ViewPos.Z) + { + notrelevant = true; + goto clearfog; + } + if ((clip3d->fake3D & FAKE3D_CLIPTOP) && textop - texheight > clip3d->sclipTop - ViewPos.Z) + { + notrelevant = true; + goto clearfog; + } + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + + if (clip3d->fake3D & FAKE3D_CLIPTOP) + { + wallupper.Project(textop < clip3d->sclipTop - ViewPos.Z ? textop : clip3d->sclipTop - ViewPos.Z, &WallC); + } + else + { + wallupper.Project(textop, &WallC); + } + if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) + { + walllower.Project(textop - texheight > clip3d->sclipBottom - ViewPos.Z ? textop - texheight : clip3d->sclipBottom - ViewPos.Z, &WallC); + } + else + { + walllower.Project(textop - texheight, &WallC); + } + + for (i = x1; i < x2; i++) + { + if (wallupper.ScreenY[i] < mceilingclip[i]) + wallupper.ScreenY[i] = mceilingclip[i]; + } + for (i = x1; i < x2; i++) + { + if (walllower.ScreenY[i] > mfloorclip[i]) + walllower.ScreenY[i] = mfloorclip[i]; + } + + if (clip3d->CurrentSkybox) + { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor + // or above the ceiling, so the appropriate end won't be clipped automatically when adding + // this drawseg. + if ((curline->linedef->flags & ML_CLIP_MIDTEX) || + (curline->sidedef->Flags & WALLF_CLIP_MIDTEX) || + (ib_compatflags & BCOMPATF_CLIPMIDTEX)) + { + ClipMidtex(x1, x2); + } + } + + mfloorclip = walllower.ScreenY; + mceilingclip = wallupper.ScreenY; + + // draw the columns one at a time + if (visible) + { + for (int x = x1; x < x2; ++x) + { + if (cameraLight->FixedColormap() == nullptr && cameraLight->FixedLightLevel() < 0) + { + columndrawerargs.SetLight(basecolormap, rw_light, wallshade); + } + + fixed_t iscale = xs_Fix<16>::ToFix(MaskedSWall[x] * MaskedScaleY); + double sprtopscreen; + if (sprflipvert) + sprtopscreen = viewport->CenterY + texturemid * spryscale; + else + sprtopscreen = viewport->CenterY - texturemid * spryscale; + + columndrawerargs.DrawMaskedColumn(Thread, x, iscale, tex, maskedtexturecol[x], spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip); + + rw_light += rw_lightstep; + spryscale += rw_scalestep; + } + } + } + else + { // Texture does wrap vertically. + if (tex->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + texturemid = (texturemid - ViewPos.Z + rowoffset) * MaskedScaleY; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + texturemid = (texturemid - ViewPos.Z) * MaskedScaleY + rowoffset; + } + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + + if (clip3d->CurrentSkybox) + { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor + // or above the ceiling, so the appropriate end won't be clipped automatically when adding + // this drawseg. + if ((curline->linedef->flags & ML_CLIP_MIDTEX) || + (curline->sidedef->Flags & WALLF_CLIP_MIDTEX) || + (ib_compatflags & BCOMPATF_CLIPMIDTEX)) + { + ClipMidtex(x1, x2); + } + } + + if (clip3d->fake3D & FAKE3D_CLIPTOP) + { + wallupper.Project(clip3d->sclipTop - ViewPos.Z, &WallC); + for (i = x1; i < x2; i++) + { + if (wallupper.ScreenY[i] < mceilingclip[i]) + wallupper.ScreenY[i] = mceilingclip[i]; + } + mceilingclip = wallupper.ScreenY; + } + if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) + { + walllower.Project(clip3d->sclipBottom - ViewPos.Z, &WallC); + for (i = x1; i < x2; i++) + { + if (walllower.ScreenY[i] > mfloorclip[i]) + walllower.ScreenY[i] = mfloorclip[i]; + } + mfloorclip = walllower.ScreenY; + } + + rw_offset = 0; + rw_pic = tex; + + double top, bot; + GetMaskedWallTopBottom(ds, top, bot); + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(walldrawerargs, frontsector, curline, WallC, rw_pic, x1, x2, mceilingclip, mfloorclip, texturemid, MaskedSWall, maskedtexturecol, ds->yscale, top, bot, true, wallshade, rw_offset, rw_light, rw_lightstep, nullptr, ds->foggy, basecolormap); + } + + clearfog: + if (ds->bFakeBoundary & 3) + { + RenderFakeWallRange(ds, x1, x2, wallshade); + } + if (!notrelevant) + { + if (clip3d->fake3D & FAKE3D_REFRESHCLIP) + { + if (!wrap) + { + assert(ds->bkup != nullptr); + memcpy(ds->sprtopclip, ds->bkup, (ds->x2 - ds->x1) * 2); + } + } + else + { + fillshort(ds->sprtopclip - ds->x1 + x1, x2 - x1, viewheight); + } + } + return; + } + + // kg3D - render one fake wall + void RenderDrawSegment::RenderFakeWall(DrawSegment *ds, int x1, int x2, F3DFloor *rover, int wallshade, FDynamicColormap *basecolormap) + { + int i; + double xscale; + double yscale; + + fixed_t Alpha = Scale(rover->alpha, OPAQUE, 255); + if (Alpha <= 0) + return; + + WallDrawerArgs drawerargs; + drawerargs.SetStyle(true, (rover->flags & FF_ADDITIVETRANS) != 0, Alpha); + + rw_lightstep = ds->lightstep; + rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; + + short *mfloorclip = ds->sprbottomclip - ds->x1; + short *mceilingclip = ds->sprtopclip - ds->x1; + + //double spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); + float *MaskedSWall = ds->swall - ds->x1; + + // find positioning + side_t *scaledside; + side_t::ETexpart scaledpart; + if (rover->flags & FF_UPPERTEXTURE) + { + scaledside = curline->sidedef; + scaledpart = side_t::top; + } + else if (rover->flags & FF_LOWERTEXTURE) + { + scaledside = curline->sidedef; + scaledpart = side_t::bottom; + } + else + { + scaledside = rover->master->sidedef[0]; + scaledpart = side_t::mid; + } + xscale = rw_pic->Scale.X * scaledside->GetTextureXScale(scaledpart); + yscale = rw_pic->Scale.Y * scaledside->GetTextureYScale(scaledpart); + + double rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureYOffset(side_t::mid); + double planez = rover->model->GetPlaneTexZ(sector_t::ceiling); + rw_offset = FLOAT2FIXED(curline->sidedef->GetTextureXOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureXOffset(side_t::mid)); + if (rowoffset < 0) + { + rowoffset += rw_pic->GetHeight(); + } + double texturemid = (planez - ViewPos.Z) * yscale; + if (rw_pic->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + + texturemid = texturemid + rowoffset * yscale; + rw_offset = xs_RoundToInt(rw_offset * xscale); + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + texturemid += rowoffset; + } + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != nullptr) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + WallC.tleft.X = ds->cx; + WallC.tleft.Y = ds->cy; + WallC.tright.X = ds->cx + ds->cdx; + WallC.tright.Y = ds->cy + ds->cdy; + WallT = ds->tmapvals; + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + wallupper.Project(clip3d->sclipTop - ViewPos.Z, &WallC); + walllower.Project(clip3d->sclipBottom - ViewPos.Z, &WallC); + + for (i = x1; i < x2; i++) + { + if (wallupper.ScreenY[i] < mceilingclip[i]) + wallupper.ScreenY[i] = mceilingclip[i]; + } + for (i = x1; i < x2; i++) + { + if (walllower.ScreenY[i] > mfloorclip[i]) + walllower.ScreenY[i] = mfloorclip[i]; + } + + ProjectedWallTexcoords walltexcoords; + walltexcoords.ProjectPos(curline->sidedef->TexelLength*xscale, ds->sx1, ds->sx2, WallT); + + double top, bot; + GetMaskedWallTopBottom(ds, top, bot); + + RenderWallPart renderWallpart(Thread); + renderWallpart.Render(drawerargs, frontsector, curline, WallC, rw_pic, x1, x2, wallupper.ScreenY, walllower.ScreenY, texturemid, MaskedSWall, walltexcoords.UPos, yscale, top, bot, true, wallshade, rw_offset, rw_light, rw_lightstep, nullptr, ds->foggy, basecolormap); + } + + // kg3D - walls of fake floors + void RenderDrawSegment::RenderFakeWallRange(DrawSegment *ds, int x1, int x2, int wallshade) + { + FTexture *const DONT_DRAW = ((FTexture*)(intptr_t)-1); + int i, j; + F3DFloor *rover, *fover = nullptr; + int passed, last; + double floorHeight; + double ceilingHeight; + + curline = ds->curline; + + frontsector = curline->frontsector; + backsector = curline->backsector; + + if (backsector == nullptr) + { + return; + } + if ((ds->bFakeBoundary & 3) == 2) + { + sector_t *sec = backsector; + backsector = frontsector; + frontsector = sec; + } + + floorHeight = backsector->CenterFloor(); + ceilingHeight = backsector->CenterCeiling(); + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + // maybe fix clipheights + if (!(clip3d->fake3D & FAKE3D_CLIPBOTTOM)) clip3d->sclipBottom = floorHeight; + if (!(clip3d->fake3D & FAKE3D_CLIPTOP)) clip3d->sclipTop = ceilingHeight; + + // maybe not visible + if (clip3d->sclipBottom >= frontsector->CenterCeiling()) return; + if (clip3d->sclipTop <= frontsector->CenterFloor()) return; + + if (clip3d->fake3D & FAKE3D_DOWN2UP) + { // bottom to viewz + last = 0; + for (i = backsector->e->XFloor.ffloors.Size() - 1; i >= 0; i--) + { + rover = backsector->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + + // visible? + passed = 0; + if (!(rover->flags & FF_RENDERSIDES) || rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || + rover->top.plane->Zat0() <= clip3d->sclipBottom || + rover->bottom.plane->Zat0() >= ceilingHeight || + rover->top.plane->Zat0() <= floorHeight) + { + if (!i) + { + passed = 1; + } + else + { + continue; + } + } + + rw_pic = nullptr; + if (rover->bottom.plane->Zat0() >= clip3d->sclipTop || passed) + { + if (last) + { + break; + } + // maybe wall from inside rendering? + fover = nullptr; + for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + fover = nullptr; + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->top.plane->Zat0() <= clip3d->sclipBottom) continue; // no + if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) + { // no, last possible + fover = nullptr; + break; + } + // it is, render inside? + if (!(fover->flags & (FF_BOTHPLANES | FF_INVERTPLANES))) + { // no + fover = nullptr; + } + break; + } + // nothing + if (!fover || j == -1) + { + break; + } + // correct texture + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + else if (fover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (fover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + else if (frontsector->e->XFloor.ffloors.Size()) + { + // maybe not visible? + fover = nullptr; + for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) // never + { + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->top.plane->Zat0() <= clip3d->sclipBottom) continue; // no + if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) + { // visible, last possible + fover = nullptr; + break; + } + if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && + !(!(fover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) + ) + { + break; + } + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + fover = nullptr; // visible + break; + } + if (fover && j != -1) + { + fover = nullptr; + last = 1; + continue; // not visible + } + } + if (!rw_pic) + { + fover = nullptr; + if (rover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (rover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + // correct colors now + FDynamicColormap *basecolormap = frontsector->ColorMap; + wallshade = ds->shade; + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() < 0) + { + if ((ds->bFakeBoundary & 3) == 2) + { + for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (clip3d->sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy), foggy); + break; + } + } + } + else + { + for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy), foggy); + break; + } + } + } + } + if (rw_pic != DONT_DRAW) + { + RenderFakeWall(ds, x1, x2, fover ? fover : rover, wallshade, basecolormap); + } + else rw_pic = nullptr; + break; + } + } + else + { // top to viewz + for (i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) + { + rover = backsector->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + + // visible? + passed = 0; + if (!(rover->flags & FF_RENDERSIDES) || + rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || + rover->bottom.plane->Zat0() >= clip3d->sclipTop || + rover->top.plane->Zat0() <= floorHeight || + rover->bottom.plane->Zat0() >= ceilingHeight) + { + if ((unsigned)i == backsector->e->XFloor.ffloors.Size() - 1) + { + passed = 1; + } + else + { + continue; + } + } + rw_pic = nullptr; + if (rover->top.plane->Zat0() <= clip3d->sclipBottom || passed) + { // maybe wall from inside rendering? + fover = nullptr; + for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + fover = nullptr; + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) continue; // no + if (fover->top.plane->Zat0() <= clip3d->sclipBottom) + { // no, last possible + fover = nullptr; + break; + } + // it is, render inside? + if (!(fover->flags & (FF_BOTHPLANES | FF_INVERTPLANES))) + { // no + fover = nullptr; + } + break; + } + // nothing + if (!fover || (unsigned)j == frontsector->e->XFloor.ffloors.Size()) + { + break; + } + // correct texture + if (fover->flags & rover->flags & FF_SWIMMABLE) + { + rw_pic = DONT_DRAW; // don't ever draw (but treat as something has been found) + } + else if (fover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (fover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + else if (frontsector->e->XFloor.ffloors.Size()) + { // maybe not visible? + fover = nullptr; + for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, its bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) continue; // no + if (fover->top.plane->Zat0() <= clip3d->sclipBottom) + { // visible, last possible + fover = nullptr; + break; + } + if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && + !(!(rover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) + ) + { + break; + } + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + fover = nullptr; // visible + break; + } + if (fover && (unsigned)j != frontsector->e->XFloor.ffloors.Size()) + { // not visible + break; + } + } + if (rw_pic == nullptr) + { + fover = nullptr; + if (rover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (rover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + // correct colors now + FDynamicColormap *basecolormap = frontsector->ColorMap; + wallshade = ds->shade; + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() < 0) + { + if ((ds->bFakeBoundary & 3) == 2) + { + for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (clip3d->sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy), foggy); + break; + } + } + } + else + { + for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy), foggy); + break; + } + } + } + } + + if (rw_pic != DONT_DRAW) + { + RenderFakeWall(ds, x1, x2, fover ? fover : rover, wallshade, basecolormap); + } + else + { + rw_pic = nullptr; + } + break; + } + } + return; + } + + // Clip a midtexture to the floor and ceiling of the sector in front of it. + void RenderDrawSegment::ClipMidtex(int x1, int x2) + { + ProjectedWallLine most; + + RenderPortal *renderportal = Thread->Portal.get(); + + most.Project(curline->frontsector->ceilingplane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); + for (int i = x1; i < x2; ++i) + { + if (wallupper.ScreenY[i] < most.ScreenY[i]) + wallupper.ScreenY[i] = most.ScreenY[i]; + } + most.Project(curline->frontsector->floorplane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); + for (int i = x1; i < x2; ++i) + { + if (walllower.ScreenY[i] > most.ScreenY[i]) + walllower.ScreenY[i] = most.ScreenY[i]; + } + } + + void RenderDrawSegment::GetMaskedWallTopBottom(DrawSegment *ds, double &top, double &bot) + { + double frontcz1 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v1); + double frontfz1 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v1); + double frontcz2 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v2); + double frontfz2 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v2); + top = MAX(frontcz1, frontcz2); + bot = MIN(frontfz1, frontfz2); + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + if (clip3d->fake3D & FAKE3D_CLIPTOP) + { + top = MIN(top, clip3d->sclipTop); + } + if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) + { + bot = MAX(bot, clip3d->sclipBottom); + } + } +} diff --git a/src/swrenderer/line/r_renderdrawsegment.h b/src/swrenderer/line/r_renderdrawsegment.h new file mode 100644 index 000000000..a13d1341d --- /dev/null +++ b/src/swrenderer/line/r_renderdrawsegment.h @@ -0,0 +1,52 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/segments/r_drawsegment.h" + +namespace swrenderer +{ + class RenderThread; + + class RenderDrawSegment + { + public: + RenderDrawSegment(RenderThread *thread); + void Render(DrawSegment *ds, int x1, int x2); + + RenderThread *Thread = nullptr; + + private: + void ClipMidtex(int x1, int x2); + void RenderFakeWall(DrawSegment *ds, int x1, int x2, F3DFloor *rover, int wallshade, FDynamicColormap *basecolormap); + void RenderFakeWallRange(DrawSegment *ds, int x1, int x2, int wallshade); + void GetMaskedWallTopBottom(DrawSegment *ds, double &top, double &bot); + + sector_t *frontsector = nullptr; + sector_t *backsector = nullptr; + + seg_t *curline = nullptr; + + FWallCoords WallC; + FWallTmapVals WallT; + + float rw_light = 0.0f; + float rw_lightstep = 0.0f; + fixed_t rw_offset = 0; + FTexture *rw_pic = nullptr; + + ProjectedWallLine wallupper; + ProjectedWallLine walllower; + }; +} diff --git a/src/swrenderer/line/r_walldraw.cpp b/src/swrenderer/line/r_walldraw.cpp new file mode 100644 index 000000000..40c9f3551 --- /dev/null +++ b/src/swrenderer/line/r_walldraw.cpp @@ -0,0 +1,534 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" + +#include "r_sky.h" +#include "v_video.h" + +#include "m_swap.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "r_walldraw.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/r_memory.h" + +namespace swrenderer +{ + WallSampler::WallSampler(int y1, double texturemid, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture) + { + auto viewport = RenderViewport::Instance(); + + xoffset += FLOAT2FIXED(xmagnitude * 0.5); + + if (!viewport->RenderTarget->IsBgra()) + { + height = texture->GetHeight(); + + int uv_fracbits = 32 - texture->HeightBits; + if (uv_fracbits != 32) + { + uv_max = height << uv_fracbits; + + // Find start uv in [0-base_height[ range. + // Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range. + double uv_stepd = swal * yrepeat; + double v = (texturemid + uv_stepd * (y1 - viewport->CenterY + 0.5)) / height; + v = v - floor(v); + v *= height; + v *= (1 << uv_fracbits); + + uv_pos = (uint32_t)v; + uv_step = xs_ToFixed(uv_fracbits, uv_stepd); + if (uv_step == 0) // To prevent divide by zero elsewhere + uv_step = 1; + } + else + { // Hack for one pixel tall textures + uv_pos = 0; + uv_step = 0; + uv_max = 1; + } + + int col = xoffset >> FRACBITS; + + // If the texture's width isn't a power of 2, then we need to make it a + // positive offset for proper clamping. + int width; + if (col < 0 && (width = texture->GetWidth()) != (1 << texture->WidthBits)) + { + col = width + (col % width); + } + + if (viewport->RenderTarget->IsBgra()) + source = (const uint8_t *)texture->GetColumnBgra(col, nullptr); + else + source = texture->GetColumn(col, nullptr); + + source2 = nullptr; + texturefracx = 0; + } + else + { + // Normalize to 0-1 range: + double uv_stepd = swal * yrepeat; + double v = (texturemid + uv_stepd * (y1 - viewport->CenterY + 0.5)) / texture->GetHeight(); + v = v - floor(v); + double v_step = uv_stepd / texture->GetHeight(); + + if (std::isnan(v) || std::isnan(v_step)) // this should never happen, but it apparently does.. + { + uv_stepd = 0.0; + v = 0.0; + v_step = 0.0; + } + + // Convert to uint32: + uv_pos = (uint32_t)(v * 0x100000000LL); + uv_step = (uint32_t)(v_step * 0x100000000LL); + uv_max = 0; + + // Texture mipmap and filter selection: + double ymagnitude = fabs(uv_stepd); + double magnitude = MAX(ymagnitude, xmagnitude); + double min_lod = -1000.0; + double lod = MAX(log2(magnitude) + r_lod_bias, min_lod); + bool magnifying = lod < 0.0f; + + int mipmap_offset = 0; + int mip_width = texture->GetWidth(); + int mip_height = texture->GetHeight(); + if (r_mipmap && texture->Mipmapped() && mip_width > 1 && mip_height > 1) + { + uint32_t xpos = (uint32_t)((((uint64_t)xoffset) << FRACBITS) / mip_width); + + int level = (int)lod; + while (level > 0 && mip_width > 1 && mip_height > 1) + { + mipmap_offset += mip_width * mip_height; + level--; + mip_width = MAX(mip_width >> 1, 1); + mip_height = MAX(mip_height >> 1, 1); + } + xoffset = (xpos >> FRACBITS) * mip_width; + } + + const uint32_t *pixels = texture->GetPixelsBgra() + mipmap_offset; + + bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter); + if (filter_nearest) + { + int tx = (xoffset >> FRACBITS) % mip_width; + if (tx < 0) + tx += mip_width; + source = (BYTE*)(pixels + tx * mip_height); + source2 = nullptr; + height = mip_height; + texturefracx = 0; + } + else + { + xoffset -= FRACUNIT / 2; + int tx0 = (xoffset >> FRACBITS) % mip_width; + if (tx0 < 0) + tx0 += mip_width; + int tx1 = (tx0 + 1) % mip_width; + source = (BYTE*)(pixels + tx0 * mip_height); + source2 = (BYTE*)(pixels + tx1 * mip_height); + height = mip_height; + texturefracx = (xoffset >> (FRACBITS - 4)) & 15; + } + } + } + + // Draw a column with support for non-power-of-two ranges + void RenderWallPart::Draw1Column(int x, int y1, int y2, WallSampler &sampler) + { + if (r_dynlights && light_list) + { + auto viewport = RenderViewport::Instance(); + + // Find column position in view space + float w1 = 1.0f / WallC.sz1; + float w2 = 1.0f / WallC.sz2; + float t = (x - WallC.sx1 + 0.5f) / (WallC.sx2 - WallC.sx1); + float wcol = w1 * (1.0f - t) + w2 * t; + float zcol = 1.0f / wcol; + drawerargs.dc_viewpos.X = (float)((x + 0.5 - viewport->CenterX) / viewport->CenterX * zcol); + drawerargs.dc_viewpos.Y = zcol; + drawerargs.dc_viewpos.Z = (float)((viewport->CenterY - y1 - 0.5) / viewport->InvZtoScale * zcol); + drawerargs.dc_viewpos_step.Z = (float)(-zcol / viewport->InvZtoScale); + + // Calculate max lights that can touch column so we can allocate memory for the list + int max_lights = 0; + FLightNode *cur_node = light_list; + while (cur_node) + { + if (!(cur_node->lightsource->flags2&MF2_DORMANT)) + max_lights++; + cur_node = cur_node->nextLight; + } + + drawerargs.dc_num_lights = 0; + drawerargs.dc_lights = Thread->FrameMemory->AllocMemory(max_lights); + + // Setup lights for column + cur_node = light_list; + while (cur_node) + { + if (!(cur_node->lightsource->flags2&MF2_DORMANT)) + { + double lightX = cur_node->lightsource->X() - ViewPos.X; + double lightY = cur_node->lightsource->Y() - ViewPos.Y; + double lightZ = cur_node->lightsource->Z() - ViewPos.Z; + + float lx = (float)(lightX * ViewSin - lightY * ViewCos) - drawerargs.dc_viewpos.X; + float ly = (float)(lightX * ViewTanCos + lightY * ViewTanSin) - drawerargs.dc_viewpos.Y; + float lz = (float)lightZ; + + // Precalculate the constant part of the dot here so the drawer doesn't have to. + bool is_point_light = (cur_node->lightsource->flags4 & MF4_ATTENUATE) != 0; + float lconstant = lx * lx + ly * ly; + float nlconstant = is_point_light ? lx * drawerargs.dc_normal.X + ly * drawerargs.dc_normal.Y : 0.0f; + + // Include light only if it touches this column + float radius = cur_node->lightsource->GetRadius(); + if (radius * radius >= lconstant && nlconstant >= 0.0f) + { + uint32_t red = cur_node->lightsource->GetRed(); + uint32_t green = cur_node->lightsource->GetGreen(); + uint32_t blue = cur_node->lightsource->GetBlue(); + + auto &light = drawerargs.dc_lights[drawerargs.dc_num_lights++]; + light.x = lconstant; + light.y = nlconstant; + light.z = lz; + light.radius = 256.0f / cur_node->lightsource->GetRadius(); + light.color = (red << 16) | (green << 8) | blue; + } + } + + cur_node = cur_node->nextLight; + } + } + else + { + drawerargs.dc_num_lights = 0; + } + + if (RenderViewport::Instance()->RenderTarget->IsBgra()) + { + int count = y2 - y1; + + drawerargs.SetTexture(sampler.source, sampler.source2, sampler.height); + drawerargs.SetTextureUPos(sampler.texturefracx); + drawerargs.SetDest(x, y1); + drawerargs.SetCount(count); + drawerargs.SetTextureVStep(sampler.uv_step); + drawerargs.SetTextureVPos(sampler.uv_pos); + drawerargs.DrawColumn(Thread); + + uint64_t step64 = sampler.uv_step; + uint64_t pos64 = sampler.uv_pos; + sampler.uv_pos = (uint32_t)(pos64 + step64 * count); + } + else + { + if (sampler.uv_max == 0 || sampler.uv_step == 0) // power of two + { + int count = y2 - y1; + + drawerargs.SetTexture(sampler.source, sampler.source2, sampler.height); + drawerargs.SetTextureUPos(sampler.texturefracx); + drawerargs.SetDest(x, y1); + drawerargs.SetCount(count); + drawerargs.SetTextureVStep(sampler.uv_step); + drawerargs.SetTextureVPos(sampler.uv_pos); + drawerargs.DrawColumn(Thread); + + uint64_t step64 = sampler.uv_step; + uint64_t pos64 = sampler.uv_pos; + sampler.uv_pos = (uint32_t)(pos64 + step64 * count); + } + else + { + uint32_t uv_pos = sampler.uv_pos; + + uint32_t left = y2 - y1; + while (left > 0) + { + uint32_t available = sampler.uv_max - uv_pos; + uint32_t next_uv_wrap = available / sampler.uv_step; + if (available % sampler.uv_step != 0) + next_uv_wrap++; + uint32_t count = MIN(left, next_uv_wrap); + + drawerargs.SetTexture(sampler.source, sampler.source2, sampler.height); + drawerargs.SetTextureUPos(sampler.texturefracx); + drawerargs.SetDest(x, y1); + drawerargs.SetCount(count); + drawerargs.SetTextureVStep(sampler.uv_step); + drawerargs.SetTextureVPos(uv_pos); + drawerargs.DrawColumn(Thread); + + left -= count; + uv_pos += sampler.uv_step * count; + if (uv_pos >= sampler.uv_max) + uv_pos -= sampler.uv_max; + } + + sampler.uv_pos = uv_pos; + } + } + } + + void RenderWallPart::ProcessWallWorker(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) + { + if (rw_pic->UseType == FTexture::TEX_Null) + return; + + rw_pic->GetHeight(); // To ensure that rw_pic->HeightBits has been set + int fracbits = 32 - rw_pic->HeightBits; + if (fracbits == 32) + { // Hack for one pixel tall textures + fracbits = 0; + yrepeat = 0; + texturemid = 0; + } + + drawerargs.SetTextureFracBits(RenderViewport::Instance()->RenderTarget->IsBgra() ? FRACBITS : fracbits); + + CameraLight *cameraLight = CameraLight::Instance(); + bool fixed = (cameraLight->FixedColormap() != NULL || cameraLight->FixedLightLevel() >= 0); + + if (cameraLight->FixedColormap()) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + else + drawerargs.SetLight(basecolormap, 0, 0); + + float dx = WallC.tright.X - WallC.tleft.X; + float dy = WallC.tright.Y - WallC.tleft.Y; + float length = sqrt(dx * dx + dy * dy); + drawerargs.dc_normal.X = dy / length; + drawerargs.dc_normal.Y = -dx / length; + drawerargs.dc_normal.Z = 0.0f; + + double xmagnitude = 1.0; + + float curlight = light; + for (int x = x1; x < x2; x++, curlight += lightstep) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + if (!fixed) + drawerargs.SetLight(basecolormap, curlight, wallshade); + + if (x + 1 < x2) xmagnitude = fabs(FIXED2DBL(lwal[x + 1]) - FIXED2DBL(lwal[x])); + + WallSampler sampler(y1, texturemid, swal[x], yrepeat, lwal[x] + xoffset, xmagnitude, rw_pic); + Draw1Column(x, y1, y2, sampler); + } + + if (Thread->MainThread) + NetUpdate(); + } + + void RenderWallPart::ProcessNormalWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) + { + ProcessWallWorker(uwal, dwal, texturemid, swal, lwal); + } + + void RenderWallPart::ProcessStripedWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) + { + ProjectedWallLine most1, most2, most3; + const short *up; + short *down; + + up = uwal; + down = most1.ScreenY; + + assert(WallC.sx1 <= x1); + assert(WallC.sx2 >= x2); + + RenderPortal *renderportal = Thread->Portal.get(); + + // kg3D - fake floors instead of zdoom light list + for (unsigned int i = 0; i < frontsector->e->XFloor.lightlist.Size(); i++) + { + ProjectedWallCull j = most3.Project(frontsector->e->XFloor.lightlist[i].plane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); + if (j != ProjectedWallCull::OutsideAbove) + { + for (int j = x1; j < x2; ++j) + { + down[j] = clamp(most3.ScreenY[j], up[j], dwal[j]); + } + ProcessNormalWall(up, down, texturemid, swal, lwal); + up = down; + down = (down == most1.ScreenY) ? most2.ScreenY : most1.ScreenY; + } + + lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; + basecolormap = lit->extra_colormap; + wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + LightVisibility::ActualExtraLight(foggy), foggy); + } + + ProcessNormalWall(up, dwal, texturemid, swal, lwal); + } + + void RenderWallPart::ProcessWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) + { + // Textures that aren't masked can use the faster ProcessNormalWall. + if (!rw_pic->bMasked && drawerargs.IsMaskedDrawer()) + { + drawerargs.SetStyle(true, false, OPAQUE); + } + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap() != NULL || cameraLight->FixedLightLevel() >= 0 || !(frontsector->e && frontsector->e->XFloor.lightlist.Size())) + { + ProcessNormalWall(uwal, dwal, texturemid, swal, lwal); + } + else + { + ProcessStripedWall(uwal, dwal, texturemid, swal, lwal); + } + } + + //============================================================================= + // + // ProcessWallNP2 + // + // This is a wrapper around ProcessWall that helps it tile textures whose heights + // are not powers of 2. It divides the wall into texture-sized strips and calls + // ProcessNormalWall for each of those. Since only one repetition of the texture fits + // in each strip, ProcessWall will not tile. + // + //============================================================================= + + void RenderWallPart::ProcessWallNP2(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal, double top, double bot) + { + ProjectedWallLine most1, most2, most3; + double texheight = rw_pic->GetHeight(); + double partition; + double scaledtexheight = texheight / yrepeat; + + if (yrepeat >= 0) + { // normal orientation: draw strips from top to bottom + partition = top - fmod(top - texturemid / yrepeat - ViewPos.Z, scaledtexheight); + if (partition == top) + { + partition -= scaledtexheight; + } + const short *up = uwal; + short *down = most1.ScreenY; + texturemid = (partition - ViewPos.Z) * yrepeat + texheight; + while (partition > bot) + { + ProjectedWallCull j = most3.Project(partition - ViewPos.Z, &WallC); + if (j != ProjectedWallCull::OutsideAbove) + { + for (int j = x1; j < x2; ++j) + { + down[j] = clamp(most3.ScreenY[j], up[j], dwal[j]); + } + ProcessWall(up, down, texturemid, swal, lwal); + up = down; + down = (down == most1.ScreenY) ? most2.ScreenY : most1.ScreenY; + } + partition -= scaledtexheight; + texturemid -= texheight; + } + ProcessWall(up, dwal, texturemid, swal, lwal); + } + else + { // upside down: draw strips from bottom to top + partition = bot - fmod(bot - texturemid / yrepeat - ViewPos.Z, scaledtexheight); + short *up = most1.ScreenY; + const short *down = dwal; + texturemid = (partition - ViewPos.Z) * yrepeat + texheight; + while (partition < top) + { + ProjectedWallCull j = most3.Project(partition - ViewPos.Z, &WallC); + if (j != ProjectedWallCull::OutsideBelow) + { + for (int j = x1; j < x2; ++j) + { + up[j] = clamp(most3.ScreenY[j], uwal[j], down[j]); + } + ProcessWall(up, down, texturemid, swal, lwal); + down = up; + up = (up == most1.ScreenY) ? most2.ScreenY : most1.ScreenY; + } + partition -= scaledtexheight; + texturemid -= texheight; + } + ProcessWall(uwal, down, texturemid, swal, lwal); + } + } + + void RenderWallPart::Render(const WallDrawerArgs &drawerargs, sector_t *frontsector, seg_t *curline, const FWallCoords &WallC, FTexture *pic, int x1, int x2, const short *walltop, const short *wallbottom, double texturemid, float *swall, fixed_t *lwall, double yscale, double top, double bottom, bool mask, int wallshade, fixed_t xoffset, float light, float lightstep, FLightNode *light_list, bool foggy, FDynamicColormap *basecolormap) + { + this->drawerargs = drawerargs; + this->x1 = x1; + this->x2 = x2; + this->frontsector = frontsector; + this->curline = curline; + this->WallC = WallC; + this->yrepeat = yscale; + this->wallshade = wallshade; + this->xoffset = xoffset; + this->light = light; + this->lightstep = lightstep; + this->foggy = foggy; + this->basecolormap = basecolormap; + this->light_list = light_list; + this->rw_pic = pic; + this->mask = mask; + + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) + { + ProcessWallNP2(walltop, wallbottom, texturemid, swall, lwall, top, bottom); + } + else + { + ProcessWall(walltop, wallbottom, texturemid, swall, lwall); + } + } + + RenderWallPart::RenderWallPart(RenderThread *thread) + { + Thread = thread; + } +} diff --git a/src/swrenderer/line/r_walldraw.h b/src/swrenderer/line/r_walldraw.h new file mode 100644 index 000000000..79d7ffc91 --- /dev/null +++ b/src/swrenderer/line/r_walldraw.h @@ -0,0 +1,108 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/viewport/r_walldrawer.h" +#include "r_line.h" + +class FTexture; +struct FLightNode; +struct seg_t; +struct FLightNode; +struct FDynamicColormap; + +namespace swrenderer +{ + class RenderThread; + struct DrawSegment; + struct FWallCoords; + class ProjectedWallLine; + class ProjectedWallTexcoords; + struct WallSampler; + + class RenderWallPart + { + public: + RenderWallPart(RenderThread *thread); + + void Render( + const WallDrawerArgs &drawerargs, + sector_t *frontsector, + seg_t *curline, + const FWallCoords &WallC, + FTexture *rw_pic, + int x1, + int x2, + const short *walltop, + const short *wallbottom, + double texturemid, + float *swall, + fixed_t *lwall, + double yscale, + double top, + double bottom, + bool mask, + int wallshade, + fixed_t xoffset, + float light, + float lightstep, + FLightNode *light_list, + bool foggy, + FDynamicColormap *basecolormap); + + RenderThread *Thread = nullptr; + + private: + void ProcessWallNP2(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal, double top, double bot); + void ProcessWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal); + void ProcessStripedWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal); + void ProcessNormalWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal); + void ProcessWallWorker(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal); + void Draw1Column(int x, int y1, int y2, WallSampler &sampler); + + int x1 = 0; + int x2 = 0; + FTexture *rw_pic = nullptr; + sector_t *frontsector = nullptr; + seg_t *curline = nullptr; + FWallCoords WallC; + + double yrepeat = 0.0; + int wallshade = 0; + fixed_t xoffset = 0; + float light = 0.0f; + float lightstep = 0.0f; + bool foggy = false; + FDynamicColormap *basecolormap = nullptr; + FLightNode *light_list = nullptr; + bool mask = false; + + WallDrawerArgs drawerargs; + }; + + struct WallSampler + { + WallSampler() { } + WallSampler(int y1, double texturemid, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture); + + uint32_t uv_pos; + uint32_t uv_step; + uint32_t uv_max; + + const BYTE *source; + const BYTE *source2; + uint32_t texturefracx; + uint32_t height; + }; +} diff --git a/src/swrenderer/line/r_wallsetup.cpp b/src/swrenderer/line/r_wallsetup.cpp new file mode 100644 index 000000000..579ff21f2 --- /dev/null +++ b/src/swrenderer/line/r_wallsetup.cpp @@ -0,0 +1,229 @@ + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "r_sky.h" +#include "v_video.h" +#include "m_swap.h" +#include "w_wad.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "r_wallsetup.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_walldraw.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/line/r_line.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" + +namespace swrenderer +{ + ProjectedWallCull ProjectedWallLine::Project(double z, const FWallCoords *wallc) + { + return Project(z, z, wallc); + } + + ProjectedWallCull ProjectedWallLine::Project(double z1, double z2, const FWallCoords *wallc) + { + auto viewport = RenderViewport::Instance(); + + float y1 = (float)(viewport->CenterY - z1 * viewport->InvZtoScale / wallc->sz1); + float y2 = (float)(viewport->CenterY - z2 * viewport->InvZtoScale / wallc->sz2); + + if (y1 < 0 && y2 < 0) // entire line is above screen + { + memset(&ScreenY[wallc->sx1], 0, (wallc->sx2 - wallc->sx1) * sizeof(ScreenY[0])); + return ProjectedWallCull::OutsideAbove; + } + else if (y1 > viewheight && y2 > viewheight) // entire line is below screen + { + fillshort(&ScreenY[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); + return ProjectedWallCull::OutsideBelow; + } + + if (wallc->sx2 <= wallc->sx1) + return ProjectedWallCull::Visible; + + float rcp_delta = 1.0f / (wallc->sx2 - wallc->sx1); + if (y1 >= 0.0f && y2 >= 0.0f && xs_RoundToInt(y1) <= viewheight && xs_RoundToInt(y2) <= viewheight) + { + for (int x = wallc->sx1; x < wallc->sx2; x++) + { + float t = (x - wallc->sx1) * rcp_delta; + float y = y1 * (1.0f - t) + y2 * t; + ScreenY[x] = (short)xs_RoundToInt(y); + } + } + else + { + for (int x = wallc->sx1; x < wallc->sx2; x++) + { + float t = (x - wallc->sx1) * rcp_delta; + float y = y1 * (1.0f - t) + y2 * t; + ScreenY[x] = (short)clamp(xs_RoundToInt(y), 0, viewheight); + } + } + + return ProjectedWallCull::Visible; + } + + ProjectedWallCull ProjectedWallLine::Project(const secplane_t &plane, const FWallCoords *wallc, seg_t *curline, bool xflip) + { + if (!plane.isSlope()) + { + return Project(plane.Zat0() - ViewPos.Z, wallc); + } + else + { + // Get Z coordinates at both ends of the line + double x, y, den, z1, z2; + if (xflip) + { + x = curline->v2->fX(); + y = curline->v2->fY(); + if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) + { + double frac = (wallc->tleft.Y + wallc->tleft.X) / den; + x -= frac * (x - curline->v1->fX()); + y -= frac * (y - curline->v1->fY()); + } + z1 = plane.ZatPoint(x, y) - ViewPos.Z; + + if (wallc->sx2 > wallc->sx1 + 1) + { + x = curline->v1->fX(); + y = curline->v1->fY(); + if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + { + double frac = (wallc->tright.Y - wallc->tright.X) / den; + x += frac * (curline->v2->fX() - x); + y += frac * (curline->v2->fY() - y); + } + z2 = plane.ZatPoint(x, y) - ViewPos.Z; + } + else + { + z2 = z1; + } + } + else + { + x = curline->v1->fX(); + y = curline->v1->fY(); + if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) + { + double frac = (wallc->tleft.Y + wallc->tleft.X) / den; + x += frac * (curline->v2->fX() - x); + y += frac * (curline->v2->fY() - y); + } + z1 = plane.ZatPoint(x, y) - ViewPos.Z; + + if (wallc->sx2 > wallc->sx1 + 1) + { + x = curline->v2->fX(); + y = curline->v2->fY(); + if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + { + double frac = (wallc->tright.Y - wallc->tright.X) / den; + x -= frac * (x - curline->v1->fX()); + y -= frac * (y - curline->v1->fY()); + } + z2 = plane.ZatPoint(x, y) - ViewPos.Z; + } + else + { + z2 = z1; + } + } + + return Project(z1, z2, wallc); + } + } + + ///////////////////////////////////////////////////////////////////////// + + void ProjectedWallTexcoords::Project(double walxrepeat, int x1, int x2, const FWallTmapVals &WallT) + { + auto viewport = RenderViewport::Instance(); + + float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - viewport->CenterX); + float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - viewport->CenterX); + float uGradient = WallT.UoverZstep; + float zGradient = WallT.InvZstep; + float xrepeat = (float)fabs(walxrepeat); + float depthScale = (float)(WallT.InvZstep * viewport->WallTMapScale2); + float depthOrg = (float)(-WallT.UoverZstep * viewport->WallTMapScale2); + + if (walxrepeat < 0.0) + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ; + + UPos[x] = (fixed_t)((xrepeat - u * xrepeat) * FRACUNIT); + VStep[x] = depthOrg + u * depthScale; + + uOverZ += uGradient; + invZ += zGradient; + } + } + else + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ; + + UPos[x] = (fixed_t)(u * xrepeat * FRACUNIT); + VStep[x] = depthOrg + u * depthScale; + + uOverZ += uGradient; + invZ += zGradient; + } + } + } + + void ProjectedWallTexcoords::ProjectPos(double walxrepeat, int x1, int x2, const FWallTmapVals &WallT) + { + auto viewport = RenderViewport::Instance(); + + float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - viewport->CenterX); + float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - viewport->CenterX); + float uGradient = WallT.UoverZstep; + float zGradient = WallT.InvZstep; + float xrepeat = (float)fabs(walxrepeat); + + if (walxrepeat < 0.0f) + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ * xrepeat - xrepeat; + + UPos[x] = (fixed_t)(u * FRACUNIT); + + uOverZ += uGradient; + invZ += zGradient; + } + } + else + { + for (int x = x1; x < x2; x++) + { + float u = uOverZ / invZ * xrepeat; + + UPos[x] = (fixed_t)(u * FRACUNIT); + + uOverZ += uGradient; + invZ += zGradient; + } + } + } +} diff --git a/src/swrenderer/line/r_wallsetup.h b/src/swrenderer/line/r_wallsetup.h new file mode 100644 index 000000000..1234a33dc --- /dev/null +++ b/src/swrenderer/line/r_wallsetup.h @@ -0,0 +1,37 @@ + +#pragma once + +#include "r_defs.h" + +namespace swrenderer +{ + struct FWallCoords; + struct FWallTmapVals; + + enum class ProjectedWallCull + { + Visible, + OutsideAbove, + OutsideBelow + }; + + class ProjectedWallLine + { + public: + short ScreenY[MAXWIDTH]; + + ProjectedWallCull Project(double z1, double z2, const FWallCoords *wallc); + ProjectedWallCull Project(const secplane_t &plane, const FWallCoords *wallc, seg_t *line, bool xflip); + ProjectedWallCull Project(double z, const FWallCoords *wallc); + }; + + class ProjectedWallTexcoords + { + public: + float VStep[MAXWIDTH]; // swall + fixed_t UPos[MAXWIDTH]; // lwall + + void Project(double walxrepeat, int x1, int x2, const FWallTmapVals &WallT); + void ProjectPos(double walxrepeat, int x1, int x2, const FWallTmapVals &WallT); + }; +} diff --git a/src/swrenderer/plane/r_flatplane.cpp b/src/swrenderer/plane/r_flatplane.cpp new file mode 100644 index 000000000..b3c695c78 --- /dev/null +++ b/src/swrenderer/plane/r_flatplane.cpp @@ -0,0 +1,290 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "r_flatplane.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + RenderFlatPlane::RenderFlatPlane(RenderThread *thread) + { + Thread = thread; + } + + void RenderFlatPlane::Render(VisiblePlane *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked, FDynamicColormap *colormap, FTexture *texture) + { + if (alpha <= 0) + { + return; + } + + drawerargs.SetSolidColor(3); + drawerargs.SetTexture(texture); + + double planeang = (pl->xform.Angle + pl->xform.baseAngle).Radians(); + double xstep, ystep, leftxfrac, leftyfrac, rightxfrac, rightyfrac; + double x; + + if (planeang != 0) + { + double cosine = cos(planeang), sine = sin(planeang); + pviewx = pl->xform.xOffs + ViewPos.X * cosine - ViewPos.Y * sine; + pviewy = pl->xform.yOffs - ViewPos.X * sine - ViewPos.Y * cosine; + } + else + { + pviewx = pl->xform.xOffs + ViewPos.X; + pviewy = pl->xform.yOffs - ViewPos.Y; + } + + pviewx = _xscale * pviewx; + pviewy = _yscale * pviewy; + + // left to right mapping + planeang += (ViewAngle - 90).Radians(); + + auto viewport = RenderViewport::Instance(); + + // Scale will be unit scale at FocalLengthX (normally SCREENWIDTH/2) distance + xstep = cos(planeang) / viewport->FocalLengthX; + ystep = -sin(planeang) / viewport->FocalLengthX; + + // [RH] flip for mirrors + RenderPortal *renderportal = Thread->Portal.get(); + if (renderportal->MirrorFlags & RF_XFLIP) + { + xstep = -xstep; + ystep = -ystep; + } + + planeang += M_PI / 2; + double cosine = cos(planeang), sine = -sin(planeang); + x = pl->right - viewport->CenterX - 0.5; + rightxfrac = _xscale * (cosine + x * xstep); + rightyfrac = _yscale * (sine + x * ystep); + x = pl->left - viewport->CenterX + 0.5; + leftxfrac = _xscale * (cosine + x * xstep); + leftyfrac = _yscale * (sine + x * ystep); + + basexfrac = leftxfrac; + baseyfrac = leftyfrac; + xstepscale = (rightxfrac - leftxfrac) / (pl->right - pl->left + 1); + ystepscale = (rightyfrac - leftyfrac) / (pl->right - pl->left + 1); + + minx = pl->left; + + planeheight = fabs(pl->height.Zat0() - ViewPos.Z); + + basecolormap = colormap; + + // [RH] set foggy flag + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); + + GlobVis = LightVisibility::Instance()->FlatPlaneGlobVis(foggy) / planeheight; + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + { + drawerargs.SetLight(basecolormap, 0, cameraLight->FixedLightLevelShade()); + plane_shade = false; + } + else if (cameraLight->FixedColormap()) + { + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + plane_shade = false; + } + else + { + plane_shade = true; + planeshade = LightVisibility::LightLevelToShade(pl->lightlevel, foggy); + } + + drawerargs.SetStyle(masked, additive, alpha); + + light_list = pl->lights; + + RenderLines(pl); + } + + void RenderFlatPlane::RenderLine(int y, int x1, int x2) + { +#ifdef RANGECHECK + if (x2 < x1 || x1<0 || x2 >= viewwidth || (unsigned)y >= (unsigned)viewheight) + { + I_FatalError("R_MapPlane: %i, %i at %i", x1, x2, y); + } +#endif + + auto viewport = RenderViewport::Instance(); + + double curxfrac = basexfrac + xstepscale * (x1 + 0.5 - minx); + double curyfrac = baseyfrac + ystepscale * (x1 + 0.5 - minx); + + double distance = viewport->PlaneDepth(y, planeheight); + + if (drawerargs.TextureWidthBits() != 0) + { + drawerargs.SetTextureUStep(xs_ToFixed(32 - drawerargs.TextureWidthBits(), distance * xstepscale)); + drawerargs.SetTextureUPos(xs_ToFixed(32 - drawerargs.TextureWidthBits(), distance * curxfrac + pviewx)); + } + else + { + drawerargs.SetTextureUStep(0); + drawerargs.SetTextureUPos(0); + } + + if (drawerargs.TextureHeightBits() != 0) + { + drawerargs.SetTextureVStep(xs_ToFixed(32 - drawerargs.TextureHeightBits(), distance * ystepscale)); + drawerargs.SetTextureVPos(xs_ToFixed(32 - drawerargs.TextureHeightBits(), distance * curyfrac + pviewy)); + } + else + { + drawerargs.SetTextureVStep(0); + drawerargs.SetTextureVPos(0); + } + + if (viewport->RenderTarget->IsBgra()) + { + double distance2 = viewport->PlaneDepth(y + 1, planeheight); + double xmagnitude = fabs(ystepscale * (distance2 - distance) * viewport->FocalLengthX); + double ymagnitude = fabs(xstepscale * (distance2 - distance) * viewport->FocalLengthX); + double magnitude = MAX(ymagnitude, xmagnitude); + double min_lod = -1000.0; + drawerargs.SetTextureLOD(MAX(log2(magnitude) + r_lod_bias, min_lod)); + } + + if (plane_shade) + { + // Determine lighting based on the span's distance from the viewer. + drawerargs.SetLight(basecolormap, (float)(GlobVis * fabs(viewport->CenterY - y)), planeshade); + } + + if (r_dynlights) + { + // Find row position in view space + float zspan = (float)(planeheight / (fabs(y + 0.5 - viewport->CenterY) / viewport->InvZtoScale)); + drawerargs.dc_viewpos.X = (float)((x1 + 0.5 - viewport->CenterX) / viewport->CenterX * zspan); + drawerargs.dc_viewpos.Y = zspan; + drawerargs.dc_viewpos.Z = (float)((viewport->CenterY - y - 0.5) / viewport->InvZtoScale * zspan); + drawerargs.dc_viewpos_step.X = (float)(zspan / viewport->CenterX); + + static DrawerLight lightbuffer[64 * 1024]; + static int nextlightindex = 0; + + // Plane normal + drawerargs.dc_normal.X = 0.0f; + drawerargs.dc_normal.Y = 0.0f; + drawerargs.dc_normal.Z = (y >= viewport->CenterY) ? 1.0f : -1.0f; + + // Setup lights for row + drawerargs.dc_num_lights = 0; + drawerargs.dc_lights = lightbuffer + nextlightindex; + VisiblePlaneLight *cur_node = light_list; + while (cur_node && nextlightindex < 64 * 1024) + { + double lightX = cur_node->lightsource->X() - ViewPos.X; + double lightY = cur_node->lightsource->Y() - ViewPos.Y; + double lightZ = cur_node->lightsource->Z() - ViewPos.Z; + + float lx = (float)(lightX * ViewSin - lightY * ViewCos); + float ly = (float)(lightX * ViewTanCos + lightY * ViewTanSin) - drawerargs.dc_viewpos.Y; + float lz = (float)lightZ - drawerargs.dc_viewpos.Z; + + // Precalculate the constant part of the dot here so the drawer doesn't have to. + bool is_point_light = (cur_node->lightsource->flags4 & MF4_ATTENUATE) != 0; + float lconstant = ly * ly + lz * lz; + float nlconstant = is_point_light ? lz * drawerargs.dc_normal.Z : 0.0f; + + // Include light only if it touches this row + float radius = cur_node->lightsource->GetRadius(); + if (radius * radius >= lconstant && nlconstant >= 0.0f) + { + uint32_t red = cur_node->lightsource->GetRed(); + uint32_t green = cur_node->lightsource->GetGreen(); + uint32_t blue = cur_node->lightsource->GetBlue(); + + nextlightindex++; + auto &light = drawerargs.dc_lights[drawerargs.dc_num_lights++]; + light.x = lx; + light.y = lconstant; + light.z = nlconstant; + light.radius = 256.0f / radius; + light.color = (red << 16) | (green << 8) | blue; + } + + cur_node = cur_node->next; + } + + if (nextlightindex == 64 * 1024) + nextlightindex = 0; + } + else + { + drawerargs.dc_num_lights = 0; + } + + drawerargs.SetDestY(y); + drawerargs.SetDestX1(x1); + drawerargs.SetDestX2(x2); + + drawerargs.DrawSpan(Thread); + } + + ///////////////////////////////////////////////////////////////////////// + + RenderColoredPlane::RenderColoredPlane(RenderThread *thread) + { + Thread = thread; + } + + void RenderColoredPlane::Render(VisiblePlane *pl) + { + RenderLines(pl); + } + + void RenderColoredPlane::RenderLine(int y, int x1, int x2) + { + drawerargs.DrawColoredSpan(Thread, y, x1, x2); + } +} diff --git a/src/swrenderer/plane/r_flatplane.h b/src/swrenderer/plane/r_flatplane.h new file mode 100644 index 000000000..1d62c09f7 --- /dev/null +++ b/src/swrenderer/plane/r_flatplane.h @@ -0,0 +1,62 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_planerenderer.h" +#include "swrenderer/viewport/r_spandrawer.h" + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlaneLight; + + class RenderFlatPlane : PlaneRenderer + { + public: + RenderFlatPlane(RenderThread *thread); + void Render(VisiblePlane *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked, FDynamicColormap *basecolormap, FTexture *texture); + + RenderThread *Thread = nullptr; + + private: + void RenderLine(int y, int x1, int x2) override; + + int minx; + double planeheight; + bool plane_shade; + int planeshade; + double GlobVis; + FDynamicColormap *basecolormap; + double pviewx, pviewy; + double xstepscale, ystepscale; + double basexfrac, baseyfrac; + VisiblePlaneLight *light_list; + + SpanDrawerArgs drawerargs; + }; + + class RenderColoredPlane : PlaneRenderer + { + public: + RenderColoredPlane(RenderThread *thread); + void Render(VisiblePlane *pl); + + RenderThread *Thread = nullptr; + + private: + void RenderLine(int y, int x1, int x2) override; + + SpanDrawerArgs drawerargs; + }; +} diff --git a/src/swrenderer/plane/r_planerenderer.cpp b/src/swrenderer/plane/r_planerenderer.cpp new file mode 100644 index 000000000..6bc023738 --- /dev/null +++ b/src/swrenderer/plane/r_planerenderer.cpp @@ -0,0 +1,97 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_planerenderer.h" + +namespace swrenderer +{ + void PlaneRenderer::RenderLines(VisiblePlane *pl) + { + // t1/b1 are at x + // t2/b2 are at x+1 + // spanend[y] is at the right edge + + int x = pl->right - 1; + int t2 = pl->top[x]; + int b2 = pl->bottom[x]; + + if (b2 > t2) + { + fillshort(spanend + t2, b2 - t2, x); + } + + for (--x; x >= pl->left; --x) + { + int t1 = pl->top[x]; + int b1 = pl->bottom[x]; + const int xr = x + 1; + int stop; + + // Draw any spans that have just closed + stop = MIN(t1, b2); + while (t2 < stop) + { + int y = t2++; + int x2 = spanend[y]; + RenderLine(y, xr, x2); + } + stop = MAX(b1, t2); + while (b2 > stop) + { + int y = --b2; + int x2 = spanend[y]; + RenderLine(y, xr, x2); + } + + // Mark any spans that have just opened + stop = MIN(t2, b1); + while (t1 < stop) + { + spanend[t1++] = x; + } + stop = MAX(b2, t2); + while (b1 > stop) + { + spanend[--b1] = x; + } + + t2 = pl->top[x]; + b2 = pl->bottom[x]; + } + // Draw any spans that are still open + while (t2 < b2) + { + int y = --b2; + int x2 = spanend[y]; + RenderLine(y, pl->left, x2); + } + } +} diff --git a/src/swrenderer/plane/r_planerenderer.h b/src/swrenderer/plane/r_planerenderer.h new file mode 100644 index 000000000..f7992cd66 --- /dev/null +++ b/src/swrenderer/plane/r_planerenderer.h @@ -0,0 +1,33 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include "r_defs.h" + +namespace swrenderer +{ + struct VisiblePlane; + + class PlaneRenderer + { + public: + void RenderLines(VisiblePlane *pl); + + virtual void RenderLine(int y, int x1, int x2) = 0; + + private: + short spanend[MAXHEIGHT]; + }; +} diff --git a/src/swrenderer/plane/r_skyplane.cpp b/src/swrenderer/plane/r_skyplane.cpp new file mode 100644 index 000000000..dd1848f7a --- /dev/null +++ b/src/swrenderer/plane/r_skyplane.cpp @@ -0,0 +1,255 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "r_skyplane.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "g_levellocals.h" + +CVAR(Bool, r_linearsky, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); +EXTERN_CVAR(Int, r_skymode) + +namespace swrenderer +{ + RenderSkyPlane::RenderSkyPlane(RenderThread *thread) + { + Thread = thread; + } + + void RenderSkyPlane::Render(VisiblePlane *pl) + { + FTextureID sky1tex, sky2tex; + double frontdpos = 0, backdpos = 0; + + if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) + { + sky1tex = sky2texture; + } + else + { + sky1tex = sky1texture; + } + sky2tex = sky2texture; + skymid = skytexturemid; + skyangle = ViewAngle.BAMs(); + + if (pl->picnum == skyflatnum) + { + if (!(pl->sky & PL_SKYFLAT)) + { // use sky1 + sky1: + frontskytex = TexMan(sky1tex, true); + if (level.flags & LEVEL_DOUBLESKY) + backskytex = TexMan(sky2tex, true); + else + backskytex = NULL; + skyflip = 0; + frontdpos = sky1pos; + backdpos = sky2pos; + frontcyl = sky1cyl; + backcyl = sky2cyl; + } + else if (pl->sky == PL_SKYFLAT) + { // use sky2 + frontskytex = TexMan(sky2tex, true); + backskytex = NULL; + frontcyl = sky2cyl; + skyflip = 0; + frontdpos = sky2pos; + } + else + { // MBF's linedef-controlled skies + // Sky Linedef + const line_t *l = &level.lines[(pl->sky & ~PL_SKYFLAT) - 1]; + + // Sky transferred from first sidedef + const side_t *s = l->sidedef[0]; + int pos; + + // Texture comes from upper texture of reference sidedef + // [RH] If swapping skies, then use the lower sidedef + if (level.flags & LEVEL_SWAPSKIES && s->GetTexture(side_t::bottom).isValid()) + { + pos = side_t::bottom; + } + else + { + pos = side_t::top; + } + + frontskytex = TexMan(s->GetTexture(pos), true); + if (frontskytex == NULL || frontskytex->UseType == FTexture::TEX_Null) + { // [RH] The blank texture: Use normal sky instead. + goto sky1; + } + backskytex = NULL; + + // Horizontal offset is turned into an angle offset, + // to allow sky rotation as well as careful positioning. + // However, the offset is scaled very small, so that it + // allows a long-period of sky rotation. + skyangle += FLOAT2FIXED(s->GetTextureXOffset(pos)); + + // Vertical offset allows careful sky positioning. + skymid = s->GetTextureYOffset(pos); + + // We sometimes flip the picture horizontally. + // + // Doom always flipped the picture, so we make it optional, + // to make it easier to use the new feature, while to still + // allow old sky textures to be used. + skyflip = l->args[2] ? 0u : ~0u; + + int frontxscale = int(frontskytex->Scale.X * 1024); + frontcyl = MAX(frontskytex->GetWidth(), frontxscale); + if (skystretch) + { + skymid = skymid * frontskytex->GetScaledHeightDouble() / SKYSTRETCH_HEIGHT; + } + } + } + frontpos = int(fmod(frontdpos, sky1cyl * 65536.0)); + if (backskytex != NULL) + { + backpos = int(fmod(backdpos, sky2cyl * 65536.0)); + } + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap()) + { + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + } + else + { + drawerargs.SetLight(&NormalLight, 0, 0); + } + + DrawSky(pl); + } + + void RenderSkyPlane::DrawSkyColumnStripe(int start_x, int y1, int y2, double scale, double texturemid, double yrepeat) + { + RenderPortal *renderportal = Thread->Portal.get(); + auto viewport = RenderViewport::Instance(); + + uint32_t height = frontskytex->GetHeight(); + + double uv_stepd = skyiscale * yrepeat; + double v = (texturemid + uv_stepd * (y1 - viewport->CenterY + 0.5)) / height; + double v_step = uv_stepd / height; + + uint32_t uv_pos = (uint32_t)(v * 0x01000000); + uint32_t uv_step = (uint32_t)(v_step * 0x01000000); + + int x = start_x; + if (renderportal->MirrorFlags & RF_XFLIP) + x = (viewwidth - x); + + uint32_t ang, angle1, angle2; + + if (r_linearsky) + { + angle_t xangle = (angle_t)((0.5 - x / (double)viewwidth) * FocalTangent * ANGLE_90); + ang = (skyangle + xangle) ^ skyflip; + } + else + { + ang = (skyangle + viewport->xtoviewangle[x]) ^ skyflip; + } + angle1 = (uint32_t)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); + angle2 = (uint32_t)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); + + drawerargs.SetFrontTexture(frontskytex, angle1); + drawerargs.SetBackTexture(backskytex, angle2); + drawerargs.SetTextureVStep(uv_step); + drawerargs.SetTextureVPos(uv_pos); + drawerargs.SetDest(start_x, y1); + drawerargs.SetCount(y2 - y1); + drawerargs.SetFadeSky(r_skymode == 2 && !(level.flags & LEVEL_FORCETILEDSKY)); + drawerargs.SetSolidTop(frontskytex->GetSkyCapColor(false)); + drawerargs.SetSolidBottom(frontskytex->GetSkyCapColor(true)); + + if (!backskytex) + drawerargs.DrawSingleSkyColumn(Thread); + else + drawerargs.DrawDoubleSkyColumn(Thread); + } + + void RenderSkyPlane::DrawSkyColumn(int start_x, int y1, int y2) + { + if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) + { + double texturemid = skymid * frontskytex->Scale.Y + frontskytex->GetHeight(); + DrawSkyColumnStripe(start_x, y1, y2, frontskytex->Scale.Y, texturemid, frontskytex->Scale.Y); + } + else + { + auto viewport = RenderViewport::Instance(); + double yrepeat = frontskytex->Scale.Y; + double scale = frontskytex->Scale.Y * skyscale; + double iscale = 1 / scale; + short drawheight = short(frontskytex->GetHeight() * scale); + double topfrac = fmod(skymid + iscale * (1 - viewport->CenterY), frontskytex->GetHeight()); + if (topfrac < 0) topfrac += frontskytex->GetHeight(); + double texturemid = topfrac - iscale * (1 - viewport->CenterY); + DrawSkyColumnStripe(start_x, y1, y2, scale, texturemid, yrepeat); + } + } + + void RenderSkyPlane::DrawSky(VisiblePlane *pl) + { + int x1 = pl->left; + int x2 = pl->right; + short *uwal = (short *)pl->top; + short *dwal = (short *)pl->bottom; + + for (int x = x1; x < x2; x++) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + DrawSkyColumn(x, y1, y2); + } + } +} diff --git a/src/swrenderer/plane/r_skyplane.h b/src/swrenderer/plane/r_skyplane.h new file mode 100644 index 000000000..9a79b1e96 --- /dev/null +++ b/src/swrenderer/plane/r_skyplane.h @@ -0,0 +1,47 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visibleplane.h" +#include "swrenderer/viewport/r_skydrawer.h" + +namespace swrenderer +{ + class RenderSkyPlane + { + public: + RenderSkyPlane(RenderThread *thread); + + void Render(VisiblePlane *pl); + + RenderThread *Thread = nullptr; + + private: + void DrawSky(VisiblePlane *pl); + void DrawSkyColumnStripe(int start_x, int y1, int y2, double scale, double texturemid, double yrepeat); + void DrawSkyColumn(int start_x, int y1, int y2); + + FTexture *frontskytex = nullptr; + FTexture *backskytex = nullptr; + angle_t skyflip = 0; + int frontpos = 0; + int backpos = 0; + fixed_t frontcyl = 0; + fixed_t backcyl = 0; + double skymid = 0.0; + angle_t skyangle = 0; + + SkyDrawerArgs drawerargs; + }; +} diff --git a/src/swrenderer/plane/r_slopeplane.cpp b/src/swrenderer/plane/r_slopeplane.cpp new file mode 100644 index 000000000..833fefe86 --- /dev/null +++ b/src/swrenderer/plane/r_slopeplane.cpp @@ -0,0 +1,201 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "g_levellocals.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "r_slopeplane.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +namespace swrenderer +{ + RenderSlopePlane::RenderSlopePlane(RenderThread *thread) + { + Thread = thread; + } + + void RenderSlopePlane::Render(VisiblePlane *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked, FDynamicColormap *colormap, FTexture *texture) + { + static const float ifloatpow2[16] = + { + // ifloatpow2[i] = 1 / (1 << i) + 64.f, 32.f, 16.f, 8.f, 4.f, 2.f, 1.f, 0.5f, + 0.25f, 0.125f, 0.0625f, 0.03125f, 0.015625f, 0.0078125f, + 0.00390625f, 0.001953125f + /*, 0.0009765625f, 0.00048828125f, 0.000244140625f, + 1.220703125e-4f, 6.103515625e-5, 3.0517578125e-5*/ + }; + double lxscale, lyscale; + double xscale, yscale; + FVector3 p, m, n; + double ang, planeang, cosine, sine; + double zeroheight; + + if (alpha <= 0) + { + return; + } + + auto viewport = RenderViewport::Instance(); + + drawerargs.SetSolidColor(3); + drawerargs.SetTexture(texture); + + lxscale = _xscale * ifloatpow2[drawerargs.TextureWidthBits()]; + lyscale = _yscale * ifloatpow2[drawerargs.TextureHeightBits()]; + xscale = 64.f / lxscale; + yscale = 64.f / lyscale; + zeroheight = pl->height.ZatPoint(ViewPos); + + pviewx = xs_ToFixed(32 - drawerargs.TextureWidthBits(), pl->xform.xOffs * pl->xform.xScale); + pviewy = xs_ToFixed(32 - drawerargs.TextureHeightBits(), pl->xform.yOffs * pl->xform.yScale); + planeang = (pl->xform.Angle + pl->xform.baseAngle).Radians(); + + // p is the texture origin in view space + // Don't add in the offsets at this stage, because doing so can result in + // errors if the flat is rotated. + ang = M_PI * 3 / 2 - ViewAngle.Radians(); + cosine = cos(ang), sine = sin(ang); + p[0] = ViewPos.X * cosine - ViewPos.Y * sine; + p[2] = ViewPos.X * sine + ViewPos.Y * cosine; + p[1] = pl->height.ZatPoint(0.0, 0.0) - ViewPos.Z; + + // m is the v direction vector in view space + ang = ang - M_PI / 2 - planeang; + cosine = cos(ang), sine = sin(ang); + m[0] = yscale * cosine; + m[2] = yscale * sine; + // m[1] = pl->height.ZatPointF (0, iyscale) - pl->height.ZatPointF (0,0)); + // VectorScale2 (m, 64.f/VectorLength(m)); + + // n is the u direction vector in view space +#if 0 + //let's use the sin/cosine we already know instead of computing new ones + ang += M_PI / 2 + n[0] = -xscale * cos(ang); + n[2] = -xscale * sin(ang); +#else + n[0] = xscale * sine; + n[2] = -xscale * cosine; +#endif + // n[1] = pl->height.ZatPointF (ixscale, 0) - pl->height.ZatPointF (0,0)); + // VectorScale2 (n, 64.f/VectorLength(n)); + + // This code keeps the texture coordinates constant across the x,y plane no matter + // how much you slope the surface. Use the commented-out code above instead to keep + // the textures a constant size across the surface's plane instead. + cosine = cos(planeang), sine = sin(planeang); + m[1] = pl->height.ZatPoint(ViewPos.X + yscale * sine, ViewPos.Y + yscale * cosine) - zeroheight; + n[1] = -(pl->height.ZatPoint(ViewPos.X - xscale * cosine, ViewPos.Y + xscale * sine) - zeroheight); + + plane_su = p ^ m; + plane_sv = p ^ n; + plane_sz = m ^ n; + + plane_su.Z *= viewport->FocalLengthX; + plane_sv.Z *= viewport->FocalLengthX; + plane_sz.Z *= viewport->FocalLengthX; + + plane_su.Y *= viewport->IYaspectMul; + plane_sv.Y *= viewport->IYaspectMul; + plane_sz.Y *= viewport->IYaspectMul; + + // Premultiply the texture vectors with the scale factors + plane_su *= 4294967296.f; + plane_sv *= 4294967296.f; + + RenderPortal *renderportal = Thread->Portal.get(); + if (renderportal->MirrorFlags & RF_XFLIP) + { + plane_su[0] = -plane_su[0]; + plane_sv[0] = -plane_sv[0]; + plane_sz[0] = -plane_sz[0]; + } + + // [RH] set foggy flag + basecolormap = colormap; + bool foggy = level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE);; + + planelightfloat = (LightVisibility::Instance()->SlopePlaneGlobVis(foggy) * lxscale * lyscale) / (fabs(pl->height.ZatPoint(ViewPos) - ViewPos.Z)) / 65536.f; + + if (pl->height.fC() > 0) + planelightfloat = -planelightfloat; + + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + { + drawerargs.SetLight(basecolormap, 0, cameraLight->FixedLightLevelShade()); + plane_shade = false; + } + else if (cameraLight->FixedColormap()) + { + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + plane_shade = false; + } + else + { + drawerargs.SetLight(basecolormap, 0, 0); + plane_shade = true; + planeshade = LightVisibility::LightLevelToShade(pl->lightlevel, foggy); + } + + // Hack in support for 1 x Z and Z x 1 texture sizes + if (drawerargs.TextureHeightBits() == 0) + { + plane_sv[2] = plane_sv[1] = plane_sv[0] = 0; + } + if (drawerargs.TextureWidthBits() == 0) + { + plane_su[2] = plane_su[1] = plane_su[0] = 0; + } + + RenderLines(pl); + } + + void RenderSlopePlane::RenderLine(int y, int x1, int x2) + { + drawerargs.DrawTiltedSpan(Thread, y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy, basecolormap); + } +} diff --git a/src/swrenderer/plane/r_slopeplane.h b/src/swrenderer/plane/r_slopeplane.h new file mode 100644 index 000000000..b707a3f7c --- /dev/null +++ b/src/swrenderer/plane/r_slopeplane.h @@ -0,0 +1,43 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_planerenderer.h" +#include "swrenderer/viewport/r_spandrawer.h" + +namespace swrenderer +{ + class RenderThread; + + class RenderSlopePlane : PlaneRenderer + { + public: + RenderSlopePlane(RenderThread *thread); + void Render(VisiblePlane *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked, FDynamicColormap *basecolormap, FTexture *texture); + + RenderThread *Thread = nullptr; + + private: + void RenderLine(int y, int x1, int x2) override; + + FVector3 plane_sz, plane_su, plane_sv; + float planelightfloat; + bool plane_shade; + int planeshade; + fixed_t pviewx, pviewy; + fixed_t xscale, yscale; + FDynamicColormap *basecolormap; + SpanDrawerArgs drawerargs; + }; +} diff --git a/src/swrenderer/plane/r_visibleplane.cpp b/src/swrenderer/plane/r_visibleplane.cpp new file mode 100644 index 000000000..b32e092b4 --- /dev/null +++ b/src/swrenderer/plane/r_visibleplane.cpp @@ -0,0 +1,139 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/plane/r_flatplane.h" +#include "swrenderer/plane/r_slopeplane.h" +#include "swrenderer/plane/r_skyplane.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/drawers/r_draw.h" + +CVAR(Bool, tilt, false, 0); + +namespace swrenderer +{ + VisiblePlane::VisiblePlane(RenderThread *thread) + { + picnum.SetNull(); + height.set(0.0, 0.0, 1.0, 0.0); + + bottom = thread->FrameMemory->AllocMemory(viewwidth); + top = thread->FrameMemory->AllocMemory(viewwidth); + + fillshort(bottom, viewwidth, 0); + fillshort(top, viewwidth, 0x7fff); + } + + void VisiblePlane::AddLights(RenderThread *thread, FLightNode *node) + { + if (!r_dynlights) + return; + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap() != NULL || cameraLight->FixedLightLevel() >= 0) + return; // [SP] no dynlights if invul or lightamp + + while (node) + { + if (!(node->lightsource->flags2&MF2_DORMANT)) + { + bool found = false; + VisiblePlaneLight *light_node = lights; + while (light_node) + { + if (light_node->lightsource == node->lightsource) + { + found = true; + break; + } + light_node = light_node->next; + } + if (!found) + { + VisiblePlaneLight *newlight = thread->FrameMemory->NewObject(); + newlight->next = lights; + newlight->lightsource = node->lightsource; + lights = newlight; + } + } + node = node->nextLight; + } + } + + void VisiblePlane::Render(RenderThread *thread, fixed_t alpha, bool additive, bool masked) + { + if (left >= right) + return; + + if (picnum == skyflatnum) // sky flat + { + RenderSkyPlane renderer(thread); + renderer.Render(this); + } + else // regular flat + { + FTexture *tex = TexMan(picnum, true); + + if (tex->UseType == FTexture::TEX_Null) + { + return; + } + + if (!masked && !additive) + { // If we're not supposed to see through this plane, draw it opaque. + alpha = OPAQUE; + } + else if (!tex->bMasked) + { // Don't waste time on a masked texture if it isn't really masked. + masked = false; + } + double xscale = xform.xScale * tex->Scale.X; + double yscale = xform.yScale * tex->Scale.Y; + + if (!height.isSlope() && !tilt) + { + RenderFlatPlane renderer(thread); + renderer.Render(this, xscale, yscale, alpha, additive, masked, colormap, tex); + } + else + { + RenderSlopePlane renderer(thread); + renderer.Render(this, xscale, yscale, alpha, additive, masked, colormap, tex); + } + } + if (thread->MainThread) + NetUpdate(); + } +} diff --git a/src/swrenderer/plane/r_visibleplane.h b/src/swrenderer/plane/r_visibleplane.h new file mode 100644 index 000000000..970eee632 --- /dev/null +++ b/src/swrenderer/plane/r_visibleplane.h @@ -0,0 +1,76 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include "r_defs.h" +#include "r_state.h" +#include "swrenderer/r_memory.h" + +class ADynamicLight; +struct FLightNode; +struct FDynamicColormap; +struct FSectorPortal; + +namespace swrenderer +{ + class RenderThread; + + struct VisiblePlaneLight + { + ADynamicLight *lightsource; + VisiblePlaneLight *next; + }; + + struct VisiblePlane + { + VisiblePlane(RenderThread *thread); + + void AddLights(RenderThread *thread, FLightNode *node); + void Render(RenderThread *thread, fixed_t alpha, bool additive, bool masked); + + VisiblePlane *next = nullptr; // Next visplane in hash chain -- killough + + FDynamicColormap *colormap = nullptr; // [RH] Support multiple colormaps + FSectorPortal *portal = nullptr; // [RH] Support sky boxes + VisiblePlaneLight *lights = nullptr; + + FTransform xform; + secplane_t height; + FTextureID picnum; + int lightlevel = 0; + int left = viewwidth; + int right = 0; + int sky = 0; + + // [RH] This set of variables copies information from the time when the + // visplane is created. They are only used by stacks so that you can + // have stacked sectors inside a skybox. If the visplane is not for a + // stack, then they are unused. + int extralight = 0; + double visibility = 0.0; + DVector3 viewpos = { 0.0, 0.0, 0.0 }; + DAngle viewangle = { 0.0 }; + fixed_t Alpha = 0; + bool Additive = false; + + // kg3D - keep track of mirror and skybox owner + int CurrentSkybox = 0; + int CurrentPortalUniq = 0; // mirror counter, counts all of them + int MirrorFlags = 0; // this is not related to CurrentMirror + + uint16_t *bottom = nullptr; + uint16_t *top = nullptr; + }; +} diff --git a/src/swrenderer/plane/r_visibleplanelist.cpp b/src/swrenderer/plane/r_visibleplanelist.cpp new file mode 100644 index 000000000..446741057 --- /dev/null +++ b/src/swrenderer/plane/r_visibleplanelist.cpp @@ -0,0 +1,378 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "gl/dynlights/gl_dynlight.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/plane/r_flatplane.h" +#include "swrenderer/plane/r_slopeplane.h" +#include "swrenderer/plane/r_skyplane.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + VisiblePlaneList::VisiblePlaneList(RenderThread *thread) + { + Thread = thread; + } + + VisiblePlaneList::VisiblePlaneList() + { + for (auto &plane : visplanes) + plane = nullptr; + } + + VisiblePlane *VisiblePlaneList::Add(unsigned hash) + { + VisiblePlane *newplane = Thread->FrameMemory->NewObject(Thread); + newplane->next = visplanes[hash]; + visplanes[hash] = newplane; + return newplane; + } + + void VisiblePlaneList::Clear() + { + for (int i = 0; i <= MAXVISPLANES; i++) + visplanes[i] = nullptr; + } + + void VisiblePlaneList::ClearKeepFakePlanes() + { + for (int i = 0; i <= MAXVISPLANES - 1; i++) + { + for (VisiblePlane **probe = &visplanes[i]; *probe != nullptr; ) + { + if ((*probe)->sky < 0) + { // fake: move past it + probe = &(*probe)->next; + } + else + { // not fake: move from list + VisiblePlane *vis = *probe; + *probe = vis->next; + vis->next = nullptr; + } + } + } + } + + VisiblePlane *VisiblePlaneList::FindPlane(const secplane_t &height, FTextureID picnum, int lightlevel, double Alpha, bool additive, const FTransform &xxform, int sky, FSectorPortal *portal, FDynamicColormap *basecolormap) + { + secplane_t plane; + VisiblePlane *check; + unsigned hash; // killough + bool isskybox; + const FTransform *xform = &xxform; + fixed_t alpha = FLOAT2FIXED(Alpha); + //angle_t angle = (xform.Angle + xform.baseAngle).BAMs(); + + FTransform nulltransform; + + if (picnum == skyflatnum) // killough 10/98 + { // most skies map together + lightlevel = 0; + xform = &nulltransform; + nulltransform.xOffs = nulltransform.yOffs = nulltransform.baseyOffs = 0; + nulltransform.xScale = nulltransform.yScale = 1; + nulltransform.Angle = nulltransform.baseAngle = 0.0; + additive = false; + // [RH] Map floor skies and ceiling skies to separate visplanes. This isn't + // always necessary, but it is needed if a floor and ceiling sky are in the + // same column but separated by a wall. If they both try to reside in the + // same visplane, then only the floor sky will be drawn. + plane.set(0., 0., height.fC(), 0.); + isskybox = portal != nullptr && !(portal->mFlags & PORTSF_INSKYBOX); + } + else if (portal != nullptr && !(portal->mFlags & PORTSF_INSKYBOX)) + { + plane = height; + isskybox = true; + } + else + { + plane = height; + isskybox = false; + // kg3D - hack, store alpha in sky + // i know there is ->alpha, but this also allows to identify fake plane + // and ->alpha is for stacked sectors + Clip3DFloors *clip3d = Thread->Clip3D.get(); + if (clip3d->fake3D & (FAKE3D_FAKEFLOOR | FAKE3D_FAKECEILING)) sky = 0x80000000 | clip3d->fakeAlpha; + else sky = 0; // not skyflatnum so it can't be a sky + portal = nullptr; + alpha = OPAQUE; + } + + // New visplane algorithm uses hash table -- killough + hash = isskybox ? ((unsigned)MAXVISPLANES) : CalcHash(picnum.GetIndex(), lightlevel, height); + + RenderPortal *renderportal = Thread->Portal.get(); + + for (check = visplanes[hash]; check; check = check->next) // killough + { + if (isskybox) + { + if (portal == check->portal && plane == check->height) + { + if (portal->mType != PORTS_SKYVIEWPOINT) + { // This skybox is really a stacked sector, so we need to + // check even more. + if (check->extralight == renderportal->stacked_extralight && + check->visibility == renderportal->stacked_visibility && + check->viewpos == renderportal->stacked_viewpos && + ( + // headache inducing logic... :( + (portal->mType != PORTS_STACKEDSECTORTHING) || + ( + check->Alpha == alpha && + check->Additive == additive && + (alpha == 0 || // if alpha is > 0 everything needs to be checked + (plane == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + basecolormap == check->colormap && // [RH] Add more checks + *xform == check->xform + ) + ) && + check->viewangle == renderportal->stacked_angle + ) + ) + ) + { + return check; + } + } + else + { + return check; + } + } + } + else + if (plane == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + basecolormap == check->colormap && // [RH] Add more checks + *xform == check->xform && + sky == check->sky && + renderportal->CurrentPortalUniq == check->CurrentPortalUniq && + renderportal->MirrorFlags == check->MirrorFlags && + Thread->Clip3D->CurrentSkybox == check->CurrentSkybox && + ViewPos == check->viewpos + ) + { + return check; + } + } + + check = Add(hash); // killough + + check->height = plane; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->xform = *xform; + check->colormap = basecolormap; // [RH] Save colormap + check->sky = sky; + check->portal = portal; + check->extralight = renderportal->stacked_extralight; + check->visibility = renderportal->stacked_visibility; + check->viewpos = renderportal->stacked_viewpos; + check->viewangle = renderportal->stacked_angle; + check->Alpha = alpha; + check->Additive = additive; + check->CurrentPortalUniq = renderportal->CurrentPortalUniq; + check->MirrorFlags = renderportal->MirrorFlags; + check->CurrentSkybox = Thread->Clip3D->CurrentSkybox; + + return check; + } + + VisiblePlane *VisiblePlaneList::GetRange(VisiblePlane *pl, int start, int stop) + { + int intrl, intrh; + int unionl, unionh; + int x; + + assert(start >= 0 && start < viewwidth); + assert(stop > start && stop <= viewwidth); + + if (start < pl->left) + { + intrl = pl->left; + unionl = start; + } + else + { + unionl = pl->left; + intrl = start; + } + + if (stop > pl->right) + { + intrh = pl->right; + unionh = stop; + } + else + { + unionh = pl->right; + intrh = stop; + } + + x = intrl; + while (x < intrh && pl->top[x] == 0x7fff) x++; + + if (x >= intrh) + { + // use the same visplane + pl->left = unionl; + pl->right = unionh; + } + else + { + // make a new visplane + unsigned hash; + + if (pl->portal != nullptr && !(pl->portal->mFlags & PORTSF_INSKYBOX) && viewactive) + { + hash = MAXVISPLANES; + } + else + { + hash = CalcHash(pl->picnum.GetIndex(), pl->lightlevel, pl->height); + } + VisiblePlane *new_pl = Add(hash); + + new_pl->height = pl->height; + new_pl->picnum = pl->picnum; + new_pl->lightlevel = pl->lightlevel; + new_pl->xform = pl->xform; + new_pl->colormap = pl->colormap; + new_pl->portal = pl->portal; + new_pl->extralight = pl->extralight; + new_pl->visibility = pl->visibility; + new_pl->viewpos = pl->viewpos; + new_pl->viewangle = pl->viewangle; + new_pl->sky = pl->sky; + new_pl->Alpha = pl->Alpha; + new_pl->Additive = pl->Additive; + new_pl->CurrentPortalUniq = pl->CurrentPortalUniq; + new_pl->MirrorFlags = pl->MirrorFlags; + new_pl->CurrentSkybox = pl->CurrentSkybox; + new_pl->lights = pl->lights; + pl = new_pl; + pl->left = start; + pl->right = stop; + } + return pl; + } + + bool VisiblePlaneList::HasPortalPlanes() const + { + return visplanes[MAXVISPLANES] != nullptr; + } + + VisiblePlane *VisiblePlaneList::PopFirstPortalPlane() + { + VisiblePlane *pl = visplanes[VisiblePlaneList::MAXVISPLANES]; + if (pl) + { + visplanes[VisiblePlaneList::MAXVISPLANES] = pl->next; + pl->next = nullptr; + } + return pl; + } + + void VisiblePlaneList::ClearPortalPlanes() + { + visplanes[VisiblePlaneList::MAXVISPLANES] = nullptr; + } + + int VisiblePlaneList::Render() + { + VisiblePlane *pl; + int i; + int vpcount = 0; + + RenderPortal *renderportal = Thread->Portal.get(); + + for (i = 0; i < MAXVISPLANES; i++) + { + for (pl = visplanes[i]; pl; pl = pl->next) + { + // kg3D - draw only correct planes + if (pl->CurrentPortalUniq != renderportal->CurrentPortalUniq || pl->CurrentSkybox != Thread->Clip3D->CurrentSkybox) + continue; + // kg3D - draw only real planes now + if (pl->sky >= 0) { + vpcount++; + pl->Render(Thread, OPAQUE, false, false); + } + } + } + return vpcount; + } + + void VisiblePlaneList::RenderHeight(double height) + { + VisiblePlane *pl; + int i; + + DVector3 oViewPos = ViewPos; + DAngle oViewAngle = ViewAngle; + + RenderPortal *renderportal = Thread->Portal.get(); + + for (i = 0; i < MAXVISPLANES; i++) + { + for (pl = visplanes[i]; pl; pl = pl->next) + { + if (pl->CurrentSkybox != Thread->Clip3D->CurrentSkybox || pl->CurrentPortalUniq != renderportal->CurrentPortalUniq) + continue; + + if (pl->sky < 0 && pl->height.Zat0() == height) + { + ViewPos = pl->viewpos; + ViewAngle = pl->viewangle; + renderportal->MirrorFlags = pl->MirrorFlags; + + pl->Render(Thread, pl->sky & 0x7FFFFFFF, pl->Additive, true); + } + } + } + ViewPos = oViewPos; + ViewAngle = oViewAngle; + } +} diff --git a/src/swrenderer/plane/r_visibleplanelist.h b/src/swrenderer/plane/r_visibleplanelist.h new file mode 100644 index 000000000..e07a086fd --- /dev/null +++ b/src/swrenderer/plane/r_visibleplanelist.h @@ -0,0 +1,55 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include "r_defs.h" + +struct FSectorPortal; + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlane; + + class VisiblePlaneList + { + public: + VisiblePlaneList(RenderThread *thread); + + void Clear(); + void ClearKeepFakePlanes(); + + VisiblePlane *FindPlane(const secplane_t &height, FTextureID picnum, int lightlevel, double Alpha, bool additive, const FTransform &xxform, int sky, FSectorPortal *portal, FDynamicColormap *basecolormap); + VisiblePlane *GetRange(VisiblePlane *pl, int start, int stop); + + bool HasPortalPlanes() const; + VisiblePlane *PopFirstPortalPlane(); + void ClearPortalPlanes(); + + int Render(); + void RenderHeight(double height); + + RenderThread *Thread = nullptr; + + private: + VisiblePlaneList(); + VisiblePlane *Add(unsigned hash); + + enum { MAXVISPLANES = 128 }; // must be a power of 2 + VisiblePlane *visplanes[MAXVISPLANES + 1]; + + static unsigned CalcHash(int picnum, int lightlevel, const secplane_t &height) { return (unsigned)((picnum) * 3 + (lightlevel)+(FLOAT2FIXED((height).fD())) * 7) & (MAXVISPLANES - 1); } + }; +} diff --git a/src/swrenderer/r_memory.cpp b/src/swrenderer/r_memory.cpp new file mode 100644 index 000000000..11c5b0b3b --- /dev/null +++ b/src/swrenderer/r_memory.cpp @@ -0,0 +1,71 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "r_memory.h" + +namespace swrenderer +{ + void *RenderMemory::AllocBytes(int size) + { + size = (size + 15) / 16 * 16; // 16-byte align + + if (UsedBlocks.empty() || UsedBlocks.back()->Position + size > BlockSize) + { + if (!FreeBlocks.empty()) + { + auto block = std::move(FreeBlocks.back()); + block->Position = 0; + FreeBlocks.pop_back(); + UsedBlocks.push_back(std::move(block)); + } + else + { + UsedBlocks.push_back(std::make_unique()); + } + } + + auto &block = UsedBlocks.back(); + void *data = block->Data + block->Position; + block->Position += size; + + return data; + } + + void RenderMemory::Clear() + { + while (!UsedBlocks.empty()) + { + auto block = std::move(UsedBlocks.back()); + UsedBlocks.pop_back(); + FreeBlocks.push_back(std::move(block)); + } + } +} diff --git a/src/swrenderer/r_memory.h b/src/swrenderer/r_memory.h new file mode 100644 index 000000000..f01d52a4d --- /dev/null +++ b/src/swrenderer/r_memory.h @@ -0,0 +1,58 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include + +namespace swrenderer +{ + // Memory needed for the duration of a frame rendering + class RenderMemory + { + public: + void Clear(); + + template + T *AllocMemory(int size = 1) + { + return (T*)AllocBytes(sizeof(T) * size); + } + + template + T *NewObject(Types &&... args) + { + void *ptr = AllocBytes(sizeof(T)); + return new (ptr)T(std::forward(args)...); + } + + private: + void *AllocBytes(int size); + + enum { BlockSize = 1024 * 1024 }; + + struct MemoryBlock + { + MemoryBlock() : Data(new uint8_t[BlockSize]), Position(0) { } + ~MemoryBlock() { delete[] Data; } + + MemoryBlock(const MemoryBlock &) = delete; + MemoryBlock &operator=(const MemoryBlock &) = delete; + + uint8_t *Data; + uint32_t Position; + }; + std::vector> UsedBlocks; + std::vector> FreeBlocks; + }; +} diff --git a/src/swrenderer/r_renderthread.cpp b/src/swrenderer/r_renderthread.cpp new file mode 100644 index 000000000..41bb2fc5a --- /dev/null +++ b/src/swrenderer/r_renderthread.cpp @@ -0,0 +1,89 @@ +/* +** Renderer multithreading framework +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "r_renderthread.h" +#include "swrenderer/things/r_visiblespritelist.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/things/r_playersprite.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/drawers/r_thread.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "swrenderer/viewport/r_viewport.h" +#include "r_memory.h" + +namespace swrenderer +{ + RenderThread::RenderThread(RenderScene *scene, bool mainThread) + { + Scene = scene; + MainThread = mainThread; + FrameMemory = std::make_unique(); + DrawQueue = std::make_shared(this); + OpaquePass = std::make_unique(this); + TranslucentPass = std::make_unique(this); + SpriteList = std::make_unique(); + Portal = std::make_unique(this); + Clip3D = std::make_unique(this); + PlayerSprites = std::make_unique(this); + PlaneList = std::make_unique(this); + DrawSegments = std::make_unique(this); + ClipSegments = std::make_unique(); + tc_drawers = std::make_unique(DrawQueue); + pal_drawers = std::make_unique(DrawQueue); + } + + RenderThread::~RenderThread() + { + } + + SWPixelFormatDrawers *RenderThread::Drawers() + { + if (RenderViewport::Instance()->RenderTarget->IsBgra()) + return tc_drawers.get(); + else + return pal_drawers.get(); + } +} diff --git a/src/swrenderer/r_renderthread.h b/src/swrenderer/r_renderthread.h new file mode 100644 index 000000000..e874dbe4e --- /dev/null +++ b/src/swrenderer/r_renderthread.h @@ -0,0 +1,83 @@ +/* +** Renderer multithreading framework +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#pragma once + +#include +#include + +class DrawerCommandQueue; +typedef std::shared_ptr DrawerCommandQueuePtr; + +namespace swrenderer +{ + class VisibleSpriteList; + class RenderPortal; + class RenderOpaquePass; + class RenderTranslucentPass; + class RenderPlayerSprites; + class RenderScene; + class Clip3DFloors; + class VisiblePlaneList; + class DrawSegmentList; + class RenderClipSegment; + class RenderMemory; + class SWPixelFormatDrawers; + class SWTruecolorDrawers; + class SWPalDrawers; + + class RenderThread + { + public: + RenderThread(RenderScene *scene, bool mainThread = true); + ~RenderThread(); + + RenderScene *Scene; + int X1 = 0; + int X2 = MAXWIDTH; + bool MainThread = false; + + std::unique_ptr FrameMemory; + std::unique_ptr OpaquePass; + std::unique_ptr TranslucentPass; + std::unique_ptr SpriteList; + std::unique_ptr Portal; + std::unique_ptr Clip3D; + std::unique_ptr PlayerSprites; + std::unique_ptr PlaneList; + std::unique_ptr DrawSegments; + std::unique_ptr ClipSegments; + DrawerCommandQueuePtr DrawQueue; + + std::thread thread; + + // VisibleSprite working buffers + short clipbot[MAXWIDTH]; + short cliptop[MAXWIDTH]; + + SWPixelFormatDrawers *Drawers(); + + private: + std::unique_ptr tc_drawers; + std::unique_ptr pal_drawers; + }; +} diff --git a/src/swrenderer/r_swcanvas.cpp b/src/swrenderer/r_swcanvas.cpp new file mode 100644 index 000000000..aa817e707 --- /dev/null +++ b/src/swrenderer/r_swcanvas.cpp @@ -0,0 +1,857 @@ +/* +** r_swcanvas.cpp +** Draw to a canvas using software rendering +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** 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 "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/r_renderthread.h" +#include "v_palette.h" +#include "v_video.h" +#include "m_png.h" +#include "colormatcher.h" +#include "r_swcanvas.h" +#include "textures/textures.h" +#include "r_data/voxels.h" +#include "drawers/r_draw_rgba.h" + +EXTERN_CVAR(Bool, r_blendmethod) + +using namespace swrenderer; + +void SWCanvas::DrawTexture(DCanvas *canvas, FTexture *img, DrawParms &parms) +{ + static short bottomclipper[MAXWIDTH], topclipper[MAXWIDTH]; + + auto viewport = RenderViewport::Instance(); + viewport->RenderTarget = canvas; + viewport->RenderTarget->Lock(true); + + lighttable_t *translation = nullptr; + FDynamicColormap *basecolormap = &identitycolormap; + int shade = 0; + + if (APART(parms.colorOverlay) != 0) + { + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + INTBOOL invertoverlay = (parms.style.Flags & STYLEF_InvertOverlay); + + if (parms.style.Flags & STYLEF_InvertSource) + { + invertoverlay = !invertoverlay; + } + if (invertoverlay) + { + parms.colorOverlay = PalEntry(parms.colorOverlay).InverseColor(); + } + // Note that this overrides the translation in software, but not in hardware. + FDynamicColormap *colormap = GetSpecialLights(MAKERGB(255, 255, 255), parms.colorOverlay & MAKEARGB(0, 255, 255, 255), 0); + + if (viewport->RenderTarget->IsBgra()) + { + basecolormap = colormap; + shade = (APART(parms.colorOverlay)*NUMCOLORMAPS / 255) << FRACBITS; + } + else + { + translation = &colormap->Maps[(APART(parms.colorOverlay)*NUMCOLORMAPS / 255) * 256]; + } + } + else if (parms.remap != NULL) + { + if (viewport->RenderTarget->IsBgra()) + translation = (lighttable_t *)parms.remap->Palette; + else + translation = parms.remap->Remap; + } + + SpriteDrawerArgs drawerargs; + + drawerargs.SetTranslationMap(translation); + drawerargs.SetLight(basecolormap, 0.0f, shade); + bool visible = drawerargs.SetStyle(parms.style, parms.Alpha, -1, parms.fillcolor, basecolormap); + + double x0 = parms.x - parms.left * parms.destwidth / parms.texwidth; + double y0 = parms.y - parms.top * parms.destheight / parms.texheight; + + if (visible) + { + double centeryback = viewport->CenterY; + viewport->CenterY = 0; + + // There is not enough precision in the drawing routines to keep the full + // precision for y0. :( + double sprtopscreen; + modf(y0, &sprtopscreen); + + double yscale = parms.destheight / img->GetHeight(); + double iyscale = 1 / yscale; + + double spryscale = yscale; + assert(spryscale > 0); + + bool sprflipvert = false; + fixed_t iscale = FLOAT2FIXED(1 / spryscale); + //dc_texturemid = (CenterY - 1 - sprtopscreen) * iscale / 65536; + fixed_t frac = 0; + double xiscale = img->GetWidth() / parms.destwidth; + double x2 = x0 + parms.destwidth; + + short *mfloorclip; + short *mceilingclip; + + if (bottomclipper[0] != parms.dclip) + { + fillshort(bottomclipper, screen->GetWidth(), (short)parms.dclip); + } + if (parms.uclip != 0) + { + if (topclipper[0] != parms.uclip) + { + fillshort(topclipper, screen->GetWidth(), (short)parms.uclip); + } + mceilingclip = topclipper; + } + else + { + mceilingclip = zeroarray; + } + mfloorclip = bottomclipper; + + if (parms.flipX) + { + frac = (img->GetWidth() << FRACBITS) - 1; + xiscale = -xiscale; + } + + if (parms.windowleft > 0 || parms.windowright < parms.texwidth) + { + double wi = MIN(parms.windowright, parms.texwidth); + double xscale = parms.destwidth / parms.texwidth; + x0 += parms.windowleft * xscale; + frac += FLOAT2FIXED(parms.windowleft); + x2 -= (parms.texwidth - wi) * xscale; + } + if (x0 < parms.lclip) + { + frac += FLOAT2FIXED((parms.lclip - x0) * xiscale); + x0 = parms.lclip; + } + if (x2 > parms.rclip) + { + x2 = parms.rclip; + } + + int x = int(x0); + int x2_i = int(x2); + fixed_t xiscale_i = FLOAT2FIXED(xiscale); + + static RenderThread thread(nullptr); + thread.DrawQueue->ThreadedRender = false; + while (x < x2_i) + { + drawerargs.DrawMaskedColumn(&thread, x, iscale, img, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, !parms.masked); + x++; + frac += xiscale_i; + } + + viewport->CenterY = centeryback; + } + + viewport->RenderTarget->Unlock(); + viewport->RenderTarget = canvas; +} + +void SWCanvas::FillSimplePoly(DCanvas *canvas, FTexture *tex, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, DAngle rotation, + FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) +{ + // Use an equation similar to player sprites to determine shade + fixed_t shade = LightVisibility::LightLevelToShade(lightlevel, true) - 12 * FRACUNIT; + float topy, boty, leftx, rightx; + int toppt, botpt, pt1, pt2; + int i; + int y1, y2, y; + fixed_t x; + bool dorotate = rotation != 0.; + double cosrot, sinrot; + + if (--npoints < 2) + { // not a polygon or we're not locked + return; + } + + if (bottomclip <= 0) + { + bottomclip = canvas->GetHeight(); + } + + // Find the extents of the polygon, in particular the highest and lowest points. + for (botpt = toppt = 0, boty = topy = points[0].Y, leftx = rightx = points[0].X, i = 1; i <= npoints; ++i) + { + if (points[i].Y < topy) + { + topy = points[i].Y; + toppt = i; + } + if (points[i].Y > boty) + { + boty = points[i].Y; + botpt = i; + } + if (points[i].X < leftx) + { + leftx = points[i].X; + } + if (points[i].X > rightx) + { + rightx = points[i].X; + } + } + if (topy >= bottomclip || // off the bottom of the screen + boty <= 0 || // off the top of the screen + leftx >= canvas->GetWidth() || // off the right of the screen + rightx <= 0) // off the left of the screen + { + return; + } + + auto viewport = RenderViewport::Instance(); + viewport->RenderTarget = canvas; + + viewport->RenderTarget->Lock(true); + + scalex /= tex->Scale.X; + scaley /= tex->Scale.Y; + + // Use the CRT's functions here. + cosrot = cos(rotation.Radians()); + sinrot = sin(rotation.Radians()); + + // Setup constant texture mapping parameters. + SpanDrawerArgs drawerargs; + drawerargs.SetTexture(tex); + if (colormap) + drawerargs.SetLight(colormap, 0, clamp(shade >> FRACBITS, 0, NUMCOLORMAPS - 1)); + else + drawerargs.SetLight(&identitycolormap, 0, 0); + if (drawerargs.TextureWidthBits() != 0) + { + scalex = double(1u << (32 - drawerargs.TextureWidthBits())) / scalex; + drawerargs.SetTextureUStep(xs_RoundToInt(cosrot * scalex)); + } + else + { // Texture is one pixel wide. + scalex = 0; + drawerargs.SetTextureUStep(0); + } + if (drawerargs.TextureHeightBits() != 0) + { + scaley = double(1u << (32 - drawerargs.TextureHeightBits())) / scaley; + drawerargs.SetTextureVStep(xs_RoundToInt(sinrot * scaley)); + } + else + { // Texture is one pixel tall. + scaley = 0; + drawerargs.SetTextureVStep(0); + } + + int width = canvas->GetWidth(); + + // Travel down the right edge and create an outline of that edge. + static short spanend[MAXHEIGHT]; + pt1 = toppt; + pt2 = toppt + 1; if (pt2 > npoints) pt2 = 0; + y1 = xs_RoundToInt(points[pt1].Y + 0.5f); + do + { + x = FLOAT2FIXED(points[pt1].X + 0.5f); + y2 = xs_RoundToInt(points[pt2].Y + 0.5f); + if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) + { + } + else + { + fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); + int y3 = MIN(y2, bottomclip); + if (y1 < 0) + { + x += xinc * -y1; + y1 = 0; + } + for (y = y1; y < y3; ++y) + { + spanend[y] = clamp(x >> FRACBITS, -1, width); + x += xinc; + } + } + y1 = y2; + pt1 = pt2; + pt2++; if (pt2 > npoints) pt2 = 0; + } while (pt1 != botpt); + + static RenderThread thread(nullptr); + thread.DrawQueue->ThreadedRender = false; + + // Travel down the left edge and fill it in. + pt1 = toppt; + pt2 = toppt - 1; if (pt2 < 0) pt2 = npoints; + y1 = xs_RoundToInt(points[pt1].Y + 0.5f); + do + { + x = FLOAT2FIXED(points[pt1].X + 0.5f); + y2 = xs_RoundToInt(points[pt2].Y + 0.5f); + if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) + { + } + else + { + fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); + int y3 = MIN(y2, bottomclip); + if (y1 < 0) + { + x += xinc * -y1; + y1 = 0; + } + for (y = y1; y < y3; ++y) + { + int x1 = x >> FRACBITS; + int x2 = spanend[y]; + if (x2 > x1 && x2 > 0 && x1 < width) + { + x1 = MAX(x1, 0); + x2 = MIN(x2, width); +#if 0 + memset(this->Buffer + y * this->Pitch + x1, (int)tex, x2 - x1); +#else + drawerargs.SetDestY(y); + drawerargs.SetDestX1(x1); + drawerargs.SetDestX2(x2 - 1); + + DVector2 tex(x1 - originx, y - originy); + if (dorotate) + { + double t = tex.X; + tex.X = t * cosrot - tex.Y * sinrot; + tex.Y = tex.Y * cosrot + t * sinrot; + } + drawerargs.SetTextureUPos(xs_RoundToInt(tex.X * scalex)); + drawerargs.SetTextureVPos(xs_RoundToInt(tex.Y * scaley)); + + drawerargs.DrawSpan(&thread); +#endif + } + x += xinc; + } + } + y1 = y2; + pt1 = pt2; + pt2--; if (pt2 < 0) pt2 = npoints; + } while (pt1 != botpt); + + viewport->RenderTarget->Unlock(); + viewport->RenderTarget = screen; +} + +void SWCanvas::DrawLine(DCanvas *canvas, int x0, int y0, int x1, int y1, int palColor, uint32 realcolor) +{ + const int WeightingScale = 0; + const int WEIGHTBITS = 6; + const int WEIGHTSHIFT = 16 - WEIGHTBITS; + const int NUMWEIGHTS = (1 << WEIGHTBITS); + const int WEIGHTMASK = (NUMWEIGHTS - 1); + + if (palColor < 0) + { + palColor = PalFromRGB(realcolor); + } + + canvas->Lock(); + int deltaX, deltaY, xDir; + + if (y0 > y1) + { + int temp = y0; y0 = y1; y1 = temp; + temp = x0; x0 = x1; x1 = temp; + } + + PUTTRANSDOT(canvas, x0, y0, palColor, 0); + + if ((deltaX = x1 - x0) >= 0) + { + xDir = 1; + } + else + { + xDir = -1; + deltaX = -deltaX; + } + + if ((deltaY = y1 - y0) == 0) + { // horizontal line + if (x0 > x1) + { + swapvalues(x0, x1); + } + if (canvas->IsBgra()) + { + uint32_t fillColor = GPalette.BaseColors[palColor].d; + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + for (int i = 0; i <= deltaX; i++) + spot[i] = fillColor; + } + else + { + memset(canvas->GetBuffer() + y0*canvas->GetPitch() + x0, palColor, deltaX + 1); + } + } + else if (deltaX == 0) + { // vertical line + if (canvas->IsBgra()) + { + uint32_t fillColor = GPalette.BaseColors[palColor].d; + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + int pitch = canvas->GetPitch(); + do + { + *spot = fillColor; + spot += pitch; + } while (--deltaY != 0); + } + else + { + BYTE *spot = canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + int pitch = canvas->GetPitch(); + do + { + *spot = palColor; + spot += pitch; + } while (--deltaY != 0); + } + } + else if (deltaX == deltaY) + { // diagonal line. + if (canvas->IsBgra()) + { + uint32_t fillColor = GPalette.BaseColors[palColor].d; + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + int advance = canvas->GetPitch() + xDir; + do + { + *spot = fillColor; + spot += advance; + } while (--deltaY != 0); + } + else + { + BYTE *spot = canvas->GetBuffer() + y0*canvas->GetPitch() + x0; + int advance = canvas->GetPitch() + xDir; + do + { + *spot = palColor; + spot += advance; + } while (--deltaY != 0); + } + } + else + { + // line is not horizontal, diagonal, or vertical + fixed_t errorAcc = 0; + + if (deltaY > deltaX) + { // y-major line + fixed_t errorAdj = (((unsigned)deltaX << 16) / (unsigned)deltaY) & 0xffff; + if (xDir < 0) + { + if (WeightingScale == 0) + { + while (--deltaY) + { + errorAcc += errorAdj; + y0++; + int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0 - (errorAcc >> 16), y0, palColor, weighting); + PUTTRANSDOT(canvas, x0 - (errorAcc >> 16) - 1, y0, + palColor, WEIGHTMASK - weighting); + } + } + else + { + while (--deltaY) + { + errorAcc += errorAdj; + y0++; + int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT + 8)) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0 - (errorAcc >> 16), y0, palColor, weighting); + PUTTRANSDOT(canvas, x0 - (errorAcc >> 16) - 1, y0, + palColor, WEIGHTMASK - weighting); + } + } + } + else + { + if (WeightingScale == 0) + { + while (--deltaY) + { + errorAcc += errorAdj; + y0++; + int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0 + (errorAcc >> 16), y0, palColor, weighting); + PUTTRANSDOT(canvas, x0 + (errorAcc >> 16) + xDir, y0, + palColor, WEIGHTMASK - weighting); + } + } + else + { + while (--deltaY) + { + errorAcc += errorAdj; + y0++; + int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT + 8)) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0 + (errorAcc >> 16), y0, palColor, weighting); + PUTTRANSDOT(canvas, x0 + (errorAcc >> 16) + xDir, y0, + palColor, WEIGHTMASK - weighting); + } + } + } + } + else + { // x-major line + fixed_t errorAdj = (((DWORD)deltaY << 16) / (DWORD)deltaX) & 0xffff; + + if (WeightingScale == 0) + { + while (--deltaX) + { + errorAcc += errorAdj; + x0 += xDir; + int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0, y0 + (errorAcc >> 16), palColor, weighting); + PUTTRANSDOT(canvas, x0, y0 + (errorAcc >> 16) + 1, + palColor, WEIGHTMASK - weighting); + } + } + else + { + while (--deltaX) + { + errorAcc += errorAdj; + x0 += xDir; + int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT + 8)) & WEIGHTMASK; + PUTTRANSDOT(canvas, x0, y0 + (errorAcc >> 16), palColor, weighting); + PUTTRANSDOT(canvas, x0, y0 + (errorAcc >> 16) + 1, + palColor, WEIGHTMASK - weighting); + } + } + } + PUTTRANSDOT(canvas, x1, y1, palColor, 0); + } + canvas->Unlock(); +} + +void SWCanvas::DrawPixel(DCanvas *canvas, int x, int y, int palColor, uint32 realcolor) +{ + if (palColor < 0) + { + palColor = PalFromRGB(realcolor); + } + + canvas->GetBuffer()[canvas->GetPitch() * y + x] = (BYTE)palColor; +} + +void SWCanvas::PUTTRANSDOT(DCanvas *canvas, int xx, int yy, int basecolor, int level) +{ + static int oldyy; + static int oldyyshifted; + + if (yy == oldyy + 1) + { + oldyy++; + oldyyshifted += canvas->GetPitch(); + } + else if (yy == oldyy - 1) + { + oldyy--; + oldyyshifted -= canvas->GetPitch(); + } + else if (yy != oldyy) + { + oldyy = yy; + oldyyshifted = yy * canvas->GetPitch(); + } + + if (canvas->IsBgra()) + { + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + oldyyshifted + xx; + + uint32_t fg = swrenderer::LightBgra::shade_pal_index_simple(basecolor, swrenderer::LightBgra::calc_light_multiplier(0)); + uint32_t fg_red = (((fg >> 16) & 0xff) * (63 - level)) >> 6; + uint32_t fg_green = (((fg >> 8) & 0xff) * (63 - level)) >> 6; + uint32_t fg_blue = ((fg & 0xff) * (63 - level)) >> 6; + + uint32_t bg_red = (((*spot >> 16) & 0xff) * level) >> 6; + uint32_t bg_green = (((*spot >> 8) & 0xff) * level) >> 6; + uint32_t bg_blue = (((*spot) & 0xff) * level) >> 6; + + uint32_t red = fg_red + bg_red; + uint32_t green = fg_green + bg_green; + uint32_t blue = fg_blue + bg_blue; + + *spot = 0xff000000 | (red << 16) | (green << 8) | blue; + } + else if (!r_blendmethod) + { + BYTE *spot = canvas->GetBuffer() + oldyyshifted + xx; + DWORD *bg2rgb = Col2RGB8[1 + level]; + DWORD *fg2rgb = Col2RGB8[63 - level]; + DWORD fg = fg2rgb[basecolor]; + DWORD bg = bg2rgb[*spot]; + bg = (fg + bg) | 0x1f07c1f; + *spot = RGB32k.All[bg&(bg >> 15)]; + } + else + { + BYTE *spot = canvas->GetBuffer() + oldyyshifted + xx; + + uint32_t r = (GPalette.BaseColors[*spot].r * (64 - level) + GPalette.BaseColors[basecolor].r * level) / 256; + uint32_t g = (GPalette.BaseColors[*spot].g * (64 - level) + GPalette.BaseColors[basecolor].g * level) / 256; + uint32_t b = (GPalette.BaseColors[*spot].b * (64 - level) + GPalette.BaseColors[basecolor].b * level) / 256; + + *spot = (BYTE)RGB256k.RGB[r][g][b]; + } +} + +void SWCanvas::Clear(DCanvas *canvas, int left, int top, int right, int bottom, int palcolor, uint32 color) +{ + int x, y; + + if (left == right || top == bottom) + { + return; + } + + assert(left < right); + assert(top < bottom); + + int Width = canvas->GetWidth(); + int Height = canvas->GetHeight(); + int Pitch = canvas->GetPitch(); + + if (left >= Width || right <= 0 || top >= Height || bottom <= 0) + { + return; + } + left = MAX(0, left); + right = MIN(Width, right); + top = MAX(0, top); + bottom = MIN(Height, bottom); + + if (palcolor < 0) + { + palcolor = PalFromRGB(color); + } + + if (canvas->IsBgra()) + { + uint32_t fill_color = GPalette.BaseColors[palcolor]; + + uint32_t *dest = (uint32_t*)canvas->GetBuffer() + top * Pitch + left; + x = right - left; + for (y = top; y < bottom; y++) + { + for (int i = 0; i < x; i++) + dest[i] = fill_color; + dest += Pitch; + } + } + else + { + BYTE *dest = canvas->GetBuffer() + top * Pitch + left; + x = right - left; + for (y = top; y < bottom; y++) + { + memset(dest, palcolor, x); + dest += Pitch; + } + } +} + +void SWCanvas::Dim(DCanvas *canvas, PalEntry color, float damount, int x1, int y1, int w, int h) +{ + if (damount == 0.f) + return; + + int Width = canvas->GetWidth(); + int Height = canvas->GetHeight(); + int Pitch = canvas->GetPitch(); + + if (x1 >= Width || y1 >= Height) + { + return; + } + if (x1 + w > Width) + { + w = Width - x1; + } + if (y1 + h > Height) + { + h = Height - y1; + } + if (w <= 0 || h <= 0) + { + return; + } + + if (canvas->IsBgra()) + { + uint32_t *spot = (uint32_t*)canvas->GetBuffer() + x1 + y1*Pitch; + int gap = Pitch - w; + + uint32_t fg = color.d; + uint32_t fg_red = (fg >> 16) & 0xff; + uint32_t fg_green = (fg >> 8) & 0xff; + uint32_t fg_blue = fg & 0xff; + + uint32_t alpha = (uint32_t)clamp(damount * 256 + 0.5f, 0.0f, 256.0f); + uint32_t inv_alpha = 256 - alpha; + + fg_red *= alpha; + fg_green *= alpha; + fg_blue *= alpha; + + for (int y = h; y != 0; y--) + { + for (int x = w; x != 0; x--) + { + uint32_t bg_red = (*spot >> 16) & 0xff; + uint32_t bg_green = (*spot >> 8) & 0xff; + uint32_t bg_blue = (*spot) & 0xff; + + uint32_t red = (fg_red + bg_red * inv_alpha) / 256; + uint32_t green = (fg_green + bg_green * inv_alpha) / 256; + uint32_t blue = (fg_blue + bg_blue * inv_alpha) / 256; + + *spot = 0xff000000 | (red << 16) | (green << 8) | blue; + spot++; + } + spot += gap; + } + } + else + { + DWORD *bg2rgb; + DWORD fg; + + BYTE *spot = canvas->GetBuffer() + x1 + y1*Pitch; + int gap = Pitch - w; + + int alpha = (int)((float)64 * damount); + int ialpha = 64 - alpha; + int dimmedcolor_r = color.r * alpha; + int dimmedcolor_g = color.g * alpha; + int dimmedcolor_b = color.b * alpha; + + if (!r_blendmethod) + { + { + int amount; + + amount = (int)(damount * 64); + bg2rgb = Col2RGB8[64 - amount]; + + fg = (((color.r * amount) >> 4) << 20) | + ((color.g * amount) >> 4) | + (((color.b * amount) >> 4) << 10); + } + + for (int y = h; y != 0; y--) + { + for (int x = w; x != 0; x--) + { + DWORD bg; + + bg = bg2rgb[(*spot) & 0xff]; + bg = (fg + bg) | 0x1f07c1f; + *spot = RGB32k.All[bg&(bg >> 15)]; + spot++; + } + spot += gap; + } + } + else + { + for (int y = h; y != 0; y--) + { + for (int x = w; x != 0; x--) + { + uint32_t r = (dimmedcolor_r + GPalette.BaseColors[*spot].r * ialpha) >> 8; + uint32_t g = (dimmedcolor_g + GPalette.BaseColors[*spot].g * ialpha) >> 8; + uint32_t b = (dimmedcolor_b + GPalette.BaseColors[*spot].b * ialpha) >> 8; + *spot = (BYTE)RGB256k.RGB[r][g][b]; + spot++; + } + spot += gap; + } + } + } +} + +int SWCanvas::PalFromRGB(uint32 rgb) +{ + // For routines that take RGB colors, cache the previous lookup in case there + // are several repetitions with the same color. + static int LastPal = -1; + static uint32 LastRGB; + + if (LastPal >= 0 && LastRGB == rgb) + { + return LastPal; + } + // Quick check for black and white. + if (rgb == MAKEARGB(255, 0, 0, 0)) + { + LastPal = GPalette.BlackIndex; + } + else if (rgb == MAKEARGB(255, 255, 255, 255)) + { + LastPal = GPalette.WhiteIndex; + } + else + { + LastPal = ColorMatcher.Pick(RPART(rgb), GPART(rgb), BPART(rgb)); + } + LastRGB = rgb; + return LastPal; +} diff --git a/src/swrenderer/r_swcanvas.h b/src/swrenderer/r_swcanvas.h new file mode 100644 index 000000000..a3f80113b --- /dev/null +++ b/src/swrenderer/r_swcanvas.h @@ -0,0 +1,22 @@ + +#pragma once + +#include "v_video.h" +#include "r_data/colormaps.h" + +class SWCanvas +{ +public: + static void DrawTexture(DCanvas *canvas, FTexture *img, DrawParms &parms); + static void FillSimplePoly(DCanvas *canvas, FTexture *tex, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, DAngle rotation, + FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip); + static void DrawLine(DCanvas *canvas, int x0, int y0, int x1, int y1, int palColor, uint32 realcolor); + static void DrawPixel(DCanvas *canvas, int x, int y, int palColor, uint32 realcolor); + static void Clear(DCanvas *canvas, int left, int top, int right, int bottom, int palcolor, uint32 color); + static void Dim(DCanvas *canvas, PalEntry color, float damount, int x1, int y1, int w, int h); + +private: + static void PUTTRANSDOT(DCanvas *canvas, int xx, int yy, int basecolor, int level); + static int PalFromRGB(uint32 rgb); +}; diff --git a/src/swrenderer/r_swrenderer.cpp b/src/swrenderer/r_swrenderer.cpp new file mode 100644 index 000000000..75a53102b --- /dev/null +++ b/src/swrenderer/r_swrenderer.cpp @@ -0,0 +1,338 @@ +/* +** r_swrender.cpp +** Software renderer interface +** +**--------------------------------------------------------------------------- +** Copyright 2011 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 "swrenderer/scene/r_scene.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/things/r_playersprite.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "v_palette.h" +#include "v_video.h" +#include "m_png.h" +#include "r_swrenderer.h" +#include "scene/r_opaque_pass.h" +#include "scene/r_3dfloors.h" +#include "scene/r_portal.h" +#include "textures/textures.h" +#include "r_data/voxels.h" +#include "drawers/r_draw_rgba.h" +#include "polyrenderer/poly_renderer.h" +#include "p_setup.h" + +void gl_ParseDefs(); +void gl_InitData(); +void gl_SetActorLights(AActor *); +void gl_PreprocessLevel(); +void gl_CleanLevelData(); + +EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Float, maxviewpitch) // [SP] CVAR from GZDoom + +CUSTOM_CVAR(Bool, r_polyrenderer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self == 1 && !hasglnodes) + { + Printf("No GL BSP detected. You must restart the map before rendering will be correct\n"); + } + + if (usergame) + { + // [SP] Update pitch limits to the netgame/gamesim. + players[consoleplayer].SendPitchLimits(); + } +} + +using namespace swrenderer; + +FSoftwareRenderer::FSoftwareRenderer() +{ +} + +FSoftwareRenderer::~FSoftwareRenderer() +{ +} + +void FSoftwareRenderer::Init() +{ + gl_ParseDefs(); + + mScene.Init(); +} + +bool FSoftwareRenderer::UsesColormap() const +{ + return true; +} + +void FSoftwareRenderer::PrecacheTexture(FTexture *tex, int cache) +{ + bool isbgra = screen->IsBgra(); + + if (tex != NULL) + { + if (cache & FTextureManager::HIT_Columnmode) + { + const FTexture::Span *spanp; + if (isbgra) + tex->GetColumnBgra(0, &spanp); + else + tex->GetColumn(0, &spanp); + } + else if (cache != 0) + { + if (isbgra) + tex->GetPixelsBgra(); + else + tex->GetPixels (); + } + else + { + tex->Unload (); + } + } +} + +void FSoftwareRenderer::Precache(BYTE *texhitlist, TMap &actorhitlist) +{ + BYTE *spritelist = new BYTE[sprites.Size()]; + TMap::Iterator it(actorhitlist); + TMap::Pair *pair; + + memset(spritelist, 0, sprites.Size()); + + while (it.NextPair(pair)) + { + PClassActor *cls = pair->Key; + + for (int i = 0; i < cls->NumOwnedStates; i++) + { + spritelist[cls->OwnedStates[i].sprite] = true; + } + } + + // Precache textures (and sprites). + + for (int i = (int)(sprites.Size() - 1); i >= 0; i--) + { + if (spritelist[i]) + { + int j, k; + for (j = 0; j < sprites[i].numframes; j++) + { + const spriteframe_t *frame = &SpriteFrames[sprites[i].spriteframes + j]; + + for (k = 0; k < 16; k++) + { + FTextureID pic = frame->Texture[k]; + if (pic.isValid()) + { + texhitlist[pic.GetIndex()] = FTextureManager::HIT_Sprite; + } + } + } + } + } + delete[] spritelist; + + int cnt = TexMan.NumTextures(); + for (int i = cnt - 1; i >= 0; i--) + { + PrecacheTexture(TexMan.ByIndex(i), texhitlist[i]); + } +} + +void FSoftwareRenderer::RenderView(player_t *player) +{ + if (r_polyrenderer) + PolyRenderer::Instance()->RenderView(player); + else + mScene.RenderView(player); + + FCanvasTextureInfo::UpdateAll(); +} + +void FSoftwareRenderer::RemapVoxels() +{ + for (unsigned i=0; iCreateBgraSlabData(); + Voxels[i]->Remap(); + } +} + +void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *file, int width, int height) +{ + DCanvas *pic = new DSimpleCanvas (width, height, false); + PalEntry palette[256]; + + // Take a snapshot of the player's view + pic->ObjectFlags |= OF_Fixed; + pic->Lock (); + if (r_polyrenderer) + PolyRenderer::Instance()->RenderViewToCanvas(player->mo, pic, 0, 0, width, height, true); + else + mScene.RenderViewToCanvas (player->mo, pic, 0, 0, width, height); + screen->GetFlashedPalette (palette); + M_CreatePNG (file, pic->GetBuffer(), palette, SS_PAL, width, height, pic->GetPitch()); + pic->Unlock (); + pic->Destroy(); + pic->ObjectFlags |= OF_YesReallyDelete; + delete pic; +} + +void FSoftwareRenderer::DrawRemainingPlayerSprites() +{ + if (!r_polyrenderer) + { + mScene.MainThread()->PlayerSprites->RenderRemaining(); + } + else + { + PolyRenderer::Instance()->RenderRemainingPlayerSprites(); + } +} + +int FSoftwareRenderer::GetMaxViewPitch(bool down) +{ + const int MAX_DN_ANGLE = 56; // Max looking down angle + const int MAX_UP_ANGLE = 32; // Max looking up angle + return (r_polyrenderer) ? int(maxviewpitch) : (down ? MAX_DN_ANGLE : MAX_UP_ANGLE); +} + +bool FSoftwareRenderer::RequireGLNodes() +{ + return true; +} + +void FSoftwareRenderer::OnModeSet () +{ + mScene.ScreenResized(); +} + +void FSoftwareRenderer::SetClearColor(int color) +{ + mScene.SetClearColor(color); +} + +void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) +{ + auto viewport = RenderViewport::Instance(); + + BYTE *Pixels = viewport->RenderTarget->IsBgra() ? (BYTE*)tex->GetPixelsBgra() : (BYTE*)tex->GetPixels(); + DSimpleCanvas *Canvas = viewport->RenderTarget->IsBgra() ? tex->GetCanvasBgra() : tex->GetCanvas(); + + // curse Doom's overuse of global variables in the renderer. + // These get clobbered by rendering to a camera texture but they need to be preserved so the final rendering can be done with the correct palette. + CameraLight savedCameraLight = *CameraLight::Instance(); + + DAngle savedfov = FieldOfView; + R_SetFOV ((double)fov); + + if (r_polyrenderer) + PolyRenderer::Instance()->RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate); + else + mScene.RenderViewToCanvas(viewpoint, Canvas, 0, 0, tex->GetWidth(), tex->GetHeight(), tex->bFirstUpdate); + + R_SetFOV (savedfov); + + if (Canvas->IsBgra()) + { + if (Pixels == Canvas->GetBuffer()) + { + FTexture::FlipSquareBlockBgra((uint32_t*)Pixels, tex->GetWidth(), tex->GetHeight()); + } + else + { + FTexture::FlipNonSquareBlockBgra((uint32_t*)Pixels, (const uint32_t*)Canvas->GetBuffer(), tex->GetWidth(), tex->GetHeight(), Canvas->GetPitch()); + } + } + else + { + if (Pixels == Canvas->GetBuffer()) + { + FTexture::FlipSquareBlockRemap(Pixels, tex->GetWidth(), tex->GetHeight(), GPalette.Remap); + } + else + { + FTexture::FlipNonSquareBlockRemap(Pixels, Canvas->GetBuffer(), tex->GetWidth(), tex->GetHeight(), Canvas->GetPitch(), GPalette.Remap); + } + } + + if (viewport->RenderTarget->IsBgra()) + { + // True color render still sometimes uses palette textures (for sprites, mostly). + // We need to make sure that both pixel buffers contain data: + int width = tex->GetWidth(); + int height = tex->GetHeight(); + BYTE *palbuffer = (BYTE *)tex->GetPixels(); + uint32_t *bgrabuffer = (uint32_t*)tex->GetPixelsBgra(); + for (int x = 0; x < width; x++) + { + for (int y = 0; y < height; y++) + { + uint32_t color = bgrabuffer[y]; + int r = RPART(color); + int g = GPART(color); + int b = BPART(color); + palbuffer[y] = RGB32k.RGB[r >> 3][g >> 3][b >> 3]; + } + palbuffer += height; + bgrabuffer += height; + } + } + + tex->SetUpdated(); + + *CameraLight::Instance() = savedCameraLight; +} + +sector_t *FSoftwareRenderer::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) +{ + return mScene.MainThread()->OpaquePass->FakeFlat(sec, tempsec, floorlightlevel, ceilinglightlevel, nullptr, 0, 0, 0, 0); +} + +void FSoftwareRenderer::StateChanged(AActor *actor) +{ + gl_SetActorLights(actor); +} + +void FSoftwareRenderer::PreprocessLevel() +{ + gl_PreprocessLevel(); +} + +void FSoftwareRenderer::CleanLevelData() +{ + gl_CleanLevelData(); +} diff --git a/src/swrenderer/r_swrenderer.h b/src/swrenderer/r_swrenderer.h new file mode 100644 index 000000000..6d9373879 --- /dev/null +++ b/src/swrenderer/r_swrenderer.h @@ -0,0 +1,47 @@ + +#pragma once + +#include "r_renderer.h" +#include "swrenderer/scene/r_scene.h" + +struct FSoftwareRenderer : public FRenderer +{ + FSoftwareRenderer(); + ~FSoftwareRenderer(); + + // Can be overridden so that the colormaps for sector color/fade won't be built. + bool UsesColormap() const override; + + // precache textures + void Precache(BYTE *texhitlist, TMap &actorhitlist) override; + + // render 3D view + void RenderView(player_t *player) override; + + // Remap voxel palette + void RemapVoxels() override; + + // renders view to a savegame picture + void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override; + + // draws player sprites with hardware acceleration (only useful for software rendering) + void DrawRemainingPlayerSprites() override; + + int GetMaxViewPitch(bool down) override; + bool RequireGLNodes() override; + + void OnModeSet() override; + void SetClearColor(int color) override; + void Init() override; + void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) override; + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) override; + + void StateChanged(AActor *actor) override; + void PreprocessLevel() override; + void CleanLevelData() override; + +private: + void PrecacheTexture(FTexture *tex, int cache); + + swrenderer::RenderScene mScene; +}; diff --git a/src/swrenderer/scene/r_3dfloors.cpp b/src/swrenderer/scene/r_3dfloors.cpp new file mode 100644 index 000000000..f49def6b3 --- /dev/null +++ b/src/swrenderer/scene/r_3dfloors.cpp @@ -0,0 +1,184 @@ +/* +** r_3dfloors.cpp +** software 3D floors addon +** +** by kgsws +*/ + +#include "templates.h" +#include "doomdef.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "r_opaque_pass.h" +#include "c_cvars.h" +#include "r_3dfloors.h" +#include "r_utility.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/r_memory.h" + +CVAR(Int, r_3dfloors, true, 0); + +namespace swrenderer +{ + Clip3DFloors::Clip3DFloors(RenderThread *thread) + { + Thread = thread; + } + + void Clip3DFloors::SetFakeFloor(F3DFloor *newFakeFloor) + { + auto &clip = FakeFloors[newFakeFloor]; + if (clip == nullptr) + { + clip = Thread->FrameMemory->NewObject(newFakeFloor); + } + fakeFloor = clip; + } + + void Clip3DFloors::Cleanup() + { + FakeFloors.clear(); + + fakeActive = false; + fake3D = 0; + while (CurrentSkybox) + { + DeleteHeights(); + LeaveSkybox(); + } + ResetClip(); + DeleteHeights(); + } + + void Clip3DFloors::DeleteHeights() + { + height_cur = height_top; + while (height_cur) + { + height_top = height_cur; + height_cur = height_cur->next; + M_Free(height_top); + } + height_max = -1; + height_top = height_cur = nullptr; + } + + void Clip3DFloors::AddHeight(secplane_t *add, sector_t *sec) + { + HeightLevel *near; + HeightLevel *curr; + + double height = add->ZatPoint(ViewPos); + if (height >= sec->CenterCeiling()) return; + if (height <= sec->CenterFloor()) return; + + fakeActive = true; + + if (height_max >= 0) + { + near = height_top; + while (near && near->height < height) near = near->next; + if (near) + { + if (near->height == height) return; + curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + curr->height = height; + curr->prev = near->prev; + curr->next = near; + if (near->prev) near->prev->next = curr; + else height_top = curr; + near->prev = curr; + } + else + { + curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + curr->height = height; + curr->prev = height_cur; + curr->next = nullptr; + height_cur->next = curr; + height_cur = curr; + } + } + else + { + height_top = height_cur = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + height_top->height = height; + height_top->prev = nullptr; + height_top->next = nullptr; + } + height_max++; + } + + void Clip3DFloors::NewClip() + { + ClipStack *curr; + + curr = (ClipStack*)M_Malloc(sizeof(ClipStack)); + curr->next = 0; + memcpy(curr->floorclip, Thread->OpaquePass->floorclip, sizeof(short) * MAXWIDTH); + memcpy(curr->ceilingclip, Thread->OpaquePass->ceilingclip, sizeof(short) * MAXWIDTH); + curr->ffloor = fakeFloor; + assert(fakeFloor->floorclip == nullptr); + assert(fakeFloor->ceilingclip == nullptr); + fakeFloor->floorclip = curr->floorclip; + fakeFloor->ceilingclip = curr->ceilingclip; + if (clip_top) + { + clip_cur->next = curr; + clip_cur = curr; + } + else + { + clip_top = clip_cur = curr; + } + } + + void Clip3DFloors::ResetClip() + { + clip_cur = clip_top; + while (clip_cur) + { + assert(clip_cur->ffloor->floorclip != nullptr); + assert(clip_cur->ffloor->ceilingclip != nullptr); + clip_cur->ffloor->ceilingclip = clip_cur->ffloor->floorclip = nullptr; + clip_top = clip_cur; + clip_cur = clip_cur->next; + M_Free(clip_top); + } + clip_cur = clip_top = nullptr; + } + + void Clip3DFloors::EnterSkybox() + { + HeightStack current; + + current.height_top = height_top; + current.height_cur = height_cur; + current.height_max = height_max; + + toplist.Push(current); + + height_top = nullptr; + height_cur = nullptr; + height_max = -1; + + CurrentSkybox++; + } + + void Clip3DFloors::LeaveSkybox() + { + HeightStack current; + + current.height_top = nullptr; + current.height_cur = nullptr; + current.height_max = -1; + + toplist.Pop(current); + + height_top = current.height_top; + height_cur = current.height_cur; + height_max = current.height_max; + + CurrentSkybox--; + } +} diff --git a/src/swrenderer/scene/r_3dfloors.h b/src/swrenderer/scene/r_3dfloors.h new file mode 100644 index 000000000..d697755b2 --- /dev/null +++ b/src/swrenderer/scene/r_3dfloors.h @@ -0,0 +1,104 @@ + +#pragma once + +#include "p_3dfloors.h" +#include + +EXTERN_CVAR(Int, r_3dfloors); + +namespace swrenderer +{ + class RenderThread; + class FakeFloorClip; + + struct HeightLevel + { + double height; + struct HeightLevel *prev; + struct HeightLevel *next; + }; + + struct HeightStack + { + HeightLevel *height_top; + HeightLevel *height_cur; + int height_max; + }; + + struct ClipStack + { + short floorclip[MAXWIDTH]; + short ceilingclip[MAXWIDTH]; + FakeFloorClip *ffloor; + ClipStack *next; + }; + + enum Fake3DOpaque + { + // BSP stage: + FAKE3D_FAKEFLOOR = 1, // fake floor, mark seg as FAKE + FAKE3D_FAKECEILING = 2, // fake ceiling, mark seg as FAKE + FAKE3D_FAKEBACK = 4, // RenderLine with fake backsector, mark seg as FAKE + FAKE3D_FAKEMASK = 7, + FAKE3D_CLIPBOTFRONT = 8, // use front sector clipping info (bottom) + FAKE3D_CLIPTOPFRONT = 16, // use front sector clipping info (top) + }; + + enum Fake3DTranslucent + { + // sorting stage: + FAKE3D_CLIPBOTTOM = 1, // clip bottom + FAKE3D_CLIPTOP = 2, // clip top + FAKE3D_REFRESHCLIP = 4, // refresh clip info + FAKE3D_DOWN2UP = 8, // rendering from down to up (floors) + }; + + class FakeFloorClip + { + public: + FakeFloorClip(F3DFloor *fakeFloor) : fakeFloor(fakeFloor) { } + + F3DFloor *fakeFloor = nullptr; + short *floorclip = nullptr; + short *ceilingclip = nullptr; + int validcount = -1; + }; + + class Clip3DFloors + { + public: + Clip3DFloors(RenderThread *thread); + + void Cleanup(); + + void DeleteHeights(); + void AddHeight(secplane_t *add, sector_t *sec); + void NewClip(); + void ResetClip(); + void EnterSkybox(); + void LeaveSkybox(); + void SetFakeFloor(F3DFloor *fakeFloor); + void ClearFakeFloors() { FakeFloors.clear(); } + + RenderThread *Thread = nullptr; + + int fake3D = 0; + + FakeFloorClip *fakeFloor = nullptr; + fixed_t fakeAlpha = 0; + bool fakeActive = false; + double sclipBottom = 0; + double sclipTop = 0; + HeightLevel *height_top = nullptr; + HeightLevel *height_cur = nullptr; + int CurrentSkybox = 0; + + private: + int height_max = -1; + TArray toplist; + ClipStack *clip_top = nullptr; + ClipStack *clip_cur = nullptr; + + std::unordered_map FakeFloors; + }; +} diff --git a/src/swrenderer/scene/r_light.cpp b/src/swrenderer/scene/r_light.cpp new file mode 100644 index 000000000..3407fb4bb --- /dev/null +++ b/src/swrenderer/scene/r_light.cpp @@ -0,0 +1,232 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "r_utility.h" +#include "d_player.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "gl/data/gl_data.h" + +CVAR(Bool, r_shadercolormaps, true, CVAR_ARCHIVE) +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) + +namespace swrenderer +{ + CameraLight *CameraLight::Instance() + { + static CameraLight instance; + return &instance; + } + + void CameraLight::SetCamera(AActor *actor) + { + player_t *player = actor->player; + if (camera && camera->player != nullptr) + player = camera->player; + + realfixedcolormap = nullptr; + fixedcolormap = nullptr; + fixedlightlev = -1; + + if (player != nullptr && camera == player->mo) + { + if (player->fixedcolormap >= 0 && player->fixedcolormap < (int)SpecialColormaps.Size()) + { + realfixedcolormap = &SpecialColormaps[player->fixedcolormap]; + auto viewport = RenderViewport::Instance(); + if (viewport->RenderTarget == screen && (viewport->RenderTarget->IsBgra() || ((DFrameBuffer *)screen->Accel2D && r_shadercolormaps))) + { + // Render everything fullbright. The copy to video memory will + // apply the special colormap, so it won't be restricted to the + // palette. + fixedcolormap = &realcolormaps; + } + else + { + fixedcolormap = &SpecialColormaps[player->fixedcolormap]; + } + } + else if (player->fixedlightlevel >= 0 && player->fixedlightlevel < NUMCOLORMAPS) + { + fixedlightlev = player->fixedlightlevel * 256; + // [SP] Emulate GZDoom's light-amp goggles. + if (r_fullbrightignoresectorcolor && fixedlightlev >= 0) + { + fixedcolormap = &FullNormalLight; + } + } + } + // [RH] Inverse light for shooting the Sigil + if (fixedcolormap == nullptr && extralight == INT_MIN) + { + fixedcolormap = &SpecialColormaps[INVERSECOLORMAP]; + extralight = 0; + } + } + + ///////////////////////////////////////////////////////////////////////// + + LightVisibility *LightVisibility::Instance() + { + static LightVisibility instance; + return &instance; + } + + // Changes how rapidly things get dark with distance + void LightVisibility::SetVisibility(double vis) + { + // Allow negative visibilities, just for novelty's sake + vis = clamp(vis, -204.7, 204.7); // (205 and larger do not work in 5:4 aspect ratio) + + CurrentVisibility = vis; + + auto viewport = RenderViewport::Instance(); + if (FocalTangent == 0 || viewport->FocalLengthY == 0) + { // If r_visibility is called before the renderer is all set up, don't + // divide by zero. This will be called again later, and the proper + // values can be initialized then. + return; + } + + BaseVisibility = vis; + + MaxVisForWall = (viewport->InvZtoScale * (SCREENWIDTH*r_Yaspect) / (viewwidth*SCREENHEIGHT * FocalTangent)); + MaxVisForWall = 32767.0 / MaxVisForWall; + MaxVisForFloor = 32767.0 / (viewheight >> 2) * viewport->FocalLengthY / 160; + + // Prevent overflow on walls + if (BaseVisibility < 0 && BaseVisibility < -MaxVisForWall) + WallVisibility = -MaxVisForWall; + else if (BaseVisibility > 0 && BaseVisibility > MaxVisForWall) + WallVisibility = MaxVisForWall; + else + WallVisibility = BaseVisibility; + + WallVisibility = (viewport->InvZtoScale * SCREENWIDTH*AspectBaseHeight(WidescreenRatio) / + (viewwidth*SCREENHEIGHT * 3)) * (WallVisibility * FocalTangent); + + // Prevent overflow on floors/ceilings. Note that the calculation of + // MaxVisForFloor means that planes less than two units from the player's + // view could still overflow, but there is no way to totally eliminate + // that while still using fixed point math. + if (BaseVisibility < 0 && BaseVisibility < -MaxVisForFloor) + FloorVisibility = -MaxVisForFloor; + else if (BaseVisibility > 0 && BaseVisibility > MaxVisForFloor) + FloorVisibility = MaxVisForFloor; + else + FloorVisibility = BaseVisibility; + + FloorVisibility = 160.0 * FloorVisibility / viewport->FocalLengthY; + + TiltVisibility = float(vis * FocalTangent * (16.f * 320.f) / viewwidth); + + NoLightFade = glset.nolightfade; + } + + fixed_t LightVisibility::LightLevelToShade(int lightlevel, bool foggy) + { + bool nolightfade = !foggy && (glset.nolightfade); + if (nolightfade) + { + return (MAX(255 - lightlevel, 0) * NUMCOLORMAPS) << (FRACBITS - 8); + } + else + { + // Convert a light level into an unbounded colormap index (shade). Result is + // fixed point. Why the +12? I wish I knew, but experimentation indicates it + // is necessary in order to best reproduce Doom's original lighting. + return (NUMCOLORMAPS * 2 * FRACUNIT) - ((lightlevel + 12) * (FRACUNIT*NUMCOLORMAPS / 128)); + } + } + + // Controls how quickly light ramps across a 1/z range. Set this, and it + // sets all the r_*Visibility variables (except r_SkyVisibilily, which is + // currently unused). + CCMD(r_visibility) + { + if (argv.argc() < 2) + { + Printf("Visibility is %g\n", LightVisibility::Instance()->GetVisibility()); + } + else if (!netgame) + { + LightVisibility::Instance()->SetVisibility(atof(argv[1])); + } + else + { + Printf("Visibility cannot be changed in net games.\n"); + } + } + + ///////////////////////////////////////////////////////////////////////// + + void ColormapLight::SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack) + { + if (fadeToBlack) + { + if (invertColormap) // Fade to white + { + basecolormap = GetSpecialLights(basecolormap->Color, MAKERGB(255, 255, 255), basecolormap->Desaturate); + invertColormap = false; + } + else // Fade to black + { + basecolormap = GetSpecialLights(basecolormap->Color, MAKERGB(0, 0, 0), basecolormap->Desaturate); + } + } + + if (invertColormap) + { + basecolormap = GetSpecialLights(basecolormap->Color, basecolormap->Fade.InverseColor(), basecolormap->Desaturate); + } + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedColormap()) + { + BaseColormap = cameraLight->FixedColormap(); + ColormapNum = 0; + } + else if (cameraLight->FixedLightLevel() >= 0) + { + BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap; + ColormapNum = cameraLight->FixedLightLevel() >> COLORMAPSHIFT; + } + else if (fullbright) + { + BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap; + ColormapNum = 0; + } + else + { + BaseColormap = basecolormap; + ColormapNum = GETPALOOKUP(visibility, shade); + } + } +} diff --git a/src/swrenderer/scene/r_light.h b/src/swrenderer/scene/r_light.h new file mode 100644 index 000000000..843faedd1 --- /dev/null +++ b/src/swrenderer/scene/r_light.h @@ -0,0 +1,113 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include "r_defs.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "r_utility.h" +#include "swrenderer/viewport/r_viewport.h" + +// Lighting. +// +// [RH] This has changed significantly from Doom, which used lookup +// tables based on 1/z for walls and z for flats and only recognized +// 16 discrete light levels. The terminology I use is borrowed from Build. + +// The size of a single colormap, in bits +#define COLORMAPSHIFT 8 + +// MAXLIGHTSCALE from original DOOM, divided by 2. +#define MAXLIGHTVIS (24.0) + +// Convert a shade and visibility to a clamped colormap index. +// Result is not fixed point. +// Change R_CalcTiltedLighting() when this changes. +#define GETPALOOKUP(vis,shade) (clamp (((shade)-FLOAT2FIXED(MIN(MAXLIGHTVIS,double(vis))))>>FRACBITS, 0, NUMCOLORMAPS-1)) + +// Calculate the light multiplier for dc_light/ds_light +// This is used instead of GETPALOOKUP when ds_colormap/dc_colormap is set to the base colormap +// Returns a value between 0 and 1 in fixed point +#define LIGHTSCALE(vis,shade) FLOAT2FIXED(clamp((FIXED2DBL(shade) - (MIN(MAXLIGHTVIS,double(vis)))) / NUMCOLORMAPS, 0.0, (NUMCOLORMAPS-1)/(double)NUMCOLORMAPS)) + +struct FSWColormap; + +namespace swrenderer +{ + class CameraLight + { + public: + static CameraLight *Instance(); + + int FixedLightLevel() const { return fixedlightlev; } + FSWColormap *FixedColormap() const { return fixedcolormap; } + FSpecialColormap *ShaderColormap() const { return realfixedcolormap; } + + fixed_t FixedLightLevelShade() const { return (FixedLightLevel() >> COLORMAPSHIFT) << FRACBITS; } + + void SetCamera(AActor *actor); + void ClearShaderColormap() { realfixedcolormap = nullptr; } + + private: + int fixedlightlev = 0; + FSWColormap *fixedcolormap = nullptr; + FSpecialColormap *realfixedcolormap = nullptr; + }; + + class LightVisibility + { + public: + static LightVisibility *Instance(); + + void SetVisibility(double visibility); + double GetVisibility() const { return CurrentVisibility; } + + double WallGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : WallVisibility; } + double SpriteGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : WallVisibility; } + double ParticleGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : (WallVisibility * 0.5); } + double FlatPlaneGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : FloorVisibility; } + double SlopePlaneGlobVis(bool foggy) const { return (NoLightFade && !foggy) ? 0.0f : TiltVisibility; } + + // The vis value to pass into the GETPALOOKUP or LIGHTSCALE macros + double WallVis(double screenZ, bool foggy) const { return WallGlobVis(foggy) / screenZ; } + double SpriteVis(double screenZ, bool foggy) const { return SpriteGlobVis(foggy) / screenZ; } + double ParticleVis(double screenZ, bool foggy) const { return ParticleGlobVis(foggy) / screenZ; } + double FlatPlaneVis(int screenY, double planeZ, bool foggy) const { return FlatPlaneGlobVis(foggy) / fabs(planeZ - ViewPos.Z) * fabs(RenderViewport::Instance()->CenterY - screenY); } + + static fixed_t LightLevelToShade(int lightlevel, bool foggy); + static int ActualExtraLight(bool fog) { return fog ? 0 : extralight << 4; } + + private: + double BaseVisibility = 0.0; + double WallVisibility = 0.0; + double FloorVisibility = 0.0; + float TiltVisibility = 0.0f; + + bool NoLightFade = false; + + double CurrentVisibility = 8.f; + double MaxVisForWall = 0.0; + double MaxVisForFloor = 0.0; + }; + + class ColormapLight + { + public: + int ColormapNum = 0; + FSWColormap *BaseColormap = nullptr; + + void SetColormap(double visibility, int shade, FDynamicColormap *basecolormap, bool fullbright, bool invertColormap, bool fadeToBlack); + }; +} diff --git a/src/swrenderer/scene/r_opaque_pass.cpp b/src/swrenderer/scene/r_opaque_pass.cpp new file mode 100644 index 000000000..7ad2c3a01 --- /dev/null +++ b/src/swrenderer/scene/r_opaque_pass.cpp @@ -0,0 +1,1051 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// BSP traversal, handling of LineSegs for rendering. +// +//----------------------------------------------------------------------------- + + +#include + +#include "templates.h" + +#include "doomdef.h" + +#include "m_bbox.h" + +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" + +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/things/r_wallsprite.h" +#include "swrenderer/things/r_voxel.h" +#include "swrenderer/things/r_particle.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_renderthread.h" +#include "r_3dfloors.h" +#include "r_portal.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "c_console.h" +#include "p_maputl.h" + +// State. +#include "doomstat.h" +#include "r_state.h" +#include "r_opaque_pass.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "g_levellocals.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); +EXTERN_CVAR(Bool, r_drawvoxels); + +namespace swrenderer +{ + RenderOpaquePass::RenderOpaquePass(RenderThread *thread) : renderline(thread) + { + Thread = thread; + } + + sector_t *RenderOpaquePass::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, seg_t *backline, int backx1, int backx2, double frontcz1, double frontcz2) + { + // If player's view height is underneath fake floor, lower the + // drawn ceiling to be just under the floor height, and replace + // the drawn floor and ceiling textures, and light level, with + // the control sector's. + // + // Similar for ceiling, only reflected. + + // [RH] allow per-plane lighting + if (floorlightlevel != nullptr) + { + *floorlightlevel = sec->GetFloorLight(); + } + + if (ceilinglightlevel != nullptr) + { + *ceilinglightlevel = sec->GetCeilingLight(); + } + + FakeSide = WaterFakeSide::Center; + + const sector_t *s = sec->GetHeightSec(); + if (s != nullptr) + { + sector_t *heightsec = viewsector->heightsec; + bool underwater = r_fakingunderwater || + (heightsec && heightsec->floorplane.PointOnSide(ViewPos) <= 0); + bool doorunderwater = false; + int diffTex = (s->MoreFlags & SECF_CLIPFAKEPLANES); + + // Replace sector being drawn with a copy to be hacked + *tempsec = *sec; + + // Replace floor and ceiling height with control sector's heights. + if (diffTex) + { + if (s->floorplane.CopyPlaneIfValid(&tempsec->floorplane, &sec->ceilingplane)) + { + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); + } + else if (s->MoreFlags & SECF_FAKEFLOORONLY) + { + if (underwater) + { + tempsec->ColorMap = s->ColorMap; + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != nullptr) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != nullptr) + { + *ceilinglightlevel = s->GetCeilingLight(); + } + } + FakeSide = WaterFakeSide::BelowFloor; + return tempsec; + } + return sec; + } + } + else + { + tempsec->floorplane = s->floorplane; + } + + if (!(s->MoreFlags & SECF_FAKEFLOORONLY)) + { + if (diffTex) + { + if (s->ceilingplane.CopyPlaneIfValid(&tempsec->ceilingplane, &sec->floorplane)) + { + tempsec->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false); + } + } + else + { + tempsec->ceilingplane = s->ceilingplane; + } + } + + double refceilz = s->ceilingplane.ZatPoint(ViewPos); + double orgceilz = sec->ceilingplane.ZatPoint(ViewPos); + +#if 1 + // [RH] Allow viewing underwater areas through doors/windows that + // are underwater but not in a water sector themselves. + // Only works if you cannot see the top surface of any deep water + // sectors at the same time. + if (backline && !r_fakingunderwater && backline->frontsector->heightsec == nullptr) + { + if (frontcz1 <= s->floorplane.ZatPoint(backline->v1) && + frontcz2 <= s->floorplane.ZatPoint(backline->v2)) + { + // Check that the window is actually visible + for (int z = backx1; z < backx2; ++z) + { + if (floorclip[z] > ceilingclip[z]) + { + doorunderwater = true; + r_fakingunderwater = true; + break; + } + } + } + } +#endif + + if (underwater || doorunderwater) + { + tempsec->floorplane = sec->floorplane; + tempsec->ceilingplane = s->floorplane; + tempsec->ceilingplane.FlipVert(); + tempsec->ceilingplane.ChangeHeight(-1 / 65536.); + tempsec->ColorMap = s->ColorMap; + } + + // killough 11/98: prevent sudden light changes from non-water sectors: + if ((underwater && !backline) || doorunderwater) + { // head-below-floor hack + tempsec->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; + + tempsec->ceilingplane = s->floorplane; + tempsec->ceilingplane.FlipVert(); + tempsec->ceilingplane.ChangeHeight(-1 / 65536.); + if (s->GetTexture(sector_t::ceiling) == skyflatnum) + { + tempsec->floorplane = tempsec->ceilingplane; + tempsec->floorplane.FlipVert(); + tempsec->floorplane.ChangeHeight(+1 / 65536.); + tempsec->SetTexture(sector_t::ceiling, tempsec->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform; + } + else + { + tempsec->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false); + tempsec->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform; + } + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != nullptr) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != nullptr) + { + *ceilinglightlevel = s->GetCeilingLight(); + } + } + FakeSide = WaterFakeSide::BelowFloor; + } + else if (heightsec && heightsec->ceilingplane.PointOnSide(ViewPos) <= 0 && + orgceilz > refceilz && !(s->MoreFlags & SECF_FAKEFLOORONLY)) + { // Above-ceiling hack + tempsec->ceilingplane = s->ceilingplane; + tempsec->floorplane = s->ceilingplane; + tempsec->floorplane.FlipVert(); + tempsec->floorplane.ChangeHeight(+1 / 65536.); + tempsec->ColorMap = s->ColorMap; + tempsec->ColorMap = s->ColorMap; + + tempsec->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false); + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false); + tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform; + + if (s->GetTexture(sector_t::floor) != skyflatnum) + { + tempsec->ceilingplane = sec->ceilingplane; + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; + } + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != nullptr) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != nullptr) + { + *ceilinglightlevel = s->GetCeilingLight(); + } + } + FakeSide = WaterFakeSide::AboveCeiling; + } + sec = tempsec; // Use other sector + } + return sec; + } + + + + // Checks BSP node/subtree bounding box. + // Returns true if some part of the bbox might be visible. + bool RenderOpaquePass::CheckBBox(float *bspcoord) + { + static const int checkcoord[12][4] = + { + { 3,0,2,1 }, + { 3,0,2,0 }, + { 3,1,2,0 }, + { 0 }, + { 2,0,2,1 }, + { 0,0,0,0 }, + { 3,1,3,0 }, + { 0 }, + { 2,0,3,1 }, + { 2,1,3,1 }, + { 2,1,3,0 } + }; + + int boxx; + int boxy; + int boxpos; + + double x1, y1, x2, y2; + double rx1, ry1, rx2, ry2; + int sx1, sx2; + + // Find the corners of the box + // that define the edges from current viewpoint. + if (ViewPos.X <= bspcoord[BOXLEFT]) + boxx = 0; + else if (ViewPos.X < bspcoord[BOXRIGHT]) + boxx = 1; + else + boxx = 2; + + if (ViewPos.Y >= bspcoord[BOXTOP]) + boxy = 0; + else if (ViewPos.Y > bspcoord[BOXBOTTOM]) + boxy = 1; + else + boxy = 2; + + boxpos = (boxy << 2) + boxx; + if (boxpos == 5) + return true; + + x1 = bspcoord[checkcoord[boxpos][0]] - ViewPos.X; + y1 = bspcoord[checkcoord[boxpos][1]] - ViewPos.Y; + x2 = bspcoord[checkcoord[boxpos][2]] - ViewPos.X; + y2 = bspcoord[checkcoord[boxpos][3]] - ViewPos.Y; + + // check clip list for an open space + + // Sitting on a line? + if (y1 * (x1 - x2) + x1 * (y2 - y1) >= -EQUAL_EPSILON) + return true; + + rx1 = x1 * ViewSin - y1 * ViewCos; + rx2 = x2 * ViewSin - y2 * ViewCos; + ry1 = x1 * ViewTanCos + y1 * ViewTanSin; + ry2 = x2 * ViewTanCos + y2 * ViewTanSin; + + if (Thread->Portal->MirrorFlags & RF_XFLIP) + { + double t = -rx1; + rx1 = -rx2; + rx2 = t; + swapvalues(ry1, ry2); + } + + auto viewport = RenderViewport::Instance(); + + if (rx1 >= -ry1) + { + if (rx1 > ry1) return false; // left edge is off the right side + if (ry1 == 0) return false; + sx1 = xs_RoundToInt(viewport->CenterX + rx1 * viewport->CenterX / ry1); + } + else + { + if (rx2 < -ry2) return false; // wall is off the left side + if (rx1 - rx2 - ry2 + ry1 == 0) return false; // wall does not intersect view volume + sx1 = 0; + } + + if (rx2 <= ry2) + { + if (rx2 < -ry2) return false; // right edge is off the left side + if (ry2 == 0) return false; + sx2 = xs_RoundToInt(viewport->CenterX + rx2 * viewport->CenterX / ry2); + } + else + { + if (rx1 > ry1) return false; // wall is off the right side + if (ry2 - ry1 - rx2 + rx1 == 0) return false; // wall does not intersect view volume + sx2 = viewwidth; + } + + // Find the first clippost that touches the source post + // (adjacent pixels are touching). + + return Thread->ClipSegments->IsVisible(sx1, sx2); + } + + void RenderOpaquePass::AddPolyobjs(subsector_t *sub) + { + if (sub->BSP == nullptr || sub->BSP->bDirty) + { + sub->BuildPolyBSP(); + } + if (sub->BSP->Nodes.Size() == 0) + { + RenderSubsector(&sub->BSP->Subsectors[0]); + } + else + { + RenderBSPNode(&sub->BSP->Nodes.Last()); + } + } + + // kg3D - add fake segs, never rendered + void RenderOpaquePass::FakeDrawLoop(subsector_t *sub, VisiblePlane *floorplane, VisiblePlane *ceilingplane, bool foggy, FDynamicColormap *basecolormap) + { + int count; + seg_t* line; + + count = sub->numlines; + line = sub->firstline; + + while (count--) + { + if ((line->sidedef) && !(line->sidedef->Flags & WALLF_POLYOBJ)) + { + renderline.Render(line, InSubsector, frontsector, nullptr, floorplane, ceilingplane, foggy, basecolormap); + } + line++; + } + } + + void RenderOpaquePass::RenderSubsector(subsector_t *sub) + { + // Determine floor/ceiling planes. + // Add sprites of things in sector. + // Draw one or more line segments. + + int count; + seg_t* line; + sector_t tempsec; // killough 3/7/98: deep water hack + int floorlightlevel; // killough 3/16/98: set floor lightlevel + int ceilinglightlevel; // killough 4/11/98 + bool outersubsector; + int fll, cll, position; + FSectorPortal *portal; + + // kg3D - fake floor stuff + VisiblePlane *backupfp; + VisiblePlane *backupcp; + //secplane_t templane; + lightlist_t *light; + + if (InSubsector != nullptr) + { // InSubsector is not nullptr. This means we are rendering from a mini-BSP. + outersubsector = false; + } + else + { + outersubsector = true; + InSubsector = sub; + } + +#ifdef RANGECHECK + if (outersubsector && sub - subsectors >= (ptrdiff_t)numsubsectors) + I_Error("RenderSubsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); +#endif + + assert(sub->sector != nullptr); + + if (sub->polys) + { // Render the polyobjs in the subsector first + AddPolyobjs(sub); + if (outersubsector) + { + InSubsector = nullptr; + } + return; + } + + frontsector = sub->sector; + frontsector->MoreFlags |= SECF_DRAWN; + count = sub->numlines; + line = sub->firstline; + + // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect + frontsector = FakeFlat(frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, nullptr, 0, 0, 0, 0); + + fll = floorlightlevel; + cll = ceilinglightlevel; + + // [RH] set foggy flag + bool foggy = level.fadeto || frontsector->ColorMap->Fade || (level.flags & LEVEL_HASFADETABLE); + + // kg3D - fake lights + CameraLight *cameraLight = CameraLight::Instance(); + FDynamicColormap *basecolormap; + if (cameraLight->FixedLightLevel() < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(frontsector, &frontsector->ceilingplane, false); + basecolormap = light->extra_colormap; + // If this is the real ceiling, don't discard plane lighting R_FakeFlat() + // accounted for. + if (light->p_lightlevel != &frontsector->lightlevel) + { + ceilinglightlevel = *light->p_lightlevel; + } + } + else + { + basecolormap = (r_fullbrightignoresectorcolor && cameraLight->FixedLightLevel() >= 0) ? &FullNormalLight : frontsector->ColorMap; + } + + portal = frontsector->ValidatePortal(sector_t::ceiling); + + VisiblePlane *ceilingplane = frontsector->ceilingplane.PointOnSide(ViewPos) > 0 || + frontsector->GetTexture(sector_t::ceiling) == skyflatnum || + portal != nullptr || + (frontsector->heightsec && + !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && + frontsector->heightsec->GetTexture(sector_t::floor) == skyflatnum) ? + Thread->PlaneList->FindPlane(frontsector->ceilingplane, // killough 3/8/98 + frontsector->GetTexture(sector_t::ceiling), + ceilinglightlevel + LightVisibility::ActualExtraLight(foggy), // killough 4/11/98 + frontsector->GetAlpha(sector_t::ceiling), + !!(frontsector->GetFlags(sector_t::ceiling) & PLANEF_ADDITIVE), + frontsector->planes[sector_t::ceiling].xform, + frontsector->sky, + portal, + basecolormap + ) : nullptr; + + if (ceilingplane) + ceilingplane->AddLights(Thread, frontsector->lighthead); + + if (cameraLight->FixedLightLevel() < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(frontsector, &frontsector->floorplane, false); + basecolormap = light->extra_colormap; + // If this is the real floor, don't discard plane lighting R_FakeFlat() + // accounted for. + if (light->p_lightlevel != &frontsector->lightlevel) + { + floorlightlevel = *light->p_lightlevel; + } + } + else + { + basecolormap = (r_fullbrightignoresectorcolor && cameraLight->FixedLightLevel() >= 0) ? &FullNormalLight : frontsector->ColorMap; + } + + // killough 3/7/98: Add (x,y) offsets to flats, add deep water check + // killough 3/16/98: add floorlightlevel + // killough 10/98: add support for skies transferred from sidedefs + portal = frontsector->ValidatePortal(sector_t::floor); + + VisiblePlane *floorplane = frontsector->floorplane.PointOnSide(ViewPos) > 0 || // killough 3/7/98 + frontsector->GetTexture(sector_t::floor) == skyflatnum || + portal != nullptr || + (frontsector->heightsec && + !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && + frontsector->heightsec->GetTexture(sector_t::ceiling) == skyflatnum) ? + Thread->PlaneList->FindPlane(frontsector->floorplane, + frontsector->GetTexture(sector_t::floor), + floorlightlevel + LightVisibility::ActualExtraLight(foggy), // killough 3/16/98 + frontsector->GetAlpha(sector_t::floor), + !!(frontsector->GetFlags(sector_t::floor) & PLANEF_ADDITIVE), + frontsector->planes[sector_t::floor].xform, + frontsector->sky, + portal, + basecolormap + ) : nullptr; + + if (floorplane) + floorplane->AddLights(Thread, frontsector->lighthead); + + // kg3D - fake planes rendering + if (r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) + { + backupfp = floorplane; + backupcp = ceilingplane; + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + + // first check all floors + for (int i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) + { + clip3d->SetFakeFloor(frontsector->e->XFloor.ffloors[i]); + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_EXISTS)) continue; + if (!clip3d->fakeFloor->fakeFloor->model) continue; + if (clip3d->fakeFloor->fakeFloor->bottom.plane->isSlope()) continue; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_NOSHADE) || (clip3d->fakeFloor->fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + { + clip3d->AddHeight(clip3d->fakeFloor->fakeFloor->top.plane, frontsector); + } + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_RENDERPLANES)) continue; + if (clip3d->fakeFloor->fakeFloor->alpha == 0) continue; + if (clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE && clip3d->fakeFloor->fakeFloor->flags & FF_INVERTSECTOR) continue; + clip3d->fakeAlpha = MIN(Scale(clip3d->fakeFloor->fakeFloor->alpha, OPAQUE, 255), OPAQUE); + if (clip3d->fakeFloor->validcount != validcount) + { + clip3d->fakeFloor->validcount = validcount; + clip3d->NewClip(); + } + double fakeHeight = clip3d->fakeFloor->fakeFloor->top.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight < ViewPos.Z && + fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)) + { + clip3d->fake3D = FAKE3D_FAKEFLOOR; + tempsec = *clip3d->fakeFloor->fakeFloor->model; + tempsec.floorplane = *clip3d->fakeFloor->fakeFloor->top.plane; + tempsec.ceilingplane = *clip3d->fakeFloor->fakeFloor->bottom.plane; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE) && !(clip3d->fakeFloor->fakeFloor->flags & FF_INVERTSECTOR)) + { + tempsec.SetTexture(sector_t::floor, tempsec.GetTexture(sector_t::ceiling)); + position = sector_t::ceiling; + } + else position = sector_t::floor; + frontsector = &tempsec; + + if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(sub->sector, &frontsector->floorplane, false); + basecolormap = light->extra_colormap; + floorlightlevel = *light->p_lightlevel; + } + + ceilingplane = nullptr; + floorplane = Thread->PlaneList->FindPlane(frontsector->floorplane, + frontsector->GetTexture(sector_t::floor), + floorlightlevel + LightVisibility::ActualExtraLight(foggy), // killough 3/16/98 + frontsector->GetAlpha(sector_t::floor), + !!(clip3d->fakeFloor->fakeFloor->flags & FF_ADDITIVETRANS), + frontsector->planes[position].xform, + frontsector->sky, + nullptr, + basecolormap); + + if (floorplane) + floorplane->AddLights(Thread, frontsector->lighthead); + + FakeDrawLoop(sub, floorplane, ceilingplane, foggy, basecolormap); + clip3d->fake3D = 0; + frontsector = sub->sector; + } + } + // and now ceilings + for (unsigned int i = 0; i < frontsector->e->XFloor.ffloors.Size(); i++) + { + clip3d->SetFakeFloor(frontsector->e->XFloor.ffloors[i]); + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_EXISTS)) continue; + if (!clip3d->fakeFloor->fakeFloor->model) continue; + if (clip3d->fakeFloor->fakeFloor->top.plane->isSlope()) continue; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_NOSHADE) || (clip3d->fakeFloor->fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + { + clip3d->AddHeight(clip3d->fakeFloor->fakeFloor->bottom.plane, frontsector); + } + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_RENDERPLANES)) continue; + if (clip3d->fakeFloor->fakeFloor->alpha == 0) continue; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE) && (clip3d->fakeFloor->fakeFloor->flags & (FF_SWIMMABLE | FF_INVERTSECTOR)) == (FF_SWIMMABLE | FF_INVERTSECTOR)) continue; + clip3d->fakeAlpha = MIN(Scale(clip3d->fakeFloor->fakeFloor->alpha, OPAQUE, 255), OPAQUE); + + if (clip3d->fakeFloor->validcount != validcount) + { + clip3d->fakeFloor->validcount = validcount; + clip3d->NewClip(); + } + double fakeHeight = clip3d->fakeFloor->fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight > ViewPos.Z && + fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)) + { + clip3d->fake3D = FAKE3D_FAKECEILING; + tempsec = *clip3d->fakeFloor->fakeFloor->model; + tempsec.floorplane = *clip3d->fakeFloor->fakeFloor->top.plane; + tempsec.ceilingplane = *clip3d->fakeFloor->fakeFloor->bottom.plane; + if ((!(clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE) && !(clip3d->fakeFloor->fakeFloor->flags & FF_INVERTSECTOR)) || + (clip3d->fakeFloor->fakeFloor->flags & FF_THISINSIDE && clip3d->fakeFloor->fakeFloor->flags & FF_INVERTSECTOR)) + { + tempsec.SetTexture(sector_t::ceiling, tempsec.GetTexture(sector_t::floor)); + position = sector_t::floor; + } + else position = sector_t::ceiling; + frontsector = &tempsec; + + tempsec.ceilingplane.ChangeHeight(-1 / 65536.); + if (cameraLight->FixedLightLevel() < 0 && sub->sector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(sub->sector, &frontsector->ceilingplane, false); + basecolormap = light->extra_colormap; + ceilinglightlevel = *light->p_lightlevel; + } + tempsec.ceilingplane.ChangeHeight(1 / 65536.); + + floorplane = nullptr; + ceilingplane = Thread->PlaneList->FindPlane(frontsector->ceilingplane, // killough 3/8/98 + frontsector->GetTexture(sector_t::ceiling), + ceilinglightlevel + LightVisibility::ActualExtraLight(foggy), // killough 4/11/98 + frontsector->GetAlpha(sector_t::ceiling), + !!(clip3d->fakeFloor->fakeFloor->flags & FF_ADDITIVETRANS), + frontsector->planes[position].xform, + frontsector->sky, + nullptr, + basecolormap); + + if (ceilingplane) + ceilingplane->AddLights(Thread, frontsector->lighthead); + + FakeDrawLoop(sub, floorplane, ceilingplane, foggy, basecolormap); + clip3d->fake3D = 0; + frontsector = sub->sector; + } + } + clip3d->fakeFloor = nullptr; + floorplane = backupfp; + ceilingplane = backupcp; + } + + basecolormap = frontsector->ColorMap; + floorlightlevel = fll; + ceilinglightlevel = cll; + + // killough 9/18/98: Fix underwater slowdown, by passing real sector + // instead of fake one. Improve sprite lighting by basing sprite + // lightlevels on floor & ceiling lightlevels in the surrounding area. + // [RH] Handle sprite lighting like Duke 3D: If the ceiling is a sky, sprites are lit by + // it, otherwise they are lit by the floor. + AddSprites(sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ? ceilinglightlevel : floorlightlevel, FakeSide, foggy, basecolormap); + + // [RH] Add particles + if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors) + { // Only do it for the main BSP. + int shade = LightVisibility::LightLevelToShade((floorlightlevel + ceilinglightlevel) / 2 + LightVisibility::ActualExtraLight(foggy), foggy); + for (WORD i = ParticlesInSubsec[(unsigned int)(sub - subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) + { + RenderParticle::Project(Thread, Particles + i, subsectors[sub - subsectors].sector, shade, FakeSide, foggy); + } + } + + count = sub->numlines; + line = sub->firstline; + + while (count--) + { + if (!outersubsector || line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) + { + // kg3D - fake planes bounding calculation + if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) + { + backupfp = floorplane; + backupcp = ceilingplane; + floorplane = nullptr; + ceilingplane = nullptr; + Clip3DFloors *clip3d = Thread->Clip3D.get(); + for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) + { + clip3d->SetFakeFloor(line->backsector->e->XFloor.ffloors[i]); + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_EXISTS)) continue; + if (!(clip3d->fakeFloor->fakeFloor->flags & FF_RENDERPLANES)) continue; + if (!clip3d->fakeFloor->fakeFloor->model) continue; + clip3d->fake3D = FAKE3D_FAKEBACK; + tempsec = *clip3d->fakeFloor->fakeFloor->model; + tempsec.floorplane = *clip3d->fakeFloor->fakeFloor->top.plane; + tempsec.ceilingplane = *clip3d->fakeFloor->fakeFloor->bottom.plane; + if (clip3d->fakeFloor->validcount != validcount) + { + clip3d->fakeFloor->validcount = validcount; + clip3d->NewClip(); + } + renderline.Render(line, InSubsector, frontsector, &tempsec, floorplane, ceilingplane, foggy, basecolormap); // fake + } + clip3d->fakeFloor = nullptr; + clip3d->fake3D = 0; + floorplane = backupfp; + ceilingplane = backupcp; + } + renderline.Render(line, InSubsector, frontsector, nullptr, floorplane, ceilingplane, foggy, basecolormap); // now real + } + line++; + } + if (outersubsector) + { + InSubsector = nullptr; + } + } + + void RenderOpaquePass::RenderScene() + { + SeenSpriteSectors.clear(); + SeenActors.clear(); + + InSubsector = nullptr; + RenderBSPNode(nodes + numnodes - 1); // The head node is the last node output. + } + + // + // RenderBSPNode + // Renders all subsectors below a given node, traversing subtree recursively. + // Just call with BSP root and -1. + // killough 5/2/98: reformatted, removed tail recursion + + void RenderOpaquePass::RenderBSPNode(void *node) + { + if (numnodes == 0) + { + RenderSubsector(subsectors); + return; + } + while (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; + + // Decide which side the view point is on. + int side = R_PointOnSide(ViewPos, bsp); + + // Recursively divide front space (toward the viewer). + RenderBSPNode(bsp->children[side]); + + // Possibly divide back space (away from the viewer). + side ^= 1; + if (!CheckBBox(bsp->bbox[side])) + return; + + node = bsp->children[side]; + } + RenderSubsector((subsector_t *)((BYTE *)node - 1)); + } + + void RenderOpaquePass::ClearClip() + { + auto viewport = RenderViewport::Instance(); + // clip ceiling to console bottom + fillshort(floorclip, viewwidth, viewheight); + fillshort(ceilingclip, viewwidth, !screen->Accel2D && ConBottom > viewwindowy && !viewport->RenderingToCanvas() ? (ConBottom - viewwindowy) : 0); + } + + void RenderOpaquePass::AddSprites(sector_t *sec, int lightlevel, WaterFakeSide fakeside, bool foggy, FDynamicColormap *basecolormap) + { + // BSP is traversed by subsector. + // A sector might have been split into several + // subsectors during BSP building. + // Thus we check whether it was already added. + if (sec->touching_renderthings == nullptr || SeenSpriteSectors.find(sec) != SeenSpriteSectors.end()/*|| sec->validcount == validcount*/) + return; + + // Well, now it will be done. + //sec->validcount = validcount; + SeenSpriteSectors.insert(sec); + + int spriteshade = LightVisibility::LightLevelToShade(lightlevel + LightVisibility::ActualExtraLight(foggy), foggy); + + // Handle all things in sector. + for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext) + { + auto thing = p->m_thing; + if (SeenActors.find(thing) != SeenActors.end()) continue; + SeenActors.insert(thing); + //if (thing->validcount == validcount) continue; + //thing->validcount = validcount; + + FIntCVar *cvar = thing->GetClass()->distancecheck; + if (cvar != nullptr && *cvar >= 0) + { + double dist = (thing->Pos() - ViewPos).LengthSquared(); + double check = (double)**cvar; + if (dist >= check * check) + { + continue; + } + } + + // find fake level + F3DFloor *fakeceiling = nullptr; + F3DFloor *fakefloor = nullptr; + for (auto rover : thing->Sector->e->XFloor.ffloors) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES)) continue; + if (!(rover->flags & FF_SOLID) || rover->alpha != 255) continue; + if (!fakefloor) + { + if (!rover->top.plane->isSlope()) + { + if (rover->top.plane->ZatPoint(0., 0.) <= thing->Z()) fakefloor = rover; + } + } + if (!rover->bottom.plane->isSlope()) + { + if (rover->bottom.plane->ZatPoint(0., 0.) >= thing->Top()) fakeceiling = rover; + } + } + + if (IsPotentiallyVisible(thing)) + { + ThingSprite sprite; + if (GetThingSprite(thing, sprite)) + { + FDynamicColormap *thingColormap = basecolormap; + int thingShade = spriteshade; + if (sec->sectornum != thing->Sector->sectornum) // compare sectornums to account for R_FakeFlat copies. + { + int lightlevel = thing->Sector->GetTexture(sector_t::ceiling) == skyflatnum ? thing->Sector->GetCeilingLight() : thing->Sector->GetFloorLight(); + thingShade = LightVisibility::LightLevelToShade(lightlevel + LightVisibility::ActualExtraLight(foggy), foggy); + thingColormap = thing->Sector->ColorMap; + } + + if ((sprite.renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) + { + RenderWallSprite::Project(Thread, thing, sprite.pos, sprite.picnum, sprite.spriteScale, sprite.renderflags, thingShade, foggy, thingColormap); + } + else if (sprite.voxel) + { + RenderVoxel::Project(Thread, thing, sprite.pos, sprite.voxel, sprite.spriteScale, sprite.renderflags, fakeside, fakefloor, fakeceiling, sec, thingShade, foggy, thingColormap); + } + else + { + RenderSprite::Project(Thread, thing, sprite.pos, sprite.tex, sprite.spriteScale, sprite.renderflags, fakeside, fakefloor, fakeceiling, sec, thingShade, foggy, thingColormap); + } + } + } + } + } + + bool RenderOpaquePass::IsPotentiallyVisible(AActor *thing) + { + // Don't waste time projecting sprites that are definitely not visible. + if (thing == nullptr || + (thing->renderflags & RF_INVISIBLE) || + !thing->RenderStyle.IsVisible(thing->Alpha) || + !thing->IsVisibleToPlayer() || + !thing->IsInsideVisibleAngles()) + { + return false; + } + + // [ZZ] Or less definitely not visible (hue) + // [ZZ] 10.01.2016: don't try to clip stuff inside a skybox against the current portal. + RenderPortal *renderportal = Thread->Portal.get(); + if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && !!P_PointOnLineSidePrecise(thing->Pos(), renderportal->CurrentPortal->dst)) + return false; + + return true; + } + + bool RenderOpaquePass::GetThingSprite(AActor *thing, ThingSprite &sprite) + { + sprite.pos = thing->InterpolatedPosition(r_TicFracF); + sprite.pos.Z += thing->GetBobOffset(r_TicFracF); + + sprite.spritenum = thing->sprite; + sprite.tex = nullptr; + sprite.voxel = nullptr; + sprite.spriteScale = thing->Scale; + sprite.renderflags = thing->renderflags; + + if (thing->player != nullptr) + { + P_CheckPlayerSprite(thing, sprite.spritenum, sprite.spriteScale); + } + + if (thing->picnum.isValid()) + { + sprite.picnum = thing->picnum; + + sprite.tex = TexMan(sprite.picnum); + if (sprite.tex->UseType == FTexture::TEX_Null) + { + return false; + } + + if (sprite.tex->Rotations != 0xFFFF) + { + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprite.tex->Rotations]; + DAngle ang = (sprite.pos - ViewPos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + sprite.picnum = sprframe->Texture[rot]; + if (sprframe->Flip & (1 << rot)) + { + sprite.renderflags ^= RF_XFLIP; + } + sprite.tex = TexMan[sprite.picnum]; // Do not animate the rotation + } + } + else + { + // decide which texture to use for the sprite + if ((unsigned)sprite.spritenum >= sprites.Size()) + { + DPrintf(DMSG_ERROR, "R_ProjectSprite: invalid sprite number %u\n", sprite.spritenum); + return false; + } + spritedef_t *sprdef = &sprites[sprite.spritenum]; + if (thing->frame >= sprdef->numframes) + { + // If there are no frames at all for this sprite, don't draw it. + return false; + } + else + { + //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; + DAngle ang = (sprite.pos - ViewPos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + sprite.picnum = sprframe->Texture[rot]; + if (sprframe->Flip & (1 << rot)) + { + sprite.renderflags ^= RF_XFLIP; + } + sprite.tex = TexMan[sprite.picnum]; // Do not animate the rotation + if (r_drawvoxels) + { + sprite.voxel = sprframe->Voxel; + } + } + + if (sprite.voxel == nullptr && (sprite.tex == nullptr || sprite.tex->UseType == FTexture::TEX_Null)) + { + return false; + } + + if (sprite.spriteScale.Y < 0) + { + sprite.spriteScale.Y = -sprite.spriteScale.Y; + sprite.renderflags ^= RF_YFLIP; + } + if (sprite.spriteScale.X < 0) + { + sprite.spriteScale.X = -sprite.spriteScale.X; + sprite.renderflags ^= RF_XFLIP; + } + } + + return true; + } +} diff --git a/src/swrenderer/scene/r_opaque_pass.h b/src/swrenderer/scene/r_opaque_pass.h new file mode 100644 index 000000000..6a14703c5 --- /dev/null +++ b/src/swrenderer/scene/r_opaque_pass.h @@ -0,0 +1,93 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "tarray.h" +#include +#include "r_defs.h" +#include "swrenderer/line/r_line.h" +#include "swrenderer/scene/r_3dfloors.h" +#include + +struct FVoxelDef; + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlane; + + // The 3072 below is just an arbitrary value picked to avoid + // drawing lines the player is too close to that would overflow + // the texture calculations. + #define TOO_CLOSE_Z (3072.0 / (1<<12)) + + enum class WaterFakeSide + { + Center, + BelowFloor, + AboveCeiling + }; + + struct ThingSprite + { + DVector3 pos; + int spritenum; + FTexture *tex; + FVoxelDef *voxel; + FTextureID picnum; + DVector2 spriteScale; + int renderflags; + }; + + class RenderOpaquePass + { + public: + RenderOpaquePass(RenderThread *thread); + + void ClearClip(); + void RenderScene(); + + void ResetFakingUnderwater() { r_fakingunderwater = false; } + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, seg_t *backline, int backx1, int backx2, double frontcz1, double frontcz2); + + void ClearSeenSprites() { SeenSpriteSectors.clear(); SeenActors.clear(); } + + short floorclip[MAXWIDTH]; + short ceilingclip[MAXWIDTH]; + + RenderThread *Thread = nullptr; + + private: + void RenderBSPNode(void *node); + void RenderSubsector(subsector_t *sub); + + bool CheckBBox(float *bspcoord); + void AddPolyobjs(subsector_t *sub); + void FakeDrawLoop(subsector_t *sub, VisiblePlane *floorplane, VisiblePlane *ceilingplane, bool foggy, FDynamicColormap *basecolormap); + + void AddSprites(sector_t *sec, int lightlevel, WaterFakeSide fakeside, bool foggy, FDynamicColormap *basecolormap); + + bool IsPotentiallyVisible(AActor *thing); + static bool GetThingSprite(AActor *thing, ThingSprite &sprite); + + subsector_t *InSubsector = nullptr; + sector_t *frontsector = nullptr; + WaterFakeSide FakeSide = WaterFakeSide::Center; + bool r_fakingunderwater = false; + + SWRenderLine renderline; + std::set SeenSpriteSectors; + std::set SeenActors; + }; +} diff --git a/src/swrenderer/scene/r_portal.cpp b/src/swrenderer/scene/r_portal.cpp new file mode 100644 index 000000000..4139e7fe3 --- /dev/null +++ b/src/swrenderer/scene/r_portal.cpp @@ -0,0 +1,544 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "doomdef.h" +#include "d_net.h" +#include "doomstat.h" +#include "m_random.h" +#include "m_bbox.h" +#include "r_portal.h" +#include "r_sky.h" +#include "st_stuff.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "v_video.h" +#include "stats.h" +#include "i_video.h" +#include "i_system.h" +#include "a_sharedglobal.h" +#include "r_data/r_translate.h" +#include "p_3dmidtex.h" +#include "r_data/r_interpolate.h" +#include "v_palette.h" +#include "po_man.h" +#include "p_effect.h" +#include "st_start.h" +#include "v_font.h" +#include "r_data/colormaps.h" +#include "p_maputl.h" +#include "p_setup.h" +#include "version.h" +#include "r_utility.h" +#include "r_3dfloors.h" +#include "g_levellocals.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +CVAR(Int, r_portal_recursions, 4, CVAR_ARCHIVE) +CVAR(Bool, r_highlight_portals, false, CVAR_ARCHIVE) +CVAR(Bool, r_skyboxes, true, 0) + +// Avoid infinite recursion with stacked sectors by limiting them. +#define MAX_SKYBOX_PLANES 1000 + +namespace swrenderer +{ + RenderPortal::RenderPortal(RenderThread *thread) + { + Thread = thread; + } + + // Draws any recorded sky boxes and then frees them. + // + // The process: + // 1. Move the camera to coincide with the SkyViewpoint. + // 2. Clear out the old planes. (They have already been drawn.) + // 3. Clear a window out of the ClipSegs just large enough for the plane. + // 4. Pretend the existing vissprites and drawsegs aren't there. + // 5. Create a drawseg at 0 distance to clip sprites to the visplane. It + // doesn't need to be associated with a line in the map, since there + // will never be any sprites in front of it. + // 6. Render the BSP, then planes, then masked stuff. + // 7. Restore the previous vissprites and drawsegs. + // 8. Repeat for any other sky boxes. + // 9. Put the camera back where it was to begin with. + // + void RenderPortal::RenderPlanePortals() + { + numskyboxes = 0; + + VisiblePlaneList *planes = Thread->PlaneList.get(); + DrawSegmentList *drawseglist = Thread->DrawSegments.get(); + + if (!planes->HasPortalPlanes()) + return; + + Thread->Clip3D->EnterSkybox(); + CurrentPortalInSkybox = true; + + int savedextralight = extralight; + DVector3 savedpos = ViewPos; + DAngle savedangle = ViewAngle; + double savedvisibility = LightVisibility::Instance()->GetVisibility(); + AActor *savedcamera = camera; + sector_t *savedsector = viewsector; + + for (VisiblePlane *pl = planes->PopFirstPortalPlane(); pl != nullptr; pl = planes->PopFirstPortalPlane()) + { + if (pl->right < pl->left || !r_skyboxes || numskyboxes == MAX_SKYBOX_PLANES || pl->portal == nullptr) + { + pl->Render(Thread, OPAQUE, false, false); + continue; + } + + numskyboxes++; + + FSectorPortal *port = pl->portal; + switch (port->mType) + { + case PORTS_SKYVIEWPOINT: + { + // Don't let gun flashes brighten the sky box + AActor *sky = port->mSkybox; + extralight = 0; + LightVisibility::Instance()->SetVisibility(sky->args[0] * 0.25f); + + ViewPos = sky->InterpolatedPosition(r_TicFracF); + ViewAngle = savedangle + (sky->PrevAngles.Yaw + deltaangle(sky->PrevAngles.Yaw, sky->Angles.Yaw) * r_TicFracF); + + CopyStackedViewParameters(); + break; + } + + case PORTS_STACKEDSECTORTHING: + case PORTS_PORTAL: + case PORTS_LINKEDPORTAL: + extralight = pl->extralight; + LightVisibility::Instance()->SetVisibility(pl->visibility); + ViewPos.X = pl->viewpos.X + port->mDisplacement.X; + ViewPos.Y = pl->viewpos.Y + port->mDisplacement.Y; + ViewPos.Z = pl->viewpos.Z; + ViewAngle = pl->viewangle; + break; + + case PORTS_HORIZON: + case PORTS_PLANE: + // not implemented yet + + default: + pl->Render(Thread, OPAQUE, false, false); + numskyboxes--; + continue; + } + + port->mFlags |= PORTSF_INSKYBOX; + if (port->mPartner > 0) level.sectorPortals[port->mPartner].mFlags |= PORTSF_INSKYBOX; + camera = nullptr; + viewsector = port->mDestination; + assert(viewsector != nullptr); + R_SetViewAngle(); + Thread->OpaquePass->ClearSeenSprites(); + Thread->Clip3D->ClearFakeFloors(); + + planes->ClearKeepFakePlanes(); + Thread->ClipSegments->Clear(pl->left, pl->right); + WindowLeft = pl->left; + WindowRight = pl->right; + + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + for (int i = pl->left; i < pl->right; i++) + { + if (pl->top[i] == 0x7fff) + { + ceilingclip[i] = viewheight; + floorclip[i] = -1; + } + else + { + ceilingclip[i] = pl->top[i]; + floorclip[i] = pl->bottom[i]; + } + } + + drawseglist->PushPortal(); + Thread->SpriteList->PushPortal(); + viewposStack.Push(ViewPos); + visplaneStack.Push(pl); + + // Create a drawseg to clip sprites to the sky plane + DrawSegment *draw_segment = Thread->FrameMemory->NewObject(); + draw_segment->CurrentPortalUniq = CurrentPortalUniq; + draw_segment->siz1 = INT_MAX; + draw_segment->siz2 = INT_MAX; + draw_segment->sz1 = 0; + draw_segment->sz2 = 0; + draw_segment->x1 = pl->left; + draw_segment->x2 = pl->right; + draw_segment->silhouette = SIL_BOTH; + draw_segment->sprbottomclip = Thread->FrameMemory->AllocMemory(pl->right - pl->left); + draw_segment->sprtopclip = Thread->FrameMemory->AllocMemory(pl->right - pl->left); + draw_segment->maskedtexturecol = nullptr; + draw_segment->swall = nullptr; + draw_segment->bFogBoundary = false; + draw_segment->curline = nullptr; + draw_segment->fake = 0; + draw_segment->foggy = false; + memcpy(draw_segment->sprbottomclip, floorclip + pl->left, (pl->right - pl->left) * sizeof(short)); + memcpy(draw_segment->sprtopclip, ceilingclip + pl->left, (pl->right - pl->left) * sizeof(short)); + drawseglist->Push(draw_segment); + + Thread->OpaquePass->RenderScene(); + Thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) + planes->Render(); + + port->mFlags &= ~PORTSF_INSKYBOX; + if (port->mPartner > 0) level.sectorPortals[port->mPartner].mFlags &= ~PORTSF_INSKYBOX; + } + + // Draw all the masked textures in a second pass, in the reverse order they + // were added. This must be done separately from the previous step for the + // sake of nested skyboxes. + while (viewposStack.Size() > 0) + { + // Masked textures and planes need the view coordinates restored for proper positioning. + viewposStack.Pop(ViewPos); + + Thread->TranslucentPass->Render(); + + VisiblePlane *pl; + visplaneStack.Pop(pl); + if (pl->Alpha > 0 && pl->picnum != skyflatnum) + { + pl->Render(Thread, pl->Alpha, pl->Additive, true); + } + + Thread->SpriteList->PopPortal(); + drawseglist->PopPortal(); + } + + camera = savedcamera; + viewsector = savedsector; + ViewPos = savedpos; + LightVisibility::Instance()->SetVisibility(savedvisibility); + extralight = savedextralight; + ViewAngle = savedangle; + R_SetViewAngle(); + + CurrentPortalInSkybox = false; + Thread->Clip3D->LeaveSkybox(); + + if (Thread->Clip3D->fakeActive) return; + + planes->ClearPortalPlanes(); + } + + void RenderPortal::RenderLinePortals() + { + // [RH] Walk through mirrors + // [ZZ] Merged with portals + size_t lastportal = WallPortals.Size(); + for (unsigned int i = 0; i < lastportal; i++) + { + RenderLinePortal(WallPortals[i], 0); + } + + CurrentPortal = nullptr; + CurrentPortalUniq = 0; + } + + void RenderPortal::RenderLinePortal(PortalDrawseg* pds, int depth) + { + auto viewport = RenderViewport::Instance(); + + // [ZZ] check depth. fill portal with black if it's exceeding the visual recursion limit, and continue like nothing happened. + if (depth >= r_portal_recursions) + { + BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 0, 0, 0, 0, 255); + int spacing = viewport->RenderTarget->GetPitch(); + for (int x = pds->x1; x < pds->x2; x++) + { + if (x < 0 || x >= viewport->RenderTarget->GetWidth()) + continue; + + int Ytop = pds->ceilingclip[x - pds->x1]; + int Ybottom = pds->floorclip[x - pds->x1]; + + if (viewport->RenderTarget->IsBgra()) + { + uint32_t *dest = (uint32_t*)viewport->RenderTarget->GetBuffer() + x + Ytop * spacing; + + uint32_t c = GPalette.BaseColors[color].d; + for (int y = Ytop; y <= Ybottom; y++) + { + *dest = c; + dest += spacing; + } + } + else + { + BYTE *dest = viewport->RenderTarget->GetBuffer() + x + Ytop * spacing; + + for (int y = Ytop; y <= Ybottom; y++) + { + *dest = color; + dest += spacing; + } + } + } + + if (r_highlight_portals) + RenderLinePortalHighlight(pds); + + return; + } + + DAngle startang = ViewAngle; + DVector3 startpos = ViewPos; + DVector3 savedpath[2] = { ViewPath[0], ViewPath[1] }; + ActorRenderFlags savedvisibility = camera ? camera->renderflags & RF_INVISIBLE : ActorRenderFlags::FromInt(0); + + camera->renderflags &= ~RF_INVISIBLE; + + CurrentPortalUniq++; + + unsigned int portalsAtStart = WallPortals.Size(); + + if (pds->mirror) + { + //vertex_t *v1 = ds->curline->v1; + vertex_t *v1 = pds->src->v1; + + // Reflect the current view behind the mirror. + if (pds->src->Delta().X == 0) + { // vertical mirror + ViewPos.X = v1->fX() - startpos.X + v1->fX(); + } + else if (pds->src->Delta().Y == 0) + { // horizontal mirror + ViewPos.Y = v1->fY() - startpos.Y + v1->fY(); + } + else + { // any mirror + vertex_t *v2 = pds->src->v2; + + double dx = v2->fX() - v1->fX(); + double dy = v2->fY() - v1->fY(); + double x1 = v1->fX(); + double y1 = v1->fY(); + double x = startpos.X; + double y = startpos.Y; + + // the above two cases catch len == 0 + double r = ((x - x1)*dx + (y - y1)*dy) / (dx*dx + dy*dy); + + ViewPos.X = (x1 + r * dx) * 2 - x; + ViewPos.Y = (y1 + r * dy) * 2 - y; + } + ViewAngle = pds->src->Delta().Angle() * 2 - startang; + } + else + { + P_TranslatePortalXY(pds->src, ViewPos.X, ViewPos.Y); + P_TranslatePortalZ(pds->src, ViewPos.Z); + P_TranslatePortalAngle(pds->src, ViewAngle); + P_TranslatePortalXY(pds->src, ViewPath[0].X, ViewPath[0].Y); + P_TranslatePortalXY(pds->src, ViewPath[1].X, ViewPath[1].Y); + + if (!r_showviewer && camera && P_PointOnLineSidePrecise(ViewPath[0], pds->dst) != P_PointOnLineSidePrecise(ViewPath[1], pds->dst)) + { + double distp = (ViewPath[0] - ViewPath[1]).Length(); + if (distp > EQUAL_EPSILON) + { + double dist1 = (ViewPos - ViewPath[0]).Length(); + double dist2 = (ViewPos - ViewPath[1]).Length(); + + if (dist1 + dist2 < distp + 1) + { + camera->renderflags |= RF_INVISIBLE; + } + } + } + } + + ViewSin = ViewAngle.Sin(); + ViewCos = ViewAngle.Cos(); + + ViewTanSin = FocalTangent * ViewSin; + ViewTanCos = FocalTangent * ViewCos; + + CopyStackedViewParameters(); + + validcount++; + PortalDrawseg* prevpds = CurrentPortal; + CurrentPortal = pds; + + Thread->PlaneList->ClearKeepFakePlanes(); + Thread->ClipSegments->Clear(pds->x1, pds->x2); + + WindowLeft = pds->x1; + WindowRight = pds->x2; + + // RF_XFLIP should be removed before calling the root function + int prevmf = MirrorFlags; + if (pds->mirror) + { + if (MirrorFlags & RF_XFLIP) + MirrorFlags &= ~RF_XFLIP; + else MirrorFlags |= RF_XFLIP; + } + + // some portals have height differences, account for this here + Thread->Clip3D->EnterSkybox(); // push 3D floor height map + CurrentPortalInSkybox = false; // first portal in a skybox should set this variable to false for proper clipping in skyboxes. + + // first pass, set clipping + auto ceilingclip = Thread->OpaquePass->ceilingclip; + auto floorclip = Thread->OpaquePass->floorclip; + memcpy(ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len * sizeof(*ceilingclip)); + memcpy(floorclip + pds->x1, &pds->floorclip[0], pds->len * sizeof(*floorclip)); + + Thread->OpaquePass->RenderScene(); + Thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) + if (!savedvisibility && camera) camera->renderflags &= ~RF_INVISIBLE; + + PlaneCycles.Clock(); + Thread->PlaneList->Render(); + RenderPlanePortals(); + PlaneCycles.Unclock(); + + double vzp = ViewPos.Z; + + int prevuniq = CurrentPortalUniq; + // depth check is in another place right now + unsigned int portalsAtEnd = WallPortals.Size(); + for (; portalsAtStart < portalsAtEnd; portalsAtStart++) + { + RenderLinePortal(WallPortals[portalsAtStart], depth + 1); + } + int prevuniq2 = CurrentPortalUniq; + CurrentPortalUniq = prevuniq; + + if (Thread->MainThread) + NetUpdate(); + + MaskedCycles.Clock(); // [ZZ] count sprites in portals/mirrors along with normal ones. + Thread->TranslucentPass->Render(); // this is required since with portals there often will be cases when more than 80% of the view is inside a portal. + MaskedCycles.Unclock(); + + if (Thread->MainThread) + NetUpdate(); + + Thread->Clip3D->LeaveSkybox(); // pop 3D floor height map + CurrentPortalUniq = prevuniq2; + + // draw a red line around a portal if it's being highlighted + if (r_highlight_portals) + RenderLinePortalHighlight(pds); + + CurrentPortal = prevpds; + MirrorFlags = prevmf; + ViewAngle = startang; + ViewPos = startpos; + ViewPath[0] = savedpath[0]; + ViewPath[1] = savedpath[1]; + } + + void RenderPortal::RenderLinePortalHighlight(PortalDrawseg* pds) + { + // [ZZ] NO OVERFLOW CHECKS HERE + // I believe it won't break. if it does, blame me. :( + + auto viewport = RenderViewport::Instance(); + + if (viewport->RenderTarget->IsBgra()) // Assuming this is just a debug function + return; + + BYTE color = (BYTE)BestColor((DWORD *)GPalette.BaseColors, 255, 0, 0, 0, 255); + + BYTE* pixels = viewport->RenderTarget->GetBuffer(); + // top edge + for (int x = pds->x1; x < pds->x2; x++) + { + if (x < 0 || x >= viewport->RenderTarget->GetWidth()) + continue; + + int p = x - pds->x1; + int Ytop = pds->ceilingclip[p]; + int Ybottom = pds->floorclip[p]; + + if (x == pds->x1 || x == pds->x2 - 1) + { + viewport->RenderTarget->DrawLine(x, Ytop, x, Ybottom + 1, color, 0); + continue; + } + + int YtopPrev = pds->ceilingclip[p - 1]; + int YbottomPrev = pds->floorclip[p - 1]; + + if (abs(Ytop - YtopPrev) > 1) + viewport->RenderTarget->DrawLine(x, YtopPrev, x, Ytop, color, 0); + else *(pixels + Ytop * viewport->RenderTarget->GetPitch() + x) = color; + + if (abs(Ybottom - YbottomPrev) > 1) + viewport->RenderTarget->DrawLine(x, YbottomPrev, x, Ybottom, color, 0); + else *(pixels + Ybottom * viewport->RenderTarget->GetPitch() + x) = color; + } + } + + void RenderPortal::CopyStackedViewParameters() + { + stacked_viewpos = ViewPos; + stacked_angle = ViewAngle; + stacked_extralight = extralight; + stacked_visibility = LightVisibility::Instance()->GetVisibility(); + } + + void RenderPortal::SetMainPortal() + { + WindowLeft = 0; + WindowRight = viewwidth; + MirrorFlags = 0; + CurrentPortal = nullptr; + CurrentPortalUniq = 0; + WallPortals.Clear(); + } + + void RenderPortal::AddLinePortal(line_t *linedef, int x1, int x2, const short *topclip, const short *bottomclip) + { + WallPortals.Push(Thread->FrameMemory->NewObject(Thread, linedef, x1, x2, topclip, bottomclip)); + } +} +/* +ADD_STAT(skyboxes) +{ + FString out; + out.Format("%d skybox planes", swrenderer::RenderPortal::Instance()->numskyboxes); + return out; +} +*/ diff --git a/src/swrenderer/scene/r_portal.h b/src/swrenderer/scene/r_portal.h new file mode 100644 index 000000000..0d65bfa74 --- /dev/null +++ b/src/swrenderer/scene/r_portal.h @@ -0,0 +1,65 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/segments/r_portalsegment.h" + +namespace swrenderer +{ + class RenderThread; + struct VisiblePlane; + + class RenderPortal + { + public: + RenderPortal(RenderThread *thread); + + void SetMainPortal(); + void CopyStackedViewParameters(); + + void RenderPlanePortals(); + void RenderLinePortals(); + + void AddLinePortal(line_t *linedef, int x1, int x2, const short *topclip, const short *bottomclip); + + RenderThread *Thread = nullptr; + + int WindowLeft = 0; + int WindowRight = 0; + uint16_t MirrorFlags = 0; + + PortalDrawseg* CurrentPortal = nullptr; + int CurrentPortalUniq = 0; + bool CurrentPortalInSkybox = false; + + // These are copies of the main parameters used when drawing stacked sectors. + // When you change the main parameters, you should copy them here too *unless* + // you are changing them to draw a stacked sector. Otherwise, stacked sectors + // won't draw in skyboxes properly. + int stacked_extralight = 0; + double stacked_visibility = 0.0; + DVector3 stacked_viewpos; + DAngle stacked_angle; + + int numskyboxes = 0; // For ADD_STAT(skyboxes) + + private: + void RenderLinePortal(PortalDrawseg* pds, int depth); + void RenderLinePortalHighlight(PortalDrawseg* pds); + + TArray viewposStack; + TArray visplaneStack; + TArray WallPortals; + }; +} diff --git a/src/swrenderer/scene/r_scene.cpp b/src/swrenderer/scene/r_scene.cpp new file mode 100644 index 000000000..f7b8c9c88 --- /dev/null +++ b/src/swrenderer/scene/r_scene.cpp @@ -0,0 +1,444 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "c_dispatch.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "p_effect.h" +#include "po_man.h" +#include "st_stuff.h" +#include "r_data/r_interpolate.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/segments/r_clipsegment.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/segments/r_portalsegment.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_thread.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "swrenderer/things/r_playersprite.h" + +EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Int, r_clearbuffer) + +CVAR(Bool, r_scene_multithreaded, false, 0); + +namespace swrenderer +{ + cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; + + RenderScene::RenderScene() + { + Threads.push_back(std::make_unique(this)); + } + + RenderScene::~RenderScene() + { + StopThreads(); + } + + void RenderScene::SetClearColor(int color) + { + clearcolor = color; + } + + void RenderScene::RenderView(player_t *player) + { + auto viewport = RenderViewport::Instance(); + viewport->RenderTarget = screen; + + int width = SCREENWIDTH; + int height = SCREENHEIGHT; + float trueratio; + ActiveRatio(width, height, &trueratio); + viewport->SetViewport(width, height, trueratio); + + if (r_clearbuffer != 0) + { + if (!viewport->RenderTarget->IsBgra()) + { + memset(viewport->RenderTarget->GetBuffer(), clearcolor, viewport->RenderTarget->GetPitch() * viewport->RenderTarget->GetHeight()); + } + else + { + uint32_t bgracolor = GPalette.BaseColors[clearcolor].d; + int size = viewport->RenderTarget->GetPitch() * viewport->RenderTarget->GetHeight(); + uint32_t *dest = (uint32_t *)viewport->RenderTarget->GetBuffer(); + for (int i = 0; i < size; i++) + dest[i] = bgracolor; + } + } + + RenderActorView(player->mo); + + // Apply special colormap if the target cannot do it + if (CameraLight::Instance()->ShaderColormap() && viewport->RenderTarget->IsBgra() && !(r_shadercolormaps && screen->Accel2D)) + { + MainThread()->DrawQueue->Push(CameraLight::Instance()->ShaderColormap(), screen); + RenderDrawQueues(); + } + } + + void RenderScene::RenderDrawQueues() + { + // Use reverse order so main thread is drawn last + std::vector queues; + for (auto it = Threads.rbegin(); it != Threads.rend(); ++it) + { + queues.push_back((*it)->DrawQueue); + } + DrawerThreads::Execute(queues); + + //using namespace std::chrono_literals; + //std::this_thread::sleep_for(0.5s); + } + + void RenderScene::RenderActorView(AActor *actor, bool dontmaplines) + { + WallCycles.Reset(); + PlaneCycles.Reset(); + MaskedCycles.Reset(); + WallScanCycles.Reset(); + + R_SetupFrame(actor); + CameraLight::Instance()->SetCamera(actor); + RenderViewport::Instance()->SetupFreelook(); + + NetUpdate(); + + this->dontmaplines = dontmaplines; + + // [RH] Setup particles for this frame + P_FindParticleSubsectors(); + + // Link the polyobjects right before drawing the scene to reduce the amounts of calls to this function + PO_LinkToSubsectors(); + + ActorRenderFlags savedflags = camera->renderflags; + // Never draw the player unless in chasecam mode + if (!r_showviewer) + { + camera->renderflags |= RF_INVISIBLE; + } + + RenderThreadSlices(); + MainThread()->PlayerSprites->Render(); + RenderDrawQueues(); + + camera->renderflags = savedflags; + interpolator.RestoreInterpolations(); + + // If we don't want shadered colormaps, NULL it now so that the + // copy to the screen does not use a special colormap shader. + if (!r_shadercolormaps && !RenderViewport::Instance()->RenderTarget->IsBgra()) + { + CameraLight::Instance()->ClearShaderColormap(); + } + } + + void RenderScene::RenderThreadSlices() + { + int numThreads = std::thread::hardware_concurrency(); + if (numThreads == 0) + numThreads = 4; + + if (!r_scene_multithreaded) + numThreads = 1; + + if (numThreads != (int)Threads.size()) + { + StopThreads(); + StartThreads(numThreads); + } + + // Setup threads: + std::unique_lock start_lock(start_mutex); + for (int i = 0; i < numThreads; i++) + { + Threads[i]->X1 = viewwidth * i / numThreads; + Threads[i]->X2 = viewwidth * (i + 1) / numThreads; + } + run_id++; + start_lock.unlock(); + + // Notify threads to run + if (Threads.size() > 1) + { + start_condition.notify_all(); + } + + // Do the main thread ourselves: + RenderThreadSlice(MainThread()); + + // Wait for everyone to finish: + if (Threads.size() > 1) + { + std::unique_lock end_lock(end_mutex); + finished_threads++; + end_condition.wait(end_lock, [&]() { return finished_threads == Threads.size(); }); + finished_threads = 0; + } + } + + void RenderScene::RenderThreadSlice(RenderThread *thread) + { + thread->FrameMemory->Clear(); + thread->Clip3D->Cleanup(); + thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) + thread->Portal->CopyStackedViewParameters(); + thread->ClipSegments->Clear(0, viewwidth); + thread->DrawSegments->Clear(); + thread->PlaneList->Clear(); + thread->TranslucentPass->Clear(); + thread->OpaquePass->ClearClip(); + thread->OpaquePass->ResetFakingUnderwater(); // [RH] Hack to make windows into underwater areas possible + thread->Portal->SetMainPortal(); + + // Cull things outside the range seen by this thread + VisibleSegmentRenderer visitor; + if (thread->X1 > 0) + thread->ClipSegments->Clip(0, thread->X1, true, &visitor); + if (thread->X2 < viewwidth) + thread->ClipSegments->Clip(thread->X2, viewwidth, true, &visitor); + + if (thread->MainThread) + WallCycles.Clock(); + + thread->OpaquePass->RenderScene(); + thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) + + if (thread == MainThread()) + WallCycles.Unclock(); + + if (thread->MainThread) + NetUpdate(); + + if (viewactive) + { + if (thread->MainThread) + PlaneCycles.Clock(); + + thread->PlaneList->Render(); + thread->Portal->RenderPlanePortals(); + + if (thread->MainThread) + PlaneCycles.Unclock(); + + thread->Portal->RenderLinePortals(); + + if (thread->MainThread) + NetUpdate(); + + if (thread->MainThread) + MaskedCycles.Clock(); + + thread->TranslucentPass->Render(); + + if (thread->MainThread) + MaskedCycles.Unclock(); + + if (thread->MainThread) + NetUpdate(); + } + } + + void RenderScene::StartThreads(size_t numThreads) + { + while (Threads.size() < (size_t)numThreads) + { + auto thread = std::make_unique(this, false); + auto renderthread = thread.get(); + int start_run_id = run_id; + thread->thread = std::thread([=]() + { + int last_run_id = start_run_id; + while (true) + { + // Wait until we are signalled to run: + std::unique_lock start_lock(start_mutex); + start_condition.wait(start_lock, [&]() { return run_id != last_run_id || shutdown_flag; }); + if (shutdown_flag) + break; + last_run_id = run_id; + start_lock.unlock(); + + RenderThreadSlice(renderthread); + + // Notify main thread that we finished: + std::unique_lock end_lock(end_mutex); + finished_threads++; + end_lock.unlock(); + end_condition.notify_all(); + } + }); + Threads.push_back(std::move(thread)); + } + } + + void RenderScene::StopThreads() + { + std::unique_lock lock(start_mutex); + shutdown_flag = true; + lock.unlock(); + start_condition.notify_all(); + while (Threads.size() > 1) + { + Threads.back()->thread.join(); + Threads.pop_back(); + } + lock.lock(); + shutdown_flag = false; + } + + void RenderScene::RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines) + { + auto viewport = RenderViewport::Instance(); + + const bool savedviewactive = viewactive; + + viewwidth = width; + viewport->RenderTarget = canvas; + + R_SetWindow(12, width, height, height, true); + viewwindowx = x; + viewwindowy = y; + viewactive = true; + viewport->SetViewport(width, height, WidescreenRatio); + + RenderActorView(actor, dontmaplines); + + viewport->RenderTarget = screen; + + R_ExecuteSetViewSize(); + float trueratio; + ActiveRatio(width, height, &trueratio); + screen->Lock(true); + viewport->SetViewport(width, height, trueratio); + screen->Unlock(); + + viewactive = savedviewactive; + } + + void RenderScene::ScreenResized() + { + auto viewport = RenderViewport::Instance(); + viewport->RenderTarget = screen; + int width = SCREENWIDTH; + int height = SCREENHEIGHT; + float trueratio; + ActiveRatio(width, height, &trueratio); + screen->Lock(true); + viewport->SetViewport(SCREENWIDTH, SCREENHEIGHT, trueratio); + screen->Unlock(); + } + + void RenderScene::Init() + { + // viewwidth / viewheight are set by the defaults + fillshort(zeroarray, MAXWIDTH, 0); + + R_InitShadeMaps(); + } + + void RenderScene::Deinit() + { + MainThread()->TranslucentPass->Deinit(); + MainThread()->Clip3D->Cleanup(); + } + + ///////////////////////////////////////////////////////////////////////// + + ADD_STAT(fps) + { + FString out; + out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms", + FrameCycles.TimeMS(), WallCycles.TimeMS(), PlaneCycles.TimeMS(), MaskedCycles.TimeMS()); + return out; + } + + static double f_acc, w_acc, p_acc, m_acc; + static int acc_c; + + ADD_STAT(fps_accumulated) + { + f_acc += FrameCycles.TimeMS(); + w_acc += WallCycles.TimeMS(); + p_acc += PlaneCycles.TimeMS(); + m_acc += MaskedCycles.TimeMS(); + acc_c++; + FString out; + out.Format("frame=%04.1f ms walls=%04.1f ms planes=%04.1f ms masked=%04.1f ms %d counts", + f_acc / acc_c, w_acc / acc_c, p_acc / acc_c, m_acc / acc_c, acc_c); + Printf(PRINT_LOG, "%s\n", out.GetChars()); + return out; + } + + static double bestwallcycles = HUGE_VAL; + + ADD_STAT(wallcycles) + { + FString out; + double cycles = WallCycles.Time(); + if (cycles && cycles < bestwallcycles) + bestwallcycles = cycles; + out.Format("%g", bestwallcycles); + return out; + } + + CCMD(clearwallcycles) + { + bestwallcycles = HUGE_VAL; + } + +#if 0 + // The replacement code for Build's wallscan doesn't have any timing calls so this does not work anymore. + static double bestscancycles = HUGE_VAL; + + ADD_STAT(scancycles) + { + FString out; + double scancycles = WallScanCycles.Time(); + if (scancycles && scancycles < bestscancycles) + bestscancycles = scancycles; + out.Format("%g", bestscancycles); + return out; + } + + CCMD(clearscancycles) + { + bestscancycles = HUGE_VAL; + } +#endif +} diff --git a/src/swrenderer/scene/r_scene.h b/src/swrenderer/scene/r_scene.h new file mode 100644 index 000000000..d3680d422 --- /dev/null +++ b/src/swrenderer/scene/r_scene.h @@ -0,0 +1,73 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "r_defs.h" +#include "d_player.h" + +extern cycle_t FrameCycles; + +namespace swrenderer +{ + extern cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; + + class RenderThread; + + class RenderScene + { + public: + RenderScene(); + ~RenderScene(); + + void Init(); + void ScreenResized(); + void Deinit(); + + void SetClearColor(int color); + + void RenderView(player_t *player); + void RenderViewToCanvas(AActor *actor, DCanvas *canvas, int x, int y, int width, int height, bool dontmaplines = false); + + bool DontMapLines() const { return dontmaplines; } + + RenderThread *MainThread() { return Threads.front().get(); } + + private: + void RenderActorView(AActor *actor, bool dontmaplines = false); + void RenderDrawQueues(); + void RenderThreadSlices(); + void RenderThreadSlice(RenderThread *thread); + + void StartThreads(size_t numThreads); + void StopThreads(); + + bool dontmaplines = false; + int clearcolor = 0; + + std::vector> Threads; + std::mutex start_mutex; + std::condition_variable start_condition; + bool shutdown_flag = false; + int run_id = 0; + std::mutex end_mutex; + std::condition_variable end_condition; + size_t finished_threads = 0; + }; +} diff --git a/src/swrenderer/scene/r_translucent_pass.cpp b/src/swrenderer/scene/r_translucent_pass.cpp new file mode 100644 index 000000000..2e360a5e6 --- /dev/null +++ b/src/swrenderer/scene/r_translucent_pass.cpp @@ -0,0 +1,217 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "g_levellocals.h" +#include "p_maputl.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/things/r_visiblespritelist.h" +#include "swrenderer/things/r_voxel.h" +#include "swrenderer/things/r_particle.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/things/r_wallsprite.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/plane/r_visibleplanelist.h" +#include "swrenderer/line/r_renderdrawsegment.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Int, r_drawfuzz) +EXTERN_CVAR(Bool, r_drawvoxels) +EXTERN_CVAR(Bool, r_blendmethod) + +CVAR(Bool, r_fullbrightignoresectorcolor, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); + +namespace swrenderer +{ + RenderTranslucentPass::RenderTranslucentPass(RenderThread *thread) + { + Thread = thread; + } + + void RenderTranslucentPass::Deinit() + { + RenderVoxel::Deinit(); + } + + void RenderTranslucentPass::Clear() + { + Thread->SpriteList->Clear(); + } + + void RenderTranslucentPass::CollectPortals() + { + // This function collects all drawsegs that may be of interest to R_ClipSpriteColumnWithPortals + // Having that function over the entire list of drawsegs can break down performance quite drastically. + // This is doing the costly stuff only once so that R_ClipSpriteColumnWithPortals can + // a) exit early if no relevant info is found and + // b) skip most of the collected drawsegs which have no portal attached. + portaldrawsegs.Clear(); + DrawSegmentList *drawseglist = Thread->DrawSegments.get(); + for (unsigned int index = 0; index != drawseglist->SegmentsCount(); index++) + { + DrawSegment *seg = drawseglist->Segment(index); + + // I don't know what makes this happen (some old top-down portal code or possibly skybox code? something adds null lines...) + // crashes at the first frame of the first map of Action2.wad + if (!seg->curline) continue; + + line_t* line = seg->curline->linedef; + // ignore minisegs from GL nodes. + if (!line) continue; + + // check if this line will clip sprites to itself + if (!line->isVisualPortal() && line->special != Line_Mirror) + continue; + + // don't clip sprites with portal's back side (it's transparent) + if (seg->curline->sidedef != line->sidedef[0]) + continue; + + portaldrawsegs.Push(seg); + } + } + + bool RenderTranslucentPass::ClipSpriteColumnWithPortals(int x, VisibleSprite *spr) + { + RenderPortal *renderportal = Thread->Portal.get(); + + // [ZZ] 10.01.2016: don't clip sprites from the root of a skybox. + if (renderportal->CurrentPortalInSkybox) + return false; + + for (DrawSegment *seg : portaldrawsegs) + { + // ignore segs from other portals + if (seg->CurrentPortalUniq != renderportal->CurrentPortalUniq) + continue; + + // (all checks that are already done in R_CollectPortals have been removed for performance reasons.) + + // don't clip if the sprite is in front of the portal + if (!P_PointOnLineSidePrecise(spr->WorldPos().X, spr->WorldPos().Y, seg->curline->linedef)) + continue; + + // now if current column is covered by this drawseg, we clip it away + if ((x >= seg->x1) && (x < seg->x2)) + return true; + } + + return false; + } + + void RenderTranslucentPass::DrawMaskedSingle(bool renew) + { + RenderPortal *renderportal = Thread->Portal.get(); + + auto &sortedSprites = Thread->SpriteList->SortedSprites; + for (int i = sortedSprites.Size(); i > 0; i--) + { + if (sortedSprites[i - 1]->IsCurrentPortalUniq(renderportal->CurrentPortalUniq)) + { + sortedSprites[i - 1]->Render(Thread); + } + } + + // render any remaining masked mid textures + + if (renew) + { + Thread->Clip3D->fake3D |= FAKE3D_REFRESHCLIP; + } + + DrawSegmentList *drawseglist = Thread->DrawSegments.get(); + for (unsigned int index = 0; index != drawseglist->SegmentsCount(); index++) + { + DrawSegment *ds = drawseglist->Segment(index); + + // [ZZ] the same as above + if (ds->CurrentPortalUniq != renderportal->CurrentPortalUniq) + continue; + // kg3D - no fake segs + if (ds->fake) continue; + if (ds->maskedtexturecol != nullptr || ds->bFogBoundary) + { + RenderDrawSegment renderer(Thread); + renderer.Render(ds, ds->x1, ds->x2); + } + } + } + + void RenderTranslucentPass::Render() + { + CollectPortals(); + Thread->SpriteList->Sort(); + Thread->DrawSegments->BuildSegmentGroups(); + + Clip3DFloors *clip3d = Thread->Clip3D.get(); + if (clip3d->height_top == nullptr) + { // kg3D - no visible 3D floors, normal rendering + DrawMaskedSingle(false); + } + else + { // kg3D - correct sorting + // ceilings + for (HeightLevel *hl = clip3d->height_cur; hl != nullptr && hl->height >= ViewPos.Z; hl = hl->prev) + { + if (hl->next) + { + clip3d->fake3D = FAKE3D_CLIPBOTTOM | FAKE3D_CLIPTOP; + clip3d->sclipTop = hl->next->height; + } + else + { + clip3d->fake3D = FAKE3D_CLIPBOTTOM; + } + clip3d->sclipBottom = hl->height; + DrawMaskedSingle(true); + Thread->PlaneList->RenderHeight(hl->height); + } + + // floors + clip3d->fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPTOP; + clip3d->sclipTop = clip3d->height_top->height; + DrawMaskedSingle(true); + for (HeightLevel *hl = clip3d->height_top; hl != nullptr && hl->height < ViewPos.Z; hl = hl->next) + { + Thread->PlaneList->RenderHeight(hl->height); + if (hl->next) + { + clip3d->fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPTOP | FAKE3D_CLIPBOTTOM; + clip3d->sclipTop = hl->next->height; + } + else + { + clip3d->fake3D = FAKE3D_DOWN2UP | FAKE3D_CLIPBOTTOM; + } + clip3d->sclipBottom = hl->height; + DrawMaskedSingle(true); + } + clip3d->DeleteHeights(); + clip3d->fake3D = 0; + } + } +} diff --git a/src/swrenderer/scene/r_translucent_pass.h b/src/swrenderer/scene/r_translucent_pass.h new file mode 100644 index 000000000..44551b286 --- /dev/null +++ b/src/swrenderer/scene/r_translucent_pass.h @@ -0,0 +1,48 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "tarray.h" + +#define MINZ double((2048*4) / double(1 << 20)) + +struct particle_t; +struct FVoxel; + +namespace swrenderer +{ + class RenderThread; + class VisibleSprite; + struct DrawSegment; + + class RenderTranslucentPass + { + public: + RenderTranslucentPass(RenderThread *thread); + + void Deinit(); + void Clear(); + void Render(); + + bool ClipSpriteColumnWithPortals(int x, VisibleSprite *spr); + + RenderThread *Thread = nullptr; + + private: + void CollectPortals(); + void DrawMaskedSingle(bool renew); + + TArray portaldrawsegs; + }; +} diff --git a/src/swrenderer/segments/r_clipsegment.cpp b/src/swrenderer/segments/r_clipsegment.cpp new file mode 100644 index 000000000..4e25daa35 --- /dev/null +++ b/src/swrenderer/segments/r_clipsegment.cpp @@ -0,0 +1,186 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "swrenderer/segments/r_clipsegment.h" + +namespace swrenderer +{ + void RenderClipSegment::Clear(short left, short right) + { + solidsegs[0].first = -0x7fff; + solidsegs[0].last = left; + solidsegs[1].first = right; + solidsegs[1].last = 0x7fff; + newend = solidsegs+2; + } + + bool RenderClipSegment::Check(int first, int last) + { + cliprange_t *start; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = solidsegs; + while (start->last < first) + start++; + + if (first < start->first) + { + return true; + } + + // Bottom contained in start? + if (last > start->last) + { + return true; + } + + return false; + } + + bool RenderClipSegment::IsVisible(int sx1, int sx2) + { + // Does not cross a pixel. + if (sx2 <= sx1) + return false; + + cliprange_t *start = solidsegs; + while (start->last < sx2) + start++; + + if (sx1 >= start->first && sx2 <= start->last) + { + // The clippost contains the new span. + return false; + } + + return true; + } + + bool RenderClipSegment::Clip(int first, int last, bool solid, VisibleSegmentRenderer *visitor) + { + cliprange_t *next, *start; + int i, j; + bool res = false; + + // Find the first range that touches the range + // (adjacent pixels are touching). + start = solidsegs; + while (start->last < first) + start++; + + if (first < start->first) + { + res = true; + if (last <= start->first) + { + // Post is entirely visible (above start). + if (!visitor->RenderWallSegment(first, last)) + return true; + + // Insert a new clippost for solid walls. + if (solid) + { + if (last == start->first) + { + start->first = first; + } + else + { + next = newend; + newend++; + while (next != start) + { + *next = *(next - 1); + next--; + } + next->first = first; + next->last = last; + } + } + return true; + } + + // There is a fragment above *start. + if (visitor->RenderWallSegment(first, start->first) && solid) + { + start->first = first; // Adjust the clip size for solid walls + } + } + + // Bottom contained in start? + if (last <= start->last) + return res; + + bool clipsegment; + next = start; + while (last >= (next + 1)->first) + { + // There is a fragment between two posts. + clipsegment = visitor->RenderWallSegment(next->last, (next + 1)->first); + next++; + + if (last <= next->last) + { + // Bottom is contained in next. + last = next->last; + goto crunch; + } + } + + // There is a fragment after *next. + clipsegment = visitor->RenderWallSegment(next->last, last); + + crunch: + if (!clipsegment) + { + return true; + } + if (solid) + { + // Adjust the clip size. + start->last = last; + + if (next != start) + { + // Remove start+1 to next from the clip list, + // because start now covers their area. + for (i = 1, j = (int)(newend - next); j > 0; i++, j--) + { + start[i] = next[i]; + } + newend = start + i; + } + } + return true; + } +} diff --git a/src/swrenderer/segments/r_clipsegment.h b/src/swrenderer/segments/r_clipsegment.h new file mode 100644 index 000000000..48a083ba6 --- /dev/null +++ b/src/swrenderer/segments/r_clipsegment.h @@ -0,0 +1,44 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +namespace swrenderer +{ + typedef bool(*VisibleSegmentCallback)(int x1, int x2); + + class VisibleSegmentRenderer + { + public: + virtual ~VisibleSegmentRenderer() { } + virtual bool RenderWallSegment(int x1, int x2) { return true; } + }; + + class RenderClipSegment + { + public: + void Clear(short left, short right); + bool Clip(int x1, int x2, bool solid, VisibleSegmentRenderer *visitor); + bool Check(int first, int last); + bool IsVisible(int x1, int x2); + + private: + struct cliprange_t + { + short first, last; + }; + + cliprange_t *newend; // newend is one past the last valid seg + cliprange_t solidsegs[MAXWIDTH / 2 + 2]; + }; +} diff --git a/src/swrenderer/segments/r_drawsegment.cpp b/src/swrenderer/segments/r_drawsegment.cpp new file mode 100644 index 000000000..e4bc3d26b --- /dev/null +++ b/src/swrenderer/segments/r_drawsegment.cpp @@ -0,0 +1,166 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "d_net.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/line/r_fogboundary.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + DrawSegmentList::DrawSegmentList(RenderThread *thread) + { + Thread = thread; + } + + void DrawSegmentList::Clear() + { + Segments.Clear(); + StartIndices.Clear(); + StartIndices.Push(0); + + InterestingSegments.Clear(); + StartInterestingIndices.Clear(); + StartInterestingIndices.Push(0); + } + + void DrawSegmentList::PushPortal() + { + StartIndices.Push(Segments.Size()); + StartInterestingIndices.Push(InterestingSegments.Size()); + } + + void DrawSegmentList::PopPortal() + { + Segments.Resize(StartIndices.Last()); + StartIndices.Pop(); + + InterestingSegments.Resize(StartInterestingIndices.Last()); + StartInterestingIndices.Pop(); + } + + void DrawSegmentList::Push(DrawSegment *segment) + { + Segments.Push(segment); + } + + void DrawSegmentList::PushInteresting(DrawSegment *segment) + { + InterestingSegments.Push(segment); + } + + void DrawSegmentList::BuildSegmentGroups() + { + SegmentGroups.Clear(); + + unsigned int groupSize = 100; + for (unsigned int index = 0; index < SegmentsCount(); index += groupSize) + { + auto ds = Segment(index); + + DrawSegmentGroup group; + group.BeginIndex = index; + group.EndIndex = MIN(index + groupSize, SegmentsCount()); + group.x1 = ds->x1; + group.x2 = ds->x2; + group.neardepth = MIN(ds->sz1, ds->sz2); + group.fardepth = MAX(ds->sz1, ds->sz2); + + for (unsigned int groupIndex = group.BeginIndex + 1; groupIndex < group.EndIndex; groupIndex++) + { + ds = Segment(groupIndex); + group.x1 = MIN(group.x1, ds->x1); + group.x2 = MAX(group.x2, ds->x2); + group.neardepth = MIN(group.neardepth, ds->sz1); + group.neardepth = MIN(group.neardepth, ds->sz2); + group.fardepth = MAX(ds->sz1, group.fardepth); + group.fardepth = MAX(ds->sz2, group.fardepth); + } + + for (int x = group.x1; x < group.x2; x++) + { + cliptop[x] = 0; + clipbottom[x] = viewheight; + } + + for (unsigned int groupIndex = group.BeginIndex; groupIndex < group.EndIndex; groupIndex++) + { + ds = Segment(groupIndex); + + // kg3D - no clipping on fake segs + if (ds->fake) continue; + + if (ds->silhouette & SIL_BOTTOM) + { + short *clip1 = clipbottom + ds->x1; + const short *clip2 = ds->sprbottomclip; + int i = ds->x2 - ds->x1; + do + { + if (*clip1 > *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + + if (ds->silhouette & SIL_TOP) + { + short *clip1 = cliptop + ds->x1; + const short *clip2 = ds->sprtopclip; + int i = ds->x2 - ds->x1; + do + { + if (*clip1 < *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + } + + group.sprtopclip = Thread->FrameMemory->AllocMemory(group.x2 - group.x1); + group.sprbottomclip = Thread->FrameMemory->AllocMemory(group.x2 - group.x1); + memcpy(group.sprtopclip, cliptop + group.x1, (group.x2 - group.x1) * sizeof(short)); + memcpy(group.sprbottomclip, clipbottom + group.x1, (group.x2 - group.x1) * sizeof(short)); + + SegmentGroups.Push(group); + } + } +} diff --git a/src/swrenderer/segments/r_drawsegment.h b/src/swrenderer/segments/r_drawsegment.h new file mode 100644 index 000000000..9bfd5dc65 --- /dev/null +++ b/src/swrenderer/segments/r_drawsegment.h @@ -0,0 +1,94 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/line/r_line.h" + +namespace swrenderer +{ + struct DrawSegment + { + seg_t *curline; + float light, lightstep; + float iscale, iscalestep; + short x1, x2; // Same as sx1 and sx2, but clipped to the drawseg + short sx1, sx2; // left, right of parent seg on screen + float sz1, sz2; // z for left, right of parent seg on screen + float siz1, siz2; // 1/z for left, right of parent seg on screen + float cx, cy, cdx, cdy; + float yscale; + uint8_t silhouette; // 0=none, 1=bottom, 2=top, 3=both + uint8_t bFogBoundary; + uint8_t bFakeBoundary; // for fake walls + int shade; + bool foggy; + + // Pointers to lists for sprite clipping, all three adjusted so [x1] is first value. + short *sprtopclip; + short *sprbottomclip; + fixed_t *maskedtexturecol; + float *swall; + short *bkup; // sprtopclip backup, for mid and fake textures + + FWallTmapVals tmapvals; + + int fake; // ident fake drawseg, don't draw and clip sprites backups + int CurrentPortalUniq; // [ZZ] to identify the portal that this drawseg is in. used for sprite clipping. + }; + + struct DrawSegmentGroup + { + short x1, x2; + float neardepth, fardepth; + short *sprtopclip; + short *sprbottomclip; + unsigned int BeginIndex; + unsigned int EndIndex; + }; + + class DrawSegmentList + { + public: + DrawSegmentList(RenderThread *thread); + + TArray SegmentGroups; + + unsigned int SegmentsCount() const { return Segments.Size() - StartIndices.Last(); } + DrawSegment *Segment(unsigned int index) const { return Segments[Segments.Size() - 1 - index]; } + + unsigned int InterestingSegmentsCount() const { return InterestingSegments.Size() - StartInterestingIndices.Last(); } + DrawSegment *InterestingSegment(unsigned int index) const { return InterestingSegments[InterestingSegments.Size() - 1 - index]; } + + void Clear(); + void PushPortal(); + void PopPortal(); + void Push(DrawSegment *segment); + void PushInteresting(DrawSegment *segment); + + void BuildSegmentGroups(); + + RenderThread *Thread = nullptr; + + private: + TArray Segments; + TArray StartIndices; + + TArray InterestingSegments; // drawsegs that have something drawn on them + TArray StartInterestingIndices; + + // For building segment groups + short cliptop[MAXWIDTH]; + short clipbottom[MAXWIDTH]; + }; +} diff --git a/src/swrenderer/segments/r_portalsegment.cpp b/src/swrenderer/segments/r_portalsegment.cpp new file mode 100644 index 000000000..f1591b666 --- /dev/null +++ b/src/swrenderer/segments/r_portalsegment.cpp @@ -0,0 +1,64 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "m_bbox.h" +#include "i_system.h" +#include "p_lnspec.h" +#include "p_setup.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "a_sharedglobal.h" +#include "g_level.h" +#include "p_effect.h" +#include "doomstat.h" +#include "r_state.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "v_palette.h" +#include "r_sky.h" +#include "po_man.h" +#include "r_data/colormaps.h" +#include "swrenderer/segments/r_portalsegment.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + PortalDrawseg::PortalDrawseg(RenderThread *thread, line_t *linedef, int x1, int x2, const short *topclip, const short *bottomclip) : x1(x1), x2(x2) + { + src = linedef; + dst = linedef->special == Line_Mirror ? linedef : linedef->getPortalDestination(); + len = x2 - x1; + + ceilingclip = thread->FrameMemory->AllocMemory(len); + floorclip = thread->FrameMemory->AllocMemory(len); + memcpy(ceilingclip, topclip, len * sizeof(short)); + memcpy(floorclip, bottomclip, len * sizeof(short)); + + for (int i = 0; i < x2 - x1; i++) + { + if (ceilingclip[i] < 0) + ceilingclip[i] = 0; + if (ceilingclip[i] >= viewheight) + ceilingclip[i] = viewheight - 1; + if (floorclip[i] < 0) + floorclip[i] = 0; + if (floorclip[i] >= viewheight) + floorclip[i] = viewheight - 1; + } + + mirror = linedef->special == Line_Mirror; + } +} diff --git a/src/swrenderer/segments/r_portalsegment.h b/src/swrenderer/segments/r_portalsegment.h new file mode 100644 index 000000000..74596bb4d --- /dev/null +++ b/src/swrenderer/segments/r_portalsegment.h @@ -0,0 +1,37 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +namespace swrenderer +{ + class RenderThread; + + /* portal structure, this is used in r_ code in order to store drawsegs with portals (and mirrors) */ + struct PortalDrawseg + { + PortalDrawseg(RenderThread *thread, line_t *linedef, int x1, int x2, const short *topclip, const short *bottomclip); + + line_t* src = nullptr; // source line (the one drawn) this doesn't change over render loops + line_t* dst = nullptr; // destination line (the one that the portal is linked with, equals 'src' for mirrors) + + int x1 = 0; // drawseg x1 + int x2 = 0; // drawseg x2 + + int len = 0; + short *ceilingclip = nullptr; + short *floorclip = nullptr; + + bool mirror = false; // true if this is a mirror (src should equal dst) + }; +} diff --git a/src/swrenderer/things/r_decal.cpp b/src/swrenderer/things/r_decal.cpp new file mode 100644 index 000000000..52e433704 --- /dev/null +++ b/src/swrenderer/things/r_decal.cpp @@ -0,0 +1,334 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include "templates.h" +#include "i_system.h" +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" +#include "r_sky.h" +#include "v_video.h" +#include "m_swap.h" +#include "w_wad.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "r_decal.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/drawers/r_draw.h" +#include "v_palette.h" +#include "r_data/colormaps.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/things/r_wallsprite.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/viewport/r_spritedrawer.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + void RenderDecal::RenderDecals(RenderThread *thread, side_t *sidedef, DrawSegment *draw_segment, int wallshade, float lightleft, float lightstep, seg_t *curline, const FWallCoords &wallC, bool foggy, FDynamicColormap *basecolormap, const short *walltop, const short *wallbottom) + { + for (DBaseDecal *decal = sidedef->AttachedDecals; decal != NULL; decal = decal->WallNext) + { + Render(thread, sidedef, decal, draw_segment, wallshade, lightleft, lightstep, curline, wallC, foggy, basecolormap, walltop, wallbottom, 0); + } + } + + // pass = 0: when seg is first drawn + // = 1: drawing masked textures (including sprites) + // Currently, only pass = 0 is done or used + + void RenderDecal::Render(RenderThread *thread, side_t *wall, DBaseDecal *decal, DrawSegment *clipper, int wallshade, float lightleft, float lightstep, seg_t *curline, const FWallCoords &savecoord, bool foggy, FDynamicColormap *basecolormap, const short *walltop, const short *wallbottom, int pass) + { + DVector2 decal_left, decal_right, decal_pos; + int x1, x2; + double yscale; + BYTE flipx; + double zpos; + int needrepeat = 0; + sector_t *front, *back; + bool calclighting; + bool rereadcolormap; + FDynamicColormap *usecolormap; + float light = 0; + const short *mfloorclip; + const short *mceilingclip; + + if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid()) + return; + + // Determine actor z + zpos = decal->Z; + front = curline->frontsector; + back = (curline->backsector != NULL) ? curline->backsector : curline->frontsector; + switch (decal->RenderFlags & RF_RELMASK) + { + default: + zpos = decal->Z; + break; + case RF_RELUPPER: + if (curline->linedef->flags & ML_DONTPEGTOP) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + else + { + zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling); + } + break; + case RF_RELLOWER: + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + else + { + zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor); + } + break; + case RF_RELMID: + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor); + } + else + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + } + + FTexture *WallSpriteTile = TexMan(decal->PicNum, true); + flipx = (BYTE)(decal->RenderFlags & RF_XFLIP); + + if (WallSpriteTile == NULL || WallSpriteTile->UseType == FTexture::TEX_Null) + { + return; + } + + // Determine left and right edges of sprite. Since this sprite is bound + // to a wall, we use the wall's angle instead of the decal's. This is + // pretty much the same as what R_AddLine() does. + + double edge_right = WallSpriteTile->GetWidth(); + double edge_left = WallSpriteTile->LeftOffset; + edge_right = (edge_right - edge_left) * decal->ScaleX; + edge_left *= decal->ScaleX; + + double dcx, dcy; + decal->GetXY(wall, dcx, dcy); + decal_pos = { dcx, dcy }; + + DVector2 angvec = (curline->v2->fPos() - curline->v1->fPos()).Unit(); + float maskedScaleY; + + decal_left = decal_pos - edge_left * angvec - ViewPos; + decal_right = decal_pos + edge_right * angvec - ViewPos; + + CameraLight *cameraLight; + double texturemid; + + FWallCoords WallC; + if (WallC.Init(thread, decal_left, decal_right, TOO_CLOSE_Z)) + return; + + x1 = WallC.sx1; + x2 = WallC.sx2; + + if (x1 >= clipper->x2 || x2 <= clipper->x1) + return; + + FWallTmapVals WallT; + WallT.InitFromWallCoords(thread, &WallC); + + // Get the top and bottom clipping arrays + switch (decal->RenderFlags & RF_CLIPMASK) + { + case RF_CLIPFULL: + if (curline->backsector == NULL) + { + if (pass != 0) + { + return; + } + mceilingclip = walltop; + mfloorclip = wallbottom; + } + else if (pass == 0) + { + mceilingclip = walltop; + mfloorclip = thread->OpaquePass->ceilingclip; + needrepeat = 1; + } + else + { + mceilingclip = clipper->sprtopclip - clipper->x1; + mfloorclip = clipper->sprbottomclip - clipper->x1; + } + break; + + case RF_CLIPUPPER: + if (pass != 0) + { + return; + } + mceilingclip = walltop; + mfloorclip = thread->OpaquePass->ceilingclip; + break; + + case RF_CLIPMID: + if (curline->backsector != NULL && pass != 2) + { + return; + } + mceilingclip = clipper->sprtopclip - clipper->x1; + mfloorclip = clipper->sprbottomclip - clipper->x1; + break; + + case RF_CLIPLOWER: + if (pass != 0) + { + return; + } + mceilingclip = thread->OpaquePass->floorclip; + mfloorclip = wallbottom; + break; + } + + yscale = decal->ScaleY; + texturemid = WallSpriteTile->TopOffset + (zpos - ViewPos.Z) / yscale; + + // Clip sprite to drawseg + x1 = MAX(clipper->x1, x1); + x2 = MIN(clipper->x2, x2); + if (x1 >= x2) + { + return; + } + + ProjectedWallTexcoords walltexcoords; + walltexcoords.Project(WallSpriteTile->GetWidth(), x1, x2, WallT); + + if (flipx) + { + int i; + int right = (WallSpriteTile->GetWidth() << FRACBITS) - 1; + + for (i = x1; i < x2; i++) + { + walltexcoords.UPos[i] = right - walltexcoords.UPos[i]; + } + } + + // Prepare lighting + calclighting = false; + usecolormap = basecolormap; + rereadcolormap = true; + + // Decals that are added to the scene must fade to black. + if (decal->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) + { + usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); + rereadcolormap = false; + } + + light = lightleft + (x1 - savecoord.sx1) * lightstep; + + cameraLight = CameraLight::Instance(); + + // Draw it + bool sprflipvert; + if (decal->RenderFlags & RF_YFLIP) + { + sprflipvert = true; + yscale = -yscale; + texturemid -= WallSpriteTile->GetHeight(); + } + else + { + sprflipvert = false; + } + + maskedScaleY = float(1 / yscale); + do + { + int x = x1; + + SpriteDrawerArgs drawerargs; + + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != NULL) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + else if (!foggy && (decal->RenderFlags & RF_FULLBRIGHT)) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, 0); + else + calclighting = true; + + bool visible = drawerargs.SetStyle(decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor, basecolormap); + + // R_SetPatchStyle can modify basecolormap. + if (rereadcolormap) + { + usecolormap = basecolormap; + } + + if (visible) + { + while (x < x2) + { + if (calclighting) + { // calculate lighting + drawerargs.SetLight(usecolormap, light, wallshade); + } + DrawColumn(thread, drawerargs, x, WallSpriteTile, walltexcoords, texturemid, maskedScaleY, sprflipvert, mfloorclip, mceilingclip); + light += lightstep; + x++; + } + } + + // If this sprite is RF_CLIPFULL on a two-sided line, needrepeat will + // be set 1 if we need to draw on the lower wall. In all other cases, + // needrepeat will be 0, and the while will fail. + mceilingclip = thread->OpaquePass->floorclip; + mfloorclip = wallbottom; + } while (needrepeat--); + } + + void RenderDecal::DrawColumn(RenderThread *thread, SpriteDrawerArgs &drawerargs, int x, FTexture *WallSpriteTile, const ProjectedWallTexcoords &walltexcoords, double texturemid, float maskedScaleY, bool sprflipvert, const short *mfloorclip, const short *mceilingclip) + { + auto viewport = RenderViewport::Instance(); + + float iscale = walltexcoords.VStep[x] * maskedScaleY; + double spryscale = 1 / iscale; + double sprtopscreen; + if (sprflipvert) + sprtopscreen = viewport->CenterY + texturemid * spryscale; + else + sprtopscreen = viewport->CenterY - texturemid * spryscale; + + drawerargs.DrawMaskedColumn(thread, x, FLOAT2FIXED(iscale), WallSpriteTile, walltexcoords.UPos[x], spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip); + } +} diff --git a/src/swrenderer/things/r_decal.h b/src/swrenderer/things/r_decal.h new file mode 100644 index 000000000..4694014b7 --- /dev/null +++ b/src/swrenderer/things/r_decal.h @@ -0,0 +1,34 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +struct side_t; +class DBaseDecal; + +namespace swrenderer +{ + struct DrawSegment; + class ProjectedWallTexcoords; + class SpriteDrawerArgs; + + class RenderDecal + { + public: + static void RenderDecals(RenderThread *thread, side_t *wall, DrawSegment *draw_segment, int wallshade, float lightleft, float lightstep, seg_t *curline, const FWallCoords &wallC, bool foggy, FDynamicColormap *basecolormap, const short *walltop, const short *wallbottom); + + private: + static void Render(RenderThread *thread, side_t *wall, DBaseDecal *first, DrawSegment *clipper, int wallshade, float lightleft, float lightstep, seg_t *curline, const FWallCoords &wallC, bool foggy, FDynamicColormap *basecolormap, const short *walltop, const short *wallbottom, int pass); + static void DrawColumn(RenderThread *thread, SpriteDrawerArgs &drawerargs, int x, FTexture *WallSpriteTile, const ProjectedWallTexcoords &walltexcoords, double texturemid, float maskedScaleY, bool sprflipvert, const short *mfloorclip, const short *mceilingclip); + }; +} diff --git a/src/swrenderer/things/r_particle.cpp b/src/swrenderer/things/r_particle.cpp new file mode 100644 index 000000000..17656d69c --- /dev/null +++ b/src/swrenderer/things/r_particle.cpp @@ -0,0 +1,291 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "v_video.h" +#include "sc_man.h" +#include "s_sound.h" +#include "sbar.h" +#include "gi.h" +#include "r_sky.h" +#include "cmdlib.h" +#include "g_level.h" +#include "d_net.h" +#include "colormatcher.h" +#include "d_netinf.h" +#include "p_effect.h" +#include "v_palette.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "p_local.h" +#include "p_maputl.h" +#include "r_voxel.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/line/r_renderdrawsegment.h" +#include "swrenderer/things/r_particle.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + void RenderParticle::Project(RenderThread *thread, particle_t *particle, const sector_t *sector, int shade, WaterFakeSide fakeside, bool foggy) + { + double tr_x, tr_y; + double tx, ty; + double tz, tiz; + double xscale, yscale; + int x1, x2, y1, y2; + sector_t* heightsec = NULL; + + RenderPortal *renderportal = thread->Portal.get(); + + // [ZZ] Particle not visible through the portal plane + if (renderportal->CurrentPortal && !!P_PointOnLineSide(particle->Pos, renderportal->CurrentPortal->dst)) + return; + + // transform the origin point + tr_x = particle->Pos.X - ViewPos.X; + tr_y = particle->Pos.Y - ViewPos.Y; + + tz = tr_x * ViewTanCos + tr_y * ViewTanSin; + + // particle is behind view plane? + if (tz < MINZ) + return; + + tx = tr_x * ViewSin - tr_y * ViewCos; + + // Flip for mirrors + if (renderportal->MirrorFlags & RF_XFLIP) + { + tx = viewwidth - tx - 1; + } + + // too far off the side? + if (tz <= fabs(tx)) + return; + + tiz = 1 / tz; + xscale = centerx * tiz; + + // calculate edges of the shape + double psize = particle->size / 8.0; + + x1 = MAX(renderportal->WindowLeft, centerx + xs_RoundToInt((tx - psize) * xscale)); + x2 = MIN(renderportal->WindowRight, centerx + xs_RoundToInt((tx + psize) * xscale)); + + if (x1 >= x2) + return; + + auto viewport = RenderViewport::Instance(); + + yscale = xscale; // YaspectMul is not needed for particles as they should always be square + ty = particle->Pos.Z - ViewPos.Z; + y1 = xs_RoundToInt(viewport->CenterY - (ty + psize) * yscale); + y2 = xs_RoundToInt(viewport->CenterY - (ty - psize) * yscale); + + // Clip the particle now. Because it's a point and projected as its subsector is + // entered, we don't need to clip it to drawsegs like a normal sprite. + + // Clip particles behind walls. + auto ceilingclip = thread->OpaquePass->ceilingclip; + auto floorclip = thread->OpaquePass->floorclip; + if (y1 < ceilingclip[x1]) y1 = ceilingclip[x1]; + if (y1 < ceilingclip[x2 - 1]) y1 = ceilingclip[x2 - 1]; + if (y2 >= floorclip[x1]) y2 = floorclip[x1] - 1; + if (y2 >= floorclip[x2 - 1]) y2 = floorclip[x2 - 1] - 1; + + if (y1 > y2) + return; + + // Clip particles above the ceiling or below the floor. + heightsec = sector->GetHeightSec(); + + const secplane_t *topplane; + const secplane_t *botplane; + FTextureID toppic; + FTextureID botpic; + FDynamicColormap *map; + + if (heightsec) // only clip things which are in special sectors + { + if (fakeside == WaterFakeSide::AboveCeiling) + { + topplane = §or->ceilingplane; + botplane = &heightsec->ceilingplane; + toppic = sector->GetTexture(sector_t::ceiling); + botpic = heightsec->GetTexture(sector_t::ceiling); + map = heightsec->ColorMap; + } + else if (fakeside == WaterFakeSide::BelowFloor) + { + topplane = &heightsec->floorplane; + botplane = §or->floorplane; + toppic = heightsec->GetTexture(sector_t::floor); + botpic = sector->GetTexture(sector_t::floor); + map = heightsec->ColorMap; + } + else + { + topplane = &heightsec->ceilingplane; + botplane = &heightsec->floorplane; + toppic = heightsec->GetTexture(sector_t::ceiling); + botpic = heightsec->GetTexture(sector_t::floor); + map = sector->ColorMap; + } + } + else + { + topplane = §or->ceilingplane; + botplane = §or->floorplane; + toppic = sector->GetTexture(sector_t::ceiling); + botpic = sector->GetTexture(sector_t::floor); + map = sector->ColorMap; + } + + if (botpic != skyflatnum && particle->Pos.Z < botplane->ZatPoint(particle->Pos)) + return; + if (toppic != skyflatnum && particle->Pos.Z >= topplane->ZatPoint(particle->Pos)) + return; + + // store information in a vissprite + RenderParticle *vis = thread->FrameMemory->NewObject(); + + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->heightsec = heightsec; + vis->xscale = FLOAT2FIXED(xscale); + vis->yscale = (float)xscale; + // vis->yscale *= InvZtoScale; + vis->depth = (float)tz; + vis->idepth = float(1 / tz); + vis->gpos = { (float)particle->Pos.X, (float)particle->Pos.Y, (float)particle->Pos.Z }; + vis->y1 = y1; + vis->y2 = y2; + vis->x1 = x1; + vis->x2 = x2; + vis->Translation = 0; + vis->startfrac = 255 & (particle->color >> 24); + vis->pic = NULL; + vis->renderflags = (short)(particle->alpha * 255.0f + 0.5f); + vis->FakeFlatStat = fakeside; + vis->floorclip = 0; + vis->foggy = foggy; + + vis->Light.SetColormap(tiz * LightVisibility::Instance()->ParticleGlobVis(foggy), shade, map, particle->bright != 0, false, false); + + thread->SpriteList->Push(vis); + } + + void RenderParticle::Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) + { + auto vis = this; + + int spacing; + BYTE color = vis->Light.BaseColormap->Maps[vis->startfrac]; + int yl = vis->y1; + int ycount = vis->y2 - yl + 1; + int x1 = vis->x1; + int countbase = vis->x2 - x1; + + if (ycount <= 0 || countbase <= 0) + return; + + DrawMaskedSegsBehindParticle(thread); + + uint32_t fg = LightBgra::shade_pal_index_simple(color, LightBgra::calc_light_multiplier(LIGHTSCALE(0, vis->Light.ColormapNum << FRACBITS))); + + // vis->renderflags holds translucency level (0-255) + fixed_t fglevel = ((vis->renderflags + 1) << 8) & ~0x3ff; + uint32_t alpha = fglevel * 256 / FRACUNIT; + + auto viewport = RenderViewport::Instance(); + + spacing = viewport->RenderTarget->GetPitch(); + + uint32_t fracstepx = PARTICLE_TEXTURE_SIZE * FRACUNIT / countbase; + uint32_t fracposx = fracstepx / 2; + + RenderTranslucentPass *translucentPass = thread->TranslucentPass.get(); + + if (viewport->RenderTarget->IsBgra()) + { + for (int x = x1; x < (x1 + countbase); x++, fracposx += fracstepx) + { + if (translucentPass->ClipSpriteColumnWithPortals(x, vis)) + continue; + uint32_t *dest = (uint32_t*)viewport->GetDest(x, yl); + thread->DrawQueue->Push(dest, yl, spacing, ycount, fg, alpha, fracposx); + } + } + else + { + for (int x = x1; x < (x1 + countbase); x++, fracposx += fracstepx) + { + if (translucentPass->ClipSpriteColumnWithPortals(x, vis)) + continue; + uint8_t *dest = viewport->GetDest(x, yl); + thread->DrawQueue->Push(dest, yl, spacing, ycount, fg, alpha, fracposx); + } + } + } + + void RenderParticle::DrawMaskedSegsBehindParticle(RenderThread *thread) + { + // Draw any masked textures behind this particle so that when the + // particle is drawn, it will be in front of them. + DrawSegmentList *segmentlist = thread->DrawSegments.get(); + for (unsigned int index = 0; index != segmentlist->InterestingSegmentsCount(); index++) + { + DrawSegment *ds = segmentlist->InterestingSegment(index); + + // kg3D - no fake segs + if (ds->fake) continue; + if (ds->x1 >= x2 || ds->x2 <= x1) + { + continue; + } + if ((ds->siz2 - ds->siz1) * ((x2 + x1) / 2 - ds->sx1) / (ds->sx2 - ds->sx1) + ds->siz1 < idepth) + { + // [ZZ] only draw stuff that's inside the same portal as the particle, other portals will care for themselves + if (ds->CurrentPortalUniq == CurrentPortalUniq) + { + RenderDrawSegment renderer(thread); + renderer.Render(ds, MAX(ds->x1, x1), MIN(ds->x2, x2)); + } + } + } + } +} diff --git a/src/swrenderer/things/r_particle.h b/src/swrenderer/things/r_particle.h new file mode 100644 index 000000000..af2f59e6c --- /dev/null +++ b/src/swrenderer/things/r_particle.h @@ -0,0 +1,42 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visiblesprite.h" +#include "swrenderer/scene/r_opaque_pass.h" + +struct particle_t; + +namespace swrenderer +{ + class RenderParticle : public VisibleSprite + { + public: + static void Project(RenderThread *thread, particle_t *, const sector_t *sector, int shade, WaterFakeSide fakeside, bool foggy); + + protected: + bool IsParticle() const override { return true; } + void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) override; + + private: + void DrawMaskedSegsBehindParticle(RenderThread *thread); + + fixed_t xscale = 0; + fixed_t startfrac = 0; // horizontal position of x1 + int y1 = 0, y2 = 0; + + uint32_t Translation = 0; + uint32_t FillColor = 0; + }; +} diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp new file mode 100644 index 000000000..4b8bb074b --- /dev/null +++ b/src/swrenderer/things/r_playersprite.cpp @@ -0,0 +1,640 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "swrenderer/things/r_playersprite.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "v_video.h" +#include "sc_man.h" +#include "s_sound.h" +#include "sbar.h" +#include "gi.h" +#include "r_sky.h" +#include "cmdlib.h" +#include "g_level.h" +#include "d_net.h" +#include "colormatcher.h" +#include "d_netinf.h" +#include "p_effect.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "v_palette.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "p_local.h" +#include "p_maputl.h" +#include "r_voxel.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "g_levellocals.h" + +EXTERN_CVAR(Bool, st_scale) +EXTERN_CVAR(Bool, r_drawplayersprites) +EXTERN_CVAR(Bool, r_deathcamera) +EXTERN_CVAR(Bool, r_shadercolormaps) +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) + +namespace swrenderer +{ + RenderPlayerSprites::RenderPlayerSprites(RenderThread *thread) + { + Thread = thread; + } + + void RenderPlayerSprites::Render() + { + int i; + int lightnum; + DPSprite* psp; + DPSprite* weapon; + sector_t* sec = NULL; + int floorlight, ceilinglight; + F3DFloor *rover; + + if (!r_drawplayersprites || + !camera || + !camera->player || + (players[consoleplayer].cheats & CF_CHASECAM) || + (r_deathcamera && camera->health <= 0)) + return; + + FDynamicColormap *basecolormap; + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() < 0 && viewsector->e && viewsector->e->XFloor.lightlist.Size()) + { + for (i = viewsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) + { + if (ViewPos.Z <= viewsector->e->XFloor.lightlist[i].plane.Zat0()) + { + rover = viewsector->e->XFloor.lightlist[i].caster; + if (rover) + { + if (rover->flags & FF_DOUBLESHADOW && ViewPos.Z <= rover->bottom.plane->Zat0()) + break; + sec = rover->model; + if (rover->flags & FF_FADEWALLS) + basecolormap = sec->ColorMap; + else + basecolormap = viewsector->e->XFloor.lightlist[i].extra_colormap; + } + break; + } + } + if (!sec) + { + sec = viewsector; + basecolormap = sec->ColorMap; + } + floorlight = ceilinglight = sec->lightlevel; + } + else + { // This used to use camera->Sector but due to interpolation that can be incorrect + // when the interpolated viewpoint is in a different sector than the camera. + sec = Thread->OpaquePass->FakeFlat(viewsector, &tempsec, &floorlight, &ceilinglight, nullptr, 0, 0, 0, 0); + + // [RH] set basecolormap + basecolormap = sec->ColorMap; + } + + // [RH] set foggy flag + bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); + + // get light level + lightnum = ((floorlight + ceilinglight) >> 1) + LightVisibility::ActualExtraLight(foggy); + int spriteshade = LightVisibility::LightLevelToShade(lightnum, foggy) - 24 * FRACUNIT; + + if (camera->player != NULL) + { + auto viewport = RenderViewport::Instance(); + + double centerhack = viewport->CenterY; + double wx, wy; + float bobx, boby; + + viewport->CenterY = viewheight / 2; + + P_BobWeapon(camera->player, &bobx, &boby, r_TicFracF); + + // Interpolate the main weapon layer once so as to be able to add it to other layers. + if ((weapon = camera->player->FindPSprite(PSP_WEAPON)) != nullptr) + { + if (weapon->firstTic) + { + wx = weapon->x; + wy = weapon->y; + } + else + { + wx = weapon->oldx + (weapon->x - weapon->oldx) * r_TicFracF; + wy = weapon->oldy + (weapon->y - weapon->oldy) * r_TicFracF; + } + } + else + { + wx = 0; + wy = 0; + } + + // add all active psprites + psp = camera->player->psprites; + while (psp) + { + // [RH] Don't draw the targeter's crosshair if the player already has a crosshair set. + // It's possible this psprite's caller is now null but the layer itself hasn't been destroyed + // because it didn't tick yet (if we typed 'take all' while in the console for example). + // In this case let's simply not draw it to avoid crashing. + + if ((psp->GetID() != PSP_TARGETCENTER || CrosshairImage == nullptr) && psp->GetCaller() != nullptr) + { + RenderSprite(psp, camera, bobx, boby, wx, wy, r_TicFracF, spriteshade, basecolormap, foggy); + } + + psp = psp->GetNext(); + } + + viewport->CenterY = centerhack; + } + } + + void RenderPlayerSprites::RenderSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy) + { + double tx; + int x1; + int x2; + double sx, sy; + spritedef_t* sprdef; + spriteframe_t* sprframe; + FTextureID picnum; + WORD flip; + FTexture* tex; + bool noaccel; + double alpha = owner->Alpha; + + // decide which patch to use + if ((unsigned)pspr->GetSprite() >= (unsigned)sprites.Size()) + { + DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite number %i\n", pspr->GetSprite()); + return; + } + sprdef = &sprites[pspr->GetSprite()]; + if (pspr->GetFrame() >= sprdef->numframes) + { + DPrintf(DMSG_ERROR, "R_DrawPSprite: invalid sprite frame %i : %i\n", pspr->GetSprite(), pspr->GetFrame()); + return; + } + sprframe = &SpriteFrames[sprdef->spriteframes + pspr->GetFrame()]; + + picnum = sprframe->Texture[0]; + flip = sprframe->Flip & 1; + tex = TexMan(picnum); + + if (tex->UseType == FTexture::TEX_Null || pspr->RenderStyle == STYLE_None) + return; + + if (pspr->firstTic) + { // Can't interpolate the first tic. + pspr->firstTic = false; + pspr->oldx = pspr->x; + pspr->oldy = pspr->y; + } + + sx = pspr->oldx + (pspr->x - pspr->oldx) * ticfrac; + sy = pspr->oldy + (pspr->y - pspr->oldy) * ticfrac + WEAPON_FUDGE_Y; + + if (pspr->Flags & PSPF_ADDBOB) + { + sx += bobx; + sy += boby; + } + + if (pspr->Flags & PSPF_ADDWEAPON && pspr->GetID() != PSP_WEAPON) + { + sx += wx; + sy += wy; + } + + auto viewport = RenderViewport::Instance(); + + double pspritexscale = centerxwide / 160.0; + double pspriteyscale = pspritexscale * viewport->YaspectMul; + double pspritexiscale = 1 / pspritexscale; + + // calculate edges of the shape + tx = sx - BASEXCENTER; + + tx -= tex->GetScaledLeftOffset(); + x1 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); + + // off the right side + if (x1 > viewwidth) + return; + + tx += tex->GetScaledWidth(); + x2 = xs_RoundToInt(viewport->CenterX + tx * pspritexscale); + + // off the left side + if (x2 <= 0) + return; + + // store information in a vissprite + NoAccelPlayerSprite vis; + + vis.renderflags = owner->renderflags; + + vis.texturemid = (BASEYCENTER - sy) * tex->Scale.Y + tex->TopOffset; + + if (camera->player && (viewport->RenderTarget != screen || + viewheight == viewport->RenderTarget->GetHeight() || + (viewport->RenderTarget->GetWidth() > (BASEXCENTER * 2) && !st_scale))) + { // Adjust PSprite for fullscreen views + AWeapon *weapon = dyn_cast(pspr->GetCaller()); + if (weapon != nullptr && weapon->YAdjust != 0) + { + if (viewport->RenderTarget != screen || viewheight == viewport->RenderTarget->GetHeight()) + { + vis.texturemid -= weapon->YAdjust; + } + else + { + vis.texturemid -= StatusBar->GetDisplacement() * weapon->YAdjust; + } + } + } + if (pspr->GetID() < PSP_TARGETCENTER) + { // Move the weapon down for 1280x1024. + vis.texturemid -= AspectPspriteOffset(WidescreenRatio); + } + vis.x1 = x1 < 0 ? 0 : x1; + vis.x2 = x2 >= viewwidth ? viewwidth : x2; + vis.xscale = FLOAT2FIXED(pspritexscale / tex->Scale.X); + vis.yscale = float(pspriteyscale / tex->Scale.Y); + vis.pic = tex; + + // If flip is used, provided that it's not already flipped (that would just invert itself) + // (It's an XOR...) + if (!(flip) != !(pspr->Flags & PSPF_FLIP)) + { + vis.xiscale = -FLOAT2FIXED(pspritexiscale * tex->Scale.X); + vis.startfrac = (tex->GetWidth() << FRACBITS) - 1; + } + else + { + vis.xiscale = FLOAT2FIXED(pspritexiscale * tex->Scale.X); + vis.startfrac = 0; + } + + if (vis.x1 > x1) + vis.startfrac += vis.xiscale*(vis.x1 - x1); + + noaccel = false; + FDynamicColormap *colormap_to_use = nullptr; + if (pspr->GetID() < PSP_TARGETCENTER) + { + // [MC] Set the render style + + if (pspr->Flags & PSPF_RENDERSTYLE) + { + const int rs = clamp(pspr->RenderStyle, 0, STYLE_Count); + + if (pspr->Flags & PSPF_FORCESTYLE) + { + vis.RenderStyle = LegacyRenderStyles[rs]; + } + else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Fuzzy]) + { + vis.RenderStyle = LegacyRenderStyles[STYLE_Fuzzy]; + } + else if (owner->RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy]) + { + vis.RenderStyle = LegacyRenderStyles[STYLE_OptFuzzy]; + vis.RenderStyle.CheckFuzz(); + } + else if (owner->RenderStyle == LegacyRenderStyles[STYLE_Subtract]) + { + vis.RenderStyle = LegacyRenderStyles[STYLE_Subtract]; + } + else + { + vis.RenderStyle = LegacyRenderStyles[rs]; + } + } + else + { + vis.RenderStyle = owner->RenderStyle; + } + + // Set the alpha based on if using the overlay's own or not. Also adjust + // and override the alpha if not forced. + if (pspr->Flags & PSPF_ALPHA) + { + if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy]) + { + alpha = owner->Alpha; + } + else if (vis.RenderStyle == LegacyRenderStyles[STYLE_OptFuzzy]) + { + FRenderStyle style = vis.RenderStyle; + style.CheckFuzz(); + switch (style.BlendOp) + { + default: + alpha = pspr->alpha * owner->Alpha; + break; + case STYLEOP_Fuzz: + case STYLEOP_Sub: + alpha = owner->Alpha; + break; + } + + } + else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Subtract]) + { + alpha = owner->Alpha; + } + else if (vis.RenderStyle == LegacyRenderStyles[STYLE_Add] || + vis.RenderStyle == LegacyRenderStyles[STYLE_Translucent] || + vis.RenderStyle == LegacyRenderStyles[STYLE_TranslucentStencil] || + vis.RenderStyle == LegacyRenderStyles[STYLE_AddStencil] || + vis.RenderStyle == LegacyRenderStyles[STYLE_AddShaded]) + { + alpha = owner->Alpha * pspr->alpha; + } + else + { + alpha = owner->Alpha; + } + } + + // Should normal renderstyle come out on top at the end and we desire alpha, + // switch it to translucent. Normal never applies any sort of alpha. + if ((pspr->Flags & PSPF_ALPHA) && + vis.RenderStyle == LegacyRenderStyles[STYLE_Normal] && + vis.Alpha < 1.0) + { + vis.RenderStyle = LegacyRenderStyles[STYLE_Translucent]; + alpha = owner->Alpha * pspr->alpha; + } + + // ALWAYS take priority if asked for, except fuzz. Fuzz does absolutely nothing + // no matter what way it's changed. + if (pspr->Flags & PSPF_FORCEALPHA) + { + //Due to lack of != operators... + if (vis.RenderStyle == LegacyRenderStyles[STYLE_Fuzzy] || + vis.RenderStyle == LegacyRenderStyles[STYLE_SoulTrans] || + vis.RenderStyle == LegacyRenderStyles[STYLE_Stencil]) + { + } + else + { + alpha = pspr->alpha; + vis.RenderStyle.Flags |= STYLEF_ForceAlpha; + } + } + vis.Alpha = clamp(float(alpha), 0.f, 1.f); + + // Due to how some of the effects are handled, going to 0 or less causes some + // weirdness to display. There's no point rendering it anyway if it's 0. + if (vis.Alpha <= 0.) + return; + + //----------------------------------------------------------------------------- + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (vis.RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + + if (vis.RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + bool fullbright = !foggy && pspr->GetState()->GetFullbright(); + bool fadeToBlack = (vis.RenderStyle.Flags & STYLEF_FadeToBlack) != 0; + + vis.Light.SetColormap(0, spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack); + + colormap_to_use = (FDynamicColormap*)vis.Light.BaseColormap; + + if (camera->Inventory != nullptr) + { + visstyle_t visstyle; + visstyle.Alpha = vis.Alpha; + visstyle.RenderStyle = STYLE_Count; + visstyle.Invert = false; + + camera->Inventory->AlterWeaponSprite(&visstyle); + + vis.Alpha = visstyle.Alpha; + + if (visstyle.RenderStyle != STYLE_Count) + { + vis.RenderStyle = visstyle.RenderStyle; + } + + if (visstyle.Invert) + { + vis.Light.BaseColormap = &SpecialColormaps[INVERSECOLORMAP]; + vis.Light.ColormapNum = 0; + noaccel = true; + } + } + // If we're drawing with a special colormap, but shaders for them are disabled, do + // not accelerate. + if (!r_shadercolormaps && (vis.Light.BaseColormap >= &SpecialColormaps[0] && + vis.Light.BaseColormap <= &SpecialColormaps.Last())) + { + noaccel = true; + } + // If drawing with a BOOM colormap, disable acceleration. + if (vis.Light.BaseColormap == &NormalLight && NormalLight.Maps != realcolormaps.Maps) + { + noaccel = true; + } + // If the main colormap has fixed lights, and this sprite is being drawn with that + // colormap, disable acceleration so that the lights can remain fixed. + CameraLight *cameraLight = CameraLight::Instance(); + if (!noaccel && cameraLight->ShaderColormap() == nullptr && + NormalLightHasFixedLights && vis.Light.BaseColormap == &NormalLight && + vis.pic->UseBasePalette()) + { + noaccel = true; + } + } + else + { + colormap_to_use = basecolormap; + + vis.Light.BaseColormap = basecolormap; + vis.Light.ColormapNum = 0; + } + + // Check for hardware-assisted 2D. If it's available, and this sprite is not + // fuzzy, don't draw it until after the switch to 2D mode. + if (!noaccel && viewport->RenderTarget == screen && (DFrameBuffer *)screen->Accel2D) + { + FRenderStyle style = vis.RenderStyle; + style.CheckFuzz(); + if (style.BlendOp != STYLEOP_Fuzz) + { + HWAccelPlayerSprite accelSprite; + + accelSprite.pic = vis.pic; + accelSprite.texturemid = vis.texturemid; + accelSprite.yscale = vis.yscale; + accelSprite.xscale = vis.xscale; + + accelSprite.Alpha = vis.Alpha; + accelSprite.RenderStyle = vis.RenderStyle; + accelSprite.Translation = vis.Translation; + accelSprite.FillColor = vis.FillColor; + + accelSprite.basecolormap = colormap_to_use; + accelSprite.x1 = x1; + accelSprite.flip = vis.xiscale < 0; + + if (vis.Light.BaseColormap >= &SpecialColormaps[0] && + vis.Light.BaseColormap < &SpecialColormaps[SpecialColormaps.Size()]) + { + accelSprite.special = static_cast(vis.Light.BaseColormap); + } + else if (CameraLight::Instance()->ShaderColormap()) + { + accelSprite.special = CameraLight::Instance()->ShaderColormap(); + } + else if (colormap_to_use->Color == PalEntry(255, 255, 255) && + colormap_to_use->Desaturate == 0) + { + accelSprite.overlay = colormap_to_use->Fade; + accelSprite.overlay.a = BYTE(vis.Light.ColormapNum * 255 / NUMCOLORMAPS); + } + else + { + accelSprite.usecolormapstyle = true; + accelSprite.colormapstyle.Color = colormap_to_use->Color; + accelSprite.colormapstyle.Fade = colormap_to_use->Fade; + accelSprite.colormapstyle.Desaturate = colormap_to_use->Desaturate; + accelSprite.colormapstyle.FadeLevel = vis.Light.ColormapNum / float(NUMCOLORMAPS); + } + + AcceleratedSprites.Push(accelSprite); + return; + } + } + + vis.Render(Thread); + } + + void RenderPlayerSprites::RenderRemaining() + { + for (const HWAccelPlayerSprite &sprite : AcceleratedSprites) + { + screen->DrawTexture(sprite.pic, + viewwindowx + sprite.x1, + viewwindowy + viewheight / 2 - sprite.texturemid * sprite.yscale - 0.5, + DTA_DestWidthF, FIXED2DBL(sprite.pic->GetWidth() * sprite.xscale), + DTA_DestHeightF, sprite.pic->GetHeight() * sprite.yscale, + DTA_TranslationIndex, sprite.Translation, + DTA_FlipX, sprite.flip, + DTA_TopOffset, 0, + DTA_LeftOffset, 0, + DTA_ClipLeft, viewwindowx, + DTA_ClipTop, viewwindowy, + DTA_ClipRight, viewwindowx + viewwidth, + DTA_ClipBottom, viewwindowy + viewheight, + DTA_Alpha, sprite.Alpha, + DTA_RenderStyle, sprite.RenderStyle, + DTA_FillColor, sprite.FillColor, + DTA_SpecialColormap, sprite.special, + DTA_ColorOverlay, sprite.overlay.d, + DTA_ColormapStyle, sprite.usecolormapstyle ? &sprite.colormapstyle : nullptr, + TAG_DONE); + } + + AcceleratedSprites.Clear(); + } + + ///////////////////////////////////////////////////////////////////////// + + void NoAccelPlayerSprite::Render(RenderThread *thread) + { + if (xscale == 0 || fabs(yscale) < (1.0f / 32000.0f)) + { // scaled to 0; can't see + return; + } + + SpriteDrawerArgs drawerargs; + drawerargs.SetLight(Light.BaseColormap, 0, Light.ColormapNum << FRACBITS); + + FDynamicColormap *basecolormap = static_cast(Light.BaseColormap); + + bool visible = drawerargs.SetStyle(RenderStyle, Alpha, Translation, FillColor, basecolormap, Light.ColormapNum << FRACBITS); + if (!visible) + return; + + double spryscale = yscale; + bool sprflipvert = false; + fixed_t iscale = FLOAT2FIXED(1 / yscale); + + auto viewport = RenderViewport::Instance(); + + double sprtopscreen; + if (renderflags & RF_YFLIP) + { + sprflipvert = true; + spryscale = -spryscale; + iscale = -iscale; + sprtopscreen = viewport->CenterY + (texturemid - pic->GetHeight()) * spryscale; + } + else + { + sprflipvert = false; + sprtopscreen = viewport->CenterY - texturemid * spryscale; + } + + // clip to screen bounds + short *mfloorclip = screenheightarray; + short *mceilingclip = zeroarray; + + fixed_t frac = startfrac; + for (int x = x1; x < x2; x++) + { + drawerargs.DrawMaskedColumn(thread, x, iscale, pic, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, false); + frac += xiscale; + } + + if (thread->MainThread) + NetUpdate(); + } +} diff --git a/src/swrenderer/things/r_playersprite.h b/src/swrenderer/things/r_playersprite.h new file mode 100644 index 000000000..588f9f8dc --- /dev/null +++ b/src/swrenderer/things/r_playersprite.h @@ -0,0 +1,93 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visiblesprite.h" +#include "r_data/colormaps.h" + +class DPSprite; + +namespace swrenderer +{ + class NoAccelPlayerSprite + { + public: + short x1 = 0; + short x2 = 0; + + double texturemid = 0.0; + + fixed_t xscale = 0; + float yscale = 0.0f; + + FTexture *pic = nullptr; + + fixed_t xiscale = 0; + fixed_t startfrac = 0; + + float Alpha = 0.0f; + FRenderStyle RenderStyle; + uint32_t Translation = 0; + uint32_t FillColor = 0; + + ColormapLight Light; + + short renderflags = 0; + + void Render(RenderThread *thread); + }; + + class HWAccelPlayerSprite + { + public: + FTexture *pic = nullptr; + double texturemid = 0.0; + float yscale = 0.0f; + fixed_t xscale = 0; + + float Alpha = 0.0f; + FRenderStyle RenderStyle; + uint32_t Translation = 0; + uint32_t FillColor = 0; + + FDynamicColormap *basecolormap = nullptr; + int x1 = 0; + + bool flip = false; + FSpecialColormap *special = nullptr; + PalEntry overlay = 0; + FColormapStyle colormapstyle; + bool usecolormapstyle = false; + }; + + class RenderPlayerSprites + { + public: + RenderPlayerSprites(RenderThread *thread); + + void Render(); + void RenderRemaining(); + + RenderThread *Thread = nullptr; + + private: + void RenderSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac, int spriteshade, FDynamicColormap *basecolormap, bool foggy); + + enum { BASEXCENTER = 160 }; + enum { BASEYCENTER = 100 }; + + TArray AcceleratedSprites; + sector_t tempsec; + }; +} diff --git a/src/swrenderer/things/r_sprite.cpp b/src/swrenderer/things/r_sprite.cpp new file mode 100644 index 000000000..097929197 --- /dev/null +++ b/src/swrenderer/things/r_sprite.cpp @@ -0,0 +1,359 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "swrenderer/things/r_wallsprite.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "v_video.h" +#include "sc_man.h" +#include "s_sound.h" +#include "sbar.h" +#include "gi.h" +#include "r_sky.h" +#include "cmdlib.h" +#include "g_level.h" +#include "d_net.h" +#include "colormatcher.h" +#include "d_netinf.h" +#include "p_effect.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "v_palette.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "p_local.h" +#include "r_voxel.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" +#include "gl/dynlights/gl_dynlight.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) +EXTERN_CVAR(Bool, gl_light_sprites) + +namespace swrenderer +{ + void RenderSprite::Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTexture *tex, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade, bool foggy, FDynamicColormap *basecolormap) + { + // transform the origin point + double tr_x = pos.X - ViewPos.X; + double tr_y = pos.Y - ViewPos.Y; + + double tz = tr_x * ViewTanCos + tr_y * ViewTanSin; + + // thing is behind view plane? + if (tz < MINZ) + return; + + double tx = tr_x * ViewSin - tr_y * ViewCos; + + // [RH] Flip for mirrors + RenderPortal *renderportal = thread->Portal.get(); + if (renderportal->MirrorFlags & RF_XFLIP) + { + tx = -tx; + } + //tx2 = tx >> 4; + + // too far off the side? + if (fabs(tx / 64) > fabs(tz)) + { + return; + } + + // [RH] Added scaling + int scaled_to = tex->GetScaledTopOffset(); + int scaled_bo = scaled_to - tex->GetScaledHeight(); + double gzt = pos.Z + spriteScale.Y * scaled_to; + double gzb = pos.Z + spriteScale.Y * scaled_bo; + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + sector_t *heightsec = thing->Sector->GetHeightSec(); + + if (heightsec != nullptr) // only clip things which are in special sectors + { + if (fakeside == WaterFakeSide::AboveCeiling) + { + if (gzt < heightsec->ceilingplane.ZatPoint(pos)) + return; + } + else if (fakeside == WaterFakeSide::BelowFloor) + { + if (gzb >= heightsec->floorplane.ZatPoint(pos)) + return; + } + else + { + if (gzt < heightsec->floorplane.ZatPoint(pos)) + return; + if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint(pos)) + return; + } + } + + auto viewport = RenderViewport::Instance(); + + double xscale = viewport->CenterX / tz; + + // [RH] Reject sprites that are off the top or bottom of the screen + if (viewport->globaluclip * tz > ViewPos.Z - gzb || viewport->globaldclip * tz < ViewPos.Z - gzt) + { + return; + } + + // [RH] Flip for mirrors + renderflags ^= renderportal->MirrorFlags & RF_XFLIP; + + // calculate edges of the shape + const double thingxscalemul = spriteScale.X / tex->Scale.X; + + tx -= ((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; + double dtx1 = tx * xscale; + int x1 = centerx + xs_RoundToInt(dtx1); + + // off the right side? + if (x1 >= renderportal->WindowRight) + return; + + tx += tex->GetWidth() * thingxscalemul; + int x2 = centerx + xs_RoundToInt(tx * xscale); + + // off the left side or too small? + if ((x2 < renderportal->WindowLeft || x2 <= x1)) + return; + + xscale = spriteScale.X * xscale / tex->Scale.X; + fixed_t iscale = (fixed_t)(FRACUNIT / xscale); // Round towards zero to avoid wrapping in edge cases + + double yscale = spriteScale.Y / tex->Scale.Y; + + // store information in a vissprite + RenderSprite *vis = thread->FrameMemory->NewObject(); + + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->xscale = FLOAT2FIXED(xscale); + vis->yscale = float(viewport->InvZtoScale * yscale / tz); + vis->idepth = float(1 / tz); + vis->floorclip = thing->Floorclip / yscale; + vis->texturemid = tex->TopOffset - (ViewPos.Z - pos.Z + thing->Floorclip) / yscale; + vis->x1 = x1 < renderportal->WindowLeft ? renderportal->WindowLeft : x1; + vis->x2 = x2 > renderportal->WindowRight ? renderportal->WindowRight : x2; + //vis->Angle = thing->Angles.Yaw; + + if (renderflags & RF_XFLIP) + { + vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + vis->startfrac += (fixed_t)(vis->xiscale * (vis->x1 - centerx + 0.5 - dtx1)); + + // killough 3/27/98: save sector for special clipping later + vis->heightsec = heightsec; + vis->sector = thing->Sector; + + vis->depth = (float)tz; + vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; + vis->gzb = (float)gzb; // [RH] use gzb, not thing->z + vis->gzt = (float)gzt; // killough 3/27/98 + vis->deltax = float(pos.X - ViewPos.X); + vis->deltay = float(pos.Y - ViewPos.Y); + vis->renderflags = renderflags; + if (thing->flags5 & MF5_BRIGHT) + vis->renderflags |= RF_FULLBRIGHT; // kg3D + vis->RenderStyle = thing->RenderStyle; + vis->FillColor = thing->fillcolor; + vis->Translation = thing->Translation; // [RH] thing translation table + vis->FakeFlatStat = fakeside; + vis->Alpha = float(thing->Alpha); + vis->fakefloor = fakefloor; + vis->fakeceiling = fakeceiling; + //vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP; + //vis->bSplitSprite = false; + + vis->pic = tex; + + vis->foggy = foggy; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (vis->RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + if (vis->RenderStyle.Flags & STYLEF_InvertSource) + invertcolormap = !invertcolormap; + + if (vis->RenderStyle == LegacyRenderStyles[STYLE_Add] && basecolormap->Fade != 0) + { + basecolormap = GetSpecialLights(basecolormap->Color, 0, basecolormap->Desaturate); + } + + bool fullbright = !vis->foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)); + bool fadeToBlack = (vis->RenderStyle.Flags & STYLEF_FadeToBlack) != 0; + + if (r_dynlights && gl_light_sprites) + { + float lit_red = 0; + float lit_green = 0; + float lit_blue = 0; + auto node = vis->sector->lighthead; + while (node != nullptr) + { + ADynamicLight *light = node->lightsource; + if (light->visibletoplayer && !(light->flags2&MF2_DORMANT) && (!(light->flags4&MF4_DONTLIGHTSELF) || light->target != thing)) + { + float lx = (float)(light->X() - thing->X()); + float ly = (float)(light->Y() - thing->Y()); + float lz = (float)(light->Z() - thing->Center()); + float LdotL = lx * lx + ly * ly + lz * lz; + float radius = node->lightsource->GetRadius(); + if (radius * radius >= LdotL) + { + float distance = sqrt(LdotL); + float attenuation = 1.0f - distance / radius; + if (attenuation > 0.0f) + { + float red = light->GetRed() * (1.0f / 255.0f); + float green = light->GetGreen() * (1.0f / 255.0f); + float blue = light->GetBlue() * (1.0f / 255.0f); + /*if (light->IsSubtractive()) + { + float bright = FVector3(lr, lg, lb).Length(); + FVector3 lightColor(lr, lg, lb); + red = (bright - lr) * -1; + green = (bright - lg) * -1; + blue = (bright - lb) * -1; + }*/ + + lit_red += red * attenuation; + lit_green += green * attenuation; + lit_blue += blue * attenuation; + } + } + } + node = node->nextLight; + } + lit_red = clamp(lit_red * 255.0f, 0.0f, 255.0f); + lit_green = clamp(lit_green * 255.0f, 0.0f, 255.0f); + lit_blue = clamp(lit_blue * 255.0f, 0.0f, 255.0f); + vis->dynlightcolor = (((uint32_t)lit_red) << 16) | (((uint32_t)lit_green) << 8) | ((uint32_t)lit_blue); + } + else + { + vis->dynlightcolor = 0; + } + + vis->Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis(foggy) / MAX(tz, MINZ), spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack); + + thread->SpriteList->Push(vis); + } + + void RenderSprite::Render(RenderThread *thread, short *mfloorclip, short *mceilingclip, int, int) + { + auto vis = this; + + fixed_t frac; + FTexture *tex; + int x2; + fixed_t xiscale; + + double spryscale, sprtopscreen; + bool sprflipvert; + + if (vis->xscale == 0 || fabs(vis->yscale) < (1.0f / 32000.0f)) + { // scaled to 0; can't see + return; + } + + SpriteDrawerArgs drawerargs; + drawerargs.SetLight(vis->Light.BaseColormap, 0, vis->Light.ColormapNum << FRACBITS); + drawerargs.SetDynamicLight(dynlightcolor); + + FDynamicColormap *basecolormap = static_cast(vis->Light.BaseColormap); + + bool visible = drawerargs.SetStyle(vis->RenderStyle, vis->Alpha, vis->Translation, vis->FillColor, basecolormap, vis->Light.ColormapNum << FRACBITS); + + if (visible) + { + tex = vis->pic; + spryscale = vis->yscale; + sprflipvert = false; + fixed_t iscale = FLOAT2FIXED(1 / vis->yscale); + frac = vis->startfrac; + xiscale = vis->xiscale; + double texturemid = vis->texturemid; + + auto viewport = RenderViewport::Instance(); + + if (vis->renderflags & RF_YFLIP) + { + sprflipvert = true; + spryscale = -spryscale; + iscale = -iscale; + texturemid -= vis->pic->GetHeight(); + sprtopscreen = viewport->CenterY + texturemid * spryscale; + } + else + { + sprflipvert = false; + sprtopscreen = viewport->CenterY - texturemid * spryscale; + } + + int x = vis->x1; + x2 = vis->x2; + + if (x < x2) + { + RenderTranslucentPass *translucentPass = thread->TranslucentPass.get(); + + while (x < x2) + { + if (!translucentPass->ClipSpriteColumnWithPortals(x, vis)) + drawerargs.DrawMaskedColumn(thread, x, iscale, tex, frac, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, false); + x++; + frac += xiscale; + } + } + } + + if (thread->MainThread) + NetUpdate(); + } +} diff --git a/src/swrenderer/things/r_sprite.h b/src/swrenderer/things/r_sprite.h new file mode 100644 index 000000000..678c90909 --- /dev/null +++ b/src/swrenderer/things/r_sprite.h @@ -0,0 +1,38 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visiblesprite.h" + +namespace swrenderer +{ + class RenderSprite : public VisibleSprite + { + public: + static void Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTexture *tex, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade, bool foggy, FDynamicColormap *basecolormap); + + protected: + void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) override; + + private: + fixed_t xscale = 0; + fixed_t startfrac = 0; // horizontal position of x1 + fixed_t xiscale = 0; // negative if flipped + + uint32_t Translation = 0; + uint32_t FillColor = 0; + + uint32_t dynlightcolor = 0; + }; +} diff --git a/src/swrenderer/things/r_visiblesprite.cpp b/src/swrenderer/things/r_visiblesprite.cpp new file mode 100644 index 000000000..b2e3da800 --- /dev/null +++ b/src/swrenderer/things/r_visiblesprite.cpp @@ -0,0 +1,458 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "g_levellocals.h" +#include "p_maputl.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/things/r_voxel.h" +#include "swrenderer/things/r_particle.h" +#include "swrenderer/things/r_sprite.h" +#include "swrenderer/things/r_wallsprite.h" +#include "swrenderer/things/r_playersprite.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/line/r_renderdrawsegment.h" +#include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + void VisibleSprite::Render(RenderThread *thread) + { + short *clipbot = thread->clipbot; + short *cliptop = thread->cliptop; + + VisibleSprite *spr = this; + + int i; + int x1, x2; + int r1, r2; + short topclip, botclip; + short *clip1, *clip2; + FSWColormap *colormap = spr->Light.BaseColormap; + int colormapnum = spr->Light.ColormapNum; + F3DFloor *rover; + + Clip3DFloors *clip3d = thread->Clip3D.get(); + + // [RH] Check for particles + if (spr->IsParticle()) + { + // kg3D - reject invisible parts + if ((clip3d->fake3D & FAKE3D_CLIPBOTTOM) && spr->gpos.Z <= clip3d->sclipBottom) return; + if ((clip3d->fake3D & FAKE3D_CLIPTOP) && spr->gpos.Z >= clip3d->sclipTop) return; + + spr->Render(thread, nullptr, nullptr, 0, 0); + return; + } + + x1 = spr->x1; + x2 = spr->x2; + + // [RH] Quickly reject sprites with bad x ranges. + if (x1 >= x2) + return; + + // Reject sprites outside the slice rendered by the thread + if (x2 < thread->X1 || x1 > thread->X2) + return; + + // [RH] Sprites split behind a one-sided line can also be discarded. + if (spr->sector == nullptr) + return; + + // kg3D - reject invisible parts + if ((clip3d->fake3D & FAKE3D_CLIPBOTTOM) && spr->gzt <= clip3d->sclipBottom) return; + if ((clip3d->fake3D & FAKE3D_CLIPTOP) && spr->gzb >= clip3d->sclipTop) return; + + // kg3D - correct colors now + CameraLight *cameraLight = CameraLight::Instance(); + if (!cameraLight->FixedColormap() && cameraLight->FixedLightLevel() < 0 && spr->sector->e && spr->sector->e->XFloor.lightlist.Size()) + { + if (!(clip3d->fake3D & FAKE3D_CLIPTOP)) + { + clip3d->sclipTop = spr->sector->ceilingplane.ZatPoint(ViewPos); + } + sector_t *sec = nullptr; + FDynamicColormap *mybasecolormap = nullptr; + for (i = spr->sector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) + { + if (clip3d->sclipTop <= spr->sector->e->XFloor.lightlist[i].plane.Zat0()) + { + rover = spr->sector->e->XFloor.lightlist[i].caster; + if (rover) + { + if (rover->flags & FF_DOUBLESHADOW && clip3d->sclipTop <= rover->bottom.plane->Zat0()) + { + break; + } + sec = rover->model; + if (rover->flags & FF_FADEWALLS) + { + mybasecolormap = sec->ColorMap; + } + else + { + mybasecolormap = spr->sector->e->XFloor.lightlist[i].extra_colormap; + } + } + break; + } + } + // found new values, recalculate + if (sec) + { + bool invertcolormap = (spr->RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + if (spr->RenderStyle.Flags & STYLEF_InvertSource) + invertcolormap = !invertcolormap; + + if (RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); + } + + bool isFullBright = !foggy && (renderflags & RF_FULLBRIGHT); + bool fadeToBlack = spr->RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0; + + int spriteshade = LightVisibility::LightLevelToShade(sec->lightlevel + LightVisibility::ActualExtraLight(spr->foggy), foggy); + + Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis(foggy) / MAX(MINZ, (double)spr->depth), spriteshade, mybasecolormap, isFullBright, invertcolormap, fadeToBlack); + } + } + + // [RH] Initialize the clipping arrays to their largest possible range + // instead of using a special "not clipped" value. This eliminates + // visual anomalies when looking down and should be faster, too. + topclip = 0; + botclip = viewheight; + + // killough 3/27/98: + // Clip the sprite against deep water and/or fake ceilings. + // [RH] rewrote this to be based on which part of the sector is really visible + + auto viewport = RenderViewport::Instance(); + + double scale = viewport->InvZtoScale * spr->idepth; + double hzb = -DBL_MAX, hzt = DBL_MAX; + + if (spr->IsVoxel() && spr->floorclip != 0) + { + hzb = spr->gzb; + } + + if (spr->heightsec && !(spr->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC)) + { // only things in specially marked sectors + if (spr->FakeFlatStat != WaterFakeSide::AboveCeiling) + { + double hz = spr->heightsec->floorplane.ZatPoint(spr->gpos); + int h = xs_RoundToInt(viewport->CenterY - (hz - ViewPos.Z) * scale); + + if (spr->FakeFlatStat == WaterFakeSide::BelowFloor) + { // seen below floor: clip top + if (!spr->IsVoxel() && h > topclip) + { + topclip = short(MIN(h, viewheight)); + } + hzt = MIN(hzt, hz); + } + else + { // seen in the middle: clip bottom + if (!spr->IsVoxel() && h < botclip) + { + botclip = MAX(0, h); + } + hzb = MAX(hzb, hz); + } + } + if (spr->FakeFlatStat != WaterFakeSide::BelowFloor && !(spr->heightsec->MoreFlags & SECF_FAKEFLOORONLY)) + { + double hz = spr->heightsec->ceilingplane.ZatPoint(spr->gpos); + int h = xs_RoundToInt(viewport->CenterY - (hz - ViewPos.Z) * scale); + + if (spr->FakeFlatStat == WaterFakeSide::AboveCeiling) + { // seen above ceiling: clip bottom + if (!spr->IsVoxel() && h < botclip) + { + botclip = MAX(0, h); + } + hzb = MAX(hzb, hz); + } + else + { // seen in the middle: clip top + if (!spr->IsVoxel() && h > topclip) + { + topclip = MIN(h, viewheight); + } + hzt = MIN(hzt, hz); + } + } + } + // killough 3/27/98: end special clipping for deep water / fake ceilings + else if (!spr->IsVoxel() && spr->floorclip) + { // [RH] Move floorclip stuff from R_DrawVisSprite to here + //int clip = ((FLOAT2FIXED(CenterY) - FixedMul (spr->texturemid - (spr->pic->GetHeight() << FRACBITS) + spr->floorclip, spr->yscale)) >> FRACBITS); + int clip = xs_RoundToInt(viewport->CenterY - (spr->texturemid - spr->pic->GetHeight() + spr->floorclip) * spr->yscale); + if (clip < botclip) + { + botclip = MAX(0, clip); + } + } + + if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) + { + if (!spr->IsVoxel()) + { + double hz = clip3d->sclipBottom; + if (spr->fakefloor) + { + double floorz = spr->fakefloor->top.plane->Zat0(); + if (ViewPos.Z > floorz && floorz == clip3d->sclipBottom) + { + hz = spr->fakefloor->bottom.plane->Zat0(); + } + } + int h = xs_RoundToInt(viewport->CenterY - (hz - ViewPos.Z) * scale); + if (h < botclip) + { + botclip = MAX(0, h); + } + } + hzb = MAX(hzb, clip3d->sclipBottom); + } + if (clip3d->fake3D & FAKE3D_CLIPTOP) + { + if (!spr->IsVoxel()) + { + double hz = clip3d->sclipTop; + if (spr->fakeceiling != nullptr) + { + double ceilingZ = spr->fakeceiling->bottom.plane->Zat0(); + if (ViewPos.Z < ceilingZ && ceilingZ == clip3d->sclipTop) + { + hz = spr->fakeceiling->top.plane->Zat0(); + } + } + int h = xs_RoundToInt(viewport->CenterY - (hz - ViewPos.Z) * scale); + if (h > topclip) + { + topclip = short(MIN(h, viewheight)); + } + } + hzt = MIN(hzt, clip3d->sclipTop); + } + + if (topclip >= botclip) + { + spr->Light.BaseColormap = colormap; + spr->Light.ColormapNum = colormapnum; + return; + } + + i = x2 - x1; + clip1 = clipbot + x1; + clip2 = cliptop + x1; + do + { + *clip1++ = botclip; + *clip2++ = topclip; + } while (--i); + + // Scan drawsegs from end to start for obscuring segs. + // The first drawseg that is closer than the sprite is the clip seg. + + DrawSegmentList *segmentlist = thread->DrawSegments.get(); + for (unsigned int groupIndex = 0; groupIndex < segmentlist->SegmentGroups.Size(); groupIndex++) + { + auto &group = segmentlist->SegmentGroups[groupIndex]; + if (group.x1 >= x2 || group.x2 <= x1) + continue; + + if (group.fardepth < spr->depth) + { + r1 = MAX(group.x1, x1); + r2 = MIN(group.x2, x2); + + // Clip bottom + clip1 = clipbot + r1; + clip2 = group.sprbottomclip + r1 - group.x1; + i = r2 - r1; + do + { + if (*clip1 > *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + + // Clip top + clip1 = cliptop + r1; + clip2 = group.sprtopclip + r1 - group.x1; + i = r2 - r1; + do + { + if (*clip1 < *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + else + { + //for (unsigned int index = segmentlist->BeginIndex(); index != segmentlist->EndIndex(); index++) + for (unsigned int index = group.BeginIndex; index != group.EndIndex; index++) + { + DrawSegment *ds = segmentlist->Segment(index); + + // [ZZ] portal handling here + //if (ds->CurrentPortalUniq != spr->CurrentPortalUniq) + // continue; + // [ZZ] WARNING: uncommenting the two above lines, totally breaks sprite clipping + + // kg3D - no clipping on fake segs + if (ds->fake) continue; + // determine if the drawseg obscures the sprite + if (ds->x1 >= x2 || ds->x2 <= x1 || + (!(ds->silhouette & SIL_BOTH) && ds->maskedtexturecol == nullptr && + !ds->bFogBoundary)) + { + // does not cover sprite + continue; + } + + r1 = MAX(ds->x1, x1); + r2 = MIN(ds->x2, x2); + + float neardepth, fardepth; + if (!spr->IsWallSprite()) + { + if (ds->sz1 < ds->sz2) + { + neardepth = ds->sz1, fardepth = ds->sz2; + } + else + { + neardepth = ds->sz2, fardepth = ds->sz1; + } + } + + // Check if sprite is in front of draw seg: + if ((!spr->IsWallSprite() && neardepth > spr->depth) || ((spr->IsWallSprite() || fardepth > spr->depth) && + (spr->gpos.Y - ds->curline->v1->fY()) * (ds->curline->v2->fX() - ds->curline->v1->fX()) - + (spr->gpos.X - ds->curline->v1->fX()) * (ds->curline->v2->fY() - ds->curline->v1->fY()) <= 0)) + { + RenderPortal *renderportal = thread->Portal.get(); + + // seg is behind sprite, so draw the mid texture if it has one + if (ds->CurrentPortalUniq == renderportal->CurrentPortalUniq && // [ZZ] instead, portal uniq check is made here + (ds->maskedtexturecol != nullptr || ds->bFogBoundary)) + { + RenderDrawSegment renderer(thread); + renderer.Render(ds, r1, r2); + } + + continue; + } + + // clip this piece of the sprite + // killough 3/27/98: optimized and made much shorter + // [RH] Optimized further (at least for VC++; + // other compilers should be at least as good as before) + + if (ds->silhouette & SIL_BOTTOM) //bottom sil + { + clip1 = clipbot + r1; + clip2 = ds->sprbottomclip + r1 - ds->x1; + i = r2 - r1; + do + { + if (*clip1 > *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + + if (ds->silhouette & SIL_TOP) // top sil + { + clip1 = cliptop + r1; + clip2 = ds->sprtopclip + r1 - ds->x1; + i = r2 - r1; + do + { + if (*clip1 < *clip2) + *clip1 = *clip2; + clip1++; + clip2++; + } while (--i); + } + } + } + } + + // all clipping has been performed, so draw the sprite + + if (!spr->IsVoxel()) + { + spr->Render(thread, clipbot, cliptop, 0, 0); + } + else + { + // If it is completely clipped away, don't bother drawing it. + if (cliptop[x2] >= clipbot[x2]) + { + for (i = x1; i < x2; ++i) + { + if (cliptop[i] < clipbot[i]) + { + break; + } + } + if (i == x2) + { + spr->Light.BaseColormap = colormap; + spr->Light.ColormapNum = colormapnum; + return; + } + } + // Add everything outside the left and right edges to the clipping array + // for R_DrawVisVoxel(). + if (x1 > 0) + { + fillshort(cliptop, x1, viewheight); + } + if (x2 < viewwidth - 1) + { + fillshort(cliptop + x2, viewwidth - x2, viewheight); + } + int minvoxely = spr->gzt <= hzt ? 0 : xs_RoundToInt((spr->gzt - hzt) / spr->yscale); + int maxvoxely = spr->gzb > hzb ? INT_MAX : xs_RoundToInt((spr->gzt - hzb) / spr->yscale); + spr->Render(thread, cliptop, clipbot, minvoxely, maxvoxely); + } + spr->Light.BaseColormap = colormap; + spr->Light.ColormapNum = colormapnum; + } +} diff --git a/src/swrenderer/things/r_visiblesprite.h b/src/swrenderer/things/r_visiblesprite.h new file mode 100644 index 000000000..7bd5b9de2 --- /dev/null +++ b/src/swrenderer/things/r_visiblesprite.h @@ -0,0 +1,80 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "swrenderer/line/r_line.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/things/r_visiblespritelist.h" + +#define MINZ double((2048*4) / double(1 << 20)) + +namespace swrenderer +{ + class RenderThread; + + class VisibleSprite + { + public: + VisibleSprite() { RenderStyle = STYLE_Normal; } + virtual ~VisibleSprite() { } + + void Render(RenderThread *thread); + + bool IsCurrentPortalUniq(int portalUniq) const { return CurrentPortalUniq == portalUniq; } + const FVector3 &WorldPos() const { return gpos; } + + double SortDist2D() const { return DVector2(deltax, deltay).LengthSquared(); } + float SortDist() const { return idepth; } + + protected: + virtual bool IsParticle() const { return false; } + virtual bool IsVoxel() const { return false; } + virtual bool IsWallSprite() const { return false; } + + virtual void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) = 0; + + FTexture *pic = nullptr; + + short x1 = 0, x2 = 0; + float gzb = 0.0f, gzt = 0.0f; // global bottom / top for silhouette clipping + + double floorclip = 0.0; + + double texturemid = 0.0; // floorclip + float yscale = 0.0f; // voxel and floorclip + + sector_t *heightsec = nullptr; // height sector for underwater/fake ceiling + WaterFakeSide FakeFlatStat = WaterFakeSide::Center; // which side of fake/floor ceiling sprite is on + + F3DFloor *fakefloor = nullptr; // 3d floor clipping + F3DFloor *fakeceiling = nullptr; + + FVector3 gpos = { 0.0f, 0.0f, 0.0f }; // origin in world coordinates + sector_t *sector = nullptr; // sector this sprite is in + + ColormapLight Light; + float Alpha = 0.0f; + FRenderStyle RenderStyle; + bool foggy = false; + short renderflags = 0; + + float depth = 0.0f; // Sort (draw segments), also light + + float deltax = 0.0f, deltay = 0.0f; // Sort (2d/voxel version) + float idepth = 0.0f; // Sort (non-voxel version) + + int CurrentPortalUniq = 0; // to identify the portal that this thing is in. used for clipping. + }; +} diff --git a/src/swrenderer/things/r_visiblespritelist.cpp b/src/swrenderer/things/r_visiblespritelist.cpp new file mode 100644 index 000000000..eac6c91f3 --- /dev/null +++ b/src/swrenderer/things/r_visiblespritelist.cpp @@ -0,0 +1,104 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "g_levellocals.h" +#include "p_maputl.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/things/r_visiblespritelist.h" +#include "swrenderer/r_memory.h" + +namespace swrenderer +{ + void VisibleSpriteList::Clear() + { + Sprites.Clear(); + StartIndices.Clear(); + SortedSprites.Clear(); + DrewAVoxel = false; + } + + void VisibleSpriteList::PushPortal() + { + StartIndices.Push(Sprites.Size()); + } + + void VisibleSpriteList::PopPortal() + { + Sprites.Resize(StartIndices.Last()); + StartIndices.Pop(); + } + + void VisibleSpriteList::Push(VisibleSprite *sprite, bool isVoxel) + { + Sprites.Push(sprite); + if (isVoxel) + DrewAVoxel = true; + } + + void VisibleSpriteList::Sort() + { + bool compare2d = DrewAVoxel; + + unsigned int first = StartIndices.Size() == 0 ? 0 : StartIndices.Last(); + unsigned int count = Sprites.Size() - first; + + SortedSprites.Resize(count); + + if (count == 0) + return; + + if (!(i_compatflags & COMPATF_SPRITESORT)) + { + for (unsigned int i = 0; i < count; i++) + SortedSprites[i] = Sprites[first + i]; + } + else + { + // If the compatibility option is on sprites of equal distance need to + // be sorted in inverse order. This is most easily achieved by + // filling the sort array backwards before the sort. + for (unsigned int i = 0; i < count; i++) + SortedSprites[i] = Sprites[first + count - i - 1]; + } + + if (compare2d) + { + // This is an alternate version, for when one or more voxel is in view. + // It does a 2D distance test based on whichever one is furthest from + // the viewpoint. + + std::stable_sort(&SortedSprites[0], &SortedSprites[count], [](VisibleSprite *a, VisibleSprite *b) -> bool + { + return a->SortDist2D() < b->SortDist2D(); + }); + } + else + { + // This is the standard version, which does a simple test based on depth. + + std::stable_sort(&SortedSprites[0], &SortedSprites[count], [](VisibleSprite *a, VisibleSprite *b) -> bool + { + return a->SortDist() > b->SortDist(); + }); + } + } +} diff --git a/src/swrenderer/things/r_visiblespritelist.h b/src/swrenderer/things/r_visiblespritelist.h new file mode 100644 index 000000000..2f53514fa --- /dev/null +++ b/src/swrenderer/things/r_visiblespritelist.h @@ -0,0 +1,37 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +namespace swrenderer +{ + struct DrawSegment; + class VisibleSprite; + + class VisibleSpriteList + { + public: + void Clear(); + void PushPortal(); + void PopPortal(); + void Push(VisibleSprite *sprite, bool isVoxel = false); + void Sort(); + + TArray SortedSprites; + + private: + TArray Sprites; + TArray StartIndices; + bool DrewAVoxel = false; + }; +} diff --git a/src/swrenderer/things/r_voxel.cpp b/src/swrenderer/things/r_voxel.cpp new file mode 100644 index 000000000..b337cd857 --- /dev/null +++ b/src/swrenderer/things/r_voxel.cpp @@ -0,0 +1,984 @@ +// +//--------------------------------------------------------------------------- +// +// Voxel rendering +// Copyright(c) 1993 - 1997 Ken Silverman +// Copyright(c) 1998 - 2016 Randy Heit +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#include +#include "templates.h" +#include "doomdef.h" +#include "sbar.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "r_data/sprites.h" +#include "d_net.h" +#include "po_man.h" +#include "r_utility.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/drawers/r_thread.h" +#include "swrenderer/things/r_visiblesprite.h" +#include "swrenderer/things/r_voxel.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/viewport/r_spritedrawer.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) + +namespace swrenderer +{ + void RenderVoxel::Project(RenderThread *thread, AActor *thing, DVector3 pos, FVoxelDef *voxel, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade, bool foggy, FDynamicColormap *basecolormap) + { + // transform the origin point + double tr_x = pos.X - ViewPos.X; + double tr_y = pos.Y - ViewPos.Y; + + double tz = tr_x * ViewTanCos + tr_y * ViewTanSin; + double tx = tr_x * ViewSin - tr_y * ViewCos; + + // [RH] Flip for mirrors + RenderPortal *renderportal = thread->Portal.get(); + if (renderportal->MirrorFlags & RF_XFLIP) + { + tx = -tx; + } + //tx2 = tx >> 4; + + // too far off the side? + if (fabs(tx / 128) > fabs(tz)) + { + return; + } + + double xscale = spriteScale.X * voxel->Scale; + double yscale = spriteScale.Y * voxel->Scale; + double piv = voxel->Voxel->Mips[0].Pivot.Z; + double gzt = pos.Z + yscale * piv - thing->Floorclip; + double gzb = pos.Z + yscale * (piv - voxel->Voxel->Mips[0].SizeZ); + if (gzt <= gzb) + return; + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + sector_t *heightsec = thing->Sector->GetHeightSec(); + + if (heightsec != nullptr) // only clip things which are in special sectors + { + if (fakeside == WaterFakeSide::AboveCeiling) + { + if (gzt < heightsec->ceilingplane.ZatPoint(pos)) + return; + } + else if (fakeside == WaterFakeSide::BelowFloor) + { + if (gzb >= heightsec->floorplane.ZatPoint(pos)) + return; + } + else + { + if (gzt < heightsec->floorplane.ZatPoint(pos)) + return; + if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint(pos)) + return; + } + } + + RenderVoxel *vis = thread->FrameMemory->NewObject(); + + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->xscale = FLOAT2FIXED(xscale); + vis->yscale = (float)yscale; + vis->x1 = renderportal->WindowLeft; + vis->x2 = renderportal->WindowRight; + vis->idepth = 1 / MINZ; + vis->floorclip = thing->Floorclip; + + pos.Z -= thing->Floorclip; + + vis->Angle = thing->Angles.Yaw + voxel->AngleOffset; + + int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; + if (voxelspin != 0) + { + DAngle ang = double(I_FPSTime()) * voxelspin / 1000; + vis->Angle -= ang; + } + + vis->pa.vpos = { (float)ViewPos.X, (float)ViewPos.Y, (float)ViewPos.Z }; + vis->pa.vang = FAngle((float)ViewAngle.Degrees); + + // killough 3/27/98: save sector for special clipping later + vis->heightsec = heightsec; + vis->sector = thing->Sector; + + vis->depth = (float)tz; + vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; + vis->gzb = (float)gzb; // [RH] use gzb, not thing->z + vis->gzt = (float)gzt; // killough 3/27/98 + vis->deltax = float(pos.X - ViewPos.X); + vis->deltay = float(pos.Y - ViewPos.Y); + vis->renderflags = renderflags; + if (thing->flags5 & MF5_BRIGHT) + vis->renderflags |= RF_FULLBRIGHT; // kg3D + vis->RenderStyle = thing->RenderStyle; + vis->FillColor = thing->fillcolor; + vis->Translation = thing->Translation; // [RH] thing translation table + vis->FakeFlatStat = fakeside; + vis->Alpha = float(thing->Alpha); + vis->fakefloor = fakefloor; + vis->fakeceiling = fakeceiling; + vis->Light.ColormapNum = 0; + vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP; + //vis->bSplitSprite = false; + + vis->voxel = voxel->Voxel; + vis->foggy = foggy; + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + bool invertcolormap = (vis->RenderStyle.Flags & STYLEF_InvertOverlay) != 0; + + if (vis->RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + // Sprites that are added to the scene must fade to black. + if (vis->RenderStyle == LegacyRenderStyles[STYLE_Add] && basecolormap->Fade != 0) + { + basecolormap = GetSpecialLights(basecolormap->Color, 0, basecolormap->Desaturate); + } + + bool fullbright = !vis->foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT)); + bool fadeToBlack = (vis->RenderStyle.Flags & STYLEF_FadeToBlack) != 0; + + vis->Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis(foggy) / MAX(tz, MINZ), spriteshade, basecolormap, fullbright, invertcolormap, fadeToBlack); + + thread->SpriteList->Push(vis, true); + } + + void RenderVoxel::Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) + { + auto spr = this; + auto viewport = RenderViewport::Instance(); + + SpriteDrawerArgs drawerargs; + drawerargs.SetLight(spr->Light.BaseColormap, 0, spr->Light.ColormapNum << FRACBITS); + + FDynamicColormap *basecolormap = (FDynamicColormap*)spr->Light.BaseColormap; + bool visible = drawerargs.SetStyle(spr->RenderStyle, spr->Alpha, spr->Translation, spr->FillColor, basecolormap); + if (!visible) + return; + + int flags = 0; + + /* + if (colfunc == fuzzcolfunc || colfunc == R_FillColumn) + { + flags = DVF_OFFSCREEN | DVF_SPANSONLY; + } + else if (colfunc != basecolfunc) + { + flags = DVF_OFFSCREEN; + } + if (flags != 0) + { + CheckOffscreenBuffer(viewport->RenderTarget->GetWidth(), viewport->RenderTarget->GetHeight(), !!(flags & DVF_SPANSONLY)); + } + if (spr->bInMirror) + { + flags |= DVF_MIRRORED; + } + */ + + // Render the voxel, either directly to the screen or offscreen. + DrawVoxel(thread, drawerargs, spr->pa.vpos, spr->pa.vang, spr->gpos, spr->Angle, + spr->xscale, FLOAT2FIXED(spr->yscale), spr->voxel, cliptop, clipbottom, + minZ, maxZ, flags); + + /* + // Blend the voxel, if that's what we need to do. + if ((flags & ~DVF_MIRRORED) != 0) + { + int pixelsize = viewport->RenderTarget->IsBgra() ? 4 : 1; + for (int x = 0; x < viewwidth; ++x) + { + if (!(flags & DVF_SPANSONLY) && (x & 3) == 0) + { + rt_initcols(OffscreenColorBuffer + x * OffscreenBufferHeight); + } + for (FCoverageBuffer::Span *span = OffscreenCoverageBuffer->Spans[x]; span != NULL; span = span->NextSpan) + { + if (flags & DVF_SPANSONLY) + { + dc_x = x; + dc_yl = span->Start; + dc_yh = span->Stop - 1; + dc_count = span->Stop - span->Start; + dc_dest = (ylookup[span->Start] + x) * pixelsize + dc_destorg; + colfunc(); + } + else + { + rt_span_coverage(x, span->Start, span->Stop - 1); + } + } + if (!(flags & DVF_SPANSONLY) && (x & 3) == 3) + { + rt_draw4cols(x - 3); + } + } + } + */ + } + + void RenderVoxel::DrawVoxel( + RenderThread *thread, SpriteDrawerArgs &drawerargs, + const FVector3 &globalpos, FAngle viewangle, const FVector3 &dasprpos, DAngle dasprang, fixed_t daxscale, fixed_t dayscale, FVoxel *voxobj, + short *daumost, short *dadmost, int minslabz, int maxslabz, int flags) + { + int i, j, k, x, y, syoff, ggxstart, ggystart, nxoff; + fixed_t cosang, sinang, sprcosang, sprsinang; + int backx, backy, gxinc, gyinc; + int daxscalerecip, dayscalerecip, cnt, gxstart, gystart, dazscale; + int lx, rx, nx, ny, x1=0, y1=0, x2=0, y2=0, yinc=0; + int yoff, xs=0, ys=0, xe, ye, xi=0, yi=0, cbackx, cbacky, dagxinc, dagyinc; + kvxslab_t *voxptr, *voxend; + FVoxelMipLevel *mip; + int z1a[64], z2a[64], yplc[64]; + + auto viewport = RenderViewport::Instance(); + + const int nytooclose = centerxwide * 2100, nytoofar = 32768*32768 - 1048576; + const int xdimenscale = FLOAT2FIXED(centerxwide * viewport->YaspectMul / 160); + const double centerxwide_f = centerxwide; + const double centerxwidebig_f = centerxwide_f * 65536*65536*8; + + // Convert to Build's coordinate system. + fixed_t globalposx = xs_Fix<4>::ToFix(globalpos.X); + fixed_t globalposy = xs_Fix<4>::ToFix(-globalpos.Y); + fixed_t globalposz = xs_Fix<8>::ToFix(-globalpos.Z); + + fixed_t dasprx = xs_Fix<4>::ToFix(dasprpos.X); + fixed_t daspry = xs_Fix<4>::ToFix(-dasprpos.Y); + fixed_t dasprz = xs_Fix<8>::ToFix(-dasprpos.Z); + + // Shift the scales from 16 bits of fractional precision to 6. + // Also do some magic voodoo scaling to make them the right size. + daxscale = daxscale / (0xC000 >> 6); + dayscale = dayscale / (0xC000 >> 6); + if (daxscale <= 0 || dayscale <= 0) + { + // won't be visible. + return; + } + + angle_t viewang = viewangle.BAMs(); + cosang = FLOAT2FIXED(viewangle.Cos()) >> 2; + sinang = FLOAT2FIXED(-viewangle.Sin()) >> 2; + sprcosang = FLOAT2FIXED(dasprang.Cos()) >> 2; + sprsinang = FLOAT2FIXED(-dasprang.Sin()) >> 2; + + // Select mip level + i = abs(DMulScale6(dasprx - globalposx, cosang, daspry - globalposy, sinang)); + i = DivScale6(i, MIN(daxscale, dayscale)); + j = xs_Fix<13>::ToFix(viewport->FocalLengthX); + for (k = 0; i >= j && k < voxobj->NumMips; ++k) + { + i >>= 1; + } + if (k >= voxobj->NumMips) k = voxobj->NumMips - 1; + + mip = &voxobj->Mips[k]; if (mip->SlabData == NULL) return; + + minslabz >>= k; + maxslabz >>= k; + + daxscale <<= (k+8); dayscale <<= (k+8); + dazscale = FixedDiv(dayscale, FLOAT2FIXED(viewport->BaseYaspectMul)); + daxscale = fixed_t(daxscale / viewport->YaspectMul); + daxscale = Scale(daxscale, xdimenscale, centerxwide << 9); + dayscale = Scale(dayscale, FixedMul(xdimenscale, viewport->viewingrangerecip), centerxwide << 9); + + daxscalerecip = (1<<30) / daxscale; + dayscalerecip = (1<<30) / dayscale; + + fixed_t piv_x = fixed_t(mip->Pivot.X*256.); + fixed_t piv_y = fixed_t(mip->Pivot.Y*256.); + fixed_t piv_z = fixed_t(mip->Pivot.Z*256.); + + x = FixedMul(globalposx - dasprx, daxscalerecip); + y = FixedMul(globalposy - daspry, daxscalerecip); + backx = (DMulScale10(x, sprcosang, y, sprsinang) + piv_x) >> 8; + backy = (DMulScale10(y, sprcosang, x, -sprsinang) + piv_y) >> 8; + cbackx = clamp(backx, 0, mip->SizeX - 1); + cbacky = clamp(backy, 0, mip->SizeY - 1); + + sprcosang = MulScale14(daxscale, sprcosang); + sprsinang = MulScale14(daxscale, sprsinang); + + x = (dasprx - globalposx) - DMulScale18(piv_x, sprcosang, piv_y, -sprsinang); + y = (daspry - globalposy) - DMulScale18(piv_y, sprcosang, piv_x, sprsinang); + + cosang = FixedMul(cosang, dayscalerecip); + sinang = FixedMul(sinang, dayscalerecip); + + gxstart = y*cosang - x*sinang; + gystart = x*cosang + y*sinang; + gxinc = DMulScale10(sprsinang, cosang, sprcosang, -sinang); + gyinc = DMulScale10(sprcosang, cosang, sprsinang, sinang); + if ((abs(globalposz - dasprz) >> 10) >= abs(dazscale)) return; + + x = 0; y = 0; j = MAX(mip->SizeX, mip->SizeY); + fixed_t *ggxinc = (fixed_t *)alloca((j + 1) * sizeof(fixed_t) * 2); + fixed_t *ggyinc = ggxinc + (j + 1); + for (i = 0; i <= j; i++) + { + ggxinc[i] = x; x += gxinc; + ggyinc[i] = y; y += gyinc; + } + + syoff = DivScale21(globalposz - dasprz, FixedMul(dazscale, 0xE800)) + (piv_z << 7); + yoff = (abs(gxinc) + abs(gyinc)) >> 1; + + for (cnt = 0; cnt < 8; cnt++) + { + switch (cnt) + { + case 0: xs = 0; ys = 0; xi = 1; yi = 1; break; + case 1: xs = mip->SizeX-1; ys = 0; xi = -1; yi = 1; break; + case 2: xs = 0; ys = mip->SizeY-1; xi = 1; yi = -1; break; + case 3: xs = mip->SizeX-1; ys = mip->SizeY-1; xi = -1; yi = -1; break; + case 4: xs = 0; ys = cbacky; xi = 1; yi = 2; break; + case 5: xs = mip->SizeX-1; ys = cbacky; xi = -1; yi = 2; break; + case 6: xs = cbackx; ys = 0; xi = 2; yi = 1; break; + case 7: xs = cbackx; ys = mip->SizeY-1; xi = 2; yi = -1; break; + } + xe = cbackx; ye = cbacky; + if (cnt < 4) + { + if ((xi < 0) && (xe >= xs)) continue; + if ((xi > 0) && (xe <= xs)) continue; + if ((yi < 0) && (ye >= ys)) continue; + if ((yi > 0) && (ye <= ys)) continue; + } + else + { + if ((xi < 0) && (xe > xs)) continue; + if ((xi > 0) && (xe < xs)) continue; + if ((yi < 0) && (ye > ys)) continue; + if ((yi > 0) && (ye < ys)) continue; + xe += xi; ye += yi; + } + + i = sgn(ys - backy) + sgn(xs - backx) * 3 + 4; + switch(i) + { + case 6: case 7: x1 = 0; y1 = 0; break; + case 8: case 5: x1 = gxinc; y1 = gyinc; break; + case 0: case 3: x1 = gyinc; y1 = -gxinc; break; + case 2: case 1: x1 = gxinc+gyinc; y1 = gyinc-gxinc; break; + } + switch(i) + { + case 2: case 5: x2 = 0; y2 = 0; break; + case 0: case 1: x2 = gxinc; y2 = gyinc; break; + case 8: case 7: x2 = gyinc; y2 = -gxinc; break; + case 6: case 3: x2 = gxinc+gyinc; y2 = gyinc-gxinc; break; + } + BYTE oand = (1 << int(xs 0) { dagxinc = gxinc; dagyinc = FixedMul(gyinc, viewport->viewingrangerecip); } + else { dagxinc = -gxinc; dagyinc = -FixedMul(gyinc, viewport->viewingrangerecip); } + + /* Fix for non 90 degree viewing ranges */ + nxoff = FixedMul(x2 - x1, viewport->viewingrangerecip); + x1 = FixedMul(x1, viewport->viewingrangerecip); + + ggxstart = gxstart + ggyinc[ys]; + ggystart = gystart - ggxinc[ys]; + + for (x = xs; x != xe; x += xi) + { + BYTE *slabxoffs = &mip->SlabData[mip->OffsetX[x]]; + short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)]; + + nx = FixedMul(ggxstart + ggxinc[x], viewport->viewingrangerecip) + x1; + ny = ggystart + ggyinc[x]; + for (y = ys; y != ye; y += yi, nx += dagyinc, ny -= dagxinc) + { + if ((ny <= nytooclose) || (ny >= nytoofar)) continue; + voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]); + voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]); + if (voxptr >= voxend) continue; + + lx = xs_RoundToInt(nx * centerxwide_f / (ny + y1)) + centerx; + if (lx < 0) lx = 0; + rx = xs_RoundToInt((nx + nxoff) * centerxwide_f / (ny + y2)) + centerx; + if (rx > viewwidth) rx = viewwidth; + if (rx <= lx) continue; + + if (flags & DVF_MIRRORED) + { + int t = viewwidth - lx; + lx = viewwidth - rx; + rx = t; + } + + fixed_t l1 = xs_RoundToInt(centerxwidebig_f / (ny - yoff)); + fixed_t l2 = xs_RoundToInt(centerxwidebig_f / (ny + yoff)); + for (; voxptr < voxend; voxptr = (kvxslab_t *)((BYTE *)voxptr + voxptr->zleng + 3)) + { + const BYTE *col = voxptr->col; + int zleng = voxptr->zleng; + int ztop = voxptr->ztop; + fixed_t z1, z2; + + if (ztop < minslabz) + { + int diff = minslabz - ztop; + ztop = minslabz; + col += diff; + zleng -= diff; + } + if (ztop + zleng > maxslabz) + { + int diff = ztop + zleng - maxslabz; + zleng -= diff; + } + if (zleng <= 0) continue; + + j = (ztop << 15) - syoff; + if (j < 0) + { + k = j + (zleng << 15); + if (k < 0) + { + if ((voxptr->backfacecull & oand32) == 0) continue; + z2 = MulScale32(l2, k) + centery; /* Below slab */ + } + else + { + if ((voxptr->backfacecull & oand) == 0) continue; /* Middle of slab */ + z2 = MulScale32(l1, k) + centery; + } + z1 = MulScale32(l1, j) + centery; + } + else + { + if ((voxptr->backfacecull & oand16) == 0) continue; + z1 = MulScale32(l2, j) + centery; /* Above slab */ + z2 = MulScale32(l1, j + (zleng << 15)) + centery; + } + + if (z2 <= z1) continue; + + if (zleng == 1) + { + yinc = 0; + } + else + { + if (z2-z1 >= 1024) yinc = FixedDiv(zleng, z2 - z1); + else yinc = (((1 << 24) - 1) / (z2 - z1)) * zleng >> 8; + } + // [RH] Clip each column separately, not just by the first one. + for (int stripwidth = MIN(countof(z1a), rx - lx), lxt = lx; + lxt < rx; + (lxt += countof(z1a)), stripwidth = MIN(countof(z1a), rx - lxt)) + { + // Calculate top and bottom pixels locations + for (int xxx = 0; xxx < stripwidth; ++xxx) + { + if (zleng == 1) + { + yplc[xxx] = 0; + z1a[xxx] = MAX(z1, daumost[lxt + xxx]); + } + else + { + if (z1 < daumost[lxt + xxx]) + { + yplc[xxx] = yinc * (daumost[lxt + xxx] - z1); + z1a[xxx] = daumost[lxt + xxx]; + } + else + { + yplc[xxx] = 0; + z1a[xxx] = z1; + } + } + z2a[xxx] = MIN(z2, dadmost[lxt + xxx]); + } + + const uint8_t *columnColors = col; + bool bgra = viewport->RenderTarget->IsBgra(); + if (bgra) + { + // The true color slab data array is identical, except its using uint32 instead of uint8. + // + // We can find the same slab column by calculating the offset from the start of SlabData + // and use that to offset into the BGRA version of the same data. + columnColors = (const uint8_t *)(&mip->SlabDataBgra[0] + (ptrdiff_t)(col - mip->SlabData)); + } + + // Find top and bottom pixels that match and draw them as one strip + for (int xxl = 0, xxr; xxl < stripwidth; ) + { + if (z1a[xxl] >= z2a[xxl]) + { // No column here + xxl++; + continue; + } + int z1 = z1a[xxl]; + int z2 = z2a[xxl]; + // How many columns share the same extents? + for (xxr = xxl + 1; xxr < stripwidth; ++xxr) + { + if (z1a[xxr] != z1 || z2a[xxr] != z2) + break; + } + + for (int x = xxl; x < xxr; ++x) + { + drawerargs.SetDest(lxt + x, z1); + drawerargs.SetCount(z2 - z1); + drawerargs.DrawVoxelColumn(thread, yplc[xxl], yinc, columnColors, zleng); + } + + /* + if (!(flags & DVF_OFFSCREEN)) + { + // Draw directly to the screen. + R_DrawSlab(xxr - xxl, yplc[xxl], z2 - z1, yinc, col, (ylookup[z1] + lxt + xxl) * pixelsize + dc_destorg); + } + else + { + // Record the area covered and possibly draw to an offscreen buffer. + dc_yl = z1; + dc_yh = z2 - 1; + dc_count = z2 - z1; + dc_iscale = yinc; + for (int x = xxl; x < xxr; ++x) + { + OffscreenCoverageBuffer->InsertSpan(lxt + x, z1, z2); + if (!(flags & DVF_SPANSONLY)) + { + dc_x = lxt + x; + rt_initcols(OffscreenColorBuffer + (dc_x & ~3) * OffscreenBufferHeight); + dc_source = col; + dc_source2 = nullptr; + dc_texturefrac = yplc[xxl]; + hcolfunc_pre(); + } + } + } + */ + + xxl = xxr; + } + } + } + } + } + } + } + + kvxslab_t *RenderVoxel::GetSlabStart(const FVoxelMipLevel &mip, int x, int y) + { + return (kvxslab_t *)&mip.SlabData[mip.OffsetX[x] + (int)mip.OffsetXY[x * (mip.SizeY + 1) + y]]; + } + + kvxslab_t *RenderVoxel::GetSlabEnd(const FVoxelMipLevel &mip, int x, int y) + { + return GetSlabStart(mip, x, y + 1); + } + + kvxslab_t *RenderVoxel::NextSlab(kvxslab_t *slab) + { + return (kvxslab_t*)(((uint8_t*)slab) + 3 + slab->zleng); + } + + void RenderVoxel::Deinit() + { + // Free offscreen buffer + if (OffscreenColorBuffer != nullptr) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = nullptr; + } + if (OffscreenCoverageBuffer != nullptr) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = nullptr; + } + OffscreenBufferHeight = OffscreenBufferWidth = 0; + } + + void RenderVoxel::CheckOffscreenBuffer(int width, int height, bool spansonly) + { + // Allocates the offscreen coverage buffer and optionally the offscreen + // color buffer. If they already exist but are the wrong size, they will + // be reallocated. + + if (OffscreenCoverageBuffer == nullptr) + { + assert(OffscreenColorBuffer == nullptr && "The color buffer cannot exist without the coverage buffer"); + OffscreenCoverageBuffer = new FCoverageBuffer(width); + } + else if (OffscreenCoverageBuffer->NumLists != (unsigned)width) + { + delete OffscreenCoverageBuffer; + OffscreenCoverageBuffer = new FCoverageBuffer(width); + if (OffscreenColorBuffer != nullptr) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = nullptr; + } + } + else + { + OffscreenCoverageBuffer->Clear(); + } + + if (!spansonly) + { + if (OffscreenColorBuffer == nullptr) + { + OffscreenColorBuffer = new uint8_t[width * height * 4]; + } + else if (OffscreenBufferWidth != width || OffscreenBufferHeight != height) + { + delete[] OffscreenColorBuffer; + OffscreenColorBuffer = new uint8_t[width * height * 4]; + } + } + OffscreenBufferWidth = width; + OffscreenBufferHeight = height; + } + + FCoverageBuffer *RenderVoxel::OffscreenCoverageBuffer; + int RenderVoxel::OffscreenBufferWidth; + int RenderVoxel::OffscreenBufferHeight; + uint8_t *RenderVoxel::OffscreenColorBuffer; + + //////////////////////////////////////////////////////////////////////////// + + FCoverageBuffer::FCoverageBuffer(int lists) + : Spans(nullptr), FreeSpans(nullptr) + { + NumLists = lists; + Spans = new Span *[lists]; + memset(Spans, 0, sizeof(Span*)*lists); + } + + FCoverageBuffer::~FCoverageBuffer() + { + if (Spans != nullptr) + { + delete[] Spans; + } + } + + void FCoverageBuffer::Clear() + { + SpanArena.FreeAll(); + memset(Spans, 0, sizeof(Span*)*NumLists); + FreeSpans = nullptr; + } + + void FCoverageBuffer::InsertSpan(int listnum, int start, int stop) + { + // start is inclusive. + // stop is exclusive. + + assert(unsigned(listnum) < NumLists); + assert(start < stop); + + Span **span_p = &Spans[listnum]; + Span *span; + + if (*span_p == nullptr || (*span_p)->Start > stop) + { // This list is empty or the first entry is after this one, so we can just insert the span. + goto addspan; + } + + // Insert the new span in order, merging with existing ones. + while (*span_p != nullptr) + { + if ((*span_p)->Stop < start) // ===== (existing span) + { // Span ends before this one starts. // ++++ (new span) + span_p = &(*span_p)->NextSpan; + continue; + } + + // Does the new span overlap or abut the existing one? + if ((*span_p)->Start <= start) + { + if ((*span_p)->Stop >= stop) // ============= + { // The existing span completely covers this one. // +++++ + return; + } + extend: // Extend the existing span with the new one. // ====== + span = *span_p; // +++++++ + span->Stop = stop; // (or) +++++ + + // Free up any spans we just covered up. + span_p = &(*span_p)->NextSpan; + while (*span_p != nullptr && (*span_p)->Start <= stop && (*span_p)->Stop <= stop) + { + Span *span = *span_p; // ====== ====== + *span_p = span->NextSpan; // +++++++++++++ + span->NextSpan = FreeSpans; + FreeSpans = span; + } + if (*span_p != nullptr && (*span_p)->Start <= stop) // ======= ======== + { // Our new span connects two existing spans. // ++++++++++++++ + // They should all be collapsed into a single span. + span->Stop = (*span_p)->Stop; + span = *span_p; + *span_p = span->NextSpan; + span->NextSpan = FreeSpans; + FreeSpans = span; + } + goto check; + } + else if ((*span_p)->Start <= stop) // ===== + { // The new span extends the existing span from // ++++ + // the beginning. // (or) ++++ + (*span_p)->Start = start; + if ((*span_p)->Stop < stop) + { // The new span also extends the existing span // ====== + // at the bottom // ++++++++++++++ + goto extend; + } + goto check; + } + else // ====== + { // No overlap, so insert a new span. // +++++ + goto addspan; + } + } + // Append a new span to the end of the list. + addspan: + span = AllocSpan(); + span->NextSpan = *span_p; + span->Start = start; + span->Stop = stop; + *span_p = span; + check: +#ifdef _DEBUG + // Validate the span list: Spans must be in order, and there must be + // at least one pixel between spans. + for (span = Spans[listnum]; span != nullptr; span = span->NextSpan) + { + assert(span->Start < span->Stop); + if (span->NextSpan != nullptr) + { + assert(span->Stop < span->NextSpan->Start); + } + } +#endif + ; + } + + FCoverageBuffer::Span *FCoverageBuffer::AllocSpan() + { + Span *span; + + if (FreeSpans != nullptr) + { + span = FreeSpans; + FreeSpans = span->NextSpan; + } + else + { + span = (Span *)SpanArena.Alloc(sizeof(Span)); + } + return span; + } + +#if 0 + void RenderVoxel::Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) + { + auto sprite = this; + auto viewport = RenderViewport::Instance(); + + FDynamicColormap *basecolormap = static_cast(sprite->Light.BaseColormap); + + SpriteDrawerArgs drawerargs; + drawerargs.SetLight(sprite->Light.BaseColormap, 0, sprite->Light.ColormapNum << FRACBITS); + + bool visible = drawerargs.SetStyle(sprite->RenderStyle, sprite->Alpha, sprite->Translation, sprite->FillColor, basecolormap); + if (!visible) + return; + + DVector3 view_origin = { sprite->pa.vpos.X, sprite->pa.vpos.Y, sprite->pa.vpos.Z }; + FAngle view_angle = sprite->pa.vang; + DVector3 sprite_origin = { sprite->gpos.X, sprite->gpos.Y, sprite->gpos.Z }; + DAngle sprite_angle = sprite->Angle; + double sprite_xscale = FIXED2DBL(sprite->xscale); + double sprite_yscale = sprite->yscale; + FVoxel *voxel = sprite->voxel; + + // Select mipmap level: + + double viewSin = view_angle.Cos(); + double viewCos = view_angle.Sin(); + double logmip = fabs((view_origin.X - sprite_origin.X) * viewCos - (view_origin.Y - sprite_origin.Y) * viewSin); + int miplevel = 0; + while (miplevel < voxel->NumMips - 1 && logmip >= viewport->FocalLengthX) + { + logmip *= 0.5; + miplevel++; + } + + const FVoxelMipLevel &mip = voxel->Mips[miplevel]; + if (mip.SlabData == nullptr) + return; + + minZ >>= miplevel; + maxZ >>= miplevel; + sprite_xscale *= (1 << miplevel); + sprite_yscale *= (1 << miplevel); + + // Find voxel cube eigenvectors and origin in world space: + + double spriteSin = sprite_angle.Sin(); + double spriteCos = sprite_angle.Cos(); + + DVector2 dirX(spriteSin * sprite_xscale, -spriteCos * sprite_xscale); + DVector2 dirY(spriteCos * sprite_xscale, spriteSin * sprite_xscale); + double dirZ = -sprite_yscale; + + DVector3 voxel_origin = sprite_origin; + voxel_origin.X -= dirX.X * mip.Pivot.X + dirX.Y * mip.Pivot.Y; + voxel_origin.Y -= dirY.X * mip.Pivot.X + dirY.Y * mip.Pivot.Y; + voxel_origin.Z -= dirZ * mip.Pivot.Z; + + // Voxel cube walking directions: + + int startX[4] = { 0, mip.SizeX - 1, 0, mip.SizeX - 1 }; + int startY[4] = { 0, 0, mip.SizeY - 1, mip.SizeY - 1 }; + int stepX[4] = { 1, -1, 1, -1 }; + int stepY[4] = { 1, 1, -1, -1 }; + + // The point in cube mipmap local space where voxel sides change from front to backfacing: + + double dx = (view_origin.X - sprite_origin.X) / sprite_xscale; + double dy = (view_origin.Y - sprite_origin.Y) / sprite_xscale; + int backX = (int)(dx * spriteCos - dy * spriteSin + mip.Pivot.X); + int backY = (int)(dy * spriteCos + dx * spriteSin + mip.Pivot.Y); + //int endX = clamp(backX, 0, mip.SizeX - 1); + //int endY = clamp(backY, 0, mip.SizeY - 1); + int endX = mip.SizeX - 1;// clamp(backX, 0, mip.SizeX - 1); + int endY = mip.SizeY - 1;// clamp(backY, 0, mip.SizeY - 1); + + // Draw the voxel cube: + + for (int index = 0; index < 1; index++) + { + /*if ((stepX[index] < 0 && endX >= startX[index]) || + (stepX[index] > 0 && endX <= startX[index]) || + (stepY[index] < 0 && endY >= startY[index]) || + (stepY[index] > 0 && endY <= startY[index])) continue;*/ + + for (int x = startX[index]; x != endX; x += stepX[index]) + { + for (int y = startY[index]; y != endY; y += stepY[index]) + { + kvxslab_t *slab_start = GetSlabStart(mip, x, y); + kvxslab_t *slab_end = GetSlabEnd(mip, x, y); + + for (kvxslab_t *slab = slab_start; slab != slab_end; slab = NextSlab(slab)) + { + // To do: check slab->backfacecull + + int ztop = slab->ztop; + int zbottom = ztop + slab->zleng; + + //ztop = MAX(ztop, minZ); + //zbottom = MIN(zbottom, maxZ); + + for (int z = ztop; z < zbottom; z++) + { + uint8_t color = slab->col[z - slab->ztop]; + + DVector3 voxel_pos = voxel_origin; + voxel_pos.X += dirX.X * x + dirX.Y * y; + voxel_pos.Y += dirY.X * x + dirY.Y * y; + voxel_pos.Z += dirZ * z; + + FillBox(thread, drawerargs, voxel_pos, sprite_xscale, sprite_yscale, color, cliptop, clipbottom, false, false); + } + } + } + } + } + } + + void RenderVoxel::FillBox(RenderThread *thread, SpriteDrawerArgs &drawerargs, DVector3 origin, double extentX, double extentY, int color, short *cliptop, short *clipbottom, bool viewspace, bool pixelstretch) + { + auto viewport = RenderViewport::Instance(); + + DVector3 viewPos = viewport->PointWorldToView(origin); + + if (viewPos.Z < 0.01f) + return; + + DVector3 screenPos = viewport->PointViewToScreen(viewPos); + DVector2 screenExtent = viewport->ScaleViewToScreen({ extentX, extentY }, viewPos.Z, pixelstretch); + + int x1 = MAX((int)(screenPos.X - screenExtent.X), 0); + int x2 = MIN((int)(screenPos.X + screenExtent.X + 0.5f), viewwidth - 1); + int y1 = MAX((int)(screenPos.Y - screenExtent.Y), 0); + int y2 = MIN((int)(screenPos.Y + screenExtent.Y + 0.5f), viewheight - 1); + + int pixelsize = viewport->RenderTarget->IsBgra() ? 4 : 1; + + if (y1 < y2) + { + for (int x = x1; x < x2; x++) + { + int columnY1 = MAX(y1, (int)cliptop[x]); + int columnY2 = MIN(y2, (int)clipbottom[x]); + if (columnY1 < columnY2) + { + drawerargs.SetDest(x, columnY1); + drawerargs.SetSolidColor(color); + drawerargs.SetCount(columnY2 - columnY1); + drawerargs.FillColumn(thread); + } + } + } + } +#endif +} diff --git a/src/swrenderer/things/r_voxel.h b/src/swrenderer/things/r_voxel.h new file mode 100644 index 000000000..e8bbe64cd --- /dev/null +++ b/src/swrenderer/things/r_voxel.h @@ -0,0 +1,109 @@ +// +//--------------------------------------------------------------------------- +// +// Voxel rendering +// Copyright(c) 1993 - 1997 Ken Silverman +// Copyright(c) 1998 - 2016 Randy Heit +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#pragma once + +#include "r_visiblesprite.h" + +struct kvxslab_t; +struct FVoxelMipLevel; +struct FVoxel; + +namespace swrenderer +{ + class SpriteDrawerArgs; + + // [RH] A c-buffer. Used for keeping track of offscreen voxel spans. + struct FCoverageBuffer + { + struct Span + { + Span *NextSpan; + short Start, Stop; + }; + + FCoverageBuffer(int size); + ~FCoverageBuffer(); + + void Clear(); + void InsertSpan(int listnum, int start, int stop); + Span *AllocSpan(); + + FMemArena SpanArena; + Span **Spans; // [0..NumLists-1] span lists + Span *FreeSpans; + unsigned int NumLists; + }; + + class RenderVoxel : public VisibleSprite + { + public: + static void Project(RenderThread *thread, AActor *thing, DVector3 pos, FVoxelDef *voxel, const DVector2 &spriteScale, int renderflags, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade, bool foggy, FDynamicColormap *basecolormap); + + static void Deinit(); + + protected: + bool IsVoxel() const override { return true; } + void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) override; + + private: + struct posang + { + FVector3 vpos = { 0.0f, 0.0f, 0.0f }; // view origin + FAngle vang = { 0.0f }; // view angle + }; + + posang pa; + DAngle Angle = { 0.0 }; + fixed_t xscale = 0; + FVoxel *voxel = nullptr; + bool bInMirror = false; + + uint32_t Translation = 0; + uint32_t FillColor = 0; + + enum { DVF_OFFSCREEN = 1, DVF_SPANSONLY = 2, DVF_MIRRORED = 4 }; + + static kvxslab_t *GetSlabStart(const FVoxelMipLevel &mip, int x, int y); + static kvxslab_t *GetSlabEnd(const FVoxelMipLevel &mip, int x, int y); + static kvxslab_t *NextSlab(kvxslab_t *slab); + + static void CheckOffscreenBuffer(int width, int height, bool spansonly); + + static FCoverageBuffer *OffscreenCoverageBuffer; + static int OffscreenBufferWidth; + static int OffscreenBufferHeight; + static uint8_t *OffscreenColorBuffer; + + void DrawVoxel( + RenderThread *thread, SpriteDrawerArgs &drawerargs, + const FVector3 &globalpos, FAngle viewangle, const FVector3 &dasprpos, DAngle dasprang, fixed_t daxscale, fixed_t dayscale, + FVoxel *voxobj, short *daumost, short *dadmost, int minslabz, int maxslabz, int flags); + + int sgn(int v) + { + return v < 0 ? -1 : v > 0 ? 1 : 0; + } + }; +} diff --git a/src/swrenderer/things/r_wallsprite.cpp b/src/swrenderer/things/r_wallsprite.cpp new file mode 100644 index 000000000..d4d8f9b58 --- /dev/null +++ b/src/swrenderer/things/r_wallsprite.cpp @@ -0,0 +1,263 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include +#include +#include "p_lnspec.h" +#include "templates.h" +#include "doomdef.h" +#include "m_swap.h" +#include "i_system.h" +#include "w_wad.h" +#include "swrenderer/things/r_wallsprite.h" +#include "c_console.h" +#include "c_cvars.h" +#include "c_dispatch.h" +#include "doomstat.h" +#include "v_video.h" +#include "sc_man.h" +#include "s_sound.h" +#include "sbar.h" +#include "gi.h" +#include "r_sky.h" +#include "cmdlib.h" +#include "g_level.h" +#include "d_net.h" +#include "colormatcher.h" +#include "d_netinf.h" +#include "p_effect.h" +#include "swrenderer/scene/r_opaque_pass.h" +#include "swrenderer/scene/r_3dfloors.h" +#include "swrenderer/scene/r_translucent_pass.h" +#include "swrenderer/drawers/r_draw_rgba.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "v_palette.h" +#include "r_data/r_translate.h" +#include "r_data/colormaps.h" +#include "r_data/voxels.h" +#include "p_local.h" +#include "p_maputl.h" +#include "r_voxel.h" +#include "swrenderer/segments/r_drawsegment.h" +#include "swrenderer/scene/r_portal.h" +#include "swrenderer/scene/r_scene.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/line/r_wallsetup.h" +#include "swrenderer/line/r_walldraw.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/r_memory.h" +#include "swrenderer/r_renderthread.h" + +EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); + +namespace swrenderer +{ + void RenderWallSprite::Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTextureID picnum, const DVector2 &scale, int renderflags, int spriteshade, bool foggy, FDynamicColormap *basecolormap) + { + FWallCoords wallc; + double x1, x2; + DVector2 left, right; + double gzb, gzt, tz; + FTexture *pic = TexMan(picnum, true); + DAngle ang = thing->Angles.Yaw + 90; + double angcos = ang.Cos(); + double angsin = ang.Sin(); + + // Determine left and right edges of sprite. The sprite's angle is its normal, + // so the edges are 90 degrees each side of it. + x2 = pic->GetScaledWidth(); + x1 = pic->GetScaledLeftOffset(); + + x1 *= scale.X; + x2 *= scale.X; + + left.X = pos.X - x1 * angcos - ViewPos.X; + left.Y = pos.Y - x1 * angsin - ViewPos.Y; + right.X = left.X + x2 * angcos; + right.Y = right.Y + x2 * angsin; + + // Is it off-screen? + if (wallc.Init(thread, left, right, TOO_CLOSE_Z)) + return; + + RenderPortal *renderportal = thread->Portal.get(); + + if (wallc.sx1 >= renderportal->WindowRight || wallc.sx2 <= renderportal->WindowLeft) + return; + + // Sprite sorting should probably treat these as walls, not sprites, + // but right now, I just want to get them drawing. + tz = (pos.X - ViewPos.X) * ViewTanCos + (pos.Y - ViewPos.Y) * ViewTanSin; + + int scaled_to = pic->GetScaledTopOffset(); + int scaled_bo = scaled_to - pic->GetScaledHeight(); + gzt = pos.Z + scale.Y * scaled_to; + gzb = pos.Z + scale.Y * scaled_bo; + + RenderWallSprite *vis = thread->FrameMemory->NewObject(); + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->x1 = wallc.sx1 < renderportal->WindowLeft ? renderportal->WindowLeft : wallc.sx1; + vis->x2 = wallc.sx2 >= renderportal->WindowRight ? renderportal->WindowRight : wallc.sx2; + vis->yscale = (float)scale.Y; + vis->idepth = float(1 / tz); + vis->depth = (float)tz; + vis->sector = thing->Sector; + vis->heightsec = NULL; + vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; + vis->gzb = (float)gzb; + vis->gzt = (float)gzt; + vis->deltax = float(pos.X - ViewPos.X); + vis->deltay = float(pos.Y - ViewPos.Y); + vis->renderflags = renderflags; + if (thing->flags5 & MF5_BRIGHT) vis->renderflags |= RF_FULLBRIGHT; // kg3D + vis->RenderStyle = thing->RenderStyle; + vis->FillColor = thing->fillcolor; + vis->Translation = thing->Translation; + vis->FakeFlatStat = WaterFakeSide::Center; + vis->Alpha = float(thing->Alpha); + vis->fakefloor = NULL; + vis->fakeceiling = NULL; + //vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP; + vis->pic = pic; + vis->wallc = wallc; + vis->foggy = foggy; + + vis->Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis(foggy) / MAX(tz, MINZ), spriteshade, basecolormap, false, false, false); + + thread->SpriteList->Push(vis); + } + + void RenderWallSprite::Render(RenderThread *thread, short *mfloorclip, short *mceilingclip, int, int) + { + auto spr = this; + + int x1, x2; + double iyscale; + bool sprflipvert; + + x1 = MAX(spr->x1, spr->wallc.sx1); + x2 = MIN(spr->x2, spr->wallc.sx2); + if (x1 >= x2) + return; + + FWallTmapVals WallT; + WallT.InitFromWallCoords(thread, &spr->wallc); + + ProjectedWallTexcoords walltexcoords; + walltexcoords.Project(spr->pic->GetWidth() << FRACBITS, x1, x2, WallT); + + iyscale = 1 / spr->yscale; + double texturemid = (spr->gzt - ViewPos.Z) * iyscale; + if (spr->renderflags & RF_XFLIP) + { + int right = (spr->pic->GetWidth() << FRACBITS) - 1; + + for (int i = x1; i < x2; i++) + { + walltexcoords.UPos[i] = right - walltexcoords.UPos[i]; + } + } + // Prepare lighting + bool calclighting = false; + FSWColormap *usecolormap = spr->Light.BaseColormap; + bool rereadcolormap = true; + + // Decals that are added to the scene must fade to black. + if (spr->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) + { + usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); + rereadcolormap = false; + } + + SpriteDrawerArgs drawerargs; + + int shade = LightVisibility::LightLevelToShade(spr->sector->lightlevel + LightVisibility::ActualExtraLight(spr->foggy), spr->foggy); + double GlobVis = LightVisibility::Instance()->WallGlobVis(foggy); + float lightleft = float(GlobVis / spr->wallc.sz1); + float lightstep = float((GlobVis / spr->wallc.sz2 - lightleft) / (spr->wallc.sx2 - spr->wallc.sx1)); + float light = lightleft + (x1 - spr->wallc.sx1) * lightstep; + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + drawerargs.SetLight(usecolormap, 0, cameraLight->FixedLightLevelShade()); + else if (cameraLight->FixedColormap() != NULL) + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + else if (!spr->foggy && (spr->renderflags & RF_FULLBRIGHT)) + drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, 0); + else + calclighting = true; + + // Draw it + FTexture *WallSpriteTile = spr->pic; + if (spr->renderflags & RF_YFLIP) + { + sprflipvert = true; + iyscale = -iyscale; + texturemid -= spr->pic->GetHeight(); + } + else + { + sprflipvert = false; + } + + float maskedScaleY = (float)iyscale; + + int x = x1; + + FDynamicColormap *basecolormap = static_cast(spr->Light.BaseColormap); + + bool visible = drawerargs.SetStyle(spr->RenderStyle, spr->Alpha, spr->Translation, spr->FillColor, basecolormap); + + // R_SetPatchStyle can modify basecolormap. + if (rereadcolormap) + { + usecolormap = spr->Light.BaseColormap; + } + + if (!visible) + { + return; + } + else + { + RenderTranslucentPass *translucentPass = thread->TranslucentPass.get(); + + while (x < x2) + { + if (calclighting) + { // calculate lighting + drawerargs.SetLight(usecolormap, light, shade); + } + if (!translucentPass->ClipSpriteColumnWithPortals(x, spr)) + DrawColumn(thread, drawerargs, x, WallSpriteTile, walltexcoords, texturemid, maskedScaleY, sprflipvert, mfloorclip, mceilingclip); + light += lightstep; + x++; + } + } + } + + void RenderWallSprite::DrawColumn(RenderThread *thread, SpriteDrawerArgs &drawerargs, int x, FTexture *WallSpriteTile, const ProjectedWallTexcoords &walltexcoords, double texturemid, float maskedScaleY, bool sprflipvert, const short *mfloorclip, const short *mceilingclip) + { + auto viewport = RenderViewport::Instance(); + + float iscale = walltexcoords.VStep[x] * maskedScaleY; + double spryscale = 1 / iscale; + double sprtopscreen; + if (sprflipvert) + sprtopscreen = viewport->CenterY + texturemid * spryscale; + else + sprtopscreen = viewport->CenterY - texturemid * spryscale; + + drawerargs.DrawMaskedColumn(thread, x, FLOAT2FIXED(iscale), WallSpriteTile, walltexcoords.UPos[x], spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip); + } +} diff --git a/src/swrenderer/things/r_wallsprite.h b/src/swrenderer/things/r_wallsprite.h new file mode 100644 index 000000000..76bbdc6c0 --- /dev/null +++ b/src/swrenderer/things/r_wallsprite.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include "r_visiblesprite.h" + +namespace swrenderer +{ + class ProjectedWallTexcoords; + class SpriteDrawerArgs; + + class RenderWallSprite : public VisibleSprite + { + public: + static void Project(RenderThread *thread, AActor *thing, const DVector3 &pos, FTextureID picnum, const DVector2 &scale, int renderflags, int spriteshade, bool foggy, FDynamicColormap *basecolormap); + + protected: + bool IsWallSprite() const override { return true; } + void Render(RenderThread *thread, short *cliptop, short *clipbottom, int minZ, int maxZ) override; + + private: + static void DrawColumn(RenderThread *thread, SpriteDrawerArgs &drawerargs, int x, FTexture *WallSpriteTile, const ProjectedWallTexcoords &walltexcoords, double texturemid, float maskedScaleY, bool sprflipvert, const short *mfloorclip, const short *mceilingclip); + + FWallCoords wallc; + uint32_t Translation = 0; + uint32_t FillColor = 0; + }; +} diff --git a/src/swrenderer/viewport/r_drawerargs.cpp b/src/swrenderer/viewport/r_drawerargs.cpp new file mode 100644 index 000000000..60a0edf61 --- /dev/null +++ b/src/swrenderer/viewport/r_drawerargs.cpp @@ -0,0 +1,77 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "r_drawerargs.h" + +namespace swrenderer +{ + void DrawerArgs::SetLight(FSWColormap *base_colormap, float light, int shade) + { + mBaseColormap = base_colormap; + mLight = light; + mShade = shade; + } + + void DrawerArgs::SetTranslationMap(lighttable_t *translation) + { + mTranslation = translation; + } + + uint8_t *DrawerArgs::Colormap() const + { + if (mBaseColormap) + { + if (RenderViewport::Instance()->RenderTarget->IsBgra()) + return mBaseColormap->Maps; + else + return mBaseColormap->Maps + (GETPALOOKUP(mLight, mShade) << COLORMAPSHIFT); + } + else + { + return mTranslation; + } + } + + ShadeConstants DrawerArgs::ColormapConstants() const + { + ShadeConstants shadeConstants; + if (mBaseColormap) + { + shadeConstants.light_red = mBaseColormap->Color.r * 256 / 255; + shadeConstants.light_green = mBaseColormap->Color.g * 256 / 255; + shadeConstants.light_blue = mBaseColormap->Color.b * 256 / 255; + shadeConstants.light_alpha = mBaseColormap->Color.a * 256 / 255; + shadeConstants.fade_red = mBaseColormap->Fade.r; + shadeConstants.fade_green = mBaseColormap->Fade.g; + shadeConstants.fade_blue = mBaseColormap->Fade.b; + shadeConstants.fade_alpha = mBaseColormap->Fade.a; + shadeConstants.desaturate = MIN(abs(mBaseColormap->Desaturate), 255) * 255 / 256; + shadeConstants.simple_shade = (mBaseColormap->Color.d == 0x00ffffff && mBaseColormap->Fade.d == 0x00000000 && mBaseColormap->Desaturate == 0); + } + else + { + shadeConstants.light_red = 256; + shadeConstants.light_green = 256; + shadeConstants.light_blue = 256; + shadeConstants.light_alpha = 256; + shadeConstants.fade_red = 0; + shadeConstants.fade_green = 0; + shadeConstants.fade_blue = 0; + shadeConstants.fade_alpha = 256; + shadeConstants.desaturate = 0; + shadeConstants.simple_shade = true; + } + return shadeConstants; + } +} diff --git a/src/swrenderer/viewport/r_drawerargs.h b/src/swrenderer/viewport/r_drawerargs.h new file mode 100644 index 000000000..ad77dadd2 --- /dev/null +++ b/src/swrenderer/viewport/r_drawerargs.h @@ -0,0 +1,62 @@ + +#pragma once + +#include "templates.h" +#include "doomtype.h" +#include "doomdef.h" +#include "r_defs.h" +#include "swrenderer/drawers/r_draw.h" +#include "v_video.h" +#include "r_data/colormaps.h" +#include "r_data/r_translate.h" +#include "swrenderer/scene/r_light.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class SWPixelFormatDrawers; + class DrawerArgs; + struct ShadeConstants; + + struct DrawerLight + { + uint32_t color; + float x, y, z; + float radius; + }; + + class DrawerArgs + { + public: + void SetLight(FSWColormap *base_colormap, float light, int shade); + void SetTranslationMap(lighttable_t *translation); + + uint8_t *Colormap() const; + uint8_t *TranslationMap() const { return mTranslation; } + + ShadeConstants ColormapConstants() const; + fixed_t Light() const { return LIGHTSCALE(mLight, mShade); } + + private: + FSWColormap *mBaseColormap = nullptr; + float mLight = 0.0f; + int mShade = 0; + uint8_t *mTranslation = nullptr; + }; + + struct ShadeConstants + { + uint16_t light_alpha; + uint16_t light_red; + uint16_t light_green; + uint16_t light_blue; + uint16_t fade_alpha; + uint16_t fade_red; + uint16_t fade_green; + uint16_t fade_blue; + uint16_t desaturate; + bool simple_shade; + }; +} diff --git a/src/swrenderer/viewport/r_skydrawer.cpp b/src/swrenderer/viewport/r_skydrawer.cpp new file mode 100644 index 000000000..74462b2fa --- /dev/null +++ b/src/swrenderer/viewport/r_skydrawer.cpp @@ -0,0 +1,69 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "r_skydrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + void SkyDrawerArgs::DrawSingleSkyColumn(RenderThread *thread) + { + thread->Drawers()->DrawSingleSkyColumn(*this); + } + + void SkyDrawerArgs::DrawDoubleSkyColumn(RenderThread *thread) + { + thread->Drawers()->DrawDoubleSkyColumn(*this); + } + + void SkyDrawerArgs::SetDest(int x, int y) + { + auto viewport = RenderViewport::Instance(); + dc_dest = viewport->GetDest(x, y); + dc_dest_y = y; + } + + void SkyDrawerArgs::SetFrontTexture(FTexture *texture, uint32_t column) + { + if (RenderViewport::Instance()->RenderTarget->IsBgra()) + { + dc_source = (const uint8_t *)texture->GetColumnBgra(column, nullptr); + dc_sourceheight = texture->GetHeight(); + } + else + { + dc_source = texture->GetColumn(column, nullptr); + dc_sourceheight = texture->GetHeight(); + } + } + + void SkyDrawerArgs::SetBackTexture(FTexture *texture, uint32_t column) + { + if (texture == nullptr) + { + dc_source2 = nullptr; + dc_sourceheight2 = 1; + } + else if (RenderViewport::Instance()->RenderTarget->IsBgra()) + { + dc_source2 = (const uint8_t *)texture->GetColumnBgra(column, nullptr); + dc_sourceheight2 = texture->GetHeight(); + } + else + { + dc_source2 = texture->GetColumn(column, nullptr); + dc_sourceheight2 = texture->GetHeight(); + } + } +} diff --git a/src/swrenderer/viewport/r_skydrawer.h b/src/swrenderer/viewport/r_skydrawer.h new file mode 100644 index 000000000..b4d23a7ee --- /dev/null +++ b/src/swrenderer/viewport/r_skydrawer.h @@ -0,0 +1,59 @@ + +#pragma once + +#include "r_drawerargs.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class RenderThread; + + class SkyDrawerArgs : public DrawerArgs + { + public: + void SetDest(int x, int y); + void SetCount(int count) { dc_count = count; } + void SetFrontTexture(FTexture *texture, uint32_t column); + void SetBackTexture(FTexture *texture, uint32_t column); + void SetTextureVPos(uint32_t texturefrac) { dc_texturefrac = texturefrac; } + void SetTextureVStep(uint32_t iscale) { dc_iscale = iscale; } + void SetSolidTop(uint32_t color) { solid_top = color; } + void SetSolidBottom(uint32_t color) { solid_bottom = color; } + void SetFadeSky(bool enable) { fadeSky = enable; } + + uint8_t *Dest() const { return dc_dest; } + int DestY() const { return dc_dest_y; } + int Count() const { return dc_count; } + + uint32_t TextureVPos() const { return dc_texturefrac; } + uint32_t TextureVStep() const { return dc_iscale; } + + uint32_t SolidTopColor() const { return solid_top; } + uint32_t SolidBottomColor() const { return solid_bottom; } + bool FadeSky() const { return fadeSky; } + + const uint8_t *FrontTexturePixels() const { return dc_source; } + const uint8_t *BackTexturePixels() const { return dc_source2; } + int FrontTextureHeight() const { return dc_sourceheight; } + int BackTextureHeight() const { return dc_sourceheight2; } + + void DrawSingleSkyColumn(RenderThread *thread); + void DrawDoubleSkyColumn(RenderThread *thread); + + private: + uint8_t *dc_dest = nullptr; + int dc_dest_y = 0; + int dc_count = 0; + const uint8_t *dc_source; + const uint8_t *dc_source2; + uint32_t dc_sourceheight; + uint32_t dc_sourceheight2; + uint32_t dc_texturefrac; + uint32_t dc_iscale; + uint32_t solid_top; + uint32_t solid_bottom; + bool fadeSky; + }; +} diff --git a/src/swrenderer/viewport/r_spandrawer.cpp b/src/swrenderer/viewport/r_spandrawer.cpp new file mode 100644 index 000000000..ac28c5242 --- /dev/null +++ b/src/swrenderer/viewport/r_spandrawer.cpp @@ -0,0 +1,119 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "r_spandrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + SpanDrawerArgs::SpanDrawerArgs() + { + spanfunc = &SWPixelFormatDrawers::DrawSpan; + } + + void SpanDrawerArgs::SetTexture(FTexture *tex) + { + tex->GetWidth(); + ds_xbits = tex->WidthBits; + ds_ybits = tex->HeightBits; + if ((1 << ds_xbits) > tex->GetWidth()) + { + ds_xbits--; + } + if ((1 << ds_ybits) > tex->GetHeight()) + { + ds_ybits--; + } + + auto viewport = RenderViewport::Instance(); + ds_source = viewport->RenderTarget->IsBgra() ? (const uint8_t*)tex->GetPixelsBgra() : tex->GetPixels(); + ds_source_mipmapped = tex->Mipmapped() && tex->GetWidth() > 1 && tex->GetHeight() > 1; + } + + void SpanDrawerArgs::SetStyle(bool masked, bool additive, fixed_t alpha) + { + if (masked) + { + if (alpha < OPAQUE || additive) + { + if (!additive) + { + spanfunc = &SWPixelFormatDrawers::DrawSpanMaskedTranslucent; + dc_srcblend = Col2RGB8[alpha >> 10]; + dc_destblend = Col2RGB8[(OPAQUE - alpha) >> 10]; + dc_srcalpha = alpha; + dc_destalpha = OPAQUE - alpha; + } + else + { + spanfunc = &SWPixelFormatDrawers::DrawSpanMaskedAddClamp; + dc_srcblend = Col2RGB8_LessPrecision[alpha >> 10]; + dc_destblend = Col2RGB8_LessPrecision[FRACUNIT >> 10]; + dc_srcalpha = alpha; + dc_destalpha = FRACUNIT; + } + } + else + { + spanfunc = &SWPixelFormatDrawers::DrawSpanMasked; + } + } + else + { + if (alpha < OPAQUE || additive) + { + if (!additive) + { + spanfunc = &SWPixelFormatDrawers::DrawSpanTranslucent; + dc_srcblend = Col2RGB8[alpha >> 10]; + dc_destblend = Col2RGB8[(OPAQUE - alpha) >> 10]; + dc_srcalpha = alpha; + dc_destalpha = OPAQUE - alpha; + } + else + { + spanfunc = &SWPixelFormatDrawers::DrawSpanAddClamp; + dc_srcblend = Col2RGB8_LessPrecision[alpha >> 10]; + dc_destblend = Col2RGB8_LessPrecision[FRACUNIT >> 10]; + dc_srcalpha = alpha; + dc_destalpha = FRACUNIT; + } + } + else + { + spanfunc = &SWPixelFormatDrawers::DrawSpan; + } + } + } + + void SpanDrawerArgs::DrawSpan(RenderThread *thread) + { + (thread->Drawers()->*spanfunc)(*this); + } + + void SpanDrawerArgs::DrawTiltedSpan(RenderThread *thread, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap) + { + thread->Drawers()->DrawTiltedSpan(*this, y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy, basecolormap); + } + + void SpanDrawerArgs::DrawFogBoundaryLine(RenderThread *thread, int y, int x1, int x2) + { + thread->Drawers()->DrawFogBoundaryLine(*this, y, x1, x2); + } + + void SpanDrawerArgs::DrawColoredSpan(RenderThread *thread, int y, int x1, int x2) + { + thread->Drawers()->DrawColoredSpan(*this, y, x1, x2); + } +} diff --git a/src/swrenderer/viewport/r_spandrawer.h b/src/swrenderer/viewport/r_spandrawer.h new file mode 100644 index 000000000..368ae8a32 --- /dev/null +++ b/src/swrenderer/viewport/r_spandrawer.h @@ -0,0 +1,81 @@ + +#pragma once + +#include "r_drawerargs.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class RenderThread; + + class SpanDrawerArgs : public DrawerArgs + { + public: + SpanDrawerArgs(); + + void SetStyle(bool masked, bool additive, fixed_t alpha); + void SetDestY(int y) { ds_y = y; } + void SetDestX1(int x) { ds_x1 = x; } + void SetDestX2(int x) { ds_x2 = x; } + void SetTexture(FTexture *tex); + void SetTextureLOD(double lod) { ds_lod = lod; } + void SetTextureUPos(dsfixed_t xfrac) { ds_xfrac = xfrac; } + void SetTextureVPos(dsfixed_t yfrac) { ds_yfrac = yfrac; } + void SetTextureUStep(dsfixed_t xstep) { ds_xstep = xstep; } + void SetTextureVStep(dsfixed_t vstep) { ds_ystep = vstep; } + void SetSolidColor(int colorIndex) { ds_color = colorIndex; } + + void DrawSpan(RenderThread *thread); + void DrawTiltedSpan(RenderThread *thread, int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy, FDynamicColormap *basecolormap); + void DrawColoredSpan(RenderThread *thread, int y, int x1, int x2); + void DrawFogBoundaryLine(RenderThread *thread, int y, int x1, int x2); + + uint32_t *SrcBlend() const { return dc_srcblend; } + uint32_t *DestBlend() const { return dc_destblend; } + fixed_t SrcAlpha() const { return dc_srcalpha; } + fixed_t DestAlpha() const { return dc_destalpha; } + int DestY() const { return ds_y; } + int DestX1() const { return ds_x1; } + int DestX2() const { return ds_x2; } + dsfixed_t TextureUPos() const { return ds_xfrac; } + dsfixed_t TextureVPos() const { return ds_yfrac; } + dsfixed_t TextureUStep() const { return ds_xstep; } + dsfixed_t TextureVStep() const { return ds_ystep; } + int SolidColor() const { return ds_color; } + int TextureWidthBits() const { return ds_xbits; } + int TextureHeightBits() const { return ds_ybits; } + const uint8_t *TexturePixels() const { return ds_source; } + bool MipmappedTexture() const { return ds_source_mipmapped; } + double TextureLOD() const { return ds_lod; } + + FVector3 dc_normal; + FVector3 dc_viewpos; + FVector3 dc_viewpos_step; + DrawerLight *dc_lights = nullptr; + int dc_num_lights = 0; + + private: + typedef void(SWPixelFormatDrawers::*SpanDrawerFunc)(const SpanDrawerArgs &args); + SpanDrawerFunc spanfunc; + + int ds_y; + int ds_x1; + int ds_x2; + int ds_xbits; + int ds_ybits; + const uint8_t *ds_source; + bool ds_source_mipmapped; + dsfixed_t ds_xfrac; + dsfixed_t ds_yfrac; + dsfixed_t ds_xstep; + dsfixed_t ds_ystep; + uint32_t *dc_srcblend; + uint32_t *dc_destblend; + fixed_t dc_srcalpha; + fixed_t dc_destalpha; + int ds_color = 0; + double ds_lod; + }; +} diff --git a/src/swrenderer/viewport/r_spritedrawer.cpp b/src/swrenderer/viewport/r_spritedrawer.cpp new file mode 100644 index 000000000..85f187259 --- /dev/null +++ b/src/swrenderer/viewport/r_spritedrawer.cpp @@ -0,0 +1,524 @@ +/* +** r_spritedrawer.cpp +** +**--------------------------------------------------------------------------- +** Copyright 1998-2016 Randy Heit +** Copyright 2016 Magnus Norddahl +** 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 "r_spritedrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + SpriteDrawerArgs::SpriteDrawerArgs() + { + colfunc = &SWPixelFormatDrawers::DrawColumn; + } + + void SpriteDrawerArgs::DrawMaskedColumn(RenderThread *thread, int x, fixed_t iscale, FTexture *tex, fixed_t col, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked) + { + if (x < thread->X1 || x >= thread->X2) + return; + + auto viewport = RenderViewport::Instance(); + + // Handle the linear filtered version in a different function to reduce chances of merge conflicts from zdoom. + if (viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input) // To do: add support to R_DrawColumnHoriz_rgba + { + DrawMaskedColumnBgra(thread, x, iscale, tex, col, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, unmasked); + return; + } + + dc_x = x; + dc_iscale = iscale; + dc_textureheight = tex->GetHeight(); + + const FTexture::Span *span; + const BYTE *column; + if (viewport->RenderTarget->IsBgra() && !drawer_needs_pal_input) + column = (const BYTE *)tex->GetColumnBgra(col >> FRACBITS, &span); + else + column = tex->GetColumn(col >> FRACBITS, &span); + + FTexture::Span unmaskedSpan[2]; + if (unmasked) + { + span = unmaskedSpan; + unmaskedSpan[0].TopOffset = 0; + unmaskedSpan[0].Length = tex->GetHeight(); + unmaskedSpan[1].TopOffset = 0; + unmaskedSpan[1].Length = 0; + } + + int pixelsize = viewport->RenderTarget->IsBgra() ? 4 : 1; + + while (span->Length != 0) + { + const int length = span->Length; + const int top = span->TopOffset; + + // calculate unclipped screen coordinates for post + dc_yl = (int)(sprtopscreen + spryscale * top + 0.5); + dc_yh = (int)(sprtopscreen + spryscale * (top + length) + 0.5) - 1; + + if (sprflipvert) + { + swapvalues(dc_yl, dc_yh); + } + + if (dc_yh >= mfloorclip[dc_x]) + { + dc_yh = mfloorclip[dc_x] - 1; + } + if (dc_yl < mceilingclip[dc_x]) + { + dc_yl = mceilingclip[dc_x]; + } + + if (dc_yl <= dc_yh) + { + dc_texturefrac = FLOAT2FIXED((dc_yl + 0.5 - sprtopscreen) / spryscale); + dc_source = column; + dc_source2 = nullptr; + SetDest(dc_x, dc_yl); + dc_count = dc_yh - dc_yl + 1; + + fixed_t maxfrac = ((top + length) << FRACBITS) - 1; + dc_texturefrac = MAX(dc_texturefrac, 0); + dc_texturefrac = MIN(dc_texturefrac, maxfrac); + if (dc_iscale > 0) + dc_count = MIN(dc_count, (maxfrac - dc_texturefrac + dc_iscale - 1) / dc_iscale); + else if (dc_iscale < 0) + dc_count = MIN(dc_count, (dc_texturefrac - dc_iscale) / (-dc_iscale)); + + (thread->Drawers()->*colfunc)(*this); + } + span++; + } + } + + void SpriteDrawerArgs::DrawMaskedColumnBgra(RenderThread *thread, int x, fixed_t iscale, FTexture *tex, fixed_t col, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked) + { + dc_x = x; + dc_iscale = iscale; + + // Normalize to 0-1 range: + double uv_stepd = FIXED2DBL(dc_iscale); + double v_step = uv_stepd / tex->GetHeight(); + + // Convert to uint32: + dc_iscale = (uint32_t)(v_step * (1 << 30)); + + // Texture mipmap and filter selection: + fixed_t xoffset = col; + + double xmagnitude = 1.0; // To do: pass this into R_DrawMaskedColumn + double ymagnitude = fabs(uv_stepd); + double magnitude = MAX(ymagnitude, xmagnitude); + double min_lod = -1000.0; + double lod = MAX(log2(magnitude) + r_lod_bias, min_lod); + bool magnifying = lod < 0.0f; + + int mipmap_offset = 0; + int mip_width = tex->GetWidth(); + int mip_height = tex->GetHeight(); + uint32_t xpos = (uint32_t)((((uint64_t)xoffset) << FRACBITS) / mip_width); + if (r_mipmap && tex->Mipmapped() && mip_width > 1 && mip_height > 1) + { + int level = (int)lod; + while (level > 0 && mip_width > 1 && mip_height > 1) + { + mipmap_offset += mip_width * mip_height; + level--; + mip_width = MAX(mip_width >> 1, 1); + mip_height = MAX(mip_height >> 1, 1); + } + } + xoffset = (xpos >> FRACBITS) * mip_width; + + const uint32_t *pixels = tex->GetPixelsBgra() + mipmap_offset; + + bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter); + if (filter_nearest) + { + xoffset = MAX(MIN(xoffset, (mip_width << FRACBITS) - 1), 0); + + int tx = xoffset >> FRACBITS; + dc_source = (BYTE*)(pixels + tx * mip_height); + dc_source2 = nullptr; + dc_textureheight = mip_height; + dc_texturefracx = 0; + } + else + { + xoffset = MAX(MIN(xoffset - (FRACUNIT / 2), (mip_width << FRACBITS) - 1), 0); + + int tx0 = xoffset >> FRACBITS; + int tx1 = MIN(tx0 + 1, mip_width - 1); + dc_source = (BYTE*)(pixels + tx0 * mip_height); + dc_source2 = (BYTE*)(pixels + tx1 * mip_height); + dc_textureheight = mip_height; + dc_texturefracx = (xoffset >> (FRACBITS - 4)) & 15; + } + + // Grab the posts we need to draw + const FTexture::Span *span; + tex->GetColumnBgra(col >> FRACBITS, &span); + FTexture::Span unmaskedSpan[2]; + if (unmasked) + { + span = unmaskedSpan; + unmaskedSpan[0].TopOffset = 0; + unmaskedSpan[0].Length = tex->GetHeight(); + unmaskedSpan[1].TopOffset = 0; + unmaskedSpan[1].Length = 0; + } + + // Draw each span post + while (span->Length != 0) + { + const int length = span->Length; + const int top = span->TopOffset; + + // calculate unclipped screen coordinates for post + dc_yl = (int)(sprtopscreen + spryscale * top + 0.5); + dc_yh = (int)(sprtopscreen + spryscale * (top + length) + 0.5) - 1; + + if (sprflipvert) + { + swapvalues(dc_yl, dc_yh); + } + + if (dc_yh >= mfloorclip[dc_x]) + { + dc_yh = mfloorclip[dc_x] - 1; + } + if (dc_yl < mceilingclip[dc_x]) + { + dc_yl = mceilingclip[dc_x]; + } + + if (dc_yl <= dc_yh) + { + SetDest(dc_x, dc_yl); + dc_count = dc_yh - dc_yl + 1; + + double v = ((dc_yl + 0.5 - sprtopscreen) / spryscale) / tex->GetHeight(); + dc_texturefrac = (uint32_t)(v * (1 << 30)); + + (thread->Drawers()->*colfunc)(*this); + } + span++; + } + } + + bool SpriteDrawerArgs::SetBlendFunc(int op, fixed_t fglevel, fixed_t bglevel, int flags) + { + // r_drawtrans is a seriously bad thing to turn off. I wonder if I should + // just remove it completely. + if (!r_drawtrans || (op == STYLEOP_Add && fglevel == FRACUNIT && bglevel == 0 && !(flags & STYLEF_InvertSource))) + { + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawTranslatedColumn; + drawer_needs_pal_input = true; + } + return true; + } + if (flags & STYLEF_InvertSource) + { + dc_srcblend = Col2RGB8_Inverse[fglevel >> 10]; + dc_destblend = Col2RGB8_LessPrecision[bglevel >> 10]; + dc_srcalpha = fglevel; + dc_destalpha = bglevel; + } + else if (op == STYLEOP_Add && fglevel + bglevel <= FRACUNIT) + { + dc_srcblend = Col2RGB8[fglevel >> 10]; + dc_destblend = Col2RGB8[bglevel >> 10]; + dc_srcalpha = fglevel; + dc_destalpha = bglevel; + } + else + { + dc_srcblend = Col2RGB8_LessPrecision[fglevel >> 10]; + dc_destblend = Col2RGB8_LessPrecision[bglevel >> 10]; + dc_srcalpha = fglevel; + dc_destalpha = bglevel; + } + switch (op) + { + case STYLEOP_Add: + if (fglevel == 0 && bglevel == FRACUNIT) + { + return false; + } + if (fglevel + bglevel <= FRACUNIT) + { // Colors won't overflow when added + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillAddColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawAddColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawTranslatedAddColumn; + drawer_needs_pal_input = true; + } + } + else + { // Colors might overflow when added + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillAddClampColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawAddClampColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawAddClampTranslatedColumn; + drawer_needs_pal_input = true; + } + } + return true; + + case STYLEOP_Sub: + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillSubClampColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawSubClampColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawSubClampTranslatedColumn; + drawer_needs_pal_input = true; + } + return true; + + case STYLEOP_RevSub: + if (fglevel == 0 && bglevel == FRACUNIT) + { + return false; + } + if (flags & STYLEF_ColorIsFixed) + { + colfunc = &SWPixelFormatDrawers::FillRevSubClampColumn; + } + else if (TranslationMap() == nullptr) + { + colfunc = &SWPixelFormatDrawers::DrawRevSubClampColumn; + } + else + { + colfunc = &SWPixelFormatDrawers::DrawRevSubClampTranslatedColumn; + drawer_needs_pal_input = true; + } + return true; + + default: + return false; + } + } + + fixed_t SpriteDrawerArgs::GetAlpha(int type, fixed_t alpha) + { + switch (type) + { + case STYLEALPHA_Zero: return 0; + case STYLEALPHA_One: return OPAQUE; + case STYLEALPHA_Src: return alpha; + case STYLEALPHA_InvSrc: return OPAQUE - alpha; + default: return 0; + } + } + + bool SpriteDrawerArgs::SetStyle(FRenderStyle style, fixed_t alpha, int translation, uint32_t color, FDynamicColormap *&basecolormap, fixed_t shadedlightshade) + { + fixed_t fglevel, bglevel; + + drawer_needs_pal_input = false; + + style.CheckFuzz(); + + if (style.BlendOp == STYLEOP_Shadow) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = OPAQUE / 3; + color = 0; + } + + if (style.Flags & STYLEF_ForceAlpha) + { + alpha = clamp(alpha, 0, OPAQUE); + } + else if (style.Flags & STYLEF_TransSoulsAlpha) + { + alpha = fixed_t(transsouls * OPAQUE); + } + else if (style.Flags & STYLEF_Alpha1) + { + alpha = OPAQUE; + } + else + { + alpha = clamp(alpha, 0, OPAQUE); + } + + auto viewport = RenderViewport::Instance(); + + if (translation != -1) + { + SetTranslationMap(nullptr); + if (translation != 0) + { + FRemapTable *table = TranslationToTable(translation); + if (table != NULL && !table->Inactive) + { + if (viewport->RenderTarget->IsBgra()) + SetTranslationMap((uint8_t*)table->Palette); + else + SetTranslationMap(table->Remap); + } + } + } + + // Check for special modes + if (style.BlendOp == STYLEOP_Fuzz) + { + colfunc = &SWPixelFormatDrawers::DrawFuzzColumn; + return true; + } + else if (style == LegacyRenderStyles[STYLE_Shaded]) + { + // Shaded drawer only gets 16 levels of alpha because it saves memory. + if ((alpha >>= 12) == 0 || basecolormap == nullptr) + return false; + colfunc = &SWPixelFormatDrawers::DrawShadedColumn; + drawer_needs_pal_input = true; + CameraLight *cameraLight = CameraLight::Instance(); + dc_color = cameraLight->FixedColormap() ? cameraLight->FixedColormap()->Maps[APART(color)] : basecolormap->Maps[APART(color)]; + basecolormap = &ShadeFakeColormap[16 - alpha]; + if (cameraLight->FixedLightLevel() >= 0 && !cameraLight->FixedColormap()) + { + fixed_t shade = shadedlightshade; + if (shade == 0) shade = cameraLight->FixedLightLevelShade(); + SetLight(basecolormap, 0, shade); + } + else + { + SetLight(basecolormap, 0, shadedlightshade); + } + return true; + } + + fglevel = GetAlpha(style.SrcAlpha, alpha); + bglevel = GetAlpha(style.DestAlpha, alpha); + + if (style.Flags & STYLEF_ColorIsFixed) + { + uint32_t x = fglevel >> 10; + uint32_t r = RPART(color); + uint32_t g = GPART(color); + uint32_t b = BPART(color); + // dc_color is used by the rt_* routines. It is indexed into dc_srcblend. + dc_color = RGB256k.RGB[r >> 2][g >> 2][b >> 2]; + if (style.Flags & STYLEF_InvertSource) + { + r = 255 - r; + g = 255 - g; + b = 255 - b; + } + uint32_t alpha = clamp(fglevel >> (FRACBITS - 8), 0, 255); + dc_srccolor_bgra = (alpha << 24) | (r << 16) | (g << 8) | b; + // dc_srccolor is used by the R_Fill* routines. It is premultiplied + // with the alpha. + dc_srccolor = ((((r*x) >> 4) << 20) | ((g*x) >> 4) | ((((b)*x) >> 4) << 10)) & 0x3feffbff; + SetLight(&identitycolormap, 0, 0); + } + + return SpriteDrawerArgs::SetBlendFunc(style.BlendOp, fglevel, bglevel, style.Flags); + } + + bool SpriteDrawerArgs::SetStyle(FRenderStyle style, float alpha, int translation, uint32_t color, FDynamicColormap *&basecolormap, fixed_t shadedlightshade) + { + return SetStyle(style, FLOAT2FIXED(alpha), translation, color, basecolormap, shadedlightshade); + } + + void SpriteDrawerArgs::FillColumn(RenderThread *thread) + { + thread->Drawers()->FillColumn(*this); + } + + void SpriteDrawerArgs::DrawVoxelColumn(RenderThread *thread, fixed_t vPos, fixed_t vStep, const uint8_t *voxels, int voxelsCount) + { + if (RenderViewport::Instance()->RenderTarget->IsBgra()) + { + double v = vPos / (double)voxelsCount / FRACUNIT; + double vstep = vStep / (double)voxelsCount / FRACUNIT; + dc_texturefrac = (int)(v * (1 << 30)); + dc_iscale = (int)(vstep * (1 << 30)); + } + else + { + dc_texturefrac = vPos; + dc_iscale = vStep; + } + + dc_texturefracx = 0; + dc_source = voxels; + dc_source2 = 0; + dc_textureheight = voxelsCount; + (thread->Drawers()->*colfunc)(*this); + } + + void SpriteDrawerArgs::SetDest(int x, int y) + { + auto viewport = RenderViewport::Instance(); + dc_dest = viewport->GetDest(x, y); + dc_dest_y = y; + } +} diff --git a/src/swrenderer/viewport/r_spritedrawer.h b/src/swrenderer/viewport/r_spritedrawer.h new file mode 100644 index 000000000..31d3861d9 --- /dev/null +++ b/src/swrenderer/viewport/r_spritedrawer.h @@ -0,0 +1,92 @@ + +#pragma once + +#include "r_drawerargs.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class RenderThread; + + class SpriteDrawerArgs : public DrawerArgs + { + public: + SpriteDrawerArgs(); + + bool SetStyle(FRenderStyle style, fixed_t alpha, int translation, uint32_t color, FDynamicColormap *&basecolormap, fixed_t shadedlightshade = 0); + bool SetStyle(FRenderStyle style, float alpha, int translation, uint32_t color, FDynamicColormap *&basecolormap, fixed_t shadedlightshade = 0); + void SetDest(int x, int y); + void SetCount(int count) { dc_count = count; } + void SetSolidColor(int color) { dc_color = color; } + void SetDynamicLight(uint32_t color) { dynlightcolor = color; } + + void DrawMaskedColumn(RenderThread *thread, int x, fixed_t iscale, FTexture *texture, fixed_t column, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked = false); + void FillColumn(RenderThread *thread); + void DrawVoxelColumn(RenderThread *thread, fixed_t vPos, fixed_t vStep, const uint8_t *voxels, int voxelsCount); + + uint8_t *Dest() const { return dc_dest; } + int DestY() const { return dc_dest_y; } + int Count() const { return dc_count; } + + int FuzzX() const { return dc_x; } + int FuzzY1() const { return dc_yl; } + int FuzzY2() const { return dc_yh; } + + uint32_t TextureUPos() const { return dc_texturefracx; } + fixed_t TextureVPos() const { return dc_texturefrac; } + fixed_t TextureVStep() const { return dc_iscale; } + + int SolidColor() const { return dc_color; } + uint32_t SrcColorIndex() const { return dc_srccolor; } + uint32_t SrcColorBgra() const { return dc_srccolor_bgra; } + + const uint8_t *TexturePixels() const { return dc_source; } + const uint8_t *TexturePixels2() const { return dc_source2; } + uint32_t TextureHeight() const { return dc_textureheight; } + + uint32_t *SrcBlend() const { return dc_srcblend; } + uint32_t *DestBlend() const { return dc_destblend; } + fixed_t SrcAlpha() const { return dc_srcalpha; } + fixed_t DestAlpha() const { return dc_destalpha; } + + uint32_t DynamicLight() const { return dynlightcolor; } + + private: + bool SetBlendFunc(int op, fixed_t fglevel, fixed_t bglevel, int flags); + static fixed_t GetAlpha(int type, fixed_t alpha); + void DrawMaskedColumnBgra(RenderThread *thread, int x, fixed_t iscale, FTexture *tex, fixed_t column, double spryscale, double sprtopscreen, bool sprflipvert, const short *mfloorclip, const short *mceilingclip, bool unmasked); + + uint8_t *dc_dest = nullptr; + int dc_dest_y = 0; + int dc_count = 0; + + fixed_t dc_iscale; + fixed_t dc_texturefrac; + uint32_t dc_texturefracx; + + uint32_t dc_textureheight = 0; + const uint8_t *dc_source = nullptr; + const uint8_t *dc_source2 = nullptr; + bool drawer_needs_pal_input = false; + + uint32_t *dc_srcblend = nullptr; + uint32_t *dc_destblend = nullptr; + fixed_t dc_srcalpha = OPAQUE; + fixed_t dc_destalpha = 0; + + int dc_x = 0; + int dc_yl = 0; + int dc_yh = 0; + + int dc_color = 0; + uint32_t dc_srccolor = 0; + uint32_t dc_srccolor_bgra = 0; + + uint32_t dynlightcolor = 0; + + typedef void(SWPixelFormatDrawers::*SpriteDrawerFunc)(const SpriteDrawerArgs &args); + SpriteDrawerFunc colfunc; + }; +} diff --git a/src/swrenderer/viewport/r_viewport.cpp b/src/swrenderer/viewport/r_viewport.cpp new file mode 100644 index 000000000..6182976d7 --- /dev/null +++ b/src/swrenderer/viewport/r_viewport.cpp @@ -0,0 +1,219 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" +#include "doomdef.h" +#include "doomstat.h" +#include "r_sky.h" +#include "stats.h" +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "r_utility.h" +#include "swrenderer/viewport/r_viewport.h" +#include "swrenderer/scene/r_light.h" +#include "swrenderer/drawers/r_draw.h" +#include "swrenderer/things/r_playersprite.h" +#include "swrenderer/plane/r_flatplane.h" +#include "swrenderer/drawers/r_draw_pal.h" +#include "swrenderer/drawers/r_draw_rgba.h" + +CVAR(String, r_viewsize, "", CVAR_NOSET) + +namespace swrenderer +{ + RenderViewport *RenderViewport::Instance() + { + static RenderViewport instance; + return &instance; + } + + RenderViewport::RenderViewport() + { + } + + RenderViewport::~RenderViewport() + { + } + + void RenderViewport::SetViewport(int fullWidth, int fullHeight, float trueratio) + { + int virtheight, virtwidth, virtwidth2, virtheight2; + + if (!RenderingToCanvas()) + { // Set r_viewsize cvar to reflect the current view size + UCVarValue value; + char temp[16]; + + mysnprintf(temp, countof(temp), "%d x %d", viewwidth, viewheight); + value.String = temp; + r_viewsize.ForceSet(value, CVAR_String); + } + + fuzzviewheight = viewheight - 2; // Maximum row the fuzzer can draw to + + CenterX = centerx; + CenterY = centery; + + virtwidth = virtwidth2 = fullWidth; + virtheight = virtheight2 = fullHeight; + + if (AspectTallerThanWide(trueratio)) + { + virtheight2 = virtheight2 * AspectMultiplier(trueratio) / 48; + } + else + { + virtwidth2 = virtwidth2 * AspectMultiplier(trueratio) / 48; + } + + if (AspectTallerThanWide(WidescreenRatio)) + { + virtheight = virtheight * AspectMultiplier(WidescreenRatio) / 48; + } + else + { + virtwidth = virtwidth * AspectMultiplier(WidescreenRatio) / 48; + } + + BaseYaspectMul = 320.0 * virtheight2 / (r_Yaspect * virtwidth2); + YaspectMul = 320.0 * virtheight / (r_Yaspect * virtwidth); + IYaspectMul = (double)virtwidth * r_Yaspect / 320.0 / virtheight; + InvZtoScale = YaspectMul * CenterX; + + WallTMapScale2 = IYaspectMul / CenterX; + + // thing clipping + fillshort(screenheightarray, viewwidth, (short)viewheight); + + InitTextureMapping(); + + // Reset r_*Visibility vars + LightVisibility *visibility = LightVisibility::Instance(); + visibility->SetVisibility(visibility->GetVisibility()); + + SetupBuffer(); + } + + void RenderViewport::SetupFreelook() + { + double dy; + + if (camera != NULL) + { + dy = FocalLengthY * (-ViewPitch).Tan(); + } + else + { + dy = 0; + } + + CenterY = (viewheight / 2.0) + dy; + centery = xs_ToInt(CenterY); + globaluclip = -CenterY / InvZtoScale; + globaldclip = (viewheight - CenterY) / InvZtoScale; + } + + void RenderViewport::SetupBuffer() + { + R_InitFuzzTable(RenderTarget->GetPitch()); + R_InitParticleTexture(); + } + + uint8_t *RenderViewport::GetDest(int x, int y) + { + x += viewwindowx; + y += viewwindowy; + + int pitch = RenderTarget->GetPitch(); + int pixelsize = RenderTarget->IsBgra() ? 4 : 1; + return RenderTarget->GetBuffer() + (x + y * pitch) * pixelsize; + } + + void RenderViewport::InitTextureMapping() + { + int i; + + // Calc focallength so FieldOfView angles cover viewwidth. + FocalLengthX = CenterX / FocalTangent; + FocalLengthY = FocalLengthX * YaspectMul; + + // This is 1/FocalTangent before the widescreen extension of FOV. + viewingrangerecip = FLOAT2FIXED(1. / tan(FieldOfView.Radians() / 2)); + + // Now generate xtoviewangle for sky texture mapping. + // [RH] Do not generate viewangletox, because texture mapping is no + // longer done with trig, so it's not needed. + const double slopestep = FocalTangent / centerx; + double slope; + + for (i = centerx, slope = 0; i <= viewwidth; i++, slope += slopestep) + { + xtoviewangle[i] = angle_t((2 * M_PI - atan(slope)) * (ANGLE_180 / M_PI)); + } + for (i = 0; i < centerx; i++) + { + xtoviewangle[i] = 0 - xtoviewangle[viewwidth - i - 1]; + } + } + + DVector2 RenderViewport::PointWorldToView(const DVector2 &worldPos) const + { + double translatedX = worldPos.X - ViewPos.X; + double translatedY = worldPos.Y - ViewPos.Y; + return { + translatedX * ViewSin - translatedY * ViewCos, + translatedX * ViewTanCos + translatedY * ViewTanSin + }; + } + + DVector3 RenderViewport::PointWorldToView(const DVector3 &worldPos) const + { + double translatedX = worldPos.X - ViewPos.X; + double translatedY = worldPos.Y - ViewPos.Y; + double translatedZ = worldPos.Z - ViewPos.Z; + return { + translatedX * ViewSin - translatedY * ViewCos, + translatedZ, + translatedX * ViewTanCos + translatedY * ViewTanSin + }; + } + + DVector3 RenderViewport::PointWorldToScreen(const DVector3 &worldPos) const + { + return PointViewToScreen(PointWorldToView(worldPos)); + } + + DVector3 RenderViewport::PointViewToScreen(const DVector3 &viewPos) const + { + double screenX = CenterX + viewPos.X / viewPos.Z * CenterX; + double screenY = CenterY - viewPos.Y / viewPos.Z * InvZtoScale; + return { screenX, screenY, viewPos.Z }; + } + + DVector2 RenderViewport::ScaleViewToScreen(const DVector2 &scale, double viewZ, bool pixelstretch) const + { + double screenScaleX = scale.X / viewZ * CenterX; + double screenScaleY = scale.Y / viewZ * InvZtoScale; + if (!pixelstretch) screenScaleY /= YaspectMul; + return { screenScaleX, screenScaleY }; + } +} diff --git a/src/swrenderer/viewport/r_viewport.h b/src/swrenderer/viewport/r_viewport.h new file mode 100644 index 000000000..a16c8fde2 --- /dev/null +++ b/src/swrenderer/viewport/r_viewport.h @@ -0,0 +1,77 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#pragma once + +#include +#include +#include "r_defs.h" + +namespace swrenderer +{ + class RenderViewport + { + public: + static RenderViewport *Instance(); + + RenderViewport(); + ~RenderViewport(); + + void SetViewport(int width, int height, float trueratio); + void SetupFreelook(); + + DCanvas *RenderTarget = nullptr; + + double FocalLengthX = 0.0; + double FocalLengthY = 0.0; + double InvZtoScale = 0.0; + double WallTMapScale2 = 0.0; + double CenterX = 0.0; + double CenterY = 0.0; + double YaspectMul = 0.0; + double IYaspectMul = 0.0; + double globaluclip = 0.0; + double globaldclip = 0.0; + + fixed_t viewingrangerecip = 0; + double BaseYaspectMul = 0.0; // yaspectmul without a forced aspect ratio + + // The xtoviewangleangle[] table maps a screen pixel + // to the lowest viewangle that maps back to x ranges + // from clipangle to -clipangle. + angle_t xtoviewangle[MAXWIDTH + 1]; + + uint8_t *GetDest(int x, int y); + + bool RenderingToCanvas() const { return RenderTarget != screen; } + + DVector3 PointWorldToView(const DVector3 &worldPos) const; + DVector3 PointWorldToScreen(const DVector3 &worldPos) const; + DVector3 PointViewToScreen(const DVector3 &viewPos) const; + + DVector2 PointWorldToView(const DVector2 &worldPos) const; + DVector2 ScaleViewToScreen(const DVector2 &scale, double viewZ, bool pixelstretch = true) const; + + double PlaneDepth(int screenY, double planeHeight) const + { + if (screenY + 0.5 < CenterY) + return FocalLengthY / (CenterY - screenY - 0.5) * planeHeight; + else + return FocalLengthY / (screenY + 0.5 - CenterY) * planeHeight; + } + + private: + void InitTextureMapping(); + void SetupBuffer(); + }; +} diff --git a/src/swrenderer/viewport/r_walldrawer.cpp b/src/swrenderer/viewport/r_walldrawer.cpp new file mode 100644 index 000000000..aaada0604 --- /dev/null +++ b/src/swrenderer/viewport/r_walldrawer.cpp @@ -0,0 +1,67 @@ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// + +#include +#include "r_walldrawer.h" +#include "swrenderer/r_renderthread.h" + +namespace swrenderer +{ + void WallDrawerArgs::SetDest(int x, int y) + { + auto viewport = RenderViewport::Instance(); + dc_dest = viewport->GetDest(x, y); + dc_dest_y = y; + } + + void WallDrawerArgs::DrawColumn(RenderThread *thread) + { + (thread->Drawers()->*wallfunc)(*this); + } + + void WallDrawerArgs::SetStyle(bool masked, bool additive, fixed_t alpha) + { + if (alpha < OPAQUE || additive) + { + if (!additive) + { + wallfunc = &SWPixelFormatDrawers::DrawWallAddColumn; + dc_srcblend = Col2RGB8[alpha >> 10]; + dc_destblend = Col2RGB8[(OPAQUE - alpha) >> 10]; + dc_srcalpha = alpha; + dc_destalpha = OPAQUE - alpha; + } + else + { + wallfunc = &SWPixelFormatDrawers::DrawWallAddClampColumn; + dc_srcblend = Col2RGB8_LessPrecision[alpha >> 10]; + dc_destblend = Col2RGB8_LessPrecision[FRACUNIT >> 10]; + dc_srcalpha = alpha; + dc_destalpha = FRACUNIT; + } + } + else if (masked) + { + wallfunc = &SWPixelFormatDrawers::DrawWallMaskedColumn; + } + else + { + wallfunc = &SWPixelFormatDrawers::DrawWallColumn; + } + } + + bool WallDrawerArgs::IsMaskedDrawer() const + { + return wallfunc == &SWPixelFormatDrawers::DrawWallMaskedColumn; + } +} diff --git a/src/swrenderer/viewport/r_walldrawer.h b/src/swrenderer/viewport/r_walldrawer.h new file mode 100644 index 000000000..e6d530932 --- /dev/null +++ b/src/swrenderer/viewport/r_walldrawer.h @@ -0,0 +1,80 @@ + +#pragma once + +#include "r_drawerargs.h" + +struct FSWColormap; +struct FLightNode; + +namespace swrenderer +{ + class RenderThread; + + class WallDrawerArgs : public DrawerArgs + { + public: + void SetStyle(bool masked, bool additive, fixed_t alpha); + void SetDest(int x, int y); + void SetCount(int count) { dc_count = count; } + void SetTexture(const uint8_t *pixels, const uint8_t *pixels2, int height) + { + dc_source = pixels; + dc_source2 = pixels2; + dc_textureheight = height; + } + void SetTextureFracBits(int bits) { dc_wall_fracbits = bits; } + void SetTextureUPos(uint32_t pos) { dc_texturefracx = pos; } + void SetTextureVPos(fixed_t pos) { dc_texturefrac = pos; } + void SetTextureVStep(fixed_t step) { dc_iscale = step; } + + bool IsMaskedDrawer() const; + + void DrawColumn(RenderThread *thread); + + uint8_t *Dest() const { return dc_dest; } + int DestY() const { return dc_dest_y; } + int Count() const { return dc_count; } + + uint32_t *SrcBlend() const { return dc_srcblend; } + uint32_t *DestBlend() const { return dc_destblend; } + fixed_t SrcAlpha() const { return dc_srcalpha; } + fixed_t DestAlpha() const { return dc_destalpha; } + + uint32_t TextureUPos() const { return dc_texturefracx; } + fixed_t TextureVPos() const { return dc_texturefrac; } + fixed_t TextureVStep() const { return dc_iscale; } + + const uint8_t *TexturePixels() const { return dc_source; } + const uint8_t *TexturePixels2() const { return dc_source2; } + uint32_t TextureHeight() const { return dc_textureheight; } + + int TextureFracBits() const { return dc_wall_fracbits; } + + FVector3 dc_normal; + FVector3 dc_viewpos; + FVector3 dc_viewpos_step; + DrawerLight *dc_lights = nullptr; + int dc_num_lights = 0; + + private: + uint8_t *dc_dest = nullptr; + int dc_dest_y = 0; + int dc_count; + + fixed_t dc_iscale; + fixed_t dc_texturefrac; + uint32_t dc_texturefracx; + uint32_t dc_textureheight; + const uint8_t *dc_source; + const uint8_t *dc_source2; + int dc_wall_fracbits; + + uint32_t *dc_srcblend; + uint32_t *dc_destblend; + fixed_t dc_srcalpha; + fixed_t dc_destalpha; + + typedef void(SWPixelFormatDrawers::*WallDrawerFunc)(const WallDrawerArgs &args); + WallDrawerFunc wallfunc = nullptr; + }; +} diff --git a/src/textures/automaptexture.cpp b/src/textures/automaptexture.cpp index 67d68b9fe..9aac379ef 100644 --- a/src/textures/automaptexture.cpp +++ b/src/textures/automaptexture.cpp @@ -122,6 +122,7 @@ void FAutomapTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/buildtexture.cpp b/src/textures/buildtexture.cpp index bfcc6333d..1155dacc4 100644 --- a/src/textures/buildtexture.cpp +++ b/src/textures/buildtexture.cpp @@ -56,7 +56,6 @@ public: const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetPixels (); - void Unload (); protected: const BYTE *Pixels; @@ -103,17 +102,6 @@ FBuildTexture::~FBuildTexture () // //========================================================================== -void FBuildTexture::Unload () -{ - // Nothing to do, since the pixels are accessed from memory-mapped files directly -} - -//========================================================================== -// -// -// -//========================================================================== - const BYTE *FBuildTexture::GetPixels () { return Pixels; diff --git a/src/textures/canvastexture.cpp b/src/textures/canvastexture.cpp index 062c3af1d..109d927ab 100644 --- a/src/textures/canvastexture.cpp +++ b/src/textures/canvastexture.cpp @@ -53,7 +53,6 @@ FCanvasTexture::FCanvasTexture (const char *name, int width, int height) DummySpans[1].TopOffset = 0; DummySpans[1].Length = 0; UseType = TEX_Wall; - Canvas = NULL; bNeedsUpdate = true; bDidUpdate = false; bHasCanvas = true; @@ -101,11 +100,22 @@ const BYTE *FCanvasTexture::GetPixels () return Pixels; } +const uint32_t *FCanvasTexture::GetPixelsBgra() +{ + bNeedsUpdate = true; + if (CanvasBgra == NULL) + { + MakeTextureBgra(); + } + return PixelsBgra; +} + void FCanvasTexture::MakeTexture () { - Canvas = new DSimpleCanvas (Width, Height); + Canvas = new DSimpleCanvas (Width, Height, false); Canvas->Lock (); GC::AddSoftRoot(Canvas); + if (Width != Height || Width != Canvas->GetPitch()) { Pixels = new BYTE[Width*Height]; @@ -113,29 +123,68 @@ void FCanvasTexture::MakeTexture () } else { - Pixels = Canvas->GetBuffer(); + Pixels = (BYTE*)Canvas->GetBuffer(); bPixelsAllocated = false; } + // Draw a special "unrendered" initial texture into the buffer. memset (Pixels, 0, Width*Height/2); memset (Pixels+Width*Height/2, 255, Width*Height/2); } +void FCanvasTexture::MakeTextureBgra() +{ + CanvasBgra = new DSimpleCanvas(Width, Height, true); + CanvasBgra->Lock(); + GC::AddSoftRoot(CanvasBgra); + + if (Width != Height || Width != CanvasBgra->GetPitch()) + { + PixelsBgra = new uint32_t[Width*Height]; + bPixelsAllocatedBgra = true; + } + else + { + PixelsBgra = (uint32_t*)CanvasBgra->GetBuffer(); + bPixelsAllocatedBgra = false; + } + + // Draw a special "unrendered" initial texture into the buffer. + memset(PixelsBgra, 0, Width*Height / 2 * 4); + memset(PixelsBgra + Width*Height / 2, 255, Width*Height / 2 * 4); +} + void FCanvasTexture::Unload () { if (bPixelsAllocated) { - if (Pixels != NULL) delete [] Pixels; + if (Pixels != NULL) delete[] Pixels; bPixelsAllocated = false; Pixels = NULL; } + if (bPixelsAllocatedBgra) + { + if (PixelsBgra != NULL) delete[] PixelsBgra; + bPixelsAllocatedBgra = false; + PixelsBgra = NULL; + } + if (Canvas != NULL) { GC::DelSoftRoot(Canvas); Canvas->Destroy(); Canvas = NULL; } + + if (CanvasBgra != NULL) + { + GC::DelSoftRoot(CanvasBgra); + CanvasBgra->Destroy(); + CanvasBgra = NULL; + } + + FTexture::Unload(); } bool FCanvasTexture::CheckModified () diff --git a/src/textures/ddstexture.cpp b/src/textures/ddstexture.cpp index c55cf11bb..90a8e38da 100644 --- a/src/textures/ddstexture.cpp +++ b/src/textures/ddstexture.cpp @@ -401,6 +401,7 @@ void FDDSTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/flattexture.cpp b/src/textures/flattexture.cpp index 840d53aaf..08e0d1221 100644 --- a/src/textures/flattexture.cpp +++ b/src/textures/flattexture.cpp @@ -138,6 +138,7 @@ void FFlatTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/imgztexture.cpp b/src/textures/imgztexture.cpp index 1c262d707..04932d4bf 100644 --- a/src/textures/imgztexture.cpp +++ b/src/textures/imgztexture.cpp @@ -142,6 +142,7 @@ void FIMGZTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/jpegtexture.cpp b/src/textures/jpegtexture.cpp index c138edbfa..684e06fa6 100644 --- a/src/textures/jpegtexture.cpp +++ b/src/textures/jpegtexture.cpp @@ -295,11 +295,9 @@ FJPEGTexture::~FJPEGTexture () void FJPEGTexture::Unload () { - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } + delete[] Pixels; + Pixels = NULL; + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/multipatchtexture.cpp b/src/textures/multipatchtexture.cpp index b6050aae6..1a704edf2 100644 --- a/src/textures/multipatchtexture.cpp +++ b/src/textures/multipatchtexture.cpp @@ -359,6 +359,7 @@ void FMultiPatchTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/patchtexture.cpp b/src/textures/patchtexture.cpp index 423ce4deb..8388515c0 100644 --- a/src/textures/patchtexture.cpp +++ b/src/textures/patchtexture.cpp @@ -184,6 +184,7 @@ void FPatchTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/pcxtexture.cpp b/src/textures/pcxtexture.cpp index dda431993..6f7c277e3 100644 --- a/src/textures/pcxtexture.cpp +++ b/src/textures/pcxtexture.cpp @@ -191,6 +191,7 @@ void FPCXTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/pngtexture.cpp b/src/textures/pngtexture.cpp index 414c424b8..30b7c6445 100644 --- a/src/textures/pngtexture.cpp +++ b/src/textures/pngtexture.cpp @@ -373,11 +373,9 @@ FPNGTexture::~FPNGTexture () void FPNGTexture::Unload () { - if (Pixels != NULL) - { - delete[] Pixels; - Pixels = NULL; - } + delete[] Pixels; + Pixels = NULL; + FTexture::Unload(); } //========================================================================== @@ -450,6 +448,7 @@ const BYTE *FPNGTexture::GetPixels () return Pixels; } + //========================================================================== // // diff --git a/src/textures/rawpagetexture.cpp b/src/textures/rawpagetexture.cpp index 1402f8844..69313fd1c 100644 --- a/src/textures/rawpagetexture.cpp +++ b/src/textures/rawpagetexture.cpp @@ -206,6 +206,7 @@ void FRawPageTexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp index 9c1b5fdb4..01906ac7b 100644 --- a/src/textures/texture.cpp +++ b/src/textures/texture.cpp @@ -176,6 +176,37 @@ FTexture::~FTexture () KillNative(); } +void FTexture::Unload() +{ + PixelsBgra = std::vector(); +} + +const uint32_t *FTexture::GetColumnBgra(unsigned int column, const Span **spans_out) +{ + const uint32_t *pixels = GetPixelsBgra(); + + column %= Width; + + if (spans_out != nullptr) + GetColumn(column, spans_out); + return pixels + column * Height; +} + +const uint32_t *FTexture::GetPixelsBgra() +{ + if (PixelsBgra.empty() || CheckModified()) + { + if (!GetColumn(0, nullptr)) + return nullptr; + + FBitmap bitmap; + bitmap.Create(GetWidth(), GetHeight()); + CopyTrueColorPixels(&bitmap, 0, 0); + GenerateBgraFromBitmap(bitmap); + } + return PixelsBgra.data(); +} + bool FTexture::CheckModified () { return false; @@ -320,6 +351,210 @@ void FTexture::FreeSpans (Span **spans) const M_Free (spans); } +void FTexture::GenerateBgraFromBitmap(const FBitmap &bitmap) +{ + CreatePixelsBgraWithMipmaps(); + + // Transpose + const uint32_t *src = (const uint32_t *)bitmap.GetPixels(); + uint32_t *dest = PixelsBgra.data(); + for (int x = 0; x < Width; x++) + { + for (int y = 0; y < Height; y++) + { + dest[y + x * Height] = src[x + y * Width]; + } + } + + GenerateBgraMipmaps(); +} + +void FTexture::CreatePixelsBgraWithMipmaps() +{ + int levels = MipmapLevels(); + int buffersize = 0; + for (int i = 0; i < levels; i++) + { + int w = MAX(Width >> i, 1); + int h = MAX(Height >> i, 1); + buffersize += w * h; + } + PixelsBgra.resize(buffersize, 0xffff0000); +} + +int FTexture::MipmapLevels() const +{ + int widthbits = 0; + while ((Width >> widthbits) != 0) widthbits++; + + int heightbits = 0; + while ((Height >> heightbits) != 0) heightbits++; + + return MAX(widthbits, heightbits); +} + +void FTexture::GenerateBgraMipmaps() +{ + struct Color4f + { + float a, r, g, b; + Color4f operator*(const Color4f &v) const { return Color4f{ a * v.a, r * v.r, g * v.g, b * v.b }; } + Color4f operator/(const Color4f &v) const { return Color4f{ a / v.a, r / v.r, g / v.g, b / v.b }; } + Color4f operator+(const Color4f &v) const { return Color4f{ a + v.a, r + v.r, g + v.g, b + v.b }; } + Color4f operator-(const Color4f &v) const { return Color4f{ a - v.a, r - v.r, g - v.g, b - v.b }; } + Color4f operator*(float s) const { return Color4f{ a * s, r * s, g * s, b * s }; } + Color4f operator/(float s) const { return Color4f{ a / s, r / s, g / s, b / s }; } + Color4f operator+(float s) const { return Color4f{ a + s, r + s, g + s, b + s }; } + Color4f operator-(float s) const { return Color4f{ a - s, r - s, g - s, b - s }; } + }; + + int levels = MipmapLevels(); + std::vector image(PixelsBgra.size()); + + // Convert to normalized linear colorspace + { + for (int x = 0; x < Width; x++) + { + for (int y = 0; y < Height; y++) + { + uint32_t c8 = PixelsBgra[x * Height + y]; + Color4f c; + c.a = powf(APART(c8) * (1.0f / 255.0f), 2.2f); + c.r = powf(RPART(c8) * (1.0f / 255.0f), 2.2f); + c.g = powf(GPART(c8) * (1.0f / 255.0f), 2.2f); + c.b = powf(BPART(c8) * (1.0f / 255.0f), 2.2f); + image[x * Height + y] = c; + } + } + } + + // Generate mipmaps + { + std::vector smoothed(Width * Height); + Color4f *src = image.data(); + Color4f *dest = src + Width * Height; + for (int i = 1; i < levels; i++) + { + int srcw = MAX(Width >> (i - 1), 1); + int srch = MAX(Height >> (i - 1), 1); + int w = MAX(Width >> i, 1); + int h = MAX(Height >> i, 1); + + // Downscale + for (int x = 0; x < w; x++) + { + int sx0 = x * 2; + int sx1 = MIN((x + 1) * 2, srcw - 1); + for (int y = 0; y < h; y++) + { + int sy0 = y * 2; + int sy1 = MIN((y + 1) * 2, srch - 1); + + Color4f src00 = src[sy0 + sx0 * srch]; + Color4f src01 = src[sy1 + sx0 * srch]; + Color4f src10 = src[sy0 + sx1 * srch]; + Color4f src11 = src[sy1 + sx1 * srch]; + Color4f c = (src00 + src01 + src10 + src11) * 0.25f; + + dest[y + x * h] = c; + } + } + + // Sharpen filter with a 3x3 kernel: + for (int x = 0; x < w; x++) + { + for (int y = 0; y < h; y++) + { + Color4f c = { 0.0f, 0.0f, 0.0f, 0.0f }; + for (int kx = -1; kx < 2; kx++) + { + for (int ky = -1; ky < 2; ky++) + { + int a = y + ky; + int b = x + kx; + if (a < 0) a = h - 1; + if (a == h) a = 0; + if (b < 0) b = w - 1; + if (b == w) b = 0; + c = c + dest[a + b * h]; + } + } + c = c * (1.0f / 9.0f); + smoothed[y + x * h] = c; + } + } + float k = 0.08f; + for (int j = 0; j < w * h; j++) + dest[j] = dest[j] + (dest[j] - smoothed[j]) * k; + + src = dest; + dest += w * h; + } + } + + // Convert to bgra8 sRGB colorspace + { + Color4f *src = image.data() + Width * Height; + uint32_t *dest = PixelsBgra.data() + Width * Height; + for (int i = 1; i < levels; i++) + { + int w = MAX(Width >> i, 1); + int h = MAX(Height >> i, 1); + for (int j = 0; j < w * h; j++) + { + uint32_t a = (uint32_t)clamp(powf(MAX(src[j].a, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f); + uint32_t r = (uint32_t)clamp(powf(MAX(src[j].r, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f); + uint32_t g = (uint32_t)clamp(powf(MAX(src[j].g, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f); + uint32_t b = (uint32_t)clamp(powf(MAX(src[j].b, 0.0f), 1.0f / 2.2f) * 255.0f + 0.5f, 0.0f, 255.0f); + dest[j] = (a << 24) | (r << 16) | (g << 8) | b; + } + src += w * h; + dest += w * h; + } + } +} + +void FTexture::GenerateBgraMipmapsFast() +{ + uint32_t *src = PixelsBgra.data(); + uint32_t *dest = src + Width * Height; + int levels = MipmapLevels(); + for (int i = 1; i < levels; i++) + { + int srcw = MAX(Width >> (i - 1), 1); + int srch = MAX(Height >> (i - 1), 1); + int w = MAX(Width >> i, 1); + int h = MAX(Height >> i, 1); + + for (int x = 0; x < w; x++) + { + int sx0 = x * 2; + int sx1 = MIN((x + 1) * 2, srcw - 1); + + for (int y = 0; y < h; y++) + { + int sy0 = y * 2; + int sy1 = MIN((y + 1) * 2, srch - 1); + + uint32_t src00 = src[sy0 + sx0 * srch]; + uint32_t src01 = src[sy1 + sx0 * srch]; + uint32_t src10 = src[sy0 + sx1 * srch]; + uint32_t src11 = src[sy1 + sx1 * srch]; + + uint32_t alpha = (APART(src00) + APART(src01) + APART(src10) + APART(src11) + 2) / 4; + uint32_t red = (RPART(src00) + RPART(src01) + RPART(src10) + RPART(src11) + 2) / 4; + uint32_t green = (GPART(src00) + GPART(src01) + GPART(src10) + GPART(src11) + 2) / 4; + uint32_t blue = (BPART(src00) + BPART(src01) + BPART(src10) + BPART(src11) + 2) / 4; + + dest[y + x * h] = (alpha << 24) | (red << 16) | (green << 8) | blue; + } + } + + src = dest; + dest += w * h; + } +} + void FTexture::CopyToBlock (BYTE *dest, int dwidth, int dheight, int xpos, int ypos, int rotate, const BYTE *translation) { const BYTE *pixels = GetPixels(); @@ -386,6 +621,29 @@ void FTexture::FlipSquareBlock (BYTE *block, int x, int y) } } +void FTexture::FlipSquareBlockBgra(uint32_t *block, int x, int y) +{ + int i, j; + + if (x != y) return; + + for (i = 0; i < x; ++i) + { + uint32_t *corner = block + x*i + i; + int count = x - i; + if (count & 1) + { + count--; + swapvalues(corner[count], corner[count*x]); + } + for (j = 0; j < count; j += 2) + { + swapvalues(corner[j], corner[j*x]); + swapvalues(corner[j + 1], corner[(j + 1)*x]); + } + } +} + void FTexture::FlipSquareBlockRemap (BYTE *block, int x, int y, const BYTE *remap) { int i, j; @@ -429,6 +687,19 @@ void FTexture::FlipNonSquareBlock (BYTE *dst, const BYTE *src, int x, int y, int } } +void FTexture::FlipNonSquareBlockBgra(uint32_t *dst, const uint32_t *src, int x, int y, int srcpitch) +{ + int i, j; + + for (i = 0; i < x; ++i) + { + for (j = 0; j < y; ++j) + { + dst[i*y + j] = src[i + j*srcpitch]; + } + } +} + void FTexture::FlipNonSquareBlockRemap (BYTE *dst, const BYTE *src, int x, int y, int srcpitch, const BYTE *remap) { int i, j; @@ -652,10 +923,6 @@ FDummyTexture::FDummyTexture () UseType = TEX_Null; } -void FDummyTexture::Unload () -{ -} - void FDummyTexture::SetSize (int width, int height) { Width = width; diff --git a/src/textures/textures.h b/src/textures/textures.h index 3e441b7bc..44d0d92bc 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -3,6 +3,7 @@ #include "doomtype.h" #include "vectors.h" +#include struct FloatRect { @@ -195,9 +196,18 @@ public: // Returns a single column of the texture virtual const BYTE *GetColumn (unsigned int column, const Span **spans_out) = 0; + // Returns a single column of the texture, in BGRA8 format + virtual const uint32_t *GetColumnBgra(unsigned int column, const Span **spans_out); + // Returns the whole texture, stored in column-major order virtual const BYTE *GetPixels () = 0; - + + // Returns the whole texture, stored in column-major order, in BGRA8 format + virtual const uint32_t *GetPixelsBgra(); + + // Returns true if GetPixelsBgra includes mipmaps + virtual bool Mipmapped() { return true; } + virtual int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate=0, FCopyInfo *inf = NULL); int CopyTrueColorTranslated(FBitmap *bmp, int x, int y, int rotate, FRemapTable *remap, FCopyInfo *inf = NULL); virtual bool UseBasePalette(); @@ -205,7 +215,7 @@ public: virtual FTexture *GetRedirect(bool wantwarped); virtual FTexture *GetRawTexture(); // for FMultiPatchTexture to override - virtual void Unload () = 0; + virtual void Unload (); // Returns the native pixel format for this image virtual FTextureFormat GetFormat(); @@ -287,6 +297,14 @@ protected: gl_info.areas = NULL; } + std::vector PixelsBgra; + + void GenerateBgraFromBitmap(const FBitmap &bitmap); + void CreatePixelsBgraWithMipmaps(); + void GenerateBgraMipmaps(); + void GenerateBgraMipmapsFast(); + int MipmapLevels() const; + private: bool bSWSkyColorDone = false; PalEntry FloorSkyColor; @@ -294,11 +312,14 @@ private: public: static void FlipSquareBlock (BYTE *block, int x, int y); + static void FlipSquareBlockBgra (uint32_t *block, int x, int y); static void FlipSquareBlockRemap (BYTE *block, int x, int y, const BYTE *remap); static void FlipNonSquareBlock (BYTE *blockto, const BYTE *blockfrom, int x, int y, int srcpitch); + static void FlipNonSquareBlockBgra (uint32_t *blockto, const uint32_t *blockfrom, int x, int y, int srcpitch); static void FlipNonSquareBlockRemap (BYTE *blockto, const BYTE *blockfrom, int x, int y, int srcpitch, const BYTE *remap); friend class D3DTex; + friend class OpenGLSWFrameBuffer; public: @@ -526,7 +547,6 @@ public: FDummyTexture (); const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetPixels (); - void Unload (); void SetSize (int width, int height); }; @@ -540,6 +560,7 @@ public: virtual int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate=0, FCopyInfo *inf = NULL); const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetPixels (); + const uint32_t *GetPixelsBgra() override; void Unload (); bool CheckModified (); @@ -573,21 +594,28 @@ public: const BYTE *GetColumn (unsigned int column, const Span **spans_out); const BYTE *GetPixels (); + const uint32_t *GetPixelsBgra() override; void Unload (); bool CheckModified (); void NeedUpdate() { bNeedsUpdate=true; } void SetUpdated() { bNeedsUpdate = false; bDidUpdate = true; bFirstUpdate = false; } DSimpleCanvas *GetCanvas() { return Canvas; } + DSimpleCanvas *GetCanvasBgra() { return CanvasBgra; } + bool Mipmapped() override { return false; } void MakeTexture (); + void MakeTextureBgra (); protected: - DSimpleCanvas *Canvas; - BYTE *Pixels; + DSimpleCanvas *Canvas = nullptr; + DSimpleCanvas *CanvasBgra = nullptr; + BYTE *Pixels = nullptr; + uint32_t *PixelsBgra = nullptr; Span DummySpans[2]; - bool bNeedsUpdate; - bool bDidUpdate; - bool bPixelsAllocated; + bool bNeedsUpdate = true; + bool bDidUpdate = false; + bool bPixelsAllocated = false; + bool bPixelsAllocatedBgra = false; public: bool bFirstUpdate; diff --git a/src/textures/tgatexture.cpp b/src/textures/tgatexture.cpp index 331747cfe..878b41084 100644 --- a/src/textures/tgatexture.cpp +++ b/src/textures/tgatexture.cpp @@ -181,6 +181,7 @@ void FTGATexture::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/textures/warptexture.cpp b/src/textures/warptexture.cpp index ee08148d2..3f1c40e68 100644 --- a/src/textures/warptexture.cpp +++ b/src/textures/warptexture.cpp @@ -39,6 +39,7 @@ #include "r_utility.h" #include "textures/textures.h" #include "warpbuffer.h" +#include "v_palette.h" FWarpTexture::FWarpTexture (FTexture *source, int warptype) @@ -74,6 +75,7 @@ void FWarpTexture::Unload () Spans = NULL; } SourcePic->Unload (); + FTexture::Unload(); } bool FWarpTexture::CheckModified () @@ -92,6 +94,25 @@ const BYTE *FWarpTexture::GetPixels () return Pixels; } +const uint32_t *FWarpTexture::GetPixelsBgra() +{ + DWORD time = r_FrameTime; + if (Pixels == NULL || time != GenTime) + { + MakeTexture(time); + CreatePixelsBgraWithMipmaps(); + for (int i = 0; i < Width * Height; i++) + { + if (Pixels[i] != 0) + PixelsBgra[i] = 0xff000000 | GPalette.BaseColors[Pixels[i]].d; + else + PixelsBgra[i] = 0; + } + GenerateBgraMipmapsFast(); + } + return PixelsBgra.data(); +} + const BYTE *FWarpTexture::GetColumn (unsigned int column, const Span **spans_out) { DWORD time = r_FrameTime; diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 0a9b75b19..5315657f4 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -32,7 +32,7 @@ ** */ -// #define NO_SWRENDER // set this if you want to exclude the software renderer. Without software renderer the base implementations of DrawTextureV and FillSimplePoly need to be disabled because they depend on it. +// #define NO_SWRENDER // set this if you want to exclude the software renderer. Without the software renderer software canvas drawing does nothing. #include #include @@ -42,10 +42,9 @@ #include "m_swap.h" #include "r_defs.h" #include "r_utility.h" +#include "r_renderer.h" #ifndef NO_SWRENDER -#include "r_draw.h" -#include "r_main.h" -#include "r_things.h" +#include "swrenderer/r_swcanvas.h" #endif #include "r_data/r_translate.h" #include "doomstat.h" @@ -81,16 +80,8 @@ int CleanWidth, CleanHeight; // Above minus 1 (or 1, if they are already 1) int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1; -// FillSimplePoly uses this -extern "C" short spanend[MAXHEIGHT]; - 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. -static int LastPal = -1; -static uint32 LastRGB; - DEFINE_ACTION_FUNCTION(_Screen, GetWidth) { PARAM_PROLOGUE; @@ -112,30 +103,6 @@ DEFINE_ACTION_FUNCTION(_Screen, PaletteColor) ACTION_RETURN_INT(index); } -static int PalFromRGB(uint32 rgb) -{ - if (LastPal >= 0 && LastRGB == rgb) - { - return LastPal; - } - // Quick check for black and white. - if (rgb == MAKEARGB(255,0,0,0)) - { - LastPal = GPalette.BlackIndex; - } - else if (rgb == MAKEARGB(255,255,255,255)) - { - LastPal = GPalette.WhiteIndex; - } - else - { - LastPal = ColorMatcher.Pick(RPART(rgb), GPART(rgb), BPART(rgb)); - } - LastRGB = rgb; - return LastPal; -} - - void DCanvas::DrawTexture (FTexture *img, double x, double y, int tags_first, ...) { Va_List tags; @@ -189,189 +156,13 @@ DEFINE_ACTION_FUNCTION(_Screen, DrawHUDTexture) void DCanvas::DrawTextureParms(FTexture *img, DrawParms &parms) { #ifndef NO_SWRENDER - using namespace swrenderer; - using namespace drawerargs; - - static short bottomclipper[MAXWIDTH], topclipper[MAXWIDTH]; - const BYTE *translation = NULL; - - if (APART(parms.colorOverlay) != 0) - { - // The software renderer cannot invert the source without inverting the overlay - // too. That means if the source is inverted, we need to do the reverse of what - // the invert overlay flag says to do. - INTBOOL invertoverlay = (parms.style.Flags & STYLEF_InvertOverlay); - - if (parms.style.Flags & STYLEF_InvertSource) - { - invertoverlay = !invertoverlay; - } - if (invertoverlay) - { - parms.colorOverlay = PalEntry(parms.colorOverlay).InverseColor(); - } - // Note that this overrides the translation in software, but not in hardware. - FDynamicColormap *colormap = GetSpecialLights(MAKERGB(255,255,255), - parms.colorOverlay & MAKEARGB(0,255,255,255), 0); - translation = &colormap->Maps[(APART(parms.colorOverlay)*NUMCOLORMAPS/255)*256]; - } - else if (parms.remap != NULL) - { - translation = parms.remap->Remap; - } - - if (translation != NULL) - { - dc_colormap = (lighttable_t *)translation; - } - else - { - dc_colormap = identitymap; - } - - fixedcolormap = dc_colormap; - ESPSResult mode = R_SetPatchStyle (parms.style, parms.Alpha, 0, parms.fillcolor); - - BYTE *destorgsave = dc_destorg; - dc_destorg = screen->GetBuffer(); - if (dc_destorg == NULL) - { - I_FatalError("Attempt to write to buffer of hardware canvas"); - } - - double x0 = parms.x - parms.left * parms.destwidth / parms.texwidth; - double y0 = parms.y - parms.top * parms.destheight / parms.texheight; - - if (mode != DontDraw) - { - int stop4; - - double centeryback = CenterY; - CenterY = 0; - - // There is not enough precision in the drawing routines to keep the full - // precision for y0. :( - modf(y0, &sprtopscreen); - - double yscale = parms.destheight / img->GetHeight(); - double iyscale = 1 / yscale; - - spryscale = yscale; - assert(spryscale > 0); - - sprflipvert = false; - //dc_iscale = FLOAT2FIXED(iyscale); - //dc_texturemid = (-y0) * iyscale; - //dc_iscale = 0xffffffffu / (unsigned)spryscale; - dc_iscale = FLOAT2FIXED(1 / spryscale); - dc_texturemid = (CenterY - 1 - sprtopscreen) * dc_iscale / 65536; - fixed_t frac = 0; - double xiscale = img->GetWidth() / parms.destwidth; - double x2 = x0 + parms.destwidth; - - if (bottomclipper[0] != parms.dclip) - { - fillshort(bottomclipper, screen->GetWidth(), (short)parms.dclip); - } - if (parms.uclip != 0) - { - if (topclipper[0] != parms.uclip) - { - fillshort(topclipper, screen->GetWidth(), (short)parms.uclip); - } - mceilingclip = topclipper; - } - else - { - mceilingclip = zeroarray; - } - mfloorclip = bottomclipper; - - if (parms.flipX) - { - frac = (img->GetWidth() << FRACBITS) - 1; - xiscale = -xiscale; - } - - if (parms.windowleft > 0 || parms.windowright < parms.texwidth) - { - double wi = MIN(parms.windowright, parms.texwidth); - double xscale = parms.destwidth / parms.texwidth; - x0 += parms.windowleft * xscale; - frac += FLOAT2FIXED(parms.windowleft); - x2 -= (parms.texwidth - wi) * xscale; - } - if (x0 < parms.lclip) - { - frac += FLOAT2FIXED((parms.lclip - x0) * xiscale); - x0 = parms.lclip; - } - if (x2 > parms.rclip) - { - x2 = parms.rclip; - } - - // Drawing short output ought to fit in the data cache well enough - // if we draw one column at a time, so do that, since it's simpler. - if (parms.destheight < 32 || (parms.dclip - parms.uclip) < 32) - { - mode = DoDraw0; - } - - dc_x = int(x0); - int x2_i = int(x2); - fixed_t xiscale_i = FLOAT2FIXED(xiscale); - - if (mode == DoDraw0) - { - // One column at a time - stop4 = dc_x; - } - else // DoDraw1` - { - // Up to four columns at a time - stop4 = x2_i & ~3; - } - - if (dc_x < x2_i) - { - while ((dc_x < stop4) && (dc_x & 3)) - { - R_DrawMaskedColumn(img, frac, false, !parms.masked); - dc_x++; - frac += xiscale_i; - } - - while (dc_x < stop4) - { - rt_initcols(); - for (int zz = 4; zz; --zz) - { - R_DrawMaskedColumn(img, frac, true, !parms.masked); - dc_x++; - frac += xiscale_i; - } - rt_draw4cols(dc_x - 4); - } - - while (dc_x < x2_i) - { - R_DrawMaskedColumn(img, frac, false, !parms.masked); - dc_x++; - frac += xiscale_i; - } - } - CenterY = centeryback; - } - R_FinishSetPatchStyle (); - - dc_destorg = destorgsave; + SWCanvas::DrawTexture(this, img, parms); +#endif if (ticdup != 0 && menuactive == MENU_Off) { NetUpdate(); } -#endif } bool DCanvas::SetTextureParms(DrawParms *parms, FTexture *img, double xx, double yy) const @@ -1082,225 +873,18 @@ void DCanvas::FillBorder (FTexture *img) } } -void DCanvas::PUTTRANSDOT (int xx, int yy, int basecolor, int level) -{ - static int oldyy; - static int oldyyshifted; - -#if 0 - if(xx < 32) - cc += 7-(xx>>2); - else if(xx > (finit_width - 32)) - cc += 7-((finit_width-xx) >> 2); -// if(cc==oldcc) //make sure that we don't double fade the corners. -// { - if(yy < 32) - cc += 7-(yy>>2); - else if(yy > (finit_height - 32)) - cc += 7-((finit_height-yy) >> 2); -// } - if(cc > cm && cm != NULL) - { - cc = cm; - } - else if(cc > oldcc+6) // don't let the color escape from the fade table... - { - cc=oldcc+6; - } -#endif - if (yy == oldyy+1) - { - oldyy++; - oldyyshifted += GetPitch(); - } - else if (yy == oldyy-1) - { - oldyy--; - oldyyshifted -= GetPitch(); - } - else if (yy != oldyy) - { - oldyy = yy; - oldyyshifted = yy * GetPitch(); - } - - BYTE *spot = GetBuffer() + oldyyshifted + xx; - DWORD *bg2rgb = Col2RGB8[1+level]; - DWORD *fg2rgb = Col2RGB8[63-level]; - DWORD fg = fg2rgb[basecolor]; - DWORD bg = bg2rgb[*spot]; - bg = (fg+bg) | 0x1f07c1f; - *spot = RGB32k.All[bg&(bg>>15)]; -} - void DCanvas::DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor) -//void DrawTransWuLine (int x0, int y0, int x1, int y1, BYTE palColor) { - const int WeightingScale = 0; - const int WEIGHTBITS = 6; - const int WEIGHTSHIFT = 16-WEIGHTBITS; - const int NUMWEIGHTS = (1< y1) - { - int temp = y0; y0 = y1; y1 = temp; - temp = x0; x0 = x1; x1 = temp; - } - - PUTTRANSDOT (x0, y0, palColor, 0); - - if ((deltaX = x1 - x0) >= 0) - { - xDir = 1; - } - else - { - xDir = -1; - deltaX = -deltaX; - } - - if ((deltaY = y1 - y0) == 0) - { // horizontal line - if (x0 > x1) - { - swapvalues (x0, x1); - } - memset (GetBuffer() + y0*GetPitch() + x0, palColor, deltaX+1); - } - else if (deltaX == 0) - { // vertical line - BYTE *spot = GetBuffer() + y0*GetPitch() + x0; - int pitch = GetPitch (); - do - { - *spot = palColor; - spot += pitch; - } while (--deltaY != 0); - } - else if (deltaX == deltaY) - { // diagonal line. - BYTE *spot = GetBuffer() + y0*GetPitch() + x0; - int advance = GetPitch() + xDir; - do - { - *spot = palColor; - spot += advance; - } while (--deltaY != 0); - } - else - { - // line is not horizontal, diagonal, or vertical - fixed_t errorAcc = 0; - - if (deltaY > deltaX) - { // y-major line - fixed_t errorAdj = (((unsigned)deltaX << 16) / (unsigned)deltaY) & 0xffff; - if (xDir < 0) - { - if (WeightingScale == 0) - { - while (--deltaY) - { - errorAcc += errorAdj; - y0++; - int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; - PUTTRANSDOT (x0 - (errorAcc >> 16), y0, palColor, weighting); - PUTTRANSDOT (x0 - (errorAcc >> 16) - 1, y0, - palColor, WEIGHTMASK - weighting); - } - } - else - { - while (--deltaY) - { - errorAcc += errorAdj; - y0++; - int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK; - PUTTRANSDOT (x0 - (errorAcc >> 16), y0, palColor, weighting); - PUTTRANSDOT (x0 - (errorAcc >> 16) - 1, y0, - palColor, WEIGHTMASK - weighting); - } - } - } - else - { - if (WeightingScale == 0) - { - while (--deltaY) - { - errorAcc += errorAdj; - y0++; - int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; - PUTTRANSDOT (x0 + (errorAcc >> 16), y0, palColor, weighting); - PUTTRANSDOT (x0 + (errorAcc >> 16) + xDir, y0, - palColor, WEIGHTMASK - weighting); - } - } - else - { - while (--deltaY) - { - errorAcc += errorAdj; - y0++; - int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK; - PUTTRANSDOT (x0 + (errorAcc >> 16), y0, palColor, weighting); - PUTTRANSDOT (x0 + (errorAcc >> 16) + xDir, y0, - palColor, WEIGHTMASK - weighting); - } - } - } - } - else - { // x-major line - fixed_t errorAdj = (((DWORD) deltaY << 16) / (DWORD) deltaX) & 0xffff; - - if (WeightingScale == 0) - { - while (--deltaX) - { - errorAcc += errorAdj; - x0 += xDir; - int weighting = (errorAcc >> WEIGHTSHIFT) & WEIGHTMASK; - PUTTRANSDOT (x0, y0 + (errorAcc >> 16), palColor, weighting); - PUTTRANSDOT (x0, y0 + (errorAcc >> 16) + 1, - palColor, WEIGHTMASK - weighting); - } - } - else - { - while (--deltaX) - { - errorAcc += errorAdj; - x0 += xDir; - int weighting = ((errorAcc * WeightingScale) >> (WEIGHTSHIFT+8)) & WEIGHTMASK; - PUTTRANSDOT (x0, y0 + (errorAcc >> 16), palColor, weighting); - PUTTRANSDOT (x0, y0 + (errorAcc >> 16) + 1, - palColor, WEIGHTMASK - weighting); - } - } - } - PUTTRANSDOT (x1, y1, palColor, 0); - } - Unlock(); +#ifndef NO_SWRENDER + SWCanvas::DrawLine(this, x0, y0, x1, y1, palColor, realcolor); +#endif } void DCanvas::DrawPixel(int x, int y, int palColor, uint32 realcolor) { - if (palColor < 0) - { - palColor = PalFromRGB(realcolor); - } - - Buffer[Pitch * y + x] = (BYTE)palColor; +#ifndef NO_SWRENDER + SWCanvas::DrawPixel(this, x, y, palColor, realcolor); +#endif } //========================================================================== @@ -1313,44 +897,16 @@ void DCanvas::DrawPixel(int x, int y, int palColor, uint32 realcolor) void DCanvas::Clear (int left, int top, int right, int bottom, int palcolor, uint32 color) { - int x, y; - BYTE *dest; - - if (left == right || top == bottom) +#ifndef NO_SWRENDER + if (palcolor < 0 && APART(color) != 255) { - return; + Dim(color, APART(color) / 255.f, left, top, right - left, bottom - top); } - - assert(left < right); - assert(top < bottom); - - if (left >= Width || right <= 0 || top >= Height || bottom <= 0) + else { - return; - } - left = MAX(0,left); - right = MIN(Width,right); - top = MAX(0,top); - bottom = MIN(Height,bottom); - - if (palcolor < 0) - { - if (APART(color) != 255) - { - Dim(color, APART(color)/255.f, left, top, right - left, bottom - top); - return; - } - - palcolor = PalFromRGB(color); - } - - dest = Buffer + top * Pitch + left; - x = right - left; - for (y = top; y < bottom; y++) - { - memset(dest, palcolor, x); - dest += Pitch; + SWCanvas::Clear(this, left, top, right, bottom, palcolor, color); } +#endif } DEFINE_ACTION_FUNCTION(_Screen, Clear) @@ -1366,6 +922,21 @@ DEFINE_ACTION_FUNCTION(_Screen, Clear) return 0; } +//========================================================================== +// +// DCanvas :: Dim +// +// Applies a colored overlay to an area of the screen. +// +//========================================================================== + +void DCanvas::Dim(PalEntry color, float damount, int x1, int y1, int w, int h) +{ +#ifndef NO_SWRENDER + SWCanvas::Dim(this, color, damount, x1, y1, w, h); +#endif +} + //========================================================================== // // DCanvas :: FillSimplePoly @@ -1386,185 +957,7 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, FDynamicColormap *colormap, PalEntry flatcolor, int lightlevel, int bottomclip) { #ifndef NO_SWRENDER - using namespace swrenderer; - using namespace drawerargs; - - // Use an equation similar to player sprites to determine shade - fixed_t shade = LIGHT2SHADE(lightlevel) - 12*FRACUNIT; - float topy, boty, leftx, rightx; - int toppt, botpt, pt1, pt2; - int i; - int y1, y2, y; - fixed_t x; - bool dorotate = rotation != 0.; - double cosrot, sinrot; - - if (--npoints < 2 || Buffer == NULL) - { // not a polygon or we're not locked - return; - } - - if (bottomclip <= 0) - { - bottomclip = Height; - } - - // Find the extents of the polygon, in particular the highest and lowest points. - for (botpt = toppt = 0, boty = topy = points[0].Y, leftx = rightx = points[0].X, i = 1; i <= npoints; ++i) - { - if (points[i].Y < topy) - { - topy = points[i].Y; - toppt = i; - } - if (points[i].Y > boty) - { - boty = points[i].Y; - botpt = i; - } - if (points[i].X < leftx) - { - leftx = points[i].X; - } - if (points[i].X > rightx) - { - rightx = points[i].X; - } - } - if (topy >= bottomclip || // off the bottom of the screen - boty <= 0 || // off the top of the screen - leftx >= Width || // off the right of the screen - rightx <= 0) // off the left of the screen - { - return; - } - - BYTE *destorgsave = dc_destorg; - dc_destorg = screen->GetBuffer(); - if (dc_destorg == NULL) - { - I_FatalError("Attempt to write to buffer of hardware canvas"); - } - - scalex /= tex->Scale.X; - scaley /= tex->Scale.Y; - - // Use the CRT's functions here. - cosrot = cos(rotation.Radians()); - sinrot = sin(rotation.Radians()); - - // Setup constant texture mapping parameters. - R_SetupSpanBits(tex); - R_SetSpanColormap(colormap != NULL ? &colormap->Maps[clamp(shade >> FRACBITS, 0, NUMCOLORMAPS-1) * 256] : identitymap); - R_SetSpanSource(tex); - if (ds_xbits != 0) - { - scalex = double(1u << (32 - ds_xbits)) / scalex; - ds_xstep = xs_RoundToInt(cosrot * scalex); - } - else - { // Texture is one pixel wide. - scalex = 0; - ds_xstep = 0; - } - if (ds_ybits != 0) - { - scaley = double(1u << (32 - ds_ybits)) / scaley; - ds_ystep = xs_RoundToInt(sinrot * scaley); - } - else - { // Texture is one pixel tall. - scaley = 0; - ds_ystep = 0; - } - - // Travel down the right edge and create an outline of that edge. - pt1 = toppt; - pt2 = toppt + 1; if (pt2 > npoints) pt2 = 0; - y1 = xs_RoundToInt(points[pt1].Y + 0.5f); - do - { - x = FLOAT2FIXED(points[pt1].X + 0.5f); - y2 = xs_RoundToInt(points[pt2].Y + 0.5f); - if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) - { - } - else - { - fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); - int y3 = MIN(y2, bottomclip); - if (y1 < 0) - { - x += xinc * -y1; - y1 = 0; - } - for (y = y1; y < y3; ++y) - { - spanend[y] = clamp(x >> FRACBITS, -1, Width); - x += xinc; - } - } - y1 = y2; - pt1 = pt2; - pt2++; if (pt2 > npoints) pt2 = 0; - } while (pt1 != botpt); - - // Travel down the left edge and fill it in. - pt1 = toppt; - pt2 = toppt - 1; if (pt2 < 0) pt2 = npoints; - y1 = xs_RoundToInt(points[pt1].Y + 0.5f); - do - { - x = FLOAT2FIXED(points[pt1].X + 0.5f); - y2 = xs_RoundToInt(points[pt2].Y + 0.5f); - if (y1 >= y2 || (y1 < 0 && y2 < 0) || (y1 >= bottomclip && y2 >= bottomclip)) - { - } - else - { - fixed_t xinc = FLOAT2FIXED((points[pt2].X - points[pt1].X) / (points[pt2].Y - points[pt1].Y)); - int y3 = MIN(y2, bottomclip); - if (y1 < 0) - { - x += xinc * -y1; - y1 = 0; - } - for (y = y1; y < y3; ++y) - { - int x1 = x >> FRACBITS; - int x2 = spanend[y]; - if (x2 > x1 && x2 > 0 && x1 < Width) - { - x1 = MAX(x1, 0); - x2 = MIN(x2, Width); -#if 0 - memset(this->Buffer + y * this->Pitch + x1, (int)tex, x2 - x1); -#else - ds_y = y; - ds_x1 = x1; - ds_x2 = x2 - 1; - - DVector2 tex(x1 - originx, y - originy); - if (dorotate) - { - double t = tex.X; - tex.X = t * cosrot - tex.Y * sinrot; - tex.Y = tex.Y * cosrot + t * sinrot; - } - ds_xfrac = xs_RoundToInt(tex.X * scalex); - ds_yfrac = xs_RoundToInt(tex.Y * scaley); - - R_DrawSpan(); -#endif - } - x += xinc; - } - } - y1 = y2; - pt1 = pt2; - pt2--; if (pt2 < 0) pt2 = npoints; - } while (pt1 != botpt); - dc_destorg = destorgsave; + SWCanvas::FillSimplePoly(this, tex, points, npoints, originx, originy, scalex, scaley, rotation, colormap, flatcolor, lightlevel, bottomclip); #endif } @@ -1582,6 +975,9 @@ void DCanvas::FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, // void DCanvas::DrawBlock (int x, int y, int _width, int _height, const BYTE *src) const { + if (IsBgra()) + return; + int srcpitch = _width; int destpitch; BYTE *dest; @@ -1608,6 +1004,9 @@ void DCanvas::DrawBlock (int x, int y, int _width, int _height, const BYTE *src) // void DCanvas::GetBlock (int x, int y, int _width, int _height, BYTE *dest) const { + if (IsBgra()) + return; + const BYTE *src; #ifdef RANGECHECK diff --git a/src/v_font.cpp b/src/v_font.cpp index d668de94c..7bd855cb5 100644 --- a/src/v_font.cpp +++ b/src/v_font.cpp @@ -1716,6 +1716,7 @@ void FFontChar1::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== @@ -1777,6 +1778,7 @@ void FFontChar2::Unload () delete[] Pixels; Pixels = NULL; } + FTexture::Unload(); } //========================================================================== diff --git a/src/v_video.cpp b/src/v_video.cpp index 7c25b0702..8d7a4e300 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -67,12 +67,15 @@ #include "menu/menu.h" #include "r_data/voxels.h" +EXTERN_CVAR(Bool, r_blendmethod) + int active_con_scale(); FRenderer *Renderer; IMPLEMENT_CLASS(DCanvas, true, false) IMPLEMENT_CLASS(DFrameBuffer, true, false) +EXTERN_CVAR (Bool, swtruecolor) EXTERN_CVAR (Bool, fullscreen) #if defined(_DEBUG) && defined(_M_IX86) && !defined(__MINGW32__) @@ -86,7 +89,7 @@ class DDummyFrameBuffer : public DFrameBuffer DECLARE_CLASS (DDummyFrameBuffer, DFrameBuffer); public: DDummyFrameBuffer (int width, int height) - : DFrameBuffer (0, 0) + : DFrameBuffer (0, 0, false) { Width = width; Height = height; @@ -122,7 +125,6 @@ public: const BYTE *GetColumn(unsigned int column, const Span **spans_out); const BYTE *GetPixels(); - void Unload(); bool CheckModified(); void SetTranslation(int num); @@ -213,13 +215,14 @@ DCanvas *DCanvas::CanvasChain = NULL; // //========================================================================== -DCanvas::DCanvas (int _width, int _height) +DCanvas::DCanvas (int _width, int _height, bool _bgra) { // Init member vars Buffer = NULL; LockCount = 0; Width = _width; Height = _height; + Bgra = _bgra; // Add to list of active canvases Next = CanvasChain; @@ -336,70 +339,6 @@ void DCanvas::Dim (PalEntry color) Dim (dimmer, amount, 0, 0, Width, Height); } -//========================================================================== -// -// DCanvas :: Dim -// -// Applies a colored overlay to an area of the screen. -// -//========================================================================== - -void DCanvas::Dim (PalEntry color, float damount, int x1, int y1, int w, int h) -{ - if (damount == 0.f) - return; - - DWORD *bg2rgb; - DWORD fg; - int gap; - BYTE *spot; - int x, y; - - if (x1 >= Width || y1 >= Height) - { - return; - } - if (x1 + w > Width) - { - w = Width - x1; - } - if (y1 + h > Height) - { - h = Height - y1; - } - if (w <= 0 || h <= 0) - { - return; - } - - { - int amount; - - amount = (int)(damount * 64); - bg2rgb = Col2RGB8[64-amount]; - - fg = (((color.r * amount) >> 4) << 20) | - ((color.g * amount) >> 4) | - (((color.b * amount) >> 4) << 10); - } - - spot = Buffer + x1 + y1*Pitch; - gap = Pitch - w; - for (y = h; y != 0; y--) - { - for (x = w; x != 0; x--) - { - DWORD bg; - - bg = bg2rgb[(*spot)&0xff]; - bg = (fg+bg) | 0x1f07c1f; - *spot = RGB32k.All[bg&(bg>>15)]; - spot++; - } - spot += gap; - } -} - DEFINE_ACTION_FUNCTION(_Screen, Dim) { PARAM_PROLOGUE; @@ -427,8 +366,8 @@ void DCanvas::GetScreenshotBuffer(const BYTE *&buffer, int &pitch, ESSType &colo { Lock(true); buffer = GetBuffer(); - pitch = GetPitch(); - color_type = SS_PAL; + pitch = IsBgra() ? GetPitch() * 4 : GetPitch(); + color_type = IsBgra() ? SS_BGRA : SS_PAL; } //========================================================================== @@ -736,13 +675,12 @@ void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256]) // I found this formula on the web at // , // but that page no longer exits. - double invgamma = 1.f / gamma; int i; for (i = 0; i < 256; i++) { - gammalookup[i] = (BYTE)(255.0 * pow (i / 255.0, invgamma)); + gammalookup[i] = (BYTE)(255.0 * pow (i / 255.0, invgamma) + 0.5); } } @@ -754,8 +692,8 @@ void DCanvas::CalcGamma (float gamma, BYTE gammalookup[256]) // //========================================================================== -DSimpleCanvas::DSimpleCanvas (int width, int height) - : DCanvas (width, height) +DSimpleCanvas::DSimpleCanvas (int width, int height, bool bgra) + : DCanvas (width, height, bgra) { MemBuffer = nullptr; Resize(width, height); @@ -807,8 +745,9 @@ void DSimpleCanvas::Resize(int width, int height) Pitch = width + MAX(0, CPU.DataL1LineSize - 8); } } - MemBuffer = new BYTE[Pitch * height]; - memset(MemBuffer, 0, Pitch * height); + int bytes_per_pixel = Bgra ? 4 : 1; + MemBuffer = new BYTE[Pitch * height * bytes_per_pixel]; + memset (MemBuffer, 0, Pitch * height * bytes_per_pixel); } //========================================================================== @@ -877,8 +816,8 @@ void DSimpleCanvas::Unlock () // //========================================================================== -DFrameBuffer::DFrameBuffer (int width, int height) - : DSimpleCanvas (width, height) +DFrameBuffer::DFrameBuffer (int width, int height, bool bgra) + : DSimpleCanvas (width, height, bgra) { LastMS = LastSec = FrameCount = LastCount = LastTic = 0; Accel2D = false; @@ -887,6 +826,70 @@ DFrameBuffer::DFrameBuffer (int width, int height) VideoHeight = height; } +//========================================================================== +// +// DFrameBuffer :: PostprocessBgra +// +// Copies data to destination buffer while performing gamma and flash. +// This is only needed if a target cannot do this with shaders. +// +//========================================================================== + +void DFrameBuffer::CopyWithGammaBgra(void *output, int pitch, const BYTE *gammared, const BYTE *gammagreen, const BYTE *gammablue, PalEntry flash, int flash_amount) +{ + const BYTE *gammatables[3] = { gammared, gammagreen, gammablue }; + + if (flash_amount > 0) + { + uint16_t inv_flash_amount = 256 - flash_amount; + uint16_t flash_red = flash.r * flash_amount; + uint16_t flash_green = flash.g * flash_amount; + uint16_t flash_blue = flash.b * flash_amount; + + for (int y = 0; y < Height; y++) + { + BYTE *dest = (BYTE*)output + y * pitch; + BYTE *src = MemBuffer + y * Pitch * 4; + for (int x = 0; x < Width; x++) + { + uint16_t fg_red = src[2]; + uint16_t fg_green = src[1]; + uint16_t fg_blue = src[0]; + uint16_t red = (fg_red * inv_flash_amount + flash_red) >> 8; + uint16_t green = (fg_green * inv_flash_amount + flash_green) >> 8; + uint16_t blue = (fg_blue * inv_flash_amount + flash_blue) >> 8; + + dest[0] = gammatables[2][blue]; + dest[1] = gammatables[1][green]; + dest[2] = gammatables[0][red]; + dest[3] = 0xff; + + dest += 4; + src += 4; + } + } + } + else + { + for (int y = 0; y < Height; y++) + { + BYTE *dest = (BYTE*)output + y * pitch; + BYTE *src = MemBuffer + y * Pitch * 4; + for (int x = 0; x < Width; x++) + { + dest[0] = gammatables[2][src[0]]; + dest[1] = gammatables[1][src[1]]; + dest[2] = gammatables[0][src[2]]; + dest[3] = 0xff; + + dest += 4; + src += 4; + } + } + } +} + + //========================================================================== // // DFrameBuffer :: DrawRateStuff @@ -946,10 +949,21 @@ void DFrameBuffer::DrawRateStuff () // Buffer can be NULL if we're doing hardware accelerated 2D if (buffer != NULL) { - buffer += (GetHeight()-1) * GetPitch(); - - for (i = 0; i < tics*2; i += 2) buffer[i] = 0xff; - for ( ; i < 20*2; i += 2) buffer[i] = 0x00; + if (IsBgra()) + { + uint32_t *buffer32 = (uint32_t*)buffer; + buffer32 += (GetHeight() - 1) * GetPitch(); + + for (i = 0; i < tics * 2; i += 2) buffer32[i] = 0xffffffff; + for (; i < 20 * 2; i += 2) buffer32[i] = 0xff000000; + } + else + { + buffer += (GetHeight() - 1) * GetPitch(); + + for (i = 0; i < tics * 2; i += 2) buffer[i] = 0xff; + for (; i < 20 * 2; i += 2) buffer[i] = 0x00; + } } else { @@ -1022,16 +1036,6 @@ void FPaletteTester::SetTranslation(int num) } } -//========================================================================== -// -// FPaletteTester :: Unload -// -//========================================================================== - -void FPaletteTester::Unload() -{ -} - //========================================================================== // // FPaletteTester :: GetColumn diff --git a/src/v_video.h b/src/v_video.h index 8618f95ba..81b666124 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -197,7 +197,7 @@ class DCanvas : public DObject { DECLARE_ABSTRACT_CLASS (DCanvas, DObject) public: - DCanvas (int width, int height); + DCanvas (int width, int height, bool bgra); virtual ~DCanvas (); // Member variable access @@ -205,6 +205,7 @@ public: inline int GetWidth () const { return Width; } inline int GetHeight () const { return Height; } inline int GetPitch () const { return Pitch; } + inline bool IsBgra() const { return Bgra; } virtual bool IsValid (); @@ -282,6 +283,7 @@ protected: int Height; int Pitch; int LockCount; + bool Bgra; void DrawTextCommon(FFont *font, int normalcolor, double x, double y, const char *string, DrawParms &parms); @@ -298,8 +300,6 @@ private: // Keep track of canvases, for automatic destruction at exit DCanvas *Next; static DCanvas *CanvasChain; - - void PUTTRANSDOT (int xx, int yy, int basecolor, int level); }; // A canvas in system memory. @@ -308,7 +308,7 @@ class DSimpleCanvas : public DCanvas { DECLARE_CLASS (DSimpleCanvas, DCanvas) public: - DSimpleCanvas (int width, int height); + DSimpleCanvas (int width, int height, bool bgra); ~DSimpleCanvas (); bool IsValid (); @@ -348,7 +348,7 @@ class DFrameBuffer : public DSimpleCanvas { DECLARE_ABSTRACT_CLASS (DFrameBuffer, DSimpleCanvas) public: - DFrameBuffer (int width, int height); + DFrameBuffer (int width, int height, bool bgra); // Force the surface to use buffered output if true is passed. virtual bool Lock (bool buffered) = 0; @@ -443,6 +443,7 @@ public: protected: void DrawRateStuff (); void CopyFromBuff (BYTE *src, int srcPitch, int width, int height, BYTE *dest); + void CopyWithGammaBgra(void *output, int pitch, const BYTE *gammared, const BYTE *gammagreen, const BYTE *gammablue, PalEntry flash, int flash_amount); DFrameBuffer () {} diff --git a/src/version.h b/src/version.h index 4f8f76fab..ad6a47390 100644 --- a/src/version.h +++ b/src/version.h @@ -46,12 +46,12 @@ const char *GetVersionString(); #ifdef GIT_DESCRIPTION #define VERSIONSTR GIT_DESCRIPTION #else -#define VERSIONSTR "2.3pre" +#define VERSIONSTR "1.3pre" #endif // The version as seen in the Windows resource -#define RC_FILEVERSION 2,3,9999,0 -#define RC_PRODUCTVERSION 2,3,9999,0 +#define RC_FILEVERSION 1,3,9999,0 +#define RC_PRODUCTVERSION 1,3,9999,0 #define RC_PRODUCTVERSION2 VERSIONSTR // These are for content versioning. The current state is '2.4'. #define VER_MAJOR 2 @@ -92,14 +92,14 @@ const char *GetVersionString(); #define SAVEVER 4550 // This is so that derivates can use the same savegame versions without worrying about engine compatibility -#define GAMESIG "GZDOOM" -#define BASEWAD "gzdoom.pk3" +#define GAMESIG "QZDOOM" +#define BASEWAD "qzdoom.pk3" // More stuff that needs to be different for derivatives. -#define GAMENAME "GZDoom" -#define GAMENAMELOWERCASE "gzdoom" -#define FORUM_URL "http://forum.drdteam.org" -#define BUGS_FORUM_URL "http://forum.drdteam.org/viewforum.php?f=24" +#define GAMENAME "QZDoom" +#define GAMENAMELOWERCASE "qzdoom" +#define FORUM_URL "http://forum.drdteam.org/viewforum.php?f=196" +#define BUGS_FORUM_URL "http://forum.drdteam.org/viewforum.php?f=197" #if defined(__APPLE__) || defined(_WIN32) #define GAME_DIR GAMENAME diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp index efdced151..5eb86d9c1 100644 --- a/src/win32/fb_d3d9.cpp +++ b/src/win32/fb_d3d9.cpp @@ -63,7 +63,6 @@ #include "v_pfx.h" #include "stats.h" #include "doomerrors.h" -#include "r_main.h" #include "r_data/r_translate.h" #include "f_wipe.h" #include "sbar.h" @@ -73,6 +72,7 @@ #include "w_wad.h" #include "r_data/colormaps.h" #include "SkylineBinPack.h" +#include "swrenderer/scene/r_light.h" // MACROS ------------------------------------------------------------------ @@ -242,8 +242,8 @@ CVAR(Bool, vid_hwaalines, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // //========================================================================== -D3DFB::D3DFB (UINT adapter, int width, int height, bool fullscreen) - : BaseWinFB (width, height) +D3DFB::D3DFB (UINT adapter, int width, int height, bool bgra, bool fullscreen) + : BaseWinFB (width, height, bgra) { D3DPRESENT_PARAMETERS d3dpp; @@ -765,14 +765,16 @@ void D3DFB::KillNativeTexs() bool D3DFB::CreateFBTexture () { - if (FAILED(D3DDevice->CreateTexture(Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) + FBFormat = IsBgra() ? D3DFMT_A8R8G8B8 : D3DFMT_L8; + + if (FAILED(D3DDevice->CreateTexture(Width, Height, 1, D3DUSAGE_DYNAMIC, FBFormat, D3DPOOL_DEFAULT, &FBTexture, NULL))) { int pow2width, pow2height, i; for (i = 1; i < Width; i <<= 1) {} pow2width = i; for (i = 1; i < Height; i <<= 1) {} pow2height = i; - if (FAILED(D3DDevice->CreateTexture(pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) + if (FAILED(D3DDevice->CreateTexture(pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, FBFormat, D3DPOOL_DEFAULT, &FBTexture, NULL))) { return false; } @@ -1304,20 +1306,45 @@ void D3DFB::Draw3DPart(bool copy3d) SUCCEEDED(FBTexture->LockRect (0, &lockrect, NULL, D3DLOCK_DISCARD))) || SUCCEEDED(FBTexture->LockRect (0, &lockrect, &texrect, 0))) { - if (lockrect.Pitch == Pitch && Pitch == Width) + if (IsBgra() && FBFormat == D3DFMT_A8R8G8B8) { - memcpy (lockrect.pBits, MemBuffer, Width * Height); + if (lockrect.Pitch == Pitch * sizeof(uint32_t) && Pitch == Width) + { + memcpy(lockrect.pBits, MemBuffer, Width * Height * sizeof(uint32_t)); + } + else + { + uint32_t *dest = (uint32_t *)lockrect.pBits; + uint32_t *src = (uint32_t*)MemBuffer; + for (int y = 0; y < Height; y++) + { + memcpy(dest, src, Width * sizeof(uint32_t)); + dest = reinterpret_cast(reinterpret_cast(dest) + lockrect.Pitch); + src += Pitch; + } + } + } + else if (!IsBgra() && FBFormat == D3DFMT_L8) + { + if (lockrect.Pitch == Pitch && Pitch == Width) + { + memcpy(lockrect.pBits, MemBuffer, Width * Height); + } + else + { + BYTE *dest = (BYTE *)lockrect.pBits; + BYTE *src = (BYTE *)MemBuffer; + for (int y = 0; y < Height; y++) + { + memcpy(dest, src, Width); + dest = reinterpret_cast(reinterpret_cast(dest) + lockrect.Pitch); + src += Pitch; + } + } } else { - BYTE *dest = (BYTE *)lockrect.pBits; - BYTE *src = MemBuffer; - for (int y = 0; y < Height; y++) - { - memcpy (dest, src, Width); - dest += lockrect.Pitch; - src += Pitch; - } + memset(lockrect.pBits, 0, lockrect.Pitch * Height); } FBTexture->UnlockRect (0); } @@ -1349,25 +1376,30 @@ void D3DFB::Draw3DPart(bool copy3d) memset(Constant, 0, sizeof(Constant)); SetAlphaBlend(D3DBLENDOP(0)); EnableAlphaTest(FALSE); - SetPixelShader(Shaders[SHADER_NormalColorPal]); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_NormalColor]); + else + SetPixelShader(Shaders[SHADER_NormalColorPal]); if (copy3d) { FBVERTEX verts[4]; D3DCOLOR color0, color1; if (Accel2D) { - if (realfixedcolormap == NULL) + auto map = swrenderer::CameraLight::Instance()->ShaderColormap(); + if (map == NULL) { color0 = 0; color1 = 0xFFFFFFF; } else { - color0 = D3DCOLOR_COLORVALUE(realfixedcolormap->ColorizeStart[0]/2, - realfixedcolormap->ColorizeStart[1]/2, realfixedcolormap->ColorizeStart[2]/2, 0); - color1 = D3DCOLOR_COLORVALUE(realfixedcolormap->ColorizeEnd[0]/2, - realfixedcolormap->ColorizeEnd[1]/2, realfixedcolormap->ColorizeEnd[2]/2, 1); - SetPixelShader(Shaders[SHADER_SpecialColormapPal]); + color0 = D3DCOLOR_COLORVALUE(map->ColorizeStart[0]/2, map->ColorizeStart[1]/2, map->ColorizeStart[2]/2, 0); + color1 = D3DCOLOR_COLORVALUE(map->ColorizeEnd[0]/2, map->ColorizeEnd[1]/2, map->ColorizeEnd[2]/2, 1); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_SpecialColormap]); + else + SetPixelShader(Shaders[SHADER_SpecialColormapPal]); } } else @@ -1378,7 +1410,10 @@ void D3DFB::Draw3DPart(bool copy3d) CalcFullscreenCoords(verts, Accel2D, false, color0, color1); D3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, sizeof(FBVERTEX)); } - SetPixelShader(Shaders[SHADER_NormalColorPal]); + if (IsBgra()) + SetPixelShader(Shaders[SHADER_NormalColor]); + else + SetPixelShader(Shaders[SHADER_NormalColorPal]); } //========================================================================== @@ -3072,7 +3107,7 @@ void D3DFB::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, DAngle rotation, FDynamicColormap *colormap, int lightlevel) { // Use an equation similar to player sprites to determine shade - double fadelevel = clamp((LIGHT2SHADE(lightlevel)/65536. - 12) / NUMCOLORMAPS, 0.0, 1.0); + double fadelevel = clamp((swrenderer::LightVisibility::LightLevelToShade(lightlevel, true)/65536. - 12) / NUMCOLORMAPS, 0.0, 1.0); BufferedTris *quad; FBVERTEX *verts; diff --git a/src/win32/fb_ddraw.cpp b/src/win32/fb_ddraw.cpp index 7cc603786..5637e9695 100644 --- a/src/win32/fb_ddraw.cpp +++ b/src/win32/fb_ddraw.cpp @@ -32,7 +32,6 @@ ** */ - // HEADER FILES ------------------------------------------------------------ #define DIRECTDRAW_VERSION 0x0300 @@ -120,7 +119,7 @@ cycle_t BlitCycles; // CODE -------------------------------------------------------------------- DDrawFB::DDrawFB (int width, int height, bool fullscreen) - : BaseWinFB (width, height) + : BaseWinFB (width, height, false) { int i; diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index fd92694cd..5a17e90ec 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -47,11 +47,13 @@ #include "doomstat.h" #include "m_argv.h" #include "version.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" EXTERN_CVAR (Bool, ticker) EXTERN_CVAR (Bool, fullscreen) +EXTERN_CVAR (Bool, swtruecolor) EXTERN_CVAR (Float, vid_winscale) +EXTERN_CVAR (Bool, vid_forceddraw) CVAR(Int, win_x, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, win_y, -1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -68,10 +70,44 @@ FRenderer *gl_CreateInterface(); void I_RestartRenderer(); int currentrenderer = -1; +int currentcanvas = -1; +int currentgpuswitch = -1; bool changerenderer; +// Optimus/Hybrid switcher +CUSTOM_CVAR(Int, vid_gpuswitch, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if (self != currentgpuswitch) + { + switch (self) + { + case 0: + Printf("Selecting default GPU...\n"); + break; + case 1: + Printf("Selecting high-performance dedicated GPU...\n"); + break; + case 2: + Printf("Selecting power-saving integrated GPU...\n"); + break; + default: + Printf("Unknown option (%d) - falling back to 'default'\n", *vid_gpuswitch); + self = 0; + break; + } + Printf("You must restart " GAMENAME " for this change to take effect.\n"); + } +} + +// Software OpenGL canvas +CUSTOM_CVAR(Bool, vid_used3d, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +{ + if ((self ? 1 : 0) != currentcanvas) + Printf("You must restart " GAMENAME " for this change to take effect.\n"); +} + // [ZDoomGL] -CUSTOM_CVAR (Int, vid_renderer, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) +CUSTOM_CVAR (Int, vid_renderer, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { // 0: Software renderer // 1: OpenGL renderer @@ -118,6 +154,13 @@ void I_InitGraphics () { UCVarValue val; + // todo: implement ATI version of this. this only works for nvidia notebooks, for now. + currentgpuswitch = vid_gpuswitch; + if (currentgpuswitch == 1) + putenv("SHIM_MCCOMPAT=0x800000001"); // discrete + else if (currentgpuswitch == 2) + putenv("SHIM_MCCOMPAT=0x800000000"); // integrated + // If the focus window is destroyed, it doesn't go back to the active window. // (e.g. because the net pane was up, and a button on it had focus) if (GetFocus() == NULL && GetActiveWindow() == Window) @@ -136,9 +179,13 @@ void I_InitGraphics () val.Bool = !!Args->CheckParm ("-devparm"); ticker.SetGenericRepDefault (val, CVAR_Bool); - //currentrenderer = vid_renderer; - if (currentrenderer==1) Video = gl_CreateVideo(); - else Video = new Win32Video (0); + if (currentcanvas == 1) // Software Canvas: 1 = D3D or DirectDraw, 0 = OpenGL + if (currentrenderer == 1) + Video = gl_CreateVideo(); + else + Video = new Win32Video(0); + else + Video = gl_CreateVideo(); if (Video == NULL) I_FatalError ("Failed to initialize display"); @@ -156,6 +203,17 @@ static void I_DeleteRenderer() void I_CreateRenderer() { currentrenderer = vid_renderer; + currentcanvas = vid_used3d; + if (currentrenderer == 1) + Printf("Renderer: OpenGL\n"); + else if (currentcanvas == 0) + Printf("Renderer: Software on OpenGL\n"); + else if (currentcanvas == 1 && vid_forceddraw == false) + Printf("Renderer: Software on Direct3D\n"); + else if (currentcanvas == 1) + Printf("Renderer: Software on DirectDraw\n"); + else + Printf("Renderer: Unknown\n"); if (Renderer == NULL) { if (currentrenderer==1) Renderer = gl_CreateInterface(); @@ -190,7 +248,7 @@ DFrameBuffer *I_SetMode (int &width, int &height, DFrameBuffer *old) } break; } - DFrameBuffer *res = Video->CreateFrameBuffer (width, height, fs, old); + DFrameBuffer *res = Video->CreateFrameBuffer (width, height, swtruecolor, fs, old); //* Right now, CreateFrameBuffer cannot return NULL if (res == NULL) @@ -357,7 +415,17 @@ void I_RestoreWindowedPos () extern int NewWidth, NewHeight, NewBits, DisplayBits; -CUSTOM_CVAR (Bool, fullscreen, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +CUSTOM_CVAR(Bool, swtruecolor, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) +{ + // Strictly speaking this doesn't require a mode switch, but it is the easiest + // way to force a CreateFramebuffer call without a lot of refactoring. + NewWidth = screen->GetWidth(); + NewHeight = screen->GetHeight(); + NewBits = DisplayBits; + setmodeneeded = true; +} + +CUSTOM_CVAR (Bool, fullscreen, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL) { NewWidth = screen->GetWidth(); NewHeight = screen->GetHeight(); diff --git a/src/win32/hardware.h b/src/win32/hardware.h index b2bafef32..184eeccf5 100644 --- a/src/win32/hardware.h +++ b/src/win32/hardware.h @@ -45,7 +45,7 @@ class IVideo virtual EDisplayType GetDisplayType () = 0; virtual void SetWindowedScale (float scale) = 0; - virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old) = 0; + virtual DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old) = 0; virtual void StartModeIterator (int bits, bool fs) = 0; virtual bool NextMode (int *width, int *height, bool *letterbox) = 0; diff --git a/src/win32/i_crash.cpp b/src/win32/i_crash.cpp index 5c1678c5c..b45cda168 100644 --- a/src/win32/i_crash.cpp +++ b/src/win32/i_crash.cpp @@ -3351,3 +3351,111 @@ void DisplayCrashLog () } CloseTarFiles (); } + +///////////////////////////////////////////////////////////////////////////// + +namespace +{ + bool __declspec(thread) DrawerExceptionSetJumpResult; + CONTEXT __declspec(thread) DrawerExceptionSetJumpContext; + PVOID __declspec(thread) DrawerExceptionHandlerHandle; + char __declspec(thread) *DrawerExceptionReason; + bool __declspec(thread) DrawerExceptionFatal; + + LONG WINAPI DrawerExceptionHandler(_EXCEPTION_POINTERS *exceptionInfo) + { + *exceptionInfo->ContextRecord = DrawerExceptionSetJumpContext; + + DrawerExceptionFatal = false; + switch (exceptionInfo->ExceptionRecord->ExceptionCode) + { + default: DrawerExceptionReason = "Unknown exception code"; break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: DrawerExceptionReason = "Array bounds exceeded"; break; + case EXCEPTION_BREAKPOINT: DrawerExceptionReason = "Breakpoint"; break; + case EXCEPTION_DATATYPE_MISALIGNMENT: DrawerExceptionReason = "Datatype misalignment"; break; + case EXCEPTION_FLT_DENORMAL_OPERAND: DrawerExceptionReason = "Float denormal operand"; break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: DrawerExceptionReason = "Float divide by zero"; break; + case EXCEPTION_FLT_INEXACT_RESULT: DrawerExceptionReason = "Float inexact result"; break; + case EXCEPTION_FLT_INVALID_OPERATION: DrawerExceptionReason = "Float invalid operation"; break; + case EXCEPTION_FLT_OVERFLOW: DrawerExceptionReason = "Float overflow"; break; + case EXCEPTION_FLT_STACK_CHECK: DrawerExceptionReason = "Float stack check"; break; + case EXCEPTION_FLT_UNDERFLOW: DrawerExceptionReason = "Float underflow"; break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: DrawerExceptionReason = "Int divide by zero"; break; + case EXCEPTION_INT_OVERFLOW: DrawerExceptionReason = "Int overflow"; break; + case EXCEPTION_INVALID_DISPOSITION: DrawerExceptionReason = "Invalid disposition"; break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: DrawerExceptionReason = "Noncontinuable exception"; break; + case EXCEPTION_PRIV_INSTRUCTION: DrawerExceptionReason = "Priv instruction"; break; + case EXCEPTION_SINGLE_STEP: DrawerExceptionReason = "Single step"; break; + case EXCEPTION_STACK_OVERFLOW: DrawerExceptionReason = "Stack overflow"; break; + + case EXCEPTION_ILLEGAL_INSTRUCTION: + DrawerExceptionReason = "Illegal instruction"; + DrawerExceptionFatal = true; + break; + + case EXCEPTION_ACCESS_VIOLATION: + if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 0) + { + DrawerExceptionReason = "Read access violation"; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) + { + DrawerExceptionReason = "Write access violation"; + DrawerExceptionFatal = true; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 8) + { + DrawerExceptionReason = "User-mode data execution prevention (DEP) violation"; + DrawerExceptionFatal = true; + } + else + { + DrawerExceptionReason = "Unknown access violation"; + DrawerExceptionFatal = true; + } + break; + + case EXCEPTION_IN_PAGE_ERROR: + if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 0) + { + DrawerExceptionReason = "In page read error"; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 1) + { + DrawerExceptionReason = "In page write error"; + DrawerExceptionFatal = true; + } + else if (exceptionInfo->ExceptionRecord->ExceptionInformation[0] == 8) + { + DrawerExceptionReason = "In page user-mode data execution prevention (DEP) error"; + DrawerExceptionFatal = true; + } + else + { + DrawerExceptionReason = "Unknown in page read error"; + DrawerExceptionFatal = true; + } + break; + } + + return EXCEPTION_CONTINUE_EXECUTION; + } +} + +void VectoredTryCatch(void *data, void(*tryBlock)(void *data), void(*catchBlock)(void *data, const char *reason, bool fatal)) +{ + DrawerExceptionSetJumpResult = false; + RtlCaptureContext(&DrawerExceptionSetJumpContext); + if (DrawerExceptionSetJumpResult) + { + RemoveVectoredExceptionHandler(DrawerExceptionHandlerHandle); + catchBlock(data, DrawerExceptionReason, DrawerExceptionFatal); + } + else + { + DrawerExceptionSetJumpResult = true; + DrawerExceptionHandlerHandle = AddVectoredExceptionHandler(1, DrawerExceptionHandler); + tryBlock(data); + RemoveVectoredExceptionHandler(DrawerExceptionHandlerHandle); + } +} diff --git a/src/win32/win32gliface.cpp b/src/win32/win32gliface.cpp index c4b5b7b43..1323787a9 100644 --- a/src/win32/win32gliface.cpp +++ b/src/win32/win32gliface.cpp @@ -25,6 +25,7 @@ #include "gl/renderer/gl_renderer.h" #include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_swframebuffer.h" extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; @@ -348,7 +349,8 @@ bool Win32GLVideo::GoFullscreen(bool yes) // //========================================================================== -DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool fs, DFrameBuffer *old) + +DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool bgra, bool fs, DFrameBuffer *old) { Win32GLFrameBuffer *fb; @@ -384,14 +386,18 @@ DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool fs, DF fb->m_Height == m_DisplayHeight && fb->m_Bits == m_DisplayBits && fb->m_RefreshHz == m_DisplayHz && - fb->m_Fullscreen == fs) + fb->m_Fullscreen == fs && + fb->m_Bgra == bgra) { return old; } //old->GetFlash(flashColor, flashAmount); delete old; } - fb = new OpenGLFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs); + if (vid_renderer == 1) + fb = new OpenGLFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs); + else + fb = new OpenGLSWFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs, bgra); return fb; } @@ -867,13 +873,14 @@ IMPLEMENT_CLASS(Win32GLFrameBuffer, true, false) // //========================================================================== -Win32GLFrameBuffer::Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : BaseWinFB(width, height) +Win32GLFrameBuffer::Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra) : BaseWinFB(width, height, bgra) { m_Width = width; m_Height = height; m_Bits = bits; m_RefreshHz = refreshHz; m_Fullscreen = fullscreen; + m_Bgra = bgra; m_Lock=0; RECT r; diff --git a/src/win32/win32gliface.h b/src/win32/win32gliface.h index e767073c4..fbaf34bd5 100644 --- a/src/win32/win32gliface.h +++ b/src/win32/win32gliface.h @@ -38,7 +38,7 @@ public: void StartModeIterator (int bits, bool fs); bool NextMode (int *width, int *height, bool *letterbox); bool GoFullscreen(bool yes); - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old); virtual bool SetResolution (int width, int height, int bits); void DumpAdapters(); bool InitHardware (HWND Window, int multisample); @@ -101,7 +101,7 @@ public: Win32GLFrameBuffer() {} // Actually, hMonitor is a HMONITOR, but it's passed as a void * as there // look to be some cross-platform bits in the way. - Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen); + Win32GLFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen, bool bgra); virtual ~Win32GLFrameBuffer(); @@ -143,7 +143,7 @@ protected: float m_Gamma, m_Brightness, m_Contrast; WORD m_origGamma[768]; BOOL m_supportsGamma; - bool m_Fullscreen; + bool m_Fullscreen, m_Bgra; int m_Width, m_Height, m_Bits, m_RefreshHz; int m_Lock; char m_displayDeviceNameBuffer[CCHDEVICENAME]; diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index 13cf603fa..0dafab6fb 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -70,7 +70,7 @@ class Win32Video : public IVideo EDisplayType GetDisplayType () { return DISPLAY_Both; } void SetWindowedScale (float scale); - DFrameBuffer *CreateFrameBuffer (int width, int height, bool fs, DFrameBuffer *old); + DFrameBuffer *CreateFrameBuffer (int width, int height, bool bgra, bool fs, DFrameBuffer *old); void StartModeIterator (int bits, bool fs); bool NextMode (int *width, int *height, bool *letterbox); @@ -121,7 +121,7 @@ class BaseWinFB : public DFrameBuffer { DECLARE_ABSTRACT_CLASS(BaseWinFB, DFrameBuffer) public: - BaseWinFB (int width, int height) : DFrameBuffer (width, height), Windowed (true) {} + BaseWinFB (int width, int height, bool bgra) : DFrameBuffer (width, height, bgra), Windowed (true) {} bool IsFullscreen () { return !Windowed; } virtual void Blank () = 0; @@ -227,7 +227,7 @@ class D3DFB : public BaseWinFB { DECLARE_CLASS(D3DFB, BaseWinFB) public: - D3DFB (UINT adapter, int width, int height, bool fullscreen); + D3DFB (UINT adapter, int width, int height, bool bgra, bool fullscreen); ~D3DFB (); bool IsValid (); @@ -421,6 +421,7 @@ private: bool NeedPalUpdate; bool NeedGammaUpdate; int FBWidth, FBHeight; + D3DFORMAT FBFormat; bool VSync; RECT BlendingRect; int In2D; diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index e22b29cba..519b3d753 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -69,7 +69,7 @@ #include "m_argv.h" #include "r_defs.h" #include "v_text.h" -#include "r_swrenderer.h" +#include "swrenderer/r_swrenderer.h" #include "version.h" #include "win32iface.h" @@ -631,7 +631,7 @@ bool Win32Video::NextMode (int *width, int *height, bool *letterbox) return false; } -DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) +DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool bgra, bool fullscreen, DFrameBuffer *old) { static int retry = 0; static int owidth, oheight; @@ -647,7 +647,8 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr BaseWinFB *fb = static_cast (old); if (fb->Width == width && fb->Height == height && - fb->Windowed == !fullscreen) + fb->Windowed == !fullscreen && + fb->Bgra == bgra) { return old; } @@ -664,12 +665,13 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr if (D3D != NULL) { - fb = new D3DFB (m_Adapter, width, height, fullscreen); + fb = new D3DFB (m_Adapter, width, height, bgra, fullscreen); } else { fb = new DDrawFB (width, height, fullscreen); } + LOG1 ("New fb created @ %p\n", fb); // If we could not create the framebuffer, try again with slightly @@ -728,7 +730,7 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr } ++retry; - fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); + fb = static_cast(CreateFrameBuffer (width, height, bgra, fullscreen, NULL)); } retry = 0; diff --git a/wadsrc/CMakeLists.txt b/wadsrc/CMakeLists.txt index 80189a328..5a85840e0 100644 --- a/wadsrc/CMakeLists.txt +++ b/wadsrc/CMakeLists.txt @@ -1,3 +1,3 @@ cmake_minimum_required( VERSION 2.8.7 ) -add_pk3(gzdoom.pk3 ${CMAKE_CURRENT_SOURCE_DIR}/static) +add_pk3(qzdoom.pk3 ${CMAKE_CURRENT_SOURCE_DIR}/static) diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index 9f7564b95..93807661e 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -516,3 +516,53 @@ B68EB7CFB4CC481796E2919B9C16DFBD // Moc11.wad e1m6 setlinespecial 2410 Sector_Set3DFloor 32002 4 1 0 0 } +1ED329858AB154C55878DA1C11A4F100 // unloved.pk3:unlovedmaps.wad map01 +{ + clipmidtex +} + +FA23E72FA955E66EC68609F72C0BA71E // unloved.pk3:unlovedmaps.wad map02 +{ + clipmidtex +} + +41BEC1F643CFEEC997AF98276A05EC88 // unloved.pk3:unlovedmaps.wad map03 +{ + clipmidtex +} + +AF9A6370BE562584BC11165ECF364713 // unloved.pk3:unlovedmaps.wad map04 +{ + clipmidtex +} + +DC96228097DD004C40CCB1DB14A91EAA // unloved.pk3:unlovedmaps.wad map05 +{ + clipmidtex +} + +261E64897A572C8DB8DC041E64BE27AD // unloved2beta1.pk3:u2_new2maps2.wad map06 +{ + clipmidtex +} + +04800B1F35E8C036EBABC8C616402927 // unloved2beta1.pk3:u2_new2maps2.wad map07 +{ + clipmidtex +} + +9E54F70648A77BBD090FF78A3AA05367 // unloved2beta1.pk3:u2_new2maps2.wad map08 +{ + clipmidtex +} + +72E9E0F41F691B7F956E62F35B4A617F // unloved2beta1.pk3:u2_new2maps2.wad map09 +{ + clipmidtex +} + +3D3FE412E87AD8B2316DAEC9E25F2E5D // unloved2beta1.pk3:u2_new2maps2.wad map10 +{ + clipmidtex +} + diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 55477ee2e..bf56bb61f 100644 --- a/wadsrc/static/language.enu +++ b/wadsrc/static/language.enu @@ -1676,6 +1676,7 @@ OPTMNU_NETWORK = "Network Options"; OPTMNU_SOUND = "Sound Options"; OPTMNU_DISPLAY = "Display Options"; OPTMNU_VIDEO = "Set video mode"; +OPTMNU_CHANGERENDER = "Change Rendering Output"; OPTMNU_DEFAULTS = "Reset to defaults"; OPTMNU_RESETTOSAVED = "Reset to last saved"; OPTMNU_CONSOLE = "Go to console"; @@ -1800,6 +1801,9 @@ DSPLYMNU_SCREENSIZE = "Screen size"; DSPLYMNU_BRIGHTNESS = "Brightness"; DSPLYMNU_VSYNC = "Vertical Sync"; DSPLYMNU_CAPFPS = "Rendering Interpolation"; +DSPLYMNU_COLUMNMETHOD = "Column render mode"; +DSPLYMNU_BLENDMETHOD = "Transparency render mode"; + DSPLYMNU_WIPETYPE = "Screen wipe style"; DSPLYMNU_SHOWENDOOM = "Show ENDOOM screen"; DSPLYMNU_BLOODFADE = "Blood Flash Intensity"; @@ -1808,6 +1812,7 @@ DSPLYMNU_WATERFADE = "Underwater Blend Intensity"; DSPLYMNU_PALLETEHACK = "DirectDraw palette hack"; // Not used DSPLYMNU_ATTACHEDSURFACES = "Use attached surfaces"; // Not used DSPLYMNU_SKYMODE = "Sky render mode"; +DSPLYMNU_LINEARSKY = "Linear skies"; DSPLYMNU_GZDFULLBRIGHT = "Fullbright overrides sector color"; DSPLYMNU_DRAWFUZZ = "Use fuzz effect"; DSPLYMNU_TRANSSOUL = "Lost Soul translucency"; @@ -1827,6 +1832,7 @@ DSPLYMNU_DIMCOLOR = "Dim color"; DSPLYMNU_MOVEBOB = "View bob amount while moving"; DSPLYMNU_STILLBOB = "View bob amount while not moving"; DSPLYMNU_BOBSPEED = "Weapon bob speed"; +DSPLYMNU_GPUSWITCH = "Notebook Switchable GPU"; // HUD Options HUDMNU_TITLE = "HUD Options"; @@ -2166,6 +2172,13 @@ MODMNU_QUALITY = "Quality"; MODMNU_VOLUMERAMPING = "Volume ramping"; MODMNU_CHIPOMATIC = "Chip-o-matic"; +// Renderer Options +RNDMNU_TITLE = "CHANGE RENDERER"; +RNDMNU_RENDERER = "Hardware Acceleration"; +RNDMNU_TRUECOLOR = "Software Truecolor Mode"; +RNDMNU_POLY = "Poly Renderer (experimental)"; +RNDMNU_CANVAS = "Software Canvas"; + // Video Options VIDMNU_TITLE = "VIDEO MODE"; VIDMNU_FULLSCREEN = "Fullscreen"; @@ -2208,9 +2221,6 @@ JOYMNU_NOAXES = "No configurable axes"; // Option Values OPTVAL_OFF = "Off"; OPTVAL_ON = "On"; -OPTVAL_LOW = "Low"; -OPTVAL_MEDIUM = "Medium"; -OPTVAL_HIGH = "High"; OPTVAL_MALE = "Male"; OPTVAL_FEMALE = "Female"; OPTVAL_OTHER = "Other"; @@ -2229,6 +2239,8 @@ OPTVAL_INVERTED = "Inverted"; OPTVAL_NOTINVERTED = "Not Inverted"; OPTVAL_ORIGINAL = "Original"; OPTVAL_OPTIMIZED = "Optimized"; +OPTVAL_CLASSIC = "Classic (Faster)"; +OPTVAL_PRECISE = "Precise"; OPTVAL_NORMAL = "Normal"; OPTVAL_STRETCH = "Stretch"; OPTVAL_CAPPED = "Capped"; @@ -2345,6 +2357,13 @@ OPTVAL_WARNINGS = "Warnings"; OPTVAL_NOTIFICATIONS = "Notifications"; OPTVAL_EVERYTHING = "Everything"; OPTVAL_FULLSCREENONLY = "Fullscreen only"; +OPTVAL_GL = "OpenGL"; +OPTVAL_D3D = "Direct3D"; +OPTVAL_HWPOLY = "OpenGL-Accelerated"; +OPTVAL_SWDOOM = "Doom Software Renderer"; +OPTVAL_DEDICATED = "High-Performance"; +OPTVAL_INTEGRATED = "Power-Saving"; + // Colors C_BRICK = "\cabrick"; C_TAN = "\cbtan"; @@ -2656,6 +2675,7 @@ GLLIGHTMNU_LIGHTDEFS = "Enable light definitions"; GLLIGHTMNU_CLIPLIGHTS = "Clip lights"; GLLIGHTMNU_LIGHTSPRITES = "Lights affect sprites"; GLLIGHTMNU_LIGHTPARTICLES = "Lights affect particles"; +GLLIGHTMNU_LIGHTMATH = "Light quality"; // OpenGL Preferences GLPREFMNU_TITLE = "OPENGL PREFERENCES"; @@ -2772,3 +2792,17 @@ OPTVAL_HIGH = "High"; OPTVAL_EXTREME = "Extreme"; OPTVAL_OBVERSEFIRST = "Obverse"; OPTVAL_REVERSEFIRST = "Reverse"; + +// QZDoom exclusive: + +DSPLYMNU_TCOPT = "TrueColor Options"; + +TCMNU_TITLE = "TRUECOLOR OPTIONS"; + + +TCMNU_TRUECOLOR = "True color output"; +TCMNU_MINFILTER = "Linear filter when downscaling"; +TCMNU_MAGFILTER = "Linear filter when upscaling"; +TCMNU_MIPMAP = "Use mipmapped textures"; +TCMNU_DYNLIGHTS = "Dynamic lights"; + diff --git a/wadsrc/static/menudef.txt b/wadsrc/static/menudef.txt index 51c3cab13..555fcfafa 100644 --- a/wadsrc/static/menudef.txt +++ b/wadsrc/static/menudef.txt @@ -345,6 +345,7 @@ OptionMenu "OptionsMenu" Submenu "$OPTMNU_SOUND", "SoundOptions" Submenu "$OPTMNU_DISPLAY", "VideoOptions" Submenu "$OPTMNU_VIDEO", "VideoModeMenu" + Submenu "$OPTMNU_CHANGERENDER", "RendererMenu" StaticText " " SafeCommand "$OPTMNU_DEFAULTS", "reset2defaults" SafeCommand "$OPTMNU_RESETTOSAVED", "reset2saved" @@ -612,6 +613,18 @@ OptionMenu "JoystickConfigMenu" // //------------------------------------------------------------------------------------------- +OptionValue ColumnMethods +{ + 0.0, "$OPTVAL_ORIGINAL" + 1.0, "$OPTVAL_OPTIMIZED" +} + +OptionValue BlendMethods +{ + 0.0, "$OPTVAL_CLASSIC" + 1.0, "$OPTVAL_PRECISE" +} + OptionValue SkyModes { 0.0, "$OPTVAL_NORMAL" @@ -670,6 +683,13 @@ OptionValue Fuzziness 2.0, "$OPTVAL_SHADOW" } +OptionValue GPUSwitch +{ + 0.0, "$OPTVAL_DEFAULT" + 1.0, "$OPTVAL_DEDICATED" + 2.0, "$OPTVAL_INTEGRATED" +} + OptionMenu "OpenGLOptions" { Title "$GLMNU_TITLE" @@ -678,11 +698,23 @@ OptionMenu "OpenGLOptions" Submenu "$GLMNU_PREFS", "GLPrefOptions" } +OptionMenu "TrueColorOptions" +{ + Title "$TCMNU_TITLE" + StaticText " " + //Option "$TCMNU_TRUECOLOR", "swtruecolor", "OnOff" + Option "$TCMNU_MINFILTER", "r_minfilter", "OnOff" + Option "$TCMNU_MAGFILTER", "r_magfilter", "OnOff" + Option "$TCMNU_MIPMAP", "r_mipmap", "OnOff" + Option "$TCMNU_DYNLIGHTS", "r_dynlights", "OnOff" +} + OptionMenu "VideoOptions" { Title "$DSPLYMNU_TITLE" Submenu "$DSPLYMNU_GLOPT", "OpenGLOptions" + Submenu "$DSPLYMNU_TCOPT", "TrueColorOptions" Submenu "$DSPLYMNU_SCOREBOARD", "ScoreboardOptions" StaticText " " Slider "$DSPLYMNU_SCREENSIZE", "screenblocks", 3.0, 12.0, 1.0, 0 @@ -697,6 +729,7 @@ OptionMenu "VideoOptions" Slider "$DSPLYMNU_BLOODFADE", "blood_fade_scalar", 0.0, 1.0, 0.05, 2 Slider "$DSPLYMNU_PICKUPFADE", "pickup_fade_scalar", 0.0, 1.0, 0.05, 2 Slider "$DSPLYMNU_WATERFADE", "underwater_fade_scalar", 0.0, 1.0, 0.05, 2 + Option "$DSPLYMNU_BLENDMETHOD", "r_blendmethod", "BlendMethods" StaticText " " Option "$DSPLYMNU_WIPETYPE", "wipetype", "Wipes" @@ -709,6 +742,7 @@ OptionMenu "VideoOptions" } Option "$DSPLYMNU_SKYMODE", "r_skymode", "SkyModes" + Option "$DSPLYMNU_LINEARSKY", "r_linearsky", "OnOff" Option "$DSPLYMNU_GZDFULLBRIGHT", "r_fullbrightignoresectorcolor", "OnOff" Option "$DSPLYMNU_DRAWFUZZ", "r_drawfuzz", "Fuzziness" Slider "$DSPLYMNU_TRANSSOUL", "transsouls", 0.25, 1.0, 0.05, 2 @@ -728,7 +762,11 @@ OptionMenu "VideoOptions" Slider "$DSPLYMNU_MOVEBOB", "movebob", 0, 1.0, 0.05, 2 Slider "$DSPLYMNU_STILLBOB", "stillbob", 0, 1.0, 0.05, 2 Slider "$DSPLYMNU_BOBSPEED", "wbobspeed", 0, 2.0, 0.1 - + IfOption(Windows) + { + StaticText " " + Option "$DSPLYMNU_GPUSWITCH", vid_gpuswitch, "GPUSwitch" + } } //------------------------------------------------------------------------------------------- @@ -1745,6 +1783,42 @@ OptionMenu ModReplayerOptions // the foo_dumb preferences in foobar2000. } +/*======================================= + * + * Change Renderer Menu + * + *=======================================*/ + +OptionValue "PolyDoom" +{ + 0, "$OPTVAL_SWDOOM" + 1, "$OPTVAL_HWPOLY" +} + +OptionValue "D3DGL" +{ + 0, "$OPTVAL_GL" + 1, "$OPTVAL_D3D" +} + +OptionValue "GLD3D" +{ + 0, "$OPTVAL_D3D" + 1, "$OPTVAL_GL" +} + +OptionMenu RendererMenu +{ + Title "$RNDMNU_TITLE" + Option "$RNDMNU_RENDERER", "vid_renderer", "PolyDoom" + Option "$RNDMNU_TRUECOLOR", "swtruecolor", "OnOff" + Option "$RNDMNU_POLY", "r_polyrenderer", "OnOff" + IfOption(Windows) + { + Option "$RNDMNU_CANVAS", "vid_used3d", "D3DGL" + } +} + /*======================================= * * Video mode menu diff --git a/wadsrc/static/shaders/glsl/swshader.fp b/wadsrc/static/shaders/glsl/swshader.fp new file mode 100644 index 000000000..e33389f29 --- /dev/null +++ b/wadsrc/static/shaders/glsl/swshader.fp @@ -0,0 +1,148 @@ + +precision mediump float; + +in vec4 PixelColor0; +in vec4 PixelColor1; +in vec4 PixelTexCoord0; + +out vec4 FragColor; + +uniform sampler2D Image; +uniform sampler2D Palette; +uniform sampler2D NewScreen; +uniform sampler2D Burn; + +uniform vec4 Desaturation; // { Desat, 1 - Desat } +uniform vec4 PaletteMod; +uniform vec4 Weights; // RGB->Gray weighting { 77/256.0, 143/256.0, 37/256.0, 1 } +uniform vec4 Gamma; + +vec4 TextureLookup(vec2 tex_coord) +{ +#if defined(PALTEX) + float index = texture(Image, tex_coord).x; + index = index * PaletteMod.x + PaletteMod.y; + return texture(Palette, vec2(index, 0.5)); +#else + return texture(Image, tex_coord); +#endif +} + +vec4 Invert(vec4 rgb) +{ +#if defined(INVERT) + rgb.rgb = Weights.www - rgb.xyz; +#endif + return rgb; +} + +float Grayscale(vec4 rgb) +{ + return dot(rgb.rgb, Weights.rgb); +} + +vec4 SampleTexture(vec2 tex_coord) +{ + return Invert(TextureLookup(tex_coord)); +} + +// Normal color calculation for most drawing modes. + +vec4 NormalColor(vec2 tex_coord, vec4 Flash, vec4 InvFlash) +{ + return Flash + SampleTexture(tex_coord) * InvFlash; +} + +// Copy the red channel to the alpha channel. Pays no attention to palettes. + +vec4 RedToAlpha(vec2 tex_coord, vec4 Flash, vec4 InvFlash) +{ + vec4 color = Invert(texture(Image, tex_coord)); + color.a = color.r; + return Flash + color * InvFlash; +} + +// Just return the value of c0. + +vec4 VertexColor(vec4 color) +{ + return color; +} + +// Emulate one of the special colormaps. (Invulnerability, gold, etc.) + +vec4 SpecialColormap(vec2 tex_coord, vec4 start, vec4 end) +{ + vec4 color = SampleTexture(tex_coord); + vec4 range = end - start; + // We can't store values greater than 1.0 in a color register, so we multiply + // the final result by 2 and expect the caller to divide the start and end by 2. + color.rgb = 2.0 * (start + Grayscale(color) * range).rgb; + // Duplicate alpha semantics of NormalColor. + color.a = start.a + color.a * end.a; + return color; +} + +// In-game colormap effect: fade to a particular color and multiply by another, with +// optional desaturation of the original color. Desaturation is stored in c1. +// Fade level is packed int fade.a. Fade.rgb has been premultiplied by alpha. +// Overall alpha is in color.a. +vec4 InGameColormap(vec2 tex_coord, vec4 color, vec4 fade) +{ + vec4 rgb = SampleTexture(tex_coord); + + // Desaturate +#if defined(DESAT) + vec3 intensity; + intensity.rgb = vec3(Grayscale(rgb) * Desaturation.x); + rgb.rgb = intensity.rgb + rgb.rgb * Desaturation.y; +#endif + + // Fade + rgb.rgb = rgb.rgb * fade.aaa + fade.rgb; + + // Shade and Alpha + rgb = rgb * color; + + return rgb; +} + +// Windowed gamma correction. + +vec4 GammaCorrection(vec2 tex_coord) +{ + vec4 color = texture(Image, tex_coord); + color.rgb = pow(color.rgb, Gamma.rgb); + return color; +} + +// The burn wipe effect. + +vec4 BurnWipe(vec4 coord) +{ + vec4 color = texture(NewScreen, coord.xy); + vec4 alpha = texture(Burn, coord.zw); + color.a = alpha.r * 2.0; + return color; +} + +void main() +{ +#if defined(ENORMALCOLOR) + FragColor = NormalColor(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EREDTOALPHA) + FragColor = RedToAlpha(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EVERTEXCOLOR) + FragColor = VertexColor(PixelColor0); +#elif defined(ESPECIALCOLORMAP) + FragColor = SpecialColormap(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EINGAMECOLORMAP) + FragColor = InGameColormap(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EBURNWIPE) + FragColor = BurnWipe(PixelTexCoord0); +#elif defined(EGAMMACORRECTION) + FragColor = GammaCorrection(PixelTexCoord0.xy); +#else + #error Entry point define is missing +#endif +} diff --git a/wadsrc/static/shaders/glsl/swshader.vp b/wadsrc/static/shaders/glsl/swshader.vp new file mode 100644 index 000000000..a317025aa --- /dev/null +++ b/wadsrc/static/shaders/glsl/swshader.vp @@ -0,0 +1,22 @@ + +in vec4 AttrPosition; +in vec4 AttrColor0; +in vec4 AttrColor1; +in vec4 AttrTexCoord0; + +out vec4 PixelColor0; +out vec4 PixelColor1; +out vec4 PixelTexCoord0; + +uniform vec4 ScreenSize; + +void main() +{ + gl_Position = vec4(AttrPosition.xy / ScreenSize.xy * 2.0 - 1.0, 1.0, 1.0); +#if defined(EGAMMACORRECTION) + gl_Position.y = -gl_Position.y; +#endif + PixelColor0 = AttrColor0.bgra; + PixelColor1 = AttrColor1.bgra; + PixelTexCoord0 = AttrTexCoord0; +} diff --git a/wadsrc/static/zscript/actor.txt b/wadsrc/static/zscript/actor.txt index 994fe6f4b..4ab9e0162 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -624,6 +624,7 @@ class Actor : Thinker native action native int OverlayID(); action native double OverlayX(int layer = 0); action native double OverlayY(int layer = 0); + action native double OverlayAlpha(int layer = 0); // DECORATE setters - it probably makes more sense to set these values directly now... void A_SetMass(int newmass) { mass = newmass; } @@ -1041,6 +1042,8 @@ class Actor : Thinker native native void A_WeaponOffset(double wx = 0, double wy = 32, int flags = 0); action native void A_OverlayOffset(int layer = PSP_WEAPON, double wx = 0, double wy = 32, int flags = 0); action native void A_OverlayFlags(int layer, int flags, bool set); + action native void A_OverlayAlpha(int layer, double alph); + action native void A_OverlayRenderStyle(int layer, int style); int ACS_NamedExecute(name script, int mapnum=0, int arg1=0, int arg2=0, int arg3=0) { diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index fa37a021c..ab0760e12 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -1,55 +1,3 @@ -struct InputEventData native version("2.4") -{ - native uint8 type; - native uint8 subtype; - native int16 data1; // keys / mouse/joystick buttons - native int16 data2; - native int16 data3; - native int x; // mouse/joystick x move - native int y; // mouse/joystick y move - - enum EGenericEvent - { - NoEvent, - KeyDown, // data1: scan code, data2: Qwerty ASCII code - KeyUp, // same - Mouse, // x, y: mouse movement deltas - GUI_Event, // subtype specifies actual event - DeviceChange,// a device has been connected or removed - } - - enum EGUIEvent - { - GUI_None, - GUI_KeyDown, // data1: unshifted ASCII, data2: shifted ASCII, data3: modifiers - GUI_KeyRepeat, // same - GUI_KeyUp, // same - GUI_Char, // data1: translated character (for user text input), data2: alt down? - GUI_FirstMouseEvent, - GUI_MouseMove, - GUI_LButtonDown, - GUI_LButtonUp, - GUI_LButtonDblClick, - GUI_MButtonDown, - GUI_MButtonUp, - GUI_MButtonDblClick, - GUI_RButtonDown, - GUI_RButtonUp, - GUI_RButtonDblClick, - GUI_WheelUp, // data3: shift/ctrl/alt - GUI_WheelDown, // " - GUI_WheelRight, // " - GUI_WheelLeft, // " - GUI_BackButtonDown, - GUI_BackButtonUp, - GUI_FwdButtonDown, - GUI_FwdButtonUp, - GUI_LastMouseEvent, - }; - - const KEY_ESCAPE = 0x01; -} - struct TexMan { enum EUseTypes diff --git a/wadsrc/static/zscript/constants.txt b/wadsrc/static/zscript/constants.txt index 1982c4472..95863452c 100644 --- a/wadsrc/static/zscript/constants.txt +++ b/wadsrc/static/zscript/constants.txt @@ -721,7 +721,11 @@ enum EPSpriteFlags PSPF_ADDBOB = 1 << 1, PSPF_POWDOUBLE = 1 << 2, PSPF_CVARFAST = 1 << 3, + PSPF_ALPHA = 1 << 4, + PSPF_RENDERSTYLE= 1 << 5, PSPF_FLIP = 1 << 6, + PSPF_FORCEALPHA = 1 << 7, + PSPF_FORCESTYLE = 1 << 8, }; // Default psprite layers diff --git a/wadsrc/static/zscript/events.txt b/wadsrc/static/zscript/events.txt index e1c2b485a..f0b0db0d5 100755 --- a/wadsrc/static/zscript/events.txt +++ b/wadsrc/static/zscript/events.txt @@ -1,6 +1,5 @@ -class BaseEvent native version("2.4") { } // just a base class. it doesn't inherit from Object on the scripting side so you can't call Destroy() on it and break everything. -class RenderEvent : BaseEvent native ui version("2.4") +struct RenderEvent native ui version("2.4") { native readonly Vector3 ViewPos; native readonly double ViewAngle; @@ -10,7 +9,7 @@ class RenderEvent : BaseEvent native ui version("2.4") native readonly Actor Camera; } -class WorldEvent : BaseEvent native play version("2.4") +struct WorldEvent native play version("2.4") { // for loaded/unloaded native readonly bool IsSaveGame; @@ -28,7 +27,7 @@ class WorldEvent : BaseEvent native play version("2.4") native readonly double DamageAngle; } -class PlayerEvent : BaseEvent native play version("2.4") +struct PlayerEvent native play version("2.4") { // this is the player number that caused the event. // note: you can get player struct from this by using players[e.PlayerNumber] @@ -37,7 +36,7 @@ class PlayerEvent : BaseEvent native play version("2.4") native readonly bool IsReturn; } -class UiEvent : BaseEvent native ui version("2.4") +struct UiEvent native ui version("2.4") { // d_gui.h enum EGUIEvent @@ -121,7 +120,7 @@ class UiEvent : BaseEvent native ui version("2.4") native readonly bool IsAlt; } -class InputEvent : BaseEvent native play version("2.4") +struct InputEvent native play version("2.4") { enum EGenericEvent { @@ -270,13 +269,16 @@ class InputEvent : BaseEvent native play version("2.4") native readonly int MouseY; } -class ConsoleEvent : BaseEvent native version("2.4") +struct ConsoleEvent native version("2.4") { // for net events, this will be the activator. // for UI events, this is always -1, and you need to check if level is loaded and use players[consoleplayer]. native readonly int Player; + // this is the name and args as specified in SendNetworkEvent or event/netevent CCMDs native readonly String Name; native readonly int Args[3]; + // this will be true if the event is fired from the console by event/netevent CCMD + native readonly bool IsManual; } class StaticEventHandler : Object native play version("2.4") @@ -318,7 +320,7 @@ class StaticEventHandler : Object native play version("2.4") // virtual native ui bool UiProcess(UiEvent e); - virtual native bool InputProcess(InputEvent e); + virtual native ui bool InputProcess(InputEvent e); // virtual native ui void ConsoleProcess(ConsoleEvent e); diff --git a/wadsrc/static/zscript/menu/conversationmenu.txt b/wadsrc/static/zscript/menu/conversationmenu.txt index 9d1d9f494..abda3dee2 100644 --- a/wadsrc/static/zscript/menu/conversationmenu.txt +++ b/wadsrc/static/zscript/menu/conversationmenu.txt @@ -335,18 +335,18 @@ class ConversationMenu : Menu // //============================================================================= - override bool Responder(InputEventData ev) + override bool OnUIEvent(UIEvent ev) { if (demoplayback) { // No interaction during demo playback return false; } - if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_Char && ev.data1 >= 48 && ev.data1 <= 57) + if (ev.type == UIEvent.Type_Char && ev.KeyChar >= 48 && ev.KeyChar <= 57) { // Activate an item of type numberedmore (dialogue only) - mSelection = ev.data1 == 48 ? 9 : ev.data1 - 49; + mSelection = ev.KeyChar == 48 ? 9 : ev.KeyChar - 49; return MenuEvent(MKEY_Enter, false); } - return Super.Responder(ev); + return Super.OnUIEvent(ev); } //============================================================================ diff --git a/wadsrc/static/zscript/menu/listmenu.txt b/wadsrc/static/zscript/menu/listmenu.txt index a7a9f71ae..e0bb72af9 100644 --- a/wadsrc/static/zscript/menu/listmenu.txt +++ b/wadsrc/static/zscript/menu/listmenu.txt @@ -99,37 +99,34 @@ class ListMenu : Menu // //============================================================================= - override bool Responder (InputEventData ev) + override bool OnUIEvent(UIEvent ev) { - if (ev.type == InputEventData.GUI_Event) + if (ev.Type == UIEvent.Type_KeyDown) { - if (ev.subtype == InputEventData.GUI_KeyDown) - { - // tolower - int ch = ev.data1; - ch = ch >= 65 && ch <91? ch + 32 : ch; + // tolower + int ch = ev.KeyChar; + ch = ch >= 65 && ch <91? ch + 32 : ch; - for(int i = mDesc.mSelectedItem + 1; i < mDesc.mItems.Size(); i++) + for(int i = mDesc.mSelectedItem + 1; i < mDesc.mItems.Size(); i++) + { + if (mDesc.mItems[i].CheckHotkey(ch)) { - if (mDesc.mItems[i].CheckHotkey(ch)) - { - mDesc.mSelectedItem = i; - MenuSound("menu/cursor"); - return true; - } + mDesc.mSelectedItem = i; + MenuSound("menu/cursor"); + return true; } - for(int i = 0; i < mDesc.mSelectedItem; i++) + } + for(int i = 0; i < mDesc.mSelectedItem; i++) + { + if (mDesc.mItems[i].CheckHotkey(ch)) { - if (mDesc.mItems[i].CheckHotkey(ch)) - { - mDesc.mSelectedItem = i; - MenuSound("menu/cursor"); - return true; - } + mDesc.mSelectedItem = i; + MenuSound("menu/cursor"); + return true; } } } - return Super.Responder(ev); + return Super.OnUIEvent(ev); } //============================================================================= diff --git a/wadsrc/static/zscript/menu/loadsavemenu.txt b/wadsrc/static/zscript/menu/loadsavemenu.txt index 05638d393..2154e1015 100644 --- a/wadsrc/static/zscript/menu/loadsavemenu.txt +++ b/wadsrc/static/zscript/menu/loadsavemenu.txt @@ -383,42 +383,39 @@ class LoadSaveMenu : ListMenu // //============================================================================= - override bool Responder (InputEventData ev) + override bool OnUIEvent(UIEvent ev) { - if (ev.type == InputEventData.GUI_Event) + if (ev.Type == UIEvent.Type_KeyDown) { - if (ev.subtype == InputEventData.GUI_KeyDown) + if (Selected < manager.SavegameCount()) { - if (Selected < manager.SavegameCount()) + switch (ev.KeyChar) { - switch (ev.data1) - { - case UIEvent.Key_F1: - manager.SetFileInfo(Selected); - return true; + case UIEvent.Key_F1: + manager.SetFileInfo(Selected); + return true; - case UIEvent.Key_DEL: - { - String EndString; - EndString = String.Format("%s%s%s%s?\n\n%s", Stringtable.Localize("$MNU_DELETESG"), TEXTCOLOR_WHITE, manager.GetSavegame(Selected).SaveTitle, TEXTCOLOR_NORMAL, Stringtable.Localize("$PRESSYN")); - StartMessage (EndString, 0); - } - return true; + case UIEvent.Key_DEL: + { + String EndString; + EndString = String.Format("%s%s%s%s?\n\n%s", Stringtable.Localize("$MNU_DELETESG"), TEXTCOLOR_WHITE, manager.GetSavegame(Selected).SaveTitle, TEXTCOLOR_NORMAL, Stringtable.Localize("$PRESSYN")); + StartMessage (EndString, 0); } + return true; } } - else if (ev.subtype == InputEventData.GUI_WheelUp) - { - if (TopItem > 0) TopItem--; - return true; - } - else if (ev.subtype == InputEventData.GUI_WheelDown) - { - if (TopItem < manager.SavegameCount() - listboxRows) TopItem++; - return true; - } } - return Super.Responder(ev); + else if (ev.Type == UIEvent.Type_WheelUp) + { + if (TopItem > 0) TopItem--; + return true; + } + else if (ev.Type == UIEvent.Type_WheelDown) + { + if (TopItem < manager.SavegameCount() - listboxRows) TopItem++; + return true; + } + return Super.OnUIEvent(ev); } @@ -503,13 +500,13 @@ class SaveMenu : LoadSaveMenu // //============================================================================= - override bool Responder (InputEventData ev) + override bool OnUIEvent(UIEvent ev) { - if (ev.subtype == InputEventData.GUI_KeyDown) + if (ev.Type == UIEvent.Type_KeyDown) { if (Selected != -1) { - switch (ev.data1) + switch (ev.KeyChar) { case UIEvent.Key_DEL: // cannot delete 'new save game' item @@ -523,9 +520,14 @@ class SaveMenu : LoadSaveMenu } } } - return Super.Responder(ev); + return Super.OnUIEvent(ev); } + //============================================================================= + // + // + // + //============================================================================= override void Ticker() { diff --git a/wadsrc/static/zscript/menu/menu.txt b/wadsrc/static/zscript/menu/menu.txt index e2861cdd3..d29211b77 100644 --- a/wadsrc/static/zscript/menu/menu.txt +++ b/wadsrc/static/zscript/menu/menu.txt @@ -169,47 +169,50 @@ class Menu : Object native ui version("2.4") // //============================================================================= - virtual bool Responder(InputEventData ev) + virtual bool OnUIEvent(UIEvent ev) { bool res = false; - if (ev.type == InputEventData.GUI_Event) + int y = ev.MouseY; + if (ev.type == UIEvent.Type_LButtonDown) { - if (ev.subtype == InputEventData.GUI_LButtonDown) + res = MouseEventBack(MOUSE_Click, ev.MouseX, y); + // make the menu's mouse handler believe that the current coordinate is outside the valid range + if (res) y = -1; + res |= MouseEvent(MOUSE_Click, ev.MouseX, y); + if (res) { - res = MouseEventBack(MOUSE_Click, ev.data1, ev.data2); - // make the menu's mouse handler believe that the current coordinate is outside the valid range - if (res) ev.data2 = -1; - res |= MouseEvent(MOUSE_Click, ev.data1, ev.data2); - if (res) - { - SetCapture(true); - } - + SetCapture(true); } - else if (ev.subtype == InputEventData.GUI_MouseMove) + + } + else if (ev.type == UIEvent.Type_MouseMove) + { + BackbuttonTime = 4*Thinker.TICRATE; + if (mMouseCapture || m_use_mouse == 1) { - BackbuttonTime = 4*Thinker.TICRATE; - if (mMouseCapture || m_use_mouse == 1) - { - res = MouseEventBack(MOUSE_Move, ev.data1, ev.data2); - if (res) ev.data2 = -1; - res |= MouseEvent(MOUSE_Move, ev.data1, ev.data2); - } + res = MouseEventBack(MOUSE_Move, ev.MouseX, y); + if (res) y = -1; + res |= MouseEvent(MOUSE_Move, ev.MouseX, y); } - else if (ev.subtype == InputEventData.GUI_LButtonUp) + } + else if (ev.type == UIEvent.Type_LButtonUp) + { + if (mMouseCapture) { - if (mMouseCapture) - { - SetCapture(false); - res = MouseEventBack(MOUSE_Release, ev.data1, ev.data2); - if (res) ev.data2 = -1; - res |= MouseEvent(MOUSE_Release, ev.data1, ev.data2); - } + SetCapture(false); + res = MouseEventBack(MOUSE_Release, ev.MouseX, y); + if (res) y = -1; + res |= MouseEvent(MOUSE_Release, ev.MouseX, y); } } return false; } + virtual bool OnInputEvent(InputEvent ev) + { + return false; + } + //============================================================================= // // diff --git a/wadsrc/static/zscript/menu/messagebox.txt b/wadsrc/static/zscript/menu/messagebox.txt index f5a60d9d4..48d9949a3 100644 --- a/wadsrc/static/zscript/menu/messagebox.txt +++ b/wadsrc/static/zscript/menu/messagebox.txt @@ -171,14 +171,14 @@ class MessageBoxMenu : Menu // //============================================================================= - override bool Responder(InputEventData ev) + override bool OnUIEvent(UIEvent ev) { - if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_KeyDown) + if (ev.type == UIEvent.Type_KeyDown) { if (mMessageMode == 0) { // tolower - int ch = ev.data1; + int ch = ev.KeyChar; ch = ch >= 65 && ch <91? ch + 32 : ch; if (ch == 110 /*'n'*/ || ch == 32) @@ -199,12 +199,17 @@ class MessageBoxMenu : Menu } return false; } - else if (ev.type == InputEventData.KeyDown) + return Super.OnUIEvent(ev); + } + + override bool OnInputEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown) { Close(); return true; } - return Super.Responder(ev); + return Super.OnInputEvent(ev); } //============================================================================= diff --git a/wadsrc/static/zscript/menu/optionmenu.txt b/wadsrc/static/zscript/menu/optionmenu.txt index f0917654a..99405cd88 100644 --- a/wadsrc/static/zscript/menu/optionmenu.txt +++ b/wadsrc/static/zscript/menu/optionmenu.txt @@ -155,35 +155,32 @@ class OptionMenu : Menu // //============================================================================= - override bool Responder (InputEventData ev) + override bool OnUIEvent(UIEvent ev) { - if (ev.type == InputEventData.GUI_Event) + if (ev.type == UIEvent.Type_WheelUp) { - if (ev.subtype == InputEventData.GUI_WheelUp) - { - int scrollamt = MIN(2, mDesc.mScrollPos); - mDesc.mScrollPos -= scrollamt; - return true; - } - else if (ev.subtype == InputEventData.GUI_WheelDown) - { - if (CanScrollDown) - { - if (VisBottom >= 0 && VisBottom < (mDesc.mItems.Size()-2)) - { - mDesc.mScrollPos += 2; - VisBottom += 2; - } - else - { - mDesc.mScrollPos++; - VisBottom++; - } - } - return true; - } + int scrollamt = MIN(2, mDesc.mScrollPos); + mDesc.mScrollPos -= scrollamt; + return true; } - return Super.Responder(ev); + else if (ev.type == UIEvent.Type_WheelDown) + { + if (CanScrollDown) + { + if (VisBottom >= 0 && VisBottom < (mDesc.mItems.Size()-2)) + { + mDesc.mScrollPos += 2; + VisBottom += 2; + } + else + { + mDesc.mScrollPos++; + VisBottom++; + } + } + return true; + } + return Super.OnUIEvent(ev); } //============================================================================= diff --git a/wadsrc/static/zscript/menu/optionmenuitems.txt b/wadsrc/static/zscript/menu/optionmenuitems.txt index 404ce5b36..c57391d44 100644 --- a/wadsrc/static/zscript/menu/optionmenuitems.txt +++ b/wadsrc/static/zscript/menu/optionmenuitems.txt @@ -396,16 +396,16 @@ class EnterKey : Menu } } - override bool Responder(InputEventData ev) + override bool OnInputEvent(InputEvent ev) { - // This checks raw keys, not GUI keys. - if (ev.type == InputEventData.KeyDown) + // This menu checks raw keys, not GUI keys because it needs the raw codes for binding. + if (ev.type == InputEvent.Type_KeyDown) { - mOwner.SendKey(ev.data1); + mOwner.SendKey(ev.KeyScan); menuactive = Menu.On; SetMenuMessage(0); Close(); - mParentMenu.MenuEvent((ev.data1 == InputEventData.KEY_ESCAPE)? Menu.MKEY_Abort : Menu.MKEY_Input, 0); + mParentMenu.MenuEvent((ev.KeyScan == InputEvent.KEY_ESCAPE)? Menu.MKEY_Abort : Menu.MKEY_Input, 0); return true; } return false; diff --git a/wadsrc/static/zscript/menu/playermenu.txt b/wadsrc/static/zscript/menu/playermenu.txt index 4502cc9fa..dac376ee9 100644 --- a/wadsrc/static/zscript/menu/playermenu.txt +++ b/wadsrc/static/zscript/menu/playermenu.txt @@ -372,9 +372,9 @@ class PlayerMenu : ListMenu // //============================================================================= - override bool Responder (InputEventData ev) + override bool OnUIEvent(UIEvent ev) { - if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_Char && ev.data1 == 32) + if (ev.Type == UIEvent.Type_Char && ev.KeyChar == 32) { // turn the player sprite around mRotation = 8 - mRotation; @@ -385,7 +385,7 @@ class PlayerMenu : ListMenu } return true; } - return Super.Responder(ev); + return Super.OnUIEvent(ev); } //============================================================================= diff --git a/wadsrc/static/zscript/menu/textentermenu.txt b/wadsrc/static/zscript/menu/textentermenu.txt index b92932aaf..69a6694f3 100644 --- a/wadsrc/static/zscript/menu/textentermenu.txt +++ b/wadsrc/static/zscript/menu/textentermenu.txt @@ -108,59 +108,56 @@ class TextEnterMenu : Menu // //============================================================================= - override bool Responder(InputEventData ev) + override bool OnUIEvent(UIEvent ev) { - if (ev.type == InputEventData.GUI_Event) + // Save game and player name string input + if (ev.Type == UIEvent.Type_Char) { - // Save game and player name string input - if (ev.subtype == InputEventData.GUI_Char) + mInputGridOkay = false; + if (mEnterString.Length() < mEnterSize && + (mSizeMode == 2/*entering player name*/ || SmallFont.StringWidth(mEnterString) < (mEnterSize-1)*8)) { - mInputGridOkay = false; - if (mEnterString.Length() < mEnterSize && - (mSizeMode == 2/*entering player name*/ || SmallFont.StringWidth(mEnterString) < (mEnterSize-1)*8)) - { - mEnterString.AppendFormat("%c", ev.data1); - } + mEnterString.AppendFormat("%c", ev.KeyChar); + } + return true; + } + int ch = ev.KeyChar; + if ((ev.Type == UIEvent.Type_KeyDown || ev.Type == UIEvent.Type_KeyRepeat) && ch == 8) + { + if (mEnterString.Length() > 0) + { + mEnterString.Truncate(mEnterString.Length() - 1); + } + } + else if (ev.Type == UIEvent.Type_KeyDown) + { + if (ch == UIEvent.Key_ESCAPE) + { + Menu parent = mParentMenu; + Close(); + parent.MenuEvent(MKEY_Abort, false); return true; } - int ch = ev.data1; - if ((ev.subtype == InputEventData.GUI_KeyDown || ev.subtype == InputEventData.GUI_KeyRepeat) && ch == 8) + else if (ch == 13) { if (mEnterString.Length() > 0) { - mEnterString.Truncate(mEnterString.Length() - 1); - } - } - else if (ev.subtype == InputEventData.GUI_KeyDown) - { - if (ch == UIEvent.Key_ESCAPE) - { + // [TP] If we allow color codes, colorize the string now. + if (AllowColors) + mEnterString = mEnterString.Filter(); + Menu parent = mParentMenu; + parent.MenuEvent(MKEY_Input, false); Close(); - parent.MenuEvent(MKEY_Abort, false); return true; } - else if (ch == 13) - { - if (mEnterString.Length() > 0) - { - // [TP] If we allow color codes, colorize the string now. - if (AllowColors) - mEnterString = mEnterString.Filter(); - - Menu parent = mParentMenu; - parent.MenuEvent(MKEY_Input, false); - Close(); - return true; - } - } - } - if (ev.subtype == InputEventData.GUI_KeyDown || ev.subtype == InputEventData.GUI_KeyRepeat) - { - return true; } } - return Super.Responder(ev); + if (ev.Type == UIEvent.Type_KeyDown || ev.Type == UIEvent.Type_KeyRepeat) + { + return true; + } + return Super.OnUIEvent(ev); } //============================================================================= diff --git a/wadsrc/static/zscript/menu/videomenu.txt b/wadsrc/static/zscript/menu/videomenu.txt index 1377f528e..a9e940095 100644 --- a/wadsrc/static/zscript/menu/videomenu.txt +++ b/wadsrc/static/zscript/menu/videomenu.txt @@ -57,9 +57,9 @@ class VideoModeMenu : OptionMenu return Super.MenuEvent(mkey, fromcontroller); } - override bool Responder(InputEventData ev) + override bool OnUIEvent(UIEvent ev) { - if (ev.type == InputEventData.GUI_Event && ev.subtype == InputEventData.GUI_KeyDown && (ev.data1 == 0x54 || ev.data1 == 0x74)) + if (ev.Type == UIEvent.Type_KeyDown && (ev.KeyChar == 0x54 || ev.KeyChar == 0x74)) { if (SetSelectedSize()) { @@ -67,7 +67,7 @@ class VideoModeMenu : OptionMenu return true; } } - return Super.Responder(ev); + return Super.OnUIEvent(ev); } } diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index b13b84015..3821ff002 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -210,12 +210,14 @@ class PSprite : Object native play native readonly PlayerInfo Owner; native SpriteID Sprite; native int Frame; + native readonly int RenderStyle; native readonly int ID; native Bool processPending; native double x; native double y; native double oldx; native double oldy; + native double alpha; native Bool firstTic; native int Tics; native bool bAddWeapon;