diff --git a/Userland/Libraries/LibGL/GL/gl.h b/Userland/Libraries/LibGL/GL/gl.h index 04e850b2d4..3a6f9a16a6 100644 --- a/Userland/Libraries/LibGL/GL/gl.h +++ b/Userland/Libraries/LibGL/GL/gl.h @@ -93,6 +93,8 @@ extern "C" { #define GL_SHADING_LANGUAGE_VERSION 0x8B8C // Get parameters +#define GL_COLOR_MATERIAL_FACE 0x0B55 +#define GL_COLOR_MATERIAL_MODE 0x0B56 #define GL_COLOR_MATERIAL 0x0B57 #define GL_FOG_START 0x0B63 #define GL_FOG_END 0x0B64 diff --git a/Userland/Libraries/LibGL/GLContext.h b/Userland/Libraries/LibGL/GLContext.h index e33eeb6b5d..896b005e7a 100644 --- a/Userland/Libraries/LibGL/GLContext.h +++ b/Userland/Libraries/LibGL/GLContext.h @@ -115,6 +115,7 @@ public: virtual void gl_lightfv(GLenum light, GLenum pname, GLfloat const* params) = 0; virtual void gl_materialf(GLenum face, GLenum pname, GLfloat param) = 0; virtual void gl_materialfv(GLenum face, GLenum pname, GLfloat const* params) = 0; + virtual void gl_color_material(GLenum face, GLenum mode) = 0; virtual void present() = 0; }; diff --git a/Userland/Libraries/LibGL/GLLights.cpp b/Userland/Libraries/LibGL/GLLights.cpp index bb95f74a99..06851f9d5e 100644 --- a/Userland/Libraries/LibGL/GLLights.cpp +++ b/Userland/Libraries/LibGL/GLLights.cpp @@ -14,8 +14,7 @@ extern GL::GLContext* g_gl_context; void glColorMaterial(GLenum face, GLenum mode) { - // FIXME: implement - dbgln_if(GL_DEBUG, "glColorMaterial({:#x}, {:#x}): unimplemented", face, mode); + g_gl_context->gl_color_material(face, mode); } void glLightf(GLenum light, GLenum pname, GLfloat param) diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.cpp b/Userland/Libraries/LibGL/SoftwareGLContext.cpp index c987df39bb..b43e47f200 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.cpp +++ b/Userland/Libraries/LibGL/SoftwareGLContext.cpp @@ -91,6 +91,12 @@ Optional SoftwareGLContext::get_context_parameter(GLenum name) return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_blend_source_factor) } }; case GL_BLUE_BITS: return ContextParameter { .type = GL_INT, .value = { .integer_value = sizeof(float) * 8 } }; + case GL_COLOR_MATERIAL: + return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_color_material_enabled } }; + case GL_COLOR_MATERIAL_FACE: + return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_color_material_face) } }; + case GL_COLOR_MATERIAL_MODE: + return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_color_material_mode) } }; case GL_CULL_FACE: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_cull_faces } }; case GL_DEPTH_BITS: @@ -644,6 +650,9 @@ void SoftwareGLContext::gl_enable(GLenum capability) bool update_rasterizer_options = false; switch (capability) { + case GL_COLOR_MATERIAL: + m_color_material_enabled = true; + break; case GL_CULL_FACE: m_cull_faces = true; rasterizer_options.enable_culling = true; @@ -741,6 +750,9 @@ void SoftwareGLContext::gl_disable(GLenum capability) bool update_rasterizer_options = false; switch (capability) { + case GL_COLOR_MATERIAL: + m_color_material_enabled = false; + break; case GL_CULL_FACE: m_cull_faces = false; rasterizer_options.enable_culling = false; @@ -2995,6 +3007,43 @@ void SoftwareGLContext::sync_light_state() m_light_state_is_dirty = false; + auto options = m_rasterizer.options(); + options.color_material_enabled = m_color_material_enabled; + switch (m_color_material_face) { + case GL_BACK: + options.color_material_face = SoftGPU::ColorMaterialFace::Back; + break; + case GL_FRONT: + options.color_material_face = SoftGPU::ColorMaterialFace::Front; + break; + case GL_FRONT_AND_BACK: + options.color_material_face = SoftGPU::ColorMaterialFace::FrontAndBack; + break; + default: + VERIFY_NOT_REACHED(); + } + switch (m_color_material_mode) { + case GL_AMBIENT: + options.color_material_mode = SoftGPU::ColorMaterialMode::Ambient; + break; + case GL_AMBIENT_AND_DIFFUSE: + options.color_material_mode = SoftGPU::ColorMaterialMode::Ambient; + options.color_material_mode = SoftGPU::ColorMaterialMode::Diffuse; + break; + case GL_DIFFUSE: + options.color_material_mode = SoftGPU::ColorMaterialMode::Diffuse; + break; + case GL_EMISSION: + options.color_material_mode = SoftGPU::ColorMaterialMode::Emissive; + break; + case GL_SPECULAR: + options.color_material_mode = SoftGPU::ColorMaterialMode::Specular; + break; + default: + VERIFY_NOT_REACHED(); + } + m_rasterizer.set_options(options); + 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); @@ -3249,4 +3298,26 @@ void SoftwareGLContext::gl_materialfv(GLenum face, GLenum pname, GLfloat const* m_light_state_is_dirty = true; } +void SoftwareGLContext::gl_color_material(GLenum face, GLenum mode) +{ + APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_color_material, face, mode); + RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); + + RETURN_WITH_ERROR_IF(face != GL_FRONT + && face != GL_BACK + && face != GL_FRONT_AND_BACK, + GL_INVALID_ENUM); + RETURN_WITH_ERROR_IF(mode != GL_EMISSION + && mode != GL_AMBIENT + && mode != GL_DIFFUSE + && mode != GL_SPECULAR + && mode != GL_AMBIENT_AND_DIFFUSE, + GL_INVALID_ENUM); + + m_color_material_face = face; + m_color_material_mode = mode; + + m_light_state_is_dirty = true; +} + } diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.h b/Userland/Libraries/LibGL/SoftwareGLContext.h index a9d9b5e6da..70ad47254d 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.h +++ b/Userland/Libraries/LibGL/SoftwareGLContext.h @@ -146,6 +146,7 @@ public: virtual void gl_lightfv(GLenum light, GLenum pname, GLfloat const* params) override; virtual void gl_materialf(GLenum face, GLenum pname, GLfloat param) override; virtual void gl_materialfv(GLenum face, GLenum pname, GLfloat const* params) override; + virtual void gl_color_material(GLenum face, GLenum mode) override; virtual void present() override; private: @@ -379,7 +380,8 @@ private: decltype(&SoftwareGLContext::gl_lightf), decltype(&SoftwareGLContext::gl_lightfv), decltype(&SoftwareGLContext::gl_materialf), - decltype(&SoftwareGLContext::gl_materialfv)>; + decltype(&SoftwareGLContext::gl_materialfv), + decltype(&SoftwareGLContext::gl_color_material)>; using ExtraSavedArguments = Variant< FloatMatrix4x4>; @@ -431,9 +433,13 @@ private: // Lighting configuration bool m_lighting_enabled { false }; - Vector m_light_states; Array m_material_states; + + // Color material + bool m_color_material_enabled { false }; + GLenum m_color_material_face { GL_FRONT_AND_BACK }; + GLenum m_color_material_mode { GL_AMBIENT_AND_DIFFUSE }; }; } diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp index 611c78e558..56988ff0a0 100644 --- a/Userland/Libraries/LibSoftGPU/Device.cpp +++ b/Userland/Libraries/LibSoftGPU/Device.cpp @@ -644,35 +644,38 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const& triangle.vertices[1].normal = transform_direction(model_view_transform, triangle.vertices[1].normal); triangle.vertices[2].normal = transform_direction(model_view_transform, triangle.vertices[2].normal); - // Transform eye coordinates into clip coordinates using the projection transform - triangle.vertices[0].clip_coordinates = projection_transform * triangle.vertices[0].eye_coordinates; - triangle.vertices[1].clip_coordinates = projection_transform * triangle.vertices[1].eye_coordinates; - triangle.vertices[2].clip_coordinates = projection_transform * triangle.vertices[2].eye_coordinates; - - // At this point, we're in clip space - // Here's where we do the clipping. This is a really crude implementation of the - // https://learnopengl.com/Getting-started/Coordinate-Systems - // "Note that if only a part of a primitive e.g. a triangle is outside the clipping volume OpenGL - // will reconstruct the triangle as one or more triangles to fit inside the clipping range. " - // - // ALL VERTICES ARE DEFINED IN A CLOCKWISE ORDER - - // Okay, let's do some face culling first - - m_clipped_vertices.clear_with_capacity(); - m_clipped_vertices.append(triangle.vertices[0]); - m_clipped_vertices.append(triangle.vertices[1]); - m_clipped_vertices.append(triangle.vertices[2]); - m_clipper.clip_triangle_against_frustum(m_clipped_vertices); - - if (m_clipped_vertices.size() < 3) - continue; - + // Calculate per-vertex lighting if (m_options.lighting_enabled) { - auto const& front_material = m_materials.at(0); - // Walk through each vertex - for (auto& vertex : m_clipped_vertices) { - FloatVector4 result_color = front_material.emissive + (front_material.ambient * m_lighting_model.scene_ambient_color); + auto const& material = m_materials.at(0); + for (auto& vertex : triangle.vertices) { + auto ambient = material.ambient; + auto diffuse = material.diffuse; + auto emissive = material.emissive; + auto specular = material.specular; + + if (m_options.color_material_enabled + && (m_options.color_material_face == ColorMaterialFace::Front || m_options.color_material_face == ColorMaterialFace::FrontAndBack)) { + switch (m_options.color_material_mode) { + case ColorMaterialMode::Ambient: + ambient = vertex.color; + break; + case ColorMaterialMode::AmbientAndDiffuse: + ambient = vertex.color; + diffuse = vertex.color; + break; + case ColorMaterialMode::Diffuse: + diffuse = vertex.color; + break; + case ColorMaterialMode::Emissive: + emissive = vertex.color; + break; + case ColorMaterialMode::Specular: + specular = vertex.color; + break; + } + } + + FloatVector4 result_color = emissive + (ambient * m_lighting_model.scene_ambient_color); for (auto const& light : m_lights) { if (!light.is_enabled) @@ -716,11 +719,11 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const& (void)m_lighting_model.two_sided_lighting; // Ambient - auto const ambient_component = front_material.ambient * light.ambient_intensity; + auto const ambient_component = ambient * light.ambient_intensity; // Diffuse auto const normal_dot_vertex_to_light = vertex.normal.dot(FloatVector3(vertex_to_light.x(), vertex_to_light.y(), vertex_to_light.z())); - auto const diffuse_component = ((front_material.diffuse * light.diffuse_intensity) * normal_dot_vertex_to_light).clamped(0.0f, 1.0f); + auto const diffuse_component = ((diffuse * light.diffuse_intensity) * normal_dot_vertex_to_light).clamped(0.0f, 1.0f); FloatVector4 color = ambient_component; color += diffuse_component; @@ -729,11 +732,35 @@ void Device::draw_primitives(PrimitiveType primitive_type, FloatMatrix4x4 const& } vertex.color = result_color; - vertex.color.set_w(front_material.diffuse.w()); // OpenGL 1.5 spec, page 59: "The A produced by lighting is the alpha value associated with diffuse color material" + vertex.color.set_w(diffuse.w()); // OpenGL 1.5 spec, page 59: "The A produced by lighting is the alpha value associated with diffuse color material" vertex.color.clamp(0.0f, 1.0f); } } + // Transform eye coordinates into clip coordinates using the projection transform + triangle.vertices[0].clip_coordinates = projection_transform * triangle.vertices[0].eye_coordinates; + triangle.vertices[1].clip_coordinates = projection_transform * triangle.vertices[1].eye_coordinates; + triangle.vertices[2].clip_coordinates = projection_transform * triangle.vertices[2].eye_coordinates; + + // At this point, we're in clip space + // Here's where we do the clipping. This is a really crude implementation of the + // https://learnopengl.com/Getting-started/Coordinate-Systems + // "Note that if only a part of a primitive e.g. a triangle is outside the clipping volume OpenGL + // will reconstruct the triangle as one or more triangles to fit inside the clipping range. " + // + // ALL VERTICES ARE DEFINED IN A CLOCKWISE ORDER + + // Okay, let's do some face culling first + + m_clipped_vertices.clear_with_capacity(); + m_clipped_vertices.append(triangle.vertices[0]); + m_clipped_vertices.append(triangle.vertices[1]); + m_clipped_vertices.append(triangle.vertices[2]); + m_clipper.clip_triangle_against_frustum(m_clipped_vertices); + + if (m_clipped_vertices.size() < 3) + continue; + for (auto& vec : m_clipped_vertices) { // To normalized device coordinates (NDC) auto const one_over_w = 1 / vec.clip_coordinates.w(); diff --git a/Userland/Libraries/LibSoftGPU/Device.h b/Userland/Libraries/LibSoftGPU/Device.h index e39c59f9b4..a0bdc80ae3 100644 --- a/Userland/Libraries/LibSoftGPU/Device.h +++ b/Userland/Libraries/LibSoftGPU/Device.h @@ -70,6 +70,9 @@ struct RasterizerOptions { Array texcoord_generation_config {}; Gfx::IntRect viewport; bool lighting_enabled { false }; + bool color_material_enabled { false }; + ColorMaterialFace color_material_face { ColorMaterialFace::FrontAndBack }; + ColorMaterialMode color_material_mode { ColorMaterialMode::AmbientAndDiffuse }; }; struct LightModelParameters { diff --git a/Userland/Libraries/LibSoftGPU/Enums.h b/Userland/Libraries/LibSoftGPU/Enums.h index e4b1f91630..de7c8d2b11 100644 --- a/Userland/Libraries/LibSoftGPU/Enums.h +++ b/Userland/Libraries/LibSoftGPU/Enums.h @@ -33,6 +33,20 @@ enum class BlendFactor { SrcAlphaSaturate, }; +enum class ColorMaterialFace { + Front, + Back, + FrontAndBack, +}; + +enum class ColorMaterialMode { + Ambient, + AmbientAndDiffuse, + Diffuse, + Emissive, + Specular, +}; + enum class DepthTestFunction { Never, Always,