/* model/material.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include "model/material.h" #include "auxiliary/functions.h" #include "filesystem/filestream.h" #include "math/functions.h" #include "sys/sys.h" namespace model { Material::ImageLoaderFuncPtr Material::material_imageloaderfunc = 0; Material::Registry Material::material_registry; Material::Material(const std::string &name) : material_name(name), material_flags(0), material_size(64.0f, 64.0f) { aux::to_lowercase(material_name); } Material::~Material() { for (Layers::iterator it = material_layers.begin(); it != material_layers.end(); ++it) { delete (*it); (*it) = 0; } } void Material::set_size(const math::Vector2f & size) { material_size.assign(size); } Layer * Material::add_layer() { Layer * layer = new Layer(); material_layers.push_back(layer); return (layer); } void Material::print() { con_print << name() << std::endl; con_print << " flags: " << flags() << " "; if (flags() == FlagNone) { con_print << "none"; } else { if (flags() & FlagIgnore) { con_print << "ignore "; } if (flags() & FlagClip) { con_print << "clip "; } if (flags() & FlagOrigin) { con_print << "origin "; } if (flags() & FlagDecal) { con_print << "decal "; } if (flags() & FlagBounds) { con_print << "bounds "; } } int count = 0; for (Layers::const_iterator lit = material_layers.begin(); lit != material_layers.end(); ++lit) { const Layer *layer = (*lit); count++; con_print << " layer " << count << std::endl; con_print << " texture: "; if (layer->texmap() == Layer::TexMapNone) { con_print << "none"; } else if (layer->texmap() == Layer::TexMapImage) { con_print << layer->texture(); } con_print << std::endl; con_print << " color: " << layer->color() << std::endl; con_print << " rgbgen: "; switch (layer->rgbgen()) { case Layer::RGBGenIdentity: con_print << "identity"; break; case Layer::RGBGenColor: con_print << "color"; break; case Layer::RGBGenEngine: con_print << "engine"; break; case Layer::RGBGenPrimary: con_print << "entity primary"; break; case Layer::RGBGenSecondary: con_print << "entity secondary"; break; case Layer::RGBGenTertiary: con_print << "entity tertiary"; break; } con_print << std::endl; if (layer->fullbright()) { con_print << " fullbright" << std::endl; } switch(layer->blendfunc()) { case Layer::BlendFuncNone: break; case Layer::BlendFuncAdd: con_print << " blendfunc: add" << std::endl; break; case Layer::BlendFuncBlend: con_print << " blendfunc: blend" << std::endl; break; } } } /* ---- static ----------------------------------------------------- */ void Material::init() { con_print << "^BInitializing materials..." << std::endl; filesystem::IFileStream shaderlistfile("materials/shaderlist.txt"); if (!shaderlistfile.is_open()) { con_warn << "Could not open " << shaderlistfile.name() << std::endl; return; } con_debug << " " << shaderlistfile.name() << std::endl; char line[1024]; while (shaderlistfile.getline(line, 1023)) { if ((line[0] == 0) || (line[0] == '#') || (line[0] == ';')) { continue; if ((line[0] == '/') && (line[1] == '/')) continue; } else { std::string s(line); aux::trim(s); load_shaderfile(s); } } shaderlistfile.close(); } void Material::clear() { con_print << "^BClearing materials..." << std::endl; for (Registry::iterator i = material_registry.begin(); i != material_registry.end(); ++i) { delete(*i).second; } material_registry.clear(); } void Material::shutdown() { clear(); } void Material::load_shaderfile(const std::string &shadername) { std::string shaderfilename("materials/"); shaderfilename.append(shadername); shaderfilename.append(".shader"); filesystem::IFileStream shaderfile(shaderfilename); if (!shaderfile.is_open()) { con_warn << "Could not open " << shaderfile.name() << std::endl; return; } int parselevel = 0; unsigned int linenumber = 0; char line[1024]; unsigned int count = 0; float r, g, b, a, shine; Material *material = 0; Layer *layer = 0; while (shaderfile.getline(line, 1023)) { linenumber++; // read materials std::string s(line); aux::trim(s); // skip empty lines if (!s.size()) continue; // skip comments if ((s[0] == '#') || (s[0] == ';') || ((s[0] == '/') && (s[1] == '/'))) continue; std::istringstream linestream(s); std::string firstword; if (linestream >> firstword) { if (firstword.compare("//") == 0) { continue; } else if (firstword.compare("}") == 0){ if (parselevel == 2) { // leaving layer definition layer = 0; } else if (parselevel == 1) { // leaving material definition material = 0; } parselevel--; } else if (firstword.compare("{") == 0) { parselevel++; } else if (parselevel == 0) { // add new material material = find(firstword); if (material) { con_warn << "Duplicate material '" << firstword << "'" << std::endl; material = 0; layer = 0; } else { material = add(firstword); count++; layer = 0; } } else if ((parselevel == 1) && (material)) { // inside material definition aux::to_lowercase(firstword); if (firstword.compare("qer_editorimage") == 0) { // keyword qer_editorimage is ignored continue; } else if (firstword.compare("qer_trans") == 0) { // keyword qer_trans is ignored continue; } else if (firstword.compare("surfaceparm") == 0) { // keyword surfaceparm is ignored continue; } else if (firstword.compare("ignore") == 0) { material->set_flags(FlagIgnore); } else if (firstword.compare("clip") == 0) { material->set_flags(FlagClip); } else if (firstword.compare("origin") == 0) { material->set_flags(FlagOrigin); } else if (firstword.compare("decal") == 0) { material->set_flags(FlagDecal); } else if (firstword.compare("bounds") == 0) { material->set_flags(FlagBounds); } else { con_warn << shaderfile.name() << " unknown material key '" << firstword << "' at line " << linenumber << std::endl; } } else if ((parselevel == 2) && (material)) { // inside layer definition // create new layer if required if (!layer) { layer = new Layer(); material->material_layers.push_back(layer); } aux::to_lowercase(firstword); if (firstword.compare("color") == 0) { if (linestream >> r >> g >> b) { if (math::max(r, math::max(g, b)) > 1.0f) { r /= 255.0f; g /= 255.0f; b /= 255.0f; } if (!(linestream >> a)) { a = 1.0f; } layer->set_color(math::Color(r, g, b, a)); layer->set_rgbgen(Layer::RGBGenColor); // set specular too, preserving the old behavior layer->set_specular(math::Color(r, g, b, a)); } } else if (firstword.compare("specular") == 0) { if (linestream >> r >> g >> b) { if (math::max(r, math::max(g, b)) > 1.0f) { r /= 255.0f; g /= 255.0f; b /= 255.0f; } if (!(linestream >> a)) { a = 1.0f; } layer->set_specular(math::Color(r, g, b, a)); } } else if (firstword.compare("shininess") == 0) { if (linestream >> shine) { layer->set_shininess(shine); } } else if (firstword.compare("engine") == 0) { layer->set_rgbgen(Layer::RGBGenEngine); } else if (firstword.compare("entity") == 0) { layer->set_rgbgen(Layer::RGBGenPrimary); } else if (firstword.compare("entitysecond") == 0) { layer->set_rgbgen(Layer::RGBGenSecondary); } else if (firstword.compare("entitythird") == 0) { layer->set_rgbgen(Layer::RGBGenTertiary); } else if ((firstword.compare("bright") == 0) || (firstword.compare("fullbright") == 0)) { layer->set_fullbright(true); } else if (firstword.compare("environment") == 0) { layer->set_tcgen(Layer::TCGenEnvironment); layer->set_texmap(Layer::TexMapEnvironment); } else if (firstword.compare("rgbgen") == 0) { if (linestream >> firstword) { aux::to_lowercase(firstword); if (firstword.compare("color") == 0) { layer->set_rgbgen(Layer::RGBGenColor); } else if (firstword.compare("engine") == 0) { layer->set_rgbgen(Layer::RGBGenEngine); } else if (firstword.compare("entity") == 0) { layer->set_rgbgen(Layer::RGBGenPrimary); } else if (firstword.compare("entitysecond") == 0) { layer->set_rgbgen(Layer::RGBGenSecondary); } else if (firstword.compare("entitythird") == 0) { layer->set_rgbgen(Layer::RGBGenTertiary); } else { con_warn << shaderfile.name() << " unknown rgbgen function '" << firstword << "' at line " << linenumber << std::endl; } } else { con_warn << shaderfile.name() << " missing rgbgen function at line " << linenumber << std::endl; } } else if (firstword.compare("blendfunc") == 0) { if (linestream >> firstword) { aux::to_lowercase(firstword); if (firstword.compare("none") == 0) { layer->set_blendfunc(Layer::BlendFuncNone); } else if (firstword.compare("add") == 0) { layer->set_blendfunc(Layer::BlendFuncAdd); } else if (firstword.compare("blend") == 0) { layer->set_blendfunc(Layer::BlendFuncBlend); } else { con_warn << shaderfile.name() << " unknown blend function '" << firstword << "' at line " << linenumber << std::endl; } } else { con_warn << shaderfile.name() << " missing blend function at line " << linenumber << std::endl; } } else if (firstword.compare("map") == 0) { if (linestream >> firstword) { aux::to_lowercase(firstword); if (firstword.compare("$environment") == 0) { layer->set_texmap(Layer::TexMapEnvironment); } else if (firstword.compare("$logo") == 0) { layer->set_texmap(Layer::TexMapLogo); } else { // regular texture from image file // FIXME strip extension from filename layer->set_texture(firstword); if (material_imageloaderfunc) { material_imageloaderfunc(layer); if ((layer->size().width() > 0) && (layer->size().height() > 0)) { material->material_size.assign(layer->size()); } } } } else { con_warn << shaderfile.name() << " missing texture map at line " << linenumber << std::endl; } } else { con_warn << shaderfile.name() << " unknown layer key '" << firstword << "' at line " << linenumber << std::endl; } } } } con_debug << " " << shaderfile.name() << " " << count << " materials " << std::endl; shaderfile.close(); } void Material::list() { for (Registry::iterator i = material_registry.begin(); i != material_registry.end(); ++i) { con_print << " " << (*i).second->name() << std::endl; } con_print << material_registry.size() << " registered materials" << std::endl; } Material * Material::add(const std::string &name) { Material *material = new Material(name); material_registry[material->name()] = material; return material; } Material *Material::find(const std::string &name) { std::string searchstr(name); aux::to_lowercase(searchstr); for (Registry::iterator i = material_registry.begin(); i != material_registry.end(); ++i) { if ((*i).first.compare(searchstr) == 0) { return (*i).second; } } return 0; } Material *Material::load(const std::string &name, const bool ui_texture) { // check if the requested material already exist Material *material = find(name); if (material) { return material; } // create a new material material = add(name); // add a single layer Layer *layer = material->add_layer(); // add a texture layer->set_texture(name); if (ui_texture) { layer->set_fullbright(true); layer->set_blendfunc(Layer::BlendFuncBlend); } if (material_imageloaderfunc) { material_imageloaderfunc(layer); if ((layer->size().width() > 0) && (layer->size().height() > 0)) { material->material_size.assign(layer->size()); } } // add the new material to the registry material_registry[material->name()] = material; return material; } void Material::set_imageloader_func(ImageLoaderFuncPtr func) { material_imageloaderfunc = func; } }