Przeglądaj źródła

LibGL+LibSoftGPU: Implement texture coordinate generation

Texture coordinate generation is the concept of automatically
generating vertex texture coordinates instead of using the provided
coordinates (i.e. `glTexCoord`).

This commit implements support for:

* The `GL_TEXTURE_GEN_Q/R/S/T` capabilities
* The `GL_OBJECT_LINEAR`, `GL_EYE_LINEAR`, `GL_SPHERE_MAP`,
  `GL_REFLECTION_MAP` and `GL_NORMAL_MAP` modes
* Object and eye plane coefficients (write-only at the moment)

This changeset allows Tux Racer to render its terrain :^)
Jelle Raaijmakers 3 lat temu
rodzic
commit
c19632128c

+ 102 - 8
Userland/Libraries/LibGL/SoftwareGLContext.cpp

@@ -652,6 +652,13 @@ void SoftwareGLContext::gl_enable(GLenum capability)
         m_active_texture_unit->set_texture_cube_map_enabled(true);
         m_sampler_config_is_dirty = true;
         break;
+    case GL_TEXTURE_GEN_Q:
+    case GL_TEXTURE_GEN_R:
+    case GL_TEXTURE_GEN_S:
+    case GL_TEXTURE_GEN_T:
+        texture_coordinate_generation(capability).enabled = true;
+        m_texcoord_generation_dirty = true;
+        break;
     default:
         RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
     }
@@ -728,6 +735,13 @@ void SoftwareGLContext::gl_disable(GLenum capability)
         m_active_texture_unit->set_texture_cube_map_enabled(false);
         m_sampler_config_is_dirty = true;
         break;
+    case GL_TEXTURE_GEN_Q:
+    case GL_TEXTURE_GEN_R:
+    case GL_TEXTURE_GEN_S:
+    case GL_TEXTURE_GEN_T:
+        texture_coordinate_generation(capability).enabled = false;
+        m_texcoord_generation_dirty = true;
+        break;
     default:
         RETURN_WITH_ERROR_IF(true, GL_INVALID_ENUM);
     }
@@ -763,6 +777,11 @@ GLboolean SoftwareGLContext::gl_is_enabled(GLenum capability)
         return rasterizer_options.scissor_enabled;
     case GL_STENCIL_TEST:
         return m_stencil_test_enabled;
+    case GL_TEXTURE_GEN_Q:
+    case GL_TEXTURE_GEN_R:
+    case GL_TEXTURE_GEN_S:
+    case GL_TEXTURE_GEN_T:
+        return texture_coordinate_generation(capability).enabled;
     }
 
     RETURN_VALUE_WITH_ERROR_IF(true, GL_INVALID_ENUM, 0);
@@ -2743,8 +2762,11 @@ void SoftwareGLContext::gl_tex_gen(GLenum coord, GLenum pname, GLint param)
             && param != GL_REFLECTION_MAP,
         GL_INVALID_ENUM);
     RETURN_WITH_ERROR_IF((coord == GL_R || coord == GL_Q) && param == GL_SPHERE_MAP, GL_INVALID_ENUM);
+    RETURN_WITH_ERROR_IF(coord == GL_Q && (param == GL_REFLECTION_MAP || param == GL_NORMAL_MAP), GL_INVALID_ENUM);
 
-    dbgln_if(GL_DEBUG, "gl_tex_gen({:#x}, {:#x}, {}): unimplemented", coord, pname, param);
+    GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
+    texture_coordinate_generation(capability).generation_mode = param;
+    m_texcoord_generation_dirty = true;
 }
 
 void SoftwareGLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* params)
@@ -2758,6 +2780,8 @@ void SoftwareGLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat co
             && pname != GL_EYE_PLANE,
         GL_INVALID_ENUM);
 
+    GLenum const capability = GL_TEXTURE_GEN_S + (coord - GL_S);
+
     switch (pname) {
     case GL_TEXTURE_GEN_MODE: {
         auto param = static_cast<GLenum>(params[0]);
@@ -2768,24 +2792,32 @@ void SoftwareGLContext::gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat co
                 && param != GL_REFLECTION_MAP,
             GL_INVALID_ENUM);
         RETURN_WITH_ERROR_IF((coord == GL_R || coord == GL_Q) && param == GL_SPHERE_MAP, GL_INVALID_ENUM);
+        RETURN_WITH_ERROR_IF(coord == GL_Q && (param == GL_REFLECTION_MAP || param == GL_NORMAL_MAP), GL_INVALID_ENUM);
 
-        dbgln_if(GL_DEBUG, "gl_tex_gen_floatv({:#x}, {:#x}, {:p}): unimplemented", coord, pname, params);
+        texture_coordinate_generation(capability).generation_mode = param;
         break;
     }
     case GL_OBJECT_PLANE:
+        texture_coordinate_generation(capability).object_plane_coefficients = { params[0], params[1], params[2], params[3] };
+        break;
     case GL_EYE_PLANE: {
-        GLfloat coefficient_p1 = params[0];
-        GLfloat coefficient_p2 = params[1];
-        GLfloat coefficient_p3 = params[2];
-        GLfloat coefficient_p4 = params[3];
+        auto inverted_model_view_matrix = m_model_view_matrix.inverse();
+        auto input_coefficients = FloatVector4 { params[0], params[1], params[2], params[3] };
 
-        dbgln_if(GL_DEBUG, "gl_tex_gen_floatv({:#x}, {:#x}, {:p}): unimplemented coefficients {} {} {} {}",
-            coord, pname, params, coefficient_p1, coefficient_p2, coefficient_p3, coefficient_p4);
+        // Note: we are allowed to store transformed coefficients here, according to the documentation on
+        //       `glGetTexGen`:
+        //
+        // "The returned values are those maintained in eye coordinates. They are not equal to the values
+        //  specified using glTexGen, unless the modelview matrix was identity when glTexGen was called."
+
+        texture_coordinate_generation(capability).eye_plane_coefficients = inverted_model_view_matrix * input_coefficients;
         break;
     }
     default:
         VERIFY_NOT_REACHED();
     }
+
+    m_texcoord_generation_dirty = true;
 }
 
 void SoftwareGLContext::present()
@@ -2796,6 +2828,7 @@ void SoftwareGLContext::present()
 void SoftwareGLContext::sync_device_config()
 {
     sync_device_sampler_config();
+    sync_device_texcoord_config();
 }
 
 void SoftwareGLContext::sync_device_sampler_config()
@@ -2915,4 +2948,65 @@ void SoftwareGLContext::sync_device_sampler_config()
     }
 }
 
+void SoftwareGLContext::sync_device_texcoord_config()
+{
+    if (!m_texcoord_generation_dirty)
+        return;
+    m_texcoord_generation_dirty = false;
+
+    auto options = m_rasterizer.options();
+
+    u8 enabled_coordinates = SoftGPU::TexCoordGenerationCoordinate::None;
+    for (GLenum capability = GL_TEXTURE_GEN_S; capability <= GL_TEXTURE_GEN_Q; ++capability) {
+        auto const context_coordinate_config = texture_coordinate_generation(capability);
+        if (!context_coordinate_config.enabled)
+            continue;
+
+        SoftGPU::TexCoordGenerationConfig* texcoord_generation_config;
+        switch (capability) {
+        case GL_TEXTURE_GEN_S:
+            enabled_coordinates |= SoftGPU::TexCoordGenerationCoordinate::S;
+            texcoord_generation_config = &options.texcoord_generation_config[0];
+            break;
+        case GL_TEXTURE_GEN_T:
+            enabled_coordinates |= SoftGPU::TexCoordGenerationCoordinate::T;
+            texcoord_generation_config = &options.texcoord_generation_config[1];
+            break;
+        case GL_TEXTURE_GEN_R:
+            enabled_coordinates |= SoftGPU::TexCoordGenerationCoordinate::R;
+            texcoord_generation_config = &options.texcoord_generation_config[2];
+            break;
+        case GL_TEXTURE_GEN_Q:
+            enabled_coordinates |= SoftGPU::TexCoordGenerationCoordinate::Q;
+            texcoord_generation_config = &options.texcoord_generation_config[3];
+            break;
+        default:
+            VERIFY_NOT_REACHED();
+        }
+
+        switch (context_coordinate_config.generation_mode) {
+        case GL_OBJECT_LINEAR:
+            texcoord_generation_config->mode = SoftGPU::TexCoordGenerationMode::ObjectLinear;
+            texcoord_generation_config->coefficients = context_coordinate_config.object_plane_coefficients;
+            break;
+        case GL_EYE_LINEAR:
+            texcoord_generation_config->mode = SoftGPU::TexCoordGenerationMode::EyeLinear;
+            texcoord_generation_config->coefficients = context_coordinate_config.eye_plane_coefficients;
+            break;
+        case GL_SPHERE_MAP:
+            texcoord_generation_config->mode = SoftGPU::TexCoordGenerationMode::SphereMap;
+            break;
+        case GL_REFLECTION_MAP:
+            texcoord_generation_config->mode = SoftGPU::TexCoordGenerationMode::ReflectionMap;
+            break;
+        case GL_NORMAL_MAP:
+            texcoord_generation_config->mode = SoftGPU::TexCoordGenerationMode::NormalMap;
+            break;
+        }
+    }
+    options.texcoord_generation_enabled_coordinates = enabled_coordinates;
+
+    m_rasterizer.set_options(options);
+}
+
 }

+ 37 - 0
Userland/Libraries/LibGL/SoftwareGLContext.h

@@ -140,6 +140,7 @@ public:
 private:
     void sync_device_config();
     void sync_device_sampler_config();
+    void sync_device_texcoord_config();
 
 private:
     template<typename T>
@@ -243,6 +244,42 @@ private:
     Vector<TextureUnit, 32> m_texture_units;
     TextureUnit* m_active_texture_unit;
 
+    // Texture coordinate generation state
+    struct TextureCoordinateGeneration {
+        bool enabled { false };
+        GLenum generation_mode { GL_EYE_LINEAR };
+        FloatVector4 object_plane_coefficients;
+        FloatVector4 eye_plane_coefficients;
+    };
+    Array<TextureCoordinateGeneration, 4> m_texture_coordinate_generation {
+        // S
+        TextureCoordinateGeneration {
+            .object_plane_coefficients = { 1.0f, 0.0f, 0.0f, 0.0f },
+            .eye_plane_coefficients = { 1.0f, 0.0f, 0.0f, 0.0f },
+        },
+        // T
+        TextureCoordinateGeneration {
+            .object_plane_coefficients = { 0.0f, 1.0f, 0.0f, 0.0f },
+            .eye_plane_coefficients = { 0.0f, 1.0f, 0.0f, 0.0f },
+        },
+        // R
+        TextureCoordinateGeneration {
+            .object_plane_coefficients = { 0.0f, 0.0f, 0.0f, 0.0f },
+            .eye_plane_coefficients = { 0.0f, 0.0f, 0.0f, 0.0f },
+        },
+        // Q
+        TextureCoordinateGeneration {
+            .object_plane_coefficients = { 0.0f, 0.0f, 0.0f, 0.0f },
+            .eye_plane_coefficients = { 0.0f, 0.0f, 0.0f, 0.0f },
+        },
+    };
+    bool m_texcoord_generation_dirty { true };
+
+    ALWAYS_INLINE TextureCoordinateGeneration& texture_coordinate_generation(GLenum capability)
+    {
+        return m_texture_coordinate_generation[capability - GL_TEXTURE_GEN_S];
+    }
+
     SoftGPU::Device m_rasterizer;
     SoftGPU::DeviceInfo const m_device_info;
     bool m_sampler_config_is_dirty { true };

+ 73 - 0
Userland/Libraries/LibSoftGPU/Device.cpp

@@ -533,6 +533,72 @@ DeviceInfo Device::info() const
     };
 }
 
+static void generate_texture_coordinates(Vertex& vertex, RasterizerOptions const& options)
+{
+    auto generate_coordinate = [&](size_t config_index) -> float {
+        auto mode = options.texcoord_generation_config[config_index].mode;
+
+        switch (mode) {
+        case TexCoordGenerationMode::ObjectLinear: {
+            auto coefficients = options.texcoord_generation_config[config_index].coefficients;
+            return coefficients.dot(vertex.position);
+        }
+        case TexCoordGenerationMode::EyeLinear: {
+            auto coefficients = options.texcoord_generation_config[config_index].coefficients;
+            return coefficients.dot(vertex.eye_coordinates);
+        }
+        case TexCoordGenerationMode::SphereMap: {
+            auto const eye_unit = vertex.eye_coordinates.normalized();
+            FloatVector3 const eye_unit_xyz = { eye_unit.x(), eye_unit.y(), eye_unit.z() };
+            auto const normal = vertex.normal;
+            auto reflection = eye_unit_xyz - normal * 2 * normal.dot(eye_unit_xyz);
+            reflection.set_z(reflection.z() + 1);
+            auto const reflection_value = (config_index == 0) ? reflection.x() : reflection.y();
+            return reflection_value / (2 * reflection.length()) + 0.5f;
+        }
+        case TexCoordGenerationMode::ReflectionMap: {
+            auto const eye_unit = vertex.eye_coordinates.normalized();
+            FloatVector3 const eye_unit_xyz = { eye_unit.x(), eye_unit.y(), eye_unit.z() };
+            auto const normal = vertex.normal;
+            auto reflection = eye_unit_xyz - normal * 2 * normal.dot(eye_unit_xyz);
+            switch (config_index) {
+            case 0:
+                return reflection.x();
+            case 1:
+                return reflection.y();
+            case 2:
+                return reflection.z();
+            default:
+                VERIFY_NOT_REACHED();
+            }
+        }
+        case TexCoordGenerationMode::NormalMap: {
+            auto const normal = vertex.normal;
+            switch (config_index) {
+            case 0:
+                return normal.x();
+            case 1:
+                return normal.y();
+            case 2:
+                return normal.z();
+            default:
+                VERIFY_NOT_REACHED();
+            }
+        }
+        default:
+            VERIFY_NOT_REACHED();
+        }
+    };
+
+    auto const enabled_coords = options.texcoord_generation_enabled_coordinates;
+    vertex.tex_coord = {
+        ((enabled_coords & TexCoordGenerationCoordinate::S) > 0) ? generate_coordinate(0) : vertex.tex_coord.x(),
+        ((enabled_coords & TexCoordGenerationCoordinate::T) > 0) ? generate_coordinate(1) : vertex.tex_coord.y(),
+        ((enabled_coords & TexCoordGenerationCoordinate::R) > 0) ? generate_coordinate(2) : vertex.tex_coord.z(),
+        ((enabled_coords & TexCoordGenerationCoordinate::Q) > 0) ? generate_coordinate(3) : vertex.tex_coord.w(),
+    };
+}
+
 void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const& model_view_transform, FloatMatrix3x3 const& normal_transform,
     FloatMatrix4x4 const& projection_transform, FloatMatrix4x4 const& texture_transform, Vector<Vertex> const& vertices,
     Vector<size_t> const& enabled_texture_units)
@@ -703,6 +769,13 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const&
             triangle.vertices[2].normal.normalize();
         }
 
+        // Generate texture coordinates if at least one coordinate is enabled
+        if (m_options.texcoord_generation_enabled_coordinates != TexCoordGenerationCoordinate::None) {
+            generate_texture_coordinates(triangle.vertices[0], m_options);
+            generate_texture_coordinates(triangle.vertices[1], m_options);
+            generate_texture_coordinates(triangle.vertices[2], m_options);
+        }
+
         // Apply texture transformation
         // FIXME: implement multi-texturing: texcoords should be stored per texture unit
         triangle.vertices[0].tex_coord = texture_transform * triangle.vertices[0].tex_coord;

+ 7 - 0
Userland/Libraries/LibSoftGPU/Device.h

@@ -26,6 +26,11 @@
 
 namespace SoftGPU {
 
+struct TexCoordGenerationConfig {
+    TexCoordGenerationMode mode { TexCoordGenerationMode::EyeLinear };
+    FloatVector4 coefficients {};
+};
+
 struct RasterizerOptions {
     bool shade_smooth { true };
     bool enable_depth_test { false };
@@ -57,6 +62,8 @@ struct RasterizerOptions {
     WindingOrder front_face { WindingOrder::CounterClockwise };
     bool cull_back { true };
     bool cull_front { false };
+    u8 texcoord_generation_enabled_coordinates { TexCoordGenerationCoordinate::None };
+    Array<TexCoordGenerationConfig, 4> texcoord_generation_config {};
 };
 
 inline static constexpr size_t const num_samplers = 32;

+ 17 - 0
Userland/Libraries/LibSoftGPU/Enums.h

@@ -68,4 +68,21 @@ enum class PrimitiveType {
     Quads,
 };
 
+enum TexCoordGenerationCoordinate {
+    None = 0x0,
+    S = 0x1,
+    T = 0x2,
+    R = 0x4,
+    Q = 0x8,
+    All = 0xF,
+};
+
+enum class TexCoordGenerationMode {
+    ObjectLinear,
+    EyeLinear,
+    SphereMap,
+    ReflectionMap,
+    NormalMap,
+};
+
 }