diff --git a/Userland/Libraries/LibGL/GL/gl.h b/Userland/Libraries/LibGL/GL/gl.h index 03dc33e803..db3d562bd4 100644 --- a/Userland/Libraries/LibGL/GL/gl.h +++ b/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); diff --git a/Userland/Libraries/LibGL/GLContext.h b/Userland/Libraries/LibGL/GLContext.h index 7fadc63046..b2fdbd4b70 100644 --- a/Userland/Libraries/LibGL/GLContext.h +++ b/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; }; diff --git a/Userland/Libraries/LibGL/GLLights.cpp b/Userland/Libraries/LibGL/GLLights.cpp index e30aeb191b..cb11fe56e5 100644 --- a/Userland/Libraries/LibGL/GLLights.cpp +++ b/Userland/Libraries/LibGL/GLLights.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2021, Stephan Unverwerth * Copyright (c) 2021, Jelle Raaijmakers + * Copyright (c) 2022, Jesse Buhagiar * * 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) diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.cpp b/Userland/Libraries/LibGL/SoftwareGLContext.cpp index 53d68cf1dc..43cd46865b 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.cpp +++ b/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 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 / 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 / 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; +} } diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.h b/Userland/Libraries/LibGL/SoftwareGLContext.h index 4248743b67..e131eaebf0 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.h +++ b/Userland/Libraries/LibGL/SoftwareGLContext.h @@ -23,6 +23,7 @@ #include #include #include +#include #include 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 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 m_light_states; }; } diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp index 741a3283d5..e20b3b819f 100644 --- a/Userland/Libraries/LibSoftGPU/Device.cpp +++ b/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; +} + } diff --git a/Userland/Libraries/LibSoftGPU/Device.h b/Userland/Libraries/LibSoftGPU/Device.h index 3b42bacdf6..23e1b4a5ec 100644 --- a/Userland/Libraries/LibSoftGPU/Device.h +++ b/Userland/Libraries/LibSoftGPU/Device.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +93,7 @@ public: NonnullRefPtr 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 m_samplers; Vector m_enabled_texture_units; AlphaBlendFactors m_alpha_blend_factors; + Array m_lights; }; } diff --git a/Userland/Libraries/LibSoftGPU/Light/Light.h b/Userland/Libraries/LibSoftGPU/Light/Light.h new file mode 100644 index 0000000000..1bfe81661f --- /dev/null +++ b/Userland/Libraries/LibSoftGPU/Light/Light.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022, Jesse Buhagiar + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +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 ambient_intensity { 0.0f, 0.0f, 0.0f, 1.0f }; + Vector4 diffuse_intensity { 0.0f, 0.0f, 0.0f, 1.0f }; + Vector4 specular_intensity { 0.0f, 0.0f, 0.0f, 1.0f }; + Vector4 position { 0.0f, 0.0f, 1.0f, 0.0f }; + Vector3 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 / 180.0f }; +}; + +}