Working on getting stuff being rendered again, focus on default waveform.

Fixed many typos, errors and crashes on the way.
This commit is contained in:
Kai Blaschke
2023-03-06 23:27:15 +01:00
parent 62b84ef724
commit e73b0f3092
38 changed files with 690 additions and 561 deletions

View File

@ -0,0 +1,10 @@
#pragma once
namespace libprojectM {
namespace Audio {
static constexpr int WaveformSamples = 576; //!< Number of waveform data samples available for rendering a frame.
static constexpr int SpectrumSamples = 512; //!< Number of spectrum analyzer samples.
} // namespace Audio
} // namespace libprojectM

View File

@ -67,6 +67,27 @@ auto BeatDetect::GetPCMScale() const noexcept -> float
}
auto BeatDetect::GetFrameAudioData() const -> FrameAudioData
{
FrameAudioData data{};
pcm.GetPcm(data.waveformLeft.data(), CHANNEL_L, WaveformSamples);
pcm.GetPcm(data.waveformRight.data(), CHANNEL_R, WaveformSamples);
pcm.GetSpectrum(data.spectrumLeft.data(), CHANNEL_L, SpectrumSamples);
data.vol = vol;
data.volAtt = volAtt;
data.bass = bass;
data.bassAtt = bassAtt;
data.mid = mid;
data.midAtt = midAtt;
data.treb = treb;
data.trebAtt = trebAtt;
return data;
}
auto BeatDetect::CalculateBeatStatistics() -> void
{
volOld = vol;
@ -115,6 +136,7 @@ auto BeatDetect::CalculateBeatStatistics() -> void
}
auto BeatDetect::LowPassFilter::Update(float nextValue) noexcept -> void
{
m_current -= m_buffer[m_bufferPos] / bufferLength;

View File

@ -23,6 +23,7 @@
*/
#pragma once
#include "FrameAudioData.hpp"
#include "PCM.hpp"
#include <array>
@ -50,6 +51,13 @@ public:
[[nodiscard]]
auto GetPCMScale() const noexcept -> float;
/**
* @brief Returns a filled FrameAudioData structure for the current frame.
* @return A FrameAudioData structure with waveform, spectrum beat detection data.
*/
[[nodiscard]]
auto GetFrameAudioData() const -> FrameAudioData;
float beatSensitivity{1.f};
float treb{0.f};

View File

@ -6,7 +6,7 @@
*/
#pragma once
#include "Constants.hpp"
#include "AudioConstants.hpp"
#include <array>

View File

@ -47,7 +47,7 @@ add_library(projectM_main OBJECT
resource.h
wipemalloc.cpp
wipemalloc.h
)
Audio/AudioConstants.hpp)
target_link_libraries(projectM_main
PUBLIC

View File

@ -1,6 +1,6 @@
#include "BlurTexture.hpp"
#include <Renderer/StaticGlShaders.h>
#include <Renderer/StaticGlShaders.hpp>
#include <array>

View File

@ -2,8 +2,9 @@
#include <glm/gtc/type_ptr.hpp>
Border::Border()
Border::Border(PresetState& presetState)
: RenderItem()
, m_presetState(presetState)
{
RenderItem::Init();
}
@ -11,54 +12,60 @@ Border::Border()
void Border::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 Border::Draw(RenderContext& context)
void Border::Draw(const PerFrameContext& presetPerFrameContext)
{
//Draw Borders
float of = outer_size * .5;
float iff = inner_size * .5;
float texof = 1.0 - of;
// Draw Borders
float const outerBorderSize = static_cast<float>(*presetPerFrameContext.ob_size) * .5f;
float const innerBorderSize = static_cast<float>(*presetPerFrameContext.ib_size) * .5f;
float const texelOffset = 1.0f - outerBorderSize;
float points[40] = {
std::array<std::array<float, 4>, 10> const points = {{
// Outer
0, 0, of, 0,
0, 1, of, texof,
1, 1, texof, texof,
1, 0, texof, of,
of, 0, of, of,
{0.0f, 0.0f, outerBorderSize, 0.0f},
{0.0f, 1.0f, outerBorderSize, texelOffset},
{1.0f, 1.0f, texelOffset, texelOffset},
{1.0f, 0.0f, texelOffset, outerBorderSize},
{outerBorderSize, 0.0f, outerBorderSize, outerBorderSize},
// Inner
of, of, of + iff, of,
of, texof, of + iff, texof - iff,
texof, texof, texof - iff, texof - iff,
texof, of, texof - iff, of + iff,
of + iff, of, of + iff, of + iff,
};
{outerBorderSize, outerBorderSize, outerBorderSize + innerBorderSize, outerBorderSize},
{outerBorderSize, texelOffset, outerBorderSize + innerBorderSize, texelOffset - innerBorderSize},
{texelOffset, texelOffset, texelOffset - innerBorderSize, texelOffset - innerBorderSize},
{texelOffset, outerBorderSize, texelOffset - innerBorderSize, outerBorderSize + innerBorderSize},
{outerBorderSize + innerBorderSize, outerBorderSize, outerBorderSize + innerBorderSize, outerBorderSize + innerBorderSize},
}};
glBindBuffer(GL_ARRAY_BUFFER, m_vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 40, NULL, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 40, points, GL_DYNAMIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 40, points.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(context.programID_v2f_c4f);
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
glVertexAttrib4f(1,
static_cast<float>(*presetPerFrameContext.ob_r),
static_cast<float>(*presetPerFrameContext.ob_g),
static_cast<float>(*presetPerFrameContext.ob_b),
static_cast<float>(*presetPerFrameContext.ob_a));
glVertexAttrib4f(1, outer_r, outer_g, outer_b, outer_a * masterAlpha);
//no additive drawing for borders
// No additive drawing for borders
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindVertexArray(m_vaoID);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 10);
glVertexAttrib4f(1, inner_r, inner_g, inner_b, inner_a * masterAlpha);
glVertexAttrib4f(1,
static_cast<float>(*presetPerFrameContext.ib_r),
static_cast<float>(*presetPerFrameContext.ib_g),
static_cast<float>(*presetPerFrameContext.ib_b),
static_cast<float>(*presetPerFrameContext.ib_a));
// 1st pass for inner
glDrawArrays(GL_TRIANGLE_STRIP, 10, 10);

View File

@ -1,5 +1,8 @@
#pragma once
#include "PerFrameContext.hpp"
#include "PresetState.hpp"
#include "Renderer/RenderItem.hpp"
/**
@ -8,29 +11,18 @@
class Border : public RenderItem
{
public:
Border() = delete;
/**
* Constructor. Initializes the required OpenGL data structures.
*/
Border();
explicit Border(PresetState& presetState);
void InitVertexAttrib();
void InitVertexAttrib() override;
/**
* Draws the border.
* @param context The render context data.
* @param presetPerFrameContext The per-frame context variables.
*/
void Draw(RenderContext &context);
void Draw(const PerFrameContext& presetPerFrameContext);
float outer_size{ 0.0 }; //!< Outer border width
float outer_r{ 0.0 }; //!< Outer border color, red channel.
float outer_g{ 0.0 }; //!< Outer border color, green channel.
float outer_b{ 0.0 }; //!< Outer border color, blue channel.
float outer_a{ 0.0 }; //!< Outer border color, alpha channel.
float inner_size{ 0.0 }; //!< Inner border width
float inner_r{ 0.0 }; //!< Inner border color, red channel.
float inner_g{ 0.0 }; //!< Inner border color, green channel.
float inner_b{ 0.0 }; //!< Inner border color, blue channel.
float inner_a{ 0.0 }; //!< Inner border color, alpha channel.
private:
PresetState& m_presetState; //!< The global preset state.
};

View File

@ -4,14 +4,13 @@
*/
#pragma once
#include <Audio/AudioConstants.hpp>
static constexpr int QVarCount = 32; //!< Number of Q variables available.
static constexpr int TVarCount = 8; //!< Number of T variables available.
static constexpr int CustomWaveformCount = 4; //!< Number of custom waveforms (expression-driven) which can be used in a preset.
static constexpr int CustomShapeCount = 4; //!< Number of custom shapes (expression-driven) which can be used in a preset.
static constexpr int WaveformSamples = 576; //!< Number of waveform data samples available for rendering a frame.
static constexpr int SpectrumSamples = 512; //!< Number of spectrum analyzer samples.
static constexpr int RenderWaveformSamples = 480; //!< Number of custom waveform data samples available for rendering a frame.
static constexpr int WaveformMaxPoints = 512; //!< Maximum number of waveform points.

View File

@ -156,7 +156,7 @@ void CustomShape::Draw(const PerFrameContext& presetPerFrameContext)
glActiveTexture(GL_TEXTURE0);
if (m_image.empty())
{
glBindTexture(GL_TEXTURE_2D, m_presetState.renderContext.textureManager->getMainTexture()->texID);
glBindTexture(GL_TEXTURE_2D, m_presetState.mainTextureId);
}
else
{
@ -170,7 +170,7 @@ void CustomShape::Draw(const PerFrameContext& presetPerFrameContext)
else
{
// No texture found, fall back to main texture.
glBindTexture(GL_TEXTURE_2D, m_presetState.renderContext.textureManager->getMainTexture()->texID);
glBindTexture(GL_TEXTURE_2D, m_presetState.mainTextureId);
}
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
@ -191,11 +191,9 @@ void CustomShape::Draw(const PerFrameContext& presetPerFrameContext)
glBufferData(GL_ARRAY_BUFFER, sizeof(ShapeVertexShaderData) * (*m_perFrameContext.sides + 2), vertexData.data(), GL_DYNAMIC_DRAW);
glUseProgram(m_presetState.renderContext.programID_v2f_c4f_t2f);
glUniformMatrix4fv(m_presetState.renderContext.uniform_v2f_c4f_t2f_vertex_transformation, 1, GL_FALSE,
glm::value_ptr(m_presetState.renderContext.mat_ortho));
glUniform1i(m_presetState.renderContext.uniform_v2f_c4f_t2f_frag_texture_sampler, 0);
m_presetState.texturedShader.Bind();
m_presetState.texturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
m_presetState.texturedShader.SetUniformInt("texture_sampler", 0);
glBindVertexArray(m_vaoID_texture);
glDrawArrays(GL_TRIANGLE_FAN, 0, *m_perFrameContext.sides + 2);
@ -211,10 +209,8 @@ void CustomShape::Draw(const PerFrameContext& presetPerFrameContext)
glBufferData(GL_ARRAY_BUFFER, sizeof(ShapeVertexShaderData) * (*m_perFrameContext.sides + 2), vertexData.data(), GL_DYNAMIC_DRAW);
glUseProgram(m_presetState.renderContext.programID_v2f_c4f);
glUniformMatrix4fv(m_presetState.renderContext.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE,
glm::value_ptr(m_presetState.renderContext.mat_ortho));
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glBindVertexArray(m_vaoID_not_texture);
glDrawArrays(GL_TRIANGLE_FAN, 0, *m_perFrameContext.sides + 2);
@ -231,10 +227,9 @@ void CustomShape::Draw(const PerFrameContext& presetPerFrameContext)
points[i].y = vertexData[i + 1].point_y;
}
glUseProgram(m_presetState.renderContext.programID_v2f_c4f);
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glUniformMatrix4fv(m_presetState.renderContext.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE,
glm::value_ptr(m_presetState.renderContext.mat_ortho));
glVertexAttrib4f(1,
*m_perFrameContext.border_r,
*m_perFrameContext.border_g,
@ -288,7 +283,6 @@ void CustomShape::Draw(const PerFrameContext& presetPerFrameContext)
glBufferData(GL_ARRAY_BUFFER, sizeof(floatPair) * (*m_perFrameContext.sides), points.data(), GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINE_LOOP, 0, *m_perFrameContext.sides);
}
}
}
@ -298,7 +292,8 @@ void CustomShape::Draw(const PerFrameContext& presetPerFrameContext)
#if USE_GLES == 0
glDisable(GL_LINE_SMOOTH);
#endif
glUseProgram(0);
Shader::Unbind();
}
void CustomShape::LoadPerFrameEvaluationVariables(const PerFrameContext& presetPerFrameContext)

View File

@ -12,7 +12,7 @@
#include <algorithm>
#include <cmath>
static constexpr int CustomWaveformMaxSamples = std::max(RenderWaveformSamples, SpectrumSamples);
static constexpr int CustomWaveformMaxSamples = std::max(RenderWaveformSamples, libprojectM::Audio::SpectrumSamples);
CustomWaveform::CustomWaveform(PresetState& presetState)
: RenderItem()
@ -72,12 +72,12 @@ void CustomWaveform::CompileCodeAndRunInitExpressions(const PerFrameContext& pre
void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext)
{
// Some safety assertions if someone plays around and changes the values in the wrong way.
static_assert(WaveformMaxPoints <= SpectrumSamples, "CustomWaveformMaxPoints is larger than SpectrumSamples");
static_assert(WaveformMaxPoints <= WaveformSamples, "CustomWaveformMaxPoints is larger than WaveformSamples");
static_assert(WaveformMaxPoints <= libprojectM::Audio::SpectrumSamples, "CustomWaveformMaxPoints is larger than SpectrumSamples");
static_assert(WaveformMaxPoints <= libprojectM::Audio::WaveformSamples, "CustomWaveformMaxPoints is larger than WaveformSamples");
static_assert(RenderWaveformSamples <= WaveformMaxPoints, "CustomWaveformSamples is larger than CustomWaveformMaxPoints");
int sampleCount{m_samples};
int const maxSampleCount{m_spectrum ? SpectrumSamples : RenderWaveformSamples};
int const maxSampleCount{m_spectrum ? libprojectM::Audio::SpectrumSamples : RenderWaveformSamples};
sampleCount = std::min(sampleCount, maxSampleCount);
sampleCount -= m_sep;
@ -163,9 +163,8 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext)
std::vector<ColoredPoint> pointsSmoothed(sampleCount * 2);
auto smoothedVertexCount = SmoothWave(pointsTransformed.data(), sampleCount, pointsSmoothed.data());
glUseProgram(m_presetState.renderContext.programID_v2f_c4f);
glUniformMatrix4fv(m_presetState.renderContext.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(m_presetState.renderContext.mat_ortho));
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
if (m_additive)
{
@ -242,7 +241,7 @@ void CustomWaveform::Draw(const PerFrameContext& presetPerFrameContext)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glUseProgram(0);
Shader::Unbind();
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

View File

@ -2,43 +2,44 @@
#include <glm/gtc/type_ptr.hpp>
DarkenCenter::DarkenCenter()
DarkenCenter::DarkenCenter(PresetState& presetState)
: RenderItem()
, m_presetState(presetState)
{
RenderItem::Init();
}
void DarkenCenter::InitVertexAttrib()
{
float points_colors[6][6] = {
{ 0.5, 0.5, 0, 0, 0, (3.0f / 32.0f) * masterAlpha },
{ 0.45, 0.5, 0, 0, 0, 0 },
{ 0.5, 0.45, 0, 0, 0, 0 },
{ 0.55, 0.5, 0, 0, 0, 0 },
{ 0.5, 0.55, 0, 0, 0, 0 },
{ 0.45, 0.5, 0, 0, 0, 0 }};
std::array<std::array<float, 6>, 6> points_colors = {{{0.5, 0.5, 0, 0, 0, 3.0f / 32.0f},
{0.45, 0.5, 0, 0, 0, 0},
{0.5, 0.45, 0, 0, 0, 0},
{0.55, 0.5, 0, 0, 0, 0},
{0.5, 0.55, 0, 0, 0, 0},
{0.45, 0.5, 0, 0, 0, 0}}};
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*) 0); // points
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*) (sizeof(float) * 2)); // colors
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 6, nullptr); // points
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 6, reinterpret_cast<void*>(sizeof(float) * 2)); // colors
glBufferData(GL_ARRAY_BUFFER, sizeof(points_colors), points_colors, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(points_colors), points_colors.data(), GL_STATIC_DRAW);
}
void DarkenCenter::Draw(RenderContext& context)
void DarkenCenter::Draw()
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(context.programID_v2f_c4f);
glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glBindVertexArray(m_vaoID);
glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
glBindVertexArray(0);
Shader::Unbind();
}

View File

@ -1,5 +1,7 @@
#pragma once
#include "PresetState.hpp"
#include "Renderer/RenderItem.hpp"
/**
@ -8,17 +10,17 @@
class DarkenCenter : public RenderItem
{
public:
DarkenCenter() = delete;
/**
* Constructor. Initializes the required OpenGL data structures.
*/
DarkenCenter();
explicit DarkenCenter(PresetState& presetState);
void InitVertexAttrib();
/**
* Applies the darkening area.
* @param context The render context data.
*/
void Draw(RenderContext &context);
void Draw();
private:
PresetState& m_presetState; //!< The global preset state.
};

View File

@ -4,130 +4,100 @@
#include "Renderer/ShaderEngine.hpp"
#include <glm/gtc/type_ptr.hpp>
Filters::Filters(PresetState& presetState)
: RenderItem()
, m_presetState(presetState)
{
RenderItem::Init();
}
void Brighten::InitVertexAttrib() {
float points[4][2] = {{-0.5, -0.5},
{-0.5, 0.5},
{ 0.5, 0.5},
{ 0.5, -0.5}};
void Filters::InitVertexAttrib()
{
std::array<std::array<float, 2>, 4> points = {{{-0.5, -0.5},
{-0.5, 0.5},
{0.5, 0.5},
{0.5, -0.5}}};
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glDisableVertexAttribArray(1);
}
void Brighten::Draw(RenderContext &context)
void Filters::Brighten()
{
glUseProgram(context.programID_v2f_c4f);
glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glBindVertexArray(m_vaoID);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
glBlendFunc(GL_ZERO, GL_DST_COLOR);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBlendFunc(GL_ZERO, GL_DST_COLOR);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindVertexArray(0);
Shader::Unbind();
}
void Darken::InitVertexAttrib() {
float points[4][2] = {{-0.5, -0.5},
{-0.5, 0.5},
{ 0.5, 0.5},
{ 0.5, -0.5}};
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glDisableVertexAttribArray(1);
}
void Darken::Draw(RenderContext &context)
void Filters::Darken()
{
glUseProgram(context.programID_v2f_c4f);
glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);
glBlendFunc(GL_ZERO, GL_DST_COLOR);
glBlendFunc(GL_ZERO, GL_DST_COLOR);
glBindVertexArray(m_vaoID);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Shader::Unbind();
}
void Invert::InitVertexAttrib() {
float points[4][2] = {{-0.5, -0.5},
{-0.5, 0.5},
{ 0.5, 0.5},
{ 0.5, -0.5}};
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glDisableVertexAttribArray(1);
}
void Invert::Draw(RenderContext &context)
void Filters::Invert()
{
glUseProgram(context.programID_v2f_c4f);
glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
glBindVertexArray(m_vaoID);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Shader::Unbind();
}
void Solarize::InitVertexAttrib() {
float points[4][2] = {{-0.5, -0.5},
{-0.5, 0.5},
{ 0.5, 0.5},
{ 0.5, -0.5}};
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
glDisableVertexAttribArray(1);
}
void Solarize::Draw(RenderContext &context)
void Filters::Solarize()
{
glUseProgram(context.programID_v2f_c4f);
glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, 1.0);
glBlendFunc(GL_ZERO, GL_ONE_MINUS_DST_COLOR);
glBlendFunc(GL_ZERO, GL_ONE_MINUS_DST_COLOR);
glBindVertexArray(m_vaoID);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
glBlendFunc(GL_DST_COLOR, GL_ONE);
glDrawArrays(GL_TRIANGLE_FAN,0,4);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBlendFunc(GL_DST_COLOR, GL_ONE);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Shader::Unbind();
}

View File

@ -1,35 +1,28 @@
#pragma once
#include "Renderer/RenderItem.hpp"
#include "PresetState.hpp"
class Brighten : public RenderItem
#include <Renderer/RenderItem.hpp>
class Filters : public RenderItem
{
public:
Brighten(){ Init(); }
void InitVertexAttrib();
void Draw(RenderContext &context);
};
Filters() = delete;
explicit Filters(PresetState& presetState);
class Darken : public RenderItem
{
public:
Darken(){ Init(); }
void InitVertexAttrib();
void Draw(RenderContext &context);
};
class Invert : public RenderItem
{
public:
Invert(){ Init(); }
void InitVertexAttrib();
void Draw(RenderContext &context);
};
/**
* @brief Brightens the image
*/
void Brighten();
class Solarize : public RenderItem
{
public:
Solarize(){ Init(); }
void InitVertexAttrib();
void Draw(RenderContext &context);
void Darken();
void Invert();
void Solarize();
private:
PresetState& m_presetState; //!< The global preset state.
};

View File

@ -34,7 +34,11 @@
MilkdropPreset::MilkdropPreset(std::istream& presetData)
: m_perFrameContext(m_state.globalMemory, &m_state.globalRegisters)
, m_perPixelContext(m_state.globalMemory, &m_state.globalRegisters)
, m_motionVectors(m_state)
, m_waveform(m_state)
, m_darkenCenter(m_state)
, m_border(m_state)
, m_filters(m_state)
{
Load(presetData);
}
@ -44,7 +48,11 @@ MilkdropPreset::MilkdropPreset(const std::string& absoluteFilePath)
: m_absoluteFilePath(absoluteFilePath)
, m_perFrameContext(m_state.globalMemory, &m_state.globalRegisters)
, m_perPixelContext(m_state.globalMemory, &m_state.globalRegisters)
, m_motionVectors(m_state)
, m_waveform(m_state)
, m_darkenCenter(m_state)
, m_border(m_state)
, m_filters(m_state)
{
Load(absoluteFilePath);
}
@ -139,6 +147,7 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
{
m_state.audioData = audioData;
m_state.renderContext = renderContext;
m_state.mainTextureId = m_framebuffer.GetColorAttachmentTexture(1, 0);
// Update framebuffer size if needed
m_framebuffer.SetSize(renderContext.viewportSizeX, renderContext.viewportSizeY);
@ -147,27 +156,24 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
PerFrameUpdate();
// 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);
//m_framebuffer.Bind(1);
m_motionVectors.Draw(m_perFrameContext);
// We now draw to the first framebuffer, but read from the second one for warping.
m_framebuffer.BindRead(1);
m_framebuffer.BindDraw(0);
//m_framebuffer.BindRead(1);
//m_framebuffer.BindDraw(0);
// TEST: Copy for now, no warp
//glBlitFramebuffer(0, 0, renderContext.viewportSizeX, renderContext.viewportSizeY,
// 0, 0, renderContext.viewportSizeX, renderContext.viewportSizeY,
// GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Draw previous frame image warped via per-pixel mesh and warp shader
// ToDo: ComputeGridAlphaValues
// ToDo: Per-Pixel and warp stuff
//m_framebuffer.Bind(0);
// Draw audio-data-related stuff
for (auto& shape : m_customShapes)
{
@ -177,23 +183,35 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
{
wave->Draw(m_perFrameContext);
}
m_waveform.Draw();
m_waveform.Draw(m_perFrameContext);
// ToDo: Sprite drawing would go here.
// Done in DrawSprites() in Milkdrop
if (*m_perFrameContext.darken_center > 0)
{
m_darkenCenter.Draw();
}
m_border.Draw(m_perFrameContext);
// Todo: Song title anim would go here
// 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);
//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);
//m_framebuffer.Bind(0);
// ToDo: Draw user sprites (can have evaluated code)
// TEST: Copy result to default framebuffer
//glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
//glBlitFramebuffer(0, 0, renderContext.viewportSizeX, renderContext.viewportSizeY,
// 0, 0, renderContext.viewportSizeX, renderContext.viewportSizeY,
// GL_COLOR_BUFFER_BIT, GL_NEAREST);
}

View File

@ -107,5 +107,9 @@ private:
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.
DarkenCenter m_darkenCenter; //!< Center darkening effect.
Border m_border; //!< Inner/outer borders.
Filters m_filters; //!< Various post-processing filters, applied if no composite shader is used.
friend class MilkdropPresetFactory;
};

View File

@ -2,7 +2,7 @@
#include "PresetState.hpp"
#include "Renderer/StaticGlShaders.h"
#include "Renderer/StaticGlShaders.hpp"
#include <GLSLGenerator.h>
#include <HLSLParser.h>

View File

@ -2,74 +2,164 @@
#include <glm/gtc/type_ptr.hpp>
MotionVectors::MotionVectors()
MotionVectors::MotionVectors(PresetState& presetState)
: RenderItem()
, m_presetState(presetState)
{
RenderItem::Init();
}
void MotionVectors::InitVertexAttrib() {
void MotionVectors::InitVertexAttrib()
{
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glDisableVertexAttribArray(1);
}
void MotionVectors::Draw(const RenderContext& context)
void MotionVectors::Draw(const PerFrameContext& presetPerFrameContext)
{
// ToDo: Implement the actual Milkdop behaviour here, including reverse propagation.
float intervalx=1.0/x_num;
float intervaly=1.0/y_num;
int countX = static_cast<int>(*presetPerFrameContext.mv_x);
int countY = static_cast<int>(*presetPerFrameContext.mv_y);
if (countX <= 0 || countY <= 0)
{
return;
}
float divertX = static_cast<float>(*presetPerFrameContext.mv_x) - static_cast<float>(countX);
float divertY = static_cast<float>(*presetPerFrameContext.mv_y) - static_cast<float>(countY);
if (countX > 64)
{
countX = 64;
divertX = 0.0f;
}
if (countY > 48)
{
countY = 48;
divertY = 0.0f;
}
auto divertX2 = static_cast<float>(*presetPerFrameContext.mv_dx);
auto divertY2 = static_cast<float>(*presetPerFrameContext.mv_dy);
auto lengthMultiplier = static_cast<float>(*presetPerFrameContext.mv_l);
// Clamp X/Y diversions to 0..1
if (divertX < 0.0f)
{
divertX = 0.0f;
}
if (divertX > 1.0f)
{
divertX = 1.0f;
}
if (divertY < 0.0f)
{
divertY = 0.0f;
}
if (divertY > 1.0f)
{
divertY = 1.0f;
}
float const inverseWidth = 1.0f / static_cast<float>(m_presetState.viewportWidth);
float const minimalLength = 1.0f * inverseWidth;
struct Point {
float x{};
float y{};
};
std::vector<Point> lineVertices(static_cast<std::size_t>(countX + 1) * 2); // countX + 1 lines for each grid row, 2 vertices each.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (x_num + y_num < 600)
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glVertexAttrib4f(1,
static_cast<float>(*presetPerFrameContext.mv_r),
static_cast<float>(*presetPerFrameContext.mv_g),
static_cast<float>(*presetPerFrameContext.mv_b),
static_cast<float>(*presetPerFrameContext.mv_a));
glBindVertexArray(m_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, m_vboID);
for (int y = 0; y < countY; y++)
{
int size = x_num * y_num ;
float const posY = (static_cast<float>(y) + 0.25f) / (static_cast<float>(countY) + divertY + 0.25f - 1.0f) - divertY2;
floatPair *points = new float[size][2];
for (int x=0;x<(int)x_num;x++)
if (posY > 0.0001f && posY < 0.9999f)
{
for(int y=0;y<(int)y_num;y++)
int vertex = 0;
for (int x = 0; x < countX; x++)
{
float lx, ly;
lx = x_offset+x*intervalx;
ly = y_offset+y*intervaly;
float const posX = (static_cast<float>(x) + 0.25f) / (static_cast<float>(countX) + divertX + 0.25f - 1.0f) + divertX2;
points[(x * (int)y_num) + y][0] = lx;
points[(x * (int)y_num) + y][1] = ly;
if (posX > 0.0001f && posX < 0.9999f)
{
float posX2{};
float posY2{};
// Uses the warp mesh texture transformation to get the motion direction of this point.
ReversePropagatePoint(posX, posY, posX2, posY2);
// Enforce minimum trail length
{
float distX = posX2 - posX;
float distY = posY2 - posY;
distX *= lengthMultiplier;
distY *= lengthMultiplier;
float length = sqrtf(distX * distX + distY * distY);
if (length > minimalLength)
{
}
else if (length > 0.00000001f)
{
length = minimalLength / length;
distX *= length;
distY *= length;
}
else
{
distX = minimalLength;
distY = minimalLength;
}
posX2 = posX + distX;
posY2 = posY + distY;
}
// Assign line vertices
lineVertices.at(vertex).x = posX * 2.0f - 1.0f;
lineVertices.at(vertex).y = posY * 2.0f - 1.0f;
lineVertices.at(vertex + 1).x = posX2 * 2.0f - 1.0f;
lineVertices.at(vertex + 1).y = posY2 * 2.0f - 1.0f;
vertex += 2;
}
}
// Draw a row of lines.
glBufferData(GL_ARRAY_BUFFER, sizeof(Point) * lineVertices.size(), lineVertices.data(), GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINES, 0, static_cast<GLsizei>(lineVertices.size()));
}
glBindBuffer(GL_ARRAY_BUFFER, m_vboID);
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);
delete[] points;
glUseProgram(context.programID_v2f_c4f);
glUniformMatrix4fv(context.uniform_v2f_c4f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
#ifndef GL_TRANSITION
if (length <= 0.0) {
glPointSize(1.0);
} else {
glPointSize(length);
}
#endif
glUniform1f(context.uniform_v2f_c4f_vertex_point_size, length);
glVertexAttrib4f(1, r, g, b, a * masterAlpha);
glBindVertexArray(m_vaoID);
// ToDo: Milkdrop draws lines in the direction of motion, not just points!
glDrawArrays(GL_POINTS,0,size);
glBindVertexArray(0);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
Shader::Unbind();
glDisable(GL_BLEND);
}
void MotionVectors::ReversePropagatePoint(float posX1, float posY1, float& posX2, float& posY2)
{
}

View File

@ -1,5 +1,8 @@
#pragma once
#include "PerFrameContext.hpp"
#include "PresetState.hpp"
#include "Renderer/RenderItem.hpp"
/**
@ -14,28 +17,20 @@
class MotionVectors : public RenderItem
{
public:
MotionVectors() = delete;
/**
* Constructor. Initializes the required OpenGL data structures.
*/
MotionVectors();
explicit MotionVectors(PresetState& presetState);
void InitVertexAttrib();
/**
* Redners the motion vectors.
* @param context The render context data.
* @param presetPerFrameContext The per-frame context variables.
*/
void Draw(const RenderContext &context);
void Draw(const PerFrameContext& presetPerFrameContext);
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).
private:
void ReversePropagatePoint(float posX1, float posY1, float& posX2, float&posY2);
PresetState& m_presetState; //!< The global preset state.
};

View File

@ -2,9 +2,18 @@
#include "PresetFileParser.hpp"
#include "Renderer/StaticGlShaders.hpp"
const glm::mat4 PresetState::orthogonalProjection = glm::ortho(0.0f, 1.0f, 0.0f, 1.0f, -40.0f, 40.0f);
PresetState::PresetState()
: globalMemory(projectm_eval_memory_buffer_create())
{
auto staticShaders = StaticGlShaders::Get();
untexturedShader.CompileProgram(staticShaders->GetV2fC4fVertexShader(),
staticShaders->GetV2fC4fFragmentShader());
texturedShader.CompileProgram(staticShaders->GetV2fC4fT2fVertexShader(),
staticShaders->GetV2fC4fT2fFragmentShader());
}
PresetState::~PresetState()

View File

@ -7,11 +7,15 @@
#include "Constants.hpp"
#include "Audio/FrameAudioData.hpp"
#include <Audio/FrameAudioData.hpp>
#include <Renderer/RenderContext.hpp>
#include <Renderer/Shader.hpp>
#include <projectm-eval.h>
#include <glm/gtc/matrix_transform.hpp>
#include <string>
class PresetFileParser;
@ -146,4 +150,11 @@ public:
std::string warpShader; //!< Warp shader code.
std::string compositeShader; //!< Composite shader code.
Shader untexturedShader; //!< Shader used to draw untextured primitives, e.g. waveforms.
Shader texturedShader; //!< Shader used to draw textured primitives, e.g. textured shapes and the warp mesh.
int mainTextureId{}; //!< ID of the previous frame texture.
static const glm::mat4 orthogonalProjection;
};

View File

@ -9,64 +9,80 @@
#include "Renderer/ShaderEngine.hpp"
#include <glm/gtc/type_ptr.hpp>
VideoEcho::VideoEcho(): a(0), zoom(1), orientation(Normal)
VideoEcho::VideoEcho(PresetState& presetState)
: RenderItem()
, m_presetState(presetState)
{
Init();
RenderItem::Init();
}
VideoEcho::~VideoEcho()
void VideoEcho::InitVertexAttrib()
{
}
void VideoEcho::InitVertexAttrib() {
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float)*4, (void*)0); // Positions
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, nullptr); // Positions
glDisableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float)*4, (void*)(sizeof(float)*2)); // Textures
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, reinterpret_cast<void*>(sizeof(float) * 2)); // Textures
}
void VideoEcho::Draw(RenderContext &context)
void VideoEcho::Draw(PerFrameContext& perFrameContext)
{
int flipx=1, flipy=1;
switch (orientation)
{
case Normal: flipx=1;flipy=1;break;
case FlipX: flipx=-1;flipy=1;break;
case FlipY: flipx=1;flipy=-1;break;
case FlipXY: flipx=-1;flipy=-1;break;
default: flipx=1;flipy=1; break;
}
int orientation = static_cast<int>(m_presetState.videoEchoOrientation) % 4;
int flipX;
int flipY;
switch (orientation)
{
case 0:
flipX = 1;
flipY = 1;
break;
case 1:
flipX = -1;
flipY = 1;
break;
case 2:
flipX = 1;
flipY = -1;
break;
case 3:
flipX = -1;
flipY = -1;
break;
default:
flipX = 1;
flipY = 1;
break;
}
float buffer_data[8][2] = {
{-0.5f*flipx, -0.5f*flipy},
{-0.5f * flipX, -0.5f * flipY},
{0.0, 1.0},
{-0.5f*flipx, 0.5f*flipy},
{-0.5f * flipX, 0.5f * flipY},
{0.0, 0.0},
{ 0.5f*flipx, 0.5f*flipy},
{0.5f * flipX, 0.5f * flipY},
{1.0, 0.0},
{ 0.5f*flipx, -0.5f*flipy},
{1.0, 1.0}
};
{0.5f * flipX, -0.5f * flipY},
{1.0, 1.0}};
glm::mat4 mat_first_translation = glm::mat4(1.0);
mat_first_translation[3][0] = -0.5;
mat_first_translation[3][1] = -0.5;
glm::mat4 mat_scale = glm::mat4(1.0);
mat_scale[0][0] = 1.0/zoom;
mat_scale[1][1] = 1.0/zoom;
glm::mat4 mat_scale = glm::mat4(1.0);
mat_scale[0][0] = 1.0f / static_cast<float>(m_presetState.videoEchoZoom);
mat_scale[1][1] = 1.0f / static_cast<float>(m_presetState.videoEchoZoom);
glm::mat4 mat_second_translation = glm::mat4(1.0);
glm::mat4 mat_second_translation = glm::mat4(1.0);
mat_second_translation[3][0] = 0.5;
mat_second_translation[3][1] = 0.5;
for (int i = 1; i < 8; i+=2) {
for (int i = 1; i < 8; i += 2)
{
glm::vec4 texture = glm::vec4(buffer_data[i][0], buffer_data[i][1], 0, 1);
texture = mat_first_translation * texture;
texture = mat_scale * texture;
@ -84,16 +100,14 @@ void VideoEcho::Draw(RenderContext &context)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glUseProgram(context.programID_v2f_c4f_t2f);
glUniformMatrix4fv(context.uniform_v2f_c4f_t2f_vertex_transformation, 1, GL_FALSE, glm::value_ptr(context.mat_ortho));
glUniform1i(context.uniform_v2f_c4f_t2f_frag_texture_sampler, 0);
m_presetState.texturedShader.Bind();
m_presetState.texturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
m_presetState.texturedShader.SetUniformInt("texture_sampler", 0);
//Now Blend the Video Echo
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, a * masterAlpha);
glVertexAttrib4f(1, 1.0, 1.0, 1.0, static_cast<float>(m_presetState.videoEchoAlpha));
glBindVertexArray(m_vaoID);
@ -102,5 +116,5 @@ void VideoEcho::Draw(RenderContext &context)
glBindVertexArray(0);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

View File

@ -1,23 +1,21 @@
#pragma once
#include "projectM-opengl.h"
#include "Renderer/RenderItem.hpp"
#include "PerFrameContext.hpp"
#include "PresetState.hpp"
enum Orientation
{
Normal=0, FlipX, FlipY, FlipXY
};
#include "projectM-opengl.h"
#include <Renderer/RenderItem.hpp>
class VideoEcho: public RenderItem
{
public:
VideoEcho();
virtual ~VideoEcho();
VideoEcho() = delete;
explicit VideoEcho(PresetState& presetState);
float a;
float zoom;
Orientation orientation;
void InitVertexAttrib() override;
void InitVertexAttrib();
void Draw(RenderContext &context);
void Draw(PerFrameContext& perFrameContext);
private:
PresetState& m_presetState; //!< The global preset state.
};

View File

@ -1,5 +1,6 @@
#include "Waveform.hpp"
#include "PerFrameContext.hpp"
#include "PresetState.hpp"
#include "Audio/BeatDetect.hpp"
@ -13,36 +14,54 @@ Waveform::Waveform(PresetState& presetState)
: RenderItem()
, m_presetState(presetState)
{
Init();
SetMode();
RenderItem::Init();
}
Waveform::~Waveform()
{
delete[] wave1Vertices;
delete[] wave2Vertices;
delete[] m_wave1Vertices;
delete[] m_wave2Vertices;
}
void Waveform::InitVertexAttrib()
{
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glDisableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
}
void Waveform::Draw()
void Waveform::Draw(const PerFrameContext& presetPerFrameContext)
{
std::array<WaveformVertex, RenderWaveformSamples * 2> smoothedWave;
WaveformMath();
#if USE_GLES == 0
glDisable(GL_LINE_SMOOTH);
#endif
glLineWidth(1);
m_presetState.untexturedShader.Bind();
m_presetState.untexturedShader.SetUniformMat4x4("vertex_transformation", m_presetState.orthogonalProjection);
glBindVertexArray(m_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, m_vboID);
// Additive wave drawing (vice overwrite)
glEnable(GL_BLEND);
if (m_presetState.additiveWaves)
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
else
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
WaveformMath(presetPerFrameContext);
std::vector<WaveformVertex> smoothedWave(m_samples * 2);
for (int waveIndex = 0; waveIndex < 2; waveIndex++)
{
auto* const waveVertices = (waveIndex == 0) ? wave1Vertices : wave2Vertices;
auto* const waveVertices = (waveIndex == 0) ? m_wave1Vertices : m_wave2Vertices;
if (!waveVertices)
{
continue;
@ -52,24 +71,12 @@ void Waveform::Draw()
// Instead of using the "break" index like Milkdrop, we simply have two separate arrays.
const auto smoothedSamples = SmoothWave(waveVertices, smoothedWave.data());
glUseProgram(m_presetState.renderContext.programID_v2f_c4f);
m_tempAlpha = m_presetState.waveAlpha;
m_tempAlpha = static_cast<float>(*presetPerFrameContext.wave_a);
if (m_presetState.modWaveAlphaByvolume)
{
ModulateOpacityByVolume();
}
MaximizeColors();
// Additive wave drawing (vice overwrite)
if (m_presetState.additiveWaves)
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
}
else
{
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
ModulateOpacityByVolume(presetPerFrameContext);
}
MaximizeColors(presetPerFrameContext);
// Always draw "thick" dots.
const auto iterations = m_presetState.waveThick || m_presetState.waveDots ? 4 : 1;
@ -80,9 +87,6 @@ void Waveform::Draw()
GLuint drawType = m_presetState.waveDots ? GL_POINTS : (m_loop ? GL_LINE_LOOP : GL_LINE_STRIP);
glBindVertexArray(m_vaoID);
glBindBuffer(GL_ARRAY_BUFFER, m_vboID);
// If thick outline is used, draw the shape four times with slight offsets
// (top left, top right, bottom right, bottom left).
for (auto iteration = 0; iteration < iterations; iteration++)
@ -117,15 +121,17 @@ void Waveform::Draw()
glBufferData(GL_ARRAY_BUFFER, sizeof(WaveformVertex) * smoothedSamples, smoothedWave.data(), GL_DYNAMIC_DRAW);
glDrawArrays(drawType, 0, smoothedSamples);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
glUseProgram(0);
glDisable(GL_BLEND);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
Shader::Unbind();
}
void Waveform::ModulateOpacityByVolume()
void Waveform::ModulateOpacityByVolume(const PerFrameContext& presetPerFrameContext)
{
//modulate volume by opacity
//
@ -138,23 +144,15 @@ void Waveform::ModulateOpacityByVolume()
}
else if (m_presetState.audioData.vol >= m_presetState.modWaveAlphaEnd)
{
m_tempAlpha = m_presetState.waveAlpha;
m_tempAlpha = static_cast<float>(*presetPerFrameContext.wave_a);
}
else
{
m_tempAlpha = m_presetState.waveAlpha * ((m_presetState.audioData.vol - m_presetState.modWaveAlphaStart) / (m_presetState.modWaveAlphaEnd - m_presetState.modWaveAlphaStart));
m_tempAlpha = static_cast<float>(*presetPerFrameContext.wave_a) * ((m_presetState.audioData.vol - m_presetState.modWaveAlphaStart) / (m_presetState.modWaveAlphaEnd - m_presetState.modWaveAlphaStart));
}
}
void Waveform::SetMode()
{
if (m_presetState.waveMode <= 8)
{
m_mode = static_cast<Mode>(m_presetState.waveMode);
}
}
void Waveform::MaximizeColors()
void Waveform::MaximizeColors(const PerFrameContext& presetPerFrameContext)
{
//wave color brightening
//
@ -209,12 +207,12 @@ void Waveform::MaximizeColors()
m_tempAlpha = 1.0f;
}
if (m_presetState.maximizeWaveColor)
if (*presetPerFrameContext.wave_brighten)
{
constexpr float fMaximizeWaveColorAmount = 1.0f;
float cr{m_presetState.waveR};
float cg{m_presetState.waveG};
float cb{m_presetState.waveB};
float cr{static_cast<float>(*presetPerFrameContext.wave_r)};
float cg{static_cast<float>(*presetPerFrameContext.wave_g)};
float cb{static_cast<float>(*presetPerFrameContext.wave_b)};
float max = cr;
if (max < cg)
@ -232,28 +230,34 @@ void Waveform::MaximizeColors()
cb = cb / max * fMaximizeWaveColorAmount + cb * (1.0f - fMaximizeWaveColorAmount);
}
glVertexAttrib4f(1, cr, cg, cb, m_tempAlpha * masterAlpha);
glVertexAttrib4f(1, cr, cg, cb, m_tempAlpha);
}
else
{
glVertexAttrib4f(1, m_presetState.waveR, m_presetState.waveG, m_presetState.waveB, m_tempAlpha * masterAlpha);
glVertexAttrib4f(1,
static_cast<float>(*presetPerFrameContext.wave_r),
static_cast<float>(*presetPerFrameContext.wave_g),
static_cast<float>(*presetPerFrameContext.wave_b),
m_tempAlpha);
}
}
void Waveform::WaveformMath()
void Waveform::WaveformMath(const PerFrameContext& presetPerFrameContext)
{
constexpr float PI{3.14159274101257324219f};
constexpr size_t audioSamples{512};
delete[] wave1Vertices;
wave1Vertices = nullptr;
delete[] wave2Vertices;
wave2Vertices = nullptr;
delete[] m_wave1Vertices;
m_wave1Vertices = nullptr;
delete[] m_wave2Vertices;
m_wave2Vertices = nullptr;
// NOTE: Buffer size is always 512 samples, waveform points 480 or less since some waveforms use a positive offset!
std::array<float, WaveformMaxPoints> pcmDataL{0.0f};
std::array<float, WaveformMaxPoints> pcmDataR{0.0f};
std::array<float, audioSamples> pcmDataL{0.0f};
std::array<float, audioSamples> pcmDataR{0.0f};
m_mode = static_cast<Mode>(m_presetState.waveMode % 9);
if (m_mode != Mode::SpectrumLine)
{
@ -299,7 +303,7 @@ void Waveform::WaveformMath()
m_loop = false;
float mysteryWaveParam = m_presetState.waveParam;
float mysteryWaveParam = static_cast<float>(*presetPerFrameContext.wave_mystery);
if ((m_mode == Mode::Circle || m_mode == Mode::XYOscillationSpiral || m_mode == Mode::DerivativeLine) && (mysteryWaveParam < 1.0f || mysteryWaveParam > 1.0f))
{
@ -309,53 +313,56 @@ void Waveform::WaveformMath()
mysteryWaveParam = mysteryWaveParam * 2 - 1;
}
auto const waveX = static_cast<float>(*presetPerFrameContext.wave_x);
auto const waveY = static_cast<float>(*presetPerFrameContext.wave_y);
switch (m_mode)
{
case Mode::Circle: {
m_loop = true;
int const samples = RenderWaveformSamples / 2;
m_samples = RenderWaveformSamples / 2;
wave1Vertices = new WaveformVertex[samples]();
m_wave1Vertices = new WaveformVertex[m_samples]();
const int sampleOffset{(RenderWaveformSamples - samples) / 2};
const int sampleOffset{(RenderWaveformSamples - m_samples) / 2};
const float inverseSamplesMinusOne{1.0f / static_cast<float>(samples)};
const float inverseSamplesMinusOne{1.0f / static_cast<float>(m_samples)};
for (int i = 0; i < samples; i++)
for (int i = 0; i < m_samples; i++)
{
float radius = 0.5f + 0.4f * pcmDataR[i + sampleOffset] + mysteryWaveParam;
float const angle = static_cast<float>(i) * inverseSamplesMinusOne * 6.28f + m_presetState.renderContext.time * 0.2f;
if (i < samples / 10)
if (i < m_samples / 10)
{
float mix = static_cast<float>(i) / (static_cast<float>(samples) * 0.1f);
float mix = static_cast<float>(i) / (static_cast<float>(m_samples) * 0.1f);
mix = 0.5f - 0.5f * cosf(mix * 3.1416f);
float const radius2 = 0.5f + 0.4f * pcmDataR[i + samples + sampleOffset] + mysteryWaveParam;
float const radius2 = 0.5f + 0.4f * pcmDataR[i + m_samples + sampleOffset] + mysteryWaveParam;
radius = radius2 * (1.0f - mix) + radius * (mix);
}
radius *= 0.5f;
wave1Vertices[i].x = radius * cosf(angle) * aspectY + m_presetState.waveX;
wave1Vertices[i].y = radius * sinf(angle) * aspectX + m_presetState.waveY;
m_wave1Vertices[i].x = radius * cosf(angle) * aspectY + waveX;
m_wave1Vertices[i].y = radius * sinf(angle) * aspectX + waveY;
}
break;
}
case Mode::XYOscillationSpiral: //circularly moving waveform
{
int const samples = RenderWaveformSamples / 2;
m_samples = RenderWaveformSamples / 2;
wave1Vertices = new WaveformVertex[samples]();
m_wave1Vertices = new WaveformVertex[m_samples]();
for (int i = 0; i < samples; i++)
for (int i = 0; i < m_samples; i++)
{
float const radius = (0.53f + 0.43f * pcmDataR[i] + mysteryWaveParam) * 0.5f;
float const angle = pcmDataL[i + 32] * PI * 0.5f + m_presetState.renderContext.time * 2.3f;
wave1Vertices[i].x = radius * cosf(angle) * aspectY + m_presetState.waveX;
wave1Vertices[i].y = radius * sinf(angle) * aspectX + m_presetState.waveY;
m_wave1Vertices[i].x = radius * cosf(angle) * aspectY + waveX;
m_wave1Vertices[i].y = radius * sinf(angle) * aspectX + waveY;
}
break;
}
@ -363,69 +370,69 @@ void Waveform::WaveformMath()
case Mode::Blob2:
case Mode::Blob3: { // Both "centered spiro" waveforms are identical. Only difference is the alpha value.
// Alpha calculation is handled in MaximizeColors().
int const samples = RenderWaveformSamples;
m_samples = RenderWaveformSamples;
wave1Vertices = new WaveformVertex[samples]();
m_wave1Vertices = new WaveformVertex[m_samples]();
for (int i = 0; i < samples; i++)
for (int i = 0; i < m_samples; i++)
{
wave1Vertices[i].x = 0.5f * pcmDataR[i] * aspectY + m_presetState.waveX;
wave1Vertices[i].y = 0.5f * pcmDataL[i + 32] * aspectX + m_presetState.waveY;
m_wave1Vertices[i].x = 0.5f * pcmDataR[i] * aspectY + waveX;
m_wave1Vertices[i].y = 0.5f * pcmDataL[i + 32] * aspectX + waveY;
}
break;
}
case Mode::DerivativeLine: {
int samples = RenderWaveformSamples;
m_samples = RenderWaveformSamples;
if (samples > m_presetState.renderContext.viewportSizeX / 3)
if (m_samples > m_presetState.renderContext.viewportSizeX / 3)
{
samples /= 3;
m_samples /= 3;
}
wave1Vertices = new WaveformVertex[samples]();
m_wave1Vertices = new WaveformVertex[m_samples]();
int const sampleOffset = (RenderWaveformSamples - samples) / 2;
int const sampleOffset = (RenderWaveformSamples - m_samples) / 2;
const float w1 = 0.45f + 0.5f * (mysteryWaveParam * 0.5f + 0.5f);
const float w2 = 1.0f - w1;
const float inverseSamples = 1.0f / static_cast<float>(samples);
const float inverseSamples = 1.0f / static_cast<float>(m_samples);
for (int i = 0; i < samples; i++)
for (int i = 0; i < m_samples; i++)
{
assert((i + 25 + sampleOffset) < 512);
wave1Vertices[i].x = (-1.0f + 2.0f * (i * inverseSamples)) * 0.5f + m_presetState.waveX;
wave1Vertices[i].y = pcmDataL[i + sampleOffset] * 0.235f + m_presetState.waveY;
wave1Vertices[i].x += pcmDataR[i + 25 + sampleOffset] * 0.22f;
m_wave1Vertices[i].x = (-1.0f + 2.0f * (i * inverseSamples)) * 0.5f + waveX;
m_wave1Vertices[i].y = pcmDataL[i + sampleOffset] * 0.235f + waveY;
m_wave1Vertices[i].x += pcmDataR[i + 25 + sampleOffset] * 0.22f;
// Momentum
if (i > 1)
{
wave1Vertices[i].x =
wave1Vertices[i].x * w2 + w1 * (wave1Vertices[i - 1].x * 2.0f - wave1Vertices[i - 2].x);
wave1Vertices[i].y =
wave1Vertices[i].y * w2 + w1 * (wave1Vertices[i - 1].y * 2.0f - wave1Vertices[i - 2].y);
m_wave1Vertices[i].x =
m_wave1Vertices[i].x * w2 + w1 * (m_wave1Vertices[i - 1].x * 2.0f - m_wave1Vertices[i - 2].x);
m_wave1Vertices[i].y =
m_wave1Vertices[i].y * w2 + w1 * (m_wave1Vertices[i - 1].y * 2.0f - m_wave1Vertices[i - 2].y);
}
}
break;
}
case Mode::ExplosiveHash: {
int const samples = RenderWaveformSamples;
m_samples = RenderWaveformSamples;
wave1Vertices = new WaveformVertex[samples]();
m_wave1Vertices = new WaveformVertex[m_samples]();
const float cosineRotation = cosf(m_presetState.renderContext.time * 0.3f);
const float sineRotation = sinf(m_presetState.renderContext.time * 0.3f);
for (int i = 0; i < samples; i++)
for (int i = 0; i < m_samples; i++)
{
const float x0 = (pcmDataR[i] * pcmDataL[i + 32] + pcmDataL[i] * pcmDataR[i + 32]);
const float y0 = (pcmDataR[i] * pcmDataR[i] - pcmDataL[i + 32] * pcmDataL[i + 32]);
wave1Vertices[i].x = ((x0 * cosineRotation - y0 * sineRotation) * 0.5f * aspectY) + m_presetState.waveX;
wave1Vertices[i].y = ((x0 * sineRotation + y0 * cosineRotation) * 0.5f * aspectX) + m_presetState.waveY;
m_wave1Vertices[i].x = ((x0 * cosineRotation - y0 * sineRotation) * 0.5f * aspectY) + waveX;
m_wave1Vertices[i].y = ((x0 * sineRotation + y0 * cosineRotation) * 0.5f * aspectX) + waveY;
}
break;
}
@ -434,40 +441,39 @@ void Waveform::WaveformMath()
case Mode::DoubleLine:
case Mode::SpectrumLine: // Unfinished
{
int samples;
if (m_mode == Mode::SpectrumLine)
{
samples = 256;
m_samples = 256;
}
else
{
samples = RenderWaveformSamples / 2;
m_samples = RenderWaveformSamples / 2;
if (samples > m_presetState.renderContext.viewportSizeX / 3)
if (m_samples > m_presetState.viewportWidth / 3)
{
samples /= 3;
m_samples /= 3;
}
}
wave1Vertices = new WaveformVertex[samples]();
m_wave1Vertices = new WaveformVertex[m_samples]();
if (m_mode == Mode::DoubleLine)
{
wave2Vertices = new WaveformVertex[samples]();
m_wave2Vertices = new WaveformVertex[m_samples]();
}
const int sampleOffset = (RenderWaveformSamples - samples) / 2;
const int sampleOffset = (RenderWaveformSamples - m_samples) / 2;
const float angle = PI * 0.5f * mysteryWaveParam; // from -PI/2 to PI/2
float dx = cosf(angle);
float dy = sinf(angle);
std::array<float, 2> edgeX{
m_presetState.waveX * cosf(angle + PI * 0.5f) - dx * 3.0f,
m_presetState.waveX * cosf(angle + PI * 0.5f) + dx * 3.0f};
waveX * cosf(angle + PI * 0.5f) - dx * 3.0f,
waveX * cosf(angle + PI * 0.5f) + dx * 3.0f};
std::array<float, 2> edgeY{
m_presetState.waveX * sinf(angle + PI * 0.5f) - dy * 3.0f,
m_presetState.waveX * sinf(angle + PI * 0.5f) + dy * 3.0f};
waveX * sinf(angle + PI * 0.5f) - dy * 3.0f,
waveX * sinf(angle + PI * 0.5f) + dy * 3.0f};
for (int i = 0; i < 2; i++)
{
@ -521,8 +527,8 @@ void Waveform::WaveformMath()
}
}
dx = (edgeX[1] - edgeX[0]) / static_cast<float>(samples);
dy = (edgeY[1] - edgeY[0]) / static_cast<float>(samples);
dx = (edgeX[1] - edgeX[0]) / static_cast<float>(m_samples);
dy = (edgeY[1] - edgeY[0]) / static_cast<float>(m_samples);
const float angle2 = atan2f(dy, dx);
const float perpetualDX = cosf(angle2 + PI * 0.5f);
@ -530,37 +536,37 @@ void Waveform::WaveformMath()
if (m_mode == Mode::Line)
{
for (int i = 0; i < samples; i++)
for (int i = 0; i < m_samples; i++)
{
wave1Vertices[i].x =
m_wave1Vertices[i].x =
edgeX[0] + dx * static_cast<float>(i) + perpetualDX * 0.25f * pcmDataL[i + sampleOffset];
wave1Vertices[i].y =
m_wave1Vertices[i].y =
edgeY[0] + dy * static_cast<float>(i) + perpetualDY * 0.25f * pcmDataL[i + sampleOffset];
}
}
else if (m_mode == Mode::SpectrumLine)
{
for (int i = 0; i < samples; i++)
for (int i = 0; i < m_samples; i++)
{
const float f = 0.1f * logf(pcmDataL[i * 2] + pcmDataL[i * 2 + 1]);
wave1Vertices[i].x = edgeX[0] + dx * static_cast<float>(i) + perpetualDX * f;
wave1Vertices[i].y = edgeY[0] + dy * static_cast<float>(i) + perpetualDY * f;
m_wave1Vertices[i].x = edgeX[0] + dx * static_cast<float>(i) + perpetualDX * f;
m_wave1Vertices[i].y = edgeY[0] + dy * static_cast<float>(i) + perpetualDY * f;
}
}
else
{
float const separation = powf(m_presetState.waveY * 0.25f + 0.25f, 2.0f);
for (int i = 0; i < samples; i++)
float const separation = powf(waveY * 0.25f + 0.25f, 2.0f);
for (int i = 0; i < m_samples; i++)
{
wave1Vertices[i].x = edgeX[0] + dx * static_cast<float>(i) +
perpetualDX * (0.25f * pcmDataL[i + sampleOffset] + separation);
wave1Vertices[i].y = edgeY[0] + dy * static_cast<float>(i) +
perpetualDY * (0.25f * pcmDataL[i + sampleOffset] + separation);
m_wave1Vertices[i].x = edgeX[0] + dx * static_cast<float>(i) +
perpetualDX * (0.25f * pcmDataL[i + sampleOffset] + separation);
m_wave1Vertices[i].y = edgeY[0] + dy * static_cast<float>(i) +
perpetualDY * (0.25f * pcmDataL[i + sampleOffset] + separation);
wave2Vertices[i].x = edgeX[0] + dx * static_cast<float>(i) +
perpetualDX * (0.25f * pcmDataR[i + sampleOffset] - separation);
wave2Vertices[i].y = edgeY[0] + dy * static_cast<float>(i) +
perpetualDY * (0.25f * pcmDataR[i + sampleOffset] - separation);
m_wave2Vertices[i].x = edgeX[0] + dx * static_cast<float>(i) +
perpetualDX * (0.25f * pcmDataR[i + sampleOffset] - separation);
m_wave2Vertices[i].y = edgeY[0] + dy * static_cast<float>(i) +
perpetualDY * (0.25f * pcmDataR[i + sampleOffset] - separation);
}
}
break;
@ -571,15 +577,15 @@ void Waveform::WaveformMath()
}
// Reverse all Y coordinates to stay consistent with the pre-VMS milkdrop
for (int i = 0; i < RenderWaveformSamples; i++)
for (int i = 0; i < m_samples; i++)
{
if (wave1Vertices)
if (m_wave1Vertices)
{
wave1Vertices[i].y = 1.0f - wave1Vertices[i].y;
m_wave1Vertices[i].y = 1.0f - m_wave1Vertices[i].y;
}
if (wave2Vertices)
if (m_wave2Vertices)
{
wave2Vertices[i].y = 1.0f - wave2Vertices[i].y;
m_wave2Vertices[i].y = 1.0f - m_wave2Vertices[i].y;
}
}
}
@ -597,10 +603,10 @@ int Waveform::SmoothWave(const Waveform::WaveformVertex* inputVertices,
int indexBelow = 0;
int indexAbove2 = 1;
for (auto inputIndex = 0; inputIndex < RenderWaveformSamples - 1; inputIndex++)
for (auto inputIndex = 0; inputIndex < m_samples - 1; inputIndex++)
{
int const indexAbove = indexAbove2;
indexAbove2 = std::min(RenderWaveformSamples - 1, inputIndex + 2);
indexAbove2 = std::min(m_samples - 1, inputIndex + 2);
outputVertices[outputIndex] = inputVertices[inputIndex];
outputVertices[outputIndex + 1].x = (c1 * inputVertices[indexBelow].x + c2 * inputVertices[inputIndex].x + c3 * inputVertices[indexAbove].x + c4 * inputVertices[indexAbove2].x) * inverseSum;
outputVertices[outputIndex + 1].y = (c1 * inputVertices[indexBelow].y + c2 * inputVertices[inputIndex].y + c3 * inputVertices[indexAbove].y + c4 * inputVertices[indexAbove2].y) * inverseSum;
@ -608,7 +614,7 @@ int Waveform::SmoothWave(const Waveform::WaveformVertex* inputVertices,
outputIndex += 2;
}
outputVertices[outputIndex] = inputVertices[RenderWaveformSamples - 1];
outputVertices[outputIndex] = inputVertices[m_samples - 1];
return outputIndex + 1;
}

View File

@ -27,7 +27,7 @@ public:
Waveform(PresetState& presetState);
~Waveform() override;
void Draw();
void Draw(const PerFrameContext& presetPerFrameContext);
void InitVertexAttrib() override;
@ -40,9 +40,9 @@ private:
void SetMode();
void MaximizeColors();
void ModulateOpacityByVolume();
void WaveformMath();
void MaximizeColors(const PerFrameContext& presetPerFrameContext);
void ModulateOpacityByVolume(const PerFrameContext& presetPerFrameContext);
void WaveformMath(const PerFrameContext& presetPerFrameContext);
/**
* @brief Does a better-than-linear smooth on a wave.
@ -50,19 +50,19 @@ private:
* Roughly doubles the number of points.
*
* @param inputVertices Pointer to an array of vertices to be smoothed.
* @param vertexCount Number of vertices/points in the input data.
* @param outputVertices Pointer to a buffer that will receive the smoothed data. Must be able to hold 2 * vertexCount vertices.
* @return The number of vertices in outputVertices after smoothing.
*/
static auto SmoothWave(const WaveformVertex* inputVertices, WaveformVertex* outputVertices) -> int;
auto SmoothWave(const WaveformVertex* inputVertices, WaveformVertex* outputVertices) -> int;
PresetState& m_presetState;
PresetState& m_presetState; //!< The preset state.
Mode m_mode{Mode::Line};
Mode m_mode{Mode::Line}; //!< Line drawing mode.
float m_tempAlpha{0.0f};
bool m_loop{false};
float m_tempAlpha{0.0f}; //!< Calculated alpha value.
bool m_loop{false}; //!< true if the waveform is a closed loop.
int m_samples{}; //!< Number of samples in the current waveform. Depends on the mode.
WaveformVertex* wave1Vertices{nullptr};
WaveformVertex* wave2Vertices{nullptr};
WaveformVertex* m_wave1Vertices{nullptr};
WaveformVertex* m_wave2Vertices{nullptr};
};

View File

@ -42,7 +42,7 @@ void WaveformPerPointContext::RegisterBuiltinVariables()
for (int t = 0; t < TVarCount; t++)
{
std::string tvar = "t" + std::to_string(t + 1);
q_vars[t] = projectm_eval_context_register_variable(perPointCodeContext, tvar.c_str());
t_vars[t] = projectm_eval_context_register_variable(perPointCodeContext, tvar.c_str());
}
REG_VAR(bass);

View File

@ -98,12 +98,14 @@ void ProjectM::LoadPresetData(std::istream& presetData, bool smoothTransition)
void ProjectM::SetTexturePaths(std::vector<std::string> texturePaths)
{
m_textureSearchPaths = std::move(texturePaths);
m_renderer->SetTextureSearchPaths(m_textureSearchPaths);
m_textureManager = std::make_unique<TextureManager>(m_textureSearchPaths);
//m_renderer->SetTextureSearchPaths(m_textureSearchPaths);
}
void ProjectM::ResetTextures()
{
m_renderer->ResetTextures();
m_textureManager = std::make_unique<TextureManager>(m_textureSearchPaths);
//m_renderer->ResetTextures();
}
void ProjectM::DumpDebugImageOnNextFrame(const std::string& outputFile)
@ -179,6 +181,23 @@ void ProjectM::RenderFrame()
}
// ToDo: Call the to-be-implemented render method in Renderer
auto audio = m_beatDetect->GetFrameAudioData();
RenderContext ctx{};
ctx.viewportSizeX = m_windowWidth;
ctx.viewportSizeY = m_windowHeight;
ctx.time = m_timeKeeper->GetRunningTime();
ctx.progress = m_timeKeeper->PresetProgressA();
ctx.fps = m_targetFps;
ctx.frame = m_count;
ctx.aspectX = (m_windowHeight > m_windowWidth) ? static_cast<float>(m_windowWidth) / static_cast<float>(m_windowHeight) : 1.0f;
ctx.aspectY = (m_windowWidth > m_windowHeight) ? static_cast<float>(m_windowHeight) / static_cast<float>(m_windowWidth) : 1.0f;
ctx.invAspectX = 1.0f / ctx.aspectX;
ctx.invAspectY = 1.0f / ctx.aspectY;
m_activePreset->RenderFrame(audio, ctx);
m_count++;
}
void ProjectM::Reset()
@ -213,6 +232,8 @@ void ProjectM::Initialize()
*m_beatDetect,
m_textureSearchPaths);
m_textureManager = std::make_unique<TextureManager>(m_textureSearchPaths);
m_presetFactoryManager->initialize();
/* Set the seed to the current time in seconds */

View File

@ -60,6 +60,8 @@ class Func;
class Renderer;
class TextureManager;
class Preset;
class TimeKeeper;
@ -256,6 +258,7 @@ private:
std::unique_ptr<PresetFactoryManager> m_presetFactoryManager; //!< Provides access to all available preset factories.
std::unique_ptr<Renderer> m_renderer; //!< The Preset renderer.
std::unique_ptr<TextureManager> m_textureManager; //!< The texture manager.
std::unique_ptr<libprojectM::Audio::BeatDetect> m_beatDetect; //!< The beat detection class.
std::unique_ptr<Preset> m_activePreset; //!< Currently loaded preset.
std::unique_ptr<Preset> m_transitioningPreset; //!< Destination preset when smooth preset switching.

View File

@ -106,6 +106,22 @@ void Framebuffer::CreateColorAttachment(int framebufferIndex, int attachmentInde
}
}
GLuint Framebuffer::GetColorAttachmentTexture(int framebufferIndex, int attachmentIndex) const
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))
{
return 0;
}
const auto& attachment = m_attachments.at(framebufferIndex);
if (attachment.find(GL_COLOR_ATTACHMENT0 + attachmentIndex) == attachment.end())
{
return 0;
}
return attachment.at(GL_COLOR_ATTACHMENT0 + attachmentIndex).TextureId();
}
void Framebuffer::CreateDepthAttachment(int framebufferIndex)
{
if (framebufferIndex < 0 || framebufferIndex >= static_cast<int>(m_framebufferIds.size()))

View File

@ -77,22 +77,34 @@ public:
/**
* @brief Adds a new color attachment to the framebuffer.
* The texture is always created in RGBA format.
* @param framebufferIndex The framebuffer index.
* @param index The index of the attachment, at least indices 0-7 are guaranteed to be available.
*/
void CreateColorAttachment(int framebufferIndex, int attachmentIndex);
/**
* @brief Returns the texture ID of the given framebuffer and color attachment.
* @param framebufferIndex The framebuffer index.
* @param attachmentIndex The index of the attachment to return the texture for.
* @return The texture ID or 0 if no texture is assigned.
*/
auto GetColorAttachmentTexture(int framebufferIndex, int attachmentIndex) const -> GLuint;
/**
* @brief Adds a depth attachment to the framebuffer.
* @param framebufferIndex The framebuffer index.
*/
void CreateDepthAttachment(int framebufferIndex);
/**
* @brief Adds a stencil buffer attachment to the framebuffer.
* @param framebufferIndex The framebuffer index.
*/
void CreateStencilAttachment(int framebufferIndex);
/**
* @brief Adds a depth stencil buffer attachment to the framebuffer.
* @param framebufferIndex The framebuffer index.
*/
void CreateDepthStencilAttachment(int framebufferIndex);

View File

@ -26,11 +26,5 @@ public:
TextureManager* textureManager{nullptr}; //!< Holds all loaded textures for shader access.
GLuint programID_v2f_c4f{0}; //!< Vertex shader program ID (no texture coordinates)
GLuint programID_v2f_c4f_t2f{0}; //!< Vertex shader program ID (with texture coordinates)
GLint uniform_v2f_c4f_vertex_transformation{0}; //!< Uniform location of the vertex shader transformation parameter (untextured output)
GLint uniform_v2f_c4f_vertex_point_size{0}; //!< Uniform location of the vertex shader point size parameter (untextured output)
GLint uniform_v2f_c4f_t2f_vertex_transformation{0}; //!< Uniform location of the vertex shader transformation parameter (textured output)
GLint uniform_v2f_c4f_t2f_frag_texture_sampler{0}; //!< Uniform location of the vertex shader transformation parameter (textured output)
glm::mat4 mat_ortho{0.0}; //!< Orthographic projection matrix.
};

View File

@ -109,7 +109,7 @@ Renderer::Renderer(int viewportWidth, int viewportHeight, int meshX, int meshY,
void Renderer::ResetTextures()
{
m_textureManager = std::make_unique<TextureManager>(m_textureSearchPaths, m_mainTextureSizeX, m_mainTextureSizeY);
//m_textureManager = std::make_unique<TextureManager>(m_textureSearchPaths);
}
void Renderer::SetTextureSearchPaths(std::vector<std::string>& textureSearchPaths)

View File

@ -1,6 +1,6 @@
#include "ShaderEngine.hpp"
#include "StaticGlShaders.h"
#include "StaticGlShaders.hpp"
#include "Texture.hpp"
#include <glm/gtc/type_ptr.hpp>

View File

@ -1,4 +1,4 @@
#include "StaticGlShaders.h"
#include "StaticGlShaders.hpp"
#include "projectM-opengl.h"

View File

@ -15,10 +15,8 @@
#define GL_TEXTURE_IMAGE_FORMAT 0x828F
#endif
#define NUM_BLUR_TEX 6
TextureManager::TextureManager(std::vector<std::string>& textureSearchPaths, int texSizeX, int texSizeY)
TextureManager::TextureManager(std::vector<std::string>& textureSearchPaths)
: m_textureDirectories(textureSearchPaths)
{
FileScanner fileScanner = FileScanner(m_textureDirectories, m_extensions);
@ -29,41 +27,6 @@ TextureManager::TextureManager(std::vector<std::string>& textureSearchPaths, int
Preload();
// Create main texture and associated samplers
m_mainTexture = new Texture("main", texSizeX, texSizeY, false);
m_mainTexture->getSampler(GL_REPEAT, GL_LINEAR);
m_mainTexture->getSampler(GL_REPEAT, GL_NEAREST);
m_mainTexture->getSampler(GL_CLAMP_TO_EDGE, GL_LINEAR);
m_mainTexture->getSampler(GL_CLAMP_TO_EDGE, GL_NEAREST);
m_textures["main"] = m_mainTexture;
// Initialize blur textures
int w = texSizeX;
int h = texSizeY;
for (int i=0; i<NUM_BLUR_TEX; i++)
{
// main VS = 1024
// blur0 = 512
// blur1 = 256 <- user sees this as "blur1"
// blur2 = 128
// blur3 = 128 <- user sees this as "blur2"
// blur4 = 64
// blur5 = 64 <- user sees this as "blur3"
if (!(i&1) || (i<2))
{
w = std::max(16, w / 2);
h = std::max(16, h / 2);
}
int w2 = ((w+3)/16)*16;
int h2 = ((h+3)/4)*4;
std::string texname = "blur" + std::to_string(i/2+1) + ((i%2) ? "" : "doNOTuseME");
Texture * textureBlur = new Texture(texname, w2, h2, false);
textureBlur->getSampler(GL_CLAMP_TO_EDGE, GL_LINEAR);
m_textures[texname] = textureBlur;
m_blurTextures.push_back(textureBlur);
}
auto noise = std::make_unique<MilkdropNoise>();
#if !USE_GLES
@ -388,19 +351,3 @@ void TextureManager::ExtractTextureSettings(const std::string qualifiedName, GLi
name = qualifiedName;
}
}
const Texture * TextureManager::getMainTexture() const {
return m_mainTexture;
}
const std::vector<Texture*> & TextureManager::getBlurTextures() const {
return m_blurTextures;
}
void TextureManager::updateMainTexture()
{
glBindTexture(GL_TEXTURE_2D, m_mainTexture->texID);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_mainTexture->width, m_mainTexture->height);
glBindTexture(GL_TEXTURE_2D, 0);
}

View File

@ -15,17 +15,12 @@ public:
/**
* Constructor.
* @param textureSearchPaths List of paths to search for textures. These paths are search in the given order.
* @param texSizeX Width of the main texture. Does not influence the size of loaded textures!
* @param texSizeY Height of the main texture. Does not influence the size of loaded textures!
*/
TextureManager(std::vector<std::string>& textureSearchPaths, int texSizeX, int texSizeY);
TextureManager(std::vector<std::string>& textureSearchPaths);
~TextureManager();
TextureSamplerDesc tryLoadingTexture(const std::string name);
TextureSamplerDesc getTexture(const std::string fullName, const GLenum defaultWrap, const GLenum defaultFilter);
const Texture* getMainTexture() const;
const std::vector<Texture*>& getBlurTextures() const;
void updateMainTexture();
TextureSamplerDesc getRandomTextureName(std::string rand_name);
void clearRandomTextures();
@ -38,8 +33,6 @@ private:
std::vector<std::string>& m_textureDirectories;
std::map<std::string, Texture*> m_textures;
std::vector<Texture*> m_blurTextures;
Texture* m_mainTexture;
std::vector<std::string> m_randomTextures;
std::vector<std::string> m_extensions{".jpg", ".jpeg", ".dds", ".png", ".tga", ".bmp", ".dib"};
};