diff --git a/Userland/Libraries/LibGL/CMakeLists.txt b/Userland/Libraries/LibGL/CMakeLists.txt index 82a62d6082d..81021c2cb1c 100644 --- a/Userland/Libraries/LibGL/CMakeLists.txt +++ b/Userland/Libraries/LibGL/CMakeLists.txt @@ -1,5 +1,6 @@ set(SOURCES Tex/NameAllocator.cpp + Tex/Sampler2D.cpp Tex/Texture2D.cpp Tex/TextureUnit.cpp Clipper.cpp diff --git a/Userland/Libraries/LibGL/SoftwareRasterizer.cpp b/Userland/Libraries/LibGL/SoftwareRasterizer.cpp index 8ef38b1a24d..a320a13ce41 100644 --- a/Userland/Libraries/LibGL/SoftwareRasterizer.cpp +++ b/Userland/Libraries/LibGL/SoftwareRasterizer.cpp @@ -435,7 +435,7 @@ void SoftwareRasterizer::submit_triangle(const GLTriangle& triangle, const Array continue; // FIXME: Don't assume Texture2D, _and_ work out how we blend/do multitexturing properly..... - texel = texel * static_ptr_cast(texture_unit.bound_texture())->sample_texel(uv); + texel = texel * static_ptr_cast(texture_unit.bound_texture())->sampler().sample(uv); } return texel; diff --git a/Userland/Libraries/LibGL/Tex/MipMap.h b/Userland/Libraries/LibGL/Tex/MipMap.h new file mode 100644 index 00000000000..790947b132a --- /dev/null +++ b/Userland/Libraries/LibGL/Tex/MipMap.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * Copyright (c) 2021, Stephan Unverwerth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace GL { + +class MipMap { +public: + MipMap() = default; + ~MipMap() = default; + + void set_width(GLsizei width) { m_width = width; } + void set_height(GLsizei height) { m_height = height; } + GLsizei width() const { return m_width; } + GLsizei height() const { return m_height; } + + Vector& pixel_data() { return m_pixel_data; } + const Vector& pixel_data() const { return m_pixel_data; } + + FloatVector4 texel(unsigned x, unsigned y) const + { + if (x >= (unsigned)m_width || y >= (unsigned)m_height) + return { 0, 0, 0, 0 }; + + u32 texel = m_pixel_data.at(y * m_width + x); + + return { + (texel & 0xff) / 255.f, + ((texel >> 8) & 0xff) / 255.f, + ((texel >> 16) & 0xff) / 255.f, + ((texel >> 24) & 0xff) / 255.f + }; + } + +private: + GLsizei m_width; + GLsizei m_height; + Vector m_pixel_data; +}; +} diff --git a/Userland/Libraries/LibGL/Tex/Sampler2D.cpp b/Userland/Libraries/LibGL/Tex/Sampler2D.cpp new file mode 100644 index 00000000000..e88babfae67 --- /dev/null +++ b/Userland/Libraries/LibGL/Tex/Sampler2D.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021, Stephan Unverwerth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "Sampler2D.h" + +#include +#include + +namespace GL { + +static constexpr float wrap_repeat(float value) +{ + return value - floorf(value); +} + +FloatVector4 Sampler2D::sample(FloatVector2 const& uv) const +{ + // FIXME: Calculate the correct mipmap level here, need to receive uv derivatives for that + unsigned lod = 0; + + MipMap const& mip = m_texture.mipmap(lod); + + float x = uv.x(); + float y = uv.y(); + + switch (m_wrap_s_mode) { + case GL_REPEAT: + x = wrap_repeat(x); + break; + + default: + VERIFY_NOT_REACHED(); + } + + switch (m_wrap_t_mode) { + case GL_REPEAT: + y = wrap_repeat(y); + break; + + default: + VERIFY_NOT_REACHED(); + } + + x *= mip.width() - 1; + y *= mip.height() - 1; + + return mip.texel(static_cast(x), static_cast(y)); +} + +} diff --git a/Userland/Libraries/LibGL/Tex/Sampler2D.h b/Userland/Libraries/LibGL/Tex/Sampler2D.h new file mode 100644 index 00000000000..0de0dec1af6 --- /dev/null +++ b/Userland/Libraries/LibGL/Tex/Sampler2D.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021, Stephan Unverwerth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include + +namespace GL { + +class Texture2D; + +class Sampler2D final { +public: + Sampler2D(Texture2D const& texture) + : m_texture(texture) + { + } + + GLint min_filter() const { return m_min_filter; } + GLint mag_filter() const { return m_mag_filter; } + GLint wrap_s_mode() const { return m_wrap_s_mode; } + GLint wrap_t_mode() const { return m_wrap_t_mode; } + + void set_min_filter(GLint value) { m_min_filter = value; } + void set_mag_filter(GLint value) { m_mag_filter = value; } + void set_wrap_s_mode(GLint value) { m_wrap_s_mode = value; } + void set_wrap_t_mode(GLint value) { m_wrap_t_mode = value; } + + FloatVector4 sample(FloatVector2 const& uv) const; + +private: + Texture2D const& m_texture; + + GLint m_min_filter { GL_NEAREST_MIPMAP_LINEAR }; + GLint m_mag_filter { GL_LINEAR }; + GLint m_wrap_s_mode { GL_REPEAT }; + GLint m_wrap_t_mode { GL_REPEAT }; +}; +} diff --git a/Userland/Libraries/LibGL/Tex/Texture2D.cpp b/Userland/Libraries/LibGL/Tex/Texture2D.cpp index 3e684e7ebdd..ebc7465d9d1 100644 --- a/Userland/Libraries/LibGL/Tex/Texture2D.cpp +++ b/Userland/Libraries/LibGL/Tex/Texture2D.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Jesse Buhagiar + * Copyright (c) 2021, Stephan Unverwerth * * SPDX-License-Identifier: BSD-2-Clause */ @@ -112,24 +113,12 @@ void Texture2D::upload_texture_data(GLenum, GLint lod, GLint internal_format, GL mip.set_height(height); } -FloatVector4 Texture2D::sample_texel(const FloatVector2& uv) const +MipMap const& Texture2D::mipmap(unsigned lod) const { - auto& mip = m_mipmaps.at(0); + if (lod >= m_mipmaps.size()) + return m_mipmaps.at(m_mipmaps.size() - 1); - // FIXME: Remove this to prevent a crash when we have proper texture binding - if (mip.width() == 0 || mip.height() == 0) - return { 1.0f, 1.0f, 1.0f, 1.0f }; - - u32 u = static_cast(uv.x() * mip.width()); - u32 v = static_cast(uv.y() * mip.height()); - - u32 pixel = mip.pixel_data().at(v * mip.width() + u); - - float b0 = ((pixel)&0xff) / 255.0f; - float b1 = ((pixel >> 8) & 0xff) / 255.0f; - float b2 = ((pixel >> 16) & 0xff) / 255.0f; - - return { b0, b1, b2, 1.0f }; + return m_mipmaps.at(lod); } } diff --git a/Userland/Libraries/LibGL/Tex/Texture2D.h b/Userland/Libraries/LibGL/Tex/Texture2D.h index 82ed949446c..c1d3022dcf2 100644 --- a/Userland/Libraries/LibGL/Tex/Texture2D.h +++ b/Userland/Libraries/LibGL/Tex/Texture2D.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Jesse Buhagiar + * Copyright (c) 2021, Stephan Unverwerth * * SPDX-License-Identifier: BSD-2-Clause */ @@ -12,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -23,46 +26,22 @@ public: static constexpr u16 MAX_TEXTURE_SIZE = 2048; static constexpr u8 LOG2_MAX_TEXTURE_SIZE = 11; - class MipMap { - public: - MipMap() = default; - ~MipMap() = default; - - void set_width(GLsizei width) { m_width = width; } - void set_height(GLsizei height) { m_height = height; } - GLsizei width() const { return m_width; } - GLsizei height() const { return m_height; } - - Vector& pixel_data() { return m_pixel_data; } - const Vector& pixel_data() const { return m_pixel_data; } - - private: - GLsizei m_width; - GLsizei m_height; - Vector m_pixel_data; - }; - - // To quote the Khronos documentation: - // "You could say that a texture object contains a sampler object, which you access through the texture interface." - // FIXME: Better name? - struct TextureSamplerParamaters { - GLint m_min_filter { GL_NEAREST_MIPMAP_LINEAR }; - GLint m_mag_filter { GL_LINEAR }; - GLint m_wrap_s_mode { GL_REPEAT }; - GLint m_wrap_t_mode { GL_REPEAT }; - }; - public: - Texture2D() = default; + Texture2D() + : m_sampler(*this) + { + } ~Texture2D() { } virtual bool is_texture_2d() const override { return true; } void upload_texture_data(GLenum target, GLint lod, GLint internal_format, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels); void replace_sub_texture_data(GLint lod, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* data); - FloatVector4 sample_texel(const FloatVector2& uv) const; + + MipMap const& mipmap(unsigned lod) const; GLenum internal_format() const { return m_internal_format; } + Sampler2D const& sampler() const { return m_sampler; } private: template @@ -76,7 +55,7 @@ private: // FIXME: Mipmaps are currently unused, but we have the plumbing for it at least Array m_mipmaps; GLenum m_internal_format; - TextureSamplerParamaters m_sampler_params; + Sampler2D m_sampler; }; }