diff --git a/oscope-of/bin/data/shaders/atari_fuji.frag b/oscope-of/bin/data/shaders/atari_fuji.frag new file mode 100644 index 0000000..ce54bd1 --- /dev/null +++ b/oscope-of/bin/data/shaders/atari_fuji.frag @@ -0,0 +1,51 @@ +#version 150 + +// Atari Fuji homage : 3 barres verticales convergentes au sommet, +// fond rouge magenta vintage. Pixelisation 4x4. Logo abstrait +// (pas la marque exacte) qui évoque le Fuji 1972. + +out vec4 fragColor; + +uniform vec2 uRes; +uniform float uTime; +uniform float uMid; +uniform float uKick; + +void main() { + // Pixelize + vec2 p = floor(gl_FragCoord.xy / 4.0) * 4.0; + vec2 c = (p / uRes - 0.5) * 2.0; + c.x *= uRes.x / uRes.y; + + // Fond gradient rouge → noir + float grad = clamp(0.5 - c.y * 0.4, 0.0, 1.0); + vec3 col = mix(vec3(0.10, 0.0, 0.05), vec3(0.95, 0.10, 0.20), grad); + + // 3 barres convergentes — modélisation : à y=-1 elles sont à x=-0.6/0/+0.6, + // à y=+1 elles sont à x=0 (toutes). Largeur des barres aussi diminue. + float yBase = (c.y + 1.0) * 0.5; // 0..1 + float top = 1.0 - yBase; + // 3 positions originales + float positions[3] = float[3](-0.5, 0.0, 0.5); + // Convergence : position(y) = posOrig * (1 - top * 1.0) + float w = 0.10 + top * 0.06; // largeur (légèrement plus large en haut) + bool inBar = false; + for (int i = 0; i < 3; i++) { + float bx = positions[i] * (1.0 - top * 0.6); // converge un peu + if (abs(c.x - bx) < w * 0.5 && c.y < 0.7 && c.y > -0.85) inBar = true; + } + // Barre transversale haute (le "chapeau") + if (c.y > 0.55 && c.y < 0.70 && abs(c.x) < 0.7) inBar = true; + + if (inBar) { + col = vec3(0.95, 0.85, 0.20) * (1.0 + uKick * 0.3); + } + + // Texte "ATARI" en bas (procédural minimal — barres horizontales) + if (c.y > -0.95 && c.y < -0.85 && abs(c.x) < 0.5) { + bool stripe = mod(floor(p.x / 8.0), 2.0) < 0.5; + if (stripe) col = vec3(0.95, 0.85, 0.20); + } + + fragColor = vec4(col, 1.0); +} diff --git a/oscope-of/bin/data/shaders/atari_fuji.vert b/oscope-of/bin/data/shaders/atari_fuji.vert new file mode 100644 index 0000000..d75e307 --- /dev/null +++ b/oscope-of/bin/data/shaders/atari_fuji.vert @@ -0,0 +1,4 @@ +#version 150 +uniform mat4 modelViewProjectionMatrix; +in vec4 position; +void main() { gl_Position = modelViewProjectionMatrix * position; } diff --git a/oscope-of/bin/data/shaders/macos_classic.frag b/oscope-of/bin/data/shaders/macos_classic.frag new file mode 100644 index 0000000..03a2b68 --- /dev/null +++ b/oscope-of/bin/data/shaders/macos_classic.frag @@ -0,0 +1,69 @@ +#version 150 + +// Classic Mac OS homage : fond gris pixelisé, fenêtre Finder centrale, +// "happy mac" face stylisé. Pixel art procédural pas de logo authentique. + +out vec4 fragColor; + +uniform vec2 uRes; +uniform float uTime; +uniform float uMid; +uniform float uKick; + +float box(vec2 p, vec2 mn, vec2 mx) { + return (p.x >= mn.x && p.x <= mx.x && p.y >= mn.y && p.y <= mx.y) + ? 1.0 : 0.0; +} + +void main() { + vec2 p = floor(gl_FragCoord.xy / 3.0) * 3.0; // pixelize 3x3 + + // Fond pattern hatch gris (Mac classic desktop) + bool hatch = mod(floor(p.x/3.0) + floor(p.y/3.0), 2.0) < 0.5; + vec3 col = hatch ? vec3(0.7, 0.7, 0.7) : vec3(0.55, 0.55, 0.55); + + // Menu bar haut (20 px) + if (gl_FragCoord.y > uRes.y - 20.0) { + col = vec3(0.95, 0.95, 0.95); + if (gl_FragCoord.y > uRes.y - 21.0) col = vec3(0.0); // séparateur + } + + // Fenêtre centrale 320x200 + vec2 wMin = uRes * 0.5 - vec2(160.0, 100.0); + vec2 wMax = wMin + vec2(320.0, 200.0); + if (box(gl_FragCoord.xy, wMin, wMax) > 0.5) { + col = vec3(0.95, 0.95, 0.95); + // Bordure noire + if (gl_FragCoord.x < wMin.x + 1.0 || gl_FragCoord.x > wMax.x - 1.0 || + gl_FragCoord.y < wMin.y + 1.0 || gl_FragCoord.y > wMax.y - 1.0) { + col = vec3(0.0); + } + // Title bar style Mac avec stripes horizontales + if (gl_FragCoord.y > wMax.y - 18.0 && gl_FragCoord.y < wMax.y - 2.0) { + bool s = mod(floor(gl_FragCoord.y), 2.0) < 0.5; + col = s ? vec3(0.85) : vec3(1.0); + if (gl_FragCoord.y > wMax.y - 4.0) col = vec3(0.0); + } + // Happy mac face (carré + 2 yeux + bouche) + vec2 face = (gl_FragCoord.xy - wMin - vec2(140.0, 60.0)); + if (face.x > 0.0 && face.x < 40.0 && face.y > 0.0 && face.y < 40.0) { + // Cadre face + col = vec3(0.0); + // Intérieur visage + if (face.x > 4.0 && face.x < 36.0 && face.y > 4.0 && face.y < 36.0) { + col = vec3(0.95, 0.95, 0.95) * (1.0 + uKick * 0.3); + // Yeux + if ((face.x > 10.0 && face.x < 14.0 && face.y > 22.0 && face.y < 26.0) || + (face.x > 26.0 && face.x < 30.0 && face.y > 22.0 && face.y < 26.0)) { + col = vec3(0.0); + } + // Bouche (sourire) + if (face.y > 12.0 && face.y < 16.0 && + face.x > 12.0 && face.x < 28.0) { + col = vec3(0.0); + } + } + } + } + fragColor = vec4(col, 1.0); +} diff --git a/oscope-of/bin/data/shaders/macos_classic.vert b/oscope-of/bin/data/shaders/macos_classic.vert new file mode 100644 index 0000000..d75e307 --- /dev/null +++ b/oscope-of/bin/data/shaders/macos_classic.vert @@ -0,0 +1,4 @@ +#version 150 +uniform mat4 modelViewProjectionMatrix; +in vec4 position; +void main() { gl_Position = modelViewProjectionMatrix * position; } diff --git a/oscope-of/bin/data/shaders/ocean_loader.frag b/oscope-of/bin/data/shaders/ocean_loader.frag new file mode 100644 index 0000000..04170a6 --- /dev/null +++ b/oscope-of/bin/data/shaders/ocean_loader.frag @@ -0,0 +1,45 @@ +#version 150 + +// Ocean Software Loader homage : bandes horizontales rainbow +// scrollantes typiques des chargements C64/Spectrum 1986-1990. +// Greetings Martin Galway, Jonathan Dunn. + +out vec4 fragColor; + +uniform vec2 uRes; +uniform float uTime; +uniform float uMid; +uniform float uKick; +uniform float uBpm; + +vec3 paletteOcean(float t) { + // Rainbow strict avec césures dures (pas de smooth) + int idx = int(floor(t * 8.0)) % 8; + if (idx == 0) return vec3(1.0, 0.0, 0.0); // rouge + if (idx == 1) return vec3(1.0, 0.5, 0.0); // orange + if (idx == 2) return vec3(1.0, 1.0, 0.0); // jaune + if (idx == 3) return vec3(0.0, 1.0, 0.0); // vert + if (idx == 4) return vec3(0.0, 1.0, 1.0); // cyan + if (idx == 5) return vec3(0.0, 0.5, 1.0); // bleu + if (idx == 6) return vec3(0.5, 0.0, 1.0); // violet + return vec3(1.0, 0.0, 1.0); // magenta +} + +void main() { + vec2 uv = gl_FragCoord.xy / uRes; + // Bandes horizontales scrollantes vers le bas + float speed = 0.5 + uBpm * 0.003; + float t = uv.y * 12.0 + uTime * speed; + vec3 col = paletteOcean(t); + + // Subtle scanlines CRT + float sl = mod(gl_FragCoord.y, 2.0); + if (sl < 1.0) col *= 0.85; + + // Border noire haut/bas (style Ocean loader avec bandes) + if (uv.y < 0.05 || uv.y > 0.95) col = vec3(0.0); + + // Pulse audio sur kick + col *= 1.0 + uKick * 0.4; + fragColor = vec4(col, 1.0); +} diff --git a/oscope-of/bin/data/shaders/ocean_loader.vert b/oscope-of/bin/data/shaders/ocean_loader.vert new file mode 100644 index 0000000..d75e307 --- /dev/null +++ b/oscope-of/bin/data/shaders/ocean_loader.vert @@ -0,0 +1,4 @@ +#version 150 +uniform mat4 modelViewProjectionMatrix; +in vec4 position; +void main() { gl_Position = modelViewProjectionMatrix * position; } diff --git a/oscope-of/bin/data/shaders/win95.frag b/oscope-of/bin/data/shaders/win95.frag new file mode 100644 index 0000000..4505307 --- /dev/null +++ b/oscope-of/bin/data/shaders/win95.frag @@ -0,0 +1,58 @@ +#version 150 + +// Windows 95 startup homage : ciel bleu nuageux, fenêtre 95 stylisée. +// Pixelisé. Greetings tous les hackers de l'ère pre-XP. + +out vec4 fragColor; + +uniform vec2 uRes; +uniform float uTime; +uniform float uMid; +uniform float uTreble; +uniform float uKick; + +float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } +float noise(vec2 p) { + vec2 i = floor(p), f = fract(p); + float a = hash(i), b = hash(i + vec2(1, 0)); + float c = hash(i + vec2(0, 1)), d = hash(i + vec2(1, 1)); + vec2 u = f * f * (3.0 - 2.0 * f); + return mix(mix(a, b, u.x), mix(c, d, u.x), u.y); +} +float fbm(vec2 p) { + float v = 0.0, amp = 0.5; + for (int i = 0; i < 4; i++) { v += amp * noise(p); p *= 2.0; amp *= 0.5; } + return v; +} + +void main() { + vec2 px = floor(gl_FragCoord.xy / 2.0) * 2.0; // pixelize 2x2 + vec2 uv = px / uRes; + + // Ciel bleu Win95 + nuages FBM + float clouds = fbm(uv * 2.5 + vec2(uTime * 0.1, 0.0)); + vec3 sky = mix(vec3(0.05, 0.20, 0.55), vec3(1.0, 1.0, 1.0), clouds); + vec3 col = sky; + + // Bandeau haut "Microsoft Windows 95" stylisé + // 4 carrés colorés (rouge/vert/bleu/jaune) ondulants comme le logo + vec2 logoC = uRes * vec2(0.5, 0.55); + vec2 d = px - logoC; + if (abs(d.x) < 90.0 && abs(d.y) < 90.0) { + // 4 quadrants avec couleurs Win + bool right = d.x > 0.0; + bool top = d.y > 0.0; + vec3 quad; + if (right && top) quad = vec3(0.95, 0.20, 0.20); // rouge + else if (!right && top) quad = vec3(0.20, 0.85, 0.20); // vert + else if (right && !top) quad = vec3(0.20, 0.40, 0.95); // bleu + else quad = vec3(0.95, 0.85, 0.10); // jaune + // Wave : déplace selon l'angle pour effet "drapeau" + float wave = sin(d.x * 0.04 + uTime * 1.0) * 8.0; + if (abs(d.y - wave) < 80.0) col = quad * (1.0 + uKick * 0.3); + } + + // Pulse uMid → flicker du logo + if (uMid > 0.5) col *= 1.1; + fragColor = vec4(col, 1.0); +} diff --git a/oscope-of/bin/data/shaders/win95.vert b/oscope-of/bin/data/shaders/win95.vert new file mode 100644 index 0000000..d75e307 --- /dev/null +++ b/oscope-of/bin/data/shaders/win95.vert @@ -0,0 +1,4 @@ +#version 150 +uniform mat4 modelViewProjectionMatrix; +in vec4 position; +void main() { gl_Position = modelViewProjectionMatrix * position; } diff --git a/oscope-of/bin/data/shaders/workbench.frag b/oscope-of/bin/data/shaders/workbench.frag new file mode 100644 index 0000000..df63880 --- /dev/null +++ b/oscope-of/bin/data/shaders/workbench.frag @@ -0,0 +1,65 @@ +#version 150 + +// Amiga Workbench 1.x homage : fond pattern damier bleu/blanc, +// barres de titre en haut, fenêtres pixelées. Pas le vrai logo. + +out vec4 fragColor; + +uniform vec2 uRes; +uniform float uTime; +uniform float uBass; +uniform float uMid; +uniform float uKick; + +void main() { + vec2 p = gl_FragCoord.xy; + vec2 uv = p / uRes; + // Pixelisation : on snap à des blocs 4x4 + vec2 px = floor(p / 4.0) * 4.0; + + // Background damier 1px noir/orange (Workbench classique) + vec2 chk = floor(px / 4.0); + bool dark = mod(chk.x + chk.y, 2.0) < 0.5; + vec3 col = dark ? vec3(0.0, 0.06, 0.40) : vec3(0.85, 0.45, 0.0); + + // Barre de titre haut (16 px high) + if (p.y > uRes.y - 18.0) { + // Pattern raz raz de tirets (style Amiga) + bool stripe = mod(floor(p.x / 4.0), 2.0) < 0.5; + col = stripe ? vec3(0.0, 0.0, 0.30) : vec3(0.85, 0.85, 0.85); + } + if (p.y > uRes.y - 22.0 && p.y < uRes.y - 18.0) { + col = vec3(0.85, 0.85, 0.85); // séparateur + } + + // Une "fenêtre" qui se déplace + float wt = uTime * 0.25; + vec2 wPos = vec2(uRes.x * 0.3 + sin(wt) * 80.0, + uRes.y * 0.4 + cos(wt * 0.8) * 60.0); + vec2 wSize = vec2(220.0, 140.0); + if (p.x > wPos.x && p.x < wPos.x + wSize.x && + p.y > wPos.y && p.y < wPos.y + wSize.y) { + // Bordure + if (p.x < wPos.x + 4.0 || p.x > wPos.x + wSize.x - 4.0 || + p.y < wPos.y + 4.0 || p.y > wPos.y + wSize.y - 4.0) { + col = vec3(0.85, 0.85, 0.85); + } else if (p.y > wPos.y + wSize.y - 18.0) { + // Title bar + bool stripe = mod(floor(p.x / 4.0), 2.0) < 0.5; + col = stripe ? vec3(0.0, 0.0, 0.30) : vec3(0.85, 0.85, 0.85); + } else { + col = vec3(0.85, 0.85, 0.85); + // Icône simple (carré coloré pulsant audio) + vec2 icon = (p - wPos - vec2(20.0, 20.0)); + if (icon.x > 0.0 && icon.x < 32.0 && icon.y > 0.0 && icon.y < 32.0) { + col = vec3(0.85, 0.0, 0.0) * (1.0 + uKick * 0.6); + } + } + } + + // Effet "bouge" sur kick + if (uKick > 0.5) { + col *= vec3(1.0 + uKick * 0.4); + } + fragColor = vec4(col, 1.0); +} diff --git a/oscope-of/bin/data/shaders/workbench.vert b/oscope-of/bin/data/shaders/workbench.vert new file mode 100644 index 0000000..d75e307 --- /dev/null +++ b/oscope-of/bin/data/shaders/workbench.vert @@ -0,0 +1,4 @@ +#version 150 +uniform mat4 modelViewProjectionMatrix; +in vec4 position; +void main() { gl_Position = modelViewProjectionMatrix * position; } diff --git a/oscope-of/src/ofApp.cpp b/oscope-of/src/ofApp.cpp index 3980cc7..f7317bc 100644 --- a/oscope-of/src/ofApp.cpp +++ b/oscope-of/src/ofApp.cpp @@ -129,6 +129,11 @@ void ofApp::setup() { modelRose_ = mp("rose3d.ply"); modelGeosphere_ = mp("geosphere.ply"); modelDNA_ = mp("dna.ply"); + workbench_ = std::make_unique("shaders/workbench"); + macosClassic_ = std::make_unique("shaders/macos_classic"); + atariFuji_ = std::make_unique("shaders/atari_fuji"); + oceanLoader_ = std::make_unique("shaders/ocean_loader"); + win95_ = std::make_unique("shaders/win95"); lissajous_->setup(W, H); spectro_->setup(W, H / 4); reactive_->setup(W, H); @@ -184,6 +189,11 @@ void ofApp::setup() { modelRose_->setup(W, H); modelGeosphere_->setup(W, H); modelDNA_->setup(W, H); + workbench_->setup(W, H); + macosClassic_->setup(W, H); + atariFuji_->setup(W, H); + oceanLoader_->setup(W, H); + win95_->setup(W, H); postfx_.setup(W, H); @@ -398,6 +408,11 @@ void ofApp::update() { modelRose_->update(frame); modelGeosphere_->update(frame); modelDNA_->update(frame); + workbench_->update(frame); + macosClassic_->update(frame); + atariFuji_->update(frame); + oceanLoader_->update(frame); + win95_->update(frame); applyOscFx(); @@ -1208,6 +1223,11 @@ void ofApp::drawScope4(int W, int H) { case BgKind::Rose3d: modelRose_->draw(0, 0, W, H); break; case BgKind::Geosphere: modelGeosphere_->draw(0, 0, W, H); break; case BgKind::DNA: modelDNA_->draw(0, 0, W, H); break; + case BgKind::Workbench: workbench_->draw(0, 0, W, H); break; + case BgKind::MacOSClassic: macosClassic_->draw(0, 0, W, H); break; + case BgKind::AtariFuji: atariFuji_->draw(0, 0, W, H); break; + case BgKind::OceanLoader: oceanLoader_->draw(0, 0, W, H); break; + case BgKind::Win95: win95_->draw(0, 0, W, H); break; } // 1bis) HUD pseudo-aléatoire de valeurs sub-10 Hz. diff --git a/oscope-of/src/ofApp.h b/oscope-of/src/ofApp.h index fc6caea..08d7697 100644 --- a/oscope-of/src/ofApp.h +++ b/oscope-of/src/ofApp.h @@ -101,7 +101,8 @@ private: Penrose, Sphere3D, IcoMesh, DodMesh, TorusMesh, Supershape, Lorenz, Hopf, Enneper, HopfLink, BoingBall, Mode7, PlasmaC64, DotTunnel, - Gear, Cone, Pyramid, Rose3d, Geosphere, DNA }; + Gear, Cone, Pyramid, Rose3d, Geosphere, DNA, + Workbench, MacOSClassic, AtariFuji, OceanLoader, Win95 }; struct DemoScene { const char* name; float durSec; @@ -210,6 +211,11 @@ private: std::unique_ptr modelRose_; std::unique_ptr modelGeosphere_; std::unique_ptr modelDNA_; + std::unique_ptr workbench_; + std::unique_ptr macosClassic_; + std::unique_ptr atariFuji_; + std::unique_ptr oceanLoader_; + std::unique_ptr win95_; std::vector ch1_, ch2_; Mode mode_ = Mode::Scope4;