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: d767f7cd5e ("Pass vcsegptridx to render_face")
This commit is contained in:
Kp 2016-12-22 05:21:16 +00:00
parent 29b9c255fa
commit d085175cbd
4 changed files with 82 additions and 34 deletions

View File

@ -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()

View File

@ -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

View File

@ -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<segnum_t>(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

View File

@ -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<int, 4> &vp, int tmap1, int tmap2, array<g3s_uvl, 4> uvl_copy, WALL_IS_DOORWAY_result_t wid_flags)
static void render_face(const segment &segp, const unsigned sidenum, const unsigned nv, const array<int, 4> &vp, const int tmap1, const int tmap2, array<g3s_uvl, 4> 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<int, 4> &vp, int tmap1, int tmap2, const array<g3s_uvl, 4> &uvl_copy)
static void check_face(const vsegidx_t segnum, const unsigned sidenum, const unsigned facenum, const unsigned nv, const array<int, 4> &vp, const int tmap1, const int tmap2, const array<g3s_uvl, 4> &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 <std::size_t... N>
static inline void check_render_face(index_sequence<N...>, const vcsegptridx_t segnum, int sidenum, unsigned facenum, const array<int, 4> &ovp, int tmap1, int tmap2, const array<uvl, 4> &uvlp, WALL_IS_DOORWAY_result_t wid_flags, const std::size_t nv)
static inline void check_render_face(index_sequence<N...>, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const array<int, 4> &ovp, const int tmap1, const int tmap2, const array<uvl, 4> &uvlp, const WALL_IS_DOORWAY_result_t wid_flags, const std::size_t nv)
{
const array<int, 4> vp{{ovp[N]...}};
const array<g3s_uvl, 4> uvl_copy{{
@ -446,7 +444,7 @@ static inline void check_render_face(index_sequence<N...>, const vcsegptridx_t s
}
template <std::size_t N0, std::size_t N1, std::size_t N2, std::size_t N3>
static inline void check_render_face(index_sequence<N0, N1, N2, N3> is, const vcsegptridx_t segnum, int sidenum, unsigned facenum, const array<int, 4> &vp, int tmap1, int tmap2, const array<uvl, 4> &uvlp, WALL_IS_DOORWAY_result_t wid_flags)
static inline void check_render_face(index_sequence<N0, N1, N2, N3> is, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const array<int, 4> &vp, const int tmap1, const int tmap2, const array<uvl, 4> &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<N0, N1, N2, N3> is, const vc
* are default constructed, gcc zero initializes all members.
*/
template <std::size_t N0, std::size_t N1, std::size_t N2>
static inline void check_render_face(index_sequence<N0, N1, N2>, const vcsegptridx_t segnum, int sidenum, unsigned facenum, const array<int, 4> &vp, int tmap1, int tmap2, const array<uvl, 4> &uvlp, WALL_IS_DOORWAY_result_t wid_flags)
static inline void check_render_face(index_sequence<N0, N1, N2>, const vcsegptridx_t segnum, const unsigned sidenum, const unsigned facenum, const array<int, 4> &vp, const int tmap1, const int tmap2, const array<uvl, 4> &uvlp, const WALL_IS_DOORWAY_result_t wid_flags)
{
check_render_face(index_sequence<N0, N1, N2, 3>(), segnum, sidenum, facenum, vp, tmap1, tmap2, uvlp, wid_flags, 3);
}