From 0836912a6d13a185a53b9739c7b10d8b5f214c71 Mon Sep 17 00:00:00 2001 From: RKBethke Date: Fri, 6 May 2022 09:40:55 +0000 Subject: [PATCH] LibGL+LibGPU+LibSoftGPU: Implement and expose glClipPlane This commit implements glClipPlane and its supporting calls, backed by new support for user-defined clip planes in the software GPU clipper. This fixes some visual bugs seen in the Quake III port, in which mirrors would only reflect correctly from close distances. --- Userland/Libraries/LibGL/CMakeLists.txt | 3 +- Userland/Libraries/LibGL/ClipPlanes.cpp | 57 +++++++++ Userland/Libraries/LibGL/ContextParameter.cpp | 24 ++++ Userland/Libraries/LibGL/GL/gl.h | 2 + Userland/Libraries/LibGL/GLAPI.cpp | 5 + Userland/Libraries/LibGL/GLContext.cpp | 11 +- Userland/Libraries/LibGL/GLContext.h | 9 ++ Userland/Libraries/LibGPU/Device.h | 1 + Userland/Libraries/LibGPU/DeviceInfo.h | 1 + Userland/Libraries/LibSoftGPU/Clipper.cpp | 117 +++++++++++------- Userland/Libraries/LibSoftGPU/Clipper.h | 14 ++- Userland/Libraries/LibSoftGPU/Config.h | 1 + Userland/Libraries/LibSoftGPU/Device.cpp | 9 ++ Userland/Libraries/LibSoftGPU/Device.h | 2 + 14 files changed, 193 insertions(+), 63 deletions(-) create mode 100644 Userland/Libraries/LibGL/ClipPlanes.cpp diff --git a/Userland/Libraries/LibGL/CMakeLists.txt b/Userland/Libraries/LibGL/CMakeLists.txt index e4bfda91143..f931ab479db 100644 --- a/Userland/Libraries/LibGL/CMakeLists.txt +++ b/Userland/Libraries/LibGL/CMakeLists.txt @@ -1,10 +1,11 @@ set(SOURCES + ClipPlanes.cpp ContextParameter.cpp GLAPI.cpp GLContext.cpp - Matrix.cpp Lighting.cpp Lists.cpp + Matrix.cpp Stencil.cpp Tex/NameAllocator.cpp Tex/Texture2D.cpp diff --git a/Userland/Libraries/LibGL/ClipPlanes.cpp b/Userland/Libraries/LibGL/ClipPlanes.cpp new file mode 100644 index 00000000000..52863abb538 --- /dev/null +++ b/Userland/Libraries/LibGL/ClipPlanes.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021, Jesse Buhagiar + * Copyright (c) 2021, Stephan Unverwerth + * Copyright (c) 2022, Jelle Raaijmakers + * Copyright (c) 2022, Ryan Bethke + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace GL { + +void GLContext::gl_clip_plane(GLenum plane, GLdouble const* equation) +{ + APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_clip_plane, plane, equation); + + RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); + RETURN_WITH_ERROR_IF((plane < GL_CLIP_PLANE0) || (plane > GL_CLIP_PLANE5), GL_INVALID_ENUM); + + auto plane_idx = static_cast(plane) - GL_CLIP_PLANE0; + + auto eqn = FloatVector4(equation[0], equation[1], equation[2], equation[3]); + m_clip_plane_attributes.eye_clip_plane[plane_idx] = m_model_view_matrix * eqn; + m_clip_planes_dirty = true; +} + +void GLContext::gl_get_clip_plane(GLenum plane, GLdouble* equation) +{ + RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); + RETURN_WITH_ERROR_IF((plane < GL_CLIP_PLANE0) || (plane > GL_CLIP_PLANE5), GL_INVALID_ENUM); + + auto plane_idx = static_cast(plane) - GL_CLIP_PLANE0; + equation[0] = static_cast(m_clip_plane_attributes.eye_clip_plane[plane_idx][0]); + equation[1] = static_cast(m_clip_plane_attributes.eye_clip_plane[plane_idx][1]); + equation[2] = static_cast(m_clip_plane_attributes.eye_clip_plane[plane_idx][2]); + equation[3] = static_cast(m_clip_plane_attributes.eye_clip_plane[plane_idx][3]); +} + +void GLContext::sync_clip_planes() +{ + if (!m_clip_planes_dirty) + return; + m_clip_planes_dirty = false; + + // TODO: Replace magic number 6 with device-dependent constant + Vector user_clip_planes; + for (size_t plane_idx = 0; plane_idx < 6; ++plane_idx) { + if ((m_clip_plane_attributes.enabled & (1 << plane_idx)) != 0u) { + user_clip_planes.append(m_clip_plane_attributes.eye_clip_plane[plane_idx]); + } + } + m_rasterizer->set_clip_planes(user_clip_planes); +} + +} diff --git a/Userland/Libraries/LibGL/ContextParameter.cpp b/Userland/Libraries/LibGL/ContextParameter.cpp index 1b702a2f19c..79c7d6ee126 100644 --- a/Userland/Libraries/LibGL/ContextParameter.cpp +++ b/Userland/Libraries/LibGL/ContextParameter.cpp @@ -52,6 +52,8 @@ Optional GLContext::get_context_parameter(GLenum name) return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_lighting_enabled } }; case GL_LINE_SMOOTH: return ContextParameter { .type = GL_BOOL, .is_capability = true, .value = { .boolean_value = m_line_smooth } }; + case GL_MAX_CLIP_PLANES: + return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_device_info.max_clip_planes) } }; case GL_MAX_LIGHTS: return ContextParameter { .type = GL_INT, .value = { .integer_value = static_cast(m_device_info.num_lights) } }; case GL_MAX_MODELVIEW_STACK_DEPTH: @@ -169,6 +171,17 @@ void GLContext::gl_disable(GLenum capability) bool update_rasterizer_options = false; switch (capability) { + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: { + auto plane_idx = static_cast(capability) - GL_CLIP_PLANE0; + m_clip_plane_attributes.enabled &= ~(1 << plane_idx); + m_clip_planes_dirty = true; + break; + } case GL_COLOR_MATERIAL: m_color_material_enabled = false; break; @@ -308,6 +321,17 @@ void GLContext::gl_enable(GLenum capability) bool update_rasterizer_options = false; switch (capability) { + case GL_CLIP_PLANE0: + case GL_CLIP_PLANE1: + case GL_CLIP_PLANE2: + case GL_CLIP_PLANE3: + case GL_CLIP_PLANE4: + case GL_CLIP_PLANE5: { + auto plane_idx = static_cast(capability) - GL_CLIP_PLANE0; + m_clip_plane_attributes.enabled |= (1 << plane_idx); + m_clip_planes_dirty = true; + break; + } case GL_COLOR_MATERIAL: m_color_material_enabled = true; break; diff --git a/Userland/Libraries/LibGL/GL/gl.h b/Userland/Libraries/LibGL/GL/gl.h index 7f1a7522bff..73c93f97dd7 100644 --- a/Userland/Libraries/LibGL/GL/gl.h +++ b/Userland/Libraries/LibGL/GL/gl.h @@ -481,6 +481,7 @@ extern "C" { #define GL_ADD 0x0104 // User clipping planes +#define GL_MAX_CLIP_PLANES 0x0D32 #define GL_CLIP_PLANE0 0x3000 #define GL_CLIP_PLANE1 0x3001 #define GL_CLIP_PLANE2 0x3002 @@ -682,6 +683,7 @@ GLAPI void glRecti(GLint x1, GLint y1, GLint x2, GLint y2); GLAPI void glGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint* params); GLAPI void glPointSize(GLfloat size); GLAPI void glClipPlane(GLenum plane, GLdouble const* equation); +GLAPI void glGetClipPlane(GLenum plane, GLdouble* equation); GLAPI void glArrayElement(GLint i); GLAPI void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); diff --git a/Userland/Libraries/LibGL/GLAPI.cpp b/Userland/Libraries/LibGL/GLAPI.cpp index 68f5da4e055..b8aed682e5c 100644 --- a/Userland/Libraries/LibGL/GLAPI.cpp +++ b/Userland/Libraries/LibGL/GLAPI.cpp @@ -399,6 +399,11 @@ void glGetBooleanv(GLenum pname, GLboolean* data) g_gl_context->gl_get_booleanv(pname, data); } +void glGetClipPlane(GLenum plane, GLdouble* equation) +{ + g_gl_context->gl_get_clip_plane(plane, equation); +} + void glGetDoublev(GLenum pname, GLdouble* params) { g_gl_context->gl_get_doublev(pname, params); diff --git a/Userland/Libraries/LibGL/GLContext.cpp b/Userland/Libraries/LibGL/GLContext.cpp index 367074c4465..76c6c48fd4b 100644 --- a/Userland/Libraries/LibGL/GLContext.cpp +++ b/Userland/Libraries/LibGL/GLContext.cpp @@ -794,16 +794,6 @@ void GLContext::gl_depth_mask(GLboolean flag) m_rasterizer->set_options(options); } -void GLContext::gl_clip_plane(GLenum plane, [[maybe_unused]] GLdouble const* equation) -{ - APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_clip_plane, plane, equation); - - RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION); - RETURN_WITH_ERROR_IF((plane < GL_CLIP_PLANE0) || (plane > GL_CLIP_PLANE5), GL_INVALID_ENUM); - - dbgln_if(GL_DEBUG, "GLContext FIXME: implement gl_clip_plane() (equation = [{} {} {} {}])", equation[0], equation[1], equation[2], equation[3]); -} - void GLContext::gl_draw_pixels(GLsizei width, GLsizei height, GLenum format, GLenum type, void const* data) { APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_draw_pixels, width, height, format, type, data); @@ -1215,6 +1205,7 @@ void GLContext::sync_device_config() sync_device_texcoord_config(); sync_light_state(); sync_stencil_configuration(); + sync_clip_planes(); } void GLContext::build_extension_string() diff --git a/Userland/Libraries/LibGL/GLContext.h b/Userland/Libraries/LibGL/GLContext.h index 9dc1f6c413c..0a77c43a71e 100644 --- a/Userland/Libraries/LibGL/GLContext.h +++ b/Userland/Libraries/LibGL/GLContext.h @@ -197,6 +197,7 @@ public: void gl_get_light(GLenum light, GLenum pname, void* params, GLenum type); void gl_get_material(GLenum face, GLenum pname, void* params, GLenum type); void gl_clip_plane(GLenum plane, GLdouble const* equation); + void gl_get_clip_plane(GLenum plane, GLdouble* equation); void gl_array_element(GLint i); void gl_copy_tex_sub_image_2d(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); void gl_point_size(GLfloat size); @@ -207,6 +208,7 @@ private: void sync_device_texcoord_config(); void sync_light_state(); void sync_stencil_configuration(); + void sync_clip_planes(); void build_extension_string(); @@ -307,6 +309,13 @@ private: GLenum m_current_read_buffer = GL_BACK; GLenum m_current_draw_buffer = GL_BACK; + // User-defined clip planes + struct ClipPlaneAttributes { + Array eye_clip_plane; // TODO: Change to use device-defined constant + GLuint enabled { 0 }; + } m_clip_plane_attributes; + bool m_clip_planes_dirty { true }; + // Client side arrays bool m_client_side_vertex_array_enabled { false }; bool m_client_side_color_array_enabled { false }; diff --git a/Userland/Libraries/LibGPU/Device.h b/Userland/Libraries/LibGPU/Device.h index 43ad35da133..c90f1a1c937 100644 --- a/Userland/Libraries/LibGPU/Device.h +++ b/Userland/Libraries/LibGPU/Device.h @@ -61,6 +61,7 @@ public: virtual void set_light_state(unsigned, Light const&) = 0; virtual void set_material_state(Face, Material const&) = 0; virtual void set_stencil_configuration(Face, StencilConfiguration const&) = 0; + virtual void set_clip_planes(Vector const&) = 0; virtual RasterPosition raster_position() const = 0; virtual void set_raster_position(RasterPosition const& raster_position) = 0; diff --git a/Userland/Libraries/LibGPU/DeviceInfo.h b/Userland/Libraries/LibGPU/DeviceInfo.h index 4c9ba9fcea8..926947b1b9e 100644 --- a/Userland/Libraries/LibGPU/DeviceInfo.h +++ b/Userland/Libraries/LibGPU/DeviceInfo.h @@ -15,6 +15,7 @@ struct DeviceInfo final { String device_name; unsigned num_texture_units; unsigned num_lights; + unsigned max_clip_planes; u8 stencil_bits; bool supports_npot_textures; }; diff --git a/Userland/Libraries/LibSoftGPU/Clipper.cpp b/Userland/Libraries/LibSoftGPU/Clipper.cpp index e26b5ec707d..b9a6351058b 100644 --- a/Userland/Libraries/LibSoftGPU/Clipper.cpp +++ b/Userland/Libraries/LibSoftGPU/Clipper.cpp @@ -7,6 +7,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -17,40 +18,42 @@ namespace SoftGPU { template static constexpr bool point_within_clip_plane(FloatVector4 const& vertex) { - if constexpr (plane == Clipper::ClipPlane::LEFT) + if constexpr (plane == Clipper::ClipPlane::Left) return vertex.x() >= -vertex.w(); - else if constexpr (plane == Clipper::ClipPlane::RIGHT) + else if constexpr (plane == Clipper::ClipPlane::Right) return vertex.x() <= vertex.w(); - else if constexpr (plane == Clipper::ClipPlane::TOP) + else if constexpr (plane == Clipper::ClipPlane::Top) return vertex.y() <= vertex.w(); - else if constexpr (plane == Clipper::ClipPlane::BOTTOM) + else if constexpr (plane == Clipper::ClipPlane::Bottom) return vertex.y() >= -vertex.w(); - else if constexpr (plane == Clipper::ClipPlane::NEAR) + else if constexpr (plane == Clipper::ClipPlane::Near) return vertex.z() >= -vertex.w(); - else if constexpr (plane == Clipper::ClipPlane::FAR) + else if constexpr (plane == Clipper::ClipPlane::Far) return vertex.z() <= vertex.w(); return false; } -template -static constexpr GPU::Vertex clip_intersection_point(GPU::Vertex const& p1, GPU::Vertex const& p2) +static bool point_within_user_plane(FloatVector4 const& vertex, FloatVector4 const& user_plane) { - constexpr FloatVector4 clip_plane_normals[] = { - { 1, 0, 0, 1 }, // Left Plane - { -1, 0, 0, 1 }, // Right Plane - { 0, -1, 0, 1 }, // Top Plane - { 0, 1, 0, 1 }, // Bottom plane - { 0, 0, 1, 1 }, // Near Plane - { 0, 0, -1, 1 } // Far Plane - }; - constexpr auto clip_plane_normal = clip_plane_normals[to_underlying(plane)]; + return vertex.dot(user_plane) >= 0; +} - // See https://www.microsoft.com/en-us/research/wp-content/uploads/1978/01/p245-blinn.pdf - // "Clipping Using Homogeneous Coordinates" Blinn/Newell, 1978 - // Clip plane normals have W=1 so the vertices' W coordinates are included in x1 and x2. +template +bool point_within_plane(GPU::Vertex const& vertex, FloatVector4 const& user_plane) +{ + if constexpr (plane == Clipper::ClipPlane::User) + return point_within_user_plane(vertex.eye_coordinates, user_plane); + else + return point_within_clip_plane(vertex.clip_coordinates); +} - auto const x1 = clip_plane_normal.dot(p1.clip_coordinates); - auto const x2 = clip_plane_normal.dot(p2.clip_coordinates); +template +static GPU::Vertex clip_intersection_point(GPU::Vertex const& p1, GPU::Vertex const& p2, FloatVector4 const& plane_normal) +{ + auto p1_coordinates = (plane == Clipper::ClipPlane::User) ? p1.eye_coordinates : p1.clip_coordinates; + auto p2_coordinates = (plane == Clipper::ClipPlane::User) ? p2.eye_coordinates : p2.clip_coordinates; + auto x1 = plane_normal.dot(p1_coordinates); + auto x2 = plane_normal.dot(p2_coordinates); auto const a = x1 / (x1 - x2); GPU::Vertex out; @@ -65,7 +68,7 @@ static constexpr GPU::Vertex clip_intersection_point(GPU::Vertex const& p1, GPU: } template -FLATTEN static constexpr void clip_plane(Vector& input_list, Vector& output_list) +FLATTEN static void clip_plane(Vector& input_list, Vector& output_list, FloatVector4 const& clip_plane) { output_list.clear_with_capacity(); @@ -74,20 +77,20 @@ FLATTEN static constexpr void clip_plane(Vector& input_list, Vector return; auto const* prev_vec = &input_list.data()[0]; - auto is_prev_point_within_clip_plane = point_within_clip_plane(prev_vec->clip_coordinates); + auto is_prev_point_within_plane = point_within_plane(*prev_vec, clip_plane); for (size_t i = 1; i <= input_list_size; i++) { auto const& curr_vec = input_list[i % input_list_size]; - auto const is_curr_point_within_clip_plane = point_within_clip_plane(curr_vec.clip_coordinates); + auto const is_curr_point_within_plane = point_within_plane(curr_vec, clip_plane); - if (is_curr_point_within_clip_plane != is_prev_point_within_clip_plane) - output_list.append(clip_intersection_point(*prev_vec, curr_vec)); + if (is_curr_point_within_plane != is_prev_point_within_plane) + output_list.append(clip_intersection_point(*prev_vec, curr_vec, clip_plane)); - if (is_curr_point_within_clip_plane) + if (is_curr_point_within_plane) output_list.append(curr_vec); prev_vec = &curr_vec; - is_prev_point_within_clip_plane = is_curr_point_within_clip_plane; + is_prev_point_within_plane = is_curr_point_within_plane; } } @@ -97,9 +100,9 @@ void Clipper::clip_points_against_frustum(Vector& vertices) for (auto& vertex : vertices) { auto const coords = vertex.clip_coordinates; - if (point_within_clip_plane(coords) && point_within_clip_plane(coords) - && point_within_clip_plane(coords) && point_within_clip_plane(coords) - && point_within_clip_plane(coords) && point_within_clip_plane(coords)) + if (point_within_clip_plane(coords) && point_within_clip_plane(coords) + && point_within_clip_plane(coords) && point_within_clip_plane(coords) + && point_within_clip_plane(coords) && point_within_clip_plane(coords)) m_vertex_buffer.append(vertex); } @@ -107,39 +110,61 @@ void Clipper::clip_points_against_frustum(Vector& vertices) vertices.extend(m_vertex_buffer); } +constexpr FloatVector4 clip_plane_eqns[] = { + { 1, 0, 0, 1 }, // Left Plane + { -1, 0, 0, 1 }, // Right Plane + { 0, -1, 0, 1 }, // Top Plane + { 0, 1, 0, 1 }, // Bottom plane + { 0, 0, 1, 1 }, // Near Plane + { 0, 0, -1, 1 } // Far Plane +}; + template static constexpr bool constrain_line_within_plane(GPU::Vertex& from, GPU::Vertex& to) { + constexpr auto clip_plane_eqn = clip_plane_eqns[to_underlying(plane)]; + auto from_within_plane = point_within_clip_plane(from.clip_coordinates); auto to_within_plane = point_within_clip_plane(to.clip_coordinates); if (!from_within_plane && !to_within_plane) return false; if (!from_within_plane) - from = clip_intersection_point(from, to); + from = clip_intersection_point(from, to, clip_plane_eqn); else if (!to_within_plane) - to = clip_intersection_point(from, to); + to = clip_intersection_point(from, to, clip_plane_eqn); return true; } bool Clipper::clip_line_against_frustum(GPU::Vertex& from, GPU::Vertex& to) { - return constrain_line_within_plane(from, to) - && constrain_line_within_plane(from, to) - && constrain_line_within_plane(from, to) - && constrain_line_within_plane(from, to) - && constrain_line_within_plane(from, to) - && constrain_line_within_plane(from, to); + return constrain_line_within_plane(from, to) + && constrain_line_within_plane(from, to) + && constrain_line_within_plane(from, to) + && constrain_line_within_plane(from, to) + && constrain_line_within_plane(from, to) + && constrain_line_within_plane(from, to); } void Clipper::clip_triangle_against_frustum(Vector& input_verts) { // FIXME C++23. Static reflection will provide looping over all enum values. - clip_plane(input_verts, m_vertex_buffer); - clip_plane(m_vertex_buffer, input_verts); - clip_plane(input_verts, m_vertex_buffer); - clip_plane(m_vertex_buffer, input_verts); - clip_plane(input_verts, m_vertex_buffer); - clip_plane(m_vertex_buffer, input_verts); + clip_plane(input_verts, m_vertex_buffer, clip_plane_eqns[0]); + clip_plane(m_vertex_buffer, input_verts, clip_plane_eqns[1]); + clip_plane(input_verts, m_vertex_buffer, clip_plane_eqns[2]); + clip_plane(m_vertex_buffer, input_verts, clip_plane_eqns[3]); + clip_plane(input_verts, m_vertex_buffer, clip_plane_eqns[4]); + clip_plane(m_vertex_buffer, input_verts, clip_plane_eqns[5]); +} + +void Clipper::clip_triangle_against_user_defined(Vector& input_verts, Vector& user_planes) +{ + // FIXME: Also implement user plane support for points and lines + auto& in = input_verts; + auto& out = m_vertex_buffer; + for (auto const& plane : user_planes) { + clip_plane(in, out, plane); + swap(in, out); + } } } diff --git a/Userland/Libraries/LibSoftGPU/Clipper.h b/Userland/Libraries/LibSoftGPU/Clipper.h index 9484f3b7bd9..306f5816fd9 100644 --- a/Userland/Libraries/LibSoftGPU/Clipper.h +++ b/Userland/Libraries/LibSoftGPU/Clipper.h @@ -16,12 +16,13 @@ namespace SoftGPU { class Clipper final { public: enum class ClipPlane : u8 { - LEFT = 0, - RIGHT, - TOP, - BOTTOM, - NEAR, - FAR + Left = 0, + Right, + Top, + Bottom, + Near, + Far, + User, // Within view space }; Clipper() = default; @@ -29,6 +30,7 @@ public: void clip_points_against_frustum(Vector& vertices); bool clip_line_against_frustum(GPU::Vertex& from, GPU::Vertex& to); void clip_triangle_against_frustum(Vector& input_vecs); + void clip_triangle_against_user_defined(Vector& input_verts, Vector& user_planes); private: Vector m_vertex_buffer; diff --git a/Userland/Libraries/LibSoftGPU/Config.h b/Userland/Libraries/LibSoftGPU/Config.h index ddb9026ccae..545207017ee 100644 --- a/Userland/Libraries/LibSoftGPU/Config.h +++ b/Userland/Libraries/LibSoftGPU/Config.h @@ -19,6 +19,7 @@ namespace SoftGPU { static constexpr bool ENABLE_STATISTICS_OVERLAY = false; static constexpr int MILLISECONDS_PER_STATISTICS_PERIOD = 500; static constexpr int NUM_LIGHTS = 8; +static constexpr int MAX_CLIP_PLANES = 6; static constexpr int SUBPIXEL_BITS = 6; // See: https://www.khronos.org/opengl/wiki/Common_Mistakes#Texture_edge_color_problem diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp index ffa0531db2e..66670591153 100644 --- a/Userland/Libraries/LibSoftGPU/Device.cpp +++ b/Userland/Libraries/LibSoftGPU/Device.cpp @@ -826,6 +826,7 @@ GPU::DeviceInfo Device::info() const .device_name = "SoftGPU", .num_texture_units = GPU::NUM_SAMPLERS, .num_lights = NUM_LIGHTS, + .max_clip_planes = MAX_CLIP_PLANES, .stencil_bits = sizeof(GPU::StencilType) * 8, .supports_npot_textures = true, }; @@ -1175,6 +1176,9 @@ void Device::draw_primitives(GPU::PrimitiveType primitive_type, FloatMatrix4x4 c m_clipped_vertices.append(triangle.vertices[2]); m_clipper.clip_triangle_against_frustum(m_clipped_vertices); + if (m_clip_planes.size() > 0) + m_clipper.clip_triangle_against_user_defined(m_clipped_vertices, m_clip_planes); + if (m_clipped_vertices.size() < 3) continue; @@ -1497,6 +1501,11 @@ void Device::set_raster_position(GPU::RasterPosition const& raster_position) m_raster_position = raster_position; } +void Device::set_clip_planes(Vector const& clip_planes) +{ + m_clip_planes = clip_planes; +} + void Device::set_raster_position(FloatVector4 const& position, FloatMatrix4x4 const& model_view_transform, FloatMatrix4x4 const& projection_transform) { auto const eye_coordinates = model_view_transform * position; diff --git a/Userland/Libraries/LibSoftGPU/Device.h b/Userland/Libraries/LibSoftGPU/Device.h index 715170db5f5..94f84fd8795 100644 --- a/Userland/Libraries/LibSoftGPU/Device.h +++ b/Userland/Libraries/LibSoftGPU/Device.h @@ -68,6 +68,7 @@ public: virtual void set_light_state(unsigned, GPU::Light const&) override; virtual void set_material_state(GPU::Face, GPU::Material const&) override; virtual void set_stencil_configuration(GPU::Face, GPU::StencilConfiguration const&) override; + virtual void set_clip_planes(Vector const&) override; virtual GPU::RasterPosition raster_position() const override { return m_raster_position; } virtual void set_raster_position(GPU::RasterPosition const& raster_position) override; @@ -107,6 +108,7 @@ private: Array m_lights; Array m_materials; GPU::RasterPosition m_raster_position; + Vector m_clip_planes; Array m_stencil_configuration; };