Clean up mdl parser
Now all the structs live at the top of the file, and the main vertex element reading loop is much nicer looking.
This commit is contained in:
parent
c78a1ab245
commit
e53e1a0e6c
1 changed files with 189 additions and 180 deletions
|
@ -8,7 +8,6 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
Model parseMDL(MemorySpan data) {
|
|
||||||
struct ModelFileHeader {
|
struct ModelFileHeader {
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
uint32_t stackSize;
|
uint32_t stackSize;
|
||||||
|
@ -23,45 +22,36 @@ Model parseMDL(MemorySpan data) {
|
||||||
bool indexBufferStreamingEnabled;
|
bool indexBufferStreamingEnabled;
|
||||||
bool hasEdgeGeometry;
|
bool hasEdgeGeometry;
|
||||||
uint8_t padding;
|
uint8_t padding;
|
||||||
} modelFileHeader;
|
};
|
||||||
|
|
||||||
data.read(&modelFileHeader);
|
enum VertexType : uint8_t {
|
||||||
|
Single3 = 2,
|
||||||
|
Single4 = 3,
|
||||||
|
UInt = 5,
|
||||||
|
ByteFloat4 = 8,
|
||||||
|
Half2 = 13,
|
||||||
|
Half4 = 14
|
||||||
|
};
|
||||||
|
|
||||||
|
enum VertexUsage : uint8_t {
|
||||||
|
Position = 0,
|
||||||
|
BlendWeights = 1,
|
||||||
|
BlendIndices = 2,
|
||||||
|
Normal = 3,
|
||||||
|
UV = 4,
|
||||||
|
Tangent2 = 5,
|
||||||
|
Tangent1 = 6,
|
||||||
|
Color = 7,
|
||||||
|
};
|
||||||
|
|
||||||
struct VertexElement {
|
struct VertexElement {
|
||||||
uint8_t stream, offset, type, usage, usageIndex;
|
uint8_t stream, offset;
|
||||||
|
VertexType type;
|
||||||
|
VertexUsage usage;
|
||||||
|
uint8_t usageIndex;
|
||||||
uint8_t padding[3];
|
uint8_t padding[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexDeclaration {
|
|
||||||
std::vector<VertexElement> elements;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<VertexDeclaration> vertexDecls(modelFileHeader.vertexDeclarationCount);
|
|
||||||
for(int i = 0; i < modelFileHeader.vertexDeclarationCount; i++) {
|
|
||||||
VertexElement element {};
|
|
||||||
data.read(&element);
|
|
||||||
|
|
||||||
do {
|
|
||||||
vertexDecls[i].elements.push_back(element);
|
|
||||||
data.read(&element);
|
|
||||||
} while (element.stream != 255);
|
|
||||||
|
|
||||||
int toSeek = 17 * 8 - (vertexDecls[i].elements.size() + 1) * 8;
|
|
||||||
data.seek(toSeek, Seek::Current);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t stringCount;
|
|
||||||
data.read(&stringCount);
|
|
||||||
|
|
||||||
// dummy
|
|
||||||
data.seek(sizeof(uint16_t), Seek::Current);
|
|
||||||
|
|
||||||
uint32_t stringSize;
|
|
||||||
data.read(&stringSize);
|
|
||||||
|
|
||||||
std::vector<uint8_t> strings;
|
|
||||||
data.read_structures(&strings, stringSize);
|
|
||||||
|
|
||||||
enum ModelFlags1 : uint8_t
|
enum ModelFlags1 : uint8_t
|
||||||
{
|
{
|
||||||
DustOcclusionEnabled = 0x80,
|
DustOcclusionEnabled = 0x80,
|
||||||
|
@ -118,28 +108,9 @@ Model parseMDL(MemorySpan data) {
|
||||||
uint8_t unknown6;
|
uint8_t unknown6;
|
||||||
unsigned short unknown7, unknown8, unknown9;
|
unsigned short unknown7, unknown8, unknown9;
|
||||||
uint8_t padding[6];
|
uint8_t padding[6];
|
||||||
} modelHeader;
|
|
||||||
|
|
||||||
data.read(&modelHeader);
|
|
||||||
|
|
||||||
struct ElementId {
|
|
||||||
uint32_t elementId;
|
|
||||||
uint32_t parentBoneName;
|
|
||||||
std::vector<float> translate;
|
|
||||||
std::vector<float> rotate;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ElementId> elementIds(modelHeader.elementIdCount);
|
struct MeshLod {
|
||||||
for(int i = 0; i < modelHeader.elementIdCount; i++) {
|
|
||||||
data.read(&elementIds[i].elementId);
|
|
||||||
data.read(&elementIds[i].parentBoneName);
|
|
||||||
|
|
||||||
// FIXME: these always seem to be 3, convert to static array? then we could probably read this all in one go!
|
|
||||||
data.read_structures(&elementIds[i].translate, 3);
|
|
||||||
data.read_structures(&elementIds[i].rotate, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Lod {
|
|
||||||
unsigned short meshIndex;
|
unsigned short meshIndex;
|
||||||
unsigned short meshCount;
|
unsigned short meshCount;
|
||||||
float modelLodRange;
|
float modelLodRange;
|
||||||
|
@ -164,10 +135,12 @@ Model parseMDL(MemorySpan data) {
|
||||||
unsigned int indexDataOffset;
|
unsigned int indexDataOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Lod> lods;
|
struct ElementId {
|
||||||
|
uint32_t elementId;
|
||||||
// TODO: support models that support more than 3 lods
|
uint32_t parentBoneName;
|
||||||
data.read_structures(&lods, 3);
|
std::vector<float> translate;
|
||||||
|
std::vector<float> rotate;
|
||||||
|
};
|
||||||
|
|
||||||
struct Mesh {
|
struct Mesh {
|
||||||
unsigned short vertexCount;
|
unsigned short vertexCount;
|
||||||
|
@ -185,6 +158,74 @@ Model parseMDL(MemorySpan data) {
|
||||||
uint8_t vertexStreamCount;
|
uint8_t vertexStreamCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Submesh {
|
||||||
|
unsigned int indexOffset;
|
||||||
|
unsigned int indexCount;
|
||||||
|
unsigned int attributeIndexMask;
|
||||||
|
unsigned short boneStartIndex;
|
||||||
|
unsigned short boneCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoneTable {
|
||||||
|
std::vector<uint16_t> boneIndex;
|
||||||
|
uint8_t boneCount;
|
||||||
|
std::vector<uint8_t> padding;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoundingBox {
|
||||||
|
std::array<float, 4> min, max;
|
||||||
|
};
|
||||||
|
|
||||||
|
Model parseMDL(MemorySpan data) {
|
||||||
|
ModelFileHeader modelFileHeader;
|
||||||
|
data.read(&modelFileHeader);
|
||||||
|
|
||||||
|
struct VertexDeclaration {
|
||||||
|
std::vector<VertexElement> elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<VertexDeclaration> vertexDecls(modelFileHeader.vertexDeclarationCount);
|
||||||
|
for(int i = 0; i < modelFileHeader.vertexDeclarationCount; i++) {
|
||||||
|
VertexElement element {};
|
||||||
|
data.read(&element);
|
||||||
|
|
||||||
|
do {
|
||||||
|
vertexDecls[i].elements.push_back(element);
|
||||||
|
data.read(&element);
|
||||||
|
} while (element.stream != 255);
|
||||||
|
|
||||||
|
int toSeek = 17 * 8 - (vertexDecls[i].elements.size() + 1) * 8;
|
||||||
|
data.seek(toSeek, Seek::Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t stringCount;
|
||||||
|
data.read(&stringCount);
|
||||||
|
|
||||||
|
// dummy
|
||||||
|
data.seek(sizeof(uint16_t), Seek::Current);
|
||||||
|
|
||||||
|
uint32_t stringSize;
|
||||||
|
data.read(&stringSize);
|
||||||
|
|
||||||
|
std::vector<uint8_t> strings;
|
||||||
|
data.read_structures(&strings, stringSize);
|
||||||
|
|
||||||
|
ModelHeader modelHeader;
|
||||||
|
data.read(&modelHeader);
|
||||||
|
|
||||||
|
std::vector<ElementId> elementIds(modelHeader.elementIdCount);
|
||||||
|
for(int i = 0; i < modelHeader.elementIdCount; i++) {
|
||||||
|
data.read(&elementIds[i].elementId);
|
||||||
|
data.read(&elementIds[i].parentBoneName);
|
||||||
|
|
||||||
|
// FIXME: these always seem to be 3, convert to static array? then we could probably read this all in one go!
|
||||||
|
data.read_structures(&elementIds[i].translate, 3);
|
||||||
|
data.read_structures(&elementIds[i].rotate, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<MeshLod> lods;
|
||||||
|
data.read_structures(&lods, modelHeader.lodCount);
|
||||||
|
|
||||||
std::vector<Mesh> meshes(modelHeader.meshCount);
|
std::vector<Mesh> meshes(modelHeader.meshCount);
|
||||||
for(int i = 0; i < modelHeader.meshCount; i++) {
|
for(int i = 0; i < modelHeader.meshCount; i++) {
|
||||||
data.read(&meshes[i].vertexCount);
|
data.read(&meshes[i].vertexCount);
|
||||||
|
@ -207,14 +248,6 @@ Model parseMDL(MemorySpan data) {
|
||||||
|
|
||||||
// TODO: implement terrain shadow meshes
|
// TODO: implement terrain shadow meshes
|
||||||
|
|
||||||
struct Submesh {
|
|
||||||
unsigned int indexOffset;
|
|
||||||
unsigned int indexCount;
|
|
||||||
unsigned int attributeIndexMask;
|
|
||||||
unsigned short boneStartIndex;
|
|
||||||
unsigned short boneCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Submesh> submeshes;
|
std::vector<Submesh> submeshes;
|
||||||
data.read_structures(&submeshes, modelHeader.submeshCount);
|
data.read_structures(&submeshes, modelHeader.submeshCount);
|
||||||
|
|
||||||
|
@ -226,12 +259,6 @@ Model parseMDL(MemorySpan data) {
|
||||||
std::vector<uint32_t> boneNameOffsets;
|
std::vector<uint32_t> boneNameOffsets;
|
||||||
data.read_structures(&boneNameOffsets, modelHeader.boneCount);
|
data.read_structures(&boneNameOffsets, modelHeader.boneCount);
|
||||||
|
|
||||||
struct BoneTable {
|
|
||||||
std::vector<uint16_t> boneIndex;
|
|
||||||
uint8_t boneCount;
|
|
||||||
std::vector<uint8_t> padding;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<BoneTable> boneTables(modelHeader.boneTableCount);
|
std::vector<BoneTable> boneTables(modelHeader.boneTableCount);
|
||||||
for(int i = 0; i < modelHeader.boneTableCount; i++) {
|
for(int i = 0; i < modelHeader.boneTableCount; i++) {
|
||||||
data.read_structures(&boneTables[i].boneIndex, 64);
|
data.read_structures(&boneTables[i].boneIndex, 64);
|
||||||
|
@ -254,10 +281,6 @@ Model parseMDL(MemorySpan data) {
|
||||||
|
|
||||||
data.seek(paddingAmount, Seek::Current);
|
data.seek(paddingAmount, Seek::Current);
|
||||||
|
|
||||||
struct BoundingBox {
|
|
||||||
std::array<float, 4> min, max;
|
|
||||||
};
|
|
||||||
|
|
||||||
BoundingBox boundingBoxes, modelBoundingBoxes, waterBoundingBoxes, verticalFogBoundingBoxes;
|
BoundingBox boundingBoxes, modelBoundingBoxes, waterBoundingBoxes, verticalFogBoundingBoxes;
|
||||||
data.read(&boundingBoxes);
|
data.read(&boundingBoxes);
|
||||||
data.read(&modelBoundingBoxes);
|
data.read(&modelBoundingBoxes);
|
||||||
|
@ -270,48 +293,22 @@ Model parseMDL(MemorySpan data) {
|
||||||
Model model;
|
Model model;
|
||||||
|
|
||||||
for(int i = 0; i < modelHeader.lodCount; i++) {
|
for(int i = 0; i < modelHeader.lodCount; i++) {
|
||||||
::Lod lod;
|
Lod lod;
|
||||||
|
|
||||||
for(int j = lods[i].meshIndex; j < (lods[i].meshIndex + lods[i].meshCount); j++) {
|
for(int j = lods[i].meshIndex; j < (lods[i].meshIndex + lods[i].meshCount); j++) {
|
||||||
Part part;
|
Part part;
|
||||||
|
|
||||||
const VertexDeclaration decl = vertexDecls[j];
|
const VertexDeclaration decl = vertexDecls[j];
|
||||||
|
|
||||||
enum VertexType : uint8_t {
|
|
||||||
Single3 = 2,
|
|
||||||
Single4 = 3,
|
|
||||||
UInt = 5,
|
|
||||||
ByteFloat4 = 8,
|
|
||||||
Half2 = 13,
|
|
||||||
Half4 = 14
|
|
||||||
};
|
|
||||||
|
|
||||||
enum VertexUsage : uint8_t {
|
|
||||||
Position = 0,
|
|
||||||
BlendWeights = 1,
|
|
||||||
BlendIndices = 2,
|
|
||||||
Normal = 3,
|
|
||||||
UV = 4,
|
|
||||||
Tangent2 = 5,
|
|
||||||
Tangent1 = 6,
|
|
||||||
Color = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
int vertexCount = meshes[j].vertexCount;
|
int vertexCount = meshes[j].vertexCount;
|
||||||
std::vector<Vertex> vertices(vertexCount);
|
std::vector<Vertex> vertices(vertexCount);
|
||||||
|
|
||||||
for(int k = 0; k < vertexCount; k++) {
|
for(int k = 0; k < vertexCount; k++) {
|
||||||
for(auto& orderedElement : decl.elements) {
|
for(auto& element : decl.elements) {
|
||||||
auto type = static_cast<VertexType>(orderedElement.type);
|
data.seek(lods[i].vertexDataOffset + meshes[j].vertexBufferOffset[element.stream] + element.offset + meshes[i].vertexBufferStride[element.stream] * k, Seek::Set);
|
||||||
auto usage = static_cast<VertexUsage>(orderedElement.usage);
|
|
||||||
|
|
||||||
const int stream = orderedElement.stream;
|
|
||||||
|
|
||||||
data.seek(lods[i].vertexDataOffset + meshes[j].vertexBufferOffset[stream] + orderedElement.offset + meshes[i].vertexBufferStride[stream] * k, Seek::Set);
|
|
||||||
|
|
||||||
std::array<float, 4> floatData = {};
|
std::array<float, 4> floatData = {};
|
||||||
|
switch(element.type) {
|
||||||
switch(type) {
|
|
||||||
case VertexType::Single3:
|
case VertexType::Single3:
|
||||||
data.read_array(floatData.data(), 3);
|
data.read_array(floatData.data(), 3);
|
||||||
break;
|
break;
|
||||||
|
@ -351,13 +348,25 @@ Model parseMDL(MemorySpan data) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(usage) {
|
switch(element.usage) {
|
||||||
case VertexUsage::Position:
|
case VertexUsage::Position:
|
||||||
memcpy(vertices[k].position.data(), floatData.data(), sizeof(float) * 3);
|
memcpy(vertices[k].position.data(), floatData.data(), sizeof(float) * 3);
|
||||||
break;
|
break;
|
||||||
case VertexUsage::Normal:
|
case VertexUsage::Normal:
|
||||||
memcpy(vertices[k].normal.data(), floatData.data(), sizeof(float) * 3);
|
memcpy(vertices[k].normal.data(), floatData.data(), sizeof(float) * 3);
|
||||||
break;
|
break;
|
||||||
|
case BlendWeights:
|
||||||
|
break;
|
||||||
|
case BlendIndices:
|
||||||
|
break;
|
||||||
|
case UV:
|
||||||
|
break;
|
||||||
|
case Tangent2:
|
||||||
|
break;
|
||||||
|
case Tangent1:
|
||||||
|
break;
|
||||||
|
case Color:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue