From d085175cbd2e3a5248d9654bb6af2f0a3e08855a Mon Sep 17 00:00:00 2001 From: Kp Date: Thu, 22 Dec 2016 05:21:16 +0000 Subject: [PATCH] Scrub invalid primary textures at level load Past releases, when rendering an invalid primary texture, would Int3() and then reset the texture to zero. Commit d767f7c changed the logic to return without resetting the texture, since the reset seemed to be unnecessary. Unfortunately, it is necessary. Some levels, including those shipped with the retail game data, specify bogus primary textures on some surfaces. After d767f7c, rendering a surface with an invalid primary texture causes the surface to be invisible, even if it has a valid secondary texture. Remove the return statement added in d767f7c. Extend validate_segment_side to validate the primary texture on the tested side. When an invalid texture is found, reset it and log a diagnostic. For built-in levels, log at level CON_VERBOSE since players cannot readily fix the level. For external levels, log at level CON_URGENT so that level authors know to fix their level before releasing it. Fixes: d767f7cd5ee99c5a4ce50c995045f0b7e422983a ("Pass vcsegptridx to render_face") --- common/include/dxxerror.h | 3 +- d1x-rebirth/CHANGELOG.txt | 2 +- similar/main/gameseg.cpp | 97 +++++++++++++++++++++++++++++---------- similar/main/render.cpp | 14 +++--- 4 files changed, 82 insertions(+), 34 deletions(-) diff --git a/common/include/dxxerror.h b/common/include/dxxerror.h index fbc01a051..4c301c618 100644 --- a/common/include/dxxerror.h +++ b/common/include/dxxerror.h @@ -75,7 +75,8 @@ void UserError(const char *fmt, ...) __noreturn __attribute_format_printf(1, 2); #define UserError(F,...) dxx_call_printf_checked(UserError,(UserError_puts),(),DXX_STRINGIZE_FL(__FILE__, __LINE__, F),##__VA_ARGS__) #define Assert assert -#define LevelError(F,...) con_printf(CON_URGENT, DXX_STRINGIZE_FL(__FILE__, __LINE__, F " Please report this to the level author, not to the Rebirth maintainers."), ##__VA_ARGS__) +#define LevelErrorV(V,F,...) con_printf(V, DXX_STRINGIZE_FL(__FILE__, __LINE__, F " Please report this to the level author, not to the Rebirth maintainers."), ##__VA_ARGS__) +#define LevelError(F,...) LevelErrorV(CON_URGENT, F, ##__VA_ARGS__) /* Compatibility with x86-specific name */ #define Int3() d_debugbreak() diff --git a/d1x-rebirth/CHANGELOG.txt b/d1x-rebirth/CHANGELOG.txt index 136f1647d..c13db2da5 100644 --- a/d1x-rebirth/CHANGELOG.txt +++ b/d1x-rebirth/CHANGELOG.txt @@ -2182,7 +2182,7 @@ main/songs.c: start at track 1 and continue playing audio CD if it's not the ori 20080615 -------- -main/gameseq.c, main/mission.c, main/newdemo.c, main/scores.c: Fixed scores write (wasn't PhysFS); Fixed PLAYING_BUILTING_MISSION in D1X; Better call for DoJasonInterpolate in demo code (hopefully) +main/gameseq.c, main/mission.c, main/newdemo.c, main/scores.c: Fixed scores write (wasn't PhysFS); Fixed PLAYING_BUILTIN_MISSION in D1X; Better call for DoJasonInterpolate in demo code (hopefully) arch/sdl/digi_mixer.c, arch/sdl/rbaudio.c, include/inferno.h, include/rbaudio.h, main/game.c, main/songs.c: tidy up music keys, ALT-SHIFT-F9 ejects audio CDs 20080612 diff --git a/similar/main/gameseg.cpp b/similar/main/gameseg.cpp index d8d20de58..29404b197 100644 --- a/similar/main/gameseg.cpp +++ b/similar/main/gameseg.cpp @@ -39,7 +39,7 @@ COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. #include "gameseq.h" #include "wall.h" #include "fuelcen.h" -#include "bm.h" +#include "textures.h" #include "fvi.h" #include "object.h" #include "byteutil.h" @@ -1230,22 +1230,17 @@ static int check_for_degenerate_segment(const vcsegptr_t sp) } -static void add_side_as_quad(const vsegptr_t sp, int sidenum, const vms_vector &normal) +static void add_side_as_quad(side *const sidep, const vms_vector &normal) { - side *sidep = &sp->sides[sidenum]; - sidep->set_type(SIDE_IS_QUAD); - sidep->normals[0] = normal; sidep->normals[1] = normal; - // If there is a connection here, we only formed the faces for the purpose of determining segment boundaries, // so don't generate polys, else they will get rendered. // if (sp->children[sidenum] != -1) // sidep->render_flag = 0; // else // sidep->render_flag = 1; - } } @@ -1401,7 +1396,7 @@ void create_walls_on_side(const vsegptridx_t sp, int sidenum) vm_vec_negate(vn); if (dist_to_plane <= PLANE_DIST_TOLERANCE) - add_side_as_quad(sp, sidenum, vn); + add_side_as_quad(&sp->sides[sidenum], vn); else { add_side_as_2_triangles(sp, sidenum); @@ -1438,28 +1433,82 @@ void create_walls_on_side(const vsegptridx_t sp, int sidenum) } - - -// ------------------------------------------------------------------------------- -static void validate_removable_wall(const vsegptridx_t sp, int sidenum, int tmap_num) -{ - create_walls_on_side(sp, sidenum); - - sp->sides[sidenum].tmap_num = tmap_num; - -// assign_default_uvs_to_side(sp, sidenum); -// assign_light_to_side(sp, sidenum); -} - // ------------------------------------------------------------------------------- // Make a just-modified segment side valid. void validate_segment_side(const vsegptridx_t sp, int sidenum) { - if (sp->sides[sidenum].wall_num == wall_none) + auto &side = sp->sides[sidenum]; + const auto old_tmap_num = side.tmap_num; create_walls_on_side(sp, sidenum); - else // create_removable_wall(sp, sidenum, sp->sides[sidenum].tmap_num); - validate_removable_wall(sp, sidenum, sp->sides[sidenum].tmap_num); + /* If the texture is correct, put it back. This is sometimes a + * wasted store, but never harmful. + * + * If the texture was wrong, fix it, and log a diagnostic. For + * builtin missions, log the diagnostic at level CON_VERBOSE, since + * retail levels trigger this during normal play. For external + * missions, log the diagnostic at level CON_URGENT. External + * levels might be fixable by contacting the author, but the retail + * levels can only be fixed by using a Rebirth level patch file (not + * supported yet). When fixing the texture, change it to 0 for + * walls and 1 for non-walls. This should make walls transparent + * for their primary texture; transparent non-walls usually generate + * ugly visual artifacts, so choose a non-zero texture for them. + * + * Known affected retail levels (incomplete list): + +Descent 2: Counterstrike +sha256: f1abf516512739c97b43e2e93611a2398fc9f8bc7a014095ebc2b6b2fd21b703 descent2.hog +Levels 1-3: clean + +Level #4 +segment #170 side #4 has invalid tmap 910 (NumTextures=910) +segment #171 side #5 has invalid tmap 910 (NumTextures=910) +segment #184 side #2 has invalid tmap 910 (NumTextures=910) +segment #188 side #5 has invalid tmap 910 (NumTextures=910) + +Level #5 +segment #141 side #4 has invalid tmap 910 (NumTextures=910) + +Level #6 +segment #128 side #4 has invalid tmap 910 (NumTextures=910) + +Level #7 +segment #26 side #5 has invalid tmap 910 (NumTextures=910) +segment #28 side #5 has invalid tmap 910 (NumTextures=910) +segment #60 side #5 has invalid tmap 910 (NumTextures=910) +segment #63 side #5 has invalid tmap 910 (NumTextures=910) +segment #161 side #4 has invalid tmap 910 (NumTextures=910) +segment #305 side #4 has invalid tmap 910 (NumTextures=910) +segment #427 side #4 has invalid tmap 910 (NumTextures=910) +segment #533 side #5 has invalid tmap 910 (NumTextures=910) +segment #536 side #4 has invalid tmap 910 (NumTextures=910) +segment #647 side #4 has invalid tmap 910 (NumTextures=910) +segment #648 side #5 has invalid tmap 910 (NumTextures=910) + +Level #8 +segment #0 side #4 has invalid tmap 910 (NumTextures=910) +segment #92 side #0 has invalid tmap 910 (NumTextures=910) +segment #92 side #5 has invalid tmap 910 (NumTextures=910) +segment #94 side #1 has invalid tmap 910 (NumTextures=910) +segment #94 side #2 has invalid tmap 910 (NumTextures=910) +segment #95 side #0 has invalid tmap 910 (NumTextures=910) +segment #95 side #1 has invalid tmap 910 (NumTextures=910) +segment #97 side #5 has invalid tmap 910 (NumTextures=910) +segment #98 side #3 has invalid tmap 910 (NumTextures=910) +segment #100 side #1 has invalid tmap 910 (NumTextures=910) +segment #102 side #1 has invalid tmap 910 (NumTextures=910) +segment #104 side #3 has invalid tmap 910 (NumTextures=910) + +Levels 9-end: unchecked + + */ + side.tmap_num = old_tmap_num < NumTextures + ? old_tmap_num + : ( + LevelErrorV(PLAYING_BUILTIN_MISSION ? CON_VERBOSE : CON_URGENT, "segment #%hu side #%i has invalid tmap %u (NumTextures=%u).", static_cast(sp), sidenum, old_tmap_num, NumTextures), + (side.wall_num == wall_none) + ); // Set render_flag. // If side doesn't have a child, then render wall. If it does have a child, but there is a temporary diff --git a/similar/main/render.cpp b/similar/main/render.cpp index 3b98a2ee2..7cdcb4e26 100644 --- a/similar/main/render.cpp +++ b/similar/main/render.cpp @@ -220,7 +220,7 @@ static inline int is_alphablend_eclip(int eclip_num) // vp is a pointer to vertex ids. // tmap1, tmap2 are texture map ids. tmap2 is the pasty one. namespace dsx { -static void render_face(const vcsegptridx_t segp, int sidenum, unsigned nv, const array &vp, int tmap1, int tmap2, array uvl_copy, WALL_IS_DOORWAY_result_t wid_flags) +static void render_face(const segment &segp, const unsigned sidenum, const unsigned nv, const array &vp, const int tmap1, const int tmap2, array uvl_copy, const WALL_IS_DOORWAY_result_t wid_flags) { grs_bitmap *bm; @@ -241,8 +241,7 @@ static void render_face(const vcsegptridx_t segp, int sidenum, unsigned nv, cons #elif defined(DXX_BUILD_DESCENT_II) //handle cloaked walls if (wid_flags & WID_CLOAKED_FLAG) { - auto wall_num = segp->sides[sidenum].wall_num; - Assert(wall_num != wall_none); + const auto wall_num = segp.sides[sidenum].wall_num; gr_settransblend(vcwallptr(wall_num)->cloak_value, GR_BLEND_NORMAL); const uint8_t color = BM_XRGB(0, 0, 0); // set to black (matters for s3) @@ -256,7 +255,6 @@ static void render_face(const vcsegptridx_t segp, int sidenum, unsigned nv, cons if (tmap1 >= NumTextures) { Int3(); - return; } #if DXX_USE_OGL @@ -370,7 +368,7 @@ static void render_face(const vcsegptridx_t segp, int sidenum, unsigned nv, cons // ---------------------------------------------------------------------------- // Only called if editor active. // Used to determine which face was clicked on. -static void check_face(segnum_t segnum, int sidenum, int facenum, unsigned nv, const array &vp, int tmap1, int tmap2, const array &uvl_copy) +static void check_face(const vsegidx_t segnum, const unsigned sidenum, const unsigned facenum, const unsigned nv, const array &vp, const int tmap1, const int tmap2, const array &uvl_copy) { #if DXX_USE_EDITOR if (_search_mode) { @@ -435,7 +433,7 @@ static void check_face(segnum_t segnum, int sidenum, int facenum, unsigned nv, c } template -static inline void check_render_face(index_sequence, const vcsegptridx_t segnum, int sidenum, unsigned facenum, const array &ovp, int tmap1, int tmap2, const array &uvlp, WALL_IS_DOORWAY_result_t wid_flags, const std::size_t nv) +static inline void check_render_face(index_sequence, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const array &ovp, const int tmap1, const int tmap2, const array &uvlp, const WALL_IS_DOORWAY_result_t wid_flags, const std::size_t nv) { const array vp{{ovp[N]...}}; const array uvl_copy{{ @@ -446,7 +444,7 @@ static inline void check_render_face(index_sequence, const vcsegptridx_t s } template -static inline void check_render_face(index_sequence is, const vcsegptridx_t segnum, int sidenum, unsigned facenum, const array &vp, int tmap1, int tmap2, const array &uvlp, WALL_IS_DOORWAY_result_t wid_flags) +static inline void check_render_face(index_sequence is, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const array &vp, const int tmap1, const int tmap2, const array &uvlp, const WALL_IS_DOORWAY_result_t wid_flags) { check_render_face(is, segnum, sidenum, facenum, vp, tmap1, tmap2, uvlp, wid_flags, 4); } @@ -455,7 +453,7 @@ static inline void check_render_face(index_sequence is, const vc * are default constructed, gcc zero initializes all members. */ template -static inline void check_render_face(index_sequence, const vcsegptridx_t segnum, int sidenum, unsigned facenum, const array &vp, int tmap1, int tmap2, const array &uvlp, WALL_IS_DOORWAY_result_t wid_flags) +static inline void check_render_face(index_sequence, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const array &vp, const int tmap1, const int tmap2, const array &uvlp, const WALL_IS_DOORWAY_result_t wid_flags) { check_render_face(index_sequence(), segnum, sidenum, facenum, vp, tmap1, tmap2, uvlp, wid_flags, 3); }