#include "loader.h" #include #include #include #include #include "../resourcemanager.h" #include #include #include #include "SparrowRenderer/texture.h" #include "utils.h" #include "font.h" #include 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(t)), std::istreambuf_iterator()); return str; } std::unordered_map* Loader::loadConfigFile(const std::string &filename) { std::unordered_map* configPtr = new std::unordered_map(); std::unordered_map& 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; iwidth*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 Loader::loadMesh(const std::string &filename){ std::vector meshes; std::vector pos; std::vector norm; std::vector 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; im_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 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_; }