Commit df9e0370 authored by Johannes Braun's avatar Johannes Braun
Browse files

Newest Texture

parent 69ca59c2
#include <array>
#include <iostream>
#include <atomic>
#include <any>
#include <memory>
#include <functional>
#include <set>
#include <queue>
#include <map>
#include <experimental/filesystem>
#include <experimental/string>
#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <chrono>
#include <mutex>
#include <thread>
#include <vector>
#include <shared_mutex>
#include <util/color.h>
#include <core/state.h>
#include <core/rendering/texture_renderer.h>
#include <util/opengl.h>
struct Texture;
template<typename From, typename To>
constexpr float conversionFactor()
{
float to_float = 0;
if constexpr(std::is_same_v<From, float>) to_float = 1.f;
else to_float = 1 / float(std::numeric_limits<From>::max());
if constexpr(std::is_same_v<To, float>) return to_float;
else return to_float * float(std::numeric_limits<To>::max());
}
template<typename TBase>
struct Image
{
friend struct Texture;
using Dimensions = std::initializer_list<int>;
// You can either have a multisampled image without data.
explicit Image(Dimensions dimensions, int comp, int samples)
: m_components(comp)
{
m_width = dimensions.begin()[0];
m_height = dimensions.size() > 1 ? dimensions.begin()[1] : 1;
m_depth = dimensions.size() > 2 ? dimensions.begin()[2] : 1;
}
// Or a single-sample image with data.
explicit Image(Dimensions dimensions, int comp, std::vector<TBase> data = std::vector<TBase>())
: m_components(comp), data(std::move(data))
{
m_width = dimensions.begin()[0];
m_height = dimensions.size() > 1 ? dimensions.begin()[1] : 1;
m_depth = dimensions.size() > 2 ? dimensions.begin()[2] : 1;
}
// Or load an image from HDD as single-sample image
explicit Image(const std::experimental::filesystem::path& path)
{
m_depth = 1;
if constexpr (std::is_same_v<TBase, uint8_t>)
{
uint8_t *raw_data = stb::stbi_load(path.string().c_str(), &m_width, &m_height, &m_components, 0);
if (!raw_data)
{
Log_Error << "Loading image failed! \"" << path << "\" not found.";
m_width = 0;
m_height = 0;
m_components = 0;
return;
}
data = std::vector<uint8_t>(raw_data, raw_data + m_width * m_height * m_components);
}
else
{
float *raw_data = stb::stbi_loadf(path.string().c_str(), &m_width, &m_height, &m_components, 0);
if (!raw_data)
{
Log_Error << "Loading image failed! \"" << path << "\" not found.";
m_width = 0;
m_height = 0;
m_components = 0;
return;
}
data = std::vector<TBase>(m_width * m_height * m_components);
for (int i = 0; i < m_width * m_height * m_components; ++i)
{
data[i] = static_cast<TBase>(conversionFactor<float, TBase>() * raw_data[i]);
}
}
flip();
}
// return r,rg,rgb or rgba
std::tuple<TBase&, TBase&, TBase&, TBase&> operator[](Dimensions pixel)
{
// Don't allow texel fetches on MS images.
assert(m_samples == 0);
// Debug pixel size check.
assert(pixel.size() == 3 || (m_depth == 1 && (pixel.size() == 2 || (m_height == 1 && pixel.size() == 1))));
if (data.empty()) throw std::out_of_range("This texture has no data. Cannot fetch a pixel for an empty texture.");
glm::ivec3 position(0);
int pos = 0;
for (auto&& p : pixel)
{
position[pos++] = p;
}
// Debug bounds check.
assert(position.x >= 0 && position.x < m_width);
assert(position.y >= 0 && position.y < m_height);
assert(position.z >= 0 && position.z < m_depth);
color<TBase> result(0);
for (int i = 0; i < m_components; ++i)
{
// 4D access :P
result[i] = data[m_width*m_height*m_components*position.z + m_width*m_components*position.y + m_components*position.x + i];
}
m_ignore = 0;
auto get = [this](const glm::ivec3 position, int offset) -> TBase&
{
return offset >= m_components ? m_ignore : data[m_width*m_height*m_components*position.z + m_width*m_components*position.y + m_components*position.x + offset];
};
return std::tie(get(position, 0), get(position, 1), get(position, 2), get(position, 3));
}
void save(const fs::path& path)
{
flip();
if constexpr(std::is_same_v<TBase, float>)
{
//if is float and extension is png, convert!
if (path.extension() != ".hdr")
{
std::vector<uint8_t> converted(m_width * m_height * m_components);
for (int i = 0; i < converted.size(); ++i)
{
converted[i] = static_cast<uint8_t>(conversionFactor<float, uint8_t>() * data[i]);
}
images::save(path, m_width, m_height, m_components, converted.data());
return;
}
//Otherwise just save the floats.
images::save(path, m_width, m_height, m_components, converted.data());
}
else if constexpr(std::is_same_v<TBase, uint8_t>)
{
images::save(path, m_width, m_height, m_components, data.data());
}
else
{
if (path.extension() == ".hdr")
{
std::vector<float> converted(m_width * m_height * m_components);
for (int i = 0; i < converted.size(); ++i)
{
converted[i] = static_cast<float>(conversionFactor<TBase, float>() * data[i]);
}
images::save(path, m_width, m_height, m_components, converted.data());
return;
}
else
{
std::vector<uint8_t> converted(m_width * m_height * m_components);
for (int i = 0; i < converted.size(); ++i)
{
converted[i] = static_cast<uint8_t>(conversionFactor<TBase, uint8_t>() * data[i]);
}
images::save(path, m_width, m_height, m_components, converted.data());
return;
}
}
flip();
}
int width() const { return m_width; }
int height() const { return m_height; }
int depth() const { return m_depth; }
int components() const { return m_components; }
int samples() const { return m_samples; }
void flip()
{
images::flip(static_cast<unsigned>(m_width), static_cast<unsigned>(m_height), static_cast<int>(m_components), data.data());
}
int dimensions() const
{
return 1 + (m_height != 1) + (m_height != 1 && m_depth != 1);
}
//private:
int m_width = 1;
int m_height = 1;
int m_depth = 1;
int m_components = 0;
int m_samples = 0;
TBase m_ignore = 0;
std::vector<TBase> data;
};
struct Texture
{
using Target = gl::TextureType;
using SubTarget = gl::ImageTarget;
using StoreFormat = gl::TextureInternalFormat;
using SubTargetMultisample = gl::ImageTargetMultisample;
struct Parameters
{
gl::TextureWrapMode wrap_r = gl::TextureWrapMode::eRepeat;
gl::TextureWrapMode wrap_s = gl::TextureWrapMode::eRepeat;
gl::TextureWrapMode wrap_t = gl::TextureWrapMode::eRepeat;
gl::TextureFilterMin filter_min = gl::TextureFilterMin::eLinearMipmapLinear;
gl::TextureFilterMag filter_mag = gl::TextureFilterMag::eLinear;
gl::TextureCompareMode compare_mode = gl::TextureCompareMode::eRToTexture;
gl::CompareFunc compare_func = gl::CompareFunc::eLess;
float anisotropy = -1.f;
};
// General initialization
Texture(Target target, StoreFormat store_format)
: m_target(target), m_handle(std::move(target)), m_format(store_format)
{
}
// Direct initialization for either 1D, 2D or 3D target.
template<typename T> Texture(const Image<T>& image)
: Texture([&image]() {
switch (image.dimensions())
{
case 1: return Target::e1D;
case 2: return Target::e2D;
case 3: return Target::e3D;
default: return Target::e2D;
}
}(), getFormat(image))
{
switch (m_target)
{
case Target::e1D:
set(SubTarget::e1D, image, m_latest_parameters);
break;
case Target::e2D:
set(SubTarget::e2D, image, m_latest_parameters);
break;
case Target::e3D:
set(SubTarget::e3D, image, m_latest_parameters);
break;
}
}
template<typename T>
Image<T> get()
{
if (m_target != gl::TextureType::e2D || m_components == -1)
{
throw std::invalid_argument("You cannot download this texture.");
}
int texture_width = width();
int texture_height = height();
int components = m_components;
gl::TextureFormat texture_format = [](int components)
{
switch (components)
{
case 1: return gl::TextureFormat::eRed;
case 2: return gl::TextureFormat::eRG;
case 3: return gl::TextureFormat::eRGB;
case 4: return gl::TextureFormat::eRGBA;
default: throw std::invalid_argument("Texture format not valid.");
}
}(components);
auto texture_type = type();
int type_size = static_cast<int>([](gl::Type type)
{
switch (type)
{
case gl::Type::eByte:
return sizeof(int8_t);
case gl::Type::eUByte:
case gl::Type::eUInt_8_8_8_8:
case gl::Type::eUInt_8_8_8_8_rev:
return sizeof(uint8_t);
case gl::Type::eInt:
return sizeof(int32_t);
case gl::Type::eUInt:
return sizeof(uint32_t);
case gl::Type::eFloat:
return sizeof(float);
default: throw std::invalid_argument("Texture format not valid.");
}
}(texture_type));
std::vector<uint8_t> data(texture_width * texture_height * components * type_size);
std::vector<T> converted(texture_width*texture_height*components);
gl::getTextureImage(m_handle, 0, texture_format, texture_type, data.size(), data.data());
for (int i = 0; i < texture_width*texture_height*components; ++i)
{
switch (texture_type)
{
case gl::Type::eByte:
converted[i] = static_cast<T>(conversionFactor<int8_t, T>() * reinterpret_cast<int8_t*>(data.data())[i]);
break;
case gl::Type::eUByte:
case gl::Type::eUInt_8_8_8_8:
case gl::Type::eUInt_8_8_8_8_rev:
converted[i] = static_cast<T>(conversionFactor<uint8_t, T>() * reinterpret_cast<uint8_t*>(data.data())[i]);
break;
case gl::Type::eInt:
converted[i] = static_cast<T>(conversionFactor<int32_t, T>() * reinterpret_cast<int32_t*>(data.data())[i]);
break;
case gl::Type::eUInt:
converted[i] = static_cast<T>(conversionFactor<uint32_t, T>() * reinterpret_cast<uint32_t*>(data.data())[i]);
break;
case gl::Type::eFloat:
converted[i] = static_cast<T>(conversionFactor<float, T>() * reinterpret_cast<float*>(data.data())[i]);
break;
default: throw std::invalid_argument("Texture format not valid.");
}
}
return Image<T>({ texture_width, texture_height }, components, converted);
}
template<typename T> void set(SubTargetMultisample t, const Image<T>& image)
{
set(t, image, m_latest_parameters);
}
// up until the set methods, no openGL is needed yet.
// Upload an image
template<typename T> void set(SubTargetMultisample t, const Image<T>& image, Parameters parameters)
{
assert(image.samples() != 0);
m_components = image.components();
gl::textureImage(m_handle, t, image.samples(), m_format, { image.width(), image.height(), image.depth() });
apply(std::move(parameters))
}
template<typename T> void set(SubTarget t, const Image<T>& image)
{
set(t, image, m_latest_parameters);
}
// up until the set methods, no openGL is needed yet.
// Upload an image
template<typename T> void set(SubTarget t, const Image<T>& image, Parameters parameters)
{
set(t, 0, image, std::move(parameters));
}
// Upload an image
template<typename T> void set(SubTarget t, int level, const Image<T>& image, Parameters parameters)
{
// Don't allow level parameter on MS textures.
// images are multisampled if sample count is not 0.
assert(image.samples() == 0);
gl::TextureFormat texture_format = [](int components)
{
switch (components)
{
case 1: return gl::TextureFormat::eRed;
case 2: return gl::TextureFormat::eRG;
case 3: return gl::TextureFormat::eRGB;
case 4: return gl::TextureFormat::eRGBA;
default: throw std::invalid_argument("Texture format not valid.");
}
}(image.components());
m_components = image.components();
gl::textureImage(m_handle, t, level, m_format, texture_format, { image.width(), image.height(), image.depth() }, image.data.empty() ? nullptr : image.data.data());
apply(std::move(parameters));
}
const Parameters& parameters() const
{
return m_latest_parameters;
}
void apply(Parameters parameters)
{
m_latest_parameters = std::move(parameters);
float anisotropy = m_latest_parameters.anisotropy;
if (anisotropy == -1.f) gl::getFloatv(gl::GetParameter::eMaxTextureMaxAnisotropy, &anisotropy);
gl::textureParameter(m_handle, gl::TextureParameter::eMaxAnisotropy, anisotropy);
gl::textureParameter(m_handle, gl::TextureParameter::eWrapR, m_latest_parameters.wrap_r);
gl::textureParameter(m_handle, gl::TextureParameter::eWrapS, m_latest_parameters.wrap_s);
gl::textureParameter(m_handle, gl::TextureParameter::eWrapT, m_latest_parameters.wrap_t);
gl::textureParameter(m_handle, gl::TextureParameter::eMagFilter, m_latest_parameters.filter_mag);
gl::textureParameter(m_handle, gl::TextureParameter::eMinFilter, m_latest_parameters.filter_min);
gl::textureParameter(m_handle, gl::TextureParameter::eCompareMode, m_latest_parameters.compare_mode);
gl::textureParameter(m_handle, gl::TextureParameter::eCompareFunc, m_latest_parameters.compare_func);
gl::generateTextureMipmap(m_handle);
}
unsigned id() const
{
return m_handle;
}
bool textureResident() const
{
return m_texture_address != 0 && gl::isTextureHandleResident(m_texture_address);
}
bool imageResident() const
{
return m_image_address != 0 && gl::isImageHandleResident(m_image_address);
}
uint64_t textureAddress() const
{
if (!textureResident())
{
m_texture_address = gl::getTextureHandle(m_handle);
gl::makeTextureHandleResident(m_texture_address);
}
assert(textureResident());
return m_texture_address;
}
uint64_t imageAddress(gl::Access access) const
{
if (!imageResident())
{
m_image_address = gl::getImageHandle(m_handle, 0, false, 0, gl::ImageUnitFormat(m_format));
gl::makeImageHandleResident(m_image_address, access);
}
assert(imageResident());
return m_image_address;
}
int samples() const
{
int s;
gl::getTextureLevelParameteriv(m_handle, 0, gl::TextureLevelParameter::eSamples, &s);
return s;
}
gl::Type type() const
{
gl::Type type;
gl::getInternalFormatParameter(m_target, m_format, gl::TextureInternalFormatParameter::eTextureImageType, reinterpret_cast<int*>(&type));
return type;
}
int width() const
{
int width;
gl::getTextureLevelParameteriv(m_handle, 0, gl::TextureLevelParameter::eWidth, &width);
return width;
}
int height() const
{
int height;
gl::getTextureLevelParameteriv(m_handle, 0, gl::TextureLevelParameter::eHeight, &height);
return height;
}
int depth() const
{
int depth;
gl::getTextureLevelParameteriv(m_handle, 0, gl::TextureLevelParameter::eDepth, &depth);
return depth;
}
private:
template<typename T>
static StoreFormat getFormat(const Image<T>& image)
{
switch (image.components())
{
case 1:
if constexpr(std::is_same_v<T, float>) return gl::TextureInternalFormat::eR32Float;
else if constexpr(std::is_same_v<T, uint8_t>) return gl::TextureInternalFormat::eR8;
else if constexpr(std::is_same_v<T, uint16_t>) return gl::TextureInternalFormat::eR16UInt;
else if constexpr(std::is_same_v<T, uint32_t>) return gl::TextureInternalFormat::eR32UInt;
else if constexpr(std::is_same_v<T, int8_t>) return gl::TextureInternalFormat::eR8Int;
else if constexpr(std::is_same_v<T, int16_t>) return gl::TextureInternalFormat::eR16Int;
else if constexpr(std::is_same_v<T, int32_t>) return gl::TextureInternalFormat::eR32Int;
case 2:
if constexpr(std::is_same_v<T, float>) return gl::TextureInternalFormat::eRG32Float;
else if constexpr(std::is_same_v<T, uint8_t>) return gl::TextureInternalFormat::eRG8;
else if constexpr(std::is_same_v<T, uint16_t>) return gl::TextureInternalFormat::eRG16UInt;
else if constexpr(std::is_same_v<T, uint32_t>) return gl::TextureInternalFormat::eRG32UInt;
else if constexpr(std::is_same_v<T, int8_t>) return gl::TextureInternalFormat::eRG8Int;
else if constexpr(std::is_same_v<T, int16_t>) return gl::TextureInternalFormat::eRG16Int;
else if constexpr(std::is_same_v<T, int32_t>) return gl::TextureInternalFormat::eRG32Int;
case 3:
if constexpr(std::is_same_v<T, float>) return gl::TextureInternalFormat::eRGB32Float;
else if constexpr(std::is_same_v<T, uint8_t>) return gl::TextureInternalFormat::eRGB8;
else if constexpr(std::is_same_v<T, uint16_t>) return gl::TextureInternalFormat::eRGB16UInt;
else if constexpr(std::is_same_v<T, uint32_t>) return gl::TextureInternalFormat::eRGB32UInt;
else if constexpr(std::is_same_v<T, int8_t>) return gl::TextureInternalFormat::eRGB8Int;
else if constexpr(std::is_same_v<T, int16_t>) return gl::TextureInternalFormat::eRGB16Int;
else if constexpr(std::is_same_v<T, int32_t>) return gl::TextureInternalFormat::eRGB32Int;
case 4:
if constexpr(std::is_same_v<T, float>) return gl::TextureInternalFormat::eRGBA32Float;
else if constexpr(std::is_same_v<T, uint8_t>) return gl::TextureInternalFormat::eRGBA8;
else if constexpr(std::is_same_v<T, uint16_t>) return gl::TextureInternalFormat::eRGBA16UInt;
else if constexpr(std::is_same_v<T, uint32_t>) return gl::TextureInternalFormat::eRGBA32UInt;
else if constexpr(std::is_same_v<T, int8_t>) return gl::TextureInternalFormat::eRGBA8Int;
else if constexpr(std::is_same_v<T, int16_t>) return gl::TextureInternalFormat::eRGBA16Int;
else if constexpr(std::is_same_v<T, int32_t>) return gl::TextureInternalFormat::eRGBA32Int;
throw std::invalid_argument("Using images other than 32 bit float and integral types is not yet implemented");
default:
throw std::invalid_argument("Malformed image! Component count not in range 1 to 4.");
}
}
int m_components = -1;
Target m_target;
StoreFormat m_format;
Parameters m_latest_parameters;
gl::handle::texture m_handle;
mutable uint64_t m_texture_address = 0;
mutable uint64_t m_image_address = 0;
};
int main()
{
auto context_id = glare::core::Context::createAsCurrent(files::asset("/preferences/default.xml"));
Image<uint8_t> image_src(files::asset("/textures/bricky.png"));
Texture texture(image_src);
auto image = texture.get<uint8_t>();
image.save(files::asset("/screenshots/bricky_output.png"));
auto imageF = texture.get<int>();
imageF.save(files::asset("/screenshots/bricky_output_float.png"));
imageF.save(files::asset("/screenshots/bricky_output_float.hdr"));
auto texrend = glare::core::DefaultTextureRenderers::makeSimpleRenderer();
glare::core::Context::current().loop([&](){
texrend->draw(texture.textureAddress());
});
system("pause");
return 0;
}
\ No newline at end of file
......@@ -607,7 +607,7 @@ namespace gl
{
case gl::ImageTarget::e1D:
case gl::ImageTarget::e1DProxy:
assert(dimensions.size() == 1);
assert(dimensions.size() >= 1);
gl::textureImage1D(texture, gl::TextureImageTarget1D(target), level, fmt, dimensions.begin()[0], 0, format, typeFromInternal(texture_target, fmt), data);
break;
......@@ -624,7 +624,7 @@ namespace gl
case gl::ImageTarget::eCubeMapNegY:
case gl::ImageTarget::eCubeMapPosZ:
case gl::ImageTarget::eCubeMapNegZ:
assert(dimensions.size() == 2);
assert(dimensions.size() >= 2);
gl::textureImage2D(texture, gl::TextureImageTarget2D(target), level, fmt, dimensions.begin()[0], dimensions.begin()[1], 0, format, typeFromInternal(texture_target, fmt), data);