From 15a083751d73d320220d907e78046a3e1dcbcc3f Mon Sep 17 00:00:00 2001 From: yoyofr Date: Wed, 19 Nov 2025 11:00:54 +0100 Subject: [PATCH] Set warp texel offset to 0 by default, as 0.5 introduces a drift. Since this might be vendor-specific behavior, a new API function to configure the X/Y texel offsets has been added. Signed-off-by: Kai Blaschke --- src/api/include/projectM-4/parameters.h | 28 +++++++++++++++++++ .../MilkdropPreset/PerPixelMesh.cpp | 4 +-- src/libprojectM/ProjectM.cpp | 17 +++++++++++ src/libprojectM/ProjectM.hpp | 6 ++++ src/libprojectM/ProjectMCWrapper.cpp | 12 ++++++++ src/libprojectM/Renderer/RenderContext.hpp | 3 ++ 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/api/include/projectM-4/parameters.h b/src/api/include/projectM-4/parameters.h index f68e10ad8..5bd7d901d 100644 --- a/src/api/include/projectM-4/parameters.h +++ b/src/api/include/projectM-4/parameters.h @@ -227,6 +227,34 @@ PROJECTM_EXPORT void projectm_set_mesh_size(projectm_handle instance, size_t wid */ PROJECTM_EXPORT void projectm_get_mesh_size(projectm_handle instance, size_t* width, size_t* height); +/** + * @brief Applies a sub-texel offset for main texture lookups in the warp shader. + * + * Original Milkdrop uses 0.5 here, but it doesn't seem to be required in OpenGL as this value + * introduces a slight warp drift to the top left. As this may be vendor-specific, this value can + * be configured externally to fix any possible drift. + * + * @param instance The projectM instance handle. + * @param offset_X The offset in texels in the horizontal direction. Milkdrop uses 0.5, default in projectM is 0.0. + * @param offset_y The offset in texels in the vertical direction. Milkdrop uses 0.5, default in projectM is 0.0. + * @since 4.2.0 + */ +PROJECTM_EXPORT void projectm_set_texel_offset(projectm_handle instance, float offset_X, float offset_y); + +/** + * @brief Retrieves the current sub-texel offsets for main texture lookups in the warp shader. + * + * Original Milkdrop uses 0.5 here, but it doesn't seem to be required in OpenGL as this value + * introduces a slight warp drift to the top left. As this may be vendor-specific, this value can + * be configured externally to fix any possible drift. + * + * @param instance The projectM instance handle. + * @param offset_X A valid pointer to a float variable that will receive the currently set horizontal texel offset. + * @param offset_y A valid pointer to a float variable that will receive the currently set vertical texel offset. + * @since 4.2.0 + */ +PROJECTM_EXPORT void projectm_get_texel_offset(projectm_handle instance, float* offset_X, float* offset_y); + /** * @brief Sets the current/average frames per second. * diff --git a/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp b/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp index bb9bd8d85..8241910a1 100644 --- a/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp +++ b/src/libprojectM/MilkdropPreset/PerPixelMesh.cpp @@ -308,8 +308,8 @@ void PerPixelMesh::WarpedBlit(const PresetState& presetState, }; // Texel alignment - glm::vec2 const texelOffsets{0.5f / static_cast(presetState.renderContext.viewportSizeX), - 0.5f / static_cast(presetState.renderContext.viewportSizeY)}; + glm::vec2 const texelOffsets{presetState.renderContext.texelOffsetX / static_cast(presetState.renderContext.viewportSizeX), + presetState.renderContext.texelOffsetY / static_cast(presetState.renderContext.viewportSizeY)}; // Decay float decay = std::min(static_cast(*perFrameContext.decay), 1.0f); diff --git a/src/libprojectM/ProjectM.cpp b/src/libprojectM/ProjectM.cpp index 495c5d4b7..dc73ea0f2 100644 --- a/src/libprojectM/ProjectM.cpp +++ b/src/libprojectM/ProjectM.cpp @@ -455,6 +455,18 @@ void ProjectM::SetMeshSize(uint32_t meshResolutionX, uint32_t meshResolutionY) m_meshY = std::max(8u, std::min(400u, m_meshY)); } +void ProjectM::TexelOffsets(float& texelOffsetX, float& texelOffsetY) const +{ + texelOffsetX = m_texelOffsetX; + texelOffsetY = m_texelOffsetY; +} + +void ProjectM::SetTexelOffsets(float texelOffsetX, float texelOffsetY) +{ + m_texelOffsetX = texelOffsetX; + m_texelOffsetY = texelOffsetY; +} + auto ProjectM::PCM() -> libprojectM::Audio::PCM& { return m_audioStorage; @@ -493,8 +505,13 @@ auto ProjectM::GetRenderContext() -> Renderer::RenderContext ctx.aspectY = (m_windowWidth > m_windowHeight) ? static_cast(m_windowHeight) / static_cast(m_windowWidth) : 1.0f; ctx.invAspectX = 1.0f / ctx.aspectX; ctx.invAspectY = 1.0f / ctx.aspectY; + ctx.perPixelMeshX = static_cast(m_meshX); ctx.perPixelMeshY = static_cast(m_meshY); + + ctx.texelOffsetX = m_texelOffsetX; + ctx.texelOffsetY = m_texelOffsetY; + ctx.textureManager = m_textureManager.get(); ctx.shaderCache = m_shaderCache.get(); diff --git a/src/libprojectM/ProjectM.hpp b/src/libprojectM/ProjectM.hpp index a718d500a..f83f9c49b 100644 --- a/src/libprojectM/ProjectM.hpp +++ b/src/libprojectM/ProjectM.hpp @@ -177,6 +177,10 @@ public: void SetMeshSize(uint32_t meshResolutionX, uint32_t meshResolutionY); + void TexelOffsets(float& texelOffsetX, float& texelOffsetY) const; + + void SetTexelOffsets(float texelOffsetX, float texelOffsetY); + void Touch(float touchX, float touchY, int pressure, int touchType); void TouchDrag(float touchX, float touchY, int pressure); @@ -265,6 +269,8 @@ private: bool m_aspectCorrection{true}; //!< If true, corrects aspect ratio for non-rectangular windows. float m_easterEgg{1.0}; //!< Random preset duration modifier. See TimeKeeper class. float m_previousFrameVolume{}; //!< Volume in previous frame, used for hard cuts. + float m_texelOffsetX{0.0}; //!< Horizontal warp shader texel offset + float m_texelOffsetY{0.0}; //!< Vertical warp shader texel offset std::vector m_textureSearchPaths; ///!< List of paths to search for texture files diff --git a/src/libprojectM/ProjectMCWrapper.cpp b/src/libprojectM/ProjectMCWrapper.cpp index 5119314cf..ac6014aaf 100644 --- a/src/libprojectM/ProjectMCWrapper.cpp +++ b/src/libprojectM/ProjectMCWrapper.cpp @@ -273,6 +273,18 @@ void projectm_get_mesh_size(projectm_handle instance, size_t* width, size_t* hei *height = static_cast(h); } +void projectm_set_texel_offset(projectm_handle instance, float offset_X, float offset_y) +{ + auto projectMInstance = handle_to_instance(instance); + projectMInstance->SetTexelOffsets(offset_X, offset_y); +} + +void projectm_get_texel_offset(projectm_handle instance, float* offset_X, float* offset_y) +{ + auto projectMInstance = handle_to_instance(instance); + projectMInstance->TexelOffsets(*offset_X, *offset_y); +} + void projectm_set_mesh_size(projectm_handle instance, size_t width, size_t height) { auto projectMInstance = handle_to_instance(instance); diff --git a/src/libprojectM/Renderer/RenderContext.hpp b/src/libprojectM/Renderer/RenderContext.hpp index 895a36b85..bd8adb472 100644 --- a/src/libprojectM/Renderer/RenderContext.hpp +++ b/src/libprojectM/Renderer/RenderContext.hpp @@ -31,6 +31,9 @@ public: int perPixelMeshX{64}; //!< Per-pixel/per-vertex mesh X resolution. int perPixelMeshY{48}; //!< Per-pixel/per-vertex mesh Y resolution. + float texelOffsetX{0.0f}; //!< Horizontal texel offset in the warp shader. + float texelOffsetY{0.0f}; //!< Vertical texel offset in the warp shader. + TextureManager* textureManager{nullptr}; //!< Holds all loaded textures for shader access. ShaderCache* shaderCache{nullptr}; //!< The shader chace of this projectM instance. };