Files
wc3re/ObjDecoder.cc

315 lines
10 KiB
C++

#include <cassert>
#include "ObjDecoder.hh"
#include "exceptions.hh"
#include "util.hh"
ObjDecoder::ObjDecoder(Resource const& res)
: iff_(res)
{
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_ = static_cast<std::string>(*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->size()%12 != 0)
throw FormatException{"Unexpected VERT size"};
for (size_t i = 0;i < it->size();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->size()%8 != 0)
throw FormatException{"Unexpected FACE size"};
auto numTriangles = face->size()/8;
if (maps->size() != 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->size()%10 != 0)
throw FormatException{"Unexpected FACE size"};
auto numQuads = face->size()/10;
if (maps->size() != 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->size() < 4)
throw FormatException{"DETA child too small"};
// uint32_t unkown = readU32LE(it->begin());
detailLevels_.emplace_back();
for (size_t pos = 4;pos < it->size()-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.size() != 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->size() < 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->size() < 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->size() < 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->size() < 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()};
}
}