- Move models into r_data

This commit is contained in:
Magnus Norddahl 2017-11-25 13:19:00 +01:00
commit 7bb92812b8
9 changed files with 12 additions and 13 deletions

View file

@ -0,0 +1,877 @@
//
//---------------------------------------------------------------------------
//
// 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 "gl/system/gl_system.h"
#include "w_wad.h"
#include "cmdlib.h"
#include "sc_man.h"
#include "m_crc32.h"
#include "c_console.h"
#include "g_game.h"
#include "doomstat.h"
#include "g_level.h"
#include "r_state.h"
#include "d_player.h"
#include "g_levellocals.h"
#include "r_utility.h"
#include "i_time.h"
#include "r_data/models/models.h"
CVAR(Bool, gl_interpolate_model_frames, true, CVAR_ARCHIVE)
EXTERN_CVAR(Bool, r_drawvoxels)
extern TDeletingArray<FVoxel *> Voxels;
extern TDeletingArray<FVoxelDef *> 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<int>(time));
}
inter = static_cast<double>(curState->Tics - curTics - ticFraction) / static_cast<double>(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; i<MAX_MODELS_PER_FRAME; i++)
{
if (smf->modelIDs[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<FSpriteModelFrame> SpriteModelFrames;
static int * SpriteModelHash;
//TArray<FStateModelFrame> 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 (; s<se; s++)
{
hash = CRC1 (hash, *s, table);
}
return hash ^ 0xffffffff;
}
//===========================================================================
//
// FindModel
//
//===========================================================================
static unsigned FindModel(const char * path, const char * modelfile)
{
FModel * model = nullptr;
FString fullname;
fullname.Format("%s%s", path, modelfile);
int lump = Wads.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 = 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;i<Models.Size();i++)
{
delete Models[i];
}
Models.Clear();
SpriteModelFrames.Clear();
DeleteModelHash();
// First, create models for each voxel
for (unsigned i = 0; i < Voxels.Size(); i++)
{
FVoxelModel *md = new FVoxelModel(Voxels[i], false);
Voxels[i]->VoxelIndex = 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 );
}

497
src/r_data/models/models.h Normal file
View file

@ -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, bool clampNoFilter, int translation) = 0;
virtual void DrawArrays(int start, int count) = 0;
virtual void DrawElements(int numIndices, 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<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, 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<FModel *>
{
public:
~DeletingModelArray()
{
for (unsigned i = 0; i<Size(); i++)
{
delete (*this)[i];
}
}
};
extern DeletingModelArray Models;
#endif

View file

@ -0,0 +1,550 @@
//
//---------------------------------------------------------------------------
//
// 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 "w_wad.h"
#include "cmdlib.h"
#include "sc_man.h"
#include "m_crc32.h"
#include "r_data/models/models.h"
#ifdef _MSC_VER
#pragma warning(disable:4244) // warning C4244: conversion from 'double' to 'float', possible loss of data
#endif
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 };
FMemLump lumpdata = Wads.ReadLump(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 (mVBuf == NULL)
{
LoadGeometry();
int VertexBufferSize = info.numFrames * lodInfo[0].numTriangles * 3;
unsigned int vindex = 0;
mVBuf = renderer->CreateVertexBuffer(false, info.numFrames == 1);
FModelVertex *vertptr = mVBuf->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++;
}
}
mVBuf->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()] |= FTexture::TEX_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, FTexture * 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(skins[0]);
if (!skin) return;
}
renderer->SetInterpolation(inter);
renderer->SetMaterial(skin, false, translation);
mVBuf->SetupFrame(renderer, 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;
FMemLump lumpdata = Wads.ReadLump(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()
{
}

View file

@ -0,0 +1,386 @@
//
//---------------------------------------------------------------------------
//
// 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 "w_wad.h"
#include "cmdlib.h"
#include "sc_man.h"
#include "m_crc32.h"
#include "r_data/models/models.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;
numFrames = LittleLong(hdr->Num_Frames);
numTags = LittleLong(hdr->Num_Tags);
numSurfaces = LittleLong(hdr->Num_Surfaces);
md3_frame_t * frm = (md3_frame_t*)(buffer + LittleLong(hdr->Ofs_Frames));
frames = new MD3Frame[numFrames];
for (int 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 = new MD3Surface[numSurfaces];
for (int 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 = new FTextureID[s->numSkins];
for (int i = 0; i < s->numSkins; i++)
{
// [BB] According to the MD3 spec, Name is supposed to include the full path.
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()
{
FMemLump lumpdata = Wads.ReadLump(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(int i=0;i<numSurfaces;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 = new MD3Triangle[s->numTriangles];
for(int 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 = new MD3TexCoord[s->numVertices];
for(int 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 = new MD3Vertex[s->numVertices * numFrames];
for(int i=0;i<s->numVertices * numFrames;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 (mVBuf == nullptr)
{
LoadGeometry();
unsigned int vbufsize = 0;
unsigned int ibufsize = 0;
for (int i = 0; i < numSurfaces; i++)
{
MD3Surface * surf = &surfaces[i];
vbufsize += numFrames * surf->numVertices;
ibufsize += 3 * surf->numTriangles;
}
mVBuf = renderer->CreateVertexBuffer(true, numFrames == 1);
FModelVertex *vertptr = mVBuf->LockVertexBuffer(vbufsize);
unsigned int *indxptr = mVBuf->LockIndexBuffer(ibufsize);
assert(vertptr != nullptr && indxptr != nullptr);
unsigned int vindex = 0, iindex = 0;
for (int i = 0; i < numSurfaces; i++)
{
MD3Surface * surf = &surfaces[i];
surf->vindex = vindex;
surf->iindex = iindex;
for (int j = 0; j < numFrames * 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 (int k = 0; k < surf->numTriangles; k++)
{
for (int l = 0; l < 3; l++)
{
indxptr[iindex++] = surf->tris[k].VertIndex[l];
}
}
surf->UnloadGeometry();
}
mVBuf->UnlockVertexBuffer();
mVBuf->UnlockIndexBuffer();
}
}
//===========================================================================
//
// for skin precaching
//
//===========================================================================
void FMD3Model::AddSkins(uint8_t *hitlist)
{
for (int i = 0; i < numSurfaces; i++)
{
if (curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
{
hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTexture::TEX_Flat;
}
MD3Surface * surf = &surfaces[i];
for (int j = 0; j < surf->numSkins; j++)
{
if (surf->skins[j].isValid())
{
hitlist[surf->skins[j].GetIndex()] |= FTexture::TEX_Flat;
}
}
}
}
//===========================================================================
//
//
//
//===========================================================================
int FMD3Model::FindFrame(const char * name)
{
for (int i=0;i<numFrames;i++)
{
if (!stricmp(name, frames[i].Name)) return i;
}
return -1;
}
//===========================================================================
//
//
//
//===========================================================================
void FMD3Model::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frameno, int frameno2, double inter, int translation)
{
if (frameno>=numFrames || frameno2>=numFrames) return;
renderer->SetInterpolation(inter);
for(int i=0;i<numSurfaces;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.
FTexture *surfaceSkin = skin;
if (!surfaceSkin)
{
if (curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid())
{
surfaceSkin = TexMan(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i]);
}
else if(surf->numSkins > 0 && surf->skins[0].isValid())
{
surfaceSkin = TexMan(surf->skins[0]);
}
if (!surfaceSkin) return;
}
renderer->SetMaterial(surfaceSkin, false, translation);
mVBuf->SetupFrame(renderer, 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);
}
//===========================================================================
//
//
//
//===========================================================================
FMD3Model::~FMD3Model()
{
if (frames) delete [] frames;
if (surfaces) delete [] surfaces;
frames = nullptr;
surfaces = nullptr;
}

View 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},

View file

@ -0,0 +1,440 @@
//
//---------------------------------------------------------------------------
//
// 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 "w_wad.h"
#include "cmdlib.h"
#include "sc_man.h"
#include "m_crc32.h"
#include "c_console.h"
#include "g_game.h"
#include "doomstat.h"
#include "g_level.h"
#include "colormatcher.h"
#include "textures/bitmap.h"
#include "g_levellocals.h"
#include "models.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 FTexture
{
public:
FVoxelTexture(FVoxel *voxel);
~FVoxelTexture();
const uint8_t *GetColumn (unsigned int column, const Span **spans_out);
const uint8_t *GetPixels ();
void Unload ();
int CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf);
bool UseBasePalette() { return false; }
protected:
FVoxel *SourceVox;
uint8_t *Pixels;
};
//===========================================================================
//
//
//
//===========================================================================
FVoxelTexture::FVoxelTexture(FVoxel *vox)
{
SourceVox = vox;
Width = 16;
Height = 16;
WidthBits = 4;
HeightBits = 4;
WidthMask = 15;
Pixels = NULL;
gl_info.bNoFilter = true;
gl_info.bNoCompress = true;
}
//===========================================================================
//
//
//
//===========================================================================
FVoxelTexture::~FVoxelTexture()
{
}
const uint8_t *FVoxelTexture::GetColumn (unsigned int column, const Span **spans_out)
{
// not needed
return NULL;
}
const uint8_t *FVoxelTexture::GetPixels ()
{
// GetPixels gets called when a translated palette is used so we still need to implement it here.
if (Pixels == NULL)
{
Pixels = new uint8_t[256];
uint8_t *pp = SourceVox->Palette;
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);
Pixels[i] = ColorMatcher.Pick(pe);
}
}
else
{
for(int i=0;i<256;i++, pp+=3)
{
Pixels[i] = (uint8_t)i;
}
}
}
return Pixels;
}
void FVoxelTexture::Unload ()
{
if (Pixels != NULL)
{
delete[] Pixels;
Pixels = NULL;
}
}
//===========================================================================
//
// FVoxelTexture::CopyTrueColorPixels
//
// This creates a dummy 16x16 paletted bitmap and converts that using the
// voxel palette
//
//===========================================================================
int FVoxelTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotate, FCopyInfo *inf)
{
PalEntry pe[256];
uint8_t bitmap[256];
uint8_t *pp = SourceVox->Palette;
if(pp != NULL)
{
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(x, y, bitmap, Width, Height, 1, 16, rotate, pe, inf);
return 0;
}
//===========================================================================
//
//
//
//===========================================================================
FVoxelModel::FVoxelModel(FVoxel *voxel, bool owned)
{
mVoxel = voxel;
mOwningVoxel = owned;
mPalette = TexMan.AddTexture(new FVoxelTexture(voxel));
}
//===========================================================================
//
//
//
//===========================================================================
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->SlabData[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 (mVBuf == NULL)
{
Initialize();
mVBuf = renderer->CreateVertexBuffer(true, true);
FModelVertex *vertptr = mVBuf->LockVertexBuffer(mVertices.Size());
unsigned int *indxptr = mVBuf->LockIndexBuffer(mIndices.Size());
memcpy(vertptr, &mVertices[0], sizeof(FModelVertex)* mVertices.Size());
memcpy(indxptr, &mIndices[0], sizeof(unsigned int)* mIndices.Size());
mVBuf->UnlockVertexBuffer();
mVBuf->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()] |= FTexture::TEX_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()
{
return level.info->pixelstretch;
}
//===========================================================================
//
// Voxels never interpolate between frames, they only have one.
//
//===========================================================================
void FVoxelModel::RenderFrame(FModelRenderer *renderer, FTexture * skin, int frame, int frame2, double inter, int translation)
{
renderer->SetMaterial(skin, true, translation);
mVBuf->SetupFrame(renderer, 0, 0, 0);
renderer->DrawElements(mNumIndices, 0);
}