406 lines
12 KiB
C++
406 lines
12 KiB
C++
#include "loader.h"
|
|
#include <fstream>
|
|
#include <streambuf>
|
|
#include <cstring>
|
|
#include <SparrowRenderer/image.h>
|
|
#include "../resourcemanager.h"
|
|
#include <SFML/Graphics/Image.hpp>
|
|
#include <SparrowRenderer/mesh.h>
|
|
#include <SparrowRenderer/pbrmaterial.h>
|
|
#include "SparrowRenderer/texture.h"
|
|
#include "utils.h"
|
|
#include "font.h"
|
|
|
|
#include <iostream>
|
|
|
|
std::string Loader::obj_directory = "";
|
|
std::string Loader::mtl_directory = "";
|
|
std::string Loader::tex_directory = "";
|
|
|
|
std::string* Loader::loadTextFile(const std::string &filename)
|
|
{
|
|
std::ifstream t(filename);
|
|
std::string *str = new std::string();
|
|
|
|
t.seekg(0, std::ios::end);
|
|
str->reserve(t.tellg());
|
|
t.seekg(0, std::ios::beg);
|
|
|
|
str->assign((std::istreambuf_iterator<char>(t)),
|
|
std::istreambuf_iterator<char>());
|
|
return str;
|
|
}
|
|
|
|
std::unordered_map<std::string, std::string>* Loader::loadConfigFile(const std::string &filename)
|
|
{
|
|
std::unordered_map<std::string, std::string>* configPtr = new std::unordered_map<std::string, std::string>();
|
|
std::unordered_map<std::string, std::string>& config = *configPtr;
|
|
|
|
std::ifstream t(filename);
|
|
|
|
if(!t.is_open())
|
|
return configPtr;
|
|
|
|
char line[256];
|
|
std::string currentLine;
|
|
|
|
while(!t.eof())
|
|
{
|
|
currentLine = "";
|
|
t.getline(line, 256);
|
|
currentLine += std::string(line);
|
|
|
|
if(t.eof() || t.fail() || currentLine[0] == '#')
|
|
continue;
|
|
|
|
unsigned int pos = currentLine.find_first_of('=');
|
|
if(pos == std::string::npos)
|
|
continue;
|
|
|
|
std::string key = currentLine.substr(0, pos);
|
|
std::string val = currentLine.substr(pos+1);
|
|
config[key] = val;
|
|
}
|
|
return configPtr;
|
|
}
|
|
|
|
Image* Loader::loadImage(const std::string &filename, int depth, bool reversed)
|
|
{
|
|
sf::Image sfImg;
|
|
bool ok = sfImg.loadFromFile(tex_directory+filename);
|
|
if(!ok)
|
|
return NULL;
|
|
if(reversed)
|
|
sfImg.flipVertically();
|
|
Image* img = new Image();
|
|
img->depth = depth;
|
|
img->width = sfImg.getSize().x;
|
|
img->height = sfImg.getSize().y;
|
|
int size = img->width*img->height*(img->depth/8);
|
|
img->allocate(size);
|
|
const sf::Uint8 *pixels = sfImg.getPixelsPtr();
|
|
if(depth == 32)
|
|
memcpy(img->pixels.data(), pixels, size);
|
|
else
|
|
{
|
|
int bytesPerPixel = depth/8;
|
|
sf::Uint8 *ptr = (sf::Uint8*)img->pixels.data();
|
|
for(int i=0; i<img->width*img->height; ++i)
|
|
memcpy(ptr + i*bytesPerPixel, pixels + i*4, bytesPerPixel);
|
|
}
|
|
return img;
|
|
}
|
|
|
|
Font* Loader::loadFont(const std::string &description_file, const std::string &texture_file)
|
|
{
|
|
std::string line;
|
|
std::ifstream file(description_file);
|
|
|
|
if(!file)
|
|
{
|
|
fprintf(stderr, "can't load '%s'.\n", description_file.c_str());
|
|
std::cerr << "Error code: " << strerror(errno) << std::endl;
|
|
return nullptr;
|
|
}
|
|
|
|
Font* font = new Font();
|
|
std::getline(file, line);
|
|
char font_n[256];
|
|
std::sscanf(line.c_str(),"info face=\"%[^\"]",font_n);
|
|
std::string font_name(font_n);
|
|
font->setName(font_name);
|
|
|
|
std::getline(file, line);
|
|
float line_h, base;
|
|
glm::vec2 scale;
|
|
std::sscanf(line.c_str(),"common lineHeight=%f base=%f scaleW=%f scaleH=%f",
|
|
&line_h,&base,&(scale.x),&(scale.y));
|
|
font->setLineHeight(line_h);
|
|
font->setBase(base);
|
|
font->setScale(1.f/scale);
|
|
|
|
std::getline(file, line);//ignore 3rd line for now => only use 1 page.
|
|
std::getline(file, line);
|
|
int nbchar;
|
|
std::sscanf(line.c_str(),"chars count=%d",&nbchar);
|
|
font->setNbChar(nbchar);
|
|
while(!file.eof()){
|
|
std::getline(file, line);
|
|
Font::CharInfo char_info;
|
|
int id;
|
|
sscanf(line.c_str(),"char id=%d x=%f y=%f width=%f height=%f xoffset=%f yoffset=%f xadvance=%f",
|
|
&id, &(char_info.pos.x),&(char_info.pos.y),&(char_info.dim.x), &(char_info.dim.y),
|
|
&(char_info.offset.x), &(char_info.offset.y), &(char_info.xadvance));
|
|
font->addCharInfo(id,char_info);
|
|
}
|
|
|
|
Image* fucking_image_of_doom = loadImage(texture_file, 32, false);
|
|
if(fucking_image_of_doom == NULL)
|
|
printf("can't load \"%s\".\n", texture_file.c_str());
|
|
Texture* texture = new Texture(fucking_image_of_doom, false); // mipmaps are doing a very bad job at interpolating alpha component
|
|
delete fucking_image_of_doom;
|
|
font->setTexture(texture);
|
|
return font;
|
|
}
|
|
|
|
std::vector<Mesh*> Loader::loadMesh(const std::string &filename){
|
|
std::vector<Mesh*> meshes;
|
|
|
|
std::vector<glm::vec3> pos;
|
|
std::vector<glm::vec3> norm;
|
|
std::vector<glm::vec2> tex;
|
|
|
|
std::string line;
|
|
|
|
Material* defaultMat = RESOURCE_GET(Material, "default");
|
|
|
|
if(defaultMat == NULL)
|
|
{
|
|
defaultMat = new PBRMaterial();
|
|
RESOURCE_ADD(defaultMat, Material, "default");
|
|
}
|
|
Material* currentMat = defaultMat;
|
|
std::ifstream file(obj_directory + filename);
|
|
|
|
if(!file.is_open())
|
|
{
|
|
fprintf(stderr, "can't load %s.\n", filename.c_str());
|
|
return meshes;
|
|
}
|
|
|
|
Mesh* currentMesh = new Mesh();
|
|
meshes.push_back(currentMesh);
|
|
currentMesh->setMaterial(currentMat);
|
|
|
|
std::getline(file, line);
|
|
while(!file.eof())
|
|
{
|
|
if(line.length() == 0) // line vide
|
|
{
|
|
std::getline(file, line);
|
|
continue;
|
|
}
|
|
|
|
switch(line[0])
|
|
{
|
|
case 'v':
|
|
//vertex attribute
|
|
switch(line[1])
|
|
{
|
|
case ' ': // vertex position
|
|
{
|
|
glm::vec3 p;
|
|
std::sscanf(line.c_str(),"v %f %f %f",&(p.x),&(p.y),&(p.z));
|
|
pos.push_back(p);
|
|
break;
|
|
}
|
|
case 't': // texCoord
|
|
{
|
|
glm::vec2 t;
|
|
std::sscanf(line.c_str(),"vt %f %f",&(t.x),&(t.y));
|
|
tex.push_back(t);
|
|
break;
|
|
}
|
|
case 'n': // normal
|
|
{
|
|
glm::vec3 n;
|
|
std::sscanf(line.c_str(),"vn %f %f %f",&(n.x),&(n.y),&(n.z));
|
|
norm.push_back(n);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case 'f': // face
|
|
{
|
|
int tab[9];
|
|
std::sscanf(line.c_str(),"f %d/%d/%d %d/%d/%d %d/%d/%d",tab,tab+1,tab+2,tab+3,tab+4,tab+5,tab+6,tab+7,tab+8);
|
|
//TODO: check sscanf success
|
|
|
|
int nb_vertices = currentMesh->m_positions3D.size();
|
|
|
|
currentMesh->addTriangle(nb_vertices, nb_vertices+1, nb_vertices+2);
|
|
for(int i=0; i<3; ++i)
|
|
{
|
|
int offset = i*3;
|
|
if(norm.size() == 0)
|
|
{
|
|
if(tex.size() == 0)
|
|
currentMesh->addVertex(pos[tab[offset]-1]);
|
|
else
|
|
currentMesh->addVertex(pos[tab[offset]-1], tex[tab[offset+1]-1]);
|
|
}
|
|
else
|
|
{
|
|
if(tex.size() == 0)
|
|
currentMesh->addVertex(pos[tab[offset]-1], norm[tab[offset+2]-1]);
|
|
else
|
|
currentMesh->addVertex(pos[tab[offset]-1], norm[tab[offset+2]-1], tex[tab[offset+1]-1]);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'g':
|
|
currentMesh = new Mesh();
|
|
meshes.push_back(currentMesh);
|
|
currentMesh->setMaterial(currentMat);
|
|
break;
|
|
case 'm': // mtllib
|
|
{
|
|
char mat_filename[256];
|
|
std::sscanf(line.c_str(),"mtllib %s",mat_filename);
|
|
loadMTL(std::string(mat_filename));
|
|
break;
|
|
}
|
|
case 'u':
|
|
{
|
|
// usemtl
|
|
char mat_name[256];
|
|
std::sscanf(line.c_str(),"usemtl %s",mat_name);
|
|
std::string material_name(mat_name);
|
|
currentMat = RESOURCE_GET(Material, material_name);
|
|
if(currentMat == NULL)
|
|
{
|
|
fprintf(stderr, "cannot find any material named : %s.\n", material_name.c_str());
|
|
currentMat = new PBRMaterial();
|
|
RESOURCE_ADD(currentMat, Material, material_name);
|
|
}
|
|
currentMesh->setMaterial(currentMat);
|
|
currentMesh->setName(material_name);
|
|
}
|
|
break;
|
|
default:
|
|
case '#':
|
|
// comment
|
|
break;
|
|
}
|
|
std::getline(file,line);
|
|
}
|
|
|
|
for(std::size_t i=0; i<meshes.size(); ++i)
|
|
{
|
|
if(meshes[i]->m_indices.size() == 0)
|
|
{
|
|
meshes[i] = meshes.back();
|
|
meshes.pop_back();
|
|
--i;
|
|
}
|
|
else
|
|
{
|
|
Mesh* m = meshes[i];
|
|
if(m->m_normals.empty())
|
|
m->computeNormals();
|
|
if(m->getFlags() & (1 << Mesh::MATERIAL_PBR_NORMAL_MAP))
|
|
m->computeTangents();
|
|
m->mergeVertices();
|
|
}
|
|
}
|
|
return meshes;
|
|
}
|
|
|
|
void initMaterialTexture(PBRMaterial* mat, PBRMaterial::TextureSlots slot, const std::string & file, int bitsPerPixel = 24)
|
|
{
|
|
mat->textures[slot] = RESOURCE_GET(Texture, file);
|
|
if (mat->textures[slot] == nullptr)
|
|
{
|
|
Image* img = Loader::loadImage(file, bitsPerPixel);
|
|
if(img != nullptr && img->width == img->height)
|
|
{
|
|
mat->textures[slot] = new Texture(img);
|
|
RESOURCE_ADD(mat->textures[slot], Texture, file);
|
|
}
|
|
if(img != nullptr)
|
|
delete img;
|
|
}
|
|
}
|
|
|
|
//load MTL
|
|
bool Loader::loadMTL(const std::string &filename)
|
|
{
|
|
std::string line;
|
|
std::ifstream file(mtl_directory + filename);
|
|
|
|
if(!file.is_open())
|
|
{
|
|
fprintf(stderr, "can't load %s.\n", filename.c_str());
|
|
return false;
|
|
}
|
|
|
|
PBRMaterial* mat = NULL;
|
|
bool hasNormalMap = false;
|
|
|
|
std::getline(file,line);
|
|
|
|
while(!file.eof())
|
|
{
|
|
if(line.length() == 0)
|
|
{
|
|
std::getline(file,line);
|
|
continue;
|
|
}
|
|
//QStringList tokens = line.split(' ');
|
|
std::vector<std::string> tokens = utils::split(line,' ');
|
|
|
|
if(tokens[0].substr(0,1) == "#")
|
|
{
|
|
// this is a comment
|
|
}
|
|
else if((tokens[0] == "newmtl") && tokens.size() == 2)
|
|
{
|
|
mat = new PBRMaterial();
|
|
RESOURCE_ADD(mat, Material, tokens[1]);
|
|
}
|
|
else if((tokens[0].compare("emission") == 0) && tokens.size() == 4)
|
|
{
|
|
mat->emission.r = std::stof(tokens[1]);
|
|
mat->emission.g = std::stof(tokens[2]);
|
|
mat->emission.b = std::stof(tokens[3]);
|
|
}
|
|
else if(tokens[0].compare("albedo") == 0 && tokens.size() == 4)
|
|
{
|
|
mat->albedo.r = std::stof(tokens[1]);
|
|
mat->albedo.g = std::stof(tokens[2]);
|
|
mat->albedo.b = std::stof(tokens[3]);
|
|
}
|
|
else if(tokens[0].compare("roughness") == 0 && tokens.size() == 2)
|
|
{
|
|
mat->roughness = std::stof(tokens[1]);
|
|
}
|
|
else if(tokens[0].compare("metallic") == 0 && tokens.size() == 2)
|
|
{
|
|
mat->metallic = std::stof(tokens[1]);
|
|
}
|
|
else if((tokens[0].substr(0,4) == "map_") && tokens.size() == 2)
|
|
{
|
|
if(tokens[0].compare("map_emission") == 0)
|
|
initMaterialTexture(mat, PBRMaterial::EMISSION_SLOT, tokens[1]);
|
|
else if(tokens[0].compare("map_albedo") == 0)
|
|
initMaterialTexture(mat, PBRMaterial::ALBEDO_SLOT, tokens[1]);
|
|
else if(tokens[0].compare("map_roughness") == 0)
|
|
initMaterialTexture(mat, PBRMaterial::ROUGHNESS_SLOT, tokens[1], 8);
|
|
else if(tokens[0].compare("map_metallic") == 0)
|
|
initMaterialTexture(mat, PBRMaterial::METALLIC_SLOT, tokens[1], 8);
|
|
else if(tokens[0].compare("map_normal") == 0)
|
|
initMaterialTexture(mat, PBRMaterial::NORMALS_SLOT, tokens[1]);
|
|
else if(tokens[0].compare("map_alpha") == 0)
|
|
initMaterialTexture(mat, PBRMaterial::ALPHA_SLOT, tokens[1], 8);
|
|
else
|
|
fprintf(stderr, "unsupported material property : \"%s\"\n", tokens[0].c_str());
|
|
}
|
|
else
|
|
fprintf(stderr, "unsupported material property : \"%s\"\n", tokens[0].c_str());
|
|
|
|
std::getline(file,line);
|
|
}
|
|
return hasNormalMap;
|
|
}
|
|
|
|
void Loader::setObjDirectory(std::string dir_){
|
|
obj_directory = dir_;
|
|
}
|
|
void Loader::setMtlDirectory(std::string dir_){
|
|
mtl_directory = dir_;
|
|
}
|
|
void Loader::setTexDirectory(std::string dir_){
|
|
tex_directory = dir_;
|
|
}
|