mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-11 21:31:37 +00:00
386 lines
10 KiB
C++
386 lines
10 KiB
C++
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright(C) 2006-2016 Christoph Oelckers
|
|
// All rights reserved.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with this program. If not, see http://www.gnu.org/licenses/
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
|
|
#include "filesystem.h"
|
|
#include "cmdlib.h"
|
|
#include "model_md3.h"
|
|
#include "texturemanager.h"
|
|
#include "modelrenderer.h"
|
|
|
|
#define MAX_QPATH 64
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning(disable:4244) // warning C4244: conversion from 'double' to 'float', possible loss of data
|
|
#endif
|
|
|
|
//===========================================================================
|
|
//
|
|
// decode the lat/lng normal to a 3 float normal
|
|
//
|
|
//===========================================================================
|
|
|
|
static void UnpackVector(unsigned short packed, float & nx, float & ny, float & nz)
|
|
{
|
|
double lat = ( packed >> 8 ) & 0xff;
|
|
double lng = ( packed & 0xff );
|
|
lat *= M_PI/128;
|
|
lng *= M_PI/128;
|
|
|
|
nx = cos(lat) * sin(lng);
|
|
ny = sin(lat) * sin(lng);
|
|
nz = cos(lng);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// MD3 File structure
|
|
//
|
|
//===========================================================================
|
|
|
|
#pragma pack(4)
|
|
struct md3_header_t
|
|
{
|
|
uint32_t Magic;
|
|
uint32_t Version;
|
|
char Name[MAX_QPATH];
|
|
uint32_t Flags;
|
|
uint32_t Num_Frames;
|
|
uint32_t Num_Tags;
|
|
uint32_t Num_Surfaces;
|
|
uint32_t Num_Skins;
|
|
uint32_t Ofs_Frames;
|
|
uint32_t Ofs_Tags;
|
|
uint32_t Ofs_Surfaces;
|
|
uint32_t Ofs_Eof;
|
|
};
|
|
|
|
struct md3_surface_t
|
|
{
|
|
uint32_t Magic;
|
|
char Name[MAX_QPATH];
|
|
uint32_t Flags;
|
|
uint32_t Num_Frames;
|
|
uint32_t Num_Shaders;
|
|
uint32_t Num_Verts;
|
|
uint32_t Num_Triangles;
|
|
uint32_t Ofs_Triangles;
|
|
uint32_t Ofs_Shaders;
|
|
uint32_t Ofs_Texcoord;
|
|
uint32_t Ofs_XYZNormal;
|
|
uint32_t Ofs_End;
|
|
};
|
|
|
|
struct md3_triangle_t
|
|
{
|
|
uint32_t vt_index[3];
|
|
};
|
|
|
|
struct md3_shader_t
|
|
{
|
|
char Name[MAX_QPATH];
|
|
uint32_t index;
|
|
};
|
|
|
|
struct md3_texcoord_t
|
|
{
|
|
float s, t;
|
|
};
|
|
|
|
struct md3_vertex_t
|
|
{
|
|
short x, y, z, n;
|
|
};
|
|
|
|
struct md3_frame_t
|
|
{
|
|
float min_Bounds[3];
|
|
float max_Bounds[3];
|
|
float localorigin[3];
|
|
float radius;
|
|
char Name[16];
|
|
};
|
|
#pragma pack()
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
//
|
|
//===========================================================================
|
|
|
|
bool FMD3Model::Load(const char * path, int lumpnum, const char * buffer, int length)
|
|
{
|
|
md3_header_t * hdr = (md3_header_t *)buffer;
|
|
|
|
auto numFrames = LittleLong(hdr->Num_Frames);
|
|
auto numSurfaces = LittleLong(hdr->Num_Surfaces);
|
|
|
|
numTags = LittleLong(hdr->Num_Tags);
|
|
|
|
md3_frame_t * frm = (md3_frame_t*)(buffer + LittleLong(hdr->Ofs_Frames));
|
|
|
|
Frames.Resize(numFrames);
|
|
for (unsigned i = 0; i < numFrames; i++)
|
|
{
|
|
strncpy(Frames[i].Name, frm[i].Name, 16);
|
|
for (int j = 0; j < 3; j++) Frames[i].origin[j] = frm[i].localorigin[j];
|
|
}
|
|
|
|
md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));
|
|
|
|
Surfaces.Resize(numSurfaces);
|
|
|
|
for (unsigned i = 0; i < numSurfaces; i++)
|
|
{
|
|
MD3Surface * s = &Surfaces[i];
|
|
md3_surface_t * ss = surf;
|
|
|
|
surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));
|
|
|
|
s->numSkins = LittleLong(ss->Num_Shaders);
|
|
s->numTriangles = LittleLong(ss->Num_Triangles);
|
|
s->numVertices = LittleLong(ss->Num_Verts);
|
|
|
|
// copy shaders (skins)
|
|
md3_shader_t * shader = (md3_shader_t*)(((char*)ss) + LittleLong(ss->Ofs_Shaders));
|
|
s->Skins.Resize(s->numSkins);
|
|
|
|
for (unsigned i = 0; i < s->numSkins; i++)
|
|
{
|
|
// [BB] According to the MD3 spec, Name is supposed to include the full path.
|
|
// ... and since some tools seem to output backslashes, these need to be replaced with forward slashes to work.
|
|
FixPathSeperator(shader[i].Name);
|
|
s->Skins[i] = LoadSkin("", shader[i].Name);
|
|
// [BB] Fall back and check if Name is relative.
|
|
if (!s->Skins[i].isValid())
|
|
s->Skins[i] = LoadSkin(path, shader[i].Name);
|
|
}
|
|
}
|
|
mLumpNum = lumpnum;
|
|
return true;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
//
|
|
//===========================================================================
|
|
|
|
void FMD3Model::LoadGeometry()
|
|
{
|
|
FileData lumpdata = fileSystem.ReadFile(mLumpNum);
|
|
const char *buffer = (const char *)lumpdata.GetMem();
|
|
md3_header_t * hdr = (md3_header_t *)buffer;
|
|
md3_surface_t * surf = (md3_surface_t*)(buffer + LittleLong(hdr->Ofs_Surfaces));
|
|
|
|
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
|
{
|
|
MD3Surface * s = &Surfaces[i];
|
|
md3_surface_t * ss = surf;
|
|
|
|
surf = (md3_surface_t *)(((char*)surf) + LittleLong(surf->Ofs_End));
|
|
|
|
// copy triangle indices
|
|
md3_triangle_t * tris = (md3_triangle_t*)(((char*)ss) + LittleLong(ss->Ofs_Triangles));
|
|
s->Tris.Resize(s->numTriangles);
|
|
|
|
for (unsigned i = 0; i < s->numTriangles; i++) for (int j = 0; j < 3; j++)
|
|
{
|
|
s->Tris[i].VertIndex[j] = LittleLong(tris[i].vt_index[j]);
|
|
}
|
|
|
|
// Load texture coordinates
|
|
md3_texcoord_t * tc = (md3_texcoord_t*)(((char*)ss) + LittleLong(ss->Ofs_Texcoord));
|
|
s->Texcoords.Resize(s->numVertices);
|
|
|
|
for (unsigned i = 0; i < s->numVertices; i++)
|
|
{
|
|
s->Texcoords[i].s = tc[i].s;
|
|
s->Texcoords[i].t = tc[i].t;
|
|
}
|
|
|
|
// Load vertices and texture coordinates
|
|
md3_vertex_t * vt = (md3_vertex_t*)(((char*)ss) + LittleLong(ss->Ofs_XYZNormal));
|
|
s->Vertices.Resize(s->numVertices * Frames.Size());
|
|
|
|
for (unsigned i = 0; i < s->numVertices * Frames.Size(); i++)
|
|
{
|
|
s->Vertices[i].x = LittleShort(vt[i].x) / 64.f;
|
|
s->Vertices[i].y = LittleShort(vt[i].y) / 64.f;
|
|
s->Vertices[i].z = LittleShort(vt[i].z) / 64.f;
|
|
UnpackVector(LittleShort(vt[i].n), s->Vertices[i].nx, s->Vertices[i].ny, s->Vertices[i].nz);
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
//
|
|
//===========================================================================
|
|
|
|
void FMD3Model::BuildVertexBuffer(FModelRenderer *renderer)
|
|
{
|
|
if (!GetVertexBuffer(renderer->GetType()))
|
|
{
|
|
LoadGeometry();
|
|
|
|
unsigned int vbufsize = 0;
|
|
unsigned int ibufsize = 0;
|
|
|
|
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
|
{
|
|
MD3Surface * surf = &Surfaces[i];
|
|
vbufsize += Frames.Size() * surf->numVertices;
|
|
ibufsize += 3 * surf->numTriangles;
|
|
}
|
|
|
|
auto vbuf = renderer->CreateVertexBuffer(true, Frames.Size() == 1);
|
|
SetVertexBuffer(renderer->GetType(), vbuf);
|
|
|
|
FModelVertex *vertptr = vbuf->LockVertexBuffer(vbufsize);
|
|
unsigned int *indxptr = vbuf->LockIndexBuffer(ibufsize);
|
|
|
|
assert(vertptr != nullptr && indxptr != nullptr);
|
|
|
|
unsigned int vindex = 0, iindex = 0;
|
|
|
|
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
|
{
|
|
MD3Surface * surf = &Surfaces[i];
|
|
|
|
surf->vindex = vindex;
|
|
surf->iindex = iindex;
|
|
for (unsigned j = 0; j < Frames.Size() * surf->numVertices; j++)
|
|
{
|
|
MD3Vertex* vert = &surf->Vertices[j];
|
|
|
|
FModelVertex *bvert = &vertptr[vindex++];
|
|
|
|
int tc = j % surf->numVertices;
|
|
bvert->Set(vert->x, vert->z, vert->y, surf->Texcoords[tc].s, surf->Texcoords[tc].t);
|
|
bvert->SetNormal(vert->nx, vert->nz, vert->ny);
|
|
}
|
|
|
|
for (unsigned k = 0; k < surf->numTriangles; k++)
|
|
{
|
|
for (int l = 0; l < 3; l++)
|
|
{
|
|
indxptr[iindex++] = surf->Tris[k].VertIndex[l];
|
|
}
|
|
}
|
|
surf->UnloadGeometry();
|
|
}
|
|
vbuf->UnlockVertexBuffer();
|
|
vbuf->UnlockIndexBuffer();
|
|
}
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// for skin precaching
|
|
//
|
|
//===========================================================================
|
|
|
|
void FMD3Model::AddSkins(uint8_t *hitlist)
|
|
{
|
|
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
|
{
|
|
int ssIndex = i + curMDLIndex * MD3_MAX_SURFACES;
|
|
if (curSpriteMDLFrame && curSpriteMDLFrame->surfaceskinIDs[ssIndex].isValid())
|
|
{
|
|
hitlist[curSpriteMDLFrame->surfaceskinIDs[ssIndex].GetIndex()] |= FTextureManager::HIT_Flat;
|
|
}
|
|
|
|
MD3Surface * surf = &Surfaces[i];
|
|
for (unsigned j = 0; j < surf->numSkins; j++)
|
|
{
|
|
if (surf->Skins[j].isValid())
|
|
{
|
|
hitlist[surf->Skins[j].GetIndex()] |= FTextureManager::HIT_Flat;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
//
|
|
//===========================================================================
|
|
|
|
int FMD3Model::FindFrame(const char * name)
|
|
{
|
|
for (unsigned i = 0; i < Frames.Size(); i++)
|
|
{
|
|
if (!stricmp(name, Frames[i].Name)) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
//
|
|
//===========================================================================
|
|
|
|
void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation)
|
|
{
|
|
if ((unsigned)frameno >= Frames.Size() || (unsigned)frameno2 >= Frames.Size()) return;
|
|
|
|
renderer->SetInterpolation(inter);
|
|
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
|
{
|
|
MD3Surface * surf = &Surfaces[i];
|
|
|
|
// [BB] In case no skin is specified via MODELDEF, check if the MD3 has a skin for the current surface.
|
|
// Note: Each surface may have a different skin.
|
|
FGameTexture *surfaceSkin = skin;
|
|
if (!surfaceSkin)
|
|
{
|
|
if (curSpriteMDLFrame)
|
|
{
|
|
int ssIndex = i + curMDLIndex * MD3_MAX_SURFACES;
|
|
if (curSpriteMDLFrame->surfaceskinIDs[ssIndex].isValid())
|
|
{
|
|
surfaceSkin = TexMan.GetGameTexture(curSpriteMDLFrame->surfaceskinIDs[ssIndex], true);
|
|
}
|
|
else if (surf->numSkins > 0 && surf->Skins[0].isValid())
|
|
{
|
|
surfaceSkin = TexMan.GetGameTexture(surf->Skins[0], true);
|
|
}
|
|
}
|
|
|
|
if (!surfaceSkin)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
renderer->SetMaterial(surfaceSkin, false, translation);
|
|
renderer->SetupFrame(this, surf->vindex + frameno * surf->numVertices, surf->vindex + frameno2 * surf->numVertices, surf->numVertices);
|
|
renderer->DrawElements(surf->numTriangles * 3, surf->iindex * sizeof(unsigned int));
|
|
}
|
|
renderer->SetInterpolation(0.f);
|
|
}
|
|
|