feat: Add reflect clock sample and enhance framebuffer handling
- Introduced a new Lua script `reflect_clock.lua` that displays a clock with a reflection effect. - Updated `rdoc.cap` to use the new sample and modified the executable path for debugging. - Enhanced `FrameBuffer` class to support dynamic depth-stencil formats, allowing for more flexible rendering options. - Added a new enum `DepthStencilFormat` to manage different depth-stencil configurations. - Updated OpenGL clear function to conditionally clear depth and stencil buffers based on their enabled state. - Improved the `Effect` class to ensure proper texture updates for GStreamer textures. - Adjusted the `GraphicsContext` to set the clip space to match NanoVG's coordinate system. - Cleaned up texture loading functions by removing unnecessary comments and ensuring clarity.
This commit is contained in:
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -7,7 +7,7 @@
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/debug/live-wallpaper",
|
||||
"args": [
|
||||
"${workspaceFolder}/samples/test.lua"
|
||||
"${workspaceFolder}/samples/reflect_clock.lua"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
@@ -26,7 +26,7 @@
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/release/live-wallpaper",
|
||||
"args": [
|
||||
"${workspaceFolder}/samples/test.lua"
|
||||
"${workspaceFolder}/samples/reflect_clock.lua"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [],
|
||||
|
||||
1675
external/glad/include/glad/gl.h
vendored
1675
external/glad/include/glad/gl.h
vendored
File diff suppressed because it is too large
Load Diff
821
external/glad/src/gl.c
vendored
821
external/glad/src/gl.c
vendored
File diff suppressed because it is too large
Load Diff
4
rdoc.cap
4
rdoc.cap
@@ -2,10 +2,10 @@
|
||||
"rdocCaptureSettings": 1,
|
||||
"settings": {
|
||||
"autoStart": false,
|
||||
"commandLine": "/media/diego/Data/Projects/live-wallpaper/samples/test.lua",
|
||||
"commandLine": "/media/diego/Data/Projects/live-wallpaper/samples/reflect_clock.lua",
|
||||
"environment": [
|
||||
],
|
||||
"executable": "/home/diego/Projects/live-wallpaper/build/live-wallpaper",
|
||||
"executable": "/home/diego/Projects/live-wallpaper/build/debug/live-wallpaper",
|
||||
"inject": false,
|
||||
"numQueuedFrames": 1,
|
||||
"options": {
|
||||
|
||||
126
samples/reflect_clock.lua
Normal file
126
samples/reflect_clock.lua
Normal file
@@ -0,0 +1,126 @@
|
||||
effect = nil
|
||||
|
||||
ctxFBO = nil
|
||||
ctx = nil
|
||||
|
||||
clockTexture = nil
|
||||
|
||||
clockText = ""
|
||||
timer = 0.0
|
||||
|
||||
function _create()
|
||||
local display0 = Displays[1]
|
||||
ctxFBO = FrameBuffer.new(math.floor(display0.z), math.floor(display0.w))
|
||||
|
||||
local att = ColorAttachment.new()
|
||||
att.format = TextureFormatType.RGBA8
|
||||
att.minFilter = TextureFilter.LinearMipmapLinear
|
||||
att.magFilter = TextureFilter.Linear
|
||||
att.wrapS = TextureWrap.ClampToEdge
|
||||
att.wrapT = TextureWrap.ClampToEdge
|
||||
|
||||
ctxFBO:SetDepthStencilFormat(DepthStencilFormat.Depth24Stencil8)
|
||||
ctxFBO:AddColorAttachment(att)
|
||||
|
||||
clockTexture = ctxFBO:GetColorTexture(0)
|
||||
|
||||
ctx = GraphicsContext.new()
|
||||
ctx:CreateFont("font", Resolve("font.otf"))
|
||||
|
||||
local fxSrc = [[uniform sampler2D uTexture;
|
||||
|
||||
const float floorPosition = 0.55;
|
||||
|
||||
float waves(in vec2 uv) {
|
||||
float speed = 4.0;
|
||||
|
||||
float topright = sin(uTime*(speed+1.0) - sin(length(uv-vec2(1.0,1.0)))*53.0);
|
||||
float topleft = sin(uTime*(speed+1.0) - sin(length(uv-vec2(0.0,1.0)))*37.0);
|
||||
float bottomright = sin(uTime*(speed) - sin(length(uv-vec2(1.0,0.0)))*61.0);
|
||||
float bottomleft = sin(uTime*(speed+2.0) - sin(length(uv-vec2(0.0,0.0)))*47.0);
|
||||
|
||||
float horizontalWaves = sin(uTime * (speed + 2.0) - sin(uv.y) * 47.0);
|
||||
|
||||
float temp = horizontalWaves + bottomleft * 0.4 + bottomright * 0.2 + topleft * 0.6 + topright * 0.3;
|
||||
|
||||
float b=smoothstep(-2.5,5.0,temp);
|
||||
return b*3.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec2 uv = vUV;
|
||||
|
||||
for (int i = 0; i < uNumDisplays; i++) {
|
||||
vec4 displayNorm = uDisplayNorm[i];
|
||||
|
||||
if (vUV.x >= displayNorm.x && vUV.x <= displayNorm.x + displayNorm.z &&
|
||||
vUV.y >= displayNorm.y && vUV.y <= displayNorm.y + displayNorm.w) {
|
||||
|
||||
vec2 duv = GetDisplayUV(uTexture, i);
|
||||
|
||||
// Base
|
||||
FragColor = texture(uTexture, duv);
|
||||
|
||||
// Reflection
|
||||
if (duv.y > floorPosition) {
|
||||
float reflectY = floorPosition - (duv.y - floorPosition);
|
||||
vec2 reflectUV = vec2(duv.x, reflectY);
|
||||
|
||||
// deform the reflection based on the height function
|
||||
float wv = waves(vec2(reflectUV.x * 2.0, reflectUV.y * 3.6 - 0.4));
|
||||
float dx = dFdx(wv);
|
||||
float dy = dFdy(wv);
|
||||
reflectUV += vec2(dx, dy) * 0.25; // Adjust deformation
|
||||
|
||||
float lod = clamp(reflectUV.y * 3.0, 0.0, 3.0); // Adjust LOD based on distance
|
||||
vec4 reflectColor = textureLod(uTexture, reflectUV, lod);
|
||||
|
||||
// Apply a simple fade based on distance from the floor
|
||||
float fade = 1.0 - smoothstep(floorPosition, floorPosition + 0.1, duv.y);
|
||||
FragColor += reflectColor * fade * 0.5; // Adjust reflection intensity
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
]]
|
||||
effect = Effect.new(fxSrc)
|
||||
clockText = os.date("%H:%M")
|
||||
end
|
||||
|
||||
function _update(dt)
|
||||
timer = timer + dt
|
||||
|
||||
if timer >= 0.5 then
|
||||
timer = 0.0
|
||||
clockText = os.date("%H:%M")
|
||||
end
|
||||
end
|
||||
|
||||
function _render()
|
||||
local display0 = Displays[1]
|
||||
|
||||
ctxFBO:Bind()
|
||||
gl.Clear(0, 0, 0, 0)
|
||||
ctx:BeginFrame(display0.z, display0.w, display0.z / display0.w)
|
||||
|
||||
ctx:FontSize(144)
|
||||
ctx:FontFace("font")
|
||||
ctx:FillColor(RGBAf(1, 1, 1, 0.75))
|
||||
ctx:TextAlign(Align.CENTER | Align.MIDDLE)
|
||||
ctx:Text(display0.z / 2, display0.w / 2, clockText)
|
||||
|
||||
ctx:EndFrame()
|
||||
ctxFBO:Unbind()
|
||||
|
||||
clockTexture:Bind()
|
||||
clockTexture:GenerateMipmaps()
|
||||
|
||||
gl.SetViewport(0, 0, math.floor(DesktopSize.x), math.floor(DesktopSize.y))
|
||||
|
||||
gl.Clear(0, 0, 0, 1.0)
|
||||
effect:Use()
|
||||
effect:SetTexture("uTexture", clockTexture, 0)
|
||||
effect:Render()
|
||||
end
|
||||
@@ -198,10 +198,9 @@ void Effect::SetTexture(const std::string &name, const Texture& texture, size_t
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + static_cast<GLuint>(textureUnit));
|
||||
texture.Bind();
|
||||
|
||||
|
||||
GStreamerTexture* gstTexture = dynamic_cast<GStreamerTexture*>(const_cast<Texture*>(&texture));
|
||||
if (gstTexture) {
|
||||
// For GStreamerTexture, we need to call Update() to upload the latest frame data
|
||||
gstTexture->Update();
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ FrameBuffer::FrameBuffer(FrameBuffer&& other) noexcept
|
||||
: m_width(other.m_width), m_height(other.m_height),
|
||||
m_attachments(std::move(other.m_attachments)),
|
||||
m_colorTextures(std::move(other.m_colorTextures)),
|
||||
m_depthRenderbuffer(other.m_depthRenderbuffer)
|
||||
m_depthRenderbuffer(other.m_depthRenderbuffer),
|
||||
m_depthStencilFormat(other.m_depthStencilFormat)
|
||||
{
|
||||
m_id = other.m_id;
|
||||
other.m_id = 0;
|
||||
@@ -42,6 +43,7 @@ FrameBuffer& FrameBuffer::operator=(FrameBuffer&& other) noexcept
|
||||
m_attachments = std::move(other.m_attachments);
|
||||
m_colorTextures = std::move(other.m_colorTextures);
|
||||
m_depthRenderbuffer = other.m_depthRenderbuffer;
|
||||
m_depthStencilFormat = other.m_depthStencilFormat;
|
||||
|
||||
other.m_id = 0;
|
||||
other.m_depthRenderbuffer = 0;
|
||||
@@ -86,6 +88,23 @@ void FrameBuffer::AddColorAttachment(const ColorAttachment& attachment)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void FrameBuffer::SetDepthStencilFormat(DepthStencilFormat format)
|
||||
{
|
||||
if (m_depthStencilFormat == format) return;
|
||||
m_depthStencilFormat = format;
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, m_id);
|
||||
DestroyDepthRenderbuffer();
|
||||
CreateDepthRenderbuffer();
|
||||
|
||||
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
throw std::runtime_error("Framebuffer is not complete");
|
||||
}
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void FrameBuffer::Resize(uint32_t width, uint32_t height)
|
||||
{
|
||||
if (m_width == width && m_height == height) return;
|
||||
@@ -141,10 +160,30 @@ std::unique_ptr<Texture2D> FrameBuffer::CreateColorTexture(const ColorAttachment
|
||||
|
||||
void FrameBuffer::CreateDepthRenderbuffer()
|
||||
{
|
||||
if (m_depthStencilFormat == DepthStencilFormat::None) return;
|
||||
|
||||
GLenum internalFormat;
|
||||
GLenum attachmentPoint;
|
||||
switch (m_depthStencilFormat) {
|
||||
case DepthStencilFormat::Depth24:
|
||||
internalFormat = GL_DEPTH_COMPONENT24;
|
||||
attachmentPoint = GL_DEPTH_ATTACHMENT;
|
||||
break;
|
||||
case DepthStencilFormat::Depth24Stencil8:
|
||||
internalFormat = GL_DEPTH24_STENCIL8;
|
||||
attachmentPoint = GL_DEPTH_STENCIL_ATTACHMENT;
|
||||
break;
|
||||
case DepthStencilFormat::Stencil8:
|
||||
internalFormat = GL_STENCIL_INDEX8;
|
||||
attachmentPoint = GL_STENCIL_ATTACHMENT;
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
glGenRenderbuffers(1, &m_depthRenderbuffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, m_width, m_height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, m_width, m_height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachmentPoint,
|
||||
GL_RENDERBUFFER, m_depthRenderbuffer);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
#include "gpu.hpp"
|
||||
#include "texture.h"
|
||||
|
||||
enum class DepthStencilFormat {
|
||||
None,
|
||||
Depth24,
|
||||
Depth24Stencil8,
|
||||
Stencil8,
|
||||
};
|
||||
|
||||
struct ColorAttachment {
|
||||
TextureFormatType format = TextureFormatType::RGBA8;
|
||||
TextureFilter minFilter = TextureFilter::Linear;
|
||||
@@ -29,6 +36,7 @@ public:
|
||||
void Unbind() const override;
|
||||
|
||||
void AddColorAttachment(const ColorAttachment& attachment = {});
|
||||
void SetDepthStencilFormat(DepthStencilFormat format);
|
||||
void Resize(uint32_t width, uint32_t height);
|
||||
|
||||
const Texture2D& GetColorTexture(uint32_t index) const;
|
||||
@@ -43,6 +51,7 @@ private:
|
||||
std::vector<ColorAttachment> m_attachments;
|
||||
std::vector<std::unique_ptr<Texture2D>> m_colorTextures;
|
||||
GLuint m_depthRenderbuffer = 0;
|
||||
DepthStencilFormat m_depthStencilFormat = DepthStencilFormat::Depth24Stencil8;
|
||||
|
||||
void Rebuild();
|
||||
std::unique_ptr<Texture2D> CreateColorTexture(const ColorAttachment& attachment) const;
|
||||
|
||||
11
src/lua.cpp
11
src/lua.cpp
@@ -338,6 +338,14 @@ static void RegisterTexture(sol::state& lua) {
|
||||
}
|
||||
|
||||
static void RegisterFrameBuffer(sol::state& lua) {
|
||||
// ── DepthStencilFormat ──────────────────────────────────────────────
|
||||
lua.new_enum<DepthStencilFormat>("DepthStencilFormat", {
|
||||
{"None", DepthStencilFormat::None},
|
||||
{"Depth24", DepthStencilFormat::Depth24},
|
||||
{"Depth24Stencil8", DepthStencilFormat::Depth24Stencil8},
|
||||
{"Stencil8", DepthStencilFormat::Stencil8}
|
||||
});
|
||||
|
||||
// ── ColorAttachment ─────────────────────────────────────────────────
|
||||
auto ca = lua.new_usertype<ColorAttachment>("ColorAttachment",
|
||||
sol::constructors<ColorAttachment()>(),
|
||||
@@ -354,6 +362,7 @@ static void RegisterFrameBuffer(sol::state& lua) {
|
||||
"Bind", &FrameBuffer::Bind,
|
||||
"Unbind", &FrameBuffer::Unbind,
|
||||
"AddColorAttachment", &FrameBuffer::AddColorAttachment,
|
||||
"SetDepthStencilFormat", &FrameBuffer::SetDepthStencilFormat,
|
||||
"Resize", &FrameBuffer::Resize,
|
||||
"GetColorTexture", &FrameBuffer::GetColorTexture,
|
||||
"GetColorAttachmentCount", &FrameBuffer::GetColorAttachmentCount,
|
||||
@@ -391,6 +400,8 @@ static void RegisterOpenGL(sol::state& lua) {
|
||||
gl["DisableBlending"] = &OpenGL::DisableBlending;
|
||||
gl["SetBlendFunc"] = &OpenGL::SetBlendFunc;
|
||||
|
||||
gl["SetViewport"] = &OpenGL::SetViewport;
|
||||
|
||||
// Blend factors
|
||||
gl["ZERO"] = GL_ZERO;
|
||||
gl["ONE"] = GL_ONE;
|
||||
|
||||
@@ -18,6 +18,7 @@ GraphicsContext::~GraphicsContext()
|
||||
void GraphicsContext::BeginFrame(float width, float height, float devicePixelRatio)
|
||||
{
|
||||
nvgBeginFrame(ctx, width, height, devicePixelRatio);
|
||||
glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE); // Set clip space to match NanoVG's coordinate system
|
||||
}
|
||||
|
||||
void GraphicsContext::CancelFrame() { nvgCancelFrame(ctx); }
|
||||
@@ -25,10 +26,7 @@ void GraphicsContext::CancelFrame() { nvgCancelFrame(ctx); }
|
||||
void GraphicsContext::EndFrame()
|
||||
{
|
||||
nvgEndFrame(ctx);
|
||||
glBindVertexArray(0);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glUseProgram(0);
|
||||
glClipControl(GL_LOWER_LEFT, GL_NEGATIVE_ONE_TO_ONE); // Restore default OpenGL clip space
|
||||
}
|
||||
|
||||
// Composite operations
|
||||
|
||||
@@ -5,7 +5,11 @@ namespace OpenGL {
|
||||
// ── Clear ───────────────────────────────────────────────────────────────
|
||||
void Clear(float r, float g, float b, float a) {
|
||||
glClearColor(r, g, b, a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLenum flags = GL_COLOR_BUFFER_BIT;
|
||||
if (glIsEnabled(GL_DEPTH_TEST)) flags |= GL_DEPTH_BUFFER_BIT;
|
||||
if (glIsEnabled(GL_STENCIL_TEST)) flags |= GL_STENCIL_BUFFER_BIT;
|
||||
glClear(flags);
|
||||
}
|
||||
|
||||
void Clear(const Vector3& color) {
|
||||
@@ -29,4 +33,9 @@ void SetBlendFunc(GLenum src, GLenum dst) {
|
||||
glBlendFunc(src, dst);
|
||||
}
|
||||
|
||||
void SetViewport(int x, int y, int width, int height)
|
||||
{
|
||||
glViewport(x, y, width, height);
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -15,4 +15,6 @@ void EnableBlending();
|
||||
void DisableBlending();
|
||||
void SetBlendFunc(GLenum src, GLenum dst);
|
||||
|
||||
void SetViewport(int x, int y, int width, int height);
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -227,7 +227,6 @@ void Texture1D::LoadData(TextureFormatType format, const void *data)
|
||||
{
|
||||
const TextureFormat& fmt = textureFormatMap[static_cast<int>(format)];
|
||||
if (!m_hasInitialData) {
|
||||
// Initialize texture storage with null data to allocate GPU memory
|
||||
glTexImage1D(
|
||||
m_target,
|
||||
0,
|
||||
@@ -238,7 +237,6 @@ void Texture1D::LoadData(TextureFormatType format, const void *data)
|
||||
);
|
||||
m_hasInitialData = true;
|
||||
} else {
|
||||
// Update existing texture data
|
||||
glTexSubImage1D(
|
||||
m_target,
|
||||
0,
|
||||
@@ -253,7 +251,6 @@ void Texture2D::LoadData(TextureFormatType format, const void *data)
|
||||
{
|
||||
const TextureFormat& fmt = textureFormatMap[static_cast<int>(format)];
|
||||
if (!m_hasInitialData) {
|
||||
// Initialize texture storage and optionally upload data
|
||||
glTexImage2D(
|
||||
m_target,
|
||||
0,
|
||||
@@ -264,7 +261,6 @@ void Texture2D::LoadData(TextureFormatType format, const void *data)
|
||||
);
|
||||
m_hasInitialData = true;
|
||||
} else {
|
||||
// Update existing texture data
|
||||
glTexSubImage2D(
|
||||
m_target,
|
||||
0,
|
||||
@@ -279,7 +275,6 @@ void Texture3D::LoadData(TextureFormatType format, const void *data)
|
||||
{
|
||||
const TextureFormat& fmt = textureFormatMap[static_cast<int>(format)];
|
||||
if (!m_hasInitialData) {
|
||||
// Initialize texture storage with null data to allocate GPU memory
|
||||
glTexImage3D(
|
||||
m_target,
|
||||
0,
|
||||
@@ -290,7 +285,6 @@ void Texture3D::LoadData(TextureFormatType format, const void *data)
|
||||
);
|
||||
m_hasInitialData = true;
|
||||
} else {
|
||||
// Update existing texture data
|
||||
glTexSubImage3D(
|
||||
m_target,
|
||||
0,
|
||||
|
||||
Reference in New Issue
Block a user