]> git.tdb.fi Git - libs/gl.git/blobdiff - source/materials/splatmaterial.cpp
Implement a splat material type
[libs/gl.git] / source / materials / splatmaterial.cpp
diff --git a/source/materials/splatmaterial.cpp b/source/materials/splatmaterial.cpp
new file mode 100644 (file)
index 0000000..21abe4c
--- /dev/null
@@ -0,0 +1,280 @@
+#include <msp/datafile/rawdata.h>
+#include <msp/strings/format.h>
+#include "pbrmaterial.h"
+#include "resources.h"
+#include "splatmaterial.h"
+#include "texture2d.h"
+#include "texture2darray.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+const Tag SplatMaterial::texture_tags[] =
+{
+       Tag("base_color_array"),
+       Tag("normal_array"),
+       Tag("metalness_array"),
+       Tag("roughness_array"),
+       Tag("occlusion_array"),
+       Tag("emission_array"),
+       Tag("fresnel_lookup"),
+       Tag()
+};
+
+SplatMaterial::SplatMaterial():
+       fresnel_lookup(PbrMaterial::get_or_create_fresnel_lookup()),
+       fresnel_sampler(Resources::get_global().get<Sampler>("_linear_clamp.samp"))
+{
+}
+
+SplatMaterial::~SplatMaterial()
+{
+       delete base_color_array.texture;
+       delete normal_array.texture;
+       delete metalness_array.texture;
+       delete roughness_array.texture;
+       delete occlusion_array.texture;
+       delete emission_array.texture;
+}
+
+void SplatMaterial::fill_program_info(string &module_name, map<string, int> &spec_values) const
+{
+       module_name = "splat.glsl";
+       spec_values["use_base_color_map"] = (base_color_array.format!=NO_PIXELFORMAT);
+       spec_values["use_normal_map"] = (normal_array.format!=NO_PIXELFORMAT);
+       spec_values["use_metalness_map"] = (metalness_array.format!=NO_PIXELFORMAT);
+       spec_values["use_roughness_map"] = (roughness_array.format!=NO_PIXELFORMAT);
+       spec_values["use_occlusion_map"] = (occlusion_array.format!=NO_PIXELFORMAT);
+       bool use_emission = (emission_array.format!=NO_PIXELFORMAT);
+       for(auto i=sub_materials.begin(); (!use_emission && i!=sub_materials.end()); ++i)
+               use_emission = (i->emission.r || i->emission.g || i->emission.b);
+       spec_values["use_emission"] = use_emission;
+       spec_values["use_emission_map"] = (emission_array.format!=NO_PIXELFORMAT);
+}
+
+const Texture *SplatMaterial::get_texture(Tag tag) const
+{
+       if(tag==texture_tags[0])
+               return base_color_array.texture;
+       else if(tag==texture_tags[1])
+               return normal_array.texture;
+       else if(tag==texture_tags[2])
+               return metalness_array.texture;
+       else if(tag==texture_tags[3])
+               return roughness_array.texture;
+       else if(tag==texture_tags[4])
+               return occlusion_array.texture;
+       else if(tag==texture_tags[5])
+               return emission_array.texture;
+       else if(tag==texture_tags[6])
+               return &fresnel_lookup;
+       else
+               return 0;
+}
+
+const Sampler *SplatMaterial::get_sampler(Tag tag) const
+{
+       if(tag==texture_tags[6])
+               return &fresnel_sampler;
+       else
+               return sampler;
+}
+
+void SplatMaterial::set_sub_uniforms(unsigned index)
+{
+       SubMaterial &sub = sub_materials[index];
+       string prefix = format("splat_materials[%d].", index);
+       shdata.uniform(prefix+"base_color", sub.base_color);
+       shdata.uniform(prefix+"tint", sub.tint);
+       shdata.uniform(prefix+"metalness", sub.metalness);
+       shdata.uniform(prefix+"roughness", sub.roughness);
+       shdata.uniform(prefix+"emission", sub.emission);
+}
+
+void SplatMaterial::upload_sub(DataFile::Collection &coll, unsigned index)
+{
+       upload_sub_map(coll, index, &SubMaterial::base_color_map, base_color_array, "base_color");
+       upload_sub_map(coll, index, &SubMaterial::normal_map, normal_array, "normal");
+       upload_sub_map(coll, index, &SubMaterial::metalness_map, metalness_array, "metalness");
+       upload_sub_map(coll, index, &SubMaterial::roughness_map, roughness_array, "roughness");
+       upload_sub_map(coll, index, &SubMaterial::occlusion_map, occlusion_array, "occlusion");
+       upload_sub_map(coll, index, &SubMaterial::emission_map, emission_array, "emission");
+}
+
+void SplatMaterial::upload_sub_map(DataFile::Collection &coll, unsigned index, SubMap SubMaterial::*map, MapArray &array, const char *name)
+{
+       SubMap &sub = sub_materials[index].*map;
+       if(sub.source_fn.empty())
+               return;
+
+       uint64_t layer_mask = 0;
+       for(const SubMaterial &s: sub_materials)
+       {
+               int l = (s.*map).layer;
+               if(l>=0)
+                       layer_mask |= 1<<l;
+       }
+
+       unsigned lowest_free_bit = (~layer_mask)&(layer_mask+1);
+       sub.layer = 0;
+       for(; lowest_free_bit>1; lowest_free_bit>>=1)
+               ++sub.layer;
+
+       RefPtr<IO::Seekable> io = coll.open_raw(sub.source_fn);
+       char magic[4] = { };
+       io->read(magic, 4);
+       io->seek(0, IO::S_BEG);
+
+       if(DataFile::RawData::detect_signature(string(magic, 4)))
+       {
+               DataFile::RawData raw_data;
+               raw_data.open_io(*io, sub.source_fn);
+               raw_data.load();
+               array.texture->layer_image(0, sub.layer, raw_data.get_data());
+       }
+       else
+       {
+               Graphics::Image image;
+               image.load_io(*io);
+               array.texture->layer_image(0, sub.layer, image);
+       }
+
+       shdata.uniform(format("splat_materials[%d].%s_layer", index, name), sub.layer);
+}
+
+
+void SplatMaterial::MapArray::create()
+{
+       if(!texture && format!=NO_PIXELFORMAT && max_layers>0)
+       {
+               texture = new Texture2DArray;
+               texture->storage(format, width, height, max_layers);
+       }
+}
+
+
+DataFile::Loader::ActionMap SplatMaterial::Loader::shared_actions;
+
+SplatMaterial::Loader::Loader(SplatMaterial &m, Collection &c):
+       DerivedObjectLoader<SplatMaterial, PropertyLoader<SplatMaterial>>(m, c)
+{
+       set_actions(shared_actions);
+}
+
+void SplatMaterial::Loader::init_actions()
+{
+       PropertyLoader<SplatMaterial>::init_actions();
+       add("base_color_storage", &Loader::map_storage, &SplatMaterial::base_color_array);
+       add("emission_storage", &Loader::map_storage, &SplatMaterial::emission_array);
+       add("metalness_storage", &Loader::map_storage, &SplatMaterial::metalness_array);
+       add("normal_storage", &Loader::map_storage, &SplatMaterial::normal_array);
+       add("occlusion_storage", &Loader::map_storage, &SplatMaterial::occlusion_array);
+       add("roughness_storage", &Loader::map_storage, &SplatMaterial::roughness_array);
+       add("sub", &SplatMaterial::Loader::sub);
+}
+
+void SplatMaterial::Loader::finish()
+{
+       obj.base_color_array.create();
+       obj.normal_array.create();
+       obj.metalness_array.create();
+       obj.roughness_array.create();
+       obj.occlusion_array.create();
+       obj.emission_array.create();
+
+       DataFile::Collection &c = get_collection();
+       for(unsigned i=0; i<obj.sub_materials.size(); ++i)
+       {
+               obj.set_sub_uniforms(i);
+               obj.upload_sub(c, i);
+       }
+
+       if(obj.base_color_array.texture)
+               obj.base_color_array.texture->generate_mipmap();
+       if(obj.normal_array.texture)
+               obj.normal_array.texture->generate_mipmap();
+       if(obj.metalness_array.texture)
+               obj.metalness_array.texture->generate_mipmap();
+       if(obj.roughness_array.texture)
+               obj.roughness_array.texture->generate_mipmap();
+       if(obj.occlusion_array.texture)
+               obj.occlusion_array.texture->generate_mipmap();
+       if(obj.emission_array.texture)
+               obj.emission_array.texture->generate_mipmap();
+}
+
+void SplatMaterial::Loader::map_storage(MapArray SplatMaterial::*array, PixelFormat f, unsigned w, unsigned h)
+{
+       (obj.*array).format = f;
+       (obj.*array).width = w;
+       (obj.*array).height = h;
+}
+
+void SplatMaterial::Loader::sub()
+{
+       SubMaterial sm;
+       load_sub(sm);
+       obj.sub_materials.push_back(sm);
+
+       if(!sm.base_color_map.source_fn.empty())
+               ++obj.base_color_array.max_layers;
+       if(!sm.normal_map.source_fn.empty())
+               ++obj.normal_array.max_layers;
+       if(!sm.metalness_map.source_fn.empty())
+               ++obj.metalness_array.max_layers;
+       if(!sm.roughness_map.source_fn.empty())
+               ++obj.roughness_array.max_layers;
+       if(!sm.occlusion_map.source_fn.empty())
+               ++obj.occlusion_array.max_layers;
+       if(!sm.emission_map.source_fn.empty())
+               ++obj.emission_array.max_layers;
+}
+
+
+DataFile::Loader::ActionMap SplatMaterial::SubMaterial::Loader::shared_actions;
+
+SplatMaterial::SubMaterial::Loader::Loader(SubMaterial &s):
+       ObjectLoader<SubMaterial>(s)
+{
+       set_actions(shared_actions);
+}
+
+void SplatMaterial::SubMaterial::Loader::init_actions()
+{
+       add("base_color", &Loader::base_color);
+       add("base_color_map", &Loader::map, &SubMaterial::base_color_map);
+       add("emission", &Loader::emission);
+       add("emission_map", &Loader::map, &SubMaterial::emission_map);
+       add("metalness", &SubMaterial::metalness);
+       add("metalness_map", &Loader::map, &SubMaterial::metalness_map);
+       add("normal_map", &Loader::map, &SubMaterial::normal_map);
+       add("occlusion_map", &Loader::map, &SubMaterial::occlusion_map);
+       add("roughness", &SubMaterial::roughness);
+       add("roughness_map", &Loader::map, &SubMaterial::roughness_map);
+       add("tint", &Loader::tint);
+}
+
+void SplatMaterial::SubMaterial::Loader::base_color(float r, float g, float b)
+{
+       obj.base_color = Color(r, g, b);
+}
+
+void SplatMaterial::SubMaterial::Loader::emission(float r, float g, float b)
+{
+       obj.emission = Color(r, g, b);
+}
+
+void SplatMaterial::SubMaterial::Loader::map(SubMap SubMaterial::*m, const string &fn)
+{
+       (obj.*m).source_fn = fn;
+}
+
+void SplatMaterial::SubMaterial::Loader::tint(float r, float g, float b, float a)
+{
+       obj.tint = Color(r, g, b, a);
+}
+
+} // namespace GL
+} // namespace Msp