-/* $Id$
-
-This file is part of libmspgl
-Copyright © 2007 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-
#ifndef MSP_GL_MATERIAL_H_
#define MSP_GL_MATERIAL_H_
-#include <msp/datafile/loader.h>
+#include <msp/datafile/collection.h>
+#include <msp/datafile/loadabletyperegistry.h>
+#include <msp/datafile/objectloader.h>
#include "color.h"
+#include "programdata.h"
+#include "texture.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 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::Loader
+ class GenericLoader: public DataFile::Loader
{
private:
- Material &mat;
+ 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 &);
- Material &get_object() const { return mat; }
-
+ GenericLoader(DataFile::Collection * = 0);
+
+ Material *get_material() { return material.release(); }
private:
- void ambient(float, float, float, float);
- void diffuse(float, float, float, float);
- void specular(float, float, float, float);
- void emission(float, float, float, float);
+ virtual void init_actions();
+
+ template<typename T>
+ void typed_material();
+
+ friend class Material;
};
private:
- Color ambient;
- Color diffuse;
- Color specular;
- Color emission;
- float shininess;
+ typedef DataFile::LoadableTypeRegistry<GenericLoader, GenericLoader::AddType> MaterialRegistry;
+
+protected:
+ ProgramData shdata;
+
+ Material() { }
+public:
+ virtual ~Material() { }
+
+ virtual Program *create_compatible_shader() const;
+ virtual const Program *create_compatible_shader(DataFile::Collection &) const;
+protected:
+ virtual std::string create_program_source() const = 0;
+
+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();
- 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);
- 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; }
- void apply() const;
+ 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)
+{
+ /* The static_cast around get_collection is needed because otherwise Android
+ SDK's g++ 4.9 fails to parse get<Texture> as a template function call */
+ (static_cast<C &>(obj).*set_texture)(&static_cast<Collection &>(get_collection()).get<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