Mostly working REAL 3D parser and renderer
This commit is contained in:
2
Makefile
2
Makefile
@@ -17,7 +17,7 @@ font2png_LIBS ::= -lpng
|
|||||||
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS)
|
mvedecode_CXXSRCS ::= mvedecode.cc TreFile.cc IffFile.cc util.cc MveDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS)
|
||||||
mvedecode_LIBS ::= -lSDL2 -lglbinding
|
mvedecode_LIBS ::= -lSDL2 -lglbinding
|
||||||
|
|
||||||
objdecode_CXXSRCS ::= objdecode.cc TreFile.cc IffFile.cc util.cc ObjDecoder.cc exceptions.cc decompress.cc $(render_CXXSRCS) $(game_CXXSRCS)
|
objdecode_CXXSRCS ::= objdecode.cc TreFile.cc IffFile.cc util.cc ObjDecoder.cc exceptions.cc decompress.cc PaletteDecoder.cc $(render_CXXSRCS) $(game_CXXSRCS)
|
||||||
objdecode_LIBS ::= -lSDL2 -lglbinding
|
objdecode_LIBS ::= -lSDL2 -lglbinding
|
||||||
|
|
||||||
progs ::= iffexplore treexplore mvedecode objdecode
|
progs ::= iffexplore treexplore mvedecode objdecode
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ std::tuple<MveDecoder::Frame const&, MveDecoder::Palette const&> MveDecoder::Mov
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_tuple(frame_, palette);
|
return std::make_tuple(std::ref(frame_), std::ref(palette));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MveDecoder::Movie::hasAudio() const
|
bool MveDecoder::Movie::hasAudio() const
|
||||||
@@ -392,7 +392,7 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
|||||||
throw FormatException{"Reference to non-existing prev. frame"};
|
throw FormatException{"Reference to non-existing prev. frame"};
|
||||||
if (ret.size()+size > prev->size())
|
if (ret.size()+size > prev->size())
|
||||||
throw FormatException{"Copy from prev exceeds frame"};
|
throw FormatException{"Copy from prev exceeds frame"};
|
||||||
|
|
||||||
std::copy(prev->begin()+ret.size(),
|
std::copy(prev->begin()+ret.size(),
|
||||||
prev->begin()+ret.size()+size,
|
prev->begin()+ret.size()+size,
|
||||||
std::back_inserter(ret));
|
std::back_inserter(ret));
|
||||||
@@ -434,8 +434,8 @@ MveDecoder::Frame MveDecoder::parseVGA(IffFile::Object const& vga, Frame const*
|
|||||||
throw FormatException{"Copy from prev exceeds frame"};
|
throw FormatException{"Copy from prev exceeds frame"};
|
||||||
|
|
||||||
std::copy(prev->begin()+ref,
|
std::copy(prev->begin()+ref,
|
||||||
prev->begin()+ref+size,
|
prev->begin()+ref+size,
|
||||||
std::back_inserter(ret));
|
std::back_inserter(ret));
|
||||||
flag = false;
|
flag = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ ObjDecoder::ObjDecoder(uint8_t const* base, size_t length)
|
|||||||
for (auto realIt = rootForm.childrenBegin();realIt != rootForm.childrenEnd();++realIt) {
|
for (auto realIt = rootForm.childrenBegin();realIt != rootForm.childrenEnd();++realIt) {
|
||||||
if (realIt->getType() == "INFO") {
|
if (realIt->getType() == "INFO") {
|
||||||
name_ = *realIt;
|
name_ = *realIt;
|
||||||
printf("Name: %s\n", name_.c_str());
|
printf("Name: \"%s\"\n", name_.c_str());
|
||||||
} else if (realIt->isForm()) {
|
} else if (realIt->isForm()) {
|
||||||
auto& itForm = dynamic_cast<IffFile::Form const&>(*realIt);
|
auto& itForm = dynamic_cast<IffFile::Form const&>(*realIt);
|
||||||
if (itForm.getSubtype() == "OBJT")
|
if (itForm.getSubtype() == "OBJT")
|
||||||
@@ -63,7 +63,7 @@ void ObjDecoder::parsePOLY_(IffFile::Form const& form)
|
|||||||
std::array<int32_t, 3> vertex;
|
std::array<int32_t, 3> vertex;
|
||||||
for (size_t j = 0;j < 3;++j)
|
for (size_t j = 0;j < 3;++j)
|
||||||
vertex[j] = readU32LE(it->begin()+i+j*4);
|
vertex[j] = readU32LE(it->begin()+i+j*4);
|
||||||
printf("V: %d %d %d\n", vertex[0], vertex[1], vertex[2]);
|
// printf("V: %d %d %d\n", vertex[0], vertex[1], vertex[2]);
|
||||||
vertices_.push_back(vertex);
|
vertices_.push_back(vertex);
|
||||||
}
|
}
|
||||||
} else if (it->isForm()) {
|
} else if (it->isForm()) {
|
||||||
@@ -129,10 +129,10 @@ void ObjDecoder::parseTRIS_(IffFile::Form const& form)
|
|||||||
tri.texCoords[j][0] = readU16LE(maps->begin()+i*16+4+j*4);
|
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);
|
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,
|
// 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[0], tri.texCoords[0][0], tri.texCoords[0][1],
|
||||||
tri.vertIdx[1], tri.texCoords[1][0], tri.texCoords[1][1],
|
// tri.vertIdx[1], tri.texCoords[1][0], tri.texCoords[1][1],
|
||||||
tri.vertIdx[2], tri.texCoords[2][0], tri.texCoords[2][1]);
|
// tri.vertIdx[2], tri.texCoords[2][0], tri.texCoords[2][1]);
|
||||||
triangles_.push_back(tri);
|
triangles_.push_back(tri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,22 +174,25 @@ void ObjDecoder::parseQUAD_(IffFile::Form const& form)
|
|||||||
quad.texCoords[j][0] = readU16LE(maps->begin()+i*20+4+j*4);
|
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);
|
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,
|
// 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[0], quad.texCoords[0][0], quad.texCoords[0][1],
|
||||||
quad.vertIdx[1], quad.texCoords[1][0], quad.texCoords[1][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[2], quad.texCoords[2][0], quad.texCoords[2][1],
|
||||||
quad.vertIdx[3], quad.texCoords[3][0], quad.texCoords[3][1]);
|
// quad.vertIdx[3], quad.texCoords[3][0], quad.texCoords[3][1]);
|
||||||
quads_.push_back(quad);
|
quads_.push_back(quad);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjDecoder::parseTXMS_(IffFile::Form const& form)
|
void ObjDecoder::parseTXMS_(IffFile::Form const& form)
|
||||||
{
|
{
|
||||||
|
if ((textures_.size() != 0) || texAnims_.size() != 0)
|
||||||
|
throw FormatException{"Multiple TXMS"};
|
||||||
|
|
||||||
if (form.getChildCount() < 1)
|
if (form.getChildCount() < 1)
|
||||||
throw FormatException{"Unexpected child count in TXMS"};
|
throw FormatException{"Unexpected child count in TXMS"};
|
||||||
|
|
||||||
auto it = form.childrenBegin();
|
auto it = form.childrenBegin();
|
||||||
auto& info = *it;
|
auto& info = *it++;
|
||||||
if (info.getType() != "INFO")
|
if (info.getType() != "INFO")
|
||||||
throw FormatException{"Missing INFO in TXMS"};
|
throw FormatException{"Missing INFO in TXMS"};
|
||||||
if (info.getSize() != 4)
|
if (info.getSize() != 4)
|
||||||
@@ -198,6 +201,59 @@ void ObjDecoder::parseTXMS_(IffFile::Form const& form)
|
|||||||
uint16_t numTXMP, numTXMA;
|
uint16_t numTXMP, numTXMA;
|
||||||
numTXMP = readU16LE(info.begin());
|
numTXMP = readU16LE(info.begin());
|
||||||
numTXMA = readU16LE(info.begin()+2);
|
numTXMA = readU16LE(info.begin()+2);
|
||||||
if (form.getChildCount() != 1+numTXMP+numTXMA)
|
if (form.getChildCount() != 1u+numTXMP+numTXMA)
|
||||||
throw FormatException{"Unexpected child count in TXMS"};
|
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()};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,20 @@ public:
|
|||||||
};
|
};
|
||||||
using Quads = std::vector<Quad>;
|
using Quads = std::vector<Quad>;
|
||||||
|
|
||||||
|
struct Texture {
|
||||||
|
std::string name;
|
||||||
|
uint16_t width, height;
|
||||||
|
std::vector<uint8_t> pixels;
|
||||||
|
};
|
||||||
|
using Textures = std::vector<Texture>;
|
||||||
|
|
||||||
|
struct TextureAnimation {
|
||||||
|
std::string name;
|
||||||
|
uint16_t width, height, frames;
|
||||||
|
std::vector<std::vector<uint8_t> > pixels;
|
||||||
|
};
|
||||||
|
using TextureAnimations = std::vector<TextureAnimation>;
|
||||||
|
|
||||||
Vertices const& getVertices() const {
|
Vertices const& getVertices() const {
|
||||||
return vertices_;
|
return vertices_;
|
||||||
}
|
}
|
||||||
@@ -43,6 +57,14 @@ public:
|
|||||||
Quads const& getQuads() const {
|
Quads const& getQuads() const {
|
||||||
return quads_;
|
return quads_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Textures const& getTextures() const {
|
||||||
|
return textures_;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureAnimations const& getTextureAnimations() const {
|
||||||
|
return texAnims_;
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
IffFile iff_;
|
IffFile iff_;
|
||||||
std::string name_;
|
std::string name_;
|
||||||
@@ -57,6 +79,8 @@ private:
|
|||||||
Vertices vertices_;
|
Vertices vertices_;
|
||||||
Triangles triangles_;
|
Triangles triangles_;
|
||||||
Quads quads_;
|
Quads quads_;
|
||||||
|
Textures textures_;
|
||||||
|
TextureAnimations texAnims_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
25
PaletteDecoder.cc
Normal file
25
PaletteDecoder.cc
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "PaletteDecoder.hh"
|
||||||
|
#include "exceptions.hh"
|
||||||
|
|
||||||
|
PaletteDecoder::PaletteDecoder(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() != "PALT")
|
||||||
|
throw FormatException{"Root form not PALT"};
|
||||||
|
|
||||||
|
for (auto paltIt = rootForm.childrenBegin();paltIt != rootForm.childrenEnd();++paltIt) {
|
||||||
|
if (paltIt->getType() == "INFO") {
|
||||||
|
} else if (paltIt->getType() == "PALT") {
|
||||||
|
std::transform(paltIt->begin()+4, paltIt->begin()+772,
|
||||||
|
palette_.begin(),
|
||||||
|
[](const char& in) -> uint8_t {return (in << 2) | ((in >> 6)&0x3);});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
PaletteDecoder.hh
Normal file
24
PaletteDecoder.hh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef WC3RE_PALETTEDECODER_HH__
|
||||||
|
#define WC3RE_PALETTEDECODER_HH__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "IffFile.hh"
|
||||||
|
|
||||||
|
class PaletteDecoder {
|
||||||
|
public:
|
||||||
|
PaletteDecoder(uint8_t const* base, size_t length);
|
||||||
|
|
||||||
|
using Palette = std::array<uint8_t, 768>;
|
||||||
|
|
||||||
|
Palette const& getPalette() const {
|
||||||
|
return palette_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
IffFile iff_;
|
||||||
|
Palette palette_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -51,9 +51,10 @@ namespace game {
|
|||||||
auto& palette = std::get<1>(dec);
|
auto& palette = std::get<1>(dec);
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (unsigned y = 0;y < movie_.getHeight();++y) {
|
auto height = movie_.getHeight(), width = movie_.getWidth();
|
||||||
for (unsigned x = 0;x < movie_.getWidth();++x) {
|
for (unsigned y = 0;y < height;++y) {
|
||||||
auto idx = frame[y*movie_.getWidth()+x];
|
for (unsigned x = 0;x < width;++x) {
|
||||||
|
auto idx = frame[y*width+x];
|
||||||
std::copy(&palette[idx*3], &palette[idx*3+3],
|
std::copy(&palette[idx*3], &palette[idx*3+3],
|
||||||
&frameRGB_[i]);
|
&frameRGB_[i]);
|
||||||
i += 3;
|
i += 3;
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
#include "render/Object.hh"
|
#include "render/Object.hh"
|
||||||
|
|
||||||
namespace game {
|
namespace game {
|
||||||
GSShowObject::GSShowObject(render::Renderer& renderer, ObjDecoder& obj)
|
GSShowObject::GSShowObject(render::Renderer& renderer, ObjDecoder& obj,
|
||||||
|
PaletteDecoder& palt)
|
||||||
: GameState(renderer), obj_(obj),
|
: GameState(renderer), obj_(obj),
|
||||||
object_(nullptr), delta_(0)
|
object_(nullptr), delta_(0)
|
||||||
{
|
{
|
||||||
object_ = std::make_unique<render::Object>(renderer, obj);
|
object_ = std::make_unique<render::Object>(renderer, obj, palt);
|
||||||
}
|
}
|
||||||
|
|
||||||
GSShowObject::~GSShowObject()
|
GSShowObject::~GSShowObject()
|
||||||
@@ -17,6 +18,9 @@ namespace game {
|
|||||||
|
|
||||||
void GSShowObject::draw(unsigned delta_ms)
|
void GSShowObject::draw(unsigned delta_ms)
|
||||||
{
|
{
|
||||||
|
delta_ += delta_ms;
|
||||||
|
object_->setRot(delta_*0.0005f);
|
||||||
|
object_->setAnimFrame((delta_/125)%8);
|
||||||
object_->draw();
|
object_->draw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "GameState.hh"
|
#include "GameState.hh"
|
||||||
|
|
||||||
class ObjDecoder;
|
class ObjDecoder;
|
||||||
|
class PaletteDecoder;
|
||||||
namespace render {
|
namespace render {
|
||||||
class Object;
|
class Object;
|
||||||
}
|
}
|
||||||
@@ -11,7 +12,8 @@ namespace render {
|
|||||||
namespace game {
|
namespace game {
|
||||||
class GSShowObject : public GameState {
|
class GSShowObject : public GameState {
|
||||||
public:
|
public:
|
||||||
GSShowObject(render::Renderer& renderer, ObjDecoder& obj);
|
GSShowObject(render::Renderer& renderer, ObjDecoder& obj,
|
||||||
|
PaletteDecoder& palt);
|
||||||
|
|
||||||
~GSShowObject() override;
|
~GSShowObject() override;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "TreFile.hh"
|
#include "TreFile.hh"
|
||||||
#include "IffFile.hh"
|
#include "IffFile.hh"
|
||||||
#include "ObjDecoder.hh"
|
#include "ObjDecoder.hh"
|
||||||
|
#include "PaletteDecoder.hh"
|
||||||
#include "render/Renderer.hh"
|
#include "render/Renderer.hh"
|
||||||
#include "game/GSShowObject.hh"
|
#include "game/GSShowObject.hh"
|
||||||
|
|
||||||
@@ -51,6 +52,9 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
MmapFile paltMmap{"tmp/1DC61F50"};
|
||||||
|
PaletteDecoder palt{paltMmap.data(), paltMmap.size()};
|
||||||
|
|
||||||
MmapFile mmap{inFile};
|
MmapFile mmap{inFile};
|
||||||
|
|
||||||
std::unique_ptr<TreFile> tre;
|
std::unique_ptr<TreFile> tre;
|
||||||
@@ -78,7 +82,7 @@ int main(int argc, char *argv[]) {
|
|||||||
obj = std::make_unique<ObjDecoder>(mmap.data(), mmap.size());
|
obj = std::make_unique<ObjDecoder>(mmap.data(), mmap.size());
|
||||||
|
|
||||||
render::Renderer renderer;
|
render::Renderer renderer;
|
||||||
renderer.pushGS(std::make_unique<game::GSShowObject>(renderer, *obj));
|
renderer.pushGS(std::make_unique<game::GSShowObject>(renderer, *obj, palt));
|
||||||
|
|
||||||
renderer.run();
|
renderer.run();
|
||||||
} catch (POSIXException &ex) {
|
} catch (POSIXException &ex) {
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ namespace render {
|
|||||||
Deleter()(handle_);
|
Deleter()(handle_);
|
||||||
handle_ = move.handle_;
|
handle_ = move.handle_;
|
||||||
move.handle_ = Handle();
|
move.handle_ = Handle();
|
||||||
|
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
~GlResource() {
|
~GlResource() {
|
||||||
|
|||||||
420
render/Object.cc
420
render/Object.cc
@@ -2,11 +2,14 @@
|
|||||||
|
|
||||||
#include <glm/gtx/transform.hpp>
|
#include <glm/gtx/transform.hpp>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
#include <glm/gtc/packing.hpp>
|
||||||
|
|
||||||
#include "Object.hh"
|
#include "Object.hh"
|
||||||
#include "ProgramProvider.hh"
|
#include "ProgramProvider.hh"
|
||||||
#include "Renderer.hh"
|
#include "Renderer.hh"
|
||||||
#include "ObjDecoder.hh"
|
#include "ObjDecoder.hh"
|
||||||
|
#include "PaletteDecoder.hh"
|
||||||
|
#include "renderutil.hh"
|
||||||
|
|
||||||
using namespace gl;
|
using namespace gl;
|
||||||
|
|
||||||
@@ -15,93 +18,390 @@ namespace render {
|
|||||||
struct VertexAttribs {
|
struct VertexAttribs {
|
||||||
int32_t vertex[3];
|
int32_t vertex[3];
|
||||||
uint16_t texCoords[2];
|
uint16_t texCoords[2];
|
||||||
} __attribute__((__packed__));
|
uint8_t useTexAnim;
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
bool operator<(VertexAttribs const& other) const {
|
||||||
for (auto& tri : tris) {
|
if (vertex[0] == other.vertex[0]) {
|
||||||
for (unsigned i = 0;i < 3;++i) {
|
if (vertex[1] == other.vertex[1]) {
|
||||||
VertexAttribs attrs;
|
if (vertex[2] == other.vertex[2]) {
|
||||||
std::copy(vertices.at(tri.vertIdx[i]).begin(), vertices.at(tri.vertIdx[i]).end(),
|
if (texCoords[0] == other.texCoords[0]) {
|
||||||
attrs.vertex);
|
if (texCoords[1] == other.texCoords[1])
|
||||||
std::copy(tri.texCoords[i].begin(), tri.texCoords[i].end(),
|
return (useTexAnim < other.useTexAnim);
|
||||||
attrs.texCoords);
|
return (texCoords[1] < other.texCoords[1]);
|
||||||
|
}
|
||||||
vertexAttribs.push_back(attrs);
|
return (texCoords[0] < other.texCoords[0]);
|
||||||
|
}
|
||||||
|
return (vertex[2] < other.vertex[2]);
|
||||||
|
}
|
||||||
|
return (vertex[1] < other.vertex[1]);
|
||||||
|
}
|
||||||
|
return (vertex[0] < other.vertex[0]);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::array<uint32_t, 256> vgaPalette = {
|
||||||
|
0x000000, 0x0000a8, 0x00a800, 0x00a8a8, 0xa80000, 0xa800a8, 0xa85400, 0xa8a8a8,
|
||||||
|
0x545454, 0x5454fc, 0x54fc54, 0x54fcfc, 0xfc5454, 0xfc54fc, 0xfcfc54, 0xfcfcfc,
|
||||||
|
0x000000, 0x141414, 0x202020, 0x2c2c2c, 0x383838, 0x444444, 0x505050, 0x606060,
|
||||||
|
0x707070, 0x808080, 0x909090, 0xa0a0a0, 0xb4b4b4, 0xc8c8c8, 0xe0e0e0, 0xfcfcfc,
|
||||||
|
0x0000fc, 0x4000fc, 0x7c00fc, 0xbc00fc, 0xfc00fc, 0xfc00bc, 0xfc007c, 0xfc0040,
|
||||||
|
0xfc0000, 0xfc4000, 0xfc7c00, 0xfcbc00, 0xfcfc00, 0xbcfc00, 0x7cfc00, 0x40fc00,
|
||||||
|
0x00fc00, 0x00fc40, 0x00fc7c, 0x00fcbc, 0x00fcfc, 0x00bcfc, 0x007cfc, 0x0040fc,
|
||||||
|
0x7c7cfc, 0x9c7cfc, 0xbc7cfc, 0xdc7cfc, 0xfc7cfc, 0xfc7cdc, 0xfc7cbc, 0xfc7c9c,
|
||||||
|
0xfc7c7c, 0xfc9c7c, 0xfcbc7c, 0xfcdc7c, 0xfcfc7c, 0xdcfc7c, 0xbcfc7c, 0x9cfc7c,
|
||||||
|
0x7cfc7c, 0x7cfc9c, 0x7cfcbc, 0x7cfcdc, 0x7cfcfc, 0x7cdcfc, 0x7cbcfc, 0x7c9cfc,
|
||||||
|
0xb4b4fc, 0xc4b4fc, 0xd8b4fc, 0xe8b4fc, 0xfcb4fc, 0xfcb4e8, 0xfcb4d8, 0xfcb4c4,
|
||||||
|
0xfcb4b4, 0xfcc4b4, 0xfcd8b4, 0xfce8b4, 0xfcfcb4, 0xe8fcb4, 0xd8fcb4, 0xc4fcb4,
|
||||||
|
0xb4fcb4, 0xb4fcc4, 0xb4fcd8, 0xb4fce8, 0xb4fcfc, 0xb4e8fc, 0xb4d8fc, 0xb4c4fc,
|
||||||
|
0x000070, 0x1c0070, 0x380070, 0x540070, 0x700070, 0x700054, 0x700038, 0x70001c,
|
||||||
|
0x700000, 0x701c00, 0x703800, 0x705400, 0x707000, 0x547000, 0x387000, 0x1c7000,
|
||||||
|
0x007000, 0x00701c, 0x007038, 0x007054, 0x007070, 0x005470, 0x003870, 0x001c70,
|
||||||
|
0x383870, 0x443870, 0x543870, 0x603870, 0x703870, 0x703860, 0x703854, 0x703844,
|
||||||
|
0x703838, 0x704438, 0x705438, 0x706038, 0x707038, 0x607038, 0x547038, 0x447038,
|
||||||
|
0x387038, 0x387044, 0x387054, 0x387060, 0x387070, 0x386070, 0x385470, 0x384470,
|
||||||
|
0x505070, 0x585070, 0x605070, 0x685070, 0x705070, 0x705068, 0x705060, 0x705058,
|
||||||
|
0x705050, 0x705850, 0x706050, 0x706850, 0x707050, 0x687050, 0x607050, 0x587050,
|
||||||
|
0x507050, 0x507058, 0x507060, 0x507068, 0x507070, 0x506870, 0x506070, 0x505870,
|
||||||
|
0x000040, 0x100040, 0x200040, 0x300040, 0x400040, 0x400030, 0x400020, 0x400010,
|
||||||
|
0x400000, 0x401000, 0x402000, 0x403000, 0x404000, 0x304000, 0x204000, 0x104000,
|
||||||
|
0x004000, 0x004010, 0x004020, 0x004030, 0x004040, 0x003040, 0x002040, 0x001040,
|
||||||
|
0x202040, 0x282040, 0x302040, 0x382040, 0x402040, 0x402038, 0x402030, 0x402028,
|
||||||
|
0x402020, 0x402820, 0x403020, 0x403820, 0x404020, 0x384020, 0x304020, 0x284020,
|
||||||
|
0x204020, 0x204028, 0x204030, 0x204038, 0x204040, 0x203840, 0x203040, 0x202840,
|
||||||
|
0x2c2c40, 0x302c40, 0x342c40, 0x3c2c40, 0x402c40, 0x402c3c, 0x402c34, 0x402c30,
|
||||||
|
0x402c2c, 0x40302c, 0x40342c, 0x403c2c, 0x40402c, 0x3c402c, 0x34402c, 0x30402c,
|
||||||
|
0x2c402c, 0x2c4030, 0x2c4034, 0x2c403c, 0x2c4040, 0x2c3c40, 0x2c3440, 0x2c3040,
|
||||||
|
0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000};
|
||||||
|
|
||||||
|
struct TexAtlasInfo {
|
||||||
|
unsigned x, y;
|
||||||
|
unsigned alignWidth, alignHeight;
|
||||||
|
float xofs, yofs, xscale, yscale;
|
||||||
|
ObjDecoder::Texture const& tex;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::tuple<std::vector<TexAtlasInfo>, TextureResource>
|
||||||
|
genTexAtlas(ObjDecoder::Textures const& texs,
|
||||||
|
PaletteDecoder::Palette const& palt)
|
||||||
|
{
|
||||||
|
// Build texture atlas for object
|
||||||
|
unsigned maxTex, maxLayers;
|
||||||
|
std::tie(maxTex, maxLayers) = getGLTextureMaximums();
|
||||||
|
printf("Maximum texture size: %d, maximum array texture layers: %d\n", maxTex, maxLayers);
|
||||||
|
|
||||||
|
|
||||||
|
unsigned minSize = std::numeric_limits<unsigned>::max();
|
||||||
|
for (auto& tex : texs) {
|
||||||
|
minSize = std::min<unsigned>(minSize, std::min<unsigned>(tex.width, tex.height));
|
||||||
|
}
|
||||||
|
unsigned minLg = ilog2(minSize), minPow2 = (1<<minLg);
|
||||||
|
if (minPow2 < minSize) {
|
||||||
|
++minLg;
|
||||||
|
minPow2 <<= 1;
|
||||||
|
}
|
||||||
|
printf("Minimum texture size %u, using %u alignment and %u mipmap levels\n",
|
||||||
|
minSize, minPow2, minLg);
|
||||||
|
|
||||||
|
std::vector<TexAtlasInfo> ret;
|
||||||
|
unsigned xpos = 0, ypos = 0, lineMaxHeight = 0;
|
||||||
|
for (auto& tex : texs) {
|
||||||
|
unsigned alignWidth = (tex.width%minPow2 == 0)?tex.width:tex.width+(minPow2-tex.width%minPow2);
|
||||||
|
unsigned alignHeight = (tex.height%minPow2 == 0)?tex.height:tex.height+(minPow2-tex.height%minPow2);
|
||||||
|
|
||||||
|
if (xpos + alignWidth > static_cast<unsigned>(maxTex)) {
|
||||||
|
ypos += lineMaxHeight;
|
||||||
|
lineMaxHeight = 0;
|
||||||
|
xpos = 0;
|
||||||
|
}
|
||||||
|
if (ypos + alignHeight > static_cast<unsigned>(maxTex))
|
||||||
|
throw Exception{"Cannot fit obj textures into GL texture"};
|
||||||
|
|
||||||
|
TexAtlasInfo info = {xpos, ypos, alignWidth, alignHeight, 0.0f, 0.0f, 0.0f, 0.0f, tex};
|
||||||
|
ret.push_back(info);
|
||||||
|
|
||||||
|
lineMaxHeight = std::max<unsigned>(lineMaxHeight, alignHeight);
|
||||||
|
xpos += alignWidth;
|
||||||
|
}
|
||||||
|
unsigned atlasWidth = xpos, atlasHeight = ypos+lineMaxHeight;
|
||||||
|
printf("Texture atlas size: %ux%u\n", atlasWidth, atlasHeight);
|
||||||
|
|
||||||
|
TextureResource tex = create2DTexture(atlasWidth, atlasHeight, false, minLg+1);
|
||||||
|
|
||||||
|
std::vector<uint8_t> pixels(atlasWidth*atlasHeight*3);
|
||||||
|
// Copy textures into atlas
|
||||||
|
for (auto& info : ret) {
|
||||||
|
info.xofs = (info.x+0.5f)/atlasWidth;
|
||||||
|
info.yofs = (info.y+0.5f)/atlasHeight;
|
||||||
|
info.xscale = (info.tex.width-1.0f)/(atlasWidth*info.tex.width);
|
||||||
|
info.yscale = (info.tex.height-1.0f)/(atlasHeight*info.tex.height);
|
||||||
|
printf("Texture coordinate factors: %f, %f; %f, %f\n",
|
||||||
|
info.xofs, info.yofs, info.xscale, info.yscale);
|
||||||
|
|
||||||
|
for (unsigned y = 0;y < info.alignHeight;++y) {
|
||||||
|
for (unsigned x = 0;x < info.alignWidth;++x) {
|
||||||
|
unsigned col;
|
||||||
|
if (y < info.tex.height) {
|
||||||
|
if (x < info.tex.width) {
|
||||||
|
col = info.tex.pixels[y*info.tex.width + x];
|
||||||
|
} else {
|
||||||
|
col = info.tex.pixels[y*info.tex.width + info.tex.width-1];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (x < info.tex.width) {
|
||||||
|
col = info.tex.pixels[(info.tex.height-1)*info.tex.width + x];
|
||||||
|
} else {
|
||||||
|
col = info.tex.pixels[info.tex.height*info.tex.width - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pixels[(y+info.y)*atlasWidth*3+(info.x+x)*3] = palt[col*3]; //vgaPalette[col]>>16;
|
||||||
|
pixels[(y+info.y)*atlasWidth*3+(info.x+x)*3+1] = palt[col*3+1]; //vgaPalette[col]>>8;
|
||||||
|
pixels[(y+info.y)*atlasWidth*3+(info.x+x)*3+2] = palt[col*3+2]; //vgaPalette[col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, atlasWidth, atlasHeight, GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
return std::make_tuple(std::move(ret), std::move(tex));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& quad : quads) {
|
struct AnimTexInfo {
|
||||||
for (unsigned i = 0;i < 3;++i) {
|
float xofs, yofs, xscale, yscale;
|
||||||
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 = {
|
std::tuple<AnimTexInfo, TextureResource>
|
||||||
// {{-1500, -1500, 0}, {0, 0}},
|
genTexAnim(ObjDecoder::TextureAnimation const& texAnim,
|
||||||
// {{-1500, 1500, 0}, {0, 0}},
|
PaletteDecoder::Palette const& palt)
|
||||||
// {{1500, -1500, 0}, {0, 0}},
|
{
|
||||||
// {{1500, 1500, 0}, {0, 0}}};
|
unsigned width = texAnim.width;
|
||||||
|
if (width%4 != 0)
|
||||||
|
width += 4 - (width%4);
|
||||||
|
unsigned height = texAnim.height;
|
||||||
|
TextureResource tex = create2DArrayTexture(width, height, texAnim.frames, false);
|
||||||
|
|
||||||
|
AnimTexInfo animInfo;
|
||||||
|
animInfo.xofs = 0.5f/width;
|
||||||
|
animInfo.yofs = 0.5f/height;
|
||||||
|
animInfo.xscale = (texAnim.width-1.0f)/(width*texAnim.width);
|
||||||
|
animInfo.yscale = (texAnim.height-1.0f)/(height*texAnim.height);
|
||||||
|
|
||||||
|
std::vector<uint8_t> pixels(texAnim.frames*width*height*3);
|
||||||
|
|
||||||
|
for (unsigned f = 0;f < texAnim.frames;++f) {
|
||||||
|
for (unsigned y = 0;y < texAnim.height;++y) {
|
||||||
|
for (unsigned x = 0;x < texAnim.width;++x) {
|
||||||
|
unsigned col = texAnim.pixels[f][y*texAnim.width+x];
|
||||||
|
|
||||||
|
pixels[f*width*height*3+y*width*3+x*3] = palt[col*3];
|
||||||
|
pixels[f*width*height*3+y*width*3+x*3+1] = palt[col*3+1];
|
||||||
|
pixels[f*width*height*3+y*width*3+x*3+2] = palt[col*3+2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, texAnim.frames,
|
||||||
|
GL_RGB, GL_UNSIGNED_BYTE, pixels.data());
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||||
|
|
||||||
|
return std::make_tuple(animInfo, std::move(tex));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<std::vector<VertexAttribs>, std::vector<uint16_t> >
|
||||||
|
genVertexAttribs(ObjDecoder& obj, std::vector<TexAtlasInfo>& atlasInfo,
|
||||||
|
AnimTexInfo *animTex = nullptr)
|
||||||
|
{
|
||||||
|
auto& tris = obj.getTriangles();
|
||||||
|
auto& quads = obj.getQuads();
|
||||||
|
|
||||||
|
// Deduplicate vertex attributes, track indices
|
||||||
|
std::map<VertexAttribs, size_t> vertexAttribsMap;
|
||||||
|
std::vector<VertexAttribs> vertexAttribs;
|
||||||
|
std::vector<uint16_t> indices;
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (tri.tex < atlasInfo.size()) {
|
||||||
|
auto& tex = atlasInfo[tri.tex];
|
||||||
|
attrs.texCoords[0] = glm::packUnorm1x16(tex.xofs + tri.texCoords[i][0]*tex.xscale);
|
||||||
|
attrs.texCoords[1] = glm::packUnorm1x16(tex.yofs + tri.texCoords[i][1]*tex.yscale);
|
||||||
|
attrs.useTexAnim = 0u;
|
||||||
|
} else if ((tri.tex == atlasInfo.size()) && animTex) {
|
||||||
|
attrs.texCoords[0] = glm::packUnorm1x16(animTex->xofs + tri.texCoords[i][0]*animTex->xscale);
|
||||||
|
attrs.texCoords[1] = glm::packUnorm1x16(animTex->yofs + tri.texCoords[i][1]*animTex->yscale);
|
||||||
|
attrs.useTexAnim = 1u;
|
||||||
|
} else
|
||||||
|
throw Exception{"Texture index out of range"};
|
||||||
|
|
||||||
|
auto ins = vertexAttribsMap.insert(std::make_pair(attrs, vertexAttribs.size()));
|
||||||
|
if (ins.second) {
|
||||||
|
indices.push_back(vertexAttribs.size());
|
||||||
|
vertexAttribs.push_back(attrs);
|
||||||
|
} else
|
||||||
|
indices.push_back(ins.first->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
if (quad.tex < atlasInfo.size()) {
|
||||||
|
auto& tex = atlasInfo[quad.tex];
|
||||||
|
attrs.texCoords[0] = glm::packUnorm1x16(tex.xofs + quad.texCoords[i][0]*tex.xscale);
|
||||||
|
attrs.texCoords[1] = glm::packUnorm1x16(tex.yofs + quad.texCoords[i][1]*tex.yscale);
|
||||||
|
attrs.useTexAnim = 0u;
|
||||||
|
} else if ((quad.tex == atlasInfo.size()) && animTex) {
|
||||||
|
attrs.texCoords[0] = glm::packUnorm1x16(animTex->xofs + quad.texCoords[i][0]*animTex->xscale);
|
||||||
|
attrs.texCoords[1] = glm::packUnorm1x16(animTex->yofs + quad.texCoords[i][1]*animTex->yscale);
|
||||||
|
attrs.useTexAnim = 1u;
|
||||||
|
} else
|
||||||
|
throw Exception{"Texture index out of range"};
|
||||||
|
|
||||||
|
auto ins = vertexAttribsMap.insert(std::make_pair(attrs, vertexAttribs.size()));
|
||||||
|
if (ins.second) {
|
||||||
|
indices.push_back(vertexAttribs.size());
|
||||||
|
vertexAttribs.push_back(attrs);
|
||||||
|
} else
|
||||||
|
indices.push_back(ins.first->second);
|
||||||
|
}
|
||||||
|
for (unsigned i = 0;i < 3;++i) {
|
||||||
|
VertexAttribs attrs;
|
||||||
|
std::copy(vertices.at(quad.vertIdx[i?(i+1):i]).begin(), vertices.at(quad.vertIdx[i?(i+1):i]).end(),
|
||||||
|
attrs.vertex);
|
||||||
|
if (quad.tex < atlasInfo.size()) {
|
||||||
|
auto& tex = atlasInfo[quad.tex];
|
||||||
|
attrs.texCoords[0] = glm::packUnorm1x16(tex.xofs + quad.texCoords[i?(i+1):i][0]*tex.xscale);
|
||||||
|
attrs.texCoords[1] = glm::packUnorm1x16(tex.yofs + quad.texCoords[i?(i+1):i][1]*tex.yscale);
|
||||||
|
attrs.useTexAnim = 0u;
|
||||||
|
} else if ((quad.tex == atlasInfo.size()) && animTex) {
|
||||||
|
attrs.texCoords[0] = glm::packUnorm1x16(animTex->xofs + quad.texCoords[i?(i+1):i][0]*animTex->xscale);
|
||||||
|
attrs.texCoords[1] = glm::packUnorm1x16(animTex->yofs + quad.texCoords[i?(i+1):i][1]*animTex->yscale);
|
||||||
|
attrs.useTexAnim = 1u;
|
||||||
|
} else
|
||||||
|
throw Exception{"Texture index out of range"};
|
||||||
|
|
||||||
|
auto ins = vertexAttribsMap.insert(std::make_pair(attrs, vertexAttribs.size()));
|
||||||
|
if (ins.second) {
|
||||||
|
indices.push_back(vertexAttribs.size());
|
||||||
|
vertexAttribs.push_back(attrs);
|
||||||
|
} else
|
||||||
|
indices.push_back(ins.first->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%lu indices, %lu attributes\n",
|
||||||
|
indices.size(), vertexAttribs.size());
|
||||||
|
|
||||||
|
return std::make_tuple(std::move(vertexAttribs), std::move(indices));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Object::Object(Renderer& renderer, ObjDecoder& obj, PaletteDecoder& palt)
|
||||||
|
: Drawable(renderer),
|
||||||
|
vbo_(), rot_(0.0f), animFrame_(0)
|
||||||
|
{
|
||||||
|
// Acquire shader
|
||||||
|
program_ = ProgramProvider::getInstance().getProgram("object", "object");
|
||||||
|
|
||||||
|
// Generate texture atlas
|
||||||
|
auto& texs = obj.getTextures();
|
||||||
|
std::vector<TexAtlasInfo> atlasInfo;
|
||||||
|
if (texs.size() > 0) {
|
||||||
|
std::tie(atlasInfo, tex_) = genTexAtlas(texs, palt.getPalette());
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast<int>(GL_LINEAR_MIPMAP_LINEAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& texAnims = obj.getTextureAnimations();
|
||||||
|
AnimTexInfo animInfo;
|
||||||
|
if (texAnims.size() > 0) {
|
||||||
|
std::tie(animInfo, texAnim_) = genTexAnim(texAnims[0], palt.getPalette());
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, static_cast<int>(GL_LINEAR_MIPMAP_LINEAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate vertex attribute array
|
||||||
|
std::vector<VertexAttribs> vertexAttribs;
|
||||||
|
std::vector<uint16_t> indices;
|
||||||
|
if (texAnims.size() > 0)
|
||||||
|
std::tie(vertexAttribs, indices) = genVertexAttribs(obj, atlasInfo, &animInfo);
|
||||||
|
else
|
||||||
|
std::tie(vertexAttribs, indices) = genVertexAttribs(obj, atlasInfo);
|
||||||
|
|
||||||
|
// Setup GL vertex buffer and vertex array
|
||||||
|
glGenVertexArrays(1, &vertexArray_.get());
|
||||||
|
vbo_ = VBOManager::getInstance().alloc(sizeof(VertexAttribs)*vertexAttribs.size()+
|
||||||
|
2*indices.size());
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId());
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_.getVBOId());
|
||||||
glBindVertexArray(vertexArray_);
|
glBindVertexArray(vertexArray_);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo_.getVBOId());
|
||||||
|
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
glVertexAttribPointer(0, 3, GL_INT, GL_FALSE, sizeof(VertexAttribs),
|
glVertexAttribPointer(0, 3, GL_INT, GL_FALSE, sizeof(VertexAttribs),
|
||||||
vbo_.getOfs(offsetof(VertexAttribs, vertex)));
|
vbo_.getOfs(offsetof(VertexAttribs, vertex)));
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(1, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(VertexAttribs),
|
||||||
|
vbo_.getOfs(offsetof(VertexAttribs, texCoords)));
|
||||||
|
glEnableVertexAttribArray(2);
|
||||||
|
glVertexAttribIPointer(2, 1, GL_UNSIGNED_BYTE, sizeof(VertexAttribs),
|
||||||
|
vbo_.getOfs(offsetof(VertexAttribs, useTexAnim)));
|
||||||
|
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, vbo_.getBase(),
|
glBufferSubData(GL_ARRAY_BUFFER, vbo_.getBase(),
|
||||||
sizeof(VertexAttribs)*numVertices_,
|
sizeof(VertexAttribs)*vertexAttribs.size(),
|
||||||
vertexAttribs.data());
|
vertexAttribs.data());
|
||||||
|
|
||||||
|
maxIndex_ = vertexAttribs.size()-1;
|
||||||
|
indexOfs_ = sizeof(VertexAttribs)*vertexAttribs.size();
|
||||||
|
numIndices_ = indices.size();
|
||||||
|
|
||||||
|
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, vbo_.getBase()+indexOfs_,
|
||||||
|
2*numIndices_,
|
||||||
|
indices.data());
|
||||||
|
|
||||||
mvp_ = glm::perspectiveFov(75.0f, static_cast<float>(renderer.getWidth()),
|
mvp_ = glm::perspectiveFov(75.0f, static_cast<float>(renderer.getWidth()),
|
||||||
static_cast<float>(renderer.getHeight()),
|
static_cast<float>(renderer.getHeight()),
|
||||||
100.f, 20000.0f) *
|
1000.f, 800000.0f) *
|
||||||
glm::lookAt(glm::vec3(0.0f, 0.0f, 10000),
|
glm::lookAt(glm::vec3(0.0f, 0.0f, 200000),
|
||||||
glm::vec3(0.0f, 0.0f, 0),
|
glm::vec3(0.0f, 0.0f, 0),
|
||||||
glm::vec3(0, 1, 0));
|
glm::vec3(0, 1, 0));
|
||||||
|
|
||||||
|
int maxVertices, maxIndices;
|
||||||
|
glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices);
|
||||||
|
glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &maxVertices);
|
||||||
|
|
||||||
|
printf("Max recommened vertices: %d, indices: %d\n",
|
||||||
|
maxVertices, maxIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Object::draw()
|
void Object::draw()
|
||||||
{
|
{
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
|
//glFrontFace(GL_CW);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||||
glUseProgram(program_);
|
useProgram(program_);
|
||||||
glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(mvp_));
|
glUniformMatrix4fv(0, 1, GL_FALSE,
|
||||||
glBindVertexArray(vertexArray_);
|
glm::value_ptr(mvp_*glm::rotate(rot_, glm::vec3(1.0f, 0.0f, 0.0f))));
|
||||||
|
glUniform1i(1, 0);
|
||||||
|
glUniform1i(2, 1);
|
||||||
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, numVertices_);
|
glBindTexture(GL_TEXTURE_2D, tex_);
|
||||||
|
if (texAnim_) {
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, texAnim_);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glUniform1ui(3, animFrame_);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(vertexArray_);
|
||||||
|
|
||||||
|
glDrawRangeElements(GL_TRIANGLES, 0, maxIndex_,
|
||||||
|
numIndices_, GL_UNSIGNED_SHORT, reinterpret_cast<void*>(indexOfs_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,20 +9,33 @@
|
|||||||
#include "VBOManager.hh"
|
#include "VBOManager.hh"
|
||||||
|
|
||||||
class ObjDecoder;
|
class ObjDecoder;
|
||||||
|
class PaletteDecoder;
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
class Object : public Drawable {
|
class Object : public Drawable {
|
||||||
public:
|
public:
|
||||||
Object(Renderer& renderer, ObjDecoder& obj);
|
Object(Renderer& renderer, ObjDecoder& obj, PaletteDecoder& palt);
|
||||||
|
|
||||||
|
void setRot(float rot) {
|
||||||
|
rot_ = rot;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAnimFrame(unsigned frame) {
|
||||||
|
animFrame_ = frame;
|
||||||
|
}
|
||||||
|
|
||||||
void draw() override;
|
void draw() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VertexArrayResource vertexArray_;
|
VertexArrayResource vertexArray_;
|
||||||
gl::GLuint program_;
|
gl::GLuint program_;
|
||||||
VBOManager::VBOAlloc vbo_;
|
VBOManager::VBOAlloc vbo_;
|
||||||
|
TextureResource tex_, texAnim_;
|
||||||
glm::mat4 mvp_;
|
glm::mat4 mvp_;
|
||||||
unsigned numVertices_;
|
uintptr_t indexOfs_;
|
||||||
|
size_t numIndices_, maxIndex_;
|
||||||
|
float rot_;
|
||||||
|
unsigned animFrame_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace render {
|
|||||||
void Overlay::draw()
|
void Overlay::draw()
|
||||||
{
|
{
|
||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
glUseProgram(program_);
|
useProgram(program_);
|
||||||
glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(ovlProj_));
|
glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(ovlProj_));
|
||||||
glUniform1i(1, 0);
|
glUniform1i(1, 0);
|
||||||
|
|
||||||
|
|||||||
@@ -2,19 +2,43 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "renderutil.hh"
|
#include "renderutil.hh"
|
||||||
|
#include "exceptions.hh"
|
||||||
|
|
||||||
using namespace gl;
|
using namespace gl;
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
namespace {
|
unsigned ilog2(unsigned in)
|
||||||
unsigned ilog2(unsigned in)
|
{
|
||||||
{
|
unsigned ret = 0u;
|
||||||
unsigned ret = 0u;
|
while (in >>= 1) ++ret;
|
||||||
while (in >>= 1) ++ret;
|
return ret;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::tuple<unsigned, unsigned> getGLTextureMaximums()
|
||||||
|
{
|
||||||
|
static int maxTexSize = -1, maxArrTexLayers = -1;
|
||||||
|
|
||||||
|
if (maxTexSize < 0)
|
||||||
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
|
||||||
|
if (maxArrTexLayers < 0)
|
||||||
|
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrTexLayers);
|
||||||
|
|
||||||
|
if ((maxTexSize < 0) || (maxArrTexLayers < 0))
|
||||||
|
throw Exception{"GL returned negative values for max tex. dimensions"};
|
||||||
|
|
||||||
|
return std::make_tuple<unsigned, unsigned>(maxTexSize, maxArrTexLayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void useProgram(GLuint program)
|
||||||
|
{
|
||||||
|
static GLuint curProgram = 0;
|
||||||
|
|
||||||
|
if (curProgram != program) {
|
||||||
|
glUseProgram(program);
|
||||||
|
curProgram = program;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TextureResource create2DTexture(unsigned width, unsigned height, bool alpha, unsigned levels)
|
TextureResource create2DTexture(unsigned width, unsigned height, bool alpha, unsigned levels)
|
||||||
{
|
{
|
||||||
TextureResource tex;
|
TextureResource tex;
|
||||||
@@ -29,14 +53,41 @@ namespace render {
|
|||||||
else {
|
else {
|
||||||
std::printf("Warning: extension GL_ARB_texture_storage not supported!\n");
|
std::printf("Warning: extension GL_ARB_texture_storage not supported!\n");
|
||||||
for (unsigned i = 0u; i < levels; ++i) {
|
for (unsigned i = 0u; i < levels; ++i) {
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels-1);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, i, static_cast<const int>(alpha?GL_RGBA8:GL_RGB8), width, height, 0,
|
glTexImage2D(GL_TEXTURE_2D, i, static_cast<const int>(alpha?GL_RGBA8:GL_RGB8), width, height, 0,
|
||||||
alpha?GL_RGBA8:GL_RGB8, GL_UNSIGNED_BYTE, NULL);
|
alpha?GL_RGBA8:GL_RGB8, GL_UNSIGNED_BYTE, NULL);
|
||||||
width = std::max(1u, (width / 2u));
|
width = std::max(1u, (width / 2u));
|
||||||
height = std::max(1u, (height / 2u));
|
height = std::max(1u, (height / 2u));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels-1);
|
||||||
|
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureResource create2DArrayTexture(unsigned width, unsigned height, unsigned count,
|
||||||
|
bool alpha, unsigned levels)
|
||||||
|
{
|
||||||
|
TextureResource tex;
|
||||||
|
glGenTextures(1, &tex.get());
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
|
||||||
|
unsigned logWidth = ilog2(width), logHeight = ilog2(height);
|
||||||
|
if (levels == 0)
|
||||||
|
levels = std::max(logWidth,logHeight)+1u;
|
||||||
|
if(SDL_GL_ExtensionSupported("GL_ARB_texture_storage"))
|
||||||
|
glTexStorage3D(GL_TEXTURE_2D_ARRAY, levels, alpha?GL_RGBA8:GL_RGB8, width, height, count);
|
||||||
|
else {
|
||||||
|
std::printf("Warning: extension GL_ARB_texture_storage not supported!\n");
|
||||||
|
for (unsigned i = 0u; i < levels; ++i) {
|
||||||
|
glTexImage3D(GL_TEXTURE_2D_ARRAY, i, static_cast<const int>(alpha?GL_RGBA8:GL_RGB8),
|
||||||
|
width, height, count,
|
||||||
|
0, alpha?GL_RGBA8:GL_RGB8, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
width = std::max(1u, (width / 2u));
|
||||||
|
height = std::max(1u, (height / 2u));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, levels-1);
|
||||||
|
|
||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,17 @@
|
|||||||
#include "GlResource.hh"
|
#include "GlResource.hh"
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
|
unsigned ilog2(unsigned in);
|
||||||
|
|
||||||
|
// Return (GL_MAX_TEXTURE_SIZE, GL_MAX_ARRAY_TEXTURE_LAYERS)
|
||||||
|
std::tuple<unsigned, unsigned> getGLTextureMaximums();
|
||||||
|
|
||||||
|
// call glUseProgram if program != current program
|
||||||
|
void useProgram(gl::GLuint program);
|
||||||
|
|
||||||
TextureResource create2DTexture(unsigned width, unsigned height, bool alpha, unsigned levels = 0);
|
TextureResource create2DTexture(unsigned width, unsigned height, bool alpha, unsigned levels = 0);
|
||||||
|
TextureResource create2DArrayTexture(unsigned width, unsigned height, unsigned count,
|
||||||
|
bool alpha, unsigned levels = 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
#extension GL_ARB_explicit_uniform_location : enable
|
#extension GL_ARB_explicit_uniform_location : enable
|
||||||
|
|
||||||
|
layout(location = 1) uniform sampler2D texBase;
|
||||||
|
layout(location = 2) uniform sampler2DArray texAnim;
|
||||||
|
layout(location = 3) uniform uint animFrame;
|
||||||
|
|
||||||
|
in vec2 fragTC;
|
||||||
|
flat in uint fragUseAnimTex;
|
||||||
|
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
color = vec4(1.0, 1.0, 1.0, 1.0);
|
vec2 texDx = dFdx(fragTC);
|
||||||
}
|
vec2 texDy = dFdy(fragTC);
|
||||||
|
if (fragUseAnimTex > 0u)
|
||||||
|
color = textureGrad(texAnim, vec3(fragTC, animFrame), texDx, texDy);
|
||||||
|
//color = vec4(1.0, 1.0, 0.0, 1.0);
|
||||||
|
else
|
||||||
|
color = textureGrad(texBase, fragTC, texDx, texDy);
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ layout(location = 0) uniform mat4 mvp_matrix;
|
|||||||
|
|
||||||
layout(location = 0) in vec3 vertex;
|
layout(location = 0) in vec3 vertex;
|
||||||
layout(location = 1) in vec2 vertexTC;
|
layout(location = 1) in vec2 vertexTC;
|
||||||
|
layout(location = 2) in uint useAnimTex;
|
||||||
|
|
||||||
out vec2 fragTC;
|
out vec2 fragTC;
|
||||||
|
flat out uint fragUseAnimTex;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
gl_Position = mvp_matrix * vec4(vertex, 1.0);
|
gl_Position = mvp_matrix * vec4(vertex, 1.0);
|
||||||
|
|
||||||
fragTC = vertexTC;
|
fragTC = vertexTC;
|
||||||
|
fragUseAnimTex = useAnimTex;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user