WIP: REAL 3D object parsing

This commit is contained in:
2015-04-30 20:06:57 +02:00
parent 82ee2586d3
commit bd773d8214
11 changed files with 575 additions and 4 deletions

View File

@@ -2,8 +2,8 @@ CXX=g++
CXXOPTS=-Og -ggdb -fvar-tracking-assignments -Wall -Wextra -Wno-unused-function -pedantic -std=c++14 -march=native -fstack-protector-strong --param=ssp-buffer-size=4 -flto -I.
LDOPTS=-Wl,--sort-common,--as-needed
render_CXXSRCS ::= render/Renderer.cc render/GlResource.cc render/ProgramProvider.cc render/renderutil.cc render/Overlay.cc render/VBOManager.cc
game_CXXSRCS ::= game/GSMvePlay.cc
render_CXXSRCS ::= render/Renderer.cc render/GlResource.cc render/ProgramProvider.cc render/renderutil.cc render/Overlay.cc render/VBOManager.cc render/Object.cc
game_CXXSRCS ::= game/GSMvePlay.cc game/GSShowObject.cc
iffexplore_CXXSRCS ::= iffexplore.cc IffFile.cc util.cc exceptions.cc
iffexplore_LIBS ::=
@@ -17,7 +17,10 @@ font2png_LIBS ::= -lpng
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS)
mvedecode_LIBS ::= -lSDL2 -lglbinding
progs ::= iffexplore treexplore mvedecode
objdecode_CXXSRCS ::= objdecode.cc TreFile.cc IffFile.cc util.cc ObjDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS)
objdecode_LIBS ::= -lSDL2 -lglbinding
progs ::= iffexplore treexplore mvedecode objdecode
all: $(progs)

203
ObjDecoder.cc Normal file
View File

@@ -0,0 +1,203 @@
#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()};
}
}
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") {
// NYI
} 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
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)
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);
}
}
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)
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);
}
}
void ObjDecoder::parseTXMS_(IffFile::Form const& form)
{
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() != 1+numTXMP+numTXMA)
throw FormatException{"Unexpected child count in TXMS"};
}

62
ObjDecoder.hh Normal file
View File

@@ -0,0 +1,62 @@
#ifndef WC3RE_OBJDECODER_HH__
#define WC3RE_OBJDECODER_HH__
#include <string>
#include <cstdint>
#include <vector>
#include <array>
#include "IffFile.hh"
class ObjDecoder {
public:
ObjDecoder(uint8_t const* base, size_t length);
using Vertex = std::array<int32_t, 3>;
using Vertices = std::vector<Vertex>;
using TexCoord = std::array<uint16_t, 2>;
struct Triangle {
uint16_t id;
uint16_t tex;
std::array<uint16_t, 3> vertIdx;
std::array<TexCoord, 3> texCoords;
};
using Triangles = std::vector<Triangle>;
struct Quad {
uint16_t id;
uint16_t tex;
std::array<uint16_t, 4> vertIdx;
std::array<TexCoord, 4> texCoords;
};
using Quads = std::vector<Quad>;
Vertices const& getVertices() const {
return vertices_;
}
Triangles const& getTriangles() const {
return triangles_;
}
Quads const& getQuads() const {
return quads_;
}
private:
IffFile iff_;
std::string name_;
void parseOBJT_(IffFile::Form const& form);
void parseAPPR_(IffFile::Form const& form);
void parsePOLY_(IffFile::Form const& form);
void parseTRIS_(IffFile::Form const& form);
void parseQUAD_(IffFile::Form const& form);
void parseTXMS_(IffFile::Form const& form);
Vertices vertices_;
Triangles triangles_;
Quads quads_;
};
#endif

View File

@@ -2,7 +2,6 @@
#define WC3RE_GAME_GSMVEPLAY_HH__
#include "GameState.hh"
#include "render/sdlutil.hh"
class MveDecoder;
namespace render {

22
game/GSShowObject.cc Normal file
View File

@@ -0,0 +1,22 @@
#include "ObjDecoder.hh"
#include "GSShowObject.hh"
#include "render/Object.hh"
namespace game {
GSShowObject::GSShowObject(render::Renderer& renderer, ObjDecoder& obj)
: GameState(renderer), obj_(obj),
object_(nullptr), delta_(0)
{
object_ = std::make_unique<render::Object>(renderer, obj);
}
GSShowObject::~GSShowObject()
{
}
void GSShowObject::draw(unsigned delta_ms)
{
object_->draw();
}
}

27
game/GSShowObject.hh Normal file
View File

@@ -0,0 +1,27 @@
#ifndef WC3RE_GAME_GSSHOWOBJECT_HH__
#define WC3RE_GAME_GSSHOWOBJECT_HH__
#include "GameState.hh"
class ObjDecoder;
namespace render {
class Object;
}
namespace game {
class GSShowObject : public GameState {
public:
GSShowObject(render::Renderer& renderer, ObjDecoder& obj);
~GSShowObject() override;
void draw(unsigned delta_ms) override;
private:
ObjDecoder& obj_;
std::unique_ptr<render::Object> object_;
unsigned delta_;
};
}
#endif

95
objdecode.cc Normal file
View File

@@ -0,0 +1,95 @@
#include <cstdio>
#include <cstring>
#include <cerrno>
#include <cstdint>
#include <memory>
#include <limits>
#include <unistd.h>
#include "common.hh"
#include "util.hh"
#include "TreFile.hh"
#include "IffFile.hh"
#include "ObjDecoder.hh"
#include "render/Renderer.hh"
#include "game/GSShowObject.hh"
void usage(char *argv0) {
fprintf(stderr, "Usage: %s [-h] (tre-file name/crc)/iff-file\n", argv0);
fprintf(stderr, "\tAttempt to decode the object stored in iff-file, or in the\n\tiff-object \"name\"/\"crc\" contained in tre-file\n");
fprintf(stderr, "\t-h\tPrint this help\n");
}
int main(int argc, char *argv[]) {
std::string inFile, objectSpec;
bool useTre = false;
{
int opt;
while ((opt = getopt(argc, argv, "h")) != -1) {
switch (opt) {
case 'h':
usage(argv[0]);
return 0;
default:
usage(argv[0]);
return 1;
}
}
if (optind >= argc) {
usage(argv[0]);
return 1;
}
inFile = argv[optind++];
if (optind < argc) {
useTre = true;
objectSpec = argv[optind];
}
}
try {
MmapFile mmap{inFile};
std::unique_ptr<TreFile> tre;
TreFile::Object object;
std::unique_ptr<ObjDecoder> obj;
if (useTre) {
tre = std::make_unique<TreFile>(mmap.data(), mmap.size());
// Try to parse objectSpec as CRC
try {
unsigned long CRC = std::stoul(objectSpec, nullptr, 16);
if (CRC <= std::numeric_limits<uint32_t>::max()) {
object = tre->openCRC(CRC);
}
} catch (std::invalid_argument &ex) {
} catch (std::out_of_range &ex) {
}
if (!object) // Wasn't a CRC, try as name
object = tre->openName(objectSpec);
obj = std::make_unique<ObjDecoder>(object.data(), object.size());
} else
obj = std::make_unique<ObjDecoder>(mmap.data(), mmap.size());
render::Renderer renderer;
renderer.pushGS(std::make_unique<game::GSShowObject>(renderer, *obj));
renderer.run();
} catch (POSIXException &ex) {
fflush(stdout);
fprintf(stderr, "%s\n", ex.toString().c_str());
return 2;
} catch (Exception &ex) {
fflush(stdout);
fprintf(stderr, "%s\n", ex.toString().c_str());
return 3;
}
return 0;
}

107
render/Object.cc Normal file
View File

@@ -0,0 +1,107 @@
#include <glbinding/gl/gl.h>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Object.hh"
#include "ProgramProvider.hh"
#include "Renderer.hh"
#include "ObjDecoder.hh"
using namespace gl;
namespace render {
namespace {
struct VertexAttribs {
int32_t vertex[3];
uint16_t texCoords[2];
} __attribute__((__packed__));
}
Object::Object(Renderer& renderer, ObjDecoder& obj)
: Drawable(renderer),
vbo_()
{
program_ = ProgramProvider::getInstance().getProgram("object", "object");
auto& tris = obj.getTriangles();
auto& quads = obj.getQuads();
numVertices_ = (tris.size()*3+quads.size()*6);
vbo_ = VBOManager::getInstance().alloc(sizeof(VertexAttribs)*numVertices_);
glGenVertexArrays(1, &vertexArray_.get());
std::vector<VertexAttribs> vertexAttribs;
vertexAttribs.reserve(numVertices_);
auto& vertices = obj.getVertices();
for (auto& tri : tris) {
for (unsigned i = 0;i < 3;++i) {
VertexAttribs attrs;
std::copy(vertices.at(tri.vertIdx[i]).begin(), vertices.at(tri.vertIdx[i]).end(),
attrs.vertex);
std::copy(tri.texCoords[i].begin(), tri.texCoords[i].end(),
attrs.texCoords);
vertexAttribs.push_back(attrs);
}
}
for (auto& quad : quads) {
for (unsigned i = 0;i < 3;++i) {
VertexAttribs attrs;
std::copy(vertices.at(quad.vertIdx[i]).begin(), vertices.at(quad.vertIdx[i]).end(),
attrs.vertex);
std::copy(quad.texCoords[i].begin(), quad.texCoords[i].end(),
attrs.texCoords);
vertexAttribs.push_back(attrs);
}
for (unsigned i = 0;i < 3;++i) {
VertexAttribs attrs;
std::copy(vertices.at(quad.vertIdx[i+1]).begin(), vertices.at(quad.vertIdx[i+1]).end(),
attrs.vertex);
std::copy(quad.texCoords[i+1].begin(), quad.texCoords[i+1].end(),
attrs.texCoords);
vertexAttribs.push_back(attrs);
}
}
// std::vector<VertexAttribs> vertexAttribs = {
// {{-1500, -1500, 0}, {0, 0}},
// {{-1500, 1500, 0}, {0, 0}},
// {{1500, -1500, 0}, {0, 0}},
// {{1500, 1500, 0}, {0, 0}}};
glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId());
glBindVertexArray(vertexArray_);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_INT, GL_FALSE, sizeof(VertexAttribs),
vbo_.getOfs(offsetof(VertexAttribs, vertex)));
glBufferSubData(GL_ARRAY_BUFFER, vbo_.getBase(),
sizeof(VertexAttribs)*numVertices_,
vertexAttribs.data());
mvp_ = glm::perspectiveFov(75.0f, static_cast<float>(renderer.getWidth()),
static_cast<float>(renderer.getHeight()),
100.f, 20000.0f) *
glm::lookAt(glm::vec3(0.0f, 0.0f, 10000),
glm::vec3(0.0f, 0.0f, 0),
glm::vec3(0, 1, 0));
}
void Object::draw()
{
glDisable(GL_CULL_FACE);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glUseProgram(program_);
glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(mvp_));
glBindVertexArray(vertexArray_);
glDrawArrays(GL_TRIANGLES, 0, numVertices_);
}
}

29
render/Object.hh Normal file
View File

@@ -0,0 +1,29 @@
#ifndef WC3RE_RENDER_OBJECT_HH__
#define WC3RE_RENDER_OBJECT_HH__
#define GLM_FORCE_RADIANS
#include <glm/glm.hpp>
#include "Drawable.hh"
#include "GlResource.hh"
#include "VBOManager.hh"
class ObjDecoder;
namespace render {
class Object : public Drawable {
public:
Object(Renderer& renderer, ObjDecoder& obj);
void draw() override;
private:
VertexArrayResource vertexArray_;
gl::GLuint program_;
VBOManager::VBOAlloc vbo_;
glm::mat4 mvp_;
unsigned numVertices_;
};
}
#endif

8
shaders/object.fs Normal file
View File

@@ -0,0 +1,8 @@
#version 330 core
#extension GL_ARB_explicit_uniform_location : enable
out vec4 color;
void main(void) {
color = vec4(1.0, 1.0, 1.0, 1.0);
}

16
shaders/object.vs Normal file
View File

@@ -0,0 +1,16 @@
#version 330 core
#extension GL_ARB_shading_language_420pack : enable
#extension GL_ARB_explicit_uniform_location : enable
layout(location = 0) uniform mat4 mvp_matrix;
layout(location = 0) in vec3 vertex;
layout(location = 1) in vec2 vertexTC;
out vec2 fragTC;
void main(void) {
gl_Position = mvp_matrix * vec4(vertex, 1.0);
fragTC = vertexTC;
}