// //--------------------------------------------------------------------------- // // Copyright(C) 2005-2016 Christoph Oelckers // All rights reserved. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see http://www.gnu.org/licenses/ // //-------------------------------------------------------------------------- // /* ** models.cpp ** ** MD2/DMD model format code ** **/ #include "filesystem.h" #include "model_md2.h" #include "texturemanager.h" #include "modelrenderer.h" #include "printf.h" #ifdef _MSC_VER #pragma warning(disable:4244) // warning C4244: conversion from 'double' to 'float', possible loss of data #endif enum { VX, VZ, VY }; #define NUMVERTEXNORMALS 162 static float avertexnormals[NUMVERTEXNORMALS][3] = { #include "tab_anorms.h" }; //=========================================================================== // // UnpackVector // Packed: pppppppy yyyyyyyy. Yaw is on the XY plane. // //=========================================================================== static void UnpackVector(unsigned short packed, float vec[3]) { float yaw = (packed & 511) / 512.0f * 2 * M_PI; float pitch = ((packed >> 9) / 127.0f - 0.5f) * M_PI; float cosp = (float) cos(pitch); vec[VX] = (float) cos(yaw) * cosp; vec[VY] = (float) sin(yaw) * cosp; vec[VZ] = (float) sin(pitch); } //=========================================================================== // // DMD file structure // //=========================================================================== struct dmd_chunk_t { int type; int length; // Next chunk follows... }; #pragma pack(1) struct dmd_packedVertex_t { uint8_t vertex[3]; unsigned short normal; // Yaw and pitch. }; struct dmd_packedFrame_t { float scale[3]; float translate[3]; char name[16]; dmd_packedVertex_t vertices[1]; }; #pragma pack() // Chunk types. enum { DMC_END, // Must be the last chunk. DMC_INFO // Required; will be expected to exist. }; //=========================================================================== // // FDMDModel::Load // //=========================================================================== bool FDMDModel::Load(const char * path, int lumpnum, const char * buffer, int length) { dmd_chunk_t * chunk = (dmd_chunk_t*)(buffer + 12); char *temp; ModelFrame *frame; int i; int fileoffset = 12 + sizeof(dmd_chunk_t); chunk->type = LittleLong(chunk->type); while (chunk->type != DMC_END) { switch (chunk->type) { case DMC_INFO: // Standard DMD information chunk. memcpy(&info, buffer + fileoffset, LittleLong(chunk->length)); info.skinWidth = LittleLong(info.skinWidth); info.skinHeight = LittleLong(info.skinHeight); info.frameSize = LittleLong(info.frameSize); info.numSkins = LittleLong(info.numSkins); info.numVertices = LittleLong(info.numVertices); info.numTexCoords = LittleLong(info.numTexCoords); info.numFrames = LittleLong(info.numFrames); info.numLODs = LittleLong(info.numLODs); info.offsetSkins = LittleLong(info.offsetSkins); info.offsetTexCoords = LittleLong(info.offsetTexCoords); info.offsetFrames = LittleLong(info.offsetFrames); info.offsetLODs = LittleLong(info.offsetLODs); info.offsetEnd = LittleLong(info.offsetEnd); fileoffset += chunk->length; break; default: // Just skip all unknown chunks. fileoffset += chunk->length; break; } // Read the next chunk header. chunk = (dmd_chunk_t*)(buffer + fileoffset); chunk->type = LittleLong(chunk->type); fileoffset += sizeof(dmd_chunk_t); } // Allocate and load in the data. skins = new FTextureID[info.numSkins]; for (i = 0; i < info.numSkins; i++) { skins[i] = LoadSkin(path, buffer + info.offsetSkins + i * 64); } temp = (char*)buffer + info.offsetFrames; frames = new ModelFrame[info.numFrames]; for (i = 0, frame = frames; i < info.numFrames; i++, frame++) { dmd_packedFrame_t *pfr = (dmd_packedFrame_t *)(temp + info.frameSize * i); memcpy(frame->name, pfr->name, sizeof(pfr->name)); frame->vindex = UINT_MAX; } mLumpNum = lumpnum; return true; } //=========================================================================== // // FDMDModel::LoadGeometry // //=========================================================================== void FDMDModel::LoadGeometry() { static int axis[3] = { VX, VY, VZ }; FileData lumpdata = fileSystem.ReadFile(mLumpNum); const char *buffer = (const char *)lumpdata.GetMem(); texCoords = new FTexCoord[info.numTexCoords]; memcpy(texCoords, buffer + info.offsetTexCoords, info.numTexCoords * sizeof(FTexCoord)); const char *temp = buffer + info.offsetFrames; framevtx= new ModelFrameVertexData[info.numFrames]; ModelFrameVertexData *framev; int i, k, c; for(i = 0, framev = framevtx; i < info.numFrames; i++, framev++) { dmd_packedFrame_t *pfr = (dmd_packedFrame_t *) (temp + info.frameSize * i); dmd_packedVertex_t *pVtx; framev->vertices = new DMDModelVertex[info.numVertices]; framev->normals = new DMDModelVertex[info.numVertices]; // Translate each vertex. for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++) { UnpackVector((unsigned short)(pVtx->normal), framev->normals[k].xyz); for(c = 0; c < 3; c++) { framev->vertices[k].xyz[axis[c]] = (pVtx->vertex[c] * float(pfr->scale[c]) + float(pfr->translate[c])); } } } memcpy(lodInfo, buffer+info.offsetLODs, info.numLODs * sizeof(DMDLoDInfo)); for(i = 0; i < info.numLODs; i++) { lodInfo[i].numTriangles = LittleLong(lodInfo[i].numTriangles); lodInfo[i].offsetTriangles = LittleLong(lodInfo[i].offsetTriangles); if (lodInfo[i].numTriangles > 0) { lods[i].triangles = new FTriangle[lodInfo[i].numTriangles]; memcpy(lods[i].triangles, buffer + lodInfo[i].offsetTriangles, lodInfo[i].numTriangles * sizeof(FTriangle)); for (int j = 0; j < lodInfo[i].numTriangles; j++) { for (int k = 0; k < 3; k++) { lods[i].triangles[j].textureIndices[k] = LittleShort(lods[i].triangles[j].textureIndices[k]); lods[i].triangles[j].vertexIndices[k] = LittleShort(lods[i].triangles[j].vertexIndices[k]); } } } } } //=========================================================================== // // Deletes everything that's no longer needed after building the vertex buffer // //=========================================================================== void FDMDModel::UnloadGeometry() { int i; if (framevtx != NULL) { for (i=0;iGetType())) { LoadGeometry(); int VertexBufferSize = info.numFrames * lodInfo[0].numTriangles * 3; unsigned int vindex = 0; auto vbuf = renderer->CreateVertexBuffer(false, info.numFrames == 1); SetVertexBuffer(renderer->GetType(), vbuf); FModelVertex *vertptr = vbuf->LockVertexBuffer(VertexBufferSize); for (int i = 0; i < info.numFrames; i++) { DMDModelVertex *vert = framevtx[i].vertices; DMDModelVertex *norm = framevtx[i].normals; frames[i].vindex = vindex; FTriangle *tri = lods[0].triangles; for (int i = 0; i < lodInfo[0].numTriangles; i++) { for (int j = 0; j < 3; j++) { int ti = tri->textureIndices[j]; int vi = tri->vertexIndices[j]; FModelVertex *bvert = &vertptr[vindex++]; bvert->Set(vert[vi].xyz[0], vert[vi].xyz[1], vert[vi].xyz[2], (float)texCoords[ti].s / info.skinWidth, (float)texCoords[ti].t / info.skinHeight); bvert->SetNormal(norm[vi].xyz[0], norm[vi].xyz[1], norm[vi].xyz[2]); } tri++; } } vbuf->UnlockVertexBuffer(); UnloadGeometry(); } } //=========================================================================== // // for skin precaching // //=========================================================================== void FDMDModel::AddSkins(uint8_t *hitlist) { for (int i = 0; i < info.numSkins; i++) { if (skins[i].isValid()) { hitlist[skins[i].GetIndex()] |= FTextureManager::HIT_Flat; } } } //=========================================================================== // // FDMDModel::FindFrame // //=========================================================================== int FDMDModel::FindFrame(const char * name) { for (int i=0;i= info.numFrames || frameno2 >= info.numFrames) return; if (!skin) { if (info.numSkins == 0 || !skins[0].isValid()) return; skin = TexMan.GetGameTexture(skins[0], true); if (!skin) return; } renderer->SetInterpolation(inter); renderer->SetMaterial(skin, false, translation); renderer->SetupFrame(this, frames[frameno].vindex, frames[frameno2].vindex, lodInfo[0].numTriangles * 3); renderer->DrawArrays(0, lodInfo[0].numTriangles * 3); renderer->SetInterpolation(0.f); } //=========================================================================== // // Internal data structures of MD2 files - only used during loading // //=========================================================================== struct md2_header_t { int magic; int version; int skinWidth; int skinHeight; int frameSize; int numSkins; int numVertices; int numTexCoords; int numTriangles; int numGlCommands; int numFrames; int offsetSkins; int offsetTexCoords; int offsetTriangles; int offsetFrames; int offsetGlCommands; int offsetEnd; }; struct md2_triangleVertex_t { uint8_t vertex[3]; uint8_t lightNormalIndex; }; struct md2_packedFrame_t { float scale[3]; float translate[3]; char name[16]; md2_triangleVertex_t vertices[1]; }; //=========================================================================== // // FMD2Model::Load // //=========================================================================== bool FMD2Model::Load(const char * path, int lumpnum, const char * buffer, int length) { md2_header_t * md2header = (md2_header_t *)buffer; ModelFrame *frame; uint8_t *md2_frames; int i; // Convert it to DMD. header.magic = MD2_MAGIC; header.version = 8; header.flags = 0; info.skinWidth = LittleLong(md2header->skinWidth); info.skinHeight = LittleLong(md2header->skinHeight); info.frameSize = LittleLong(md2header->frameSize); info.numLODs = 1; info.numSkins = LittleLong(md2header->numSkins); info.numTexCoords = LittleLong(md2header->numTexCoords); info.numVertices = LittleLong(md2header->numVertices); info.numFrames = LittleLong(md2header->numFrames); info.offsetSkins = LittleLong(md2header->offsetSkins); info.offsetTexCoords = LittleLong(md2header->offsetTexCoords); info.offsetFrames = LittleLong(md2header->offsetFrames); info.offsetLODs = LittleLong(md2header->offsetEnd); // Doesn't exist. lodInfo[0].numTriangles = LittleLong(md2header->numTriangles); lodInfo[0].numGlCommands = LittleLong(md2header->numGlCommands); lodInfo[0].offsetTriangles = LittleLong(md2header->offsetTriangles); lodInfo[0].offsetGlCommands = LittleLong(md2header->offsetGlCommands); info.offsetEnd = LittleLong(md2header->offsetEnd); if (info.offsetFrames + info.frameSize * info.numFrames > length) { Printf("LoadModel: Model '%s' file too short\n", path); return false; } if (lodInfo[0].numGlCommands <= 0) { Printf("LoadModel: Model '%s' invalid NumGLCommands\n", path); return false; } skins = new FTextureID[info.numSkins]; for (i = 0; i < info.numSkins; i++) { skins[i] = LoadSkin(path, buffer + info.offsetSkins + i * 64); } // The frames need to be unpacked. md2_frames = (uint8_t*)buffer + info.offsetFrames; frames = new ModelFrame[info.numFrames]; for (i = 0, frame = frames; i < info.numFrames; i++, frame++) { md2_packedFrame_t *pfr = (md2_packedFrame_t *)(md2_frames + info.frameSize * i); memcpy(frame->name, pfr->name, sizeof(pfr->name)); frame->vindex = UINT_MAX; } mLumpNum = lumpnum; return true; } //=========================================================================== // // FMD2Model::LoadGeometry // //=========================================================================== void FMD2Model::LoadGeometry() { static int axis[3] = { VX, VY, VZ }; uint8_t *md2_frames; FileData lumpdata = fileSystem.ReadFile(mLumpNum); const char *buffer = (const char *)lumpdata.GetMem(); texCoords = new FTexCoord[info.numTexCoords]; memcpy(texCoords, (uint8_t*)buffer + info.offsetTexCoords, info.numTexCoords * sizeof(FTexCoord)); md2_frames = (uint8_t*)buffer + info.offsetFrames; framevtx = new ModelFrameVertexData[info.numFrames]; ModelFrameVertexData *framev; int i, k, c; for(i = 0, framev = framevtx; i < info.numFrames; i++, framev++) { md2_packedFrame_t *pfr = (md2_packedFrame_t *) (md2_frames + info.frameSize * i); md2_triangleVertex_t *pVtx; framev->vertices = new DMDModelVertex[info.numVertices]; framev->normals = new DMDModelVertex[info.numVertices]; // Translate each vertex. for(k = 0, pVtx = pfr->vertices; k < info.numVertices; k++, pVtx++) { memcpy(framev->normals[k].xyz, avertexnormals[pVtx->lightNormalIndex], sizeof(float) * 3); for(c = 0; c < 3; c++) { framev->vertices[k].xyz[axis[c]] = (pVtx->vertex[c] * pfr->scale[c] + pfr->translate[c]); } } } lods[0].triangles = new FTriangle[lodInfo[0].numTriangles]; int cnt = lodInfo[0].numTriangles; memcpy(lods[0].triangles, buffer + lodInfo[0].offsetTriangles, sizeof(FTriangle) * cnt); for (int j = 0; j < cnt; j++) { for (int k = 0; k < 3; k++) { lods[0].triangles[j].textureIndices[k] = LittleShort(lods[0].triangles[j].textureIndices[k]); lods[0].triangles[j].vertexIndices[k] = LittleShort(lods[0].triangles[j].vertexIndices[k]); } } } FMD2Model::~FMD2Model() { }