2013-12-20 14:03:32 +00:00
|
|
|
#include <loaders/LoaderDFF.hpp>
|
2016-04-07 01:13:46 +01:00
|
|
|
#include <data/Model.hpp>
|
2013-07-02 07:06:03 +01:00
|
|
|
|
|
|
|
#include <iostream>
|
2013-07-26 06:40:21 +01:00
|
|
|
#include <algorithm>
|
2014-08-15 23:58:03 +02:00
|
|
|
#include <numeric>
|
2013-09-11 18:23:31 +00:00
|
|
|
#include <set>
|
2013-09-25 09:05:18 +01:00
|
|
|
#include <cstring>
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
enum DFFChunks
|
2013-07-02 07:06:03 +01:00
|
|
|
{
|
2014-08-04 22:21:01 +01:00
|
|
|
CHUNK_STRUCT = 0x0001,
|
|
|
|
CHUNK_EXTENSION = 0x0003,
|
|
|
|
CHUNK_TEXTURE = 0x0006,
|
|
|
|
CHUNK_MATERIAL = 0x0007,
|
|
|
|
CHUNK_MATERIALLIST = 0x0008,
|
|
|
|
CHUNK_FRAMELIST = 0x000E,
|
|
|
|
CHUNK_GEOMETRY = 0x000F,
|
|
|
|
CHUNK_CLUMP = 0x0010,
|
|
|
|
|
|
|
|
CHUNK_ATOMIC = 0x0014,
|
|
|
|
|
|
|
|
CHUNK_GEOMETRYLIST = 0x001A,
|
|
|
|
|
|
|
|
CHUNK_BINMESHPLG = 0x050E,
|
|
|
|
|
|
|
|
CHUNK_NODENAME = 0x0253F2FE,
|
|
|
|
};
|
|
|
|
|
|
|
|
// These structs are used to interpret raw bytes from the stream.
|
|
|
|
/// @todo worry about endianness.
|
|
|
|
|
|
|
|
typedef glm::vec3 BSTVector3;
|
|
|
|
typedef glm::mat3 BSTMatrix;
|
|
|
|
typedef glm::i8vec4 BSTColour;
|
|
|
|
|
|
|
|
struct RWBSFrame
|
|
|
|
{
|
|
|
|
BSTMatrix rotation;
|
|
|
|
BSTVector3 position;
|
|
|
|
int32_t index;
|
|
|
|
uint32_t matrixflags; // Not used
|
|
|
|
};
|
|
|
|
|
|
|
|
void LoaderDFF::readFrameList(Model *model, const RWBStream &stream)
|
|
|
|
{
|
|
|
|
auto listStream = stream.getInnerStream();
|
|
|
|
|
|
|
|
auto listStructID = listStream.getNextChunk();
|
|
|
|
if( listStructID != CHUNK_STRUCT ) {
|
|
|
|
throw DFFLoaderException("Frame List missing struct chunk");
|
|
|
|
}
|
|
|
|
|
|
|
|
char* headerPtr = listStream.getCursor();
|
|
|
|
|
|
|
|
unsigned int numFrames = *(std::uint32_t*)headerPtr;
|
|
|
|
headerPtr += sizeof(std::uint32_t);
|
|
|
|
|
|
|
|
model->frames.reserve(numFrames);
|
|
|
|
|
|
|
|
for( size_t f = 0; f < numFrames; ++f ) {
|
|
|
|
auto data = (RWBSFrame*)headerPtr;
|
|
|
|
headerPtr += sizeof(RWBSFrame);
|
|
|
|
|
|
|
|
ModelFrame* parent = nullptr;
|
|
|
|
if( data->index != -1 ) {
|
|
|
|
parent = model->frames[data->index];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
model->rootFrameIdx = f;
|
|
|
|
}
|
|
|
|
|
2014-12-11 17:48:47 +00:00
|
|
|
auto frame = new ModelFrame(f, parent, data->rotation, data->position);
|
2014-08-04 22:21:01 +01:00
|
|
|
model->frames.push_back(frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t namedFrames = 0;
|
|
|
|
|
|
|
|
/// @todo perhaps flatten this out a little
|
|
|
|
for( auto chunkID = listStream.getNextChunk(); chunkID != 0; chunkID = listStream.getNextChunk() )
|
|
|
|
{
|
|
|
|
switch(chunkID) {
|
|
|
|
case CHUNK_EXTENSION: {
|
|
|
|
auto extStream = listStream.getInnerStream();
|
|
|
|
for( auto chunkID = extStream.getNextChunk(); chunkID != 0; chunkID = extStream.getNextChunk() )
|
|
|
|
{
|
|
|
|
switch( chunkID ) {
|
|
|
|
case CHUNK_NODENAME: {
|
|
|
|
std::string fname(extStream.getCursor(), extStream.getCurrentChunkSize());
|
|
|
|
std::transform(fname.begin(), fname.end(), fname.begin(), ::tolower );
|
|
|
|
|
|
|
|
if( namedFrames < model->frames.size() ) {
|
|
|
|
model->frames[namedFrames++]->setName(fname);
|
2013-07-03 19:00:15 +01:00
|
|
|
}
|
|
|
|
}
|
2014-08-04 22:21:01 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-07-03 19:00:15 +01:00
|
|
|
}
|
2014-08-04 22:21:01 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2013-07-03 19:00:15 +01:00
|
|
|
break;
|
|
|
|
}
|
2014-08-04 22:21:01 +01:00
|
|
|
}
|
|
|
|
}
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
void LoaderDFF::readGeometryList(Model *model, const RWBStream &stream)
|
|
|
|
{
|
|
|
|
auto listStream = stream.getInnerStream();
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
auto listStructID = listStream.getNextChunk();
|
|
|
|
if( listStructID != CHUNK_STRUCT ) {
|
|
|
|
throw DFFLoaderException("Geometry List missing struct chunk");
|
|
|
|
}
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
char* headerPtr = listStream.getCursor();
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
unsigned int numGeometries = *(std::uint32_t*)headerPtr;
|
|
|
|
headerPtr += sizeof(std::uint32_t);
|
2013-09-11 18:23:31 +00:00
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
model->geometries.reserve(numGeometries);
|
|
|
|
|
|
|
|
for( auto chunkID = listStream.getNextChunk(); chunkID != 0; chunkID = listStream.getNextChunk() )
|
|
|
|
{
|
|
|
|
switch(chunkID) {
|
|
|
|
case CHUNK_GEOMETRY:
|
|
|
|
readGeometry(model, listStream);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderDFF::readGeometry(Model *model, const RWBStream &stream)
|
|
|
|
{
|
|
|
|
auto geomStream = stream.getInnerStream();
|
|
|
|
|
|
|
|
auto geomStructID = geomStream.getNextChunk();
|
|
|
|
if( geomStructID != CHUNK_STRUCT ) {
|
|
|
|
throw DFFLoaderException("Geometry missing struct chunk");
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Model::Geometry> geom(new Model::Geometry);
|
|
|
|
|
|
|
|
char* headerPtr = geomStream.getCursor();
|
|
|
|
|
|
|
|
geom->flags = *(std::uint16_t*)headerPtr;
|
|
|
|
headerPtr += sizeof(std::uint16_t);
|
|
|
|
|
2014-08-06 00:23:50 +01:00
|
|
|
/*unsigned short numUVs = *(std::uint8_t*)headerPtr;*/
|
2014-08-04 22:21:01 +01:00
|
|
|
headerPtr += sizeof(std::uint8_t);
|
2014-08-06 00:23:50 +01:00
|
|
|
/*unsigned short moreFlags = *(std::uint8_t*)headerPtr;*/
|
2014-08-04 22:21:01 +01:00
|
|
|
headerPtr += sizeof(std::uint8_t);
|
|
|
|
|
|
|
|
unsigned int numTris = *(std::uint32_t*)headerPtr;
|
|
|
|
headerPtr += sizeof(std::uint32_t);
|
|
|
|
unsigned int numVerts = *(std::uint32_t*)headerPtr;
|
|
|
|
headerPtr += sizeof(std::uint32_t);
|
2014-08-06 00:23:50 +01:00
|
|
|
/*unsigned int numFrames = *(std::uint32_t*)headerPtr;*/
|
2014-08-04 22:21:01 +01:00
|
|
|
headerPtr += sizeof(std::uint32_t);
|
|
|
|
|
|
|
|
std::vector<Model::GeometryVertex> verts;
|
|
|
|
verts.resize(numVerts);
|
|
|
|
|
|
|
|
if( geomStream.getChunkVersion() < 0x1003FFFF ) {
|
|
|
|
headerPtr += sizeof(RW::BSGeometryColor);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @todo extract magic numbers.
|
|
|
|
|
|
|
|
if( (geom->flags & 8) == 8 ) {
|
|
|
|
for(size_t v = 0; v < numVerts; ++v) {
|
|
|
|
verts[v].colour = *(glm::u8vec4*)headerPtr;
|
|
|
|
headerPtr += sizeof(glm::u8vec4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for(size_t v = 0; v < numVerts; ++v) {
|
|
|
|
verts[v].colour = {255, 255, 255, 255};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (geom->flags & 4) == 4 || (geom->flags & 128) == 128) {
|
|
|
|
for(size_t v = 0; v < numVerts; ++v) {
|
|
|
|
verts[v].texcoord = *(glm::vec2*)headerPtr;
|
|
|
|
headerPtr += sizeof(glm::vec2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-30 22:15:55 +01:00
|
|
|
// Grab indicies data to generate normals (if applicable).
|
|
|
|
RW::BSGeometryTriangle* triangles = (RW::BSGeometryTriangle*)headerPtr;
|
2014-08-04 22:21:01 +01:00
|
|
|
headerPtr += sizeof(RW::BSGeometryTriangle) * numTris;
|
|
|
|
|
|
|
|
geom->geometryBounds = *(RW::BSGeometryBounds*)headerPtr;
|
|
|
|
geom->geometryBounds.radius = std::abs(geom->geometryBounds.radius);
|
|
|
|
headerPtr += sizeof(RW::BSGeometryBounds);
|
|
|
|
|
|
|
|
for(size_t v = 0; v < numVerts; ++v) {
|
|
|
|
verts[v].position = *(glm::vec3*)headerPtr;
|
|
|
|
headerPtr += sizeof(glm::vec3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if( (geom->flags & 16) == 16 ) {
|
|
|
|
for(size_t v = 0; v < numVerts; ++v) {
|
|
|
|
verts[v].normal = *(glm::vec3*)headerPtr;
|
|
|
|
headerPtr += sizeof(glm::vec3);
|
|
|
|
}
|
|
|
|
}
|
2015-03-30 22:15:55 +01:00
|
|
|
else {
|
|
|
|
// Use triangle data to calculate normals for each vert.
|
|
|
|
for( int t = 0; t < numTris; ++t ) {
|
|
|
|
auto& triangle = triangles[t];
|
|
|
|
auto& A = verts[triangle.first];
|
|
|
|
auto& B = verts[triangle.second];
|
|
|
|
auto& C = verts[triangle.third];
|
|
|
|
auto normal = glm::normalize(glm::cross(C.position-A.position, B.position-A.position));
|
|
|
|
A.normal = normal;
|
|
|
|
B.normal = normal;
|
|
|
|
C.normal = normal;
|
|
|
|
}
|
|
|
|
}
|
2014-08-04 22:21:01 +01:00
|
|
|
|
|
|
|
// Add the geometry to the model now so that it can be accessed.
|
|
|
|
model->geometries.push_back(geom);
|
|
|
|
|
|
|
|
// Process the geometry child sections
|
|
|
|
for(auto chunkID = geomStream.getNextChunk(); chunkID != 0; chunkID = geomStream.getNextChunk())
|
|
|
|
{
|
|
|
|
switch( chunkID ) {
|
|
|
|
case CHUNK_MATERIALLIST:
|
|
|
|
readMaterialList(model, geomStream);
|
|
|
|
break;
|
|
|
|
case CHUNK_EXTENSION:
|
|
|
|
readGeometryExtension(model, geomStream);
|
|
|
|
break;
|
|
|
|
default:
|
2013-07-03 19:00:15 +01:00
|
|
|
break;
|
|
|
|
}
|
2014-08-04 22:21:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
geom->dbuff.setFaceType(geom->facetype == Model::Triangles ?
|
|
|
|
GL_TRIANGLES : GL_TRIANGLE_STRIP);
|
|
|
|
geom->gbuff.uploadVertices(verts);
|
|
|
|
geom->dbuff.addGeometry(&geom->gbuff);
|
|
|
|
|
|
|
|
glGenBuffers(1, &geom->EBO);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geom->EBO);
|
|
|
|
|
|
|
|
size_t icount = std::accumulate(geom->subgeom.begin(), geom->subgeom.end(),
|
|
|
|
0u,
|
|
|
|
[](size_t a, const Model::SubGeometry& b) {return a + b.numIndices;});
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * icount, 0, GL_STATIC_DRAW);
|
|
|
|
for(auto& sg : geom->subgeom) {
|
|
|
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER,
|
|
|
|
sg.start * sizeof(uint32_t),
|
|
|
|
sizeof(uint32_t) * sg.numIndices,
|
2014-08-10 20:09:54 +01:00
|
|
|
sg.indices.data());
|
2014-08-04 22:21:01 +01:00
|
|
|
}
|
2014-08-22 14:29:46 +01:00
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderDFF::readMaterialList(Model *model, const RWBStream &stream)
|
|
|
|
{
|
|
|
|
auto listStream = stream.getInnerStream();
|
|
|
|
|
|
|
|
auto listStructID = listStream.getNextChunk();
|
|
|
|
if( listStructID != CHUNK_STRUCT ) {
|
|
|
|
throw DFFLoaderException("MaterialList missing struct chunk");
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int numMaterials = *(std::uint32_t*)listStream.getCursor();
|
|
|
|
|
|
|
|
model->geometries.back()->materials.reserve(numMaterials);
|
|
|
|
|
|
|
|
RWBStream::ChunkID chunkID;
|
|
|
|
while( (chunkID = listStream.getNextChunk()) ) {
|
|
|
|
switch( chunkID ) {
|
|
|
|
case CHUNK_MATERIAL:
|
|
|
|
readMaterial(model, listStream);
|
|
|
|
break;
|
|
|
|
default:
|
2013-07-03 19:00:15 +01:00
|
|
|
break;
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
2014-08-04 22:21:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderDFF::readMaterial(Model *model, const RWBStream &stream)
|
|
|
|
{
|
|
|
|
auto materialStream = stream.getInnerStream();
|
|
|
|
|
|
|
|
auto matStructID = materialStream.getNextChunk();
|
|
|
|
if( matStructID != CHUNK_STRUCT ) {
|
|
|
|
throw DFFLoaderException("Material missing struct chunk");
|
|
|
|
}
|
|
|
|
|
|
|
|
char* matData = materialStream.getCursor();
|
|
|
|
|
|
|
|
Model::Material material;
|
|
|
|
|
|
|
|
// Unkown
|
|
|
|
matData += sizeof(std::uint32_t);
|
|
|
|
material.colour = *(glm::u8vec4*)matData;
|
|
|
|
matData += sizeof(std::uint32_t);
|
|
|
|
// Unkown
|
|
|
|
matData += sizeof(std::uint32_t);
|
2014-08-06 00:23:50 +01:00
|
|
|
/*bool usesTexture = *(std::uint32_t*)matData;*/
|
2014-08-04 22:21:01 +01:00
|
|
|
matData += sizeof(std::uint32_t);
|
|
|
|
|
|
|
|
material.ambientIntensity = *(float*)matData;
|
|
|
|
matData += sizeof(float);
|
|
|
|
/*float specular = *(float*)matData;*/
|
|
|
|
matData += sizeof(float);
|
|
|
|
material.diffuseIntensity = *(float*)matData;
|
|
|
|
matData += sizeof(float);
|
|
|
|
material.flags = 0;
|
|
|
|
|
|
|
|
model->geometries.back()->materials.push_back(material);
|
|
|
|
|
|
|
|
RWBStream::ChunkID chunkID;
|
2014-08-06 00:23:50 +01:00
|
|
|
while( ( chunkID = materialStream.getNextChunk() ) ) {
|
2014-08-04 22:21:01 +01:00
|
|
|
switch( chunkID ) {
|
|
|
|
case CHUNK_TEXTURE:
|
|
|
|
readTexture(model, materialStream);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
}
|
2014-08-04 22:21:01 +01:00
|
|
|
}
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
void LoaderDFF::readTexture(Model *model, const RWBStream &stream)
|
|
|
|
{
|
|
|
|
auto texStream = stream.getInnerStream();
|
|
|
|
|
|
|
|
auto texStructID = texStream.getNextChunk();
|
|
|
|
if( texStructID != CHUNK_STRUCT ) {
|
|
|
|
throw DFFLoaderException("Texture missing struct chunk");
|
|
|
|
}
|
|
|
|
|
|
|
|
// There's some data in the Texture's struct, but we don't know what it is.
|
|
|
|
|
|
|
|
/// @todo improve how these strings are read.
|
|
|
|
std::string name, alpha;
|
|
|
|
|
|
|
|
texStream.getNextChunk();
|
|
|
|
name = texStream.getCursor();
|
|
|
|
texStream.getNextChunk();
|
|
|
|
alpha = texStream.getCursor();
|
|
|
|
|
|
|
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower );
|
|
|
|
std::transform(alpha.begin(), alpha.end(), alpha.begin(), ::tolower );
|
|
|
|
|
|
|
|
model->geometries.back()->materials.back().textures.push_back({name, alpha});
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
void LoaderDFF::readGeometryExtension(Model *model, const RWBStream &stream)
|
2013-07-02 07:06:03 +01:00
|
|
|
{
|
2014-08-04 22:21:01 +01:00
|
|
|
auto extStream = stream.getInnerStream();
|
|
|
|
|
|
|
|
RWBStream::ChunkID chunkID;
|
|
|
|
while( (chunkID = extStream.getNextChunk()) ) {
|
|
|
|
switch( chunkID ) {
|
|
|
|
case CHUNK_BINMESHPLG:
|
|
|
|
readBinMeshPLG(model, extStream);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderDFF::readBinMeshPLG(Model *model, const RWBStream &stream)
|
|
|
|
{
|
|
|
|
auto data = stream.getCursor();
|
|
|
|
|
|
|
|
model->geometries.back()->facetype = static_cast<Model::FaceType>(
|
|
|
|
*(std::uint32_t*)data);
|
|
|
|
data += sizeof(std::uint32_t);
|
|
|
|
|
|
|
|
unsigned int numSplits = *(std::uint32_t*)data;
|
|
|
|
data += sizeof(std::uint32_t);
|
|
|
|
|
|
|
|
// Number of triangles.
|
|
|
|
data += sizeof(std::uint32_t);
|
|
|
|
|
|
|
|
model->geometries.back()->subgeom.reserve(numSplits);
|
|
|
|
|
|
|
|
size_t start = 0;
|
|
|
|
|
|
|
|
for(size_t s = 0; s < numSplits; ++s) {
|
|
|
|
Model::SubGeometry sg;
|
|
|
|
sg.numIndices = *(std::uint32_t*)data;
|
|
|
|
data += sizeof(std::uint32_t);
|
|
|
|
sg.material = *(std::uint32_t*)data;
|
|
|
|
data += sizeof(std::uint32_t);
|
|
|
|
sg.start = start;
|
|
|
|
start += sg.numIndices;
|
|
|
|
|
2014-08-10 20:09:54 +01:00
|
|
|
sg.indices.resize(sg.numIndices);
|
|
|
|
std::memcpy(sg.indices.data(), data, sizeof(std::uint32_t) * sg.numIndices);
|
|
|
|
data += sizeof(std::uint32_t) * sg.numIndices;
|
2014-08-04 22:21:01 +01:00
|
|
|
|
|
|
|
model->geometries.back()->subgeom.push_back(sg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoaderDFF::readAtomic(Model *model, const RWBStream &stream)
|
|
|
|
{
|
|
|
|
auto atomicStream = stream.getInnerStream();
|
|
|
|
|
|
|
|
auto atomicStructID = atomicStream.getNextChunk();
|
|
|
|
if( atomicStructID != CHUNK_STRUCT ) {
|
|
|
|
throw DFFLoaderException("Atomic missing struct chunk");
|
|
|
|
}
|
|
|
|
|
|
|
|
Model::Atomic atom;
|
|
|
|
auto data = atomicStream.getCursor();
|
|
|
|
atom.frame = *(std::uint32_t*)data;
|
|
|
|
data += sizeof(std::uint32_t);
|
|
|
|
atom.geometry = *(std::uint32_t*)data;
|
|
|
|
model->frames[atom.frame]->addGeometry(atom.geometry);
|
|
|
|
model->atomics.push_back(atom);
|
|
|
|
|
|
|
|
/// @todo are any atomic extensions important?
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
|
2015-03-06 04:38:22 +00:00
|
|
|
Model* LoaderDFF::loadFromMemory(FileHandle file)
|
2013-07-02 07:06:03 +01:00
|
|
|
{
|
2014-08-04 22:21:01 +01:00
|
|
|
auto model = new Model;
|
|
|
|
|
|
|
|
RWBStream rootStream(file->data, file->length);
|
|
|
|
|
|
|
|
auto rootID = rootStream.getNextChunk();
|
|
|
|
if( rootID != CHUNK_CLUMP ) {
|
|
|
|
throw DFFLoaderException("Invalid root section ID " + std::to_string(rootID));
|
|
|
|
}
|
|
|
|
|
|
|
|
RWBStream modelStream = rootStream.getInnerStream();
|
|
|
|
auto rootStructID = modelStream.getNextChunk();
|
|
|
|
if( rootStructID != CHUNK_STRUCT ) {
|
|
|
|
throw DFFLoaderException("Clump missing struct chunk");
|
|
|
|
}
|
|
|
|
|
|
|
|
// There is only one value in the struct section.
|
|
|
|
model->numAtomics = *(std::uint32_t*)rootStream.getCursor();
|
|
|
|
|
|
|
|
// Process everything inside the clump stream.
|
|
|
|
RWBStream::ChunkID chunkID;
|
2014-08-06 00:23:50 +01:00
|
|
|
while( ( chunkID = modelStream.getNextChunk() ) ) {
|
2014-08-04 22:21:01 +01:00
|
|
|
switch( chunkID ) {
|
|
|
|
case CHUNK_FRAMELIST:
|
|
|
|
readFrameList(model, modelStream);
|
|
|
|
break;
|
|
|
|
case CHUNK_GEOMETRYLIST:
|
|
|
|
readGeometryList(model, modelStream);
|
|
|
|
break;
|
|
|
|
case CHUNK_ATOMIC:
|
|
|
|
readAtomic(model, modelStream);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-19 01:20:54 +01:00
|
|
|
// Ensure the model has cached metrics
|
|
|
|
model->recalculateMetrics();
|
|
|
|
|
2014-08-04 22:21:01 +01:00
|
|
|
return model;
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|