diff --git a/oscope-of/src/ofApp.cpp b/oscope-of/src/ofApp.cpp index bc1ac87..25b04df 100644 --- a/oscope-of/src/ofApp.cpp +++ b/oscope-of/src/ofApp.cpp @@ -135,6 +135,24 @@ void ofApp::setup() { oceanLoader_ = std::make_unique("shaders/ocean_loader"); win95_ = std::make_unique("shaders/win95"); sphereWave_ = std::make_unique(); + auto img = [](const char* fn){ + return std::make_unique( + ofToDataPath(std::string("sprites/") + fn, true)); + }; + imgWin1_ = img("windows_1_0.png"); + imgWin311_ = img("windows_311.png"); + imgWin95_ = img("windows_95.png"); + imgLotus_ = img("lotus_123.png"); + imgDos_ = img("msdos.png"); + imgAtari_ = img("atari_fuji.png"); + imgApple_ = img("apple_rainbow.png"); + imgWB_ = img("amiga_wb.png"); + imgNeXT_ = img("nextstep.png"); + imgBeOS_ = img("beos.png"); + imgOS2_ = img("os2_warp.png"); + imgIrix_ = img("irix.png"); + imgZX_ = img("zx_spectrum.png"); + imgC64_ = img("c64_basic.png"); lissajous_->setup(W, H); spectro_->setup(W, H / 4); reactive_->setup(W, H); @@ -196,6 +214,12 @@ void ofApp::setup() { oceanLoader_->setup(W, H); win95_->setup(W, H); sphereWave_->setup(W, H); + imgWin1_->setup(W, H); imgWin311_->setup(W, H); imgWin95_->setup(W, H); + imgLotus_->setup(W, H); imgDos_->setup(W, H); + imgAtari_->setup(W, H); imgApple_->setup(W, H); + imgWB_->setup(W, H); imgNeXT_->setup(W, H); imgBeOS_->setup(W, H); + imgOS2_->setup(W, H); imgIrix_->setup(W, H); + imgZX_->setup(W, H); imgC64_->setup(W, H); postfx_.setup(W, H); @@ -416,6 +440,12 @@ void ofApp::update() { oceanLoader_->update(frame); win95_->update(frame); sphereWave_->update(frame); + imgWin1_->update(frame); imgWin311_->update(frame); imgWin95_->update(frame); + imgLotus_->update(frame); imgDos_->update(frame); + imgAtari_->update(frame); imgApple_->update(frame); + imgWB_->update(frame); imgNeXT_->update(frame); imgBeOS_->update(frame); + imgOS2_->update(frame); imgIrix_->update(frame); + imgZX_->update(frame); imgC64_->update(frame); applyOscFx(); @@ -590,15 +620,19 @@ void ofApp::initDemos() { " PYRAMID OF MEMORY GIZA OF VECTORS " " A 4-SIDED MONUMENT FROM 1986 ", "B", BgKind::Pyramid}, + {"WORKBENCH", 22.0f, polSpe(), SS::Cascade, + " WORKBENCH 1.3 KICKSTART READY " + " DH0: DISK CONTAINS THE DEMO ", "M", + BgKind::ImgWB}, }}; // ─── 2 · C64 LOWLIFE ───────────────────────────────────── demos_[1] = {"C64 LOWLIFE", { - {"BOOT", 15.0f, stars(), SS::Glitch, + {"BOOT", 18.0f, stars(), SS::Glitch, " **** COMMODORE 64 BASIC V2 **** " " 64K RAM SYSTEM 38911 BASIC BYTES FREE " " READY. LOAD \"DEMO\",8,1 SEARCHING FOR DEMO ", "N", - BgKind::Starfield}, + BgKind::ImgC64}, {"PLASMA", 25.0f, all(), SS::Wavy3D, " *** PLASMA EFFECT *** SID CHIP 6581 SCREAMING " " 8 COULEURS INDEXEES PALETTE FIXE " @@ -763,9 +797,16 @@ void ofApp::initDemos() { // ─── 9 · MEMORY LANE ───────────────────────────────────── demos_[8] = {"MEMORY LANE", { - {"REWIND", 20.0f, starsWave(), SS::Mirror, + {"REWIND", 18.0f, starsWave(), SS::Mirror, " *** AESTHETIC MODE *** SLOWING DOWN " " LET THE TAPE WHIRR ", "U", BgKind::Rotozoom}, + {"NEXTSTEP", 18.0f, polSpe(), SS::Chrome, + " NEXTSTEP / OPENSTEP 1989 PURE OBJC ELEGANCE " + " GREETINGS TO STEVE JOBS AND BERTRAND SERLET ", "U", + BgKind::ImgNeXT}, + {"BEOS", 18.0f, polSpe(), SS::Cascade, + " BEOS R5 1995 MULTITASK BEAUTIFUL " + " JEAN LOUIS GASSEE WAS RIGHT ", "R", BgKind::ImgBeOS}, {"STATUE", 30.0f, polSpe(), SS::Chrome, " AN ANGEL FROM THE STANFORD ARCHIVE " " LUCY 50K POINTS MARBLE FROZEN IN TIME ", "R", @@ -805,10 +846,29 @@ void ofApp::initDemos() { " TWISTER BARS SPACEBALLS STYLE ", "G", BgKind::Twister}, {"COPPER", 15.0f, all(), SS::Rainbow, " COPPER LIST POETRY ", "G", BgKind::Twister}, - {"FRACTAL", 18.0f, all(), SS::Wavy3D, + {"FRACTAL", 15.0f, all(), SS::Wavy3D, " FRACTAL ITERATIONS ", "L", BgKind::Kifs}, - {"MOBIUS", 18.0f, all(), SS::Mirror, + {"MOBIUS", 15.0f, all(), SS::Mirror, " IMPOSSIBLE TOPOLOGY ", "L", BgKind::Mobius}, + {"WIN 1.0", 12.0f, polSpe(), SS::Bouncy, + " 1985 WINDOWS 1.0 ", "M", BgKind::ImgWin1}, + {"LOTUS 123", 12.0f, polSpe(), SS::Cascade, + " LOTUS 1-2-3 SPREADSHEET KING 1983 ", "M", + BgKind::ImgLotus}, + {"ATARI", 12.0f, polSpe(), SS::Rainbow, + " ATARI 1972 GAME OVER OR NEW HIGH SCORE ", "M", + BgKind::ImgAtari}, + {"APPLE 6", 12.0f, polSpe(), SS::Mirror, + " APPLE RAINBOW LOGO 1977 ", "M", BgKind::ImgApple}, + {"OS/2 WARP", 12.0f, polSpe(), SS::Cascade, + " OS/2 WARP IBM 1996 " + " THE OS THAT COULD HAVE BEEN ", "M", BgKind::ImgOS2}, + {"IRIX SGI", 12.0f, polSpe(), SS::Chrome, + " SILICON GRAPHICS IRIX JURASSIC PARK MAGIC ", + "M", BgKind::ImgIrix}, + {"ZX SPECTRUM",12.0f, polSpe(), SS::Rainbow, + " SINCLAIR 1982 UK COMPUTING REVOLUTION ", "M", + BgKind::ImgZX}, {"ROLL CALL", 35.0f, all(), SS::Rainbow, " GREETINGS TO :: KXKM CREW :: HYPNEUM LAB :: SUPERCOLLIDER ::" " OPENFRAMEWORKS HACKERS :: COOKIE COLLECTIVE :: ALL LIVE CODERS " @@ -863,6 +923,10 @@ void ofApp::initDemos() { {"WILDFIRE", 25.0f, all(), SS::Glitch, " FLAMES RISE FAST EVERY KICK FUELS THE BLAZE " " GREETINGS TO HARDCODE AND TITAN ", "T", BgKind::Vortex}, + {"DOS PROMPT", 20.0f, polSpe(), SS::Glitch, + " C:\\DEMOS\\>RUN INFERNO.EXE " + " 640K OUGHT TO BE ENOUGH FOR ANYBODY ", "E", + BgKind::ImgDos}, {"FORGE", 25.0f, polSpe(), SS::Cascade, " THE METAL MELTS GEAR TURNS IN MOLTEN STEEL " " HEPHAESTUS WORKS THE ANVIL ", "P", BgKind::Gear}, @@ -1232,6 +1296,20 @@ void ofApp::drawScope4(int W, int H) { case BgKind::OceanLoader: oceanLoader_->draw(0, 0, W, H); break; case BgKind::Win95: win95_->draw(0, 0, W, H); break; case BgKind::SphereWave: sphereWave_->draw(0, 0, W, H); break; + case BgKind::ImgWin1: imgWin1_->draw(0, 0, W, H); break; + case BgKind::ImgWin311: imgWin311_->draw(0, 0, W, H); break; + case BgKind::ImgWin95: imgWin95_->draw(0, 0, W, H); break; + case BgKind::ImgLotus: imgLotus_->draw(0, 0, W, H); break; + case BgKind::ImgDos: imgDos_->draw(0, 0, W, H); break; + case BgKind::ImgAtari: imgAtari_->draw(0, 0, W, H); break; + case BgKind::ImgApple: imgApple_->draw(0, 0, W, H); break; + case BgKind::ImgWB: imgWB_->draw(0, 0, W, H); break; + case BgKind::ImgNeXT: imgNeXT_->draw(0, 0, W, H); break; + case BgKind::ImgBeOS: imgBeOS_->draw(0, 0, W, H); break; + case BgKind::ImgOS2: imgOS2_->draw(0, 0, W, H); break; + case BgKind::ImgIrix: imgIrix_->draw(0, 0, W, H); break; + case BgKind::ImgZX: imgZX_->draw(0, 0, W, H); break; + case BgKind::ImgC64: imgC64_->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 84355c6..aacc352 100644 --- a/oscope-of/src/ofApp.h +++ b/oscope-of/src/ofApp.h @@ -28,6 +28,7 @@ #include "visualizers/VectorCubesVis.h" #include "visualizers/ModelVis.h" #include "visualizers/SphereWaveVis.h" +#include "visualizers/ImageVis.h" #include @@ -104,7 +105,10 @@ private: BoingBall, Mode7, PlasmaC64, DotTunnel, Gear, Cone, Pyramid, Rose3d, Geosphere, DNA, Workbench, MacOSClassic, AtariFuji, OceanLoader, Win95, - SphereWave }; + SphereWave, + ImgWin1, ImgWin311, ImgWin95, ImgLotus, ImgDos, + ImgAtari, ImgApple, ImgWB, ImgNeXT, ImgBeOS, + ImgOS2, ImgIrix, ImgZX, ImgC64 }; struct DemoScene { const char* name; float durSec; @@ -219,6 +223,10 @@ private: std::unique_ptr oceanLoader_; std::unique_ptr win95_; std::unique_ptr sphereWave_; + std::unique_ptr imgWin1_, imgWin311_, imgWin95_; + std::unique_ptr imgLotus_, imgDos_, imgAtari_, imgApple_; + std::unique_ptr imgWB_, imgNeXT_, imgBeOS_, imgOS2_; + std::unique_ptr imgIrix_, imgZX_, imgC64_; std::vector ch1_, ch2_; Mode mode_ = Mode::Scope4; diff --git a/oscope-of/src/visualizers/ImageVis.h b/oscope-of/src/visualizers/ImageVis.h new file mode 100644 index 0000000..af2271e --- /dev/null +++ b/oscope-of/src/visualizers/ImageVis.h @@ -0,0 +1,165 @@ +#pragma once + +// ImageVis : charge un PNG et l'affiche en background animé. +// Animations : zoom pulsé sur kick, slow drift, hue cycle, scanlines CRT, +// flicker pour effet rétro. + +#include "Visualizer.h" +#include "ofMain.h" + +#include + +namespace oscope { + +class ImageVis : public Visualizer { +public: + explicit ImageVis(const std::string& spritePath) + : spritePath_(spritePath) {} + + void setup(int w, int h) override { + w_ = w; h_ = h; + img_.load(spritePath_); + if (img_.isAllocated()) { + img_.setImageType(OF_IMAGE_COLOR_ALPHA); + } + } + + void update(const VisFrame& frame) override { + t_ += static_cast(ofGetLastFrameTime()); + bass_ = frame.bands.bass; + mid_ = frame.bands.mid; + treble_ = frame.bands.treble; + kick_ = frame.bands.kick; + } + + void draw(int x, int y, int w, int h) override { + if (!img_.isAllocated()) { + ofPushStyle(); + ofSetColor(40); + ofDrawRectangle(x, y, w, h); + ofSetColor(255, 80, 80); + ofDrawBitmapString("sprite missing : " + spritePath_, x+12, y+24); + ofPopStyle(); + return; + } + ofPushStyle(); + // Fond noir + ofSetColor(0); + ofDrawRectangle(x, y, w, h); + + // Calcule taille image affichée — fit dans la moitié de l'écran + const float aspect = static_cast(img_.getWidth()) / + std::max(1.0f, static_cast(img_.getHeight())); + float drawH = static_cast(h) * 0.65f; + float drawW = drawH * aspect; + if (drawW > w * 0.85f) { + drawW = w * 0.85f; + drawH = drawW / aspect; + } + // Pulse sur kick + lente respiration + const float pulse = 1.0f + kick_ * 0.15f + + std::sin(t_ * 0.7f) * 0.04f; + drawW *= pulse; + drawH *= pulse; + + // Drift lent X/Y + const float dx = std::sin(t_ * 0.3f) * (w * 0.04f); + const float dy = std::cos(t_ * 0.4f) * (h * 0.03f); + const float cx = x + w * 0.5f + dx; + const float cy = y + h * 0.5f + dy; + + // Tint hue cycle léger sur les blancs/clairs (multiply mode visible) + ofColor tint; + tint.setHsb(static_cast(std::fmod(t_ * 12.0f, 255.0f)), + 50, + 240); + // Flicker pseudo-aléatoire occasionnel (vintage CRT) + if (std::fmod(t_, 3.0f) < 0.05f) tint.a = 220; + else tint.a = 255; + + // === Effets demoscene sur l'image === + const float ix = cx - drawW * 0.5f; + const float iy = cy - drawH * 0.5f; + + // 1) Réflexion bas (mirror) — flip Y avec alpha gradient + ofPushMatrix(); + ofTranslate(cx, iy + drawH * 1.95f); + ofScale(1.0f, -0.7f, 1.0f); + ofSetColor(255, 255, 255, 100); + img_.draw(-drawW * 0.5f, 0, drawW, drawH); + ofPopMatrix(); + + // 2) Ghost trail derrière — 3 copies offsets + for (int g = 3; g >= 1; --g) { + const float ox = std::cos(t_ * 0.6f + g) * 12.0f * g; + const float oy = std::sin(t_ * 0.5f + g) * 8.0f * g; + ofSetColor(180, 200, 255, 50 / g); + img_.draw(ix + ox, iy + oy, drawW, drawH); + } + + // 3) Chromatic aberration RGB — 3 passes avec offset radial + const float chroma = 4.0f + kick_ * 8.0f; + ofEnableBlendMode(OF_BLENDMODE_ADD); + ofSetColor(255, 0, 0, 80); + img_.draw(ix - chroma, iy, drawW, drawH); + ofSetColor(0, 0, 255, 80); + img_.draw(ix + chroma, iy, drawW, drawH); + ofDisableBlendMode(); + + // 4) Image principale tinted + ofSetColor(tint); + img_.draw(ix, iy, drawW, drawH); + + // 5) Twister wobble : image redessinée découpée en 8 bandes + // horizontales avec offset X par sin(y, t). Effet "wave". + if (std::fmod(t_, 6.0f) < 2.5f) { // toggle pour pas saturer + ofSetColor(255, 255, 255, 80); + const int slices = 12; + for (int s = 0; s < slices; ++s) { + const float sy = drawH * s / slices; + const float sh = drawH / slices + 1.0f; + const float ox = std::sin(t_ * 2.0f + s * 0.7f) * + (4.0f + bass_ * 12.0f); + // Subset region : on dessine la bande cropée + img_.drawSubsection(ix + ox, iy + sy, drawW, sh, + 0, img_.getHeight() * s / slices, + img_.getWidth(), + img_.getHeight() / slices + 1); + } + } + + // 6) Scanlines CRT + ofSetColor(0, 0, 0, 40); + for (int sy = y; sy < y + h; sy += 3) { + ofDrawRectangle(x, sy, w, 1); + } + + // 7) Glitch tear horizontal — bandes qui glissent aléatoirement + if (kick_ > 0.5f) { + ofSetColor(255, 100, 200, + static_cast(60 * std::min(1.0f, kick_ * 2.0f))); + const int gh = 3; + for (int gy = static_cast(iy); gy < iy + drawH; gy += 18) { + const float gx = ofRandom(-30, 30); + ofDrawRectangle(ix + gx, gy, drawW, gh); + } + } + + // 8) Boost luminance sur kick + if (kick_ > 0.4f) { + ofSetColor(255, 220, 180, + static_cast(80 * std::min(1.0f, kick_ * 2.0f))); + ofDrawRectangle(x, y, w, h); + } + ofPopStyle(); + } + +private: + std::string spritePath_; + int w_ = 0, h_ = 0; + ofImage img_; + float t_ = 0.0f; + float bass_ = 0, mid_ = 0, treble_ = 0, kick_ = 0; +}; + +} // namespace oscope