From 3500f13f51dabadd2e7f06b81820936520cc8115 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 3 Jun 2020 21:56:00 +0300 Subject: [PATCH] Redesign the material system Now that legacy OpenGL support is removed and all lighting calculations are done in shaders, there's no reason to stick to the old material model anymore. The new model is closer to that used by many 3D modeling programs, with textures applied to material properties. It also allows different types of materials with different shading models. This change breaks source compatibility, but the new BasicMaterial class is mostly compatible with the old Material class, with only minor changes needed in code and datafiles. --- shaderlib/msp_interface.glsl | 13 ++- shaderlib/singlepass.glsl | 8 +- source/basicmaterial.cpp | 113 ++++++++++++++++++++ source/basicmaterial.h | 54 ++++++++++ source/material.cpp | 124 +++++---------------- source/material.h | 201 ++++++++++++++++++++++++++--------- source/renderpass.cpp | 21 ++-- source/renderpass.h | 1 + source/resources.cpp | 15 ++- source/resources.h | 2 + 10 files changed, 386 insertions(+), 166 deletions(-) create mode 100644 source/basicmaterial.cpp create mode 100644 source/basicmaterial.h diff --git a/shaderlib/msp_interface.glsl b/shaderlib/msp_interface.glsl index 5f391bb5..6a8ee8d8 100644 --- a/shaderlib/msp_interface.glsl +++ b/shaderlib/msp_interface.glsl @@ -5,12 +5,13 @@ struct LightSourceParameters vec4 specular; }; -struct MaterialParameters +struct BasicMaterialParameters { - vec4 ambient; vec4 diffuse; vec4 specular; + vec4 emission; float shininess; + float reflectivity; }; struct ClipPlane @@ -26,10 +27,9 @@ uniform Transform mat4 projection_matrix; }; -uniform Material +uniform BasicMaterial { - MaterialParameters material; - float reflectivity; + BasicMaterialParameters basic_material; }; uniform Lighting @@ -45,6 +45,9 @@ uniform Lighting }; uniform sampler2D diffuse_map; +uniform sampler2D specular_map; +uniform sampler2D shininess_map; +uniform sampler2D emission_map; uniform sampler2D normal_map; uniform sampler2DShadow shadow; diff --git a/shaderlib/singlepass.glsl b/shaderlib/singlepass.glsl index f782f3da..ff421ea0 100644 --- a/shaderlib/singlepass.glsl +++ b/shaderlib/singlepass.glsl @@ -101,23 +101,23 @@ vec3 singlepass_lighting() vec3 diffuse_light = diffuse_intensity*light_sources[0].diffuse.rgb; vec3 half_vec = normalize(light_dir-incident_dir); - float specular_intensity = pow(max(dot(half_vec, normal), 0.0), material.shininess); + float specular_intensity = pow(max(dot(half_vec, normal), 0.0), basic_material.shininess); if(use_shadow_map) specular_intensity *= shadow_intensity; vec3 specular_light = specular_intensity*light_sources[0].specular.rgb; - vec3 result = material.ambient.rgb*ambient_light+material.diffuse.rgb*diffuse_light; + vec3 result = basic_material.diffuse.rgb*ambient_light+basic_material.diffuse.rgb*diffuse_light; if(use_diffuse_map) result *= diffuse_sample.rgb; if(use_specular) - result += material.specular.rgb*specular_light; + result += basic_material.specular.rgb*specular_light; return result; } float singlepass_transparency() { - float alpha = material.diffuse.a; + float alpha = basic_material.diffuse.a; if(use_diffuse_map) alpha *= diffuse_sample.a; return alpha; diff --git a/source/basicmaterial.cpp b/source/basicmaterial.cpp new file mode 100644 index 00000000..7ba8380c --- /dev/null +++ b/source/basicmaterial.cpp @@ -0,0 +1,113 @@ +#include "basicmaterial.h" + +using namespace std; + +namespace Msp { +namespace GL { + +BasicMaterial::BasicMaterial() +{ + set_diffuse(Color(1.0f)); + set_specular(Color(0.0f)); + set_emission(Color(0.0f)); + set_shininess(50.0f); + set_reflectivity(0.0f); +} + +void BasicMaterial::attach_textures_to(Texturing &texturing, ProgramData &tex_shdata) const +{ + attach_texture_to(diffuse.texture, texturing, tex_shdata, "diffuse_map"); + attach_texture_to(specular.texture, texturing, tex_shdata, "specular_map"); + attach_texture_to(normal.texture, texturing, tex_shdata, "normal_map"); + attach_texture_to(emission.texture, texturing, tex_shdata, "emission_map"); + attach_texture_to(shininess.texture, texturing, tex_shdata, "shininess_map"); + attach_texture_to(reflectivity.texture, texturing, tex_shdata, "reflectivity_map"); +} + +void BasicMaterial::set_diffuse(const Color &color) +{ + diffuse.value = color; + shdata.uniform("basic_material.diffuse", color); +} + +void BasicMaterial::set_diffuse_map(const Texture *tex) +{ + diffuse.texture = tex; +} + +void BasicMaterial::set_specular(const Color &color) +{ + specular.value = color; + shdata.uniform("basic_material.specular", color); +} + +void BasicMaterial::set_specular_map(const Texture *tex) +{ + specular.texture = tex; +} + +void BasicMaterial::set_normal_map(const Texture *tex) +{ + normal.texture = tex; +} + +void BasicMaterial::set_emission(const Color &color) +{ + emission.value = color; + shdata.uniform("basic_material.emission", color); +} + +void BasicMaterial::set_emission_map(const Texture *tex) +{ + emission.texture = tex; +} + +void BasicMaterial::set_shininess(float value) +{ + shininess.value = value; + shdata.uniform("basic_material.shininess", value); +} + +void BasicMaterial::set_shininess_map(const Texture *tex) +{ + shininess.texture = tex; +} + +void BasicMaterial::set_reflectivity(float value) +{ + reflectivity.value = value; + shdata.uniform("basic_material.reflectivity", value); +} + +void BasicMaterial::set_reflectivity_map(const Texture *tex) +{ + reflectivity.texture = tex; +} + + +DataFile::Loader::ActionMap BasicMaterial::Loader::shared_actions; + +BasicMaterial::Loader::Loader(BasicMaterial &m): + DerivedObjectLoader >(m) +{ + set_actions(shared_actions); +} + +BasicMaterial::Loader::Loader(BasicMaterial &m, Collection &c): + DerivedObjectLoader >(m, c) +{ + set_actions(shared_actions); +} + +void BasicMaterial::Loader::init_actions() +{ + add_property("diffuse", &BasicMaterial::set_diffuse, &BasicMaterial::set_diffuse_map, true); + add_property("specular", &BasicMaterial::set_specular, &BasicMaterial::set_specular_map, false); + add_property("normal", &BasicMaterial::set_normal_map); + add_property("emission", &BasicMaterial::set_emission, &BasicMaterial::set_emission_map, false); + add_property("shininess", &BasicMaterial::set_shininess, &BasicMaterial::set_shininess_map); + add_property("reflectivity", &BasicMaterial::set_reflectivity, &BasicMaterial::set_reflectivity_map); +} + +} // namespace GL +} // namespace Msp diff --git a/source/basicmaterial.h b/source/basicmaterial.h new file mode 100644 index 00000000..9b7eab07 --- /dev/null +++ b/source/basicmaterial.h @@ -0,0 +1,54 @@ +#ifndef MSP_GL_BASICMATERIAL_H_ +#define MSP_GL_BASICMATERIAL_H_ + +#include "material.h" + +namespace Msp { +namespace GL { + +class BasicMaterial: public Material +{ +public: + class Loader: public DataFile::DerivedObjectLoader > + { + private: + static ActionMap shared_actions; + + public: + Loader(BasicMaterial &); + Loader(BasicMaterial &, Collection &); + + private: + virtual void init_actions(); + }; + +private: + Property diffuse; + Property specular; + Property shininess; + Property normal; + Property emission; + Property reflectivity; + +public: + BasicMaterial(); + + void attach_textures_to(Texturing &, ProgramData &) const; + + void set_diffuse(const Color &); + void set_diffuse_map(const Texture *); + void set_specular(const Color &); + void set_specular_map(const Texture *); + void set_normal_map(const Texture *); + void set_emission(const Color &); + void set_emission_map(const Texture *); + void set_shininess(float); + void set_shininess_map(const Texture *); + void set_reflectivity(float); + void set_reflectivity_map(const Texture *); +}; + +} // namespace GL +} // namespace Msp + +#endif diff --git a/source/material.cpp b/source/material.cpp index 1d5a4087..8bd6e54c 100644 --- a/source/material.cpp +++ b/source/material.cpp @@ -1,120 +1,48 @@ +#include "basicmaterial.h" #include "gl.h" -#include "material.h" #include "resources.h" +#include "texturing.h" + +using namespace std; namespace Msp { namespace GL { -Material::Material() -{ - set_ambient(0.2); - set_diffuse(0.8); - set_specular(0); - set_emission(0); - set_shininess(0); - set_reflectivity(0); -} - -void Material::set_ambient(const Color &a) -{ - ambient = a; - shdata.uniform("material.ambient", ambient); -} - -void Material::set_diffuse(const Color &d) -{ - diffuse = d; - shdata.uniform("material.diffuse", diffuse); -} - -void Material::set_specular(const Color &s) +void Material::attach_texture_to(const Texture *tex, Texturing &texturing, ProgramData &tex_shdata, const string &name) const { - specular = s; - shdata.uniform("material.specular", specular); -} + if(!tex) + return; -void Material::set_emission(const Color &e) -{ - emission = e; - shdata.uniform("material.emission", emission); -} - -void Material::set_shininess(float s) -{ - shininess = s; - shdata.uniform("material.shininess", shininess); + int unit = texturing.find_free_unit(name); + if(unit<0) + throw runtime_error("no free texunit"); + texturing.attach(unit, *tex); + tex_shdata.uniform(name, unit); } -void Material::set_reflectivity(float r) +Material::MaterialRegistry &Material::get_material_registry() { - reflectivity = r; - shdata.uniform("reflectivity", reflectivity); + static MaterialRegistry registry; + static bool initialized = false; + if(!initialized) + { + registry.register_type("basic"); + } + return registry; } -Material::Loader::Loader(Material &m): - DataFile::CollectionObjectLoader(m, 0) -{ - init(); -} - -Material::Loader::Loader(Material &m, Collection &c): - DataFile::CollectionObjectLoader(m, &c) -{ - init(); -} - -void Material::Loader::init() -{ - if(Resources *res = dynamic_cast(coll)) - srgb = res->get_srgb_conversion(); - else - srgb = false; - - add("ambient", &Loader::ambient); - add("diffuse", &Loader::diffuse); - add("specular", &Loader::specular); - add("emission", &Loader::emission); - add("shininess", &Loader::shininess); - add("reflectivity", &Loader::reflectivity); -} - -Color Material::Loader::make_color(float r, float g, float b, float a) -{ - Color c(r, g, b, a); - if(srgb) - c = c.to_linear(); - return c; -} - -void Material::Loader::ambient(float r, float g, float b, float a) -{ - obj.set_ambient(make_color(r, g, b, a)); -} - -void Material::Loader::diffuse(float r, float g, float b, float a) -{ - obj.set_diffuse(make_color(r, g, b, a)); -} - -void Material::Loader::specular(float r, float g, float b, float a) -{ - obj.set_specular(make_color(r, g, b, a)); -} - -void Material::Loader::emission(float r, float g, float b, float a) -{ - obj.set_emission(make_color(r, g, b, a)); -} +DataFile::Loader::ActionMap Material::GenericLoader::shared_actions; -void Material::Loader::shininess(float s) +Material::GenericLoader::GenericLoader(DataFile::Collection *c): + coll(c) { - obj.set_shininess(s); + set_actions(shared_actions); } -void Material::Loader::reflectivity(float r) +void Material::GenericLoader::init_actions() { - obj.set_reflectivity(r); + get_material_registry().add_all(*this); } } // namespace GL diff --git a/source/material.h b/source/material.h index ead11618..df55a567 100644 --- a/source/material.h +++ b/source/material.h @@ -1,6 +1,8 @@ #ifndef MSP_GL_MATERIAL_H_ #define MSP_GL_MATERIAL_H_ +#include +#include #include #include "color.h" #include "programdata.h" @@ -8,80 +10,175 @@ namespace Msp { namespace GL { -/** -Stores basic material properties. This includes color and reflection -parameters, but does not include texturing. Materials interact with light -sources and ambient lighting to produce the base color of a surface. Textures -can be used to add detail. +class Texture; +class Texturing; -Material provides a set of uniform variables for use with shaders. -*/ class Material { +protected: + template + struct Property + { + T value; + const Texture *texture; + + Property(): value(T()), texture(0) { } + }; + + template + class LoaderBase: public DataFile::CollectionObjectLoader + { + protected: + LoaderBase(C &m): CollectionObjectLoader(m, 0) { } + LoaderBase(C &m, Collection &c): CollectionObjectLoader(m, &c) { } + + void add_property(const std::string &, void (C::*)(float), void (C::*)(const Texture *)); + void add_property(const std::string &, void (C::*)(const Color &), void (C::*)(const Texture *), bool); + void add_property(const std::string &, void (C::*)(const Texture *)); + + void property_value_scalar(void (C::*)(float), float); + void property_value_rgb(void (C::*)(const Color &), float, float, float); + void property_value_rgba(void (C::*)(const Color &), float, float, float, float); + void property_value_srgb(void (C::*)(const Color &), float, float, float); + void property_value_srgb_alpha(void (C::*)(const Color &), float, float, float, float); + void property_texture(void (C::*)(const Texture *), const std::string &); + }; + public: - class Loader: public DataFile::CollectionObjectLoader + class GenericLoader: public DataFile::Loader { private: - bool srgb; + template + struct AddType + { + static void add(GenericLoader &ldr, const std::string &kw) { ldr.add(kw, &GenericLoader::typed_material); } + }; + + DataFile::Collection *coll; + RefPtr material; + + static ActionMap shared_actions; public: - Loader(Material &); - Loader(Material &, Collection &); + GenericLoader(DataFile::Collection * = 0); + + Material *get_material() { return material.release(); } private: - void init(); - - Color make_color(float, float, float, float); - void ambient(float, float, float, float); - void diffuse(float, float, float, float); - void specular(float, float, float, float); - void emission(float, float, float, float); - void shininess(float); - void reflectivity(float); + virtual void init_actions(); + + template + void typed_material(); + + friend class Material; }; private: - Color ambient; - Color diffuse; - Color specular; - Color emission; - float shininess; - float reflectivity; + typedef DataFile::LoadableTypeRegistry MaterialRegistry; + +protected: ProgramData shdata; + Material() { } +public: + virtual ~Material() { } + +public: + /** Returns the uniforms for the material. */ + const ProgramData &get_shader_data() const { return shdata; } + +protected: + void attach_texture_to(const Texture *, Texturing &, ProgramData &, const std::string &) const; public: - Material(); + virtual void attach_textures_to(Texturing &, ProgramData &) const = 0; + + template + static void register_type(const std::string &); +private: + static MaterialRegistry &get_material_registry(); +}; - /** Sets the ambient color of the material. Provided to shaders with the - name material.ambient. */ - void set_ambient(const Color &); +template +void Material::register_type(const std::string &kw) +{ + get_material_registry().register_type(kw); +} - /** Sets the diffuse (direction-independent) color of the material. - Provided to shaders with the name material.diffuse. */ - void set_diffuse(const Color &); - /** Sets the specular (direction-dependent) color of the material. Provided - to shaders with the name material.specular. */ - void set_specular(const Color &); - void set_emission(const Color &); +template +void Material::LoaderBase::add_property(const std::string &kw, void (C::*set_value)(float), void (C::*set_texture)(const Texture *)) +{ + add(kw, &LoaderBase::property_value_scalar, set_value); + add(kw+"_map", &LoaderBase::property_texture, set_texture); +} - /** Sets the specular exponent of the material. Provided to shaders with - the name material.shininess. */ - void set_shininess(float); +template +void Material::LoaderBase::add_property(const std::string &kw, void (C::*set_value)(const Color &), void (C::*set_texture)(const Texture *), bool allow_alpha) +{ + add(kw, &LoaderBase::property_value_rgb, set_value); + add(kw+"_srgb", &LoaderBase::property_value_srgb, set_value); + if(allow_alpha) + { + add(kw, &LoaderBase::property_value_rgba, set_value); + add(kw+"_srgb", &LoaderBase::property_value_srgb_alpha, set_value); + } + add(kw+"_map", &LoaderBase::property_texture, set_texture); +} + +template +void Material::LoaderBase::add_property(const std::string &kw, void (C::*set_texture)(const Texture *)) +{ + add(kw+"_map", &LoaderBase::property_texture, set_texture); +} - /** Sets the reflectivity of the material. Provided to shaders with the - name reflectivity. Has no effect when shaders are not used. */ - void set_reflectivity(float); +template +void Material::LoaderBase::property_value_scalar(void (C::*set_value)(float), float value) +{ + (static_cast(obj).*set_value)(value); +} - const Color &get_ambient() const { return ambient; } - const Color &get_diffuse() const { return diffuse; } - const Color &get_specular() const { return specular; } - const Color &get_emission() const { return emission; } - float get_shininess() const { return shininess; } - float get_reflectivity() const { return reflectivity; } +template +void Material::LoaderBase::property_value_rgb(void (C::*set_value)(const Color &), float r, float g, float b) +{ + (static_cast(obj).*set_value)(Color(r, g, b)); +} - /** Returns the uniforms for the material. */ - const ProgramData &get_shader_data() const { return shdata; } -}; +template +void Material::LoaderBase::property_value_rgba(void (C::*set_value)(const Color &), float r, float g, float b, float a) +{ + (static_cast(obj).*set_value)(Color(r, g, b, a)); +} + +template +void Material::LoaderBase::property_value_srgb(void (C::*set_value)(const Color &), float r, float g, float b) +{ + (static_cast(obj).*set_value)(Color(r, g, b).to_linear()); +} + +template +void Material::LoaderBase::property_value_srgb_alpha(void (C::*set_value)(const Color &), float r, float g, float b, float a) +{ + (static_cast(obj).*set_value)(Color(r, g, b, a).to_linear()); +} + +template +void Material::LoaderBase::property_texture(void (C::*set_texture)(const Texture *), const std::string &name) +{ + (static_cast(obj).*set_texture)(&get_collection().get(name)); +} + + +template +void Material::GenericLoader::typed_material() +{ + if(material) + throw std::logic_error("Material was already loaded"); + RefPtr mat = new T; + if(coll) + load_sub(*mat, *coll); + else + load_sub(*mat); + material = mat; +} } // namespace GL } // namespace Msp diff --git a/source/renderpass.cpp b/source/renderpass.cpp index 0d76913b..34a311e7 100644 --- a/source/renderpass.cpp +++ b/source/renderpass.cpp @@ -52,6 +52,15 @@ RenderPass::~RenderPass() delete texturing; } +void RenderPass::finalize_material() +{ + ensure_private_shader_data(); + + if(!texturing) + texturing = new Texturing; + material->attach_textures_to(*texturing, *shdata); +} + void RenderPass::ensure_private_shader_data() { if(!shprog) @@ -84,6 +93,7 @@ void RenderPass::set_material(const Material *mat) { material = mat; material.keep(); + finalize_material(); } void RenderPass::set_texture(unsigned index, const Texture *tex) @@ -139,18 +149,17 @@ void RenderPass::Loader::init() void RenderPass::Loader::material_inline() { - RefPtr mat = new Material; - if(coll) - load_sub(*mat, get_collection()); - else - load_sub(*mat); - obj.material = mat; + Material::GenericLoader ldr(coll); + load_sub_with(ldr); + obj.material = ldr.get_material(); + obj.finalize_material(); } void RenderPass::Loader::material(const string &name) { obj.material = &get_collection().get(name); obj.material.keep(); + obj.finalize_material(); } void RenderPass::Loader::texunit(unsigned i) diff --git a/source/renderpass.h b/source/renderpass.h index d4f7a34d..35701512 100644 --- a/source/renderpass.h +++ b/source/renderpass.h @@ -74,6 +74,7 @@ public: ~RenderPass(); private: + void finalize_material(); void ensure_private_shader_data(); public: diff --git a/source/resources.cpp b/source/resources.cpp index 22814cb8..8aecb933 100644 --- a/source/resources.cpp +++ b/source/resources.cpp @@ -40,7 +40,7 @@ Resources::Resources(): add_type().keyword("font"); add_type().suffix(".kframe").keyword("keyframe"); add_type().suffix(".lightn").keyword("lighting"); - add_type().suffix(".mat").keyword("material"); + add_type().suffix(".mat").creator(&Resources::create_material); add_type().keyword("mesh").creator(&Resources::create_mesh); add_type().keyword("object"); add_type().suffix(".pipe").keyword("pipeline"); @@ -90,6 +90,19 @@ void Resources::set_resource_manager(ResourceManager *m) resource_manager = m; } +Material *Resources::create_material(const string &name) +{ + if(RefPtr io = open_raw(name)) + { + DataFile::Parser parser(*io, name); + Material::GenericLoader ldr(this); + ldr.load(parser); + return ldr.get_material(); + } + + return 0; +} + Mesh *Resources::create_mesh(const string &name) { if(!resource_manager) diff --git a/source/resources.h b/source/resources.h index 238d6c54..0b374499 100644 --- a/source/resources.h +++ b/source/resources.h @@ -7,6 +7,7 @@ namespace Msp { namespace GL { +class Material; class Mesh; class Program; class ResourceManager; @@ -41,6 +42,7 @@ public: void set_resource_manager(ResourceManager *); protected: + Material *create_material(const std::string &); Mesh *create_mesh(const std::string &); Texture2D *create_texture2d(const std::string &); Program *create_program(const std::string &); -- 2.43.0