Skip to content

Reimplement random texture selection #736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libprojectM/MilkdropPreset/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ add_library(MilkdropPreset OBJECT
Filters.hpp
FinalComposite.cpp
FinalComposite.hpp
FlipTexture.cpp
FlipTexture.hpp
IdlePreset.cpp
IdlePreset.hpp
MilkdropNoise.cpp
Expand Down
2 changes: 1 addition & 1 deletion src/libprojectM/MilkdropPreset/CustomShape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ void CustomShape::Draw()
const float angle = cornerProgress * pi * 2.0f + static_cast<float>(*m_perFrameContext.tex_ang) + pi * 0.25f;

vertexData[i].u = 0.5f + 0.5f * cosf(angle) / static_cast<float>(*m_perFrameContext.tex_zoom) * textureAspectY;
vertexData[i].v = 0.5f - 0.5f * sinf(angle) / static_cast<float>(*m_perFrameContext.tex_zoom);
vertexData[i].v = 1.0f - (0.5f - 0.5f * sinf(angle) / static_cast<float>(*m_perFrameContext.tex_zoom)); // Vertical flip required!
}

vertexData[sides + 1] = vertexData[1];
Expand Down
2 changes: 1 addition & 1 deletion src/libprojectM/MilkdropPreset/CustomWaveform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext)
}

m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjectionFlipped);
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection);

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

Expand Down
3 changes: 2 additions & 1 deletion src/libprojectM/MilkdropPreset/Filters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void Filters::Draw()
glEnable(GL_BLEND);

m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjectionFlipped);
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection);

glBindVertexArray(m_vaoID);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);
Expand Down Expand Up @@ -88,6 +88,7 @@ void Filters::Invert()
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

void Filters::UpdateMesh()
{
if (m_viewportWidth == m_presetState.renderContext.viewportSizeX &&
Expand Down
5 changes: 5 additions & 0 deletions src/libprojectM/MilkdropPreset/FinalComposite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ void FinalComposite::Draw(const PresetState& presetState, const PerFrameContext&
Shader::Unbind();
}

auto FinalComposite::HasCompositeShader() const -> bool
{
return m_compositeShader != nullptr;
}

void FinalComposite::InitializeMesh(const PresetState& presetState)
{
if (m_viewportWidth == presetState.renderContext.viewportSizeX &&
Expand Down
6 changes: 6 additions & 0 deletions src/libprojectM/MilkdropPreset/FinalComposite.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ class FinalComposite : public RenderItem
void Draw(const PresetState& presetState,
const PerFrameContext& perFrameContext);

/**
* @brief Returns if the final composite is using a shader or classic filters.
* @return true if the final composite is done via a shader, false if not.
*/
auto HasCompositeShader() const -> bool;

private:
/**
* Composite mesh vertex with all required attributes.
Expand Down
148 changes: 148 additions & 0 deletions src/libprojectM/MilkdropPreset/FlipTexture.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include "FlipTexture.hpp"

FlipTexture::FlipTexture(const PresetState& presetState)
: RenderItem()
, m_presetState(presetState)
{
RenderItem::Init();

m_framebuffer.CreateColorAttachment(0, 0);
}

void FlipTexture::InitVertexAttrib()
{
glEnableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glEnableVertexAttribArray(2);

glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedPoint), reinterpret_cast<void*>(offsetof(TexturedPoint, x))); // Position
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedPoint), reinterpret_cast<void*>(offsetof(TexturedPoint, u))); // Texture coordinate

std::array<RenderItem::TexturedPoint, 4> points;

points[0].x = -1.0;
points[0].y = 1.0;
points[1].x = 1.0;
points[1].y = 1.0;
points[2].x = -1.0;
points[2].y = -1.0;
points[3].x = 1.0;
points[3].y = -1.0;

points[0].u = 0.0;
points[0].v = 1.0;
points[1].u = 1.0;
points[1].v = 1.0;
points[2].u = 0.0;
points[2].v = 0.0;
points[3].u = 1.0;
points[3].v = 0.0;

glBufferData(GL_ARRAY_BUFFER, sizeof(points), points.data(), GL_STATIC_DRAW);
}

void FlipTexture::Draw(const std::shared_ptr<Texture>& originalTexture, const std::shared_ptr<Texture>& targetTexture)
{
if (originalTexture == nullptr || originalTexture == targetTexture)
{
return;
}

UpdateTextureSize();

if (m_viewportWidth == 0 || m_viewportHeight == 0)
{
return;
}

std::shared_ptr<Texture> internalTexture;

m_framebuffer.Bind(0);

// Draw from unflipped texture
originalTexture->Bind(0);

if (targetTexture)
{
internalTexture = m_framebuffer.GetColorAttachmentTexture(0, 0);
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(targetTexture);
}

Flip();

// Rebind our internal texture.
if (targetTexture)
{
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(internalTexture);
}

Framebuffer::Unbind();
}

void FlipTexture::Draw(const std::shared_ptr<Texture>& originalTexture, Framebuffer& framebuffer, int framebufferIndex)
{
if (originalTexture == nullptr || framebuffer.GetColorAttachmentTexture(framebufferIndex, 0) == nullptr)
{
return;
}

UpdateTextureSize();

if (m_viewportWidth == 0 || m_viewportHeight == 0)
{
return;
}

m_framebuffer.Bind(0);

// Draw from unflipped texture
originalTexture->Bind(0);

Flip();

// Swap texture attachments
auto tempAttachment = framebuffer.GetAttachment(framebufferIndex, TextureAttachment::AttachmentType::Color, 0);
framebuffer.RemoveColorAttachment(framebufferIndex, 0);
framebuffer.SetAttachment(framebufferIndex, 0, m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0));
m_framebuffer.RemoveColorAttachment(0, 0);
m_framebuffer.SetAttachment(0, 0, tempAttachment);

Framebuffer::Unbind();
}

auto FlipTexture::FlippedTexture() -> std::shared_ptr<Texture>
{
return m_framebuffer.GetColorAttachmentTexture(0, 0);
}

void FlipTexture::UpdateTextureSize()
{
if (m_viewportWidth == m_presetState.renderContext.viewportSizeX &&
m_viewportHeight == m_presetState.renderContext.viewportSizeY)
{
return;
}

m_viewportWidth = m_presetState.renderContext.viewportSizeX;
m_viewportHeight = m_presetState.renderContext.viewportSizeY;

m_framebuffer.SetSize(m_viewportWidth, m_viewportHeight);
}

void FlipTexture::Flip() const
{
m_presetState.texturedShader.Bind();
m_presetState.texturedShader.SetUniformMat4x4("vertex_transformation", PresetState::orthogonalProjection);
m_presetState.texturedShader.SetUniformInt("texture_sampler", 0);

m_sampler.Bind(0);

glBindVertexArray(m_vaoID);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0); // Color
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindVertexArray(0);

glBindTexture(GL_TEXTURE_2D, 0);
Sampler::Unbind(0);
Shader::Unbind();
}
67 changes: 67 additions & 0 deletions src/libprojectM/MilkdropPreset/FlipTexture.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include "PresetState.hpp"

#include <Renderer/Framebuffer.hpp>
#include <Renderer/RenderItem.hpp>

/**
* @class FlipTexture
* @brief Flips the given input texture upside-down.
*
* Milkdrop uses HLSL, so the input and output UV coordinates in the draw call are upside-down because
* DirectX has the origin at the top-left while OpenGL/Vulkan use the bottom-left.
*
* Some presets need the input, the calculated UVs and the output to be properly aligned, so at some
* point, input textures must be flipped for the next rendering step. This class uses a simple draw
* call with a pass-through shader to draw the same image 1:1, but vertically flipped.
*/
class FlipTexture : public RenderItem
{
public:
FlipTexture() = delete;
explicit FlipTexture(const PresetState& presetState);

void InitVertexAttrib();

/**
* @brief Flips the original texture either into the object's internal framebuffer or a given target texture.
* The original and target textures must not be the same.
* @param originalTexture The texture to be flipped.
* @param targetTexture Optional target texture to draw onto.
*/
void Draw(const std::shared_ptr<Texture>& originalTexture, const std::shared_ptr<Texture>& targetTexture = {});

/**
* @brief Flips the texture bound the given framebuffer's first color attachment.
* This is done by drawing into a second framebuffer, then swapping the textures, so the original texture
* can be the current color attachment of targetFramebuffer.
* @param originalTexture The texture to be flipped.
* @param targetFramebuffer Optional target texture to draw onto.
* @param framebufferIndex The index of the framebuffer to use.
*/
void Draw(const std::shared_ptr<Texture>& originalTexture, Framebuffer& framebuffer, int framebufferIndex);

/**
* @brief Returns the flipped texture.
*
* @return The flipped texture.
*/
auto FlippedTexture() -> std::shared_ptr<Texture>;

private:
/**
* Updates the mesh
*/
void UpdateTextureSize();

void Flip() const;

const PresetState& m_presetState; //!< The global preset state.

Framebuffer m_framebuffer{1}; //!< Framebuffer for drawing the flipped texture
Sampler m_sampler{GL_CLAMP_TO_EDGE, GL_NEAREST}; //!< Texture sampler settings

int m_viewportWidth{}; //!< Last known viewport width
int m_viewportHeight{}; //!< Last known viewport height
};
20 changes: 16 additions & 4 deletions src/libprojectM/MilkdropPreset/MilkdropPreset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,12 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
m_motionVectors.Draw(m_perFrameContext, m_motionVectorUVMap->Texture());
}

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

// We now draw to the current framebuffer.
m_framebuffer.Bind(m_currentFrameBuffer);

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

// Todo: Song title anim would go here

// y-flip the image for final compositing again
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0));
m_state.mainTexture = m_flipTexture.FlippedTexture();

// We no longer need the previous frame image, use it to render the final composite.
m_framebuffer.BindRead(m_currentFrameBuffer);
m_framebuffer.BindDraw(m_previousFrameBuffer);
m_state.mainTexture = m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0);

m_finalComposite.Draw(m_state, m_perFrameContext);

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

if (!m_finalComposite.HasCompositeShader())
{
// Flip texture again in "previous" framebuffer as old-school effects are still upside down.
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), m_framebuffer, m_previousFrameBuffer);
}

// TEST: Copy result to default framebuffer
m_framebuffer.BindRead(m_previousFrameBuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
Expand Down
2 changes: 2 additions & 0 deletions src/libprojectM/MilkdropPreset/MilkdropPreset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "DarkenCenter.hpp"
#include "Filters.hpp"
#include "FinalComposite.hpp"
#include "FlipTexture.hpp"
#include "MotionVectors.hpp"
#include "PerFrameContext.hpp"
#include "PerPixelContext.hpp"
Expand Down Expand Up @@ -116,6 +117,7 @@ class MilkdropPreset : public Preset
std::array<std::unique_ptr<CustomShape>, CustomShapeCount> m_customShapes; //!< Custom shapes in this preset.
DarkenCenter m_darkenCenter; //!< Center darkening effect.
Border m_border; //!< Inner/outer borders.
FlipTexture m_flipTexture{ m_state }; //!< Texture flip filter

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

Expand Down
Loading