mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-15 00:42:08 +00:00
- backend update with GZDoom model code.
This commit is contained in:
parent
7e5c3e066c
commit
edb2cb31ee
44 changed files with 4443 additions and 220 deletions
|
@ -636,6 +636,7 @@ file( GLOB HEADER_FILES
|
|||
common/platform/posix/cocoa/*.h
|
||||
common/platform/posix/sdl/*.h
|
||||
common/platform/win32/*.h
|
||||
common/models/*.h
|
||||
common/textures/*.h
|
||||
common/textures/hires/hqnx/*.h
|
||||
common/textures/hires/hqnx_asm/*.h
|
||||
|
@ -815,6 +816,13 @@ set (PCH_SOURCES
|
|||
common/textures/formats/tgatexture.cpp
|
||||
common/textures/formats/stbtexture.cpp
|
||||
common/textures/hires/hqresize.cpp
|
||||
common/models/models_md3.cpp
|
||||
common/models/models_md2.cpp
|
||||
common/models/models_voxel.cpp
|
||||
common/models/models_ue1.cpp
|
||||
common/models/models_obj.cpp
|
||||
common/models/model.cpp
|
||||
common/models/voxels.cpp
|
||||
common/console/c_commandline.cpp
|
||||
common/console/c_buttons.cpp
|
||||
common/console/c_bind.cpp
|
||||
|
@ -838,6 +846,7 @@ set (PCH_SOURCES
|
|||
common/utility/s_playlist.cpp
|
||||
common/utility/zstrformat.cpp
|
||||
common/utility/name.cpp
|
||||
common/utility/m_bbox.cpp
|
||||
common/thirdparty/md5.cpp
|
||||
common/thirdparty/superfasthash.cpp
|
||||
common/filesystem/filesystem.cpp
|
||||
|
@ -1037,6 +1046,7 @@ include_directories(
|
|||
common/textures/formats
|
||||
common/textures/hires
|
||||
common/textures
|
||||
common/models
|
||||
common/filesystem
|
||||
common/utility
|
||||
common/console
|
||||
|
@ -1185,6 +1195,8 @@ source_group("Common\\Rendering\\Hardware Renderer" REGULAR_EXPRESSION "^${CMAKE
|
|||
source_group("Common\\Rendering\\Hardware Renderer\\Data" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/hwrenderer/data/.+")
|
||||
source_group("Common\\Rendering\\Hardware Renderer\\Postprocessing" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/hwrenderer/postprocessing/.+")
|
||||
source_group("Common\\Rendering\\OpenGL Loader" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/gl_load/.+")
|
||||
source_group("Common\\Rendering\\OpenGL Backend" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/gl/.+")
|
||||
source_group("Common\\Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/models/.+")
|
||||
source_group("Common\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/.+")
|
||||
source_group("Common\\Textures\\Hires" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/hires/.+")
|
||||
source_group("Common\\Textures\\Hires\\HQ Resize" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/hires/hqnx/.+")
|
||||
|
|
|
@ -64,6 +64,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "gstrings.h"
|
||||
#include "v_2ddrawer.h"
|
||||
#include "v_video.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
|
||||
CVARD(Bool, hud_powerupduration, true, CVAR_ARCHIVE/*|CVAR_FRONTEND_BLOOD*/, "enable/disable displaying the remaining seconds for power-ups")
|
||||
|
||||
|
@ -3241,7 +3242,7 @@ void viewDrawScreen(bool sceneonly)
|
|||
}
|
||||
r enderSetTarget(4079, 128, 128);
|
||||
renderSetAspect(65536, 78643);
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
int vd8 = pOther->pSprite->x;
|
||||
int vd4 = pOther->pSprite->y;
|
||||
int vd0 = pOther->zView;
|
||||
|
@ -3307,7 +3308,7 @@ void viewDrawScreen(bool sceneonly)
|
|||
memcpy(gotpic+510, bakMirrorGotpic, 2);
|
||||
viewProcessSprites(vd8, vd4, vd0, v50, gInterpolate);
|
||||
renderDrawMasks();
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
renderRestoreTarget();
|
||||
#endif
|
||||
}
|
||||
|
@ -3349,7 +3350,7 @@ void viewDrawScreen(bool sceneonly)
|
|||
}
|
||||
nSprite = nextspritestat[nSprite];
|
||||
}
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
g_visibility = (int32_t)(ClipLow(gVisibility - 32 * gView->visibility - unk, 0) * (numplayers > 1 ? 1.f : r_ambientlightrecip));
|
||||
cA = (cA + interpolateangfix16(fix16_from_int(deliriumTurnO), fix16_from_int(deliriumTurn), gInterpolate)) & 0x7ffffff;
|
||||
int vfc, vf8;
|
||||
|
@ -3404,7 +3405,7 @@ void viewDrawScreen(bool sceneonly)
|
|||
sub_557C4(cX, cY, gInterpolate);
|
||||
renderDrawMasks();
|
||||
gView->pSprite->cstat = bakCstat;
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
|
||||
if ((v78 || bDelirium) && !sceneonly)
|
||||
{
|
||||
|
|
236
source/common/models/model.cpp
Normal file
236
source/common/models/model.cpp
Normal file
|
@ -0,0 +1,236 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-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_models.cpp
|
||||
**
|
||||
** General model handling code
|
||||
**
|
||||
**/
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "cmdlib.h"
|
||||
#include "sc_man.h"
|
||||
#include "m_crc32.h"
|
||||
#include "printf.h"
|
||||
#include "model_ue1.h"
|
||||
#include "model_obj.h"
|
||||
#include "model_md2.h"
|
||||
#include "model_md3.h"
|
||||
#include "model_kvx.h"
|
||||
#include "i_time.h"
|
||||
#include "voxels.h"
|
||||
#include "texturemanager.h"
|
||||
#include "modelrenderer.h"
|
||||
|
||||
|
||||
TDeletingArray<FModel*> Models;
|
||||
TArray<FSpriteModelFrame> SpriteModelFrames;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FlushModels()
|
||||
{
|
||||
for (int i = Models.Size() - 1; i >= 0; i--)
|
||||
{
|
||||
Models[i]->DestroyVertexBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FModel::FModel()
|
||||
{
|
||||
for (int i = 0; i < NumModelRendererTypes; i++)
|
||||
mVBuf[i] = nullptr;
|
||||
}
|
||||
|
||||
FModel::~FModel()
|
||||
{
|
||||
DestroyVertexBuffer();
|
||||
}
|
||||
|
||||
void FModel::DestroyVertexBuffer()
|
||||
{
|
||||
for (int i = 0; i < NumModelRendererTypes; i++)
|
||||
{
|
||||
delete mVBuf[i];
|
||||
mVBuf[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FindGFXFile
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
static int FindGFXFile(FString & fn)
|
||||
{
|
||||
int lump = fileSystem.CheckNumForFullName(fn); // if we find something that matches the name plus the extension, return it and do not enter the substitution logic below.
|
||||
if (lump != -1) return lump;
|
||||
|
||||
int best = -1;
|
||||
int dot = fn.LastIndexOf('.');
|
||||
int slash = fn.LastIndexOf('/');
|
||||
if (dot > slash) fn.Truncate(dot);
|
||||
|
||||
static const char * extensions[] = { ".png", ".jpg", ".tga", ".pcx", nullptr };
|
||||
|
||||
for (const char ** extp=extensions; *extp; extp++)
|
||||
{
|
||||
int lump = fileSystem.CheckNumForFullName(fn + *extp);
|
||||
if (lump >= best) best = lump;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// LoadSkin
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FTextureID LoadSkin(const char * path, const char * fn)
|
||||
{
|
||||
FString buffer;
|
||||
|
||||
buffer.Format("%s%s", path, fn);
|
||||
|
||||
int texlump = FindGFXFile(buffer);
|
||||
const char * const texname = texlump < 0 ? fn : fileSystem.GetFileFullName(texlump);
|
||||
return TexMan.CheckForTexture(texname, ETextureType::Any, FTextureManager::TEXMAN_TryAny);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// ModelFrameHash
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
int ModelFrameHash(FSpriteModelFrame * smf)
|
||||
{
|
||||
const uint32_t *table = GetCRCTable ();
|
||||
uint32_t hash = 0xffffffff;
|
||||
|
||||
const char * s = (const char *)(&smf->type); // this uses type, sprite and frame for hashing
|
||||
const char * se= (const char *)(&smf->hashnext);
|
||||
|
||||
for (; s<se; s++)
|
||||
{
|
||||
hash = CRC1 (hash, *s, table);
|
||||
}
|
||||
return hash ^ 0xffffffff;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FindModel
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
unsigned FindModel(const char * path, const char * modelfile)
|
||||
{
|
||||
FModel * model = nullptr;
|
||||
FString fullname;
|
||||
|
||||
fullname.Format("%s%s", path, modelfile);
|
||||
int lump = fileSystem.CheckNumForFullName(fullname);
|
||||
|
||||
if (lump<0)
|
||||
{
|
||||
Printf("FindModel: '%s' not found\n", fullname.GetChars());
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i< Models.Size(); i++)
|
||||
{
|
||||
if (!Models[i]->mFileName.CompareNoCase(fullname)) return i;
|
||||
}
|
||||
|
||||
int len = fileSystem.FileLength(lump);
|
||||
FileData lumpd = fileSystem.ReadFile(lump);
|
||||
char * buffer = (char*)lumpd.GetMem();
|
||||
|
||||
if ( (size_t)fullname.LastIndexOf("_d.3d") == fullname.Len()-5 )
|
||||
{
|
||||
FString anivfile = fullname.GetChars();
|
||||
anivfile.Substitute("_d.3d","_a.3d");
|
||||
if ( fileSystem.CheckNumForFullName(anivfile) > 0 )
|
||||
{
|
||||
model = new FUE1Model;
|
||||
}
|
||||
}
|
||||
else if ( (size_t)fullname.LastIndexOf("_a.3d") == fullname.Len()-5 )
|
||||
{
|
||||
FString datafile = fullname.GetChars();
|
||||
datafile.Substitute("_a.3d","_d.3d");
|
||||
if ( fileSystem.CheckNumForFullName(datafile) > 0 )
|
||||
{
|
||||
model = new FUE1Model;
|
||||
}
|
||||
}
|
||||
else if ( (size_t)fullname.LastIndexOf(".obj") == fullname.Len() - 4 )
|
||||
{
|
||||
model = new FOBJModel;
|
||||
}
|
||||
else if (!memcmp(buffer, "DMDM", 4))
|
||||
{
|
||||
model = new FDMDModel;
|
||||
}
|
||||
else if (!memcmp(buffer, "IDP2", 4))
|
||||
{
|
||||
model = new FMD2Model;
|
||||
}
|
||||
else if (!memcmp(buffer, "IDP3", 4))
|
||||
{
|
||||
model = new FMD3Model;
|
||||
}
|
||||
|
||||
if (model != nullptr)
|
||||
{
|
||||
if (!model->Load(path, lump, buffer, len))
|
||||
{
|
||||
delete model;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// try loading as a voxel
|
||||
FVoxel *voxel = R_LoadKVX(lump);
|
||||
if (voxel != nullptr)
|
||||
{
|
||||
model = new FVoxelModel(voxel, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("LoadModel: Unknown model format in '%s'\n", fullname.GetChars());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// The vertex buffer cannot be initialized here because this gets called before OpenGL is initialized
|
||||
model->mFileName = fullname;
|
||||
return Models.Push(model);
|
||||
}
|
||||
|
83
source/common/models/model.h
Normal file
83
source/common/models/model.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "textureid.h"
|
||||
#include "i_modelvertexbuffer.h"
|
||||
|
||||
class FModelRenderer;
|
||||
class FGameTexture;
|
||||
class IModelVertexBuffer;
|
||||
class FModel;
|
||||
struct FSpriteModelFrame;
|
||||
|
||||
FTextureID LoadSkin(const char* path, const char* fn);
|
||||
void FlushModels();
|
||||
extern TDeletingArray<FModel*> Models;
|
||||
extern TArray<FSpriteModelFrame> SpriteModelFrames;
|
||||
|
||||
#define MAX_MODELS_PER_FRAME 4
|
||||
#define MD3_MAX_SURFACES 32
|
||||
|
||||
struct FSpriteModelFrame
|
||||
{
|
||||
int modelIDs[MAX_MODELS_PER_FRAME];
|
||||
FTextureID skinIDs[MAX_MODELS_PER_FRAME];
|
||||
FTextureID surfaceskinIDs[MAX_MODELS_PER_FRAME][MD3_MAX_SURFACES];
|
||||
int modelframes[MAX_MODELS_PER_FRAME];
|
||||
float xscale, yscale, zscale;
|
||||
// [BB] Added zoffset, rotation parameters and flags.
|
||||
// Added xoffset, yoffset
|
||||
float xoffset, yoffset, zoffset;
|
||||
float xrotate, yrotate, zrotate;
|
||||
float rotationCenterX, rotationCenterY, rotationCenterZ;
|
||||
float rotationSpeed;
|
||||
unsigned int flags;
|
||||
const void* type; // used for hashing, must point to something usable as identifier for the model's owner.
|
||||
short sprite;
|
||||
short frame;
|
||||
int hashnext;
|
||||
float angleoffset;
|
||||
// added pithoffset, rolloffset.
|
||||
float pitchoffset, rolloffset; // I don't want to bother with type transformations, so I made this variables float.
|
||||
bool isVoxel;
|
||||
};
|
||||
|
||||
|
||||
enum ModelRendererType
|
||||
{
|
||||
GLModelRendererType,
|
||||
SWModelRendererType,
|
||||
PolyModelRendererType,
|
||||
NumModelRendererTypes
|
||||
};
|
||||
|
||||
class FModel
|
||||
{
|
||||
public:
|
||||
FModel();
|
||||
virtual ~FModel();
|
||||
|
||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0;
|
||||
virtual int FindFrame(const char * name) = 0;
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation=0) = 0;
|
||||
virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0;
|
||||
virtual void AddSkins(uint8_t *hitlist) = 0;
|
||||
virtual float getAspectFactor(float vscale) { return 1.f; }
|
||||
|
||||
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
|
||||
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }
|
||||
void DestroyVertexBuffer();
|
||||
|
||||
const FSpriteModelFrame *curSpriteMDLFrame;
|
||||
int curMDLIndex;
|
||||
void PushSpriteMDLFrame(const FSpriteModelFrame *smf, int index) { curSpriteMDLFrame = smf; curMDLIndex = index; };
|
||||
|
||||
FString mFileName;
|
||||
|
||||
private:
|
||||
IModelVertexBuffer *mVBuf[NumModelRendererTypes];
|
||||
};
|
||||
|
||||
int ModelFrameHash(FSpriteModelFrame* smf);
|
||||
unsigned FindModel(const char* path, const char* modelfile);
|
||||
|
69
source/common/models/model_kvx.h
Normal file
69
source/common/models/model_kvx.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
#include "model.h"
|
||||
#include "i_modelvertexbuffer.h"
|
||||
#include "tarray.h"
|
||||
#include "xs_Float.h"
|
||||
|
||||
struct FVoxel;
|
||||
struct kvxslab_t;
|
||||
class FModelRenderer;
|
||||
class FGameTexture;
|
||||
|
||||
struct FVoxelVertexHash
|
||||
{
|
||||
// Returns the hash value for a key.
|
||||
hash_t Hash(const FModelVertex &key)
|
||||
{
|
||||
int ix = xs_RoundToInt(key.x);
|
||||
int iy = xs_RoundToInt(key.y);
|
||||
int iz = xs_RoundToInt(key.z);
|
||||
return (hash_t)(ix + (iy<<9) + (iz<<18));
|
||||
}
|
||||
|
||||
// Compares two keys, returning zero if they are the same.
|
||||
int Compare(const FModelVertex &left, const FModelVertex &right)
|
||||
{
|
||||
return left.x != right.x || left.y != right.y || left.z != right.z || left.u != right.u || left.v != right.v;
|
||||
}
|
||||
};
|
||||
|
||||
struct FIndexInit
|
||||
{
|
||||
void Init(unsigned int &value)
|
||||
{
|
||||
value = 0xffffffff;
|
||||
}
|
||||
};
|
||||
|
||||
typedef TMap<FModelVertex, unsigned int, FVoxelVertexHash, FIndexInit> FVoxelMap;
|
||||
|
||||
|
||||
class FVoxelModel : public FModel
|
||||
{
|
||||
protected:
|
||||
FVoxel *mVoxel;
|
||||
bool mOwningVoxel; // if created through MODELDEF deleting this object must also delete the voxel object
|
||||
FTextureID mPalette;
|
||||
unsigned int mNumIndices;
|
||||
TArray<FModelVertex> mVertices;
|
||||
TArray<unsigned int> mIndices;
|
||||
|
||||
void MakeSlabPolys(int x, int y, kvxslab_t *voxptr, FVoxelMap &check);
|
||||
void AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3, int x4, int y4, int z4, uint8_t color, FVoxelMap &check);
|
||||
unsigned int AddVertex(FModelVertex &vert, FVoxelMap &check);
|
||||
|
||||
public:
|
||||
FVoxelModel(FVoxel *voxel, bool owned);
|
||||
~FVoxelModel();
|
||||
bool Load(const char * fn, int lumpnum, const char * buffer, int length);
|
||||
void Initialize();
|
||||
virtual int FindFrame(const char * name);
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation=0);
|
||||
virtual void AddSkins(uint8_t *hitlist);
|
||||
FTextureID GetPaletteTexture() const { return mPalette; }
|
||||
void BuildVertexBuffer(FModelRenderer *renderer);
|
||||
float getAspectFactor(float vscale) override;
|
||||
};
|
||||
|
||||
|
137
source/common/models/model_md2.h
Normal file
137
source/common/models/model_md2.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
#include "model.h"
|
||||
|
||||
#define MD2_MAGIC 0x32504449
|
||||
#define DMD_MAGIC 0x4D444D44
|
||||
#define MAX_LODS 4
|
||||
|
||||
class FDMDModel : public FModel
|
||||
{
|
||||
protected:
|
||||
|
||||
struct FTriangle
|
||||
{
|
||||
unsigned short vertexIndices[3];
|
||||
unsigned short textureIndices[3];
|
||||
};
|
||||
|
||||
|
||||
struct DMDHeader
|
||||
{
|
||||
int magic;
|
||||
int version;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct DMDModelVertex
|
||||
{
|
||||
float xyz[3];
|
||||
};
|
||||
|
||||
struct FTexCoord
|
||||
{
|
||||
short s, t;
|
||||
};
|
||||
|
||||
struct FGLCommandVertex
|
||||
{
|
||||
float s, t;
|
||||
int index;
|
||||
};
|
||||
|
||||
struct DMDInfo
|
||||
{
|
||||
int skinWidth;
|
||||
int skinHeight;
|
||||
int frameSize;
|
||||
int numSkins;
|
||||
int numVertices;
|
||||
int numTexCoords;
|
||||
int numFrames;
|
||||
int numLODs;
|
||||
int offsetSkins;
|
||||
int offsetTexCoords;
|
||||
int offsetFrames;
|
||||
int offsetLODs;
|
||||
int offsetEnd;
|
||||
};
|
||||
|
||||
struct ModelFrame
|
||||
{
|
||||
char name[16];
|
||||
unsigned int vindex;
|
||||
};
|
||||
|
||||
struct ModelFrameVertexData
|
||||
{
|
||||
DMDModelVertex *vertices;
|
||||
DMDModelVertex *normals;
|
||||
};
|
||||
|
||||
struct DMDLoDInfo
|
||||
{
|
||||
int numTriangles;
|
||||
int numGlCommands;
|
||||
int offsetTriangles;
|
||||
int offsetGlCommands;
|
||||
};
|
||||
|
||||
struct DMDLoD
|
||||
{
|
||||
FTriangle * triangles;
|
||||
};
|
||||
|
||||
|
||||
int mLumpNum;
|
||||
DMDHeader header;
|
||||
DMDInfo info;
|
||||
FTextureID * skins;
|
||||
ModelFrame * frames;
|
||||
bool allowTexComp; // Allow texture compression with this.
|
||||
|
||||
// Temp data only needed for buffer construction
|
||||
FTexCoord * texCoords;
|
||||
ModelFrameVertexData *framevtx;
|
||||
DMDLoDInfo lodInfo[MAX_LODS];
|
||||
DMDLoD lods[MAX_LODS];
|
||||
|
||||
public:
|
||||
FDMDModel()
|
||||
{
|
||||
mLumpNum = -1;
|
||||
frames = NULL;
|
||||
skins = NULL;
|
||||
for (int i = 0; i < MAX_LODS; i++)
|
||||
{
|
||||
lods[i].triangles = NULL;
|
||||
}
|
||||
info.numLODs = 0;
|
||||
texCoords = NULL;
|
||||
framevtx = NULL;
|
||||
}
|
||||
virtual ~FDMDModel();
|
||||
|
||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length);
|
||||
virtual int FindFrame(const char * name);
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation=0);
|
||||
virtual void LoadGeometry();
|
||||
virtual void AddSkins(uint8_t *hitlist);
|
||||
|
||||
void UnloadGeometry();
|
||||
void BuildVertexBuffer(FModelRenderer *renderer);
|
||||
|
||||
};
|
||||
|
||||
// This uses the same internal representation as DMD
|
||||
class FMD2Model : public FDMDModel
|
||||
{
|
||||
public:
|
||||
FMD2Model() {}
|
||||
virtual ~FMD2Model();
|
||||
|
||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length);
|
||||
virtual void LoadGeometry();
|
||||
|
||||
};
|
||||
|
||||
|
75
source/common/models/model_md3.h
Normal file
75
source/common/models/model_md3.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
#include "model.h"
|
||||
|
||||
#define MD3_MAGIC 0x33504449
|
||||
|
||||
class FMD3Model : public FModel
|
||||
{
|
||||
struct MD3Tag
|
||||
{
|
||||
// Currently I have no use for this
|
||||
};
|
||||
|
||||
struct MD3TexCoord
|
||||
{
|
||||
float s,t;
|
||||
};
|
||||
|
||||
struct MD3Vertex
|
||||
{
|
||||
float x,y,z;
|
||||
float nx,ny,nz;
|
||||
};
|
||||
|
||||
struct MD3Triangle
|
||||
{
|
||||
int VertIndex[3];
|
||||
};
|
||||
|
||||
struct MD3Surface
|
||||
{
|
||||
unsigned numVertices;
|
||||
unsigned numTriangles;
|
||||
unsigned numSkins;
|
||||
|
||||
TArray<FTextureID> Skins;
|
||||
TArray<MD3Triangle> Tris;
|
||||
TArray<MD3TexCoord> Texcoords;
|
||||
TArray<MD3Vertex> Vertices;
|
||||
|
||||
unsigned int vindex = UINT_MAX; // contains numframes arrays of vertices
|
||||
unsigned int iindex = UINT_MAX;
|
||||
|
||||
void UnloadGeometry()
|
||||
{
|
||||
Tris.Reset();
|
||||
Vertices.Reset();
|
||||
Texcoords.Reset();
|
||||
}
|
||||
};
|
||||
|
||||
struct MD3Frame
|
||||
{
|
||||
// The bounding box information is of no use in the Doom engine
|
||||
// That will still be done with the actor's size information.
|
||||
char Name[16];
|
||||
float origin[3];
|
||||
};
|
||||
|
||||
int numTags;
|
||||
int mLumpNum;
|
||||
|
||||
TArray<MD3Frame> Frames;
|
||||
TArray<MD3Surface> Surfaces;
|
||||
|
||||
public:
|
||||
FMD3Model() = default;
|
||||
|
||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length);
|
||||
virtual int FindFrame(const char * name);
|
||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation=0);
|
||||
void LoadGeometry();
|
||||
void BuildVertexBuffer(FModelRenderer *renderer);
|
||||
virtual void AddSkins(uint8_t *hitlist);
|
||||
};
|
||||
|
106
source/common/models/model_obj.h
Normal file
106
source/common/models/model_obj.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2018 Kevin Caccamo
|
||||
// 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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#ifndef __GL_MODELS_OBJ_H__
|
||||
#define __GL_MODELS_OBJ_H__
|
||||
|
||||
#include "model.h"
|
||||
#include "sc_man.h"
|
||||
#include "tarray.h"
|
||||
#include "vectors.h"
|
||||
|
||||
class FOBJModel : public FModel
|
||||
{
|
||||
private:
|
||||
const char *newSideSep = "$"; // OBJ side separator is /, which is parsed as a line comment by FScanner if two of them are next to each other.
|
||||
bool hasMissingNormals;
|
||||
bool hasSmoothGroups;
|
||||
|
||||
enum class FaceElement
|
||||
{
|
||||
VertexIndex,
|
||||
UVIndex,
|
||||
VNormalIndex
|
||||
};
|
||||
|
||||
struct OBJTriRef
|
||||
{
|
||||
unsigned int surf;
|
||||
unsigned int tri;
|
||||
OBJTriRef(): surf(0), tri(0) {}
|
||||
OBJTriRef(unsigned int surf, unsigned int tri): surf(surf), tri(tri) {}
|
||||
bool operator== (OBJTriRef other) { return surf == other.surf && tri == other.tri; }
|
||||
};
|
||||
struct OBJFaceSide
|
||||
{
|
||||
int vertref;
|
||||
int normref;
|
||||
int uvref;
|
||||
};
|
||||
struct OBJFace
|
||||
{
|
||||
unsigned int sideCount;
|
||||
unsigned int smoothGroup;
|
||||
OBJFaceSide sides[4];
|
||||
OBJFace(): sideCount(0), smoothGroup(0) {}
|
||||
};
|
||||
struct OBJSurface // 1 surface per 'usemtl'
|
||||
{
|
||||
unsigned int numTris; // Number of triangulated faces
|
||||
unsigned int numFaces; // Number of faces
|
||||
unsigned int vbStart; // First index in vertex buffer
|
||||
unsigned int faceStart; // Index of first face in faces array
|
||||
OBJFace* tris; // Triangles
|
||||
FTextureID skin;
|
||||
OBJSurface(FTextureID skin): numTris(0), numFaces(0), vbStart(0), faceStart(0), tris(nullptr), skin(skin) {}
|
||||
};
|
||||
|
||||
TArray<FVector3> verts;
|
||||
TArray<FVector3> norms;
|
||||
TArray<FVector2> uvs;
|
||||
TArray<OBJFace> faces;
|
||||
TArray<OBJSurface> surfaces;
|
||||
FScanner sc;
|
||||
TArray<OBJTriRef>* vertFaces;
|
||||
|
||||
int ResolveIndex(int origIndex, FaceElement el);
|
||||
template<typename T, size_t L> void ParseVector(TArray<T> &array);
|
||||
bool ParseFaceSide(const FString &side, OBJFace &face, int sidx);
|
||||
void ConstructSurfaceTris(OBJSurface &surf);
|
||||
void AddVertFaces();
|
||||
void TriangulateQuad(const OBJFace &quad, OBJFace *tris);
|
||||
FVector3 RealignVector(FVector3 vecToRealign);
|
||||
FVector2 FixUV(FVector2 vecToRealign);
|
||||
FVector3 CalculateNormalFlat(unsigned int surfIdx, unsigned int triIdx);
|
||||
FVector3 CalculateNormalFlat(OBJTriRef otr);
|
||||
FVector3 CalculateNormalSmooth(unsigned int vidx, unsigned int smoothGroup);
|
||||
public:
|
||||
FOBJModel(): hasMissingNormals(false), hasSmoothGroups(false), vertFaces(nullptr) {}
|
||||
~FOBJModel();
|
||||
bool Load(const char* fn, int lumpnum, const char* buffer, int length) override;
|
||||
int FindFrame(const char* name) override;
|
||||
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation=0) override;
|
||||
void BuildVertexBuffer(FModelRenderer* renderer) override;
|
||||
void AddSkins(uint8_t* hitlist) override;
|
||||
};
|
||||
|
||||
#endif
|
111
source/common/models/model_ue1.h
Normal file
111
source/common/models/model_ue1.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "model.h"
|
||||
#include "vectors.h"
|
||||
|
||||
class FUE1Model : public FModel
|
||||
{
|
||||
public:
|
||||
enum EPolyType
|
||||
{
|
||||
PT_Normal = 0, // normal renderstyle
|
||||
PT_TwoSided = 1, // like normal, but don't cull backfaces
|
||||
PT_Translucent = 2, // additive blending
|
||||
PT_Masked = 3, // draw with alpha testing
|
||||
PT_Modulated = 4, // overlay-like blending (rgb values below 128 darken, 128 is unchanged, and above 128 lighten)
|
||||
// types mask
|
||||
PT_Type = 7,
|
||||
// flags
|
||||
PT_WeaponTriangle = 0x08, // this poly is used for positioning a weapon attachment and should not be drawn
|
||||
PT_Unlit = 0x10, // this poly is fullbright
|
||||
PT_Curvy = 0x20, // this poly uses the facet normal
|
||||
PT_EnvironmentMap = 0x40, // vertex UVs are remapped to their view-space X and Z normals, fake cubemap look
|
||||
PT_NoSmooth = 0x80 // this poly forcibly uses nearest filtering
|
||||
};
|
||||
|
||||
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||
int FindFrame(const char * name) override;
|
||||
void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation=0) override;
|
||||
void BuildVertexBuffer(FModelRenderer *renderer) override;
|
||||
void AddSkins(uint8_t *hitlist) override;
|
||||
void LoadGeometry();
|
||||
void UnloadGeometry();
|
||||
FUE1Model()
|
||||
{
|
||||
mDataLump = -1;
|
||||
mAnivLump = -1;
|
||||
mDataLoaded = false;
|
||||
dhead = NULL;
|
||||
dpolys = NULL;
|
||||
ahead = NULL;
|
||||
averts = NULL;
|
||||
numVerts = 0;
|
||||
numFrames = 0;
|
||||
numPolys = 0;
|
||||
numGroups = 0;
|
||||
}
|
||||
~FUE1Model();
|
||||
|
||||
private:
|
||||
int mDataLump, mAnivLump;
|
||||
bool mDataLoaded;
|
||||
|
||||
// raw data structures
|
||||
struct d3dhead
|
||||
{
|
||||
uint16_t numpolys, numverts;
|
||||
uint16_t bogusrot, bogusframe;
|
||||
uint32_t bogusnorm[3];
|
||||
uint32_t fixscale;
|
||||
uint32_t unused[3];
|
||||
uint8_t padding[12];
|
||||
};
|
||||
struct d3dpoly
|
||||
{
|
||||
uint16_t vertices[3];
|
||||
uint8_t type, color;
|
||||
uint8_t uv[3][2];
|
||||
uint8_t texnum, flags;
|
||||
};
|
||||
struct a3dhead
|
||||
{
|
||||
uint16_t numframes, framesize;
|
||||
};
|
||||
d3dhead * dhead;
|
||||
d3dpoly * dpolys;
|
||||
a3dhead * ahead;
|
||||
uint32_t * averts;
|
||||
struct dxvert
|
||||
{
|
||||
int16_t x, y, z, pad;
|
||||
};
|
||||
dxvert * dxverts;
|
||||
|
||||
// converted data structures
|
||||
struct UE1Vertex
|
||||
{
|
||||
FVector3 Pos, Normal;
|
||||
};
|
||||
struct UE1Poly
|
||||
{
|
||||
int V[3];
|
||||
FVector2 C[3];
|
||||
TArray<FVector3> Normals;
|
||||
};
|
||||
struct UE1Group
|
||||
{
|
||||
TArray<int> P;
|
||||
int numPolys, texNum, type;
|
||||
};
|
||||
|
||||
int numVerts;
|
||||
int numFrames;
|
||||
int numPolys;
|
||||
int numGroups;
|
||||
int weaponPoly; // for future model attachment support, unused for now
|
||||
|
||||
TArray<UE1Vertex> verts;
|
||||
TArray<UE1Poly> polys;
|
||||
TArray<UE1Group> groups;
|
||||
};
|
29
source/common/models/modelrenderer.h
Normal file
29
source/common/models/modelrenderer.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
|
||||
#include "renderstyle.h"
|
||||
#include "matrix.h"
|
||||
#include "model.h"
|
||||
|
||||
class FModelRenderer
|
||||
{
|
||||
public:
|
||||
virtual ~FModelRenderer() = default;
|
||||
|
||||
virtual ModelRendererType GetType() const = 0;
|
||||
|
||||
virtual void BeginDrawModel(FRenderStyle style, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix, bool mirrored) = 0;
|
||||
virtual void EndDrawModel(FRenderStyle style, FSpriteModelFrame *smf) = 0;
|
||||
|
||||
virtual IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) = 0;
|
||||
|
||||
virtual VSMatrix GetViewToWorldMatrix() = 0;
|
||||
|
||||
virtual void BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored) = 0;
|
||||
virtual void EndDrawHUDModel(FRenderStyle style) = 0;
|
||||
|
||||
virtual void SetInterpolation(double interpolation) = 0;
|
||||
virtual void SetMaterial(FGameTexture *skin, bool clampNoFilter, int translation) = 0;
|
||||
virtual void DrawArrays(int start, int count) = 0;
|
||||
virtual void DrawElements(int numIndices, size_t offset) = 0;
|
||||
virtual void SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size) = 0;
|
||||
};
|
||||
|
555
source/common/models/models_md2.cpp
Normal file
555
source/common/models/models_md2.cpp
Normal file
|
@ -0,0 +1,555 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2005-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
/*
|
||||
** models.cpp
|
||||
**
|
||||
** MD2/DMD model format code
|
||||
**
|
||||
**/
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "model_md2.h"
|
||||
#include "texturemanager.h"
|
||||
#include "modelrenderer.h"
|
||||
#include "printf.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4244) // warning C4244: conversion from 'double' to 'float', possible loss of data
|
||||
#endif
|
||||
|
||||
enum { VX, VZ, VY };
|
||||
#define NUMVERTEXNORMALS 162
|
||||
|
||||
static float avertexnormals[NUMVERTEXNORMALS][3] = {
|
||||
#include "tab_anorms.h"
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// UnpackVector
|
||||
// Packed: pppppppy yyyyyyyy. Yaw is on the XY plane.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
static void UnpackVector(unsigned short packed, float vec[3])
|
||||
{
|
||||
float yaw = (packed & 511) / 512.0f * 2 * M_PI;
|
||||
float pitch = ((packed >> 9) / 127.0f - 0.5f) * M_PI;
|
||||
float cosp = (float) cos(pitch);
|
||||
|
||||
vec[VX] = (float) cos(yaw) * cosp;
|
||||
vec[VY] = (float) sin(yaw) * cosp;
|
||||
vec[VZ] = (float) sin(pitch);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// DMD file structure
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
struct dmd_chunk_t
|
||||
{
|
||||
int type;
|
||||
int length; // Next chunk follows...
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct dmd_packedVertex_t
|
||||
{
|
||||
uint8_t vertex[3];
|
||||
unsigned short normal; // Yaw and pitch.
|
||||
};
|
||||
|
||||
struct dmd_packedFrame_t
|
||||
{
|
||||
float scale[3];
|
||||
float translate[3];
|
||||
char name[16];
|
||||
dmd_packedVertex_t vertices[1];
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
// Chunk types.
|
||||
enum
|
||||
{
|
||||
DMC_END, // Must be the last chunk.
|
||||
DMC_INFO // Required; will be expected to exist.
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDMDModel::Load
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FDMDModel::Load(const char * path, int lumpnum, const char * buffer, int length)
|
||||
{
|
||||
dmd_chunk_t * chunk = (dmd_chunk_t*)(buffer + 12);
|
||||
char *temp;
|
||||
ModelFrame *frame;
|
||||
int i;
|
||||
|
||||
int fileoffset = 12 + sizeof(dmd_chunk_t);
|
||||
|
||||
chunk->type = LittleLong(chunk->type);
|
||||
while (chunk->type != DMC_END)
|
||||
{
|
||||
switch (chunk->type)
|
||||
{
|
||||
case DMC_INFO: // Standard DMD information chunk.
|
||||
memcpy(&info, buffer + fileoffset, LittleLong(chunk->length));
|
||||
info.skinWidth = LittleLong(info.skinWidth);
|
||||
info.skinHeight = LittleLong(info.skinHeight);
|
||||
info.frameSize = LittleLong(info.frameSize);
|
||||
info.numSkins = LittleLong(info.numSkins);
|
||||
info.numVertices = LittleLong(info.numVertices);
|
||||
info.numTexCoords = LittleLong(info.numTexCoords);
|
||||
info.numFrames = LittleLong(info.numFrames);
|
||||
info.numLODs = LittleLong(info.numLODs);
|
||||
info.offsetSkins = LittleLong(info.offsetSkins);
|
||||
info.offsetTexCoords = LittleLong(info.offsetTexCoords);
|
||||
info.offsetFrames = LittleLong(info.offsetFrames);
|
||||
info.offsetLODs = LittleLong(info.offsetLODs);
|
||||
info.offsetEnd = LittleLong(info.offsetEnd);
|
||||
fileoffset += chunk->length;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Just skip all unknown chunks.
|
||||
fileoffset += chunk->length;
|
||||
break;
|
||||
}
|
||||
// Read the next chunk header.
|
||||
chunk = (dmd_chunk_t*)(buffer + fileoffset);
|
||||
chunk->type = LittleLong(chunk->type);
|
||||
fileoffset += sizeof(dmd_chunk_t);
|
||||
}
|
||||
|
||||
// Allocate and load in the data.
|
||||
skins = new FTextureID[info.numSkins];
|
||||
|
||||
for (i = 0; i < info.numSkins; i++)
|
||||
{
|
||||
skins[i] = LoadSkin(path, buffer + info.offsetSkins + i * 64);
|
||||
}
|
||||
temp = (char*)buffer + info.offsetFrames;
|
||||
frames = new ModelFrame[info.numFrames];
|
||||
|
||||
for (i = 0, frame = frames; i < info.numFrames; i++, frame++)
|
||||
{
|
||||
dmd_packedFrame_t *pfr = (dmd_packedFrame_t *)(temp + info.frameSize * i);
|
||||
|
||||
memcpy(frame->name, pfr->name, sizeof(pfr->name));
|
||||
frame->vindex = UINT_MAX;
|
||||
}
|
||||
mLumpNum = lumpnum;
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDMDModel::LoadGeometry
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDMDModel::LoadGeometry()
|
||||
{
|
||||
static int axis[3] = { VX, VY, VZ };
|
||||
FileData lumpdata = fileSystem.ReadFile(mLumpNum);
|
||||
const char *buffer = (const char *)lumpdata.GetMem();
|
||||
texCoords = new FTexCoord[info.numTexCoords];
|
||||
memcpy(texCoords, buffer + info.offsetTexCoords, info.numTexCoords * sizeof(FTexCoord));
|
||||
|
||||
const char *temp = buffer + info.offsetFrames;
|
||||
framevtx= new ModelFrameVertexData[info.numFrames];
|
||||
|
||||
ModelFrameVertexData *framev;
|
||||
int i, k, c;
|
||||
for(i = 0, framev = framevtx; i < info.numFrames; i++, framev++)
|
||||
{
|
||||
dmd_packedFrame_t *pfr = (dmd_packedFrame_t *) (temp + info.frameSize * i);
|
||||
dmd_packedVertex_t *pVtx;
|
||||
|
||||
framev->vertices = new DMDModelVertex[info.numVertices];
|
||||
framev->normals = new DMDModelVertex[info.numVertices];
|
||||
|
||||
// Translate each vertex.
|
||||
for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++)
|
||||
{
|
||||
UnpackVector((unsigned short)(pVtx->normal), framev->normals[k].xyz);
|
||||
for(c = 0; c < 3; c++)
|
||||
{
|
||||
framev->vertices[k].xyz[axis[c]] =
|
||||
(pVtx->vertex[c] * float(pfr->scale[c]) + float(pfr->translate[c]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(lodInfo, buffer+info.offsetLODs, info.numLODs * sizeof(DMDLoDInfo));
|
||||
for(i = 0; i < info.numLODs; i++)
|
||||
{
|
||||
lodInfo[i].numTriangles = LittleLong(lodInfo[i].numTriangles);
|
||||
lodInfo[i].offsetTriangles = LittleLong(lodInfo[i].offsetTriangles);
|
||||
if (lodInfo[i].numTriangles > 0)
|
||||
{
|
||||
lods[i].triangles = new FTriangle[lodInfo[i].numTriangles];
|
||||
memcpy(lods[i].triangles, buffer + lodInfo[i].offsetTriangles, lodInfo[i].numTriangles * sizeof(FTriangle));
|
||||
for (int j = 0; j < lodInfo[i].numTriangles; j++)
|
||||
{
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
lods[i].triangles[j].textureIndices[k] = LittleShort(lods[i].triangles[j].textureIndices[k]);
|
||||
lods[i].triangles[j].vertexIndices[k] = LittleShort(lods[i].triangles[j].vertexIndices[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Deletes everything that's no longer needed after building the vertex buffer
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDMDModel::UnloadGeometry()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (framevtx != NULL)
|
||||
{
|
||||
for (i=0;i<info.numFrames;i++)
|
||||
{
|
||||
if (framevtx[i].vertices != NULL) delete [] framevtx[i].vertices;
|
||||
if (framevtx[i].normals != NULL) delete [] framevtx[i].normals;
|
||||
|
||||
framevtx[i].vertices = NULL;
|
||||
framevtx[i].normals = NULL;
|
||||
}
|
||||
delete[] framevtx;
|
||||
framevtx = NULL;
|
||||
}
|
||||
|
||||
for(i = 0; i < info.numLODs; i++)
|
||||
{
|
||||
if (lods[i].triangles != NULL) delete[] lods[i].triangles;
|
||||
lods[i].triangles = NULL;
|
||||
}
|
||||
|
||||
if (texCoords != NULL) delete[] texCoords;
|
||||
texCoords = NULL;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FDMDModel::~FDMDModel()
|
||||
{
|
||||
UnloadGeometry();
|
||||
|
||||
// skins are managed by the texture manager so they must not be deleted here.
|
||||
if (skins != NULL) delete [] skins;
|
||||
if (frames != NULL) delete [] frames;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDMDModel::BuildVertexBuffer(FModelRenderer *renderer)
|
||||
{
|
||||
if (!GetVertexBuffer(renderer->GetType()))
|
||||
{
|
||||
LoadGeometry();
|
||||
|
||||
int VertexBufferSize = info.numFrames * lodInfo[0].numTriangles * 3;
|
||||
unsigned int vindex = 0;
|
||||
|
||||
auto vbuf = renderer->CreateVertexBuffer(false, info.numFrames == 1);
|
||||
SetVertexBuffer(renderer->GetType(), vbuf);
|
||||
|
||||
FModelVertex *vertptr = vbuf->LockVertexBuffer(VertexBufferSize);
|
||||
|
||||
for (int i = 0; i < info.numFrames; i++)
|
||||
{
|
||||
DMDModelVertex *vert = framevtx[i].vertices;
|
||||
DMDModelVertex *norm = framevtx[i].normals;
|
||||
|
||||
frames[i].vindex = vindex;
|
||||
|
||||
FTriangle *tri = lods[0].triangles;
|
||||
|
||||
for (int i = 0; i < lodInfo[0].numTriangles; i++)
|
||||
{
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
|
||||
int ti = tri->textureIndices[j];
|
||||
int vi = tri->vertexIndices[j];
|
||||
|
||||
FModelVertex *bvert = &vertptr[vindex++];
|
||||
bvert->Set(vert[vi].xyz[0], vert[vi].xyz[1], vert[vi].xyz[2], (float)texCoords[ti].s / info.skinWidth, (float)texCoords[ti].t / info.skinHeight);
|
||||
bvert->SetNormal(norm[vi].xyz[0], norm[vi].xyz[1], norm[vi].xyz[2]);
|
||||
}
|
||||
tri++;
|
||||
}
|
||||
}
|
||||
vbuf->UnlockVertexBuffer();
|
||||
UnloadGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// for skin precaching
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDMDModel::AddSkins(uint8_t *hitlist)
|
||||
{
|
||||
for (int i = 0; i < info.numSkins; i++)
|
||||
{
|
||||
if (skins[i].isValid())
|
||||
{
|
||||
hitlist[skins[i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FDMDModel::FindFrame
|
||||
//
|
||||
//===========================================================================
|
||||
int FDMDModel::FindFrame(const char * name)
|
||||
{
|
||||
for (int i=0;i<info.numFrames;i++)
|
||||
{
|
||||
if (!stricmp(name, frames[i].name)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation)
|
||||
{
|
||||
if (frameno >= info.numFrames || frameno2 >= info.numFrames) return;
|
||||
|
||||
if (!skin)
|
||||
{
|
||||
if (info.numSkins == 0 || !skins[0].isValid()) return;
|
||||
skin = TexMan.GetGameTexture(skins[0], true);
|
||||
if (!skin) return;
|
||||
}
|
||||
|
||||
renderer->SetInterpolation(inter);
|
||||
renderer->SetMaterial(skin, false, translation);
|
||||
renderer->SetupFrame(this, frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3);
|
||||
renderer->DrawArrays(0, lodInfo[0].numTriangles * 3);
|
||||
renderer->SetInterpolation(0.f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Internal data structures of MD2 files - only used during loading
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
struct md2_header_t
|
||||
{
|
||||
int magic;
|
||||
int version;
|
||||
int skinWidth;
|
||||
int skinHeight;
|
||||
int frameSize;
|
||||
int numSkins;
|
||||
int numVertices;
|
||||
int numTexCoords;
|
||||
int numTriangles;
|
||||
int numGlCommands;
|
||||
int numFrames;
|
||||
int offsetSkins;
|
||||
int offsetTexCoords;
|
||||
int offsetTriangles;
|
||||
int offsetFrames;
|
||||
int offsetGlCommands;
|
||||
int offsetEnd;
|
||||
};
|
||||
|
||||
struct md2_triangleVertex_t
|
||||
{
|
||||
uint8_t vertex[3];
|
||||
uint8_t lightNormalIndex;
|
||||
};
|
||||
|
||||
struct md2_packedFrame_t
|
||||
{
|
||||
float scale[3];
|
||||
float translate[3];
|
||||
char name[16];
|
||||
md2_triangleVertex_t vertices[1];
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FMD2Model::Load
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FMD2Model::Load(const char * path, int lumpnum, const char * buffer, int length)
|
||||
{
|
||||
md2_header_t * md2header = (md2_header_t *)buffer;
|
||||
ModelFrame *frame;
|
||||
uint8_t *md2_frames;
|
||||
int i;
|
||||
|
||||
// Convert it to DMD.
|
||||
header.magic = MD2_MAGIC;
|
||||
header.version = 8;
|
||||
header.flags = 0;
|
||||
info.skinWidth = LittleLong(md2header->skinWidth);
|
||||
info.skinHeight = LittleLong(md2header->skinHeight);
|
||||
info.frameSize = LittleLong(md2header->frameSize);
|
||||
info.numLODs = 1;
|
||||
info.numSkins = LittleLong(md2header->numSkins);
|
||||
info.numTexCoords = LittleLong(md2header->numTexCoords);
|
||||
info.numVertices = LittleLong(md2header->numVertices);
|
||||
info.numFrames = LittleLong(md2header->numFrames);
|
||||
info.offsetSkins = LittleLong(md2header->offsetSkins);
|
||||
info.offsetTexCoords = LittleLong(md2header->offsetTexCoords);
|
||||
info.offsetFrames = LittleLong(md2header->offsetFrames);
|
||||
info.offsetLODs = LittleLong(md2header->offsetEnd); // Doesn't exist.
|
||||
lodInfo[0].numTriangles = LittleLong(md2header->numTriangles);
|
||||
lodInfo[0].numGlCommands = LittleLong(md2header->numGlCommands);
|
||||
lodInfo[0].offsetTriangles = LittleLong(md2header->offsetTriangles);
|
||||
lodInfo[0].offsetGlCommands = LittleLong(md2header->offsetGlCommands);
|
||||
info.offsetEnd = LittleLong(md2header->offsetEnd);
|
||||
|
||||
if (info.offsetFrames + info.frameSize * info.numFrames > length)
|
||||
{
|
||||
Printf("LoadModel: Model '%s' file too short\n", path);
|
||||
return false;
|
||||
}
|
||||
if (lodInfo[0].numGlCommands <= 0)
|
||||
{
|
||||
Printf("LoadModel: Model '%s' invalid NumGLCommands\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
skins = new FTextureID[info.numSkins];
|
||||
|
||||
for (i = 0; i < info.numSkins; i++)
|
||||
{
|
||||
skins[i] = LoadSkin(path, buffer + info.offsetSkins + i * 64);
|
||||
}
|
||||
|
||||
// The frames need to be unpacked.
|
||||
md2_frames = (uint8_t*)buffer + info.offsetFrames;
|
||||
frames = new ModelFrame[info.numFrames];
|
||||
|
||||
for (i = 0, frame = frames; i < info.numFrames; i++, frame++)
|
||||
{
|
||||
md2_packedFrame_t *pfr = (md2_packedFrame_t *)(md2_frames + info.frameSize * i);
|
||||
|
||||
memcpy(frame->name, pfr->name, sizeof(pfr->name));
|
||||
frame->vindex = UINT_MAX;
|
||||
}
|
||||
mLumpNum = lumpnum;
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FMD2Model::LoadGeometry
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FMD2Model::LoadGeometry()
|
||||
{
|
||||
static int axis[3] = { VX, VY, VZ };
|
||||
uint8_t *md2_frames;
|
||||
FileData lumpdata = fileSystem.ReadFile(mLumpNum);
|
||||
const char *buffer = (const char *)lumpdata.GetMem();
|
||||
|
||||
texCoords = new FTexCoord[info.numTexCoords];
|
||||
memcpy(texCoords, (uint8_t*)buffer + info.offsetTexCoords, info.numTexCoords * sizeof(FTexCoord));
|
||||
|
||||
md2_frames = (uint8_t*)buffer + info.offsetFrames;
|
||||
framevtx = new ModelFrameVertexData[info.numFrames];
|
||||
ModelFrameVertexData *framev;
|
||||
int i, k, c;
|
||||
|
||||
for(i = 0, framev = framevtx; i < info.numFrames; i++, framev++)
|
||||
{
|
||||
md2_packedFrame_t *pfr = (md2_packedFrame_t *) (md2_frames + info.frameSize * i);
|
||||
md2_triangleVertex_t *pVtx;
|
||||
|
||||
framev->vertices = new DMDModelVertex[info.numVertices];
|
||||
framev->normals = new DMDModelVertex[info.numVertices];
|
||||
|
||||
// Translate each vertex.
|
||||
for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++)
|
||||
{
|
||||
memcpy(framev->normals[k].xyz,
|
||||
avertexnormals[pVtx->lightNormalIndex], sizeof(float) * 3);
|
||||
|
||||
for(c = 0; c < 3; c++)
|
||||
{
|
||||
framev->vertices[k].xyz[axis[c]] =
|
||||
(pVtx->vertex[c] * pfr->scale[c] + pfr->translate[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lods[0].triangles = new FTriangle[lodInfo[0].numTriangles];
|
||||
|
||||
int cnt = lodInfo[0].numTriangles;
|
||||
memcpy(lods[0].triangles, buffer + lodInfo[0].offsetTriangles, sizeof(FTriangle) * cnt);
|
||||
for (int j = 0; j < cnt; j++)
|
||||
{
|
||||
for (int k = 0; k < 3; k++)
|
||||
{
|
||||
lods[0].triangles[j].textureIndices[k] = LittleShort(lods[0].triangles[j].textureIndices[k]);
|
||||
lods[0].triangles[j].vertexIndices[k] = LittleShort(lods[0].triangles[j].vertexIndices[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FMD2Model::~FMD2Model()
|
||||
{
|
||||
}
|
||||
|
381
source/common/models/models_md3.cpp
Normal file
381
source/common/models/models_md3.cpp
Normal file
|
@ -0,0 +1,381 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2006-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/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "cmdlib.h"
|
||||
#include "model_md3.h"
|
||||
#include "texturemanager.h"
|
||||
#include "modelrenderer.h"
|
||||
|
||||
#define MAX_QPATH 64
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4244) // warning C4244: conversion from 'double' to 'float', possible loss of data
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// decode the lat/lng normal to a 3 float normal
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
static void UnpackVector(unsigned short packed, float & nx, float & ny, float & nz)
|
||||
{
|
||||
double lat = ( packed >> 8 ) & 0xff;
|
||||
double lng = ( packed & 0xff );
|
||||
lat *= M_PI/128;
|
||||
lng *= M_PI/128;
|
||||
|
||||
nx = cos(lat) * sin(lng);
|
||||
ny = sin(lat) * sin(lng);
|
||||
nz = cos(lng);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// MD3 File structure
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
#pragma pack(4)
|
||||
struct md3_header_t
|
||||
{
|
||||
uint32_t Magic;
|
||||
uint32_t Version;
|
||||
char Name[MAX_QPATH];
|
||||
uint32_t Flags;
|
||||
uint32_t Num_Frames;
|
||||
uint32_t Num_Tags;
|
||||
uint32_t Num_Surfaces;
|
||||
uint32_t Num_Skins;
|
||||
uint32_t Ofs_Frames;
|
||||
uint32_t Ofs_Tags;
|
||||
uint32_t Ofs_Surfaces;
|
||||
uint32_t Ofs_Eof;
|
||||
};
|
||||
|
||||
struct md3_surface_t
|
||||
{
|
||||
uint32_t Magic;
|
||||
char Name[MAX_QPATH];
|
||||
uint32_t Flags;
|
||||
uint32_t Num_Frames;
|
||||
uint32_t Num_Shaders;
|
||||
uint32_t Num_Verts;
|
||||
uint32_t Num_Triangles;
|
||||
uint32_t Ofs_Triangles;
|
||||
uint32_t Ofs_Shaders;
|
||||
uint32_t Ofs_Texcoord;
|
||||
uint32_t Ofs_XYZNormal;
|
||||
uint32_t Ofs_End;
|
||||
};
|
||||
|
||||
struct md3_triangle_t
|
||||
{
|
||||
uint32_t vt_index[3];
|
||||
};
|
||||
|
||||
struct md3_shader_t
|
||||
{
|
||||
char Name[MAX_QPATH];
|
||||
uint32_t index;
|
||||
};
|
||||
|
||||
struct md3_texcoord_t
|
||||
{
|
||||
float s, t;
|
||||
};
|
||||
|
||||
struct md3_vertex_t
|
||||
{
|
||||
short x, y, z, n;
|
||||
};
|
||||
|
||||
struct md3_frame_t
|
||||
{
|
||||
float min_Bounds[3];
|
||||
float max_Bounds[3];
|
||||
float localorigin[3];
|
||||
float radius;
|
||||
char Name[16];
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FMD3Model::Load(const char * path, int lumpnum, const char * buffer, int length)
|
||||
{
|
||||
md3_header_t * hdr = (md3_header_t *)buffer;
|
||||
|
||||
auto numFrames = LittleLong(hdr->Num_Frames);
|
||||
auto numSurfaces = LittleLong(hdr->Num_Surfaces);
|
||||
|
||||
numTags = LittleLong(hdr->Num_Tags);
|
||||
|
||||
md3_frame_t * frm = (md3_frame_t*)(buffer + LittleLong(hdr->Ofs_Frames));
|
||||
|
||||
Frames.Resize(numFrames);
|
||||
for (unsigned i = 0; i < numFrames; i++)
|
||||
{
|
||||
strncpy(Frames[i].Name, frm[i].Name, 16);
|
||||
for (int j = 0; j < 3; j++) Frames[i].origin[j] = frm[i].localorigin[j];
|
||||
}
|
||||
|
||||
md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));
|
||||
|
||||
Surfaces.Resize(numSurfaces);
|
||||
|
||||
for (unsigned i = 0; i < numSurfaces; i++)
|
||||
{
|
||||
MD3Surface * s = &Surfaces[i];
|
||||
md3_surface_t * ss = surf;
|
||||
|
||||
surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));
|
||||
|
||||
s->numSkins = LittleLong(ss->Num_Shaders);
|
||||
s->numTriangles = LittleLong(ss->Num_Triangles);
|
||||
s->numVertices = LittleLong(ss->Num_Verts);
|
||||
|
||||
// copy shaders (skins)
|
||||
md3_shader_t * shader = (md3_shader_t*)(((char*)ss) + LittleLong(ss->Ofs_Shaders));
|
||||
s->Skins.Resize(s->numSkins);
|
||||
|
||||
for (unsigned i = 0; i < s->numSkins; i++)
|
||||
{
|
||||
// [BB] According to the MD3 spec, Name is supposed to include the full path.
|
||||
// ... and since some tools seem to output backslashes, these need to be replaced with forward slashes to work.
|
||||
FixPathSeperator(shader[i].Name);
|
||||
s->Skins[i] = LoadSkin("", shader[i].Name);
|
||||
// [BB] Fall back and check if Name is relative.
|
||||
if (!s->Skins[i].isValid())
|
||||
s->Skins[i] = LoadSkin(path, shader[i].Name);
|
||||
}
|
||||
}
|
||||
mLumpNum = lumpnum;
|
||||
return true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FMD3Model::LoadGeometry()
|
||||
{
|
||||
FileData lumpdata = fileSystem.ReadFile(mLumpNum);
|
||||
const char *buffer = (const char *)lumpdata.GetMem();
|
||||
md3_header_t * hdr = (md3_header_t *)buffer;
|
||||
md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));
|
||||
|
||||
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
||||
{
|
||||
MD3Surface * s = &Surfaces[i];
|
||||
md3_surface_t * ss = surf;
|
||||
|
||||
surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));
|
||||
|
||||
// copy triangle indices
|
||||
md3_triangle_t * tris = (md3_triangle_t*)(((char*)ss) + LittleLong(ss->Ofs_Triangles));
|
||||
s->Tris.Resize(s->numTriangles);
|
||||
|
||||
for (unsigned i = 0; i < s->numTriangles; i++) for (int j = 0; j < 3; j++)
|
||||
{
|
||||
s->Tris[i].VertIndex[j] = LittleLong(tris[i].vt_index[j]);
|
||||
}
|
||||
|
||||
// Load texture coordinates
|
||||
md3_texcoord_t * tc = (md3_texcoord_t*)(((char*)ss) + LittleLong(ss->Ofs_Texcoord));
|
||||
s->Texcoords.Resize(s->numVertices);
|
||||
|
||||
for (unsigned i = 0; i < s->numVertices; i++)
|
||||
{
|
||||
s->Texcoords[i].s = tc[i].s;
|
||||
s->Texcoords[i].t = tc[i].t;
|
||||
}
|
||||
|
||||
// Load vertices and texture coordinates
|
||||
md3_vertex_t * vt = (md3_vertex_t*)(((char*)ss) + LittleLong(ss->Ofs_XYZNormal));
|
||||
s->Vertices.Resize(s->numVertices * Frames.Size());
|
||||
|
||||
for (unsigned i = 0; i < s->numVertices * Frames.Size(); i++)
|
||||
{
|
||||
s->Vertices[i].x = LittleShort(vt[i].x) / 64.f;
|
||||
s->Vertices[i].y = LittleShort(vt[i].y) / 64.f;
|
||||
s->Vertices[i].z = LittleShort(vt[i].z) / 64.f;
|
||||
UnpackVector(LittleShort(vt[i].n), s->Vertices[i].nx, s->Vertices[i].ny, s->Vertices[i].nz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FMD3Model::BuildVertexBuffer(FModelRenderer *renderer)
|
||||
{
|
||||
if (!GetVertexBuffer(renderer->GetType()))
|
||||
{
|
||||
LoadGeometry();
|
||||
|
||||
unsigned int vbufsize = 0;
|
||||
unsigned int ibufsize = 0;
|
||||
|
||||
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
||||
{
|
||||
MD3Surface * surf = &Surfaces[i];
|
||||
vbufsize += Frames.Size() * surf->numVertices;
|
||||
ibufsize += 3 * surf->numTriangles;
|
||||
}
|
||||
|
||||
auto vbuf = renderer->CreateVertexBuffer(true, Frames.Size() == 1);
|
||||
SetVertexBuffer(renderer->GetType(), vbuf);
|
||||
|
||||
FModelVertex *vertptr = vbuf->LockVertexBuffer(vbufsize);
|
||||
unsigned int *indxptr = vbuf->LockIndexBuffer(ibufsize);
|
||||
|
||||
assert(vertptr != nullptr && indxptr != nullptr);
|
||||
|
||||
unsigned int vindex = 0, iindex = 0;
|
||||
|
||||
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
||||
{
|
||||
MD3Surface * surf = &Surfaces[i];
|
||||
|
||||
surf->vindex = vindex;
|
||||
surf->iindex = iindex;
|
||||
for (unsigned j = 0; j < Frames.Size() * surf->numVertices; j++)
|
||||
{
|
||||
MD3Vertex* vert = &surf->Vertices[j];
|
||||
|
||||
FModelVertex *bvert = &vertptr[vindex++];
|
||||
|
||||
int tc = j % surf->numVertices;
|
||||
bvert->Set(vert->x, vert->z, vert->y, surf->Texcoords[tc].s, surf->Texcoords[tc].t);
|
||||
bvert->SetNormal(vert->nx, vert->nz, vert->ny);
|
||||
}
|
||||
|
||||
for (unsigned k = 0; k < surf->numTriangles; k++)
|
||||
{
|
||||
for (int l = 0; l < 3; l++)
|
||||
{
|
||||
indxptr[iindex++] = surf->Tris[k].VertIndex[l];
|
||||
}
|
||||
}
|
||||
surf->UnloadGeometry();
|
||||
}
|
||||
vbuf->UnlockVertexBuffer();
|
||||
vbuf->UnlockIndexBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// for skin precaching
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FMD3Model::AddSkins(uint8_t *hitlist)
|
||||
{
|
||||
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
||||
{
|
||||
if (curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
|
||||
{
|
||||
hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
|
||||
MD3Surface * surf = &Surfaces[i];
|
||||
for (unsigned j = 0; j < surf->numSkins; j++)
|
||||
{
|
||||
if (surf->Skins[j].isValid())
|
||||
{
|
||||
hitlist[surf->Skins[j].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
int FMD3Model::FindFrame(const char * name)
|
||||
{
|
||||
for (unsigned i = 0; i < Frames.Size(); i++)
|
||||
{
|
||||
if (!stricmp(name, Frames[i].Name)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation)
|
||||
{
|
||||
if ((unsigned)frameno >= Frames.Size() || (unsigned)frameno2 >= Frames.Size()) return;
|
||||
|
||||
renderer->SetInterpolation(inter);
|
||||
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
||||
{
|
||||
MD3Surface * surf = &Surfaces[i];
|
||||
|
||||
// [BB] In case no skin is specified via MODELDEF, check if the MD3 has a skin for the current surface.
|
||||
// Note: Each surface may have a different skin.
|
||||
FGameTexture *surfaceSkin = skin;
|
||||
if (!surfaceSkin)
|
||||
{
|
||||
if (curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
|
||||
{
|
||||
surfaceSkin = TexMan.GetGameTexture(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i], true);
|
||||
}
|
||||
else if (surf->numSkins > 0 && surf->Skins[0].isValid())
|
||||
{
|
||||
surfaceSkin = TexMan.GetGameTexture(surf->Skins[0], true);
|
||||
}
|
||||
|
||||
if (!surfaceSkin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
renderer->SetMaterial(surfaceSkin, false, translation);
|
||||
renderer->SetupFrame(this, surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices);
|
||||
renderer->DrawElements(surf->numTriangles * 3, surf->iindex * sizeof(unsigned int));
|
||||
}
|
||||
renderer->SetInterpolation(0.f);
|
||||
}
|
||||
|
700
source/common/models/models_obj.cpp
Normal file
700
source/common/models/models_obj.cpp
Normal file
|
@ -0,0 +1,700 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2018 Kevin Caccamo
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "model_obj.h"
|
||||
#include "texturemanager.h"
|
||||
#include "modelrenderer.h"
|
||||
#include "printf.h"
|
||||
#include "textureid.h"
|
||||
|
||||
/**
|
||||
* Load an OBJ model
|
||||
*
|
||||
* @param fn The path to the model file
|
||||
* @param lumpnum The lump index in the wad collection
|
||||
* @param buffer The contents of the model file
|
||||
* @param length The size of the model file
|
||||
* @return Whether or not the model was parsed successfully
|
||||
*/
|
||||
bool FOBJModel::Load(const char* fn, int lumpnum, const char* buffer, int length)
|
||||
{
|
||||
FString objName = fileSystem.GetFileFullPath(lumpnum);
|
||||
FString objBuf(buffer, length);
|
||||
|
||||
// Do some replacements before we parse the OBJ string
|
||||
{
|
||||
// Ensure usemtl statements remain intact
|
||||
TArray<FString> mtlUsages;
|
||||
TArray<long> mtlUsageIdxs;
|
||||
long bpos = 0, nlpos = 0, slashpos = 0;
|
||||
while (1)
|
||||
{
|
||||
bpos = objBuf.IndexOf("\nusemtl", bpos);
|
||||
if (bpos == -1) break;
|
||||
slashpos = objBuf.IndexOf('/', bpos);
|
||||
nlpos = objBuf.IndexOf('\n', ++bpos);
|
||||
if (slashpos > nlpos || slashpos == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (nlpos == -1)
|
||||
{
|
||||
nlpos = (long)objBuf.Len();
|
||||
}
|
||||
FString lineStr(objBuf.GetChars() + bpos, nlpos - bpos);
|
||||
mtlUsages.Push(lineStr);
|
||||
mtlUsageIdxs.Push(bpos);
|
||||
}
|
||||
|
||||
// Replace forward slashes with percent signs so they aren't parsed as line comments
|
||||
objBuf.ReplaceChars('/', *newSideSep);
|
||||
char* wObjBuf = objBuf.LockBuffer();
|
||||
|
||||
// Substitute broken usemtl statements with old ones
|
||||
for (size_t i = 0; i < mtlUsages.Size(); i++)
|
||||
{
|
||||
bpos = mtlUsageIdxs[i];
|
||||
nlpos = objBuf.IndexOf('\n', bpos);
|
||||
if (nlpos == -1)
|
||||
{
|
||||
nlpos = (long)objBuf.Len();
|
||||
}
|
||||
memcpy(wObjBuf + bpos, mtlUsages[i].GetChars(), nlpos - bpos);
|
||||
}
|
||||
|
||||
bpos = 0;
|
||||
// Find each OBJ line comment, and convert each to a C-style line comment
|
||||
while (1)
|
||||
{
|
||||
bpos = objBuf.IndexOf('#', bpos);
|
||||
if (bpos == -1) break;
|
||||
if (objBuf[(unsigned int)bpos + 1] == '\n')
|
||||
{
|
||||
wObjBuf[bpos] = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
wObjBuf[bpos] = '/';
|
||||
wObjBuf[bpos+1] = '/';
|
||||
}
|
||||
bpos += 1;
|
||||
}
|
||||
wObjBuf = nullptr;
|
||||
objBuf.UnlockBuffer();
|
||||
}
|
||||
sc.OpenString(objName, objBuf);
|
||||
|
||||
FTextureID curMtl = FNullTextureID();
|
||||
OBJSurface *curSurface = nullptr;
|
||||
unsigned int aggSurfFaceCount = 0;
|
||||
unsigned int curSurfFaceCount = 0;
|
||||
unsigned int curSmoothGroup = 0;
|
||||
|
||||
while(sc.GetString())
|
||||
{
|
||||
if (sc.Compare("v")) // Vertex
|
||||
{
|
||||
ParseVector<FVector3, 3>(this->verts);
|
||||
}
|
||||
else if (sc.Compare("vn")) // Vertex normal
|
||||
{
|
||||
ParseVector<FVector3, 3>(this->norms);
|
||||
}
|
||||
else if (sc.Compare("vt")) // UV Coordinates
|
||||
{
|
||||
ParseVector<FVector2, 2>(this->uvs);
|
||||
}
|
||||
else if (sc.Compare("usemtl"))
|
||||
{
|
||||
// Get material name and try to load it
|
||||
sc.MustGetString();
|
||||
|
||||
curMtl = LoadSkin("", sc.String);
|
||||
if (!curMtl.isValid())
|
||||
{
|
||||
// Relative to model file path?
|
||||
curMtl = LoadSkin(fn, sc.String);
|
||||
}
|
||||
|
||||
if (!curMtl.isValid())
|
||||
{
|
||||
sc.ScriptMessage("Material %s (#%u) not found.", sc.String, surfaces.Size());
|
||||
}
|
||||
|
||||
// Build surface...
|
||||
if (curSurface == nullptr)
|
||||
{
|
||||
// First surface
|
||||
curSurface = new OBJSurface(curMtl);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curSurfFaceCount > 0)
|
||||
{
|
||||
// Add previous surface
|
||||
curSurface->numFaces = curSurfFaceCount;
|
||||
curSurface->faceStart = aggSurfFaceCount;
|
||||
surfaces.Push(*curSurface);
|
||||
delete curSurface;
|
||||
// Go to next surface
|
||||
curSurface = new OBJSurface(curMtl);
|
||||
aggSurfFaceCount += curSurfFaceCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
curSurface->skin = curMtl;
|
||||
}
|
||||
}
|
||||
curSurfFaceCount = 0;
|
||||
}
|
||||
else if (sc.Compare("f"))
|
||||
{
|
||||
FString sides[4];
|
||||
OBJFace face;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// A face must have at least 3 sides
|
||||
sc.MustGetString();
|
||||
sides[i] = sc.String;
|
||||
if (!ParseFaceSide(sides[i], face, i)) return false;
|
||||
}
|
||||
face.sideCount = 3;
|
||||
if (sc.GetString())
|
||||
{
|
||||
if (!sc.Compare("f") && FString(sc.String).IndexOfAny("-0123456789") == 0)
|
||||
{
|
||||
sides[3] = sc.String;
|
||||
face.sideCount += 1;
|
||||
if (!ParseFaceSide(sides[3], face, 3)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.UnGet(); // No 4th side, move back
|
||||
}
|
||||
}
|
||||
face.smoothGroup = curSmoothGroup;
|
||||
faces.Push(face);
|
||||
curSurfFaceCount += 1;
|
||||
}
|
||||
else if (sc.Compare("s"))
|
||||
{
|
||||
sc.MustGetString();
|
||||
if (sc.Compare("off"))
|
||||
{
|
||||
curSmoothGroup = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.UnGet();
|
||||
sc.MustGetNumber();
|
||||
curSmoothGroup = sc.Number;
|
||||
hasSmoothGroups = hasSmoothGroups || curSmoothGroup > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
sc.Close();
|
||||
|
||||
if (curSurface == nullptr)
|
||||
{ // No valid materials detected
|
||||
FTextureID dummyMtl = LoadSkin("", "-NOFLAT-"); // Built-in to GZDoom
|
||||
curSurface = new OBJSurface(dummyMtl);
|
||||
}
|
||||
curSurface->numFaces = curSurfFaceCount;
|
||||
curSurface->faceStart = aggSurfFaceCount;
|
||||
surfaces.Push(*curSurface);
|
||||
delete curSurface;
|
||||
|
||||
if (uvs.Size() == 0)
|
||||
{ // Needed so that OBJs without UVs can work
|
||||
uvs.Push(FVector2(0.0, 0.0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an x-Dimensional vector
|
||||
*
|
||||
* @tparam T A subclass of TVector2 to be used
|
||||
* @tparam L The length of the vector to parse
|
||||
* @param[out] array The array to append the parsed vector to
|
||||
*/
|
||||
template<typename T, size_t L> void FOBJModel::ParseVector(TArray<T> &array)
|
||||
{
|
||||
float coord[L];
|
||||
for (size_t axis = 0; axis < L; axis++)
|
||||
{
|
||||
sc.MustGetFloat();
|
||||
coord[axis] = (float)sc.Float;
|
||||
}
|
||||
T vec(coord);
|
||||
array.Push(vec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a side of a face
|
||||
*
|
||||
* @param[in] sideStr The side definition string
|
||||
* @param[out] face The face to assign the parsed side data to
|
||||
* @param sidx The 0-based index of the side
|
||||
* @return Whether or not the face side was parsed successfully
|
||||
*/
|
||||
bool FOBJModel::ParseFaceSide(const FString &sideStr, OBJFace &face, int sidx)
|
||||
{
|
||||
OBJFaceSide side;
|
||||
int origIdx;
|
||||
if (sideStr.IndexOf(newSideSep) >= 0)
|
||||
{
|
||||
TArray<FString> sides = sideStr.Split(newSideSep, FString::TOK_KEEPEMPTY);
|
||||
|
||||
if (sides[0].Len() > 0)
|
||||
{
|
||||
origIdx = atoi(sides[0].GetChars());
|
||||
side.vertref = ResolveIndex(origIdx, FaceElement::VertexIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.ScriptError("Vertex reference is not optional!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sides[1].Len() > 0)
|
||||
{
|
||||
origIdx = atoi(sides[1].GetChars());
|
||||
side.uvref = ResolveIndex(origIdx, FaceElement::UVIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
side.uvref = -1;
|
||||
}
|
||||
|
||||
if (sides.Size() > 2)
|
||||
{
|
||||
if (sides[2].Len() > 0)
|
||||
{
|
||||
origIdx = atoi(sides[2].GetChars());
|
||||
side.normref = ResolveIndex(origIdx, FaceElement::VNormalIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
side.normref = -1;
|
||||
hasMissingNormals = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
side.normref = -1;
|
||||
hasMissingNormals = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
origIdx = atoi(sideStr.GetChars());
|
||||
side.vertref = ResolveIndex(origIdx, FaceElement::VertexIndex);
|
||||
side.normref = -1;
|
||||
hasMissingNormals = true;
|
||||
side.uvref = -1;
|
||||
}
|
||||
face.sides[sidx] = side;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve an OBJ index to an absolute index
|
||||
*
|
||||
* OBJ indices are 1-based, and can also be negative
|
||||
*
|
||||
* @param origIndex The original OBJ index to resolve
|
||||
* @param el What type of element the index references
|
||||
* @return The absolute index of the element
|
||||
*/
|
||||
int FOBJModel::ResolveIndex(int origIndex, FaceElement el)
|
||||
{
|
||||
if (origIndex > 0)
|
||||
{
|
||||
return origIndex - 1; // OBJ indices start at 1
|
||||
}
|
||||
else if (origIndex < 0)
|
||||
{
|
||||
if (el == FaceElement::VertexIndex)
|
||||
{
|
||||
return verts.Size() + origIndex; // origIndex is negative
|
||||
}
|
||||
else if (el == FaceElement::UVIndex)
|
||||
{
|
||||
return uvs.Size() + origIndex;
|
||||
}
|
||||
else if (el == FaceElement::VNormalIndex)
|
||||
{
|
||||
return norms.Size() + origIndex;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the vertex buffer for this model
|
||||
*
|
||||
* @param renderer A pointer to the model renderer. Used to allocate the vertex buffer.
|
||||
*/
|
||||
void FOBJModel::BuildVertexBuffer(FModelRenderer *renderer)
|
||||
{
|
||||
if (GetVertexBuffer(renderer->GetType()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int vbufsize = 0;
|
||||
|
||||
for (size_t i = 0; i < surfaces.Size(); i++)
|
||||
{
|
||||
ConstructSurfaceTris(surfaces[i]);
|
||||
surfaces[i].vbStart = vbufsize;
|
||||
vbufsize += surfaces[i].numTris * 3;
|
||||
}
|
||||
// Initialize/populate vertFaces
|
||||
if (hasMissingNormals && hasSmoothGroups)
|
||||
{
|
||||
AddVertFaces();
|
||||
}
|
||||
|
||||
auto vbuf = renderer->CreateVertexBuffer(false,true);
|
||||
SetVertexBuffer(renderer->GetType(), vbuf);
|
||||
|
||||
FModelVertex *vertptr = vbuf->LockVertexBuffer(vbufsize);
|
||||
|
||||
for (unsigned int i = 0; i < surfaces.Size(); i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < surfaces[i].numTris; j++)
|
||||
{
|
||||
for (size_t side = 0; side < 3; side++)
|
||||
{
|
||||
FModelVertex *mdv = vertptr +
|
||||
side + j * 3 + // Current surface and previous triangles
|
||||
surfaces[i].vbStart; // Previous surfaces
|
||||
|
||||
OBJFaceSide &curSide = surfaces[i].tris[j].sides[2 - side];
|
||||
|
||||
int vidx = curSide.vertref;
|
||||
int uvidx = (curSide.uvref >= 0 && (unsigned int)curSide.uvref < uvs.Size()) ? curSide.uvref : 0;
|
||||
int nidx = curSide.normref;
|
||||
|
||||
FVector3 curVvec = RealignVector(verts[vidx]);
|
||||
FVector2 curUvec = FixUV(uvs[uvidx]);
|
||||
FVector3 nvec;
|
||||
|
||||
mdv->Set(curVvec.X, curVvec.Y, curVvec.Z, curUvec.X, curUvec.Y);
|
||||
|
||||
if (nidx >= 0 && (unsigned int)nidx < norms.Size())
|
||||
{
|
||||
nvec = RealignVector(norms[nidx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (surfaces[i].tris[j].smoothGroup == 0)
|
||||
{
|
||||
nvec = CalculateNormalFlat(i, j);
|
||||
}
|
||||
else
|
||||
{
|
||||
nvec = CalculateNormalSmooth(vidx, surfaces[i].tris[j].smoothGroup);
|
||||
}
|
||||
}
|
||||
mdv->SetNormal(nvec.X, nvec.Y, nvec.Z);
|
||||
}
|
||||
}
|
||||
delete[] surfaces[i].tris;
|
||||
}
|
||||
|
||||
// Destroy vertFaces
|
||||
if (hasMissingNormals && hasSmoothGroups)
|
||||
{
|
||||
for (size_t i = 0; i < verts.Size(); i++)
|
||||
{
|
||||
vertFaces[i].Clear();
|
||||
}
|
||||
delete[] vertFaces;
|
||||
}
|
||||
vbuf->UnlockVertexBuffer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill in the triangle data for a surface
|
||||
*
|
||||
* @param[in,out] surf The surface to fill in the triangle data for
|
||||
*/
|
||||
void FOBJModel::ConstructSurfaceTris(OBJSurface &surf)
|
||||
{
|
||||
unsigned int triCount = 0;
|
||||
|
||||
size_t start = surf.faceStart;
|
||||
size_t end = start + surf.numFaces;
|
||||
for (size_t i = start; i < end; i++)
|
||||
{
|
||||
triCount += faces[i].sideCount - 2;
|
||||
}
|
||||
|
||||
surf.numTris = triCount;
|
||||
surf.tris = new OBJFace[triCount];
|
||||
|
||||
for (size_t i = start, triIdx = 0; i < end; i++, triIdx++)
|
||||
{
|
||||
surf.tris[triIdx].sideCount = 3;
|
||||
if (faces[i].sideCount == 3)
|
||||
{
|
||||
surf.tris[triIdx].smoothGroup = faces[i].smoothGroup;
|
||||
memcpy(surf.tris[triIdx].sides, faces[i].sides, sizeof(OBJFaceSide) * 3);
|
||||
}
|
||||
else if (faces[i].sideCount == 4) // Triangulate face
|
||||
{
|
||||
OBJFace *triangulated = new OBJFace[2];
|
||||
TriangulateQuad(faces[i], triangulated);
|
||||
memcpy(surf.tris[triIdx].sides, triangulated[0].sides, sizeof(OBJFaceSide) * 3);
|
||||
memcpy(surf.tris[triIdx+1].sides, triangulated[1].sides, sizeof(OBJFaceSide) * 3);
|
||||
delete[] triangulated;
|
||||
triIdx += 1; // Filling out two faces
|
||||
}
|
||||
DPrintf(DMSG_SPAMMY, "Smooth group: %d\n", surf.tris[triIdx].smoothGroup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triangulate a 4-sided face
|
||||
*
|
||||
* @param[in] quad The 4-sided face to triangulate
|
||||
* @param[out] tris The resultant triangle data
|
||||
*/
|
||||
void FOBJModel::TriangulateQuad(const OBJFace &quad, OBJFace *tris)
|
||||
{
|
||||
tris[0].sideCount = 3;
|
||||
tris[0].smoothGroup = quad.smoothGroup;
|
||||
tris[1].sideCount = 3;
|
||||
tris[1].smoothGroup = quad.smoothGroup;
|
||||
|
||||
int tsidx[2][3] = {{0, 1, 3}, {1, 2, 3}};
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
tris[j].sides[i].vertref = quad.sides[tsidx[j][i]].vertref;
|
||||
tris[j].sides[i].uvref = quad.sides[tsidx[j][i]].uvref;
|
||||
tris[j].sides[i].normref = quad.sides[tsidx[j][i]].normref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the vertices of all surfaces' triangles to the array of vertex->triangle references
|
||||
*/
|
||||
void FOBJModel::AddVertFaces() {
|
||||
// Initialize and populate vertFaces - this array stores references to triangles per vertex
|
||||
vertFaces = new TArray<OBJTriRef>[verts.Size()];
|
||||
for (unsigned int i = 0; i < surfaces.Size(); i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < surfaces[i].numTris; j++)
|
||||
{
|
||||
OBJTriRef otr = OBJTriRef(i, j);
|
||||
for (size_t k = 0; k < surfaces[i].tris[j].sideCount; k++)
|
||||
{
|
||||
int vidx = surfaces[i].tris[j].sides[k].vertref;
|
||||
vertFaces[vidx].Push(otr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-align a vector to match MD3 alignment
|
||||
*
|
||||
* @param vecToRealign The vector to re-align
|
||||
* @return The re-aligned vector
|
||||
*/
|
||||
inline FVector3 FOBJModel::RealignVector(FVector3 vecToRealign)
|
||||
{
|
||||
vecToRealign.Z *= -1;
|
||||
return vecToRealign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix UV coordinates of a UV vector
|
||||
*
|
||||
* @param vecToRealign The vector to fix
|
||||
* @return The fixed UV coordinate vector
|
||||
*/
|
||||
inline FVector2 FOBJModel::FixUV(FVector2 vecToRealign)
|
||||
{
|
||||
vecToRealign.Y *= -1;
|
||||
return vecToRealign;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the surface normal for a triangle
|
||||
*
|
||||
* @param surfIdx The surface index
|
||||
* @param triIdx The triangle Index
|
||||
* @return The surface normal vector
|
||||
*/
|
||||
FVector3 FOBJModel::CalculateNormalFlat(unsigned int surfIdx, unsigned int triIdx)
|
||||
{
|
||||
// https://www.khronos.org/opengl/wiki/Calculating_a_Surface_Normal
|
||||
int curVert = surfaces[surfIdx].tris[triIdx].sides[0].vertref;
|
||||
int nextVert = surfaces[surfIdx].tris[triIdx].sides[2].vertref;
|
||||
int lastVert = surfaces[surfIdx].tris[triIdx].sides[1].vertref;
|
||||
|
||||
// Cross-multiply the U-vector and V-vector
|
||||
FVector3 curVvec = RealignVector(verts[curVert]);
|
||||
FVector3 uvec = RealignVector(verts[nextVert]) - curVvec;
|
||||
FVector3 vvec = RealignVector(verts[lastVert]) - curVvec;
|
||||
|
||||
return uvec ^ vvec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the surface normal for a triangle
|
||||
*
|
||||
* @param otr A reference to the surface, and a triangle within that surface, as an OBJTriRef
|
||||
* @return The surface normal vector
|
||||
*/
|
||||
FVector3 FOBJModel::CalculateNormalFlat(OBJTriRef otr)
|
||||
{
|
||||
return CalculateNormalFlat(otr.surf, otr.tri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the normal of a vertex in a specific smooth group
|
||||
*
|
||||
* @param vidx The index of the vertex in the array of vertices
|
||||
* @param smoothGroup The smooth group number
|
||||
*/
|
||||
FVector3 FOBJModel::CalculateNormalSmooth(unsigned int vidx, unsigned int smoothGroup)
|
||||
{
|
||||
unsigned int connectedFaces = 0;
|
||||
TArray<OBJTriRef>& vTris = vertFaces[vidx];
|
||||
|
||||
FVector3 vNormal(0,0,0);
|
||||
for (size_t face = 0; face < vTris.Size(); face++)
|
||||
{
|
||||
OBJFace& tri = surfaces[vTris[face].surf].tris[vTris[face].tri];
|
||||
if (tri.smoothGroup == smoothGroup)
|
||||
{
|
||||
FVector3 fNormal = CalculateNormalFlat(vTris[face]);
|
||||
connectedFaces += 1;
|
||||
vNormal += fNormal;
|
||||
}
|
||||
}
|
||||
vNormal /= (float)connectedFaces;
|
||||
return vNormal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the frame with the given name
|
||||
*
|
||||
* OBJ models are not animated, so this always returns 0
|
||||
*
|
||||
* @param name The name of the frame
|
||||
* @return The index of the frame
|
||||
*/
|
||||
int FOBJModel::FindFrame(const char* name)
|
||||
{
|
||||
return 0; // OBJs are not animated.
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the model
|
||||
*
|
||||
* @param renderer The model renderer
|
||||
* @param skin The loaded skin for the surface
|
||||
* @param frameno Unused
|
||||
* @param frameno2 Unused
|
||||
* @param inter Unused
|
||||
* @param translation The translation for the skin
|
||||
*/
|
||||
void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation)
|
||||
{
|
||||
for (unsigned int i = 0; i < surfaces.Size(); i++)
|
||||
{
|
||||
OBJSurface *surf = &surfaces[i];
|
||||
|
||||
FGameTexture *userSkin = skin;
|
||||
if (!userSkin)
|
||||
{
|
||||
if (i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
|
||||
{
|
||||
userSkin = TexMan.GetGameTexture(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i], true);
|
||||
}
|
||||
else if (surf->skin.isValid())
|
||||
{
|
||||
userSkin = TexMan.GetGameTexture(surf->skin, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Still no skin after checking for one?
|
||||
if (!userSkin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
renderer->SetMaterial(userSkin, false, translation);
|
||||
renderer->SetupFrame(this, surf->vbStart, surf->vbStart, surf->numTris * 3);
|
||||
renderer->DrawArrays(0, surf->numTris * 3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-cache skins for the model
|
||||
*
|
||||
* @param hitlist The list of textures
|
||||
*/
|
||||
void FOBJModel::AddSkins(uint8_t* hitlist)
|
||||
{
|
||||
for (size_t i = 0; i < surfaces.Size(); i++)
|
||||
{
|
||||
if (i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
|
||||
{
|
||||
// Precache skins manually reassigned by the user.
|
||||
// On OBJs with lots of skins, such as Doom map OBJs exported from GZDB,
|
||||
// there may be too many skins for the user to manually change, unless
|
||||
// the limit is bumped or surfaceskinIDs is changed to a TArray<FTextureID>.
|
||||
hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
return; // No need to precache skin that was replaced
|
||||
}
|
||||
|
||||
OBJSurface * surf = &surfaces[i];
|
||||
if (surf->skin.isValid())
|
||||
{
|
||||
hitlist[surf->skin.GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the data that was loaded
|
||||
*/
|
||||
FOBJModel::~FOBJModel()
|
||||
{
|
||||
verts.Clear();
|
||||
norms.Clear();
|
||||
uvs.Clear();
|
||||
faces.Clear();
|
||||
surfaces.Clear();
|
||||
}
|
307
source/common/models/models_ue1.cpp
Normal file
307
source/common/models/models_ue1.cpp
Normal file
|
@ -0,0 +1,307 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2018 Marisa Kirisame
|
||||
// All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with this program. If not, see http://www.gnu.org/licenses/
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "cmdlib.h"
|
||||
#include "model_ue1.h"
|
||||
#include "texturemanager.h"
|
||||
#include "modelrenderer.h"
|
||||
|
||||
float unpackuvert( uint32_t n, int c )
|
||||
{
|
||||
switch( c )
|
||||
{
|
||||
case 0:
|
||||
return ((int16_t)((n&0x7ff)<<5))/128.f;
|
||||
case 1:
|
||||
return ((int16_t)(((n>>11)&0x7ff)<<5))/128.f;
|
||||
case 2:
|
||||
return ((int16_t)(((n>>22)&0x3ff)<<6))/128.f;
|
||||
default:
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
bool FUE1Model::Load( const char *filename, int lumpnum, const char *buffer, int length )
|
||||
{
|
||||
int lumpnum2;
|
||||
FString realfilename = fileSystem.GetFileFullName(lumpnum);
|
||||
if ( (size_t)realfilename.IndexOf("_d.3d") == realfilename.Len()-5 )
|
||||
{
|
||||
realfilename.Substitute("_d.3d","_a.3d");
|
||||
lumpnum2 = fileSystem.CheckNumForFullName(realfilename);
|
||||
mDataLump = lumpnum;
|
||||
mAnivLump = lumpnum2;
|
||||
}
|
||||
else
|
||||
{
|
||||
realfilename.Substitute("_a.3d","_d.3d");
|
||||
lumpnum2 = fileSystem.CheckNumForFullName(realfilename);
|
||||
mAnivLump = lumpnum;
|
||||
mDataLump = lumpnum2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FUE1Model::LoadGeometry()
|
||||
{
|
||||
FileData lump, lump2;
|
||||
const char *buffer, *buffer2;
|
||||
lump = fileSystem.ReadFile(mDataLump);
|
||||
buffer = (char*)lump.GetMem();
|
||||
lump2 = fileSystem.ReadFile(mAnivLump);
|
||||
buffer2 = (char*)lump2.GetMem();
|
||||
// map structures
|
||||
dhead = (d3dhead*)(buffer);
|
||||
dpolys = (d3dpoly*)(buffer+sizeof(d3dhead));
|
||||
ahead = (a3dhead*)(buffer2);
|
||||
// detect deus ex format
|
||||
if ( (ahead->framesize/dhead->numverts) == 8 )
|
||||
{
|
||||
averts = NULL;
|
||||
dxverts = (dxvert*)(buffer2+sizeof(a3dhead));
|
||||
}
|
||||
else
|
||||
{
|
||||
averts = (uint32_t*)(buffer2+sizeof(a3dhead));
|
||||
dxverts = NULL;
|
||||
}
|
||||
weaponPoly = -1;
|
||||
// set counters
|
||||
numVerts = dhead->numverts;
|
||||
numFrames = ahead->numframes;
|
||||
numPolys = dhead->numpolys;
|
||||
numGroups = 0;
|
||||
// populate vertex arrays
|
||||
for ( int i=0; i<numFrames; i++ )
|
||||
{
|
||||
for ( int j=0; j<numVerts; j++ )
|
||||
{
|
||||
UE1Vertex Vert;
|
||||
if ( dxverts != NULL )
|
||||
{
|
||||
// convert padded XYZ16
|
||||
Vert.Pos = FVector3(dxverts[j+i*numVerts].x,
|
||||
dxverts[j+i*numVerts].z,
|
||||
(float)-dxverts[j+i*numVerts].y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert packed XY11Z10
|
||||
Vert.Pos = FVector3(unpackuvert(averts[j+i*numVerts],0),
|
||||
unpackuvert(averts[j+i*numVerts],2),
|
||||
-unpackuvert(averts[j+i*numVerts],1));
|
||||
}
|
||||
// push vertex (without normals, will be calculated later)
|
||||
verts.Push(Vert);
|
||||
}
|
||||
}
|
||||
// populate poly arrays
|
||||
for ( int i=0; i<numPolys; i++ )
|
||||
{
|
||||
UE1Poly Poly;
|
||||
// set indices
|
||||
for ( int j=0; j<3; j++ )
|
||||
Poly.V[j] = dpolys[i].vertices[j];
|
||||
// unpack coords
|
||||
for ( int j=0; j<3; j++ )
|
||||
Poly.C[j] = FVector2(dpolys[i].uv[j][0]/255.f,dpolys[i].uv[j][1]/255.f);
|
||||
// compute facet normals
|
||||
for ( int j=0; j<numFrames; j++ )
|
||||
{
|
||||
FVector3 dir[2];
|
||||
dir[0] = verts[Poly.V[1]+numVerts*j].Pos-verts[Poly.V[0]+numVerts*j].Pos;
|
||||
dir[1] = verts[Poly.V[2]+numVerts*j].Pos-verts[Poly.V[0]+numVerts*j].Pos;
|
||||
Poly.Normals.Push((dir[0]^dir[1]).Unit());
|
||||
}
|
||||
// push
|
||||
polys.Push(Poly);
|
||||
}
|
||||
// compute normals for vertex arrays
|
||||
// iterates through all polys and compute the average of all facet normals
|
||||
// from those who use this vertex. not pretty, but does the job
|
||||
for ( int i=0; i<numFrames; i++ )
|
||||
{
|
||||
for ( int j=0; j<numVerts; j++ )
|
||||
{
|
||||
FVector3 nsum = FVector3(0,0,0);
|
||||
for ( int k=0; k<numPolys; k++ )
|
||||
{
|
||||
if ( (polys[k].V[0] != j) && (polys[k].V[1] != j) && (polys[k].V[2] != j) ) continue;
|
||||
nsum += polys[k].Normals[i];
|
||||
}
|
||||
verts[j+numVerts*i].Normal = nsum.Unit();
|
||||
}
|
||||
}
|
||||
// populate poly groups (subdivided by texture number and type)
|
||||
// this method minimizes searches in the group list as much as possible
|
||||
// while still doing a single pass through the poly list
|
||||
int curgroup = -1;
|
||||
UE1Group Group;
|
||||
for ( int i=0; i<numPolys; i++ )
|
||||
{
|
||||
// while we're at it, look for the weapon triangle
|
||||
if ( dpolys[i].type&PT_WeaponTriangle ) weaponPoly = i;
|
||||
if ( curgroup == -1 )
|
||||
{
|
||||
// no group, create it
|
||||
Group.P.Reset();
|
||||
Group.numPolys = 0;
|
||||
Group.texNum = dpolys[i].texnum;
|
||||
Group.type = dpolys[i].type;
|
||||
groups.Push(Group);
|
||||
curgroup = numGroups++;
|
||||
}
|
||||
else if ( (dpolys[i].texnum != groups[curgroup].texNum) || (dpolys[i].type != groups[curgroup].type) )
|
||||
{
|
||||
// different attributes than last time
|
||||
// search for existing group with new attributes, create one if not found
|
||||
curgroup = -1;
|
||||
for ( int j=0; j<numGroups; j++ )
|
||||
{
|
||||
if ( (groups[j].texNum != dpolys[i].texnum) || (groups[j].type != dpolys[i].type) ) continue;
|
||||
curgroup = j;
|
||||
break;
|
||||
}
|
||||
// counter the increment that will happen after continuing this loop
|
||||
// otherwise it'll be skipped over
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
groups[curgroup].P.Push(i);
|
||||
groups[curgroup].numPolys++;
|
||||
}
|
||||
// ... and it's finally done
|
||||
mDataLoaded = true;
|
||||
}
|
||||
|
||||
void FUE1Model::UnloadGeometry()
|
||||
{
|
||||
mDataLoaded = false;
|
||||
weaponPoly = -1;
|
||||
numVerts = 0;
|
||||
numFrames = 0;
|
||||
numPolys = 0;
|
||||
numGroups = 0;
|
||||
verts.Reset();
|
||||
for ( int i=0; i<numPolys; i++ )
|
||||
polys[i].Normals.Reset();
|
||||
polys.Reset();
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
groups[i].P.Reset();
|
||||
groups.Reset();
|
||||
}
|
||||
|
||||
int FUE1Model::FindFrame( const char *name )
|
||||
{
|
||||
// unsupported, there are no named frames
|
||||
return -1;
|
||||
}
|
||||
|
||||
void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int frame, int frame2, double inter, int translation )
|
||||
{
|
||||
// the moment of magic
|
||||
if ( (frame >= numFrames) || (frame2 >= numFrames) ) return;
|
||||
renderer->SetInterpolation(inter);
|
||||
int vsize, fsize = 0, vofs = 0;
|
||||
for ( int i=0; i<numGroups; i++ ) fsize += groups[i].numPolys*3;
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
{
|
||||
vsize = groups[i].numPolys*3;
|
||||
if ( groups[i].type&PT_WeaponTriangle )
|
||||
{
|
||||
// weapon triangle should never be drawn, it only exists to calculate attachment position and orientation
|
||||
vofs += vsize;
|
||||
continue;
|
||||
}
|
||||
FGameTexture *sskin = skin;
|
||||
if ( !sskin )
|
||||
{
|
||||
if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].isValid() )
|
||||
sskin = TexMan.GetGameTexture(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum], true);
|
||||
if ( !sskin )
|
||||
{
|
||||
vofs += vsize;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// TODO: Handle per-group render styles and other flags once functions for it are implemented
|
||||
// Future note: poly renderstyles should always be enforced unless the actor itself has a style other than Normal
|
||||
renderer->SetMaterial(sskin,false,translation);
|
||||
renderer->SetupFrame(this, vofs+frame*fsize,vofs+frame2*fsize,vsize);
|
||||
renderer->DrawArrays(0,vsize);
|
||||
vofs += vsize;
|
||||
}
|
||||
renderer->SetInterpolation(0.f);
|
||||
}
|
||||
|
||||
void FUE1Model::BuildVertexBuffer( FModelRenderer *renderer )
|
||||
{
|
||||
if (GetVertexBuffer(renderer->GetType()))
|
||||
return;
|
||||
if ( !mDataLoaded )
|
||||
LoadGeometry();
|
||||
int vsize = 0;
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
vsize += groups[i].numPolys*3;
|
||||
vsize *= numFrames;
|
||||
auto vbuf = renderer->CreateVertexBuffer(false,numFrames==1);
|
||||
SetVertexBuffer(renderer->GetType(), vbuf);
|
||||
FModelVertex *vptr = vbuf->LockVertexBuffer(vsize);
|
||||
int vidx = 0;
|
||||
for ( int i=0; i<numFrames; i++ )
|
||||
{
|
||||
for ( int j=0; j<numGroups; j++ )
|
||||
{
|
||||
for ( int k=0; k<groups[j].numPolys; k++ )
|
||||
{
|
||||
for ( int l=0; l<3; l++ )
|
||||
{
|
||||
UE1Vertex V = verts[polys[groups[j].P[k]].V[l]+i*numVerts];
|
||||
FVector2 C = polys[groups[j].P[k]].C[l];
|
||||
FModelVertex *vert = &vptr[vidx++];
|
||||
vert->Set(V.Pos.X,V.Pos.Y,V.Pos.Z,C.X,C.Y);
|
||||
if ( groups[j].type&PT_Curvy ) // use facet normal
|
||||
{
|
||||
vert->SetNormal(polys[groups[j].P[k]].Normals[i].X,
|
||||
polys[groups[j].P[k]].Normals[i].Y,
|
||||
polys[groups[j].P[k]].Normals[i].Z);
|
||||
}
|
||||
else vert->SetNormal(V.Normal.X,V.Normal.Y,V.Normal.Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vbuf->UnlockVertexBuffer();
|
||||
}
|
||||
|
||||
void FUE1Model::AddSkins( uint8_t *hitlist )
|
||||
{
|
||||
for ( int i=0; i<numGroups; i++ )
|
||||
if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].isValid() )
|
||||
hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
|
||||
FUE1Model::~FUE1Model()
|
||||
{
|
||||
UnloadGeometry();
|
||||
}
|
407
source/common/models/models_voxel.cpp
Normal file
407
source/common/models/models_voxel.cpp
Normal file
|
@ -0,0 +1,407 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2010-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_voxels.cpp
|
||||
**
|
||||
** Voxel management
|
||||
**
|
||||
**/
|
||||
|
||||
#include "filesystem.h"
|
||||
#include "colormatcher.h"
|
||||
#include "bitmap.h"
|
||||
#include "model_kvx.h"
|
||||
#include "image.h"
|
||||
#include "texturemanager.h"
|
||||
#include "modelrenderer.h"
|
||||
#include "voxels.h"
|
||||
#include "texturemanager.h"
|
||||
#include "palettecontainer.h"
|
||||
#include "textures.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4244) // warning C4244: conversion from 'double' to 'float', possible loss of data
|
||||
#endif
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Creates a 16x16 texture from the palette so that we can
|
||||
// use the existing palette manipulation code to render the voxel
|
||||
// Otherwise all shaders had to be duplicated and the non-shader code
|
||||
// would be a lot less efficient.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
class FVoxelTexture : public FImageSource
|
||||
{
|
||||
public:
|
||||
FVoxelTexture(FVoxel *voxel);
|
||||
|
||||
int CopyPixels(FBitmap *bmp, int conversion) override;
|
||||
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
|
||||
|
||||
protected:
|
||||
FVoxel *SourceVox;
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FVoxelTexture::FVoxelTexture(FVoxel *vox)
|
||||
{
|
||||
SourceVox = vox;
|
||||
Width = 16;
|
||||
Height = 16;
|
||||
//bNoCompress = true;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
TArray<uint8_t> FVoxelTexture::CreatePalettedPixels(int conversion)
|
||||
{
|
||||
// GetPixels gets called when a translated palette is used so we still need to implement it here.
|
||||
TArray<uint8_t> Pixels(256, true);
|
||||
uint8_t *pp = SourceVox->Palette.Data();
|
||||
|
||||
if(pp != NULL)
|
||||
{
|
||||
for(int i=0;i<256;i++, pp+=3)
|
||||
{
|
||||
PalEntry pe;
|
||||
pe.r = (pp[0] << 2) | (pp[0] >> 4);
|
||||
pe.g = (pp[1] << 2) | (pp[1] >> 4);
|
||||
pe.b = (pp[2] << 2) | (pp[2] >> 4);
|
||||
// Alphatexture handling is just for completeness, but rather unlikely to be used ever.
|
||||
Pixels[i] = conversion == luminance ? pe.r : ColorMatcher.Pick(pe);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i=0;i<256;i++, pp+=3)
|
||||
{
|
||||
Pixels[i] = (uint8_t)i;
|
||||
}
|
||||
}
|
||||
return Pixels;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// FVoxelTexture::CopyPixels
|
||||
//
|
||||
// This creates a dummy 16x16 paletted bitmap and converts that using the
|
||||
// voxel palette
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
int FVoxelTexture::CopyPixels(FBitmap *bmp, int conversion)
|
||||
{
|
||||
PalEntry pe[256];
|
||||
uint8_t bitmap[256];
|
||||
uint8_t *pp = SourceVox->Palette.Data();
|
||||
|
||||
if(pp != nullptr)
|
||||
{
|
||||
for(int i=0;i<256;i++, pp+=3)
|
||||
{
|
||||
bitmap[i] = (uint8_t)i;
|
||||
pe[i].r = (pp[0] << 2) | (pp[0] >> 4);
|
||||
pe[i].g = (pp[1] << 2) | (pp[1] >> 4);
|
||||
pe[i].b = (pp[2] << 2) | (pp[2] >> 4);
|
||||
pe[i].a = 255;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i=0;i<256;i++, pp+=3)
|
||||
{
|
||||
bitmap[i] = (uint8_t)i;
|
||||
pe[i] = GPalette.BaseColors[i];
|
||||
pe[i].a = 255;
|
||||
}
|
||||
}
|
||||
bmp->CopyPixelData(0, 0, bitmap, Width, Height, 1, 16, 0, pe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FVoxelModel::FVoxelModel(FVoxel *voxel, bool owned)
|
||||
{
|
||||
mVoxel = voxel;
|
||||
mOwningVoxel = owned;
|
||||
mPalette = TexMan.AddGameTexture(MakeGameTexture(new FImageTexture(new FVoxelTexture(voxel)), nullptr, ETextureType::Override));
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
FVoxelModel::~FVoxelModel()
|
||||
{
|
||||
if (mOwningVoxel) delete mVoxel;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
unsigned int FVoxelModel::AddVertex(FModelVertex &vert, FVoxelMap &check)
|
||||
{
|
||||
unsigned int index = check[vert];
|
||||
if (index == 0xffffffff)
|
||||
{
|
||||
index = check[vert] =mVertices.Push(vert);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FVoxelModel::AddFace(int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3, int x4, int y4, int z4, uint8_t col, FVoxelMap &check)
|
||||
{
|
||||
float PivotX = mVoxel->Mips[0].Pivot.X;
|
||||
float PivotY = mVoxel->Mips[0].Pivot.Y;
|
||||
float PivotZ = mVoxel->Mips[0].Pivot.Z;
|
||||
int h = mVoxel->Mips[0].SizeZ;
|
||||
FModelVertex vert;
|
||||
unsigned int indx[4];
|
||||
|
||||
vert.packedNormal = 0; // currently this is not being used for voxels.
|
||||
vert.u = (((col & 15) * 255 / 16) + 7) / 255.f;
|
||||
vert.v = (((col / 16) * 255 / 16) + 7) / 255.f;
|
||||
|
||||
vert.x = x1 - PivotX;
|
||||
vert.z = -y1 + PivotY;
|
||||
vert.y = -z1 + PivotZ;
|
||||
indx[0] = AddVertex(vert, check);
|
||||
|
||||
vert.x = x2 - PivotX;
|
||||
vert.z = -y2 + PivotY;
|
||||
vert.y = -z2 + PivotZ;
|
||||
indx[1] = AddVertex(vert, check);
|
||||
|
||||
vert.x = x4 - PivotX;
|
||||
vert.z = -y4 + PivotY;
|
||||
vert.y = -z4 + PivotZ;
|
||||
indx[2] = AddVertex(vert, check);
|
||||
|
||||
vert.x = x3 - PivotX;
|
||||
vert.z = -y3 + PivotY;
|
||||
vert.y = -z3 + PivotZ;
|
||||
indx[3] = AddVertex(vert, check);
|
||||
|
||||
|
||||
mIndices.Push(indx[0]);
|
||||
mIndices.Push(indx[1]);
|
||||
mIndices.Push(indx[3]);
|
||||
mIndices.Push(indx[1]);
|
||||
mIndices.Push(indx[2]);
|
||||
mIndices.Push(indx[3]);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FVoxelModel::MakeSlabPolys(int x, int y, kvxslab_t *voxptr, FVoxelMap &check)
|
||||
{
|
||||
const uint8_t *col = voxptr->col;
|
||||
int zleng = voxptr->zleng;
|
||||
int ztop = voxptr->ztop;
|
||||
int cull = voxptr->backfacecull;
|
||||
|
||||
if (cull & 16)
|
||||
{
|
||||
AddFace(x, y, ztop, x+1, y, ztop, x, y+1, ztop, x+1, y+1, ztop, *col, check);
|
||||
}
|
||||
int z = ztop;
|
||||
while (z < ztop+zleng)
|
||||
{
|
||||
int c = 0;
|
||||
while (z+c < ztop+zleng && col[c] == col[0]) c++;
|
||||
|
||||
if (cull & 1)
|
||||
{
|
||||
AddFace(x, y, z, x, y+1, z, x, y, z+c, x, y+1, z+c, *col, check);
|
||||
}
|
||||
if (cull & 2)
|
||||
{
|
||||
AddFace(x+1, y+1, z, x+1, y, z, x+1, y+1, z+c, x+1, y, z+c, *col, check);
|
||||
}
|
||||
if (cull & 4)
|
||||
{
|
||||
AddFace(x+1, y, z, x, y, z, x+1, y, z+c, x, y, z+c, *col, check);
|
||||
}
|
||||
if (cull & 8)
|
||||
{
|
||||
AddFace(x, y+1, z, x+1, y+1, z, x, y+1, z+c, x+1, y+1, z+c, *col, check);
|
||||
}
|
||||
z+=c;
|
||||
col+=c;
|
||||
}
|
||||
if (cull & 32)
|
||||
{
|
||||
int z = ztop+zleng-1;
|
||||
AddFace(x+1, y, z+1, x, y, z+1, x+1, y+1, z+1, x, y+1, z+1, voxptr->col[zleng-1], check);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FVoxelModel::Initialize()
|
||||
{
|
||||
FVoxelMap check;
|
||||
FVoxelMipLevel *mip = &mVoxel->Mips[0];
|
||||
for (int x = 0; x < mip->SizeX; x++)
|
||||
{
|
||||
uint8_t *slabxoffs = &mip->GetSlabData(false)[mip->OffsetX[x]];
|
||||
short *xyoffs = &mip->OffsetXY[x * (mip->SizeY + 1)];
|
||||
for (int y = 0; y < mip->SizeY; y++)
|
||||
{
|
||||
kvxslab_t *voxptr = (kvxslab_t *)(slabxoffs + xyoffs[y]);
|
||||
kvxslab_t *voxend = (kvxslab_t *)(slabxoffs + xyoffs[y+1]);
|
||||
for (; voxptr < voxend; voxptr = (kvxslab_t *)((uint8_t *)voxptr + voxptr->zleng + 3))
|
||||
{
|
||||
MakeSlabPolys(x, y, voxptr, check);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FVoxelModel::BuildVertexBuffer(FModelRenderer *renderer)
|
||||
{
|
||||
if (!GetVertexBuffer(renderer->GetType()))
|
||||
{
|
||||
Initialize();
|
||||
|
||||
auto vbuf = renderer->CreateVertexBuffer(true, true);
|
||||
SetVertexBuffer(renderer->GetType(), vbuf);
|
||||
|
||||
FModelVertex *vertptr = vbuf->LockVertexBuffer(mVertices.Size());
|
||||
unsigned int *indxptr = vbuf->LockIndexBuffer(mIndices.Size());
|
||||
|
||||
memcpy(vertptr, &mVertices[0], sizeof(FModelVertex)* mVertices.Size());
|
||||
memcpy(indxptr, &mIndices[0], sizeof(unsigned int)* mIndices.Size());
|
||||
|
||||
vbuf->UnlockVertexBuffer();
|
||||
vbuf->UnlockIndexBuffer();
|
||||
mNumIndices = mIndices.Size();
|
||||
|
||||
// delete our temporary buffers
|
||||
mVertices.Clear();
|
||||
mIndices.Clear();
|
||||
mVertices.ShrinkToFit();
|
||||
mIndices.ShrinkToFit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// for skin precaching
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FVoxelModel::AddSkins(uint8_t *hitlist)
|
||||
{
|
||||
hitlist[mPalette.GetIndex()] |= FTextureManager::HIT_Flat;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
bool FVoxelModel::Load(const char * fn, int lumpnum, const char * buffer, int length)
|
||||
{
|
||||
return false; // not needed
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Voxels don't have frames so always return 0
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
int FVoxelModel::FindFrame(const char * name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Voxels need aspect ratio correction according to the current map's setting
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
float FVoxelModel::getAspectFactor(float stretch)
|
||||
{
|
||||
return stretch;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// Voxels never interpolate between frames, they only have one.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void FVoxelModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation)
|
||||
{
|
||||
renderer->SetMaterial(skin, true, translation);
|
||||
renderer->SetupFrame(this, 0, 0, 0);
|
||||
renderer->DrawElements(mNumIndices, 0);
|
||||
}
|
||||
|
489
source/common/models/tab_anorms.h
Normal file
489
source/common/models/tab_anorms.h
Normal file
|
@ -0,0 +1,489 @@
|
|||
#ifdef _WIN32
|
||||
#pragma warning(disable:4305)
|
||||
#endif
|
||||
|
||||
{
|
||||
-0.525731f, 0.000000f, 0.850651f},
|
||||
|
||||
{
|
||||
-0.442863f, 0.238856f, 0.864188f},
|
||||
|
||||
{
|
||||
-0.295242f, 0.000000f, 0.955423f},
|
||||
|
||||
{
|
||||
-0.309017f, 0.500000f, 0.809017f},
|
||||
|
||||
{
|
||||
-0.162460f, 0.262866f, 0.951056f},
|
||||
|
||||
{
|
||||
0.000000f, 0.000000f, 1.000000f},
|
||||
|
||||
{
|
||||
0.000000f, 0.850651f, 0.525731f},
|
||||
|
||||
{
|
||||
-0.147621f, 0.716567f, 0.681718f},
|
||||
|
||||
{
|
||||
0.147621f, 0.716567f, 0.681718f},
|
||||
|
||||
{
|
||||
0.000000f, 0.525731f, 0.850651f},
|
||||
|
||||
{
|
||||
0.309017f, 0.500000f, 0.809017f},
|
||||
|
||||
{
|
||||
0.525731f, 0.000000f, 0.850651f},
|
||||
|
||||
{
|
||||
0.295242f, 0.000000f, 0.955423f},
|
||||
|
||||
{
|
||||
0.442863f, 0.238856f, 0.864188f},
|
||||
|
||||
{
|
||||
0.162460f, 0.262866f, 0.951056f},
|
||||
|
||||
{
|
||||
-0.681718f, 0.147621f, 0.716567f},
|
||||
|
||||
{
|
||||
-0.809017f, 0.309017f, 0.500000f},
|
||||
|
||||
{
|
||||
-0.587785f, 0.425325f, 0.688191f},
|
||||
|
||||
{
|
||||
-0.850651f, 0.525731f, 0.000000f},
|
||||
|
||||
{
|
||||
-0.864188f, 0.442863f, 0.238856f},
|
||||
|
||||
{
|
||||
-0.716567f, 0.681718f, 0.147621f},
|
||||
|
||||
{
|
||||
-0.688191f, 0.587785f, 0.425325f},
|
||||
|
||||
{
|
||||
-0.500000f, 0.809017f, 0.309017f},
|
||||
|
||||
{
|
||||
-0.238856f, 0.864188f, 0.442863f},
|
||||
|
||||
{
|
||||
-0.425325f, 0.688191f, 0.587785f},
|
||||
|
||||
{
|
||||
-0.716567f, 0.681718f, -0.147621f},
|
||||
|
||||
{
|
||||
-0.500000f, 0.809017f, -0.309017f},
|
||||
|
||||
{
|
||||
-0.525731f, 0.850651f, 0.000000f},
|
||||
|
||||
{
|
||||
0.000000f, 0.850651f, -0.525731f},
|
||||
|
||||
{
|
||||
-0.238856f, 0.864188f, -0.442863f},
|
||||
|
||||
{
|
||||
0.000000f, 0.955423f, -0.295242f},
|
||||
|
||||
{
|
||||
-0.262866f, 0.951056f, -0.162460f},
|
||||
|
||||
{
|
||||
0.000000f, 1.000000f, 0.000000f},
|
||||
|
||||
{
|
||||
0.000000f, 0.955423f, 0.295242f},
|
||||
|
||||
{
|
||||
-0.262866f, 0.951056f, 0.162460f},
|
||||
|
||||
{
|
||||
0.238856f, 0.864188f, 0.442863f},
|
||||
|
||||
{
|
||||
0.262866f, 0.951056f, 0.162460f},
|
||||
|
||||
{
|
||||
0.500000f, 0.809017f, 0.309017f},
|
||||
|
||||
{
|
||||
0.238856f, 0.864188f, -0.442863f},
|
||||
|
||||
{
|
||||
0.262866f, 0.951056f, -0.162460f},
|
||||
|
||||
{
|
||||
0.500000f, 0.809017f, -0.309017f},
|
||||
|
||||
{
|
||||
0.850651f, 0.525731f, 0.000000f},
|
||||
|
||||
{
|
||||
0.716567f, 0.681718f, 0.147621f},
|
||||
|
||||
{
|
||||
0.716567f, 0.681718f, -0.147621f},
|
||||
|
||||
{
|
||||
0.525731f, 0.850651f, 0.000000f},
|
||||
|
||||
{
|
||||
0.425325f, 0.688191f, 0.587785f},
|
||||
|
||||
{
|
||||
0.864188f, 0.442863f, 0.238856f},
|
||||
|
||||
{
|
||||
0.688191f, 0.587785f, 0.425325f},
|
||||
|
||||
{
|
||||
0.809017f, 0.309017f, 0.500000f},
|
||||
|
||||
{
|
||||
0.681718f, 0.147621f, 0.716567f},
|
||||
|
||||
{
|
||||
0.587785f, 0.425325f, 0.688191f},
|
||||
|
||||
{
|
||||
0.955423f, 0.295242f, 0.000000f},
|
||||
|
||||
{
|
||||
1.000000f, 0.000000f, 0.000000f},
|
||||
|
||||
{
|
||||
0.951056f, 0.162460f, 0.262866f},
|
||||
|
||||
{
|
||||
0.850651f, -0.525731f, 0.000000f},
|
||||
|
||||
{
|
||||
0.955423f, -0.295242f, 0.000000f},
|
||||
|
||||
{
|
||||
0.864188f, -0.442863f, 0.238856f},
|
||||
|
||||
{
|
||||
0.951056f, -0.162460f, 0.262866f},
|
||||
|
||||
{
|
||||
0.809017f, -0.309017f, 0.500000f},
|
||||
|
||||
{
|
||||
0.681718f, -0.147621f, 0.716567f},
|
||||
|
||||
{
|
||||
0.850651f, 0.000000f, 0.525731f},
|
||||
|
||||
{
|
||||
0.864188f, 0.442863f, -0.238856f},
|
||||
|
||||
{
|
||||
0.809017f, 0.309017f, -0.500000f},
|
||||
|
||||
{
|
||||
0.951056f, 0.162460f, -0.262866f},
|
||||
|
||||
{
|
||||
0.525731f, 0.000000f, -0.850651f},
|
||||
|
||||
{
|
||||
0.681718f, 0.147621f, -0.716567f},
|
||||
|
||||
{
|
||||
0.681718f, -0.147621f, -0.716567f},
|
||||
|
||||
{
|
||||
0.850651f, 0.000000f, -0.525731f},
|
||||
|
||||
{
|
||||
0.809017f, -0.309017f, -0.500000f},
|
||||
|
||||
{
|
||||
0.864188f, -0.442863f, -0.238856f},
|
||||
|
||||
{
|
||||
0.951056f, -0.162460f, -0.262866f},
|
||||
|
||||
{
|
||||
0.147621f, 0.716567f, -0.681718f},
|
||||
|
||||
{
|
||||
0.309017f, 0.500000f, -0.809017f},
|
||||
|
||||
{
|
||||
0.425325f, 0.688191f, -0.587785f},
|
||||
|
||||
{
|
||||
0.442863f, 0.238856f, -0.864188f},
|
||||
|
||||
{
|
||||
0.587785f, 0.425325f, -0.688191f},
|
||||
|
||||
{
|
||||
0.688191f, 0.587785f, -0.425325f},
|
||||
|
||||
{
|
||||
-0.147621f, 0.716567f, -0.681718f},
|
||||
|
||||
{
|
||||
-0.309017f, 0.500000f, -0.809017f},
|
||||
|
||||
{
|
||||
0.000000f, 0.525731f, -0.850651f},
|
||||
|
||||
{
|
||||
-0.525731f, 0.000000f, -0.850651f},
|
||||
|
||||
{
|
||||
-0.442863f, 0.238856f, -0.864188f},
|
||||
|
||||
{
|
||||
-0.295242f, 0.000000f, -0.955423f},
|
||||
|
||||
{
|
||||
-0.162460f, 0.262866f, -0.951056f},
|
||||
|
||||
{
|
||||
0.000000f, 0.000000f, -1.000000f},
|
||||
|
||||
{
|
||||
0.295242f, 0.000000f, -0.955423f},
|
||||
|
||||
{
|
||||
0.162460f, 0.262866f, -0.951056f},
|
||||
|
||||
{
|
||||
-0.442863f, -0.238856f, -0.864188f},
|
||||
|
||||
{
|
||||
-0.309017f, -0.500000f, -0.809017f},
|
||||
|
||||
{
|
||||
-0.162460f, -0.262866f, -0.951056f},
|
||||
|
||||
{
|
||||
0.000000f, -0.850651f, -0.525731f},
|
||||
|
||||
{
|
||||
-0.147621f, -0.716567f, -0.681718f},
|
||||
|
||||
{
|
||||
0.147621f, -0.716567f, -0.681718f},
|
||||
|
||||
{
|
||||
0.000000f, -0.525731f, -0.850651f},
|
||||
|
||||
{
|
||||
0.309017f, -0.500000f, -0.809017f},
|
||||
|
||||
{
|
||||
0.442863f, -0.238856f, -0.864188f},
|
||||
|
||||
{
|
||||
0.162460f, -0.262866f, -0.951056f},
|
||||
|
||||
{
|
||||
0.238856f, -0.864188f, -0.442863f},
|
||||
|
||||
{
|
||||
0.500000f, -0.809017f, -0.309017f},
|
||||
|
||||
{
|
||||
0.425325f, -0.688191f, -0.587785f},
|
||||
|
||||
{
|
||||
0.716567f, -0.681718f, -0.147621f},
|
||||
|
||||
{
|
||||
0.688191f, -0.587785f, -0.425325f},
|
||||
|
||||
{
|
||||
0.587785f, -0.425325f, -0.688191f},
|
||||
|
||||
{
|
||||
0.000000f, -0.955423f, -0.295242f},
|
||||
|
||||
{
|
||||
0.000000f, -1.000000f, 0.000000f},
|
||||
|
||||
{
|
||||
0.262866f, -0.951056f, -0.162460f},
|
||||
|
||||
{
|
||||
0.000000f, -0.850651f, 0.525731f},
|
||||
|
||||
{
|
||||
0.000000f, -0.955423f, 0.295242f},
|
||||
|
||||
{
|
||||
0.238856f, -0.864188f, 0.442863f},
|
||||
|
||||
{
|
||||
0.262866f, -0.951056f, 0.162460f},
|
||||
|
||||
{
|
||||
0.500000f, -0.809017f, 0.309017f},
|
||||
|
||||
{
|
||||
0.716567f, -0.681718f, 0.147621f},
|
||||
|
||||
{
|
||||
0.525731f, -0.850651f, 0.000000f},
|
||||
|
||||
{
|
||||
-0.238856f, -0.864188f, -0.442863f},
|
||||
|
||||
{
|
||||
-0.500000f, -0.809017f, -0.309017f},
|
||||
|
||||
{
|
||||
-0.262866f, -0.951056f, -0.162460f},
|
||||
|
||||
{
|
||||
-0.850651f, -0.525731f, 0.000000f},
|
||||
|
||||
{
|
||||
-0.716567f, -0.681718f, -0.147621f},
|
||||
|
||||
{
|
||||
-0.716567f, -0.681718f, 0.147621f},
|
||||
|
||||
{
|
||||
-0.525731f, -0.850651f, 0.000000f},
|
||||
|
||||
{
|
||||
-0.500000f, -0.809017f, 0.309017f},
|
||||
|
||||
{
|
||||
-0.238856f, -0.864188f, 0.442863f},
|
||||
|
||||
{
|
||||
-0.262866f, -0.951056f, 0.162460f},
|
||||
|
||||
{
|
||||
-0.864188f, -0.442863f, 0.238856f},
|
||||
|
||||
{
|
||||
-0.809017f, -0.309017f, 0.500000f},
|
||||
|
||||
{
|
||||
-0.688191f, -0.587785f, 0.425325f},
|
||||
|
||||
{
|
||||
-0.681718f, -0.147621f, 0.716567f},
|
||||
|
||||
{
|
||||
-0.442863f, -0.238856f, 0.864188f},
|
||||
|
||||
{
|
||||
-0.587785f, -0.425325f, 0.688191f},
|
||||
|
||||
{
|
||||
-0.309017f, -0.500000f, 0.809017f},
|
||||
|
||||
{
|
||||
-0.147621f, -0.716567f, 0.681718f},
|
||||
|
||||
{
|
||||
-0.425325f, -0.688191f, 0.587785f},
|
||||
|
||||
{
|
||||
-0.162460f, -0.262866f, 0.951056f},
|
||||
|
||||
{
|
||||
0.442863f, -0.238856f, 0.864188f},
|
||||
|
||||
{
|
||||
0.162460f, -0.262866f, 0.951056f},
|
||||
|
||||
{
|
||||
0.309017f, -0.500000f, 0.809017f},
|
||||
|
||||
{
|
||||
0.147621f, -0.716567f, 0.681718f},
|
||||
|
||||
{
|
||||
0.000000f, -0.525731f, 0.850651f},
|
||||
|
||||
{
|
||||
0.425325f, -0.688191f, 0.587785f},
|
||||
|
||||
{
|
||||
0.587785f, -0.425325f, 0.688191f},
|
||||
|
||||
{
|
||||
0.688191f, -0.587785f, 0.425325f},
|
||||
|
||||
{
|
||||
-0.955423f, 0.295242f, 0.000000f},
|
||||
|
||||
{
|
||||
-0.951056f, 0.162460f, 0.262866f},
|
||||
|
||||
{
|
||||
-1.000000f, 0.000000f, 0.000000f},
|
||||
|
||||
{
|
||||
-0.850651f, 0.000000f, 0.525731f},
|
||||
|
||||
{
|
||||
-0.955423f, -0.295242f, 0.000000f},
|
||||
|
||||
{
|
||||
-0.951056f, -0.162460f, 0.262866f},
|
||||
|
||||
{
|
||||
-0.864188f, 0.442863f, -0.238856f},
|
||||
|
||||
{
|
||||
-0.951056f, 0.162460f, -0.262866f},
|
||||
|
||||
{
|
||||
-0.809017f, 0.309017f, -0.500000f},
|
||||
|
||||
{
|
||||
-0.864188f, -0.442863f, -0.238856f},
|
||||
|
||||
{
|
||||
-0.951056f, -0.162460f, -0.262866f},
|
||||
|
||||
{
|
||||
-0.809017f, -0.309017f, -0.500000f},
|
||||
|
||||
{
|
||||
-0.681718f, 0.147621f, -0.716567f},
|
||||
|
||||
{
|
||||
-0.681718f, -0.147621f, -0.716567f},
|
||||
|
||||
{
|
||||
-0.850651f, 0.000000f, -0.525731f},
|
||||
|
||||
{
|
||||
-0.688191f, 0.587785f, -0.425325f},
|
||||
|
||||
{
|
||||
-0.587785f, 0.425325f, -0.688191f},
|
||||
|
||||
{
|
||||
-0.425325f, 0.688191f, -0.587785f},
|
||||
|
||||
{
|
||||
-0.425325f, -0.688191f, -0.587785f},
|
||||
|
||||
{
|
||||
-0.587785f, -0.425325f, -0.688191f},
|
||||
|
||||
{
|
||||
-0.688191f, -0.587785f, -0.425325f},
|
489
source/common/models/voxels.cpp
Normal file
489
source/common/models/voxels.cpp
Normal file
|
@ -0,0 +1,489 @@
|
|||
/*
|
||||
** 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 <ctype.h>
|
||||
|
||||
#include "m_swap.h"
|
||||
#include "m_argv.h"
|
||||
#include "filesystem.h"
|
||||
#include "v_video.h"
|
||||
#include "sc_man.h"
|
||||
#include "voxels.h"
|
||||
#include "printf.h"
|
||||
|
||||
void VOX_AddVoxel(int sprnum, int frame, FVoxelDef *def);
|
||||
|
||||
TDeletingArray<FVoxel *> Voxels; // used only to auto-delete voxels on exit.
|
||||
TDeletingArray<FVoxelDef *> VoxelDefs;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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 uint8_t *GetVoxelRemap(const uint8_t *pal)
|
||||
{
|
||||
static uint8_t remap[256];
|
||||
static uint8_t 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_t *)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 *)((uint8_t *)src + slabzleng);
|
||||
dest = (kvxslab_t *)((uint8_t *)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 uint8_t *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 *)((uint8_t *)dest + slabzleng);
|
||||
size -= slabzleng;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// R_LoadKVX
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
#if defined __GNUC__ && !defined __clang__
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-fno-tree-loop-vectorize")
|
||||
#endif // __GNUC__ && !__clang__
|
||||
|
||||
FVoxel *R_LoadKVX(int lumpnum)
|
||||
{
|
||||
const kvxslab_t *slabs[MAXVOXMIPS];
|
||||
FVoxel *voxel = new FVoxel;
|
||||
const uint8_t *rawmip;
|
||||
int mip, maxmipsize;
|
||||
int i, j, n;
|
||||
|
||||
FileData lump = fileSystem.ReadFile(lumpnum); // FileData adds an extra 0 byte to the end.
|
||||
uint8_t *rawvoxel = (uint8_t *)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->Pivot.X = GetInt(rawmip + 12) / 256.;
|
||||
mipl->Pivot.Y = GetInt(rawmip + 16) / 256.;
|
||||
mipl->Pivot.Z = GetInt(rawmip + 20) / 256.;
|
||||
|
||||
// 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 = (uint8_t *)(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;
|
||||
|
||||
// Fix pivot data for submips, since some tools seem to like to just center these.
|
||||
for (i = 1; i < mip; ++i)
|
||||
{
|
||||
voxel->Mips[i].Pivot = voxel->Mips[i - 1].Pivot / 2;
|
||||
}
|
||||
|
||||
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.Resize(768);
|
||||
memcpy(voxel->Palette.Data(), rawvoxel + voxelsize - 768, 768);
|
||||
|
||||
return voxel;
|
||||
}
|
||||
|
||||
#if defined __GNUC__ && !defined __clang__
|
||||
#pragma GCC pop_options
|
||||
#endif // __GNUC__ && !__clang__
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxelDef *R_LoadVoxelDef(int lumpnum, int spin)
|
||||
{
|
||||
FVoxel *vox = R_LoadKVX(lumpnum);
|
||||
if (vox == NULL)
|
||||
{
|
||||
Printf("%s is not a valid voxel file\n", fileSystem.GetFileFullName(lumpnum));
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
FVoxelDef *voxdef = new FVoxelDef;
|
||||
voxdef->Voxel = vox;
|
||||
voxdef->Scale = 1.;
|
||||
voxdef->DroppedSpin = voxdef->PlacedSpin = spin;
|
||||
voxdef->AngleOffset = 90.;
|
||||
|
||||
Voxels.Push(vox);
|
||||
VoxelDefs.Push(voxdef);
|
||||
return voxdef;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxelMipLevel Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxelMipLevel::FVoxelMipLevel()
|
||||
{
|
||||
SizeZ = SizeY = SizeX = 0;
|
||||
Pivot.Zero();
|
||||
OffsetX = NULL;
|
||||
OffsetXY = NULL;
|
||||
SlabData = NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxelMipLevel Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FVoxelMipLevel::~FVoxelMipLevel()
|
||||
{
|
||||
if (OffsetX != NULL)
|
||||
{
|
||||
delete[] OffsetX;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FVoxelMipLevel :: GetSlabData
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
uint8_t *FVoxelMipLevel::GetSlabData(bool wantremapped) const
|
||||
{
|
||||
if (wantremapped && SlabDataRemapped.Size() > 0) return &SlabDataRemapped[0];
|
||||
return SlabData;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Create true color version of the slab data
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FVoxel::CreateBgraSlabData()
|
||||
{
|
||||
if (Bgramade) return;
|
||||
Bgramade = true;
|
||||
for (int i = 0; i < NumMips; ++i)
|
||||
{
|
||||
int size = Mips[i].OffsetX[Mips[i].SizeX];
|
||||
if (size <= 0) continue;
|
||||
|
||||
Mips[i].SlabDataBgra.Resize(size);
|
||||
|
||||
kvxslab_t *src = (kvxslab_t*)Mips[i].SlabData;
|
||||
kvxslab_bgra_t *dest = (kvxslab_bgra_t*)&Mips[i].SlabDataBgra[0];
|
||||
|
||||
while (size >= 3)
|
||||
{
|
||||
dest->backfacecull = src->backfacecull;
|
||||
dest->ztop = src->ztop;
|
||||
dest->zleng = src->zleng;
|
||||
|
||||
int slabzleng = src->zleng;
|
||||
for (int j = 0; j < slabzleng; ++j)
|
||||
{
|
||||
int colorIndex = src->col[j];
|
||||
|
||||
uint32_t red, green, blue;
|
||||
if (Palette.Size())
|
||||
{
|
||||
red = (Palette[colorIndex * 3 + 0] << 2) | (Palette[colorIndex * 3 + 0] >> 4);
|
||||
green = (Palette[colorIndex * 3 + 1] << 2) | (Palette[colorIndex * 3 + 1] >> 4);
|
||||
blue = (Palette[colorIndex * 3 + 2] << 2) | (Palette[colorIndex * 3 + 2] >> 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
red = GPalette.BaseColors[colorIndex].r;
|
||||
green = GPalette.BaseColors[colorIndex].g;
|
||||
blue = GPalette.BaseColors[colorIndex].b;
|
||||
}
|
||||
|
||||
dest->col[j] = 0xff000000 | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
slabzleng += 3;
|
||||
|
||||
dest = (kvxslab_bgra_t *)((uint32_t *)dest + slabzleng);
|
||||
src = (kvxslab_t *)((uint8_t *)src + slabzleng);
|
||||
size -= slabzleng;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Remap the voxel to the game palette
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FVoxel::Remap()
|
||||
{
|
||||
if (Remapped) return;
|
||||
Remapped = true;
|
||||
if (Palette.Size())
|
||||
{
|
||||
uint8_t *remap = GetVoxelRemap(Palette.Data());
|
||||
for (int i = 0; i < NumMips; ++i)
|
||||
{
|
||||
int size = Mips[i].OffsetX[Mips[i].SizeX];
|
||||
if (size <= 0) continue;
|
||||
|
||||
Mips[i].SlabDataRemapped.Resize(size);
|
||||
memcpy(&Mips[i].SlabDataRemapped [0], Mips[i].SlabData, size);
|
||||
RemapVoxelSlabs((kvxslab_t *)&Mips[i].SlabDataRemapped[0], Mips[i].OffsetX[Mips[i].SizeX], remap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Delete the voxel's built-in palette
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FVoxel::RemovePalette()
|
||||
{
|
||||
Palette.Reset();
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
83
source/common/models/voxels.h
Normal file
83
source/common/models/voxels.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#ifndef __RES_VOXEL_H
|
||||
#define __RES_VOXEL_H
|
||||
|
||||
#include <stdint.h>
|
||||
// [RH] Voxels from Build
|
||||
|
||||
#define MAXVOXMIPS 5
|
||||
|
||||
struct kvxslab_t
|
||||
{
|
||||
uint8_t ztop; // starting z coordinate of top of slab
|
||||
uint8_t zleng; // # of bytes in the color array - slab height
|
||||
uint8_t backfacecull; // low 6 bits tell which of 6 faces are exposed
|
||||
uint8_t col[1/*zleng*/];// color data from top to bottom
|
||||
};
|
||||
|
||||
struct kvxslab_bgra_t
|
||||
{
|
||||
uint32_t ztop; // starting z coordinate of top of slab
|
||||
uint32_t zleng; // # of bytes in the color array - slab height
|
||||
uint32_t backfacecull; // low 6 bits tell which of 6 faces are exposed
|
||||
uint32_t col[1/*zleng*/];// color data from top to bottom
|
||||
};
|
||||
|
||||
struct FVoxel;
|
||||
|
||||
struct FVoxelMipLevel
|
||||
{
|
||||
FVoxelMipLevel();
|
||||
~FVoxelMipLevel();
|
||||
|
||||
int SizeX;
|
||||
int SizeY;
|
||||
int SizeZ;
|
||||
DVector3 Pivot;
|
||||
int *OffsetX;
|
||||
short *OffsetXY;
|
||||
private:
|
||||
uint8_t *SlabData;
|
||||
TArray<uint8_t> SlabDataRemapped;
|
||||
public:
|
||||
TArray<uint32_t> SlabDataBgra;
|
||||
|
||||
uint8_t *GetSlabData(bool wantpaletted) const;
|
||||
|
||||
friend FVoxel *R_LoadKVX(int lumpnum);
|
||||
friend struct FVoxel;
|
||||
};
|
||||
|
||||
struct FVoxel
|
||||
{
|
||||
TArray<uint8_t> Palette;
|
||||
int LumpNum;
|
||||
int NumMips;
|
||||
int VoxelIndex;
|
||||
FVoxelMipLevel Mips[MAXVOXMIPS];
|
||||
bool Remapped = false;
|
||||
bool Bgramade = false;
|
||||
|
||||
void CreateBgraSlabData();
|
||||
void Remap();
|
||||
void RemovePalette();
|
||||
};
|
||||
|
||||
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
|
||||
int VoxeldefIndex; // Needed by GZDoom
|
||||
double Scale;
|
||||
DAngle 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* VOX_GetVoxel(int lumpnum);
|
||||
|
||||
FVoxel *R_LoadKVX(int lumpnum);
|
||||
FVoxelDef *R_LoadVoxelDef(int lumpnum, int spin);
|
||||
|
||||
#endif
|
|
@ -35,6 +35,7 @@
|
|||
#include "v_video.h"
|
||||
#include "templates.h"
|
||||
#include "hw_vrmodes.h"
|
||||
#include "v_draw.h"
|
||||
|
||||
extern bool vid_hdr_active;
|
||||
|
||||
|
@ -123,7 +124,7 @@ void FGLRenderer::Flush()
|
|||
if (eyeCount - eye_ix > 1)
|
||||
mBuffers->NextEye(eyeCount);
|
||||
}
|
||||
screen->Clear2D();
|
||||
twod->Clear();
|
||||
|
||||
FGLPostProcessState savedState;
|
||||
FGLDebug::PushGroup("PresentEyes");
|
||||
|
@ -143,7 +144,7 @@ void FGLRenderer::Flush()
|
|||
void FGLRenderer::CopyToBackbuffer(const IntRect *bounds, bool applyGamma)
|
||||
{
|
||||
screen->Draw2D(); // draw all pending 2D stuff before copying the buffer
|
||||
screen->Clear2D();
|
||||
twod->Clear();
|
||||
|
||||
GLPPRenderState renderstate(mBuffers);
|
||||
hw_postprocess.customShaders.Run(&renderstate, "screen");
|
||||
|
|
|
@ -549,7 +549,7 @@ void FGLRenderState::ClearScreen()
|
|||
{
|
||||
bool multi = !!glIsEnabled(GL_MULTISAMPLE);
|
||||
|
||||
//screen->mViewpoints->Set2D(*this, SCREENWIDTH, SCREENHEIGHT);
|
||||
screen->mViewpoints->Set2D(*this, SCREENWIDTH, SCREENHEIGHT);
|
||||
SetColor(0, 0, 0);
|
||||
Apply();
|
||||
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
#include "md5.h"
|
||||
#include "gl_shader.h"
|
||||
#include "hw_shaderpatcher.h"
|
||||
#include "hwrenderer/data/shaderuniforms.h"
|
||||
#include "hwrenderer/scene/hw_viewpointuniforms.h"
|
||||
#include "shaderuniforms.h"
|
||||
#include "hw_viewpointuniforms.h"
|
||||
#include "hw_lightbuffer.h"
|
||||
#include "i_specialpaths.h"
|
||||
#include "printf.h"
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "hw_vrmodes.h"
|
||||
#include "v_video.h"
|
||||
#include "version.h"
|
||||
#include "i_interface.h"
|
||||
|
||||
// Set up 3D-specific console variables:
|
||||
CVAR(Int, vr_mode, 0, CVAR_GLOBALCONFIG)
|
||||
|
@ -60,7 +61,9 @@ static VRMode vrmi_checker = { 2, isqrt2, isqrt2, 1.f,{ { -.5f, 1.f },{ .5f, 1.f
|
|||
const VRMode *VRMode::GetVRMode(bool toscreen)
|
||||
{
|
||||
#ifdef VR3D_ENABLED
|
||||
switch (toscreen && vid_rendermode == 4 ? vr_mode : 0)
|
||||
int mode = !toscreen || (sysCallbacks && sysCallbacks->DisableTextureFilter && sysCallbacks->DisableTextureFilter()) ? 0 : vr_mode;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
default:
|
||||
case VR_MONO:
|
||||
|
|
|
@ -40,12 +40,11 @@
|
|||
#include "v_draw.h"
|
||||
#include "i_interface.h"
|
||||
#include "printf.h"
|
||||
#include "version.h"
|
||||
|
||||
#define NUMSCALEMODES countof(vScaleTable)
|
||||
extern bool setsizeneeded;
|
||||
|
||||
EXTERN_CVAR(Int, vid_aspect)
|
||||
|
||||
CUSTOM_CVAR(Int, vid_scale_customwidth, VID_MIN_WIDTH, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
|
||||
{
|
||||
if (self < VID_MIN_WIDTH)
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "texturemanager.h"
|
||||
#include "c_cvars.h"
|
||||
#include "imagehelpers.h"
|
||||
#include "v_video.h"
|
||||
|
||||
// Wrappers to keep the definitions of these classes out of here.
|
||||
IHardwareTexture* CreateHardwareTexture(int numchannels);
|
||||
|
@ -531,7 +532,7 @@ IHardwareTexture* FTexture::GetHardwareTexture(int translation, int scaleflags)
|
|||
IHardwareTexture* hwtex = SystemTextures.GetHardwareTexture(translation, scaleflags);
|
||||
if (hwtex == nullptr)
|
||||
{
|
||||
hwtex = CreateHardwareTexture(4);
|
||||
hwtex = screen->CreateHardwareTexture(4);
|
||||
SystemTextures.AddHardwareTexture(translation, scaleflags, hwtex);
|
||||
}
|
||||
return hwtex;
|
||||
|
@ -552,7 +553,7 @@ FWrapperTexture::FWrapperTexture(int w, int h, int bits)
|
|||
Height = h;
|
||||
Format = bits;
|
||||
//bNoCompress = true;
|
||||
auto hwtex = CreateHardwareTexture(4);
|
||||
auto hwtex = screen->CreateHardwareTexture(4);
|
||||
// todo: Initialize here.
|
||||
SystemTextures.AddHardwareTexture(0, false, hwtex);
|
||||
}
|
||||
|
|
0
source/common/utility/m_bbox.cpp
Normal file
0
source/common/utility/m_bbox.cpp
Normal file
|
@ -263,7 +263,7 @@ void FGameConfigFile::DoGameSetup (const char *gamename)
|
|||
const char *key;
|
||||
const char *value;
|
||||
|
||||
sublen = countof(section) - 1 - snprintf (section, countof(section), "%s.", gamename);
|
||||
sublen = countof(section) - 1 - mysnprintf (section, countof(section), "%s.", gamename);
|
||||
subsection = section + countof(section) - sublen - 1;
|
||||
section[countof(section) - 1] = '\0';
|
||||
|
||||
|
@ -314,14 +314,14 @@ void FGameConfigFile::DoKeySetup(const char *gamename)
|
|||
{ "AutomapBindings", &AutomapBindings },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
const char *key, *value;
|
||||
|
||||
sublen = countof(section) - 1 - snprintf(section, countof(section), "%s.", gamename);
|
||||
sublen = countof(section) - 1 - mysnprintf(section, countof(section), "%s.", gamename);
|
||||
subsection = section + countof(section) - sublen - 1;
|
||||
section[countof(section) - 1] = '\0';
|
||||
|
||||
C_SetDefaultBindings ();
|
||||
|
||||
const char* key, * value;
|
||||
for (int i = 0; binders[i].label != NULL; ++i)
|
||||
{
|
||||
strncpy(subsection, binders[i].label, sublen);
|
||||
|
@ -372,7 +372,7 @@ void FGameConfigFile::ArchiveGameData (const char *gamename)
|
|||
{
|
||||
char section[32*3], *subsection;
|
||||
|
||||
sublen = countof(section) - 1 - snprintf (section, countof(section), "%s.", gamename);
|
||||
sublen = countof(section) - 1 - mysnprintf (section, countof(section), "%s.", gamename);
|
||||
subsection = section + countof(section) - 1 - sublen;
|
||||
|
||||
strncpy (subsection, "Player", sublen);
|
||||
|
@ -425,7 +425,6 @@ void FGameConfigFile::ArchiveGameData (const char *gamename)
|
|||
strncpy (subsection, "AutomapBindings", sublen);
|
||||
SetSection (section, true);
|
||||
AutomapBindings.ArchiveBindings (this);
|
||||
|
||||
}
|
||||
|
||||
void FGameConfigFile::ArchiveGlobalData ()
|
||||
|
|
|
@ -92,7 +92,6 @@ int myconnectindex, numplayers;
|
|||
int connecthead, connectpoint2[MAXMULTIPLAYERS];
|
||||
int32_t xres = -1, yres = -1, bpp = 0;
|
||||
auto vsnprintfptr = vsnprintf; // This is an inline in Visual Studio but we need an address for it to satisfy the MinGW compiled libraries.
|
||||
bool setsizeneeded;
|
||||
|
||||
|
||||
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.
|
||||
|
@ -188,6 +187,11 @@ static bool System_CaptureModeInGame()
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool System_DisableTextureFilter()
|
||||
{
|
||||
return hw_useindexedcolortextures;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DoomSpecificInfo
|
||||
|
@ -473,6 +477,10 @@ int GameMain()
|
|||
System_NetGame,
|
||||
System_WantNativeMouse,
|
||||
System_CaptureModeInGame,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
System_DisableTextureFilter,
|
||||
};
|
||||
sysCallbacks = &syscb;
|
||||
|
||||
|
|
|
@ -88,8 +88,6 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
void DrawScene(HWDrawInfo *di, int drawmode);
|
||||
|
||||
bool QuadStereoCheckInitialRenderContextState();
|
||||
void PresentAnaglyph(bool r, bool g, bool b);
|
||||
void PresentSideBySide();
|
||||
|
|
|
@ -72,6 +72,7 @@ EXTERN_CVAR(Int, gl_ssao)
|
|||
|
||||
void gl_LoadExtensions();
|
||||
void gl_PrintStartupLog();
|
||||
void DrawRateStuff();
|
||||
//void Draw2D(F2DDrawer *drawer, FRenderState &state);
|
||||
|
||||
extern bool vid_hdr_active;
|
||||
|
@ -418,11 +419,11 @@ void OpenGLFrameBuffer::Draw2D()
|
|||
GLRenderer->mBuffers->BindCurrentFB();
|
||||
::DrawFullscreenBlends();
|
||||
DrawRateStuff();
|
||||
GLInterface.Draw2D(&twodgen);
|
||||
GLInterface.Draw2D(twod);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLFrameBuffer::PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
|
||||
void OpenGLFrameBuffer::PostProcessScene(bool swscene, int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
|
||||
{
|
||||
GLRenderer->PostProcessScene(fixedcm, afterBloomDrawEndScene2D);
|
||||
}
|
||||
|
@ -448,7 +449,7 @@ void videoShowFrame(int32_t w)
|
|||
}
|
||||
|
||||
OpenGLRenderer::GLRenderer->mBuffers->BlitSceneToTexture(); // Copy the resulting scene to the current post process texture
|
||||
screen->PostProcessScene(0, []() {
|
||||
screen->PostProcessScene(false, 0, []() {
|
||||
GLInterface.Draw2D(&twodpsp); // draws the weapon sprites
|
||||
});
|
||||
screen->Update();
|
||||
|
@ -464,6 +465,6 @@ void videoShowFrame(int32_t w)
|
|||
OpenGLRenderer::GLRenderer->mBuffers->BindSceneFB(false);
|
||||
}
|
||||
twodpsp.Clear();
|
||||
twodgen.Clear();
|
||||
twod->Clear();
|
||||
GLInterface.ResetFrame();
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ public:
|
|||
void SetVSync(bool vsync) override;
|
||||
|
||||
//void Draw2D() override;
|
||||
void PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D) override;
|
||||
void PostProcessScene(bool swscene, int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D) override;
|
||||
|
||||
bool HWGammaActive = false; // Are we using hardware or software gamma?
|
||||
std::shared_ptr<FGLDebug> mDebug; // Debug API
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "i_time.h"
|
||||
#include "v_2ddrawer.h"
|
||||
#include "build.h"
|
||||
#include "vm.h"
|
||||
#include "../glbackend/glbackend.h"
|
||||
#include "hw_material.h"
|
||||
/*
|
||||
|
@ -53,42 +54,18 @@
|
|||
#include "hwrenderer/utility/hw_clock.h"
|
||||
*/
|
||||
#include "hwrenderer/data/flatvertices.h"
|
||||
#include "version.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
|
||||
CVAR(Bool, gl_scale_viewport, true, CVAR_ARCHIVE);
|
||||
CVAR(Bool, vid_fps, false, 0)
|
||||
CVAR(Int, vid_showpalette, 0, 0)
|
||||
|
||||
EXTERN_CVAR(Bool, ticker)
|
||||
EXTERN_CVAR(Float, vid_brightness)
|
||||
EXTERN_CVAR(Float, vid_contrast)
|
||||
EXTERN_CVAR(Int, vid_maxfps)
|
||||
EXTERN_CVAR(Bool, cl_capfps)
|
||||
EXTERN_CVAR(Int, screenblocks)
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DCanvas :: CalcGamma
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFrameBuffer::CalcGamma (float gamma, uint8_t gammalookup[256])
|
||||
{
|
||||
// I found this formula on the web at
|
||||
// <http://panda.mostang.com/sane/sane-gamma.html>,
|
||||
// but that page no longer exits.
|
||||
double invgamma = 1.f / gamma;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
gammalookup[i] = (uint8_t)(255.0 * pow (i / 255.0, invgamma) + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DFrameBuffer Constructor
|
||||
|
@ -101,12 +78,10 @@ void DFrameBuffer::CalcGamma (float gamma, uint8_t gammalookup[256])
|
|||
DFrameBuffer::DFrameBuffer (int width, int height)
|
||||
{
|
||||
SetSize(width, height);
|
||||
//mPortalState = new FPortalSceneState;
|
||||
}
|
||||
|
||||
DFrameBuffer::~DFrameBuffer()
|
||||
{
|
||||
//delete mPortalState;
|
||||
}
|
||||
|
||||
void DFrameBuffer::SetSize(int width, int height)
|
||||
|
@ -117,62 +92,6 @@ void DFrameBuffer::SetSize(int width, int height)
|
|||
twodpsp.SetSize(Width, Height);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DFrameBuffer :: DrawRateStuff
|
||||
//
|
||||
// Draws the fps counter, dot ticker, and palette debug.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DFrameBuffer::DrawRateStuff ()
|
||||
{
|
||||
// Draws frame time and cumulative fps
|
||||
if (vid_fps)
|
||||
{
|
||||
FString fpsbuff = gi->statFPS();
|
||||
|
||||
int textScale = active_con_scale(twod);
|
||||
int rate_x = Width / textScale - NewConsoleFont->StringWidth(&fpsbuff[0]);
|
||||
twod->AddColorOnlyQuad(rate_x * textScale, 0, Width, NewConsoleFont->GetHeight() * textScale, MAKEARGB(255,0,0,0));
|
||||
DrawText (twod, NewConsoleFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0],
|
||||
DTA_VirtualWidth, screen->GetWidth() / textScale,
|
||||
DTA_VirtualHeight, screen->GetHeight() / textScale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
|
||||
#if 0
|
||||
uint64_t ms = screen->FrameTime;
|
||||
uint64_t howlong = ms - LastMS;
|
||||
if ((signed)howlong >= 0)
|
||||
{
|
||||
char fpsbuff[40];
|
||||
int chars;
|
||||
int rate_x;
|
||||
|
||||
int textScale = active_con_scale();
|
||||
|
||||
chars = snprintf (fpsbuff, countof(fpsbuff), "%2llu ms (%3llu fps)", (unsigned long long)howlong, (unsigned long long)LastCount);
|
||||
rate_x = Width / textScale - NewConsoleFont->StringWidth(&fpsbuff[0]);
|
||||
twod->AddColorOnlyQuad(rate_x * textScale, 0, Width, NewConsoleFont->GetHeight() * textScale, 0);
|
||||
DrawText (twod, NewConsoleFont, CR_WHITE, rate_x, 0, (char *)&fpsbuff[0],
|
||||
DTA_VirtualWidth, screen->GetWidth() / textScale,
|
||||
DTA_VirtualHeight, screen->GetHeight() / textScale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
|
||||
uint32_t thisSec = (uint32_t)(ms/1000);
|
||||
if (LastSec < thisSec)
|
||||
{
|
||||
LastCount = FrameCount / (thisSec - LastSec);
|
||||
LastSec = thisSec;
|
||||
FrameCount = 0;
|
||||
}
|
||||
FrameCount++;
|
||||
}
|
||||
LastMS = ms;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Palette stuff.
|
||||
|
@ -197,6 +116,15 @@ void DFrameBuffer::Update()
|
|||
}
|
||||
}
|
||||
|
||||
void DFrameBuffer::SetClearColor(int color)
|
||||
{
|
||||
PalEntry pe = GPalette.BaseColors[color];
|
||||
mSceneClearColor[0] = pe.r / 255.f;
|
||||
mSceneClearColor[1] = pe.g / 255.f;
|
||||
mSceneClearColor[2] = pe.b / 255.f;
|
||||
mSceneClearColor[3] = 1.f;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DFrameBuffer :: SetVSync
|
||||
|
@ -238,16 +166,6 @@ FTexture *DFrameBuffer::WipeEndScreen()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
void DFrameBuffer::WriteSavePic(FileWriter *file, int width, int height)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Calculates the viewport values needed for 2D and 3D operations
|
||||
|
@ -258,7 +176,7 @@ void DFrameBuffer::SetViewportRects(IntRect *bounds)
|
|||
{
|
||||
if (bounds)
|
||||
{
|
||||
//mSceneViewport = *bounds;
|
||||
mSceneViewport = *bounds;
|
||||
mScreenViewport = *bounds;
|
||||
mOutputLetterbox = *bounds;
|
||||
return;
|
||||
|
@ -382,28 +300,31 @@ FMaterial* DFrameBuffer::CreateMaterial(FGameTexture* tex, int scaleflags)
|
|||
return new FMaterial(tex, scaleflags);
|
||||
}
|
||||
|
||||
void DFrameBuffer::BeginScene()
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ZScript wrappers for inlines
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(_Screen, GetWidth)
|
||||
{
|
||||
if (videoGetRenderMode() < REND_POLYMOST) return;
|
||||
assert(BufferLock >= 0);
|
||||
if (BufferLock++ == 0)
|
||||
{
|
||||
mVertexData->Map();
|
||||
}
|
||||
PARAM_PROLOGUE;
|
||||
ACTION_RETURN_INT(screen->GetWidth());
|
||||
}
|
||||
|
||||
void DFrameBuffer::FinishScene()
|
||||
DEFINE_ACTION_FUNCTION(_Screen, GetHeight)
|
||||
{
|
||||
if (videoGetRenderMode() < REND_POLYMOST) return;
|
||||
assert(BufferLock > 0);
|
||||
if (--BufferLock == 0)
|
||||
{
|
||||
mVertexData->Unmap();
|
||||
GLInterface.DoDraw();
|
||||
}
|
||||
PARAM_PROLOGUE;
|
||||
ACTION_RETURN_INT(screen->GetHeight());
|
||||
}
|
||||
|
||||
IHardwareTexture* CreateHardwareTexture(int numchannels)
|
||||
DEFINE_ACTION_FUNCTION(_Screen, PaletteColor)
|
||||
{
|
||||
return screen->CreateHardwareTexture(numchannels);
|
||||
PARAM_PROLOGUE;
|
||||
PARAM_INT(index);
|
||||
if (index < 0 || index > 255) index = 0;
|
||||
else index = GPalette.BaseColors[index];
|
||||
ACTION_RETURN_INT(index);
|
||||
}
|
||||
|
||||
|
|
|
@ -174,6 +174,7 @@ CUSTOM_CVAR (Bool, vid_vsync, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
|
|||
|
||||
// [RH] Set true when vid_setmode command has been executed
|
||||
bool setmodeneeded = false;
|
||||
bool setsizeneeded = false;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
|
|
|
@ -44,8 +44,6 @@
|
|||
#include "intrect.h"
|
||||
#include "hw_shadowmap.h"
|
||||
|
||||
static const int VID_MIN_WIDTH = 640;
|
||||
static const int VID_MIN_HEIGHT = 400;
|
||||
|
||||
struct sector_t;
|
||||
struct FPortalSceneState;
|
||||
|
@ -82,7 +80,6 @@ extern int DisplayWidth, DisplayHeight;
|
|||
void V_UpdateModeSize (int width, int height);
|
||||
void V_OutputResized (int width, int height);
|
||||
|
||||
EXTERN_CVAR(Int, vid_rendermode)
|
||||
EXTERN_CVAR(Bool, vid_fullscreen)
|
||||
EXTERN_CVAR(Int, win_x)
|
||||
EXTERN_CVAR(Int, win_y)
|
||||
|
@ -90,28 +87,6 @@ EXTERN_CVAR(Int, win_w)
|
|||
EXTERN_CVAR(Int, win_h)
|
||||
EXTERN_CVAR(Bool, win_maximized)
|
||||
|
||||
|
||||
inline bool V_IsHardwareRenderer()
|
||||
{
|
||||
return vid_rendermode == 4;
|
||||
}
|
||||
|
||||
inline bool V_IsSoftwareRenderer()
|
||||
{
|
||||
return vid_rendermode < 2;
|
||||
}
|
||||
|
||||
inline bool V_IsPolyRenderer()
|
||||
{
|
||||
return vid_rendermode == 2 || vid_rendermode == 3;
|
||||
}
|
||||
|
||||
inline bool V_IsTrueColor()
|
||||
{
|
||||
return vid_rendermode == 1 || vid_rendermode == 3 || vid_rendermode == 4;
|
||||
}
|
||||
|
||||
|
||||
struct FColormap;
|
||||
class FileWriter;
|
||||
enum FTextureFormat : uint32_t;
|
||||
|
@ -152,11 +127,10 @@ class DFrameBuffer
|
|||
{
|
||||
protected:
|
||||
|
||||
F2DDrawer m2DDrawer;
|
||||
//F2DDrawer m2DDrawer;
|
||||
private:
|
||||
int Width = 0;
|
||||
int Height = 0;
|
||||
int BufferLock = 0;
|
||||
|
||||
public:
|
||||
// Hardware render state that needs to be exposed to the API independent part of the renderer. For ease of access this is stored in the base class.
|
||||
|
@ -167,10 +141,9 @@ public:
|
|||
unsigned int uniformblockalignment = 256; // Hardware dependent uniform buffer alignment.
|
||||
unsigned int maxuniformblock = 65536;
|
||||
const char *vendorstring; // We have to account for some issues with particular vendors.
|
||||
//FPortalSceneState *mPortalState; // global portal state.
|
||||
//FSkyVertexBuffer *mSkyData = nullptr; // the sky vertex buffer
|
||||
FSkyVertexBuffer *mSkyData = nullptr; // the sky vertex buffer
|
||||
FFlatVertexBuffer *mVertexData = nullptr; // Global vertex data
|
||||
//HWViewpointBuffer *mViewpoints = nullptr; // Viewpoint render data.
|
||||
HWViewpointBuffer *mViewpoints = nullptr; // Viewpoint render data.
|
||||
FLightBuffer *mLights = nullptr; // Dynamic lights
|
||||
IShadowMap mShadowMap;
|
||||
|
||||
|
@ -246,23 +219,6 @@ public:
|
|||
virtual IDataBuffer *CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize) { return nullptr; }
|
||||
bool BuffersArePersistent() { return !!(hwcaps & RFL_BUFFER_STORAGE); }
|
||||
|
||||
// Begin/End 2D drawing operations.
|
||||
void Begin2D()
|
||||
{
|
||||
m2DDrawer.Begin(Width, Height);
|
||||
}
|
||||
void End2D() { m2DDrawer.End(); }
|
||||
|
||||
void BeginScene();
|
||||
void FinishScene();
|
||||
|
||||
void End2DAndUpdate()
|
||||
{
|
||||
DrawRateStuff();
|
||||
m2DDrawer.End();
|
||||
Update();
|
||||
}
|
||||
|
||||
// This is overridable in case Vulkan does it differently.
|
||||
virtual bool RenderTextureIsFlipped() const
|
||||
{
|
||||
|
@ -270,25 +226,31 @@ public:
|
|||
}
|
||||
|
||||
// Report a game restart
|
||||
void SetClearColor(int color);
|
||||
virtual int Backend() { return 0; }
|
||||
virtual const char* DeviceName() const { return "Unknown"; }
|
||||
virtual void WriteSavePic(FileWriter *file, int width, int height);
|
||||
virtual void AmbientOccludeScene(float m5) {}
|
||||
virtual void FirstEye() {}
|
||||
virtual void NextEye(int eyecount) {}
|
||||
virtual void SetSceneRenderTarget(bool useSSAO) {}
|
||||
virtual void UpdateShadowMap() {}
|
||||
virtual void WaitForCommands(bool finish) {}
|
||||
virtual void SetSaveBuffers(bool yes) {}
|
||||
virtual void ImageTransitionScene(bool unknown) {}
|
||||
virtual void CopyScreenToBuffer(int width, int height, uint8_t* buffer) { memset(buffer, 0, width* height); }
|
||||
virtual bool FlipSavePic() const { return false; }
|
||||
virtual void RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect&)> renderFunc) {}
|
||||
virtual void SetActiveRenderTarget() {}
|
||||
|
||||
// Screen wiping
|
||||
virtual FTexture *WipeStartScreen();
|
||||
virtual FTexture *WipeEndScreen();
|
||||
|
||||
virtual void PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D) { if (afterBloomDrawEndScene2D) afterBloomDrawEndScene2D(); }
|
||||
virtual void PostProcessScene(bool swscene, int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D) { if (afterBloomDrawEndScene2D) afterBloomDrawEndScene2D(); }
|
||||
|
||||
void ScaleCoordsFromWindow(int16_t &x, int16_t &y);
|
||||
|
||||
uint64_t GetLastFPS() const { return LastCount; }
|
||||
|
||||
virtual void Draw2D() {}
|
||||
void Clear2D() {}
|
||||
|
||||
// Calculate gamma table
|
||||
void CalcGamma(float gamma, uint8_t gammalookup[256]);
|
||||
|
||||
virtual void SetViewportRects(IntRect *bounds);
|
||||
int ScreenToWindowX(int x);
|
||||
|
@ -307,14 +269,9 @@ public:
|
|||
// The original size of the framebuffer as selected in the video menu.
|
||||
uint64_t FrameTime = 0;
|
||||
|
||||
protected:
|
||||
void DrawRateStuff ();
|
||||
|
||||
private:
|
||||
uint64_t fpsLimitTime = 0;
|
||||
|
||||
uint64_t LastMS = 0, LastSec = 0, FrameCount = 0, LastCount = 0, LastTic = 0;
|
||||
|
||||
bool isIn2D = false;
|
||||
};
|
||||
|
||||
|
@ -339,9 +296,6 @@ void V_Init2 ();
|
|||
void V_Shutdown ();
|
||||
|
||||
inline bool IsRatioWidescreen(int ratio) { return (ratio & 3) != 0; }
|
||||
|
||||
void ScaleWithAspect(int &w, int &h, int Width, int Height);
|
||||
|
||||
extern bool setsizeneeded, setmodeneeded;
|
||||
|
||||
|
||||
|
|
|
@ -86,6 +86,8 @@ const char *GetVersionString();
|
|||
|
||||
const int SAVEPICWIDTH = 240;
|
||||
const int SAVEPICHEIGHT = 180;
|
||||
const int VID_MIN_WIDTH = 640;
|
||||
const int VID_MIN_HEIGHT = 400;
|
||||
|
||||
|
||||
#endif //__VERSION_H__
|
||||
|
|
|
@ -51,6 +51,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "menu/menu.h"
|
||||
#include "mapinfo.h"
|
||||
#include "rendering/v_video.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
|
||||
// Uncomment to prevent anything except mirrors from drawing. It is sensible to
|
||||
// also uncomment ENGINE_CLEAR_SCREEN in build/src/engine_priv.h.
|
||||
|
@ -678,7 +679,7 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
|
|||
"other values are reserved.\n");
|
||||
#endif
|
||||
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
#ifdef LEGACY_ROR
|
||||
G_SE40(smoothRatio);
|
||||
#endif
|
||||
|
@ -691,7 +692,7 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
|
|||
yax_drawrooms(G_DoSpriteAnimations, pSprite->sectnum, 0, smoothRatio);
|
||||
G_DoSpriteAnimations(pSprite->x, pSprite->y, pSprite->z - ZOFFSET6, fix16_to_int(CAMERA(q16ang)), smoothRatio);
|
||||
renderDrawMasks();
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -843,7 +844,7 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
|
|||
Printf(TEXTCOLOR_RED "ERROR: EVENT_DISPLAYROOMS return value must be 0 or 1, "
|
||||
"other values are reserved.\n");
|
||||
#endif
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
|
||||
G_HandleMirror(CAMERA(pos.x), CAMERA(pos.y), CAMERA(pos.z), CAMERA(q16ang), CAMERA(q16horiz), smoothRatio);
|
||||
G_ClearGotMirror();
|
||||
|
@ -873,7 +874,7 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
|
|||
#endif
|
||||
renderDrawMasks();
|
||||
#endif
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "colormatcher.h"
|
||||
|
||||
#include "debugbreak.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
|
||||
FString C_CON_GetBoundKeyForLastInput(int gameFunc);
|
||||
const char* C_CON_GetButtonFunc(int num);
|
||||
|
@ -1217,7 +1218,7 @@ LUNATIC_EXTERN void G_ShowView(vec3_t vec, fix16_t a, fix16_t horiz, int sect, i
|
|||
renderSetAspect(viewingRange, yxAspect);
|
||||
int const smoothratio = calc_smoothratio(totalclock, ototalclock);
|
||||
G_DoInterpolations(smoothratio);
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
if (!display_mirror)
|
||||
G_HandleMirror(vec.x, vec.y, vec.z, a, horiz, smoothratio);
|
||||
#ifdef POLYMER
|
||||
|
@ -1232,7 +1233,7 @@ LUNATIC_EXTERN void G_ShowView(vec3_t vec, fix16_t a, fix16_t horiz, int sect, i
|
|||
G_DoSpriteAnimations(vec.x, vec.y, vec.z, fix16_to_int(a), smoothratio);
|
||||
display_mirror = 0;
|
||||
renderDrawMasks();
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
G_RestoreInterpolations();
|
||||
G_UpdateScreenArea();
|
||||
renderSetAspect(viewingRange, yxAspect);
|
||||
|
|
|
@ -29,6 +29,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "printf.h"
|
||||
#include "secrets.h"
|
||||
#include "v_video.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
|
||||
BEGIN_DUKE_NS
|
||||
|
||||
|
@ -400,7 +401,7 @@ static void G_SetupCamTile(int spriteNum, int tileNum, int smoothRatio)
|
|||
Printf(TEXTCOLOR_RED "ERROR: EVENT_DISPLAYROOMSCAMERATILE return value must be 0 or 1, "
|
||||
"other values are reserved.\n");
|
||||
#endif
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
|
||||
yax_preparedrawrooms();
|
||||
drawrooms(camera.x, camera.y, camera.z, SA(spriteNum), 100 + sprite[spriteNum].shade, SECT(spriteNum));
|
||||
|
@ -410,7 +411,7 @@ static void G_SetupCamTile(int spriteNum, int tileNum, int smoothRatio)
|
|||
G_DoSpriteAnimations(camera.x, camera.y, camera.z, SA(spriteNum), smoothRatio);
|
||||
display_mirror = saveMirror;
|
||||
renderDrawMasks();
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
|
||||
finishTileSetup:
|
||||
renderRestoreTarget();
|
||||
|
|
|
@ -37,6 +37,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "trigdat.h"
|
||||
#include "runlist.h"
|
||||
#include "v_video.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
#include <string.h>
|
||||
|
||||
BEGIN_PS_NS
|
||||
|
@ -493,11 +494,11 @@ void DrawView(int smoothRatio, bool sceneonly)
|
|||
}
|
||||
}
|
||||
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
renderDrawRoomsQ16(nCamerax, nCameray, viewz, nCameraa, nCamerapan, nSector);
|
||||
analyzesprites();
|
||||
renderDrawMasks();
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
|
||||
if (HavePLURemap())
|
||||
{
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
**
|
||||
*/
|
||||
#include <memory>
|
||||
#include <assert.h>
|
||||
#include "gl_load.h"
|
||||
#include "glbackend.h"
|
||||
#include "gl_samplers.h"
|
||||
|
@ -50,6 +51,8 @@
|
|||
#include "flatvertices.h"
|
||||
#include "gl_renderer.h"
|
||||
#include "build.h"
|
||||
#include "v_draw.h"
|
||||
#include "v_font.h"
|
||||
|
||||
float shadediv[MAXPALOOKUPS];
|
||||
|
||||
|
@ -555,7 +558,7 @@ void WriteSavePic(FileWriter* file, int width, int height)
|
|||
videoSetViewableArea(oldwindowxy1.x, oldwindowxy1.y, oldwindowxy2.x, oldwindowxy2.y);
|
||||
|
||||
// The 2D drawers can contain some garbage from the dirty render setup. Get rid of that first.
|
||||
twodgen.Clear();
|
||||
twod->Clear();
|
||||
twodpsp.Clear();
|
||||
OpenGLRenderer::GLRenderer->CopyToBackbuffer(&bounds, false);
|
||||
|
||||
|
@ -580,3 +583,53 @@ void WriteSavePic(FileWriter* file, int width, int height)
|
|||
}
|
||||
|
||||
|
||||
static int BufferLock = 0;
|
||||
|
||||
void renderBeginScene()
|
||||
{
|
||||
if (videoGetRenderMode() < REND_POLYMOST) return;
|
||||
assert(BufferLock >= 0);
|
||||
if (BufferLock++ == 0)
|
||||
{
|
||||
screen->mVertexData->Map();
|
||||
}
|
||||
}
|
||||
|
||||
void renderFinishScene()
|
||||
{
|
||||
if (videoGetRenderMode() < REND_POLYMOST) return;
|
||||
assert(BufferLock > 0);
|
||||
if (--BufferLock == 0)
|
||||
{
|
||||
screen->mVertexData->Unmap();
|
||||
GLInterface.DoDraw();
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// DFrameBuffer :: DrawRateStuff
|
||||
//
|
||||
// Draws the fps counter, dot ticker, and palette debug.
|
||||
//
|
||||
//==========================================================================
|
||||
CVAR(Bool, vid_fps, false, 0)
|
||||
|
||||
void DrawRateStuff()
|
||||
{
|
||||
// Draws frame time and cumulative fps
|
||||
if (vid_fps)
|
||||
{
|
||||
FString fpsbuff = gi->statFPS();
|
||||
|
||||
int textScale = active_con_scale(twod);
|
||||
int rate_x = screen->GetWidth() / textScale - NewConsoleFont->StringWidth(&fpsbuff[0]);
|
||||
twod->AddColorOnlyQuad(rate_x * textScale, 0, screen->GetWidth(), NewConsoleFont->GetHeight() * textScale, MAKEARGB(255, 0, 0, 0));
|
||||
DrawText(twod, NewConsoleFont, CR_WHITE, rate_x, 0, (char*)&fpsbuff[0],
|
||||
DTA_VirtualWidth, screen->GetWidth() / textScale,
|
||||
DTA_VirtualHeight, screen->GetHeight() / textScale,
|
||||
DTA_KeepRatio, true, TAG_DONE);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -514,3 +514,7 @@ public:
|
|||
};
|
||||
|
||||
extern GLInstance GLInterface;
|
||||
|
||||
void renderBeginScene();
|
||||
void renderFinishScene();
|
||||
void DrawRateStuff();
|
||||
|
|
|
@ -48,6 +48,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
#include "c_dispatch.h"
|
||||
#include "mapinfo.h"
|
||||
#include "rendering/v_video.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
#include "playmve.h"
|
||||
|
||||
// Uncomment to prevent anything except mirrors from drawing. It is sensible to
|
||||
|
@ -1132,7 +1133,7 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
|
|||
|
||||
CAMERA(q16horiz) = fix16_clamp(CAMERA(q16horiz), F16(HORIZ_MIN), F16(HORIZ_MAX));
|
||||
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
G_HandleMirror(CAMERA(pos.x), CAMERA(pos.y), CAMERA(pos.z), CAMERA(q16ang), CAMERA(q16horiz), smoothRatio);
|
||||
#ifdef LEGACY_ROR
|
||||
if (!RR)
|
||||
|
@ -1262,7 +1263,7 @@ void G_DrawRooms(int32_t playerNum, int32_t smoothRatio)
|
|||
#endif
|
||||
renderDrawMasks();
|
||||
#endif
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
}
|
||||
|
||||
G_RestoreInterpolations();
|
||||
|
|
|
@ -27,6 +27,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|||
|
||||
#include "secrets.h"
|
||||
#include "v_video.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
|
||||
BEGIN_RR_NS
|
||||
|
||||
|
@ -506,7 +507,7 @@ static void G_SetupCamTile(int spriteNum, int tileNum, int smoothRatio)
|
|||
int const saveMirror = display_mirror;
|
||||
|
||||
renderSetTarget(tileNum, tilesiz[tileNum].y, tilesiz[tileNum].x);
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
|
||||
yax_preparedrawrooms();
|
||||
drawrooms(camera.x, camera.y, camera.z, SA(spriteNum), 100 + sprite[spriteNum].shade, SECT(spriteNum));
|
||||
|
@ -516,7 +517,7 @@ static void G_SetupCamTile(int spriteNum, int tileNum, int smoothRatio)
|
|||
G_DoSpriteAnimations(camera.x, camera.y, camera.z, SA(spriteNum), smoothRatio);
|
||||
display_mirror = saveMirror;
|
||||
renderDrawMasks();
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
|
||||
renderRestoreTarget();
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
|
|||
#include "swcvar.h"
|
||||
#include "v_2ddrawer.h"
|
||||
#include "v_video.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
|
||||
BEGIN_SW_NS
|
||||
|
||||
|
@ -2173,7 +2174,7 @@ drawscreen(PLAYERp pp)
|
|||
JS_DrawCameras(pp, tx, ty, tz);
|
||||
}
|
||||
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
OverlapDraw = TRUE;
|
||||
DrawOverlapRoom(tx, ty, tz, tq16ang, tq16horiz, tsectnum);
|
||||
OverlapDraw = FALSE;
|
||||
|
@ -2193,7 +2194,7 @@ drawscreen(PLAYERp pp)
|
|||
analyzesprites(tx, ty, tz, FALSE);
|
||||
post_analyzesprites();
|
||||
renderDrawMasks();
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
|
||||
if (r_usenewaspect)
|
||||
{
|
||||
|
|
|
@ -46,6 +46,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
|
|||
#include "pal.h"
|
||||
#include "parent.h"
|
||||
#include "v_video.h"
|
||||
#include "glbackend/glbackend.h"
|
||||
|
||||
BEGIN_SW_NS
|
||||
|
||||
|
@ -477,12 +478,12 @@ void drawroomstotile(int daposx, int daposy, int daposz,
|
|||
TileFiles.MakeCanvas(tilenume, tilesiz[tilenume].x, tilesiz[tilenume].y);
|
||||
|
||||
renderSetTarget(tilenume, tilesiz[tilenume].x, tilesiz[tilenume].y);
|
||||
screen->BeginScene();
|
||||
renderBeginScene();
|
||||
|
||||
renderDrawRoomsQ16(daposx, daposy, daposz, daq16ang, daq16horiz, dacursectnum);
|
||||
analyzesprites(daposx, daposy, daposz, FALSE);
|
||||
renderDrawMasks();
|
||||
screen->FinishScene();
|
||||
renderFinishScene();
|
||||
|
||||
renderRestoreTarget();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue