feat(of): ImageVis fx + 14 OS logos demos

ImageVis : charge un PNG et l'anime avec effets demoscene :
- Mirror reflection bas (flip Y alpha gradient)
- Ghost trail 3 couches en arrière (RGB shifted)
- Chromatic aberration RGB (rouge gauche / bleu droite, kick boost)
- Twister wobble : 12 bandes horizontales avec offset X sin(t,y)
- Scanlines CRT 3px
- Glitch tear horizontal sur kick > 0.5
- Boost luminance global sur kick

14 nouveaux BgKind (ImgWin1/311/95, ImgLotus, ImgDos, ImgAtari,
ImgApple, ImgWB, ImgNeXT, ImgBeOS, ImgOS2, ImgIrix, ImgZX,
ImgC64) qui chargent leurs PNG respectifs depuis sprites/.

Intégrations dans démos :
- AMIGA : ajout WORKBENCH (ImgWB) avec narration DH0
- C64   : BOOT acte utilise maintenant ImgC64
- INFERNO : ajout DOS PROMPT acte (ImgDos)
- MEMORY LANE : ajout NEXTSTEP + BEOS actes
- GREETINGS : 7 nouveaux actes 12s parade des logos OS
  WIN 1.0 / LOTUS 123 / ATARI / APPLE 6 / OS/2 WARP /
  IRIX SGI / ZX SPECTRUM

GREETINGS demo passe de 11 à 18 actes (parade complète).
This commit is contained in:
L'électron rare
2026-05-07 22:56:11 +02:00
parent 582a839895
commit c0c0d541cf
3 changed files with 257 additions and 6 deletions
+83 -5
View File
@@ -135,6 +135,24 @@ void ofApp::setup() {
oceanLoader_ = std::make_unique<oscope::ShaderVis>("shaders/ocean_loader");
win95_ = std::make_unique<oscope::ShaderVis>("shaders/win95");
sphereWave_ = std::make_unique<oscope::SphereWaveVis>();
auto img = [](const char* fn){
return std::make_unique<oscope::ImageVis>(
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.
+9 -1
View File
@@ -28,6 +28,7 @@
#include "visualizers/VectorCubesVis.h"
#include "visualizers/ModelVis.h"
#include "visualizers/SphereWaveVis.h"
#include "visualizers/ImageVis.h"
#include <memory>
@@ -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<oscope::ShaderVis> oceanLoader_;
std::unique_ptr<oscope::ShaderVis> win95_;
std::unique_ptr<oscope::SphereWaveVis> sphereWave_;
std::unique_ptr<oscope::ImageVis> imgWin1_, imgWin311_, imgWin95_;
std::unique_ptr<oscope::ImageVis> imgLotus_, imgDos_, imgAtari_, imgApple_;
std::unique_ptr<oscope::ImageVis> imgWB_, imgNeXT_, imgBeOS_, imgOS2_;
std::unique_ptr<oscope::ImageVis> imgIrix_, imgZX_, imgC64_;
std::vector<float> ch1_, ch2_;
Mode mode_ = Mode::Scope4;
+165
View File
@@ -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 <string>
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<float>(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<float>(img_.getWidth()) /
std::max(1.0f, static_cast<float>(img_.getHeight()));
float drawH = static_cast<float>(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<int>(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<int>(60 * std::min(1.0f, kick_ * 2.0f)));
const int gh = 3;
for (int gy = static_cast<int>(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<int>(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