+#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