#ifndef MSP_GL_MATERIAL_H_
#define MSP_GL_MATERIAL_H_
+#include <msp/datafile/collection.h>
+#include <msp/datafile/loadabletyperegistry.h>
#include <msp/datafile/objectloader.h>
-#include "bindable.h"
#include "color.h"
#include "programdata.h"
namespace Msp {
namespace GL {
-/**
-Stores OpenGL material properties. Since OpenGL does not support material
-objects, application of material is done with several calls to glMaterial.
-*/
-class Material: public BindableWithDefault<Material>
+class Texture;
+class Texturing;
+
+class Material
{
+protected:
+ template<typename T>
+ struct Property
+ {
+ T value;
+ const Texture *texture;
+
+ Property(): value(T()), texture(0) { }
+ };
+
+ template<typename C>
+ class LoaderBase: public DataFile::CollectionObjectLoader<Material>
+ {
+ protected:
+ LoaderBase(C &m): CollectionObjectLoader<Material>(m, 0) { }
+ LoaderBase(C &m, Collection &c): CollectionObjectLoader<Material>(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<Material>
+ class GenericLoader: public DataFile::Loader
{
private:
- bool srgb;
+ template<typename T>
+ struct AddType
+ {
+ static void add(GenericLoader &ldr, const std::string &kw) { ldr.add(kw, &GenericLoader::typed_material<T>); }
+ };
+
+ DataFile::Collection *coll;
+ RefPtr<Material> 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<typename T>
+ void typed_material();
+
+ friend class Material;
};
private:
- enum ParameterMask
- {
- AMBIENT = 1,
- DIFFUSE = 2,
- SPECULAR = 4,
- EMISSION = 8,
- SHININESS = 16
- };
+ typedef DataFile::LoadableTypeRegistry<GenericLoader, GenericLoader::AddType> MaterialRegistry;
- Color ambient;
- Color diffuse;
- Color specular;
- Color emission;
- float shininess;
- float reflectivity;
+protected:
ProgramData shdata;
+ Material() { }
public:
- Material();
-
-private:
- void update_parameter(int) const;
+ virtual ~Material() { }
public:
- void set_ambient(const Color &a);
- void set_diffuse(const Color &d);
- void set_specular(const Color &s);
- void set_emission(const Color &e);
- void set_shininess(float s);
- void set_reflectivity(float);
- 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; }
+ /** Returns the uniforms for the material. */
const ProgramData &get_shader_data() const { return shdata; }
- void bind() const;
+
+protected:
+ void attach_texture_to(const Texture *, Texturing &, ProgramData &, const std::string &) const;
+public:
+ virtual void attach_textures_to(Texturing &, ProgramData &) const = 0;
+
+ template<typename T>
+ static void register_type(const std::string &);
+private:
+ static MaterialRegistry &get_material_registry();
};
+template<typename T>
+void Material::register_type(const std::string &kw)
+{
+ get_material_registry().register_type<T>(kw);
+}
+
+
+template<typename C>
+void Material::LoaderBase<C>::add_property(const std::string &kw, void (C::*set_value)(float), void (C::*set_texture)(const Texture *))
+{
+ add(kw, &LoaderBase<C>::property_value_scalar, set_value);
+ add(kw+"_map", &LoaderBase<C>::property_texture, set_texture);
+}
+
+template<typename C>
+void Material::LoaderBase<C>::add_property(const std::string &kw, void (C::*set_value)(const Color &), void (C::*set_texture)(const Texture *), bool allow_alpha)
+{
+ add(kw, &LoaderBase<C>::property_value_rgb, set_value);
+ add(kw+"_srgb", &LoaderBase<C>::property_value_srgb, set_value);
+ if(allow_alpha)
+ {
+ add(kw, &LoaderBase<C>::property_value_rgba, set_value);
+ add(kw+"_srgb", &LoaderBase<C>::property_value_srgb_alpha, set_value);
+ }
+ add(kw+"_map", &LoaderBase<C>::property_texture, set_texture);
+}
+
+template<typename C>
+void Material::LoaderBase<C>::add_property(const std::string &kw, void (C::*set_texture)(const Texture *))
+{
+ add(kw+"_map", &LoaderBase<C>::property_texture, set_texture);
+}
+
+template<typename C>
+void Material::LoaderBase<C>::property_value_scalar(void (C::*set_value)(float), float value)
+{
+ (static_cast<C &>(obj).*set_value)(value);
+}
+
+template<typename C>
+void Material::LoaderBase<C>::property_value_rgb(void (C::*set_value)(const Color &), float r, float g, float b)
+{
+ (static_cast<C &>(obj).*set_value)(Color(r, g, b));
+}
+
+template<typename C>
+void Material::LoaderBase<C>::property_value_rgba(void (C::*set_value)(const Color &), float r, float g, float b, float a)
+{
+ (static_cast<C &>(obj).*set_value)(Color(r, g, b, a));
+}
+
+template<typename C>
+void Material::LoaderBase<C>::property_value_srgb(void (C::*set_value)(const Color &), float r, float g, float b)
+{
+ (static_cast<C &>(obj).*set_value)(Color(r, g, b).to_linear());
+}
+
+template<typename C>
+void Material::LoaderBase<C>::property_value_srgb_alpha(void (C::*set_value)(const Color &), float r, float g, float b, float a)
+{
+ (static_cast<C &>(obj).*set_value)(Color(r, g, b, a).to_linear());
+}
+
+template<typename C>
+void Material::LoaderBase<C>::property_texture(void (C::*set_texture)(const Texture *), const std::string &name)
+{
+ (static_cast<C &>(obj).*set_texture)(&get_collection().get<GL::Texture>(name));
+}
+
+
+template<typename T>
+void Material::GenericLoader::typed_material()
+{
+ if(material)
+ throw std::logic_error("Material was already loaded");
+ RefPtr<T> mat = new T;
+ if(coll)
+ load_sub(*mat, *coll);
+ else
+ load_sub(*mat);
+ material = mat;
+}
+
} // namespace GL
} // namespace Msp