diff --git a/src/glsl/terrain_frag.glsl b/src/glsl/terrain_frag.glsl index b54ef175..ffe2c57b 100644 --- a/src/glsl/terrain_frag.glsl +++ b/src/glsl/terrain_frag.glsl @@ -31,10 +31,14 @@ layout (std140) uniform overlay_params }; uniform sampler2D shadow_map; -uniform sampler2D tex0; -uniform sampler2D tex1; -uniform sampler2D tex2; -uniform sampler2D tex3; +uniform sampler2DArray tex0; +uniform sampler2DArray tex1; +uniform sampler2DArray tex2; +uniform sampler2DArray tex3; +uniform int tex_temp_0; +uniform int tex_temp_1; +uniform int tex_temp_2; +uniform int tex_temp_3; uniform sampler2D stampBrush; uniform vec2 tex_anim_0; uniform vec2 tex_anim_1; @@ -79,10 +83,10 @@ vec4 texture_blend() float a1 = alpha.g; float a2 = alpha.b; - vec3 t0 = texture(tex0, vary_texcoord + tex_anim_0).rgb; - vec3 t1 = texture(tex1, vary_texcoord + tex_anim_1).rgb; - vec3 t2 = texture(tex2, vary_texcoord + tex_anim_2).rgb; - vec3 t3 = texture(tex3, vary_texcoord + tex_anim_3).rgb; + vec3 t0 = texture(tex0, vec3(vary_texcoord + tex_anim_0, tex_temp_0)).rgb; + vec3 t1 = texture(tex1, vec3(vary_texcoord + tex_anim_1, tex_temp_1)).rgb; + vec3 t2 = texture(tex2, vec3(vary_texcoord + tex_anim_2, tex_temp_2)).rgb; + vec3 t3 = texture(tex3, vec3(vary_texcoord + tex_anim_3, tex_temp_3)).rgb; return mix(vec4 (1.0, 1.0, 1.0, 1.0), vec4 (t0 * (1.0 - (a0 + a1 + a2)) + t1 * a0 + t2 * a1 + t3 * a2, 1.0), int(layer_count > 0)); } diff --git a/src/noggit/MapChunk.cpp b/src/noggit/MapChunk.cpp index 13952cb1..0be36e13 100644 --- a/src/noggit/MapChunk.cpp +++ b/src/noggit/MapChunk.cpp @@ -652,6 +652,7 @@ void MapChunk::draw ( math::frustum const& frustum for (int i = 0; i < texture_set->num(); ++i) { texture_set->bindTexture(i, i + 1, textures_bound); + mcnk_shader.uniform("tex_temp_" + std::to_string(i), (*texture_set->getTextures())[i]->array_index()); if (texture_set->is_animated(i)) { diff --git a/src/noggit/TextureManager.cpp b/src/noggit/TextureManager.cpp index b53e0881..c18b7bad 100644 --- a/src/noggit/TextureManager.cpp +++ b/src/noggit/TextureManager.cpp @@ -10,6 +10,7 @@ #include decltype (TextureManager::_) TextureManager::_; +decltype (TextureManager::_tex_arrays) TextureManager::_tex_arrays; void TextureManager::report() { @@ -33,6 +34,95 @@ void TextureManager::unload_all(noggit::NoggitRenderContext context) ); } +TexArrayParams& TextureManager::get_tex_array(int width, int height, int mip_level, + noggit::NoggitRenderContext context) +{ + TexArrayParams& array_params = _tex_arrays[context][std::make_tuple(-1, width, height, mip_level)]; + + GLint n_layers = 0; + gl.getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &n_layers); + + int index_x = array_params.n_used / n_layers; + + if (array_params.arrays.size() <= index_x) + { + GLuint array; + + gl.genTextures(1, &array); + gl.bindTexture(GL_TEXTURE_2D_ARRAY, array); + + array_params.arrays.emplace_back(array); + + GLint n_layers = 0; + gl.getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &n_layers); + + int width_ = width; + int height_ = height; + + for (int i = 0; i < mip_level; ++i) + { + gl.texImage3D(GL_TEXTURE_2D_ARRAY, i, GL_RGBA8, width_, height_, n_layers, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + + width_ = std::max(width_ >> 1, 1); + height_ = std::max(height_ >> 1, 1); + } + + gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, mip_level - 1); + gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + gl.bindTexture(GL_TEXTURE_2D_ARRAY, array_params.arrays[index_x]); + } + + return array_params; +} + +TexArrayParams& TextureManager::get_tex_array(GLint compression, int width, int height, int mip_level, + std::map>& comp_data, noggit::NoggitRenderContext context) +{ + + TexArrayParams& array_params = _tex_arrays[context][std::make_tuple(compression, width, height, mip_level)]; + + GLint n_layers = 0; + gl.getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &n_layers); + + int index_x = array_params.n_used / n_layers; + + if (array_params.arrays.size() <= index_x) + { + GLuint array; + + gl.genTextures(1, &array); + gl.bindTexture(GL_TEXTURE_2D_ARRAY, array); + + array_params.arrays.emplace_back(array); + + int width_ = width; + int height_ = height; + + for (int i = 0; i < mip_level; ++i) + { + gl.compressedTexImage3D(GL_TEXTURE_2D_ARRAY, i, compression, width_, height_, n_layers, 0, comp_data[i].size() * n_layers, nullptr); + + width_ = std::max(width_ >> 1, 1); + height_ = std::max(height_ >> 1, 1); + } + + gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, mip_level - 1); + gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + gl.bindTexture(GL_TEXTURE_2D_ARRAY, array_params.arrays[index_x]); + } + + return array_params; +} + #include //! \todo Cross-platform syntax for packed structs. #pragma pack(push,1) @@ -67,6 +157,8 @@ void blp_texture::bind() { upload(); } + + gl.bindTexture(GL_TEXTURE_2D_ARRAY, _texture_array); } void blp_texture::upload() @@ -78,28 +170,57 @@ void blp_texture::upload() int width = _width, height = _height; + GLint n_layers; + gl.getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &n_layers); + if (!_compression_format) { + auto& params = TextureManager::get_tex_array( _width, _height, _data.size(), _context); + + int index_x = params.n_used / n_layers; + int index_y = params.n_used % n_layers; + + _texture_array = params.arrays[index_x]; + _array_index = index_y; + for (int i = 0; i < _data.size(); ++i) { gl.texImage2D(GL_TEXTURE_2D, i, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, _data[i].data()); + gl.texSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, index_y, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, _data[i].data()); width = std::max(width >> 1, 1); height = std::max(height >> 1, 1); } + params.n_used++; + + //LogDebug << "Mip level: " << std::to_string(_data.size()) << std::endl; + _data.clear(); } else { + auto& params = TextureManager::get_tex_array(_compression_format.get(), _width, _height, _compressed_data.size(), _compressed_data, _context); + + int index_x = params.n_used / n_layers; + int index_y = params.n_used % n_layers; + + _texture_array = params.arrays[index_x]; + _array_index = index_y; + for (int i = 0; i < _compressed_data.size(); ++i) { gl.compressedTexImage2D(GL_TEXTURE_2D, i, _compression_format.get(), width, height, 0, _compressed_data[i].size(), _compressed_data[i].data()); + gl.compressedTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, index_y, width, height, 1, _compression_format.get(), _compressed_data[i].size(), _compressed_data[i].data()); width = std::max(width >> 1, 1); height = std::max(height >> 1, 1); } + params.n_used++; + + //LogDebug << "Mip level (compressed): " << std::to_string(_compressed_data.size()) << std::endl; + gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, _compressed_data.size() - 1); _compressed_data.clear(); } diff --git a/src/noggit/TextureManager.h b/src/noggit/TextureManager.h index 52fe564a..4afe2089 100644 --- a/src/noggit/TextureManager.h +++ b/src/noggit/TextureManager.h @@ -18,10 +18,27 @@ #include #include +#include #include #include +#include +#include +struct tuple_hash +{ + template + std::size_t operator() (const std::tuple &p) const + { + auto h1 = std::hash{}(std::get<0>(p)); + auto h2 = std::hash{}(std::get<1>(p)); + auto h3 = std::hash{}(std::get<2>(p)); + auto h4 = std::hash{}(std::get<3>(p)); + + return h1 ^ h2 ^ h3 ^ h4; // use hash combine here + } +}; + struct BLPHeader; struct scoped_blp_texture_reference; @@ -41,6 +58,8 @@ struct blp_texture : public opengl::texture, AsyncObject void bind(); void upload(); void unload(); + GLuint texture_array() { return _texture_array; }; + int array_index() { return _array_index; }; noggit::NoggitRenderContext getContext() { return _context; }; @@ -61,6 +80,14 @@ private: std::map> _data; std::map> _compressed_data; boost::optional _compression_format; + int _array_index = -1; + GLuint _texture_array = 0; +}; + +struct TexArrayParams +{ + std::vector arrays; + int n_used = 0; }; class TextureManager @@ -68,10 +95,14 @@ class TextureManager public: static void report(); static void unload_all(noggit::NoggitRenderContext context); + static TexArrayParams& get_tex_array(int width, int height, int mip_level, noggit::NoggitRenderContext context); + static TexArrayParams& get_tex_array(GLint compression, int width, int height, int mip_level, std::map>& comp_data, noggit::NoggitRenderContext context); private: friend struct scoped_blp_texture_reference; static noggit::async_object_multimap_with_normalized_key _; + static std::array, TexArrayParams, tuple_hash>, 7> _tex_arrays; + }; struct scoped_blp_texture_reference diff --git a/src/noggit/texture_set.cpp b/src/noggit/texture_set.cpp index 31c96fc2..b63432c9 100644 --- a/src/noggit/texture_set.cpp +++ b/src/noggit/texture_set.cpp @@ -268,6 +268,7 @@ void TextureSet::bindTexture(size_t id, size_t activeTexture, std::arraybind(); textures_bound[id] = textures[id]->id(); } + } math::vector_2d TextureSet::anim_uv_offset(int id, int animtime) const diff --git a/src/opengl/context.hpp b/src/opengl/context.hpp index ed83fcb6..bf952046 100644 --- a/src/opengl/context.hpp +++ b/src/opengl/context.hpp @@ -77,7 +77,31 @@ namespace opengl BOOST_FORCEINLINE void deleteTextures (GLuint, GLuint*); BOOST_FORCEINLINE void bindTexture (GLenum target, GLuint); BOOST_FORCEINLINE void texImage2D (GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, GLvoid const* data); + BOOST_FORCEINLINE void texImage3D (GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, GLvoid const* data); + BOOST_FORCEINLINE void texSubImage3D (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLenum type, + const void * pixels); + BOOST_FORCEINLINE void compressedTexSubImage3D (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLsizei imageSize, + const void * data); BOOST_FORCEINLINE void compressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid const* data); + BOOST_FORCEINLINE void compressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, GLvoid const* data); BOOST_FORCEINLINE void generateMipmap (GLenum); BOOST_FORCEINLINE void activeTexture (GLenum); diff --git a/src/opengl/context.inl b/src/opengl/context.inl index 778a9836..0ffb40a4 100644 --- a/src/opengl/context.inl +++ b/src/opengl/context.inl @@ -296,6 +296,47 @@ void opengl::context::texImage2D (GLenum target, GLint level, GLint internal_for #endif return _current_context->functions()->glTexImage2D (target, level, internal_format, width, height, border, format, type, data); } +void opengl::context::texImage3D (GLenum target, GLint level, GLint internal_format, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, GLvoid const* data) +{ +#ifndef NOGGIT_DO_NOT_CHECK_FOR_OPENGL_ERRORS + verify_context_and_check_for_gl_errors const _ (_current_context, BOOST_CURRENT_FUNCTION); +#endif + return _4_1_core_func->glTexImage3D (target, level, internal_format, width, height, depth, border, format, type, data); +} +void opengl::context::texSubImage3D (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLenum type, + const void * pixels) +{ +#ifndef NOGGIT_DO_NOT_CHECK_FOR_OPENGL_ERRORS + verify_context_and_check_for_gl_errors const _ (_current_context, BOOST_CURRENT_FUNCTION); +#endif + return _4_1_core_func->glTexSubImage3D (target, level, xoffset,yoffset ,zoffset, width, height, depth, format, type, pixels); +} +void opengl::context::compressedTexSubImage3D (GLenum target, + GLint level, + GLint xoffset, + GLint yoffset, + GLint zoffset, + GLsizei width, + GLsizei height, + GLsizei depth, + GLenum format, + GLsizei imageSize, + const void * data) +{ +#ifndef NOGGIT_DO_NOT_CHECK_FOR_OPENGL_ERRORS + verify_context_and_check_for_gl_errors const _ (_current_context, BOOST_CURRENT_FUNCTION); +#endif + return _4_1_core_func->glCompressedTexSubImage3D (target, level, xoffset,yoffset ,zoffset, width, height, depth, format, imageSize, data); +} void opengl::context::compressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, GLvoid const* data) { #ifndef NOGGIT_DO_NOT_CHECK_FOR_OPENGL_ERRORS @@ -303,6 +344,13 @@ void opengl::context::compressedTexImage2D (GLenum target, GLint level, GLenum i #endif return _current_context->functions()->glCompressedTexImage2D (target, level, internalformat, width, height, border, imageSize, data); } +void opengl::context::compressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, GLvoid const* data) +{ +#ifndef NOGGIT_DO_NOT_CHECK_FOR_OPENGL_ERRORS + verify_context_and_check_for_gl_errors const _ (_current_context, BOOST_CURRENT_FUNCTION); +#endif + return _4_1_core_func->glCompressedTexImage3D (target, level, internalformat, width, height, depth, border, imageSize, data); +} void opengl::context::generateMipmap (GLenum target) { #ifndef NOGGIT_DO_NOT_CHECK_FOR_OPENGL_ERRORS