diff options
author | Jelle Raaijmakers <jelle@gmta.nl> | 2021-12-30 00:56:41 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-12-30 14:24:29 +0100 |
commit | c19632128c84e1190b95d1b18baf56ffe306e192 (patch) | |
tree | e48f9903422b8ff627da6739cd2911ae0b101fdd /Userland/Libraries | |
parent | 69da279073db68b53956e2e2f1c96f86e48ce0c9 (diff) | |
download | serenity-c19632128c84e1190b95d1b18baf56ffe306e192.zip |
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 :^)
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibGL/SoftwareGLContext.cpp | 110 | ||||
-rw-r--r-- | Userland/Libraries/LibGL/SoftwareGLContext.h | 37 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Device.cpp | 73 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Device.h | 7 | ||||
-rw-r--r-- | Userland/Libraries/LibSoftGPU/Enums.h | 17 |
5 files changed, 236 insertions, 8 deletions
diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.cpp b/Userland/Libraries/LibGL/SoftwareGLContext.cpp index 927bc450c2..8e61cc03bf 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.cpp +++ b/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); +} + } diff --git a/Userland/Libraries/LibGL/SoftwareGLContext.h b/Userland/Libraries/LibGL/SoftwareGLContext.h index 1099525e50..e685710004 100644 --- a/Userland/Libraries/LibGL/SoftwareGLContext.h +++ b/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 }; diff --git a/Userland/Libraries/LibSoftGPU/Device.cpp b/Userland/Libraries/LibSoftGPU/Device.cpp index b561428dd2..833224b5ea 100644 --- a/Userland/Libraries/LibSoftGPU/Device.cpp +++ b/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; diff --git a/Userland/Libraries/LibSoftGPU/Device.h b/Userland/Libraries/LibSoftGPU/Device.h index 01e7165462..2456a2d89d 100644 --- a/Userland/Libraries/LibSoftGPU/Device.h +++ b/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; diff --git a/Userland/Libraries/LibSoftGPU/Enums.h b/Userland/Libraries/LibSoftGPU/Enums.h index e349ced509..e4b1f91630 100644 --- a/Userland/Libraries/LibSoftGPU/Enums.h +++ b/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, +}; + } |