Skip to content

Commit 0d2ac56

Browse files
committed
Fix upside-down and vertical effect misalignment issues.
If anyone else can come up with a solution that doesn't involve up to three vertical flips, please implement.
1 parent 70132f1 commit 0d2ac56

19 files changed

+321
-16
lines changed

Diff for: src/libprojectM/MilkdropPreset/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ add_library(MilkdropPreset OBJECT
5656
Filters.hpp
5757
FinalComposite.cpp
5858
FinalComposite.hpp
59+
FlipTexture.cpp
60+
FlipTexture.hpp
5961
IdlePreset.cpp
6062
IdlePreset.hpp
6163
MilkdropNoise.cpp

Diff for: src/libprojectM/MilkdropPreset/CustomWaveform.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext)
183183
}
184184

185185
m_presetState.untexturedShader.Bind();
186-
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjectionFlipped);
186+
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection);
187187

188188
auto iterations = (m_drawThick && !m_useDots) ? 4 : 1;
189189

Diff for: src/libprojectM/MilkdropPreset/Filters.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ void Filters::Draw()
2929
glEnable(GL_BLEND);
3030

3131
m_presetState.untexturedShader.Bind();
32-
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjectionFlipped);
32+
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection);
3333

3434
glBindVertexArray(m_vaoID);
3535
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);
@@ -88,6 +88,7 @@ void Filters::Invert()
8888
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
8989
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
9090
}
91+
9192
void Filters::UpdateMesh()
9293
{
9394
if (m_viewportWidth == m_presetState.renderContext.viewportSizeX &&

Diff for: src/libprojectM/MilkdropPreset/FinalComposite.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ void FinalComposite::Draw(const PresetState& presetState, const PerFrameContext&
131131
Shader::Unbind();
132132
}
133133

134+
auto FinalComposite::HasCompositeShader() const -> bool
135+
{
136+
return m_compositeShader != nullptr;
137+
}
138+
134139
void FinalComposite::InitializeMesh(const PresetState& presetState)
135140
{
136141
if (m_viewportWidth == presetState.renderContext.viewportSizeX &&

Diff for: src/libprojectM/MilkdropPreset/FinalComposite.hpp

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ class FinalComposite : public RenderItem
3939
void Draw(const PresetState& presetState,
4040
const PerFrameContext& perFrameContext);
4141

42+
/**
43+
* @brief Returns if the final composite is using a shader or classic filters.
44+
* @return true if the final composite is done via a shader, false if not.
45+
*/
46+
auto HasCompositeShader() const -> bool;
47+
4248
private:
4349
/**
4450
* Composite mesh vertex with all required attributes.

Diff for: src/libprojectM/MilkdropPreset/FlipTexture.cpp

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#include "FlipTexture.hpp"
2+
3+
FlipTexture::FlipTexture(const PresetState& presetState)
4+
: RenderItem()
5+
, m_presetState(presetState)
6+
{
7+
RenderItem::Init();
8+
9+
m_framebuffer.CreateColorAttachment(0, 0);
10+
}
11+
12+
void FlipTexture::InitVertexAttrib()
13+
{
14+
glEnableVertexAttribArray(0);
15+
glDisableVertexAttribArray(1);
16+
glEnableVertexAttribArray(2);
17+
18+
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedPoint), reinterpret_cast<void*>(offsetof(TexturedPoint, x))); // Position
19+
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedPoint), reinterpret_cast<void*>(offsetof(TexturedPoint, u))); // Texture coordinate
20+
21+
std::array<RenderItem::TexturedPoint, 4> points;
22+
23+
points[0].x = -1.0;
24+
points[0].y = 1.0;
25+
points[1].x = 1.0;
26+
points[1].y = 1.0;
27+
points[2].x = -1.0;
28+
points[2].y = -1.0;
29+
points[3].x = 1.0;
30+
points[3].y = -1.0;
31+
32+
points[0].u = 0.0;
33+
points[0].v = 1.0;
34+
points[1].u = 1.0;
35+
points[1].v = 1.0;
36+
points[2].u = 0.0;
37+
points[2].v = 0.0;
38+
points[3].u = 1.0;
39+
points[3].v = 0.0;
40+
41+
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points.data(), GL_STATIC_DRAW);
42+
}
43+
44+
void FlipTexture::Draw(const std::shared_ptr<Texture>& originalTexture, const std::shared_ptr<Texture>& targetTexture)
45+
{
46+
if (originalTexture == nullptr || originalTexture == targetTexture)
47+
{
48+
return;
49+
}
50+
51+
UpdateTextureSize();
52+
53+
if (m_viewportWidth == 0 || m_viewportHeight == 0)
54+
{
55+
return;
56+
}
57+
58+
std::shared_ptr<Texture> internalTexture;
59+
60+
m_framebuffer.Bind(0);
61+
62+
// Draw from unflipped texture
63+
originalTexture->Bind(0);
64+
65+
if (targetTexture)
66+
{
67+
internalTexture = m_framebuffer.GetColorAttachmentTexture(0, 0);
68+
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(targetTexture);
69+
}
70+
71+
Flip();
72+
73+
// Rebind our internal texture.
74+
if (targetTexture)
75+
{
76+
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(internalTexture);
77+
}
78+
79+
Framebuffer::Unbind();
80+
}
81+
82+
void FlipTexture::Draw(const std::shared_ptr<Texture>& originalTexture, Framebuffer& framebuffer, int framebufferIndex)
83+
{
84+
if (originalTexture == nullptr || framebuffer.GetColorAttachmentTexture(framebufferIndex, 0) == nullptr)
85+
{
86+
return;
87+
}
88+
89+
UpdateTextureSize();
90+
91+
if (m_viewportWidth == 0 || m_viewportHeight == 0)
92+
{
93+
return;
94+
}
95+
96+
m_framebuffer.Bind(0);
97+
98+
// Draw from unflipped texture
99+
originalTexture->Bind(0);
100+
101+
Flip();
102+
103+
// Swap texture attachments
104+
auto tempAttachment = framebuffer.GetAttachment(framebufferIndex, TextureAttachment::AttachmentType::Color, 0);
105+
framebuffer.RemoveColorAttachment(framebufferIndex, 0);
106+
framebuffer.SetAttachment(framebufferIndex, 0, m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0));
107+
m_framebuffer.RemoveColorAttachment(0, 0);
108+
m_framebuffer.SetAttachment(0, 0, tempAttachment);
109+
110+
Framebuffer::Unbind();
111+
}
112+
113+
auto FlipTexture::FlippedTexture() -> std::shared_ptr<Texture>
114+
{
115+
return m_framebuffer.GetColorAttachmentTexture(0, 0);
116+
}
117+
118+
void FlipTexture::UpdateTextureSize()
119+
{
120+
if (m_viewportWidth == m_presetState.renderContext.viewportSizeX &&
121+
m_viewportHeight == m_presetState.renderContext.viewportSizeY)
122+
{
123+
return;
124+
}
125+
126+
m_viewportWidth = m_presetState.renderContext.viewportSizeX;
127+
m_viewportHeight = m_presetState.renderContext.viewportSizeY;
128+
129+
m_framebuffer.SetSize(m_viewportWidth, m_viewportHeight);
130+
}
131+
132+
void FlipTexture::Flip() const
133+
{
134+
m_presetState.texturedShader.Bind();
135+
m_presetState.texturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection);
136+
m_presetState.texturedShader.SetUniformInt("texture_sampler", 0);
137+
138+
m_sampler.Bind(0);
139+
140+
glBindVertexArray(m_vaoID);
141+
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0); // Color
142+
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
143+
glBindVertexArray(0);
144+
145+
glBindTexture(GL_TEXTURE_2D, 0);
146+
Sampler::Unbind(0);
147+
Shader::Unbind();
148+
}

Diff for: src/libprojectM/MilkdropPreset/FlipTexture.hpp

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#pragma once
2+
3+
#include "PresetState.hpp"
4+
5+
#include <Renderer/Framebuffer.hpp>
6+
#include <Renderer/RenderItem.hpp>
7+
8+
/**
9+
* @class FlipTexture
10+
* @brief Flips the given input texture upside-down.
11+
*
12+
* Milkdrop uses HLSL, so the input and output UV coordinates in the draw call are upside-down because
13+
* DirectX has the origin at the top-left while OpenGL/Vulkan use the bottom-left.
14+
*
15+
* Some presets need the input, the calculated UVs and the output to be properly aligned, so at some
16+
* point, input textures must be flipped for the next rendering step. This class uses a simple draw
17+
* call with a pass-through shader to draw the same image 1:1, but vertically flipped.
18+
*/
19+
class FlipTexture : public RenderItem
20+
{
21+
public:
22+
FlipTexture() = delete;
23+
explicit FlipTexture(const PresetState& presetState);
24+
25+
void InitVertexAttrib();
26+
27+
/**
28+
* @brief Flips the original texture either into the object's internal framebuffer or a given target texture.
29+
* The original and target textures must not be the same.
30+
* @param originalTexture The texture to be flipped.
31+
* @param targetTexture Optional target texture to draw onto.
32+
*/
33+
void Draw(const std::shared_ptr<Texture>& originalTexture, const std::shared_ptr<Texture>& targetTexture = {});
34+
35+
/**
36+
* @brief Flips the texture bound the given framebuffer's first color attachment.
37+
* This is done by drawing into a second framebuffer, then swapping the textures, so the original texture
38+
* can be the current color attachment of targetFramebuffer.
39+
* @param originalTexture The texture to be flipped.
40+
* @param targetFramebuffer Optional target texture to draw onto.
41+
* @param framebufferIndex The index of the framebuffer to use.
42+
*/
43+
void Draw(const std::shared_ptr<Texture>& originalTexture, Framebuffer& framebuffer, int framebufferIndex);
44+
45+
/**
46+
* @brief Returns the flipped texture.
47+
*
48+
* @return The flipped texture.
49+
*/
50+
auto FlippedTexture() -> std::shared_ptr<Texture>;
51+
52+
private:
53+
/**
54+
* Updates the mesh
55+
*/
56+
void UpdateTextureSize();
57+
58+
void Flip() const;
59+
60+
const PresetState& m_presetState; //!< The global preset state.
61+
62+
Framebuffer m_framebuffer{1}; //!< Framebuffer for drawing the flipped texture
63+
Sampler m_sampler{GL_CLAMP_TO_EDGE, GL_NEAREST}; //!< Texture sampler settings
64+
65+
int m_viewportWidth{}; //!< Last known viewport width
66+
int m_viewportHeight{}; //!< Last known viewport height
67+
};

Diff for: src/libprojectM/MilkdropPreset/MilkdropPreset.cpp

+16-4
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,12 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
9999
m_motionVectors.Draw(m_perFrameContext, m_motionVectorUVMap->Texture());
100100
}
101101

102-
// We now draw to the first framebuffer, but read from the second one for warping and textured shapes.
103-
m_framebuffer.BindRead(m_previousFrameBuffer);
104-
m_framebuffer.BindDraw(m_currentFrameBuffer);
102+
// y-flip the previous frame and assign the flipped texture as "main"
103+
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0));
104+
m_state.mainTexture = m_flipTexture.FlippedTexture();
105+
106+
// We now draw to the current framebuffer.
107+
m_framebuffer.Bind(m_currentFrameBuffer);
105108

106109
// Add motion vector u/v texture for the warp mesh draw and clean both buffers.
107110
m_framebuffer.SetAttachment(m_currentFrameBuffer, 1, m_motionVectorUVMap);
@@ -135,15 +138,24 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
135138

136139
// Todo: Song title anim would go here
137140

141+
// y-flip the image for final compositing again
142+
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0));
143+
m_state.mainTexture = m_flipTexture.FlippedTexture();
144+
138145
// We no longer need the previous frame image, use it to render the final composite.
139146
m_framebuffer.BindRead(m_currentFrameBuffer);
140147
m_framebuffer.BindDraw(m_previousFrameBuffer);
141-
m_state.mainTexture = m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0);
142148

143149
m_finalComposite.Draw(m_state, m_perFrameContext);
144150

145151
// ToDo: Draw user sprites (can have evaluated code)
146152

153+
if (!m_finalComposite.HasCompositeShader())
154+
{
155+
// Flip texture again in "previous" framebuffer as old-school effects are still upside down.
156+
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), m_framebuffer, m_previousFrameBuffer);
157+
}
158+
147159
// TEST: Copy result to default framebuffer
148160
m_framebuffer.BindRead(m_previousFrameBuffer);
149161
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

Diff for: src/libprojectM/MilkdropPreset/MilkdropPreset.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "DarkenCenter.hpp"
3030
#include "Filters.hpp"
3131
#include "FinalComposite.hpp"
32+
#include "FlipTexture.hpp"
3233
#include "MotionVectors.hpp"
3334
#include "PerFrameContext.hpp"
3435
#include "PerPixelContext.hpp"
@@ -116,6 +117,7 @@ class MilkdropPreset : public Preset
116117
std::array<std::unique_ptr<CustomShape>, CustomShapeCount> m_customShapes; //!< Custom shapes in this preset.
117118
DarkenCenter m_darkenCenter; //!< Center darkening effect.
118119
Border m_border; //!< Inner/outer borders.
120+
FlipTexture m_flipTexture{ m_state }; //!< Texture flip filter
119121

120122
FinalComposite m_finalComposite; //!< Final composite shader or filters.
121123

Diff for: src/libprojectM/MilkdropPreset/MilkdropShader.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ void MilkdropShader::LoadVariables(const PresetState& presetState, const PerFram
122122

123123
m_shader.Bind();
124124

125-
m_shader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjectionFlipped);
125+
m_shader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection);
126126

127127
m_shader.SetUniformFloat4("rand_frame", {floatRand(),
128128
floatRand(),

Diff for: src/libprojectM/MilkdropPreset/PerPixelMesh.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ void PerPixelMesh::WarpedBlit(const PresetState& presetState,
306306
if (!m_warpShader)
307307
{
308308
m_perPixelMeshShader.Bind();
309-
m_perPixelMeshShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjectionFlipped);
309+
m_perPixelMeshShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection);
310310
m_perPixelMeshShader.SetUniformInt("texture_sampler", 0);
311311
m_perPixelMeshShader.SetUniformFloat4("aspect", {presetState.renderContext.aspectX,
312312
presetState.renderContext.aspectY,

Diff for: src/libprojectM/MilkdropPreset/PresetState.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66

77
#include <random>
88

9-
const glm::mat4 PresetState::orthogonalProjection = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -40.0f, 40.0f);
10-
const glm::mat4 PresetState::orthogonalProjectionFlipped = glm::ortho(-1.0f, 1.0f, 1.0f, -1.0f, -40.0f, 40.0f);
9+
const glm::mat4 PresetState::orthogonalProjection = glm::ortho(-1.0f, 1.0f, 1.0f, -1.0f, -40.0f, 40.0f);
10+
const glm::mat4 PresetState::orthogonalProjectionFlipped = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, -40.0f, 40.0f);
1111

1212
PresetState::PresetState()
1313
: globalMemory(projectm_eval_memory_buffer_create())
1414
{
1515
auto staticShaders = libprojectM::MilkdropPreset::MilkdropStaticShaders::Get();
1616
untexturedShader.CompileProgram(staticShaders->GetUntexturedDrawVertexShader(),
17-
staticShaders->GetUntexturedDrawFragmentShader());
17+
staticShaders->GetUntexturedDrawFragmentShader());
1818
texturedShader.CompileProgram(staticShaders->GetTexturedDrawVertexShader(),
19-
staticShaders->GetTexturedDrawFragmentShader());
19+
staticShaders->GetTexturedDrawFragmentShader());
2020

2121
std::random_device randomDevice;
2222
std::mt19937 randomGenerator(randomDevice());

Diff for: src/libprojectM/MilkdropPreset/Shaders/PresetMotionVectorsVertexShaderGlsl330.vert

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ void main() {
2323
// Milkdrop's original code did a simple bilinear interpolation, but here it was already
2424
// done by the fragment shader during the warp mesh drawing. We just need to look up the
2525
// motion vector coordinate.
26-
vec2 oldUV = texture(warp_coordinates, pos.xy).xy;
26+
// We simply invert the y coordinate because it's easier than flipping the u/v texture.
27+
vec2 oldUV = texture(warp_coordinates, vec2(pos.x, 1.0 - pos.y)).xy;
2728

2829
// Enforce minimum trail length
2930
vec2 dist = oldUV - pos;

Diff for: src/libprojectM/MilkdropPreset/Shaders/PresetWarpVertexShaderGlsl330.vert

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ void main() {
4040

4141
// Initial texture coordinates, with built-in zoom factor
4242
float u = pos.x * aspectX * 0.5 * zoom2Inverse + 0.5;
43-
float v = -pos.y * aspectY * 0.5 * zoom2Inverse + 0.5;
43+
float v = pos.y * aspectY * 0.5 * zoom2Inverse + 0.5;
4444

4545
// original UV coordinates
4646
vec2 uv_original = vec2(pos.x * 0.5 + 0.5 + texelOffset.x,
47-
-pos.y * 0.5 + 0.5 + texelOffset.y);
47+
pos.y * 0.5 + 0.5 + texelOffset.y);
4848

4949
// Stretch on X, Y
5050
u = (u - center.x) / stretch.x + center.x;

0 commit comments

Comments
 (0)