Skip to content
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
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL Emscripten)
endif()
message(STATUS " Use system GLM: ${ENABLE_SYSTEM_GLM}")
message(STATUS " Use system projectM-eval: ${ENABLE_SYSTEM_PROJECTM_EVAL}")
if(ENABLE_SYSTEM_PROJECTM_EVAL)
message(STATUS " projectM-eval version: ${projectM-Eval_VERSION}")
endif()
message(STATUS " Link UI with shared lib: ${ENABLE_SHARED_LINKING}")
message(STATUS "")
message(STATUS "Targets and applications:")
Expand Down
15 changes: 15 additions & 0 deletions src/api/include/projectM-4/render_opengl.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@ PROJECTM_EXPORT void projectm_opengl_render_frame(projectm_handle instance);
*/
PROJECTM_EXPORT void projectm_opengl_render_frame_fbo(projectm_handle instance, uint32_t framebuffer_object_id);

/**
* @brief Burn-in the provided texture into the active preset(s) main texture.
*
* During transitions, the image is drawn onto both active presets.
*
* @param instance The projectM instance handle.
* @param texture The OpenGL texture ID to draw onto the current preset.
* @param left The left offset in screen coordinates.
* @param top The top offset in screen coordinates.
* @param width The width in screen coordinates. Negative values will flip the image horizontally.
* @param height The height in screen coordinates. Negative values will flip the image vertically.
* @since 4.2.0
*/
PROJECTM_EXPORT void projectm_opengl_burn_texture(projectm_handle instance, uint32_t texture, int left, int top, int width, int height);

#ifdef __cplusplus
} // extern "C"
#endif
8 changes: 4 additions & 4 deletions src/libprojectM/MilkdropPreset/MilkdropPreset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
}

// y-flip the previous frame and assign the flipped texture as "main"
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), nullptr, true, false);
m_flipTexture.Draw(*renderContext.shaderCache, m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), nullptr, true, false);
m_state.mainTexture = m_flipTexture.Texture();

// We now draw to the current framebuffer.
Expand Down Expand Up @@ -146,7 +146,7 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
m_border.Draw(m_perFrameContext);

// y-flip the image for final compositing again
m_flipTexture.Draw(m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0), nullptr, true, false);
m_flipTexture.Draw(*renderContext.shaderCache, m_framebuffer.GetColorAttachmentTexture(m_currentFrameBuffer, 0), nullptr, true, false);
m_state.mainTexture = m_flipTexture.Texture();

// We no longer need the previous frame image, use it to render the final composite.
Expand All @@ -158,7 +158,7 @@ void MilkdropPreset::RenderFrame(const libprojectM::Audio::FrameAudioData& audio
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, true, false);
m_flipTexture.Draw(*renderContext.shaderCache, m_framebuffer.GetColorAttachmentTexture(m_previousFrameBuffer, 0), m_framebuffer, m_previousFrameBuffer, true, false);
}

// Swap framebuffer IDs for the next frame.
Expand All @@ -178,7 +178,7 @@ void MilkdropPreset::DrawInitialImage(const std::shared_ptr<Renderer::Texture>&
m_framebuffer.SetSize(renderContext.viewportSizeX, renderContext.viewportSizeY);

// Render to previous framebuffer, as this is the image used to draw the next frame on.
m_flipTexture.Draw(image, m_framebuffer, m_previousFrameBuffer);
m_flipTexture.Draw(*renderContext.shaderCache, image, m_framebuffer, m_previousFrameBuffer);
}

void MilkdropPreset::BindFramebuffer()
Expand Down
23 changes: 20 additions & 3 deletions src/libprojectM/ProjectM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@

#include <Renderer/CopyTexture.hpp>
#include <Renderer/PresetTransition.hpp>
#include <Renderer/TextureManager.hpp>
#include <Renderer/ShaderCache.hpp>
#include <Renderer/TextureManager.hpp>
#include <Renderer/TransitionShaderManager.hpp>

#include <UserSprites/SpriteManager.hpp>
Expand Down Expand Up @@ -177,11 +177,11 @@ void ProjectM::RenderFrame(uint32_t targetFramebufferObject /*= 0*/)
}
else
{
m_textureCopier->Draw(m_activePreset->OutputTexture(), false, false);
m_textureCopier->Draw(*renderContext.shaderCache, m_activePreset->OutputTexture(), false, false);
}

// Draw user sprites
m_spriteManager->Draw(audioData, renderContext, targetFramebufferObject, { m_activePreset, m_transitioningPreset });
m_spriteManager->Draw(audioData, renderContext, targetFramebufferObject, {m_activePreset, m_transitioningPreset});

m_frameCount++;
m_previousFrameVolume = audioData.vol;
Expand Down Expand Up @@ -312,6 +312,23 @@ auto ProjectM::UserSpriteIdentifiers() const -> std::vector<uint32_t>
return m_spriteManager->ActiveSpriteIdentifiers();
}

void ProjectM::BurnInTexture(uint32_t openGlTextureId, int left, int top, int width, int height)
{
if (m_activePreset)
{
m_activePreset->BindFramebuffer();
m_textureCopier->Draw(*m_shaderCache, openGlTextureId, m_windowWidth, m_windowHeight, left, top, width, height);
}

if (m_transitioningPreset)
{
m_transitioningPreset->BindFramebuffer();
m_textureCopier->Draw(*m_shaderCache, openGlTextureId, m_windowWidth, m_windowHeight, left, top, width, height);
}

Renderer::Framebuffer::Unbind();
}

void ProjectM::SetPresetLocked(bool locked)
{
// ToDo: Add a preset switch timer separate from the display timer and reset to 0 when
Expand Down
10 changes: 10 additions & 0 deletions src/libprojectM/ProjectM.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,16 @@ class PROJECTM_EXPORT ProjectM
*/
auto UserSpriteIdentifiers() const -> std::vector<uint32_t>;

/**
* @brief Draws the given texture on the active preset's main texture to get a "burn-in" effect.
* @param openGlTextureId The OpenGL texture to draw onto the active preset(s).
* @param left Left coordinate in pixels on the destination texture.
* @param top Top coordinate in pixels on the destination texture.
* @param width Width of the final image on the destination texture in pixels, can be negative to flip it horizontally.
* @param height Height of the final image on the destination texture in pixels, can be negative to flip it vertically.
*/
void BurnInTexture(uint32_t openGlTextureId, int left, int top, int width, int height);

private:
void Initialize();

Expand Down
6 changes: 6 additions & 0 deletions src/libprojectM/ProjectMCWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ void projectm_opengl_render_frame_fbo(projectm_handle instance, uint32_t framebu
projectMInstance->RenderFrame(framebuffer_object_id);
}

void projectm_opengl_burn_texture(projectm_handle instance, uint32_t texture, int left, int top, int width, int height)
{
auto projectMInstance = handle_to_instance(instance);
projectMInstance->BurnInTexture(texture, left, top, width, height);
}

void projectm_set_frame_time(projectm_handle instance, double seconds_since_first_frame)
{
auto projectMInstance = handle_to_instance(instance);
Expand Down
172 changes: 145 additions & 27 deletions src/libprojectM/Renderer/CopyTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,11 @@ layout(location = 2) in vec2 tex_coord;

out vec2 fragment_tex_coord;

uniform ivec2 flip;
uniform mat4 vertex_transformation;

void main() {
gl_Position = vec4(position, 0.0, 1.0);
gl_Position = vec4(position, 0.0, 1.0) * vertex_transformation;
fragment_tex_coord = tex_coord;
if (flip.x > 0)
{
fragment_tex_coord.s = 1.0 - fragment_tex_coord.s;
}
if (flip.y > 0)
{
fragment_tex_coord.t = 1.0 - fragment_tex_coord.t;
}
}
)";

Expand All @@ -53,13 +45,6 @@ CopyTexture::CopyTexture()
{
m_framebuffer.CreateColorAttachment(0, 0);

std::string vertexShader(ShaderVersion);
std::string fragmentShader(ShaderVersion);
vertexShader.append(CopyTextureVertexShader);
fragmentShader.append(CopyTextureFragmentShader);

m_shader.CompileProgram(vertexShader, fragmentShader);

m_mesh.SetRenderPrimitiveType(Mesh::PrimitiveType::TriangleStrip);

m_mesh.SetVertexCount(4);
Expand All @@ -78,7 +63,9 @@ CopyTexture::CopyTexture()
m_mesh.Update();
}

void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, bool flipVertical, bool flipHorizontal)
void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
bool flipVertical, bool flipHorizontal)
{
if (originalTexture == nullptr)
{
Expand All @@ -87,10 +74,12 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, bo

// Just bind the texture and draw it to the currently bound buffer.
originalTexture->Bind(0);
Copy(flipVertical, flipHorizontal);
Copy(shaderCache, flipVertical, flipHorizontal);
}

void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, const std::shared_ptr<class Texture>& targetTexture,
void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
const std::shared_ptr<class Texture>& targetTexture,
bool flipVertical, bool flipHorizontal)
{
if (originalTexture == nullptr ||
Expand Down Expand Up @@ -128,7 +117,7 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, co
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(targetTexture);
}

Copy(flipVertical, flipHorizontal);
Copy(shaderCache, flipVertical, flipHorizontal);

// Rebind our internal texture.
if (targetTexture)
Expand All @@ -139,7 +128,9 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, co
Framebuffer::Unbind();
}

void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, Framebuffer& framebuffer, int framebufferIndex,
void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<class Texture>& originalTexture,
Framebuffer& framebuffer, int framebufferIndex,
bool flipVertical, bool flipHorizontal)
{
if (originalTexture == nullptr //
Expand All @@ -162,7 +153,7 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, Fr
// Draw from unflipped texture
originalTexture->Bind(0);

Copy(flipVertical, flipHorizontal);
Copy(shaderCache, flipVertical, flipHorizontal);

// Swap texture attachments
auto tempAttachment = framebuffer.GetAttachment(framebufferIndex, TextureAttachment::AttachmentType::Color, 0);
Expand All @@ -174,6 +165,74 @@ void CopyTexture::Draw(const std::shared_ptr<class Texture>& originalTexture, Fr
Framebuffer::Unbind();
}

void CopyTexture::Draw(ShaderCache& shaderCache,
const std::shared_ptr<struct Texture>& originalTexture,
const std::shared_ptr<struct Texture>& targetTexture,
int left, int top, int width, int height)
{
if (originalTexture == nullptr ||
originalTexture->Empty() ||
targetTexture == nullptr ||
targetTexture->Empty() ||
originalTexture == targetTexture)
{
return;
}

UpdateTextureSize(targetTexture->Width(), targetTexture->Height());

if (m_width == 0 || m_height == 0)
{
return;
}

std::shared_ptr<class Texture> internalTexture;

m_framebuffer.Bind(0);

// Draw from original texture
originalTexture->Bind(0);
internalTexture = m_framebuffer.GetColorAttachmentTexture(0, 0);
m_framebuffer.GetAttachment(0, TextureAttachment::AttachmentType::Color, 0)->Texture(targetTexture);

Copy(shaderCache, left, top, width, height);

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

Framebuffer::Unbind();
}

void CopyTexture::Draw(ShaderCache& shaderCache,
GLuint originalTexture,
int viewportWidth, int viewportHeight,
int left, int top, int width, int height)
{
if (originalTexture == 0)
{
return;
}

if (viewportWidth == 0 || viewportHeight == 0)
{
return;
}

int oldWidth = m_width;
int oldHeight = m_height;

m_width = viewportWidth;
m_height = viewportHeight;

// Draw from original texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, originalTexture);
Copy(shaderCache, left, top, width, height);

m_width = oldWidth;
m_height = oldHeight;
}

auto CopyTexture::Texture() -> std::shared_ptr<class Texture>
{
return m_framebuffer.GetColorAttachmentTexture(0, 0);
Expand All @@ -193,11 +252,18 @@ void CopyTexture::UpdateTextureSize(int width, int height)
m_framebuffer.SetSize(m_width, m_height);
}

void CopyTexture::Copy(bool flipVertical, bool flipHorizontal)
void CopyTexture::Copy(ShaderCache& shaderCache,
bool flipVertical, bool flipHorizontal)
{
m_shader.Bind();
m_shader.SetUniformInt("texture_sampler", 0);
m_shader.SetUniformInt2("flip", {flipHorizontal ? 1 : 0, flipVertical ? 1 : 0});
glm::mat4x4 flipMatrix(1.0);

flipMatrix[0][0] = flipHorizontal ? -1.0 : 1.0;
flipMatrix[1][1] = flipVertical ? -1.0 : 1.0;

std::shared_ptr<Shader> shader = BindShader(shaderCache);

shader->SetUniformInt("texture_sampler", 0);
shader->SetUniformMat4x4("vertex_transformation", flipMatrix);

m_sampler.Bind(0);

Expand All @@ -209,5 +275,57 @@ void CopyTexture::Copy(bool flipVertical, bool flipHorizontal)
Shader::Unbind();
}

void CopyTexture::Copy(ShaderCache& shaderCache,
int left, int top, int width, int height)
{
glm::mat4x4 translationMatrix(1.0);
translationMatrix[0][0] = static_cast<float>(width) / static_cast<float>(m_width);
translationMatrix[1][1] = static_cast<float>(height) / static_cast<float>(m_height);

translationMatrix[3][0] = static_cast<float>(left) / static_cast<float>(m_width);
translationMatrix[3][1] = static_cast<float>(top) / static_cast<float>(m_height);

std::shared_ptr<Shader> shader = BindShader(shaderCache);

shader->SetUniformInt("texture_sampler", 0);
shader->SetUniformMat4x4("vertex_transformation", translationMatrix);

m_sampler.Bind(0);

m_mesh.Draw();

Mesh::Unbind();
Sampler::Unbind(0);
Shader::Unbind();
}

std::shared_ptr<Shader> CopyTexture::BindShader(ShaderCache& shaderCache)
{
auto shader = m_shader.lock();

if (!shader)
{
shader = shaderCache.Get("copy_texture");
}

if (!shader)
{
std::string vertexShader(ShaderVersion);
std::string fragmentShader(ShaderVersion);
vertexShader.append(CopyTextureVertexShader);
fragmentShader.append(CopyTextureFragmentShader);

shader = std::make_shared<Shader>();
shader->CompileProgram(vertexShader, fragmentShader);

m_shader = shader;
shaderCache.Insert("copy_texture", shader);
}

shader->Bind();

return shader;
}

} // namespace Renderer
} // namespace libprojectM
Loading
Loading