Enhance video texture handling and add release build configurations

This commit is contained in:
Diego Lopes
2026-03-21 11:36:31 -04:00
parent 52dc6fc757
commit f60384c5c3
14 changed files with 186 additions and 55 deletions

19
.vscode/launch.json vendored
View File

@@ -19,6 +19,25 @@
"ignoreFailures": true
}
]
},
{
"name": "Release live-wallpaper",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/release/live-wallpaper",
"args": [
"${workspaceFolder}/samples/test.lua"
],
"cwd": "${workspaceFolder}",
"environment": [],
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

22
.vscode/tasks.json vendored
View File

@@ -25,6 +25,28 @@
},
"problemMatcher": "$gcc",
"dependsOn": "CMake: configure debug"
},
{
"label": "CMake: configure release",
"type": "shell",
"command": "cmake",
"args": ["--preset", "release"],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "CMake: build release",
"type": "shell",
"command": "cmake",
"args": ["--build", "--preset", "release"],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": "$gcc",
"dependsOn": "CMake: configure release"
}
]
}

View File

@@ -6,8 +6,6 @@ effect = nil
function _create()
local fxSrc = [[
in vec2 vPosition;
#define time uTime
mat2 mm2(in float a){float c = cos(a), s = sin(a);return mat2(c,s,-s,c);}

View File

@@ -13,8 +13,6 @@ function _create()
rockTexture:GenerateMipmaps()
local fxSrc = [[
in vec2 vPosition;
uniform sampler2D iChannel0;
#define FAR 80.

View File

@@ -1,8 +1,7 @@
effect = nil
function _create()
local fxSrc = [[in vec2 vPosition;
local fxSrc = [[
void main() {
vec2 uv = vPosition * 0.5 + 0.5;
FragColor = vec4(uv, 0.5 + 0.5 * sin(uTime), 1.0);

View File

@@ -5,8 +5,6 @@ effect = nil
function _create()
local fxSrc = [[
in vec2 vPosition;
const bool USE_MOUSE = false;
const float PI = 3.14159265;

View File

@@ -2,16 +2,22 @@ effect = nil
video = nil
function _create()
video = Texture.FromGStreamer("filesrc location=/media/diego/Data/Projects/live-wallpaper/samples/video.mp4")
video = Texture.FromGStreamer("filesrc location=" .. Resolve("sunset.mkv"))
local fxSrc = [[in vec2 vPosition;
uniform sampler2D uTexture;
local fxSrc = [[uniform sampler2D uTexture;
void main() {
vec2 uv = vPosition * 0.5 + 0.5;
uv.y = 1.0 - uv.y;
FragColor = texture(uTexture, uv);
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) {
FragColor = texture(uTexture, GetDisplayUV(uTexture, i));
break;
}
}
}
]]
effect = Effect.new(fxSrc)

Binary file not shown.

View File

@@ -13,6 +13,106 @@
GLuint Effect::g_dummyVAO = 0;
static std::string shaderHeader = R"(#version 460 core
out vec4 FragColor;
in vec2 vUV;
in vec2 vPosition;
uniform float uTime;
uniform vec4 uMouse;
uniform vec2 uDesktopSize;
uniform vec4 uDisplay[16]; // support up to 16 displays, more can be added if needed
uniform vec4 uDisplayNorm[16]; // pre-normalized display rects for convenience
uniform int uNumDisplays;
#define MAX_DISPLAYS 16
// Background modes
#define BG_FIT_WIDTH 0
#define BG_FIT_HEIGHT 1
#define BG_COVER 2
#define BG_STRETCH 3
#define BG_CONTAIN 4
#define BG_TILE 5
vec2 GetDisplayUV(sampler2D tex, int displayIndex, int mode) {
vec2 inUV = vec2(0.0);
if (displayIndex < 0 || displayIndex >= uNumDisplays) {
// Entire desktop as fallback
inUV = vUV;
} else {
// Map global UV to display-local [0,1]
inUV = (vUV - uDisplayNorm[displayIndex].xy) / uDisplayNorm[displayIndex].zw;
}
vec2 uv = inUV;
if (mode == BG_STRETCH) {
// No adjustment needed
return uv;
}
vec4 display = uDisplay[displayIndex];
float displayRatio = display.z / display.w;
vec2 texSize = vec2(textureSize(tex, 0));
float textureRatio = texSize.x / texSize.y;
if (mode == BG_COVER) {
if (displayRatio > textureRatio) {
// Display wider: crop top/bottom
float scale = textureRatio / displayRatio;
uv.y = uv.y * scale + (1.0 - scale) * 0.5;
} else {
// Display taller: crop left/right
float scale = displayRatio / textureRatio;
uv.x = uv.x * scale + (1.0 - scale) * 0.5;
}
} else if (mode == BG_CONTAIN) {
if (displayRatio > textureRatio) {
// Display wider: pillarbox left/right
float scale = displayRatio / textureRatio;
uv.x = uv.x * scale + (1.0 - scale) * 0.5;
} else {
// Display taller: letterbox top/bottom
float scale = textureRatio / displayRatio;
uv.y = uv.y * scale + (1.0 - scale) * 0.5;
}
} else if (mode == BG_FIT_WIDTH) {
// Scale to match display width, adjust Y
float scale = textureRatio / displayRatio;
uv.y = uv.y * scale + (1.0 - scale) * 0.5;
} else if (mode == BG_FIT_HEIGHT) {
// Scale to match display height, adjust X
float scale = displayRatio / textureRatio;
uv.x = uv.x * scale + (1.0 - scale) * 0.5;
} else if (mode == BG_TILE) {
// Tile at native texture resolution
uv = uv * display.zw / texSize;
}
return uv;
}
// Default mode = BG_COVER
vec2 GetDisplayUV(sampler2D tex, int displayIndex) {
return GetDisplayUV(tex, displayIndex, BG_COVER);
}
vec2 GetFullUV(sampler2D tex) {
return GetDisplayUV(tex, -1, BG_COVER);
}
vec2 GetFullUV(sampler2D tex, int mode) {
return GetDisplayUV(tex, -1, mode);
}
#line 1
)";
static std::vector<std::string> SplitLines(const std::string& str) {
std::vector<std::string> lines;
size_t start = 0, end = 0;
@@ -32,27 +132,13 @@ Effect::Effect(const std::string& fragmentShaderSource)
#include "fx_vertex.glsl.h"
;
std::string code =
"#version 460 core\n\n"
"out vec4 FragColor;\n\n"
"uniform float uTime;\n"
"uniform vec4 uMouse;\n"
"uniform vec2 uDesktopSize;\n";
for (size_t i = 0; i < g_Displays.size(); i++) {
code += "uniform vec4 uDisplay" + std::to_string(i) + ";\n";
}
// reset line number
code += "#line 1\n";
GLuint vertexShader = CreateShader(vertexShaderSource, GL_VERTEX_SHADER);
if (!vertexShader) {
throw std::runtime_error("Failed to compile vertex shader");
return;
}
GLuint fragmentShader = CreateShader(code + fragmentShaderSource, GL_FRAGMENT_SHADER);
GLuint fragmentShader = CreateShader(shaderHeader + fragmentShaderSource, GL_FRAGMENT_SHADER);
if (!fragmentShader) {
glDeleteShader(vertexShader);
throw std::runtime_error("Failed to compile fragment shader");
@@ -122,6 +208,11 @@ void Effect::SetTexture(const std::string &name, const Texture& texture, size_t
glUniform1i(GetUniformLocation(name), static_cast<GLint>(textureUnit));
}
void Effect::SetInt(const std::string &name, int value)
{
glUniform1i(GetUniformLocation(name), value);
}
void Effect::SetFloat(const std::string &name, float value)
{
glUniform1f(GetUniformLocation(name), value);
@@ -156,8 +247,10 @@ void Effect::Render()
SetVector4("uMouse", g_Mouse);
SetVector2("uDesktopSize", g_DesktopSize);
for (size_t i = 0; i < g_Displays.size(); i++) {
SetVector4("uDisplay" + std::to_string(i), g_Displays[i]);
SetVector4("uDisplay[" + std::to_string(i) + "]", g_Displays[i]);
SetVector4("uDisplayNorm[" + std::to_string(i) + "]", g_Displays[i] / Vector4(g_DesktopSize, g_DesktopSize));
}
SetInt("uNumDisplays", static_cast<int>(g_Displays.size()));
glBindVertexArray(g_dummyVAO);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

View File

@@ -19,6 +19,7 @@ public:
GLuint GetUniformLocation(const std::string& name);
void SetTexture(const std::string& name, const Texture& texture, size_t textureUnit);
void SetInt(const std::string& name, int value);
void SetFloat(const std::string& name, float value);
void SetVector2(const std::string& name, const Vector2& value);
void SetVector3(const std::string& name, const Vector3& value);

View File

@@ -1,5 +1,6 @@
R"(#version 460 core
out vec2 vPosition;
out vec2 vUV;
vec2 coords[] = vec2[](
vec2(-1.0, -1.0),
@@ -10,5 +11,8 @@ vec2 coords[] = vec2[](
void main() {
vPosition = coords[gl_VertexID];
vUV = vPosition * 0.5 + 0.5;
vUV.y = 1.0 - vUV.y; // flip Y for texture coordinates
gl_Position = vec4(vPosition, 0.0, 1.0);
})"

View File

@@ -328,13 +328,12 @@ static void RegisterTexture(sol::state& lua) {
tex["CreateTexture3D"] = static_cast<std::unique_ptr<Texture3D>(*)(uint32_t, uint32_t, uint32_t, TextureFormatType)>(&Texture::CreateTexture);
tex["CreateCubeMap"] = &Texture::CreateCubeMap;
tex["FromGStreamer"] = &Texture::FromGStreamer;
tex["FromFile"] = [](const std::string& filepath) -> std::unique_ptr<Texture2D> {
std::filesystem::path p(filepath);
if (p.is_relative() && !g_ScriptDir.empty()) {
p = std::filesystem::path(g_ScriptDir) / p;
}
return Texture::FromFile(p.string());
};
tex["FromFile"] = &Texture::FromFile;
// ── Useful functions ───────────────────────────────────────────────
lua.set_function("Resolve", [](const std::string& path) {
return std::filesystem::absolute(std::filesystem::path(g_ScriptDir) / path).string();
});
}
static void RegisterFrameBuffer(sol::state& lua) {

View File

@@ -171,24 +171,17 @@ int main(int argc, char* argv[])
if (event.type == SDL_EVENT_QUIT) {
running = false;
}
}
// Mouse position and button state
switch (event.type) {
case SDL_EVENT_MOUSE_MOTION:
g_Mouse.x = static_cast<float>(event.motion.x);
g_Mouse.y = static_cast<float>(event.motion.y);
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
case SDL_EVENT_MOUSE_BUTTON_UP:
if (event.button.button == SDL_BUTTON_LEFT) {
g_Mouse.z = (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? 1.0f : 0.0f;
} else if (event.button.button == SDL_BUTTON_RIGHT) {
g_Mouse.w = (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? 1.0f : 0.0f;
}
break;
default:
break;
}
// Poll global mouse position (the window is click-through so it
// receives no input events — query the pointer directly instead).
{
float gx, gy;
SDL_MouseButtonFlags buttons = SDL_GetGlobalMouseState(&gx, &gy);
g_Mouse.x = gx;
g_Mouse.y = g_DesktopSize.y - gy;
g_Mouse.z = (buttons & SDL_BUTTON_LMASK) ? 1.0f : 0.0f;
g_Mouse.w = (buttons & SDL_BUTTON_RMASK) ? 1.0f : 0.0f;
}
bool canRender = false;

View File

@@ -271,6 +271,7 @@ public:
constexpr Vector4(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
constexpr Vector4(const Vector2& v, float z = 0.0f, float w = 0.0f) : x(v.x), y(v.y), z(z), w(w) {}
constexpr Vector4(const Vector3& v, float w = 0.0f) : x(v.x), y(v.y), z(v.z), w(w) {}
constexpr Vector4(const Vector2& v1, const Vector2& v2) : x(v1.x), y(v1.y), z(v2.x), w(v2.y) {}
[[nodiscard]] constexpr Vector2 toVector2() const { return Vector2(x, y); }
[[nodiscard]] constexpr Vector3 toVector3() const { return Vector3(x, y, z); }