Procházet zdrojové kódy

LibGL: Implement `glLightf{v}` and fix `gl.h` prototype

This implements the `glLightf{v}` family of functions used to set
lighting parameters per light in the GL. It also fixes an incorrect
prototype for the user exposed version of `glLightf{v}` in which
`params` was not marked as `const`.
Jesse Buhagiar před 3 roky
rodič
revize
bf294612a7

+ 1 - 1
Userland/Libraries/LibGL/GL/gl.h

@@ -571,7 +571,7 @@ GLAPI void glFogi(GLenum pname, GLint param);
 GLAPI void glPixelStorei(GLenum pname, GLint param);
 GLAPI void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
 GLAPI void glLightf(GLenum light, GLenum pname, GLfloat param);
-GLAPI void glLightfv(GLenum light, GLenum pname, GLfloat* param);
+GLAPI void glLightfv(GLenum light, GLenum pname, GLfloat const* param);
 GLAPI void glLightModelf(GLenum pname, GLfloat param);
 GLAPI void glLightModelfv(GLenum pname, GLfloat const* params);
 GLAPI void glLightModeli(GLenum pname, GLint param);

+ 2 - 0
Userland/Libraries/LibGL/GLContext.h

@@ -121,6 +121,8 @@ public:
     virtual void gl_rect(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2) = 0;
     virtual void gl_tex_gen(GLenum coord, GLenum pname, GLint param) = 0;
     virtual void gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* params) = 0;
+    virtual void gl_lightf(GLenum light, GLenum pname, GLfloat param) = 0;
+    virtual void gl_lightfv(GLenum light, GLenum pname, GLfloat const* params) = 0;
 
     virtual void present() = 0;
 };

+ 4 - 5
Userland/Libraries/LibGL/GLLights.cpp

@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
  * Copyright (c) 2021, Jelle Raaijmakers <jelle@gmta.nl>
+ * Copyright (c) 2022, Jesse Buhagiar <jooster669@gmail.com>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -19,14 +20,12 @@ void glColorMaterial(GLenum face, GLenum mode)
 
 void glLightf(GLenum light, GLenum pname, GLfloat param)
 {
-    // FIXME: implement
-    dbgln_if(GL_DEBUG, "glLightf({}, {}, {}): unimplemented", light, pname, param);
+    g_gl_context->gl_lightf(light, pname, param);
 }
 
-void glLightfv(GLenum light, GLenum pname, GLfloat* param)
+void glLightfv(GLenum light, GLenum pname, GLfloat const* param)
 {
-    // FIXME: implement
-    dbgln_if(GL_DEBUG, "glLightfv({}, {}, {}): unimplemented", light, pname, param);
+    g_gl_context->gl_lightfv(light, pname, param);
 }
 
 void glLightModelf(GLenum pname, GLfloat param)

+ 125 - 0
Userland/Libraries/LibGL/SoftwareGLContext.cpp

@@ -63,6 +63,17 @@ SoftwareGLContext::SoftwareGLContext(Gfx::Bitmap& frontbuffer)
 {
     m_texture_units.resize(m_device_info.num_texture_units);
     m_active_texture_unit = &m_texture_units[0];
+
+    // Query the number lights from the device and set set up their state
+    // locally in the GL
+    m_light_states.resize(m_device_info.num_lights);
+
+    // Set-up light0's state, as it has a different default state
+    // to the other lights, as per the OpenGL 1.5 spec
+    auto& light0 = m_light_states.at(0);
+    light0.diffuse_intensity = { 1.0f, 1.0f, 1.0f, 1.0f };
+    light0.specular_intensity = { 1.0f, 1.0f, 1.0f, 1.0f };
+    m_light_state_is_dirty = true;
 }
 
 Optional<ContextParameter> SoftwareGLContext::get_context_parameter(GLenum name)
@@ -2839,6 +2850,7 @@ void SoftwareGLContext::sync_device_config()
 {
     sync_device_sampler_config();
     sync_device_texcoord_config();
+    sync_light_state();
 }
 
 void SoftwareGLContext::sync_device_sampler_config()
@@ -2958,6 +2970,34 @@ void SoftwareGLContext::sync_device_sampler_config()
     }
 }
 
+void SoftwareGLContext::sync_light_state()
+{
+    if (!m_light_state_is_dirty)
+        return;
+
+    m_light_state_is_dirty = false;
+
+    for (auto light_id = 0u; light_id < SoftGPU::NUM_LIGHTS; light_id++) {
+        SoftGPU::Light light;
+        auto const& current_light_state = m_light_states.at(light_id);
+
+        light.is_enabled = current_light_state.is_enabled;
+        light.ambient_intensity = current_light_state.ambient_intensity;
+        light.diffuse_intensity = current_light_state.diffuse_intensity;
+        light.specular_intensity = current_light_state.specular_intensity;
+        light.position = current_light_state.position;
+        light.spotlight_direction = current_light_state.spotlight_direction;
+        light.spotlight_exponent = current_light_state.spotlight_exponent;
+        light.spotlight_cutoff_angle = current_light_state.spotlight_cutoff_angle;
+        light.spotlight_cutoff_angle_rads = current_light_state.spotlight_cutoff_angle_rads;
+        light.constant_attenuation = current_light_state.constant_attenuation;
+        light.linear_attenuation = current_light_state.linear_attenuation;
+        light.quadratic_attenuation = current_light_state.quadratic_attenuation;
+
+        m_rasterizer.set_light_state(light_id, light);
+    }
+}
+
 void SoftwareGLContext::sync_device_texcoord_config()
 {
     if (!m_texcoord_generation_dirty)
@@ -3019,4 +3059,89 @@ void SoftwareGLContext::sync_device_texcoord_config()
     m_rasterizer.set_options(options);
 }
 
+void SoftwareGLContext::gl_lightf(GLenum light, GLenum pname, GLfloat param)
+{
+    APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_lightf, light, pname, param);
+
+    RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
+    RETURN_WITH_ERROR_IF(light < GL_LIGHT0 || light >= (GL_LIGHT0 + m_rasterizer.info().num_lights), GL_INVALID_ENUM);
+    RETURN_WITH_ERROR_IF(!(pname == GL_CONSTANT_ATTENUATION || pname == GL_LINEAR_ATTENUATION || pname == GL_QUADRATIC_ATTENUATION || pname != GL_SPOT_EXPONENT || pname != GL_SPOT_CUTOFF), GL_INVALID_ENUM);
+
+    auto& light_state = m_light_states.at(light - GL_LIGHT0);
+
+    switch (pname) {
+    case GL_CONSTANT_ATTENUATION:
+        light_state.constant_attenuation = param;
+        break;
+    case GL_LINEAR_ATTENUATION:
+        light_state.linear_attenuation = param;
+        break;
+    case GL_QUADRATIC_ATTENUATION:
+        light_state.quadratic_attenuation = param;
+        break;
+    case GL_SPOT_EXPONENT:
+        light_state.spotlight_exponent = param;
+        break;
+    case GL_SPOT_CUTOFF:
+        light_state.spotlight_cutoff_angle = param;
+        light_state.spotlight_cutoff_angle_rads = param * (AK::Pi<float> / 180.0f);
+        break;
+    default:
+        VERIFY_NOT_REACHED();
+    }
+
+    m_light_state_is_dirty = true;
+}
+
+void SoftwareGLContext::gl_lightfv(GLenum light, GLenum pname, GLfloat const* params)
+{
+    APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_lightfv, light, pname, params);
+    RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
+    RETURN_WITH_ERROR_IF(light < GL_LIGHT0 || light >= (GL_LIGHT0 + m_rasterizer.info().num_lights), GL_INVALID_ENUM);
+    RETURN_WITH_ERROR_IF(!(pname == GL_AMBIENT || pname == GL_DIFFUSE || pname == GL_SPECULAR || pname == GL_POSITION || pname == GL_CONSTANT_ATTENUATION || pname == GL_LINEAR_ATTENUATION || pname == GL_QUADRATIC_ATTENUATION || pname == GL_SPOT_CUTOFF || pname == GL_SPOT_EXPONENT || pname == GL_SPOT_DIRECTION), GL_INVALID_ENUM);
+
+    auto& light_state = m_light_states.at(light - GL_LIGHT0);
+
+    switch (pname) {
+    case GL_AMBIENT:
+        light_state.ambient_intensity = { params[0], params[1], params[2], params[3] };
+        break;
+    case GL_DIFFUSE:
+        light_state.diffuse_intensity = { params[0], params[1], params[2], params[3] };
+        break;
+    case GL_SPECULAR:
+        light_state.specular_intensity = { params[0], params[1], params[2], params[3] };
+        break;
+    case GL_POSITION:
+        light_state.position = { params[0], params[1], params[2], params[3] };
+        light_state.position = m_model_view_matrix * light_state.position;
+        break;
+    case GL_CONSTANT_ATTENUATION:
+        light_state.constant_attenuation = *params;
+        break;
+    case GL_LINEAR_ATTENUATION:
+        light_state.linear_attenuation = *params;
+        break;
+    case GL_QUADRATIC_ATTENUATION:
+        light_state.quadratic_attenuation = *params;
+        break;
+    case GL_SPOT_EXPONENT:
+        light_state.spotlight_exponent = *params;
+        break;
+    case GL_SPOT_CUTOFF:
+        light_state.spotlight_cutoff_angle = *params;
+        light_state.spotlight_cutoff_angle_rads = *params * (AK::Pi<float> / 180.0f);
+        break;
+    case GL_SPOT_DIRECTION: {
+        FloatVector4 direction_vector = { params[0], params[1], params[2], 0.0f };
+        direction_vector = m_model_view_matrix * direction_vector;
+        light_state.spotlight_direction = { direction_vector.x(), direction_vector.y(), direction_vector.z() };
+        break;
+    }
+    default:
+        VERIFY_NOT_REACHED();
+    }
+
+    m_light_state_is_dirty = true;
+}
 }

+ 10 - 1
Userland/Libraries/LibGL/SoftwareGLContext.h

@@ -23,6 +23,7 @@
 #include <LibGfx/Vector3.h>
 #include <LibSoftGPU/Clipper.h>
 #include <LibSoftGPU/Device.h>
+#include <LibSoftGPU/Light/Light.h>
 #include <LibSoftGPU/Vertex.h>
 
 namespace GL {
@@ -136,6 +137,8 @@ public:
     virtual void gl_rect(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2) override;
     virtual void gl_tex_gen(GLenum coord, GLenum pname, GLint param) override;
     virtual void gl_tex_gen_floatv(GLenum coord, GLenum pname, GLfloat const* params) override;
+    virtual void gl_lightf(GLenum light, GLenum pname, GLfloat param) override;
+    virtual void gl_lightfv(GLenum light, GLenum pname, GLfloat const* params) override;
 
     virtual void present() override;
 
@@ -143,6 +146,7 @@ private:
     void sync_device_config();
     void sync_device_sampler_config();
     void sync_device_texcoord_config();
+    void sync_light_state();
 
     template<typename T>
     T* store_in_listing(T value)
@@ -286,6 +290,7 @@ private:
     SoftGPU::Device m_rasterizer;
     SoftGPU::DeviceInfo const m_device_info;
     bool m_sampler_config_is_dirty { true };
+    bool m_light_state_is_dirty { true };
 
     struct Listing {
 
@@ -365,7 +370,9 @@ private:
             decltype(&SoftwareGLContext::gl_tex_gen_floatv),
             decltype(&SoftwareGLContext::gl_fogf),
             decltype(&SoftwareGLContext::gl_fogfv),
-            decltype(&SoftwareGLContext::gl_fogi)>;
+            decltype(&SoftwareGLContext::gl_fogi),
+            decltype(&SoftwareGLContext::gl_lightf),
+            decltype(&SoftwareGLContext::gl_lightfv)>;
 
         using ExtraSavedArguments = Variant<
             FloatMatrix4x4>;
@@ -419,6 +426,8 @@ private:
     bool m_lighting_enabled { false };
     FloatVector4 m_light_model_ambient { 0.2f, 0.2f, 0.2f, 1.0f };
     GLfloat m_light_model_two_side { 0.0f };
+
+    Vector<SoftGPU::Light> m_light_states;
 };
 
 }

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

@@ -1004,4 +1004,9 @@ void Device::set_sampler_config(unsigned sampler, SamplerConfig const& config)
     m_samplers[sampler].set_config(config);
 }
 
+void Device::set_light_state(unsigned int light_id, Light const& light)
+{
+    m_lights.at(light_id) = light;
+}
+
 }

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

@@ -22,6 +22,7 @@
 #include <LibSoftGPU/Enums.h>
 #include <LibSoftGPU/Image.h>
 #include <LibSoftGPU/ImageFormat.h>
+#include <LibSoftGPU/Light/Light.h>
 #include <LibSoftGPU/Sampler.h>
 #include <LibSoftGPU/Triangle.h>
 #include <LibSoftGPU/Vertex.h>
@@ -92,6 +93,7 @@ public:
     NonnullRefPtr<Image> create_image(ImageFormat, unsigned width, unsigned height, unsigned depth, unsigned levels, unsigned layers);
 
     void set_sampler_config(unsigned, SamplerConfig const&);
+    void set_light_state(unsigned, Light const&);
 
 private:
     void draw_statistics_overlay(Gfx::Bitmap&);
@@ -112,6 +114,7 @@ private:
     Array<Sampler, NUM_SAMPLERS> m_samplers;
     Vector<size_t> m_enabled_texture_units;
     AlphaBlendFactors m_alpha_blend_factors;
+    Array<Light, NUM_LIGHTS> m_lights;
 };
 
 }

+ 35 - 0
Userland/Libraries/LibSoftGPU/Light/Light.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2022, Jesse Buhagiar <jooster669@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibGfx/Vector3.h>
+#include <LibGfx/Vector4.h>
+
+namespace SoftGPU {
+
+struct Light {
+    bool is_enabled { false };
+
+    // According to the OpenGL 1.5 specification, page 56, all of the parameters
+    // for the following data members (positions, colors, and reals) are all
+    // floating point.
+    Vector4<float> ambient_intensity { 0.0f, 0.0f, 0.0f, 1.0f };
+    Vector4<float> diffuse_intensity { 0.0f, 0.0f, 0.0f, 1.0f };
+    Vector4<float> specular_intensity { 0.0f, 0.0f, 0.0f, 1.0f };
+    Vector4<float> position { 0.0f, 0.0f, 1.0f, 0.0f };
+    Vector3<float> spotlight_direction { 0.0f, 0.0f, -1.0f };
+
+    float spotlight_exponent { 0.0f };
+    float spotlight_cutoff_angle { 180.0f };
+    float constant_attenuation { 1.0f };  // This is referred to `k0i` in the OpenGL spec
+    float linear_attenuation { 0.0f };    // This is referred to `k1i` in the OpenGL spec
+    float quadratic_attenuation { 0.0f }; // This is referred to `k2i` in the OpenGL spec
+
+    float spotlight_cutoff_angle_rads { AK::Pi<float> / 180.0f };
+};
+
+}