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/CMakeLists.txt b/CMakeLists.txt index eae0702f8..9df5498e4 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 @@ -174,20 +174,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 4ff73e32c..b7f85541c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -692,6 +692,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 ) @@ -756,6 +757,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 @@ -790,19 +804,67 @@ set( NOT_COMPILED_SOURCE_FILES ) set( FASTMATH_PCH_SOURCES - r_swrenderer.cpp - r_3dfloors.cpp - r_bsp.cpp - r_draw.cpp - r_draw_pal.cpp - r_drawt_pal.cpp - r_thread.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 - r_walldraw.cpp s_advsound.cpp s_environment.cpp s_playlist.cpp @@ -953,6 +1015,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 @@ -1223,6 +1287,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() @@ -1232,6 +1300,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") endif() target_link_libraries( zdoom ${ZDOOM_LIBS} gdtoa dumb lzma ) + include_directories( . g_statusbar g_shared @@ -1379,8 +1448,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 8df074182..a6aa36e84 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -1932,7 +1932,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 0b1e58287..b5b4c84ac 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 0b27a1efa..6bc7e18cc 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -113,6 +113,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 }, @@ -151,7 +152,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 77e0edda0..e5cf881ba 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -697,8 +697,6 @@ void D_Display () } } - RenderTarget = screen; - // change the view size if needed if (setsizeneeded && StatusBar != NULL) { @@ -973,7 +971,6 @@ void D_ErrorCleanup () menuactive = MENU_Off; } insave = false; - Renderer->ErrorCleanup(); } //========================================================================== diff --git a/src/doomdef.h b/src/doomdef.h index a07985bb6..520f5b6ed 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -359,6 +359,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/f_wipe.cpp b/src/f_wipe.cpp index a3ceb8d50..1fb7c982b 100644 --- a/src/f_wipe.cpp +++ b/src/f_wipe.cpp @@ -28,6 +28,9 @@ #include "f_wipe.h" #include "c_cvars.h" #include "templates.h" +#include "v_palette.h" + +EXTERN_CVAR(Bool, r_blendmethod) // // SCREEN WIPE PACKAGE @@ -280,39 +283,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); } @@ -347,19 +391,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; @@ -382,6 +448,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) @@ -395,11 +464,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); } @@ -410,6 +483,9 @@ bool wipe_ScreenWipe (int ticks) { bool rc; + if (screen->IsBgra()) + return true; + if (CurrentWipeType == wipe_None) return true; @@ -423,6 +499,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 b0de51b43..1dabfd2cc 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 1255401ae..f1fba1818 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) //========================================================================== @@ -232,10 +231,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 1a4582d80..8d9685ea0 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..04c2b7eb7 --- /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((LIGHT2SHADE(lightlevel) / 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/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 130e27ce2..5a8152591 100644 --- a/src/p_pspr.h +++ b/src/p_pspr.h @@ -49,11 +49,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 @@ -77,11 +81,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 62d44c74e..20245612a 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -2034,11 +2034,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 @@ -2048,7 +2049,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..fad44862f --- /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(); + 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..39f5514f4 --- /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(); + + 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..2aec6b08d --- /dev/null +++ b/src/polyrenderer/scene/poly_plane.cpp @@ -0,0 +1,503 @@ +/* +** 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; + 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() * 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); + 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() * 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..339d3051a --- /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 = LIGHT2SHADE(owner->Sector->lightlevel + actualextralight); + double minz = double((2048 * 4) / double(1 << 20)); + ColormapNum = GETPALOOKUP(swrenderer::LightVisibility::Instance()->SpriteGlobVis() / 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..9685c4241 --- /dev/null +++ b/src/polyrenderer/scene/poly_scene.cpp @@ -0,0 +1,367 @@ +/* +** 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) +{ + 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(); + 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..af24364fe --- /dev/null +++ b/src/polyrenderer/scene/poly_sky.cpp @@ -0,0 +1,186 @@ +/* +** 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; + 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(); + 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..0599a4b20 --- /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(); + 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..d8fb2a7ae --- /dev/null +++ b/src/polyrenderer/scene/poly_wall.cpp @@ -0,0 +1,481 @@ +/* +** 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) +{ + 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(); + 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..31aded9ac --- /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(); + 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 f4ac13fc5..84508b728 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 87c8af618..000000000 --- a/src/r_3dfloors.cpp +++ /dev/null @@ -1,166 +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" - -CVAR(Int, r_3dfloors, true, 0); - -namespace swrenderer -{ - -// 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; - -// 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 height = add->ZatPoint(ViewPos); - if(height >= sec->CenterCeiling()) return; - if(height <= sec->CenterFloor()) return; - - 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 a703ae19a..000000000 --- a/src/r_3dfloors.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef SOFT_FAKE3D_H -#define SOFT_FAKE3D_H - -#include "p_3dfloors.h" - -EXTERN_CVAR(Int, r_3dfloors); - -namespace swrenderer -{ - -// 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; - -// 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 14a2f153f..000000000 --- a/src/r_bsp.cpp +++ /dev/null @@ -1,1400 +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. -// -//----------------------------------------------------------------------------- - - -#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" -#include "g_levellocals.h" - -CVAR (Bool, r_drawflat, false, 0) // [RH] Don't texture segs? -EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); - -namespace swrenderer -{ - using namespace drawerargs; - -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; - - - -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) - { - 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); - - if (fake3D & FAKE3D_FAKEBACK) - { - if (rw_frontfz1 >= rw_backfz1 && rw_frontfz2 >= rw_backfz2) - { - fake3D |= FAKE3D_CLIPBOTFRONT; - } - if (rw_frontcz1 <= rw_backcz1 && rw_frontcz2 <= rw_backcz2) - { - fake3D |= FAKE3D_CLIPTOPFRONT; - } - } - - // 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; - R_CreateWallSegmentYSloped (wallupper, backsector->ceilingplane, &WallC); - } - if (rw_frontfz1 < rw_backfz1 || rw_frontfz2 < rw_backfz2) - { - rw_havelow = true; - R_CreateWallSegmentYSloped (walllower, backsector->floorplane, &WallC); - } - - // Portal - if (line->linedef->isVisualPortal() && line->sidedef == line->linedef->sidedef[0]) - { - solid = true; - } - // Closed door. - else 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 - fillshort (walltop+WallC.sx1, WallC.sx2 - WallC.sx1, centery); - fillshort (wallbottom+WallC.sx1, WallC.sx2 - WallC.sx1, centery); - } - else - { - rw_ceilstat = R_CreateWallSegmentYSloped (walltop, frontsector->ceilingplane, &WallC); - rw_floorstat = R_CreateWallSegmentYSloped (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 (float *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 <= 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 (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 = (r_fullbrightignoresectorcolor && fixedlightlev >= 0) ? &FullNormalLight : 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 = (r_fullbrightignoresectorcolor && fixedlightlev >= 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); - - 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(); - } - 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 e4d70c4cf..000000000 --- a/src/r_bsp.h +++ /dev/null @@ -1,127 +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" - -EXTERN_CVAR (Bool, r_drawflat) // [RH] Don't texture segs? - -namespace swrenderer -{ - -// 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); - -// 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 abf43fdf1..63c615500 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -59,7 +59,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 c4449f73b..000000000 --- a/src/r_draw.cpp +++ /dev/null @@ -1,1298 +0,0 @@ -/* -** 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 "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 "r_plane.h" -#include "r_draw.h" -#include "r_draw_pal.h" -#include "r_thread.h" - -namespace swrenderer -{ - // Needed by R_DrawFogBoundary (which probably shouldn't be part of this file) - extern "C" short spanend[MAXHEIGHT]; - extern float rw_light; - extern float rw_lightstep; - extern int wallshade; - - double dc_texturemid; - - int ylookup[MAXHEIGHT]; - uint8_t shadetables[NUMCOLORMAPS * 16 * 256]; - FDynamicColormap ShadeFakeColormap[16]; - uint8_t identitymap[256]; - FDynamicColormap identitycolormap; - int fuzzoffset[FUZZTABLE + 1]; - int fuzzpos; - int fuzzviewheight; - - namespace drawerargs - { - int dc_pitch; - lighttable_t *dc_colormap; - int dc_x; - int dc_yl; - int dc_yh; - fixed_t dc_iscale; - fixed_t dc_texturefrac; - uint32_t dc_textureheight; - int dc_color; - uint32_t dc_srccolor; - uint32_t dc_srccolor_bgra; - uint32_t *dc_srcblend; - uint32_t *dc_destblend; - fixed_t dc_srcalpha; - fixed_t dc_destalpha; - const uint8_t *dc_source; - const uint8_t *dc_source2; - uint32_t dc_texturefracx; - uint8_t *dc_translation; - uint8_t *dc_dest; - uint8_t *dc_destorg; - int dc_destheight; - int dc_count; - uint32_t dc_wall_texturefrac[4]; - uint32_t dc_wall_iscale[4]; - uint8_t *dc_wall_colormap[4]; - fixed_t dc_wall_light[4]; - const uint8_t *dc_wall_source[4]; - const uint8_t *dc_wall_source2[4]; - uint32_t dc_wall_texturefracx[4]; - uint32_t dc_wall_sourceheight[4]; - int dc_wall_fracbits; - int ds_y; - int ds_x1; - int ds_x2; - lighttable_t * ds_colormap; - dsfixed_t ds_light; - dsfixed_t ds_xfrac; - dsfixed_t ds_yfrac; - dsfixed_t ds_xstep; - dsfixed_t ds_ystep; - int ds_xbits; - int ds_ybits; - fixed_t ds_alpha; - double ds_lod; - const uint8_t *ds_source; - int ds_color; - unsigned int dc_tspans[4][MAXHEIGHT]; - unsigned int *dc_ctspan[4]; - unsigned int *horizspan[4]; - } - - void R_InitColumnDrawers() - { - colfunc = basecolfunc = R_DrawColumn; - fuzzcolfunc = R_DrawFuzzColumn; - transcolfunc = R_DrawTranslatedColumn; - spanfunc = R_DrawSpan; - hcolfunc_pre = R_DrawColumnHoriz; - hcolfunc_post1 = rt_map1col; - hcolfunc_post4 = rt_map4cols; - } - - 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; - } - } - - namespace - { - bool R_SetBlendFunc(int op, fixed_t fglevel, fixed_t bglevel, int flags) - { - using namespace drawerargs; - - // 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_FillColumn; - 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]; - 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 = R_FillAddColumn; - hcolfunc_post1 = rt_add1col; - hcolfunc_post4 = rt_add4cols; - } - else if (dc_translation == NULL) - { - colfunc = R_DrawAddColumn; - hcolfunc_post1 = rt_add1col; - hcolfunc_post4 = rt_add4cols; - } - else - { - colfunc = R_DrawTlatedAddColumn; - 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_DrawAddClampColumn; - hcolfunc_post1 = rt_addclamp1col; - hcolfunc_post4 = rt_addclamp4cols; - } - else - { - colfunc = R_DrawAddClampTranslatedColumn; - 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_DrawSubClampColumn; - hcolfunc_post1 = rt_subclamp1col; - hcolfunc_post4 = rt_subclamp4cols; - } - else - { - colfunc = R_DrawSubClampTranslatedColumn; - 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_DrawRevSubClampColumn; - hcolfunc_post1 = rt_revsubclamp1col; - hcolfunc_post4 = rt_revsubclamp4cols; - } - else - { - colfunc = R_DrawRevSubClampTranslatedColumn; - hcolfunc_post1 = rt_tlaterevsubclamp1col; - hcolfunc_post4 = rt_tlaterevsubclamp4cols; - } - return true; - - default: - return false; - } - } - - 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; - } - } - - FDynamicColormap *basecolormapsave; - } - - ESPSResult R_SetPatchStyle(FRenderStyle style, fixed_t alpha, int translation, uint32_t color) - { - using namespace drawerargs; - - fixed_t fglevel, bglevel; - - style.CheckFuzz(); - - if (style.BlendOp == STYLEOP_Shadow) - { - style = LegacyRenderStyles[STYLE_TranslucentStencil]; - alpha = OPAQUE / 3; - color = 0; - } - - if (style.Flags & STYLEF_TransSoulsAlpha) - { - alpha = fixed_t(transsouls * OPAQUE); - } - else if (style.Flags & STYLEF_Alpha1) - { - alpha = OPAQUE; - } - else - { - alpha = clamp(alpha, 0, OPAQUE); - } - - if (translation != -1) - { - 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 DoDraw0; - } - - 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 = RGB32k.RGB[r >> 3][g >> 3][b >> 3]; - 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; - hcolfunc_pre = R_FillColumnHoriz; - R_SetColorMapLight(identitycolormap.Maps, 0, 0); - } - - if (!R_SetBlendFunc(style.BlendOp, fglevel, bglevel, style.Flags)) - { - return DontDraw; - } - return DoDraw0; - } - - ESPSResult R_SetPatchStyle(FRenderStyle style, float alpha, int translation, uint32_t color) - { - return R_SetPatchStyle(style, FLOAT2FIXED(alpha), translation, color); - } - - void R_FinishSetPatchStyle() - { - basecolormap = basecolormapsave; - } - - const uint8_t *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, nullptr); - } - - bool R_GetTransMaskDrawers(void(**drawCol1)(), void(**drawCol4)()) - { - if (colfunc == R_DrawAddColumn) - { - *drawCol1 = R_DrawWallAddCol1; - *drawCol4 = R_DrawWallAddCol4; - return true; - } - if (colfunc == R_DrawAddClampColumn) - { - *drawCol1 = R_DrawWallAddClampCol1; - *drawCol4 = R_DrawWallAddClampCol4; - return true; - } - if (colfunc == R_DrawSubClampColumn) - { - *drawCol1 = R_DrawWallSubClampCol1; - *drawCol4 = R_DrawWallSubClampCol4; - return true; - } - if (colfunc == R_DrawRevSubClampColumn) - { - *drawCol1 = R_DrawWallRevSubClampCol1; - *drawCol4 = R_DrawWallRevSubClampCol4; - return true; - } - return false; - } - - void R_SetColorMapLight(lighttable_t *base_colormap, float light, int shade) - { - using namespace drawerargs; - - dc_colormap = base_colormap + (GETPALOOKUP(light, shade) << COLORMAPSHIFT); - } - - void R_SetColorMapLight(FDynamicColormap *base_colormap, float light, int shade) - { - R_SetColorMapLight(base_colormap->Maps, light, shade); - } - - void R_SetDSColorMapLight(lighttable_t *base_colormap, float light, int shade) - { - using namespace drawerargs; - - ds_colormap = base_colormap + (GETPALOOKUP(light, shade) << COLORMAPSHIFT); - } - - void R_SetDSColorMapLight(FDynamicColormap *base_colormap, float light, int shade) - { - R_SetDSColorMapLight(base_colormap->Maps, light, shade); - } - - void R_SetTranslationMap(lighttable_t *translation) - { - using namespace drawerargs; - - dc_colormap = translation; - } - - void rt_initcols(uint8_t *buffer) - { - using namespace drawerargs; - - for (int y = 3; y >= 0; y--) - horizspan[y] = dc_ctspan[y] = &dc_tspans[y][0]; - - DrawerCommandQueue::QueueCommand(buffer); - } - - void rt_span_coverage(int x, int start, int stop) - { - using namespace drawerargs; - - unsigned int **tspan = &dc_ctspan[x & 3]; - (*tspan)[0] = start; - (*tspan)[1] = stop; - *tspan += 2; - } - - void rt_flip_posts() - { - using namespace drawerargs; - - unsigned int *front = horizspan[dc_x & 3]; - unsigned int *back = dc_ctspan[dc_x & 3] - 2; - - while (front < back) - { - swapvalues(front[0], back[0]); - swapvalues(front[1], back[1]); - front += 2; - back -= 2; - } - } - - void rt_draw4cols(int sx) - { - using namespace drawerargs; - - int x, bad; - unsigned int maxtop, minbot, minnexttop; - - // Place a dummy "span" in each column. These don't get - // drawn. They're just here to avoid special cases in the - // max/min calculations below. - for (x = 0; x < 4; ++x) - { - dc_ctspan[x][0] = screen->GetHeight()+1; - dc_ctspan[x][1] = screen->GetHeight(); - } - - for (;;) - { - // If a column is out of spans, mark it as such - bad = 0; - minnexttop = 0xffffffff; - for (x = 0; x < 4; ++x) - { - if (horizspan[x] >= dc_ctspan[x]) - { - bad |= 1 << x; - } - else if ((horizspan[x]+2)[0] < minnexttop) - { - minnexttop = (horizspan[x]+2)[0]; - } - } - // Once all columns are out of spans, we're done - if (bad == 15) - { - return; - } - - // Find the largest shared area for the spans in each column - maxtop = MAX (MAX (horizspan[0][0], horizspan[1][0]), - MAX (horizspan[2][0], horizspan[3][0])); - minbot = MIN (MIN (horizspan[0][1], horizspan[1][1]), - MIN (horizspan[2][1], horizspan[3][1])); - - // If there is no shared area with these spans, draw each span - // individually and advance to the next spans until we reach a shared area. - // However, only draw spans down to the highest span in the next set of - // spans. If we allow the entire height of a span to be drawn, it could - // prevent any more shared areas from being drawn in these four columns. - // - // Example: Suppose we have the following arrangement: - // A CD - // A CD - // B D - // B D - // aB D - // aBcD - // aBcD - // aBc - // - // If we draw the entire height of the spans, we end up drawing this first: - // A CD - // A CD - // B D - // B D - // B D - // B D - // B D - // B D - // B - // - // This leaves only the "a" and "c" columns to be drawn, and they are not - // part of a shared area, but if we can include B and D with them, we can - // get a shared area. So we cut off everything in the first set just - // above the "a" column and end up drawing this first: - // A CD - // A CD - // B D - // B D - // - // Then the next time through, we have the following arrangement with an - // easily shared area to draw: - // aB D - // aBcD - // aBcD - // aBc - if (bad != 0 || maxtop > minbot) - { - int drawcount = 0; - for (x = 0; x < 4; ++x) - { - if (!(bad & 1)) - { - if (horizspan[x][1] < minnexttop) - { - hcolfunc_post1 (x, sx+x, horizspan[x][0], horizspan[x][1]); - horizspan[x] += 2; - drawcount++; - } - else if (minnexttop > horizspan[x][0]) - { - hcolfunc_post1 (x, sx+x, horizspan[x][0], minnexttop-1); - horizspan[x][0] = minnexttop; - drawcount++; - } - } - bad >>= 1; - } - // Drawcount *should* always be non-zero. The reality is that some situations - // can make this not true. Unfortunately, I'm not sure what those situations are. - if (drawcount == 0) - { - return; - } - continue; - } - - // Draw any span fragments above the shared area. - for (x = 0; x < 4; ++x) - { - if (maxtop > horizspan[x][0]) - { - hcolfunc_post1 (x, sx+x, horizspan[x][0], maxtop-1); - } - } - - // Draw the shared area. - hcolfunc_post4 (sx, maxtop, minbot); - - // For each column, if part of the span is past the shared area, - // set its top to just below the shared area. Otherwise, advance - // to the next span in that column. - for (x = 0; x < 4; ++x) - { - if (minbot < horizspan[x][1]) - { - horizspan[x][0] = minbot+1; - } - else - { - horizspan[x] += 2; - } - } - } - } - - void R_SetupSpanBits(FTexture *tex) - { - using namespace drawerargs; - - 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--; - } - } - - void R_SetSpanColormap(lighttable_t *colormap) - { - using namespace drawerargs; - - ds_colormap = colormap; - } - - void R_SetSpanSource(FTexture *tex) - { - using namespace drawerargs; - - ds_source = tex->GetPixels(); - } - - ///////////////////////////////////////////////////////////////////////// - - void R_FillColumnHoriz() - { - using namespace drawerargs; - - if (dc_count <= 0) - return; - - int x = dc_x & 3; - unsigned int **span = &dc_ctspan[x]; - (*span)[0] = dc_yl; - (*span)[1] = dc_yh; - *span += 2; - - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawColumnHoriz() - { - using namespace drawerargs; - - if (dc_count <= 0) - return; - - int x = dc_x & 3; - unsigned int **span = &dc_ctspan[x]; - (*span)[0] = dc_yl; - (*span)[1] = dc_yh; - *span += 2; - - DrawerCommandQueue::QueueCommand(); - } - - // Copies one span at hx to the screen at sx. - void rt_copy1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Copies all four spans to the screen starting at sx. - void rt_copy4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Maps one span at hx to the screen at sx. - void rt_map1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Maps all four spans to the screen starting at sx. - void rt_map4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates one span at hx to the screen at sx. - void rt_tlate1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_map1col(hx, sx, yl, yh); - } - - // Translates all four spans to the screen starting at sx. - void rt_tlate4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_map4cols(sx, yl, yh); - } - - // Adds one span at hx to the screen at sx without clamping. - void rt_add1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Adds all four spans to the screen starting at sx without clamping. - void rt_add4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates and adds one span at hx to the screen at sx without clamping. - void rt_tlateadd1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_add1col(hx, sx, yl, yh); - } - - // Translates and adds all four spans to the screen starting at sx without clamping. - void rt_tlateadd4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_add4cols(sx, yl, yh); - } - - // Shades one span at hx to the screen at sx. - void rt_shaded1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Shades all four spans to the screen starting at sx. - void rt_shaded4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Adds one span at hx to the screen at sx with clamping. - void rt_addclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Adds all four spans to the screen starting at sx with clamping. - void rt_addclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates and adds one span at hx to the screen at sx with clamping. - void rt_tlateaddclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_addclamp1col(hx, sx, yl, yh); - } - - // Translates and adds all four spans to the screen starting at sx with clamping. - void rt_tlateaddclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_addclamp4cols(sx, yl, yh); - } - - // Subtracts one span at hx to the screen at sx with clamping. - void rt_subclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Subtracts all four spans to the screen starting at sx with clamping. - void rt_subclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates and subtracts one span at hx to the screen at sx with clamping. - void rt_tlatesubclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_subclamp1col(hx, sx, yl, yh); - } - - // Translates and subtracts all four spans to the screen starting at sx with clamping. - void rt_tlatesubclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_subclamp4cols(sx, yl, yh); - } - - // Subtracts one span at hx from the screen at sx with clamping. - void rt_revsubclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - } - - // Subtracts all four spans from the screen starting at sx with clamping. - void rt_revsubclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - } - - // Translates and subtracts one span at hx from the screen at sx with clamping. - void rt_tlaterevsubclamp1col(int hx, int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); - rt_revsubclamp1col(hx, sx, yl, yh); - } - - // Translates and subtracts all four spans from the screen starting at sx with clamping. - void rt_tlaterevsubclamp4cols(int sx, int yl, int yh) - { - DrawerCommandQueue::QueueCommand(0, sx, yl, yh); - rt_revsubclamp4cols(sx, yl, yh); - } - - void R_DrawWallCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallMaskedCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallMaskedCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallAddCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallAddCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallAddClampCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallAddClampCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallSubClampCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallSubClampCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallRevSubClampCol1() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawWallRevSubClampCol4() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSingleSkyCol1(uint32_t solid_top, uint32_t solid_bottom) - { - DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); - } - - void R_DrawSingleSkyCol4(uint32_t solid_top, uint32_t solid_bottom) - { - DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); - } - - void R_DrawDoubleSkyCol1(uint32_t solid_top, uint32_t solid_bottom) - { - DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); - } - - void R_DrawDoubleSkyCol4(uint32_t solid_top, uint32_t solid_bottom) - { - DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); - } - - void R_DrawColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillAddColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillAddClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillSubClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillRevSubClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawFuzzColumn() - { - using namespace drawerargs; - - DrawerCommandQueue::QueueCommand(); - - dc_yl = MAX(dc_yl, 1); - dc_yh = MIN(dc_yh, fuzzviewheight); - if (dc_yl <= dc_yh) - fuzzpos = (fuzzpos + dc_yh - dc_yl + 1) % FUZZTABLE; - } - - void R_DrawAddColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawTranslatedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawTlatedAddColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawShadedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawAddClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawAddClampTranslatedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSubClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSubClampTranslatedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawRevSubClampColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawRevSubClampTranslatedColumn() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpan() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanMasked() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanTranslucent() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanMaskedTranslucent() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanAddClamp() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawSpanMaskedAddClamp() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_FillSpan() - { - DrawerCommandQueue::QueueCommand(); - } - - void R_DrawTiltedSpan(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) - { - DrawerCommandQueue::QueueCommand(y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy); - } - - void R_DrawColoredSpan(int y, int x1, int x2) - { - DrawerCommandQueue::QueueCommand(y, x1, x2); - } - - namespace - { - const uint8_t *slab_colormap; - } - - void R_SetupDrawSlab(uint8_t *colormap) - { - slab_colormap = colormap; - } - - void R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p) - { - DrawerCommandQueue::QueueCommand(dx, v, dy, vi, vptr, p, slab_colormap); - } - - void R_DrawFogBoundarySection(int y, int y2, int x1) - { - for (; y < y2; ++y) - { - int x2 = spanend[y]; - DrawerCommandQueue::QueueCommand(y, x1, 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; - uint8_t *basecolormapdata = basecolormap->Maps; - - if (b2 > t2) - { - fillshort(spanend + t2, b2 - t2, x); - } - - R_SetColorMapLight(basecolormap->Maps, (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 -= 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) - { - fillshort(spanend + t2, b2 - t2, x); - } - rcolormap = lcolormap; - R_SetColorMapLight(basecolormap->Maps, (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++; - DrawerCommandQueue::QueueCommand(y, xr, spanend[y]); - } - stop = MAX(b1, t2); - while (b2 > stop) - { - int y = --b2; - DrawerCommandQueue::QueueCommand(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) - { - R_DrawFogBoundarySection(t2, b2, x1); - } - } - - void R_DrawParticle(vissprite_t *sprite) - { - R_DrawParticle_C(sprite); - } -} diff --git a/src/r_draw.h b/src/r_draw.h deleted file mode 100644 index c245034d7..000000000 --- a/src/r_draw.h +++ /dev/null @@ -1,197 +0,0 @@ - -#pragma once - -#include "r_defs.h" - -EXTERN_CVAR(Bool, r_multithreaded); -EXTERN_CVAR(Int, r_drawfuzz); -EXTERN_CVAR(Bool, r_drawtrans); -EXTERN_CVAR(Float, transsouls); -EXTERN_CVAR(Int, r_columnmethod); - -namespace swrenderer -{ - struct vissprite_t; - - extern double dc_texturemid; - - namespace drawerargs - { - extern int dc_pitch; - extern lighttable_t *dc_colormap; - extern int dc_x; - extern int dc_yl; - extern int dc_yh; - extern fixed_t dc_iscale; - extern fixed_t dc_texturefrac; - extern uint32_t dc_textureheight; - extern int dc_color; - extern uint32_t dc_srccolor; - extern uint32_t dc_srccolor_bgra; - extern uint32_t *dc_srcblend; - extern uint32_t *dc_destblend; - extern fixed_t dc_srcalpha; - extern fixed_t dc_destalpha; - extern const uint8_t *dc_source; - extern const uint8_t *dc_source2; - extern uint32_t dc_texturefracx; - extern uint8_t *dc_translation; - extern uint8_t *dc_dest; - extern uint8_t *dc_destorg; - extern int dc_destheight; - extern int dc_count; - - extern uint32_t dc_wall_texturefrac[4]; - extern uint32_t dc_wall_iscale[4]; - extern uint8_t *dc_wall_colormap[4]; - extern fixed_t dc_wall_light[4]; - extern const uint8_t *dc_wall_source[4]; - extern const uint8_t *dc_wall_source2[4]; - extern uint32_t dc_wall_texturefracx[4]; - extern uint32_t dc_wall_sourceheight[4]; - extern int dc_wall_fracbits; - - extern int ds_y; - extern int ds_x1; - extern int ds_x2; - extern lighttable_t * ds_colormap; - extern dsfixed_t ds_light; - extern dsfixed_t ds_xfrac; - extern dsfixed_t ds_yfrac; - extern dsfixed_t ds_xstep; - extern dsfixed_t ds_ystep; - extern int ds_xbits; - extern int ds_ybits; - extern fixed_t ds_alpha; - extern double ds_lod; - extern const uint8_t *ds_source; - extern int ds_color; - - extern unsigned int dc_tspans[4][MAXHEIGHT]; - extern unsigned int *dc_ctspan[4]; - extern unsigned int *horizspan[4]; - } - - extern int ylookup[MAXHEIGHT]; - extern uint8_t shadetables[/*NUMCOLORMAPS*16*256*/]; - extern FDynamicColormap ShadeFakeColormap[16]; - extern uint8_t identitymap[256]; - extern FDynamicColormap identitycolormap; - - // Spectre/Invisibility. - #define FUZZTABLE 50 - extern int fuzzoffset[FUZZTABLE + 1]; - extern int fuzzpos; - extern int fuzzviewheight; - - void R_InitColumnDrawers(); - void R_InitShadeMaps(); - void R_InitFuzzTable(int fuzzoff); - - 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, uint32_t color); - ESPSResult R_SetPatchStyle(FRenderStyle style, float alpha, int translation, uint32_t color); - void R_FinishSetPatchStyle(); // Call this after finished drawing the current thing, in case its style was STYLE_Shade - bool R_GetTransMaskDrawers(void(**drawCol1)(), void(**drawCol4)()); - - const uint8_t *R_GetColumn(FTexture *tex, int col); - - void rt_initcols(uint8_t *buffer = nullptr); - void rt_span_coverage(int x, int start, int stop); - void rt_draw4cols(int sx); - void rt_flip_posts(); - void rt_copy1col(int hx, int sx, int yl, int yh); - void rt_copy4cols(int sx, int yl, int yh); - void rt_shaded1col(int hx, int sx, int yl, int yh); - void rt_shaded4cols(int sx, int yl, int yh); - void rt_map1col(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(int sx, int yl, int yh); - void rt_add4cols(int sx, int yl, int yh); - void rt_addclamp4cols(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 R_DrawColumnHoriz(); - void R_DrawColumn(); - void R_DrawFuzzColumn(); - void R_DrawTranslatedColumn(); - void R_DrawShadedColumn(); - void R_FillColumn(); - void R_FillAddColumn(); - void R_FillAddClampColumn(); - void R_FillSubClampColumn(); - void R_FillRevSubClampColumn(); - void R_DrawAddColumn(); - void R_DrawTlatedAddColumn(); - void R_DrawAddClampColumn(); - void R_DrawAddClampTranslatedColumn(); - void R_DrawSubClampColumn(); - void R_DrawSubClampTranslatedColumn(); - void R_DrawRevSubClampColumn(); - void R_DrawRevSubClampTranslatedColumn(); - void R_DrawSpan(); - void R_DrawSpanMasked(); - void R_DrawSpanTranslucent(); - void R_DrawSpanMaskedTranslucent(); - void R_DrawSpanAddClamp(); - void R_DrawSpanMaskedAddClamp(); - void R_FillSpan(); - void R_DrawTiltedSpan(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 R_DrawColoredSpan(int y, int x1, int x2); - void R_SetupDrawSlab(uint8_t *colormap); - void R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p); - void R_DrawFogBoundary(int x1, int x2, short *uclip, short *dclip); - void R_FillColumnHoriz(); - void R_FillSpan(); - - void R_DrawWallCol1(); - void R_DrawWallCol4(); - void R_DrawWallMaskedCol1(); - void R_DrawWallMaskedCol4(); - void R_DrawWallAddCol1(); - void R_DrawWallAddCol4(); - void R_DrawWallAddClampCol1(); - void R_DrawWallAddClampCol4(); - void R_DrawWallSubClampCol1(); - void R_DrawWallSubClampCol4(); - void R_DrawWallRevSubClampCol1(); - void R_DrawWallRevSubClampCol4(); - - void R_DrawSingleSkyCol1(uint32_t solid_top, uint32_t solid_bottom); - void R_DrawSingleSkyCol4(uint32_t solid_top, uint32_t solid_bottom); - void R_DrawDoubleSkyCol1(uint32_t solid_top, uint32_t solid_bottom); - void R_DrawDoubleSkyCol4(uint32_t solid_top, uint32_t solid_bottom); - - void R_SetColorMapLight(lighttable_t *base_colormap, float light, int shade); - void R_SetColorMapLight(FDynamicColormap *base_colormap, float light, int shade); - void R_SetDSColorMapLight(lighttable_t *base_colormap, float light, int shade); - void R_SetDSColorMapLight(FDynamicColormap *base_colormap, float light, int shade); - void R_SetTranslationMap(lighttable_t *translation); - - void R_SetupSpanBits(FTexture *tex); - void R_SetSpanColormap(lighttable_t *colormap); - void R_SetSpanSource(FTexture *tex); - - void R_MapTiltedPlane(int y, int x1); - void R_MapColoredPlane(int y, int x1); - void R_DrawParticle(vissprite_t *); -} 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 b0ba8841e..000000000 --- a/src/r_local.h +++ /dev/null @@ -1,40 +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. - -// 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 4a8e796ad..000000000 --- a/src/r_main.cpp +++ /dev/null @@ -1,1077 +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 "p_maputl.h" -#include "r_thread.h" -#include "events.h" - -CVAR (String, r_viewsize, "", CVAR_NOSET) -CVAR (Bool, r_shadercolormaps, true, CVAR_ARCHIVE) - -CUSTOM_CVAR (Int, r_columnmethod, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (self != 0 && self != 1) - { - self = 1; - } - else - { // Trigger the change - setsizeneeded = true; - } -} - -CVAR(Int, r_portal_recursions, 4, CVAR_ARCHIVE) -CVAR(Bool, r_highlight_portals, false, CVAR_ARCHIVE) - -EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor) - -extern cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; -extern cycle_t FrameCycles; - -extern bool r_showviewer; - -cycle_t WallCycles, PlaneCycles, MaskedCycles, WallScanCycles; - -namespace swrenderer -{ - using namespace drawerargs; - -// 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 int fuzzviewheight; -extern subsector_t *InSubsector; - - -// PRIVATE DATA DECLARATIONS ----------------------------------------------- - -static double CurrentVisibility = 8.f; -static double MaxVisForWall; -static double MaxVisForFloor; -bool r_dontmaplines; - -// PUBLIC DATA DEFINITIONS ------------------------------------------------- - -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); - -// PRIVATE DATA DEFINITIONS ------------------------------------------------ - -static int lastcenteryfrac; - -// CODE -------------------------------------------------------------------- - -//========================================================================== -// -// R_InitTextureMapping -// -//========================================================================== - -void R_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]; - } -} - -//========================================================================== -// -// 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*AspectBaseHeight(WidescreenRatio) / - (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, float 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 - - lastcenteryfrac = 1<<30; - 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; - - // psprite scales - pspritexscale = centerxwide / 160.0; - pspriteyscale = pspritexscale * YaspectMul; - pspritexiscale = 1 / pspritexscale; - - // thing clipping - fillshort (screenheightarray, viewwidth, (short)viewheight); - - R_InitTextureMapping (); - - MaxVisForWall = (InvZtoScale * (SCREENWIDTH*r_Yaspect) / - (viewwidth*SCREENHEIGHT * FocalTangent)); - MaxVisForWall = 32767.0 / MaxVisForWall; - MaxVisForFloor = 32767.0 / (viewheight >> 2) * FocalLengthY / 160; - - // Reset r_*Visibility vars - R_SetVisibility(R_GetVisibility()); -} - -//========================================================================== -// -// R_Init -// -//========================================================================== - -void R_InitRenderer() -{ - atterm(R_ShutdownRenderer); - // viewwidth / viewheight are set by the defaults - fillshort (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; - // [SP] Emulate GZDoom's light-amp goggles. - if (r_fullbrightignoresectorcolor && fixedlightlev >= 0) - { - fixedcolormap = FullNormalLight.Maps; - } - } - } - // [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] = focus / den; - den -= 1; - } while (++i < e); - } - else - { - do { - yslope[i] = focus / den; - den -= 1; - } while (++i < centery); - den = i - cy + 0.5f; - do { - yslope[i] = focus / den; - den += 1; - } while (++i < e); - } - } - else - { - den = i - cy + 0.5f; - do { - yslope[i] = 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 -// -//========================================================================== - -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() * 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; - - 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); - } - 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_FillColumnHoriz; - hcolfunc_post1 = rt_copy1col; - hcolfunc_post4 = rt_copy4cols; - colfunc = R_FillColumn; - 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; - - R_BeginDrawerCommands(); - - viewwidth = width; - RenderTarget = canvas; - bRenderingToCanvas = true; - - R_SetWindow (12, width, height, height, true); - viewwindowx = x; - viewwindowy = y; - viewactive = true; - - R_RenderActorView (actor, dontmaplines); - - R_EndDrawerCommands(); - - 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 -// -//========================================================================== - -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 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 - -} \ No newline at end of file diff --git a/src/r_main.h b/src/r_main.h deleted file mode 100644 index be1c36306..000000000 --- a/src/r_main.h +++ /dev/null @@ -1,153 +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" - -extern double ViewCos; -extern double ViewSin; -extern int viewwindowx; -extern int viewwindowy; - -typedef BYTE lighttable_t; // This could be wider for >8 bit display. - -namespace swrenderer -{ - -// -// POV related. -// -extern bool bRenderingToCanvas; -extern fixed_t viewingrangerecip; -extern double FocalLengthX, FocalLengthY; -extern double InvZtoScale; - -extern double WallTMapScale2; - - -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)) - -// Converts fixedlightlev into a shade value -#define FIXEDLIGHT2SHADE(lightlev) (((lightlev) >> COLORMAPSHIFT) << FRACBITS) - -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 62730e731..000000000 --- a/src/r_plane.cpp +++ /dev/null @@ -1,1789 +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" -#include "g_levellocals.h" -#include "events.h" - -#ifdef _MSC_VER -#pragma warning(disable:4244) -#endif - -CVAR(Bool, tilt, false, 0); -CVAR(Bool, r_skyboxes, true, 0) - -EXTERN_CVAR(Int, r_skymode) - -namespace swrenderer -{ - using namespace drawerargs; - -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]; - -int planeshade; -FVector3 plane_sz, plane_su, plane_sv; -float planelightfloat; -bool plane_shade; -fixed_t pviewx, pviewy; -} - -float yslope[MAXHEIGHT]; -static fixed_t xscale, yscale; -static double xstepscale, ystepscale; -static double basexfrac, baseyfrac; - -void R_DrawSinglePlane (visplane_t *, fixed_t alpha, bool additive, bool masked); -void R_DrawSkySegment(visplane_t *vis, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const uint8_t *(*getcol)(FTexture *tex, int col)); - -//========================================================================== -// -// 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]; - double 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 = planeheight * yslope[y]; - - if (ds_xbits != 0) - { - ds_xstep = xs_ToFixed(32 - ds_xbits, distance * xstepscale); - ds_xfrac = xs_ToFixed(32 - ds_xbits, distance * basexfrac) + pviewx; - } - else - { - ds_xstep = 0; - ds_xfrac = 0; - } - if (ds_ybits != 0) - { - ds_ystep = xs_ToFixed(32 - ds_ybits, distance * ystepscale); - ds_yfrac = xs_ToFixed(32 - ds_ybits, distance * baseyfrac) + pviewy; - } - else - { - ds_ystep = 0; - ds_yfrac = 0; - } - - if (plane_shade) - { - // Determine lighting based on the span's distance from the viewer. - R_SetDSColorMapLight(basecolormap, GlobVis * fabs(CenterY - y), planeshade); - } - - ds_y = y; - ds_x1 = x1; - ds_x2 = x2; - - spanfunc (); -} - -//========================================================================== -// -// R_MapTiltedPlane -// -//========================================================================== - -void R_MapTiltedPlane (int y, int x1) -{ - R_DrawTiltedSpan(y, x1, spanend[y], plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy); -} - -//========================================================================== -// -// R_MapColoredPlane -// -//========================================================================== - -void R_MapColoredPlane(int y, int x1) -{ - R_DrawColoredSpan(y, x1, spanend[y]); -} - -//========================================================================== -// -// 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 - fillshort (floorclip, viewwidth, viewheight); - // [RH] clip ceiling to console bottom - fillshort (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 &xxform, - int sky, FSectorPortal *portal) -{ - secplane_t plane; - visplane_t *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 != 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 && - basecolormap == check->colormap && // [RH] Add more checks - *xform == check->xform - ) - ) && - check->viewangle == 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 && - 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->xform = *xform; - 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; - - fillshort (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->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; - pl = new_pl; - pl->left = start; - pl->right = stop; - fillshort (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 R_DrawSkySegment 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_DrawSkyColumnStripe(int start_x, int y1, int y2, int columns, double scale, double texturemid, double yrepeat) -{ - uint32_t height = frontskytex->GetHeight(); - - for (int i = 0; i < columns; i++) - { - double uv_stepd = skyiscale * yrepeat; - double v = (texturemid + uv_stepd * (y1 - 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 + i; - if (MirrorFlags & RF_XFLIP) - x = (viewwidth - x); - - DWORD ang, angle1, angle2; - - ang = (skyangle + xtoviewangle[x]) ^ skyflip; - angle1 = (DWORD)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); - angle2 = (DWORD)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); - - dc_wall_source[i] = (const BYTE *)frontskytex->GetColumn(angle1, nullptr); - dc_wall_source2[i] = backskytex ? (const BYTE *)backskytex->GetColumn(angle2, nullptr) : nullptr; - - dc_wall_iscale[i] = uv_step; - dc_wall_texturefrac[i] = uv_pos; - } - - dc_wall_sourceheight[0] = height; - dc_wall_sourceheight[1] = backskytex ? backskytex->GetHeight() : height; - dc_dest = (ylookup[y1] + start_x) + dc_destorg; - dc_count = y2 - y1; - - uint32_t solid_top = frontskytex->GetSkyCapColor(false); - uint32_t solid_bottom = frontskytex->GetSkyCapColor(true); - - if (columns == 4) - if (!backskytex) - R_DrawSingleSkyCol4(solid_top, solid_bottom); - else - R_DrawDoubleSkyCol4(solid_top, solid_bottom); - else - if (!backskytex) - R_DrawSingleSkyCol1(solid_top, solid_bottom); - else - R_DrawDoubleSkyCol1(solid_top, solid_bottom); -} - -static void R_DrawSkyColumn(int start_x, int y1, int y2, int columns) -{ - if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) - { - double texturemid = skymid * frontskytex->Scale.Y + frontskytex->GetHeight(); - R_DrawSkyColumnStripe(start_x, y1, y2, columns, frontskytex->Scale.Y, texturemid, frontskytex->Scale.Y); - } - else - { - 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 - CenterY), frontskytex->GetHeight()); - if (topfrac < 0) topfrac += frontskytex->GetHeight(); - double texturemid = topfrac - iscale * (1 - CenterY); - R_DrawSkyColumnStripe(start_x, y1, y2, columns, scale, texturemid, yrepeat); - } -} - -static void R_DrawCapSky(visplane_t *pl) -{ - int x1 = pl->left; - int x2 = pl->right; - short *uwal = (short *)pl->top; - short *dwal = (short *)pl->bottom; - - // 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++) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - R_DrawSkyColumn(x, y1, y2, 1); - } - - // 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] }; - - // 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; - - R_DrawSkyColumn(x + i, y1[i], y2[i], 1); - } - continue; - } - - // Draw the first rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (y1[i] < middle_y1) - R_DrawSkyColumn(x + i, y1[i], middle_y1, 1); - } - - // Draw the area where all 4 columns are active - R_DrawSkyColumn(x, middle_y1, middle_y2, 4); - - // Draw the last rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (middle_y2 < y2[i]) - R_DrawSkyColumn(x + i, middle_y2, y2[i], 1); - } - } - - // The last unaligned columns: - for (int x = aligned_x2; x < x2; x++) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - R_DrawSkyColumn(x, y1, y2, 1); - } -} - -static void R_DrawSky (visplane_t *pl) -{ - if (r_skymode == 2 && !(level.flags & LEVEL_FORCETILEDSKY)) - { - R_DrawCapSky(pl); - return; - } - - 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; - } - R_DrawSkySegment (pl, (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; - } - R_DrawSkySegment (pl, 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. -// -//========================================================================== - -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); - double xscale = pl->xform.xScale * tex->Scale.X; - double yscale = pl->xform.yScale * tex->Scale.Y; - R_SetSpanSource(tex); - - basecolormap = pl->colormap; - planeshade = LIGHT2SHADE(pl->lightlevel); - - if (r_drawflat || (!pl->height.isSlope() && !tilt)) - { - R_DrawNormalPlane(pl, xscale, yscale, alpha, additive, masked); - } - else - { - R_DrawTiltedPlane(pl, xscale, yscale, 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. -// -//========================================================================== -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 - AActor *sky = 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) level.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) 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 (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 && pl->picnum != skyflatnum) - { - 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 = &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) - 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) - { - R_SetColorMapLight(fixedcolormap, 0, 0); - } - else - { - fakefixed = true; - fixedcolormap = NormalLight.Maps; - R_SetColorMapLight(fixedcolormap, 0, 0); - } - - R_DrawSky (pl); - - if (fakefixed) - fixedcolormap = NULL; -} - -//========================================================================== -// -// R_DrawNormalPlane -// -//========================================================================== - -void R_DrawNormalPlane (visplane_t *pl, double _xscale, double _yscale, fixed_t alpha, bool additive, bool masked) -{ - if (alpha <= 0) - { - return; - } - - double planeang = (pl->xform.Angle + pl->xform.baseAngle).Radians(); - double xstep, ystep, leftxfrac, leftyfrac, rightxfrac, rightyfrac; - double x; - - xscale = xs_ToFixed(32 - ds_xbits, _xscale); - yscale = xs_ToFixed(32 - ds_ybits, _yscale); - if (planeang != 0) - { - double cosine = cos(planeang), sine = sin(planeang); - pviewx = FLOAT2FIXED(pl->xform.xOffs + ViewPos.X * cosine - ViewPos.Y * sine); - pviewy = FLOAT2FIXED(pl->xform.yOffs - ViewPos.X * sine - ViewPos.Y * cosine); - } - else - { - pviewx = FLOAT2FIXED(pl->xform.xOffs + ViewPos.X); - pviewy = FLOAT2FIXED(pl->xform.yOffs - ViewPos.Y); - } - - pviewx = FixedMul (xscale, pviewx); - pviewy = FixedMul (yscale, pviewy); - - // left to right mapping - planeang += (ViewAngle - 90).Radians(); - - // Scale will be unit scale at FocalLengthX (normally SCREENWIDTH/2) distance - xstep = cos(planeang) / FocalLengthX; - ystep = -sin(planeang) / FocalLengthX; - - // [RH] flip for mirrors - if (MirrorFlags & RF_XFLIP) - { - xstep = -xstep; - ystep = -ystep; - } - - planeang += M_PI/2; - double cosine = cos(planeang), sine = -sin(planeang); - x = pl->right - centerx - 0.5; - rightxfrac = _xscale * (cosine + x * xstep); - rightyfrac = _yscale * (sine + x * ystep); - x = pl->left - centerx - 0.5; - leftxfrac = _xscale * (cosine + x * xstep); - leftyfrac = _yscale * (sine + x * ystep); - - basexfrac = rightxfrac; - baseyfrac = rightyfrac; - xstepscale = (rightxfrac - leftxfrac) / (pl->right - pl->left); - ystepscale = (rightyfrac - leftyfrac) / (pl->right - pl->left); - - planeheight = fabs(pl->height.Zat0() - ViewPos.Z); - - GlobVis = r_FloorVisibility / planeheight; - ds_light = 0; - if (fixedlightlev >= 0) - { - R_SetDSColorMapLight(basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - plane_shade = false; - } - else if (fixedcolormap) - { - R_SetDSColorMapLight(fixedcolormap, 0, 0); - 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, double _xscale, double _yscale, 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, planeang, cosine, sine; - double zeroheight; - - if (alpha <= 0) - { - return; - } - - lxscale = _xscale * ifloatpow2[ds_xbits]; - lyscale = _yscale * ifloatpow2[ds_ybits]; - xscale = 64.f / lxscale; - yscale = 64.f / lyscale; - zeroheight = pl->height.ZatPoint(ViewPos); - - pviewx = xs_ToFixed(32 - ds_xbits, pl->xform.xOffs * pl->xform.xScale); - pviewy = xs_ToFixed(32 - ds_ybits, 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 *= 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) - { - R_SetDSColorMapLight(basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - plane_shade = false; - } - else if (fixedcolormap) - { - R_SetDSColorMapLight(fixedcolormap, 0, 0); - plane_shade = false; - } - else - { - R_SetDSColorMapLight(basecolormap, 0, 0); - plane_shade = true; - } - - // Hack in support for 1 x Z and Z x 1 texture sizes - if (ds_ybits == 0) - { - plane_sv[2] = plane_sv[1] = plane_sv[0] = 0; - } - if (ds_xbits == 0) - { - plane_su[2] = plane_su[1] = plane_su[0] = 0; - } - - R_MapVisPlane (pl, R_MapTiltedPlane); -} - -//========================================================================== -// -// 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) - { - 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) - { - 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 cd1405a7c..000000000 --- a/src/r_plane.h +++ /dev/null @@ -1,119 +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 - -namespace swrenderer -{ - -// -// The infamous visplane -// -struct visplane_s -{ - struct visplane_s *next; // Next visplane in hash chain -- killough - - FDynamicColormap *colormap; // [RH] Support multiple colormaps - FSectorPortal *portal; // [RH] Support sky boxes - - FTransform xform; - secplane_t height; - FTextureID picnum; - int lightlevel; - int left, right; - int sky; - - // [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 float 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, double xscale, double yscale, fixed_t alpha, bool additive, bool masked); -void R_DrawTiltedPlane (visplane_t *pl, double xscale, double yscale, 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 0873cb373..000000000 --- a/src/r_segs.cpp +++ /dev/null @@ -1,2410 +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. -// -//----------------------------------------------------------------------------- - -#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 "r_draw.h" -#include "v_palette.h" -#include "r_data/colormaps.h" - -#define WALLYREPEAT 8 - - -CVAR(Bool, r_fogboundary, true, 0) -CVAR(Bool, r_drawmirrors, true, 0) -EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); - -namespace swrenderer -{ - using namespace drawerargs; - - 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); - void R_DrawDrawSeg(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat); - -#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)); - -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); -} - - -// -// R_RenderMaskedSegRange -// -float *MaskedSWall; -float MaskedScaleY; - -static void BlastMaskedColumn (FTexture *tex, bool useRt) -{ - // calculate lighting - if (fixedcolormap == NULL && fixedlightlev < 0) - { - R_SetColorMapLight(basecolormap, rw_light, wallshade); - } - - 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 - R_DrawMaskedColumn(tex, maskedtexturecol[dc_x], useRt); - 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]; - - R_CreateWallSegmentYSloped(most, curline->frontsector->ceilingplane, &WallC); - for (int i = x1; i < x2; ++i) - { - if (wallupper[i] < most[i]) - wallupper[i] = most[i]; - } - R_CreateWallSegmentYSloped(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; - bool wrap = false; - - 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) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - - // 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); - - 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. - 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) - { - R_CreateWallSegmentY(wallupper, textop < sclipTop - ViewPos.Z ? textop : sclipTop - ViewPos.Z, &WallC); - } - else - { - R_CreateWallSegmentY(wallupper, textop, &WallC); - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - R_CreateWallSegmentY(walllower, textop - texheight > sclipBottom - ViewPos.Z ? textop - texheight : sclipBottom - ViewPos.Z, &WallC); - } - else - { - R_CreateWallSegmentY(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 (tex, false); - } - } - 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 (tex, false); - dc_x++; - } - - while (dc_x < stop) - { - rt_initcols(nullptr); - BlastMaskedColumn (tex, true); dc_x++; - BlastMaskedColumn (tex, true); dc_x++; - BlastMaskedColumn (tex, true); dc_x++; - BlastMaskedColumn (tex, true); - rt_draw4cols (dc_x - 3); - dc_x++; - } - - while (dc_x < x2) - { - BlastMaskedColumn (tex, false); - 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 - ViewPos.Z + rowoffset) * 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) - { - R_CreateWallSegmentY(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) - { - R_CreateWallSegmentY(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; - R_DrawDrawSeg(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) - { - if (!wrap) - { - assert(ds->bkup >= 0); - memcpy(openings + ds->sprtopclip, openings + ds->bkup, (ds->x2 - ds->x1) * 2); - } - } - else - { - fillshort(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; - double 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 = 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 = xs_RoundToInt(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) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(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; - - R_CreateWallSegmentY(wallupper, sclipTop - ViewPos.Z, &WallC); - R_CreateWallSegmentY(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); - R_DrawDrawSeg(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; - double floorHeight; - double 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 = backsector->CenterFloor(); - ceilingHeight = backsector->CenterCeiling(); - - // maybe fix clipheights - if (!(fake3D & FAKE3D_CLIPBOTTOM)) sclipBottom = floorHeight; - if (!(fake3D & FAKE3D_CLIPTOP)) sclipTop = ceilingHeight; - - // maybe not visible - if (sclipBottom >= frontsector->CenterCeiling()) return; - if (sclipTop <= 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; -} - -// -// 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. -// - -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) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - - // 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; - } - R_DrawWallSegment(rw_pic, x1, x2, walltop, wallbottom, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_frontfz1, rw_frontfz2), false); - } - fillshort (ceilingclip+x1, x2-x1, viewheight); - fillshort (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; - } - R_DrawWallSegment(rw_pic, x1, x2, walltop, wallupper, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_backcz1, rw_backcz2), 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; - } - R_DrawWallSegment(rw_pic, x1, x2, walllower, wallbottom, swall, lwall, yscale, MAX(rw_backfz1, rw_backfz2), MIN(rw_frontfz1, rw_frontfz2), 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->special == Line_Mirror && r_drawmirrors)) // [ZZ] compatibility with r_drawmirrors cvar that existed way before portals - { - markfloor = markceiling = true; // act like a 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->isVisualPortal()) - { - rw_markportal = true; - } - else 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. - R_CreateWallSegmentYSloped (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 (linedef->isVisualPortal()) - { - markceiling = markfloor = true; - } - else 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; - } - } - rw_markportal = linedef->isVisualPortal(); - } - - // 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 (DMSG_NOTIFY, "MaxDrawSegs increased to %zu\n", MaxDrawSegs); - } -} - -// -// R_CheckOpenings -// - -ptrdiff_t R_NewOpening (ptrdiff_t len) -{ - ptrdiff_t res = lastopening; - len = (len + 1) & ~1; // only return DWORD aligned addresses because some code stores fixed_t's and floats in openings... - 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 (DMSG_NOTIFY, "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); - fillshort (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); - fillshort (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 - - // note: This should never have used the openings array to store its data! - 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.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++ = 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 R_CreateWallSegmentY(short *outbuf, double z1, double z2, const FWallCoords *wallc) -{ - float y1 = (float)(CenterY - z1 * InvZtoScale / wallc->sz1); - float y2 = (float)(CenterY - z2 * InvZtoScale / wallc->sz2); - - if (y1 < 0 && y2 < 0) // entire line is above screen - { - memset(&outbuf[wallc->sx1], 0, (wallc->sx2 - wallc->sx1) * sizeof(outbuf[0])); - return 3; - } - else if (y1 > viewheight && y2 > viewheight) // entire line is below screen - { - fillshort(&outbuf[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); - return 12; - } - - if (wallc->sx2 <= wallc->sx1) - return 0; - - 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; - outbuf[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; - outbuf[x] = (short)clamp(xs_RoundToInt(y), 0, viewheight); - } - } - - return 0; -} - -int R_CreateWallSegmentYSloped(short *outbuf, const secplane_t &plane, const FWallCoords *wallc) -{ - if (!plane.isSlope()) - { - return R_CreateWallSegmentY(outbuf, plane.Zat0() - ViewPos.Z, wallc); - } - else - { - // Get Z coordinates at both ends of the line - double x, y, den, z1, z2; - 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 = 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 R_CreateWallSegmentY(outbuf, z1, z2, wallc); - } -} - -void PrepWall(float *vstep, fixed_t *upos, double walxrepeat, int x1, int x2) -{ - float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - CenterX); - float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - CenterX); - float uGradient = WallT.UoverZstep; - float zGradient = WallT.InvZstep; - float xrepeat = (float)fabs(walxrepeat); - float depthScale = (float)(WallT.InvZstep * WallTMapScale2); - float depthOrg = (float)(-WallT.UoverZstep * 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 PrepLWall(fixed_t *upos, double walxrepeat, int x1, int x2) -{ - float uOverZ = WallT.UoverZorg + WallT.UoverZstep * (float)(x1 + 0.5 - CenterX); - float invZ = WallT.InvZorg + WallT.InvZstep * (float)(x1 + 0.5 - 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; - } - } -} - -// 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 - savecoord.sx1) * rw_lightstep; - if (fixedlightlev >= 0) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - else if (!foggy && (decal->RenderFlags & RF_FULLBRIGHT)) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, 0); - 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 - R_SetColorMapLight(usecolormap, rw_light, wallshade); - } - R_WallSpriteColumn (false); - dc_x++; - } - - while (dc_x < stop4) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, wallshade); - } - rt_initcols(nullptr); - for (int zz = 4; zz; --zz) - { - R_WallSpriteColumn (true); - dc_x++; - } - rt_draw4cols (dc_x - 4); - } - - while (dc_x < x2) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, wallshade); - } - R_WallSpriteColumn (false); - 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 8f552daee..000000000 --- a/src/r_segs.h +++ /dev/null @@ -1,83 +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__ - -namespace swrenderer -{ - -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 R_CreateWallSegmentY (short *outbuf, double z1, double z2, const FWallCoords *wallc); -int R_CreateWallSegmentYSloped (short *outbuf, const secplane_t &plane, const FWallCoords *wallc); -inline int R_CreateWallSegmentY(short *outbuf, double z, const FWallCoords *wallc) -{ - return R_CreateWallSegmentY(outbuf, z, z, 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 7151c0441..9cd8400b4 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -68,7 +68,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 87bce4013..000000000 --- a/src/r_swrenderer.cpp +++ /dev/null @@ -1,357 +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" -#include "r_thread.h" - -namespace swrenderer -{ - -void R_SWRSetWindow(int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio); -void R_SetupColormap(player_t *); -void R_SetupFreelook(); -void R_InitRenderer(); - -} - -using namespace swrenderer; - -//========================================================================== -// -// 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 (); - } - } -} - -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]); - } -} - -//=========================================================================== -// -// Render the view -// -//=========================================================================== - -void FSoftwareRenderer::RenderView(player_t *player) -{ - R_BeginDrawerCommands(); - R_RenderActorView (player->mo); - // [RH] Let cameras draw onto textures that were visible this frame. - FCanvasTextureInfo::UpdateAll (); - R_EndDrawerCommands(); -} - -//========================================================================== -// -// -// -//========================================================================== - -void FSoftwareRenderer::RemapVoxels() -{ - for (unsigned i=0; iRemap(); - } -} - -//=========================================================================== -// -// Render the view to a savegame picture -// -//=========================================================================== - -void FSoftwareRenderer::WriteSavePic (player_t *player, FileWriter *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, float 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; - - DAngle savedfov = FieldOfView; - R_SetFOV ((double)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 f9d5609a0..000000000 --- a/src/r_swrenderer.h +++ /dev/null @@ -1,42 +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 override; - - // precache one texture - void PrecacheTexture(FTexture *tex, int cache); - virtual void Precache(BYTE *texhitlist, TMap &actorhitlist) override; - - // render 3D view - virtual void RenderView(player_t *player) override; - - // Remap voxel palette - virtual void RemapVoxels() override; - - // renders view to a savegame picture - virtual void WriteSavePic (player_t *player, FileWriter *file, int width, int height) override; - - // draws player sprites with hardware acceleration (only useful for software rendering) - virtual void DrawRemainingPlayerSprites() override; - - virtual int GetMaxViewPitch(bool down) override; - - void OnModeSet () override; - void ErrorCleanup () override; - void ClearBuffer(int color) override; - void Init() override; - void SetWindow (int windowSize, int fullWidth, int fullHeight, int stHeight, float trueratio) override; - void SetupFrame(player_t *player) override; - void CopyStackedViewParameters() override; - void RenderTextureView (FCanvasTexture *tex, AActor *viewpoint, int fov) override; - sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, bool back) override; - -}; - - -#endif diff --git a/src/r_things.cpp b/src/r_things.cpp deleted file mode 100644 index 76dae9eea..000000000 --- a/src/r_things.cpp +++ /dev/null @@ -1,3263 +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" -#include "g_levellocals.h" -#include "r_thread.h" -#include "events.h" - -EXTERN_CVAR(Bool, st_scale) -EXTERN_CVAR(Bool, r_shadercolormaps) -EXTERN_CVAR(Int, r_drawfuzz) -EXTERN_CVAR(Bool, r_deathcamera); -EXTERN_CVAR(Bool, r_drawplayersprites) -EXTERN_CVAR(Bool, r_drawvoxels) - -CVAR(Bool, r_fullbrightignoresectorcolor, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -//CVAR(Bool, r_splitsprites, true, CVAR_ARCHIVE) - -namespace swrenderer -{ - using namespace drawerargs; - -// [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 BASEXCENTER (160) -#define BASEYCENTER (100) - -// -// 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 - -// Used to store a psprite's drawing information if it needs to be drawn later. -struct vispsp_t -{ - vissprite_t *vis; - FDynamicColormap *basecolormap; - int x1; -}; -TArray vispsprites; -unsigned int vispspindex; - -static int spriteshade; - -FTexture *WallSpriteTile; - -// constant arrays -// used for psprite clipping and initializing clipping -short zeroarray[MAXWIDTH]; -short screenheightarray[MAXWIDTH]; - -// -// 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 (DMSG_NOTIFY, "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 (FTexture *tex, fixed_t col, bool useRt, bool unmasked) -{ - const FTexture::Span *span; - const BYTE *column; - - 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; - } - - 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_dest = (ylookup[dc_yl] + dc_x) + dc_destorg; - 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)); - - if (useRt) - hcolfunc_pre(); - else - colfunc (); - } - span++; - } - - if (sprflipvert && useRt) - rt_flip_posts(); -} - -// [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) -{ - 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 || fabs(vis->yscale) < (1.0f / 32000.0f)) - { // scaled to 0; can't see - return; - } - - fixed_t centeryfrac = FLOAT2FIXED(CenterY); - R_SetColorMapLight(vis->colormap, 0.0f, 0); - - mode = R_SetPatchStyle (vis->RenderStyle, vis->Style.Alpha, vis->Translation, vis->FillColor); - - if (vis->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. - R_SetColorMapLight(dc_colormap, 0, vis->ColormapNum << FRACBITS); - } - - 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)) - { - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumn (tex, frac, false); - dc_x++; - frac += xiscale; - } - - while (dc_x < stop4) - { - rt_initcols(nullptr); - for (int zz = 4; zz; --zz) - { - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumn (tex, frac, true); - dc_x++; - frac += xiscale; - } - rt_draw4cols (dc_x - 4); - } - - while (dc_x < x2) - { - if (ispsprite || !R_ClipSpriteColumnWithPortals(vis)) - R_DrawMaskedColumn (tex, frac, false); - 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->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) - R_SetColorMapLight(usecolormap, 0, FIXEDLIGHT2SHADE(fixedlightlev)); - else if (fixedcolormap != NULL) - R_SetColorMapLight(fixedcolormap, 0, 0); - else if (!foggy && (spr->renderflags & RF_FULLBRIGHT)) - R_SetColorMapLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : usecolormap, 0, 0); - 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->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 - R_SetColorMapLight(usecolormap, rw_light, shade); - } - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(false); - dc_x++; - } - - while (dc_x < stop4) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, shade); - } - rt_initcols(nullptr); - for (int zz = 4; zz; --zz) - { - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(true); - dc_x++; - } - rt_draw4cols(dc_x - 4); - } - - while (dc_x < x2) - { - if (calclighting) - { // calculate lighting - R_SetColorMapLight(usecolormap, rw_light, shade); - } - if (!R_ClipSpriteColumnWithPortals(spr)) - R_WallSpriteColumn(false); - dc_x++; - } - } - R_FinishSetPatchStyle(); -} - -void R_WallSpriteColumn (bool useRt) -{ - 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; - - dc_texturefrac = 0; - R_DrawMaskedColumn(WallSpriteTile, lwall[dc_x], useRt); - 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. - R_SetColorMapLight(spr->colormap, 0.0f, 0); - mode = R_SetPatchStyle(spr->RenderStyle, spr->Style.Alpha, spr->Translation, spr->FillColor); - - if (mode == DontDraw) - { - return; - } - if (colfunc == fuzzcolfunc || colfunc == R_FillColumn) - { - 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->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 - { - rt_span_coverage(x, span->Start, span->Stop - 1); - } - } - 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, sector_t *current_sector) -{ - 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() || - !thing->IsInsideVisibleAngles()) - { - 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]) - { - 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; - } - 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 - if ((unsigned)spritenum >= sprites.Size ()) - { - DPrintf (DMSG_ERROR, "R_ProjectSprite: invalid sprite number %u\n", spritenum); - return; - } - 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]) - { - 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; - } - 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) > fabs(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; - double dtx1 = tx * xscale; - x1 = centerx + xs_RoundToInt(dtx1); - - // 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 = (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 - 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; - - 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)); - } - 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 + 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->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->RenderStyle.Flags & STYLEF_InvertOverlay); - - if (vis->RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - FDynamicColormap *mybasecolormap = basecolormap; - if (current_sector->sectornum != thing->Sector->sectornum) // compare sectornums to account for R_FakeFlat copies. - { - // Todo: The actor is from a different sector so we have to retrieve the proper basecolormap for that sector. - } - - // Sprites that are added to the scene must fade to black. - if (vis->RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); - } - - if (vis->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->colormap = fixedcolormap; - } - else - { - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - vis->colormap = mybasecolormap->Maps + fixedlightlev; - } - else if (!foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) - { // full bright - vis->colormap = (r_fullbrightignoresectorcolor) ? FullNormalLight.Maps : mybasecolormap->Maps; - } - else - { // diminished light - vis->ColormapNum = GETPALOOKUP( - r_SpriteVisibility / MAX(tz, MINZ), spriteshade); - vis->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->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->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) -{ - 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->touching_renderthings == nullptr || sec->validcount == validcount) - return; - - // Well, now it will be done. - sec->validcount = validcount; - - spriteshade = LIGHT2SHADE(lightlevel + r_actualextralight); - - // Handle all things in sector. - for(auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext) - { - auto thing = p->m_thing; - if (thing->validcount == validcount) continue; - thing->validcount = validcount; - - 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 : 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; - } - } - R_ProjectSprite (thing, fakeside, fakefloor, fakeceiling, sec); - fakeceiling = NULL; - fakefloor = NULL; - } -} - -// -// R_DrawPSprite -// -void R_DrawPSprite(DPSprite *pspr, AActor *owner, float bobx, float boby, double wx, double wy, double ticfrac) -{ - double tx; - int x1; - int x2; - double sx, sy; - spritedef_t* sprdef; - spriteframe_t* sprframe; - FTextureID picnum; - WORD flip; - FTexture* tex; - vissprite_t* vis; - bool noaccel; - static TArray avis; - - if (avis.Size() < vispspindex + 1) - avis.Reserve(avis.Size() - vispspindex + 1); - - // 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) - 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; - } - - // calculate edges of the shape - tx = sx - BASEXCENTER; - - 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 = &avis[vispspindex]; - memset(vis, 0, sizeof(*vis)); - vis->renderflags = owner->renderflags; - vis->floorclip = 0; - vis->sector = nullptr; - - vis->texturemid = (BASEYCENTER - sy) * tex->Scale.Y + tex->TopOffset; - - if (camera->player && (RenderTarget != screen || - viewheight == RenderTarget->GetHeight() || - (RenderTarget->GetWidth() > (BASEXCENTER * 2) && !st_scale))) - { // Adjust PSprite for fullscreen views - AWeapon *weapon = dyn_cast(pspr->GetCaller()); - if (weapon != nullptr && weapon->YAdjust != 0) - { - if (RenderTarget != screen || viewheight == 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->Translation = 0; // [RH] Use default colors - vis->pic = tex; - vis->ColormapNum = 0; - - 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) - { - vis->Style.Alpha = float(owner->Alpha); - vis->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->RenderStyle.Flags & STYLEF_InvertOverlay); - - if (vis->RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - FDynamicColormap *mybasecolormap = basecolormap; - - if (vis->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 != nullptr) - { // fixed color - vis->colormap = realfixedcolormap->Colormap; - } - else - { - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - vis->colormap = (r_fullbrightignoresectorcolor) ? (FullNormalLight.Maps + fixedlightlev) : (mybasecolormap->Maps + fixedlightlev); - } - else if (!foggy && pspr->GetState()->GetFullbright()) - { // full bright - vis->colormap = (r_fullbrightignoresectorcolor) ? FullNormalLight.Maps : mybasecolormap->Maps; // [RH] use basecolormap - } - else - { // local light - vis->colormap = mybasecolormap->Maps + (GETPALOOKUP(0, spriteshade) << COLORMAPSHIFT); - } - } - if (camera->Inventory != nullptr) - { - vis->Style.RenderStyle = STYLE_Count; - vis->Style.Invert = false; - camera->AlterWeaponSprite(&vis->Style); - if (vis->Style.Invert) - { - vis->colormap = SpecialColormaps[0].Colormap; - - // Has the basecolormap changed? If so, we can't hardware accelerate it, - // since we don't know what it is anymore. - if (vis->colormap < mybasecolormap->Maps || - vis->colormap >= mybasecolormap->Maps + NUMCOLORMAPS * 256) - { - noaccel = true; - } - } - if (vis->Style.RenderStyle != STYLE_Count) vis->RenderStyle = vis->Style.RenderStyle; - } - // If we're drawing with a special colormap, but shaders for them are disabled, do - // not accelerate. - if (!r_shadercolormaps && (vis->colormap >= SpecialColormaps[0].Colormap && - vis->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 == nullptr && - NormalLightHasFixedLights && mybasecolormap == &NormalLight && - vis->pic->UseBasePalette()) - { - noaccel = true; - } - // [SP] If emulating GZDoom fullbright, disable acceleration - if (r_fullbrightignoresectorcolor && fixedlightlev >= 0) - mybasecolormap = &FullNormalLight; - if (r_fullbrightignoresectorcolor && !foggy && pspr->GetState()->GetFullbright()) - mybasecolormap = &FullNormalLight; - colormap_to_use = mybasecolormap; - } - else - { - colormap_to_use = basecolormap; - vis->colormap = basecolormap->Maps; - vis->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->RenderStyle; - style.CheckFuzz(); - if (style.BlendOp != STYLEOP_Fuzz) - { - if (vispsprites.Size() < vispspindex + 1) - vispsprites.Reserve(vispsprites.Size() - vispspindex + 1); - - vispsprites[vispspindex].vis = vis; - vispsprites[vispspindex].basecolormap = colormap_to_use; - vispsprites[vispspindex].x1 = x1; - vispspindex++; - return; - } - } - R_DrawVisSprite(vis); -} - -//========================================================================== -// -// R_DrawPlayerSprites -// -//========================================================================== - -void R_DrawPlayerSprites () -{ - int i; - int lightnum; - DPSprite* psp; - DPSprite* weapon; - 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; - double wx, wy; - float bobx, boby; - - 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) - { - R_DrawPSprite(psp, camera, bobx, boby, wx, wy, r_TicFracF); - } - - psp = psp->GetNext(); - } - - CenterY = centerhack; - } -} - -//========================================================================== -// -// R_DrawRemainingPlayerSprites -// -// Called from D_Display to draw sprites that were not drawn by -// R_DrawPlayerSprites(). -// -//========================================================================== - -void R_DrawRemainingPlayerSprites() -{ - for (unsigned int i = 0; i < vispspindex; i++) - { - vissprite_t *vis; - - vis = vispsprites[i].vis; - FDynamicColormap *colormap = vispsprites[i].basecolormap; - bool flip = vis->xiscale < 0; - FSpecialColormap *special = NULL; - PalEntry overlay = 0; - FColormapStyle colormapstyle; - bool usecolormapstyle = false; - - if (vis->colormap >= SpecialColormaps[0].Colormap && - vis->colormap < SpecialColormaps[SpecialColormaps.Size()].Colormap) - { - // Yuck! There needs to be a better way to store colormaps in the vissprite... :( - ptrdiff_t specialmap = (vis->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->colormap - colormap->Maps) >> 8) * 255 / NUMCOLORMAPS); - } - else - { - usecolormapstyle = true; - colormapstyle.Color = colormap->Color; - colormapstyle.Fade = colormap->Fade; - colormapstyle.Desaturate = colormap->Desaturate; - colormapstyle.FadeLevel = ((vis->colormap - colormap->Maps) >> 8) / float(NUMCOLORMAPS); - } - screen->DrawTexture(vis->pic, - viewwindowx + vispsprites[i].x1, - 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_TranslationIndex, 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_Alpha, vis->Style.Alpha, - DTA_RenderStyle, vis->RenderStyle, - DTA_FillColor, vis->FillColor, - DTA_SpecialColormap, special, - DTA_ColorOverlay, overlay.d, - DTA_ColormapStyle, usecolormapstyle ? &colormapstyle : NULL, - TAG_DONE); - } - - vispspindex = 0; -} - -// -// 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; -} - -// 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->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->RenderStyle.Flags & STYLEF_InvertOverlay); - - if (spr->RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - // Sprites that are added to the scene must fade to black. - if (spr->RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); - } - - if (spr->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->colormap = mybasecolormap->Maps + fixedlightlev; - } - else if (!foggy && (spr->renderflags & RF_FULLBRIGHT)) - { // full bright - spr->colormap = (r_fullbrightignoresectorcolor) ? FullNormalLight.Maps : mybasecolormap->Maps; - } - else - { // diminished light - spriteshade = LIGHT2SHADE(sec->lightlevel + r_actualextralight); - spr->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_MAX, 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 = short(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 = short(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 = short(MIN(h, viewheight)); - } - } -#endif - - if (topclip >= botclip) - { - spr->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->colormap = colormap; - 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); - R_DrawVisVoxel(spr, minvoxely, maxvoxely, cliptop, clipbot); - } - spr->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 = YaspectMul * xscale; - ty = 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 = short(particle->alpha * 255); - vis->FakeFlatStat = fakeside; - vis->floorclip = 0; - vis->ColormapNum = 0; - - if (fixedlightlev >= 0) - { - vis->colormap = map + fixedlightlev; - } - else if (fixedcolormap) - { - vis->colormap = fixedcolormap; - } - else if (particle->bright) - { - vis->colormap = (r_fullbrightignoresectorcolor) ? FullNormalLight.Maps : map; - } - else - { - // Particles are slightly more visible than regular sprites. - vis->ColormapNum = GETPALOOKUP(tiz * r_SpriteVisibility * 0.5, shade); - vis->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_C (vissprite_t *vis) -{ - DWORD *bg2rgb; - int spacing; - BYTE *dest; - DWORD fg; - BYTE color = vis->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); - - DrawerCommandQueue::WaitForWorkers(); - - // 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;; - -inline int sgn(int v) -{ - return v < 0 ? -1 : v > 0 ? 1 : 0; -} - -void R_DrawVoxel(const FVector3 &globalpos, FAngle viewangle, - const FVector3 &dasprpos, DAngle 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); - 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; - - 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 = 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, 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 07676dc44..000000000 --- a/src/r_things.h +++ /dev/null @@ -1,157 +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" - -struct particle_t; -struct FVoxel; - -namespace swrenderer -{ - -// 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 - { - struct - { - float gzb, gzt; // global bottom / top for silhouette clipping - }; - struct - { - int y1, y2; // top / bottom of particle on screen - }; - }; - DAngle 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 - lighttable_t *colormap; - FRenderStyle RenderStyle; - visstyle_t Style; - int CurrentPortalUniq; // [ZZ] to identify the portal that this thing is in. used for clipping. - - vissprite_t() {} -}; - -void R_DrawParticle_C (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 (FTexture *texture, fixed_t column, bool useRt, bool unmasked = false); -void R_WallSpriteColumn (bool useRt); - -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_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, DAngle 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 a7a3860bb..046cd9ab5 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -101,8 +101,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; @@ -202,8 +200,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; @@ -225,11 +221,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); @@ -259,7 +254,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); } //========================================================================== @@ -899,22 +893,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; @@ -939,7 +930,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/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..94b1cfc4b --- /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 = LIGHT2SHADE(mLineSegment->sidedef->GetLightLevel(foggy, mLineSegment->frontsector->lightlevel) + R_ActualExtraLight(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 = LIGHT2SHADE(mLineSegment->sidedef->GetLightLevel(foggy, mFrontSector->lightlevel) + R_ActualExtraLight(foggy)); + double GlobVis = LightVisibility::Instance()->WallGlobVis(); + 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, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + 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, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + 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, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + 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..bb7823beb --- /dev/null +++ b/src/swrenderer/line/r_renderdrawsegment.cpp @@ -0,0 +1,953 @@ +// +// 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/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; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + R_ActualExtraLight(ds->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, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + columndrawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + } + 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, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + 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; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + R_ActualExtraLight(ds->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; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + R_ActualExtraLight(ds->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; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + R_ActualExtraLight(ds->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; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + R_ActualExtraLight(ds->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..5f4cd8485 --- /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 = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + R_ActualExtraLight(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..28961baad --- /dev/null +++ b/src/swrenderer/plane/r_flatplane.cpp @@ -0,0 +1,285 @@ +// +// 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_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; + GlobVis = LightVisibility::Instance()->FlatPlaneGlobVis() / planeheight; + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + { + drawerargs.SetLight(basecolormap, 0, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + plane_shade = false; + } + else if (cameraLight->FixedColormap()) + { + drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); + plane_shade = false; + } + else + { + plane_shade = true; + planeshade = LIGHT2SHADE(pl->lightlevel); + } + + 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..6be3b1a7b --- /dev/null +++ b/src/swrenderer/plane/r_slopeplane.cpp @@ -0,0 +1,197 @@ +// +// 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_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]; + } + + planelightfloat = (LightVisibility::Instance()->SlopePlaneGlobVis() * lxscale * lyscale) / (fabs(pl->height.ZatPoint(ViewPos) - ViewPos.Z)) / 65536.f; + + if (pl->height.fC() > 0) + planelightfloat = -planelightfloat; + + basecolormap = colormap; + + CameraLight *cameraLight = CameraLight::Instance(); + if (cameraLight->FixedLightLevel() >= 0) + { + drawerargs.SetLight(basecolormap, 0, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + 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 = LIGHT2SHADE(pl->lightlevel); + } + + // 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..c728dd315 --- /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 = 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) + { // 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..5be847bf8 --- /dev/null +++ b/src/swrenderer/scene/r_light.cpp @@ -0,0 +1,213 @@ +// +// 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" + +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); + } + + // 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..fd919fba1 --- /dev/null +++ b/src/swrenderer/scene/r_light.h @@ -0,0 +1,116 @@ +// +// 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 + +// 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)) + +// 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)) + +// Converts fixedlightlev into a shade value +#define FIXEDLIGHT2SHADE(lightlev) (((lightlev) >> COLORMAPSHIFT) << FRACBITS) + +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; } + + 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() const { return WallVisibility; } + double SpriteGlobVis() const { return WallVisibility; } + double ParticleGlobVis() const { return WallVisibility * 0.5; } + double FlatPlaneGlobVis() const { return FloorVisibility; } + double SlopePlaneGlobVis() const { return TiltVisibility; } + + // The vis value to pass into the GETPALOOKUP or LIGHTSCALE macros + double WallVis(double screenZ) const { return WallGlobVis() / screenZ; } + double SpriteVis(double screenZ) const { return SpriteGlobVis() / screenZ; } + double ParticleVis(double screenZ) const { return ParticleGlobVis() / screenZ; } + double FlatPlaneVis(int screenY, double planeZ) const { return FlatPlaneGlobVis() / fabs(planeZ - ViewPos.Z) * fabs(RenderViewport::Instance()->CenterY - screenY); } + + private: + double BaseVisibility = 0.0; + double WallVisibility = 0.0; + double FloorVisibility = 0.0; + float TiltVisibility = 0.0f; + + 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); + }; + + inline int R_ActualExtraLight(bool fog) { return fog ? 0 : extralight << 4; } +} diff --git a/src/swrenderer/scene/r_opaque_pass.cpp b/src/swrenderer/scene/r_opaque_pass.cpp new file mode 100644 index 000000000..ac8505dd2 --- /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 + R_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 + R_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 + R_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 + R_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 = LIGHT2SHADE((floorlightlevel + ceilinglightlevel) / 2 + R_ActualExtraLight(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 = LIGHT2SHADE(lightlevel + R_ActualExtraLight(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 = LIGHT2SHADE(lightlevel + R_ActualExtraLight(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..47a89727b --- /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, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + 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..9212004e6 --- /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(), 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..929acab53 --- /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) + R_ActualExtraLight(foggy); + int spriteshade = LIGHT2SHADE(lightnum) - 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..2ebf1fd57 --- /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() / 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..1f402de81 --- /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 = LIGHT2SHADE(sec->lightlevel + R_ActualExtraLight(spr->foggy)); + + Light.SetColormap(LightVisibility::Instance()->SpriteGlobVis() / 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..33e889c3f --- /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() / 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..38a26aab1 --- /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() / 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 = LIGHT2SHADE(spr->sector->lightlevel + R_ActualExtraLight(spr->foggy)); + double GlobVis = LightVisibility::Instance()->WallGlobVis(); + 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, FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel())); + 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..118fd18b0 --- /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) FIXEDLIGHT2SHADE(cameraLight->FixedLightLevel()); + 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 a2c69b38b..e839c020a 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 aca00cfb3..2a804c392 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -66,12 +66,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) #if defined(_DEBUG) && defined(_M_IX86) && !defined(__MINGW32__) #define DBGBREAK { __asm int 3 } @@ -84,7 +87,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; @@ -120,7 +123,6 @@ public: const BYTE *GetColumn(unsigned int column, const Span **spans_out); const BYTE *GetPixels(); - void Unload(); bool CheckModified(); void SetTranslation(int num); @@ -211,13 +213,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; @@ -334,70 +337,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; @@ -425,8 +364,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; } //========================================================================== @@ -734,13 +673,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); } } @@ -752,8 +690,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); @@ -805,8 +743,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); } //========================================================================== @@ -875,8 +814,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; @@ -885,6 +824,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 @@ -944,10 +947,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 { @@ -1020,16 +1034,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 d0910f8b4..22b8c30b7 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 // Version identifier for network games. @@ -88,14 +88,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 789eacd98..e0f79f51d 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; } @@ -1323,20 +1325,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); } @@ -1368,14 +1395,17 @@ 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) { - auto &map = swrenderer::realfixedcolormap; + auto map = swrenderer::CameraLight::Instance()->ShaderColormap(); if (map == NULL) { color0 = 0; @@ -1383,9 +1413,12 @@ void D3DFB::Draw3DPart(bool copy3d) } else { - 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); - 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 @@ -1396,7 +1429,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]); } //========================================================================== diff --git a/src/win32/fb_ddraw.cpp b/src/win32/fb_ddraw.cpp index 85f0c4768..5b50f146c 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 787c0a4f3..70898ebe5 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,18 @@ FRenderer *gl_CreateInterface(); void I_RestartRenderer(); int currentrenderer = -1; +int currentcanvas = -1; bool changerenderer; +// 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 @@ -136,9 +146,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 +170,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 +215,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,6 +382,16 @@ void I_RestoreWindowedPos () 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|CVAR_NOINITCALL) { NewWidth = screen->GetWidth(); 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 0a32edc02..5a09a7af7 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" @@ -632,7 +632,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; @@ -653,7 +653,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; } @@ -670,12 +671,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 @@ -734,7 +736,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/src/win32/zdoom.rc b/src/win32/zdoom.rc index 0d21faae5..cede1e894 100644 --- a/src/win32/zdoom.rc +++ b/src/win32/zdoom.rc @@ -72,13 +72,13 @@ BEGIN " BEGIN\r\n" " VALUE ""Comments"", ""Thanks to id Software for creating DOOM and then releasing the source code. Thanks also to TeamTNT for creating BOOM, which ZDoom is partially based on. Includes code based on the Cajun Bot 0.97 by Martin Collberg.""\r\n" " VALUE ""CompanyName"", "" ""\r\n" - " VALUE ""FileDescription"", ""GZDoom""\r\n" + " VALUE ""FileDescription"", ""QZDoom""\r\n" " VALUE ""FileVersion"", RC_FILEVERSION2\r\n" - " VALUE ""InternalName"", ""GZDoom""\r\n" + " VALUE ""InternalName"", ""QZDoom""\r\n" " VALUE ""LegalCopyright"", ""Copyright \\u00A9 1993-1996 id Software, 1998-2010 Randy Heit, 2002-2010 Christoph Oelckers, et al.""\r\n" " VALUE ""LegalTrademarks"", ""DoomR is a Registered Trademark of id Software, Inc.""\r\n" - " VALUE ""OriginalFilename"", ""gzdoom.exe""\r\n" - " VALUE ""ProductName"", ""GZDoom""\r\n" + " VALUE ""OriginalFilename"", ""qzdoom.exe""\r\n" + " VALUE ""ProductName"", ""QZDoom""\r\n" " VALUE ""ProductVersion"", RC_PRODUCTVERSION2\r\n" " END\r\n" " END\r\n" @@ -228,7 +228,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US FONT 8, "MS Shell Dlg" { CONTROL 101, IDC_STATIC, STATIC, SS_ICON | WS_CHILD | WS_VISIBLE, 7, 7, 21, 20 - CONTROL "Welcome to GZDoom!", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 42, 8, 180, 8 + CONTROL "Welcome to QZDoom!", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 42, 8, 180, 8 CONTROL "", IDC_WELCOME_VERSION, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 42, 18, 180, 8 CONTROL "IWAD selection", IDC_STATIC, BUTTON, BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 8, 32, 208, 117 CONTROL "Select which game file (IWAD) to run.", IDC_STATIC, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 12, 44, 190, 8 @@ -242,7 +242,7 @@ FONT 8, "MS Shell Dlg" CONTROL "Load lights", IDC_WELCOME_LIGHTS, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 180, 51, 10 CONTROL "Load brightmaps", IDC_WELCOME_BRIGHTMAPS, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 190, 65, 10 CONTROL "Don't ask me this again", IDC_DONTASKIWAD, BUTTON, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 73, 211, 87, 10 - CONTROL "Play GZDoom", IDOK, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 8, 228, 90, 14 + CONTROL "Play QZDoom", IDOK, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 8, 228, 90, 14 CONTROL "Exit", IDCANCEL, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 166, 228, 50, 14 } @@ -504,13 +504,13 @@ BEGIN BEGIN VALUE "Comments", "Thanks to id Software for creating DOOM and then releasing the source code. Thanks also to TeamTNT for creating BOOM, which ZDoom is partially based on. Includes code based on the Cajun Bot 0.97 by Martin Collberg." VALUE "CompanyName", " " - VALUE "FileDescription", "GZDoom" + VALUE "FileDescription", "QZDoom" VALUE "FileVersion", RC_FILEVERSION2 - VALUE "InternalName", "GZDoom" + VALUE "InternalName", "QZDoom" VALUE "LegalCopyright", "Copyright \u00A9 1993-1996 id Software, 1998-2010 Randy Heit, 2002-2010 Christoph Oelckers, et al." VALUE "LegalTrademarks", "DoomR is a Registered Trademark of id Software, Inc." - VALUE "OriginalFilename", "gzdoom.exe" - VALUE "ProductName", "GZDoom" + VALUE "OriginalFilename", "qzdoom.exe" + VALUE "ProductName", "QZDoom" VALUE "ProductVersion", RC_PRODUCTVERSION2 END END 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 020d8477d..4750a2bdf 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -510,3 +510,28 @@ ABC4EB5A1535ECCD0061AD14F3547908 // Plutonia Experiment, map26 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 +} + diff --git a/wadsrc/static/language.enu b/wadsrc/static/language.enu index 64cf4034b..75ef5eb51 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"; @@ -2166,6 +2171,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 +2220,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 +2238,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"; @@ -2344,6 +2355,11 @@ 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"; + // Colors C_BRICK = "\cabrick"; C_TAN = "\cbtan"; @@ -2655,6 +2671,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"; @@ -2771,3 +2788,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 b66b381ba..ef2032241 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" @@ -678,11 +691,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 +722,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 +735,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 @@ -1750,6 +1777,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 99c23c7bf..d22eac800 100644 --- a/wadsrc/static/zscript/actor.txt +++ b/wadsrc/static/zscript/actor.txt @@ -521,6 +521,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; } @@ -880,6 +881,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/constants.txt b/wadsrc/static/zscript/constants.txt index c15433cd6..d0daae8ef 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/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index d2bd2d012..3ccc0e84b 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -176,12 +176,14 @@ class PSprite : Object native 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;