summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorJelle Raaijmakers <jelle@gmta.nl>2021-12-30 00:56:41 +0100
committerAndreas Kling <kling@serenityos.org>2021-12-30 14:24:29 +0100
commitc19632128c84e1190b95d1b18baf56ffe306e192 (patch)
treee48f9903422b8ff627da6739cd2911ae0b101fdd /Userland/Libraries
parent69da279073db68b53956e2e2f1c96f86e48ce0c9 (diff)
downloadserenity-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.cpp110
-rw-r--r--Userland/Libraries/LibGL/SoftwareGLContext.h37
-rw-r--r--Userland/Libraries/LibSoftGPU/Device.cpp73
-rw-r--r--Userland/Libraries/LibSoftGPU/Device.h7
-rw-r--r--Userland/Libraries/LibSoftGPU/Enums.h17
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,
+};
+
}