2013-12-20 15:03:32 +01:00
|
|
|
#include <loaders/TextureLoader.hpp>
|
2013-12-20 17:02:46 +01:00
|
|
|
#include <engine/GameData.hpp>
|
2013-12-20 15:03:32 +01:00
|
|
|
#include <render/TextureAtlas.hpp>
|
2013-07-02 08:06:03 +02:00
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
#include <iostream>
|
2013-12-20 16:45:41 +01:00
|
|
|
#include <algorithm>
|
2013-07-02 08:06:03 +02:00
|
|
|
|
2013-12-20 17:02:46 +01:00
|
|
|
bool TextureLoader::loadFromFile(std::string filename, GameData* gameData)
|
2013-07-02 08:06:03 +02:00
|
|
|
{
|
|
|
|
std::ifstream dfile(filename);
|
|
|
|
if ( ! dfile.is_open()) {
|
|
|
|
std::cerr << "Error opening file " << filename << std::endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
dfile.seekg(0, std::ios_base::end);
|
|
|
|
size_t length = dfile.tellg();
|
|
|
|
dfile.seekg(0);
|
|
|
|
char *data = new char[length];
|
|
|
|
dfile.read(data, length);
|
|
|
|
|
2013-09-11 20:23:31 +02:00
|
|
|
return loadFromMemory(data, gameData);
|
2013-07-02 08:06:03 +02:00
|
|
|
}
|
|
|
|
|
2013-12-20 16:45:41 +01:00
|
|
|
GLuint gErrorTextureData[] = { 0xFFFF00FF, 0xFF000000, 0xFF000000, 0xFFFF00FF };
|
2013-12-20 14:43:12 +01:00
|
|
|
GLuint gDebugTextureData[] = {0xFF0000FF, 0xFF00FF00};
|
2013-12-20 16:45:41 +01:00
|
|
|
GLuint gTextureRed[] = {0xFF0000FF};
|
|
|
|
GLuint gTextureGreen[] = {0xFF00FF00};
|
|
|
|
GLuint gTextureBlue[] = {0xFFFF0000};
|
2013-07-02 08:06:03 +02:00
|
|
|
|
2013-09-14 06:29:28 +02:00
|
|
|
GLuint getErrorTexture()
|
2013-07-02 08:06:03 +02:00
|
|
|
{
|
2013-09-14 06:29:28 +02:00
|
|
|
static GLuint errTexName = 0;
|
|
|
|
if(errTexName == 0)
|
|
|
|
{
|
|
|
|
glGenTextures(1, &errTexName);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, errTexName);
|
|
|
|
glTexImage2D(
|
|
|
|
GL_TEXTURE_2D, 0, GL_RGBA,
|
|
|
|
2, 2, 0,
|
|
|
|
GL_RGBA, GL_UNSIGNED_BYTE, gErrorTextureData
|
|
|
|
);
|
2013-12-20 16:45:41 +01:00
|
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
2013-09-14 06:29:28 +02:00
|
|
|
}
|
|
|
|
return errTexName;
|
|
|
|
}
|
2013-07-02 08:06:03 +02:00
|
|
|
|
2013-09-15 04:39:07 +02:00
|
|
|
const size_t paletteSize = 1024;
|
|
|
|
void processPalette(uint32_t* fullColor, RW::BSTextureNative& texNative, RW::BinaryStreamSection& rootSection)
|
|
|
|
{
|
|
|
|
uint8_t* dataBase = reinterpret_cast<uint8_t*>(rootSection.raw() + sizeof(RW::BSSectionHeader) + sizeof(RW::BSTextureNative) - 4);
|
|
|
|
|
|
|
|
uint8_t* coldata = (dataBase + paletteSize + sizeof(uint32_t));
|
|
|
|
uint32_t raster_size = *reinterpret_cast<uint32_t*>(dataBase + paletteSize);
|
|
|
|
uint32_t* palette = reinterpret_cast<uint32_t*>(dataBase);
|
|
|
|
|
|
|
|
for(size_t j = 0; j < raster_size; ++j)
|
|
|
|
{
|
|
|
|
fullColor[j] = palette[coldata[j]];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-12-20 14:43:12 +01:00
|
|
|
GLuint createTexture(RW::BSTextureNative& texNative, RW::BinaryStreamSection& rootSection, bool* transparent)
|
2013-09-14 06:29:28 +02:00
|
|
|
{
|
|
|
|
// TODO: Exception handling.
|
|
|
|
if(texNative.platform != 8) {
|
|
|
|
std::cerr << "Unsupported texture platform " << std::dec << texNative.platform << std::endl;
|
|
|
|
return getErrorTexture();
|
|
|
|
}
|
2013-07-02 08:06:03 +02:00
|
|
|
|
2013-09-14 06:29:28 +02:00
|
|
|
bool isPal8 = (texNative.rasterformat & RW::BSTextureNative::FORMAT_EXT_PAL8) == RW::BSTextureNative::FORMAT_EXT_PAL8;
|
|
|
|
bool isFulc = texNative.rasterformat == RW::BSTextureNative::FORMAT_1555 ||
|
|
|
|
texNative.rasterformat == RW::BSTextureNative::FORMAT_8888 ||
|
|
|
|
texNative.rasterformat == RW::BSTextureNative::FORMAT_888;
|
2013-12-20 14:43:12 +01:00
|
|
|
// Export this value
|
|
|
|
*transparent = !((texNative.rasterformat&RW::BSTextureNative::FORMAT_888) == RW::BSTextureNative::FORMAT_888);
|
|
|
|
|
2013-09-14 06:29:28 +02:00
|
|
|
if(! (isPal8 || isFulc)) {
|
|
|
|
std::cerr << "Unsuported raster format " << std::dec << texNative.rasterformat << std::endl;
|
|
|
|
return getErrorTexture();
|
|
|
|
}
|
2013-07-02 08:06:03 +02:00
|
|
|
|
2013-09-14 06:29:28 +02:00
|
|
|
GLuint textureName = 0;
|
2013-12-20 14:43:12 +01:00
|
|
|
|
|
|
|
#if ENABLE_ABHORENT_DEBUGGING
|
|
|
|
if(true)
|
|
|
|
{
|
|
|
|
glGenTextures(1, &textureName);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
|
|
glTexImage2D(
|
|
|
|
GL_TEXTURE_2D, 0, GL_RGBA,
|
|
|
|
1, 1, 0,
|
|
|
|
GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)(gDebugTextureData) + (*transparent ? 0 : 4)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
2013-09-14 06:29:28 +02:00
|
|
|
if(isPal8)
|
|
|
|
{
|
|
|
|
uint32_t fullColor[texNative.width * texNative.height];
|
2013-09-11 20:23:31 +02:00
|
|
|
|
2013-09-15 04:39:07 +02:00
|
|
|
processPalette(fullColor, texNative, rootSection);
|
2013-09-14 06:29:28 +02:00
|
|
|
|
|
|
|
glGenTextures(1, &textureName);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
|
|
glTexImage2D(
|
|
|
|
GL_TEXTURE_2D, 0, GL_RGBA,
|
|
|
|
texNative.width, texNative.height, 0,
|
|
|
|
GL_RGBA, GL_UNSIGNED_BYTE, fullColor
|
|
|
|
);
|
|
|
|
}
|
|
|
|
else if(isFulc)
|
|
|
|
{
|
|
|
|
auto coldata = rootSection.raw() + sizeof(RW::BSTextureNative);
|
|
|
|
coldata += sizeof(uint32_t);
|
|
|
|
|
2014-06-19 17:31:01 +02:00
|
|
|
GLenum type = GL_UNSIGNED_BYTE, format = GL_RGBA;
|
2013-09-14 06:29:28 +02:00
|
|
|
switch(texNative.rasterformat)
|
2013-07-02 08:06:03 +02:00
|
|
|
{
|
2013-09-14 06:29:28 +02:00
|
|
|
case RW::BSTextureNative::FORMAT_1555:
|
|
|
|
format = GL_RGBA;
|
|
|
|
type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
|
|
|
|
break;
|
|
|
|
case RW::BSTextureNative::FORMAT_8888:
|
2013-12-20 16:45:41 +01:00
|
|
|
format = GL_BGRA;
|
|
|
|
//type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
|
|
|
type = GL_UNSIGNED_BYTE;
|
2013-09-14 06:29:28 +02:00
|
|
|
break;
|
|
|
|
case RW::BSTextureNative::FORMAT_888:
|
|
|
|
format = GL_BGRA;
|
|
|
|
type = GL_UNSIGNED_BYTE;
|
|
|
|
break;
|
2014-06-19 17:31:01 +02:00
|
|
|
default:
|
|
|
|
break;
|
2013-07-02 08:06:03 +02:00
|
|
|
}
|
|
|
|
|
2013-09-14 06:29:28 +02:00
|
|
|
glGenTextures(1, &textureName);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
|
|
glTexImage2D(
|
2013-09-15 04:39:07 +02:00
|
|
|
GL_TEXTURE_2D, 0, GL_RGBA,
|
2013-09-14 06:29:28 +02:00
|
|
|
texNative.width, texNative.height, 0,
|
|
|
|
format, type, coldata
|
|
|
|
);
|
|
|
|
}
|
2013-12-20 16:45:41 +01:00
|
|
|
else {
|
|
|
|
return getErrorTexture();
|
|
|
|
}
|
2013-07-02 08:06:03 +02:00
|
|
|
|
2013-09-14 06:29:28 +02:00
|
|
|
GLenum texFilter = GL_LINEAR;
|
|
|
|
switch(texNative.filterflags & 0xFF) {
|
|
|
|
default:
|
|
|
|
case RW::BSTextureNative::FILTER_LINEAR:
|
|
|
|
texFilter = GL_LINEAR;
|
|
|
|
break;
|
|
|
|
case RW::BSTextureNative::FILTER_NEAREST:
|
|
|
|
texFilter = GL_NEAREST;
|
|
|
|
break;
|
|
|
|
}
|
2013-07-02 08:06:03 +02:00
|
|
|
|
2015-01-22 21:16:28 +01:00
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
2013-09-14 06:29:28 +02:00
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texFilter);
|
|
|
|
|
|
|
|
GLenum texwrap = GL_REPEAT;
|
|
|
|
switch(texNative.wrapU) {
|
|
|
|
default:
|
|
|
|
case RW::BSTextureNative::WRAP_WRAP:
|
|
|
|
texwrap = GL_REPEAT;
|
|
|
|
break;
|
|
|
|
case RW::BSTextureNative::WRAP_CLAMP:
|
|
|
|
texwrap = GL_CLAMP_TO_EDGE;
|
|
|
|
break;
|
|
|
|
case RW::BSTextureNative::WRAP_MIRROR:
|
|
|
|
texwrap = GL_MIRRORED_REPEAT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texwrap );
|
|
|
|
|
|
|
|
switch(texNative.wrapV) {
|
|
|
|
default:
|
|
|
|
case RW::BSTextureNative::WRAP_WRAP:
|
|
|
|
texwrap = GL_REPEAT;
|
|
|
|
break;
|
|
|
|
case RW::BSTextureNative::WRAP_CLAMP:
|
|
|
|
texwrap = GL_CLAMP_TO_EDGE;
|
|
|
|
break;
|
|
|
|
case RW::BSTextureNative::WRAP_MIRROR:
|
|
|
|
texwrap = GL_MIRRORED_REPEAT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texwrap );
|
|
|
|
|
|
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
return textureName;
|
|
|
|
}
|
|
|
|
|
2013-12-20 17:02:46 +01:00
|
|
|
bool TextureLoader::loadFromMemory(char *data, GameData *gameData)
|
2013-09-14 06:29:28 +02:00
|
|
|
{
|
|
|
|
RW::BinaryStreamSection root(data);
|
2013-10-08 22:26:11 +02:00
|
|
|
/*auto texDict =*/ root.readStructure<RW::BSTextureDictionary>();
|
2013-09-14 06:29:28 +02:00
|
|
|
|
|
|
|
size_t rootI = 0;
|
|
|
|
while (root.hasMoreData(rootI)) {
|
|
|
|
auto rootSection = root.getNextChildSection(rootI);
|
|
|
|
|
|
|
|
if (rootSection.header.id != RW::SID_TextureNative)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
RW::BSTextureNative texNative = rootSection.readStructure<RW::BSTextureNative>();
|
|
|
|
std::string name = std::string(texNative.diffuseName);
|
2014-06-18 07:59:24 +02:00
|
|
|
std::string alpha = std::string(texNative.alphaName);
|
2013-12-20 16:45:41 +01:00
|
|
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower );
|
2014-06-18 07:59:24 +02:00
|
|
|
std::transform(alpha.begin(), alpha.end(), alpha.begin(), ::tolower );
|
2014-06-19 17:31:01 +02:00
|
|
|
|
2013-12-20 14:43:12 +01:00
|
|
|
bool transparent = false;
|
|
|
|
GLuint id = createTexture(texNative, rootSection, &transparent);
|
2013-07-02 08:06:03 +02:00
|
|
|
|
2014-06-19 17:31:01 +02:00
|
|
|
gameData->textures[{name, alpha}] = {id, transparent};
|
|
|
|
|
|
|
|
if( !alpha.empty() ) {
|
|
|
|
gameData->textures[{name, ""}] = {id, transparent};
|
|
|
|
}
|
2013-07-02 08:06:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2014-06-06 13:18:32 +02:00
|
|
|
|
|
|
|
|
|
|
|
LoadTextureArchiveJob::LoadTextureArchiveJob(WorkContext *context, GameData *gd, const std::string &file)
|
|
|
|
: WorkJob(context), _gameData(gd), _file(file), _data(nullptr)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadTextureArchiveJob::work()
|
|
|
|
{
|
|
|
|
_data = _gameData->openFile(_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LoadTextureArchiveJob::complete()
|
|
|
|
{
|
|
|
|
// TODO error status
|
|
|
|
if(_data) {
|
|
|
|
TextureLoader loader;
|
|
|
|
loader.loadFromMemory(_data, _gameData);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] _data;
|
|
|
|
}
|