diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c88d50b15..c54372e0e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -645,6 +645,7 @@ file( GLOB HEADER_FILES g_inventory/*.h intermission/*.h menu/*.h + models/*.h sound/oplsynth/*.h sound/oplsynth/dosbox/*.h posix/*.h @@ -824,6 +825,7 @@ set( FASTMATH_SOURCES gl/dynlights/gl_dynlight1.cpp gl/system/gl_load.c gl/models/gl_models.cpp + models/models.cpp ) set (PCH_SOURCES @@ -997,9 +999,6 @@ set (PCH_SOURCES gl/dynlights/gl_lightbuffer.cpp gl/dynlights/gl_aabbtree.cpp gl/dynlights/gl_shadowmap.cpp - gl/models/gl_models_md3.cpp - gl/models/gl_models_md2.cpp - gl/models/gl_voxels.cpp gl/renderer/gl_quaddrawer.cpp gl/renderer/gl_renderer.cpp gl/renderer/gl_renderstate.cpp @@ -1084,6 +1083,9 @@ set (PCH_SOURCES textures/tgatexture.cpp textures/warptexture.cpp textures/skyboxtexture.cpp + models/models_md3.cpp + models/models_md2.cpp + models/voxels.cpp xlat/parse_xlat.cpp fragglescript/t_func.cpp fragglescript/t_load.cpp @@ -1331,6 +1333,7 @@ source_group("FraggleScript" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/fr source_group("Intermission" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/intermission/.+") source_group("Inventory" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/g_inventory/.+") source_group("Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/menu/.+") +source_group("Models" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/models/.+") source_group("OpenGL Renderer" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/.+") source_group("OpenGL Renderer\\Data" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/data/.+") source_group("OpenGL Renderer\\Dynamic Lights" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/gl/dynlights/.+") diff --git a/src/gl/data/gl_vertexbuffer.h b/src/gl/data/gl_vertexbuffer.h index 8f4b22550..2fab5b8fc 100644 --- a/src/gl/data/gl_vertexbuffer.h +++ b/src/gl/data/gl_vertexbuffer.h @@ -26,6 +26,7 @@ #include "tarray.h" #include "gl/utility/gl_clock.h" #include "gl/system/gl_interface.h" +#include "models/models.h" struct vertex_t; struct secplane_t; @@ -266,49 +267,6 @@ public: }; -#define VSO ((FSkyVertex*)NULL) - -struct FModelVertex -{ - float x, y, z; // world position - float u, v; // texture coordinates - unsigned packedNormal; // normal vector as GL_INT_2_10_10_10_REV. - - void Set(float xx, float yy, float zz, float uu, float vv) - { - x = xx; - y = yy; - z = zz; - u = uu; - v = vv; - } - - void SetNormal(float nx, float ny, float nz) - { - int inx = clamp(int(nx * 512), -512, 511); - int iny = clamp(int(ny * 512), -512, 511); - int inz = clamp(int(nz * 512), -512, 511); - int inw = 0; - packedNormal = (inw << 30) | ((inz & 1023) << 20) | ((iny & 1023) << 10) | (inx & 1023); - } -}; - -class FModelRenderer; - -class IModelVertexBuffer -{ -public: - virtual ~IModelVertexBuffer() { } - - virtual FModelVertex *LockVertexBuffer(unsigned int size) = 0; - virtual void UnlockVertexBuffer() = 0; - - virtual unsigned int *LockIndexBuffer(unsigned int size) = 0; - virtual void UnlockIndexBuffer() = 0; - - virtual void SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) = 0; -}; - class FModelVertexBuffer : public FVertexBuffer, public IModelVertexBuffer { int mIndexFrame[2]; @@ -330,7 +288,6 @@ public: void BindVBO() override; }; -#define VMO ((FModelVertex*)NULL) - +#define VSO ((FSkyVertex*)NULL) #endif \ No newline at end of file diff --git a/src/gl/models/gl_models.cpp b/src/gl/models/gl_models.cpp index c64f05854..4b2f1d7d8 100644 --- a/src/gl/models/gl_models.cpp +++ b/src/gl/models/gl_models.cpp @@ -22,7 +22,7 @@ /* ** gl_models.cpp ** -** General model handling code +** OpenGL renderer model handling code ** **/ @@ -53,233 +53,7 @@ #include "gl/renderer/gl_renderstate.h" #include "gl/shaders/gl_shader.h" -CVAR(Bool, gl_interpolate_model_frames, true, CVAR_ARCHIVE) CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE) -EXTERN_CVAR(Int, gl_fogmode) - -extern TDeletingArray Voxels; -extern TDeletingArray VoxelDefs; - -DeletingModelArray Models; - -void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *smf, AActor *actor) -{ - // Setup transformation. - - int translation = 0; - if (!(smf->flags & MDL_IGNORETRANSLATION)) - translation = actor->Translation; - - // y scale for a sprite means height, i.e. z in the world! - float scaleFactorX = actor->Scale.X * smf->xscale; - float scaleFactorY = actor->Scale.X * smf->yscale; - float scaleFactorZ = actor->Scale.Y * smf->zscale; - float pitch = 0; - float roll = 0; - double rotateOffset = 0; - float angle = actor->Angles.Yaw.Degrees; - - // [BB] Workaround for the missing pitch information. - if ((smf->flags & MDL_PITCHFROMMOMENTUM)) - { - const double x = actor->Vel.X; - const double y = actor->Vel.Y; - const double z = actor->Vel.Z; - - if (actor->Vel.LengthSquared() > EQUAL_EPSILON) - { - // [BB] Calculate the pitch using spherical coordinates. - if (z || x || y) pitch = float(atan(z / sqrt(x*x + y*y)) / M_PI * 180); - - // Correcting pitch if model is moving backwards - if (fabs(x) > EQUAL_EPSILON || fabs(y) > EQUAL_EPSILON) - { - if ((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1; - } - else pitch = fabs(pitch); - } - } - - if (smf->flags & MDL_ROTATING) - { - const double time = smf->rotationSpeed*GetTimeFloat() / 200.; - rotateOffset = double((time - xs_FloorToInt(time)) *360.); - } - - // Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing. - // If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the velocity vector pitch. - if (smf->flags & MDL_USEACTORPITCH) - { - double d = actor->Angles.Pitch.Degrees; - if (smf->flags & MDL_BADROTATION) pitch += d; - else pitch -= d; - } - if (smf->flags & MDL_USEACTORROLL) roll += actor->Angles.Roll.Degrees; - - VSMatrix objectToWorldMatrix; - objectToWorldMatrix.loadIdentity(); - - // Model space => World space - objectToWorldMatrix.translate(x, z, y); - - // [Nash] take SpriteRotation into account - angle += actor->SpriteRotation.Degrees; - - if (actor->renderflags & RF_INTERPOLATEANGLES) - { - // [Nash] use interpolated angles - DRotator Angles = actor->InterpolatedAngles(r_viewpoint.TicFrac); - angle = Angles.Yaw.Degrees; - } - - // Applying model transformations: - // 1) Applying actor angle, pitch and roll to the model - objectToWorldMatrix.rotate(-angle, 0, 1, 0); - objectToWorldMatrix.rotate(pitch, 0, 0, 1); - objectToWorldMatrix.rotate(-roll, 1, 0, 0); - - // 2) Applying Doomsday like rotation of the weapon pickup models - // The rotation angle is based on the elapsed time. - - if (smf->flags & MDL_ROTATING) - { - objectToWorldMatrix.translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); - objectToWorldMatrix.rotate(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); - objectToWorldMatrix.translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); - } - - // 3) Scaling model. - objectToWorldMatrix.scale(scaleFactorX, scaleFactorZ, scaleFactorY); - - // 4) Aplying model offsets (model offsets do not depend on model scalings). - objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); - - // 5) Applying model rotations. - objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); - objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); - objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); - - // consider the pixel stretching. For non-voxels this must be factored out here - float stretch = (smf->modelIDs[0] != -1 ? Models[smf->modelIDs[0]]->getAspectFactor() : 1.f) / level.info->pixelstretch; - objectToWorldMatrix.scale(1, stretch, 1); - - BeginDrawModel(actor, smf, objectToWorldMatrix); - RenderFrameModels(smf, actor->state, actor->tics, actor->GetClass(), nullptr, translation); - EndDrawModel(actor, smf); -} - -void FModelRenderer::RenderHUDModel(DPSprite *psp, float ofsX, float ofsY) -{ - AActor * playermo = players[consoleplayer].camera; - FSpriteModelFrame *smf = gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false); - - // [BB] No model found for this sprite, so we can't render anything. - if (smf == nullptr) - return; - - // The model position and orientation has to be drawn independently from the position of the player, - // but we need to position it correctly in the world for light to work properly. - VSMatrix objectToWorldMatrix = GetViewToWorldMatrix(); - - // Scaling model (y scale for a sprite means height, i.e. z in the world!). - objectToWorldMatrix.scale(smf->xscale, smf->zscale, smf->yscale); - - // Aplying model offsets (model offsets do not depend on model scalings). - objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); - - // [BB] Weapon bob, very similar to the normal Doom weapon bob. - objectToWorldMatrix.rotate(ofsX / 4, 0, 1, 0); - objectToWorldMatrix.rotate((ofsY - WEAPONTOP) / -4., 1, 0, 0); - - // [BB] For some reason the jDoom models need to be rotated. - objectToWorldMatrix.rotate(90.f, 0, 1, 0); - - // Applying angleoffset, pitchoffset, rolloffset. - objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); - objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); - objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); - - BeginDrawHUDModel(playermo, objectToWorldMatrix); - RenderFrameModels(smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0); - EndDrawHUDModel(playermo); -} - -void FModelRenderer::RenderFrameModels(const FSpriteModelFrame *smf, - const FState *curState, - const int curTics, - const PClass *ti, - Matrix3x4 *normaltransform, - int translation) -{ - // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation - // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. - FSpriteModelFrame * smfNext = nullptr; - double inter = 0.; - if (gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION)) - { - FState *nextState = curState->GetNextState(); - if (curState != nextState && nextState) - { - // [BB] To interpolate at more than 35 fps we take tic fractions into account. - float ticFraction = 0.; - // [BB] In case the tic counter is frozen we have to leave ticFraction at zero. - if (ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN)) - { - double time = GetTimeFloat(); - ticFraction = (time - static_cast(time)); - } - inter = static_cast(curState->Tics - curTics - ticFraction) / static_cast(curState->Tics); - - // [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics. - // In this case inter is negative and we need to set it to zero. - if (inter < 0.) - inter = 0.; - else - { - // [BB] Workaround for actors that use the same frame twice in a row. - // Most of the standard Doom monsters do this in their see state. - if ((smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES)) - { - const FState *prevState = curState - 1; - if ((curState->sprite == prevState->sprite) && (curState->Frame == prevState->Frame)) - { - inter /= 2.; - inter += 0.5; - } - if ((curState->sprite == nextState->sprite) && (curState->Frame == nextState->Frame)) - { - inter /= 2.; - nextState = nextState->GetNextState(); - } - } - if (inter != 0.0) - smfNext = gl_FindModelFrame(ti, nextState->sprite, nextState->Frame, false); - } - } - } - - for (int i = 0; imodelIDs[i] != -1) - { - FModel * mdl = Models[smf->modelIDs[i]]; - FTexture *tex = smf->skinIDs[i].isValid() ? TexMan(smf->skinIDs[i]) : nullptr; - mdl->BuildVertexBuffer(this); - SetVertexBuffer(mdl->mVBuf); - - mdl->PushSpriteMDLFrame(smf, i); - - if (smfNext && smf->modelframes[i] != smfNext->modelframes[i]) - mdl->RenderFrame(this, tex, smf->modelframes[i], smfNext->modelframes[i], inter, translation); - else - mdl->RenderFrame(this, tex, smf->modelframes[i], smf->modelframes[i], 0.f, translation); - - ResetVertexBuffer(); - } - } -} - -///////////////////////////////////////////////////////////////////////////// extern int modellightindex; @@ -387,26 +161,6 @@ double FGLModelRenderer::GetTimeFloat() return (float)I_msTime() * (float)TICRATE / 1000.0f; } -///////////////////////////////////////////////////////////////////////////// - -void gl_LoadModels() -{ - /* - for (int i = Models.Size() - 1; i >= 0; i--) - { - Models[i]->BuildVertexBuffer(); - } - */ -} - -void gl_FlushModels() -{ - for (int i = Models.Size() - 1; i >= 0; i--) - { - Models[i]->DestroyVertexBuffer(); - } -} - //=========================================================================== // // Uses a hardware buffer if either single frame (i.e. no interpolation needed) @@ -600,583 +354,6 @@ void FModelVertexBuffer::SetupFrame(FModelRenderer *renderer, unsigned int frame } } -//=========================================================================== -// -// FModel::~FModel -// -//=========================================================================== - -FModel::~FModel() -{ - if (mVBuf != nullptr) delete mVBuf; -} - - - - -static TArray SpriteModelFrames; -static int * SpriteModelHash; -//TArray StateModelFrames; - -static void DeleteModelHash() -{ - if (SpriteModelHash != nullptr) delete [] SpriteModelHash; - SpriteModelHash = nullptr; -} - -//=========================================================================== -// -// FindGFXFile -// -//=========================================================================== - -static int FindGFXFile(FString & fn) -{ - int lump = Wads.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 = Wads.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 : Wads.GetLumpFullName(texlump); - return TexMan.CheckForTexture(texname, FTexture::TEX_Any, FTextureManager::TEXMAN_TryAny); -} - -//=========================================================================== -// -// ModelFrameHash -// -//=========================================================================== - -static 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 (; smFileName.CompareNoCase(fullname)) return i; - } - - int len = Wads.LumpLength(lump); - FMemLump lumpd = Wads.ReadLump(lump); - char * buffer = (char*)lumpd.GetMem(); - - 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); -} - -//=========================================================================== -// -// gl_InitModels -// -//=========================================================================== - -void gl_InitModels() -{ - int Lump, lastLump; - FString path; - int index, surface; - int i; - - FSpriteModelFrame smf; - - lastLump = 0; - - for(unsigned i=0;iVoxelIndex = Models.Push(md); - } - // now create GL model frames for the voxeldefs - for (unsigned i = 0; i < VoxelDefs.Size(); i++) - { - FVoxelModel *md = (FVoxelModel*)Models[VoxelDefs[i]->Voxel->VoxelIndex]; - memset(&smf, 0, sizeof(smf)); - smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1; - smf.modelIDs[0] = VoxelDefs[i]->Voxel->VoxelIndex; - smf.skinIDs[0] = md->GetPaletteTexture(); - smf.xscale = smf.yscale = smf.zscale = VoxelDefs[i]->Scale; - smf.angleoffset = VoxelDefs[i]->AngleOffset.Degrees; - if (VoxelDefs[i]->PlacedSpin != 0) - { - smf.yrotate = 1.f; - smf.rotationSpeed = VoxelDefs[i]->PlacedSpin / 55.55f; - smf.flags |= MDL_ROTATING; - } - VoxelDefs[i]->VoxeldefIndex = SpriteModelFrames.Push(smf); - if (VoxelDefs[i]->PlacedSpin != VoxelDefs[i]->DroppedSpin) - { - if (VoxelDefs[i]->DroppedSpin != 0) - { - smf.yrotate = 1.f; - smf.rotationSpeed = VoxelDefs[i]->DroppedSpin / 55.55f; - smf.flags |= MDL_ROTATING; - } - else - { - smf.yrotate = 0; - smf.rotationSpeed = 0; - smf.flags &= ~MDL_ROTATING; - } - SpriteModelFrames.Push(smf); - } - } - - memset(&smf, 0, sizeof(smf)); - smf.modelIDs[0] = smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1; - while ((Lump = Wads.FindLump("MODELDEF", &lastLump)) != -1) - { - FScanner sc(Lump); - while (sc.GetString()) - { - if (sc.Compare("model")) - { - path = ""; - sc.MustGetString(); - memset(&smf, 0, sizeof(smf)); - smf.modelIDs[0] = smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1; - smf.xscale=smf.yscale=smf.zscale=1.f; - - smf.type = PClass::FindClass(sc.String); - if (!smf.type || smf.type->Defaults == nullptr) - { - sc.ScriptError("MODELDEF: Unknown actor type '%s'\n", sc.String); - } - sc.MustGetStringName("{"); - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("path")) - { - sc.MustGetString(); - FixPathSeperator(sc.String); - path = sc.String; - if (path[(int)path.Len()-1]!='/') path+='/'; - } - else if (sc.Compare("model")) - { - sc.MustGetNumber(); - index = sc.Number; - if (index < 0 || index >= MAX_MODELS_PER_FRAME) - { - sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); - } - sc.MustGetString(); - FixPathSeperator(sc.String); - smf.modelIDs[index] = FindModel(path.GetChars(), sc.String); - if (smf.modelIDs[index] == -1) - { - Printf("%s: model not found in %s\n", sc.String, path.GetChars()); - } - } - else if (sc.Compare("scale")) - { - sc.MustGetFloat(); - smf.xscale = sc.Float; - sc.MustGetFloat(); - smf.yscale = sc.Float; - sc.MustGetFloat(); - smf.zscale = sc.Float; - } - // [BB] Added zoffset reading. - // Now it must be considered deprecated. - else if (sc.Compare("zoffset")) - { - sc.MustGetFloat(); - smf.zoffset=sc.Float; - } - // Offset reading. - else if (sc.Compare("offset")) - { - sc.MustGetFloat(); - smf.xoffset = sc.Float; - sc.MustGetFloat(); - smf.yoffset = sc.Float; - sc.MustGetFloat(); - smf.zoffset = sc.Float; - } - // angleoffset, pitchoffset and rolloffset reading. - else if (sc.Compare("angleoffset")) - { - sc.MustGetFloat(); - smf.angleoffset = sc.Float; - } - else if (sc.Compare("pitchoffset")) - { - sc.MustGetFloat(); - smf.pitchoffset = sc.Float; - } - else if (sc.Compare("rolloffset")) - { - sc.MustGetFloat(); - smf.rolloffset = sc.Float; - } - // [BB] Added model flags reading. - else if (sc.Compare("ignoretranslation")) - { - smf.flags |= MDL_IGNORETRANSLATION; - } - else if (sc.Compare("pitchfrommomentum")) - { - smf.flags |= MDL_PITCHFROMMOMENTUM; - } - else if (sc.Compare("inheritactorpitch")) - { - smf.flags |= MDL_USEACTORPITCH | MDL_BADROTATION; - } - else if (sc.Compare("inheritactorroll")) - { - smf.flags |= MDL_USEACTORROLL; - } - else if (sc.Compare("useactorpitch")) - { - smf.flags |= MDL_USEACTORPITCH; - } - else if (sc.Compare("useactorroll")) - { - smf.flags |= MDL_USEACTORROLL; - } - else if (sc.Compare("rotating")) - { - smf.flags |= MDL_ROTATING; - smf.xrotate = 0.; - smf.yrotate = 1.; - smf.zrotate = 0.; - smf.rotationCenterX = 0.; - smf.rotationCenterY = 0.; - smf.rotationCenterZ = 0.; - smf.rotationSpeed = 1.; - } - else if (sc.Compare("rotation-speed")) - { - sc.MustGetFloat(); - smf.rotationSpeed = sc.Float; - } - else if (sc.Compare("rotation-vector")) - { - sc.MustGetFloat(); - smf.xrotate = sc.Float; - sc.MustGetFloat(); - smf.yrotate = sc.Float; - sc.MustGetFloat(); - smf.zrotate = sc.Float; - } - else if (sc.Compare("rotation-center")) - { - sc.MustGetFloat(); - smf.rotationCenterX = sc.Float; - sc.MustGetFloat(); - smf.rotationCenterY = sc.Float; - sc.MustGetFloat(); - smf.rotationCenterZ = sc.Float; - } - else if (sc.Compare("interpolatedoubledframes")) - { - smf.flags |= MDL_INTERPOLATEDOUBLEDFRAMES; - } - else if (sc.Compare("nointerpolation")) - { - smf.flags |= MDL_NOINTERPOLATION; - } - else if (sc.Compare("skin")) - { - sc.MustGetNumber(); - index=sc.Number; - if (index<0 || index>=MAX_MODELS_PER_FRAME) - { - sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); - } - sc.MustGetString(); - FixPathSeperator(sc.String); - if (sc.Compare("")) - { - smf.skinIDs[index]=FNullTextureID(); - } - else - { - smf.skinIDs[index] = LoadSkin(path.GetChars(), sc.String); - if (!smf.skinIDs[index].isValid()) - { - Printf("Skin '%s' not found in '%s'\n", - sc.String, smf.type->TypeName.GetChars()); - } - } - } - else if (sc.Compare("surfaceskin")) - { - sc.MustGetNumber(); - index = sc.Number; - sc.MustGetNumber(); - surface = sc.Number; - - if (index<0 || index >= MAX_MODELS_PER_FRAME) - { - sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); - } - - if (surface<0 || surface >= MD3_MAX_SURFACES) - { - sc.ScriptError("Invalid MD3 Surface %d in %s", MD3_MAX_SURFACES, smf.type->TypeName.GetChars()); - } - - sc.MustGetString(); - FixPathSeperator(sc.String); - if (sc.Compare("")) - { - smf.surfaceskinIDs[index][surface] = FNullTextureID(); - } - else - { - smf.surfaceskinIDs[index][surface] = LoadSkin(path.GetChars(), sc.String); - if (!smf.surfaceskinIDs[index][surface].isValid()) - { - Printf("Surface Skin '%s' not found in '%s'\n", - sc.String, smf.type->TypeName.GetChars()); - } - } - } - else if (sc.Compare("frameindex") || sc.Compare("frame")) - { - bool isframe=!!sc.Compare("frame"); - - sc.MustGetString(); - smf.sprite = -1; - for (i = 0; i < (int)sprites.Size (); ++i) - { - if (strnicmp (sprites[i].name, sc.String, 4) == 0) - { - if (sprites[i].numframes==0) - { - //sc.ScriptError("Sprite %s has no frames", sc.String); - } - smf.sprite = i; - break; - } - } - if (smf.sprite==-1) - { - sc.ScriptError("Unknown sprite %s in model definition for %s", sc.String, smf.type->TypeName.GetChars()); - } - - sc.MustGetString(); - FString framechars = sc.String; - - sc.MustGetNumber(); - index=sc.Number; - if (index<0 || index>=MAX_MODELS_PER_FRAME) - { - sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); - } - if (isframe) - { - sc.MustGetString(); - if (smf.modelIDs[index] != -1) - { - FModel *model = Models[smf.modelIDs[index]]; - smf.modelframes[index] = model->FindFrame(sc.String); - if (smf.modelframes[index]==-1) sc.ScriptError("Unknown frame '%s' in %s", sc.String, smf.type->TypeName.GetChars()); - } - else smf.modelframes[index] = -1; - } - else - { - sc.MustGetNumber(); - smf.modelframes[index] = sc.Number; - } - - for(i=0; framechars[i]>0; i++) - { - char map[29]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - int c = toupper(framechars[i])-'A'; - - if (c<0 || c>=29) - { - sc.ScriptError("Invalid frame character %c found", c+'A'); - } - if (map[c]) continue; - smf.frame=c; - SpriteModelFrames.Push(smf); - GetDefaultByType(smf.type)->hasmodel = true; - map[c]=1; - } - } - else if (sc.Compare("dontcullbackfaces")) - { - smf.flags |= MDL_DONTCULLBACKFACES; - } - else - { - sc.ScriptMessage("Unrecognized string \"%s\"", sc.String); - } - } - } - } - } - - // create a hash table for quick access - SpriteModelHash = new int[SpriteModelFrames.Size ()]; - atterm(DeleteModelHash); - memset(SpriteModelHash, 0xff, SpriteModelFrames.Size () * sizeof(int)); - - for (i = 0; i < (int)SpriteModelFrames.Size (); i++) - { - int j = ModelFrameHash(&SpriteModelFrames[i]) % SpriteModelFrames.Size (); - - SpriteModelFrames[i].hashnext = SpriteModelHash[j]; - SpriteModelHash[j]=i; - } -} - - -//=========================================================================== -// -// gl_FindModelFrame -// -//=========================================================================== -EXTERN_CVAR (Bool, r_drawvoxels) - -FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped) -{ - if (GetDefaultByType(ti)->hasmodel) - { - FSpriteModelFrame smf; - - memset(&smf, 0, sizeof(smf)); - smf.type=ti; - smf.sprite=sprite; - smf.frame=frame; - - int hash = SpriteModelHash[ModelFrameHash(&smf) % SpriteModelFrames.Size()]; - - while (hash>=0) - { - FSpriteModelFrame * smff = &SpriteModelFrames[hash]; - if (smff->type==ti && smff->sprite==sprite && smff->frame==frame) return smff; - hash=smff->hashnext; - } - } - - // Check for voxel replacements - if (r_drawvoxels) - { - spritedef_t *sprdef = &sprites[sprite]; - if (frame < sprdef->numframes) - { - spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + frame]; - if (sprframe->Voxel != nullptr) - { - int index = sprframe->Voxel->VoxeldefIndex; - if (dropped && sprframe->Voxel->DroppedSpin !=sprframe->Voxel->PlacedSpin) index++; - return &SpriteModelFrames[index]; - } - } - } - return nullptr; -} - - //=========================================================================== // // gl_RenderModel @@ -1200,25 +377,3 @@ void gl_RenderHUDModel(DPSprite *psp, float ofsX, float ofsY) FGLModelRenderer renderer; renderer.RenderHUDModel(psp, ofsX, ofsY); } - -//=========================================================================== -// -// gl_IsHUDModelForPlayerAvailable -// -//=========================================================================== - -bool gl_IsHUDModelForPlayerAvailable (player_t * player) -{ - if (player == nullptr || player->ReadyWeapon == nullptr) - return false; - - DPSprite *psp = player->FindPSprite(PSP_WEAPON); - - if (psp == nullptr || psp->GetState() == nullptr) - return false; - - FState* state = psp->GetState(); - FSpriteModelFrame *smf = gl_FindModelFrame(player->ReadyWeapon->GetClass(), state->sprite, state->GetFrame(), false); - return ( smf != nullptr ); -} - diff --git a/src/gl/models/gl_models.h b/src/gl/models/gl_models.h index cc41466db..aa4d44939 100644 --- a/src/gl/models/gl_models.h +++ b/src/gl/models/gl_models.h @@ -20,62 +20,14 @@ //-------------------------------------------------------------------------- // -#ifndef __GL_MODELS_H_ -#define __GL_MODELS_H_ +#pragma once #include "tarray.h" #include "gl/utility/gl_geometric.h" #include "gl/data/gl_vertexbuffer.h" #include "p_pspr.h" #include "r_data/voxels.h" - - -#define MAX_LODS 4 - -enum { VX, VZ, VY }; - -#define MD2_MAGIC 0x32504449 -#define DMD_MAGIC 0x4D444D44 -#define MD3_MAGIC 0x33504449 -#define NUMVERTEXNORMALS 162 -#define MD3_MAX_SURFACES 32 - -FTextureID LoadSkin(const char * path, const char * fn); - -// [JM] Necessary forward declaration -typedef struct FSpriteModelFrame FSpriteModelFrame; - -class FModelRenderer -{ -public: - virtual ~FModelRenderer() { } - - void RenderModel(float x, float y, float z, FSpriteModelFrame *modelframe, AActor *actor); - void RenderHUDModel(DPSprite *psp, float ofsx, float ofsy); - - virtual void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) = 0; - virtual void EndDrawModel(AActor *actor, FSpriteModelFrame *smf) = 0; - - virtual IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) = 0; - - virtual void SetVertexBuffer(IModelVertexBuffer *buffer) = 0; - virtual void ResetVertexBuffer() = 0; - - virtual VSMatrix GetViewToWorldMatrix() = 0; - - virtual void BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) = 0; - virtual void EndDrawHUDModel(AActor *actor) = 0; - - virtual void SetInterpolation(double interpolation) = 0; - virtual void SetMaterial(FTexture *skin, int clampmode, int translation) = 0; - virtual void DrawArrays(int primitiveType, int start, int count) = 0; - virtual void DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) = 0; - - virtual double GetTimeFloat() = 0; - -private: - void RenderFrameModels(const FSpriteModelFrame *smf, const FState *curState, const int curTics, const PClass *ti, Matrix3x4 *normaltransform, int translation); -}; +#include "models/models.h" class FGLModelRenderer : public FModelRenderer { @@ -95,382 +47,5 @@ public: double GetTimeFloat() override; }; -class FModel -{ -public: - - FModel() - { - mVBuf = NULL; - } - 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, FTexture * skin, int frame, int frame2, double inter, int translation=0) = 0; - virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0; - virtual void AddSkins(uint8_t *hitlist) = 0; - void DestroyVertexBuffer() - { - delete mVBuf; - mVBuf = NULL; - } - virtual float getAspectFactor() { return 1.f; } - - const FSpriteModelFrame *curSpriteMDLFrame; - int curMDLIndex; - void PushSpriteMDLFrame(const FSpriteModelFrame *smf, int index) { curSpriteMDLFrame = smf; curMDLIndex = index; }; - - IModelVertexBuffer *mVBuf; - FString mFileName; -}; - -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, FTexture * 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(); - -}; - - -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 - { - int numVertices; - int numTriangles; - int numSkins; - - FTextureID * skins; - MD3Triangle * tris; - MD3TexCoord * texcoords; - MD3Vertex * vertices; - - unsigned int vindex; // contains numframes arrays of vertices - unsigned int iindex; - - MD3Surface() - { - tris=NULL; - vertices=NULL; - texcoords=NULL; - vindex = iindex = UINT_MAX; - } - - ~MD3Surface() - { - if (skins) delete [] skins; - UnloadGeometry(); - } - - void UnloadGeometry() - { - if (tris) delete [] tris; - if (vertices) delete [] vertices; - if (texcoords) delete [] texcoords; - tris = NULL; - vertices = NULL; - texcoords = NULL; - } - }; - - 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 numFrames; - int numTags; - int numSurfaces; - int mLumpNum; - - MD3Frame * frames; - MD3Surface * surfaces; - -public: - FMD3Model() { } - virtual ~FMD3Model(); - - virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length); - virtual int FindFrame(const char * name); - virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0); - void LoadGeometry(); - void BuildVertexBuffer(FModelRenderer *renderer); - virtual void AddSkins(uint8_t *hitlist); -}; - -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 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 mVertices; - TArray 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, FTexture * 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(); -}; - - - -#define MAX_MODELS_PER_FRAME 4 - -// -// [BB] Model rendering flags. -// -enum -{ - // [BB] Color translations for the model skin are ignored. This is - // useful if the skin texture is not using the game palette. - MDL_IGNORETRANSLATION = 1, - MDL_PITCHFROMMOMENTUM = 2, - MDL_ROTATING = 4, - MDL_INTERPOLATEDOUBLEDFRAMES = 8, - MDL_NOINTERPOLATION = 16, - MDL_USEACTORPITCH = 32, - MDL_USEACTORROLL = 64, - MDL_BADROTATION = 128, - MDL_DONTCULLBACKFACES = 256, -}; - -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 PClass * type; - short sprite; - short frame; - FState * state; // for later! - 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. -}; - -class GLSprite; - -FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped); - void gl_RenderModel(GLSprite * spr); -// [BB] HUD weapon model rendering functions. void gl_RenderHUDModel(DPSprite *psp, float ofsx, float ofsy); -bool gl_IsHUDModelForPlayerAvailable (player_t * player); - - -class DeletingModelArray : public TArray -{ -public: - - ~DeletingModelArray() - { - for (unsigned i = 0; i Voxels; +extern TDeletingArray VoxelDefs; + +DeletingModelArray Models; + +void FModelRenderer::RenderModel(float x, float y, float z, FSpriteModelFrame *smf, AActor *actor) +{ + // Setup transformation. + + int translation = 0; + if (!(smf->flags & MDL_IGNORETRANSLATION)) + translation = actor->Translation; + + // y scale for a sprite means height, i.e. z in the world! + float scaleFactorX = actor->Scale.X * smf->xscale; + float scaleFactorY = actor->Scale.X * smf->yscale; + float scaleFactorZ = actor->Scale.Y * smf->zscale; + float pitch = 0; + float roll = 0; + double rotateOffset = 0; + float angle = actor->Angles.Yaw.Degrees; + + // [BB] Workaround for the missing pitch information. + if ((smf->flags & MDL_PITCHFROMMOMENTUM)) + { + const double x = actor->Vel.X; + const double y = actor->Vel.Y; + const double z = actor->Vel.Z; + + if (actor->Vel.LengthSquared() > EQUAL_EPSILON) + { + // [BB] Calculate the pitch using spherical coordinates. + if (z || x || y) pitch = float(atan(z / sqrt(x*x + y*y)) / M_PI * 180); + + // Correcting pitch if model is moving backwards + if (fabs(x) > EQUAL_EPSILON || fabs(y) > EQUAL_EPSILON) + { + if ((x * cos(angle * M_PI / 180) + y * sin(angle * M_PI / 180)) / sqrt(x * x + y * y) < 0) pitch *= -1; + } + else pitch = fabs(pitch); + } + } + + if (smf->flags & MDL_ROTATING) + { + const double time = smf->rotationSpeed*GetTimeFloat() / 200.; + rotateOffset = double((time - xs_FloorToInt(time)) *360.); + } + + // Added MDL_USEACTORPITCH and MDL_USEACTORROLL flags processing. + // If both flags MDL_USEACTORPITCH and MDL_PITCHFROMMOMENTUM are set, the pitch sums up the actor pitch and the velocity vector pitch. + if (smf->flags & MDL_USEACTORPITCH) + { + double d = actor->Angles.Pitch.Degrees; + if (smf->flags & MDL_BADROTATION) pitch += d; + else pitch -= d; + } + if (smf->flags & MDL_USEACTORROLL) roll += actor->Angles.Roll.Degrees; + + VSMatrix objectToWorldMatrix; + objectToWorldMatrix.loadIdentity(); + + // Model space => World space + objectToWorldMatrix.translate(x, z, y); + + // [Nash] take SpriteRotation into account + angle += actor->SpriteRotation.Degrees; + + if (actor->renderflags & RF_INTERPOLATEANGLES) + { + // [Nash] use interpolated angles + DRotator Angles = actor->InterpolatedAngles(r_viewpoint.TicFrac); + angle = Angles.Yaw.Degrees; + } + + // Applying model transformations: + // 1) Applying actor angle, pitch and roll to the model + objectToWorldMatrix.rotate(-angle, 0, 1, 0); + objectToWorldMatrix.rotate(pitch, 0, 0, 1); + objectToWorldMatrix.rotate(-roll, 1, 0, 0); + + // 2) Applying Doomsday like rotation of the weapon pickup models + // The rotation angle is based on the elapsed time. + + if (smf->flags & MDL_ROTATING) + { + objectToWorldMatrix.translate(smf->rotationCenterX, smf->rotationCenterY, smf->rotationCenterZ); + objectToWorldMatrix.rotate(rotateOffset, smf->xrotate, smf->yrotate, smf->zrotate); + objectToWorldMatrix.translate(-smf->rotationCenterX, -smf->rotationCenterY, -smf->rotationCenterZ); + } + + // 3) Scaling model. + objectToWorldMatrix.scale(scaleFactorX, scaleFactorZ, scaleFactorY); + + // 4) Aplying model offsets (model offsets do not depend on model scalings). + objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); + + // 5) Applying model rotations. + objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); + objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); + objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); + + // consider the pixel stretching. For non-voxels this must be factored out here + float stretch = (smf->modelIDs[0] != -1 ? Models[smf->modelIDs[0]]->getAspectFactor() : 1.f) / level.info->pixelstretch; + objectToWorldMatrix.scale(1, stretch, 1); + + BeginDrawModel(actor, smf, objectToWorldMatrix); + RenderFrameModels(smf, actor->state, actor->tics, actor->GetClass(), nullptr, translation); + EndDrawModel(actor, smf); +} + +void FModelRenderer::RenderHUDModel(DPSprite *psp, float ofsX, float ofsY) +{ + AActor * playermo = players[consoleplayer].camera; + FSpriteModelFrame *smf = gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false); + + // [BB] No model found for this sprite, so we can't render anything. + if (smf == nullptr) + return; + + // The model position and orientation has to be drawn independently from the position of the player, + // but we need to position it correctly in the world for light to work properly. + VSMatrix objectToWorldMatrix = GetViewToWorldMatrix(); + + // Scaling model (y scale for a sprite means height, i.e. z in the world!). + objectToWorldMatrix.scale(smf->xscale, smf->zscale, smf->yscale); + + // Aplying model offsets (model offsets do not depend on model scalings). + objectToWorldMatrix.translate(smf->xoffset / smf->xscale, smf->zoffset / smf->zscale, smf->yoffset / smf->yscale); + + // [BB] Weapon bob, very similar to the normal Doom weapon bob. + objectToWorldMatrix.rotate(ofsX / 4, 0, 1, 0); + objectToWorldMatrix.rotate((ofsY - WEAPONTOP) / -4., 1, 0, 0); + + // [BB] For some reason the jDoom models need to be rotated. + objectToWorldMatrix.rotate(90.f, 0, 1, 0); + + // Applying angleoffset, pitchoffset, rolloffset. + objectToWorldMatrix.rotate(-smf->angleoffset, 0, 1, 0); + objectToWorldMatrix.rotate(smf->pitchoffset, 0, 0, 1); + objectToWorldMatrix.rotate(-smf->rolloffset, 1, 0, 0); + + BeginDrawHUDModel(playermo, objectToWorldMatrix); + RenderFrameModels(smf, psp->GetState(), psp->GetTics(), playermo->player->ReadyWeapon->GetClass(), nullptr, 0); + EndDrawHUDModel(playermo); +} + +void FModelRenderer::RenderFrameModels(const FSpriteModelFrame *smf, + const FState *curState, + const int curTics, + const PClass *ti, + Matrix3x4 *normaltransform, + int translation) +{ + // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation + // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. + FSpriteModelFrame * smfNext = nullptr; + double inter = 0.; + if (gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION)) + { + FState *nextState = curState->GetNextState(); + if (curState != nextState && nextState) + { + // [BB] To interpolate at more than 35 fps we take tic fractions into account. + float ticFraction = 0.; + // [BB] In case the tic counter is frozen we have to leave ticFraction at zero. + if (ConsoleState == c_up && menuactive != MENU_On && !(level.flags2 & LEVEL2_FROZEN)) + { + double time = GetTimeFloat(); + ticFraction = (time - static_cast(time)); + } + inter = static_cast(curState->Tics - curTics - ticFraction) / static_cast(curState->Tics); + + // [BB] For some actors (e.g. ZPoisonShroom) spr->actor->tics can be bigger than curState->Tics. + // In this case inter is negative and we need to set it to zero. + if (inter < 0.) + inter = 0.; + else + { + // [BB] Workaround for actors that use the same frame twice in a row. + // Most of the standard Doom monsters do this in their see state. + if ((smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES)) + { + const FState *prevState = curState - 1; + if ((curState->sprite == prevState->sprite) && (curState->Frame == prevState->Frame)) + { + inter /= 2.; + inter += 0.5; + } + if ((curState->sprite == nextState->sprite) && (curState->Frame == nextState->Frame)) + { + inter /= 2.; + nextState = nextState->GetNextState(); + } + } + if (inter != 0.0) + smfNext = gl_FindModelFrame(ti, nextState->sprite, nextState->Frame, false); + } + } + } + + for (int i = 0; imodelIDs[i] != -1) + { + FModel * mdl = Models[smf->modelIDs[i]]; + FTexture *tex = smf->skinIDs[i].isValid() ? TexMan(smf->skinIDs[i]) : nullptr; + mdl->BuildVertexBuffer(this); + SetVertexBuffer(mdl->mVBuf); + + mdl->PushSpriteMDLFrame(smf, i); + + if (smfNext && smf->modelframes[i] != smfNext->modelframes[i]) + mdl->RenderFrame(this, tex, smf->modelframes[i], smfNext->modelframes[i], inter, translation); + else + mdl->RenderFrame(this, tex, smf->modelframes[i], smf->modelframes[i], 0.f, translation); + + ResetVertexBuffer(); + } + } +} + +///////////////////////////////////////////////////////////////////////////// + +void gl_LoadModels() +{ + /* + for (int i = Models.Size() - 1; i >= 0; i--) + { + Models[i]->BuildVertexBuffer(); + } + */ +} + +void gl_FlushModels() +{ + for (int i = Models.Size() - 1; i >= 0; i--) + { + Models[i]->DestroyVertexBuffer(); + } +} + +///////////////////////////////////////////////////////////////////////////// + +FModel::~FModel() +{ + if (mVBuf != nullptr) delete mVBuf; +} + +static TArray SpriteModelFrames; +static int * SpriteModelHash; +//TArray StateModelFrames; + +static void DeleteModelHash() +{ + if (SpriteModelHash != nullptr) delete [] SpriteModelHash; + SpriteModelHash = nullptr; +} + +//=========================================================================== +// +// FindGFXFile +// +//=========================================================================== + +static int FindGFXFile(FString & fn) +{ + int lump = Wads.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 = Wads.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 : Wads.GetLumpFullName(texlump); + return TexMan.CheckForTexture(texname, FTexture::TEX_Any, FTextureManager::TEXMAN_TryAny); +} + +//=========================================================================== +// +// ModelFrameHash +// +//=========================================================================== + +static 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 (; smFileName.CompareNoCase(fullname)) return i; + } + + int len = Wads.LumpLength(lump); + FMemLump lumpd = Wads.ReadLump(lump); + char * buffer = (char*)lumpd.GetMem(); + + 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); +} + +//=========================================================================== +// +// gl_InitModels +// +//=========================================================================== + +void gl_InitModels() +{ + int Lump, lastLump; + FString path; + int index, surface; + int i; + + FSpriteModelFrame smf; + + lastLump = 0; + + for(unsigned i=0;iVoxelIndex = Models.Push(md); + } + // now create GL model frames for the voxeldefs + for (unsigned i = 0; i < VoxelDefs.Size(); i++) + { + FVoxelModel *md = (FVoxelModel*)Models[VoxelDefs[i]->Voxel->VoxelIndex]; + memset(&smf, 0, sizeof(smf)); + smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1; + smf.modelIDs[0] = VoxelDefs[i]->Voxel->VoxelIndex; + smf.skinIDs[0] = md->GetPaletteTexture(); + smf.xscale = smf.yscale = smf.zscale = VoxelDefs[i]->Scale; + smf.angleoffset = VoxelDefs[i]->AngleOffset.Degrees; + if (VoxelDefs[i]->PlacedSpin != 0) + { + smf.yrotate = 1.f; + smf.rotationSpeed = VoxelDefs[i]->PlacedSpin / 55.55f; + smf.flags |= MDL_ROTATING; + } + VoxelDefs[i]->VoxeldefIndex = SpriteModelFrames.Push(smf); + if (VoxelDefs[i]->PlacedSpin != VoxelDefs[i]->DroppedSpin) + { + if (VoxelDefs[i]->DroppedSpin != 0) + { + smf.yrotate = 1.f; + smf.rotationSpeed = VoxelDefs[i]->DroppedSpin / 55.55f; + smf.flags |= MDL_ROTATING; + } + else + { + smf.yrotate = 0; + smf.rotationSpeed = 0; + smf.flags &= ~MDL_ROTATING; + } + SpriteModelFrames.Push(smf); + } + } + + memset(&smf, 0, sizeof(smf)); + smf.modelIDs[0] = smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1; + while ((Lump = Wads.FindLump("MODELDEF", &lastLump)) != -1) + { + FScanner sc(Lump); + while (sc.GetString()) + { + if (sc.Compare("model")) + { + path = ""; + sc.MustGetString(); + memset(&smf, 0, sizeof(smf)); + smf.modelIDs[0] = smf.modelIDs[1] = smf.modelIDs[2] = smf.modelIDs[3] = -1; + smf.xscale=smf.yscale=smf.zscale=1.f; + + smf.type = PClass::FindClass(sc.String); + if (!smf.type || smf.type->Defaults == nullptr) + { + sc.ScriptError("MODELDEF: Unknown actor type '%s'\n", sc.String); + } + sc.MustGetStringName("{"); + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("path")) + { + sc.MustGetString(); + FixPathSeperator(sc.String); + path = sc.String; + if (path[(int)path.Len()-1]!='/') path+='/'; + } + else if (sc.Compare("model")) + { + sc.MustGetNumber(); + index = sc.Number; + if (index < 0 || index >= MAX_MODELS_PER_FRAME) + { + sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); + } + sc.MustGetString(); + FixPathSeperator(sc.String); + smf.modelIDs[index] = FindModel(path.GetChars(), sc.String); + if (smf.modelIDs[index] == -1) + { + Printf("%s: model not found in %s\n", sc.String, path.GetChars()); + } + } + else if (sc.Compare("scale")) + { + sc.MustGetFloat(); + smf.xscale = sc.Float; + sc.MustGetFloat(); + smf.yscale = sc.Float; + sc.MustGetFloat(); + smf.zscale = sc.Float; + } + // [BB] Added zoffset reading. + // Now it must be considered deprecated. + else if (sc.Compare("zoffset")) + { + sc.MustGetFloat(); + smf.zoffset=sc.Float; + } + // Offset reading. + else if (sc.Compare("offset")) + { + sc.MustGetFloat(); + smf.xoffset = sc.Float; + sc.MustGetFloat(); + smf.yoffset = sc.Float; + sc.MustGetFloat(); + smf.zoffset = sc.Float; + } + // angleoffset, pitchoffset and rolloffset reading. + else if (sc.Compare("angleoffset")) + { + sc.MustGetFloat(); + smf.angleoffset = sc.Float; + } + else if (sc.Compare("pitchoffset")) + { + sc.MustGetFloat(); + smf.pitchoffset = sc.Float; + } + else if (sc.Compare("rolloffset")) + { + sc.MustGetFloat(); + smf.rolloffset = sc.Float; + } + // [BB] Added model flags reading. + else if (sc.Compare("ignoretranslation")) + { + smf.flags |= MDL_IGNORETRANSLATION; + } + else if (sc.Compare("pitchfrommomentum")) + { + smf.flags |= MDL_PITCHFROMMOMENTUM; + } + else if (sc.Compare("inheritactorpitch")) + { + smf.flags |= MDL_USEACTORPITCH | MDL_BADROTATION; + } + else if (sc.Compare("inheritactorroll")) + { + smf.flags |= MDL_USEACTORROLL; + } + else if (sc.Compare("useactorpitch")) + { + smf.flags |= MDL_USEACTORPITCH; + } + else if (sc.Compare("useactorroll")) + { + smf.flags |= MDL_USEACTORROLL; + } + else if (sc.Compare("rotating")) + { + smf.flags |= MDL_ROTATING; + smf.xrotate = 0.; + smf.yrotate = 1.; + smf.zrotate = 0.; + smf.rotationCenterX = 0.; + smf.rotationCenterY = 0.; + smf.rotationCenterZ = 0.; + smf.rotationSpeed = 1.; + } + else if (sc.Compare("rotation-speed")) + { + sc.MustGetFloat(); + smf.rotationSpeed = sc.Float; + } + else if (sc.Compare("rotation-vector")) + { + sc.MustGetFloat(); + smf.xrotate = sc.Float; + sc.MustGetFloat(); + smf.yrotate = sc.Float; + sc.MustGetFloat(); + smf.zrotate = sc.Float; + } + else if (sc.Compare("rotation-center")) + { + sc.MustGetFloat(); + smf.rotationCenterX = sc.Float; + sc.MustGetFloat(); + smf.rotationCenterY = sc.Float; + sc.MustGetFloat(); + smf.rotationCenterZ = sc.Float; + } + else if (sc.Compare("interpolatedoubledframes")) + { + smf.flags |= MDL_INTERPOLATEDOUBLEDFRAMES; + } + else if (sc.Compare("nointerpolation")) + { + smf.flags |= MDL_NOINTERPOLATION; + } + else if (sc.Compare("skin")) + { + sc.MustGetNumber(); + index=sc.Number; + if (index<0 || index>=MAX_MODELS_PER_FRAME) + { + sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); + } + sc.MustGetString(); + FixPathSeperator(sc.String); + if (sc.Compare("")) + { + smf.skinIDs[index]=FNullTextureID(); + } + else + { + smf.skinIDs[index] = LoadSkin(path.GetChars(), sc.String); + if (!smf.skinIDs[index].isValid()) + { + Printf("Skin '%s' not found in '%s'\n", + sc.String, smf.type->TypeName.GetChars()); + } + } + } + else if (sc.Compare("surfaceskin")) + { + sc.MustGetNumber(); + index = sc.Number; + sc.MustGetNumber(); + surface = sc.Number; + + if (index<0 || index >= MAX_MODELS_PER_FRAME) + { + sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); + } + + if (surface<0 || surface >= MD3_MAX_SURFACES) + { + sc.ScriptError("Invalid MD3 Surface %d in %s", MD3_MAX_SURFACES, smf.type->TypeName.GetChars()); + } + + sc.MustGetString(); + FixPathSeperator(sc.String); + if (sc.Compare("")) + { + smf.surfaceskinIDs[index][surface] = FNullTextureID(); + } + else + { + smf.surfaceskinIDs[index][surface] = LoadSkin(path.GetChars(), sc.String); + if (!smf.surfaceskinIDs[index][surface].isValid()) + { + Printf("Surface Skin '%s' not found in '%s'\n", + sc.String, smf.type->TypeName.GetChars()); + } + } + } + else if (sc.Compare("frameindex") || sc.Compare("frame")) + { + bool isframe=!!sc.Compare("frame"); + + sc.MustGetString(); + smf.sprite = -1; + for (i = 0; i < (int)sprites.Size (); ++i) + { + if (strnicmp (sprites[i].name, sc.String, 4) == 0) + { + if (sprites[i].numframes==0) + { + //sc.ScriptError("Sprite %s has no frames", sc.String); + } + smf.sprite = i; + break; + } + } + if (smf.sprite==-1) + { + sc.ScriptError("Unknown sprite %s in model definition for %s", sc.String, smf.type->TypeName.GetChars()); + } + + sc.MustGetString(); + FString framechars = sc.String; + + sc.MustGetNumber(); + index=sc.Number; + if (index<0 || index>=MAX_MODELS_PER_FRAME) + { + sc.ScriptError("Too many models in %s", smf.type->TypeName.GetChars()); + } + if (isframe) + { + sc.MustGetString(); + if (smf.modelIDs[index] != -1) + { + FModel *model = Models[smf.modelIDs[index]]; + smf.modelframes[index] = model->FindFrame(sc.String); + if (smf.modelframes[index]==-1) sc.ScriptError("Unknown frame '%s' in %s", sc.String, smf.type->TypeName.GetChars()); + } + else smf.modelframes[index] = -1; + } + else + { + sc.MustGetNumber(); + smf.modelframes[index] = sc.Number; + } + + for(i=0; framechars[i]>0; i++) + { + char map[29]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + int c = toupper(framechars[i])-'A'; + + if (c<0 || c>=29) + { + sc.ScriptError("Invalid frame character %c found", c+'A'); + } + if (map[c]) continue; + smf.frame=c; + SpriteModelFrames.Push(smf); + GetDefaultByType(smf.type)->hasmodel = true; + map[c]=1; + } + } + else if (sc.Compare("dontcullbackfaces")) + { + smf.flags |= MDL_DONTCULLBACKFACES; + } + else + { + sc.ScriptMessage("Unrecognized string \"%s\"", sc.String); + } + } + } + } + } + + // create a hash table for quick access + SpriteModelHash = new int[SpriteModelFrames.Size ()]; + atterm(DeleteModelHash); + memset(SpriteModelHash, 0xff, SpriteModelFrames.Size () * sizeof(int)); + + for (i = 0; i < (int)SpriteModelFrames.Size (); i++) + { + int j = ModelFrameHash(&SpriteModelFrames[i]) % SpriteModelFrames.Size (); + + SpriteModelFrames[i].hashnext = SpriteModelHash[j]; + SpriteModelHash[j]=i; + } +} + +//=========================================================================== +// +// gl_FindModelFrame +// +//=========================================================================== + +FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped) +{ + if (GetDefaultByType(ti)->hasmodel) + { + FSpriteModelFrame smf; + + memset(&smf, 0, sizeof(smf)); + smf.type=ti; + smf.sprite=sprite; + smf.frame=frame; + + int hash = SpriteModelHash[ModelFrameHash(&smf) % SpriteModelFrames.Size()]; + + while (hash>=0) + { + FSpriteModelFrame * smff = &SpriteModelFrames[hash]; + if (smff->type==ti && smff->sprite==sprite && smff->frame==frame) return smff; + hash=smff->hashnext; + } + } + + // Check for voxel replacements + if (r_drawvoxels) + { + spritedef_t *sprdef = &sprites[sprite]; + if (frame < sprdef->numframes) + { + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + frame]; + if (sprframe->Voxel != nullptr) + { + int index = sprframe->Voxel->VoxeldefIndex; + if (dropped && sprframe->Voxel->DroppedSpin !=sprframe->Voxel->PlacedSpin) index++; + return &SpriteModelFrames[index]; + } + } + } + return nullptr; +} + +//=========================================================================== +// +// gl_IsHUDModelForPlayerAvailable +// +//=========================================================================== + +bool gl_IsHUDModelForPlayerAvailable (player_t * player) +{ + if (player == nullptr || player->ReadyWeapon == nullptr) + return false; + + DPSprite *psp = player->FindPSprite(PSP_WEAPON); + + if (psp == nullptr || psp->GetState() == nullptr) + return false; + + FState* state = psp->GetState(); + FSpriteModelFrame *smf = gl_FindModelFrame(player->ReadyWeapon->GetClass(), state->sprite, state->GetFrame(), false); + return ( smf != nullptr ); +} + diff --git a/src/models/models.h b/src/models/models.h new file mode 100644 index 000000000..d7172323d --- /dev/null +++ b/src/models/models.h @@ -0,0 +1,497 @@ +// +//--------------------------------------------------------------------------- +// +// 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/ +// +//-------------------------------------------------------------------------- +// + +#ifndef __GL_MODELS_H_ +#define __GL_MODELS_H_ + +#include "tarray.h" +#include "gl/utility/gl_geometric.h" +#include "gl/data/gl_matrix.h" +#include "actor.h" +#include "dobject.h" +#include "p_pspr.h" +#include "r_data/voxels.h" +#include "info.h" + +#define MAX_LODS 4 + +enum { VX, VZ, VY }; + +#define MD2_MAGIC 0x32504449 +#define DMD_MAGIC 0x4D444D44 +#define MD3_MAGIC 0x33504449 +#define NUMVERTEXNORMALS 162 +#define MD3_MAX_SURFACES 32 + +FTextureID LoadSkin(const char * path, const char * fn); + +struct FSpriteModelFrame; +class IModelVertexBuffer; + +class FModelRenderer +{ +public: + virtual ~FModelRenderer() { } + + void RenderModel(float x, float y, float z, FSpriteModelFrame *modelframe, AActor *actor); + void RenderHUDModel(DPSprite *psp, float ofsx, float ofsy); + + virtual void BeginDrawModel(AActor *actor, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix) = 0; + virtual void EndDrawModel(AActor *actor, FSpriteModelFrame *smf) = 0; + + virtual IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) = 0; + + virtual void SetVertexBuffer(IModelVertexBuffer *buffer) = 0; + virtual void ResetVertexBuffer() = 0; + + virtual VSMatrix GetViewToWorldMatrix() = 0; + + virtual void BeginDrawHUDModel(AActor *actor, const VSMatrix &objectToWorldMatrix) = 0; + virtual void EndDrawHUDModel(AActor *actor) = 0; + + virtual void SetInterpolation(double interpolation) = 0; + virtual void SetMaterial(FTexture *skin, int clampmode, int translation) = 0; + virtual void DrawArrays(int primitiveType, int start, int count) = 0; + virtual void DrawElements(int primitiveType, int numIndices, int elementType, size_t offset) = 0; + + virtual double GetTimeFloat() = 0; + +private: + void RenderFrameModels(const FSpriteModelFrame *smf, const FState *curState, const int curTics, const PClass *ti, Matrix3x4 *normaltransform, int translation); +}; + +struct FModelVertex +{ + float x, y, z; // world position + float u, v; // texture coordinates + unsigned packedNormal; // normal vector as GL_INT_2_10_10_10_REV. + + void Set(float xx, float yy, float zz, float uu, float vv) + { + x = xx; + y = yy; + z = zz; + u = uu; + v = vv; + } + + void SetNormal(float nx, float ny, float nz) + { + int inx = clamp(int(nx * 512), -512, 511); + int iny = clamp(int(ny * 512), -512, 511); + int inz = clamp(int(nz * 512), -512, 511); + int inw = 0; + packedNormal = (inw << 30) | ((inz & 1023) << 20) | ((iny & 1023) << 10) | (inx & 1023); + } +}; + +#define VMO ((FModelVertex*)NULL) + +class FModelRenderer; + +class IModelVertexBuffer +{ +public: + virtual ~IModelVertexBuffer() { } + + virtual FModelVertex *LockVertexBuffer(unsigned int size) = 0; + virtual void UnlockVertexBuffer() = 0; + + virtual unsigned int *LockIndexBuffer(unsigned int size) = 0; + virtual void UnlockIndexBuffer() = 0; + + virtual void SetupFrame(FModelRenderer *renderer, unsigned int frame1, unsigned int frame2, unsigned int size) = 0; +}; + +class FModel +{ +public: + + FModel() + { + mVBuf = NULL; + } + 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, FTexture * skin, int frame, int frame2, double inter, int translation=0) = 0; + virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0; + virtual void AddSkins(uint8_t *hitlist) = 0; + void DestroyVertexBuffer() + { + delete mVBuf; + mVBuf = NULL; + } + virtual float getAspectFactor() { return 1.f; } + + const FSpriteModelFrame *curSpriteMDLFrame; + int curMDLIndex; + void PushSpriteMDLFrame(const FSpriteModelFrame *smf, int index) { curSpriteMDLFrame = smf; curMDLIndex = index; }; + + IModelVertexBuffer *mVBuf; + FString mFileName; +}; + +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, FTexture * 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(); + +}; + + +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 + { + int numVertices; + int numTriangles; + int numSkins; + + FTextureID * skins; + MD3Triangle * tris; + MD3TexCoord * texcoords; + MD3Vertex * vertices; + + unsigned int vindex; // contains numframes arrays of vertices + unsigned int iindex; + + MD3Surface() + { + tris=NULL; + vertices=NULL; + texcoords=NULL; + vindex = iindex = UINT_MAX; + } + + ~MD3Surface() + { + if (skins) delete [] skins; + UnloadGeometry(); + } + + void UnloadGeometry() + { + if (tris) delete [] tris; + if (vertices) delete [] vertices; + if (texcoords) delete [] texcoords; + tris = NULL; + vertices = NULL; + texcoords = NULL; + } + }; + + 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 numFrames; + int numTags; + int numSurfaces; + int mLumpNum; + + MD3Frame * frames; + MD3Surface * surfaces; + +public: + FMD3Model() { } + virtual ~FMD3Model(); + + virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length); + virtual int FindFrame(const char * name); + virtual void RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation=0); + void LoadGeometry(); + void BuildVertexBuffer(FModelRenderer *renderer); + virtual void AddSkins(uint8_t *hitlist); +}; + +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 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 mVertices; + TArray 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, FTexture * 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(); +}; + + + +#define MAX_MODELS_PER_FRAME 4 + +// +// [BB] Model rendering flags. +// +enum +{ + // [BB] Color translations for the model skin are ignored. This is + // useful if the skin texture is not using the game palette. + MDL_IGNORETRANSLATION = 1, + MDL_PITCHFROMMOMENTUM = 2, + MDL_ROTATING = 4, + MDL_INTERPOLATEDOUBLEDFRAMES = 8, + MDL_NOINTERPOLATION = 16, + MDL_USEACTORPITCH = 32, + MDL_USEACTORROLL = 64, + MDL_BADROTATION = 128, + MDL_DONTCULLBACKFACES = 256, +}; + +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 PClass * type; + short sprite; + short frame; + FState * state; // for later! + 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. +}; + +FSpriteModelFrame * gl_FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped); + +bool gl_IsHUDModelForPlayerAvailable (player_t * player); + +class DeletingModelArray : public TArray +{ +public: + + ~DeletingModelArray() + { + for (unsigned i = 0; i