Merge branch 'newrenderer'

This commit is contained in:
Christoph Oelckers 2021-05-06 17:04:35 +02:00
commit 2f9e32f748
336 changed files with 30036 additions and 14899 deletions

View file

@ -638,6 +638,9 @@ file( GLOB HEADER_FILES
core/music/*.h
core/menu/*.h
core/input/*.h
core/rendering/*.h
core/rendering/scene/*.h
core/nodebuilder/*.h
common/audio/sound/thirdparty/*.h
common/audio/sound/*.h
@ -746,7 +749,6 @@ set( NOT_COMPILED_SOURCE_FILES
games/blood/src/callback.cpp
games/blood/src/choke.cpp
games/blood/src/controls.cpp
games/blood/src/credits.cpp
games/blood/src/db.cpp
games/blood/src/dude.cpp
games/blood/src/d_menu.cpp
@ -832,7 +834,6 @@ set( NOT_COMPILED_SOURCE_FILES
games/duke/src/spawn_r.cpp
# Shadow Warrior
games/sw/src/2d.cpp
games/sw/src/actor.cpp
games/sw/src/ai.cpp
games/sw/src/break.cpp
@ -1019,19 +1020,15 @@ set (FASTMATH_SOURCES ${FASTMATH_SOURCES})
set (PCH_SOURCES
glbackend/glbackend.cpp
glbackend/gl_palmanager.cpp
glbackend/gl_texture.cpp
glbackend/gl_models.cpp
thirdparty/src/md4.cpp
# Todo: Split out the license-safe code from this.
build/src/clip.cpp
build/src/defs.cpp
build/src/engine.cpp
build/src/mdsprite.cpp
build/src/polymost.cpp
build/src/voxmodel.cpp
core/movie/playmve.cpp
core/movie/movieplayer.cpp
@ -1050,6 +1047,7 @@ set (PCH_SOURCES
core/gamehud.cpp
core/gamefuncs.cpp
core/gameinput.cpp
core/g_mapinfo.cpp
core/interpolate.cpp
core/inputstate.cpp
core/maphack.cpp
@ -1064,12 +1062,38 @@ set (PCH_SOURCES
core/precache.cpp
core/quotes.cpp
core/screenshot.cpp
core/sectorgeometry.cpp
core/raze_music.cpp
core/raze_sound.cpp
core/palette.cpp
core/zcompile.cpp
core/statusbar2.cpp
core/gi.cpp
core/defparser.cpp
core/nodebuilder/nodebuild.cpp
core/nodebuilder/nodebuild_classify_nosse2.cpp
core/nodebuilder/nodebuild_events.cpp
core/nodebuilder/nodebuild_extract.cpp
core/nodebuilder/nodebuild_gl.cpp
core/nodebuilder/nodebuild_utility.cpp
core/rendering/hw_entrypoint.cpp
core/rendering/hw_models.cpp
core/rendering/hw_voxels.cpp
core/rendering/hw_palmanager.cpp
core/rendering/hw_sections.cpp
core/rendering/scene/hw_clipper.cpp
core/rendering/scene/hw_walls.cpp
core/rendering/scene/hw_flats.cpp
core/rendering/scene/hw_sprites.cpp
core/rendering/scene/hw_drawlistadd.cpp
core/rendering/scene/hw_drawlist.cpp
core/rendering/scene/hw_drawinfo.cpp
core/rendering/scene/hw_bunchdrawer.cpp
core/rendering/scene/hw_portal.cpp
core/rendering/scene/hw_skyportal.cpp
core/rendering/scene/hw_sky.cpp
core/console/c_notifybuffer.cpp
core/console/d_event.cpp
@ -1274,6 +1298,7 @@ set (PCH_SOURCES
games/exhumed/all.cpp
games/blood/all.cpp
games/sw/all.cpp
)
if( ${HAVE_VM_JIT} )
@ -1363,6 +1388,8 @@ include_directories(
core/dobject
core/menu
core/input
core/rendering
core/rendering/scene
platform
common/audio/sound
common/audio/music
@ -1513,6 +1540,8 @@ source_group("Core\\2D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/2d
source_group("Core\\Console" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/console/.+")
source_group("Core\\DObject" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/dobject/.+")
source_group("Core\\Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/menu/.+")
source_group("Core\\Rendering" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/rendering/.+")
source_group("Core\\Rendering\\Scene" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/rendering/scene/.+")
source_group("Rendering" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/glbackend/.+")
source_group("Platform" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/.+")
source_group("Platform\\Win32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/win32/.+")

View file

@ -15,12 +15,10 @@
static_assert('\xff' == 255, "Char must be unsigned!");
#if !defined __cplusplus || (__cplusplus < 201103L && !defined _MSC_VER)
# error C++11 or greater is required.
#endif
#include "compat.h"
#include "printf.h"
#include "palette.h"
#include "binaryangle.h"
//Make all variables in BUILD.H defined in the ENGINE,
//and externed in GAME
@ -35,7 +33,6 @@ EXTERN int16_t sintable[2048];
#include "buildtiles.h"
#include "c_cvars.h"
#include "cmdlib.h"
#include "binaryangle.h"
#include "mathutil.h"
typedef int64_t coord_t;
@ -48,11 +45,6 @@ enum
MAXVOXMIPS = 5,
MAXXDIM = 7680,
MAXYDIM = 3200,
MINXDIM = 640,
MINYDIM = 480,
MAXWALLSB = ((MAXWALLS >> 2) + (MAXWALLS >> 3)),
MAXVOXELS = 1024,
@ -100,24 +92,11 @@ enum {
};
enum {
SPR_XFLIP = 4,
SPR_YFLIP = 8,
SPR_WALL = 16,
SPR_FLOOR = 32,
SPR_ALIGN_MASK = 32+16,
};
#include "buildtypes.h"
using usectortype = sectortype;
using uwalltype = walltype;
using uspritetype = spritetype;
using uspriteptr_t = uspritetype const *;
using uwallptr_t = uwalltype const *;
using usectorptr_t = usectortype const *;
using uspriteptr_t = spritetype const *;
using uwallptr_t = walltype const *;
using usectorptr_t = sectortype const *;
using tspriteptr_t = tspritetype *;
@ -150,21 +129,14 @@ typedef struct {
#define SPREXT_TSPRACCESS 16
#define SPREXT_TEMPINVISIBLE 32
#define NEG_ALPHA_TO_BLEND(alpha, blend, orientation) do { \
if ((alpha) < 0) { (blend) = -(alpha); (alpha) = 0; (orientation) |= RS_TRANS1; } \
} while (0)
// using the clipdist field
enum
{
TSPR_FLAGS_MDHACK = 1u<<0u,
TSPR_FLAGS_DRAW_LAST = 1u<<1u,
TSPR_FLAGS_NO_SHADOW = 1u<<2u,
TSPR_FLAGS_INVISIBLE_WITH_SHADOW = 1u<<3u,
};
EXTERN int32_t guniqhudid;
EXTERN int32_t spritesortcnt;
struct usermaphack_t
{
@ -176,55 +148,35 @@ struct usermaphack_t
EXTERN spriteext_t *spriteext;
EXTERN spritesmooth_t *spritesmooth;
// Wrapper that makes an array of pointers look like an array of references. (Refactoring helper.)
template<class T, int size>
class ReferenceArray
{
T* data[size];
public:
T& operator[](size_t index)
{
assert(index < size);
return *data[index];
}
void set(int pos, T* spr)
{
data[pos] = spr;
}
};
EXTERN sectortype *sector;
EXTERN walltype *wall;
EXTERN spritetype *sprite;
EXTERN tspriteptr_t tsprite;
EXTERN int leveltimer;
extern sectortype sectorbackup[MAXSECTORS];
extern walltype wallbackup[MAXWALLS];
static inline tspriteptr_t renderMakeTSpriteFromSprite(tspriteptr_t const tspr, uint16_t const spritenum)
inline tspriteptr_t renderAddTSpriteFromSprite(spritetype* tsprite, int& spritesortcnt, uint16_t const spritenum)
{
auto tspr = &tsprite[spritesortcnt++];
auto const spr = &sprite[spritenum];
*tspr = *spr;
tspr->clipdist = 0;
tspr->owner = spritenum;
return tspr;
}
static inline tspriteptr_t renderAddTSpriteFromSprite(uint16_t const spritenum)
// returns: 0=continue sprite collecting;
// 1=break out of sprite collecting;
inline int32_t renderAddTsprite(spritetype* tsprite, int& spritesortcnt, int16_t z, int16_t sectnum)
{
return renderMakeTSpriteFromSprite(&tsprite[spritesortcnt++], spritenum);
if (spritesortcnt >= MAXSPRITESONSCREEN) return 1;
renderAddTSpriteFromSprite(tsprite, spritesortcnt, z);
return 0;
}
EXTERN int16_t maskwall[MAXWALLSB], maskwallcnt;
EXTERN int16_t thewall[MAXWALLSB];
EXTERN tspriteptr_t tspriteptr[MAXSPRITESONSCREEN + 1];
EXTERN int32_t xdim, ydim;
EXTERN int32_t yxaspect, viewingrange;
@ -235,30 +187,15 @@ EXTERN int32_t display_mirror;
EXTERN int32_t randomseed;
EXTERN int16_t numshades;
EXTERN uint8_t paletteloaded;
// Return type is int because this gets passed to variadic functions where structs may produce undefined behavior.
inline int shadeToLight(int shade)
{
shade = clamp(shade, 0, numshades-1);
int light = Scale(numshades-1-shade, 255, numshades-1);
return PalEntry(255,light,light,light);
}
EXTERN int32_t maxspritesonscreen;
enum {
PALETTE_MAIN = 1<<0,
PALETTE_SHADE = 1<<1,
PALETTE_TRANSLUC = 1<<2,
};
EXTERN int32_t g_visibility, parallaxvisibility;
// blendtable[1] to blendtable[numalphatabs] are considered to be
// alpha-blending tables:
EXTERN uint8_t numalphatabs;
EXTERN int32_t g_visibility;
EXTERN vec2_t windowxy1, windowxy2;
@ -271,13 +208,13 @@ typedef struct {
// The proportion at which looking up/down affects the apparent 'horiz' of
// a parallaxed sky, scaled by 65536 (so, a value of 65536 makes it align
// with the drawn surrounding scene):
int32_t horizfrac;
int horizfrac;
// The texel index offset in the y direction of a parallaxed sky:
// XXX: currently always 0.
int32_t yoffs;
int yoffs;
int8_t lognumtiles; // 1<<lognumtiles: number of tiles in multi-sky
int lognumtiles; // 1<<lognumtiles: number of tiles in multi-sky
int16_t tileofs[MAXPSKYTILES]; // for 0 <= j < (1<<lognumtiles): tile offset relative to basetile
int32_t yscale;
@ -299,6 +236,11 @@ static inline psky_t *getpskyidx(int32_t picnum)
EXTERN psky_t * tileSetupSky(int32_t tilenum);
psky_t* defineSky(int32_t const tilenum, int horiz, int lognumtiles, const uint16_t* tileofs, int yoff = 0);
// Get properties of parallaxed sky to draw.
// Returns: pointer to tile offset array. Sets-by-pointer the other three.
const int16_t* getpsky(int32_t picnum, int32_t* dapyscale, int32_t* dapskybits, int32_t* dapyoffs, int32_t* daptileyscale);
EXTERN char parallaxtype;
EXTERN int32_t parallaxyoffs_override, parallaxyscale_override;
extern int16_t pskybits_override;
@ -313,17 +255,11 @@ EXTERN int16_t prevspritesect[MAXSPRITES], prevspritestat[MAXSPRITES];
EXTERN int16_t nextspritesect[MAXSPRITES], nextspritestat[MAXSPRITES];
EXTERN uint8_t gotpic[(MAXTILES+7)>>3];
EXTERN char gotsector[(MAXSECTORS+7)>>3];
extern FixedBitArray<MAXSECTORS> gotsector;
extern uint32_t drawlinepat;
extern int32_t novoxmips;
extern int16_t tiletovox[MAXTILES];
extern int32_t voxscale[MAXVOXELS];
extern char g_haveVoxels;
extern uint8_t globalr, globalg, globalb;
enum {
@ -332,8 +268,6 @@ enum {
GLOBAL_NO_GL_FOGSHADE = 1<<2,
};
extern int32_t globalflags;
extern const char *engineerrstr;
EXTERN int32_t editorzrange[2];
@ -377,22 +311,8 @@ SPRITE VARIABLES:
be in some sector, and must have some kind of status that you define.
TILE VARIABLES:
NUMTILES - the number of tiles found TILES.DAT.
TIMING VARIABLES:
NUMFRAMES - The number of times the draw3dscreen function was called
since the engine was initialized. This helps to determine frame
rate. (Frame rate = numframes * 120 / I_GetBuildTime().)
OTHER VARIABLES:
STARTUMOST[320] is an array of the highest y-coordinates on each column
that my engine is allowed to write to. You need to set it only
once.
STARTDMOST[320] is an array of the lowest y-coordinates on each column
that my engine is allowed to write to. You need to set it only
once.
SINTABLE[2048] is a sin table with 2048 angles rather than the
normal 360 angles for higher precision. Also since SINTABLE is in
all integers, the range is multiplied by 16383, so instead of the
@ -425,29 +345,14 @@ void engineLoadBoard(const char *filename, int flags, vec3_t *dapos, int16_t *da
void loadMapBackup(const char* filename);
void G_LoadMapHack(const char* filename, const unsigned char*);
int32_t qloadkvx(int32_t voxindex, const char *filename);
void vox_undefine(int32_t const);
void vox_deinit();
void videoSetCorrectedAspect();
void videoSetViewableArea(int32_t x1, int32_t y1, int32_t x2, int32_t y2);
void renderSetAspect(int32_t daxrange, int32_t daaspect);
void plotpixel(int32_t x, int32_t y, char col);
FCanvasTexture *renderSetTarget(int16_t tilenume);
void renderRestoreTarget();
void renderPrepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang, fixed_t dahoriz, int16_t dawall,
int32_t *tposx, int32_t *tposy, fixed_t *tang);
void renderCompleteMirror(void);
int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, fixed_t daang, fixed_t dahoriz, int16_t dacursectnum);
void renderDrawMasks(void);
void setVideoMode();
void videoInit();
void videoClearViewableArea(int32_t dacol);
void videoClearScreen(int32_t dacol);
void renderDrawMapView(int32_t dax, int32_t day, int32_t zoome, int16_t ang);
class F2DDrawer;
@ -498,13 +403,12 @@ void updatesectorneighbor(int32_t const x, int32_t const y, int16_t * const sect
void updatesectorneighborz(int32_t const x, int32_t const y, int32_t const z, int16_t * const sectnum, int32_t initialMaxDistance = INITIALUPDATESECTORDIST, int32_t maxDistance = MAXUPDATESECTORDIST) ATTRIBUTE((nonnull(4)));
int findwallbetweensectors(int sect1, int sect2);
static FORCE_INLINE int sectoradjacent(int sect1, int sect2) { return findwallbetweensectors(sect1, sect2) != -1; }
inline int sectoradjacent(int sect1, int sect2) { return findwallbetweensectors(sect1, sect2) != -1; }
int32_t getsectordist(vec2_t const in, int const sectnum, vec2_t * const out = nullptr);
extern const int16_t *chsecptr_onextwall;
int32_t checksectorpointer(int16_t i, int16_t sectnum);
#if !KRANDDEBUG
static FORCE_INLINE int32_t krand(void)
inline int32_t krand(void)
{
randomseed = (randomseed * 1664525ul) + 221297ul;
return ((uint32_t) randomseed)>>16;
@ -513,16 +417,19 @@ static FORCE_INLINE int32_t krand(void)
int32_t krand(void);
#endif
int32_t ksqrt(uint32_t num);
int32_t getangle(int32_t xvect, int32_t yvect);
fixed_t gethiq16angle(int32_t xvect, int32_t yvect);
inline int32_t ksqrt(uint32_t num)
{
return int(sqrt((float)num));
}
static FORCE_INLINE constexpr uint32_t uhypsq(int32_t const dx, int32_t const dy)
int32_t getangle(int32_t xvect, int32_t yvect);
inline constexpr uint32_t uhypsq(int32_t const dx, int32_t const dy)
{
return (uint32_t)dx*dx + (uint32_t)dy*dy;
}
static FORCE_INLINE int32_t logapproach(int32_t const val, int32_t const targetval)
inline int32_t logapproach(int32_t const val, int32_t const targetval)
{
int32_t const dif = targetval - val;
return (dif>>1) ? val + (dif>>1) : targetval;
@ -551,36 +458,36 @@ void yax_getzsofslope(int sectNum, int playerX, int playerY, int32_t* pCeilZ, in
int32_t yax_getceilzofslope(int const sectnum, vec2_t const vect);
int32_t yax_getflorzofslope(int const sectnum, vec2_t const vect);
static FORCE_INLINE int32_t getceilzofslope(int16_t sectnum, int32_t dax, int32_t day)
inline int32_t getceilzofslope(int16_t sectnum, int32_t dax, int32_t day)
{
return getceilzofslopeptr((usectorptr_t)&sector[sectnum], dax, day);
}
static FORCE_INLINE int32_t getflorzofslope(int16_t sectnum, int32_t dax, int32_t day)
inline int32_t getflorzofslope(int16_t sectnum, int32_t dax, int32_t day)
{
return getflorzofslopeptr((usectorptr_t)&sector[sectnum], dax, day);
}
static FORCE_INLINE void getzsofslope(int16_t sectnum, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz)
inline void getzsofslope(int16_t sectnum, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz)
{
getzsofslopeptr((usectorptr_t)&sector[sectnum], dax, day, ceilz, florz);
}
static FORCE_INLINE void getcorrectzsofslope(int16_t sectnum, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz)
inline void getcorrectzsofslope(int16_t sectnum, int32_t dax, int32_t day, int32_t *ceilz, int32_t *florz)
{
vec2_t closest = { dax, day };
getsectordist(closest, sectnum, &closest);
getzsofslopeptr((usectorptr_t)&sector[sectnum], closest.x, closest.y, ceilz, florz);
}
static FORCE_INLINE int32_t getcorrectceilzofslope(int16_t sectnum, int32_t dax, int32_t day)
inline int32_t getcorrectceilzofslope(int16_t sectnum, int32_t dax, int32_t day)
{
vec2_t closest = { dax, day };
getsectordist(closest, sectnum, &closest);
return getceilzofslopeptr((usectorptr_t)&sector[sectnum], closest.x, closest.y);
}
static FORCE_INLINE int32_t getcorrectflorzofslope(int16_t sectnum, int32_t dax, int32_t day)
inline int32_t getcorrectflorzofslope(int16_t sectnum, int32_t dax, int32_t day)
{
vec2_t closest = { dax, day };
getsectordist(closest, sectnum, &closest);
@ -589,12 +496,12 @@ static FORCE_INLINE int32_t getcorrectflorzofslope(int16_t sectnum, int32_t dax,
// Is <wal> a red wall in a safe fashion, i.e. only if consistency invariant
// ".nextsector >= 0 iff .nextwall >= 0" holds.
static FORCE_INLINE int32_t redwallp(uwallptr_t wal)
inline int32_t redwallp(uwallptr_t wal)
{
return (wal->nextwall >= 0 && wal->nextsector >= 0);
}
static FORCE_INLINE int32_t E_SpriteIsValid(const int32_t i)
inline int32_t E_SpriteIsValid(const int32_t i)
{
return ((unsigned)i < MAXSPRITES && sprite[i].statnum != MAXSTATUS);
}
@ -602,7 +509,6 @@ static FORCE_INLINE int32_t E_SpriteIsValid(const int32_t i)
void alignceilslope(int16_t dasect, int32_t x, int32_t y, int32_t z);
void alignflorslope(int16_t dasect, int32_t x, int32_t y, int32_t z);
int32_t sectorofwall(int16_t wallNum);
void setslope(int32_t sectnum, int32_t cf, int16_t slope);
int32_t lintersect(int32_t originX, int32_t originY, int32_t originZ,
@ -612,13 +518,6 @@ int32_t lintersect(int32_t originX, int32_t originY, int32_t originZ,
int32_t rayintersect(int32_t x1, int32_t y1, int32_t z1, int32_t vx, int32_t vy, int32_t vz, int32_t x3,
int32_t y3, int32_t x4, int32_t y4, int32_t *intx, int32_t *inty, int32_t *intz);
#if !defined NETCODE_DISABLE
void do_insertsprite_at_headofstat(int16_t spritenum, int16_t statnum);
int32_t insertspritestat(int16_t statnum);
void do_deletespritestat(int16_t deleteme);
void do_insertsprite_at_headofsect(int16_t spritenum, int16_t sectnum);
void do_deletespritesect(int16_t deleteme);
#endif
int32_t insertsprite(int16_t sectnum, int16_t statnum);
int32_t deletesprite(int16_t spritenum);
@ -638,49 +537,15 @@ inline void setspritepos(int spnum, int x, int y, int z)
int32_t setspritez(int16_t spritenum, const vec3_t *) ATTRIBUTE((nonnull(2)));
int32_t spriteheightofsptr(uspriteptr_t spr, int32_t *height, int32_t alsotileyofs);
static FORCE_INLINE int32_t spriteheightofs(int16_t i, int32_t *height, int32_t alsotileyofs)
inline int32_t spriteheightofs(int16_t i, int32_t *height, int32_t alsotileyofs)
{
return spriteheightofsptr((uspriteptr_t)&sprite[i], height, alsotileyofs);
}
int videoCaptureScreen();
struct OutputFileCounter {
uint16_t count = 0;
FileWriter *opennextfile(char *, char *);
FileWriter *opennextfile_withext(char *, const char *);
};
// PLAG: line utility functions
typedef struct s_equation
{
float a, b, c;
} _equation;
#define STATUS2DSIZ 144
#define STATUS2DSIZ2 26
#ifdef USE_OPENGL
void renderSetRollAngle(float rolla);
#endif
void Polymost_Startup();
typedef uint16_t polytintflags_t;
enum cutsceneflags {
CUTSCENE_FORCEFILTER = 1,
CUTSCENE_FORCENOFILTER = 2,
CUTSCENE_TEXTUREFILTER = 4,
};
enum {
TEXFILTER_OFF = 0, // GL_NEAREST
TEXFILTER_ON = 5, // GL_LINEAR_MIPMAP_LINEAR
};
extern int32_t gltexmaxsize;
EXTERN_CVAR(Bool, hw_animsmoothing)
EXTERN_CVAR(Bool, hw_hightile)
EXTERN_CVAR(Bool, hw_models)
@ -692,7 +557,6 @@ EXTERN_CVAR(Bool, hw_useindexedcolortextures)
EXTERN_CVAR(Bool, hw_parallaxskypanning)
EXTERN_CVAR(Bool, r_voxels)
extern int32_t r_downsize;
extern int32_t mdtims, omdtims;
extern int32_t r_rortexture;
@ -705,11 +569,8 @@ int32_t md_loadmodel(const char *fn);
int32_t md_setmisc(int32_t modelid, float scale, int32_t shadeoff, float zadd, float yoffset, int32_t flags);
// int32_t md_tilehasmodel(int32_t tilenume, int32_t pal);
extern TArray<FString> g_clipMapFiles;
EXTERN int32_t nextvoxid;
EXTERN int8_t voxreserve[(MAXVOXELS+7)>>3];
EXTERN int8_t voxrotate[(MAXVOXELS+7)>>3];
EXTERN FixedBitArray<MAXVOXELS>voxreserve;
#ifdef USE_OPENGL
// TODO: dynamically allocate this
@ -723,7 +584,7 @@ typedef struct
int16_t framenum; // calculate the number from the name when declaring
int16_t nexttile;
uint16_t smoothduration;
hudtyp *hudmem[2];
hudtyp hudmem[2];
int8_t skinnum;
char pal;
} tile2model_t;
@ -733,19 +594,13 @@ typedef struct
EXTERN int32_t mdinited;
EXTERN tile2model_t tile2model[MAXTILES+EXTRATILES];
static FORCE_INLINE int32_t md_tilehasmodel(int32_t const tilenume, int32_t const pal)
inline int32_t md_tilehasmodel(int32_t const tilenume, int32_t const pal)
{
return mdinited ? tile2model[Ptile2tile(tilenume,pal)].modelid : -1;
}
#endif // defined USE_OPENGL
static FORCE_INLINE int tilehasmodelorvoxel(int const tilenume, int pal)
{
UNREFERENCED_PARAMETER(pal);
return
(mdinited && hw_models && tile2model[Ptile2tile(tilenume, pal)].modelid != -1) ||
(r_voxels && tiletovox[tilenume] != -1);
}
int tilehasmodelorvoxel(int const tilenume, int pal);
int32_t md_defineframe(int32_t modelid, const char *framename, int32_t tilenume,
int32_t skinnum, float smoothduration, int32_t pal);
@ -758,12 +613,6 @@ int32_t md_definehud (int32_t modelid, int32_t tilex, vec3f_t add,
int32_t md_undefinetile(int32_t tile);
int32_t md_undefinemodel(int32_t modelid);
int32_t loaddefinitionsfile(const char *fn, bool loadadds = false, bool cumulative = false);
// if loadboard() fails with -2 return, try loadoldboard(). if it fails with
// -2, board is dodgy
int32_t engineLoadBoardV5V6(const char *filename, char fromwhere, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum);
#ifdef USE_OPENGL
# include "polymost.h"
#endif
@ -772,7 +621,7 @@ extern int skiptile;
static vec2_t const zerovec = { 0, 0 };
static FORCE_INLINE int inside_p(int32_t const x, int32_t const y, int const sectnum) { return (sectnum >= 0 && inside(x, y, sectnum) == 1); }
inline int inside_p(int32_t const x, int32_t const y, int const sectnum) { return (sectnum >= 0 && inside(x, y, sectnum) == 1); }
#define SET_AND_RETURN(Lval, Rval) \
do \
@ -842,15 +691,11 @@ extern int32_t rintersect(int32_t x1, int32_t y1, int32_t z1,
int32_t *intx, int32_t *inty, int32_t *intz);
extern int32_t(*animateoffs_replace)(int const tilenum, int fakevar);
extern int32_t(*getpalookup_replace)(int32_t davis, int32_t dashade);
extern void(*initspritelists_replace)(void);
extern int32_t(*insertsprite_replace)(int16_t sectnum, int16_t statnum);
extern int32_t(*deletesprite_replace)(int16_t spritenum);
extern int32_t(*changespritesect_replace)(int16_t spritenum, int16_t newsectnum);
extern int32_t(*changespritestat_replace)(int16_t spritenum, int16_t newstatnum);
#ifdef USE_OPENGL
extern void(*PolymostProcessVoxels_Callback)(void);
#endif
// Masking these into the object index to keep it in 16 bit was probably the single most dumbest and pointless thing Build ever did.
// Gonna be fun to globally replace these to finally lift the limit this imposes on map size.
@ -867,6 +712,23 @@ enum EHitBits
void updateModelInterpolation();
inline void tileUpdatePicnum(int* const tileptr, int const obj, int stat)
{
auto& tile = *tileptr;
if (picanm[tile].sf & PICANM_ANIMTYPE_MASK)
tile += animateoffs(tile, obj);
if (((obj & 16384) == 16384) && (stat & CSTAT_WALL_ROTATE_90) && RotTile(tile).newtile != -1)
tile = RotTile(tile).newtile;
}
inline void setgotpic(int32_t tilenume)
{
gotpic[tilenume >> 3] |= 1 << (tilenume & 7);
}
#include "iterators.h"

View file

@ -3,7 +3,7 @@
//ceilingstat/floorstat:
// bit 0: 1 = parallaxing, 0 = not "P"
// bit 1: 1 = groudraw, 0 = not
// bit 1: 1 = sloped, 0 = not
// bit 2: 1 = swap x&y, 0 = not "F"
// bit 3: 1 = double smooshiness "E"
// bit 4: 1 = x-flip "F"
@ -17,9 +17,38 @@
// bit 9: 1 = blocking ceiling/floor
// bit 10: 1 = YAX'ed ceiling/floor
// bit 11: 1 = hitscan-sensitive ceiling/floor
// bits 12-15: reserved
// bits 12-14: reserved
// bit 15: SW: block FAF hitscans
//////////////////// Version 7 map format ////////////////////
enum
{
CSTAT_SECTOR_SKY = 1,
CSTAT_SECTOR_SLOPE = 2,
CSTAT_SECTOR_SWAPXY = 4,
CSTAT_SECTOR_TEXHALF = 8,
CSTAT_SECTOR_XFLIP = 16,
CSTAT_SECTOR_YFLIP = 32,
CSTAT_SECTOR_ALIGN = 64,
CSTAT_SECTOR_TRANS = 128,
CSTAT_SECTOR_TRANS_INVERT = 256,
CSTAT_SECTOR_METHOD = 384,
SECTOREX_CLOUDSCROLL = 1,
};
enum
{
PORTAL_SECTOR_FLOOR = 1,
PORTAL_SECTOR_CEILING = 2,
PORTAL_SECTOR_FLOOR_REFLECT = 3,
PORTAL_SECTOR_CEILING_REFLECT = 4,
PORTAL_WALL_VIEW = 5,
PORTAL_WALL_MIRROR = 6,
PORTAL_WALL_TO_SPRITE = 7,
PORTAL_SECTOR_GEOMETRY = 8,
};
//40 bytes
struct sectortype
@ -40,7 +69,11 @@ struct sectortype
int16_t hitag;
int16_t extra;
uint8_t dirty;
uint8_t exflags;
float ceilingxpan_, ceilingypan_, floorxpan_, floorypan_;
uint8_t portalflags;
int8_t portalnum;
int ceilingxpan() const { return int(ceilingxpan_); }
int ceilingypan() const { return int(ceilingypan_); }
@ -80,7 +113,7 @@ struct walltype
};
vec2_t pos;
};
int16_t point2, nextwall, nextsector;
int16_t point2, nextwall, sector, nextsector;
uint16_t cstat;
int16_t picnum, overpicnum;
int8_t shade;
@ -91,6 +124,9 @@ struct walltype
int16_t hitag;
int16_t extra;
float xpan_, ypan_;
binangle clipangle;
uint8_t portalflags;
uint16_t portalnum;
int xpan() const { return int(xpan_); }
int ypan() const { return int(ypan_); }
@ -216,19 +252,13 @@ struct spritetype
int8_t xoffset, yoffset;
int16_t sectnum, statnum;
int16_t oang, ang, owner;
union {
struct
{
union {
int16_t xvel, index;
};
int16_t yvel;
union {
int16_t zvel, inittype;
};
union {
int16_t xvel, index;
};
int16_t yvel;
union {
int16_t zvel, inittype;
};
vec3_16_t vel;
};
union {
int16_t lotag, type;
};
@ -237,6 +267,7 @@ struct spritetype
};
int16_t extra;
int16_t detail;
int time;
#if 0
// make sure we do not accidentally copy this
@ -287,39 +318,41 @@ struct spritetype
int32_t interpolatedx(double const smoothratio, int const scale = 16)
{
return ox + MulScale(x - ox, smoothratio, scale);
return interpolatedvalue(ox, x, smoothratio, scale);
}
int32_t interpolatedy(double const smoothratio, int const scale = 16)
{
return oy + MulScale(y - oy, smoothratio, scale);
return interpolatedvalue(oy, y, smoothratio, scale);
}
int32_t interpolatedz(double const smoothratio, int const scale = 16)
{
return oz + MulScale(z - oz, smoothratio, scale);
return interpolatedvalue(oz, z, smoothratio, scale);
}
vec2_t interpolatedvec2(double const smoothratio, int const scale = 16)
{
return vec2_t({
return
{
interpolatedx(smoothratio, scale),
interpolatedy(smoothratio, scale)
});
};
}
vec3_t interpolatedvec3(double const smoothratio, int const scale = 16)
{
return vec3_t({
return
{
interpolatedx(smoothratio, scale),
interpolatedy(smoothratio, scale),
interpolatedz(smoothratio, scale)
});
};
}
int16_t interpolatedang(double const smoothratio)
{
return oang + MulScale(((ang + 1024 - oang) & 2047) - 1024, smoothratio, 16);
return interpolatedangle(oang, ang, smoothratio, 16);
}
};

View file

@ -11,54 +11,9 @@
#include "m_alloc.h"
#include "intvec.h"
#include "m_swap.h"
#include "serializer.h"
////////// Compiler detection //////////
#ifdef __GNUC__
# define EDUKE32_GCC_PREREQ(major, minor) (major < __GNUC__ || (major == __GNUC__ && minor <= __GNUC_MINOR__))
#else
# define EDUKE32_GCC_PREREQ(major, minor) 0
#endif
////////// Language detection //////////
////////// Language and compiler feature polyfills //////////
# define EXTERNC
#ifndef UNREFERENCED_PARAMETER
# define UNREFERENCED_PARAMETER(x) (x) = (x)
#endif
#if defined __GNUC__ || defined __clang__
# define ATTRIBUTE(attrlist) __attribute__(attrlist)
#else
# define ATTRIBUTE(attrlist)
#endif
#ifndef MAY_ALIAS
# ifdef _MSC_VER
# define MAY_ALIAS
# else
# define MAY_ALIAS __attribute__((may_alias))
# endif
#endif
#ifndef FORCE_INLINE
# ifdef _MSC_VER
# define FORCE_INLINE __forceinline
# else
# ifdef __GNUC__
# define FORCE_INLINE inline __attribute__((always_inline))
# else
# define FORCE_INLINE inline
# endif
# endif
#endif
# define fallthrough__ [[fallthrough]]
////////// Architecture detection //////////
@ -103,45 +58,18 @@
#include "engineerrors.h"
////////// DEPRECATED: Standard library prefixing //////////
typedef intptr_t ssize_t;
typedef ssize_t bssize_t;
typedef intptr_t bssize_t;
#define BMAX_PATH 256
////////// Metaprogramming structs //////////
using std::enable_if_t;
using native_t = intptr_t;
typedef struct MAY_ALIAS {
int32_t x, y;
} vec2_t;
typedef struct {
float x, y;
} vec2f_t;
typedef struct {
double x, y;
} vec2d_t;
typedef struct MAY_ALIAS {
union {
struct { int32_t x, y, z; };
vec2_t vec2;
};
} vec3_t;
typedef struct MAY_ALIAS {
union {
struct { int16_t x, y, z; };
vec2_16_t vec2;
};
} vec3_16_t;
typedef struct {
union {
struct {
@ -155,57 +83,14 @@ typedef struct {
static_assert(sizeof(vec3f_t) == sizeof(float) * 3);
typedef struct {
union { double x; double d; };
union { double y; double u; };
union { double z; double v; };
} vec3d_t;
static_assert(sizeof(vec3d_t) == sizeof(double) * 3);
////////// Language tricks that depend on size_t //////////
#include "basics.h"
////////// Pointer management //////////
#define DO_FREE_AND_NULL(var) do { \
Xfree(var); (var) = NULL; \
} while (0)
////////// Data serialization //////////
inline int32_t B_LITTLE32(int32_t val) { return LittleLong(val); }
inline uint32_t B_LITTLE32(uint32_t val) { return LittleLong(val); }
inline int32_t B_LITTLE16(int16_t val) { return LittleShort(val); }
inline uint32_t B_LITTLE16(uint16_t val) { return LittleShort(val); }
static FORCE_INLINE void B_BUF32(void * const buf, uint32_t const x) { *(uint32_t *) buf = x; }
static FORCE_INLINE uint32_t B_UNBUF32(void const * const buf) { return *(uint32_t const *) buf; }
static FORCE_INLINE uint16_t B_UNBUF16(void const * const buf) { return *(uint16_t const *) buf; }
////////// Abstract data operations //////////
using std::min;
using std::max;
////////// Bitfield manipulation //////////
// This once was a static array, requiring a memory acces where a shift would suffice.
// Revert the above to a real bit shift through some C++ operator magic. That saves me from reverting all the code that uses this construct.
// Note: Only occurs 25 times in the code, should be removed for good.
static struct
{
constexpr uint8_t operator[](int index) const { return 1 << index; };
} pow2char;
static FORCE_INLINE void bitmap_set(uint8_t *const ptr, int const n) { ptr[n>>3] |= 1 << (n&7); }
static FORCE_INLINE char bitmap_test(uint8_t const *const ptr, int const n) { return ptr[n>>3] & (1 << (n&7)); }
inline void bitmap_set(uint8_t *const ptr, int const n) { ptr[n>>3] |= 1 << (n&7); }
inline char bitmap_test(uint8_t const *const ptr, int const n) { return ptr[n>>3] & (1 << (n&7)); }
////////// Utility functions //////////
@ -230,42 +115,4 @@ void bfirst_search_try(T *const list, uint8_t *const bitmap, T *const eltnumptr,
}
}
////////// PANICKING ALLOCATION WRAPPERS //////////
#define Xstrdup(s) (strdup(s))
#define Xmalloc(size) (M_Malloc(size))
#define Xcalloc(nmemb, size) (M_Calloc(nmemb, size))
#define Xrealloc(ptr, size) (M_Realloc(ptr, size))
#define Xfree(ptr) (M_Free(ptr))
////////// Inlined external libraries //////////
/* End dependence on compat.o object. */
inline FSerializer& Serialize(FSerializer& arc, const char* key, vec2_t& c, vec2_t* def)
{
if (def && !memcmp(&c, def, sizeof(c))) return arc;
if (arc.BeginObject(key))
{
arc("x", c.x, def? &def->x : nullptr)
("y", c.y, def ? &def->y : nullptr)
.EndObject();
}
return arc;
}
inline FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def)
{
if (def && !memcmp(&c, def, sizeof(c))) return arc;
if (arc.BeginObject(key))
{
arc("x", c.x, def ? &def->x : nullptr)
("y", c.y, def ? &def->y : nullptr)
("z", c.z, def ? &def->z : nullptr)
.EndObject();
}
return arc;
}
#endif // compat_h_

View file

@ -1,42 +0,0 @@
#pragma once
// nobody uses these. What's so cool about naked numbers? :(
// system defines for status bits
#define CEILING_STAT_PLAX BIT(0)
#define CEILING_STAT_SLOPE BIT(1)
#define CEILING_STAT_SWAPXY BIT(2)
#define CEILING_STAT_SMOOSH BIT(3)
#define CEILING_STAT_XFLIP BIT(4)
#define CEILING_STAT_YFLIP BIT(5)
#define CEILING_STAT_RELATIVE BIT(6)
#define CEILING_STAT_TYPE_MASK (BIT(7)|BIT(8))
#define CEILING_STAT_MASKED BIT(7)
#define CEILING_STAT_TRANS BIT(8)
#define CEILING_STAT_TRANS_FLIP (BIT(7)|BIT(8))
#define CEILING_STAT_FAF_BLOCK_HITSCAN BIT(15)
#define FLOOR_STAT_PLAX BIT(0)
#define FLOOR_STAT_SLOPE BIT(1)
#define FLOOR_STAT_SWAPXY BIT(2)
#define FLOOR_STAT_SMOOSH BIT(3)
#define FLOOR_STAT_XFLIP BIT(4)
#define FLOOR_STAT_YFLIP BIT(5)
#define FLOOR_STAT_RELATIVE BIT(6)
#define FLOOR_STAT_TYPE_MASK (BIT(7)|BIT(8))
#define FLOOR_STAT_MASKED BIT(7)
#define FLOOR_STAT_TRANS BIT(8)
#define FLOOR_STAT_TRANS_FLIP (BIT(7)|BIT(8))
#define FLOOR_STAT_FAF_BLOCK_HITSCAN BIT(15)
#define CSTAT_WALL_BLOCK BIT(0)
#define CSTAT_WALL_BOTTOM_SWAP BIT(1)
#define CSTAT_WALL_ALIGN_BOTTOM BIT(2)
#define CSTAT_WALL_XFLIP BIT(3)
#define CSTAT_WALL_MASKED BIT(4)
#define CSTAT_WALL_1WAY BIT(5)
#define CSTAT_WALL_BLOCK_HITSCAN BIT(6)
#define CSTAT_WALL_TRANSLUCENT BIT(7)
#define CSTAT_WALL_YFLIP BIT(8)
#define CSTAT_WALL_TRANS_FLIP BIT(9)
#define CSTAT_WALL_BLOCK_ACTOR (BIT(14)) // my def
#define CSTAT_WALL_WARP_HITSCAN (BIT(15)) // my def

View file

@ -177,25 +177,6 @@ struct md3model_t : public idmodel_t
*/
};
#define VOXBORDWIDTH 1 //use 0 to save memory, but has texture artifacts; 1 looks better...
#define VOXUSECHAR 0
#if (VOXUSECHAR != 0)
typedef struct { uint8_t x, y, z, u, v; } vert_t;
#else
typedef struct { uint16_t x, y, z, u, v; } vert_t;
#endif
typedef struct { vert_t v[4]; } voxrect_t;
struct voxmodel_t : public mdmodel_t
{
FVoxelModel* model = nullptr;
vec3_t siz;
vec3f_t piv;
int32_t is8bit;
};
EXTERN mdmodel_t **models;
FGameTexture* mdloadskin(idmodel_t* m, int32_t number, int32_t pal, int32_t surf, bool* exact);
@ -206,15 +187,6 @@ EXTERN void md3_vox_calcmat_common(tspriteptr_t tspr, const vec3f_t *a0, float f
EXTERN int32_t mdpause;
EXTERN int32_t nextmodelid;
EXTERN voxmodel_t *voxmodels[MAXVOXELS];
void voxfree(voxmodel_t *m);
voxmodel_t *voxload(int lumpnum);
int32_t polymost_voxdraw(voxmodel_t *m, tspriteptr_t const tspr, bool rotate);
int md3postload_polymer(md3model_t* m);
//int32_t md_thinoutmodel(int32_t modelid, uint8_t *usedframebitmap);
EXTERN void md_freevbos(void);
#endif // defined USE_OPENGL

View file

@ -1,19 +0,0 @@
// "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.
//
// This file has been modified from Ken Silverman's original release
// by Jonathon Fowler (jf@jonof.id.au)
// by the EDuke32 team (development@voidpoint.com)
#ifndef mmulti_h_
#define mmulti_h_
#define MAXMULTIPLAYERS 16
extern int myconnectindex, numplayers;
extern int connecthead, connectpoint2[MAXMULTIPLAYERS];
#endif // mmulti_h_

View file

@ -4,18 +4,20 @@
#include "mdsprite.h"
typedef struct { uint8_t r, g, b, a; } coltype;
typedef struct { float r, g, b, a; } coltypef;
extern tspritetype pm_tsprite[MAXSPRITESONSCREEN];
extern int pm_spritesortcnt;
extern int pm_smoothratio;
namespace Polymost
{
extern float gtang;
extern double gxyaspect;
extern float grhalfxdown10x;
extern float gcosang, gsinang, gcosang2, gsinang2;
extern int pm_smoothratio;
extern void Polymost_prepare_loadboard(void);
void polymost_outputGLDebugMessage(uint8_t severity, const char* format, ...);
//void phex(char v, char *s);
void polymost_drawsprite(int32_t snum);
@ -25,57 +27,40 @@ void polymost_initosdfuncs(void);
void polymost_drawrooms(void);
void polymost_prepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang, fixed_t dahoriz, int16_t mirrorWall);
void polymost_completeMirror();
void polymost_deletesprite(int num);
int32_t polymost_maskWallHasTranslucency(uwalltype const * const wall);
int32_t polymost_spriteHasTranslucency(tspritetype const * const tspr);
int32_t polymost_spriteIsModelOrVoxel(tspritetype const * const tspr);
int32_t polymost_maskWallHasTranslucency(walltype const * const wall);
int32_t polymost_spriteHasTranslucency(spritetype const * const tspr);
void polymost_glreset(void);
enum {
INVALIDATE_ALL,
INVALIDATE_ART,
INVALIDATE_ALL_NON_INDEXED,
INVALIDATE_ART_NON_INDEXED
};
void polymost_scansector(int32_t sectnum);
extern float curpolygonoffset;
}
void renderPrepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang, fixed_t dahoriz, int16_t dawall,
int32_t* tposx, int32_t* tposy, fixed_t* tang);
void renderCompleteMirror(void);
int32_t renderDrawRoomsQ16(int32_t daposx, int32_t daposy, int32_t daposz, fixed_t daang, fixed_t dahoriz, int16_t dacursectnum);
void renderDrawMasks(void);
// PLAG: line utility functions
typedef struct s_equation
{
float a, b, c;
} _equation;
void renderSetRollAngle(float rolla);
// these are defined in engine.cpp.
extern int16_t globalpicnum;
#define POLYMOST_CHOOSE_FOG_PAL(fogpal, pal) \
((fogpal) ? (fogpal) : (pal))
static FORCE_INLINE int32_t get_floor_fogpal(usectorptr_t const sec)
{
return POLYMOST_CHOOSE_FOG_PAL(sec->fogpal, sec->floorpal);
}
static FORCE_INLINE int32_t get_ceiling_fogpal(usectorptr_t const sec)
{
return POLYMOST_CHOOSE_FOG_PAL(sec->fogpal, sec->ceilingpal);
}
static FORCE_INLINE int32_t fogshade(int32_t const shade, int32_t const pal)
{
return (globalflags & GLOBAL_NO_GL_FOGSHADE) ? 0 : shade;
}
static constexpr inline int check_nonpow2(int32_t const x)
{
return (x > 1 && (x&(x-1)));
}
static inline float polymost_invsqrt_approximation(float x)
{
#if !B_BIG_ENDIAN
float const haf = x * .5f;
union { float f; uint32_t i; } n = { x };
n.i = 0x5f375a86 - (n.i >> 1);
return n.f * (1.5f - haf * (n.f * n.f));
#else
// this is the comment
return 1.f / sqrtf(x);
#endif
}
extern float fcosglobalang, fsinglobalang;
extern float fydimen, fviewingrange;
extern int32_t viewingrangerecip;
// Flags of the <dameth> argument of various functions
enum {
@ -96,11 +81,4 @@ enum {
DAMETH_BACKFACECULL = -1,
};
#define DAMETH_NARROW_MASKPROPS(dameth) (((dameth)&(~DAMETH_TRANS1))|(((dameth)&DAMETH_TRANS1)>>1))
static_assert(DAMETH_NARROW_MASKPROPS(DAMETH_MASKPROPS) == DAMETH_MASK);
extern float fcosglobalang, fsinglobalang;
extern float fydimen, fviewingrange;
extern int32_t viewingrangerecip;
#endif

View file

@ -1,136 +0,0 @@
#ifndef BUILD_SCRIPTFILE_H_
#define BUILD_SCRIPTFILE_H_
#include "sc_man.h"
#include "filesystem.h"
using scriptfile = FScanner;
inline int32_t scriptfile_getnumber(scriptfile *sf, int32_t *num)
{
bool res = sf->GetNumber();
if (num)
{
if (res) *num = sf->Number;
else *num = 0;
}
return !res;
}
inline int32_t scriptfile_getdouble(scriptfile *sf, double *num)
{
bool res = sf->GetFloat();
if (num)
{
if (res) *num = sf->Float;
else *num = 0;
}
return !res;
}
inline int32_t scriptfile_getstring(scriptfile *sf, FString *st)
{
bool res = sf->GetString();
if (st)
{
if (res) *st = sf->String;
else *st = "";
}
return !res;
}
inline int32_t scriptfile_getsymbol(scriptfile *sf, int32_t *num)
{
bool res = sf->GetNumber(true);
if (num)
{
if (res) *num = sf->Number;
else *num = 0;
}
return !res;
}
inline int32_t scriptfile_getsymbol(scriptfile* sf, int64_t* num)
{
bool res = sf->GetNumber(true);
if (num)
{
if (res) *num = sf->BigNumber;
else *num = 0;
}
return !res;
}
inline FScriptPosition scriptfile_getposition(scriptfile *sf)
{
return FScriptPosition(*sf);
}
inline int32_t scriptfile_getbraces(scriptfile *sf, FScanner::SavedPos *braceend)
{
if (sf->CheckString("{"))
{
auto here = sf->SavePos();
sf->SkipToEndOfBlock();
*braceend = sf->SavePos();
sf->RestorePos(here);
return 0;
}
else
{
sf->ScriptError("'{' expected");
return -1;
}
}
inline bool scriptfile_endofblock(scriptfile* sf, FScanner::SavedPos& braceend)
{
auto here = sf->SavePos();
return here.SavedScriptPtr >= braceend.SavedScriptPtr;
}
inline void scriptfile_setposition(scriptfile* sf, const FScanner::SavedPos& pos)
{
sf->RestorePos(pos);
}
inline scriptfile *scriptfile_fromfile(const char *fn)
{
int lump = fileSystem.FindFile(fn);
if (lump < 0) return nullptr;
auto sc = new FScanner;
sc->OpenLumpNum(lump);
sc->SetNoOctals(true);
sc->SetNoFatalErrors(true);
return sc;
}
inline void scriptfile_close(scriptfile *sf)
{
delete sf;
}
inline int32_t scriptfile_addsymbolvalue(scriptfile *sf, char const *name, int32_t val)
{
sf->AddSymbol(name, val);
return 1;
}
typedef struct
{
const char *text;
int32_t tokenid;
}
tokenlist;
enum
{
T_EOF = -2,
T_ERROR = -1,
};
#endif

View file

@ -10,7 +10,9 @@
#include "clip.h"
#include "engine_priv.h"
#include "printf.h"
#include "gamefuncs.h"
enum { MAXCLIPDIST = 1024 };
static int16_t clipnum;
static linetype clipit[MAXCLIPNUM];
@ -21,46 +23,83 @@ static uint8_t clipsectormap[(MAXSECTORS+7)>>3];
static uint8_t origclipsectormap[(MAXSECTORS+7)>>3];
static int16_t clipobjectval[MAXCLIPNUM];
static uint8_t clipignore[(MAXCLIPNUM+7)>>3];
static int32_t rxi[8], ryi[8];
int32_t quickloadboard=0;
static int32_t numclipmaps;
static int32_t numclipsects; // number in sectq[]
static int16_t *sectoidx;
static int16_t *sectq; // [numsectors]
static int16_t pictoidx[MAXTILES]; // maps tile num to clipinfo[] index
static int16_t *tempictoidx;
static usectortype *loadsector;
static uwalltype *loadwall, *loadwallinv;
static uspritetype *loadsprite;
vec2_t hitscangoal = { (1<<29)-1, (1<<29)-1 };
int32_t hitallsprites = 0;
void engineInitClipMaps()
////////// CLIPMOVE //////////
// x1, y1: in/out
// rest x/y: out
template <typename T>
static inline void get_wallspr_points(T const * const spr, int32_t *x1, int32_t *x2,
int32_t *y1, int32_t *y2)
{
numclipmaps = 0;
numclipsects = 0;
//These lines get the 2 points of the rotated sprite
//Given: (x1, y1) starts out as the center point
DO_FREE_AND_NULL(sectq);
DO_FREE_AND_NULL(sectoidx);
DO_FREE_AND_NULL(tempictoidx);
DO_FREE_AND_NULL(loadsector);
DO_FREE_AND_NULL(loadwall);
DO_FREE_AND_NULL(loadwallinv);
DO_FREE_AND_NULL(loadsprite);
const int32_t tilenum=spr->picnum, ang=spr->ang;
const int32_t xrepeat = spr->xrepeat;
int32_t xoff = tileLeftOffset(tilenum) + spr->xoffset;
int32_t k, l, dax, day;
// two's complement trick, -1 = 0xff
memset(&pictoidx, -1, sizeof(pictoidx));
if (spr->cstat&4)
xoff = -xoff;
numsectors = 0;
numwalls = 0;
dax = bsin(ang) * xrepeat;
day = -bcos(ang) * xrepeat;
l = tileWidth(tilenum);
k = (l>>1)+xoff;
*x1 -= MulScale(dax,k, 16);
*x2 = *x1 + MulScale(dax,l, 16);
*y1 -= MulScale(day,k, 16);
*y2 = *y1 + MulScale(day,l, 16);
}
////////// CLIPMOVE //////////
// x1, y1: in/out
// rest x/y: out
template <typename T>
static inline void get_floorspr_points(T const * const spr, int32_t px, int32_t py,
int32_t *x1, int32_t *x2, int32_t *x3, int32_t *x4,
int32_t *y1, int32_t *y2, int32_t *y3, int32_t *y4)
{
const int32_t tilenum = spr->picnum;
const int32_t cosang = bcos(spr->ang);
const int32_t sinang = bsin(spr->ang);
vec2_t const span = { tileWidth(tilenum), tileHeight(tilenum)};
vec2_t const repeat = { spr->xrepeat, spr->yrepeat };
vec2_t adjofs = { tileLeftOffset(tilenum) + spr->xoffset, tileTopOffset(tilenum) + spr->yoffset };
if (spr->cstat & 4)
adjofs.x = -adjofs.x;
if (spr->cstat & 8)
adjofs.y = -adjofs.y;
vec2_t const center = { ((span.x >> 1) + adjofs.x) * repeat.x, ((span.y >> 1) + adjofs.y) * repeat.y };
vec2_t const rspan = { span.x * repeat.x, span.y * repeat.y };
vec2_t const ofs = { -MulScale(cosang, rspan.y, 16), -MulScale(sinang, rspan.y, 16) };
*x1 += DMulScale(sinang, center.x, cosang, center.y, 16) - px;
*y1 += DMulScale(sinang, center.y, -cosang, center.x, 16) - py;
*x2 = *x1 - MulScale(sinang, rspan.x, 16);
*y2 = *y1 + MulScale(cosang, rspan.x, 16);
*x3 = *x2 + ofs.x, *x4 = *x1 + ofs.x;
*y3 = *y2 + ofs.y, *y4 = *y1 + ofs.y;
}
int32_t clipmoveboxtracenum = 3;
@ -155,14 +194,14 @@ static void addclipline(int32_t dax1, int32_t day1, int32_t dax2, int32_t day2,
clipit[clipnum].x2 = dax2; clipit[clipnum].y2 = day2;
clipobjectval[clipnum] = daoval;
uint32_t const mask = pow2char[clipnum&7];
uint32_t const mask = (1 << (clipnum&7));
uint8_t &value = clipignore[clipnum>>3];
value = (value & ~mask) | (-nofix & mask);
clipnum++;
}
static FORCE_INLINE void clipmove_tweak_pos(const vec3_t *pos, int32_t gx, int32_t gy, int32_t x1, int32_t y1, int32_t x2,
inline void clipmove_tweak_pos(const vec3_t *pos, int32_t gx, int32_t gy, int32_t x1, int32_t y1, int32_t x2,
int32_t y2, int32_t *daxptr, int32_t *dayptr)
{
int32_t daz;
@ -175,34 +214,6 @@ static FORCE_INLINE void clipmove_tweak_pos(const vec3_t *pos, int32_t gx, int32
}
}
int32_t getceilzofslope_old(int32_t sectnum, int32_t dax, int32_t day)
{
int32_t dx, dy, i, j;
if (!(sector[sectnum].ceilingstat&2)) return sector[sectnum].ceilingz;
j = sector[sectnum].wallptr;
dx = wall[wall[j].point2].x-wall[j].x;
dy = wall[wall[j].point2].y-wall[j].y;
i = (ksqrtasm_old(dx*dx+dy*dy)); if (i == 0) return(sector[sectnum].ceilingz);
i = DivScale(sector[sectnum].ceilingheinum,i, 15);
dx *= i; dy *= i;
return sector[sectnum].ceilingz+DMulScale(dx,day-wall[j].y,-dy,dax-wall[j].x, 23);
}
int32_t getflorzofslope_old(int32_t sectnum, int32_t dax, int32_t day)
{
int32_t dx, dy, i, j;
if (!(sector[sectnum].floorstat&2)) return sector[sectnum].floorz;
j = sector[sectnum].wallptr;
dx = wall[wall[j].point2].x-wall[j].x;
dy = wall[wall[j].point2].y-wall[j].y;
i = (ksqrtasm_old(dx*dx+dy*dy)); if (i == 0) return sector[sectnum].floorz;
i = DivScale(sector[sectnum].floorheinum,i, 15);
dx *= i; dy *= i;
return sector[sectnum].floorz+DMulScale(dx,day-wall[j].y,-dy,dax-wall[j].x, 23);
}
// Returns: should clip?
static int cliptestsector(int const dasect, int const nextsect, int32_t const flordist, int32_t const ceildist, vec2_t const pos, int32_t const posz)
{
@ -214,20 +225,6 @@ static int cliptestsector(int const dasect, int const nextsect, int32_t const fl
{
case ENGINECOMPATIBILITY_NONE:
break;
case ENGINECOMPATIBILITY_19950829:
{
int32_t daz = getflorzofslope_old(dasect, pos.x, pos.y);
int32_t daz2 = getflorzofslope_old(nextsect, pos.x, pos.y);
if (daz2 < daz && (sec2->floorstat&1) == 0)
if (posz >= daz2-(flordist-1)) return 1;
daz = getceilzofslope_old(dasect, pos.x, pos.y);
daz2 = getceilzofslope_old(nextsect, pos.x, pos.y);
if (daz2 > daz && (sec2->ceilingstat&1) == 0)
if (posz <= daz2+(ceildist-1)) return 1;
return 0;
}
default:
{
int32_t daz = getflorzofslope(dasect, pos.x, pos.y);
@ -491,7 +488,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int
//Extra walldist for sprites on sector lines
vec2_t const diff = { goal.x - (pos->x), goal.y - (pos->y) };
int32_t const rad = clip_nsqrtasm(compat_maybe_truncate_to_int32(uhypsq(diff.x, diff.y))) + MAXCLIPDIST + walldist + 8;
int32_t const rad = ksqrt(compat_maybe_truncate_to_int32(uhypsq(diff.x, diff.y))) + MAXCLIPDIST + walldist + 8;
vec2_t const clipMin = { cent.x - rad, cent.y - rad };
vec2_t const clipMax = { cent.x + rad, cent.y + rad };
@ -550,7 +547,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int
{
clipyou = 1;
}
else if (editstatus == 0)
else
{
clipmove_tweak_pos(pos, diff.x, diff.y, p1.x, p1.y, p2.x, p2.y, &v.x, &v.y);
clipyou = cliptestsector(dasect, wal->nextsector, flordist, ceildist, v, pos->z);
@ -570,7 +567,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int
if (clipyou)
{
int16_t const objtype = curspr ? (int16_t)(curspr - (uspritetype *)sprite) + 49152 : (int16_t)j + 32768;
int16_t const objtype = curspr ? (int16_t)(curspr - sprite) + 49152 : (int16_t)j + 32768;
//Add 2 boxes at endpoints
int32_t bsz = walldist; if (diff.x < 0) bsz = -bsz;
@ -616,7 +613,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int
if ((cstat&dasprclipmask) == 0)
continue;
vec2_t p1 = *(vec2_t const *)spr;
auto p1 = spr->pos.vec2;
switch (cstat & (CSTAT_SPRITE_ALIGNMENT_WALL | CSTAT_SPRITE_ALIGNMENT_FLOOR))
{
@ -989,11 +986,6 @@ void getzrange(const vec3_t *pos, int16_t sectnum,
vec2_t closest = pos->vec2;
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
getsectordist(closest, sectnum, &closest);
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
{
*ceilz = getceilzofslope_old(sectnum,closest.x,closest.y);
*florz = getflorzofslope_old(sectnum,closest.x,closest.y);
}
else
getzsofslope(sectnum,closest.x,closest.y,ceilz,florz);
*ceilhit = sectnum+16384; *florhit = sectnum+16384;
@ -1037,11 +1029,8 @@ void getzrange(const vec3_t *pos, int16_t sectnum,
if (wall[j].cstat&dawalclipmask) continue; // XXX?
auto const sec = (usectorptr_t)&sector[k];
if (editstatus == 0)
{
if (((sec->ceilingstat&1) == 0) && (pos->z <= sec->ceilingz+(3<<8))) continue;
if (((sec->floorstat&1) == 0) && (pos->z >= sec->floorz-(3<<8))) continue;
}
if (((sec->ceilingstat&1) == 0) && (pos->z <= sec->ceilingz+(3<<8))) continue;
if (((sec->floorstat&1) == 0) && (pos->z >= sec->floorz-(3<<8))) continue;
if (bitmap_test(clipsectormap, k) == 0)
addclipsect(k);
@ -1061,11 +1050,6 @@ void getzrange(const vec3_t *pos, int16_t sectnum,
closest = pos->vec2;
if (enginecompatibility_mode == ENGINECOMPATIBILITY_NONE)
getsectordist(closest, k, &closest);
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
{
daz = getceilzofslope_old(k, closest.x,closest.y);
daz2 = getflorzofslope_old(k, closest.x,closest.y);
}
else
getzsofslope(k, closest.x,closest.y, &daz,&daz2);
@ -1241,7 +1225,7 @@ static int32_t hitscan_trysector(const vec3_t *sv, usectorptr_t sec, hitdata_t *
auto const wal2 = (uwallptr_t)&wall[wal->point2];
int32_t j, dax=wal2->x-wal->x, day=wal2->y-wal->y;
i = nsqrtasm(compat_maybe_truncate_to_int32(uhypsq(dax,day))); if (i == 0) return 1; //continue;
i = ksqrt(compat_maybe_truncate_to_int32(uhypsq(dax,day))); if (i == 0) return 1; //continue;
i = DivScale(heinum,i, 15);
dax *= i; day *= i;
@ -1273,22 +1257,22 @@ static int32_t hitscan_trysector(const vec3_t *sv, usectorptr_t sec, hitdata_t *
{
if (tmp==NULL)
{
if (inside(x1,y1,sec-(usectortype *)sector) == 1)
if (inside(x1,y1,sec-sector) == 1)
{
hit_set(hit, sec-(usectortype *)sector, -1, -1, x1, y1, z1);
hit_set(hit, sec-sector, -1, -1, x1, y1, z1);
hitscan_hitsectcf = (how+1)>>1;
}
}
else
{
const int32_t curidx=(int32_t)tmp[0];
auto const curspr=(uspritetype *)tmp[1];
auto const curspr=(spritetype *)tmp[1];
const int32_t thislastsec = tmp[2];
if (!thislastsec)
{
if (inside(x1,y1,sec-(usectortype *)sector) == 1)
hit_set(hit, curspr->sectnum, -1, curspr-(uspritetype *)sprite, x1, y1, z1);
if (inside(x1,y1,sec-sector) == 1)
hit_set(hit, curspr->sectnum, -1, curspr-sprite, x1, y1, z1);
}
}
}
@ -1476,7 +1460,7 @@ int32_t hitscan(const vec3_t *sv, int16_t sectnum, int32_t vx, int32_t vy, int32
{
if (picanm[tilenum].sf&PICANM_TEXHITSCAN_BIT)
{
tileUpdatePicnum(&tilenum, 0);
tileUpdatePicnum(&tilenum, 0, 0);
if (tileLoad(tilenum))
{

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -8,84 +8,13 @@
#pragma once
#include "cmdlib.h"
#ifndef ENGINE_PRIV_H
#define ENGINE_PRIV_H
#define MAXARTFILES_BASE 200
#define MAXARTFILES_TOTAL 220
#define MAXCLIPDIST 1024
// Uncomment to clear the screen before each top-level draw (classic only).
// FIXME: doesn't work with mirrors.
//#define ENGINE_CLEAR_SCREEN
extern intptr_t asm1, asm2;
extern int32_t globalx1, globaly2;
extern uint16_t sqrtable[4096], shlookup[4096+256],sqrtable_old[2048];
static inline int32_t nsqrtasm(uint32_t a)
{
// JBF 20030901: This was a damn lot simpler to reverse engineer than
// msqrtasm was. Really, it was just like simplifying an algebra equation.
uint16_t c;
if (a & 0xff000000) // test eax, 0xff000000 / jnz short over24
{
c = shlookup[(a >> 24) + 4096]; // mov ebx, eax
// over24: shr ebx, 24
// mov cx, word ptr shlookup[ebx*2+8192]
}
else
{
c = shlookup[a >> 12]; // mov ebx, eax
// shr ebx, 12
// mov cx, word ptr shlookup[ebx*2]
// jmp short under24
}
a >>= c&0xff; // under24: shr eax, cl
a = (a&0xffff0000)|(sqrtable[a]); // mov ax, word ptr sqrtable[eax*2]
a >>= ((c&0xff00) >> 8); // mov cl, ch
// shr eax, cl
return a;
}
static inline int32_t getclipmask(int32_t a, int32_t b, int32_t c, int32_t d)
{
// Ken did this
d = ((a<0)<<3) + ((b<0)<<2) + ((c<0)<<1) + (d<0);
return (((d<<4)^0xf0)|d);
}
inline int32_t ksqrtasm_old(int32_t n)
{
uint32_t shift = 0;
n = abs((int32_t)n);
while (n >= 2048)
{
n >>= 2;
++shift;
}
uint32_t const s = sqrtable_old[n];
return (s << shift) >> 10;
}
inline int32_t clip_nsqrtasm(int32_t n)
{
if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829)
return ksqrtasm_old(n);
return nsqrtasm(n);
}
extern int16_t thesector[MAXWALLSB], thewall[MAXWALLSB];
extern int16_t bunchfirst[MAXWALLSB], bunchlast[MAXWALLSB];
extern int16_t maskwall[MAXWALLSB], maskwallcnt;
extern tspriteptr_t tspriteptr[MAXSPRITESONSCREEN + 1];
extern int32_t xdimen, xdimenrecip, halfxdimen, xdimenscale, xdimscale, ydimen;
extern int32_t globalpal, globalfloorpal;
extern int32_t xdimen, xdimenscale, xdimscale, ydimen;
extern float fxdimen;
extern int32_t globalposx, globalposy, globalposz;
extern fixed_t qglobalhoriz, qglobalang;
@ -99,170 +28,35 @@ extern int16_t globalpicnum;
extern int32_t globalorientation;
extern int16_t editstatus;
extern int16_t searchit;
extern int16_t searchsector, searchwall, searchstat;
extern int16_t searchbottomwall, searchisbottom;
extern char inpreparemirror;
extern int16_t sectorborder[256];
extern int32_t hitallsprites;
extern int32_t xb1[MAXWALLSB];
extern int32_t rx1[MAXWALLSB], ry1[MAXWALLSB];
extern int16_t bunchp2[MAXWALLSB];
extern int16_t numscans, numbunches;
extern int32_t rxi[8], ryi[8];
extern int32_t reciptable[2048];
// int32_t wallmost(int16_t *mostbuf, int32_t w, int32_t sectnum, char dastat);
int32_t wallfront(int32_t l1, int32_t l2);
void set_globalang(fixed_t const ang);
int32_t animateoffs(int tilenum, int fakevar);
static FORCE_INLINE int32_t bad_tspr(tspriteptr_t tspr)
inline int32_t bad_tspr(tspriteptr_t tspr)
{
// NOTE: tspr->owner >= MAXSPRITES (could be model) has to be handled by
// caller.
return (tspr->owner < 0 || (unsigned)tspr->picnum >= MAXTILES);
}
//
// getpalookup (internal)
//
static FORCE_INLINE int32_t getpalookup(int32_t davis, int32_t dashade)
{
if (getpalookup_replace)
return getpalookup_replace(davis, dashade);
return min(max(dashade + (davis >> 8), 0), numshades - 1);
}
static FORCE_INLINE int32_t getpalookupsh(int32_t davis) { return getpalookup(davis, globalshade) << 8; }
////// yax'y stuff //////
#ifdef USE_OPENGL
extern void polymost_scansector(int32_t sectnum);
#endif
int32_t renderAddTsprite(int16_t z, int16_t sectnum);
static FORCE_INLINE void setgotpic(int32_t tilenume)
{
gotpic[tilenume>>3] |= pow2char[tilenume&7];
}
// Get properties of parallaxed sky to draw.
// Returns: pointer to tile offset array. Sets-by-pointer the other three.
const int16_t* getpsky(int32_t picnum, int32_t* dapyscale, int32_t* dapskybits, int32_t* dapyoffs, int32_t* daptileyscale);
static FORCE_INLINE void set_globalpos(int32_t const x, int32_t const y, int32_t const z)
inline void set_globalpos(int32_t const x, int32_t const y, int32_t const z)
{
globalposx = x, fglobalposx = (float)x;
globalposy = y, fglobalposy = (float)y;
globalposz = z, fglobalposz = (float)z;
}
template <typename T> static FORCE_INLINE void tileUpdatePicnum(T * const tileptr, int const obj)
{
auto &tile = *tileptr;
if (picanm[tile].sf & PICANM_ANIMTYPE_MASK)
tile += animateoffs(tile, obj);
if (((obj & 16384) == 16384) && (globalorientation & CSTAT_WALL_ROTATE_90) && RotTile(tile).newtile != -1)
tile = RotTile(tile).newtile;
}
// x1, y1: in/out
// rest x/y: out
template <typename T>
static inline void get_wallspr_points(T const * const spr, int32_t *x1, int32_t *x2,
int32_t *y1, int32_t *y2)
{
//These lines get the 2 points of the rotated sprite
//Given: (x1, y1) starts out as the center point
const int32_t tilenum=spr->picnum, ang=spr->ang;
const int32_t xrepeat = spr->xrepeat;
int32_t xoff = tileLeftOffset(tilenum) + spr->xoffset;
int32_t k, l, dax, day;
if (spr->cstat&4)
xoff = -xoff;
dax = bsin(ang) * xrepeat;
day = -bcos(ang) * xrepeat;
l = tileWidth(tilenum);
k = (l>>1)+xoff;
*x1 -= MulScale(dax,k, 16);
*x2 = *x1 + MulScale(dax,l, 16);
*y1 -= MulScale(day,k, 16);
*y2 = *y1 + MulScale(day,l, 16);
}
// x1, y1: in/out
// rest x/y: out
template <typename T>
static inline void get_floorspr_points(T const * const spr, int32_t px, int32_t py,
int32_t *x1, int32_t *x2, int32_t *x3, int32_t *x4,
int32_t *y1, int32_t *y2, int32_t *y3, int32_t *y4)
{
const int32_t tilenum = spr->picnum;
const int32_t cosang = bcos(spr->ang);
const int32_t sinang = bsin(spr->ang);
vec2_t const span = { tileWidth(tilenum), tileHeight(tilenum)};
vec2_t const repeat = { spr->xrepeat, spr->yrepeat };
vec2_t adjofs = { tileLeftOffset(tilenum) + spr->xoffset, tileTopOffset(tilenum) + spr->yoffset };
if (spr->cstat & 4)
adjofs.x = -adjofs.x;
if (spr->cstat & 8)
adjofs.y = -adjofs.y;
vec2_t const center = { ((span.x >> 1) + adjofs.x) * repeat.x, ((span.y >> 1) + adjofs.y) * repeat.y };
vec2_t const rspan = { span.x * repeat.x, span.y * repeat.y };
vec2_t const ofs = { -MulScale(cosang, rspan.y, 16), -MulScale(sinang, rspan.y, 16) };
*x1 += DMulScale(sinang, center.x, cosang, center.y, 16) - px;
*y1 += DMulScale(sinang, center.y, -cosang, center.x, 16) - py;
*x2 = *x1 - MulScale(sinang, rspan.x, 16);
*y2 = *y1 + MulScale(cosang, rspan.x, 16);
*x3 = *x2 + ofs.x, *x4 = *x1 + ofs.x;
*y3 = *y2 + ofs.y, *y4 = *y1 + ofs.y;
}
inline int widthBits(int num)
{
int w = tileWidth(num);
int j = 15;
while ((j > 1) && ((1 << j) > w))
j--;
return j;
return sizeToBits(tileWidth(num));
}
inline int heightBits(int num)
{
int w = tileHeight(num);
int j = 15;
while ((j > 1) && ((1 << j) > w))
j--;
return j;
return sizeToBits(tileHeight(num));
}

View file

@ -16,18 +16,20 @@
#include "texturemanager.h"
#include "hw_renderstate.h"
#include "printf.h"
#include "hw_voxels.h"
#include "../../glbackend/glbackend.h"
static int32_t curextra=MAXTILES;
#define MIN_CACHETIME_PRINT 10
using namespace Polymost;
int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr, bool rotate);
static int32_t addtileP(int32_t model,int32_t tile,int32_t pallet)
static int32_t addtileP(int32_t ,int32_t tile,int32_t pallet)
{
// tile >= 0 && tile < MAXTILES
UNREFERENCED_PARAMETER(model);
if (curextra==MAXTILES+EXTRATILES-1)
{
Printf("warning: max EXTRATILES reached\n");
@ -97,27 +99,23 @@ void freeallmodels()
if (models)
{
for (i=0; i<nextmodelid; i++) mdfree(models[i]);
DO_FREE_AND_NULL(models);
M_Free(models);
models = nullptr;
nummodelsalloced = 0;
nextmodelid = 0;
}
memset(tile2model,-1,sizeof(tile2model));
for (i=0; i<MAXTILES; i++)
memset(tile2model[i].hudmem, 0, sizeof(tile2model[i].hudmem));
curextra=MAXTILES;
if (vertlist)
{
DO_FREE_AND_NULL(vertlist);
M_Free(vertlist);
vertlist = nullptr;
allocmodelverts = maxmodelverts = 0;
allocmodeltris = maxmodeltris = 0;
}
#ifdef POLYMER
DO_FREE_AND_NULL(tribuf);
#endif
}
void mdinit()
@ -134,7 +132,7 @@ int32_t md_loadmodel(const char *fn)
if (nextmodelid >= nummodelsalloced)
{
ml = (mdmodel_t **)Xrealloc(models,(nummodelsalloced+MODELALLOCGROUP)*sizeof(void *));
ml = (mdmodel_t **)M_Realloc(models,(nummodelsalloced+MODELALLOCGROUP)*sizeof(void *));
models = ml; nummodelsalloced += MODELALLOCGROUP;
}
@ -246,7 +244,7 @@ int32_t md_defineanimation(int32_t modelid, const char *framestart, const char *
ma.fpssc = fpssc;
ma.flags = flags;
map = (mdanim_t *)Xmalloc(sizeof(mdanim_t));
map = (mdanim_t *)M_Malloc(sizeof(mdanim_t));
memcpy(map, &ma, sizeof(ma));
@ -256,110 +254,6 @@ int32_t md_defineanimation(int32_t modelid, const char *framestart, const char *
return 0;
}
#if 0
// FIXME: CURRENTLY DISABLED: interpolation may access frames we consider 'unused'?
int32_t md_thinoutmodel(int32_t modelid, uint8_t *usedframebitmap)
{
md3model_t *m;
md3surf_t *s;
mdanim_t *anm;
int32_t i, surfi, sub, usedframes;
static int16_t otonframe[1024];
if ((uint32_t)modelid >= (uint32_t)nextmodelid) return -1;
m = (md3model_t *)models[modelid];
if (m->mdnum != 3) return -2;
for (anm=m->animations; anm; anm=anm->next)
{
if (anm->endframe <= anm->startframe)
{
// Printf("backward anim %d-%d\n", anm->startframe, anm->endframe);
return -3;
}
for (i=anm->startframe; i<anm->endframe; i++)
usedframebitmap[i>>3] |= pow2char[i&7];
}
sub = 0;
for (i=0; i<m->numframes; i++)
{
if (!(usedframebitmap[i>>3]&pow2char[i&7]))
{
sub++;
otonframe[i] = -1;
continue;
}
otonframe[i] = i-sub;
}
usedframes = m->numframes - sub;
if (usedframes==0 || usedframes==m->numframes)
return usedframes;
//// THIN OUT! ////
for (i=0; i<m->numframes; i++)
{
if (otonframe[i]>=0 && otonframe[i] != i)
{
if (m->muladdframes)
memcpy(&m->muladdframes[2*otonframe[i]], &m->muladdframes[2*i], 2*sizeof(vec3f_t));
memcpy(&m->head.frames[otonframe[i]], &m->head.frames[i], sizeof(md3frame_t));
}
}
for (surfi=0; surfi < m->head.numsurfs; surfi++)
{
s = &m->head.surfs[surfi];
for (i=0; i<m->numframes; i++)
if (otonframe[i]>=0 && otonframe[i] != i)
memcpy(&s->xyzn[otonframe[i]*s->numverts], &s->xyzn[i*s->numverts], s->numverts*sizeof(md3xyzn_t));
}
////// tweak frame indices in various places
for (anm=m->animations; anm; anm=anm->next)
{
if (otonframe[anm->startframe]==-1 || otonframe[anm->endframe-1]==-1)
Printf("md %d WTF: anm %d %d\n", modelid, anm->startframe, anm->endframe);
anm->startframe = otonframe[anm->startframe];
anm->endframe = otonframe[anm->endframe-1];
}
for (i=0; i<MAXTILES+EXTRATILES; i++)
if (tile2model[i].modelid == modelid)
{
if (otonframe[tile2model[i].framenum]==-1)
Printf("md %d WTF: tile %d, fr %d\n", modelid, i, tile2model[i].framenum);
tile2model[i].framenum = otonframe[tile2model[i].framenum];
}
////// realloc & change "numframes" everywhere
if (m->muladdframes)
m->muladdframes = Xrealloc(m->muladdframes, 2*sizeof(vec3f_t)*usedframes);
m->head.frames = Xrealloc(m->head.frames, sizeof(md3frame_t)*usedframes);
for (surfi=0; surfi < m->head.numsurfs; surfi++)
{
m->head.surfs[surfi].numframes = usedframes;
// CAN'T do that because xyzn is offset from a larger block when loaded from md3:
// m->head.surfs[surfi].xyzn = Xrealloc(m->head.surfs[surfi].xyzn, s->numverts*usedframes*sizeof(md3xyzn_t));
}
m->head.numframes = usedframes;
m->numframes = usedframes;
////////////
return usedframes;
}
#endif
int32_t md_defineskin(int32_t modelid, const char *skinfn, int32_t palnum, int32_t skinnum, int32_t surfnum, float param, float specpower, float specfactor, int32_t flags)
{
mdskinmap_t *sk, *skl;
@ -381,7 +275,7 @@ int32_t md_defineskin(int32_t modelid, const char *skinfn, int32_t palnum, int32
break;
if (!sk)
{
sk = (mdskinmap_t *)Xcalloc(1,sizeof(mdskinmap_t));
sk = (mdskinmap_t *)M_Calloc(1,sizeof(mdskinmap_t));
if (!skl) m->skinmap = sk;
else skl->next = sk;
@ -410,9 +304,7 @@ int32_t md_definehud(int32_t modelid, int32_t tilex, vec3f_t add, int32_t angadd
if ((uint32_t)modelid >= (uint32_t)nextmodelid) return -1;
if ((uint32_t)tilex >= (uint32_t)MAXTILES) return -2;
tile2model[tilex].hudmem[(flags>>2)&1] = (hudtyp *)Xmalloc(sizeof(hudtyp));
hudtyp * const hud = tile2model[tilex].hudmem[(flags>>2)&1];
hudtyp * const hud = &tile2model[tilex].hudmem[(flags>>2)&1];
hud->add = add;
hud->angadd = ((int16_t)angadd)|2048;
@ -429,8 +321,6 @@ int32_t md_undefinetile(int32_t tile)
tile2model[tile].modelid = -1;
tile2model[tile].nexttile = -1;
DO_FREE_AND_NULL(tile2model[tile].hudmem[0]);
DO_FREE_AND_NULL(tile2model[tile].hudmem[1]);
return 0;
}
@ -446,8 +336,6 @@ int32_t md_undefinemodel(int32_t modelid)
if (tile2model[i].modelid == modelid)
{
tile2model[i].modelid = -1;
DO_FREE_AND_NULL(tile2model[i].hudmem[0]);
DO_FREE_AND_NULL(tile2model[i].hudmem[1]);
}
if (models)
@ -669,23 +557,23 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
int32_t ournumskins, ournumglcmds;
m = (md2model_t *)Xcalloc(1,sizeof(md2model_t));
m = (md2model_t *)M_Calloc(1,sizeof(md2model_t));
m->mdnum = 2; m->scale = .01f;
fil.Read((char *)&head,sizeof(md2head_t));
#if B_BIG_ENDIAN != 0
head.id = B_LITTLE32(head.id); head.vers = B_LITTLE32(head.vers);
head.skinxsiz = B_LITTLE32(head.skinxsiz); head.skinysiz = B_LITTLE32(head.skinysiz);
head.framebytes = B_LITTLE32(head.framebytes); head.numskins = B_LITTLE32(head.numskins);
head.numverts = B_LITTLE32(head.numverts); head.numuv = B_LITTLE32(head.numuv);
head.numtris = B_LITTLE32(head.numtris); head.numglcmds = B_LITTLE32(head.numglcmds);
head.numframes = B_LITTLE32(head.numframes); head.ofsskins = B_LITTLE32(head.ofsskins);
head.ofsuv = B_LITTLE32(head.ofsuv); head.ofstris = B_LITTLE32(head.ofstris);
head.ofsframes = B_LITTLE32(head.ofsframes); head.ofsglcmds = B_LITTLE32(head.ofsglcmds);
head.ofseof = B_LITTLE32(head.ofseof);
head.id = LittleLong(head.id); head.vers = LittleLong(head.vers);
head.skinxsiz = LittleLong(head.skinxsiz); head.skinysiz = LittleLong(head.skinysiz);
head.framebytes = LittleLong(head.framebytes); head.numskins = LittleLong(head.numskins);
head.numverts = LittleLong(head.numverts); head.numuv = LittleLong(head.numuv);
head.numtris = LittleLong(head.numtris); head.numglcmds = LittleLong(head.numglcmds);
head.numframes = LittleLong(head.numframes); head.ofsskins = LittleLong(head.ofsskins);
head.ofsuv = LittleLong(head.ofsuv); head.ofstris = LittleLong(head.ofstris);
head.ofsframes = LittleLong(head.ofsframes); head.ofsglcmds = LittleLong(head.ofsglcmds);
head.ofseof = LittleLong(head.ofseof);
#endif
if ((head.id != IDP2_MAGIC) || (head.vers != 8)) { Xfree(m); return 0; } //"IDP2"
if ((head.id != IDP2_MAGIC) || (head.vers != 8)) { M_Free(m); return 0; } //"IDP2"
ournumskins = head.numskins ? head.numskins : 1;
ournumglcmds = head.numglcmds ? head.numglcmds : 1;
@ -696,29 +584,29 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
m->numglcmds = head.numglcmds;
m->framebytes = head.framebytes;
m->frames = (char *)Xmalloc(m->numframes*m->framebytes);
m->glcmds = (int32_t *)Xmalloc(ournumglcmds*sizeof(int32_t));
m->tris = (md2tri_t *)Xmalloc(head.numtris*sizeof(md2tri_t));
m->uv = (md2uv_t *)Xmalloc(head.numuv*sizeof(md2uv_t));
m->frames = (char *)M_Malloc(m->numframes*m->framebytes);
m->glcmds = (int32_t *)M_Malloc(ournumglcmds*sizeof(int32_t));
m->tris = (md2tri_t *)M_Malloc(head.numtris*sizeof(md2tri_t));
m->uv = (md2uv_t *)M_Malloc(head.numuv*sizeof(md2uv_t));
fil.Seek(head.ofsframes,FileReader::SeekSet);
if (fil.Read((char *)m->frames,m->numframes*m->framebytes) != m->numframes*m->framebytes)
{ Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; }
{ M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; }
if (m->numglcmds > 0)
{
fil.Seek(head.ofsglcmds,FileReader::SeekSet);
if (fil.Read((char *)m->glcmds,m->numglcmds*sizeof(int32_t)) != (int32_t)(m->numglcmds*sizeof(int32_t)))
{ Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; }
{ M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; }
}
fil.Seek(head.ofstris,FileReader::SeekSet);
if (fil.Read((char *)m->tris,head.numtris*sizeof(md2tri_t)) != (int32_t)(head.numtris*sizeof(md2tri_t)))
{ Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; }
{ M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; }
fil.Seek(head.ofsuv,FileReader::SeekSet);
if (fil.Read((char *)m->uv,head.numuv*sizeof(md2uv_t)) != (int32_t)(head.numuv*sizeof(md2uv_t)))
{ Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; }
{ M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; }
#if B_BIG_ENDIAN != 0
{
@ -730,27 +618,27 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
{
fr = (md2frame_t *)f;
l = (int32_t *)&fr->mul;
for (j=5; j>=0; j--) l[j] = B_LITTLE32(l[j]);
for (j=5; j>=0; j--) l[j] = LittleLong(l[j]);
f += m->framebytes;
}
for (i = m->numglcmds-1; i>=0; i--)
{
m->glcmds[i] = B_LITTLE32(m->glcmds[i]);
m->glcmds[i] = LittleLong(m->glcmds[i]);
}
for (i = head.numtris-1; i>=0; i--)
{
m->tris[i].v[0] = B_LITTLE16(m->tris[i].v[0]);
m->tris[i].v[1] = B_LITTLE16(m->tris[i].v[1]);
m->tris[i].v[2] = B_LITTLE16(m->tris[i].v[2]);
m->tris[i].u[0] = B_LITTLE16(m->tris[i].u[0]);
m->tris[i].u[1] = B_LITTLE16(m->tris[i].u[1]);
m->tris[i].u[2] = B_LITTLE16(m->tris[i].u[2]);
m->tris[i].v[0] = LittleShort(m->tris[i].v[0]);
m->tris[i].v[1] = LittleShort(m->tris[i].v[1]);
m->tris[i].v[2] = LittleShort(m->tris[i].v[2]);
m->tris[i].u[0] = LittleShort(m->tris[i].u[0]);
m->tris[i].u[1] = LittleShort(m->tris[i].u[1]);
m->tris[i].u[2] = LittleShort(m->tris[i].u[2]);
}
for (i = head.numuv-1; i>=0; i--)
{
m->uv[i].u = B_LITTLE16(m->uv[i].u);
m->uv[i].v = B_LITTLE16(m->uv[i].v);
m->uv[i].u = LittleShort(m->uv[i].u);
m->uv[i].v = LittleShort(m->uv[i].v);
}
}
#endif
@ -760,15 +648,15 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
if ((st[i] == '/') || (st[i] == '\\')) { i++; break; }
if (i<0) i=0;
st[i] = 0;
m->basepath = (char *)Xmalloc(i+1);
m->basepath = (char *)M_Malloc(i+1);
strcpy(m->basepath, st);
m->skinfn = (char *)Xmalloc(ournumskins*64);
m->skinfn = (char *)M_Malloc(ournumskins*64);
if (m->numskins > 0)
{
fil.Seek(head.ofsskins,FileReader::SeekSet);
if (fil.Read(m->skinfn,64*m->numskins) != 64*m->numskins)
{ Xfree(m->glcmds); Xfree(m->frames); Xfree(m); return 0; }
{ M_Free(m->glcmds); M_Free(m->frames); M_Free(m); return 0; }
}
maxmodelverts = max(maxmodelverts, m->numverts);
@ -778,7 +666,7 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
// the MD2 is now loaded internally - let's begin the MD3 conversion process
//Printf("Beginning md3 conversion.\n");
m3 = (md3model_t *)Xcalloc(1, sizeof(md3model_t));
m3 = (md3model_t *)M_Calloc(1, sizeof(md3model_t));
m3->mdnum = 3; m3->texture = nullptr; m3->scale = m->scale;
m3->head.id = IDP3_MAGIC; m3->head.vers = 15;
@ -791,8 +679,8 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
m3->numskins = m3->head.numskins;
m3->numframes = m3->head.numframes;
m3->head.frames = (md3frame_t *)Xcalloc(m3->head.numframes, sizeof(md3frame_t));
m3->muladdframes = (vec3f_t *)Xcalloc(m->numframes * 2, sizeof(vec3f_t));
m3->head.frames = (md3frame_t *)M_Calloc(m3->head.numframes, sizeof(md3frame_t));
m3->muladdframes = (vec3f_t *)M_Calloc(m->numframes * 2, sizeof(vec3f_t));
f = (md2frame_t *)(m->frames);
@ -810,7 +698,7 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
m3->head.tags = NULL;
m3->head.surfs = (md3surf_t *)Xcalloc(1, sizeof(md3surf_t));
m3->head.surfs = (md3surf_t *)M_Calloc(1, sizeof(md3surf_t));
s = m3->head.surfs;
// model converting
@ -827,9 +715,9 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
s->shaders = NULL;
s->tris = (md3tri_t *)Xcalloc(head.numtris, sizeof(md3tri_t));
s->uv = (md3uv_t *)Xcalloc(s->numverts, sizeof(md3uv_t));
s->xyzn = (md3xyzn_t *)Xcalloc(s->numverts * m->numframes, sizeof(md3xyzn_t));
s->tris = (md3tri_t *)M_Calloc(head.numtris, sizeof(md3tri_t));
s->uv = (md3uv_t *)M_Calloc(s->numverts, sizeof(md3uv_t));
s->xyzn = (md3xyzn_t *)M_Calloc(s->numverts * m->numframes, sizeof(md3xyzn_t));
//memoryusage += (s->numverts * m->numframes * sizeof(md3xyzn_t));
//Printf("Current model geometry memory usage : %i.\n", memoryusage);
@ -874,7 +762,7 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
{
mdskinmap_t *sk;
sk = (mdskinmap_t *)Xcalloc(1,sizeof(mdskinmap_t));
sk = (mdskinmap_t *)M_Calloc(1,sizeof(mdskinmap_t));
sk->palette = 0;
sk->skinnum = 0;
sk->surfnum = 0;
@ -891,12 +779,12 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
m3->skinmap = sk;
}
m3->indexes = (uint16_t *)Xmalloc(sizeof(uint16_t) * s->numtris);
m3->vindexes = (uint16_t *)Xmalloc(sizeof(uint16_t) * s->numtris * 3);
m3->maxdepths = (float *)Xmalloc(sizeof(float) * s->numtris);
m3->indexes = (uint16_t *)M_Malloc(sizeof(uint16_t) * s->numtris);
m3->vindexes = (uint16_t *)M_Malloc(sizeof(uint16_t) * s->numtris * 3);
m3->maxdepths = (float *)M_Malloc(sizeof(float) * s->numtris);
// die MD2 ! DIE !
Xfree(m->skinfn); Xfree(m->basepath); Xfree(m->uv); Xfree(m->tris); Xfree(m->glcmds); Xfree(m->frames); Xfree(m);
M_Free(m->skinfn); M_Free(m->basepath); M_Free(m->uv); M_Free(m->tris); M_Free(m->glcmds); M_Free(m->frames); M_Free(m);
return ((md2model_t *)m3);
}
@ -948,7 +836,7 @@ static md3model_t *md3load(FileReader & fil)
md3model_t *m;
md3surf_t *s;
m = (md3model_t *)Xcalloc(1,sizeof(md3model_t));
m = (md3model_t *)M_Calloc(1,sizeof(md3model_t));
m->mdnum = 3; m->texture = nullptr; m->scale = .01f;
m->muladdframes = NULL;
@ -956,15 +844,15 @@ static md3model_t *md3load(FileReader & fil)
fil.Read(&m->head,SIZEOF_MD3HEAD_T);
#if B_BIG_ENDIAN != 0
m->head.id = B_LITTLE32(m->head.id); m->head.vers = B_LITTLE32(m->head.vers);
m->head.flags = B_LITTLE32(m->head.flags); m->head.numframes = B_LITTLE32(m->head.numframes);
m->head.numtags = B_LITTLE32(m->head.numtags); m->head.numsurfs = B_LITTLE32(m->head.numsurfs);
m->head.numskins = B_LITTLE32(m->head.numskins); m->head.ofsframes = B_LITTLE32(m->head.ofsframes);
m->head.ofstags = B_LITTLE32(m->head.ofstags); m->head.ofssurfs = B_LITTLE32(m->head.ofssurfs);
m->head.eof = B_LITTLE32(m->head.eof);
m->head.id = LittleLong(m->head.id); m->head.vers = LittleLong(m->head.vers);
m->head.flags = LittleLong(m->head.flags); m->head.numframes = LittleLong(m->head.numframes);
m->head.numtags = LittleLong(m->head.numtags); m->head.numsurfs = LittleLong(m->head.numsurfs);
m->head.numskins = LittleLong(m->head.numskins); m->head.ofsframes = LittleLong(m->head.ofsframes);
m->head.ofstags = LittleLong(m->head.ofstags); m->head.ofssurfs = LittleLong(m->head.ofssurfs);
m->head.eof = LittleLong(m->head.eof);
#endif
if ((m->head.id != IDP3_MAGIC) && (m->head.vers != 15)) { Xfree(m); return 0; } //"IDP3"
if ((m->head.id != IDP3_MAGIC) && (m->head.vers != 15)) { M_Free(m); return 0; } //"IDP3"
m->numskins = m->head.numskins; //<- dead code?
m->numframes = m->head.numframes;
@ -972,19 +860,19 @@ static md3model_t *md3load(FileReader & fil)
ofsurf = m->head.ofssurfs;
fil.Seek(m->head.ofsframes,FileReader::SeekSet); i = m->head.numframes*sizeof(md3frame_t);
m->head.frames = (md3frame_t *)Xmalloc(i);
m->head.frames = (md3frame_t *)M_Malloc(i);
fil.Read(m->head.frames,i);
if (m->head.numtags == 0) m->head.tags = NULL;
else
{
fil.Seek(m->head.ofstags,FileReader::SeekSet); i = m->head.numtags*sizeof(md3tag_t);
m->head.tags = (md3tag_t *)Xmalloc(i);
m->head.tags = (md3tag_t *)M_Malloc(i);
fil.Read(m->head.tags,i);
}
fil.Seek(m->head.ofssurfs,FileReader::SeekSet);
m->head.surfs = (md3surf_t *)Xcalloc(m->head.numsurfs, sizeof(md3surf_t));
m->head.surfs = (md3surf_t *)M_Calloc(m->head.numsurfs, sizeof(md3surf_t));
// NOTE: We assume that NULL is represented by all-zeros.
// surfs[0].geometry is for POLYMER_MD_PROCESS_CHECK (else: crashes).
// surfs[i].geometry is for FREE_SURFS_GEOMETRY.
@ -997,13 +885,13 @@ static md3model_t *md3load(FileReader & fil)
for (i = m->head.numframes-1; i>=0; i--)
{
l = (int32_t *)&m->head.frames[i].min;
for (j=3+3+3+1-1; j>=0; j--) l[j] = B_LITTLE32(l[j]);
for (j=3+3+3+1-1; j>=0; j--) l[j] = LittleLong(l[j]);
}
for (i = m->head.numtags-1; i>=0; i--)
{
l = (int32_t *)&m->head.tags[i].p;
for (j=3+3+3+3-1; j>=0; j--) l[j] = B_LITTLE32(l[j]);
for (j=3+3+3+3-1; j>=0; j--) l[j] = LittleLong(l[j]);
}
}
#endif
@ -1018,9 +906,9 @@ static md3model_t *md3load(FileReader & fil)
#if B_BIG_ENDIAN != 0
{
int32_t j, *l;
s->id = B_LITTLE32(s->id);
s->id = LittleLong(s->id);
l = (int32_t *)&s->flags;
for (j=1+1+1+1+1+1+1+1+1+1-1; j>=0; j--) l[j] = B_LITTLE32(l[j]);
for (j=1+1+1+1+1+1+1+1+1+1-1; j>=0; j--) l[j] = LittleLong(l[j]);
}
#endif
@ -1037,7 +925,7 @@ static md3model_t *md3load(FileReader & fil)
//memoryusage += (s->numverts * s->numframes * sizeof(md3xyzn_t));
//Printf("Current model geometry memory usage : %i.\n", memoryusage);
s->tris = (md3tri_t *)Xmalloc((leng[0] + leng[1]) + (leng[2] + leng[3]));
s->tris = (md3tri_t *)M_Malloc((leng[0] + leng[1]) + (leng[2] + leng[3]));
s->shaders = (md3shader_t *)(((intptr_t)s->tris)+leng[0]);
s->uv = (md3uv_t *)(((intptr_t)s->shaders)+leng[1]);
@ -1054,23 +942,23 @@ static md3model_t *md3load(FileReader & fil)
for (i=s->numtris-1; i>=0; i--)
{
for (j=2; j>=0; j--) s->tris[i].i[j] = B_LITTLE32(s->tris[i].i[j]);
for (j=2; j>=0; j--) s->tris[i].i[j] = LittleLong(s->tris[i].i[j]);
}
for (i=s->numshaders-1; i>=0; i--)
{
s->shaders[i].i = B_LITTLE32(s->shaders[i].i);
s->shaders[i].i = LittleLong(s->shaders[i].i);
}
for (i=s->numverts-1; i>=0; i--)
{
l = (int32_t *)&s->uv[i].u;
l[0] = B_LITTLE32(l[0]);
l[1] = B_LITTLE32(l[1]);
l[0] = LittleLong(l[0]);
l[1] = LittleLong(l[1]);
}
for (i=s->numframes*s->numverts-1; i>=0; i--)
{
s->xyzn[i].x = (int16_t)B_LITTLE16((uint16_t)s->xyzn[i].x);
s->xyzn[i].y = (int16_t)B_LITTLE16((uint16_t)s->xyzn[i].y);
s->xyzn[i].z = (int16_t)B_LITTLE16((uint16_t)s->xyzn[i].z);
s->xyzn[i].x = (int16_t)LittleShort((uint16_t)s->xyzn[i].x);
s->xyzn[i].y = (int16_t)LittleShort((uint16_t)s->xyzn[i].y);
s->xyzn[i].z = (int16_t)LittleShort((uint16_t)s->xyzn[i].z);
}
}
#endif
@ -1080,9 +968,9 @@ static md3model_t *md3load(FileReader & fil)
ofsurf += s->ofsend;
}
m->indexes = (uint16_t *)Xmalloc(sizeof(uint16_t) * maxtrispersurf);
m->vindexes = (uint16_t *)Xmalloc(sizeof(uint16_t) * maxtrispersurf * 3);
m->maxdepths = (float *)Xmalloc(sizeof(float) * maxtrispersurf);
m->indexes = (uint16_t *)M_Malloc(sizeof(uint16_t) * maxtrispersurf);
m->vindexes = (uint16_t *)M_Malloc(sizeof(uint16_t) * maxtrispersurf * 3);
m->maxdepths = (float *)M_Malloc(sizeof(float) * maxtrispersurf);
return m;
}
@ -1219,7 +1107,7 @@ void md3_vox_calcmat_common(tspriteptr_t tspr, const vec3f_t *a0, float f, float
}
static void md3draw_handle_triangles(const md3surf_t *s, uint16_t *indexhandle,
int32_t texunits, const md3model_t *M)
int32_t , const md3model_t *M)
{
int32_t i;
@ -1241,10 +1129,6 @@ static void md3draw_handle_triangles(const md3surf_t *s, uint16_t *indexhandle,
}
}
GLInterface.Draw(DT_Triangles, data.second, s->numtris *3);
#ifndef USE_GLEXT
UNREFERENCED_PARAMETER(texunits);
#endif
}
static int32_t polymost_md3draw(md3model_t *m, tspriteptr_t tspr)
@ -1261,9 +1145,6 @@ static int32_t polymost_md3draw(md3model_t *m, tspriteptr_t tspr)
const uint8_t lpal = ((unsigned)owner < MAXSPRITES) ? sprite[tspr->owner].pal : tspr->pal;
const int32_t sizyrep = tileHeight(tspr->picnum) * tspr->yrepeat;
polymost_outputGLDebugMessage(3, "polymost_md3draw(m:%p, tspr:%p)", m, tspr);
// if ((tspr->cstat&48) == 32) return 0;
updateanimation((md2model_t *)m, tspr, lpal);
//create current&next frame's vertex list from whole list
@ -1558,12 +1439,12 @@ static void md3free(md3model_t *m)
for (anim=m->animations; anim; anim=nanim)
{
nanim = anim->next;
Xfree(anim);
M_Free(anim);
}
for (sk=m->skinmap; sk; sk=nsk)
{
nsk = sk->next;
Xfree(sk);
M_Free(sk);
}
if (m->head.surfs)
@ -1571,21 +1452,21 @@ static void md3free(md3model_t *m)
for (bssize_t surfi=m->head.numsurfs-1; surfi>=0; surfi--)
{
md3surf_t *s = &m->head.surfs[surfi];
Xfree(s->tris);
Xfree(s->geometry); // FREE_SURFS_GEOMETRY
M_Free(s->tris);
M_Free(s->geometry); // FREE_SURFS_GEOMETRY
}
Xfree(m->head.surfs);
M_Free(m->head.surfs);
}
Xfree(m->head.tags);
Xfree(m->head.frames);
M_Free(m->head.tags);
M_Free(m->head.frames);
Xfree(m->muladdframes);
M_Free(m->muladdframes);
Xfree(m->indexes);
Xfree(m->vindexes);
Xfree(m->maxdepths);
M_Free(m->indexes);
M_Free(m->vindexes);
M_Free(m->maxdepths);
Xfree(m);
M_Free(m);
}
//---------------------------------------- MD3 LIBRARY ENDS ----------------------------------------
@ -1607,7 +1488,7 @@ static mdmodel_t *mdload(const char *filnam)
fil.Read(&i,4);
fil.Seek(0,FileReader::SeekSet);
switch (B_LITTLE32(i))
switch (LittleLong(i))
{
case IDP2_MAGIC:
// Printf("Warning: model \"%s\" is version IDP2; wanted version IDP3\n",filnam);
@ -1641,7 +1522,7 @@ int32_t polymost_mddraw(tspriteptr_t tspr)
{
if (maxmodelverts > allocmodelverts)
{
vertlist = (vec3f_t *) Xrealloc(vertlist, sizeof(vec3f_t)*maxmodelverts);
vertlist = (vec3f_t *) M_Realloc(vertlist, sizeof(vec3f_t)*maxmodelverts);
allocmodelverts = maxmodelverts;
}
@ -1654,6 +1535,11 @@ int32_t polymost_mddraw(tspriteptr_t tspr)
return 0;
}
void voxfree(voxmodel_t* m)
{
if (m) delete m;
}
static void mdfree(mdmodel_t *vm)
{
if (vm->mdnum == 1) { voxfree((voxmodel_t *)vm); return; }

File diff suppressed because it is too large Load diff

View file

@ -1,209 +0,0 @@
//--------------------------------------- VOX LIBRARY BEGINS ---------------------------------------
#ifdef USE_OPENGL
#include "compat.h"
#include "build.h"
#include "engine_priv.h"
#include "polymost.h"
#include "mdsprite.h"
#include "v_video.h"
#include "flatvertices.h"
#include "hw_renderstate.h"
#include "texturemanager.h"
#include "voxels.h"
#include "gamecontrol.h"
#include "glbackend/gl_models.h"
#include "palette.h"
#include "../../glbackend/glbackend.h"
void voxfree(voxmodel_t *m)
{
if (!m)
return;
Xfree(m);
}
voxmodel_t *voxload(int lumpnum)
{
FVoxel* voxel = R_LoadKVX(lumpnum);
if (voxel != nullptr)
{
voxmodel_t* vm = (voxmodel_t*)Xmalloc(sizeof(voxmodel_t));
memset(vm, 0, sizeof(voxmodel_t));
auto pivot = voxel->Mips[0].Pivot;
vm->mdnum = 1; //VOXel model id
vm->scale = vm->bscale = 1.f;
vm->piv.x = float(pivot.X);
vm->piv.y = float(pivot.Y);
vm->piv.z = float(pivot.Z);
vm->siz.x = voxel->Mips[0].SizeX;
vm->siz.y = voxel->Mips[0].SizeY;
vm->siz.z = voxel->Mips[0].SizeZ;
vm->is8bit = true;
voxel->Mips[0].Pivot.Zero(); // Needs to be taken out of the voxel data because it gets baked into the vertex buffer which we cannot use here.
vm->model = new FVoxelModel(voxel, true);
return vm;
}
return nullptr;
}
//Draw voxel model as perfect cubes
int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr, bool rotate)
{
float f, g, k0, zoff;
if ((intptr_t)m == (intptr_t)(-1)) // hackhackhack
return 0;
if ((tspr->cstat & 48) == 32)
return 0;
polymost_outputGLDebugMessage(3, "polymost_voxdraw(m:%p, tspr:%p)", m, tspr);
//updateanimation((md2model *)m,tspr);
if ((tspr->cstat & CSTAT_SPRITE_MDLROTATE) || rotate)
{
int myclock = (PlayClock << 3) + MulScale(4 << 3, pm_smoothratio, 16);
tspr->ang = (tspr->ang + myclock) & 2047; // will be applied in md3_vox_calcmat_common.
}
vec3f_t m0 = { m->scale, m->scale, m->scale };
vec3f_t a0 = { 0, 0, m->zadd*m->scale };
k0 = m->bscale / 64.f;
f = (float) tspr->xrepeat * (256.f/320.f) * k0;
if ((sprite[tspr->owner].cstat&48)==16)
{
f *= 1.25f;
a0.y -= tspr->xoffset * bcosf(spriteext[tspr->owner].angoff, -20);
a0.x += tspr->xoffset * bsinf(spriteext[tspr->owner].angoff, -20);
}
if (globalorientation&8) { m0.z = -m0.z; a0.z = -a0.z; } //y-flipping
if (globalorientation&4) { m0.x = -m0.x; a0.x = -a0.x; a0.y = -a0.y; } //x-flipping
m0.x *= f; a0.x *= f; f = -f;
m0.y *= f; a0.y *= f;
f = (float) tspr->yrepeat * k0;
m0.z *= f; a0.z *= f;
k0 = (float) (tspr->z+spriteext[tspr->owner].position_offset.z);
f = ((globalorientation&8) && (sprite[tspr->owner].cstat&48)!=0) ? -4.f : 4.f;
k0 -= (tspr->yoffset*tspr->yrepeat)*f*m->bscale;
zoff = m->siz.z*.5f;
if (!(tspr->cstat&128))
zoff += m->piv.z;
else if ((tspr->cstat&48) != 48)
{
zoff += m->piv.z;
zoff -= m->siz.z*.5f;
}
if (globalorientation&8) zoff = m->siz.z-zoff;
f = (65536.f*512.f) / ((float)xdimen*viewingrange);
g = 32.f / ((float)xdimen*gxyaspect);
int const shadowHack = !!(tspr->clipdist & TSPR_FLAGS_MDHACK);
m0.y *= f; a0.y = (((float)(tspr->x+spriteext[tspr->owner].position_offset.x-globalposx)) * (1.f/1024.f) + a0.y) * f;
m0.x *=-f; a0.x = (((float)(tspr->y+spriteext[tspr->owner].position_offset.y-globalposy)) * -(1.f/1024.f) + a0.x) * -f;
m0.z *= g; a0.z = (((float)(k0 -globalposz - shadowHack)) * -(1.f/16384.f) + a0.z) * g;
float mat[16];
md3_vox_calcmat_common(tspr, &a0, f, mat);
//Mirrors
if (grhalfxdown10x < 0)
{
mat[0] = -mat[0];
mat[4] = -mat[4];
mat[8] = -mat[8];
mat[12] = -mat[12];
}
if (shadowHack)
{
GLInterface.SetDepthFunc(DF_LEqual);
}
int winding = ((grhalfxdown10x >= 0) ^ ((globalorientation & 8) != 0) ^ ((globalorientation & 4) != 0)) ? Winding_CW : Winding_CCW;
GLInterface.SetCull(Cull_Back, winding);
float pc[4];
if (!shadowHack)
{
pc[3] = (tspr->cstat & 2) ? glblend[tspr->blend].def[!!(tspr->cstat & 512)].alpha : 1.0f;
pc[3] *= 1.0f - spriteext[tspr->owner].alpha;
SetRenderStyleFromBlend(!!(tspr->cstat & 2), tspr->blend, !!(tspr->cstat & 512));
if (!(tspr->cstat & 2) || spriteext[tspr->owner].alpha > 0.f || pc[3] < 1.0f)
GLInterface.EnableBlend(true); // else GLInterface.EnableBlend(false);
}
else pc[3] = 1.f;
GLInterface.SetShade(std::max(0, globalshade), numshades);
pc[0] = (float)globalr * (1.f / 255.f);
pc[1] = (float)globalg * (1.f / 255.f);
pc[2] = (float)globalb * (1.f / 255.f);
GLInterface.SetColor(pc[0], pc[1], pc[2], pc[3]);
//------------
//transform to Build coords
float omat[16];
memcpy(omat, mat, sizeof(omat));
f = 1.f/64.f;
g = m0.x*f; mat[0] *= g; mat[1] *= g; mat[2] *= g;
g = m0.y*f; mat[4] = omat[8]*g; mat[5] = omat[9]*g; mat[6] = omat[10]*g;
g =-m0.z*f; mat[8] = omat[4]*g; mat[9] = omat[5]*g; mat[10] = omat[6]*g;
//
mat[12] -= (m->piv.x*mat[0] + m->piv.y*mat[4] + zoff*mat[8]);
mat[13] -= (m->piv.x*mat[1] + m->piv.y*mat[5] + zoff*mat[9]);
mat[14] -= (m->piv.x*mat[2] + m->piv.y*mat[6] + zoff*mat[10]);
//
//Let OpenGL (and perhaps hardware :) handle the matrix rotation
mat[3] = mat[7] = mat[11] = 0.f; mat[15] = 1.f;
for (int i = 0; i < 15; i++) mat[i] *= 1024.f;
// Adjust to backend coordinate system being used by the vertex buffer.
for (int i = 4; i < 8; i++)
{
float f = mat[i];
mat[i] = -mat[i + 4];
mat[i + 4] = -f;
}
GLInterface.SetMatrix(Matrix_Model, mat);
int palId = TRANSLATION(Translation_Remap + curbasepal, globalpal);
GLInterface.SetPalswap(globalpal);
GLInterface.SetFade(sector[tspr->sectnum].floorpal);
auto tex = TexMan.GetGameTexture(m->model->GetPaletteTexture());
GLInterface.SetTexture(tex, TRANSLATION(Translation_Remap + curbasepal, globalpal), CLAMP_NOFILTER_XY, true);
GLInterface.SetModel(m->model, 0, 0, 0);
GLInterface.Draw(DT_Triangles, 0, 0);
GLInterface.SetModel(nullptr, 0, 0, 0);
GLInterface.SetCull(Cull_None);
if (shadowHack)
{
GLInterface.SetDepthFunc(DF_Less);
}
GLInterface.SetIdentityMatrix(Matrix_Model);
GLInterface.SetFadeDisable(false);
return 1;
}
#endif
//---------------------------------------- VOX LIBRARY ENDS ----------------------------------------

View file

@ -739,12 +739,12 @@ void F2DDrawer::AddPoly(FGameTexture *texture, FVector2 *points, int npoints,
//
//==========================================================================
void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, unsigned int* ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2)
void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, const unsigned int* ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2)
{
RenderCommand dg;
int method = 0;
if (!img->isValid()) return;
if (!img || !img->isValid()) return;
dg.mType = DrawTypeTriangles;
if (clipx1 > 0 || clipy1 > 0 || clipx2 < GetWidth() - 1 || clipy2 < GetHeight() - 1)
@ -770,14 +770,28 @@ void F2DDrawer::AddPoly(FGameTexture* img, FVector4* vt, size_t vtcount, unsigne
Set(ptr, vt[i].X, vt[i].Y, 0.f, vt[i].Z, vt[i].W, color);
ptr++;
}
dg.mIndexIndex = mIndices.Size();
mIndices.Reserve(idxcount);
for (size_t i = 0; i < idxcount; i++)
if (idxcount > 0)
{
mIndices[dg.mIndexIndex + i] = ind[i] + dg.mVertIndex;
mIndices.Reserve(idxcount);
for (size_t i = 0; i < idxcount; i++)
{
mIndices[dg.mIndexIndex + i] = ind[i] + dg.mVertIndex;
}
dg.mIndexCount = (int)idxcount;
}
else
{
// If we have no index buffer, treat this as an unindexed list of triangles.
mIndices.Reserve(vtcount);
for (size_t i = 0; i < vtcount; i++)
{
mIndices[dg.mIndexIndex + i] = int(i + dg.mVertIndex);
}
dg.mIndexCount = (int)vtcount;
}
dg.mIndexCount = (int)idxcount;
AddCommand(&dg);
}

View file

@ -189,7 +189,7 @@ public:
void AddPoly(FGameTexture *texture, FVector2 *points, int npoints,
double originx, double originy, double scalex, double scaley,
DAngle rotation, const FColormap &colormap, PalEntry flatcolor, double lightlevel, uint32_t *indices, size_t indexcount);
void AddPoly(FGameTexture* img, FVector4 *vt, size_t vtcount, unsigned int *ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2);
void AddPoly(FGameTexture* img, FVector4 *vt, size_t vtcount, const unsigned int *ind, size_t idxcount, int translation, PalEntry color, FRenderStyle style, int clipx1, int clipy1, int clipx2, int clipy2);
void FillPolygon(int* rx1, int* ry1, int* xb1, int32_t npoints, int picnum, int palette, int shade, int props, const FVector2& xtex, const FVector2& ytex, const FVector2& otex,
int clipx1, int clipy1, int clipx2, int clipy2);
void AddFlatFill(int left, int top, int right, int bottom, FGameTexture *src, int local_origin = false, double flatscale = 1.0, PalEntry color = 0xffffffff, ERenderStyle rs = STYLE_Normal);

View file

@ -50,7 +50,7 @@ CVAR(Bool, ui_screenborder_classic_scaling, true, CVAR_ARCHIVE)
// nonsense that graphics should not be able to actually use that extra screen space.
extern bool setsizeneeded;
CUSTOM_CVAR(Bool, vid_allowtrueultrawide, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL)
CUSTOM_CVAR(Int, vid_allowtrueultrawide, 1, CVAR_ARCHIVE|CVAR_NOINITCALL)
{
setsizeneeded = true;
}
@ -324,6 +324,22 @@ DEFINE_ACTION_FUNCTION(_Screen, ClearClipRect)
return 0;
}
DEFINE_ACTION_FUNCTION(_Screen, ClearScreen)
{
PARAM_PROLOGUE;
twod->ClearScreen();
return 0;
}
DEFINE_ACTION_FUNCTION(_Screen, SetScreenFade)
{
PARAM_PROLOGUE;
PARAM_FLOAT(x);
twod->SetScreenFade(float(x));
return 0;
}
void F2DDrawer::GetClipRect(int *x, int *y, int *w, int *h)
{
if (x) *x = clipleft;
@ -1263,11 +1279,20 @@ static void VirtualToRealCoords(F2DDrawer *drawer, double Width, double Height,
{
float myratio = float(handleaspect ? ActiveRatio (Width, Height) : (4.0 / 3.0));
// if 21:9 AR, map to 16:9 for all callers.
// this allows for black bars and stops the stretching of fullscreen images
if ((myratio > 1.7f) && !vid_allowtrueultrawide) {
myratio = 16.0f / 9.0f;
}
// if 21:9 AR, map to 16:9 for all callers.
// this allows for black bars and stops the stretching of fullscreen images
switch (vid_allowtrueultrawide)
{
case 1:
default:
myratio = MIN(64.0f / 27.0f, myratio);
break;
case 0:
myratio = MIN(16.0f / 9.0f, myratio);
case -1:
break;
}
double right = x + w;
double bottom = y + h;

View file

@ -138,6 +138,10 @@ void S_StopCustomStream(SoundStream *stream)
void S_PauseAllCustomStreams(bool on)
{
static bool paused = false;
if (paused == on) return;
paused = on;
for (auto s : customStreams)
{
s->SetPaused(on);

View file

@ -293,8 +293,8 @@ CCMD (md5sum)
}
for (int i = 1; i < argv.argc(); ++i)
{
FileReader fr;
if (!fr.OpenFile(argv[i]))
FileReader fr = fileSystem.OpenFileReader(argv[i]);
if (!fr.isOpen())
{
Printf("%s: %s\n", argv[i], strerror(errno));
}

View file

@ -1107,4 +1107,4 @@ xy(menu_change, "menu/change")
xy(menu_advance, "menu/advance")
xx(zoomsize)
xx(ScreenJobRunner)

View file

@ -1287,6 +1287,42 @@ void FScanner::AddSymbol(const char* name, double value)
symbols.Insert(name, sym);
}
//==========================================================================
//
//
//
//==========================================================================
int FScanner::StartBraces(FScanner::SavedPos* braceend)
{
if (CheckString("{"))
{
auto here = SavePos();
SkipToEndOfBlock();
*braceend = SavePos();
RestorePos(here);
return 0;
}
else
{
ScriptError("'{' expected");
return -1;
}
}
//==========================================================================
//
//
//
//==========================================================================
bool FScanner::FoundEndBrace(FScanner::SavedPos& braceend)
{
auto here = SavePos();
return here.SavedScriptPtr >= braceend.SavedScriptPtr;
}
//==========================================================================
//
// a class that remembers a parser position

View file

@ -94,6 +94,8 @@ public:
inline void AddSymbol(const char* name, uint32_t value) { return AddSymbol(name, uint64_t(value)); }
void AddSymbol(const char* name, double value);
void SkipToEndOfBlock();
int StartBraces(FScanner::SavedPos* braceend);
bool FoundEndBrace(FScanner::SavedPos& braceend);
static FString TokenName(int token, const char *string=NULL);
@ -120,7 +122,30 @@ public:
return true;
}
bool GetNumber(int64_t& var, bool evaluate = false)
{
if (!GetNumber(evaluate)) return false;
var = BigNumber;
return true;
}
bool GetString(FString& var)
{
if (!GetString()) return false;
var = String;
return true;
}
bool GetFloat(bool evaluate = false);
bool GetFloat(double& var, bool evaluate = false)
{
if (!GetFloat(evaluate)) return false;
var = Float;
return true;
}
void MustGetFloat(bool evaluate = false);
bool CheckFloat(bool evaluate = false);

View file

@ -61,12 +61,9 @@
// MACROS ------------------------------------------------------------------
// Requires SDL 2.0.6 or newer
//#define SDL2_STATIC_LIBRARY
#if defined SDL2_STATIC_LIBRARY && defined HAVE_VULKAN
#if defined HAVE_VULKAN
#include <SDL_vulkan.h>
#endif // SDL2_STATIC_LIBRARY && HAVE_VULKAN
#endif // HAVE_VULKAN
// TYPES -------------------------------------------------------------------
@ -118,30 +115,7 @@ CCMD(vid_list_sdl_render_drivers)
namespace Priv
{
#ifdef SDL2_STATIC_LIBRARY
#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \
RESULT(*NAME)(__VA_ARGS__) = SDL_ ## NAME
#else // !SDL2_STATIC_LIBRARY
FModule library("SDL2");
#define SDL2_OPTIONAL_FUNCTION(RESULT, NAME, ...) \
static TOptProc<library, RESULT(*)(__VA_ARGS__)> NAME("SDL_" #NAME)
#endif // SDL2_STATIC_LIBRARY
SDL2_OPTIONAL_FUNCTION(int, GetWindowBordersSize, SDL_Window *window, int *top, int *left, int *bottom, int *right);
#ifdef HAVE_VULKAN
SDL2_OPTIONAL_FUNCTION(void, Vulkan_GetDrawableSize, SDL_Window *window, int *width, int *height);
SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_GetInstanceExtensions, SDL_Window *window, unsigned int *count, const char **names);
SDL2_OPTIONAL_FUNCTION(SDL_bool, Vulkan_CreateSurface, SDL_Window *window, VkInstance instance, VkSurfaceKHR *surface);
#endif
#undef SDL2_OPTIONAL_FUNCTION
static const uint32_t VulkanWindowFlag = 0x1000'0000;
static const uint32_t VulkanWindowFlag = SDL_WINDOW_VULKAN;
SDL_Window *window;
bool vulkanEnabled;
@ -240,22 +214,21 @@ void I_GetVulkanDrawableSize(int *width, int *height)
{
assert(Priv::vulkanEnabled);
assert(Priv::window != nullptr);
assert(Priv::Vulkan_GetDrawableSize);
Priv::Vulkan_GetDrawableSize(Priv::window, width, height);
SDL_Vulkan_GetDrawableSize(Priv::window, width, height);
}
bool I_GetVulkanPlatformExtensions(unsigned int *count, const char **names)
{
assert(Priv::vulkanEnabled);
assert(Priv::window != nullptr);
return Priv::Vulkan_GetInstanceExtensions(Priv::window, count, names) == SDL_TRUE;
return SDL_Vulkan_GetInstanceExtensions(Priv::window, count, names) == SDL_TRUE;
}
bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface)
{
assert(Priv::vulkanEnabled);
assert(Priv::window != nullptr);
return Priv::Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE;
return SDL_Vulkan_CreateSurface(Priv::window, instance, surface) == SDL_TRUE;
}
#endif
@ -419,20 +392,19 @@ SDLVideo::SDLVideo ()
return;
}
#ifndef SDL2_STATIC_LIBRARY
// Load optional SDL functions
if (!Priv::library.IsLoaded())
// Fail gracefully if we somehow reach here after linking against a SDL2 library older than 2.0.6.
SDL_version sdlver;
SDL_GetVersion(&sdlver);
if (!(sdlver.patch >= 6))
{
Priv::library.Load({ "libSDL2-2.0.so.0", "libSDL2-2.0.so", "libSDL2.so" });
I_FatalError("Only SDL 2.0.6 or later is supported.");
}
#endif // !SDL2_STATIC_LIBRARY
#ifdef HAVE_SOFTPOLY
Priv::softpolyEnabled = vid_preferbackend == 2;
#endif
#ifdef HAVE_VULKAN
Priv::vulkanEnabled = vid_preferbackend == 1
&& Priv::Vulkan_GetDrawableSize && Priv::Vulkan_GetInstanceExtensions && Priv::Vulkan_CreateSurface;
Priv::vulkanEnabled = vid_preferbackend == 1;
if (Priv::vulkanEnabled)
{
@ -539,7 +511,7 @@ int SystemBaseFrameBuffer::GetClientWidth()
#ifdef HAVE_VULKAN
assert(Priv::vulkanEnabled);
Priv::Vulkan_GetDrawableSize(Priv::window, &width, nullptr);
SDL_Vulkan_GetDrawableSize(Priv::window, &width, nullptr);
#endif
return width;
@ -562,7 +534,7 @@ int SystemBaseFrameBuffer::GetClientHeight()
#ifdef HAVE_VULKAN
assert(Priv::vulkanEnabled);
Priv::Vulkan_GetDrawableSize(Priv::window, nullptr, &height);
SDL_Vulkan_GetDrawableSize(Priv::window, nullptr, &height);
#endif
return height;
@ -745,10 +717,10 @@ void ProcessSDLWindowEvent(const SDL_WindowEvent &event)
break;
case SDL_WINDOWEVENT_MOVED:
if (!vid_fullscreen && Priv::GetWindowBordersSize)
if (!vid_fullscreen)
{
int top = 0, left = 0;
Priv::GetWindowBordersSize(Priv::window, &top, &left, nullptr, nullptr);
SDL_GetWindowBordersSize(Priv::window, &top, &left, nullptr, nullptr);
win_x = event.data1-left;
win_y = event.data2-top;
}

View file

@ -371,7 +371,6 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer()
SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE);
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
I_GetEvent();
static_cast<Win32BaseVideo *>(Video)->Shutdown();
}

View file

@ -9,7 +9,6 @@
class FRenderState;
struct secplane_t;
struct subsector_t;
struct FFlatVertex
{

View file

@ -4,8 +4,6 @@
#include "tarray.h"
#include "vectors.h"
struct FLevelLocals;
namespace hwrenderer
{

View file

@ -62,13 +62,7 @@
#include "hw_renderstate.h"
#include "v_video.h"
#include "hwrenderer/data/buffers.h"
// 57 world units roughly represent one sky texel for the glTranslate call.
enum
{
skyoffsetfactor = 57
};
#include "version.h"
//-----------------------------------------------------------------------------
//
@ -150,7 +144,7 @@ FSkyVertexBuffer::~FSkyVertexBuffer()
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip)
void FSkyVertexBuffer::SkyVertexDoom(int r, int c, bool zflip)
{
static const FAngle maxSideAngle = 60.f;
static const float scale = 10000.;
@ -187,6 +181,37 @@ void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip)
mVertices.Push(vert);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::SkyVertexBuild(int r, int c, bool zflip)
{
static const FAngle maxSideAngle = 60.f;
static const float scale = 10000.;
FAngle topAngle = (c / (float)mColumns * 360.f);
FVector2 pos = topAngle.ToVector(scale);
float z = (!zflip) ? (mRows - r) * 4000.f : -(mRows - r) * 4000.f;
FSkyVertex vert;
vert.color = r == 0 ? 0xffffff : 0xffffffff;
// And the texture coordinates.
if (zflip) r = mRows * 2 - r;
vert.u = 0.5f + (-c / (float)mColumns);
vert.v = (r / (float)(2*mRows));
// And finally the vertex.
vert.x = pos.X;
vert.y = z - 1.f;
vert.z = pos.Y;
mVertices.Push(vert);
}
//-----------------------------------------------------------------------------
//
@ -194,27 +219,58 @@ void FSkyVertexBuffer::SkyVertex(int r, int c, bool zflip)
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::CreateSkyHemisphere(int hemi)
void FSkyVertexBuffer::CreateSkyHemisphereDoom(int hemi)
{
int r, c;
bool zflip = !!(hemi & SKYHEMI_LOWER);
mPrimStart.Push(mVertices.Size());
mPrimStartDoom.Push(mVertices.Size());
for (c = 0; c < mColumns; c++)
{
SkyVertex(1, c, zflip);
SkyVertexDoom(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());
mPrimStartDoom.Push(mVertices.Size());
for (c = 0; c <= mColumns; c++)
{
SkyVertex(r + zflip, c, zflip);
SkyVertex(r + 1 - zflip, c, zflip);
SkyVertexDoom(r + zflip, c, zflip);
SkyVertexDoom(r + 1 - zflip, c, zflip);
}
}
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::CreateSkyHemisphereBuild(int hemi)
{
int r, c;
bool zflip = !!(hemi & SKYHEMI_LOWER);
mPrimStartBuild.Push(mVertices.Size());
for (c = 0; c < mColumns; c++)
{
SkyVertexBuild(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++)
{
mPrimStartBuild.Push(mVertices.Size());
for (c = 0; c <= mColumns; c++)
{
SkyVertexBuild(r + zflip, c, zflip);
SkyVertexBuild(r + 1 - zflip, c, zflip);
}
}
}
@ -248,9 +304,13 @@ void FSkyVertexBuffer::CreateDome()
mColumns = 128;
mRows = 4;
CreateSkyHemisphere(SKYHEMI_UPPER);
CreateSkyHemisphere(SKYHEMI_LOWER);
mPrimStart.Push(mVertices.Size());
CreateSkyHemisphereDoom(SKYHEMI_UPPER);
CreateSkyHemisphereDoom(SKYHEMI_LOWER);
mPrimStartDoom.Push(mVertices.Size());
CreateSkyHemisphereBuild(SKYHEMI_UPPER);
CreateSkyHemisphereBuild(SKYHEMI_LOWER);
mPrimStartBuild.Push(mVertices.Size());
mSideStart = mVertices.Size();
mFaceStart[0] = mSideStart + 10;
@ -324,7 +384,7 @@ void FSkyVertexBuffer::CreateDome()
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelMatrix, VSMatrix &textureMatrix, bool tiled)
void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelMatrix, VSMatrix &textureMatrix, bool tiled, float xscale, float yscale)
{
float texw = tex->GetDisplayWidth();
float texh = tex->GetDisplayHeight();
@ -332,37 +392,46 @@ void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_
modelMatrix.loadIdentity();
modelMatrix.rotate(-180.0f + x_offset, 0.f, 1.f, 0.f);
float xscale = texw < 1024.f ? floorf(1024.f / float(texw)) : 1.f;
float yscale = 1.f;
if (xscale == 0) xscale = texw < 1024.f ? floorf(1024.f / float(texw)) : 1.f;
auto texskyoffset = tex->GetSkyOffset() + skyoffset;
if (texh <= 128 && tiled)
if (yscale == 0)
{
modelMatrix.translate(0.f, (-40 + texskyoffset)*skyoffsetfactor, 0.f);
modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f);
yscale = 240.f / texh;
}
else if (texh < 128)
{
// smaller sky textures must be tiled. We restrict it to 128 sky pixels, though
modelMatrix.translate(0.f, -1250.f, 0.f);
modelMatrix.scale(1.f, 128 / 230.f, 1.f);
yscale = float(128 / texh); // intentionally left as integer.
}
else if (texh < 200)
{
modelMatrix.translate(0.f, -1250.f, 0.f);
modelMatrix.scale(1.f, texh / 230.f, 1.f);
}
else if (texh <= 240)
{
modelMatrix.translate(0.f, (200 - texh + texskyoffset)*skyoffsetfactor, 0.f);
modelMatrix.scale(1.f, 1.f + ((texh - 200.f) / 200.f) * 1.17f, 1.f);
if (texh <= 128 && tiled)
{
modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f);
modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f);
yscale = 240.f / texh;
}
else if (texh < 128)
{
// smaller sky textures must be tiled. We restrict it to 128 sky pixels, though
modelMatrix.translate(0.f, -1250.f, 0.f);
modelMatrix.scale(1.f, 128 / 230.f, 1.f);
yscale = float(128 / texh); // intentionally left as integer.
}
else if (texh < 200)
{
modelMatrix.translate(0.f, -1250.f, 0.f);
modelMatrix.scale(1.f, texh / 230.f, 1.f);
yscale = 1.f;
}
else if (texh <= 240)
{
modelMatrix.translate(0.f, (200 - texh + texskyoffset) * skyoffsetfactor, 0.f);
modelMatrix.scale(1.f, 1.f + ((texh - 200.f) / 200.f) * 1.17f, 1.f);
yscale = 1.f;
}
else
{
modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f);
modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f);
yscale = 240.f / texh;
}
}
else
{
modelMatrix.translate(0.f, (-40 + texskyoffset)*skyoffsetfactor, 0.f);
modelMatrix.scale(1.f, 1.2f * 1.17f, 1.f);
yscale = 240.f / texh;
modelMatrix.translate(0.f, (-40 + texskyoffset) * skyoffsetfactor, 0.f);
modelMatrix.scale(1.f, 0.8f * 1.17f, 1.f);
}
textureMatrix.loadIdentity();
textureMatrix.scale(mirror ? -xscale : xscale, yscale, 1.f);
@ -375,7 +444,7 @@ void FSkyVertexBuffer::SetupMatrices(FGameTexture *tex, float x_offset, float y_
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, bool apply)
void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, TArray<unsigned int>& mPrimStart, bool apply)
{
state.Draw(prim, mPrimStart[row], mPrimStart[row + 1] - mPrimStart[row]);
}
@ -386,36 +455,35 @@ void FSkyVertexBuffer::RenderRow(FRenderState& state, EDrawType prim, int row, b
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled)
void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, int mode, bool which)
{
if (tex)
auto& primStart = which ? mPrimStartBuild : mPrimStartDoom;
if (tex && tex->isValid())
{
state.SetMaterial(tex, UF_Texture, 0, CLAMP_NONE, 0, -1);
state.EnableModelMatrix(true);
state.EnableTextureMatrix(true);
SetupMatrices(tex, x_offset, y_offset, mirror, mode, state.mModelMatrix, state.mTextureMatrix, tiled);
}
int rc = mRows + 1;
// The caps only get drawn for the main layer but not for the overlay.
if (mode == FSkyVertexBuffer::SKYMODE_MAINLAYER && tex != NULL)
if (mode == FSkyVertexBuffer::SKYMODE_MAINLAYER && tex != nullptr)
{
auto& col = R_GetSkyCapColor(tex);
state.SetObjectColor(col.first);
state.EnableTexture(false);
RenderRow(state, DT_TriangleFan, 0);
RenderRow(state, DT_TriangleFan, 0, primStart);
state.SetObjectColor(col.second);
RenderRow(state, DT_TriangleFan, rc);
RenderRow(state, DT_TriangleFan, rc, primStart);
state.EnableTexture(true);
}
state.SetObjectColor(0xffffffff);
for (int i = 1; i <= mRows; i++)
{
RenderRow(state, DT_TriangleStrip, i, i == 1);
RenderRow(state, DT_TriangleStrip, rc + i, false);
RenderRow(state, DT_TriangleStrip, i, primStart, i == 1);
RenderRow(state, DT_TriangleStrip, rc + i, primStart, false);
}
state.EnableTextureMatrix(false);
@ -423,13 +491,29 @@ void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float
}
//-----------------------------------------------------------------------------
//
// This is only for Doom-style skies.
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled, float xscale, float yscale)
{
if (tex)
{
SetupMatrices(tex, x_offset, y_offset, mirror, mode, state.mModelMatrix, state.mTextureMatrix, tiled, xscale, yscale);
}
RenderDome(state, tex, mode, false);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void FSkyVertexBuffer::RenderBox(FRenderState& state, FTextureID texno, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2)
void FSkyVertexBuffer::RenderBox(FRenderState& state, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2)
{
int faces;

View file

@ -11,6 +11,12 @@ class IVertexBuffer;
struct HWSkyPortal;
struct HWDrawInfo;
// 57 world units roughly represent one sky texel for the glTranslate call.
enum
{
skyoffsetfactor = 57
};
struct FSkyVertex
{
float x, y, z, u, v;
@ -55,7 +61,8 @@ public:
IVertexBuffer *mVertexBuffer;
TArray<FSkyVertex> mVertices;
TArray<unsigned int> mPrimStart;
TArray<unsigned int> mPrimStartDoom;
TArray<unsigned int> mPrimStartBuild;
int mRows, mColumns;
@ -63,15 +70,17 @@ public:
int mFaceStart[7];
int mSideStart;
void SkyVertex(int r, int c, bool yflip);
void CreateSkyHemisphere(int hemi);
void SkyVertexDoom(int r, int c, bool yflip);
void SkyVertexBuild(int r, int c, bool yflip);
void CreateSkyHemisphereDoom(int hemi);
void CreateSkyHemisphereBuild(int hemi);
void CreateDome();
public:
FSkyVertexBuffer();
~FSkyVertexBuffer();
void SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix, bool tiled);
void SetupMatrices(FGameTexture *tex, float x_offset, float y_offset, bool mirror, int mode, VSMatrix &modelmatrix, VSMatrix &textureMatrix, bool tiled, float xscale = 0, float vertscale = 0);
std::pair<IVertexBuffer *, IIndexBuffer *> GetBufferObjects() const
{
return std::make_pair(mVertexBuffer, nullptr);
@ -83,8 +92,9 @@ public:
else return mSideStart;
}
void RenderRow(FRenderState& state, EDrawType prim, int row, bool apply = true);
void RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled);
void RenderBox(FRenderState& state, FTextureID texno, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2);
void RenderRow(FRenderState& state, EDrawType prim, int row, TArray<unsigned int>& mPrimStart, bool apply = true);
void RenderDome(FRenderState& state, FGameTexture* tex, int mode, bool which);
void RenderDome(FRenderState& state, FGameTexture* tex, float x_offset, float y_offset, bool mirror, int mode, bool tiled, float xscale = 0, float yscale = 0);
void RenderBox(FRenderState& state, FSkyBox* tex, float x_offset, bool sky2, float stretch, const FVector3& skyrotatevector, const FVector3& skyrotatevector2);
};

View file

@ -60,7 +60,6 @@ static VRMode vrmi_checker = { 2, isqrt2, isqrt2, 1.f,{ { -.5f, 1.f },{ .5f, 1.f
const VRMode *VRMode::GetVRMode(bool toscreen)
{
#ifdef VR3D_ENABLED
int mode = !toscreen || (sysCallbacks.DisableTextureFilter && sysCallbacks.DisableTextureFilter()) ? 0 : vr_mode;
switch (mode)
@ -96,9 +95,6 @@ const VRMode *VRMode::GetVRMode(bool toscreen)
case VR_CHECKERINTERLEAVED:
return &vrmi_checker;
}
#else
return &vrmi_mono;
#endif
}
void VRMode::AdjustViewport(DFrameBuffer *screen) const

View file

@ -45,7 +45,6 @@
#include "hw_shadowmap.h"
struct sector_t;
struct FPortalSceneState;
class FSkyVertexBuffer;
class IIndexBuffer;

View file

@ -8706,7 +8706,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
bool writable;
ArgList[i] = ArgList[i]->Resolve(ctx); // must be resolved before the address is requested.
if (ArgList[i]->ValueType->isRealPointer())
if (ArgList[i] && ArgList[i]->ValueType->isRealPointer())
{
auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType;
if (pointedType && pointedType->isDynArray())

View file

@ -37,6 +37,7 @@
#include "dobject.h"
#include "vm.h"
#include "types.h"
#include "v_draw.h"
// We need one specific type for each of the 8 integral VM types and instantiate the needed functions for each of them.
// Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible.
@ -412,6 +413,28 @@ DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Push, ArrayPush<FDynArray_I32 COMMA
ACTION_RETURN_INT(self->Push(val));
}
DEFINE_ACTION_FUNCTION(FDynArray_I32, PushV)
{
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
PARAM_VA_POINTER(va_reginfo); // Get the hidden type information array
VMVa_List args = { param + 1, 0, numparam - 2, va_reginfo + 1 };
while (args.curindex < args.numargs)
{
if (args.reginfo[args.curindex] == REGT_INT)
{
self->Push(args.args[args.curindex++].i);
}
else if (args.reginfo[args.curindex] == REGT_FLOAT)
{
self->Push(int(args.args[args.curindex++].f));
}
else ThrowAbortException(X_OTHER, "Invalid parameter in pushv, int expected");
}
ACTION_RETURN_INT(self->Size()-1);
}
DEFINE_ACTION_FUNCTION_NATIVE(FDynArray_I32, Pop, ArrayPop<FDynArray_I32>)
{
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);

View file

@ -550,7 +550,21 @@ DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, ToDouble, StringToDbl)
ACTION_RETURN_FLOAT(self->ToDouble());
}
static void StringSplit(FString *self, TArray<FString> *tokens, const FString &delimiter, int keepEmpty)
static void StringSubst(FString *self, const FString &substr, const FString& replc)
{
self->Substitute(substr, replc);
}
DEFINE_ACTION_FUNCTION_NATIVE(FStringStruct, Substitute, StringSubst)
{
PARAM_SELF_STRUCT_PROLOGUE(FString);
PARAM_STRING(substr);
PARAM_STRING(replc);
StringSubst(self, substr, replc);
return 0;
}
static void StringSplit(FString* self, TArray<FString>* tokens, const FString& delimiter, int keepEmpty)
{
self->Split(*tokens, delimiter, static_cast<FString::EmptyTokenType>(keepEmpty));
}

View file

@ -49,6 +49,7 @@
#include "s_music.h"
#include "i_interface.h"
#include "base_sbar.h"
#include "image.h"
//==========================================================================
//
@ -80,10 +81,10 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, StatusbarToRealCoords, StatusbarTo
return MIN(4, numret);
}
void SBar_DrawTexture(DStatusBarCore* self, int texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY)
void SBar_DrawTexture(DStatusBarCore* self, int texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY, int style)
{
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
self->DrawGraphic(FSetTextureID(texid), x, y, flags, alpha, w, h, scaleX, scaleY);
self->DrawGraphic(FSetTextureID(texid), x, y, flags, alpha, w, h, scaleX, scaleY, ERenderStyle(style));
}
DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawTexture, SBar_DrawTexture)
@ -98,14 +99,15 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawTexture, SBar_DrawTexture)
PARAM_FLOAT(h);
PARAM_FLOAT(scaleX);
PARAM_FLOAT(scaleY);
SBar_DrawTexture(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY);
PARAM_INT(style);
SBar_DrawTexture(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY, style);
return 0;
}
void SBar_DrawImage(DStatusBarCore* self, const FString& texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY)
void SBar_DrawImage(DStatusBarCore* self, const FString& texid, double x, double y, int flags, double alpha, double w, double h, double scaleX, double scaleY, int style)
{
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
self->DrawGraphic(TexMan.CheckForTexture(texid, ETextureType::Any), x, y, flags, alpha, w, h, scaleX, scaleY);
self->DrawGraphic(TexMan.CheckForTexture(texid, ETextureType::Any), x, y, flags, alpha, w, h, scaleX, scaleY, ERenderStyle(style));
}
DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImage, SBar_DrawImage)
@ -120,11 +122,12 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawImage, SBar_DrawImage)
PARAM_FLOAT(h);
PARAM_FLOAT(scaleX);
PARAM_FLOAT(scaleY);
SBar_DrawImage(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY);
PARAM_INT(style);
SBar_DrawImage(self, texid, x, y, flags, alpha, w, h, scaleX, scaleY, style);
return 0;
}
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int translation);
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int translation, int style);
DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawString, SBar_DrawString)
{
@ -141,7 +144,8 @@ DEFINE_ACTION_FUNCTION_NATIVE(DStatusBarCore, DrawString, SBar_DrawString)
PARAM_FLOAT(scaleX);
PARAM_FLOAT(scaleY);
PARAM_INT(pt);
SBar_DrawString(self, font, string, x, y, flags, trans, alpha, wrapwidth, linespacing, scaleX, scaleY, pt);
PARAM_INT(style);
SBar_DrawString(self, font, string, x, y, flags, trans, alpha, wrapwidth, linespacing, scaleX, scaleY, pt, style);
return 0;
}
@ -336,8 +340,7 @@ DEFINE_ACTION_FUNCTION(_TexMan, GetName)
static int CheckForTexture(const FString& name, int type, int flags)
{
// ForceLookup is intentionally blocked here, this flag is for internal use only.
return TexMan.CheckForTexture(name, static_cast<ETextureType>(type), (flags & ~FTextureManager::TEXMAN_ForceLookup)).GetIndex();
return TexMan.CheckForTexture(name, static_cast<ETextureType>(type), flags).GetIndex();
}
DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, CheckForTexture, CheckForTexture)
@ -477,6 +480,20 @@ DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, OkForLocalization, OkForLocalization_)
ACTION_RETURN_INT(OkForLocalization_(name, subst));
}
static int UseGamePalette(int index)
{
auto tex = TexMan.GameByIndex(index, false);
if (!tex) return false;
auto image = tex->GetTexture()->GetImage();
return image ? image->UseGamePalette() : false;
}
DEFINE_ACTION_FUNCTION_NATIVE(_TexMan, UseGamePalette, UseGamePalette)
{
PARAM_PROLOGUE;
PARAM_INT(texid);
ACTION_RETURN_INT(UseGamePalette(texid));
}
//=====================================================================================
//
@ -867,6 +884,13 @@ DEFINE_ACTION_FUNCTION(FKeyBindings, GetAllKeysForCommand)
return 0;
}
DEFINE_ACTION_FUNCTION(FKeyBindings, GetBinding)
{
PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings);
PARAM_INT(key);
ACTION_RETURN_STRING(self->GetBinding(key));
}
DEFINE_ACTION_FUNCTION(FKeyBindings, UnbindACommand)
{
PARAM_SELF_STRUCT_PROLOGUE(FKeyBindings);
@ -914,6 +938,7 @@ DEFINE_GLOBAL_NAMED(mus_playing, musplaying);
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, name);
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, baseorder);
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, loop);
DEFINE_FIELD_X(MusPlayingInfo, MusPlayingInfo, handle);
DEFINE_GLOBAL_NAMED(PClass::AllClasses, AllClasses)
DEFINE_GLOBAL(Bindings)

View file

@ -452,16 +452,16 @@ void DStatusBarCore::StatusbarToRealCoords(double& x, double& y, double& w, doub
//
//============================================================================
void DStatusBarCore::DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style, double clipwidth)
void DStatusBarCore::DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style, PalEntry color, int translation, double clipwidth)
{
if (!texture.isValid())
return;
FGameTexture* tex = TexMan.GetGameTexture(texture, !(flags & DI_DONTANIMATE));
DrawGraphic(tex, x, y, flags, Alpha, boxwidth, boxheight, scaleX, scaleY, color, translation, style);
DrawGraphic(tex, x, y, flags, Alpha, boxwidth, boxheight, scaleX, scaleY, style, color, translation);
}
void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color, int translation, ERenderStyle style, double clipwidth)
void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style, PalEntry color, int translation, double clipwidth)
{
double texwidth = tex->GetDisplayWidth() * scaleX;
double texheight = tex->GetDisplayHeight() * scaleY;
@ -695,7 +695,7 @@ void DStatusBarCore::DrawRotated(FGameTexture* tex, double x, double y, int flag
//
//============================================================================
void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt)
void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt, int style)
{
bool monospaced = monospacing != EMonospacing::Off;
double dx = 0;
@ -822,6 +822,7 @@ void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, d
DTA_DestHeightF, rh,
DTA_Alpha, Alpha,
DTA_TranslationIndex, pt,
DTA_LegacyRenderStyle, ERenderStyle(style),
TAG_DONE);
dx = monospaced
@ -833,7 +834,7 @@ void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, d
}
}
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt)
void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string, double x, double y, int flags, int trans, double alpha, int wrapwidth, int linespacing, double scaleX, double scaleY, int pt, int style)
{
if (font == nullptr) ThrowAbortException(X_READ_NIL, nullptr);
if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function");
@ -852,13 +853,13 @@ void SBar_DrawString(DStatusBarCore* self, DHUDFont* font, const FString& string
auto brk = V_BreakLines(font->mFont, int(wrapwidth * scaleX), string, true);
for (auto& line : brk)
{
self->DrawString(font->mFont, line.Text, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt);
self->DrawString(font->mFont, line.Text, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt, style);
y += (font->mFont->GetHeight() + linespacing) * scaleY;
}
}
else
{
self->DrawString(font->mFont, string, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt);
self->DrawString(font->mFont, string, x, y, flags, alpha, trans, font->mSpacing, font->mMonospacing, font->mShadowX, font->mShadowY, scaleX, scaleY, pt, style);
}
}

View file

@ -184,11 +184,11 @@ public:
virtual void SetScale();
void ValidateResolution(int& hres, int& vres) const;
void StatusbarToRealCoords(double& x, double& y, double& w, double& h) const;
void DrawGraphic(FGameTexture* texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent, double clipwidth = -1.0);
void DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent, double clipwidth = -1.0);
void DrawGraphic(FGameTexture* texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style = STYLE_Translucent, PalEntry color = 0xffffffff, int translation = 0, double clipwidth = -1.0);
void DrawGraphic(FTextureID texture, double x, double y, int flags, double Alpha, double boxwidth, double boxheight, double scaleX, double scaleY, ERenderStyle style = STYLE_Translucent, PalEntry color = 0xffffffff, int translation = 0, double clipwidth = -1.0);
void DrawRotated(FTextureID texture, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent);
void DrawRotated(FGameTexture* tex, double x, double y, int flags, double angle, double Alpha, double scaleX, double scaleY, PalEntry color = 0xffffffff, int translation = 0, ERenderStyle style = STYLE_Translucent);
void DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt);
void DrawString(FFont* font, const FString& cstring, double x, double y, int flags, double Alpha, int translation, int spacing, EMonospacing monospacing, int shadowX, int shadowY, double scaleX, double scaleY, int pt, int style);
void TransformRect(double& x, double& y, double& w, double& h, int flags = 0);
void Fill(PalEntry color, double x, double y, double w, double h, int flags = 0);
void SetClipRect(double x, double y, double w, double h, int flags = 0);

View file

@ -231,6 +231,7 @@ public:
void SetFullbright() { flags |= GTexf_RenderFullbright; }
void SetDisableFullbright(bool on) { if (on) flags |= GTexf_DisableFullbrightSprites; else flags &= ~GTexf_DisableFullbrightSprites; }
void SetGlowing(PalEntry color) { flags = (flags & ~GTexf_AutoGlowing) | GTexf_Glowing; GlowColor = color; }
void SetDisableBrightmap() { flags |= GTexf_BrightmapChecked; Brightmap = nullptr; }
bool isUserContent() const;
int CheckRealHeight() { return xs_RoundToInt(Base->CheckRealHeight() / ScaleY); }

View file

@ -30,6 +30,7 @@
#include "v_video.h"
static IHardwareTexture* (*layercallback)(int layer, int translation);
TArray<UserShaderDesc> usershaders;
void FMaterial::SetLayerCallback(IHardwareTexture* (*cb)(int layer, int translation))
{

View file

@ -2,6 +2,7 @@
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#define MAXWIDTH 12000
#define MAXHEIGHT 5000
@ -101,3 +102,6 @@ enum EStateUseFlags
SUF_WEAPON = 4,
SUF_ITEM = 8,
};
using std::min;
using std::max;

View file

@ -103,4 +103,14 @@ inline void fillshort(void* buff, size_t count, uint16_t clear)
template<typename T> inline constexpr T Sgn(const T& val) { return (val > 0) - (val < 0); }
inline int sizeToBits(int w)
{
int j = 15;
while ((j > 1) && ((1 << j) > w))
j--;
return j;
}
#endif

View file

@ -33,9 +33,7 @@ class VSMatrix {
public:
VSMatrix()
{
}
VSMatrix() = default;
VSMatrix(int)
{

View file

@ -27,7 +27,6 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
//-------------------------------------------------------------------------
#include "automap.h"
#include "cstat.h"
#include "c_dispatch.h"
#include "c_cvars.h"
#include "gstrings.h"
@ -44,6 +43,9 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
#include "v_video.h"
#include "gamestruct.h"
#include "v_draw.h"
#include "sectorgeometry.h"
#include "gamefuncs.h"
#include "hw_sections.h"
CVAR(Bool, am_followplayer, true, CVAR_ARCHIVE)
CVAR(Bool, am_rotate, true, CVAR_ARCHIVE)
@ -408,8 +410,6 @@ void drawredlines(int cposx, int cposy, int czoom, int cang)
{
int xvect = -bsin(cang) * czoom;
int yvect = -bcos(cang) * czoom;
int xvect2 = MulScale(xvect, yxaspect, 16);
int yvect2 = MulScale(yvect, yxaspect, 16);
int width = screen->GetWidth();
int height = screen->GetHeight();
@ -441,13 +441,13 @@ void drawredlines(int cposx, int cposy, int czoom, int cang)
int ox = wal->x - cposx;
int oy = wal->y - cposy;
int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11);
int y1 = DMulScale(oy, xvect2, ox, yvect2, 16) + (height << 11);
int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11);
auto wal2 = &wall[wal->point2];
ox = wal2->x - cposx;
oy = wal2->y - cposy;
int x2 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11);
int y2 = DMulScale(oy, xvect2, ox, yvect2, 16) + (height << 11);
int y2 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11);
drawlinergb(x1, y1, x2, y2, RedLineColor());
}
@ -465,8 +465,6 @@ static void drawwhitelines(int cposx, int cposy, int czoom, int cang)
{
int xvect = -bsin(cang) * czoom;
int yvect = -bcos(cang) * czoom;
int xvect2 = MulScale(xvect, yxaspect, 16);
int yvect2 = MulScale(yvect, yxaspect, 16);
int width = screen->GetWidth();
int height = screen->GetHeight();
@ -483,7 +481,7 @@ static void drawwhitelines(int cposx, int cposy, int czoom, int cang)
for (j = startwall, wal = &wall[startwall]; j < endwall; j++, wal++)
{
if (wal->nextwall >= 0) continue;
if (!tileGetTexture(wal->picnum)->isValid()) continue;
if (!gFullMap && !tileGetTexture(wal->picnum)->isValid()) continue;
if ((g_gameType & GAMEFLAG_SW) && !gFullMap && !show2dwall[j])
continue;
@ -491,20 +489,25 @@ static void drawwhitelines(int cposx, int cposy, int czoom, int cang)
int ox = wal->x - cposx;
int oy = wal->y - cposy;
int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11);
int y1 = DMulScale(oy, xvect2, ox, yvect2, 16) + (height << 11);
int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11);
int k = wal->point2;
auto wal2 = &wall[k];
ox = wal2->x - cposx;
oy = wal2->y - cposy;
int x2 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11);
int y2 = DMulScale(oy, xvect2, ox, yvect2, 16) + (height << 11);
int y2 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11);
drawlinergb(x1, y1, x2, y2, WhiteLineColor());
}
}
}
//---------------------------------------------------------------------------
//
// player sprite fallback
//
//---------------------------------------------------------------------------
void DrawPlayerArrow(int cposx, int cposy, int cang, int pl_x, int pl_y, int zoom, int pl_angle)
{
@ -517,8 +520,6 @@ void DrawPlayerArrow(int cposx, int cposy, int cang, int pl_x, int pl_y, int zoo
int xvect = -bsin(cang) * zoom;
int yvect = -bcos(cang) * zoom;
int xvect2 = MulScale(xvect, yxaspect, 16);
int yvect2 = MulScale(yvect, yxaspect, 16);
int pxvect = -bsin(pl_angle);
int pyvect = -bcos(pl_angle);
@ -540,14 +541,124 @@ void DrawPlayerArrow(int cposx, int cposy, int cang, int pl_x, int pl_y, int zoo
int oy2 = py2 - cposx;
int sx1 = DMulScale(ox1, xvect, -oy1, yvect, 16) + (width << 11);
int sy1 = DMulScale(oy1, xvect2, ox1, yvect2, 16) + (height << 11);
int sy1 = DMulScale(oy1, xvect, ox1, yvect, 16) + (height << 11);
int sx2 = DMulScale(ox2, xvect, -oy2, yvect, 16) + (width << 11);
int sy2 = DMulScale(oy2, xvect2, ox2, yvect2, 16) + (height << 11);
int sy2 = DMulScale(oy2, xvect, ox2, yvect, 16) + (height << 11);
drawlinergb(sx1, sy1, sx2, sy2, WhiteLineColor());
}
}
//---------------------------------------------------------------------------
//
// floor textures
//
//---------------------------------------------------------------------------
void renderDrawMapView(int cposx, int cposy, int czoom, int cang)
{
int xvect = -bsin(cang) * czoom;
int yvect = -bcos(cang) * czoom;
int width = screen->GetWidth();
int height = screen->GetHeight();
TArray<FVector4> vertices;
TArray<int> floorsprites;
for (int i = numsectors - 1; i >= 0; i--)
{
if (!gFullMap && !show2dsector[i]) continue;
//Collect floor sprites to draw
SectIterator it(i);
int s;
while ((s = it.NextIndex()) >= 0)
{
if (sprite[s].cstat & CSTAT_SPRITE_INVISIBLE)
continue;
if ((sprite[s].cstat & CSTAT_SPRITE_ALIGNMENT_MASK) == CSTAT_SPRITE_ALIGNMENT_FLOOR)
{
if ((sprite[s].cstat & (CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_YFLIP)) == (CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_YFLIP))
continue; // upside down
floorsprites.Push(s);
}
}
if (sector[i].floorstat & CSTAT_SECTOR_SKY) continue;
int picnum = sector[i].floorpicnum;
if ((unsigned)picnum >= (unsigned)MAXTILES) continue;
for (auto ii : sectionspersector[i])
{
auto mesh = sectorGeometry.get(ii, 0, { 0.f,0.f });
vertices.Resize(mesh->vertices.Size());
for (unsigned j = 0; j < mesh->vertices.Size(); j++)
{
int ox = int(mesh->vertices[j].X * 16.f) - cposx;
int oy = int(mesh->vertices[j].Y * -16.f) - cposy;
int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11);
int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11);
vertices[j] = { x1 / 4096.f, y1 / 4096.f, mesh->texcoords[j].X, mesh->texcoords[j].Y };
}
}
int translation = TRANSLATION(Translation_Remap + curbasepal, sector[i].floorpal);
setgotpic(picnum);
twod->AddPoly(tileGetTexture(picnum, true), vertices.Data(), vertices.Size(), nullptr, 0, translation, shadeToLight(sector[i].floorshade),
LegacyRenderStyles[STYLE_Translucent], windowxy1.x, windowxy1.y, windowxy2.x + 1, windowxy2.y + 1);
}
qsort(floorsprites.Data(), floorsprites.Size(), sizeof(int), [](const void* a, const void* b)
{
int A = *(int*)a;
int B = *(int*)b;
if (sprite[A].z != sprite[B].z) return sprite[B].z - sprite[A].z;
return A - B; // ensures stable sort.
});
vertices.Resize(4);
for (auto sn : floorsprites)
{
if (!gFullMap && !show2dsprite[sn]) continue;
auto spr = &sprite[sn];
vec2_t pp[4];
GetFlatSpritePosition(spr, spr->pos.vec2, pp, true);
for (unsigned j = 0; j < 4; j++)
{
int ox = pp[j].x - cposx;
int oy = pp[j].y - cposy;
int x1 = DMulScale(ox, xvect, -oy, yvect, 16) + (width << 11);
int y1 = DMulScale(oy, xvect, ox, yvect, 16) + (height << 11);
vertices[j] = { x1 / 4096.f, y1 / 4096.f, j == 1 || j == 2 ? 1.f : 0.f, j == 2 || j == 3 ? 1.f : 0.f };
}
int shade;
if ((sector[spr->sectnum].ceilingstat & CSTAT_SECTOR_SKY)) shade = sector[spr->sectnum].ceilingshade;
else shade = sector[spr->sectnum].floorshade;
shade += spr->shade;
PalEntry color = shadeToLight(shade);
FRenderStyle rs = LegacyRenderStyles[STYLE_Translucent];
float alpha = 1;
if (spr->cstat & CSTAT_SPRITE_TRANSLUCENT)
{
rs = GetRenderStyle(0, !!(spr->cstat & CSTAT_SPRITE_TRANSLUCENT_INVERT));
alpha = GetAlphaFromBlend((spr->cstat & CSTAT_SPRITE_TRANSLUCENT_INVERT) ? DAMETH_TRANS2 : DAMETH_TRANS1, 0);
color.a = uint8_t(alpha * 255);
}
int translation = TRANSLATION(Translation_Remap + curbasepal, spr->pal);
int picnum = spr->picnum;
setgotpic(picnum);
const static unsigned indices[] = { 0, 1, 2, 0, 2, 3 };
twod->AddPoly(tileGetTexture(picnum, true), vertices.Data(), vertices.Size(), indices, 6, translation, color, rs,
windowxy1.x, windowxy1.y, windowxy2.x + 1, windowxy2.y + 1);
}
}
//---------------------------------------------------------------------------
//
//
@ -573,7 +684,6 @@ void DrawOverheadMap(int pl_x, int pl_y, int pl_angle, double const smoothratio)
renderDrawMapView(x, y, gZoom, follow_a);
}
int32_t tmpydim = (width * 5) / 8;
renderSetAspect(65536, DivScale(tmpydim * 320, width * 200, 16));
drawredlines(x, y, gZoom, follow_a);
drawwhitelines(x, y, gZoom, follow_a);

View file

@ -40,8 +40,8 @@
#include "m_fixed.h"
#include "xs_Float.h" // needed for reliably overflowing float->int conversions.
#include "serializer.h"
#include "build.h"
#include "math/cmath.h"
#include "templates.h"
class FSerializer;
@ -59,9 +59,9 @@ enum
//---------------------------------------------------------------------------
constexpr double BAngRadian = pi::pi() * (1. / 1024.);
constexpr double BRadAngScale = 1. / BAngRadian;
constexpr double BAngToDegree = 360. / 2048.;
extern int16_t sintable[2048];
//---------------------------------------------------------------------------
//
@ -69,11 +69,11 @@ constexpr double BAngToDegree = 360. / 2048.;
//
//---------------------------------------------------------------------------
inline int32_t bsin(const int ang, const int8_t shift = 0)
inline int bsin(const int ang, const int shift = 0)
{
return shift < 0 ? sintable[ang & 2047] >> abs(shift) : sintable[ang & 2047] << shift;
}
inline double bsinf(const double ang, const int8_t shift = 0)
inline double bsinf(const double ang, const int shift = 0)
{
return g_sin(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
}
@ -85,156 +85,16 @@ inline double bsinf(const double ang, const int8_t shift = 0)
//
//---------------------------------------------------------------------------
inline int32_t bcos(const int ang, const int8_t shift = 0)
inline int bcos(const int ang, const int shift = 0)
{
return shift < 0 ? sintable[(ang + 512) & 2047] >> abs(shift) : sintable[(ang + 512) & 2047] << shift;
}
inline double bcosf(const double ang, const int8_t shift = 0)
inline double bcosf(const double ang, const int shift = 0)
{
return g_cos(ang * BAngRadian) * (shift >= -SINSHIFT ? uint64_t(1) << (SINSHIFT + shift) : 1. / (uint64_t(1) << abs(SINSHIFT + shift)));
}
//---------------------------------------------------------------------------
//
// Shift a Build angle left by 21 bits.
//
//---------------------------------------------------------------------------
inline constexpr int64_t BAngToBAM(int ang)
{
return ang << BAMBITS;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class lookangle
{
int32_t value;
constexpr lookangle(int32_t v) : value(v) {}
friend constexpr lookangle bamlook(int32_t v);
friend constexpr lookangle q16look(int32_t v);
friend constexpr lookangle buildlook(int32_t v);
friend lookangle buildflook(double v);
friend lookangle radlook(double v);
friend lookangle deglook(double v);
friend FSerializer &Serialize(FSerializer &arc, const char *key, lookangle &obj, lookangle *defval);
friend class binangle;
public:
lookangle() = default;
lookangle(const lookangle &other) = default;
// This class intentionally makes no allowances for implicit type conversions because those would render it ineffective.
constexpr short asbuild() const { return value >> 21; }
constexpr double asbuildf() const { return value * (1. / BAMUNIT); }
constexpr fixed_t asq16() const { return value >> 5; }
constexpr double asrad() const { return value * (pi::pi() / 0x80000000u); }
constexpr double asdeg() const { return AngleToFloat(value); }
constexpr int32_t asbam() const { return value; }
double fsin() const { return g_sin(asrad()); }
double fcos() const { return g_cos(asrad()); }
double ftan() const { return g_tan(asrad()); }
int bsin(const int8_t& shift = 0) const { return ::bsin(asbuild(), shift); }
int bcos(const int8_t& shift = 0) const { return ::bcos(asbuild(), shift); }
bool operator< (lookangle other) const
{
return value < other.value;
}
bool operator> (lookangle other) const
{
return value > other.value;
}
bool operator<= (lookangle other) const
{
return value <= other.value;
}
bool operator>= (lookangle other) const
{
return value >= other.value;
}
constexpr bool operator== (lookangle other) const
{
return value == other.value;
}
constexpr bool operator!= (lookangle other) const
{
return value != other.value;
}
constexpr lookangle &operator+= (lookangle other)
{
value += other.value;
return *this;
}
constexpr lookangle &operator-= (lookangle other)
{
value -= other.value;
return *this;
}
constexpr lookangle operator+ (lookangle other) const
{
return lookangle(value + other.value);
}
constexpr lookangle operator- (lookangle other) const
{
return lookangle(value - other.value);
}
constexpr lookangle &operator<<= (const uint8_t shift)
{
value <<= shift;
return *this;
}
constexpr lookangle &operator>>= (const uint8_t shift)
{
value >>= shift;
return *this;
}
constexpr lookangle operator<< (const uint8_t shift) const
{
return lookangle(value << shift);
}
constexpr lookangle operator>> (const uint8_t shift) const
{
return lookangle(value >> shift);
}
};
inline constexpr lookangle bamlook(int32_t v) { return lookangle(v); }
inline constexpr lookangle q16look(int32_t v) { return lookangle(v << 5); }
inline constexpr lookangle buildlook(int32_t v) { return lookangle(v << BAMBITS); }
inline lookangle buildflook(double v) { return lookangle(xs_CRoundToInt(v * BAMUNIT)); }
inline lookangle radlook(double v) { return lookangle(xs_CRoundToInt(v * (0x80000000u / pi::pi()))); }
inline lookangle deglook(double v) { return lookangle(FloatToAngle(v)); }
inline FSerializer &Serialize(FSerializer &arc, const char *key, lookangle &obj, lookangle *defval)
{
return Serialize(arc, key, obj.value, defval ? &defval->value : nullptr);
}
//---------------------------------------------------------------------------
//
//
@ -260,12 +120,19 @@ public:
binangle() = default;
binangle(const binangle &other) = default;
// This class intentionally makes no allowances for implicit type conversions because those would render it ineffective.
constexpr short asbuild() const { return value >> 21; }
constexpr int32_t tosigned() const { return value > INT32_MAX ? int64_t(value) - UINT32_MAX : value; }
constexpr short asbuild() const { return value >> BAMBITS; }
constexpr double asbuildf() const { return value * (1. / BAMUNIT); }
constexpr fixed_t asq16() const { return value >> 5; }
constexpr uint32_t asbam() const { return value; }
constexpr double asrad() const { return value * (pi::pi() / 0x80000000u); }
constexpr double asdeg() const { return AngleToFloat(value); }
constexpr uint32_t asbam() const { return value; }
constexpr short signedbuild() const { return tosigned() >> BAMBITS; }
constexpr double signedbuildf() const { return tosigned() * (1. / BAMUNIT); }
constexpr fixed_t signedq16() const { return tosigned() >> 5; }
constexpr int32_t signedbam() const { return tosigned(); }
constexpr double signedrad() const { return tosigned() * (pi::pi() / 0x80000000u); }
constexpr double signeddeg() const { return AngleToFloat(tosigned()); }
double fsin() const { return g_sin(asrad()); }
double fcos() const { return g_cos(asrad()); }
@ -305,28 +172,6 @@ public:
return binangle(value - other.value);
}
constexpr binangle &operator+= (lookangle other)
{
value += other.value;
return *this;
}
constexpr binangle &operator-= (lookangle other)
{
value -= other.value;
return *this;
}
constexpr binangle operator+ (lookangle other) const
{
return binangle(value + other.value);
}
constexpr binangle operator- (lookangle other) const
{
return binangle(value - other.value);
}
constexpr binangle &operator<<= (const uint8_t shift)
{
value <<= shift;
@ -348,7 +193,6 @@ public:
{
return binangle(value >> shift);
}
};
inline constexpr binangle bamang(uint32_t v) { return binangle(v); }
@ -366,24 +210,14 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, binangle &obj,
//---------------------------------------------------------------------------
//
// Constants and functions for use with fixedhoriz and friendly functions.
// Functions for use with fixedhoriz and friendly functions.
//
//---------------------------------------------------------------------------
// 280039127 is the maximum horizon in Q16.16 the engine will handle before wrapping around.
constexpr double horizDiff = 280039127 * 3. / 100.;
// Degrees needed to convert horizAngle into pitch degrees.
constexpr double horizDegrees = 183.503609961216825;
// Ratio to convert inverse tangent to -90/90 degrees of pitch.
constexpr double horizRatio = horizDegrees / pi::pi();
// Horizon conversion functions.
inline double HorizToPitch(double horiz) { return atan2(horiz, horizDiff / 65536.) * horizRatio; }
inline double HorizToPitch(fixed_t q16horiz) { return atan2(q16horiz, horizDiff) * horizRatio; }
inline fixed_t PitchToHoriz(double horizAngle) { return xs_CRoundToInt(horizDiff * tan(horizAngle * (pi::pi() / horizDegrees))); }
inline int32_t PitchToBAM(double horizAngle) { return xs_CRoundToInt(clamp(horizAngle * (1073741823.5 / 45.), -INT32_MAX, INT32_MAX)); }
inline double HorizToPitch(double horiz) { return atan2(horiz, 128) * (180. / pi::pi()); }
inline double HorizToPitch(fixed_t q16horiz) { return atan2(q16horiz, IntToFixed(128)) * (180. / pi::pi()); }
inline fixed_t PitchToHoriz(double pitch) { return xs_CRoundToInt(IntToFixed(128) * tan(pitch * (pi::pi() / 180.))); }
inline int32_t PitchToBAM(double pitch) { return xs_CRoundToInt(clamp(pitch * (1073741823.5 / 45.), -INT32_MAX, INT32_MAX)); }
inline constexpr double BAMToPitch(int32_t bam) { return bam * (45. / 1073741823.5); }
@ -512,54 +346,38 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, fixedhoriz &obj
//---------------------------------------------------------------------------
//
// Double-precision implementation of `getangle()` with associated wrappers and helper functions.
// High precision vector angle function, mainly for the renderer.
//
//---------------------------------------------------------------------------
inline double bradarangf(const double& vect)
inline binangle bvectangbam(int32_t x, int32_t y)
{
return atan(vect) * BRadAngScale;
return radang(atan2(y, x));
}
inline double bvectangf(const int32_t& x, const int32_t& y)
//---------------------------------------------------------------------------
//
// Interpolation functions for use throughout games.
//
//---------------------------------------------------------------------------
inline int32_t interpolatedvalue(int32_t oval, int32_t val, double const smoothratio, int const scale = 16)
{
if ((x | y) == 0)
{
return 0;
}
else if (x == 0)
{
return 512 + ((y < 0) << 10);
}
else if (y == 0)
{
return ((x < 0) << 10);
}
else if (x == y)
{
return 256 + ((x < 0) << 10);
}
else if (x == -y)
{
return 768 + ((x > 0) << 10);
}
else if (abs(x) > abs(y))
{
return fmod(bradarangf(double(y) / x) + ((x < 0) << 10), 2048.);
}
else
{
return fmod(bradarangf(double(x) / -y) + 512 + ((y < 0) << 10), 2048.);
}
return oval + MulScale(val - oval, smoothratio, scale);
}
inline int32_t bvectang(const int32_t& x, const int32_t& y)
inline double interpolatedvaluef(double oval, double val, double const smoothratio, int const scale = 16)
{
return xs_CRoundToInt(bvectangf(x, y));
return oval + MulScaleF(val - oval, smoothratio, scale);
}
inline fixed_t bvectangq16(const int32_t& x, const int32_t& y)
inline int32_t interpolatedangle(int32_t oang, int32_t ang, double const smoothratio, int const scale = 16)
{
return FloatToFixed(bvectangf(x, y));
return oang + MulScale(((ang + 1024 - oang) & 2047) - 1024, smoothratio, scale);
}
inline binangle bvectangbam(const int32_t& x, const int32_t& y)
inline binangle interpolatedangle(binangle oang, binangle ang, double const smoothratio, int const scale = 16)
{
return bamang(xs_CRoundToUInt(bvectangf(x, y) * BAMUNIT));
return bamang(oang.asbam() + MulScale(((ang.asbam() + 0x80000000 - oang.asbam()) & 0xFFFFFFFF) - 0x80000000, smoothratio, scale));
}

View file

@ -41,7 +41,6 @@
#include "c_dispatch.h"
#include "d_net.h"
#include "gamestate.h"
#include "mmulti.h"
#include "gstrings.h"
#include "gamecontrol.h"
#include "screenjob.h"
@ -242,7 +241,7 @@ void changeMap(int player, uint8_t** stream, bool skip)
void endScreenJob(int player, uint8_t** stream, bool skip)
{
if (!skip) EndScreenJob();
if (!skip) gameaction = ga_endscreenjob;
}
//---------------------------------------------------------------------------
@ -280,6 +279,7 @@ void DeferedStartGame(MapRecord* map, int skill, bool nostopsound)
static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, const char *t2)
{
int numparm = g_gameType & (GAMEFLAG_SW | GAMEFLAG_PSEXHUMED) ? 1 : 2; // Handle games with episodic and non-episodic level order.
if (numparm == 2 && argv.argc() == 2) numparm = 1;
if (argv.argc() <= numparm)
{
if (numparm == 2) Printf(PRINT_BOLD, "%s <e> <m>: %s episode 'e' and map 'm'\n", cmdname, t2);
@ -294,7 +294,7 @@ static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, cons
Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n");
return nullptr;
}
auto map = FindMapByLevelNum(numparm == 1 ? m : levelnum(e - 1, m - 1));
auto map = FindMapByIndex(e, m);
if (!map)
{
if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]);

View file

@ -63,7 +63,6 @@
#include "vm.h"
#include "gstrings.h"
#include "s_music.h"
#include "mmulti.h"
#include "printf.h"
#include "i_time.h"
#include "d_ticcmd.h"

View file

@ -10,6 +10,9 @@ enum
MAXPLAYERS = 8
};
extern int myconnectindex, numplayers;
extern int connecthead, connectpoint2[MAXPLAYERS];
class FDynamicBuffer
{
public:

View file

@ -1,139 +0,0 @@
/*
** def.cpp
** Rewritten .def parser free of Build license restrictions.
**
**---------------------------------------------------------------------------
** Copyright 2020 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 OFf
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "build.h"
#include "compat.h"
#include "mdsprite.h" // md3model_t
#include "buildtiles.h"
#include "bitmap.h"
#include "m_argv.h"
#include "gamecontrol.h"
#include "palettecontainer.h"
#include "mapinfo.h"
#include "sc_man.h"
bool ParseDefFile(const char* file, FScanner *parent)
{
int lump = fileSystem.FindFile(file);
bool success = false;
if (lump == -1)
{
if (!parent) Printf(PRINT_BOLD, "%sd: file not found\n", file);
else parent->ScriptError("%s: file not found\n", file);
return false;
}
FScanner sc;
if (parent) sc.symbols = std::move(parent->symbols); // the child parser needs to add to the parent's symbol table so transfer its ownership for the parsing run.
sc.OpenLumpNum(lump);
sc.SetCMode(true);
sc.SetNoOctals(true);
sc.SetNoFatalErrors(true);
FString str;
while (sc.GetString())
{
if (sc.Compare({"#include", "include"}))
{
if (!sc.MustGetString())
ParseDefFile(sc.String, &sc);
}
else if (sc.Compare({"#includedefault", "includedefault"}))
{
ParseDefFile(G_DefaultDefFile(), &sc);
}
else if (sc.Compare({"#define", "define"}))
{
parseDefine(sc);
}
else if (sc.Compare("definetexture"))
{
parseDefineTexture(sc);
}
else if (sc.Compare("defineskybox"))
{
parseDefineSkybox(sc);
}
else if (sc.Compare("definetint"))
{
parseDefineTint(sc);
}
else if (sc.Compare("alphahack")) // why 'hack'?
{
parseAlphaHack(sc);
}
else if (sc.Compare("alphahackrange")) // why 'hack'?
{
parseAlphaHackRange(sc);
}
if (sc.Compare({"spritecol", "2dcolidxrange")) // only used by Mapster32 so just read over them and ignore the result
{
parseDiscard<3>(sc);
}
else if (sc.Compare("2dcol")) // same here
{
parseDiscard<4>(sc);
}
else if (sc.Compare("fogpal"))
{
parseFogPal(sc);
}
else if (sc.Compare("nofloorpalrange"))
{
parseNoFloorpalRange(sc);
}
else if (sc.Compare("loadgrp"))
{
parseLoadGrp(sc);
}
else if (sc.Compare("cachesize") || sc.Compare("shadefactor"))
{
parseDiscard<1>(sc);
}
else if (sc.Compare("artfile"))
{
parseArtFile(sc);
}
}
success = true;
if (parent) parent->symbols = std::move(sc.symbols);
return success;
}

2159
source/core/defparser.cpp Normal file

File diff suppressed because it is too large Load diff

1321
source/core/g_mapinfo.cpp Normal file

File diff suppressed because it is too large Load diff

108
source/core/g_mapinfo.h Normal file
View file

@ -0,0 +1,108 @@
/*
** g_level.h
**
**---------------------------------------------------------------------------
** 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.
**---------------------------------------------------------------------------
**
*/
#ifndef __G_LEVEL_H__
#define __G_LEVEL_H__
#include "autosegs.h"
#include "vectors.h"
#include "sc_man.h"
#include "file_zip.h"
struct FMapInfoParser
{
FScanner sc;
bool Internal;
MapRecord* defaultinfoptr;
FMapInfoParser(bool internal = false)
{
Internal = internal;
}
bool CheckLegacyMapDefinition(FString& mapname);
bool ParseLookupName(FString &dest);
void ParseMusic(FString &name, int &order);
void ParseLumpOrTextureName(FString &name);
void ParseCutscene(CutsceneDef& cdef);
void ParseCluster();
void ParseMapName(FString &mapname);
MapRecord *ParseMapHeader(MapRecord &defaultinfo);
void ParseMapDefinition(MapRecord &leveldef);
void ParseEpisodeInfo ();
void ParseCutsceneInfo();
void ParseGameInfo();
void ParseMapInfo (int lump, MapRecord &gamedefaults, MapRecord &defaultinfo);
void ParseOpenBrace();
bool ParseCloseBrace();
bool CheckAssign();
void ParseAssign();
void MustParseAssign();
void ParseComma();
bool CheckNumber();
bool CheckFloat();
void SkipToNext();
void CheckEndOfFile(const char *block);
};
#if defined(_MSC_VER)
#pragma section(SECTION_YREG,read)
#define MSVC_YSEG __declspec(allocate(SECTION_YREG))
#define GCC_YSEG
#else
#define MSVC_YSEG
#define GCC_YSEG __attribute__((section(SECTION_YREG))) __attribute__((used))
#endif
#define DEFINE_MAP_OPTION(name, old) \
static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info); \
static FMapOptInfo MapOpt_##name = \
{ #name, MapOptHandler_##name, old }; \
MSVC_YSEG FMapOptInfo *mapopt_##name GCC_YSEG = &MapOpt_##name; \
static void MapOptHandler_##name(FMapInfoParser &parse, MapRecord *info)
struct FMapOptInfo
{
const char *name;
void (*handler) (FMapInfoParser &parse, MapRecord *levelinfo);
bool old;
};
void G_ParseMapInfo();
#endif //__G_LEVEL_H__

View file

@ -57,7 +57,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "c_dispatch.h"
#include "glbackend/glbackend.h"
#include "engineerrors.h"
#include "mmulti.h"
#include "gamestate.h"
#include "gstrings.h"
#include "texturemanager.h"
@ -73,6 +72,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "automap.h"
#include "v_draw.h"
#include "gi.h"
#include "vm.h"
#include "g_mapinfo.h"
#include "gamefuncs.h"
#include "hw_voxels.h"
#include "hw_palmanager.h"
CVAR(Bool, autoloadlights, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Bool, autoloadbrightmaps, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
@ -101,7 +105,7 @@ CUSTOM_CVAR(Int, mouse_capturemode, 1, CVAR_GLOBALCONFIG | CVAR_ARCHIVE)
// The last remains of sdlayer.cpp
GameInterface* gi;
int myconnectindex, numplayers;
int connecthead, connectpoint2[MAXMULTIPLAYERS];
int connecthead, connectpoint2[MAXPLAYERS];
auto vsnprintfptr = vsnprintf; // This is an inline in Visual Studio but we need an address for it to satisfy the MinGW compiled libraries.
int lastTic;
@ -134,6 +138,7 @@ void SetConsoleNotifyBuffer();
bool PreBindTexture(FRenderState* state, FGameTexture*& tex, EUpscaleFlags& flags, int& scaleflags, int& clampmode, int& translation, int& overrideshader);
void PostLoadSetup();
void FontCharCreated(FGameTexture* base, FGameTexture* untranslated, FGameTexture* translated);
void LoadVoxelModels();
DBaseStatusBar* StatusBar;
@ -224,14 +229,21 @@ static bool System_DisableTextureFilter()
static IntRect System_GetSceneRect()
{
// Special handling so the view with a visible status bar displays properly
int height = windowxy2.y - windowxy1.y + 1, width = windowxy2.x - windowxy1.x + 1;
int viewbottom = windowxy2.y + 1;
int viewheight = viewbottom - windowxy1.y;
int viewright = windowxy2.x + 1;
int viewwidth = viewright - windowxy1.x;
int renderheight;
if (viewheight == screen->GetHeight()) renderheight = viewheight;
else renderheight = (viewwidth * screen->GetHeight() / screen->GetWidth()) & ~7;
IntRect mSceneViewport;
mSceneViewport.left = windowxy1.x;
mSceneViewport.top = windowxy1.y;
mSceneViewport.width = width;
mSceneViewport.height = height;
mSceneViewport.top = screen->GetHeight() - (renderheight + windowxy1.y - ((renderheight - viewheight) / 2));
mSceneViewport.width = viewwidth;
mSceneViewport.height = renderheight;
return mSceneViewport;
}
@ -275,6 +287,11 @@ void System_CrashInfo(char* buffer, size_t bufflen, const char *lfstr)
UserConfig userConfig;
DEFINE_GLOBAL(userConfig)
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nomonsters)
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nosound)
DEFINE_FIELD_X(UserConfigStruct, UserConfig, nologo)
void UserConfig::ProcessOptions()
{
// -help etc are omitted
@ -549,7 +566,7 @@ int GameMain()
I_ShowFatalError(err.what());
r = -1;
}
DeleteScreenJob();
//DeleteScreenJob();
DeinitMenus();
if (StatusBar) StatusBar->Destroy();
StatusBar = nullptr;
@ -565,12 +582,12 @@ int GameMain()
G_SaveConfig();
C_DeinitConsole();
V_ClearFonts();
vox_deinit();
voxClear();
ClearPalManager();
TexMan.DeleteAll();
TileFiles.CloseAll(); // delete the texture data before shutting down graphics.
GLInterface.Deinit();
I_ShutdownGraphics();
engineUnInit();
freeallmodels();
if (gi)
{
delete gi;
@ -590,13 +607,17 @@ int GameMain()
void SetDefaultStrings()
{
// Duke 1.3 does not define its episodes through CON.
if ((g_gameType & GAMEFLAG_DUKE) && fileSystem.FindFile("E4L1.MAP") < 0)
{
auto vol0 = AllocateVolume(); vol0->index = 0;
auto vol1 = AllocateVolume(); vol1->index = 1; vol1->flags = VF_SHAREWARELOCK;
auto vol2 = AllocateVolume(); vol2->index = 2; vol1->flags = VF_SHAREWARELOCK;
// Pre-Atomic releases do not define this.
gVolumeNames[0] = "$L.A. Meltdown";
gVolumeNames[1] = "$Lunar Apocalypse";
gVolumeNames[2] = "$Shrapnel City";
if (g_gameType & GAMEFLAG_SHAREWARE) gVolumeNames[3] = "$The Birth";
vol0->name = "$L.A. Meltdown";
vol1->name = "$Lunar Apocalypse";
vol2->name = "$Shrapnel City";
gSkillNames[0] = "$Piece of Cake";
gSkillNames[1] = "$Let's Rock";
gSkillNames[2] = "$Come get Some";
@ -648,13 +669,16 @@ static TArray<GrpEntry> SetupGame()
{
for (auto& str : game)
{
int g = 0;
for (auto& grp : groups)
{
if (grp.FileInfo.gameid.CompareNoCase(str) == 0)
{
userConfig.gamegrp = grp.FileName;
groupno = g;
goto foundit;
}
g++;
}
}
}
@ -662,16 +686,18 @@ static TArray<GrpEntry> SetupGame()
// If the user has specified a file name, let's see if we know it.
//
if (userConfig.gamegrp.Len())
if (groupno == -1 && userConfig.gamegrp.Len())
{
FString gamegrplower = "/" + userConfig.gamegrp.MakeLower();
FString gamegrplower = userConfig.gamegrp.MakeLower();
if (gamegrplower[1] != ':' || gamegrplower[2] != '/') gamegrplower.Insert(0, "/");
int g = 0;
for (auto& grp : groups)
{
auto grplower = grp.FileName.MakeLower();
grplower.Substitute("\\", "/");
if (grplower.LastIndexOf(gamegrplower) == grplower.Len() - gamegrplower.Len())
FixPathSeperator(grplower);
int pos = grplower.LastIndexOf(gamegrplower);
if (pos >= 0 && pos == grplower.Len() - gamegrplower.Len())
{
groupno = g;
break;
@ -992,6 +1018,7 @@ int RunGame()
LoadScripts();
StartScreen->Progress();
SetDefaultStrings();
Job_Init();
if (Args->CheckParm("-sounddebug"))
C_DoCommand("stat sounddebug");
@ -999,8 +1026,15 @@ int RunGame()
SetupGameButtons();
gameinfo.mBackButton = "engine/graphics/m_back.png";
StartScreen->Progress();
GPalette.Init(MAXPALOOKUPS + 1); // one slot for each translation, plus a separate one for the base palettes.
gi->loadPalette();
voxInit();
TileFiles.LoadArtSet("tiles%03d.art"); // it's the same for all games.
engineInit();
gi->app_init();
StartScreen->Progress();
G_ParseMapInfo();
CreateStatusBar();
SetDefaultMenuColors();
M_Init();
@ -1012,9 +1046,24 @@ int RunGame()
V_LoadTranslations(); // loading the translations must be delayed until the palettes have been fully set up.
lookups.postLoadTables();
PostLoadSetup();
videoInit();
lookups.postLoadLookups();
FMaterial::SetLayerCallback(setpalettelayer);
if (GameStartupInfo.Name.IsNotEmpty()) I_SetWindowTitle(GameStartupInfo.Name);
V_Init2();
twod->Begin(screen->GetWidth(), screen->GetHeight());
twod->End();
UpdateJoystickMenu(NULL);
UpdateVRModes();
setVideoMode();
LoadVoxelModels();
GLInterface.Init(screen->GetWidth());
screen->BeginFrame();
screen->SetTextureFilterMode();
setViewport(hud_size);
D_CheckNetGame();
UpdateGenericUI(ui_generic);
MainLoop();
@ -1061,7 +1110,7 @@ void updatePauseStatus()
//
//==========================================================================
void PolymostProcessVoxels(void);
void LoadVoxelModels(void);
void setVideoMode()
{
@ -1069,25 +1118,6 @@ void setVideoMode()
ydim = screen->GetHeight();
V_UpdateModeSize(xdim, ydim);
videoSetViewableArea(0, 0, xdim - 1, ydim - 1);
videoClearScreen(0);
}
void videoInit()
{
lookups.postLoadLookups();
V_Init2();
setVideoMode();
PolymostProcessVoxels();
GLInterface.Init(screen->GetWidth());
screen->BeginFrame();
screen->SetTextureFilterMode();
setViewport(hud_size);
}
void G_FatalEngineError(void)
{
I_FatalError("There was a problem initializing the engine: %s\n\nThe application will now close.", engineerrstr);
}
//==========================================================================
@ -1378,16 +1408,11 @@ void DrawCrosshair(int deftile, int health, double xdelta, double ydelta, double
void LoadDefinitions()
{
cycle_t deftimer;
deftimer.Reset();
deftimer.Clock();
const char* loaded = nullptr;
const char* defsfile = G_DefFile();
FString razedefsfile = defsfile;
razedefsfile.Substitute(".def", "-raze.def");
loaddefinitionsfile("engine/engine.def", false); // Internal stuff that is required.
loaddefinitionsfile("engine/engine.def"); // Internal stuff that is required.
// check what we have.
// user .defs override the default ones and are not cumulative.
@ -1395,31 +1420,33 @@ void LoadDefinitions()
// otherwise the default rules inherited from older ports apply.
if (userConfig.UserDef.IsNotEmpty())
{
if (!loaddefinitionsfile(userConfig.UserDef, true, false)) loaded = userConfig.UserDef;
loaddefinitionsfile(userConfig.UserDef, false);
}
else
{
if (fileSystem.FileExists(razedefsfile))
{
if (!loaddefinitionsfile(razedefsfile, true, true)) loaded = razedefsfile;
loaddefinitionsfile(razedefsfile, true);
}
else
else if (fileSystem.FileExists(defsfile))
{
if (!loaddefinitionsfile(defsfile, true, false)) loaded = defsfile;
loaddefinitionsfile(defsfile, false);
}
}
if (userConfig.AddDefs)
{
for (auto& m : *userConfig.AddDefs)
{
loaddefinitionsfile(m, false);
}
userConfig.AddDefs.reset();
}
if (GameStartupInfo.def.IsNotEmpty())
{
loaddefinitionsfile(GameStartupInfo.def, false); // Stuff from gameinfo.
loaddefinitionsfile(GameStartupInfo.def); // Stuff from gameinfo.
}
if (loaded)
{
deftimer.Unclock();
DPrintf(DMSG_SPAMMY, "Definitions file \"%s\" loaded, %f ms.\n", loaded, deftimer.TimeMS());
}
userConfig.AddDefs.reset();
// load the widescreen replacements last. This ensures that mods still get the correct CRCs for their own tile replacements.
if (fileSystem.FindFile("engine/widescreen.def") >= 0 && !Args->CheckParm("-nowidescreen"))
@ -1481,13 +1508,69 @@ DEFINE_ACTION_FUNCTION(_Screen, GetViewWindow)
return MIN(numret, 4);
}
DEFINE_ACTION_FUNCTION_NATIVE(_Build, ShadeToLight, shadeToLight)
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, ShadeToLight, shadeToLight)
{
PARAM_PROLOGUE;
PARAM_INT(shade);
ACTION_RETURN_INT(shadeToLight(shade));
}
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, StopAllSounds, FX_StopAllSounds)
{
FX_StopAllSounds();
return 0;
}
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, StopMusic, Mus_Stop)
{
Mus_Stop();
return 0;
}
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, SoundEnabled, SoundEnabled)
{
ACTION_RETURN_INT(SoundEnabled());
}
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, MusicEnabled, MusicEnabled)
{
ACTION_RETURN_INT(MusicEnabled());
}
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, GetTimeFrac, I_GetTimeFrac)
{
ACTION_RETURN_INT(I_GetTimeFrac());
}
DEFINE_ACTION_FUNCTION(_Raze, PlayerName)
{
PARAM_PROLOGUE;
PARAM_INT(index);
ACTION_RETURN_STRING(unsigned(index) >= MAXPLAYERS ? "" : PlayerName(index));
}
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, bsin, bsin)
{
PARAM_PROLOGUE;
PARAM_INT(v);
PARAM_INT(shift);
ACTION_RETURN_INT(bsin(v, shift));
}
DEFINE_ACTION_FUNCTION_NATIVE(_Raze, bcos, bcos)
{
PARAM_PROLOGUE;
PARAM_INT(v);
PARAM_INT(shift);
ACTION_RETURN_INT(bcos(v, shift));
}
DEFINE_ACTION_FUNCTION(_MapRecord, GetCluster)
{
PARAM_SELF_STRUCT_PROLOGUE(MapRecord);
ACTION_RETURN_POINTER(FindCluster(self->cluster));
}
extern bool demoplayback;
DEFINE_GLOBAL(multiplayer)
DEFINE_GLOBAL(netgame)
@ -1495,3 +1578,41 @@ DEFINE_GLOBAL(gameaction)
DEFINE_GLOBAL(gamestate)
DEFINE_GLOBAL(demoplayback)
DEFINE_GLOBAL(consoleplayer)
DEFINE_GLOBAL(currentLevel)
DEFINE_GLOBAL(paused)
DEFINE_FIELD_X(ClusterDef, ClusterDef, name)
DEFINE_FIELD_X(ClusterDef, ClusterDef, InterBackground)
DEFINE_FIELD_X(MapRecord, MapRecord, parTime)
DEFINE_FIELD_X(MapRecord, MapRecord, designerTime)
DEFINE_FIELD_X(MapRecord, MapRecord, fileName)
DEFINE_FIELD_X(MapRecord, MapRecord, labelName)
DEFINE_FIELD_X(MapRecord, MapRecord, name)
DEFINE_FIELD_X(MapRecord, MapRecord, music)
DEFINE_FIELD_X(MapRecord, MapRecord, cdSongId)
DEFINE_FIELD_X(MapRecord, MapRecord, flags)
DEFINE_FIELD_X(MapRecord, MapRecord, levelNumber)
DEFINE_FIELD_X(MapRecord, MapRecord, cluster)
DEFINE_FIELD_X(MapRecord, MapRecord, NextMap)
DEFINE_FIELD_X(MapRecord, MapRecord, NextSecret)
//native readonly String messages[MAX_MESSAGES];
DEFINE_FIELD_X(MapRecord, MapRecord, Author)
DEFINE_FIELD_X(MapRecord, MapRecord, InterBackground)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, kills)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxkills)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, secrets)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, maxsecrets)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, supersecrets)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, playercount)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, time)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, cheated)
DEFINE_FIELD_X(SummaryInfo, SummaryInfo, endofgame)
void InitBuildTiles()
{
// need to find a better way to handle this thing.
}

View file

@ -70,12 +70,12 @@ extern UserConfig userConfig;
extern int nomusic;
extern bool nosound;
inline bool MusicEnabled()
inline int MusicEnabled() // int return is for scripting
{
return mus_enabled && !nomusic;
}
inline bool SoundEnabled()
inline int SoundEnabled()
{
return snd_enabled && !nosound;
}
@ -99,8 +99,15 @@ enum
GAMEFLAG_POWERSLAVE = 0x00002000,
GAMEFLAG_EXHUMED = 0x00004000,
GAMEFLAG_PSEXHUMED = GAMEFLAG_POWERSLAVE | GAMEFLAG_EXHUMED, // the two games really are the same, except for the name and the publisher.
GAMEFLAG_WORLDTOUR = 0x00008000,
GAMEFLAG_DUKEDC = 0x00010000,
GAMEFLAG_WORLDTOUR = 0x00008000,
GAMEFLAG_DUKEDC = 0x00010000,
GAMEFLAG_DUKENW = 0x00020000,
GAMEFLAG_DUKEVACA = 0x00040000,
GAMEFLAG_BLOODCP = 0x00080000,
GAMEFLAG_ROUTE66 = 0x00100000,
GAMEFLAG_SWWANTON = 0x00200000,
GAMEFLAG_SWTWINDRAG = 0x00400000,
GAMEFLAG_DUKECOMPAT = GAMEFLAG_DUKE | GAMEFLAG_NAM | GAMEFLAG_NAPALM | GAMEFLAG_WW2GI | GAMEFLAG_RRALL,
GAMEFLAGMASK = 0x0000FFFF, // flags allowed from grpinfo
@ -160,6 +167,11 @@ inline bool isWW2GI()
return g_gameType & (GAMEFLAG_WW2GI);
}
inline bool isDuke()
{
return g_gameType & (GAMEFLAG_DUKE);
}
inline bool isRR()
{
return g_gameType & (GAMEFLAG_RRALL);
@ -195,7 +207,6 @@ void S_PauseSound(bool notmusic, bool notsfx);
void S_ResumeSound(bool notsfx);
void S_SetSoundPaused(int state);
void G_FatalEngineError(void);
enum
{
MaxSmoothRatio = FRACUNIT

View file

@ -81,6 +81,7 @@ CVARD(Bool, cl_smoothsway, false, CVAR_ARCHIVE, "move SW weapon left and right s
CVARD(Bool, cl_showmagamt, false, CVAR_ARCHIVE, "show the amount of rounds left in the magazine of your weapon on the modern HUD")
CVARD(Bool, cl_nomeleeblur, false, CVAR_ARCHIVE, "enable/disable blur effect with melee weapons in SW")
CVARD(Bool, cl_exhumedoldturn, false, CVAR_ARCHIVE, "enable/disable legacy turning speed for Powerslave/Exhumed")
CVARD(Bool, cl_hudinterpolation, true, CVAR_ARCHIVE, "enable/disable HUD (weapon drawer) interpolation")
CUSTOM_CVARD(Int, cl_autoaim, 1, CVAR_ARCHIVE|CVAR_USERINFO, "enable/disable weapon autoaim")

View file

@ -26,6 +26,7 @@ EXTERN_CVAR(Bool, cl_smoothsway)
EXTERN_CVAR(Bool, cl_showmagamt)
EXTERN_CVAR(Bool, cl_nomeleeblur)
EXTERN_CVAR(Bool, cl_exhumedoldturn)
EXTERN_CVAR(Bool, cl_hudinterpolation)
EXTERN_CVAR(Bool, demorec_seeds_cvar)
EXTERN_CVAR(Bool, demoplay_diffs)

View file

@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gamefuncs.h"
#include "gamestruct.h"
#include "intvec.h"
//---------------------------------------------------------------------------
@ -146,3 +147,180 @@ bool calcChaseCamPos(int* px, int* py, int* pz, spritetype* pspr, short *psectnu
return true;
}
//==========================================================================
//
// note that this returns values in renderer coordinate space with inverted sign!
//
//==========================================================================
void PlanesAtPoint(const sectortype* sec, float dax, float day, float* pceilz, float* pflorz)
{
float ceilz = float(sec->ceilingz);
float florz = float(sec->floorz);
if (((sec->ceilingstat | sec->floorstat) & CSTAT_SECTOR_SLOPE) == CSTAT_SECTOR_SLOPE)
{
auto wal = &wall[sec->wallptr];
auto wal2 = &wall[wal->point2];
float dx = wal2->x - wal->x;
float dy = wal2->y - wal->y;
int i = (int)sqrt(dx * dx + dy * dy) << 5; // length of sector's first wall.
if (i != 0)
{
float const j = (dx * (day - wal->y) - dy * (dax - wal->x)) * (1.f / 8.f);
if (sec->ceilingstat & CSTAT_SECTOR_SLOPE) ceilz += (sec->ceilingheinum * j) / i;
if (sec->floorstat & CSTAT_SECTOR_SLOPE) florz += (sec->floorheinum * j) / i;
}
}
// Scale to render coordinates.
if (pceilz) *pceilz = ceilz * -(1.f / 256.f);
if (pflorz) *pflorz = florz * -(1.f / 256.f);
}
//==========================================================================
//
// Calculate the position of a wall sprite in the world
//
//==========================================================================
void GetWallSpritePosition(const spritetype* spr, vec2_t pos, vec2_t* out, bool render)
{
auto tex = tileGetTexture(spr->picnum);
int width, leftofs;
if (render && hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize)
{
width = TileFiles.tiledata[spr->picnum].hiofs.xsize;
leftofs = (TileFiles.tiledata[spr->picnum].hiofs.xoffs + spr->xoffset);
}
else
{
width = (int)tex->GetDisplayWidth();
leftofs = ((int)tex->GetDisplayLeftOffset() + spr->xoffset);
}
int x = bsin(spr->ang) * spr->xrepeat;
int y = -bcos(spr->ang) * spr->xrepeat;
int xoff = leftofs;
if (spr->cstat & CSTAT_SPRITE_XFLIP) xoff = -xoff;
int origin = (width >> 1) + xoff;
out[0].x = pos.x - MulScale(x, origin, 16);
out[0].y = pos.y - MulScale(y, origin, 16);
out[1].x = out[0].x + MulScale(x, width, 16);
out[1].y = out[0].y + MulScale(y, width, 16);
}
//==========================================================================
//
// Calculate the position of a wall sprite in the world
//
//==========================================================================
void GetFlatSpritePosition(const spritetype* spr, vec2_t pos, vec2_t* out, bool render)
{
auto tex = tileGetTexture(spr->picnum);
int width, height, leftofs, topofs;
if (render && hw_hightile && TileFiles.tiledata[spr->picnum].hiofs.xsize)
{
width = TileFiles.tiledata[spr->picnum].hiofs.xsize * spr->xrepeat;
height = TileFiles.tiledata[spr->picnum].hiofs.ysize * spr->yrepeat;
leftofs = (TileFiles.tiledata[spr->picnum].hiofs.xoffs + spr->xoffset) * spr->xrepeat;
topofs = (TileFiles.tiledata[spr->picnum].hiofs.yoffs + spr->yoffset) * spr->yrepeat;
}
else
{
width = (int)tex->GetDisplayWidth() * spr->xrepeat;
height = (int)tex->GetDisplayHeight() * spr->yrepeat;
leftofs = ((int)tex->GetDisplayLeftOffset() + spr->xoffset) * spr->xrepeat;
topofs = ((int)tex->GetDisplayTopOffset() + spr->yoffset) * spr->yrepeat;
}
if (spr->cstat & CSTAT_SPRITE_XFLIP) leftofs = -leftofs;
if (spr->cstat & CSTAT_SPRITE_YFLIP) topofs = -topofs;
int sprcenterx = (width >> 1) + leftofs;
int sprcentery = (height >> 1) + topofs;
int cosang = bcos(spr->ang);
int sinang = bsin(spr->ang);
out[0].x = pos.x + DMulScale(sinang, sprcenterx, cosang, sprcentery, 16);
out[0].y = pos.y + DMulScale(sinang, sprcentery, -cosang, sprcenterx, 16);
out[1].x = out[0].x - MulScale(sinang, width, 16);
out[1].y = out[0].y + MulScale(cosang, width, 16);
vec2_t sub = { MulScale(cosang, height, 16), MulScale(sinang, height, 16) };
out[2] = out[1] - sub;
out[3] = out[0] - sub;
}
//==========================================================================
//
// Check if some walls are set to use rotated textures.
// Ideally this should just have been done with texture rotation,
// but the effects on the render code would be too severe due to the alignment mess.
//
//==========================================================================
void checkRotatedWalls()
{
for (int i = 0; i < numwalls; ++i)
{
if (wall[i].cstat & CSTAT_WALL_ROTATE_90)
{
auto& w = wall[i];
auto& tile = RotTile(w.picnum + animateoffs(w.picnum, 16384));
if (tile.newtile == -1 && tile.owner == -1)
{
auto owner = w.picnum + animateoffs(w.picnum, 16384);
tile.newtile = TileFiles.tileCreateRotated(owner);
assert(tile.newtile != -1);
RotTile(tile.newtile).owner = w.picnum + animateoffs(w.picnum, 16384);
}
}
}
}
//==========================================================================
//
// vector serializers
//
//==========================================================================
FSerializer& Serialize(FSerializer& arc, const char* key, vec2_t& c, vec2_t* def)
{
if (def && !memcmp(&c, def, sizeof(c))) return arc;
if (arc.BeginObject(key))
{
arc("x", c.x, def ? &def->x : nullptr)
("y", c.y, def ? &def->y : nullptr)
.EndObject();
}
return arc;
}
FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def)
{
if (def && !memcmp(&c, def, sizeof(c))) return arc;
if (arc.BeginObject(key))
{
arc("x", c.x, def ? &def->x : nullptr)
("y", c.y, def ? &def->y : nullptr)
("z", c.z, def ? &def->z : nullptr)
.EndObject();
}
return arc;
}

View file

@ -1,9 +1,121 @@
#pragma once
#include "gamecontrol.h"
#include "buildtypes.h"
#include "binaryangle.h"
#include "build.h"
extern int cameradist, cameraclock;
bool calcChaseCamPos(int* px, int* py, int* pz, spritetype* pspr, short *psectnum, binangle ang, fixedhoriz horiz, double const smoothratio);
void loaddefinitionsfile(const char* fn, bool cumulative = false);
bool calcChaseCamPos(int* px, int* py, int* pz, spritetype* pspr, short *psectnum, binangle ang, fixedhoriz horiz, double const smoothratio);
void PlanesAtPoint(const sectortype* sec, float dax, float day, float* ceilz, float* florz);
void setWallSectors();
void GetWallSpritePosition(const spritetype* spr, vec2_t pos, vec2_t* out, bool render = false);
void GetFlatSpritePosition(const spritetype* spr, vec2_t pos, vec2_t* out, bool render = false);
void checkRotatedWalls();
// y is negated so that the orientation is the same as in GZDoom, in order to use its utilities.
// The render code should NOT use Build coordinates for anything!
inline double WallStartX(int wallnum)
{
return wall[wallnum].x * (1 / 16.);
}
inline double WallStartY(int wallnum)
{
return wall[wallnum].y * (1 / -16.);
}
inline double WallEndX(int wallnum)
{
return wall[wall[wallnum].point2].x * (1 / 16.);
}
inline double WallEndY(int wallnum)
{
return wall[wall[wallnum].point2].y * (1 / -16.);
}
inline double WallStartX(const walltype* wallnum)
{
return wallnum->x * (1 / 16.);
}
inline double WallStartY(const walltype* wallnum)
{
return wallnum->y * (1 / -16.);
}
inline DVector2 WallStart(const walltype* wallnum)
{
return { WallStartX(wallnum), WallStartY(wallnum) };
}
inline double WallEndX(const walltype* wallnum)
{
return wall[wallnum->point2].x * (1 / 16.);
}
inline double WallEndY(const walltype* wallnum)
{
return wall[wallnum->point2].y * (1 / -16.);
}
inline DVector2 WallEnd(const walltype* wallnum)
{
return { WallEndX(wallnum), WallEndY(wallnum) };
}
inline DVector2 WallDelta(const walltype* wallnum)
{
return WallEnd(wallnum) - WallStart(wallnum);
}
inline double SpriteX(int wallnum)
{
return sprite[wallnum].x * (1 / 16.);
}
inline double SpriteY(int wallnum)
{
return sprite[wallnum].y * (1 / -16.);
}
inline double PointOnLineSide(double x, double y, double linex, double liney, double deltax, double deltay)
{
return (x - linex) * deltay - (y - liney) * deltax;
}
inline double PointOnLineSide(const DVector2 &pos, const walltype *line)
{
return (pos.X - WallStartX(line)) * WallDelta(line).Y - (pos.Y - WallStartY(line)) * WallDelta(line).X;
}
template<class T>
inline double PointOnLineSide(const TVector2<T>& pos, const TVector2<T>& linestart, const TVector2<T>& lineend)
{
return (pos.X - linestart.X) * (lineend.Y - linestart.Y) - (pos.Y - linestart.Y) * (lineend.X - linestart.X);
}
inline int sectorofwall(int wallNum)
{
if ((unsigned)wallNum < (unsigned)numwalls) return wall[wallNum].sector;
return -1;
}
extern int numshades;
// Return type is int because this gets passed to variadic functions where structs may produce undefined behavior.
inline int shadeToLight(int shade)
{
shade = clamp(shade, 0, numshades - 1);
int light = Scale(numshades - 1 - shade, 255, numshades - 1);
return PalEntry(255, light, light, light);
}
inline void copyfloorpal(spritetype* spr, const sectortype* sect)
{
if (!lookups.noFloorPal(sect->floorpal)) spr->pal = sect->floorpal;
}

View file

@ -45,6 +45,7 @@
#include "v_draw.h"
#include "v_font.h"
#include "gamestruct.h"
#include "gamefuncs.h"
F2DDrawer twodpsp;

View file

@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gameinput.h"
#include "gamestruct.h"
#include "serializer.h"
#include "build.h"
CVARD(Bool, invertmousex, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert horizontal mouse movement")
CVARD(Bool, invertmouse, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "invert vertical mouse movement")
@ -49,46 +50,18 @@ int getincangle(int a, int na)
return na-a;
}
double getincanglef(double a, double na)
binangle getincanglebam(binangle a, binangle na)
{
a = fmod(a, 2048.);
na = fmod(na, 2048.);
int64_t cura = a.asbam();
int64_t newa = na.asbam();
if(fabs(a-na) >= 1024)
if(abs(cura-newa) > INT32_MAX)
{
if(na > 1024) na -= 2048;
if(a > 1024) a -= 2048;
if(newa > INT32_MAX) newa -= UINT32_MAX;
if(cura > INT32_MAX) cura -= UINT32_MAX;
}
return na-a;
}
fixed_t getincangleq16(fixed_t a, fixed_t na)
{
a &= 0x7FFFFFF;
na &= 0x7FFFFFF;
if(abs(a-na) >= IntToFixed(1024))
{
if(na > IntToFixed(1024)) na -= IntToFixed(2048);
if(a > IntToFixed(1024)) a -= IntToFixed(2048);
}
return na-a;
}
lookangle getincanglebam(binangle a, binangle na)
{
int64_t cura = a.asbam() & 0xFFFFFFFF;
int64_t newa = na.asbam() & 0xFFFFFFFF;
if(abs(cura-newa) >= BAngToBAM(1024))
{
if(newa > BAngToBAM(1024)) newa -= BAngToBAM(2048);
if(cura > BAngToBAM(1024)) cura -= BAngToBAM(2048);
}
return bamlook(newa-cura);
return bamang(newa-cura);
}
//---------------------------------------------------------------------------
@ -271,13 +244,13 @@ void processMovement(InputPacket* currInput, InputPacket* inputBuffer, ControlIn
//
//---------------------------------------------------------------------------
void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, double const scaleAdjust)
void PlayerHorizon::applyinput(float const horz, ESyncBits* actions, double const scaleAdjust)
{
// Process only if no targeted horizon set.
if (!horizon->targetset())
if (!targetset())
{
// Store current horizon as true pitch.
double pitch = horizon->horiz.aspitch();
double pitch = horiz.aspitch();
if (horz)
{
@ -312,20 +285,17 @@ void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, do
}
// clamp before converting back to horizon
horizon->horiz = q16horiz(clamp(PitchToHoriz(pitch), gi->playerHorizMin(), gi->playerHorizMax()));
horiz = q16horiz(clamp(PitchToHoriz(pitch), gi->playerHorizMin(), gi->playerHorizMax()));
// return to center if conditions met.
if ((*actions & SB_CENTERVIEW) && !(*actions & (SB_LOOK_UP|SB_LOOK_DOWN)))
if ((*actions & SB_CENTERVIEW) && !(*actions & (SB_LOOK_UP|SB_LOOK_DOWN)) && horiz.asq16())
{
if (abs(horizon->horiz.asq16()) > FloatToFixed(0.25))
{
// move horiz back to 0
horizon->horiz -= q16horiz(xs_CRoundToInt(scaleAdjust * horizon->horiz.asq16() * (10. / GameTicRate)));
}
else
// move horiz back to 0
horiz -= buildfhoriz(scaleAdjust * horiz.asbuildf() * (10. / GameTicRate));
if (abs(horiz.asq16()) < (FRACUNIT >> 2))
{
// not looking anymore because horiz is back at 0
horizon->horiz = q16horiz(0);
horiz = q16horiz(0);
*actions &= ~SB_CENTERVIEW;
}
}
@ -342,66 +312,72 @@ void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, do
//
//---------------------------------------------------------------------------
void applylook(PlayerAngle* angle, float const avel, ESyncBits* actions, double const scaleAdjust)
void PlayerAngle::applyinput(float const avel, ESyncBits* actions, double const scaleAdjust)
{
// return q16rotscrnang to 0 and set to 0 if less than a quarter of a unit
angle->rotscrnang -= bamlook(xs_CRoundToInt(scaleAdjust * angle->rotscrnang.asbam() * (15. / GameTicRate)));
if (abs(angle->rotscrnang.asbam()) < (BAMUNIT >> 2)) angle->rotscrnang = bamlook(0);
if (rotscrnang.asbam())
{
// return rotscrnang to 0
rotscrnang -= buildfang(scaleAdjust * rotscrnang.signedbuildf() * (15. / GameTicRate));
if (abs(rotscrnang.signedbam()) < (BAMUNIT >> 2)) rotscrnang = bamang(0);
}
// return q16look_ang to 0 and set to 0 if less than a quarter of a unit
angle->look_ang -= bamlook(xs_CRoundToInt(scaleAdjust * angle->look_ang.asbam() * (7.5 / GameTicRate)));
if (abs(angle->look_ang.asbam()) < (BAMUNIT >> 2)) angle->look_ang = bamlook(0);
if (look_ang.asbam())
{
// return look_ang to 0
look_ang -= buildfang(scaleAdjust * look_ang.signedbuildf() * (7.5 / GameTicRate));
if (abs(look_ang.signedbam()) < (BAMUNIT >> 2)) look_ang = bamang(0);
}
if (*actions & SB_LOOK_LEFT)
{
// start looking left
angle->look_ang -= bamlook(xs_CRoundToInt(scaleAdjust * (4560. / GameTicRate) * BAMUNIT));
angle->rotscrnang += bamlook(xs_CRoundToInt(scaleAdjust * (720. / GameTicRate) * BAMUNIT));
look_ang += buildfang(scaleAdjust * -(4560. / GameTicRate));
rotscrnang += buildfang(scaleAdjust * (720. / GameTicRate));
}
if (*actions & SB_LOOK_RIGHT)
{
// start looking right
angle->look_ang += bamlook(xs_CRoundToInt(scaleAdjust * (4560. / GameTicRate) * BAMUNIT));
angle->rotscrnang -= bamlook(xs_CRoundToInt(scaleAdjust * (720. / GameTicRate) * BAMUNIT));
look_ang += buildfang(scaleAdjust * (4560. / GameTicRate));
rotscrnang += buildfang(scaleAdjust * -(720. / GameTicRate));
}
if (!angle->targetset())
if (!targetset())
{
if (*actions & SB_TURNAROUND)
{
if (angle->spin.asbam() == 0)
if (spin == 0)
{
// currently not spinning, so start a spin
angle->spin = buildlook(-1024);
spin = -1024.;
}
*actions &= ~SB_TURNAROUND;
}
if (angle->spin.asbam() < 0)
{
// return spin to 0
lookangle add = bamlook(xs_CRoundToUInt(scaleAdjust * ((!(*actions & SB_CROUCH) ? 3840. : 1920.) / GameTicRate) * BAMUNIT));
angle->spin += add;
if (angle->spin.asbam() > 0)
{
// Don't overshoot our target. With variable factor this is possible.
add -= angle->spin;
angle->spin = bamlook(0);
}
angle->ang += bamang(add.asbam());
}
if (avel)
{
// add player's input
angle->ang += degang(avel);
angle->spin = bamlook(0);
ang += degang(avel);
spin = 0;
}
if (spin < 0)
{
// return spin to 0
double add = scaleAdjust * ((!(*actions & SB_CROUCH) ? 3840. : 1920.) / GameTicRate);
spin += add;
if (spin > 0)
{
// Don't overshoot our target. With variable factor this is possible.
add -= spin;
spin = 0;
}
ang += buildfang(add);
}
}
else
{
angle->spin = bamlook(0);
spin = 0;
}
}
@ -411,7 +387,7 @@ void applylook(PlayerAngle* angle, float const avel, ESyncBits* actions, double
//
//---------------------------------------------------------------------------
void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust, bool const climbing)
void PlayerHorizon::calcviewpitch(vec2_t const pos, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust, bool const climbing)
{
if (cl_slopetilting)
{
@ -438,7 +414,7 @@ void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, b
// accordingly
if (cursectnum == tempsect || (!isBlood() && abs(getflorzofslope(tempsect, x, y) - k) <= (4 << 8)))
{
*horizoff += q16horiz(xs_CRoundToInt(scaleAdjust * ((j - k) * (!isBlood() ? 160 : 1408))));
horizoff += q16horiz(xs_CRoundToInt(scaleAdjust * ((j - k) * (!isBlood() ? 160 : 1408))));
}
}
}
@ -446,21 +422,21 @@ void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, b
if (climbing)
{
// tilt when climbing but you can't even really tell it.
if (horizoff->asq16() < IntToFixed(100))
*horizoff += q16horiz(xs_CRoundToInt(scaleAdjust * (((IntToFixed(100) - horizoff->asq16()) >> 3) + FRACUNIT)));
if (horizoff.asq16() < IntToFixed(100))
horizoff += q16horiz(xs_CRoundToInt(scaleAdjust * (((IntToFixed(100) - horizoff.asq16()) >> 3) + FRACUNIT)));
}
else
{
// Make horizoff grow towards 0 since horizoff is not modified when you're not on a slope.
if (horizoff->asq16() > 0)
if (horizoff.asq16() > 0)
{
*horizoff += q16horiz(xs_CRoundToInt(-scaleAdjust * ((horizoff->asq16() >> 3) + FRACUNIT)));
if (horizoff->asq16() < 0) *horizoff = q16horiz(0);
horizoff += q16horiz(xs_CRoundToInt(-scaleAdjust * ((horizoff.asq16() >> 3) + FRACUNIT)));
if (horizoff.asq16() < 0) horizoff = q16horiz(0);
}
if (horizoff->asq16() < 0)
if (horizoff.asq16() < 0)
{
*horizoff += q16horiz(xs_CRoundToInt(-scaleAdjust * ((horizoff->asq16() >> 3) - FRACUNIT)));
if (horizoff->asq16() > 0) *horizoff = q16horiz(0);
horizoff += q16horiz(xs_CRoundToInt(-scaleAdjust * ((horizoff.asq16() >> 3) - FRACUNIT)));
if (horizoff.asq16() > 0) horizoff = q16horiz(0);
}
}
}

View file

@ -7,9 +7,7 @@
#include "packet.h"
int getincangle(int a, int na);
double getincanglef(double a, double na);
fixed_t getincangleq16(fixed_t a, fixed_t na);
lookangle getincanglebam(binangle a, binangle na);
binangle getincanglebam(binangle a, binangle na);
struct PlayerHorizon
{
@ -29,7 +27,12 @@ struct PlayerHorizon
void addadjustment(double value)
{
__addadjustment(q16horiz(FloatToFixed(value)));
__addadjustment(buildfhoriz(value));
}
void addadjustment(fixedhoriz value)
{
__addadjustment(value);
}
void resetadjustment()
@ -37,19 +40,14 @@ struct PlayerHorizon
adjustment = 0;
}
void settarget(int value, bool backup = false)
{
__settarget(buildhoriz(clamp(value, FixedToInt(gi->playerHorizMin()), FixedToInt(gi->playerHorizMax()))), backup);
}
void settarget(double value, bool backup = false)
{
__settarget(buildfhoriz(clamp(value, FixedToFloat(gi->playerHorizMin()), FixedToFloat(gi->playerHorizMax()))), backup);
__settarget(buildfhoriz(value), backup);
}
void settarget(fixedhoriz value, bool backup = false)
{
__settarget(q16horiz(clamp(value.asq16(), gi->playerHorizMin(), gi->playerHorizMax())), backup);
__settarget(value, backup);
}
bool targetset()
@ -61,11 +59,11 @@ struct PlayerHorizon
{
if (targetset())
{
auto delta = (target - horiz).asq16();
auto delta = (target - horiz).asbuildf();
if (abs(delta) > FRACUNIT)
if (abs(delta) > 1)
{
horiz += q16horiz(xs_CRoundToInt(scaleAdjust * delta));
horiz += buildfhoriz(scaleAdjust * delta);
}
else
{
@ -75,7 +73,7 @@ struct PlayerHorizon
}
else if (adjustment)
{
horiz += q16horiz(xs_CRoundToInt(scaleAdjust * adjustment));
horiz += buildfhoriz(scaleAdjust * adjustment);
}
}
@ -91,12 +89,17 @@ struct PlayerHorizon
fixedhoriz interpolatedsum(double const smoothratio)
{
double const ratio = smoothratio * (1. / FRACUNIT);
fixed_t const prev = osum().asq16();
fixed_t const curr = sum().asq16();
return q16horiz(prev + xs_CRoundToInt(ratio * (curr - prev)));
return q16horiz(interpolatedvalue(osum().asq16(), sum().asq16(), smoothratio));
}
double horizsumfrac(double const smoothratio)
{
return (!SyncInput() ? sum() : interpolatedsum(smoothratio)).asbuildf() * (1. / 16.); // Used within draw code for Duke.
}
void applyinput(float const horz, ESyncBits* actions, double const scaleAdjust = 1);
void calcviewpitch(vec2_t const pos, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust = 1, bool const climbing = false);
private:
fixedhoriz target;
double adjustment;
@ -105,7 +108,7 @@ private:
{
if (!SyncInput())
{
adjustment += value.asq16();
adjustment += value.asbuildf();
}
else
{
@ -113,8 +116,10 @@ private:
}
}
void __settarget(fixedhoriz value, bool backup = false)
void __settarget(fixedhoriz value, bool backup)
{
value = q16horiz(clamp(value.asq16(), gi->playerHorizMin(), gi->playerHorizMax()));
if (!SyncInput() && !backup)
{
target = value;
@ -130,8 +135,8 @@ private:
struct PlayerAngle
{
binangle ang, oang;
lookangle look_ang, olook_ang, rotscrnang, orotscrnang, spin;
binangle ang, oang, look_ang, olook_ang, rotscrnang, orotscrnang;
double spin;
void backup()
{
@ -147,24 +152,14 @@ struct PlayerAngle
rotscrnang = orotscrnang;
}
void addadjustment(int value)
{
__addadjustment(buildlook(value));
}
void addadjustment(double value)
{
__addadjustment(buildflook(value));
}
void addadjustment(lookangle value)
{
__addadjustment(value);
__addadjustment(buildfang(value));
}
void addadjustment(binangle value)
{
__addadjustment(bamlook(value.asbam()));
__addadjustment(value);
}
void resetadjustment()
@ -172,14 +167,9 @@ struct PlayerAngle
adjustment = 0;
}
void settarget(int value, bool backup = false)
{
__settarget(buildang(value & 2047), backup);
}
void settarget(double value, bool backup = false)
{
__settarget(buildfang(fmod(value, 2048)), backup);
__settarget(buildfang(value), backup);
}
void settarget(binangle value, bool backup = false)
@ -196,11 +186,11 @@ struct PlayerAngle
{
if (targetset())
{
auto delta = getincanglebam(ang, target).asbam();
auto delta = getincanglebam(ang, target).signedbuildf();
if (delta > BAMUNIT)
if (abs(delta) > 1)
{
ang += bamang(xs_CRoundToUInt(scaleAdjust * delta));
ang += buildfang(scaleAdjust * delta);
}
else
{
@ -210,7 +200,7 @@ struct PlayerAngle
}
else if (adjustment)
{
ang += bamang(xs_CRoundToUInt(scaleAdjust * adjustment));
ang += buildfang(scaleAdjust * adjustment);
}
}
@ -226,39 +216,40 @@ struct PlayerAngle
binangle interpolatedsum(double const smoothratio)
{
double const ratio = smoothratio * (1. / FRACUNIT);
uint32_t const dang = UINT32_MAX >> 1;
int64_t const prev = osum().asbam();
int64_t const curr = sum().asbam();
return bamang(prev + xs_CRoundToUInt(ratio * (((curr + dang - prev) & 0xFFFFFFFF) - dang)));
return interpolatedangle(osum(), sum(), smoothratio);
}
lookangle interpolatedlookang(double const smoothratio)
binangle interpolatedlookang(double const smoothratio)
{
double const ratio = smoothratio * (1. / FRACUNIT);
return bamlook(olook_ang.asbam() + xs_CRoundToInt(ratio * (look_ang - olook_ang).asbam()));
return interpolatedangle(olook_ang, look_ang, smoothratio);
}
lookangle interpolatedrotscrn(double const smoothratio)
binangle interpolatedrotscrn(double const smoothratio)
{
double const ratio = smoothratio * (1. / FRACUNIT);
return bamlook(orotscrnang.asbam() + xs_CRoundToInt(ratio * (rotscrnang - orotscrnang).asbam()));
return interpolatedangle(orotscrnang, rotscrnang, smoothratio);
}
double look_anghalf(double const smoothratio)
{
return (!SyncInput() ? look_ang : interpolatedlookang(smoothratio)).asbam() * (0.5 / BAMUNIT); // Used within draw code for weapon and crosshair when looking left/right.
return (!SyncInput() ? look_ang : interpolatedlookang(smoothratio)).signedbuildf() * 0.5; // Used within draw code for weapon and crosshair when looking left/right.
}
double looking_arc(double const smoothratio)
{
return fabs((!SyncInput() ? look_ang : interpolatedlookang(smoothratio)).signedbuildf()) * (1. / 9.); // Used within draw code for weapon and crosshair when looking left/right.
}
void applyinput(float const avel, ESyncBits* actions, double const scaleAdjust = 1);
private:
binangle target;
double adjustment;
void __addadjustment(lookangle value)
void __addadjustment(binangle value)
{
if (!SyncInput())
{
adjustment += value.asbam();
adjustment += value.signedbuildf();
}
else
{
@ -266,7 +257,7 @@ private:
}
}
void __settarget(binangle value, bool backup = false)
void __settarget(binangle value, bool backup)
{
if (!SyncInput() && !backup)
{
@ -290,6 +281,3 @@ void updateTurnHeldAmt(double const scaleAdjust);
bool const isTurboTurnTime();
void resetTurnHeldAmt();
void processMovement(InputPacket* currInput, InputPacket* inputBuffer, ControlInfo* const hidInput, double const scaleAdjust, int const drink_amt = 0, bool const allowstrafe = true, double const turnscale = 1);
void sethorizon(PlayerHorizon* horizon, float const horz, ESyncBits* actions, double const scaleAdjust = 1);
void applylook(PlayerAngle* angle, float const avel, ESyncBits* actions, double const scaleAdjust = 1);
void calcviewpitch(vec2_t const pos, fixedhoriz* horizoff, binangle const ang, bool const aimmode, bool const canslopetilt, int const cursectnum, double const scaleAdjust = 1, bool const climbing = false);

View file

@ -43,8 +43,10 @@ enum gameaction_t : int
ga_nextlevel, // Actually start the next level.
ga_loadgamehidecon,
ga_newgamenostopsound, // start a new game
ga_endscreenjob,
ga_fullconsole,
};
extern gamestate_t gamestate;
extern gameaction_t gameaction;
extern int intermissiondelay;

View file

@ -11,6 +11,8 @@ bool System_WantGuiCapture(); // During playing this tells us whether the game m
#include "inputstate.h"
class FSerializer;
struct FRenderViewpoint;
struct spritetype;
struct GameStats
{
@ -45,6 +47,19 @@ struct MapRecord;
extern cycle_t drawtime, actortime, thinktime, gameupdatetime;
struct GeoEffect
{
int* geosectorwarp;
int* geosectorwarp2;
int* geosector;
int* geox;
int* geoy;
int* geox2;
int* geoy2;
int geocnt;
};
struct GameInterface
{
virtual const char* Name() { return "$"; }
@ -64,12 +79,11 @@ struct GameInterface
virtual void MenuSound(EMenuSounds snd) {}
virtual bool CanSave() { return true; }
virtual void CustomMenuSelection(int menu, int item) {}
virtual bool StartGame(FNewGameStartup& gs) { return false; }
virtual bool StartGame(FNewGameStartup& gs) { return true; }
virtual FSavegameInfo GetSaveSig() { return { "", 0, 0}; }
virtual double SmallFontScale() { return 1; }
virtual void SerializeGameState(FSerializer& arc) {}
virtual void DrawPlayerSprite(const DVector2& origin, bool onteam) {}
virtual void QuitToTitle() {}
virtual void SetAmbience(bool on) {}
virtual FString GetCoordString() { return "'stat coord' not implemented"; }
virtual void ExitFromMenu() { throw CExitEvent(0); }
@ -99,6 +113,11 @@ struct GameInterface
virtual int chaseCamX(binangle ang) { return 0; }
virtual int chaseCamY(binangle ang) { return 0; }
virtual int chaseCamZ(fixedhoriz horiz) { return 0; }
virtual void processSprites(spritetype* tsprite, int& spritesortcnt, int viewx, int viewy, int viewz, binangle viewang, double smoothRatio) = 0;
virtual void UpdateCameras(double smoothratio) {}
virtual void EnterPortal(spritetype* viewer, int type) {}
virtual void LeavePortal(spritetype* viewer, int type) {}
virtual bool GetGeoEffect(GeoEffect* eff, int viewsector) { return false; }
virtual int Voxelize(int sprnum) { return -1; }
virtual FString statFPS()

View file

@ -61,6 +61,7 @@ double Get(int index, int type)
void Set(int index, int type, double val)
{
int old;
switch(type)
{
case Interp_Sect_Floorz: sector[index].floorz = xs_CRoundToInt(val); break;
@ -72,8 +73,8 @@ void Set(int index, int type, double val)
case Interp_Sect_CeilingPanX: sector[index].ceilingxpan_ = float(val); break;
case Interp_Sect_CeilingPanY: sector[index].ceilingypan_ = float(val); break;
case Interp_Wall_X: wall[index].x = xs_CRoundToInt(val); break;
case Interp_Wall_Y: wall[index].y = xs_CRoundToInt(val); break;
case Interp_Wall_X: old = wall[index].x; wall[index].x = xs_CRoundToInt(val); if (wall[index].x != old) sector[wall[index].sector].dirty = 255; break;
case Interp_Wall_Y: old = wall[index].y; wall[index].y = xs_CRoundToInt(val); if (wall[index].y != old) sector[wall[index].sector].dirty = 255; break;
case Interp_Wall_PanX: wall[index].xpan_ = float(val); break;
case Interp_Wall_PanY: wall[index].ypan_ = float(val); break;

View file

@ -1,37 +1,54 @@
#pragma once
class FSerializer;
struct vec2_16_t
{
int16_t x, y;
};
#if 0
struct vec2_t
struct vec2_t
{
int32_t x, y;
};
struct vec2f_t
{
float x, y;
};
struct vec2d_t
{
double x, y;
vec2_t() = default;
vec2_t(const vec2_t&) = default;
vec2_t(int x_, int y_) : x(x_), y(y_) {}
vec2_t operator+(const vec2_t& other) const { return { x + other.x, y + other.y }; }
vec2_t operator-(const vec2_t& other) const { return { x - other.x, y - other.y }; }
vec2_t& operator+=(const vec2_t& other) { x += other.x; y += other.y; return *this; };
vec2_t& operator-=(const vec2_t& other) { x -= other.x; y -= other.y; return *this; };
bool operator == (const vec2_t& other) const { return x == other.x && y == other.y; };
};
struct vec3_t
{
union
{
struct
{
int32_t x, y, z;
};
union
{
struct
{
int32_t x, y, z;
};
vec2_t vec2;
};
vec3_t() = default;
vec3_t(const vec3_t&) = default;
vec3_t(int x_, int y_, int z_) : x(x_), y(y_), z(z_) {}
vec3_t operator+(const vec3_t& other) const { return { x + other.x, y + other.y, z + other.z }; }
vec3_t operator-(const vec3_t& other) const { return { x - other.x, y - other.y, z - other.z }; }
vec3_t& operator+=(const vec3_t & other) { x += other.x; y += other.y; z += other.z; return *this; };
vec3_t& operator-=(const vec3_t & other) { x -= other.x; y -= other.y; z += other.z; return *this; };
};
#if 0
struct vec2f_t
{
float x, y;
};
struct vec3_16_t
@ -46,3 +63,6 @@ struct vec3_16_t
};
};
#endif
FSerializer& Serialize(FSerializer& arc, const char* key, vec2_t& c, vec2_t* def);
FSerializer& Serialize(FSerializer& arc, const char* key, vec3_t& c, vec3_t* def);

View file

@ -74,7 +74,6 @@
#include "vm.h"
#include "gamestate.h"
#include "screenjob.h"
#include "mmulti.h"
#include "c_console.h"
#include "uiinput.h"
#include "v_video.h"
@ -105,6 +104,7 @@ bool r_NoInterpolate;
int entertic;
int oldentertics;
int gametic;
int intermissiondelay;
FString BackupSaveGame;
@ -133,6 +133,20 @@ void G_BuildTiccmd(ticcmd_t* cmd)
//==========================================================================
bool newGameStarted;
void NewGame(MapRecord* map, int skill, bool ns = false)
{
newGameStarted = true;
ShowIntermission(nullptr, map, nullptr, [=](bool) {
gi->NewGame(map, skill, ns);
});
}
//==========================================================================
//
//
//
//==========================================================================
static void GameTicker()
{
int i;
@ -159,7 +173,7 @@ static void GameTicker()
FX_SetReverb(0);
gi->FreeLevelData();
gameaction = ga_level;
gi->NewGame(g_nextmap, -1);
NewGame(g_nextmap, -1);
BackupSaveGame = "";
}
break;
@ -191,13 +205,12 @@ static void GameTicker()
FX_StopAllSounds();
case ga_newgamenostopsound:
DeleteScreenJob();
newGameStarted = true;
FX_SetReverb(0);
gi->FreeLevelData();
C_FlushDisplay();
gameaction = ga_level;
BackupSaveGame = "";
gi->NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
break;
case ga_startup:
@ -209,6 +222,7 @@ static void GameTicker()
case ga_mainmenu:
FX_StopAllSounds();
if (isBlood()) Mus_Stop();
case ga_mainmenunostopsound:
gi->FreeLevelData();
gamestate = GS_MENUSCREEN;
@ -253,6 +267,10 @@ static void GameTicker()
gameaction = ga_nothing;
break;
case ga_endscreenjob:
EndScreenJob();
break;
// for later
// case ga_recordgame, // start a new demo recording (later)
// case ga_loadgameplaydemo, // load a savegame and play a demo.
@ -331,7 +349,16 @@ static void GameTicker()
break;
case GS_INTERMISSION:
case GS_INTRO:
ScreenJobTick();
if (intermissiondelay > 0)
{
intermissiondelay--;
break;
}
if (ScreenJobTick())
{
// synchronize termination with the playsim.
Net_WriteByte(DEM_ENDSCREENJOB);
}
break;
}
@ -371,7 +398,7 @@ void Display()
case GS_INTRO:
case GS_INTERMISSION:
// screen jobs are not bound by the game ticker so they need to be ticked in the display loop.
ScreenJobDraw();
if (intermissiondelay <= 0) ScreenJobDraw();
break;
case GS_LEVEL:
@ -633,6 +660,16 @@ void MainLoop ()
// Clamp the timer to TICRATE until the playloop has been entered.
r_NoInterpolate = true;
if (userConfig.CommandMap.IsNotEmpty())
{
auto maprecord = FindMapByName(userConfig.CommandMap);
userConfig.CommandMap = "";
if (maprecord)
{
NewGame(maprecord, /*userConfig.skill*/2); // todo: fix the skill.
}
}
for (;;)
{
try

View file

@ -35,8 +35,12 @@
#include "build.h"
#include "sc_man.h"
#include "printf.h"
#include "c_dispatch.h"
#include "md4.h"
#include "hw_sections.h"
static TArray<usermaphack_t> usermaphacks;
TArray<int> blockingpairs[MAXWALLS];
void AddUserMapHack(usermaphack_t& mhk)
{
@ -45,7 +49,9 @@ void AddUserMapHack(usermaphack_t& mhk)
static int32_t LoadMapHack(const char *filename)
{
int32_t currentsprite = -1;
int currentsprite = -1;
int currentwall = -1;
int currentsector = -1;
FScanner sc;
int lump = fileSystem.FindFile(filename);
@ -68,8 +74,30 @@ static int32_t LoadMapHack(const char *filename)
return true;
};
auto validateWall = [&]()
{
if (currentwall < 0)
{
sc.ScriptMessage("Using %s without a valid wall", token.GetChars());
return false;
}
return true;
};
auto validateSector = [&]()
{
if (currentsector < 0)
{
sc.ScriptMessage("Using %s without a valid sector", token.GetChars());
return false;
}
return true;
};
if (sc.Compare("sprite"))
{
currentwall = -1;
currentsector = -1;
if (sc.CheckNumber())
{
currentsprite = sc.Number;
@ -81,6 +109,137 @@ static int32_t LoadMapHack(const char *filename)
}
else currentsprite = -1;
}
if (sc.Compare("wall"))
{
currentsprite = -1;
currentsector = -1;
if (sc.CheckNumber())
{
currentwall = sc.Number;
if (currentwall < 0 || currentwall >= MAXWALLS)
{
sc.ScriptMessage("Invalid wall number %d", currentwall);
currentwall = -1;
}
}
else currentwall = -1;
}
if (sc.Compare("sector"))
{
currentsprite = -1;
currentwall = -1;
if (sc.CheckNumber())
{
currentsector = sc.Number;
if (currentsector < 0 || currentsector >= MAXSECTORS)
{
sc.ScriptMessage("Invalid sector number %d", currentsector);
currentsector = -1;
}
}
else currentsector = -1;
}
else if (sc.Compare("blocks"))
{
if (sc.CheckNumber() && validateWall())
{
blockingpairs[currentwall].Push(sc.Number);
}
}
else if (sc.Compare("picnum"))
{
if (sc.CheckNumber())
{
if (currentwall != -1 && validateWall())
{
wall[currentwall].picnum = sc.Number;
}
else if (currentsprite != -1 && validateSprite())
{
sprite[currentsprite].picnum = sc.Number;
}
}
}
else if (sc.Compare("overpicnum"))
{
if (sc.CheckNumber() && validateWall())
{
wall[currentwall].overpicnum = sc.Number;
}
}
else if (sc.Compare("overpicnum"))
{
if (sc.CheckNumber() && validateWall())
{
wall[currentwall].overpicnum = sc.Number;
}
}
else if (sc.Compare("split"))
{
int start = -1, end = -1;
if (sc.CheckNumber()) start = sc.Number;
if (sc.CheckNumber()) end = sc.Number;
if (end >= 0 && validateSector())
{
hw_SetSplitSector(currentsector, start, end);
}
}
else if (sc.Compare("clearflags"))
{
if (currentsector != -1 && validateSector())
{
sc.GetString();
if (sc.Compare("floor") && sc.CheckNumber())
{
sector[currentsector].floorstat &= ~sc.Number;
}
else if (sc.Compare("ceiling") && sc.CheckNumber())
{
sector[currentsector].ceilingstat &= ~sc.Number;
}
else sc.ScriptError("Bad token %s", sc.String);
}
else if (sc.CheckNumber())
{
if (currentwall != -1 && validateWall())
{
wall[currentwall].cstat &= ~sc.Number;
}
else if (currentsprite != -1 && validateSprite())
{
sprite[currentsprite].cstat &= ~sc.Number;
}
}
}
else if (sc.Compare("setflags"))
{
if (sc.CheckNumber())
{
if (currentwall != -1 && validateWall())
{
wall[currentwall].cstat |= sc.Number;
}
else if (currentsprite != -1 && validateSprite())
{
sprite[currentsprite].cstat |= sc.Number;
}
}
}
else if (sc.Compare("lotag"))
{
if (sc.CheckNumber())
{
if (currentwall != -1 && validateWall())
{
wall[currentwall].lotag = sc.Number;
}
else if (currentsprite != -1 && validateSprite())
{
sprite[currentsprite].lotag = sc.Number;
}
}
}
else if (sc.Compare("angleoff") || sc.Compare("angoff"))
{
if (sc.CheckNumber() && validateSprite())
@ -201,6 +360,13 @@ static int32_t LoadMapHack(const char *filename)
void G_LoadMapHack(const char* filename, const unsigned char* md4)
{
for (auto& p : blockingpairs) p.Clear();
FString internal = "engine/compatibility/";
for (int j = 0; j < 16; ++j)
{
internal.AppendFormat("%02x", md4[j]);
}
LoadMapHack(internal + ".mhk");
FString hack = StripExtension(filename) + ".mhk";
if (LoadMapHack(hack))
@ -215,3 +381,31 @@ void G_LoadMapHack(const char* filename, const unsigned char* md4)
}
}
// Map hacks use MD4 instead of MD5. Oh, well...
CCMD(md4sum)
{
if (argv.argc() < 2)
{
Printf("Usage: md4sum <file> ...\n");
}
for (int i = 1; i < argv.argc(); ++i)
{
FileReader fr = fileSystem.OpenFileReader(argv[i]);
if (!fr.isOpen())
{
Printf("%s: %s\n", argv[i], strerror(errno));
}
else
{
auto data = fr.Read();
uint8_t digest[16];
md4once(data.Data(), data.Size(), digest);
for (int j = 0; j < 16; ++j)
{
Printf("%02x", digest[j]);
}
Printf(" //*%s\n", argv[i]);
}
}
}

View file

@ -41,43 +41,49 @@
#include "raze_sound.h"
FString gSkillNames[MAXSKILLS];
FString gVolumeNames[MAXVOLUMES];
FString gVolumeSubtitles[MAXVOLUMES];
int32_t gVolumeFlags[MAXVOLUMES];
int gDefaultVolume = 0, gDefaultSkill = 1;
MapRecord mapList[512]; // Due to how this gets used it needs to be static. EDuke defines 7 episode plus one spare episode with 64 potential levels each and relies on the static array which is freely accessible by scripts.
MapRecord *currentLevel; // level that is currently played. (The real level, not what script hacks modfifying the current level index can pretend.)
GlobalCutscenes globalCutscenes;
TArray<ClusterDef> clusters;
TArray<VolumeRecord> volumes;
TArray<TPointer<MapRecord>> mapList; // must be allocated as pointers because it can whack the currentlLevel pointer if this was a flat array.
MapRecord *currentLevel; // level that is currently played.
MapRecord* lastLevel; // Same here, for the last level.
unsigned int numUsedSlots;
CCMD(listmaps)
{
for (unsigned int i = 0; i < numUsedSlots; i++)
for (auto& map : mapList)
{
int lump = fileSystem.FindFile(mapList[i].fileName);
int lump = fileSystem.FindFile(map->fileName);
if (lump >= 0)
{
int rfnum = fileSystem.GetFileContainer(lump);
Printf("%s - %s (%s)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName(), fileSystem.GetResourceFileName(rfnum));
Printf("%s - %s (%s)\n", map->LabelName(), map->DisplayName(), fileSystem.GetResourceFileName(rfnum));
}
else
{
Printf("%s - %s (defined but does not exist)\n", mapList[i].fileName.GetChars(), mapList[i].DisplayName());
Printf("%s - %s (defined but does not exist)\n", map->fileName.GetChars(), map->DisplayName());
}
}
}
int CutsceneDef::GetSound()
{
int id;
if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName);
if (id <= 0) id = soundEngine->FindSoundByResID(soundID);
return id;
}
MapRecord *FindMapByName(const char *nm)
{
for (unsigned i = 0; i < numUsedSlots; i++)
for (auto& map : mapList)
{
auto &map = mapList[i];
if (map.labelName.CompareNoCase(nm) == 0)
if (map->labelName.CompareNoCase(nm) == 0)
{
return &map;
return map.Data();
}
}
return nullptr;
@ -86,23 +92,79 @@ MapRecord *FindMapByName(const char *nm)
MapRecord *FindMapByLevelNum(int num)
{
for (unsigned i = 0; i < numUsedSlots; i++)
for (auto& map : mapList)
{
auto &map = mapList[i];
if (map.levelNumber == num)
if (map->levelNumber == num)
{
return &map;
return map.Data();
}
}
return nullptr;
}
MapRecord *FindNextMap(MapRecord *thismap)
VolumeRecord* FindVolume(int index)
{
if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel);
return FindMapByLevelNum(thismap->levelNumber+1);
for (auto& vol : volumes)
{
if (vol.index == index) return &vol;
}
return nullptr;
}
ClusterDef* FindCluster(int index)
{
for (auto& vol : clusters)
{
if (vol.index == index) return &vol;
}
return nullptr;
}
ClusterDef* AllocateCluster()
{
return &clusters[clusters.Reserve(1)];
}
VolumeRecord* AllocateVolume()
{
return &volumes[volumes.Reserve(1)];
}
MapRecord* FindMapByIndexOnly(int cluster, int num)
{
int levelnum = makelevelnum(cluster, num);
for (auto& map : mapList)
{
if (map->levelNumber == levelnum) return map.Data();
}
return nullptr;
}
MapRecord* FindMapByIndex(int cluster, int num)
{
auto map = FindMapByLevelNum(num);
if (!map && num < 1000) map = FindMapByLevelNum(makelevelnum(cluster, num));
return map;
}
MapRecord* FindNextMap(MapRecord* thismap)
{
MapRecord* next = nullptr;
if (!thismap->NextMap.Compare("-")) return nullptr; // '-' means to forcibly end the game here.
if (thismap->NextMap.IsNotEmpty()) next = FindMapByName(thismap->NextMap);
if (!next) next = FindMapByLevelNum(thismap->levelNumber + 1);
return next;
}
MapRecord* FindNextSecretMap(MapRecord* thismap)
{
MapRecord* next = nullptr;
if (!thismap->NextSecret.Compare("-")) return nullptr; // '-' means to forcibly end the game here.
if (thismap->NextSecret.IsNotEmpty()) next = FindMapByName(thismap->NextSecret);
return next? next : FindNextMap(thismap);
}
bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
{
static const char* specials[] = { "intro", "briefing", "loading" };
@ -129,7 +191,7 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
if (numMatches != 4 || toupper(b1) != 'E' || toupper(b2) != 'L')
return false;
index = FindMapByLevelNum(levelnum(ep - 1, lev - 1));
index = FindMapByIndexOnly(ep, lev);
}
if (index != nullptr)
@ -142,18 +204,19 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
MapRecord *AllocateMap()
{
return &mapList[numUsedSlots++];
auto&p = mapList[mapList.Reserve(1)];
p.Alloc();
return p.Data();
}
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic)
{
for (unsigned i = 0; i < numUsedSlots; i++)
for (auto& map : mapList)
{
auto &map = mapList[i];
if (map.fileName.CompareNoCase(boardfilename) == 0)
if (map->fileName.CompareNoCase(boardfilename) == 0)
{
return &map;
return map.Data();
}
}

View file

@ -3,28 +3,58 @@
#include "gstrings.h"
#include "cmdlib.h"
#include "quotemgr.h"
#include "palentry.h"
#include "vectors.h"
#ifdef GetMessage
#undef GetMessage // Windows strikes...
#endif
enum EMax
{
MAXSKILLS = 7,
MAXVOLUMES = 7,
MAXMENUGAMEPLAYENTRIES = 7,
};
enum EVolFlags
{
EF_HIDEFROMSP = 1,
VF_HIDEFROMSP = 1,
VF_OPTIONAL = 2,
VF_SHAREWARELOCK = 4, // show in shareware but lock access.
VF_NOSKILL = 8,
};
enum EMapFlags
{
LEVEL_NOINTERMISSION = 1,
LEVEL_SECRETEXITOVERRIDE = 2, // when given an explicit level number, override with secret exit in the map, mainly for compiling episodes out of single levels.
LEVEL_CLEARINVENTORY = 4,
LEVEL_CLEARWEAPONS = 8,
LEVEL_FORCENOEOG = 16, // RR E1L7 needs this to override its boss's death ending the game.
};
enum EMapGameFlags
{
LEVEL_RR_HULKSPAWN = 1,
LEVEL_RR_CLEARMOONSHINE = 2,
LEVEL_EX_COUNTDOWN = 4,
LEVEL_EX_TRAINING = 8,
LEVEL_EX_ALTSOUND = 16,
LEVEL_EX_MULTI = 32,
LEVEL_SW_SPAWNMINES = 64,
LEVEL_SW_BOSSMETER_SERPENT = 128,
LEVEL_SW_BOSSMETER_SUMO = 256,
LEVEL_SW_BOSSMETER_ZILLA = 512,
LEVEL_SW_DEATHEXIT_SERPENT = 1024,
LEVEL_SW_DEATHEXIT_SUMO = 2048,
LEVEL_SW_DEATHEXIT_ZILLA = 4096,
};
// These get filled in by the map definition parsers of the front ends.
extern FString gSkillNames[MAXSKILLS];
extern FString gVolumeNames[MAXVOLUMES];
extern FString gVolumeSubtitles[MAXVOLUMES];
extern int32_t gVolumeFlags[MAXVOLUMES];
extern int gDefaultVolume, gDefaultSkill;
@ -46,6 +76,57 @@ enum {
MAX_MESSAGES = 32
};
class DObject;
struct MapRecord;
struct CutsceneDef
{
FString video;
FString function;
FString soundName;
int soundID = -1; // ResID not SoundID!
int framespersec = 0; // only relevant for ANM.
bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game.
void Create(DObject* runner);
bool Create(DObject* runner, MapRecord* map, bool transition);
bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); }
int GetSound();
};
struct GlobalCutscenes
{
CutsceneDef Intro;
CutsceneDef DefaultMapIntro;
CutsceneDef DefaultMapOutro;
CutsceneDef DefaultGameover;
CutsceneDef SharewareEnd;
CutsceneDef LoadingScreen;
FString MPSummaryScreen;
FString SummaryScreen;
};
struct ClusterDef
{
FString name; // What gets displayed for this cluster. In Duke this is normally the corresponding volume name but does not have to be.
CutsceneDef intro; // plays when entering this cluster
CutsceneDef outro; // plays when leaving this cluster
CutsceneDef gameover; // when defined, plays when the player dies in this cluster
FString InterBackground;
int index = -1;
int flags = 0; // engine and common flags
};
struct VolumeRecord // episodes
{
FString startmap;
FString name;
FString subtitle;
int index = -1;
int flags = 0;
int shortcut = 0;
};
struct MapRecord
{
int parTime = 0;
@ -54,16 +135,38 @@ struct MapRecord
FString labelName;
FString name;
FString music;
FString Author;
FString NextMap;
FString NextSecret;
int cdSongId = -1;
int musicorder = -1;
CutsceneDef intro;
CutsceneDef outro;
int flags = 0;
int gameflags = 0;
int levelNumber = -1;
int cluster = -1;
PalEntry fadeto = 0;
int fogdensity = 0;
int skyfog = 0;
FString BorderTexture;
FString InterBackground;
TArray<FString> PrecacheTextures;
FVector4 skyrotatevector;
// The rest is only used by Blood
int nextLevel = -1;
int nextSecret = -1;
FString messages[MAX_MESSAGES];
FString author;
int8_t fog = -1, weather = -1; // Blood defines these but they aren't used.
// game specific stuff
int rr_startsound = 0;
int rr_mamaspawn = 15;
int ex_ramses_horiz = 11;
int ex_ramses_cdtrack = -1; // this is not music, it is the actual dialogue!
FString ex_ramses_pup;
FString ex_ramses_text;
const char* LabelName() const
{
@ -97,39 +200,65 @@ struct MapRecord
{
messages[num] = msg;
}
};
struct SummaryInfo
{
int kills;
int maxkills;
int secrets;
int maxsecrets;
int supersecrets;
int time;
int playercount;
bool cheated;
bool endofgame;
};
extern MapRecord mapList[512];
extern GlobalCutscenes globalCutscenes;
extern MapRecord *currentLevel;
bool SetMusicForMap(const char* mapname, const char* music, bool namehack = false);
MapRecord *FindMapByName(const char *nm);
MapRecord *FindMapByLevelNum(int num);
MapRecord* FindMapByIndexOnly(int clst, int num); // this is for map setup where fallbacks are undesirable.
MapRecord* FindMapByIndex(int clst, int num);
MapRecord *FindNextMap(MapRecord *thismap);
MapRecord* FindNextSecretMap(MapRecord* thismap);
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr);
MapRecord* AllocateMap();
VolumeRecord* FindVolume(int index);
ClusterDef* FindCluster(int index);
ClusterDef* AllocateCluster();
VolumeRecord* AllocateVolume();
void SetLevelNum(MapRecord* info, int num);
inline VolumeRecord* MustFindVolume(int index)
{
auto r = FindVolume(index);
if (r) return r;
r = AllocateVolume();
r->index = index;
return r;
}
inline ClusterDef* MustFindCluster(int index)
{
auto r = FindCluster(index);
if (r) return r;
r = AllocateCluster();
r->index = index;
return r;
}
// These should be the only places converting between level numbers and volume/map pairs
constexpr inline int levelnum(int vol, int map)
constexpr inline int makelevelnum(int vol, int map)
{
return vol * 1000 + map;
}
constexpr inline int volfromlevelnum(int num)
{
return num >= 0 ? num / 1000 : 0;
}
constexpr inline int mapfromlevelnum(int num)
{
return num >= 0 ? num % 1000 : -1;
}
enum
{
RRENDSLOT = 127

View file

@ -41,7 +41,10 @@
#include "inputstate.h"
#include "md4.h"
#include "gamecontrol.h"
#include "gamefuncs.h"
#include "sectorgeometry.h"
#include "render.h"
#include "hw_sections.h"
static void ReadSectorV7(FileReader& fr, sectortype& sect)
{
@ -368,10 +371,14 @@ static void insertAllSprites(const char* filename, const vec3_t* pos, int16_t* c
assert(realnumsprites == Numsprites);
}
void addBlockingPairs();
void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang, int16_t* cursectnum)
{
inputState.ClearAllInput();
memset(sector, 0, sizeof(*sector) * MAXSECTORS);
memset(wall, 0, sizeof(*wall) * MAXWALLS);
memset(sprite, 0, sizeof(*sector) * MAXSPRITES);
FileReader fr = fileSystem.OpenFileReader(filename);
if (!fr.isOpen()) I_Error("Unable to open map %s", filename);
@ -385,7 +392,7 @@ void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang,
memset(spritesmooth, 0, sizeof(spritesmooth_t) * (MAXSPRITES + MAXUNIQHUDID));
initspritelists();
ClearAutomap();
Polymost_prepare_loadboard();
Polymost::Polymost_prepare_loadboard();
pos->x = fr.ReadInt32();
pos->y = fr.ReadInt32();
@ -444,6 +451,10 @@ void engineLoadBoard(const char* filename, int flags, vec3_t* pos, int16_t* ang,
unsigned char md4[16];
md4once(buffer.Data(), buffer.Size(), md4);
G_LoadMapHack(filename, md4);
setWallSectors();
hw_BuildSections();
sectorGeometry.SetSize(numsections);
memcpy(wallbackup, wall, sizeof(wallbackup));
memcpy(sectorbackup, sector, sizeof(sectorbackup));
@ -468,4 +479,18 @@ void loadMapBackup(const char* filename)
engineLoadBoard(filename, 0, &pos, &scratch, &scratch);
initspritelists();
}
}
// Sets the sector reference for each wall. We need this for the triangulation cache.
void setWallSectors()
{
for (int i = 0; i < numsectors; i++)
{
sector[i].dirty = 255;
sector[i].exflags = 0;
for (int w = 0; w < sector[i].wallnum; w++)
{
wall[sector[i].wallptr + w].sector = i;
}
}
}

View file

@ -64,6 +64,7 @@
#include "i_net.h"
#include "savegamehelp.h"
#include "gi.h"
#include "raze_music.h"
EXTERN_CVAR(Int, cl_gfxlocalization)
EXTERN_CVAR(Bool, m_quickexit)
@ -83,8 +84,31 @@ bool help_disabled;
FNewGameStartup NewGameStartupInfo;
//FNewGameStartup NewGameStartupInfo;
static bool DoStartGame(FNewGameStartup& gs)
{
auto vol = FindVolume(gs.Episode);
if (!vol) return false;
if (isShareware() && (vol->flags & VF_SHAREWARELOCK))
{
M_StartMessage(GStrings("SHAREWARELOCK"), 1, NAME_None);
return false;
}
auto map = FindMapByName(vol->startmap);
if (!map) return false;
soundEngine->StopAllChannels();
gi->StartGame(gs); // play game specific effects (like Duke/RR/SW's voice lines when starting a game.)
DeferedStartGame(map, gs.Skill);
return true;
}
bool M_SetSpecialMenu(FName& menu, int param)
{
@ -115,13 +139,19 @@ bool M_SetSpecialMenu(FName& menu, int param)
case NAME_Startgame:
case NAME_StartgameNoSkill:
menu = NAME_Startgame;
NewGameStartupInfo.Skill = param;
if (menu == NAME_StartgameNoSkill) NewGameStartupInfo.Episode = param;
if (gi->StartGame(NewGameStartupInfo))
if (menu == NAME_StartgameNoSkill)
{
menu = NAME_Startgame;
NewGameStartupInfo.Episode = param;
NewGameStartupInfo.Skill = 1;
}
if (DoStartGame(NewGameStartupInfo))
{
M_ClearMenus();
STAT_StartNewGame(gVolumeNames[NewGameStartupInfo.Episode], NewGameStartupInfo.Skill);
int ep = NewGameStartupInfo.Episode;
auto vol = FindVolume(ep);
if (vol) STAT_StartNewGame(vol->name, NewGameStartupInfo.Skill);
inputState.ClearAllInput();
}
return false;
@ -243,7 +273,8 @@ CCMD(menu_endgame)
{
STAT_Cancel();
M_ClearMenus();
gi->QuitToTitle();
Mus_Stop();
gameaction = ga_mainmenu;
});
M_ActivateMenu(newmenu);
@ -365,6 +396,7 @@ static DMenuItemBase* CreateCustomListMenuItemText(double x, double y, int heigh
// Creates the episode menu
//
//=============================================================================
extern TArray<VolumeRecord> volumes;
static void BuildEpisodeMenu()
{
@ -385,22 +417,22 @@ static void BuildEpisodeMenu()
ld->mSelectedItem = gDefaultVolume + ld->mItems.Size(); // account for pre-added items
int y = ld->mYpos;
for (int i = 0; i < MAXVOLUMES; i++)
// Volume definitions should be sorted by intended menu order.
for (auto &vol : volumes)
{
if (gVolumeNames[i].IsNotEmpty() && !(gVolumeFlags[i] & EF_HIDEFROMSP))
if (vol.name.IsNotEmpty() && !(vol.flags & VF_HIDEFROMSP))
{
int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && i > 0);
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, gVolumeNames[i][0],
gVolumeNames[i], ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, i); // font colors are not used, so hijack one for the shareware flag.
int isShareware = ((g_gameType & GAMEFLAG_DUKE) && (g_gameType & GAMEFLAG_SHAREWARE) && (vol.flags & VF_SHAREWARELOCK));
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing, vol.name[0],
vol.name, ld->mFont, CR_UNTRANSLATED, isShareware, NAME_Skillmenu, vol.index); // font colors are not used, so hijack one for the shareware flag.
y += ld->mLinespacing;
ld->mItems.Push(it);
addedVolumes++;
if (gVolumeSubtitles[i].IsNotEmpty())
if (vol.subtitle.IsNotEmpty())
{
auto it = CreateCustomListMenuItemText(ld->mXpos, y, ld->mLinespacing * 6 / 10, 1,
gVolumeSubtitles[i], SmallFont, CR_GRAY, false, NAME_None, i);
vol.subtitle, SmallFont, CR_GRAY, false, NAME_None, vol.index);
y += ld->mLinespacing * 6 / 10;
ld->mItems.Push(it);
textadded = true;
@ -731,3 +763,29 @@ DEFINE_ACTION_FUNCTION(_PlayerMenu, DrawPlayerSprite)
return 0;
}
#ifdef _WIN32
EXTERN_CVAR(Bool, vr_enable_quadbuffered)
#endif
void UpdateVRModes(bool considerQuadBuffered)
{
FOptionValues** pVRModes = OptionValues.CheckKey("VRMode");
if (pVRModes == nullptr) return;
TArray<FOptionValues::Pair>& vals = (*pVRModes)->mValues;
TArray<FOptionValues::Pair> filteredValues;
int cnt = vals.Size();
for (int i = 0; i < cnt; ++i) {
auto const& mode = vals[i];
if (mode.Value == 7) { // Quad-buffered stereo
#ifdef _WIN32
if (!vr_enable_quadbuffered) continue;
#else
continue; // Remove quad-buffered option on Mac and Linux
#endif
if (!considerQuadBuffered) continue; // Probably no compatible screen mode was found
}
filteredValues.Push(mode);
}
vals = filteredValues;
}

View file

@ -49,15 +49,25 @@
#include <vpx/vpx_decoder.h>
#include <vpx/vp8dx.h>
#include "raze_music.h"
#include "vm.h"
class MoviePlayer
{
protected:
enum EMovieFlags
{
NOSOUNDCUTOFF = 1,
FIXEDVIEWPORT = 2, // Forces fixed 640x480 screen size like for Blood's intros.
};
int flags;
public:
virtual void Start() {}
virtual bool Frame(uint64_t clock) = 0;
virtual void Stop() {}
virtual ~MoviePlayer() = default;
virtual FTextureID GetTexture() = 0;
};
//---------------------------------------------------------------------------
@ -76,16 +86,17 @@ class AnmPlayer : public MoviePlayer
int frametime = 0;
int nextframetime = 0;
AnimTextures animtex;
const AnimSound* animSnd;
const int* frameTicks;
bool nostopsound;
const TArray<int> animSnd;
int frameTicks[3];
public:
bool isvalid() { return numframes > 0; }
AnmPlayer(FileReader& fr, const AnimSound* ans, const int *frameticks, bool nosoundcutoff)
: animSnd(ans), frameTicks(frameticks), nostopsound(nosoundcutoff)
AnmPlayer(FileReader& fr, TArray<int>& ans, const int *frameticks, int flags_)
: animSnd(std::move(ans))
{
memcpy(frameTicks, frameticks, 3 * sizeof(int));
flags = flags_;
buffer = fr.ReadPadded(1);
fr.Close();
@ -109,17 +120,12 @@ public:
if (currentclock < nextframetime - 1)
{
twod->ClearScreen();
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
return true;
}
animtex.SetFrame(ANIM_GetPalette(&anim), ANIM_DrawFrame(&anim, curframe));
frametime = currentclock;
twod->ClearScreen();
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Masked, false, TAG_DONE);
int delay = 20;
if (frameTicks)
{
@ -129,11 +135,12 @@ public:
}
nextframetime += delay;
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
bool nostopsound = (flags & NOSOUNDCUTOFF);
for (unsigned i = 0; i < animSnd.Size(); i+=2)
{
if (animSnd[i].framenum == curframe)
if (animSnd[i] == curframe)
{
int sound = animSnd[i].soundnum;
int sound = animSnd[i+1];
if (sound == -1)
soundEngine->StopAllChannels();
else if (SoundEnabled())
@ -147,6 +154,7 @@ public:
void Stop() override
{
bool nostopsound = (flags & NOSOUNDCUTOFF);
if (!nostopsound) soundEngine->StopAllChannels();
}
@ -156,6 +164,11 @@ public:
buffer.Reset();
animtex.Clean();
}
FTextureID GetTexture() override
{
return animtex.GetFrameID();
}
};
//---------------------------------------------------------------------------
@ -187,8 +200,6 @@ public:
{
if (failed) return false;
bool playon = decoder.RunFrame(clock);
twod->ClearScreen();
DrawTexture(twod, decoder.animTex().GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
return playon;
}
@ -196,6 +207,11 @@ public:
{
decoder.Close();
}
FTextureID GetTexture() override
{
return decoder.animTex().GetFrameID();
}
};
//---------------------------------------------------------------------------
@ -209,7 +225,7 @@ class VpxPlayer : public MoviePlayer
bool failed = false;
FileReader fr;
AnimTextures animtex;
const AnimSound* animSnd;
const TArray<int> animSnd;
unsigned width, height;
TArray<uint8_t> Pic;
@ -234,10 +250,10 @@ public:
public:
bool isvalid() { return !failed; }
VpxPlayer(FileReader& fr_, const AnimSound* animSnd_, int origframedelay, FString& error)
VpxPlayer(FileReader& fr_, TArray<int>& animSnd_, int flags_, int origframedelay, FString& error) : animSnd(std::move(animSnd_))
{
fr = std::move(fr_);
animSnd = animSnd_;
flags = flags_;
if (!ReadIVFHeader(origframedelay))
{
@ -433,30 +449,35 @@ public:
framenum++;
if (framenum >= numframes) stop = true;
bool nostopsound = (flags & NOSOUNDCUTOFF);
int soundframe = convdenom ? Scale(framenum, convnumer, convdenom) : framenum;
if (soundframe > lastsoundframe)
{
if (animSnd && soundtrack == -1) for (int i = 0; animSnd[i].framenum >= 0; i++)
if (soundtrack == -1)
{
if (animSnd[i].framenum == soundframe)
for (unsigned i = 0; i < animSnd.Size(); i += 2)
{
int sound = animSnd[i].soundnum;
if (sound == -1)
soundEngine->StopAllChannels();
else if (SoundEnabled())
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
if (animSnd[i] == soundframe)
{
int sound = animSnd[i + 1];
if (sound == -1)
soundEngine->StopAllChannels();
else if (SoundEnabled())
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE);
}
}
}
lastsoundframe = soundframe;
}
}
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit, TAG_DONE);
return !stop;
}
void Stop()
{
Mus_Stop();
bool nostopsound = (flags & NOSOUNDCUTOFF);
if (!nostopsound) soundEngine->StopAllChannels();
}
~VpxPlayer()
@ -464,6 +485,11 @@ public:
vpx_codec_destroy(&codec);
animtex.Clean();
}
FTextureID GetTexture() override
{
return animtex.GetFrameID();
}
};
//---------------------------------------------------------------------------
@ -498,9 +524,10 @@ class SmkPlayer : public MoviePlayer
bool fullscreenScale;
uint64_t nFrameNs;
int nFrame = 0;
const AnimSound* animSnd;
const TArray<int> animSnd;
FString filename;
SoundStream* stream = nullptr;
bool hassound = false;
public:
bool isvalid() { return hSMK.isValid; }
@ -534,22 +561,21 @@ public:
}
SmkPlayer(const char *fn, const AnimSound* ans, bool fixedviewport)
SmkPlayer(const char *fn, TArray<int>& ans, int flags_) : animSnd(std::move(ans))
{
hSMK = Smacker_Open(fn);
if (!hSMK.isValid)
{
return;
}
flags = flags_;
Smacker_GetFrameSize(hSMK, nWidth, nHeight);
pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight));
nFrameRate = Smacker_GetFrameRate(hSMK);
nFrameNs = 1'000'000'000 / nFrameRate;
nFrames = Smacker_GetNumFrames(hSMK);
Smacker_GetPalette(hSMK, palette);
fullscreenScale = (!fixedviewport || (nWidth <= 320 && nHeight <= 200) || nWidth >= 640 || nHeight >= 480);
bool hassound = false;
numAudioTracks = Smacker_GetNumAudioTracks(hSMK);
if (numAudioTracks)
{
@ -562,14 +588,12 @@ public:
auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data());
if (adata.inf.bitsPerSample == 8) copy8bitSamples(read);
else copy16bitSamples(read);
animSnd = nullptr;
hassound = true;
}
}
if (!hassound)
{
adata.inf = {};
animSnd = ans;
}
}
@ -612,27 +636,21 @@ public:
}
}
if (fullscreenScale)
{
DrawTexture(twod, animtex.GetFrame(), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE);
}
else
{
DrawTexture(twod, animtex.GetFrame(), 320, 240, DTA_VirtualWidth, 640, DTA_VirtualHeight, 480, DTA_CenterOffset, true, TAG_DONE);
}
if (frame > nFrame)
{
nFrame++;
Smacker_GetNextFrame(hSMK);
if (animSnd) for (int i = 0; animSnd[i].framenum >= 0; i++)
bool nostopsound = (flags & NOSOUNDCUTOFF);
if (!hassound) for (unsigned i = 0; i < animSnd.Size(); i += 2)
{
if (animSnd[i].framenum == nFrame)
if (animSnd[i] == nFrame)
{
int sound = animSnd[i].soundnum;
int sound = animSnd[i + 1];
if (sound == -1)
soundEngine->StopAllChannels();
else if (SoundEnabled())
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, CHANF_NONE, sound, 1.f, ATTN_NONE);
soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE);
}
}
}
@ -640,13 +658,24 @@ public:
return nFrame < nFrames;
}
void Stop() override
{
if (stream) S_StopCustomStream(stream);
bool nostopsound = (flags & NOSOUNDCUTOFF);
if (!nostopsound && !hassound) soundEngine->StopAllChannels();
}
~SmkPlayer()
{
Smacker_Close(hSMK);
if (stream) S_StopCustomStream(stream);
soundEngine->StopAllChannels();
animtex.Clean();
}
FTextureID GetTexture() override
{
return animtex.GetFrameID();
}
};
//---------------------------------------------------------------------------
@ -655,61 +684,11 @@ public:
//
//---------------------------------------------------------------------------
class DMoviePlayer : public DSkippableScreenJob
{
MoviePlayer* player;
bool started = false;
public:
DMoviePlayer(MoviePlayer* mp)
{
player = mp;
pausable = false;
}
void Draw(double smoothratio) override
{
if (!player)
{
state = stopped;
return;
}
if (!started)
{
started = true;
player->Start();
}
uint64_t clock = (ticks + smoothratio) * 1'000'000'000. / GameTicRate;
if (state == running && !player->Frame(clock))
{
state = finished;
}
}
void OnDestroy() override
{
if (player)
{
player->Stop();
delete player;
}
player = nullptr;
}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff, FString& error)
MoviePlayer* OpenMovie(const char* filename, TArray<int>& ans, const int* frameticks, int flags, FString& error)
{
FileReader fr;
// first try as .ivf - but only if sounds are provided - the decoder is video only.
if (ans)
if (ans.Size())
{
auto fn = StripExtension(filename);
DefaultExtension(fn, ".ivf");
@ -739,7 +718,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
if (!memcmp(id, "LPF ", 4))
{
auto anm = new AnmPlayer(fr, ans, frameticks, nosoundcutoff);
auto anm = new AnmPlayer(fr, ans, frameticks, flags);
if (!anm->isvalid())
{
error.Format("%s: invalid ANM file.\n", filename);
@ -751,7 +730,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
else if (!memcmp(id, "SMK2", 4))
{
fr.Close();
auto anm = new SmkPlayer(filename, ans, true); // Fixme: Handle Blood's video scaling behavior more intelligently.
auto anm = new SmkPlayer(filename, ans, flags);
if (!anm->isvalid())
{
error.Format("%s: invalid SMK file.\n", filename);
@ -772,7 +751,7 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
}
else if (!memcmp(id, "DKIF\0\0 \0VP80", 12))
{
auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, error);
auto anm = new VpxPlayer(fr, ans, frameticks ? frameticks[1] : 0, flags, error);
if (!anm->isvalid())
{
delete anm;
@ -795,20 +774,53 @@ MoviePlayer* OpenMovie(const char* filename, const AnimSound* ans, const int* fr
//
//---------------------------------------------------------------------------
DScreenJob* PlayVideo(const char* filename, const AnimSound* ans, const int* frameticks, bool nosoundcutoff)
DEFINE_ACTION_FUNCTION(_MoviePlayer, Create)
{
if (!filename)
{
return Create<DBlackScreen>(1);
}
PARAM_PROLOGUE;
PARAM_STRING(filename);
PARAM_POINTER(sndinf, TArray<int>);
PARAM_INT(flags);
PARAM_INT(frametime);
PARAM_INT(firstframetime);
PARAM_INT(lastframetime);
FString error;
auto movie = OpenMovie(filename, ans, frameticks, nosoundcutoff, error);
if (firstframetime == -1) firstframetime = frametime;
if (lastframetime == -1) lastframetime = frametime;
int frametimes[] = { firstframetime, frametime, lastframetime };
auto movie = OpenMovie(filename, *sndinf, frametime == -1? nullptr : frametimes, flags, error);
if (!movie)
{
Printf(TEXTCOLOR_YELLOW, "%s", error.GetChars());
return Create<DBlackScreen>(1);
}
return Create<DMoviePlayer>(movie);
ACTION_RETURN_POINTER(movie);
}
DEFINE_ACTION_FUNCTION(_MoviePlayer, Start)
{
PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer);
self->Start();
return 0;
}
DEFINE_ACTION_FUNCTION(_MoviePlayer, Frame)
{
PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer);
PARAM_FLOAT(clock);
ACTION_RETURN_INT(self->Frame(int64_t(clock)));
return 0;
}
DEFINE_ACTION_FUNCTION(_MoviePlayer, Destroy)
{
PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer);
self->Stop();
delete self;
return 0;
}
DEFINE_ACTION_FUNCTION(_MoviePlayer, GetTexture)
{
PARAM_SELF_STRUCT_PROLOGUE(MoviePlayer);
ACTION_RETURN_INT(self->GetTexture().GetIndex());
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,470 @@
/*
** nodebuild.cpp
**
**---------------------------------------------------------------------------
** Copyright 2002-2016 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.
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
** covered by the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or (at
** your option) any later version.
**
** 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.
**---------------------------------------------------------------------------
**
*/
#pragma once
#include "tarray.h"
#include "x86.h"
#include "build.h"
struct FPolySeg;
struct FMiniBSP;
struct FLevelLocals;
struct FEventInfo
{
int Vertex;
uint32_t FrontSeg;
};
struct FEvent
{
FEvent *Parent, *Left, *Right;
double Distance;
FEventInfo Info;
};
class FEventTree
{
public:
FEventTree ();
~FEventTree ();
FEvent *GetMinimum ();
FEvent *GetSuccessor (FEvent *event) const { FEvent *node = Successor(event); return node == &Nil ? NULL : node; }
FEvent *GetPredecessor (FEvent *event) const { FEvent *node = Predecessor(event); return node == &Nil ? NULL : node; }
FEvent *GetNewNode ();
void Insert (FEvent *event);
FEvent *FindEvent (double distance) const;
void DeleteAll ();
void PrintTree () const { PrintTree (Root); }
private:
FEvent Nil;
FEvent *Root;
FEvent *Spare;
void DeletionTraverser (FEvent *event);
FEvent *Successor (FEvent *event) const;
FEvent *Predecessor (FEvent *event) const;
void PrintTree (const FEvent *event) const;
};
struct FSimpleVert
{
fixed_t x, y;
};
typedef int64_t fixed64_t;
struct vertex_t
{
DVector2 p;
void set(fixed_t x, fixed_t y)
{
p.X = x / 65536.;
p.Y = y / 65536.;
}
double fX() const
{
return p.X;
}
double fY() const
{
return p.Y;
}
fixed_t fixX() const
{
return FLOAT2FIXED(p.X);
}
fixed_t fixY() const
{
return FLOAT2FIXED(p.Y);
}
};
struct side_t;
struct line_t
{
vertex_t* v1, * v2; // vertices, from v1 to v2
side_t* sidedef[2];
sectortype* frontsector, * backsector;
int linenum;
int Index() const { return linenum; }
};
struct side_t
{
sectortype* sector; // Sector the SideDef is facing.
int sidenum;
int Index() const { return sidenum; }
};
struct subsector_t;
struct seg_t
{
vertex_t* v1;
vertex_t* v2;
side_t* sidedef;
line_t* linedef;
// Sector references. Could be retrieved from linedef, too.
sectortype* frontsector;
sectortype* backsector; // NULL for one-sided lines
seg_t* PartnerSeg;
subsector_t* Subsector;
int segnum;
int Index() const { return segnum; }
};
struct glseg_t : public seg_t
{
uint32_t Partner;
};
struct subsector_t
{
sectortype* sector;
seg_t* firstline;
uint32_t numlines;
};
struct node_t
{
// Partition line.
fixed_t x;
fixed_t y;
fixed_t dx;
fixed_t dy;
union
{
float bbox[2][4]; // Bounding box for each child.
fixed_t nb_bbox[2][4]; // Used by nodebuilder.
};
float len;
int nodenum;
union
{
void* children[2]; // If bit 0 is set, it's a subsector.
int intchildren[2]; // Used by nodebuilder.
};
int Index() const { return nodenum; }
};
struct FLevelLocals
{
TArray<vertex_t> vertexes;
TArray<subsector_t> subsectors;
TArray<node_t> nodes;
TArray<seg_t> segs;
};
enum { NO_SIDE = 0x7fffffff };
class FNodeBuilder
{
struct FPrivSeg
{
int v1, v2;
int sidedef;
int linedef;
sectortype *frontsector;
sectortype *backsector;
uint32_t next;
uint32_t nextforvert;
uint32_t nextforvert2;
int loopnum; // loop number for split avoidance (0 means splitting is okay)
uint32_t partner; // seg on back side
uint32_t storedseg; // seg # in the GL_SEGS lump
int planenum;
bool planefront;
FPrivSeg *hashnext;
};
struct FPrivVert : FSimpleVert
{
uint32_t segs; // segs that use this vertex as v1
uint32_t segs2; // segs that use this vertex as v2
bool operator== (const FPrivVert &other)
{
return x == other.x && y == other.y;
}
};
struct FSimpleLine
{
fixed_t x, y, dx, dy;
};
union USegPtr
{
uint32_t SegNum;
FPrivSeg *SegPtr;
};
struct FSplitSharer
{
double Distance;
uint32_t Seg;
bool Forward;
};
// Like a blockmap, but for vertices instead of lines
class IVertexMap
{
public:
virtual ~IVertexMap();
virtual int SelectVertexExact(FPrivVert &vert) = 0;
virtual int SelectVertexClose(FPrivVert &vert) = 0;
private:
IVertexMap &operator=(const IVertexMap &);
};
class FVertexMap : public IVertexMap
{
public:
FVertexMap (FNodeBuilder &builder, fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy);
~FVertexMap ();
int SelectVertexExact (FPrivVert &vert);
int SelectVertexClose (FPrivVert &vert);
private:
FNodeBuilder &MyBuilder;
TArray<int> *VertexGrid;
fixed64_t MinX, MinY, MaxX, MaxY;
int BlocksWide, BlocksTall;
enum { BLOCK_SHIFT = 8 + FRACBITS };
enum { BLOCK_SIZE = 1 << BLOCK_SHIFT };
int InsertVertex (FPrivVert &vert);
inline int GetBlock (fixed64_t x, fixed64_t y)
{
assert (x >= MinX);
assert (y >= MinY);
assert (x <= MaxX);
assert (y <= MaxY);
return (unsigned(x - MinX) >> BLOCK_SHIFT) + (unsigned(y - MinY) >> BLOCK_SHIFT) * BlocksWide;
}
};
class FVertexMapSimple : public IVertexMap
{
public:
FVertexMapSimple(FNodeBuilder &builder);
int SelectVertexExact(FPrivVert &vert);
int SelectVertexClose(FPrivVert &vert);
private:
int InsertVertex(FPrivVert &vert);
FNodeBuilder &MyBuilder;
};
friend class FVertexMap;
friend class FVertexMapSimple;
public:
struct FLevel
{
vertex_t *Vertices; int NumVertices;
side_t *Sides; int NumSides;
line_t *Lines; int NumLines;
fixed_t MinX, MinY, MaxX, MaxY;
void FindMapBounds();
void ResetMapBounds()
{
MinX = FIXED_MAX;
MinY = FIXED_MAX;
MaxX = FIXED_MIN;
MaxY = FIXED_MIN;
}
};
struct FPolyStart
{
int polynum;
fixed_t x, y;
};
FNodeBuilder (FLevel &lev);
~FNodeBuilder ();
void Extract(FLevelLocals &lev);
const int *GetOldVertexTable();
// These are used for building sub-BSP trees for polyobjects.
void Clear();
void AddSegs(seg_t *segs, int numsegs);
void BuildMini(bool makeGLNodes);
static angle_t PointToAngle (fixed_t dx, fixed_t dy);
// < 0 : in front of line
// == 0 : on line
// > 0 : behind line
static inline int PointOnSide (int x, int y, int x1, int y1, int dx, int dy);
private:
IVertexMap *VertexMap;
int *OldVertexTable;
TArray<node_t> Nodes;
TArray<subsector_t> Subsectors;
TArray<uint32_t> SubsectorSets;
TArray<FPrivSeg> Segs;
TArray<FPrivVert> Vertices;
TArray<USegPtr> SegList;
TArray<uint8_t> PlaneChecked;
TArray<FSimpleLine> Planes;
TArray<int> Touched; // Loops a splitter touches on a vertex
TArray<int> Colinear; // Loops with edges colinear to a splitter
FEventTree Events; // Vertices intersected by the current splitter
TArray<FSplitSharer> SplitSharers; // Segs colinear with the current splitter
uint32_t HackSeg; // Seg to force to back of splitter
uint32_t HackMate; // Seg to use in front of hack seg
FLevel &Level;
bool GLNodes; // Add minisegs to make GL nodes?
// Progress meter stuff
int SegsStuffed;
void FindUsedVertices (vertex_t *vertices, int max);
void BuildTree ();
void MakeSegsFromSides ();
int CreateSeg (int linenum, int sidenum);
void GroupSegPlanes ();
void GroupSegPlanesSimple ();
void AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg);
int CreateNode (uint32_t set, unsigned int count, fixed_t bbox[4]);
int CreateSubsector (uint32_t set, fixed_t bbox[4]);
void CreateSubsectorsForReal ();
bool CheckSubsector (uint32_t set, node_t &node, uint32_t &splitseg);
bool CheckSubsectorOverlappingSegs (uint32_t set, node_t &node, uint32_t &splitseg);
bool ShoveSegBehind (uint32_t set, node_t &node, uint32_t seg, uint32_t mate); int SelectSplitter (uint32_t set, node_t &node, uint32_t &splitseg, int step, bool nosplit);
void SplitSegs (uint32_t set, node_t &node, uint32_t splitseg, uint32_t &outset0, uint32_t &outset1, unsigned int &count0, unsigned int &count1);
uint32_t SplitSeg (uint32_t segnum, int splitvert, int v1InFront);
int Heuristic (node_t &node, uint32_t set, bool honorNoSplit);
// Returns:
// 0 = seg is in front
// 1 = seg is in back
// -1 = seg cuts the node
int ClassifyLine (node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2]);
void FixSplitSharers (const node_t &node);
double AddIntersection (const node_t &node, int vertex);
void AddMinisegs (const node_t &node, uint32_t splitseg, uint32_t &fset, uint32_t &rset);
uint32_t CheckLoopStart (fixed_t dx, fixed_t dy, int vertex1, int vertex2);
uint32_t CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex2);
void RemoveSegFromVert1 (uint32_t segnum, int vertnum);
void RemoveSegFromVert2 (uint32_t segnum, int vertnum);
uint32_t AddMiniseg (int v1, int v2, uint32_t partner, uint32_t seg1, uint32_t splitseg);
void SetNodeFromSeg (node_t &node, const FPrivSeg *pseg) const;
int CloseSubsector (TArray<glseg_t> &segs, int subsector, vertex_t *outVerts);
uint32_t PushGLSeg (TArray<glseg_t> &segs, const FPrivSeg *seg, vertex_t *outVerts);
void PushConnectingGLSeg (int subsector, TArray<glseg_t> &segs, vertex_t *v1, vertex_t *v2);
int OutputDegenerateSubsector (TArray<glseg_t> &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts);
static int SortSegs (const void *a, const void *b);
double InterceptVector (const node_t &splitter, const FPrivSeg &seg);
void PrintSet (int l, uint32_t set);
FNodeBuilder &operator= (const FNodeBuilder &) { return *this; }
};
// Points within this distance of a line will be considered on the line.
// Units are in fixed_ts.
const double SIDE_EPSILON = 6.5536;
// Vertices within this distance of each other will be considered as the same vertex.
#define VERTEX_EPSILON 6 // This is a fixed_t value
inline int FNodeBuilder::PointOnSide (int x, int y, int x1, int y1, int dx, int dy)
{
// For most cases, a simple dot product is enough.
double d_dx = double(dx);
double d_dy = double(dy);
double d_x = double(x);
double d_y = double(y);
double d_x1 = double(x1);
double d_y1 = double(y1);
double s_num = (d_y1-d_y)*d_dx - (d_x1-d_x)*d_dy;
if (fabs(s_num) < 17179869184.f) // 4<<32
{
// Either the point is very near the line, or the segment defining
// the line is very short: Do a more expensive test to determine
// just how far from the line the point is.
double l = d_dx*d_dx + d_dy*d_dy; // double l = sqrt(d_dx*d_dx+d_dy*d_dy);
double dist = s_num * s_num / l; // double dist = fabs(s_num)/l;
if (dist < SIDE_EPSILON*SIDE_EPSILON) // if (dist < SIDE_EPSILON)
{
return 0;
}
}
return s_num > 0.0 ? -1 : 1;
}
using sector_t = sectortype;

View file

@ -0,0 +1,135 @@
#include "nodebuild.h"
#define FAR_ENOUGH 17179869184.f // 4<<32
int FNodeBuilder::ClassifyLine(node_t &node, const FPrivVert *v1, const FPrivVert *v2, int sidev[2])
{
double d_x1 = double(node.x);
double d_y1 = double(node.y);
double d_dx = double(node.dx);
double d_dy = double(node.dy);
double d_xv1 = double(v1->x);
double d_xv2 = double(v2->x);
double d_yv1 = double(v1->y);
double d_yv2 = double(v2->y);
double s_num1 = (d_y1 - d_yv1) * d_dx - (d_x1 - d_xv1) * d_dy;
double s_num2 = (d_y1 - d_yv2) * d_dx - (d_x1 - d_xv2) * d_dy;
int nears = 0;
if (s_num1 <= -FAR_ENOUGH)
{
if (s_num2 <= -FAR_ENOUGH)
{
sidev[0] = sidev[1] = 1;
return 1;
}
if (s_num2 >= FAR_ENOUGH)
{
sidev[0] = 1;
sidev[1] = -1;
return -1;
}
nears = 1;
}
else if (s_num1 >= FAR_ENOUGH)
{
if (s_num2 >= FAR_ENOUGH)
{
sidev[0] = sidev[1] = -1;
return 0;
}
if (s_num2 <= -FAR_ENOUGH)
{
sidev[0] = -1;
sidev[1] = 1;
return -1;
}
nears = 1;
}
else
{
nears = 2 | int(fabs(s_num2) < FAR_ENOUGH);
}
if (nears)
{
double l = 1.f / (d_dx*d_dx + d_dy*d_dy);
if (nears & 2)
{
double dist = s_num1 * s_num1 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev[0] = 0;
}
else
{
sidev[0] = s_num1 > 0.0 ? -1 : 1;
}
}
else
{
sidev[0] = s_num1 > 0.0 ? -1 : 1;
}
if (nears & 1)
{
double dist = s_num2 * s_num2 * l;
if (dist < SIDE_EPSILON*SIDE_EPSILON)
{
sidev[1] = 0;
}
else
{
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
}
else
{
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
}
else
{
sidev[0] = s_num1 > 0.0 ? -1 : 1;
sidev[1] = s_num2 > 0.0 ? -1 : 1;
}
if ((sidev[0] | sidev[1]) == 0)
{ // seg is coplanar with the splitter, so use its orientation to determine
// which child it ends up in. If it faces the same direction as the splitter,
// it goes in front. Otherwise, it goes in back.
if (node.dx != 0)
{
if ((node.dx > 0 && v2->x > v1->x) || (node.dx < 0 && v2->x < v1->x))
{
return 0;
}
else
{
return 1;
}
}
else
{
if ((node.dy > 0 && v2->y > v1->y) || (node.dy < 0 && v2->y < v1->y))
{
return 0;
}
else
{
return 1;
}
}
}
else if (sidev[0] <= 0 && sidev[1] <= 0)
{
return 0;
}
else if (sidev[0] >= 0 && sidev[1] >= 0)
{
return 1;
}
return -1;
}

View file

@ -0,0 +1,226 @@
/*
** nodebuild_events.cpp
**
** A red-black tree for keeping track of segs that get touched by a splitter.
**
**---------------------------------------------------------------------------
** Copyright 2002-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.
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
** covered by the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or (at
** your option) any later version.
**
** 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 <string.h>
#include "nodebuild.h"
#include "printf.h"
FEventTree::FEventTree ()
: Root (&Nil), Spare (NULL)
{
memset (&Nil, 0, sizeof(Nil));
}
FEventTree::~FEventTree ()
{
FEvent *probe;
DeleteAll ();
probe = Spare;
while (probe != NULL)
{
FEvent *next = probe->Left;
delete probe;
probe = next;
}
}
void FEventTree::DeleteAll ()
{
DeletionTraverser (Root);
Root = &Nil;
}
void FEventTree::DeletionTraverser (FEvent *node)
{
if (node != &Nil && node != NULL)
{
DeletionTraverser (node->Left);
DeletionTraverser (node->Right);
node->Left = Spare;
Spare = node;
}
}
FEvent *FEventTree::GetNewNode ()
{
FEvent *node;
if (Spare != NULL)
{
node = Spare;
Spare = node->Left;
}
else
{
node = new FEvent;
}
return node;
}
void FEventTree::Insert (FEvent *z)
{
FEvent *y = &Nil;
FEvent *x = Root;
while (x != &Nil)
{
y = x;
if (z->Distance < x->Distance)
{
x = x->Left;
}
else
{
x = x->Right;
}
}
z->Parent = y;
if (y == &Nil)
{
Root = z;
}
else if (z->Distance < y->Distance)
{
y->Left = z;
}
else
{
y->Right = z;
}
z->Left = &Nil;
z->Right = &Nil;
}
FEvent *FEventTree::Successor (FEvent *event) const
{
if (event->Right != &Nil)
{
event = event->Right;
while (event->Left != &Nil)
{
event = event->Left;
}
return event;
}
else
{
FEvent *y = event->Parent;
while (y != &Nil && event == y->Right)
{
event = y;
y = y->Parent;
}
return y;
}
}
FEvent *FEventTree::Predecessor (FEvent *event) const
{
if (event->Left != &Nil)
{
event = event->Left;
while (event->Right != &Nil)
{
event = event->Right;
}
return event;
}
else
{
FEvent *y = event->Parent;
while (y != &Nil && event == y->Left)
{
event = y;
y = y->Parent;
}
return y;
}
}
FEvent *FEventTree::FindEvent (double key) const
{
FEvent *node = Root;
while (node != &Nil)
{
if (node->Distance == key)
{
return node;
}
else if (node->Distance > key)
{
node = node->Left;
}
else
{
node = node->Right;
}
}
return NULL;
}
FEvent *FEventTree::GetMinimum ()
{
FEvent *node = Root;
if (node == &Nil)
{
return NULL;
}
while (node->Left != &Nil)
{
node = node->Left;
}
return node;
}
void FEventTree::PrintTree (const FEvent *event) const
{
// Use the CRT's sprintf so that it shares the same formatting as ZDBSP's output.
char buff[100];
if (event != &Nil)
{
PrintTree(event->Left);
sprintf(buff, " Distance %g, vertex %d, seg %u\n",
g_sqrt(event->Distance/4294967296.0), event->Info.Vertex, (unsigned)event->Info.FrontSeg);
Printf(PRINT_LOG, "%s", buff);
PrintTree(event->Right);
}
}

View file

@ -0,0 +1,440 @@
/*
** nodebuild_extract.cpp
**
** Converts the nodes, segs, and subsectors from the node builder's
** internal format to the format used by the rest of the game.
**
**---------------------------------------------------------------------------
** Copyright 2002-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.
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
** covered by the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or (at
** your option) any later version.
**
** 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 <string.h>
#include <float.h>
#include "nodebuild.h"
#if 0
#define D(x) x
#define DD 1
#else
#define D(x) do{}while(0)
#undef DD
#endif
void FNodeBuilder::Extract (FLevelLocals &theLevel)
{
int i;
auto &outVerts = theLevel.vertexes;
int vertCount = Vertices.Size ();
outVerts.Alloc(vertCount);
for (i = 0; i < vertCount; ++i)
{
outVerts[i].set(Vertices[i].x, Vertices[i].y);
}
auto &outSubs = theLevel.subsectors;
auto subCount = Subsectors.Size();
outSubs.Alloc(subCount);
memset(&outSubs[0], 0, subCount * sizeof(subsector_t));
auto &outNodes = theLevel.nodes;
auto nodeCount = Nodes.Size ();
outNodes.Alloc(nodeCount);
memcpy (&outNodes[0], &Nodes[0], nodeCount*sizeof(node_t));
for (unsigned i = 0; i < nodeCount; ++i)
{
D(Printf(PRINT_LOG, "Node %d: Splitter[%08x,%08x] [%08x,%08x]\n", i,
outNodes[i].x, outNodes[i].y, outNodes[i].dx, outNodes[i].dy));
// Go backwards because on 64-bit systems, both of the intchildren are
// inside the first in-game child.
for (int j = 1; j >= 0; --j)
{
if (outNodes[i].intchildren[j] & 0x80000000)
{
D(Printf(PRINT_LOG, " subsector %d\n", outNodes[i].intchildren[j] & 0x7FFFFFFF));
outNodes[i].children[j] = (uint8_t *)(&outSubs[(outNodes[i].intchildren[j] & 0x7fffffff)]) + 1;
}
else
{
D(Printf(PRINT_LOG, " node %d\n", outNodes[i].intchildren[j]));
outNodes[i].children[j] = &outNodes[outNodes[i].intchildren[j]];
}
}
for (int j = 0; j < 2; ++j)
{
for (int k = 0; k < 4; ++k)
{
outNodes[i].bbox[j][k] = FIXED2FLOAT(outNodes[i].nb_bbox[j][k]);
}
}
}
auto &outSegs = theLevel.segs;
if (GLNodes)
{
TArray<glseg_t> segs (Segs.Size()*5/4);
for (unsigned i = 0; i < subCount; ++i)
{
uint32_t numsegs = CloseSubsector (segs, i, &outVerts[0]);
outSubs[i].numlines = numsegs;
outSubs[i].firstline = (seg_t *)(size_t)(segs.Size() - numsegs);
}
auto segCount = segs.Size ();
outSegs.Alloc(segCount);
for (unsigned i = 0; i < segCount; ++i)
{
outSegs[i] = *(seg_t *)&segs[i];
if (segs[i].Partner != UINT_MAX)
{
const uint32_t storedseg = Segs[segs[i].Partner].storedseg;
outSegs[i].PartnerSeg = UINT_MAX == storedseg ? nullptr : &outSegs[storedseg];
}
else
{
outSegs[i].PartnerSeg = nullptr;
}
}
}
else
{
memcpy (&outSubs[0], &Subsectors[0], subCount*sizeof(subsector_t));
auto segCount = Segs.Size ();
outSegs.Alloc(segCount);
for (unsigned i = 0; i < segCount; ++i)
{
const FPrivSeg *org = &Segs[SegList[i].SegNum];
seg_t *out = &outSegs[i];
D(Printf(PRINT_LOG, "Seg %d: v1(%d) -> v2(%d)\n", i, org->v1, org->v2));
out->v1 = &outVerts[org->v1];
out->v2 = &outVerts[org->v2];
out->backsector = org->backsector;
out->frontsector = org->frontsector;
out->linedef = Level.Lines + org->linedef;
out->sidedef = Level.Sides + org->sidedef;
out->PartnerSeg = nullptr;
}
}
for (unsigned i = 0; i < subCount; ++i)
{
outSubs[i].firstline = &outSegs[(size_t)outSubs[i].firstline];
}
D(Printf("%i segs, %i nodes, %i subsectors\n", segCount, nodeCount, subCount));
for (i = 0; i < Level.NumLines; ++i)
{
Level.Lines[i].v1 = &outVerts[(size_t)Level.Lines[i].v1];
Level.Lines[i].v2 = &outVerts[(size_t)Level.Lines[i].v2];
}
}
int FNodeBuilder::CloseSubsector (TArray<glseg_t> &segs, int subsector, vertex_t *outVerts)
{
FPrivSeg *seg, *prev;
angle_t prevAngle;
double accumx, accumy;
fixed_t midx, midy;
int firstVert;
uint32_t first, max, count, i, j;
bool diffplanes;
int firstplane;
first = (uint32_t)(size_t)Subsectors[subsector].firstline;
max = first + Subsectors[subsector].numlines;
count = 0;
accumx = accumy = 0.0;
diffplanes = false;
firstplane = Segs[SegList[first].SegNum].planenum;
// Calculate the midpoint of the subsector and also check for degenerate subsectors.
// A subsector is degenerate if it exists in only one dimension, which can be
// detected when all the segs lie in the same plane. This can happen if you have
// outward-facing lines in the void that don't point toward any sector. (Some of the
// polyobjects in Hexen are constructed like this.)
for (i = first; i < max; ++i)
{
seg = &Segs[SegList[i].SegNum];
accumx += double(Vertices[seg->v1].x) + double(Vertices[seg->v2].x);
accumy += double(Vertices[seg->v1].y) + double(Vertices[seg->v2].y);
if (firstplane != seg->planenum)
{
diffplanes = true;
}
}
midx = fixed_t(accumx / (max - first) / 2);
midy = fixed_t(accumy / (max - first) / 2);
seg = &Segs[SegList[first].SegNum];
prevAngle = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
seg->storedseg = PushGLSeg (segs, seg, outVerts);
count = 1;
prev = seg;
firstVert = seg->v1;
#ifdef DD
Printf(PRINT_LOG, "--%d--\n", subsector);
for (j = first; j < max; ++j)
{
seg = &Segs[SegList[j].SegNum];
angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
Printf(PRINT_LOG, "%d%c %5d(%5d,%5d)->%5d(%5d,%5d) - %3.5f %d,%d [%08x,%08x]-[%08x,%08x]\n", j,
seg->linedef == -1 ? '+' : ':',
seg->v1, Vertices[seg->v1].x>>16, Vertices[seg->v1].y>>16,
seg->v2, Vertices[seg->v2].x>>16, Vertices[seg->v2].y>>16,
double(ang/2)*180/(1<<30),
seg->planenum, seg->planefront,
Vertices[seg->v1].x, Vertices[seg->v1].y,
Vertices[seg->v2].x, Vertices[seg->v2].y);
}
#endif
if (diffplanes)
{ // A well-behaved subsector. Output the segs sorted by the angle formed by connecting
// the subsector's center to their first vertex.
D(Printf(PRINT_LOG, "Well behaved subsector\n"));
for (i = first + 1; i < max; ++i)
{
angle_t bestdiff = ANGLE_MAX;
FPrivSeg *bestseg = NULL;
uint32_t bestj = UINT_MAX;
j = first;
do
{
seg = &Segs[SegList[j].SegNum];
angle_t ang = PointToAngle (Vertices[seg->v1].x - midx, Vertices[seg->v1].y - midy);
angle_t diff = prevAngle - ang;
if (seg->v1 == prev->v2)
{
bestdiff = diff;
bestseg = seg;
bestj = j;
break;
}
if (diff < bestdiff && diff > 0)
{
bestdiff = diff;
bestseg = seg;
bestj = j;
}
}
while (++j < max);
// Is a NULL bestseg actually okay?
if (bestseg != NULL)
{
seg = bestseg;
}
if (prev->v2 != seg->v1)
{
// Add a new miniseg to connect the two segs
PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[seg->v1]);
count++;
}
#ifdef DD
Printf(PRINT_LOG, "+%d\n", bestj);
#endif
prevAngle -= bestdiff;
seg->storedseg = PushGLSeg (segs, seg, outVerts);
count++;
prev = seg;
if (seg->v2 == firstVert)
{
prev = seg;
break;
}
}
#ifdef DD
Printf(PRINT_LOG, "\n");
#endif
}
else
{ // A degenerate subsector. These are handled in three stages:
// Stage 1. Proceed in the same direction as the start seg until we
// hit the seg furthest from it.
// Stage 2. Reverse direction and proceed until we hit the seg
// furthest from the start seg.
// Stage 3. Reverse direction again and insert segs until we get
// to the start seg.
// A dot product serves to determine distance from the start seg.
D(Printf(PRINT_LOG, "degenerate subsector\n"));
// Stage 1. Go forward.
count += OutputDegenerateSubsector (segs, subsector, true, 0, prev, outVerts);
// Stage 2. Go backward.
count += OutputDegenerateSubsector (segs, subsector, false, DBL_MAX, prev, outVerts);
// Stage 3. Go forward again.
count += OutputDegenerateSubsector (segs, subsector, true, -DBL_MAX, prev, outVerts);
}
if (prev->v2 != firstVert)
{
PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[firstVert]);
count++;
}
#ifdef DD
Printf(PRINT_LOG, "Output GL subsector %d:\n", subsector);
for (i = segs.Size() - count; i < (int)segs.Size(); ++i)
{
Printf(PRINT_LOG, " Seg %5d%c(%5d,%5d)-(%5d,%5d) [%08x,%08x]-[%08x,%08x]\n", i,
segs[i].linedef == NULL ? '+' : ' ',
segs[i].v1->fixX()>>16,
segs[i].v1->fixY()>>16,
segs[i].v2->fixX()>>16,
segs[i].v2->fixY()>>16,
segs[i].v1->fixX(),
segs[i].v1->fixY(),
segs[i].v2->fixX(),
segs[i].v2->fixY());
}
#endif
return count;
}
int FNodeBuilder::OutputDegenerateSubsector (TArray<glseg_t> &segs, int subsector, bool bForward, double lastdot, FPrivSeg *&prev, vertex_t *outVerts)
{
static const double bestinit[2] = { -DBL_MAX, DBL_MAX };
FPrivSeg *seg;
int i, j, first, max, count;
double dot, x1, y1, dx, dy, dx2, dy2;
bool wantside;
first = (uint32_t)(size_t)Subsectors[subsector].firstline;
max = first + Subsectors[subsector].numlines;
count = 0;
seg = &Segs[SegList[first].SegNum];
x1 = Vertices[seg->v1].x;
y1 = Vertices[seg->v1].y;
dx = Vertices[seg->v2].x - x1;
dy = Vertices[seg->v2].y - y1;
wantside = seg->planefront ^ !bForward;
for (i = first + 1; i < max; ++i)
{
double bestdot = bestinit[bForward];
FPrivSeg *bestseg = NULL;
for (j = first + 1; j < max; ++j)
{
seg = &Segs[SegList[j].SegNum];
if (seg->planefront != wantside)
{
continue;
}
dx2 = Vertices[seg->v1].x - x1;
dy2 = Vertices[seg->v1].y - y1;
dot = dx*dx2 + dy*dy2;
if (bForward)
{
if (dot < bestdot && dot > lastdot)
{
bestdot = dot;
bestseg = seg;
}
}
else
{
if (dot > bestdot && dot < lastdot)
{
bestdot = dot;
bestseg = seg;
}
}
}
if (bestseg != NULL)
{
if (prev->v2 != bestseg->v1)
{
PushConnectingGLSeg (subsector, segs, &outVerts[prev->v2], &outVerts[bestseg->v1]);
count++;
}
seg->storedseg = PushGLSeg (segs, bestseg, outVerts);
count++;
prev = bestseg;
lastdot = bestdot;
}
}
return count;
}
uint32_t FNodeBuilder::PushGLSeg (TArray<glseg_t> &segs, const FPrivSeg *seg, vertex_t *outVerts)
{
glseg_t newseg;
newseg.v1 = outVerts + seg->v1;
newseg.v2 = outVerts + seg->v2;
newseg.backsector = seg->backsector;
newseg.frontsector = seg->frontsector;
if (seg->linedef != -1)
{
newseg.linedef = Level.Lines + seg->linedef;
newseg.sidedef = Level.Sides + seg->sidedef;
}
else
{
newseg.linedef = NULL;
newseg.sidedef = NULL;
}
newseg.Partner = seg->partner;
return (uint32_t)segs.Push (newseg);
}
void FNodeBuilder::PushConnectingGLSeg (int subsector, TArray<glseg_t> &segs, vertex_t *v1, vertex_t *v2)
{
glseg_t newseg;
newseg.v1 = v1;
newseg.v2 = v2;
newseg.backsector = NULL;
newseg.frontsector = NULL;
newseg.linedef = NULL;
newseg.sidedef = NULL;
newseg.Partner = UINT_MAX;
segs.Push (newseg);
}

View file

@ -0,0 +1,397 @@
/*
** nodebuild_gl.cpp
**
** Extra functions for the node builder to create minisegs.
**
**---------------------------------------------------------------------------
** Copyright 2002-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.
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
** covered by the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or (at
** your option) any later version.
**
** 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 "nodebuild.h"
static inline void Warn (const char *format, ...)
{
}
static const angle_t ANGLE_EPSILON = 5000;
#if 0
#define D(x) x
#else
#define D(x) do{}while(0)
#endif
double FNodeBuilder::AddIntersection (const node_t &node, int vertex)
{
static const FEventInfo defaultInfo =
{
-1, UINT_MAX
};
// Calculate signed distance of intersection vertex from start of splitter.
// Only ordering is important, so we don't need a sqrt.
FPrivVert *v = &Vertices[vertex];
double dist = (double(v->x) - node.x)*(node.dx) + (double(v->y) - node.y)*(node.dy);
FEvent *event = Events.FindEvent (dist);
if (event == NULL)
{
event = Events.GetNewNode ();
event->Distance = dist;
event->Info = defaultInfo;
event->Info.Vertex = vertex;
Events.Insert (event);
}
return dist;
}
// If there are any segs on the splitter that span more than two events, they
// must be split. Alien Vendetta is one example wad that is quite bad about
// having overlapping lines. If we skip this step, these segs will still be
// split later, but minisegs will erroneously be added for them, and partner
// seg information will be messed up in the generated tree.
void FNodeBuilder::FixSplitSharers (const node_t &node)
{
D(Printf(PRINT_LOG, "events:\n"));
D(Events.PrintTree());
for (unsigned int i = 0; i < SplitSharers.Size(); ++i)
{
uint32_t seg = SplitSharers[i].Seg;
int v2 = Segs[seg].v2;
FEvent *event = Events.FindEvent (SplitSharers[i].Distance);
FEvent *next;
if (event == NULL)
{ // Should not happen
continue;
}
// Use the CRT's printf so the formatting matches ZDBSP's
D(char buff[200]);
D(sprintf(buff, "Considering events on seg %d(%d[%d,%d]->%d[%d,%d]) [%g:%g]\n", seg,
Segs[seg].v1,
Vertices[Segs[seg].v1].x>>16,
Vertices[Segs[seg].v1].y>>16,
Segs[seg].v2,
Vertices[Segs[seg].v2].x>>16,
Vertices[Segs[seg].v2].y>>16,
SplitSharers[i].Distance, event->Distance));
D(Printf(PRINT_LOG, "%s", buff));
if (SplitSharers[i].Forward)
{
event = Events.GetSuccessor (event);
if (event == NULL)
{
continue;
}
next = Events.GetSuccessor (event);
}
else
{
event = Events.GetPredecessor (event);
if (event == NULL)
{
continue;
}
next = Events.GetPredecessor (event);
}
while (event != NULL && next != NULL && event->Info.Vertex != v2)
{
D(Printf(PRINT_LOG, "Forced split of seg %d(%d->%d) at %d(%d,%d)\n", seg,
Segs[seg].v1, Segs[seg].v2,
event->Info.Vertex,
Vertices[event->Info.Vertex].x>>16,
Vertices[event->Info.Vertex].y>>16));
uint32_t newseg = SplitSeg (seg, event->Info.Vertex, 1);
Segs[newseg].next = Segs[seg].next;
Segs[seg].next = newseg;
uint32_t partner = Segs[seg].partner;
if (partner != UINT_MAX)
{
int endpartner = SplitSeg (partner, event->Info.Vertex, 1);
Segs[endpartner].next = Segs[partner].next;
Segs[partner].next = endpartner;
Segs[seg].partner = endpartner;
Segs[partner].partner = newseg;
}
seg = newseg;
if (SplitSharers[i].Forward)
{
event = next;
next = Events.GetSuccessor (next);
}
else
{
event = next;
next = Events.GetPredecessor (next);
}
}
}
}
void FNodeBuilder::AddMinisegs (const node_t &node, uint32_t splitseg, uint32_t &fset, uint32_t &bset)
{
FEvent *event = Events.GetMinimum (), *prev = NULL;
while (event != NULL)
{
if (prev != NULL)
{
uint32_t fseg1, bseg1, fseg2, bseg2;
uint32_t fnseg, bnseg;
// Minisegs should only be added when they can create valid loops on both the front and
// back of the splitter. This means some subsectors could be unclosed if their sectors
// are unclosed, but at least we won't be needlessly creating subsectors in void space.
// Unclosed subsectors can be closed trivially once the BSP tree is complete.
if ((fseg1 = CheckLoopStart (node.dx, node.dy, prev->Info.Vertex, event->Info.Vertex)) != UINT_MAX &&
(bseg1 = CheckLoopStart (-node.dx, -node.dy, event->Info.Vertex, prev->Info.Vertex)) != UINT_MAX &&
(fseg2 = CheckLoopEnd (node.dx, node.dy, event->Info.Vertex)) != UINT_MAX &&
(bseg2 = CheckLoopEnd (-node.dx, -node.dy, prev->Info.Vertex)) != UINT_MAX)
{
// Add miniseg on the front side
fnseg = AddMiniseg (prev->Info.Vertex, event->Info.Vertex, UINT_MAX, fseg1, splitseg);
Segs[fnseg].next = fset;
fset = fnseg;
// Add miniseg on the back side
bnseg = AddMiniseg (event->Info.Vertex, prev->Info.Vertex, fnseg, bseg1, splitseg);
Segs[bnseg].next = bset;
bset = bnseg;
sector_t *fsector, *bsector;
fsector = Segs[fseg1].frontsector;
bsector = Segs[bseg1].frontsector;
Segs[fnseg].frontsector = fsector;
Segs[fnseg].backsector = bsector;
Segs[bnseg].frontsector = bsector;
Segs[bnseg].backsector = fsector;
// Only print the warning if this might be bad.
if (fsector != bsector &&
fsector != Segs[fseg1].backsector &&
bsector != Segs[bseg1].backsector)
{
Warn ("Sectors %d at (%d,%d) and %d at (%d,%d) don't match.\n",
Segs[fseg1].frontsector,
Vertices[prev->Info.Vertex].x>>FRACBITS, Vertices[prev->Info.Vertex].y>>FRACBITS,
Segs[bseg1].frontsector,
Vertices[event->Info.Vertex].x>>FRACBITS, Vertices[event->Info.Vertex].y>>FRACBITS
);
}
D(Printf (PRINT_LOG, "**Minisegs** %d/%d added %d(%d,%d)->%d(%d,%d)\n", fnseg, bnseg,
prev->Info.Vertex,
Vertices[prev->Info.Vertex].x>>16, Vertices[prev->Info.Vertex].y>>16,
event->Info.Vertex,
Vertices[event->Info.Vertex].x>>16, Vertices[event->Info.Vertex].y>>16));
}
}
prev = event;
event = Events.GetSuccessor (event);
}
}
uint32_t FNodeBuilder::AddMiniseg (int v1, int v2, uint32_t partner, uint32_t seg1, uint32_t splitseg)
{
uint32_t nseg;
FPrivSeg *seg = &Segs[seg1];
FPrivSeg newseg;
newseg.sidedef = NO_SIDE;
newseg.linedef = -1;
newseg.loopnum = 0;
newseg.next = UINT_MAX;
newseg.planefront = true;
newseg.hashnext = NULL;
newseg.storedseg = UINT_MAX;
newseg.frontsector = NULL;
newseg.backsector = NULL;
if (splitseg != UINT_MAX)
{
newseg.planenum = Segs[splitseg].planenum;
}
else
{
newseg.planenum = -1;
}
newseg.v1 = v1;
newseg.v2 = v2;
newseg.nextforvert = Vertices[v1].segs;
newseg.nextforvert2 = Vertices[v2].segs2;
newseg.next = seg->next;
if (partner != UINT_MAX)
{
newseg.partner = partner;
}
else
{
newseg.partner = UINT_MAX;
}
nseg = Segs.Push (newseg);
if (newseg.partner != UINT_MAX)
{
Segs[partner].partner = nseg;
}
Vertices[v1].segs = nseg;
Vertices[v2].segs2 = nseg;
//Printf ("Between %d and %d::::\n", seg1, seg2);
return nseg;
}
uint32_t FNodeBuilder::CheckLoopStart (fixed_t dx, fixed_t dy, int vertex, int vertex2)
{
FPrivVert *v = &Vertices[vertex];
angle_t splitAngle = PointToAngle (dx, dy);
uint32_t segnum;
angle_t bestang;
uint32_t bestseg;
// Find the seg ending at this vertex that forms the smallest angle
// to the splitter.
segnum = v->segs2;
bestang = ANGLE_MAX;
bestseg = UINT_MAX;
while (segnum != UINT_MAX)
{
FPrivSeg *seg = &Segs[segnum];
angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y);
angle_t diff = splitAngle - segAngle;
if (diff < ANGLE_EPSILON &&
PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0)
{
// If a seg lies right on the splitter, don't count it
}
else
{
if (diff <= bestang)
{
bestang = diff;
bestseg = segnum;
}
}
segnum = seg->nextforvert2;
}
if (bestseg == UINT_MAX)
{
return UINT_MAX;
}
// Now make sure there are no segs starting at this vertex that form
// an even smaller angle to the splitter.
segnum = v->segs;
while (segnum != UINT_MAX)
{
FPrivSeg *seg = &Segs[segnum];
if (seg->v2 == vertex2)
{
return UINT_MAX;
}
angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y);
angle_t diff = splitAngle - segAngle;
if (diff < bestang && seg->partner != bestseg)
{
return UINT_MAX;
}
segnum = seg->nextforvert;
}
return bestseg;
}
uint32_t FNodeBuilder::CheckLoopEnd (fixed_t dx, fixed_t dy, int vertex)
{
FPrivVert *v = &Vertices[vertex];
angle_t splitAngle = PointToAngle (dx, dy) + ANGLE_180;
uint32_t segnum;
angle_t bestang;
uint32_t bestseg;
// Find the seg starting at this vertex that forms the smallest angle
// to the splitter.
segnum = v->segs;
bestang = ANGLE_MAX;
bestseg = UINT_MAX;
while (segnum != UINT_MAX)
{
FPrivSeg *seg = &Segs[segnum];
angle_t segAngle = PointToAngle (Vertices[seg->v2].x - v->x, Vertices[seg->v2].y - v->y);
angle_t diff = segAngle - splitAngle;
if (diff < ANGLE_EPSILON &&
PointOnSide (Vertices[seg->v1].x, Vertices[seg->v1].y, v->x, v->y, dx, dy) == 0)
{
// If a seg lies right on the splitter, don't count it
}
else
{
if (diff <= bestang)
{
bestang = diff;
bestseg = segnum;
}
}
segnum = seg->nextforvert;
}
if (bestseg == UINT_MAX)
{
return UINT_MAX;
}
// Now make sure there are no segs ending at this vertex that form
// an even smaller angle to the splitter.
segnum = v->segs2;
while (segnum != UINT_MAX)
{
FPrivSeg *seg = &Segs[segnum];
angle_t segAngle = PointToAngle (Vertices[seg->v1].x - v->x, Vertices[seg->v1].y - v->y);
angle_t diff = segAngle - splitAngle;
if (diff < bestang && seg->partner != bestseg)
{
return UINT_MAX;
}
segnum = seg->nextforvert2;
}
return bestseg;
}

View file

@ -0,0 +1,535 @@
/*
** nodebuild_utility.cpp
**
** Miscellaneous node builder utility functions.
**
**---------------------------------------------------------------------------
** Copyright 2002-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.
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
** covered by the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or (at
** your option) any later version.
**
** 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 <stdlib.h>
#ifdef _MSC_VER
#include <malloc.h>
#endif
#include <string.h>
#include "nodebuild.h"
#include "printf.h"
#include "m_fixed.h"
#include "m_bbox.h"
#if 0
#define D(x) x
#else
#define D(x) do{}while(0)
#endif
#if 0
#define P(x) x
#else
#define P(x) do{}while(0)
#endif
angle_t FNodeBuilder::PointToAngle (fixed_t x, fixed_t y)
{
const double rad2bam = double(1<<30) / M_PI;
double ang = g_atan2 (double(y), double(x));
// Convert to signed first since negative double to unsigned is undefined.
return angle_t(int(ang * rad2bam)) << 1;
}
void FNodeBuilder::FindUsedVertices (vertex_t *oldverts, int max)
{
int *map = new int[max];
int i;
FPrivVert newvert;
memset (&map[0], -1, sizeof(int)*max);
for (i = 0; i < Level.NumLines; ++i)
{
ptrdiff_t v1 = Level.Lines[i].v1 - oldverts;
ptrdiff_t v2 = Level.Lines[i].v2 - oldverts;
if (map[v1] == -1)
{
newvert.x = oldverts[v1].fixX();
newvert.y = oldverts[v1].fixY();
map[v1] = VertexMap->SelectVertexExact (newvert);
}
if (map[v2] == -1)
{
newvert.x = oldverts[v2].fixX();
newvert.y = oldverts[v2].fixY();
map[v2] = VertexMap->SelectVertexExact (newvert);
}
Level.Lines[i].v1 = (vertex_t *)(size_t)map[v1];
Level.Lines[i].v2 = (vertex_t *)(size_t)map[v2];
}
OldVertexTable = map;
}
// Retrieves the original vertex -> current vertex table.
// Doing so prevents the node builder from freeing it.
const int *FNodeBuilder::GetOldVertexTable()
{
int *table = OldVertexTable;
OldVertexTable = NULL;
return table;
}
// For every sidedef in the map, create a corresponding seg.
void FNodeBuilder::MakeSegsFromSides ()
{
int i, j;
if (Level.NumLines == 0)
{
I_Error ("Map is empty.\n");
}
for (i = 0; i < Level.NumLines; ++i)
{
if (Level.Lines[i].sidedef[0] != NULL)
{
CreateSeg (i, 0);
}
else
{
Printf ("Linedef %d does not have a front side.\n", i);
}
if (Level.Lines[i].sidedef[1] != NULL)
{
j = CreateSeg (i, 1);
if (Level.Lines[i].sidedef[0] != NULL)
{
Segs[j-1].partner = j;
Segs[j].partner = j-1;
}
}
}
}
int FNodeBuilder::CreateSeg (int linenum, int sidenum)
{
FPrivSeg seg;
int segnum;
seg.next = UINT_MAX;
seg.loopnum = 0;
seg.partner = UINT_MAX;
seg.hashnext = NULL;
seg.planefront = false;
seg.planenum = UINT_MAX;
seg.storedseg = UINT_MAX;
if (sidenum == 0)
{ // front
seg.frontsector = Level.Lines[linenum].frontsector;
seg.backsector = Level.Lines[linenum].backsector;
seg.v1 = (int)(size_t)Level.Lines[linenum].v1;
seg.v2 = (int)(size_t)Level.Lines[linenum].v2;
}
else
{ // back
seg.frontsector = Level.Lines[linenum].backsector;
seg.backsector = Level.Lines[linenum].frontsector;
seg.v2 = (int)(size_t)Level.Lines[linenum].v1;
seg.v1 = (int)(size_t)Level.Lines[linenum].v2;
}
seg.linedef = linenum;
side_t *sd = Level.Lines[linenum].sidedef[sidenum];
seg.sidedef = sd != NULL? sd->Index() : int(NO_SIDE);
seg.nextforvert = Vertices[seg.v1].segs;
seg.nextforvert2 = Vertices[seg.v2].segs2;
segnum = (int)Segs.Push (seg);
Vertices[seg.v1].segs = segnum;
Vertices[seg.v2].segs2 = segnum;
D(Printf(PRINT_LOG, "Seg %4d: From line %d, side %s (%5d,%5d)-(%5d,%5d) [%08x,%08x]-[%08x,%08x]\n", segnum, linenum, sidenum ? "back " : "front",
Vertices[seg.v1].x>>16, Vertices[seg.v1].y>>16, Vertices[seg.v2].x>>16, Vertices[seg.v2].y>>16,
Vertices[seg.v1].x, Vertices[seg.v1].y, Vertices[seg.v2].x, Vertices[seg.v2].y));
return segnum;
}
// For every seg, create FPrivSegs and FPrivVerts.
void FNodeBuilder::AddSegs(seg_t *segs, int numsegs)
{
assert(numsegs > 0);
for (int i = 0; i < numsegs; ++i)
{
FPrivSeg seg;
FPrivVert vert;
int segnum;
seg.next = UINT_MAX;
seg.loopnum = 0;
seg.partner = UINT_MAX;
seg.hashnext = NULL;
seg.planefront = false;
seg.planenum = UINT_MAX;
seg.storedseg = UINT_MAX;
seg.frontsector = segs[i].frontsector;
seg.backsector = segs[i].backsector;
vert.x = segs[i].v1->fixX();
vert.y = segs[i].v1->fixY();
seg.v1 = VertexMap->SelectVertexExact(vert);
vert.x = segs[i].v2->fixX();
vert.y = segs[i].v2->fixY();
seg.v2 = VertexMap->SelectVertexExact(vert);
seg.linedef = segs[i].linedef->Index();
seg.sidedef = segs[i].sidedef != NULL ? segs[i].sidedef->Index() : int(NO_SIDE);
seg.nextforvert = Vertices[seg.v1].segs;
seg.nextforvert2 = Vertices[seg.v2].segs2;
segnum = (int)Segs.Push(seg);
Vertices[seg.v1].segs = segnum;
Vertices[seg.v2].segs2 = segnum;
}
}
// Group colinear segs together so that only one seg per line needs to be checked
// by SelectSplitter().
void FNodeBuilder::GroupSegPlanes ()
{
const int bucketbits = 12;
FPrivSeg *buckets[1<<bucketbits] = { 0 };
int i, planenum;
for (i = 0; i < (int)Segs.Size(); ++i)
{
FPrivSeg *seg = &Segs[i];
seg->next = i+1;
seg->hashnext = NULL;
}
Segs[Segs.Size()-1].next = UINT_MAX;
for (i = planenum = 0; i < (int)Segs.Size(); ++i)
{
FPrivSeg *seg = &Segs[i];
fixed_t x1 = Vertices[seg->v1].x;
fixed_t y1 = Vertices[seg->v1].y;
fixed_t x2 = Vertices[seg->v2].x;
fixed_t y2 = Vertices[seg->v2].y;
angle_t ang = PointToAngle (x2 - x1, y2 - y1);
if (ang >= 1u<<31)
ang += 1u<<31;
FPrivSeg *check = buckets[ang >>= 31-bucketbits];
while (check != NULL)
{
fixed_t cx1 = Vertices[check->v1].x;
fixed_t cy1 = Vertices[check->v1].y;
fixed_t cdx = Vertices[check->v2].x - cx1;
fixed_t cdy = Vertices[check->v2].y - cy1;
if (PointOnSide (x1, y1, cx1, cy1, cdx, cdy) == 0 &&
PointOnSide (x2, y2, cx1, cy1, cdx, cdy) == 0)
{
break;
}
check = check->hashnext;
}
if (check != NULL)
{
seg->planenum = check->planenum;
const FSimpleLine *line = &Planes[seg->planenum];
if (line->dx != 0)
{
if ((line->dx > 0 && x2 > x1) || (line->dx < 0 && x2 < x1))
{
seg->planefront = true;
}
else
{
seg->planefront = false;
}
}
else
{
if ((line->dy > 0 && y2 > y1) || (line->dy < 0 && y2 < y1))
{
seg->planefront = true;
}
else
{
seg->planefront = false;
}
}
}
else
{
seg->hashnext = buckets[ang];
buckets[ang] = seg;
seg->planenum = planenum++;
seg->planefront = true;
FSimpleLine pline = { Vertices[seg->v1].x,
Vertices[seg->v1].y,
Vertices[seg->v2].x - Vertices[seg->v1].x,
Vertices[seg->v2].y - Vertices[seg->v1].y };
Planes.Push (pline);
}
}
D(Printf ("%d planes from %d segs\n", planenum, Segs.Size()));
PlaneChecked.Reserve ((planenum + 7) / 8);
}
// Just create one plane per seg. Should be good enough for mini BSPs.
void FNodeBuilder::GroupSegPlanesSimple()
{
Planes.Resize(Segs.Size());
for (int i = 0; i < (int)Segs.Size(); ++i)
{
FPrivSeg *seg = &Segs[i];
FSimpleLine *pline = &Planes[i];
seg->next = i+1;
seg->hashnext = NULL;
seg->planenum = i;
seg->planefront = true;
pline->x = Vertices[seg->v1].x;
pline->y = Vertices[seg->v1].y;
pline->dx = Vertices[seg->v2].x - Vertices[seg->v1].x;
pline->dy = Vertices[seg->v2].y - Vertices[seg->v1].y;
}
Segs.Last().next = UINT_MAX;
PlaneChecked.Reserve((Segs.Size() + 7) / 8);
}
void FNodeBuilder::AddSegToBBox (fixed_t bbox[4], const FPrivSeg *seg)
{
FPrivVert *v1 = &Vertices[seg->v1];
FPrivVert *v2 = &Vertices[seg->v2];
if (v1->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v1->x;
if (v1->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v1->x;
if (v1->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v1->y;
if (v1->y > bbox[BOXTOP]) bbox[BOXTOP] = v1->y;
if (v2->x < bbox[BOXLEFT]) bbox[BOXLEFT] = v2->x;
if (v2->x > bbox[BOXRIGHT]) bbox[BOXRIGHT] = v2->x;
if (v2->y < bbox[BOXBOTTOM]) bbox[BOXBOTTOM] = v2->y;
if (v2->y > bbox[BOXTOP]) bbox[BOXTOP] = v2->y;
}
void FNodeBuilder::FLevel::FindMapBounds()
{
double minx, maxx, miny, maxy;
minx = maxx = Vertices[0].fX();
miny = maxy = Vertices[0].fY();
for (int i = 1; i < NumLines; ++i)
{
for (int j = 0; j < 2; j++)
{
vertex_t *v = (j == 0 ? Lines[i].v1 : Lines[i].v2);
if (v->fX() < minx) minx = v->fX();
else if (v->fX() > maxx) maxx = v->fX();
if (v->fY() < miny) miny = v->fY();
else if (v->fY() > maxy) maxy = v->fY();
}
}
MinX = FLOAT2FIXED(minx);
MinY = FLOAT2FIXED(miny);
MaxX = FLOAT2FIXED(maxx);
MaxY = FLOAT2FIXED(maxy);
}
FNodeBuilder::IVertexMap::~IVertexMap()
{
}
FNodeBuilder::FVertexMap::FVertexMap (FNodeBuilder &builder,
fixed_t minx, fixed_t miny, fixed_t maxx, fixed_t maxy)
: MyBuilder(builder)
{
MinX = minx;
MinY = miny;
BlocksWide = int(((double(maxx) - minx + 1) + (BLOCK_SIZE - 1)) / BLOCK_SIZE);
BlocksTall = int(((double(maxy) - miny + 1) + (BLOCK_SIZE - 1)) / BLOCK_SIZE);
MaxX = MinX + fixed64_t(BlocksWide) * BLOCK_SIZE - 1;
MaxY = MinY + fixed64_t(BlocksTall) * BLOCK_SIZE - 1;
VertexGrid = new TArray<int>[BlocksWide * BlocksTall];
}
FNodeBuilder::FVertexMap::~FVertexMap ()
{
delete[] VertexGrid;
}
int FNodeBuilder::FVertexMap::SelectVertexExact (FNodeBuilder::FPrivVert &vert)
{
TArray<int> &block = VertexGrid[GetBlock (vert.x, vert.y)];
FPrivVert *vertices = &MyBuilder.Vertices[0];
unsigned int i;
for (i = 0; i < block.Size(); ++i)
{
if (vertices[block[i]].x == vert.x && vertices[block[i]].y == vert.y)
{
return block[i];
}
}
// Not present: add it!
return InsertVertex (vert);
}
int FNodeBuilder::FVertexMap::SelectVertexClose (FNodeBuilder::FPrivVert &vert)
{
TArray<int> &block = VertexGrid[GetBlock (vert.x, vert.y)];
FPrivVert *vertices = &MyBuilder.Vertices[0];
unsigned int i;
for (i = 0; i < block.Size(); ++i)
{
#if VERTEX_EPSILON <= 1
if (vertices[block[i]].x == vert.x && vertices[block[i]].y == vert.y)
#else
if (abs(vertices[block[i]].x - vert.x) < VERTEX_EPSILON &&
abs(vertices[block[i]].y - vert.y) < VERTEX_EPSILON)
#endif
{
return block[i];
}
}
// Not present: add it!
return InsertVertex (vert);
}
int FNodeBuilder::FVertexMap::InsertVertex (FNodeBuilder::FPrivVert &vert)
{
int vertnum;
vert.segs = UINT_MAX;
vert.segs2 = UINT_MAX;
vertnum = (int)MyBuilder.Vertices.Push (vert);
// If a vertex is near a block boundary, then it will be inserted on
// both sides of the boundary so that SelectVertexClose can find
// it by checking in only one block.
fixed64_t minx = MAX (MinX, fixed64_t(vert.x) - VERTEX_EPSILON);
fixed64_t maxx = MIN (MaxX, fixed64_t(vert.x) + VERTEX_EPSILON);
fixed64_t miny = MAX (MinY, fixed64_t(vert.y) - VERTEX_EPSILON);
fixed64_t maxy = MIN (MaxY, fixed64_t(vert.y) + VERTEX_EPSILON);
int blk[4] =
{
GetBlock (minx, miny),
GetBlock (maxx, miny),
GetBlock (minx, maxy),
GetBlock (maxx, maxy)
};
unsigned int blkcount[4] =
{
VertexGrid[blk[0]].Size(),
VertexGrid[blk[1]].Size(),
VertexGrid[blk[2]].Size(),
VertexGrid[blk[3]].Size()
};
for (int i = 0; i < 4; ++i)
{
if (VertexGrid[blk[i]].Size() == blkcount[i])
{
VertexGrid[blk[i]].Push (vertnum);
}
}
return vertnum;
}
FNodeBuilder::FVertexMapSimple::FVertexMapSimple(FNodeBuilder &builder)
: MyBuilder(builder)
{
}
int FNodeBuilder::FVertexMapSimple::SelectVertexExact(FNodeBuilder::FPrivVert &vert)
{
FPrivVert *verts = &MyBuilder.Vertices[0];
unsigned int stop = MyBuilder.Vertices.Size();
for (unsigned int i = 0; i < stop; ++i)
{
if (verts[i].x == vert.x && verts[i].y == vert.y)
{
return i;
}
}
// Not present: add it!
return InsertVertex(vert);
}
int FNodeBuilder::FVertexMapSimple::SelectVertexClose(FNodeBuilder::FPrivVert &vert)
{
FPrivVert *verts = &MyBuilder.Vertices[0];
unsigned int stop = MyBuilder.Vertices.Size();
for (unsigned int i = 0; i < stop; ++i)
{
#if VERTEX_EPSILON <= 1
if (verts[i].x == vert.x && verts[i].y == y)
#else
if (abs(verts[i].x - vert.x) < VERTEX_EPSILON &&
abs(verts[i].y - vert.y) < VERTEX_EPSILON)
#endif
{
return i;
}
}
// Not present: add it!
return InsertVertex (vert);
}
int FNodeBuilder::FVertexMapSimple::InsertVertex (FNodeBuilder::FPrivVert &vert)
{
vert.segs = UINT_MAX;
vert.segs2 = UINT_MAX;
return (int)MyBuilder.Vertices.Push (vert);
}

View file

@ -48,6 +48,7 @@
#include "../../glbackend/glbackend.h"
LookupTableInfo lookups;
int numshades;
//==========================================================================
//
@ -136,7 +137,6 @@ void paletteLoadFromDisk(void)
void LookupTableInfo::postLoadTables(void)
{
globalpal = 0;
GPalette.GenerateGlobalBrightmapFromColormap(getTable(0), numshades);
// Try to detect fullbright translations. Unfortunately this cannot be used to detect fade strength because of loss of color precision in the palette map.

View file

@ -17,16 +17,21 @@
#include "palentry.h"
#include "templates.h"
#define MAXBASEPALS 256
#define MAXPALOOKUPS 256
#define MAXBLENDTABS 256
enum
{
MAXBASEPALS = 256,
MAXPALOOKUPS = 256,
MAXBLENDTABS = 256,
RESERVEDPALS = 4, // don't forget to increment this when adding reserved pals
DETAILPAL = (MAXPALOOKUPS - 1),
GLOWPAL = (MAXPALOOKUPS - 2),
SPECULARPAL = (MAXPALOOKUPS - 3),
NORMALPAL = (MAXPALOOKUPS - 4),
BRIGHTPAL = (MAXPALOOKUPS),
#define RESERVEDPALS 4 // don't forget to increment this when adding reserved pals
#define DETAILPAL (MAXPALOOKUPS - 1)
#define GLOWPAL (MAXPALOOKUPS - 2)
#define SPECULARPAL (MAXPALOOKUPS - 3)
#define NORMALPAL (MAXPALOOKUPS - 4)
#define BRIGHTPAL (MAXPALOOKUPS)
MAXREALPAL = MAXPALOOKUPS - RESERVEDPALS
};
// fixme: should use the flags from the PRSFlags enum directly
enum
@ -165,7 +170,7 @@ inline void videoclearFade()
void videoTintBlood(int32_t r, int32_t g, int32_t b);
extern int32_t globalpal, globalfloorpal;
extern int numshades;
extern void paletteLoadFromDisk(void);

View file

@ -1,48 +0,0 @@
/*
** parsefuncs.h
** handlers for .def parser
** only to be included by the actual parser
**
**---------------------------------------------------------------------------
** Copyright 2021 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.
**---------------------------------------------------------------------------
**
**
*/
void parseAnimTileRange(FScanner& sc, FScriptPosition& pos)
{
SetAnim set;
if (!sc.GetNumber(set.tile1, true)) return;
if (!sc.GetNumber(set.tile2, true)) return;
if (!sc.GetNumber(set.speed, true)) return;
if (!sc.GetNumber(set.type, true)) return;
processSetAnim("animtilerange", pos, set);
}

View file

@ -39,7 +39,10 @@
#include "hw_material.h"
#include "gamestruct.h"
#include "gamecontrol.h"
#include "glbackend/gl_models.h"
#include "texturemanager.h"
#include "hw_models.h"
#include "hw_voxels.h"
#include "mapinfo.h"
BEGIN_BLD_NS
extern short voxelIndex[MAXTILES];
@ -128,6 +131,19 @@ void precacheMarkedTiles()
int dapalnum = pair->Key >> 32;
doprecache(dapicnum, dapalnum);
}
// Cache everything the map explicitly declares.
TMap<FString, bool> cachetexmap;
for (auto& tex : currentLevel->PrecacheTextures) cachetexmap.Insert(tex, true);
decltype(cachetexmap)::Iterator it2(cachetexmap);
decltype(cachetexmap)::Pair* pair2;
while (it2.NextPair(pair2))
{
auto tex = TexMan.FindGameTexture(pair2->Key, ETextureType::Any);
if (tex) PrecacheTex(tex, 0);
}
cachemap.Clear();
}

View file

@ -2,5 +2,6 @@
void PrecacheHardwareTextures(int nTile);
void markTileForPrecache(int tilenum, int palnum);
void markTextureForPrecache(const char* texname);
void markVoxelForPrecache(int voxnum);
void precacheMarkedTiles();

View file

@ -0,0 +1,400 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2004-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/
//
//--------------------------------------------------------------------------
//
/*
** gl_scene.cpp
** manages the rendering of the player's view
**
*/
#include "gi.h"
#include "build.h"
#include "v_draw.h"
//#include "a_dynlight.h"
#include "v_video.h"
#include "m_png.h"
#include "i_time.h"
#include "hw_dynlightdata.h"
#include "hw_clock.h"
#include "flatvertices.h"
#include "hw_renderstate.h"
#include "hw_lightbuffer.h"
#include "hw_cvars.h"
#include "hw_viewpointbuffer.h"
#include "hw_clipper.h"
//#include "hwrenderer/scene/hw_portal.h"
#include "hw_vrmodes.h"
#include "hw_drawstructs.h"
#include "hw_drawlist.h"
#include "hw_drawinfo.h"
#include "gamecvars.h"
#include "render.h"
#include "gamestruct.h"
#include "gamehud.h"
EXTERN_CVAR(Bool, cl_capfps)
PalEntry GlobalMapFog;
float GlobalFogDensity = 350.f;
TArray<PortalDesc> allPortals;
#if 0
void CollectLights(FLevelLocals* Level)
{
IShadowMap* sm = &screen->mShadowMap;
int lightindex = 0;
// Todo: this should go through the blockmap in a spiral pattern around the player so that closer lights are preferred.
for (auto light = Level->lights; light; light = light->next)
{
IShadowMap::LightsProcessed++;
if (light->shadowmapped && light->IsActive() && lightindex < 1024)
{
IShadowMap::LightsShadowmapped++;
light->mShadowmapIndex = lightindex;
sm->SetLight(lightindex, (float)light->X(), (float)light->Y(), (float)light->Z(), light->GetRadius());
lightindex++;
}
else
{
light->mShadowmapIndex = 1024;
}
}
for (; lightindex < 1024; lightindex++)
{
sm->SetLight(lightindex, 0, 0, 0, 0);
}
}
#endif
//-----------------------------------------------------------------------------
//
// Renders one viewpoint in a scene
//
//-----------------------------------------------------------------------------
void RenderViewpoint(FRenderViewpoint& mainvp, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen)
{
auto& RenderState = *screen->RenderState();
/*
if (mainview && toscreen)
{
screen->SetAABBTree(camera->Level->aabbTree);
screen->mShadowMap.SetCollectLights([=] {
CollectLights(camera->Level);
});
screen->UpdateShadowMap();
}
*/
// Render (potentially) multiple views for stereo 3d
// Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode.
auto vrmode = VRMode::GetVRMode(mainview && toscreen);
const int eyeCount = vrmode->mEyeCount;
screen->FirstEye();
hw_int_useindexedcolortextures = eyeCount > 1? false : *hw_useindexedcolortextures;
for (int eye_ix = 0; eye_ix < eyeCount; ++eye_ix)
{
const auto& eye = vrmode->mEyes[eye_ix];
screen->SetViewportRects(bounds);
if (mainview) // Bind the scene frame buffer and turn on draw buffers used by ssao
{
bool useSSAO = (gl_ssao != 0);
screen->SetSceneRenderTarget(useSSAO);
RenderState.SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS);
RenderState.EnableDrawBuffers(RenderState.GetPassDrawBufferCount(), true);
}
auto di = HWDrawInfo::StartDrawInfo(nullptr, mainvp, nullptr);
auto& vp = di->Viewpoint;
vp = mainvp;
di->Set3DViewport(RenderState);
float flash = 1.f;
di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint)
// Stereo mode specific perspective projection
di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio);
// Stereo mode specific viewpoint adjustment
vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees);
di->SetupView(RenderState, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false);
di->ProcessScene(toscreen);
if (mainview)
{
PostProcess.Clock();
if (toscreen) di->EndDrawScene(RenderState); // do not call this for camera textures.
if (RenderState.GetPassType() == GBUFFER_PASS) // Turn off ssao draw buffers
{
RenderState.SetPassType(NORMAL_PASS);
RenderState.EnableDrawBuffers(1);
}
screen->PostProcessScene(false, CM_DEFAULT, flash, [&]() { });
PostProcess.Unclock();
}
di->EndDrawInfo();
if (eyeCount - eye_ix > 1)
screen->NextEye(eyeCount);
}
hw_int_useindexedcolortextures = false;
}
//===========================================================================
//
// Set up the view point.
//
//===========================================================================
FRenderViewpoint SetupViewpoint(spritetype* cam, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang)
{
FRenderViewpoint r_viewpoint{};
r_viewpoint.CameraSprite = cam;
r_viewpoint.SectNums = nullptr;
r_viewpoint.SectCount = sectnum;
r_viewpoint.Pos = { position.x / 16.f, position.y / -16.f, position.z / -256.f };
r_viewpoint.HWAngles.Yaw = -90.f + angle.asdeg();
r_viewpoint.HWAngles.Pitch = -horizon.aspitch();
r_viewpoint.HWAngles.Roll = -rollang.asdeg();
r_viewpoint.FieldOfView = (float)r_fov;
r_viewpoint.RotAngle = angle.asbam();
double FocalTangent = tan(r_viewpoint.FieldOfView.Radians() / 2);
DAngle an = 270. - r_viewpoint.HWAngles.Yaw.Degrees;
r_viewpoint.TanSin = FocalTangent * an.Sin();
r_viewpoint.TanCos = FocalTangent * an.Cos();
r_viewpoint.ViewVector = an.ToVector();
return r_viewpoint;
}
void DoWriteSavePic(FileWriter* file, uint8_t* scr, int width, int height, bool upsidedown)
{
int pixelsize = 3;
int pitch = width * pixelsize;
if (upsidedown)
{
scr += ((height - 1) * width * pixelsize);
pitch *= -1;
}
M_CreatePNG(file, scr, nullptr, SS_RGB, width, height, pitch, vid_gamma);
}
//===========================================================================
//
// Render the view to a savegame picture
//
// Currently a bit messy because the game side still needs to be able to
// handle Polymost.
//
//===========================================================================
bool writingsavepic;
FileWriter* savefile;
int savewidth, saveheight;
void PM_WriteSavePic(FileWriter* file, int width, int height);
EXTERN_CVAR(Bool, testnewrenderer);
void WriteSavePic(FileWriter* file, int width, int height)
{
if (!testnewrenderer)
{
PM_WriteSavePic(file, width, height);
return;
}
int oldx = xdim;
int oldy = ydim;
auto oldwindowxy1 = windowxy1;
auto oldwindowxy2 = windowxy2;
xdim = width;
ydim = height;
videoSetViewableArea(0, 0, width - 1, height - 1);
writingsavepic = true;
savefile = file;
savewidth = width;
saveheight = height;
bool didit = gi->GenerateSavePic();
writingsavepic = false;
xdim = oldx;
ydim = oldy;
videoSetViewableArea(oldwindowxy1.x, oldwindowxy1.y, oldwindowxy2.x, oldwindowxy2.y);
}
void RenderToSavePic(FRenderViewpoint& vp, FileWriter* file, int width, int height)
{
IntRect bounds;
bounds.left = 0;
bounds.top = 0;
bounds.width = width;
bounds.height = height;
auto& RenderState = *screen->RenderState();
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
screen->WaitForCommands(false);
// Switch to render buffers dimensioned for the savepic
screen->SetSaveBuffers(true);
screen->ImageTransitionScene(true);
RenderState.SetVertexBuffer(screen->mVertexData);
screen->mVertexData->Reset();
screen->mLights->Clear();
screen->mViewpoints->Clear();
twodpsp.Clear();
RenderViewpoint(vp, &bounds, vp.FieldOfView.Degrees, 1.333f, 1.333f, true, false);
int numpixels = width * height;
uint8_t* scr = (uint8_t*)M_Malloc(numpixels * 3);
screen->CopyScreenToBuffer(width, height, scr);
DoWriteSavePic(file, scr, width, height, screen->FlipSavePic());
M_Free(scr);
// Switch back the screen render buffers
screen->SetViewportRects(nullptr);
screen->SetSaveBuffers(false);
}
//===========================================================================
//
// Renders the main view
//
//===========================================================================
static void CheckTimer(FRenderState &state, uint64_t ShaderStartTime)
{
// if firstFrame is not yet initialized, initialize it to current time
// if we're going to overflow a float (after ~4.6 hours, or 24 bits), re-init to regain precision
if ((state.firstFrame == 0) || (screen->FrameTime - state.firstFrame >= 1 << 24) || ShaderStartTime >= state.firstFrame)
state.firstFrame = screen->FrameTime;
}
void animatecamsprite(double s);
void render_drawrooms(spritetype* playersprite, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang, double smoothratio)
{
checkRotatedWalls();
if (gl_fogmode == 1) gl_fogmode = 2; // still needed?
int16_t sect = sectnum;
updatesector(position.x, position.y, &sect);
if (sect >= 0) sectnum = sect;
if (sectnum < 0) return;
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0;
checkBenchActive();
// reset statistics counters
ResetProfilingData();
// Get this before everything else
FRenderViewpoint r_viewpoint = SetupViewpoint(playersprite, position, sectnum, angle, horizon, rollang);
if (cl_capfps) r_viewpoint.TicFrac = 1.;
else r_viewpoint.TicFrac = smoothratio * (1/65536.);
screen->mLights->Clear();
screen->mViewpoints->Clear();
screen->mVertexData->Reset();
if (writingsavepic) // hack alert! The save code should not go through render_drawrooms, but we can only clean up the game side when Polymost is gone for good.
{
RenderToSavePic(r_viewpoint, savefile, savewidth, saveheight);
return;
}
// Shader start time does not need to be handled per level. Just use the one from the camera to render from.
auto RenderState = screen->RenderState();
CheckTimer(*RenderState, 0/*ShaderStartTime*/);
// prepare all camera textures that have been used in the last frame.
gi->UpdateCameras(r_viewpoint.TicFrac);
RenderState->SetVertexBuffer(screen->mVertexData);
// now render the main view
float fovratio;
float ratio = ActiveRatio(screen->GetWidth(), screen->GetHeight());
if (ratio >= 1.33f)
{
fovratio = 1.33f;
}
else
{
fovratio = ratio;
}
screen->ImageTransitionScene(true); // Only relevant for Vulkan.
RenderViewpoint(r_viewpoint, nullptr, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true);
All.Unclock();
}
void render_camtex(spritetype* playersprite, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang, FGameTexture* camtex, IntRect& rect, double smoothratio)
{
int16_t sect = sectnum;
updatesector(position.x, position.y, &sect);
if (sect >= 0) sectnum = sect;
if (sectnum < 0) return;
screen->RenderState()->SetVertexBuffer(screen->mVertexData);
// now render the main view
float ratio = camtex->GetDisplayWidth() / camtex->GetDisplayHeight();
FRenderViewpoint r_viewpoint = SetupViewpoint(playersprite, position, sectnum, angle, horizon, rollang);
if (cl_capfps) r_viewpoint.TicFrac = smoothratio;
RenderViewpoint(r_viewpoint, &rect, r_viewpoint.FieldOfView.Degrees, ratio, ratio, false, false);
All.Unclock();
}
FSerializer& Serialize(FSerializer& arc, const char* key, PortalDesc& obj, PortalDesc* defval)
{
if (arc.BeginObject(key))
{
arc("type", obj.type)
("dx", obj.dx)
("dy", obj.dy)
("dz", obj.dz)
("targets", obj.targets)
.EndObject();
}
return arc;
}

View file

@ -6,7 +6,7 @@
//
// 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
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
@ -33,7 +33,7 @@
#include "hwrenderer/data/buffers.h"
#include "flatvertices.h"
#include "hw_renderstate.h"
#include "gl_models.h"
#include "hw_models.h"
//CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE)

View file

@ -6,7 +6,7 @@
//
// 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
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,

View file

@ -35,7 +35,7 @@
#include <memory>
#include "m_crc32.h"
#include "glbackend.h"
#include "hw_palmanager.h"
#include "resourcefile.h"
#include "imagehelpers.h"
@ -44,6 +44,8 @@
#include "build.h"
#include "v_video.h"
static PaletteManager* palmanager;
//===========================================================================
//
// This class manages the hardware data for the indexed render mode.
@ -138,4 +140,24 @@ IHardwareTexture* PaletteManager::GetLookup(int index)
return nullptr;
}
//===========================================================================
//
//
//
//===========================================================================
IHardwareTexture *setpalettelayer(int layer, int translation)
{
if (!palmanager) palmanager = new PaletteManager;
if (layer == 1)
return palmanager->GetPalette(GetTranslationType(translation) - Translation_Remap);
else if (layer == 2)
return palmanager->GetLookup(GetTranslationIndex(translation));
else return nullptr;
}
void ClearPalManager()
{
if (palmanager) delete palmanager;
palmanager = nullptr;
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "gl_hwtexture.h"
struct palette_t;
class PaletteManager
{
IHardwareTexture* palettetextures[256] = {};
IHardwareTexture* lookuptextures[256] = {};
unsigned FindPalswap(const uint8_t* paldata, palette_t& fadecolor);
public:
~PaletteManager();
void DeleteAll();
IHardwareTexture *GetPalette(int index);
IHardwareTexture* GetLookup(int index);
};
IHardwareTexture* setpalettelayer(int layer, int translation);
void ClearPalManager();

View file

@ -0,0 +1,221 @@
/*
** hw_sectiona.cpp
** For decoupling the renderer from internal Build structures
**
**---------------------------------------------------------------------------
** Copyright 2021 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.
**---------------------------------------------------------------------------
**
** The sole reason for existence of this file is that Build's sector setup
** does not allow for easy splitting of sectors, either for having disjoint parts
** or requiring partial rendering. So we need to add a superstructure
** where we can shuffle around some content without disturbing the original
** order...
**
*/
#include "hw_sections.h"
SectionLine sectionLines[MAXWALLS + (MAXWALLS >> 2)];
Section sections[MAXSECTORS + (MAXSECTORS >> 2)];
TArray<int> sectionspersector[MAXSECTORS]; // reverse map, mainly for the automap
int numsections;
int numsectionlines;
void hw_SplitSector(int sector, int startpos, int endpos);
TArray<int> splits;
void hw_BuildSections()
{
// Initial setup just creates a 1:1 mapping of walls to section lines and sectors to sections.
numsectionlines = numwalls;
numsections = numsectors;
for (int i = 0; i < numwalls; i++)
{
sectionLines[i].startpoint = sectionLines[i].wall = i;
sectionLines[i].endpoint = wall[i].point2;
sectionLines[i].partner = wall[i].nextwall;
sectionLines[i].section = wall[i].sector;
sectionLines[i].partnersection = wall[i].nextsector;
sectionLines[i].point2index = wall[i].point2 - sector[wall[i].sector].wallptr;
}
for (int i = 0; i < numsectors; i++)
{
sections[i].sector = i;
sections[i].lines.Resize(sector[i].wallnum);
for (int j = 0; j < sector[i].wallnum; j++) sections[i].lines[j] = sector[i].wallptr + j;
sectionspersector[i].Resize(1);
sectionspersector[i][0] = i;
}
for (unsigned i = 0; i < splits.Size(); i += 3)
hw_SplitSector(splits[i], splits[i + 1], splits[i + 2]);
}
static void SplitSection(int section, int start, int end)
{
// note: to do this, the sector's lines must be well ordered and there must only be one outline and no holes.
// This also can only apply a single split to a given sector.
int firstsection = numsections++;
int secondsection = numsections++;
auto& sect = sections[section];
Section* sect1 = &sections[firstsection];
Section* sect2 = &sections[secondsection];
sect1->sector = sect.sector;
sect2->sector = sect.sector;
sect1->lines.Clear();
sect2->lines.Clear();
for (int aline : sect.lines)
{
int line = sectionLines[aline].wall;
if (line < start || line >= end)
{
sect1->lines.Push(aline);
}
if (line == start)
{
sect1->lines.Push(-1);
sect2->lines.Push(-1);
}
if (line >= start && line < end)
{
sect2->lines.Push(aline);
}
}
int firstnewline = numsectionlines;
int thisline = numsectionlines;
int splitline1, splitline2;
//numsectionlines += sect1->lines.Size() + 1;
for (unsigned i = 0; i < sect1->lines.Size(); i++)// auto& sline : sect1->lines)
{
int sline = sect1->lines[i];
sect1->lines[i] = thisline;
if (sline != -1)
{
SectionLine& newline = sectionLines[thisline];
newline = sectionLines[sline];
newline.section = int16_t(sect1 - sections);
if (i != sect1->lines.Size() - 1) newline.point2index = thisline + 1 - firstnewline;
else newline.point2index = 0;
assert(newline.point2index >= 0);
// relink the partner
auto& partnerline = sectionLines[newline.partner];
partnerline.partner = thisline;
partnerline.partnersection = newline.section;
thisline++;
}
else
{
splitline1 = thisline++;
sectionLines[splitline1].wall = -1;
sectionLines[splitline1].section = int16_t(sect1 - sections);
sectionLines[splitline1].partnersection = int16_t(sect2 - sections);
sectionLines[splitline1].startpoint = start;
sectionLines[splitline1].endpoint = end;
sectionLines[splitline1].point2index = splitline1 + 1 - firstnewline;
}
}
firstnewline = thisline;
for (unsigned i = 0; i < sect2->lines.Size(); i++)// auto& sline : sect1->lines)
{
int sline = sect2->lines[i];
sect2->lines[i] = thisline;
if (sline != -1)
{
SectionLine& newline = sectionLines[thisline];
newline = sectionLines[sline];
newline.section = int16_t(sect2 - sections);
if (i != sect2->lines.Size() - 1) newline.point2index = thisline + 1 - firstnewline;
else newline.point2index = 0;
assert(newline.point2index >= 0);
// relink the partner
auto& partnerline = sectionLines[newline.partner];
partnerline.partner = thisline;
partnerline.partnersection = newline.section;
thisline++;
}
else
{
splitline2 = thisline++;
sectionLines[splitline2].wall = -1;
sectionLines[splitline2].section = int16_t(sect2 - sections);
sectionLines[splitline2].partnersection = int16_t(sect1 - sections);
sectionLines[splitline2].startpoint = end;
sectionLines[splitline2].endpoint = start;
sectionLines[splitline2].point2index = splitline2 + 1 - firstnewline;
}
}
sectionLines[splitline1].partner = splitline2;
sectionLines[splitline2].partner = splitline1;
sectionspersector[sect.sector].Resize(2);
sectionspersector[sect.sector][0] = int16_t(sect1 - sections);
sectionspersector[sect.sector][1] = int16_t(sect2 - sections);
}
void hw_SplitSector(int sectnum, int start, int end)
{
int wallstart = sector[sectnum].wallptr;
int wallend = wallstart + sector[sectnum].wallnum;
if (start < wallstart || start >= wallend || end < wallstart || end >= wallend || end < start) return;
for (unsigned i = 0; i < sectionspersector[sectnum].Size(); i++)
{
int sect = sectionspersector[sectnum][i];
bool foundstart = false, foundend = false;
for (int aline : sections[sect].lines)
{
int line = sectionLines[aline].wall;
if (line == start) foundstart = true;
if (line == end) foundend = true;
}
if (foundstart && foundend)
{
sectionspersector->Delete(i);
SplitSection(sect, start, end);
return;
}
}
}
void hw_SetSplitSector(int sectnum, int start, int end)
{
splits.Push(sectnum);
splits.Push(start);
splits.Push(end);
}

View file

@ -0,0 +1,32 @@
#pragma once
#include "build.h"
struct SectionLine
{
int16_t section;
int16_t partnersection;
int16_t startpoint;
int16_t endpoint;
int16_t wall;
int16_t partner;
int16_t point2index;
};
struct Section
{
int sector;
// this is the whole point of sections - instead of just having a start index and count, we have an explicit list of lines that's a lot easier to change when needed.
TArray<int16_t> lines;
};
// giving 25% more may be a bit high as normally this should be small numbers only.
extern SectionLine sectionLines[MAXWALLS + (MAXWALLS >> 2)];
extern Section sections[MAXSECTORS + (MAXSECTORS >> 2)];
extern TArray<int> sectionspersector[MAXSECTORS]; // reverse map, mainly for the automap
extern int numsections;
extern int numsectionlines;
void hw_BuildSections();
void hw_SetSplitSector(int sector, int startpos, int endpos);

View file

@ -0,0 +1,114 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2021 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 2 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/
//
//--------------------------------------------------------------------------
//
/*
** hw_voxels.cpp
**
** voxel handling.
**
**/
#include "build.h"
#include "voxels.h"
#include "hw_voxels.h"
#include "gamecontrol.h"
int16_t tiletovox[MAXTILES];
static int voxlumps[MAXVOXELS];
float voxscale[MAXVOXELS];
voxmodel_t* voxmodels[MAXVOXELS];
FixedBitArray<MAXVOXELS> voxrotate;
void voxInit()
{
for (auto& v : tiletovox) v = -1;
for (auto& v : voxscale) v = 1.f;
voxrotate.Zero();
}
void voxClear()
{
for (auto& vox : voxmodels)
{
if (vox) delete vox;
vox = nullptr;
}
}
int voxDefine(int voxindex, const char* filename)
{
if ((unsigned)voxindex >= MAXVOXELS)
return -1;
int i = fileSystem.FindFile(filename);
voxlumps[voxindex] = i;
return i < 0 ? -1 : 0;
}
static voxmodel_t* voxload(int lumpnum)
{
FVoxel* voxel = R_LoadKVX(lumpnum);
if (voxel != nullptr)
{
voxmodel_t* vm = new voxmodel_t;
*vm = {};
auto pivot = voxel->Mips[0].Pivot;
vm->mdnum = 1; //VOXel model id
vm->scale = vm->bscale = 1.f;
vm->piv.x = float(pivot.X);
vm->piv.y = float(pivot.Y);
vm->piv.z = float(pivot.Z);
vm->siz.x = voxel->Mips[0].SizeX;
vm->siz.y = voxel->Mips[0].SizeY;
vm->siz.z = voxel->Mips[0].SizeZ;
vm->is8bit = true;
voxel->Mips[0].Pivot.Zero(); // Needs to be taken out of the voxel data because it gets baked into the vertex buffer which we cannot use here.
vm->model = new FVoxelModel(voxel, true);
return vm;
}
return nullptr;
}
void LoadVoxelModels()
{
for (int i = 0; i < MAXVOXELS; i++)
{
int lumpnum = voxlumps[i];
if (lumpnum > 0)
{
voxmodels[i] = voxload(lumpnum);
if (voxmodels[i])
voxmodels[i]->scale = voxscale[i];
else
Printf("Unable to load voxel from %s\n", fileSystem.GetFileFullPath(lumpnum).GetChars());
}
else
{
auto index = fileSystem.FindResource(i, "KVX");
if (index >= 0)
{
voxmodels[i] = voxload(index);
}
}
}
}

View file

@ -0,0 +1,24 @@
#pragma once
#include <stdint.h>
#include "mdsprite.h"
// We still need the relation to mdmodel_t as long as the model code hasn't been redone.
struct voxmodel_t : public mdmodel_t
{
FVoxelModel* model = nullptr;
vec3_t siz;
vec3f_t piv;
int32_t is8bit;
};
extern int16_t tiletovox[];
extern float voxscale[];
extern voxmodel_t* voxmodels[MAXVOXELS];
extern FixedBitArray<MAXVOXELS> voxrotate;
void voxInit();
void voxClear();
int voxDefine(int voxindex, const char* filename);

View file

@ -0,0 +1,78 @@
#pragma once
#include "build.h"
class FSerializer;
struct IntRect;
void render_drawrooms(spritetype* playersprite, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang, double smoothratio);
void render_camtex(spritetype* playersprite, const vec3_t& position, int sectnum, binangle angle, fixedhoriz horizon, binangle rollang, FGameTexture* camtex, IntRect& rect, double smoothratio);
struct PortalDesc
{
int type;
int dx, dy, dz;
TArray<int> targets;
};
FSerializer& Serialize(FSerializer& arc, const char* key, PortalDesc& obj, PortalDesc* defval);
extern TArray<PortalDesc> allPortals;
inline void portalClear()
{
allPortals.Clear();
}
inline int portalAdd(int type, int target, int dx = 0, int dy = 0, int dz = 0)
{
auto& pt = allPortals[allPortals.Reserve(1)];
pt.type = type;
if (target >= 0) pt.targets.Push(target);
pt.dx = dx;
pt.dy = dy;
pt.dz = dz;
return allPortals.Size() - 1;
}
// merges portals in adjoining sectors.
inline void mergePortals()
{
//Printf("Have %d portals\n", allPortals.Size());
bool didsomething = true;
while (didsomething)
{
didsomething = false;
for (unsigned i = 0; i < allPortals.Size(); i++)
{
auto& pt1 = allPortals[i];
if (pt1.type == PORTAL_SECTOR_CEILING || pt1.type == PORTAL_SECTOR_FLOOR)
{
for (unsigned j = i + 1; j < allPortals.Size(); j++)
{
auto& pt2 = allPortals[j];
if (pt1.type != pt2.type || pt1.dx != pt2.dx || pt1.dy != pt2.dy || pt1.dz != pt2.dz) continue;
for (unsigned s = 0; s < pt1.targets.Size() && pt2.targets.Size(); s++)
{
for (unsigned t = 0; t < pt2.targets.Size(); t++)
{
if (findwallbetweensectors(pt1.targets[s], pt2.targets[t]) >= 0)
{
pt1.targets.Append(pt2.targets);
pt2.targets.Reset();
pt2.type = -1;
for (int n = 0; n < numsectors; n++)
{
//Printf("Merged %d and %d\n", i, j);
if (sector[n].portalnum == j) sector[n].portalnum = i;
}
didsomething = true;
break;
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,633 @@
/*
** hw_bunchdrawer.cpp
**
**---------------------------------------------------------------------------
** Copyright 2008-2021 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 "hw_drawinfo.h"
#include "hw_bunchdrawer.h"
#include "hw_clipper.h"
#include "hw_clock.h"
#include "hw_drawstructs.h"
#include "automap.h"
#include "gamefuncs.h"
#include "hw_portal.h"
#include "gamestruct.h"
#include "hw_voxels.h"
#include "mapinfo.h"
#include "gamecontrol.h"
#include "hw_sections.h"
extern TArray<int> blockingpairs[MAXWALLS];
//==========================================================================
//
//
//
//==========================================================================
void BunchDrawer::Init(HWDrawInfo *_di, Clipper* c, vec2_t& view, binangle a1, binangle a2)
{
ang1 = a1;
ang2 = a2;
di = _di;
clipper = c;
viewx = view.x * (1/ 16.f);
viewy = view.y * -(1/ 16.f);
iview = view;
StartScene();
clipper->SetViewpoint(view);
gcosang = bamang(di->Viewpoint.RotAngle).fcos();
gsinang = bamang(di->Viewpoint.RotAngle).fsin();
for (int i = 0; i < numwalls; i++)
{
// Precalculate the clip angles to avoid doing this repeatedly during level traversal.
// Reverse the orientation so that startangle and endangle are properly ordered.
wall[i].clipangle = clipper->PointToAngle(wall[i].pos);
}
memset(sectionstartang, -1, sizeof(sectionstartang));
memset(sectionendang, -1, sizeof(sectionendang));
}
//==========================================================================
//
//
//
//==========================================================================
void BunchDrawer::StartScene()
{
LastBunch = 0;
StartTime = I_msTime();
Bunches.Clear();
CompareData.Clear();
gotsector.Zero();
gotsection2.Zero();
gotwall.Zero();
blockwall.Zero();
}
//==========================================================================
//
//
//
//==========================================================================
bool BunchDrawer::StartBunch(int sectnum, int linenum, binangle startan, binangle endan, bool portal)
{
FBunch* bunch = &Bunches[LastBunch = Bunches.Reserve(1)];
bunch->sectnum = sectnum;
bunch->startline = bunch->endline = linenum;
bunch->startangle = (startan.asbam() - ang1.asbam()) > ANGLE_180? ang1 :startan;
bunch->endangle = (endan.asbam() - ang2.asbam()) < ANGLE_180 ? ang2 : endan;
bunch->portal = portal;
return bunch->endangle != ang2;
}
//==========================================================================
//
//
//
//==========================================================================
bool BunchDrawer::AddLineToBunch(int line, binangle newan)
{
Bunches[LastBunch].endline++;
Bunches[LastBunch].endangle = (newan.asbam() - ang2.asbam()) < ANGLE_180 ? ang2 : newan;
return Bunches[LastBunch].endangle != ang2;
}
//==========================================================================
//
//
//
//==========================================================================
void BunchDrawer::DeleteBunch(int index)
{
Bunches[index] = Bunches.Last();
Bunches.Pop();
}
bool BunchDrawer::CheckClip(walltype* wal)
{
auto pt2 = &wall[wal->point2];
sectortype* backsector = &sector[wal->nextsector];
sectortype* frontsector = &sector[wall[wal->nextwall].nextsector];
// if one plane is sky on both sides, the line must not clip.
if (frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY) return false;
if (frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY) return false;
float bs_floorheight1;
float bs_floorheight2;
float bs_ceilingheight1;
float bs_ceilingheight2;
float fs_floorheight1;
float fs_floorheight2;
float fs_ceilingheight1;
float fs_ceilingheight2;
// Mirrors and horizons always block the view
//if (linedef->special==Line_Mirror || linedef->special==Line_Horizon) return true;
PlanesAtPoint(frontsector, wal->x, wal->y, &fs_ceilingheight1, &fs_floorheight1);
PlanesAtPoint(frontsector, pt2->x, pt2->y, &fs_ceilingheight2, &fs_floorheight2);
PlanesAtPoint(backsector, wal->x, wal->y, &bs_ceilingheight1, &bs_floorheight1);
PlanesAtPoint(backsector, pt2->x, pt2->y, &bs_ceilingheight2, &bs_floorheight2);
// now check for closed sectors! No idea if we really need the sky checks. We'll see.
if (bs_ceilingheight1 <= fs_floorheight1 && bs_ceilingheight2 <= fs_floorheight2)
{
// backsector's ceiling is below frontsector's floor.
return true;
}
if (fs_ceilingheight1 <= bs_floorheight1 && fs_ceilingheight2 <= bs_floorheight2)
{
// backsector's floor is above frontsector's ceiling
return true;
}
if (bs_ceilingheight1 <= bs_floorheight1 && bs_ceilingheight2 <= bs_floorheight2)
{
// backsector is closed
return true;
}
return false;
}
//==========================================================================
//
// ClipLine
// Clips the given segment
//
//==========================================================================
int BunchDrawer::ClipLine(int aline, bool portal)
{
auto cline = &sectionLines[aline];
int section = cline->section;
int line = cline->wall;
auto startAngleBam = wall[cline->startpoint].clipangle;
auto endAngleBam = wall[cline->endpoint].clipangle;
// Back side, i.e. backface culling - read: endAngle <= startAngle!
if (startAngleBam.asbam() - endAngleBam.asbam() < ANGLE_180)
{
return CL_Skip;
}
if (line >= 0 && blockwall[line]) return CL_Draw;
// convert to clipper coordinates and clamp to valid range.
int startAngle = startAngleBam.asbam() - ang1.asbam();
int endAngle = endAngleBam.asbam() - ang1.asbam();
if (startAngle < 0) startAngle = 0;
if (endAngle < 0) endAngle = INT_MAX;
// since these values are derived from previous calls of this function they cannot be out of range.
int sectStartAngle = sectionstartang[section];
auto sectEndAngle = sectionendang[section];
// check against the maximum possible viewing range of the sector.
// Todo: check if this is sufficient or if we really have to do a more costly check against the single visible segments.
if (sectStartAngle != -1)
{
if (sectStartAngle > endAngle || sectEndAngle < startAngle)
return CL_Skip; // completely outside the valid range for this sector.
if (sectStartAngle > startAngle) startAngle = sectStartAngle;
if (sectEndAngle < endAngle) endAngle = sectEndAngle;
if (endAngle <= startAngle) return CL_Skip; // can this even happen?
}
if (!portal && !clipper->IsRangeVisible(startAngle, endAngle))
{
return CL_Skip;
}
auto wal = &wall[line];
if (cline->partner == -1 || (wal->cstat & CSTAT_WALL_1WAY) || CheckClip(wal))
{
// one-sided
if (!portal) clipper->AddClipRange(startAngle, endAngle);
return CL_Draw;
}
else
{
if (portal) clipper->RemoveClipRange(startAngle, endAngle);
// set potentially visible viewing range for this line's back sector.
int nsection = cline->partnersection;
if (sectionstartang[nsection] == -1)
{
sectionstartang[nsection] = startAngle;
sectionendang[nsection] = endAngle;
}
else
{
if (startAngle < sectionstartang[nsection]) sectionstartang[nsection] = startAngle;
if (endAngle > sectionendang[nsection]) sectionendang[nsection] = endAngle;
}
return CL_Draw | CL_Pass;
}
}
//==========================================================================
//
//
//
//==========================================================================
void BunchDrawer::ProcessBunch(int bnch)
{
FBunch* bunch = &Bunches[bnch];
ClipWall.Clock();
for (int i = bunch->startline; i <= bunch->endline; i++)
{
int clipped = ClipLine(i, bunch->portal);
if (clipped & CL_Draw)
{
int ww = sectionLines[i].wall;
if (ww != -1)
{
for (auto p : blockingpairs[ww]) blockwall.Set(sectionLines[p].wall);
show2dwall.Set(ww);
if (!gotwall[i])
{
gotwall.Set(i);
ClipWall.Unclock();
Bsp.Unclock();
SetupWall.Clock();
HWWall hwwall;
hwwall.Process(di, &wall[ww], &sector[bunch->sectnum], wall[ww].nextsector < 0 ? nullptr : &sector[wall[ww].nextsector]);
rendered_lines++;
SetupWall.Unclock();
Bsp.Clock();
ClipWall.Clock();
}
}
}
if (clipped & CL_Pass)
{
ClipWall.Unclock();
ProcessSection(sectionLines[i].partnersection, false);
ClipWall.Clock();
}
}
ClipWall.Unclock();
}
//==========================================================================
//
//
//
//==========================================================================
int BunchDrawer::WallInFront(int line1, int line2)
{
int wall1s = sectionLines[line1].startpoint;
int wall1e = sectionLines[line1].endpoint;
int wall2s = sectionLines[line2].startpoint;
int wall2e = sectionLines[line2].endpoint;
double x1s = WallStartX(wall1s);
double y1s = WallStartY(wall1s);
double x1e = WallStartX(wall1e);
double y1e = WallStartY(wall1e);
double x2s = WallStartX(wall2s);
double y2s = WallStartY(wall2s);
double x2e = WallStartX(wall2e);
double y2e = WallStartY(wall2e);
double dx = x1e - x1s;
double dy = y1e - y1s;
double t1 = PointOnLineSide(x2s, y2s, x1s, y1s, dx, dy);
double t2 = PointOnLineSide(x2e, y2e, x1s, y1s, dx, dy);
if (t1 == 0)
{
if (t2 == 0) return(-1);
t1 = t2;
}
if (t2 == 0) t2 = t1;
if ((t1 * t2) >= 0)
{
t2 = PointOnLineSide(viewx, viewy, x1s, y1s, dx, dy);
return((t2 * t1) <= 0);
}
dx = x2e - x2s;
dy = y2e - y2s;
t1 = PointOnLineSide(x1s, y1s, x2s, y2s, dx, dy);
t2 = PointOnLineSide(x1e, y1e, x2s, y2s, dx, dy);
if (t1 == 0)
{
if (t2 == 0) return(-1);
t1 = t2;
}
if (t2 == 0) t2 = t1;
if ((t1 * t2) >= 0)
{
t2 = PointOnLineSide(viewx, viewy, x2s, y2s, dx, dy);
return((t2 * t1) > 0);
}
return(-2);
}
//==========================================================================
//
// This is a bit more complicated than it looks because angles can wrap
// around so we can only compare angle differences.
//
// Rules:
// 1. Any bunch can span at most 180°.
// 2. 2 bunches can never overlap at both ends
// 3. if there is an overlap one of the 2 starting points must be in the
// overlapping area.
//
//==========================================================================
int BunchDrawer::BunchInFront(FBunch* b1, FBunch* b2)
{
binangle anglecheck, endang;
if (b2->startangle.asbam() - b1->startangle.asbam() < b1->endangle.asbam() - b1->startangle.asbam())
{
// we have an overlap at b2->startangle
anglecheck = b2->startangle - b1->startangle;
// Find the wall in b1 that overlaps b2->startangle
for (int i = b1->startline; i <= b1->endline; i++)
{
endang = wall[wall[i].point2].clipangle - b1->startangle;
if (endang.asbam() > anglecheck.asbam())
{
// found a line
int ret = WallInFront(b2->startline, i);
return ret;
}
}
}
else if (b1->startangle.asbam() - b2->startangle.asbam() < b2->endangle.asbam() - b2->startangle.asbam())
{
// we have an overlap at b1->startangle
anglecheck = b1->startangle - b2->startangle;
// Find the wall in b2 that overlaps b1->startangle
for (int i = b2->startline; i <= b2->endline; i++)
{
endang = wall[wall[i].point2].clipangle - b2->startangle;
if (endang.asbam() > anglecheck.asbam())
{
// found a line
int ret = WallInFront(i, b1->startline);
return ret;
}
}
}
// we have no overlap
return -1;
}
//==========================================================================
//
//
//
//==========================================================================
int BunchDrawer::FindClosestBunch()
{
int closest = 0; //Almost works, but not quite :(
CompareData.Clear();
for (unsigned i = 1; i < Bunches.Size(); i++)
{
switch (BunchInFront(&Bunches[i], &Bunches[closest]))
{
case 0: // i is in front
closest = i;
continue;
case 1: // i is behind
continue;
default: // can't determine
CompareData.Push(i); // mark for later comparison
continue;
}
}
// we need to do a second pass to see how the marked bunches relate to the currently closest one.
for (unsigned i = 0; i < CompareData.Size(); i++)
{
switch (BunchInFront(&Bunches[CompareData[i]], &Bunches[closest]))
{
case 0: // is in front
closest = CompareData[i];
CompareData[i] = CompareData.Last();
CompareData.Pop();
i = -1; // we need to recheck everything that's still marked. -1 because this will get incremented before being used.
continue;
case 1: // is behind
CompareData[i] = CompareData.Last();
CompareData.Pop();
i--;
continue;
default:
continue;
}
}
//Printf("picked bunch starting at %d\n", Bunches[closest].startline);
return closest;
}
//==========================================================================
//
//
//
//==========================================================================
void BunchDrawer::ProcessSection(int sectionnum, bool portal)
{
if (gotsection2[sectionnum]) return;
gotsection2.Set(sectionnum);
bool inbunch;
binangle startangle;
SetupSprite.Clock();
int z;
int sectnum = sections[sectionnum].sector;
if (!gotsector[sectnum])
{
gotsector.Set(sectnum);
SectIterator it(sectnum);
while ((z = it.NextIndex()) >= 0)
{
auto const spr = (uspriteptr_t)&sprite[z];
if ((spr->cstat & CSTAT_SPRITE_INVISIBLE) || spr->xrepeat == 0 || spr->yrepeat == 0) // skip invisible sprites
continue;
int sx = spr->x - iview.x, sy = spr->y - int(iview.y);
// this checks if the sprite is it behind the camera, which will not work if the pitch is high enough to necessitate a FOV of more than 180°.
//if ((spr->cstat & CSTAT_SPRITE_ALIGNMENT_MASK) || (hw_models && tile2model[spr->picnum].modelid >= 0) || ((sx * gcosang) + (sy * gsinang) > 0))
{
if ((spr->cstat & (CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_ALIGNMENT_MASK)) != (CSTAT_SPRITE_ONE_SIDED | CSTAT_SPRITE_ALIGNMENT_WALL) ||
(r_voxels && tiletovox[spr->picnum] >= 0 && voxmodels[tiletovox[spr->picnum]]) ||
(r_voxels && gi->Voxelize(spr->picnum) > -1) ||
DMulScale(bcos(spr->ang), -sx, bsin(spr->ang), -sy, 6) > 0)
if (renderAddTsprite(di->tsprite, di->spritesortcnt, z, sectnum))
break;
}
}
SetupSprite.Unclock();
}
if (automapping)
show2dsector.Set(sectnum);
SetupFlat.Clock();
HWFlat flat;
flat.ProcessSector(di, &sector[sectnum], sectionnum);
SetupFlat.Unclock();
//Todo: process subsectors
inbunch = false;
auto section = &sections[sectionnum];
for (unsigned i = 0; i < section->lines.Size(); i++)
{
auto thisline = &sectionLines[section->lines[i]];
#ifdef _DEBUG
// For displaying positions in debugger
//DVector2 start = { WallStartX(thiswall), WallStartY(thiswall) };
//DVector2 end = { WallStartX(thiswall->point2), WallStartY(thiswall->point2) };
#endif
binangle walang1 = wall[thisline->startpoint].clipangle;
binangle walang2 = wall[thisline->endpoint].clipangle;
// outside the visible area or seen from the backside.
if ((walang1.asbam() - ang1.asbam() > ANGLE_180 && walang2.asbam() - ang1.asbam() > ANGLE_180) ||
(walang1.asbam() - ang2.asbam() < ANGLE_180 && walang2.asbam() - ang2.asbam() < ANGLE_180) ||
(walang1.asbam() - walang2.asbam() < ANGLE_180))
{
inbunch = false;
}
else if (!inbunch)
{
startangle = walang1;
//Printf("Starting bunch:\n\tWall %d\n", sect->wallptr + i);
inbunch = StartBunch(sectnum, section->lines[i], walang1, walang2, portal);
}
else
{
//Printf("\tWall %d\n", sect->wallptr + i);
inbunch = AddLineToBunch(section->lines[i], walang2);
}
if (thisline->endpoint != section->lines[i] + 1) inbunch = false;
}
}
//==========================================================================
//
//
//
//==========================================================================
void BunchDrawer::RenderScene(const int* viewsectors, unsigned sectcount, bool portal)
{
//Printf("----------------------------------------- \nstart at sector %d\n", viewsectors[0]);
auto process = [&]()
{
clipper->Clear(ang1);
for (unsigned i = 0; i < sectcount; i++)
{
for (auto j : sectionspersector[viewsectors[i]])
{
sectionstartang[j] = 0;
sectionendang[j] = int(ang2.asbam() - ang1.asbam());
}
}
for (unsigned i = 0; i < sectcount; i++)
{
for (auto j : sectionspersector[viewsectors[i]])
{
ProcessSection(j, portal);
}
}
while (Bunches.Size() > 0)
{
int closest = FindClosestBunch();
ProcessBunch(closest);
DeleteBunch(closest);
}
};
Bsp.Clock();
if (ang1.asbam() != 0 || ang2.asbam() != 0)
{
process();
}
else
{
// with a 360° field of view we need to split the scene into two halves.
// The BunchInFront check can fail with angles that may wrap around.
auto rotang = di->Viewpoint.RotAngle;
ang1 = bamang(rotang - ANGLE_90);
ang2 = bamang(rotang + ANGLE_90 - 1);
process();
gotsection2.Zero();
ang1 = bamang(rotang + ANGLE_90);
ang2 = bamang(rotang - ANGLE_90 - 1);
process();
}
Bsp.Unclock();
}

View file

@ -0,0 +1,63 @@
#pragma once
#include "tarray.h"
#include "basics.h"
struct HWDrawInfo;
class Clipper;
struct FBunch
{
int sectnum;
int startline;
int endline;
bool portal;
binangle startangle;
binangle endangle;
};
class BunchDrawer
{
HWDrawInfo *di;
Clipper *clipper;
int LastBunch;
int StartTime;
TArray<FBunch> Bunches;
TArray<int> CompareData;
double viewx, viewy;
vec2_t iview;
float gcosang, gsinang;
FixedBitArray<MAXSECTORS> gotsector;
FixedBitArray<MAXSECTORS*5/4> gotsection2;
FixedBitArray<MAXWALLS> gotwall;
FixedBitArray<MAXWALLS> blockwall;
binangle ang1, ang2;
int sectionstartang[MAXSECTORS*5/4], sectionendang[MAXSECTORS*5/4];
private:
enum
{
CL_Skip = 0,
CL_Draw = 1,
CL_Pass = 2,
};
void StartScene();
bool StartBunch(int sectnum, int linenum, binangle startan, binangle endan, bool portal);
bool AddLineToBunch(int line, binangle newan);
void DeleteBunch(int index);
bool CheckClip(walltype* wal);
int ClipLine(int line, bool portal);
void ProcessBunch(int bnch);
int WallInFront(int wall1, int wall2);
int BunchInFront(FBunch* b1, FBunch* b2);
int FindClosestBunch();
void ProcessSection(int sectnum, bool portal);
public:
void Init(HWDrawInfo* _di, Clipper* c, vec2_t& view, binangle a1, binangle a2);
void RenderScene(const int* viewsectors, unsigned sectcount, bool portal);
const FixedBitArray<MAXSECTORS>& GotSector() const { return gotsector; }
};

View file

@ -0,0 +1,294 @@
/*
*
** gl_clipper.cpp
**
** Handles visibility checks.
** Loosely based on the JDoom clipper.
**
**---------------------------------------------------------------------------
** Copyright 2003 Tim Stump
** 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 "hw_clipper.h"
#include "basics.h"
#include "build.h"
#include "printf.h"
//-----------------------------------------------------------------------------
//
// RemoveRange
//
//-----------------------------------------------------------------------------
void Clipper::RemoveRange(ClipNode * range)
{
if (range == cliphead)
{
cliphead = cliphead->next;
}
else
{
if (range->prev) range->prev->next = range->next;
if (range->next) range->next->prev = range->prev;
}
Free(range);
}
//-----------------------------------------------------------------------------
//
// Clear
//
//-----------------------------------------------------------------------------
void Clipper::Clear(binangle rangestart)
{
ClipNode *node = cliphead;
ClipNode *temp;
while (node != nullptr)
{
temp = node;
node = node->next;
Free(temp);
}
cliphead = nullptr;
if (visibleStart.asbam() != 0 || visibleEnd.asbam() != 0)
{
int vstart = int(visibleStart.asbam() - rangestart.asbam());
if (vstart > 1) AddClipRange(0, vstart - 1);
int vend = int(visibleEnd.asbam() - rangestart.asbam());
if (vend > 0 && vend < INT_MAX - 1) AddClipRange(vend + 1, INT_MAX);
}
}
//-----------------------------------------------------------------------------
//
// IsRangeVisible
//
//-----------------------------------------------------------------------------
bool Clipper::IsRangeVisible(int startAngle, int endAngle)
{
ClipNode *ci;
ci = cliphead;
if (endAngle == 0 && ci && ci->start==0) return false;
while (ci != nullptr && ci->start < endAngle)
{
if (startAngle >= ci->start && endAngle <= ci->end)
{
return false;
}
ci = ci->next;
}
return true;
}
//-----------------------------------------------------------------------------
//
// AddClipRange
//
//-----------------------------------------------------------------------------
void Clipper::AddClipRange(int start, int end)
{
ClipNode *node, *temp, *prevNode;
if (cliphead)
{
//check to see if range contains any old ranges
node = cliphead;
while (node != nullptr && node->start < end)
{
if (node->start >= start && node->end <= end)
{
temp = node;
node = node->next;
RemoveRange(temp);
}
else if (node->start<=start && node->end>=end)
{
return;
}
else
{
node = node->next;
}
}
//check to see if range overlaps a range (or possibly 2)
node = cliphead;
while (node != nullptr && node->start <= end)
{
if (node->end >= start)
{
// we found the first overlapping node
if (node->start > start)
{
// the new range overlaps with this node's start point
node->start = start;
}
if (node->end < end)
{
node->end = end;
}
ClipNode *node2 = node->next;
while (node2 && node2->start <= node->end)
{
if (node2->end > node->end) node->end = node2->end;
ClipNode *delnode = node2;
node2 = node2->next;
RemoveRange(delnode);
}
return;
}
node = node->next;
}
//just add range
node = cliphead;
prevNode = nullptr;
temp = NewRange(start, end);
while (node != nullptr && node->start < end)
{
prevNode = node;
node = node->next;
}
temp->next = node;
if (node == nullptr)
{
temp->prev = prevNode;
if (prevNode) prevNode->next = temp;
if (!cliphead) cliphead = temp;
}
else
{
if (node == cliphead)
{
cliphead->prev = temp;
cliphead = temp;
}
else
{
temp->prev = prevNode;
prevNode->next = temp;
node->prev = temp;
}
}
}
else
{
temp = NewRange(start, end);
cliphead = temp;
return;
}
}
//-----------------------------------------------------------------------------
//
// RemoveClipRange
//
//-----------------------------------------------------------------------------
void Clipper::RemoveClipRange(int start, int end)
{
ClipNode *node, *temp;
if (cliphead)
{
//check to see if range contains any old ranges
node = cliphead;
while (node != nullptr && node->start < end)
{
if (node->start >= start && node->end <= end)
{
temp = node;
node = node->next;
RemoveRange(temp);
}
else
{
node = node->next;
}
}
//check to see if range overlaps a range (or possibly 2)
node = cliphead;
while (node != nullptr)
{
if (node->start >= start && node->start <= end)
{
node->start = end;
break;
}
else if (node->end >= start && node->end <= end)
{
node->end=start;
}
else if (node->start < start && node->end > end)
{
temp = NewRange(end, node->end);
node->end=start;
temp->next=node->next;
temp->prev=node;
node->next=temp;
if (temp->next) temp->next->prev=temp;
break;
}
node = node->next;
}
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void Clipper::DumpClipper()
{
for (auto node = cliphead; node; node = node->next)
{
Printf("Range from %f to %f\n", bamang(node->start).asdeg(), bamang(node->end).asdeg());
}
}

View file

@ -0,0 +1,115 @@
#ifndef __GL_CLIPPER
#define __GL_CLIPPER
#include "xs_Float.h"
#include "memarena.h"
#include "basics.h"
#include "vectors.h"
#include "binaryangle.h"
#include "intvec.h"
class ClipNode
{
friend class Clipper;
ClipNode *prev, *next;
int start, end;
bool operator== (const ClipNode &other)
{
return other.start == start && other.end == end;
}
};
class Clipper
{
FMemArena nodearena;
ClipNode * freelist = nullptr;
ClipNode * clipnodes = nullptr;
ClipNode * cliphead = nullptr;
vec2_t viewpoint;
void RemoveRange(ClipNode* cn);
binangle visibleStart, visibleEnd;
public:
bool IsRangeVisible(int startangle, int endangle);
void AddClipRange(int startangle, int endangle);
void RemoveClipRange(int startangle, int endangle);
public:
void Clear(binangle rangestart);
void Free(ClipNode *node)
{
node->next = freelist;
freelist = node;
}
private:
ClipNode * GetNew()
{
if (freelist)
{
ClipNode * p = freelist;
freelist = p->next;
return p;
}
else return (ClipNode*)nodearena.Alloc(sizeof(ClipNode));
}
ClipNode * NewRange(int start, int end)
{
ClipNode * c = GetNew();
c->start = start;
c->end = end;
c->next = c->prev = NULL;
return c;
}
public:
void SetViewpoint(const vec2_t &vp)
{
viewpoint = vp;
}
void SetVisibleRange(angle_t a1, angle_t a2)
{
if (a2 != 0xffffffff)
{
visibleStart = bamang(a1 - a2);
visibleEnd = bamang(a1 + a2);
}
else visibleStart = visibleEnd = bamang(0);
}
void RestrictVisibleRange(binangle a1, binangle a2)
{
if (visibleStart == visibleEnd)
{
visibleStart = a1;
visibleEnd = a2;
}
else
{
if (a1.asbam() - visibleStart.asbam() < visibleEnd.asbam() - visibleStart.asbam()) visibleStart = a1;
if (a2.asbam() - visibleStart.asbam() < visibleEnd.asbam() - visibleStart.asbam()) visibleStart = a2;
}
}
void DumpClipper();
binangle PointToAngle(const vec2_t& pos)
{
vec2_t vec = pos - viewpoint;
return bvectangbam(vec.x, vec.y);
}
};
#endif

View file

@ -0,0 +1,734 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2000-2018 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 2 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/
//
//--------------------------------------------------------------------------
//
/*
** gl_drawinfo.cpp
** Basic scene draw info management class
**
*/
#include "hw_portal.h"
#include "build.h"
#include "hw_renderstate.h"
#include "hw_drawinfo.h"
//#include "models.h"
#include "hw_clock.h"
#include "hw_cvars.h"
#include "hw_viewpointbuffer.h"
#include "flatvertices.h"
#include "hw_lightbuffer.h"
#include "hw_vrmodes.h"
#include "hw_clipper.h"
#include "v_draw.h"
#include "gamecvars.h"
#include "gamestruct.h"
#include "automap.h"
#include "hw_voxels.h"
EXTERN_CVAR(Float, r_visibility)
CVAR(Bool, gl_no_skyclear, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, gl_texture, true, 0)
CVAR(Float, gl_mask_threshold, 0.5f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Float, gl_mask_sprite_threshold, 0.5f, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
FixedBitArray<MAXSECTORS> gotsector;
//==========================================================================
//
//
//
//==========================================================================
class FDrawInfoList
{
public:
TDeletingArray<HWDrawInfo *> mList;
HWDrawInfo * GetNew();
void Release(HWDrawInfo *);
};
FDrawInfoList di_list;
//==========================================================================
//
// Try to reuse the lists as often as possible as they contain resources that
// are expensive to create and delete.
//
// Note: If multithreading gets used, this class needs synchronization.
//
//==========================================================================
HWDrawInfo *FDrawInfoList::GetNew()
{
if (mList.Size() > 0)
{
HWDrawInfo *di;
mList.Pop(di);
return di;
}
return new HWDrawInfo();
}
void FDrawInfoList::Release(HWDrawInfo * di)
{
di->ClearBuffers();
mList.Push(di);
}
//==========================================================================
//
// Sets up a new drawinfo struct
//
//==========================================================================
HWDrawInfo *HWDrawInfo::StartDrawInfo(HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms)
{
HWDrawInfo *di = di_list.GetNew();
di->StartScene(parentvp, uniforms);
return di;
}
//==========================================================================
//
//
//
//==========================================================================
static Clipper staticClipper; // Since all scenes are processed sequentially we only need one clipper.
static HWDrawInfo * gl_drawinfo; // This is a linked list of all active DrawInfos and needed to free the memory arena after the last one goes out of scope.
void HWDrawInfo::StartScene(FRenderViewpoint& parentvp, HWViewpointUniforms* uniforms)
{
mClipper = &staticClipper;
Viewpoint = parentvp;
//lightmode = Level->lightMode;
if (uniforms)
{
VPUniforms = *uniforms;
// The clip planes will never be inherited from the parent drawinfo.
VPUniforms.mClipLine.X = -1000001.f;
VPUniforms.mClipHeight = 0;
}
else
{
VPUniforms.mProjectionMatrix.loadIdentity();
VPUniforms.mViewMatrix.loadIdentity();
VPUniforms.mNormalViewMatrix.loadIdentity();
//VPUniforms.mViewHeight = viewheight;
VPUniforms.mGlobVis = (2 / 65536.f) * g_visibility / r_ambientlight;
VPUniforms.mPalLightLevels = numshades | (static_cast<int>(gl_fogmode) << 8) | (5 << 16);
VPUniforms.mClipLine.X = -10000000.0f;
VPUniforms.mShadowmapFilter = gl_shadowmap_filter;
}
vec2_t view = { int(Viewpoint.Pos.X * 16), int(Viewpoint.Pos.Y * -16) };
mClipper->SetViewpoint(view);
ClearBuffers();
for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset();
vpIndex = 0;
// Fullbright information needs to be propagated from the main view.
if (outer != nullptr) FullbrightFlags = outer->FullbrightFlags;
else FullbrightFlags = 0;
outer = gl_drawinfo;
gl_drawinfo = this;
}
//==========================================================================
//
//
//
//==========================================================================
HWDrawInfo *HWDrawInfo::EndDrawInfo()
{
assert(this == gl_drawinfo);
for (int i = 0; i < GLDL_TYPES; i++) drawlists[i].Reset();
gl_drawinfo = outer;
di_list.Release(this);
if (gl_drawinfo == nullptr)
ResetRenderDataAllocator();
return gl_drawinfo;
}
//==========================================================================
//
//
//
//==========================================================================
void HWDrawInfo::ClearBuffers()
{
spriteindex = 0;
mClipPortal = nullptr;
mCurrentPortal = nullptr;
}
//-----------------------------------------------------------------------------
//
// R_FrustumAngle
//
//-----------------------------------------------------------------------------
angle_t HWDrawInfo::FrustumAngle()
{
float WidescreenRatio = 1.6666f; // fixme - this is a placeholder.
float tilt = fabs(Viewpoint.HWAngles.Pitch.Degrees);
// If the pitch is larger than this you can look all around at a FOV of 90°
if (tilt > 46.0f) return 0xffffffff;
// ok, this is a gross hack that barely works...
// but at least it doesn't overestimate too much...
double floatangle = 2.0 + (45.0 + ((tilt / 1.9)))*Viewpoint.FieldOfView.Degrees*48.0 / AspectMultiplier(WidescreenRatio) / 90.0;
angle_t a1 = DAngle(floatangle).BAMs();
if (a1 >= ANGLE_90) return 0xffffffff; // it's either below 90 or bust.
return a1;
}
//-----------------------------------------------------------------------------
//
// Setup the modelview matrix
//
//-----------------------------------------------------------------------------
void HWDrawInfo::SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror)
{
float mult = mirror ? -1.f : 1.f;
float planemult = planemirror ? -1 : 1;// Level->info->pixelstretch : Level->info->pixelstretch;
VPUniforms.mViewMatrix.loadIdentity();
VPUniforms.mViewMatrix.rotate(angles.Roll.Degrees, 0.0f, 0.0f, 1.0f);
VPUniforms.mViewMatrix.rotate(angles.Pitch.Degrees, 1.0f, 0.0f, 0.0f);
VPUniforms.mViewMatrix.rotate(angles.Yaw.Degrees, 0.0f, mult, 0.0f);
VPUniforms.mViewMatrix.translate(vx * mult, -vz * planemult, -vy);
VPUniforms.mViewMatrix.scale(-mult, planemult, 1);
}
//-----------------------------------------------------------------------------
//
// SetupView
// Setup the view rotation matrix for the given viewpoint
//
//-----------------------------------------------------------------------------
void HWDrawInfo::SetupView(FRenderState &state, float vx, float vy, float vz, bool mirror, bool planemirror)
{
auto &vp = Viewpoint;
//vp.SetViewAngle(r_viewwindow); // todo: need to pass in.
SetViewMatrix(vp.HWAngles, vx, vy, vz, mirror, planemirror);
SetCameraPos(vp.Pos);
VPUniforms.CalcDependencies();
vpIndex = screen->mViewpoints->SetViewpoint(state, &VPUniforms);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
HWPortal * HWDrawInfo::FindPortal(const void * src)
{
int i = Portals.Size() - 1;
while (i >= 0 && Portals[i] && Portals[i]->GetSource() != src) i--;
return i >= 0 ? Portals[i] : nullptr;
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
void HWDrawInfo::DispatchSprites()
{
for (int i = 0; i < spritesortcnt; i++)
{
auto tspr = &tsprite[i];
int tilenum = tspr->picnum;
int spritenum = tspr->owner;
if (spritenum < 0 || (unsigned)tilenum >= MAXTILES)
continue;
if (automapping == 1 && (unsigned)spritenum < MAXSPRITES)
show2dsprite.Set(spritenum);
setgotpic(tilenum);
if (!(spriteext[spritenum].flags & SPREXT_NOTMD))
{
int pt = Ptile2tile(tspr->picnum, tspr->pal);
if (hw_models && tile2model[pt].modelid >= 0 && tile2model[pt].framenum >= 0)
{
//HWSprite hwsprite;
//if (hwsprite.ProcessModel(pt, tspr)) continue;
}
if (r_voxels)
{
if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) != CSTAT_SPRITE_ALIGNMENT_SLAB && tiletovox[tspr->picnum] >= 0 && voxmodels[tiletovox[tspr->picnum]])
{
HWSprite hwsprite;
int num = tiletovox[tspr->picnum];
if (hwsprite.ProcessVoxel(this, voxmodels[tiletovox[tspr->picnum]], tspr, &sector[tspr->sectnum], voxrotate[num]))
continue;
}
else if ((tspr->cstat & CSTAT_SPRITE_ALIGNMENT) == CSTAT_SPRITE_ALIGNMENT_SLAB && tspr->picnum < MAXVOXELS && voxmodels[tspr->picnum])
{
HWSprite hwsprite;
int num = tspr->picnum;
hwsprite.ProcessVoxel(this, voxmodels[tspr->picnum], tspr, &sector[tspr->sectnum], voxrotate[num]);
continue;
}
}
}
if (spriteext[spritenum].flags & SPREXT_AWAY1)
{
tspr->pos.x += bcos(tspr->ang, -13);
tspr->pos.y += bsin(tspr->ang, -13);
}
else if (spriteext[spritenum].flags & SPREXT_AWAY2)
{
tspr->pos.x -= bcos(tspr->ang, -13);
tspr->pos.y -= bsin(tspr->ang, -13);
}
tileUpdatePicnum(&tilenum, sprite->owner + 32768, 0);
tspr->picnum = tilenum;
switch (tspr->cstat & CSTAT_SPRITE_ALIGNMENT)
{
case CSTAT_SPRITE_ALIGNMENT_FACING:
{
HWSprite sprite;
sprite.Process(this, tspr, &sector[tspr->sectnum], false);
break;
}
case CSTAT_SPRITE_ALIGNMENT_WALL:
{
HWWall wall;
wall.ProcessWallSprite(this, tspr, &sector[tspr->sectnum]);
break;
}
case CSTAT_SPRITE_ALIGNMENT_FLOOR:
{
HWFlat flat;
flat.ProcessFlatSprite(this, tspr, &sector[tspr->sectnum]);
break;
}
default:
break;
}
}
}
//-----------------------------------------------------------------------------
//
// CreateScene
//
// creates the draw lists for the current scene
//
//-----------------------------------------------------------------------------
void HWDrawInfo::CreateScene(bool portal)
{
const auto& vp = Viewpoint;
angle_t a1 = FrustumAngle();
// reset the portal manager
portalState.StartFrame();
ProcessAll.Clock();
// clip the scene and fill the drawlists
screen->mVertexData->Map();
screen->mLights->Map();
spritesortcnt = 0;
ingeo = false;
geoofs = { 0,0 };
vec2_t view = { int(vp.Pos.X * 16), int(vp.Pos.Y * -16) };
if(!portal) mClipper->SetVisibleRange(vp.RotAngle, a1);
if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1));
else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0));
if (vp.SectNums)
mDrawer.RenderScene(vp.SectNums, vp.SectCount, portal);
else
mDrawer.RenderScene(&vp.SectCount, 1, portal);
SetupSprite.Clock();
gi->processSprites(tsprite, spritesortcnt, view.x, view.y, vp.Pos.Z * -256, bamang(vp.RotAngle), vp.TicFrac * 65536);
DispatchSprites();
SetupSprite.Unclock();
GeoEffect eff;
int effsect = vp.SectNums ? vp.SectNums[0] : vp.SectCount;
int drawsect = effsect;
// RR geometry hack. Ugh...
// This just adds to the existing render list, so we must offset the effect areas to the same xy-space as the main one as we cannot change the view matrix.
if (gi->GetGeoEffect(&eff, effsect))
{
ingeo = true;
geoofs = { (float)eff.geox[0], (float)eff.geoy[0] };
// process the first layer.
for (int i = 0; i < eff.geocnt; i++)
{
auto sect = &sector[eff.geosectorwarp[i]];
for (auto w = 0; w < sect->wallnum; w++)
{
auto wal = &wall[sect->wallptr + w];
wal->x += eff.geox[i];
wal->y += eff.geoy[i];
}
sect->dirty = 255;
if (eff.geosector[i] == effsect) drawsect = eff.geosectorwarp[i];
}
if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1));
else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0));
mDrawer.RenderScene(&drawsect, 1, false);
for (int i = 0; i < eff.geocnt; i++)
{
auto sect = &sector[eff.geosectorwarp[i]];
for (auto w = 0; w < sect->wallnum; w++)
{
auto wal = &wall[sect->wallptr + w];
wal->x -= eff.geox[i];
wal->y -= eff.geoy[i];
}
}
// Now the second layer. Same shit, different arrays.
geoofs = { (float)eff.geox2[0], (float)eff.geoy2[0] };
for (int i = 0; i < eff.geocnt; i++)
{
auto sect = &sector[eff.geosectorwarp2[i]];
for (auto w = 0; w < sect->wallnum; w++)
{
auto wal = &wall[sect->wallptr + w];
wal->x += eff.geox2[i];
wal->y += eff.geoy2[i];
}
sect->dirty = 255;
if (eff.geosector[i] == effsect) drawsect = eff.geosectorwarp2[i];
}
if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1));
else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0));
mDrawer.RenderScene(&drawsect, 1, false);
for (int i = 0; i < eff.geocnt; i++)
{
auto sect = &sector[eff.geosectorwarp2[i]];
for (auto w = 0; w < sect->wallnum; w++)
{
auto wal = &wall[sect->wallptr + w];
wal->x -= eff.geox2[i];
wal->y -= eff.geoy2[i];
}
}
ingeo = false;
}
screen->mLights->Unmap();
screen->mVertexData->Unmap();
ProcessAll.Unclock();
}
//-----------------------------------------------------------------------------
//
// RenderScene
//
// Draws the current draw lists for the non GLSL renderer
//
//-----------------------------------------------------------------------------
void HWDrawInfo::RenderScene(FRenderState &state)
{
const auto &vp = Viewpoint;
RenderAll.Clock();
state.SetDepthMask(true);
state.EnableFog(true);
state.SetRenderStyle(STYLE_Source);
// Part 1: solid geometry. This is set up so that there are no transparent parts
state.SetDepthFunc(DF_Less);
state.AlphaFunc(Alpha_GEqual, 0.f);
state.ClearDepthBias();
state.EnableTexture(gl_texture);
state.EnableBrightmap(true);
drawlists[GLDL_PLAINWALLS].DrawWalls(this, state, false);
drawlists[GLDL_PLAINFLATS].DrawFlats(this, state, false);
// Part 2: masked geometry. This is set up so that only pixels with alpha>gl_mask_threshold will show
state.AlphaFunc(Alpha_GEqual, gl_mask_threshold);
// This list is masked, non-translucent walls.
drawlists[GLDL_MASKEDWALLS].DrawWalls(this, state, false);
// These lists must be drawn in two passes for color and depth to avoid depth fighting with overlapping entries
drawlists[GLDL_MASKEDFLATS].SortFlats(this);
drawlists[GLDL_MASKEDWALLSV].SortWallsHorz(this);
drawlists[GLDL_MASKEDWALLSH].SortWallsVert(this);
state.SetDepthBias(-1, -128);
// these lists are only wall and floor sprites - often attached to walls and floors - so they need to be offset from the plane they may be attached to.
drawlists[GLDL_MASKEDWALLSS].DrawWalls(this, state, false);
// Each list must draw both its passes before the next one to ensure proper depth buffer contents.
state.SetDepthMask(false);
drawlists[GLDL_MASKEDWALLSV].DrawWalls(this, state, false);
state.SetDepthMask(true);
state.SetColorMask(false);
drawlists[GLDL_MASKEDWALLSV].DrawWalls(this, state, false);
state.SetColorMask(true);
state.SetDepthMask(false);
drawlists[GLDL_MASKEDWALLSH].DrawWalls(this, state, false);
state.SetDepthMask(true);
state.SetColorMask(false);
drawlists[GLDL_MASKEDWALLSH].DrawWalls(this, state, false);
state.SetColorMask(true);
state.SetDepthMask(false);
drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false);
state.SetDepthMask(true);
state.SetColorMask(false);
drawlists[GLDL_MASKEDFLATS].DrawFlats(this, state, false);
state.SetColorMask(true);
state.ClearDepthBias();
drawlists[GLDL_MODELS].Draw(this, state, false);
state.SetRenderStyle(STYLE_Translucent);
state.SetDepthFunc(DF_LEqual);
RenderAll.Unclock();
}
//-----------------------------------------------------------------------------
//
// RenderTranslucent
//
//-----------------------------------------------------------------------------
void HWDrawInfo::RenderTranslucent(FRenderState &state)
{
RenderAll.Clock();
state.SetDepthBias(-1, -128);
// final pass: translucent stuff
state.AlphaFunc(Alpha_GEqual, gl_mask_sprite_threshold);
state.SetRenderStyle(STYLE_Translucent);
state.EnableBrightmap(true);
drawlists[GLDL_TRANSLUCENTBORDER].Draw(this, state, true);
state.SetDepthMask(false);
drawlists[GLDL_TRANSLUCENT].DrawSorted(this, state);
state.EnableBrightmap(false);
state.ClearDepthBias();
state.AlphaFunc(Alpha_GEqual, 0.5f);
state.SetDepthMask(true);
RenderAll.Unclock();
}
//-----------------------------------------------------------------------------
//
// RenderTranslucent
//
//-----------------------------------------------------------------------------
void HWDrawInfo::RenderPortal(HWPortal *p, FRenderState &state, bool usestencil)
{
auto gp = static_cast<HWPortal *>(p);
gp->SetupStencil(this, state, usestencil);
auto new_di = StartDrawInfo(this, Viewpoint, &VPUniforms);
new_di->mCurrentPortal = gp;
state.SetLightIndex(-1);
gp->DrawContents(new_di, state);
new_di->EndDrawInfo();
state.SetVertexBuffer(screen->mVertexData);
screen->mViewpoints->Bind(state, vpIndex);
gp->RemoveStencil(this, state, usestencil);
}
//-----------------------------------------------------------------------------
//
// Draws player sprites and color blend
//
//-----------------------------------------------------------------------------
void HWDrawInfo::EndDrawScene(FRenderState &state)
{
state.EnableFog(false);
#if 0
// [BB] HUD models need to be rendered here.
const bool renderHUDModel = IsHUDModelForPlayerAvailable(players[consoleplayer].camera->player);
if (renderHUDModel)
{
// [BB] The HUD model should be drawn over everything else already drawn.
state.Clear(CT_Depth);
DrawPlayerSprites(true, state);
}
#endif
state.EnableStencil(false);
state.SetViewport(screen->mScreenViewport.left, screen->mScreenViewport.top, screen->mScreenViewport.width, screen->mScreenViewport.height);
// Restore standard rendering state
state.SetRenderStyle(STYLE_Translucent);
state.ResetColor();
state.EnableTexture(true);
state.SetScissor(0, 0, -1, -1);
}
//-----------------------------------------------------------------------------
//
// sets 3D viewport and initial state
//
//-----------------------------------------------------------------------------
void HWDrawInfo::Set3DViewport(FRenderState &state)
{
// Always clear all buffers with scissor test disabled.
// This is faster on newer hardware because it allows the GPU to skip
// reading from slower memory where the full buffers are stored.
state.SetScissor(0, 0, -1, -1);
state.Clear(CT_Color | CT_Depth | CT_Stencil);
const auto &bounds = screen->mSceneViewport;
state.SetViewport(bounds.left, bounds.top, bounds.width, bounds.height);
state.SetScissor(bounds.left, bounds.top, bounds.width, bounds.height);
state.EnableMultisampling(true);
state.EnableDepthTest(true);
state.EnableStencil(true);
state.SetStencil(0, SOP_Keep, SF_AllOn);
}
//-----------------------------------------------------------------------------
//
// gl_drawscene - this function renders the scene from the current
// viewpoint, including mirrors and skyboxes and other portals
// It is assumed that the HWPortal::EndFrame returns with the
// stencil, z-buffer and the projection matrix intact!
//
//-----------------------------------------------------------------------------
void HWDrawInfo::DrawScene(int drawmode, bool portal)
{
static int recursion = 0;
static int ssao_portals_available = 0;
const auto& vp = Viewpoint;
bool applySSAO = false;
if (drawmode == DM_MAINVIEW)
{
ssao_portals_available = gl_ssao_portals;
applySSAO = true;
}
else if (drawmode == DM_OFFSCREEN)
{
ssao_portals_available = 0;
}
else if (drawmode == DM_PORTAL && ssao_portals_available > 0)
{
applySSAO = (mCurrentPortal->AllowSSAO()/* || Level->flags3&LEVEL3_SKYBOXAO*/);
ssao_portals_available--;
}
CreateScene(portal);
auto& RenderState = *screen->RenderState();
RenderState.SetDepthMask(true);
if (!gl_no_skyclear) portalState.RenderFirstSkyPortal(recursion, this, RenderState);
RenderScene(RenderState);
if (applySSAO && RenderState.GetPassType() == GBUFFER_PASS)
{
screen->AmbientOccludeScene(VPUniforms.mProjectionMatrix.get()[5]);
screen->mViewpoints->Bind(RenderState, vpIndex);
}
// Handle all portals after rendering the opaque objects but before
// doing all translucent stuff
recursion++;
portalState.EndFrame(this, RenderState);
recursion--;
RenderTranslucent(RenderState);
}
//-----------------------------------------------------------------------------
//
// R_RenderView - renders one view - either the screen or a camera texture
//
//-----------------------------------------------------------------------------
void HWDrawInfo::ProcessScene(bool toscreen)
{
portalState.BeginScene();
DrawScene(toscreen ? DM_MAINVIEW : DM_OFFSCREEN, false);
if (toscreen && isBlood())
{
gotsector = mDrawer.GotSector(); // Blood needs this to implement some lighting effect hacks. Needs to be refactored to use better info.
}
}

View file

@ -0,0 +1,220 @@
#pragma once
#include <atomic>
#include <functional>
#include "build.h"
#include "vectors.h"
#include "hw_viewpointuniforms.h"
#include "v_video.h"
#include "hw_drawlist.h"
#include "hw_bunchdrawer.h"
//#include "r_viewpoint.h"
enum EDrawMode
{
DM_MAINVIEW,
DM_OFFSCREEN,
DM_PORTAL,
DM_SKYPORTAL
};
struct FSectorPortalGroup;
struct FFlatVertex;
class HWWall;
class HWFlat;
class HWSprite;
class IShadowMap;
struct FDynLightData;
class Clipper;
class HWPortal;
class FFlatVertexBuffer;
class IRenderQueue;
class HWScenePortalBase;
class FRenderState;
struct FRenderViewpoint
{
spritetype* CameraSprite;
DVector3 Pos;
FRotator HWAngles;
FAngle FieldOfView;
angle_t RotAngle;
int* SectNums;
int SectCount;
double TicFrac;
double TanCos, TanSin; // needed for calculating a sprite's screen depth.
DVector2 ViewVector; // direction the camera is facing.
};
//==========================================================================
//
// these are used to link faked planes due to missing textures to a sector
//
//==========================================================================
enum SectorRenderFlags
{
// This is used to merge several subsectors into a single draw item
SSRF_RENDERFLOOR = 1,
SSRF_RENDERCEILING = 2,
SSRF_RENDERALL = 7,
SSRF_PROCESSED = 8,
SSRF_SEEN = 16,
};
enum EPortalClip
{
PClip_InFront,
PClip_Inside,
PClip_Behind,
};
enum DrawListType
{
GLDL_PLAINWALLS,
GLDL_PLAINFLATS,
GLDL_MASKEDWALLS,
GLDL_MASKEDWALLSS, // arbitrary wall sprites.
GLDL_MASKEDWALLSV, // vertical wall sprites
GLDL_MASKEDWALLSH, // horizontal wall sprites. These two lists merely exist for easier sorting.
GLDL_MASKEDFLATS,
GLDL_MODELS,
GLDL_TRANSLUCENT,
GLDL_TRANSLUCENTBORDER,
GLDL_TYPES,
};
struct HWDrawInfo
{
struct wallseg
{
float x1, y1, z1, x2, y2, z2;
};
HWDrawList drawlists[GLDL_TYPES];
int vpIndex;
//ELightMode lightmode;
HWDrawInfo * outer = nullptr;
int FullbrightFlags;
std::atomic<int> spriteindex;
HWPortal *mClipPortal;
HWPortal *mCurrentPortal;
//FRotator mAngles;
BunchDrawer mDrawer;
Clipper *mClipper;
FRenderViewpoint Viewpoint;
HWViewpointUniforms VPUniforms; // per-viewpoint uniform state
TArray<HWPortal *> Portals;
spritetype tsprite[MAXSPRITESONSCREEN];
int spritesortcnt;
// This is needed by the BSP traverser.
bool multithread;
bool ingeo;
FVector2 geoofs;
private:
bool inview;
sectortype *currentsector;
void WorkerThread();
void UnclipSubsector(sectortype *sub);
void AddLine(walltype *seg, bool portalclip);
void AddLines(sectortype* sector);
void AddSpecialPortalLines(sectortype * sector, walltype* line);
public:
//void RenderThings(sectortype * sub, sectortype * sector);
//void RenderParticles(sectortype *sub, sectortype *front);
void SetColor(FRenderState &state, int sectorlightlevel, int rellight, bool fullbright, const FColormap &cm, float alpha, bool weapon = false);
public:
void SetCameraPos(const DVector3 &pos)
{
VPUniforms.mCameraPos = { (float)pos.X, (float)pos.Z, (float)pos.Y, 0.f };
}
void SetClipHeight(float h, float d)
{
VPUniforms.mClipHeight = h;
VPUniforms.mClipHeightDirection = d;
VPUniforms.mClipLine.X = -1000001.f;
}
void SetClipLine(walltype *line)
{
//VPUniforms.mClipLine = { (float)line->v1->fX(), (float)line->v1->fY(), (float)line->Delta().X, (float)line->Delta().Y };
VPUniforms.mClipHeight = 0;
}
HWPortal * FindPortal(const void * src);
static HWDrawInfo *StartDrawInfo(HWDrawInfo *parent, FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
void StartScene(FRenderViewpoint &parentvp, HWViewpointUniforms *uniforms);
void ClearBuffers();
HWDrawInfo *EndDrawInfo();
void DrawScene(int drawmode, bool portal);
void CreateScene(bool portal);
void DispatchSprites();
void RenderScene(FRenderState &state);
void RenderTranslucent(FRenderState &state);
void RenderPortal(HWPortal *p, FRenderState &state, bool usestencil);
void EndDrawScene(FRenderState &state);
void Set3DViewport(FRenderState &state);
void ProcessScene(bool toscreen);
//void GetDynSpriteLight(AActor *self, float x, float y, float z, FLightNode *node, int portalgroup, float *out);
//void GetDynSpriteLight(AActor *thing, particle_t *particle, float *out);
void SetViewMatrix(const FRotator &angles, float vx, float vy, float vz, bool mirror, bool planemirror);
void SetupView(FRenderState &state, float vx, float vy, float vz, bool mirror, bool planemirror);
angle_t FrustumAngle();
void DrawPlayerSprites(bool hudModelStep, FRenderState &state);
//void AddSubsectorToPortal(FSectorPortalGroup *portal, sectortype *sub);
void AddWall(HWWall *w);
void AddMirrorSurface(HWWall *w);
void AddFlat(HWFlat *flat);
void AddSprite(HWSprite *sprite, bool translucent);
bool isSoftwareLighting() const
{
return true;// lightmode == ELightMode::ZDoomSoftware || lightmode == ELightMode::DoomSoftware || lightmode == ELightMode::Build;
}
bool isBuildSoftwareLighting() const
{
return true;// lightmode == ELightMode::Build;
}
bool isDoomSoftwareLighting() const
{
return false;// lightmode == ELightMode::ZDoomSoftware || lightmode == ELightMode::DoomSoftware;
}
bool isDarkLightMode() const
{
return false;// lightmode == ELightMode::Doom || lightmode == ELightMode::DoomDark;
}
void SetFallbackLightMode()
{
//lightmode = ELightMode::Doom;
}
};
void CleanSWDrawer();
//sectortype* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen);
//void WriteSavePic(player_t* player, FileWriter* file, int width, int height);
//sectortype* RenderView(player_t* player);

Some files were not shown because too many files have changed in this diff Show more