]> git.tdb.fi Git - libs/gl.git/commitdiff
Give materials the capability to automatically create a suitable shader
authorMikko Rasa <tdb@tdb.fi>
Thu, 11 Jun 2020 13:23:19 +0000 (16:23 +0300)
committerMikko Rasa <tdb@tdb.fi>
Thu, 11 Jun 2020 19:06:49 +0000 (22:06 +0300)
source/basicmaterial.cpp
source/basicmaterial.h
source/material.cpp
source/material.h
source/renderpass.cpp
source/renderpass.h

index 7ba8380c1b47b62db0dad9746d19db1a1ec5413b..858c65daf56e8a31e5cdb9c22f1ab0da3f43ae57 100644 (file)
@@ -14,6 +14,36 @@ BasicMaterial::BasicMaterial()
        set_reflectivity(0.0f);
 }
 
+string BasicMaterial::create_program_source() const
+{
+       string source = "import phong;\n";
+       if(diffuse.texture)
+               source += "const bool use_diffuse_map = true;\n";
+       if(specular.texture || specular.value.r || specular.value.g || specular.value.b)
+       {
+               source += "const bool use_specular = true;\n";
+               if(specular.texture)
+                       source += "const bool use_specular_map = true;\n";
+               if(shininess.texture)
+                       source += "const bool use_shininess_map = true;\n";
+       }
+       if(normal.texture)
+               source += "const bool use_normal_map = true;\n";
+       if(emission.texture || emission.value.r || emission.value.g || emission.value.b)
+       {
+               source += "const bool use_emission = true;\n";
+               if(emission.texture)
+                       source += "const bool use_emission_map = true;\n";
+       }
+       if(reflectivity.value || reflectivity.texture)
+       {
+               source += "const bool use_reflectivity = true;\n";
+               if (reflectivity.texture)
+                       source += "const bool use_reflectivity_map = true;\n";
+       }
+       return source;
+}
+
 void BasicMaterial::attach_textures_to(Texturing &texturing, ProgramData &tex_shdata) const
 {
        attach_texture_to(diffuse.texture, texturing, tex_shdata, "diffuse_map");
index 9b7eab074ed5df6c43cc6b5afaa99a7d6a5eb219..5fca56a0300127e54fd7f0b06be52bb4e97586e1 100644 (file)
@@ -33,6 +33,10 @@ private:
 public:
        BasicMaterial();
 
+protected:
+       virtual std::string create_program_source() const;
+
+public:
        void attach_textures_to(Texturing &, ProgramData &) const;
 
        void set_diffuse(const Color &);
index b0f62bbc5fbef9f1d07b7bcfd4b688135394c32e..ef3662e964d8be98421e6e187256b0f028daa232 100644 (file)
@@ -1,3 +1,5 @@
+#include <msp/core/hash.h>
+#include <msp/strings/format.h>
 #include "basicmaterial.h"
 #include "gl.h"
 #include "resources.h"
@@ -9,6 +11,33 @@ using namespace std;
 namespace Msp {
 namespace GL {
 
+Program *Material::create_compatible_shader() const
+{
+       return new Program(create_program_source());
+}
+
+const Program *Material::create_compatible_shader(DataFile::Collection &coll) const
+{
+       string source = create_program_source();
+       string name = format("_material_%016x.glsl", hash64(source));
+       Program *shprog = coll.find<Program>(name);
+       if(shprog)
+               return shprog;
+
+       shprog = new Program(create_program_source());
+       try
+       {
+               coll.add(name, shprog);
+       }
+       catch(...)
+       {
+               delete shprog;
+               throw;
+       }
+
+       return shprog;
+}
+
 void Material::attach_texture_to(const Texture *tex, Texturing &texturing, ProgramData &tex_shdata, const string &name) const
 {
        if(!tex)
index df55a56767c6318ad9f6f3effed314520604fcd7..3e9765d2a2b9cbfa7d408a37642ca12577fa9ed1 100644 (file)
@@ -82,6 +82,11 @@ protected:
 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; }
index 7c7cf2c4a2022cbb1da62c84c385914d543c2edf..b6eb1d954a958990716a730fd90702ee5341f092 100644 (file)
@@ -17,6 +17,7 @@ namespace GL {
 
 RenderPass::RenderPass():
        shprog(0),
+       shprog_from_material(false),
        shdata(0),
        material(0),
        texturing(0),
@@ -25,6 +26,7 @@ RenderPass::RenderPass():
 
 RenderPass::RenderPass(const RenderPass &other):
        shprog(other.shprog),
+       shprog_from_material(other.shprog_from_material),
        shdata(other.shdata),
        uniform_slots(other.uniform_slots),
        material(other.material),
@@ -37,6 +39,7 @@ RenderPass::RenderPass(const RenderPass &other):
 RenderPass &RenderPass::operator=(const RenderPass &other)
 {
        shprog = other.shprog;
+       shprog_from_material = other.shprog_from_material;
        shdata = other.shdata;
        uniform_slots = other.uniform_slots;
        material = other.material;
@@ -52,8 +55,9 @@ RenderPass::~RenderPass()
        delete texturing;
 }
 
-void RenderPass::finalize_material()
+void RenderPass::finalize_material(DataFile::Collection *coll)
 {
+       maybe_create_material_shader(coll);
        ensure_private_shader_data();
 
        if(!texturing)
@@ -61,21 +65,44 @@ void RenderPass::finalize_material()
        material->attach_textures_to(*texturing, *shdata);
 }
 
+void RenderPass::maybe_create_material_shader(DataFile::Collection *coll)
+{
+       if(shprog && !shprog_from_material)
+               return;
+
+       if(coll)
+       {
+               shprog = material->create_compatible_shader(*coll);
+               shprog.keep();
+       }
+       else
+               shprog = material->create_compatible_shader();
+
+       if(shdata)
+               shdata = new ProgramData(*shdata, shprog.get());
+
+       shprog_from_material = true;
+}
+
 void RenderPass::ensure_private_shader_data()
 {
        if(!shprog)
                throw invalid_operation("RenderPass::ensure_private_shader_data");
 
        if(!shdata)
-               shdata = new ProgramData(shprog);
-       else if(shdata.refcount() > 1)
+               shdata = new ProgramData(shprog.get());
+       else if(shdata.refcount()>1)
                shdata = new ProgramData(*shdata);
 }
 
 void RenderPass::set_shader_program(const Program *prog, const ProgramData *data)
 {
        shprog = prog;
+       shprog.keep();
+       shprog_from_material = false;
        shdata = (data ? new ProgramData(*data) : 0);
+       if(material)
+               finalize_material(0);
 }
 
 const string &RenderPass::get_slotted_uniform_name(const string &slot) const
@@ -93,7 +120,7 @@ void RenderPass::set_material(const Material *mat)
 {
        material = mat;
        material.keep();
-       finalize_material();
+       finalize_material(0);
 }
 
 void RenderPass::set_texture(unsigned index, const Texture *tex)
@@ -116,7 +143,7 @@ void RenderPass::apply(Renderer &renderer) const
 {
        renderer.set_texturing(texturing);
        renderer.set_material(material.get());
-       renderer.set_shader_program(shprog, shdata.get());
+       renderer.set_shader_program(shprog.get(), shdata.get());
        renderer.set_reverse_winding(back_faces);
 }
 
@@ -152,23 +179,25 @@ void RenderPass::Loader::material_inline()
        Material::GenericLoader ldr(coll);
        load_sub_with(ldr);
        obj.material = ldr.get_material();
-       obj.finalize_material();
+       obj.finalize_material(coll);
 }
 
 void RenderPass::Loader::material(const string &name)
 {
        obj.material = &get_collection().get<Material>(name);
        obj.material.keep();
-       obj.finalize_material();
+       obj.finalize_material(coll);
 }
 
 void RenderPass::Loader::shader(const string &n)
 {
        obj.shprog = &get_collection().get<Program>(n);
+       obj.shprog.keep();
+       obj.shprog_from_material = false;
        if(obj.shdata)
-               obj.shdata = new ProgramData(*obj.shdata, obj.shprog);
+               obj.shdata = new ProgramData(*obj.shdata, obj.shprog.get());
        if(obj.material)
-               obj.finalize_material();
+               obj.finalize_material(coll);
 }
 
 void RenderPass::Loader::texunit(unsigned i)
index 261669148de31838cb7dfc980f0bc70c2cd08bb7..428c74ce7b1f280046e4268b66ec7005d2cf7a4c 100644 (file)
@@ -59,7 +59,8 @@ private:
                void texture2d();
        };
 
-       const Program *shprog;
+       RefPtr<const Program> shprog;
+       bool shprog_from_material;
        RefPtr<ProgramData> shdata;
        std::map<std::string, std::string> uniform_slots;
        RefPtr<const Material> material;
@@ -75,12 +76,13 @@ public:
        ~RenderPass();
 
 private:
-       void finalize_material();
+       void finalize_material(DataFile::Collection *);
+       void maybe_create_material_shader(DataFile::Collection *);
        void ensure_private_shader_data();
 
 public:
        void set_shader_program(const Program *, const ProgramData *);
-       const Program *get_shader_program() const { return shprog; }
+       const Program *get_shader_program() const { return shprog.get(); }
        const ProgramData *get_shader_data() const { return shdata.get(); }
        const std::string &get_slotted_uniform_name(const std::string &) const;
        void set_material(const Material *);