315 lines
10 KiB
C++
315 lines
10 KiB
C++
#include <cassert>
|
|
|
|
#include "ObjDecoder.hh"
|
|
#include "exceptions.hh"
|
|
#include "util.hh"
|
|
|
|
ObjDecoder::ObjDecoder(uint8_t const* base, size_t length)
|
|
: iff_(base, length)
|
|
{
|
|
auto& root = iff_.getRoot();
|
|
if (!root.isForm())
|
|
throw FormatException{"Root node not FORM"};
|
|
|
|
auto& rootForm = dynamic_cast<IffFile::Form const&>(root);
|
|
if (rootForm.getSubtype() != "REAL")
|
|
throw FormatException{"Root form not REAL"};
|
|
|
|
for (auto realIt = rootForm.childrenBegin();realIt != rootForm.childrenEnd();++realIt) {
|
|
if (realIt->getType() == "INFO") {
|
|
name_ = *realIt;
|
|
printf("Name: \"%s\"\n", name_.c_str());
|
|
} else if (realIt->isForm()) {
|
|
auto& itForm = dynamic_cast<IffFile::Form const&>(*realIt);
|
|
if (itForm.getSubtype() == "OBJT")
|
|
parseOBJT_(itForm);
|
|
else if (itForm.getSubtype() == "APPR")
|
|
parseAPPR_(itForm);
|
|
else
|
|
throw FormatException{"Unknown FORM type " + itForm.getSubtype()};
|
|
} else
|
|
throw FormatException{"Unknown chunk type " + realIt->getType()};
|
|
}
|
|
}
|
|
|
|
ObjDecoder::Triangles ObjDecoder::getTriangles(unsigned lvl) const
|
|
{
|
|
Triangles ret;
|
|
for (auto idx : detailLevels_.at(lvl)) {
|
|
if (idx < triangles_.size())
|
|
ret.push_back(triangles_[idx]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
ObjDecoder::Quads ObjDecoder::getQuads(unsigned lvl) const
|
|
{
|
|
Quads ret;
|
|
for (auto idx : detailLevels_.at(lvl)) {
|
|
if (idx >= triangles_.size())
|
|
ret.push_back(quads_[idx-triangles_.size()]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ObjDecoder::parseOBJT_(IffFile::Form const& form)
|
|
{
|
|
}
|
|
|
|
void ObjDecoder::parseAPPR_(IffFile::Form const& form)
|
|
{
|
|
if (form.getChildCount() != 1)
|
|
throw FormatException{"Unexpected child count in APPR"};
|
|
|
|
auto& child = *form.childrenBegin();
|
|
if (!child.isForm())
|
|
throw FormatException{"APPR child not FORM"};
|
|
|
|
auto& childForm = dynamic_cast<IffFile::Form const&>(child);
|
|
if (childForm.getSubtype() != "POLY")
|
|
throw FormatException{"APPR child form not POLY"};
|
|
|
|
parsePOLY_(childForm);
|
|
}
|
|
|
|
void ObjDecoder::parsePOLY_(IffFile::Form const& form)
|
|
{
|
|
for (auto it = form.childrenBegin();it != form.childrenEnd();++it) {
|
|
if (it->getType() == "INFO") {
|
|
} else if (it->getType() == "VERT") {
|
|
if (it->getSize()%12 != 0)
|
|
throw FormatException{"Unexpected VERT size"};
|
|
for (size_t i = 0;i < it->getSize();i+=12) {
|
|
std::array<int32_t, 3> vertex;
|
|
for (size_t j = 0;j < 3;++j)
|
|
vertex[j] = readU32LE(it->begin()+i+j*4);
|
|
// printf("V: %d %d %d\n", vertex[0], vertex[1], vertex[2]);
|
|
vertices_.push_back(vertex);
|
|
}
|
|
} else if (it->isForm()) {
|
|
auto& itForm = dynamic_cast<IffFile::Form const&>(*it);
|
|
if (itForm.getSubtype() == "TRIS") {
|
|
parseTRIS_(itForm);
|
|
} else if (itForm.getSubtype() == "QUAD") {
|
|
parseQUAD_(itForm);
|
|
} else if (itForm.getSubtype() == "DETA") {
|
|
parseDETA_(itForm);
|
|
} else if (itForm.getSubtype() == "TXMS") {
|
|
parseTXMS_(itForm);
|
|
} else if (itForm.getSubtype() == "GRUP") {
|
|
// NYI
|
|
} else
|
|
throw FormatException{"Unknown POLY subform " + itForm.getSubtype()};
|
|
} else if (it->getType() == "ATTR") {
|
|
// NYI
|
|
} else if (it->getType() == "XATR") {
|
|
// NYI
|
|
} else if (it->getType() == "SUPR") {
|
|
// NYI
|
|
} else
|
|
throw FormatException{"Unknown POLY child " + it->getType()};
|
|
}
|
|
}
|
|
|
|
void ObjDecoder::parseTRIS_(IffFile::Form const& form)
|
|
{
|
|
if (form.getChildCount() != 2)
|
|
throw FormatException{"Unexpected child count in TRIS"};
|
|
|
|
auto vertCount = vertices_.size();
|
|
printf("VC: %lu\n", vertCount);
|
|
|
|
IffFile::Object const *face = nullptr, *maps = nullptr;
|
|
for (auto it = form.childrenBegin();it != form.childrenEnd();++it) {
|
|
if (it->getType() == "FACE") {
|
|
if (face)
|
|
throw FormatException{"Multiple FACE in TRIS"};
|
|
face = &*it;
|
|
} else if (it->getType() == "MAPS") {
|
|
if (maps)
|
|
throw FormatException{"Multiple MAPS in TRIS"};
|
|
maps = &*it;
|
|
} else
|
|
throw FormatException{"Unknown TRIS child " + it->getType()};
|
|
}
|
|
|
|
assert(face && maps);
|
|
|
|
if (face->getSize()%8 != 0)
|
|
throw FormatException{"Unexpected FACE size"};
|
|
auto numTriangles = face->getSize()/8;
|
|
if (maps->getSize() != numTriangles*16)
|
|
printf("WARNING: Unexpected MAPS size\n");
|
|
// throw FormatException{"Unexpected MAPS size"};
|
|
|
|
for (unsigned i = 0;i < numTriangles;++i) {
|
|
Triangle tri;
|
|
for (unsigned j = 0;j < 3;++j)
|
|
tri.vertIdx[j] = readU16LE(face->begin()+i*8+2+j*2);
|
|
tri.id = readU16LE(maps->begin()+i*16);
|
|
tri.tex = readU16LE(maps->begin()+i*16+2);
|
|
for (unsigned j = 0;j < 3;++j) {
|
|
tri.texCoords[j][0] = readU16LE(maps->begin()+i*16+4+j*4);
|
|
tri.texCoords[j][1] = readU16LE(maps->begin()+i*16+4+j*4+2);
|
|
}
|
|
// printf("T%u: (%hu (%hu %hu)), (%hu (%hu %hu)), (%hu (%hu %hu))\n", tri.id,
|
|
// tri.vertIdx[0], tri.texCoords[0][0], tri.texCoords[0][1],
|
|
// tri.vertIdx[1], tri.texCoords[1][0], tri.texCoords[1][1],
|
|
// tri.vertIdx[2], tri.texCoords[2][0], tri.texCoords[2][1]);
|
|
triangles_.push_back(tri);
|
|
}
|
|
|
|
printf("TC: %lu\n", triangles_.size());
|
|
}
|
|
|
|
void ObjDecoder::parseQUAD_(IffFile::Form const& form)
|
|
{
|
|
if (form.getChildCount() != 2)
|
|
throw FormatException{"Unexpected child count in QUAD"};
|
|
|
|
IffFile::Object const *face = nullptr, *maps = nullptr;
|
|
for (auto it = form.childrenBegin();it != form.childrenEnd();++it) {
|
|
if (it->getType() == "FACE") {
|
|
if (face)
|
|
throw FormatException{"Multiple FACE in QUAD"};
|
|
face = &*it;
|
|
} else if (it->getType() == "MAPS") {
|
|
if (maps)
|
|
throw FormatException{"Multiple MAPS in QUAD"};
|
|
maps = &*it;
|
|
} else
|
|
throw FormatException{"Unknown TRIS child " + it->getType()};
|
|
}
|
|
|
|
assert(face && maps);
|
|
|
|
if (face->getSize()%10 != 0)
|
|
throw FormatException{"Unexpected FACE size"};
|
|
auto numQuads = face->getSize()/10;
|
|
if (maps->getSize() != numQuads*20)
|
|
printf("WARNING: Unexpected MAPS size\n");
|
|
//throw FormatException{"Unexpected MAPS size"};
|
|
|
|
for (unsigned i = 0;i < numQuads;++i) {
|
|
Quad quad;
|
|
for (unsigned j = 0;j < 4;++j)
|
|
quad.vertIdx[j] = readU16LE(face->begin()+i*10+2+j*2);
|
|
quad.id = readU16LE(maps->begin()+i*20);
|
|
quad.tex = readU16LE(maps->begin()+i*20+2);
|
|
for (unsigned j = 0;j < 4;++j) {
|
|
quad.texCoords[j][0] = readU16LE(maps->begin()+i*20+4+j*4);
|
|
quad.texCoords[j][1] = readU16LE(maps->begin()+i*20+4+j*4+2);
|
|
}
|
|
// printf("Q%u: (%hu (%hu %hu)), (%hu (%hu %hu)), (%hu (%hu %hu)), (%hu (%hu %hu))\n", quad.id,
|
|
// quad.vertIdx[0], quad.texCoords[0][0], quad.texCoords[0][1],
|
|
// quad.vertIdx[1], quad.texCoords[1][0], quad.texCoords[1][1],
|
|
// quad.vertIdx[2], quad.texCoords[2][0], quad.texCoords[2][1],
|
|
// quad.vertIdx[3], quad.texCoords[3][0], quad.texCoords[3][1]);
|
|
quads_.push_back(quad);
|
|
}
|
|
|
|
printf("QC: %lu\n", quads_.size());
|
|
}
|
|
|
|
void ObjDecoder::parseDETA_(IffFile::Form const& form)
|
|
{
|
|
if (form.getChildCount() < 1)
|
|
throw FormatException{"Unexpected child count in DETA"};
|
|
|
|
unsigned lvl = 0;
|
|
for (auto it = form.childrenBegin();it != form.childrenEnd();++it) {
|
|
if (it->getType().substr(0, 3) != "LVL")
|
|
throw FormatException{"Unexpected child " + it->getType() + " in DETA"};
|
|
if (std::stoi(it->getType().substr(3)) != lvl)
|
|
throw FormatException{"Unexpected detail level"};
|
|
|
|
if (it->getSize() < 4)
|
|
throw FormatException{"DETA child too small"};
|
|
|
|
// uint32_t unkown = readU32LE(it->begin());
|
|
detailLevels_.emplace_back();
|
|
for (size_t pos = 4;pos < it->getSize()-1;pos += 2)
|
|
detailLevels_.back().push_back(readU16LE(it->begin()+pos));
|
|
|
|
printf("LVL %u: %lu entries\n", lvl, detailLevels_.back().size());
|
|
++lvl;
|
|
}
|
|
}
|
|
|
|
void ObjDecoder::parseTXMS_(IffFile::Form const& form)
|
|
{
|
|
if ((textures_.size() != 0) || texAnims_.size() != 0)
|
|
throw FormatException{"Multiple TXMS"};
|
|
|
|
if (form.getChildCount() < 1)
|
|
throw FormatException{"Unexpected child count in TXMS"};
|
|
|
|
auto it = form.childrenBegin();
|
|
auto& info = *it++;
|
|
if (info.getType() != "INFO")
|
|
throw FormatException{"Missing INFO in TXMS"};
|
|
if (info.getSize() != 4)
|
|
throw FormatException{"Unexpected INFO size in TXMS"};
|
|
|
|
uint16_t numTXMP, numTXMA;
|
|
numTXMP = readU16LE(info.begin());
|
|
numTXMA = readU16LE(info.begin()+2);
|
|
if (form.getChildCount() != 1u+numTXMP+numTXMA)
|
|
throw FormatException{"Unexpected child count in TXMS"};
|
|
|
|
textures_.reserve(numTXMP);
|
|
texAnims_.reserve(numTXMA);
|
|
for (;it != form.childrenEnd();++it) {
|
|
if (it->getType() == "TXMP") {
|
|
if (it->getSize() < 12u)
|
|
throw FormatException{"TXMP smaller than header"};
|
|
|
|
Texture tex;
|
|
if (it->begin()[7] == '\0')
|
|
tex.name = std::string(reinterpret_cast<char const*>(it->begin()));
|
|
else
|
|
tex.name = std::string(reinterpret_cast<char const*>(it->begin()), 8);
|
|
printf("TXMP name \"%s\"\n", tex.name.c_str());
|
|
|
|
tex.width = readU16LE(it->begin()+8);
|
|
tex.height = readU16LE(it->begin()+10);
|
|
printf("TXMP size %hux%hu\n", tex.width, tex.height);
|
|
|
|
if (it->getSize() < 12u+tex.width*tex.height)
|
|
throw FormatException{"TXMP too small for pixel data"};
|
|
tex.pixels = std::vector<uint8_t>(it->begin()+12, it->begin()+12+tex.width*tex.height);
|
|
textures_.emplace_back(std::move(tex));
|
|
} else if (it->getType() == "TXMA") {
|
|
if (it->getSize() < 14u)
|
|
throw FormatException{"TXMA smaller than header"};
|
|
|
|
TextureAnimation tex;
|
|
if (it->begin()[7] == '\0')
|
|
tex.name = std::string(reinterpret_cast<char const*>(it->begin()));
|
|
else
|
|
tex.name = std::string(reinterpret_cast<char const*>(it->begin()), 8);
|
|
printf("TXMA name \"%s\"\n", tex.name.c_str());
|
|
|
|
tex.width = readU16LE(it->begin()+8);
|
|
tex.height = readU16LE(it->begin()+10);
|
|
tex.frames = readU16LE(it->begin()+12);
|
|
printf("TXMA size %hux%hu, %hu frames\n", tex.width, tex.height, tex.frames);
|
|
|
|
if (it->getSize() < 14u+tex.width*tex.height*tex.frames)
|
|
throw FormatException{"TXMA too small for pixel data"};
|
|
|
|
tex.pixels.reserve(tex.frames);
|
|
uint8_t const* pos = it->begin()+14;
|
|
for (unsigned i = 0;i < tex.frames;++i) {
|
|
tex.pixels.emplace_back(pos, pos+tex.width*tex.height);
|
|
pos += tex.width*tex.height;
|
|
}
|
|
|
|
texAnims_.emplace_back(std::move(tex));
|
|
} else
|
|
throw FormatException{"Unkown TXMS child " + it->getType()};
|
|
}
|
|
}
|