feat(of): 10 demoparty presets + scroller styles
User : "les touches 0 à 10 lanceront une démo party avec la track supercollider associée + plus de styles de scroller + q reste le mode live". DemoFx::ScrollerStyle (6 variants) : - Classic sine wave wobble par défaut - Wavy3D sine + scale per char (effet respiration 3D) - Rainbow hue cycle large par char - Mirror classic + reflet vertical en bas (alpha gradient) - Glitch jitter horizontal + couleurs cassées (rouge/bleu) - Neon outline cyan large + cœur magenta fin ofApp::DemoPreset (10 presets) : 1 AMIGA classic -> album A (acid_journey) 2 C64 wavy3d -> album M (chiptune_madness) 3 OLDSKOOL mirror -> album G (detroit_soulful) 4 NEWSKOOL rainbow -> album L (future_garage) 5 TUNNEL classic -> album P (industrial) 6 STARFIELD neon -> album J (ambient_cinematic) 7 SCOPE classic -> album I (liquid_dnb) 8 CYBER glitch -> album V (glitch_idm) 9 VAPOR mirror -> album U (vocal_trance) 0 RAVE rainbow -> album T (hardcore_gabber) Chaque preset = état toggles ScopeToggles + style scroller + lettre album SC (envoyée via /control/playAlbum à sclang). Touches 1..9 + 0 lancent les presets (au lieu de toggles individuels). 'q' reste libre pour le mode live (contrôle manuel des éléments via les autres touches a-z). Overlay bas droite affiche le preset actif en gros (magenta).
This commit is contained in:
+71
-14
@@ -58,31 +58,88 @@ void DemoFx::drawScroller(int W, int H) {
|
||||
ofPushStyle();
|
||||
ofEnableBlendMode(OF_BLENDMODE_ADD);
|
||||
|
||||
// 4x scale via push matrix per char ; chaque char a un offset Y sin.
|
||||
const float scale = 3.0f;
|
||||
const float charW = 8.0f * scale; // bitmap 8px wide
|
||||
const float charW = 8.0f * scale;
|
||||
const float baseY = H - 60.0f;
|
||||
const float amp = 14.0f;
|
||||
|
||||
// Wrap : quand tout le texte est sorti à gauche, on remet à droite.
|
||||
const float fullW = charW * static_cast<float>(text_.size());
|
||||
if (scrollX_ < -fullW) scrollX_ = static_cast<float>(W);
|
||||
|
||||
auto drawChar = [&](char ch, float x, float y, float sx, float sy,
|
||||
float rot, ofColor col) {
|
||||
ofSetColor(col);
|
||||
ofPushMatrix();
|
||||
ofTranslate(x, y);
|
||||
if (rot != 0.0f) ofRotateRad(rot);
|
||||
ofScale(sx, sy, 1.0f);
|
||||
const char buf[2] = { ch, 0 };
|
||||
ofDrawBitmapString(buf, 0, 0);
|
||||
ofPopMatrix();
|
||||
};
|
||||
|
||||
for (std::size_t i = 0; i < text_.size(); ++i) {
|
||||
const float cx = scrollX_ + i * charW;
|
||||
if (cx < -charW || cx > W + charW) continue;
|
||||
const float wob = std::sin(t_ * 2.5f + i * 0.28f) * amp;
|
||||
// Couleur cycle phosphor → ambre par index
|
||||
const float hue = std::fmod(i * 4.0f + t_ * 25.0f, 60.0f) + 80.0f;
|
||||
ofColor c;
|
||||
c.setHsb(hue, 200.0f, 255.0f);
|
||||
ofSetColor(c, 230);
|
||||
ofPushMatrix();
|
||||
ofTranslate(cx, baseY + wob);
|
||||
ofScale(scale, scale, 1.0f);
|
||||
const char buf[2] = { text_[i], 0 };
|
||||
ofDrawBitmapString(buf, 0, 0);
|
||||
ofPopMatrix();
|
||||
|
||||
switch (scrollerStyle_) {
|
||||
case ScrollerStyle::Classic: {
|
||||
const float hue = std::fmod(i * 4.0f + t_ * 25.0f, 60.0f) + 80.0f;
|
||||
ofColor c; c.setHsb(hue, 200.0f, 255.0f); c.a = 230;
|
||||
drawChar(text_[i], cx, baseY + wob, scale, scale, 0.0f, c);
|
||||
break;
|
||||
}
|
||||
case ScrollerStyle::Wavy3D: {
|
||||
const float pulse = 0.5f + 0.5f * std::sin(t_ * 3.0f + i * 0.45f);
|
||||
const float sxv = scale * (0.6f + pulse * 0.9f);
|
||||
const float syv = scale * (1.0f - pulse * 0.3f);
|
||||
ofColor c; c.setHsb(140.0f + pulse * 60.0f, 200.0f, 255.0f); c.a = 220;
|
||||
drawChar(text_[i], cx, baseY + wob, sxv, syv, 0.0f, c);
|
||||
break;
|
||||
}
|
||||
case ScrollerStyle::Rainbow: {
|
||||
const float hue = std::fmod(i * 12.0f + t_ * 60.0f, 255.0f);
|
||||
ofColor c; c.setHsb(hue, 230.0f, 255.0f); c.a = 240;
|
||||
drawChar(text_[i], cx, baseY + wob, scale, scale, 0.0f, c);
|
||||
break;
|
||||
}
|
||||
case ScrollerStyle::Mirror: {
|
||||
const float hue = std::fmod(i * 4.0f + t_ * 25.0f, 60.0f) + 80.0f;
|
||||
ofColor c; c.setHsb(hue, 200.0f, 255.0f);
|
||||
ofColor cTop = c; cTop.a = 230;
|
||||
ofColor cBot = c; cBot.a = 90;
|
||||
drawChar(text_[i], cx, baseY + wob, scale, scale, 0.0f, cTop);
|
||||
// Reflet : translate Y de 2× scale + flip Y
|
||||
drawChar(text_[i], cx, baseY + wob + 14.0f * scale,
|
||||
scale, -scale * 0.7f, 0.0f, cBot);
|
||||
break;
|
||||
}
|
||||
case ScrollerStyle::Glitch: {
|
||||
const float jx = std::sin(t_ * 60.0f + i * 1.7f) * 6.0f;
|
||||
const float jy = std::cos(t_ * 73.0f + i * 2.1f) * 4.0f;
|
||||
const bool corrupt = (std::sin(t_ * 11.0f + i * 0.6f) > 0.85f);
|
||||
ofColor c;
|
||||
c.setHsb(corrupt ? 0.0f : 180.0f, 230.0f,
|
||||
200.0f + 55.0f * (corrupt ? 1.0f : 0.0f));
|
||||
c.a = 230;
|
||||
drawChar(text_[i], cx + jx, baseY + wob + jy, scale, scale, 0.0f, c);
|
||||
break;
|
||||
}
|
||||
case ScrollerStyle::Neon: {
|
||||
// Outline cyan large + cœur magenta fin
|
||||
ofColor outline(80, 220, 255, 110);
|
||||
for (int dx = -1; dx <= 1; ++dx)
|
||||
for (int dy = -1; dy <= 1; ++dy) {
|
||||
if (dx == 0 && dy == 0) continue;
|
||||
drawChar(text_[i], cx + dx * 2.0f, baseY + wob + dy * 2.0f,
|
||||
scale, scale, 0.0f, outline);
|
||||
}
|
||||
ofColor core(255, 100, 200, 240);
|
||||
drawChar(text_[i], cx, baseY + wob, scale, scale, 0.0f, core);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ofDisableBlendMode();
|
||||
ofPopStyle();
|
||||
|
||||
@@ -15,6 +15,15 @@
|
||||
|
||||
namespace oscope {
|
||||
|
||||
enum class ScrollerStyle {
|
||||
Classic, // sine wave wobble (par défaut)
|
||||
Wavy3D, // sine + scale per char, effet de respiration 3D
|
||||
Rainbow, // hue cycle large par char (cycle complet sur le texte)
|
||||
Mirror, // classic + reflet vertical en bas avec gradient alpha
|
||||
Glitch, // jitter horizontal aléatoire + couleurs cassées
|
||||
Neon // double passe : outline cyan + cœur magenta
|
||||
};
|
||||
|
||||
class DemoFx {
|
||||
public:
|
||||
void setup(const std::string& greetingsPath);
|
||||
@@ -22,6 +31,8 @@ public:
|
||||
|
||||
/// Scroller bas d'écran. Lit text_, défile, sine wave par char.
|
||||
void drawScroller(int W, int H);
|
||||
void setScrollerStyle(ScrollerStyle s) { scrollerStyle_ = s; }
|
||||
ScrollerStyle scrollerStyle() const { return scrollerStyle_; }
|
||||
|
||||
/// Starfield 3D fullscreen (peut être utilisé en place du tunnel).
|
||||
void drawStarfield(int W, int H);
|
||||
@@ -42,6 +53,7 @@ private:
|
||||
std::string text_;
|
||||
float scrollX_ = 0.0f;
|
||||
bool scrollerOn_ = true;
|
||||
ScrollerStyle scrollerStyle_ = ScrollerStyle::Classic;
|
||||
|
||||
// Starfield
|
||||
struct Star { float x, y, z; };
|
||||
|
||||
+72
-14
@@ -94,6 +94,7 @@ void ofApp::setup() {
|
||||
postfx_.setup(W, H);
|
||||
|
||||
demo_.setup(ofToDataPath("greetings.txt", true));
|
||||
initPresets();
|
||||
|
||||
gui_.setup("oscope-of");
|
||||
gui_.add(modeLabel_.setup("Mode", modeName(mode_)));
|
||||
@@ -308,6 +309,59 @@ void ofApp::drawHybrid(int W, int H) {
|
||||
spectro_->draw(0, H - sh, W, sh);
|
||||
}
|
||||
|
||||
void ofApp::initPresets() {
|
||||
using SS = oscope::ScrollerStyle;
|
||||
auto allOn = []{ return ScopeToggles{}; };
|
||||
auto onlyTunnel = []{
|
||||
ScopeToggles s{};
|
||||
s.starfield = false; s.spectroRing = false; s.polar = false;
|
||||
s.waveform = false; s.bobs = false; s.copperBars = false;
|
||||
return s;
|
||||
};
|
||||
auto cleanScope = []{
|
||||
ScopeToggles s{};
|
||||
s.tunnel = false; s.starfield = false; s.copperBars = false;
|
||||
s.bobs = false; s.tunnelHud = false;
|
||||
return s;
|
||||
};
|
||||
auto starOnly = []{
|
||||
ScopeToggles s{};
|
||||
s.tunnel = false; s.starfield = true; s.copperBars = false;
|
||||
s.bobs = false; s.tunnelHud = false; s.polar = false;
|
||||
s.spectroRing = false; s.waveform = true;
|
||||
return s;
|
||||
};
|
||||
|
||||
// 10 presets demoparty — chacun avec son ambiance + track SC associée.
|
||||
presets_[0] = {"AMIGA", allOn(), SS::Classic, "A"}; // acid
|
||||
presets_[1] = {"C64", allOn(), SS::Wavy3D, "M"}; // chiptune
|
||||
presets_[2] = {"OLDSKOOL", allOn(), SS::Mirror, "G"}; // detroit
|
||||
presets_[3] = {"NEWSKOOL", allOn(), SS::Rainbow, "L"}; // future garage
|
||||
presets_[4] = {"TUNNEL", onlyTunnel(), SS::Classic, "P"}; // industrial
|
||||
presets_[5] = {"STARFIELD", starOnly(), SS::Neon, "J"}; // ambient
|
||||
presets_[6] = {"SCOPE", cleanScope(), SS::Classic, "I"}; // dnb liquide
|
||||
presets_[7] = {"CYBER", allOn(), SS::Glitch, "V"}; // glitch idm
|
||||
presets_[8] = {"VAPOR", allOn(), SS::Mirror, "U"}; // vocal trance
|
||||
presets_[9] = {"RAVE", allOn(), SS::Rainbow, "T"}; // hardcore gabber
|
||||
|
||||
// Defaults : appliquer preset 0 au boot.
|
||||
applyPreset(0);
|
||||
}
|
||||
|
||||
void ofApp::applyPreset(int idx) {
|
||||
if (idx < 0 || idx >= 10) return;
|
||||
presetIdx_ = idx;
|
||||
const auto& p = presets_[idx];
|
||||
scope4_ = p.toggles;
|
||||
demo_.setScrollerStyle(p.scroller);
|
||||
if (p.albumLetter) {
|
||||
osc_.sendControl("/control/playAlbum",
|
||||
std::string(p.albumLetter));
|
||||
}
|
||||
ofLogNotice("ofApp") << "preset " << idx << " : " << p.name
|
||||
<< " -> album " << (p.albumLetter ?: "none");
|
||||
}
|
||||
|
||||
void ofApp::drawTunnelHud(int W, int H) {
|
||||
const float now = ofGetElapsedTimef();
|
||||
const float dt = ofGetLastFrameTime();
|
||||
@@ -498,9 +552,13 @@ void ofApp::drawScope4(int W, int H) {
|
||||
ofSetColor(120, 200, 100, 130);
|
||||
ofDrawRectangle(sx + barW * 0.5f - 0.5f, rowY + 14, 1, 6);
|
||||
}
|
||||
// Hint reset à droite
|
||||
// Hint reset à droite + nom du preset courant en gros
|
||||
ofSetColor(140, 200, 200, 180);
|
||||
ofDrawBitmapString("[w] reset", W - 90, rowY);
|
||||
ofDrawBitmapString("[w] reset", W - 100, rowY);
|
||||
ofSetColor(255, 100, 200, 200);
|
||||
ofDrawBitmapString(std::string("[") +
|
||||
ofToString(presetIdx_ < 9 ? presetIdx_ + 1 : 0) + "] " +
|
||||
presets_[presetIdx_].name, W - 220, rowY - 18);
|
||||
ofDisableBlendMode();
|
||||
ofPopStyle();
|
||||
}
|
||||
@@ -601,19 +659,19 @@ void ofApp::windowResized(int w, int h) {
|
||||
}
|
||||
|
||||
void ofApp::keyPressed(int key) {
|
||||
// Toggles Scope4 — touches 1..9 chacune un effet demoscene, 0 = reset.
|
||||
auto resetToggles = [&]() { scope4_ = ScopeToggles{}; };
|
||||
// Touches 1..9 + 0 — lance un preset demoparty (visuel + track SC).
|
||||
// 'q' reste libre pour le mode live (pas de preset, contrôle manuel).
|
||||
switch (key) {
|
||||
case '1': scope4_.tunnel = !scope4_.tunnel; break;
|
||||
case '2': scope4_.starfield = !scope4_.starfield; break;
|
||||
case '3': scope4_.scroller = !scope4_.scroller; break;
|
||||
case '4': scope4_.copperBars = !scope4_.copperBars; break;
|
||||
case '5': scope4_.bobs = !scope4_.bobs; break;
|
||||
case '6': scope4_.tunnelHud = !scope4_.tunnelHud; break;
|
||||
case '7': scope4_.spectroRing = !scope4_.spectroRing; break;
|
||||
case '8': scope4_.polar = !scope4_.polar; break;
|
||||
case '9': scope4_.waveform = !scope4_.waveform; break;
|
||||
case '0': resetToggles(); break;
|
||||
case '1': applyPreset(0); break; // AMIGA + acid
|
||||
case '2': applyPreset(1); break; // C64 + chiptune
|
||||
case '3': applyPreset(2); break; // OLDSKOOL + detroit
|
||||
case '4': applyPreset(3); break; // NEWSKOOL + future garage
|
||||
case '5': applyPreset(4); break; // TUNNEL + industrial
|
||||
case '6': applyPreset(5); break; // STARFIELD + ambient
|
||||
case '7': applyPreset(6); break; // SCOPE + dnb liquide
|
||||
case '8': applyPreset(7); break; // CYBER + glitch idm
|
||||
case '9': applyPreset(8); break; // VAPOR + vocal trance
|
||||
case '0': applyPreset(9); break; // RAVE + hardcore gabber
|
||||
case 'f':
|
||||
fullscreen_ = !fullscreen_;
|
||||
ofSetFullscreen(fullscreen_);
|
||||
|
||||
+23
-10
@@ -73,19 +73,32 @@ private:
|
||||
oscope::AudioAnalyzer audio_;
|
||||
oscope::DemoFx demo_;
|
||||
|
||||
// Toggles Scope4 — touches 1-9 + 0 reset.
|
||||
// Toggles Scope4 — modulés par les presets demoparty 0-9.
|
||||
struct ScopeToggles {
|
||||
bool tunnel = true; // 1 — tunnel 3D background
|
||||
bool starfield = false; // 2 — starfield (remplace le tunnel)
|
||||
bool scroller = true; // 3 — sine scroller greetings.txt
|
||||
bool copperBars = true; // 4 — copper bars haut
|
||||
bool bobs = true; // 5 — logo bobs
|
||||
bool tunnelHud = true; // 6 — HUD aléatoire sub-10Hz
|
||||
bool spectroRing = true; // 7 — spectro circular ring
|
||||
bool polar = true; // 8 — polar central
|
||||
bool waveform = true; // 9 — waveform circular CRT
|
||||
bool tunnel = true;
|
||||
bool starfield = false;
|
||||
bool scroller = true;
|
||||
bool copperBars = true;
|
||||
bool bobs = true;
|
||||
bool tunnelHud = true;
|
||||
bool spectroRing = true;
|
||||
bool polar = true;
|
||||
bool waveform = true;
|
||||
} scope4_;
|
||||
|
||||
// 10 demoparty presets — appliqués via touches 1..9 + 0.
|
||||
// Chaque preset = nom + état toggles + style scroller + lettre album SC.
|
||||
struct DemoPreset {
|
||||
const char* name;
|
||||
ScopeToggles toggles;
|
||||
oscope::ScrollerStyle scroller;
|
||||
const char* albumLetter; // A..W ou nullptr
|
||||
};
|
||||
DemoPreset presets_[10];
|
||||
int presetIdx_ = 0;
|
||||
void initPresets();
|
||||
void applyPreset(int idx);
|
||||
|
||||
std::unique_ptr<oscope::LissajousVis> lissajous_;
|
||||
std::unique_ptr<oscope::SpectrogramVis> spectro_;
|
||||
std::unique_ptr<oscope::ReactiveVis> reactive_;
|
||||
|
||||
Reference in New Issue
Block a user