From e37851b98dde5082ee92570354746f2f92e21940 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 29 Apr 2014 08:28:27 +0300 Subject: [PATCH] Support linear to sRGB conversion when loading materials and textures --- source/material.cpp | 35 ++++++++++++++++++++++++++++++----- source/material.h | 10 ++++++++-- source/pixelformat.cpp | 16 ++++++++++++++++ source/pixelformat.h | 1 + source/renderpass.cpp | 10 ++++++++-- source/resources.cpp | 8 +++++++- source/resources.h | 7 +++++++ source/texture.cpp | 19 ++++++++++++++++++- source/texture.h | 10 +++++++++- source/texture2d.cpp | 29 ++++++++++++++++++++++++----- source/texture2d.h | 7 +++++-- source/texturecube.cpp | 26 +++++++++++++++++++++----- source/texturecube.h | 5 ++++- 13 files changed, 158 insertions(+), 25 deletions(-) diff --git a/source/material.cpp b/source/material.cpp index 7d90dd5a..13f2d1d9 100644 --- a/source/material.cpp +++ b/source/material.cpp @@ -1,5 +1,6 @@ #include "gl.h" #include "material.h" +#include "resources.h" namespace Msp { namespace GL { @@ -73,8 +74,24 @@ void Material::bind() const Material::Loader::Loader(Material &m): - DataFile::ObjectLoader(m) + DataFile::CollectionObjectLoader(m, 0) { + init(); +} + +Material::Loader::Loader(Material &m, Collection &c): + DataFile::CollectionObjectLoader(m, &c) +{ + init(); +} + +void Material::Loader::init() +{ + if(Resources *res = dynamic_cast(coll)) + srgb = res->get_srgb_conversion(); + else + srgb = false; + add("ambient", &Loader::ambient); add("diffuse", &Loader::diffuse); add("specular", &Loader::specular); @@ -82,24 +99,32 @@ Material::Loader::Loader(Material &m): add("shininess", &Loader::shininess); } +Color Material::Loader::make_color(float r, float g, float b, float a) +{ + Color c(r, g, b, a); + if(srgb) + c = c.to_linear(); + return c; +} + void Material::Loader::ambient(float r, float g, float b, float a) { - obj.set_ambient(GL::Color(r, g, b, a)); + obj.set_ambient(make_color(r, g, b, a)); } void Material::Loader::diffuse(float r, float g, float b, float a) { - obj.set_diffuse(GL::Color(r, g, b, a)); + obj.set_diffuse(make_color(r, g, b, a)); } void Material::Loader::specular(float r, float g, float b, float a) { - obj.set_specular(GL::Color(r, g, b, a)); + obj.set_specular(make_color(r, g, b, a)); } void Material::Loader::emission(float r, float g, float b, float a) { - obj.set_emission(GL::Color(r, g, b, a)); + obj.set_emission(make_color(r, g, b, a)); } void Material::Loader::shininess(float s) diff --git a/source/material.h b/source/material.h index 5a71aa34..a38dddd6 100644 --- a/source/material.h +++ b/source/material.h @@ -16,12 +16,18 @@ objects, application of material is done with several calls to glMaterial. class Material: public BindableWithDefault { public: - class Loader: public DataFile::ObjectLoader + class Loader: public DataFile::CollectionObjectLoader { + private: + bool srgb; + public: Loader(Material &); - + Loader(Material &, Collection &); 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); diff --git a/source/pixelformat.cpp b/source/pixelformat.cpp index 027ae88c..b8180b2f 100644 --- a/source/pixelformat.cpp +++ b/source/pixelformat.cpp @@ -105,6 +105,22 @@ PixelFormat get_base_pixelformat(PixelFormat pf) } } +PixelFormat get_srgb_pixelformat(PixelFormat pf) +{ + switch(pf) + { + case RGB: return SRGB; + case RGBA: return SRGB_ALPHA; + case RGB8: return SRGB8; + case RGBA8: return SRGB8_ALPHA8; + case LUMINANCE: return SLUMINANCE; + case LUMINANCE8: return SLUMINANCE8; + case LUMINANCE_ALPHA: return SLUMINANCE_ALPHA; + case LUMINANCE_ALPHA8: return SLUMINANCE8_ALPHA8; + default: return pf; + } +} + unsigned get_component_count(PixelFormat pf) { switch(get_base_pixelformat(pf)) diff --git a/source/pixelformat.h b/source/pixelformat.h index 01f626c4..590ab97e 100644 --- a/source/pixelformat.h +++ b/source/pixelformat.h @@ -54,6 +54,7 @@ PixelFormat pixelformat_from_graphics(Graphics::PixelFormat); PixelFormat storage_pixelformat_from_graphics(Graphics::PixelFormat); PixelFormat get_base_pixelformat(PixelFormat); +PixelFormat get_srgb_pixelformat(PixelFormat); unsigned get_component_count(PixelFormat); void require_pixelformat(PixelFormat); diff --git a/source/renderpass.cpp b/source/renderpass.cpp index 158e9fc8..448db60a 100644 --- a/source/renderpass.cpp +++ b/source/renderpass.cpp @@ -102,7 +102,10 @@ void RenderPass::Loader::init() void RenderPass::Loader::material_inline() { RefPtr mat = new Material; - load_sub(*mat); + if(coll) + load_sub(*mat, get_collection()); + else + load_sub(*mat); obj.material = mat; } @@ -173,7 +176,10 @@ void RenderPass::TextureLoader::texture(const string &name) void RenderPass::TextureLoader::texture2d() { tex = new Texture2D; - load_sub(static_cast(*tex)); + if(coll) + load_sub(static_cast(*tex), get_collection()); + else + load_sub(static_cast(*tex)); } } // namespace GL diff --git a/source/resources.cpp b/source/resources.cpp index 8e872d5d..10d76148 100644 --- a/source/resources.cpp +++ b/source/resources.cpp @@ -19,7 +19,8 @@ namespace Msp { namespace GL { Resources::Resources(): - default_tex_filter(LINEAR_MIPMAP_LINEAR) + default_tex_filter(LINEAR_MIPMAP_LINEAR), + srgb_conversion(false) { add_type().suffix(".anim").keyword("animation"); add_type().suffix(".arma").keyword("armature"); @@ -40,6 +41,11 @@ void Resources::set_default_texture_filter(TextureFilter tf) default_tex_filter = tf; } +void Resources::set_srgb_conversion(bool c) +{ + srgb_conversion = c; +} + Texture2D *Resources::create_texture2d(const string &name) { string ext = FS::extpart(name); diff --git a/source/resources.h b/source/resources.h index 8871c16e..0390d859 100644 --- a/source/resources.h +++ b/source/resources.h @@ -13,12 +13,19 @@ class Resources: virtual public DataFile::Collection { private: TextureFilter default_tex_filter; + bool srgb_conversion; public: Resources(); void set_default_texture_filter(TextureFilter); + /** Enables or disables sRGB conversion. If enabled, textures and material + colors are converted from sRGB to linear color space when loaded. */ + void set_srgb_conversion(bool); + + bool get_srgb_conversion() const { return srgb_conversion; } + protected: Texture2D *create_texture2d(const std::string &); }; diff --git a/source/texture.cpp b/source/texture.cpp index fe8b7be9..d738bb36 100644 --- a/source/texture.cpp +++ b/source/texture.cpp @@ -2,6 +2,7 @@ #include #include #include "error.h" +#include "resources.h" #include "texture.h" #include "texunit.h" @@ -209,8 +210,24 @@ void Texture::unbind_from(unsigned i) Texture::Loader::Loader(Texture &t): - DataFile::ObjectLoader(t) + DataFile::CollectionObjectLoader(t, 0) { + init(); +} + +Texture::Loader::Loader(Texture &t, Collection &c): + DataFile::CollectionObjectLoader(t, &c) +{ + init(); +} + +void Texture::Loader::init() +{ + if(Resources *res = dynamic_cast(coll)) + srgb = res->get_srgb_conversion(); + else + srgb = false; + add("filter", &Loader::filter); add("max_anisotropy", &Loader::max_anisotropy); add("generate_mipmap", &Loader::generate_mipmap); diff --git a/source/texture.h b/source/texture.h index c091c884..477eaca3 100644 --- a/source/texture.h +++ b/source/texture.h @@ -8,6 +8,8 @@ namespace Msp { namespace GL { +class Resources; + enum TextureFilter { /// No filtering @@ -66,11 +68,17 @@ wrapping is applied. The default for all directions is REPEAT. class Texture { protected: - class Loader: public DataFile::ObjectLoader + class Loader: public DataFile::CollectionObjectLoader { + protected: + bool srgb; + public: Loader(Texture &); + Loader(Texture &, Collection &); private: + void init(); + void filter(TextureFilter); void generate_mipmap(bool); void mag_filter(TextureFilter); diff --git a/source/texture2d.cpp b/source/texture2d.cpp index f4af7e72..96da77d1 100644 --- a/source/texture2d.cpp +++ b/source/texture2d.cpp @@ -2,6 +2,7 @@ #include "bindable.h" #include "error.h" #include "pixelstore.h" +#include "resources.h" #include "texture2d.h" using namespace std; @@ -68,21 +69,26 @@ void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht glTexSubImage2D(target, level, x, y, wd, ht, fmt, type, data); } -void Texture2D::load_image(const string &fn) +void Texture2D::load_image(const string &fn, bool srgb) { Graphics::Image img; img.load_file(fn); - image(img); + image(img, srgb); } -void Texture2D::image(const Graphics::Image &img) +void Texture2D::image(const Graphics::Image &img, bool srgb) { unsigned w = img.get_width(); unsigned h = img.get_height(); PixelFormat fmt = pixelformat_from_graphics(img.get_format()); if(width==0) - storage(storage_pixelformat_from_graphics(img.get_format()), w, h); + { + PixelFormat f = storage_pixelformat_from_graphics(img.get_format()); + if(srgb) + f = get_srgb_pixelformat(f); + storage(f, w, h); + } else if(w!=width || h!=height) throw incompatible_data("Texture2D::image"); @@ -106,6 +112,17 @@ void Texture2D::get_level_size(unsigned level, unsigned &w, unsigned &h) Texture2D::Loader::Loader(Texture2D &t): DataFile::DerivedObjectLoader(t) +{ + init(); +} + +Texture2D::Loader::Loader(Texture2D &t, Collection &c): + DataFile::DerivedObjectLoader(t, c) +{ + init(); +} + +void Texture2D::Loader::init() { add("image_data", &Loader::image_data); add("raw_data", &Loader::raw_data); @@ -119,7 +136,7 @@ void Texture2D::Loader::image_data(const string &data) IO::Memory mem(data.data(), data.size()); img.load_io(mem); - obj.image(img); + obj.image(img, srgb); } void Texture2D::Loader::raw_data(const string &data) @@ -129,6 +146,8 @@ void Texture2D::Loader::raw_data(const string &data) void Texture2D::Loader::storage(PixelFormat fmt, unsigned w, unsigned h) { + if(srgb) + fmt = get_srgb_pixelformat(fmt); obj.storage(fmt, w, h); } diff --git a/source/texture2d.h b/source/texture2d.h index 6c089ed2..dce58a78 100644 --- a/source/texture2d.h +++ b/source/texture2d.h @@ -22,7 +22,10 @@ public: { public: Loader(Texture2D &); + Loader(Texture2D &, Collection &); private: + void init(); + void image_data(const std::string &); void raw_data(const std::string &); void storage(PixelFormat, unsigned, unsigned); @@ -60,12 +63,12 @@ public: /** Loads an image from a file and uploads it to the texture. If storage has not been defined, it will be set to match the loaded image. Otherwise the image must be compatible with the defined storage. */ - void load_image(const std::string &fn); + void load_image(const std::string &fn, bool srgb = false); /** Uploads an image to the texture. If storage has not been defined, it will be set to match the image. Otherwise the image must be compatible with the defined storage. */ - void image(const Graphics::Image &); + void image(const Graphics::Image &, bool srgb = false); unsigned get_width() const { return width; } unsigned get_height() const { return height; } diff --git a/source/texturecube.cpp b/source/texturecube.cpp index d5453c18..b071e854 100644 --- a/source/texturecube.cpp +++ b/source/texturecube.cpp @@ -71,17 +71,20 @@ void TextureCube::image(TextureCubeFace face, unsigned level, PixelFormat fmt, D } } -void TextureCube::image(TextureCubeFace face, const Graphics::Image &img) +void TextureCube::image(TextureCubeFace face, const Graphics::Image &img, bool srgb) { unsigned w = img.get_width(); unsigned h = img.get_height(); PixelFormat fmt = pixelformat_from_graphics(img.get_format()); if(size==0) { - if(w==h) - storage(storage_pixelformat_from_graphics(img.get_format()), w); - else + if(w!=h) throw incompatible_data("TextureCube::image"); + + PixelFormat f = storage_pixelformat_from_graphics(img.get_format()); + if(srgb) + f = get_srgb_pixelformat(f); + storage(f, w); } else if(w!=size || h!=size) throw incompatible_data("TextureCube::image"); @@ -166,6 +169,17 @@ Vector3 TextureCube::get_texel_direction(TextureCubeFace face, unsigned u, unsig TextureCube::Loader::Loader(TextureCube &t): DataFile::DerivedObjectLoader(t) +{ + init(); +} + +TextureCube::Loader::Loader(TextureCube &t, Collection &c): + DataFile::DerivedObjectLoader(t, c) +{ + init(); +} + +void TextureCube::Loader::init() { add("image_data", &Loader::image_data); add("raw_data", &Loader::raw_data); @@ -178,7 +192,7 @@ void TextureCube::Loader::image_data(TextureCubeFace face, const string &data) IO::Memory mem(data.data(), data.size()); img.load_io(mem); - obj.image(face, img); + obj.image(face, img, srgb); } void TextureCube::Loader::raw_data(TextureCubeFace face, const string &data) @@ -188,6 +202,8 @@ void TextureCube::Loader::raw_data(TextureCubeFace face, const string &data) void TextureCube::Loader::storage(PixelFormat fmt, unsigned s) { + if(srgb) + fmt = get_srgb_pixelformat(fmt); obj.storage(fmt, s); } diff --git a/source/texturecube.h b/source/texturecube.h index e775be1d..077b58ac 100644 --- a/source/texturecube.h +++ b/source/texturecube.h @@ -40,7 +40,10 @@ public: { public: Loader(TextureCube &); + Loader(TextureCube &, Collection &); private: + void init(); + void image_data(TextureCubeFace, const std::string &); void raw_data(TextureCubeFace, const std::string &); void storage(PixelFormat, unsigned); @@ -77,7 +80,7 @@ public: int x, int y, unsigned w, unsigned h, PixelFormat fmt, DataType type, const void *data); - void image(TextureCubeFace, const Graphics::Image &); + void image(TextureCubeFace, const Graphics::Image &, bool = false); unsigned get_size() const { return size; } private: -- 2.45.2