From 0c27e8164a3e7347c293012db2b4fb032ee21eae Mon Sep 17 00:00:00 2001 From: Dane Wagner Date: Tue, 13 Feb 2024 15:22:57 -0600 Subject: [PATCH] Mimic Milkdrop code when handling RGBA values outside [0.0, 1.0] --- .../MilkdropPreset/CustomShape.cpp | 23 ++++++---- .../MilkdropPreset/CustomWaveform.cpp | 8 ++-- src/libprojectM/Renderer/RenderItem.hpp | 42 +++++++++++++++++++ 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/libprojectM/MilkdropPreset/CustomShape.cpp b/src/libprojectM/MilkdropPreset/CustomShape.cpp index 0b5649064..d88a1e946 100644 --- a/src/libprojectM/MilkdropPreset/CustomShape.cpp +++ b/src/libprojectM/MilkdropPreset/CustomShape.cpp @@ -3,6 +3,7 @@ #include "PresetFileParser.hpp" #include +#include #include @@ -155,15 +156,21 @@ void CustomShape::Draw() vertexData[0].u = 0.5f; vertexData[0].v = 0.5f; - vertexData[0].r = static_cast(*m_perFrameContext.r); - vertexData[0].g = static_cast(*m_perFrameContext.g); - vertexData[0].b = static_cast(*m_perFrameContext.b); - vertexData[0].a = static_cast(*m_perFrameContext.a); + // x = f*255.0 & 0xFF = (f*255.0) % 256 + // f' = x/255.0 = f % (256/255) + // 1.0 -> 255 (0xFF) + // 2.0 -> 254 (0xFE) + // -1.0 -> 0x01 - vertexData[1].r = static_cast(*m_perFrameContext.r2); - vertexData[1].g = static_cast(*m_perFrameContext.g2); - vertexData[1].b = static_cast(*m_perFrameContext.b2); - vertexData[1].a = static_cast(*m_perFrameContext.a2); + vertexData[0].r = Renderer::color_modulo(*m_perFrameContext.r); + vertexData[0].g = Renderer::color_modulo(*m_perFrameContext.g); + vertexData[0].b = Renderer::color_modulo(*m_perFrameContext.b); + vertexData[0].a = Renderer::color_modulo(*m_perFrameContext.a); + + vertexData[1].r = Renderer::color_modulo(*m_perFrameContext.r2); + vertexData[1].g = Renderer::color_modulo(*m_perFrameContext.g2); + vertexData[1].b = Renderer::color_modulo(*m_perFrameContext.b2); + vertexData[1].a = Renderer::color_modulo(*m_perFrameContext.a2); for (int i = 1; i < sides + 1; i++) { diff --git a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp index e63e8710a..d9d0937f6 100644 --- a/src/libprojectM/MilkdropPreset/CustomWaveform.cpp +++ b/src/libprojectM/MilkdropPreset/CustomWaveform.cpp @@ -157,10 +157,10 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext) pointsTransformed[sample].x = static_cast((*m_perPointContext.x * 2.0 - 1.0) * m_presetState.renderContext.invAspectX); pointsTransformed[sample].y = static_cast((*m_perPointContext.y * -2.0 + 1.0) * m_presetState.renderContext.invAspectY); - pointsTransformed[sample].r = static_cast(*m_perPointContext.r); - pointsTransformed[sample].g = static_cast(*m_perPointContext.g); - pointsTransformed[sample].b = static_cast(*m_perPointContext.b); - pointsTransformed[sample].a = static_cast(*m_perPointContext.a); + pointsTransformed[sample].r = Renderer::color_modulo(*m_perPointContext.r); + pointsTransformed[sample].g = Renderer::color_modulo(*m_perPointContext.g); + pointsTransformed[sample].b = Renderer::color_modulo(*m_perPointContext.b); + pointsTransformed[sample].a = Renderer::color_modulo(*m_perPointContext.a); } std::vector pointsSmoothed(sampleCount * 2); diff --git a/src/libprojectM/Renderer/RenderItem.hpp b/src/libprojectM/Renderer/RenderItem.hpp index 3da337eda..7ebf9928f 100644 --- a/src/libprojectM/Renderer/RenderItem.hpp +++ b/src/libprojectM/Renderer/RenderItem.hpp @@ -1,10 +1,52 @@ #pragma once #include +#include namespace libprojectM { namespace Renderer { +/** + * @brief Computes the modulus to wrap float values into the range of [0.0, 1.0]. + * + * This code mimics the following equations used by the original Milkdrop code: + * + * v[0].Diffuse = + * ((((int)(*pState->m_shape[i].var_pf_a * 255 * alpha_mult)) & 0xFF) << 24) | + * ((((int)(*pState->m_shape[i].var_pf_r * 255)) & 0xFF) << 16) | + * ((((int)(*pState->m_shape[i].var_pf_g * 255)) & 0xFF) << 8) | + * ((((int)(*pState->m_shape[i].var_pf_b * 255)) & 0xFF) ); + * + * In projectM, we use float values when drawing primitives or configuring vertices. + * Converting the above back to a float looks like this: + * + * d = (f * 255.0) & 0xFF = int((f * 255.0) % 256.0); * + * f' = float(d)/255.0; + * + * * Here % represents the Euclidean modulus, not the traditional (signed) fractional + * remainder. + * + * To avoid limiting ourselves to 8 bits, we combine the above equations into one that + * does not discard any information: + * + * f' = ((f * 255.0) % 256.0) / 255.0; + * = f % (256.0/255.0); + * + * Since we're using the Euclidean modulus we need to generate it from the fractional + * remainder using a standard equation. + */ +inline float color_modulo(float x) +{ + const float m = 256.0f / 255.0f; + return std::fmod(std::fmod(x, m) + m, m); +} + +inline float color_modulo(double x) +{ + // Convert the input to float before performing the computation. + return color_modulo(static_cast(x)); +} + /** * @brief Base class for render meshes. * Also defines a few standard vertex attribute structures for use with the shaders.