mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-01-18 15:42:34 +00:00
- separated all voxel related resource maintenance code from r_things.cpp and r_data.cpp into its own file.
SVN r3246 (trunk)
This commit is contained in:
parent
4626492f50
commit
7308e05ed0
11 changed files with 716 additions and 621 deletions
|
@ -61,7 +61,7 @@ extern void ClearStrifeTypes();
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
int GetSpriteIndex(const char * spritename)
|
||||
int GetSpriteIndex(const char * spritename, bool add)
|
||||
{
|
||||
static char lastsprite[5];
|
||||
static int lastindex;
|
||||
|
@ -87,6 +87,10 @@ int GetSpriteIndex(const char * spritename)
|
|||
return (lastindex = (int)i);
|
||||
}
|
||||
}
|
||||
if (!add)
|
||||
{
|
||||
return (lastindex = -1);
|
||||
}
|
||||
spritedef_t temp;
|
||||
strcpy (temp.name, upper);
|
||||
temp.numframes = 0;
|
||||
|
|
|
@ -240,7 +240,7 @@ private:
|
|||
|
||||
extern FDoomEdMap DoomEdMap;
|
||||
|
||||
int GetSpriteIndex(const char * spritename);
|
||||
int GetSpriteIndex(const char * spritename, bool add = true);
|
||||
TArray<FName> &MakeStateNameList(const char * fname);
|
||||
void AddStateLight(FState *state, const char *lname);
|
||||
|
||||
|
|
299
src/r_data.cpp
299
src/r_data.cpp
|
@ -133,305 +133,6 @@ const BYTE *R_GetColumn (FTexture *tex, int col)
|
|||
return tex->GetColumn (col, NULL);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetVoxelRemap
|
||||
//
|
||||
// Calculates a remap table for the voxel's palette. Results are cached so
|
||||
// passing the same palette repeatedly will not require repeated
|
||||
// recalculations.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static BYTE *GetVoxelRemap(const BYTE *pal)
|
||||
{
|
||||
static BYTE remap[256];
|
||||
static BYTE oldpal[768];
|
||||
static bool firsttime = true;
|
||||
|
||||
if (firsttime || memcmp(oldpal, pal, 768) != 0)
|
||||
{ // Not the same palette as last time, so recalculate.
|
||||
firsttime = false;
|
||||
memcpy(oldpal, pal, 768);
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
// The voxel palette uses VGA colors, so we have to expand it
|
||||
// from 6 to 8 bits per component.
|
||||
remap[i] = BestColor((uint32 *)GPalette.BaseColors,
|
||||
(oldpal[i*3 + 0] << 2) | (oldpal[i*3 + 0] >> 4),
|
||||
(oldpal[i*3 + 1] << 2) | (oldpal[i*3 + 1] >> 4),
|
||||
(oldpal[i*3 + 2] << 2) | (oldpal[i*3 + 2] >> 4));
|
||||
}
|
||||
}
|
||||
return remap;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CopyVoxelSlabs
|
||||
//
|
||||
// Copy all the slabs in a block of slabs.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool CopyVoxelSlabs(kvxslab_t *dest, const kvxslab_t *src, int size)
|
||||
{
|
||||
while (size >= 3)
|
||||
{
|
||||
int slabzleng = src->zleng;
|
||||
|
||||
if (3 + slabzleng > size)
|
||||
{ // slab is too tall
|
||||
return false;
|
||||
}
|
||||
|
||||
dest->ztop = src->ztop;
|
||||
dest->zleng = src->zleng;
|
||||
dest->backfacecull = src->backfacecull;
|
||||
|
||||
for (int j = 0; j < slabzleng; ++j)
|
||||
{
|
||||
dest->col[j] = src->col[j];
|
||||
}
|
||||
slabzleng += 3;
|
||||
src = (kvxslab_t *)((BYTE *)src + slabzleng);
|
||||
dest = (kvxslab_t *)((BYTE *)dest + slabzleng);
|
||||
size -= slabzleng;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// RemapVoxelSlabs
|
||||
//
|
||||
// Remaps all the slabs in a block of slabs.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void RemapVoxelSlabs(kvxslab_t *dest, int size, const BYTE *remap)
|
||||
{
|
||||
while (size >= 3)
|
||||
{
|
||||
int slabzleng = dest->zleng;
|
||||
|
||||
for (int j = 0; j < slabzleng; ++j)
|
||||
{
|
||||
dest->col[j] = remap[dest->col[j]];
|
||||
}
|
||||
slabzleng += 3;
|
||||
dest = (kvxslab_t *)((BYTE *)dest + slabzleng);
|
||||
size -= slabzleng;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_LoadKVX
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxel *R_LoadKVX(int lumpnum)
|
||||
{
|
||||
const kvxslab_t *slabs[MAXVOXMIPS];
|
||||
FVoxel *voxel = new FVoxel;
|
||||
const BYTE *rawmip;
|
||||
int mip, maxmipsize;
|
||||
int i, j, n;
|
||||
|
||||
FMemLump lump = Wads.ReadLump(lumpnum); // FMemLump adds an extra 0 byte to the end.
|
||||
BYTE *rawvoxel = (BYTE *)lump.GetMem();
|
||||
int voxelsize = (int)(lump.GetSize()-1);
|
||||
|
||||
// Oh, KVX, why couldn't you have a proper header? We'll just go through
|
||||
// and collect each MIP level, doing lots of range checking, and if the
|
||||
// last one doesn't end exactly 768 bytes before the end of the file,
|
||||
// we'll reject it.
|
||||
|
||||
for (mip = 0, rawmip = rawvoxel, maxmipsize = voxelsize - 768 - 4;
|
||||
mip < MAXVOXMIPS;
|
||||
mip++)
|
||||
{
|
||||
int numbytes = GetInt(rawmip);
|
||||
if (numbytes > maxmipsize || numbytes < 24)
|
||||
{
|
||||
break;
|
||||
}
|
||||
rawmip += 4;
|
||||
|
||||
FVoxelMipLevel *mipl = &voxel->Mips[mip];
|
||||
|
||||
// Load header data.
|
||||
mipl->SizeX = GetInt(rawmip + 0);
|
||||
mipl->SizeY = GetInt(rawmip + 4);
|
||||
mipl->SizeZ = GetInt(rawmip + 8);
|
||||
mipl->PivotX = GetInt(rawmip + 12);
|
||||
mipl->PivotY = GetInt(rawmip + 16);
|
||||
mipl->PivotZ = GetInt(rawmip + 20);
|
||||
|
||||
// How much space do we have for voxdata?
|
||||
int offsetsize = (mipl->SizeX + 1) * 4 + mipl->SizeX * (mipl->SizeY + 1) * 2;
|
||||
int voxdatasize = numbytes - 24 - offsetsize;
|
||||
if (voxdatasize < 0)
|
||||
{ // Clearly, not enough.
|
||||
break;
|
||||
}
|
||||
if (voxdatasize == 0)
|
||||
{ // This mip level is empty.
|
||||
goto nextmip;
|
||||
}
|
||||
|
||||
// Allocate slab data space.
|
||||
mipl->OffsetX = new int[(numbytes - 24 + 3) / 4];
|
||||
mipl->OffsetXY = (short *)(mipl->OffsetX + mipl->SizeX + 1);
|
||||
mipl->SlabData = (BYTE *)(mipl->OffsetXY + mipl->SizeX * (mipl->SizeY + 1));
|
||||
|
||||
// Load x offsets.
|
||||
for (i = 0, n = mipl->SizeX; i <= n; ++i)
|
||||
{
|
||||
// The X offsets stored in the KVX file are relative to the start of the
|
||||
// X offsets array. Make them relative to voxdata instead.
|
||||
mipl->OffsetX[i] = GetInt(rawmip + 24 + i * 4) - offsetsize;
|
||||
}
|
||||
|
||||
// The first X offset must be 0 (since we subtracted offsetsize), according to the spec:
|
||||
// NOTE: xoffset[0] = (xsiz+1)*4 + xsiz*(ysiz+1)*2 (ALWAYS)
|
||||
if (mipl->OffsetX[0] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// And the final X offset must point just past the end of the voxdata.
|
||||
if (mipl->OffsetX[mipl->SizeX] != voxdatasize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Load xy offsets.
|
||||
i = 24 + i * 4;
|
||||
for (j = 0, n *= mipl->SizeY + 1; j < n; ++j)
|
||||
{
|
||||
mipl->OffsetXY[j] = GetShort(rawmip + i + j * 2);
|
||||
}
|
||||
|
||||
// Ensure all offsets are within bounds.
|
||||
for (i = 0; i < mipl->SizeX; ++i)
|
||||
{
|
||||
int xoff = mipl->OffsetX[i];
|
||||
for (j = 0; j < mipl->SizeY; ++j)
|
||||
{
|
||||
int yoff = mipl->OffsetXY[(mipl->SizeY + 1) * i + j];
|
||||
if (unsigned(xoff + yoff) > unsigned(voxdatasize))
|
||||
{
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record slab location for the end.
|
||||
slabs[mip] = (kvxslab_t *)(rawmip + 24 + offsetsize);
|
||||
|
||||
// Time for the next mip Level.
|
||||
nextmip:
|
||||
rawmip += numbytes;
|
||||
maxmipsize -= numbytes + 4;
|
||||
}
|
||||
// Did we get any mip levels, and if so, does the last one leave just
|
||||
// enough room for the palette after it?
|
||||
if (mip == 0 || rawmip != rawvoxel + voxelsize - 768)
|
||||
{
|
||||
bad: delete voxel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Do not count empty mips at the end.
|
||||
for (; mip > 0; --mip)
|
||||
{
|
||||
if (voxel->Mips[mip - 1].SlabData != NULL)
|
||||
break;
|
||||
}
|
||||
voxel->NumMips = mip;
|
||||
|
||||
for (i = 0; i < mip; ++i)
|
||||
{
|
||||
if (!CopyVoxelSlabs((kvxslab_t *)voxel->Mips[i].SlabData, slabs[i], voxel->Mips[i].OffsetX[voxel->Mips[i].SizeX]))
|
||||
{ // Invalid slabs encountered. Reject this voxel.
|
||||
delete voxel;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
voxel->LumpNum = lumpnum;
|
||||
voxel->Palette = new BYTE[768];
|
||||
memcpy(voxel->Palette, rawvoxel + voxelsize - 768, 768);
|
||||
|
||||
return voxel;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxelMipLevel Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxelMipLevel::FVoxelMipLevel()
|
||||
{
|
||||
SizeZ = SizeY = SizeX = 0;
|
||||
PivotZ = PivotY = PivotX = 0;
|
||||
OffsetX = NULL;
|
||||
OffsetXY = NULL;
|
||||
SlabData = NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxelMipLevel Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxelMipLevel::~FVoxelMipLevel()
|
||||
{
|
||||
if (OffsetX != NULL)
|
||||
{
|
||||
delete[] OffsetX;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxel Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxel::FVoxel()
|
||||
{
|
||||
Palette = NULL;
|
||||
}
|
||||
|
||||
FVoxel::~FVoxel()
|
||||
{
|
||||
if (Palette != NULL) delete [] Palette;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Remap the voxel to the game palette
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FVoxel::Remap()
|
||||
{
|
||||
if (Palette != NULL)
|
||||
{
|
||||
BYTE *remap = GetVoxelRemap(Palette);
|
||||
for (int i = 0; i < NumMips; ++i)
|
||||
{
|
||||
RemapVoxelSlabs((kvxslab_t *)Mips[i].SlabData, Mips[i].OffsetX[Mips[i].SizeX], remap);
|
||||
}
|
||||
delete [] Palette;
|
||||
Palette = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Debug stuff
|
||||
|
|
|
@ -138,6 +138,4 @@ void R_PrecacheLevel (void);
|
|||
|
||||
int R_FindSkin (const char *name, int pclass); // [RH] Find a skin
|
||||
|
||||
FVoxel *R_LoadKVX(int lumpnum);
|
||||
|
||||
#endif
|
||||
|
|
49
src/r_defs.h
49
src/r_defs.h
|
@ -1145,55 +1145,6 @@ public:
|
|||
};
|
||||
|
||||
|
||||
// [RH] Voxels from Build
|
||||
|
||||
#define MAXVOXMIPS 5
|
||||
|
||||
struct kvxslab_t
|
||||
{
|
||||
BYTE ztop; // starting z coordinate of top of slab
|
||||
BYTE zleng; // # of bytes in the color array - slab height
|
||||
BYTE backfacecull; // low 6 bits tell which of 6 faces are exposed
|
||||
BYTE col[1/*zleng*/];// color data from top to bottom
|
||||
};
|
||||
|
||||
struct FVoxelMipLevel
|
||||
{
|
||||
FVoxelMipLevel();
|
||||
~FVoxelMipLevel();
|
||||
|
||||
int SizeX;
|
||||
int SizeY;
|
||||
int SizeZ;
|
||||
fixed_t PivotX; // 24.8 fixed point
|
||||
fixed_t PivotY; // ""
|
||||
fixed_t PivotZ; // ""
|
||||
int *OffsetX;
|
||||
short *OffsetXY;
|
||||
BYTE *SlabData;
|
||||
};
|
||||
|
||||
struct FVoxel
|
||||
{
|
||||
int LumpNum;
|
||||
int NumMips;
|
||||
BYTE *Palette;
|
||||
FVoxelMipLevel Mips[MAXVOXMIPS];
|
||||
|
||||
FVoxel();
|
||||
~FVoxel();
|
||||
void Remap();
|
||||
};
|
||||
|
||||
struct FVoxelDef
|
||||
{
|
||||
FVoxel *Voxel;
|
||||
int PlacedSpin; // degrees/sec to spin actors without MF_DROPPED set
|
||||
int DroppedSpin; // degrees/sec to spin actors with MF_DROPPED set
|
||||
fixed_t Scale;
|
||||
angle_t AngleOffset; // added to actor's angle to compensate for wrong-facing voxels
|
||||
};
|
||||
|
||||
// [RH] A c-buffer. Used for keeping track of offscreen voxel spans.
|
||||
|
||||
struct FCoverageBuffer
|
||||
|
|
272
src/r_things.cpp
272
src/r_things.cpp
|
@ -60,6 +60,7 @@
|
|||
#include "v_palette.h"
|
||||
#include "r_translate.h"
|
||||
#include "resources/colormaps.h"
|
||||
#include "resources/voxels.h"
|
||||
|
||||
extern fixed_t globaluclip, globaldclip;
|
||||
|
||||
|
@ -96,9 +97,6 @@ TArray<WORD> ParticlesInSubsec;
|
|||
short zeroarray[MAXWIDTH];
|
||||
short screenheightarray[MAXWIDTH];
|
||||
|
||||
#define MAX_SPRITE_FRAMES 29 // [RH] Macro-ized as in BOOM.
|
||||
|
||||
|
||||
CVAR (Bool, r_drawplayersprites, true, 0) // [RH] Draw player sprites?
|
||||
|
||||
CVAR (Bool, r_drawvoxels, true, 0)
|
||||
|
@ -113,9 +111,6 @@ TArray<spritedef_t> sprites;
|
|||
TArray<spriteframe_t> SpriteFrames;
|
||||
DWORD NumStdSprites; // The first x sprites that don't belong to skins.
|
||||
|
||||
TDeletingArray<FVoxel *> Voxels; // used only to auto-delete voxels on exit.
|
||||
TDeletingArray<FVoxelDef *> VoxelDefs;
|
||||
|
||||
int OffscreenBufferWidth, OffscreenBufferHeight;
|
||||
BYTE *OffscreenColorBuffer;
|
||||
FCoverageBuffer *OffscreenCoverageBuffer;
|
||||
|
@ -128,18 +123,6 @@ sprtemp[MAX_SPRITE_FRAMES];
|
|||
int maxframe;
|
||||
char* spritename;
|
||||
|
||||
struct VoxelOptions
|
||||
{
|
||||
VoxelOptions()
|
||||
: DroppedSpin(0), PlacedSpin(0), Scale(FRACUNIT), AngleOffset(0)
|
||||
{}
|
||||
|
||||
int DroppedSpin;
|
||||
int PlacedSpin;
|
||||
fixed_t Scale;
|
||||
angle_t AngleOffset;
|
||||
};
|
||||
|
||||
// [RH] skin globals
|
||||
FPlayerSkin *skins;
|
||||
size_t numskins;
|
||||
|
@ -487,23 +470,9 @@ void R_InitSpriteDefs ()
|
|||
VHasher *vh = &vhashes[hash];
|
||||
if (vh->Name == (int)intname)
|
||||
{
|
||||
FVoxel *vox = R_LoadKVX(hash);
|
||||
if (vox == NULL)
|
||||
FVoxelDef *voxdef = R_LoadVoxelDef(hash, vh->Spin);
|
||||
if (voxdef != NULL)
|
||||
{
|
||||
Printf("%s is not a valid voxel file\n", Wads.GetLumpFullName(hash));
|
||||
}
|
||||
else
|
||||
{
|
||||
FVoxelDef *voxdef = new FVoxelDef;
|
||||
|
||||
voxdef->Voxel = vox;
|
||||
voxdef->Scale = FRACUNIT;
|
||||
voxdef->DroppedSpin = voxdef->PlacedSpin = vh->Spin;
|
||||
voxdef->AngleOffset = 0;
|
||||
|
||||
Voxels.Push(vox);
|
||||
VoxelDefs.Push(voxdef);
|
||||
|
||||
if (vh->Frame == ' ' || vh->Frame == '\0')
|
||||
{ // voxel applies to every sprite frame
|
||||
for (j = 0; j < MAX_SPRITE_FRAMES; ++j)
|
||||
|
@ -573,163 +542,6 @@ static void R_ExtendSpriteFrames(spritedef_t &spr, int frame)
|
|||
spr.numframes = frame;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VOX_ReadSpriteNames
|
||||
//
|
||||
// Reads a list of sprite names from a VOXELDEF lump.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool VOX_ReadSpriteNames(FScanner &sc, TArray<DWORD> &vsprites)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
vsprites.Clear();
|
||||
while (sc.GetString())
|
||||
{
|
||||
// A sprite name list is terminated by an '=' character.
|
||||
if (sc.String[0] == '=')
|
||||
{
|
||||
if (vsprites.Size() == 0)
|
||||
{
|
||||
sc.ScriptMessage("No sprites specified for voxel.\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sc.StringLen != 4 && sc.StringLen != 5)
|
||||
{
|
||||
sc.ScriptMessage("Sprite name \"%s\" is wrong size.\n", sc.String);
|
||||
}
|
||||
else if (sc.StringLen == 5 && (sc.String[4] = toupper(sc.String[4]), sc.String[4] < 'A' || sc.String[4] >= 'A' + MAX_SPRITE_FRAMES))
|
||||
{
|
||||
sc.ScriptMessage("Sprite frame %s is invalid.\n", sc.String[4]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int frame = (sc.StringLen == 4) ? 255 : sc.String[4] - 'A';
|
||||
int spritename;
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
sc.String[i] = toupper(sc.String[i]);
|
||||
}
|
||||
spritename = *(int *)sc.String;
|
||||
for (i = 0; i < sprites.Size(); ++i)
|
||||
{
|
||||
if ((int)sprites[i].dwName == spritename)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != sprites.Size())
|
||||
{
|
||||
vsprites.Push((frame << 24) | i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vsprites.Size() != 0)
|
||||
{
|
||||
sc.ScriptMessage("Unexpected end of file\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VOX_ReadOptions
|
||||
//
|
||||
// Reads a list of options from a VOXELDEF lump, terminated with a '}'
|
||||
// character. The leading '{' must already be consumed
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void VOX_ReadOptions(FScanner &sc, VoxelOptions &opts)
|
||||
{
|
||||
while (sc.GetToken())
|
||||
{
|
||||
if (sc.TokenType == '}')
|
||||
{
|
||||
return;
|
||||
}
|
||||
sc.TokenMustBe(TK_Identifier);
|
||||
if (sc.Compare("scale"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetToken(TK_FloatConst);
|
||||
opts.Scale = FLOAT2FIXED(sc.Float);
|
||||
}
|
||||
else if (sc.Compare("spin"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetToken(TK_IntConst);
|
||||
opts.DroppedSpin = opts.PlacedSpin = sc.Number;
|
||||
}
|
||||
else if (sc.Compare("placedspin"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetToken(TK_IntConst);
|
||||
opts.PlacedSpin = sc.Number;
|
||||
}
|
||||
else if (sc.Compare("droppedspin"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetToken(TK_IntConst);
|
||||
opts.DroppedSpin = sc.Number;
|
||||
}
|
||||
else if (sc.Compare("angleoffset"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetAnyToken();
|
||||
if (sc.TokenType == TK_IntConst)
|
||||
{
|
||||
sc.Float = sc.Number;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.TokenMustBe(TK_FloatConst);
|
||||
}
|
||||
opts.AngleOffset = angle_t(sc.Float * ANGLE_180 / 180.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.ScriptMessage("Unknown voxel option '%s'\n", sc.String);
|
||||
if (sc.CheckToken('='))
|
||||
{
|
||||
sc.MustGetAnyToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
sc.ScriptMessage("Unterminated voxel option block\n");
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VOX_GetVoxel
|
||||
//
|
||||
// Returns a voxel object for the given lump or NULL if it is not a valid
|
||||
// voxel. If the voxel has already been loaded, it will be reused.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FVoxel *VOX_GetVoxel(int lumpnum)
|
||||
{
|
||||
// Is this voxel already loaded? If so, return it.
|
||||
for (unsigned i = 0; i < Voxels.Size(); ++i)
|
||||
{
|
||||
if (Voxels[i]->LumpNum == lumpnum)
|
||||
{
|
||||
return Voxels[i];
|
||||
}
|
||||
}
|
||||
FVoxel *vox = R_LoadKVX(lumpnum);
|
||||
if (vox != NULL)
|
||||
{
|
||||
Voxels.Push(vox);
|
||||
}
|
||||
return vox;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VOX_AddVoxel
|
||||
|
@ -738,88 +550,12 @@ static FVoxel *VOX_GetVoxel(int lumpnum)
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
static void VOX_AddVoxel(int sprnum, int frame, FVoxelDef *def)
|
||||
void VOX_AddVoxel(int sprnum, int frame, FVoxelDef *def)
|
||||
{
|
||||
R_ExtendSpriteFrames(sprites[sprnum], frame);
|
||||
SpriteFrames[sprites[sprnum].spriteframes + frame].Voxel = def;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_InitVoxels
|
||||
//
|
||||
// Process VOXELDEF lumps for defining voxel options that cannot be
|
||||
// condensed neatly into a sprite name format.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void R_InitVoxels()
|
||||
{
|
||||
int lump, lastlump = 0;
|
||||
|
||||
while ((lump = Wads.FindLump("VOXELDEF", &lastlump)) != -1)
|
||||
{
|
||||
FScanner sc(lump);
|
||||
TArray<DWORD> vsprites;
|
||||
|
||||
while (VOX_ReadSpriteNames(sc, vsprites))
|
||||
{
|
||||
FVoxel *voxeldata = NULL;
|
||||
int voxelfile;
|
||||
VoxelOptions opts;
|
||||
|
||||
sc.SetCMode(true);
|
||||
sc.MustGetToken(TK_StringConst);
|
||||
voxelfile = Wads.CheckNumForFullName(sc.String, true, ns_voxels);
|
||||
if (voxelfile < 0)
|
||||
{
|
||||
sc.ScriptMessage("Voxel \"%s\" not found.\n", sc.String);
|
||||
}
|
||||
else
|
||||
{
|
||||
voxeldata = VOX_GetVoxel(voxelfile);
|
||||
if (voxeldata == NULL)
|
||||
{
|
||||
sc.ScriptMessage("\"%s\" is not a valid voxel file.\n", sc.String);
|
||||
}
|
||||
}
|
||||
if (sc.CheckToken('{'))
|
||||
{
|
||||
VOX_ReadOptions(sc, opts);
|
||||
}
|
||||
sc.SetCMode(false);
|
||||
if (voxeldata != NULL && vsprites.Size() != 0)
|
||||
{
|
||||
FVoxelDef *def = new FVoxelDef;
|
||||
|
||||
def->Voxel = voxeldata;
|
||||
def->Scale = opts.Scale;
|
||||
def->DroppedSpin = opts.DroppedSpin;
|
||||
def->PlacedSpin = opts.PlacedSpin;
|
||||
def->AngleOffset = opts.AngleOffset;
|
||||
VoxelDefs.Push(def);
|
||||
|
||||
for (unsigned i = 0; i < vsprites.Size(); ++i)
|
||||
{
|
||||
int sprnum = int(vsprites[i] & 0xFFFFFF);
|
||||
int frame = int(vsprites[i] >> 24);
|
||||
if (frame == 255)
|
||||
{ // Apply voxel to all frames.
|
||||
for (int j = MAX_SPRITE_FRAMES - 1; j >= 0; --j)
|
||||
{
|
||||
VOX_AddVoxel(sprnum, j, def);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Apply voxel to only one frame.
|
||||
VOX_AddVoxel(sprnum, frame, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [RH]
|
||||
// R_InitSkins
|
||||
// Reads in everything applicable to a skin. The skins should have already
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#ifndef __R_THINGS__
|
||||
#define __R_THINGS__
|
||||
|
||||
#define MAX_SPRITE_FRAMES 29 // [RH] Macro-ized as in BOOM.
|
||||
|
||||
// [RH] Particle details
|
||||
struct particle_t
|
||||
{
|
||||
|
|
633
src/resources/voxels.cpp
Normal file
633
src/resources/voxels.cpp
Normal file
|
@ -0,0 +1,633 @@
|
|||
/*
|
||||
** voxels.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2010-2011 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
**
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "templates.h"
|
||||
#include "doomdef.h"
|
||||
#include "m_swap.h"
|
||||
#include "m_argv.h"
|
||||
#include "i_system.h"
|
||||
#include "w_wad.h"
|
||||
#include "r_local.h"
|
||||
#include "c_console.h"
|
||||
#include "c_cvars.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "doomstat.h"
|
||||
#include "v_video.h"
|
||||
#include "sc_man.h"
|
||||
#include "s_sound.h"
|
||||
#include "sbar.h"
|
||||
#include "gi.h"
|
||||
#include "r_sky.h"
|
||||
#include "cmdlib.h"
|
||||
#include "g_level.h"
|
||||
#include "d_net.h"
|
||||
#include "colormatcher.h"
|
||||
#include "d_netinf.h"
|
||||
#include "r_bsp.h"
|
||||
#include "r_plane.h"
|
||||
#include "r_segs.h"
|
||||
#include "v_palette.h"
|
||||
#include "r_translate.h"
|
||||
#include "resources/colormaps.h"
|
||||
#include "voxels.h"
|
||||
|
||||
void VOX_AddVoxel(int sprnum, int frame, FVoxelDef *def);
|
||||
|
||||
TDeletingArray<FVoxel *> Voxels; // used only to auto-delete voxels on exit.
|
||||
TDeletingArray<FVoxelDef *> VoxelDefs;
|
||||
|
||||
struct VoxelOptions
|
||||
{
|
||||
VoxelOptions()
|
||||
: DroppedSpin(0), PlacedSpin(0), Scale(FRACUNIT), AngleOffset(0)
|
||||
{}
|
||||
|
||||
int DroppedSpin;
|
||||
int PlacedSpin;
|
||||
fixed_t Scale;
|
||||
angle_t AngleOffset;
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// GetVoxelRemap
|
||||
//
|
||||
// Calculates a remap table for the voxel's palette. Results are cached so
|
||||
// passing the same palette repeatedly will not require repeated
|
||||
// recalculations.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static BYTE *GetVoxelRemap(const BYTE *pal)
|
||||
{
|
||||
static BYTE remap[256];
|
||||
static BYTE oldpal[768];
|
||||
static bool firsttime = true;
|
||||
|
||||
if (firsttime || memcmp(oldpal, pal, 768) != 0)
|
||||
{ // Not the same palette as last time, so recalculate.
|
||||
firsttime = false;
|
||||
memcpy(oldpal, pal, 768);
|
||||
for (int i = 0; i < 256; ++i)
|
||||
{
|
||||
// The voxel palette uses VGA colors, so we have to expand it
|
||||
// from 6 to 8 bits per component.
|
||||
remap[i] = BestColor((uint32 *)GPalette.BaseColors,
|
||||
(oldpal[i*3 + 0] << 2) | (oldpal[i*3 + 0] >> 4),
|
||||
(oldpal[i*3 + 1] << 2) | (oldpal[i*3 + 1] >> 4),
|
||||
(oldpal[i*3 + 2] << 2) | (oldpal[i*3 + 2] >> 4));
|
||||
}
|
||||
}
|
||||
return remap;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CopyVoxelSlabs
|
||||
//
|
||||
// Copy all the slabs in a block of slabs.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool CopyVoxelSlabs(kvxslab_t *dest, const kvxslab_t *src, int size)
|
||||
{
|
||||
while (size >= 3)
|
||||
{
|
||||
int slabzleng = src->zleng;
|
||||
|
||||
if (3 + slabzleng > size)
|
||||
{ // slab is too tall
|
||||
return false;
|
||||
}
|
||||
|
||||
dest->ztop = src->ztop;
|
||||
dest->zleng = src->zleng;
|
||||
dest->backfacecull = src->backfacecull;
|
||||
|
||||
for (int j = 0; j < slabzleng; ++j)
|
||||
{
|
||||
dest->col[j] = src->col[j];
|
||||
}
|
||||
slabzleng += 3;
|
||||
src = (kvxslab_t *)((BYTE *)src + slabzleng);
|
||||
dest = (kvxslab_t *)((BYTE *)dest + slabzleng);
|
||||
size -= slabzleng;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// RemapVoxelSlabs
|
||||
//
|
||||
// Remaps all the slabs in a block of slabs.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void RemapVoxelSlabs(kvxslab_t *dest, int size, const BYTE *remap)
|
||||
{
|
||||
while (size >= 3)
|
||||
{
|
||||
int slabzleng = dest->zleng;
|
||||
|
||||
for (int j = 0; j < slabzleng; ++j)
|
||||
{
|
||||
dest->col[j] = remap[dest->col[j]];
|
||||
}
|
||||
slabzleng += 3;
|
||||
dest = (kvxslab_t *)((BYTE *)dest + slabzleng);
|
||||
size -= slabzleng;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_LoadKVX
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxel *R_LoadKVX(int lumpnum)
|
||||
{
|
||||
const kvxslab_t *slabs[MAXVOXMIPS];
|
||||
FVoxel *voxel = new FVoxel;
|
||||
const BYTE *rawmip;
|
||||
int mip, maxmipsize;
|
||||
int i, j, n;
|
||||
|
||||
FMemLump lump = Wads.ReadLump(lumpnum); // FMemLump adds an extra 0 byte to the end.
|
||||
BYTE *rawvoxel = (BYTE *)lump.GetMem();
|
||||
int voxelsize = (int)(lump.GetSize()-1);
|
||||
|
||||
// Oh, KVX, why couldn't you have a proper header? We'll just go through
|
||||
// and collect each MIP level, doing lots of range checking, and if the
|
||||
// last one doesn't end exactly 768 bytes before the end of the file,
|
||||
// we'll reject it.
|
||||
|
||||
for (mip = 0, rawmip = rawvoxel, maxmipsize = voxelsize - 768 - 4;
|
||||
mip < MAXVOXMIPS;
|
||||
mip++)
|
||||
{
|
||||
int numbytes = GetInt(rawmip);
|
||||
if (numbytes > maxmipsize || numbytes < 24)
|
||||
{
|
||||
break;
|
||||
}
|
||||
rawmip += 4;
|
||||
|
||||
FVoxelMipLevel *mipl = &voxel->Mips[mip];
|
||||
|
||||
// Load header data.
|
||||
mipl->SizeX = GetInt(rawmip + 0);
|
||||
mipl->SizeY = GetInt(rawmip + 4);
|
||||
mipl->SizeZ = GetInt(rawmip + 8);
|
||||
mipl->PivotX = GetInt(rawmip + 12);
|
||||
mipl->PivotY = GetInt(rawmip + 16);
|
||||
mipl->PivotZ = GetInt(rawmip + 20);
|
||||
|
||||
// How much space do we have for voxdata?
|
||||
int offsetsize = (mipl->SizeX + 1) * 4 + mipl->SizeX * (mipl->SizeY + 1) * 2;
|
||||
int voxdatasize = numbytes - 24 - offsetsize;
|
||||
if (voxdatasize < 0)
|
||||
{ // Clearly, not enough.
|
||||
break;
|
||||
}
|
||||
if (voxdatasize != 0)
|
||||
{ // This mip level is not empty.
|
||||
// Allocate slab data space.
|
||||
mipl->OffsetX = new int[(numbytes - 24 + 3) / 4];
|
||||
mipl->OffsetXY = (short *)(mipl->OffsetX + mipl->SizeX + 1);
|
||||
mipl->SlabData = (BYTE *)(mipl->OffsetXY + mipl->SizeX * (mipl->SizeY + 1));
|
||||
|
||||
// Load x offsets.
|
||||
for (i = 0, n = mipl->SizeX; i <= n; ++i)
|
||||
{
|
||||
// The X offsets stored in the KVX file are relative to the start of the
|
||||
// X offsets array. Make them relative to voxdata instead.
|
||||
mipl->OffsetX[i] = GetInt(rawmip + 24 + i * 4) - offsetsize;
|
||||
}
|
||||
|
||||
// The first X offset must be 0 (since we subtracted offsetsize), according to the spec:
|
||||
// NOTE: xoffset[0] = (xsiz+1)*4 + xsiz*(ysiz+1)*2 (ALWAYS)
|
||||
if (mipl->OffsetX[0] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// And the final X offset must point just past the end of the voxdata.
|
||||
if (mipl->OffsetX[mipl->SizeX] != voxdatasize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Load xy offsets.
|
||||
i = 24 + i * 4;
|
||||
for (j = 0, n *= mipl->SizeY + 1; j < n; ++j)
|
||||
{
|
||||
mipl->OffsetXY[j] = GetShort(rawmip + i + j * 2);
|
||||
}
|
||||
|
||||
// Ensure all offsets are within bounds.
|
||||
for (i = 0; i < mipl->SizeX; ++i)
|
||||
{
|
||||
int xoff = mipl->OffsetX[i];
|
||||
for (j = 0; j < mipl->SizeY; ++j)
|
||||
{
|
||||
int yoff = mipl->OffsetXY[(mipl->SizeY + 1) * i + j];
|
||||
if (unsigned(xoff + yoff) > unsigned(voxdatasize))
|
||||
{
|
||||
delete voxel;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record slab location for the end.
|
||||
slabs[mip] = (kvxslab_t *)(rawmip + 24 + offsetsize);
|
||||
}
|
||||
|
||||
// Time for the next mip Level.
|
||||
rawmip += numbytes;
|
||||
maxmipsize -= numbytes + 4;
|
||||
}
|
||||
// Did we get any mip levels, and if so, does the last one leave just
|
||||
// enough room for the palette after it?
|
||||
if (mip == 0 || rawmip != rawvoxel + voxelsize - 768)
|
||||
{
|
||||
delete voxel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Do not count empty mips at the end.
|
||||
for (; mip > 0; --mip)
|
||||
{
|
||||
if (voxel->Mips[mip - 1].SlabData != NULL)
|
||||
break;
|
||||
}
|
||||
voxel->NumMips = mip;
|
||||
|
||||
for (i = 0; i < mip; ++i)
|
||||
{
|
||||
if (!CopyVoxelSlabs((kvxslab_t *)voxel->Mips[i].SlabData, slabs[i], voxel->Mips[i].OffsetX[voxel->Mips[i].SizeX]))
|
||||
{ // Invalid slabs encountered. Reject this voxel.
|
||||
delete voxel;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
voxel->LumpNum = lumpnum;
|
||||
voxel->Palette = new BYTE[768];
|
||||
memcpy(voxel->Palette, rawvoxel + voxelsize - 768, 768);
|
||||
|
||||
return voxel;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxelDef *R_LoadVoxelDef(int lumpnum, int spin)
|
||||
{
|
||||
FVoxel *vox = R_LoadKVX(lumpnum);
|
||||
if (vox == NULL)
|
||||
{
|
||||
Printf("%s is not a valid voxel file\n", Wads.GetLumpFullName(lumpnum));
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
FVoxelDef *voxdef = new FVoxelDef;
|
||||
voxdef->Voxel = vox;
|
||||
voxdef->Scale = FRACUNIT;
|
||||
voxdef->DroppedSpin = voxdef->PlacedSpin = spin;
|
||||
voxdef->AngleOffset = 0;
|
||||
|
||||
Voxels.Push(vox);
|
||||
VoxelDefs.Push(voxdef);
|
||||
return voxdef;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxelMipLevel Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxelMipLevel::FVoxelMipLevel()
|
||||
{
|
||||
SizeZ = SizeY = SizeX = 0;
|
||||
PivotZ = PivotY = PivotX = 0;
|
||||
OffsetX = NULL;
|
||||
OffsetXY = NULL;
|
||||
SlabData = NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxelMipLevel Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxelMipLevel::~FVoxelMipLevel()
|
||||
{
|
||||
if (OffsetX != NULL)
|
||||
{
|
||||
delete[] OffsetX;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxel Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxel::FVoxel()
|
||||
{
|
||||
Palette = NULL;
|
||||
}
|
||||
|
||||
FVoxel::~FVoxel()
|
||||
{
|
||||
if (Palette != NULL) delete [] Palette;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Remap the voxel to the game palette
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FVoxel::Remap()
|
||||
{
|
||||
if (Palette != NULL)
|
||||
{
|
||||
BYTE *remap = GetVoxelRemap(Palette);
|
||||
for (int i = 0; i < NumMips; ++i)
|
||||
{
|
||||
RemapVoxelSlabs((kvxslab_t *)Mips[i].SlabData, Mips[i].OffsetX[Mips[i].SizeX], remap);
|
||||
}
|
||||
delete [] Palette;
|
||||
Palette = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VOX_ReadSpriteNames
|
||||
//
|
||||
// Reads a list of sprite names from a VOXELDEF lump.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static bool VOX_ReadSpriteNames(FScanner &sc, TArray<DWORD> &vsprites)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
vsprites.Clear();
|
||||
while (sc.GetString())
|
||||
{
|
||||
// A sprite name list is terminated by an '=' character.
|
||||
if (sc.String[0] == '=')
|
||||
{
|
||||
if (vsprites.Size() == 0)
|
||||
{
|
||||
sc.ScriptMessage("No sprites specified for voxel.\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (sc.StringLen != 4 && sc.StringLen != 5)
|
||||
{
|
||||
sc.ScriptMessage("Sprite name \"%s\" is wrong size.\n", sc.String);
|
||||
}
|
||||
else if (sc.StringLen == 5 && (sc.String[4] = toupper(sc.String[4]), sc.String[4] < 'A' || sc.String[4] >= 'A' + MAX_SPRITE_FRAMES))
|
||||
{
|
||||
sc.ScriptMessage("Sprite frame %s is invalid.\n", sc.String[4]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int frame = (sc.StringLen == 4) ? 255 : sc.String[4] - 'A';
|
||||
|
||||
i = GetSpriteIndex(sc.String, false);
|
||||
if (i != -1)
|
||||
{
|
||||
vsprites.Push((frame << 24) | i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vsprites.Size() != 0)
|
||||
{
|
||||
sc.ScriptMessage("Unexpected end of file\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VOX_ReadOptions
|
||||
//
|
||||
// Reads a list of options from a VOXELDEF lump, terminated with a '}'
|
||||
// character. The leading '{' must already be consumed
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void VOX_ReadOptions(FScanner &sc, VoxelOptions &opts)
|
||||
{
|
||||
while (sc.GetToken())
|
||||
{
|
||||
if (sc.TokenType == '}')
|
||||
{
|
||||
return;
|
||||
}
|
||||
sc.TokenMustBe(TK_Identifier);
|
||||
if (sc.Compare("scale"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetToken(TK_FloatConst);
|
||||
opts.Scale = FLOAT2FIXED(sc.Float);
|
||||
}
|
||||
else if (sc.Compare("spin"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetToken(TK_IntConst);
|
||||
opts.DroppedSpin = opts.PlacedSpin = sc.Number;
|
||||
}
|
||||
else if (sc.Compare("placedspin"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetToken(TK_IntConst);
|
||||
opts.PlacedSpin = sc.Number;
|
||||
}
|
||||
else if (sc.Compare("droppedspin"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetToken(TK_IntConst);
|
||||
opts.DroppedSpin = sc.Number;
|
||||
}
|
||||
else if (sc.Compare("angleoffset"))
|
||||
{
|
||||
sc.MustGetToken('=');
|
||||
sc.MustGetAnyToken();
|
||||
if (sc.TokenType == TK_IntConst)
|
||||
{
|
||||
sc.Float = sc.Number;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.TokenMustBe(TK_FloatConst);
|
||||
}
|
||||
opts.AngleOffset = angle_t(sc.Float * ANGLE_180 / 180.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.ScriptMessage("Unknown voxel option '%s'\n", sc.String);
|
||||
if (sc.CheckToken('='))
|
||||
{
|
||||
sc.MustGetAnyToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
sc.ScriptMessage("Unterminated voxel option block\n");
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VOX_GetVoxel
|
||||
//
|
||||
// Returns a voxel object for the given lump or NULL if it is not a valid
|
||||
// voxel. If the voxel has already been loaded, it will be reused.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FVoxel *VOX_GetVoxel(int lumpnum)
|
||||
{
|
||||
// Is this voxel already loaded? If so, return it.
|
||||
for (unsigned i = 0; i < Voxels.Size(); ++i)
|
||||
{
|
||||
if (Voxels[i]->LumpNum == lumpnum)
|
||||
{
|
||||
return Voxels[i];
|
||||
}
|
||||
}
|
||||
FVoxel *vox = R_LoadKVX(lumpnum);
|
||||
if (vox != NULL)
|
||||
{
|
||||
Voxels.Push(vox);
|
||||
}
|
||||
return vox;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_InitVoxels
|
||||
//
|
||||
// Process VOXELDEF lumps for defining voxel options that cannot be
|
||||
// condensed neatly into a sprite name format.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void R_InitVoxels()
|
||||
{
|
||||
int lump, lastlump = 0;
|
||||
|
||||
while ((lump = Wads.FindLump("VOXELDEF", &lastlump)) != -1)
|
||||
{
|
||||
FScanner sc(lump);
|
||||
TArray<DWORD> vsprites;
|
||||
|
||||
while (VOX_ReadSpriteNames(sc, vsprites))
|
||||
{
|
||||
FVoxel *voxeldata = NULL;
|
||||
int voxelfile;
|
||||
VoxelOptions opts;
|
||||
|
||||
sc.SetCMode(true);
|
||||
sc.MustGetToken(TK_StringConst);
|
||||
voxelfile = Wads.CheckNumForFullName(sc.String, true, ns_voxels);
|
||||
if (voxelfile < 0)
|
||||
{
|
||||
sc.ScriptMessage("Voxel \"%s\" not found.\n", sc.String);
|
||||
}
|
||||
else
|
||||
{
|
||||
voxeldata = VOX_GetVoxel(voxelfile);
|
||||
if (voxeldata == NULL)
|
||||
{
|
||||
sc.ScriptMessage("\"%s\" is not a valid voxel file.\n", sc.String);
|
||||
}
|
||||
}
|
||||
if (sc.CheckToken('{'))
|
||||
{
|
||||
VOX_ReadOptions(sc, opts);
|
||||
}
|
||||
sc.SetCMode(false);
|
||||
if (voxeldata != NULL && vsprites.Size() != 0)
|
||||
{
|
||||
FVoxelDef *def = new FVoxelDef;
|
||||
|
||||
def->Voxel = voxeldata;
|
||||
def->Scale = opts.Scale;
|
||||
def->DroppedSpin = opts.DroppedSpin;
|
||||
def->PlacedSpin = opts.PlacedSpin;
|
||||
def->AngleOffset = opts.AngleOffset;
|
||||
VoxelDefs.Push(def);
|
||||
|
||||
for (unsigned i = 0; i < vsprites.Size(); ++i)
|
||||
{
|
||||
int sprnum = int(vsprites[i] & 0xFFFFFF);
|
||||
int frame = int(vsprites[i] >> 24);
|
||||
if (frame == 255)
|
||||
{ // Apply voxel to all frames.
|
||||
for (int j = MAX_SPRITE_FRAMES - 1; j >= 0; --j)
|
||||
{
|
||||
VOX_AddVoxel(sprnum, j, def);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Apply voxel to only one frame.
|
||||
VOX_AddVoxel(sprnum, frame, def);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/resources/voxels.h
Normal file
62
src/resources/voxels.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
#ifndef __RES_VOXEL_H
|
||||
#define __RES_VOXEL_H
|
||||
|
||||
#include "doomdef.h"
|
||||
|
||||
// [RH] Voxels from Build
|
||||
|
||||
#define MAXVOXMIPS 5
|
||||
|
||||
struct kvxslab_t
|
||||
{
|
||||
BYTE ztop; // starting z coordinate of top of slab
|
||||
BYTE zleng; // # of bytes in the color array - slab height
|
||||
BYTE backfacecull; // low 6 bits tell which of 6 faces are exposed
|
||||
BYTE col[1/*zleng*/];// color data from top to bottom
|
||||
};
|
||||
|
||||
struct FVoxelMipLevel
|
||||
{
|
||||
FVoxelMipLevel();
|
||||
~FVoxelMipLevel();
|
||||
|
||||
int SizeX;
|
||||
int SizeY;
|
||||
int SizeZ;
|
||||
fixed_t PivotX; // 24.8 fixed point
|
||||
fixed_t PivotY; // ""
|
||||
fixed_t PivotZ; // ""
|
||||
int *OffsetX;
|
||||
short *OffsetXY;
|
||||
BYTE *SlabData;
|
||||
};
|
||||
|
||||
struct FVoxel
|
||||
{
|
||||
int LumpNum;
|
||||
int NumMips;
|
||||
BYTE *Palette;
|
||||
FVoxelMipLevel Mips[MAXVOXMIPS];
|
||||
|
||||
FVoxel();
|
||||
~FVoxel();
|
||||
void Remap();
|
||||
};
|
||||
|
||||
struct FVoxelDef
|
||||
{
|
||||
FVoxel *Voxel;
|
||||
int PlacedSpin; // degrees/sec to spin actors without MF_DROPPED set
|
||||
int DroppedSpin; // degrees/sec to spin actors with MF_DROPPED set
|
||||
fixed_t Scale;
|
||||
angle_t AngleOffset; // added to actor's angle to compensate for wrong-facing voxels
|
||||
};
|
||||
|
||||
extern TDeletingArray<FVoxel *> Voxels; // used only to auto-delete voxels on exit.
|
||||
extern TDeletingArray<FVoxelDef *> VoxelDefs;
|
||||
|
||||
FVoxel *R_LoadKVX(int lumpnum);
|
||||
FVoxelDef *R_LoadVoxelDef(int lumpnum, int spin);
|
||||
void R_InitVoxels();
|
||||
|
||||
#endif
|
|
@ -63,6 +63,7 @@
|
|||
#include "v_palette.h"
|
||||
#include "r_sky.h"
|
||||
#include "menu/menu.h"
|
||||
#include "resources/voxels.h"
|
||||
|
||||
|
||||
IMPLEMENT_ABSTRACT_CLASS (DCanvas)
|
||||
|
@ -1318,7 +1319,6 @@ void DFrameBuffer::RenderView(player_t *player)
|
|||
//
|
||||
//
|
||||
//==========================================================================
|
||||
extern TDeletingArray<FVoxel *> Voxels;
|
||||
|
||||
void DFrameBuffer::RemapVoxels()
|
||||
{
|
||||
|
|
|
@ -6644,6 +6644,10 @@
|
|||
RelativePath=".\src\resources\colormaps.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\resources\voxels.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Sources"
|
||||
|
@ -6652,6 +6656,10 @@
|
|||
RelativePath=".\src\resources\colormaps.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\resources\voxels.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
</Files>
|
||||
|
|
Loading…
Reference in a new issue