Added a framebuffer class for drawing the presets into.

This commit is contained in:
Kai Blaschke
2023-03-05 18:42:09 +01:00
parent 7db50da0c9
commit 62b84ef724
9 changed files with 465 additions and 22 deletions

View File

@ -26,12 +26,6 @@
#include "PresetFileParser.hpp"
#include "PresetFrameIO.hpp"
#ifdef _WIN32
#include "dirent.h"
#else
#include <dirent.h>
#endif /** _WIN32 */
#ifdef MILKDROP_PRESET_DEBUG
#include <iostream>
#endif
@ -57,6 +51,10 @@ MilkdropPreset::MilkdropPreset(const std::string& absoluteFilePath)
void MilkdropPreset::InitializePreset(PresetFileParser& parsedFile)
{
// Create the offscreen rendering surfaces.
m_framebuffer.CreateColorAttachment(0, 0);
m_framebuffer.CreateColorAttachment(1, 0);
// Load global init variables into the state
m_state.Initialize(parsedFile);
@ -142,11 +140,29 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
m_state.audioData = audioData;
m_state.renderContext = renderContext;
// Update framebuffer size if needed
m_framebuffer.SetSize(renderContext.viewportSizeX, renderContext.viewportSizeY);
// First evaluate per-frame code
PerFrameUpdate();
// Motion vector field
// ToDo: Draw motion vectors to separate texture
// Motion vector field. Drawn to the previous frame texture before warping it.
m_framebuffer.Bind(1);
// ToDo: Move this to the draw call and pass in the per-frame context.
m_motionVectors.r = static_cast<float>(*m_perFrameContext.mv_r);
m_motionVectors.g = static_cast<float>(*m_perFrameContext.mv_g);
m_motionVectors.b = static_cast<float>(*m_perFrameContext.mv_b);
m_motionVectors.a = static_cast<float>(*m_perFrameContext.mv_a);
m_motionVectors.length = static_cast<float>(*m_perFrameContext.mv_l);
m_motionVectors.x_num = static_cast<float>(*m_perFrameContext.mv_x);
m_motionVectors.y_num = static_cast<float>(*m_perFrameContext.mv_y);
m_motionVectors.x_offset = static_cast<float>(*m_perFrameContext.mv_dx);
m_motionVectors.y_offset = static_cast<float>(*m_perFrameContext.mv_dy);
m_motionVectors.Draw(renderContext);
// We now draw to the first framebuffer, but read from the second one for warping.
m_framebuffer.BindRead(1);
m_framebuffer.BindDraw(0);
// Draw previous frame image warped via per-pixel mesh and warp shader
// ToDo: ComputeGridAlphaValues
@ -162,12 +178,20 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
wave->Draw(m_perFrameContext);
}
m_waveform.Draw();
// ToDo: Sprite drawing would go here.
// Todo: Song title anim would go here
// ToDo: Store main texture for next frame
// Copy pixels from framebuffer index 0 to 1
m_framebuffer.BindRead(0);
m_framebuffer.BindDraw(1);
glBlitFramebuffer(0, 0, renderContext.viewportSizeX, renderContext.viewportSizeY,
0, 0, renderContext.viewportSizeX, renderContext.viewportSizeY,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
// ToDo: Apply composite shader
m_framebuffer.Bind(0);
// ToDo: Draw user sprites (can have evaluated code)
}

View File

@ -27,10 +27,13 @@
#include "CustomWaveform.hpp"
#include "PerFrameContext.hpp"
#include "PerPixelContext.hpp"
#include "PerPixelMesh.hpp"
#include "Preset.hpp"
#include "PresetFrameIO.hpp"
#include "Waveform.hpp"
#include <Renderer/Framebuffer.hpp>
#include <cassert>
#include <map>
#include <memory>
@ -91,10 +94,15 @@ private:
std::string m_absoluteFilePath; //!< The absolute file path of the MilkdropPreset
std::string m_absolutePath; //!< The absolute path of the MilkdropPreset
Framebuffer m_framebuffer{2}; //!< Preset rendering framebuffer with two surfaces (last frame and current frame).
PresetState m_state; //!< Preset state container.
PerFrameContext m_perFrameContext; //!< Preset per-frame evaluation code context.
PerPixelContext m_perPixelContext; //!< Preset per-pixel/per-vertex evaluation code context.
PerPixelMesh m_perPixelMesh; //!< The per-pixel/per-vertex mesh, responsible for most of the movement/warp effects in Milkdrop presets.
MotionVectors m_motionVectors; //!< Motion vector grid.
Waveform m_waveform; //!< Preset default waveform.
std::array<std::unique_ptr<CustomWaveform>, CustomWaveformCount> m_customWaveforms; //!< Custom waveforms in this preset.
std::array<std::unique_ptr<CustomShape>, CustomShapeCount> m_customShapes; //!< Custom shapes in this preset.

View File

@ -10,12 +10,13 @@ MotionVectors::MotionVectors()
void MotionVectors::InitVertexAttrib() {
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glDisableVertexAttribArray(1);
}
void MotionVectors::Draw(RenderContext &context)
void MotionVectors::Draw(const RenderContext& context)
{
// ToDo: Implement the actual Milkdop behaviour here, including reverse propagation.
float intervalx=1.0/x_num;
float intervaly=1.0/y_num;
@ -42,7 +43,7 @@ void MotionVectors::Draw(RenderContext &context)
glBindBuffer(GL_ARRAY_BUFFER, m_vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(floatPair) * size, NULL, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(floatPair) * size, nullptr, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(floatPair) * size, points, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
@ -66,6 +67,7 @@ void MotionVectors::Draw(RenderContext &context)
glBindVertexArray(m_vaoID);
// ToDo: Milkdrop draws lines in the direction of motion, not just points!
glDrawArrays(GL_POINTS,0,size);
glBindVertexArray(0);

View File

@ -26,16 +26,16 @@ public:
* Redners the motion vectors.
* @param context The render context data.
*/
void Draw(RenderContext &context);
void Draw(const RenderContext &context);
float r{ 0.0 }; //!< Red color channel of the motion vectors.
float g{ 0.0 }; //!< Green color channel of the motion vectors.
float b{ 0.0 }; //!< Blue color channel of the motion vectors.
float a{ 0.0 }; //!< Alpha channel of the motion vectors.
float length{ 0.0 }; //!< Line length of the motion vectors.
float x_num{ 0.0 }; //!< Horizontal grid size.
float y_num{ 0.0 }; //!< Vertical grid size.
float x_offset{ 0.0 }; //!< Horizontal grid offset.
float y_offset{ 0.0 }; //!< Vertical grid offset.
float r{ 0.0 }; //!< Red color channel of the motion vectors (mv_r).
float g{ 0.0 }; //!< Green color channel of the motion vectors (mv_g).
float b{ 0.0 }; //!< Blue color channel of the motion vectors (mv_b).
float a{ 0.0 }; //!< Alpha channel of the motion vectors (mv_a).
float length{ 0.0 }; //!< Line length of the motion vectors (mv_l).
float x_num{ 0.0 }; //!< Horizontal grid size (integer part of mv_x).
float y_num{ 0.0 }; //!< Vertical grid size (integer part of mv_y).
float x_offset{ 0.0 }; //!< Horizontal grid offset (mv_dx).
float y_offset{ 0.0 }; //!< Vertical grid offset (mv_dy).
};

View File

@ -1,6 +1,8 @@
add_library(Renderer OBJECT
FileScanner.cpp
FileScanner.hpp
Framebuffer.cpp
Framebuffer.hpp
RenderContext.hpp
RenderItem.cpp
RenderItem.hpp
@ -15,6 +17,8 @@ add_library(Renderer OBJECT
StaticGlShaders.cpp
Texture.cpp
Texture.hpp
TextureAttachment.cpp
TextureAttachment.hpp
TextureManager.cpp
TextureManager.hpp
)

View File

@ -0,0 +1,164 @@
#include "Framebuffer.hpp"
Framebuffer::Framebuffer()
{
m_framebufferIds.resize(1);
glGenFramebuffers(1, m_framebufferIds.data());
m_attachments.insert({0, {}});
}
Framebuffer::Framebuffer(int framebufferCount)
{
m_framebufferIds.resize(framebufferCount);
glGenFramebuffers(framebufferCount, m_framebufferIds.data());
for (int index = 0; index < framebufferCount; index++)
{
m_attachments.insert({index, {}});
}
}
Framebuffer::~Framebuffer()
{
if (!m_framebufferIds.empty())
{
// Delete attached textures first
m_attachments.clear();
glDeleteFramebuffers(static_cast<int>(m_framebufferIds.size()), m_framebufferIds.data());
m_framebufferIds.clear();
}
}
auto Framebuffer::Count() const -> int
{
return static_cast<int>(m_framebufferIds.size());
}
void Framebuffer::Bind(int framebufferIndex)
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
{
return;
}
glBindFramebuffer(GL_FRAMEBUFFER, m_framebufferIds.at(framebufferIndex));
}
void Framebuffer::BindRead(int framebufferIndex)
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
{
return;
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebufferIds.at(framebufferIndex));
}
void Framebuffer::BindDraw(int framebufferIndex)
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
{
return;
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_framebufferIds.at(framebufferIndex));
}
void Framebuffer::SetSize(int width, int height)
{
if (width == 0 || height == 0 ||
(width == m_width && height == m_height))
{
return;
}
m_width = width;
m_height = height;
for (auto& attachments : m_attachments)
{
Bind(attachments.first);
for (auto& texture : attachments.second)
{
texture.second.SetSize(width, height);
glFramebufferTexture2D(GL_FRAMEBUFFER, texture.first, GL_TEXTURE_2D, texture.second.TextureId(), 0);
}
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Framebuffer::CreateColorAttachment(int framebufferIndex, int attachmentIndex)
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
{
return;
}
TextureAttachment textureAttachment{TextureAttachment::AttachmentType::Color, m_width, m_height};
auto textureId = textureAttachment.TextureId();
m_attachments.at(framebufferIndex).insert({GL_COLOR_ATTACHMENT0 + attachmentIndex, std::move(textureAttachment)});
if (m_width > 0 && m_height > 0)
{
Bind(framebufferIndex);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachmentIndex, GL_TEXTURE_2D, textureId, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void Framebuffer::CreateDepthAttachment(int framebufferIndex)
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
{
return;
}
TextureAttachment textureAttachment{TextureAttachment::AttachmentType::Depth, m_width, m_height};
auto textureId = textureAttachment.TextureId();
m_attachments.at(framebufferIndex).insert({GL_DEPTH_ATTACHMENT, std::move(textureAttachment)});
if (m_width > 0 && m_height > 0)
{
Bind(framebufferIndex);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureId, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void Framebuffer::CreateStencilAttachment(int framebufferIndex)
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
{
return;
}
TextureAttachment textureAttachment{TextureAttachment::AttachmentType::Stencil, m_width, m_height};
auto textureId = textureAttachment.TextureId();
m_attachments.at(framebufferIndex).insert({GL_STENCIL_ATTACHMENT, std::move(textureAttachment)});
if (m_width > 0 && m_height > 0)
{
Bind(framebufferIndex);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textureId, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void Framebuffer::CreateDepthStencilAttachment(int framebufferIndex)
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
{
return;
}
TextureAttachment textureAttachment{TextureAttachment::AttachmentType::DepthStencil, m_width, m_height};
auto textureId = textureAttachment.TextureId();
m_attachments.at(framebufferIndex).insert({GL_DEPTH_STENCIL_ATTACHMENT, std::move(textureAttachment)});
if (m_width > 0 && m_height > 0)
{
Bind(framebufferIndex);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textureId, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}

View File

@ -0,0 +1,105 @@
#pragma once
#include "TextureAttachment.hpp"
#include "projectM-opengl.h"
#include <map>
#include <vector>
/**
* @brief A framebuffer class holding one or more framebuffer objects.
*
* Framebuffers act as both render targets and sampling sources. This class holds one or more of those
* objects, either used to store current and previous frame images or sub images for multi-stage
* rendering.
*
* Each framebuffer can have multiple color attachments (at least up to 8), one depth buffer,
* one stencil buffer and one depth stencil buffer.
*
* All framebuffers and their attachments will share the same size.
*/
class Framebuffer
{
public:
/**
* @brief Creates a new framebuffer object with one framebuffer.
*/
Framebuffer();
/**
* @brief Creates a new framebuffer object with a given number of framebuffers.
* @param framebufferCount The number of framebuffers to create. Must be at least 1.
*/
explicit Framebuffer(int framebufferCount);
/**
* @brief Destroys the framebuffer object and all attachments.
*/
~Framebuffer();
/**
* @brief Returns the number of framebuffers in this object.
* @return The number of framebuffers in this object.
*/
int Count() const;
/**
* @brief Binds the given index as the current read/write framebuffer.
* @param framebufferIndex The framebuffer index.
*/
void Bind(int framebufferIndex);
/**
* @brief Binds the given index as the current read framebuffer.
* @param framebufferIndex The framebuffer index.
*/
void BindRead(int framebufferIndex);
/**
* @brief Binds the given index as the current write/draw framebuffer.
* @param framebufferIndex The framebuffer index.
*/
void BindDraw(int framebufferIndex);
/**
* @brief Sets the framebuffer texture size.
*
* This will bind the framebuffers and reallocate all attachment textures, creating new
* textures with the given size. The default framebuffer is bound after the call is finished.
* If either width or height is zero or both equal the the current size, the framebuffer contents
* won't be changed.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
*/
void SetSize(int width, int height);
/**
* @brief Adds a new color attachment to the framebuffer.
* The texture is always created in RGBA format.
* @param index The index of the attachment, at least indices 0-7 are guaranteed to be available.
*/
void CreateColorAttachment(int framebufferIndex, int attachmentIndex);
/**
* @brief Adds a depth attachment to the framebuffer.
*/
void CreateDepthAttachment(int framebufferIndex);
/**
* @brief Adds a stencil buffer attachment to the framebuffer.
*/
void CreateStencilAttachment(int framebufferIndex);
/**
* @brief Adds a depth stencil buffer attachment to the framebuffer.
*/
void CreateDepthStencilAttachment(int framebufferIndex);
private:
std::vector<unsigned int> m_framebufferIds{}; //!< The framebuffer IDs returned by OpenGL
std::map<int, std::map<GLenum, TextureAttachment>> m_attachments; //!< Framebuffer texture attachments.
int m_width{}; //!< Framebuffers texture width
int m_height{}; //!< Framebuffers texture height.
};

View File

@ -0,0 +1,92 @@
#include "TextureAttachment.hpp"
TextureAttachment::TextureAttachment(AttachmentType attachment, int width, int height)
: m_attachmentType(attachment)
{
if (width > 0 && height > 0)
{
ReplaceTexture(width, height);
}
}
TextureAttachment::~TextureAttachment()
{
if (m_textureId > 0)
{
glDeleteTextures(1, &m_textureId);
m_textureId = 0;
}
}
auto TextureAttachment::Type() const -> TextureAttachment::AttachmentType
{
return m_attachmentType;
}
auto TextureAttachment::TextureId() const -> GLuint
{
return m_textureId;
}
void TextureAttachment::SetSize(int width, int height)
{
if (width > 0 && height > 0)
{
ReplaceTexture(width, height);
}
else if (m_textureId > 0)
{
glDeleteTextures(1, &m_textureId);
m_textureId = 0;
}
}
void TextureAttachment::ReplaceTexture(int width, int height)
{
if (m_textureId > 0)
{
glDeleteTextures(1, &m_textureId);
}
GLint internalFormat;
GLint textureFormat;
GLenum pixelFormat;
switch(m_attachmentType)
{
case AttachmentType::Color:
internalFormat = GL_RGBA;
textureFormat = GL_RGBA;
pixelFormat = GL_TEXTURE_2D;
break;
case AttachmentType::Depth:
internalFormat = GL_DEPTH_COMPONENT;
textureFormat = GL_DEPTH_COMPONENT;
pixelFormat = GL_FLOAT;
break;
case AttachmentType::Stencil:
internalFormat = GL_STENCIL_INDEX8;
textureFormat = GL_STENCIL_INDEX;
pixelFormat = GL_UNSIGNED_BYTE;
break;
case AttachmentType::DepthStencil:
internalFormat = GL_DEPTH24_STENCIL8;
textureFormat = GL_DEPTH_STENCIL;
pixelFormat = GL_UNSIGNED_INT_24_8;
break;
default:
return;
}
glGenTextures(1, &m_textureId);
glBindTexture(GL_TEXTURE_2D, m_textureId);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, textureFormat, pixelFormat, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
}

View File

@ -0,0 +1,44 @@
#pragma once
#include "projectM-opengl.h"
#include <memory>
/**
* @brief Framebuffer texture attachment. Stores the texture and attachment type.
*/
class TextureAttachment
{
public:
enum class AttachmentType
{
Color,
Depth,
Stencil,
DepthStencil
};
TextureAttachment() = delete;
explicit TextureAttachment(AttachmentType attachmentType, int width, int height);
~TextureAttachment();
auto Type() const -> AttachmentType;
auto TextureId() const -> GLuint;
/**
* @brief Sets a new texture size.
* Effectively reallocates the texture.
* @param width The new width.
* @param height The new height.
*/
void SetSize(int width, int height);
private:
void ReplaceTexture(int width, int height);
AttachmentType m_attachmentType{AttachmentType::Color}; //!< Attachment type of this texture.
GLuint m_textureId{}; //!< The Texture ID.
};