X-Git-Url: https://git.piment-noir.org/?p=SugarCubes.git;a=blobdiff_plain;f=Internals.pde;h=b593f3ef07b9c4aa7db088430348d3da9c653ae7;hp=a1b8baf03f6ea1ca1cfda7833ec6021098b6c22b;hb=7d60f6f6ea82fd5c9794524ce11d40e7d6c00bf7;hpb=cc9e8c8be45767207e09c434bad100b0156877e6 diff --git a/Internals.pde b/Internals.pde index a1b8baf..b593f3e 100644 --- a/Internals.pde +++ b/Internals.pde @@ -13,48 +13,56 @@ * for general animation work. */ -import glucose.*; -import glucose.model.*; import heronarts.lx.*; import heronarts.lx.effect.*; +import heronarts.lx.model.*; import heronarts.lx.modulator.*; import heronarts.lx.parameter.*; import heronarts.lx.pattern.*; import heronarts.lx.transform.*; import heronarts.lx.transition.*; +import heronarts.lx.ui.*; +import heronarts.lx.ui.component.*; +import heronarts.lx.ui.control.*; import ddf.minim.*; import ddf.minim.analysis.*; import processing.opengl.*; import rwmidi.*; import java.lang.reflect.*; -import javax.media.opengl.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; -final int VIEWPORT_WIDTH = 900; -final int VIEWPORT_HEIGHT = 700; +static final int VIEWPORT_WIDTH = 900; +static final int VIEWPORT_HEIGHT = 700; + +static final int LEFT_DECK = 0; +static final int RIGHT_DECK = 1; // The trailer is measured from the outside of the black metal (but not including the higher welded part on the front) -final float TRAILER_WIDTH = 240; -final float TRAILER_DEPTH = 97; -final float TRAILER_HEIGHT = 33; +static final float TRAILER_WIDTH = 192; +static final float TRAILER_DEPTH = 192; +static final float TRAILER_HEIGHT = 33; int targetFramerate = 60; int startMillis, lastMillis; // Core engine variables -GLucose glucose; LX lx; +Model model; LXPattern[] patterns; +LXTransition[] transitions; Effects effects; +LXEffect[] effectsArr; +DiscreteParameter selectedEffect; MappingTool mappingTool; -PandaDriver[] pandaBoards; +GrizzlyOutput[] grizzlies; PresetManager presetManager; MidiEngine midiEngine; // Display configuration mode boolean mappingMode = false; boolean debugMode = false; -DebugUI debugUI; -boolean uiOn = true; boolean simulationOn = true; boolean diagnosticsOn = false; LXPattern restoreToPattern = null; @@ -62,7 +70,6 @@ PImage logo; float[] hsb = new float[3]; // Handles to UI objects -UIContext[] overlays; UIPatternDeck uiPatternA; UICrossfader uiCrossfader; UIMidi uiMidi; @@ -70,30 +77,27 @@ UIMapping uiMapping; UIDebugText uiDebugText; UISpeed uiSpeed; -// Camera variables -float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ; - /** * Engine construction and initialization. */ -LXTransition _transition(GLucose glucose) { - return new DissolveTransition(glucose.lx).setDuration(1000); +LXTransition _transition(LX lx) { + return new DissolveTransition(lx).setDuration(1000); } -LXPattern[] _leftPatterns(GLucose glucose) { - LXPattern[] patterns = patterns(glucose); +LXPattern[] _leftPatterns(LX lx) { + LXPattern[] patterns = patterns(lx); for (LXPattern p : patterns) { - p.setTransition(_transition(glucose)); + p.setTransition(_transition(lx)); } return patterns; } -LXPattern[] _rightPatterns(GLucose glucose) { - LXPattern[] patterns = _leftPatterns(glucose); +LXPattern[] _rightPatterns(LX lx) { + LXPattern[] patterns = _leftPatterns(lx); LXPattern[] rightPatterns = new LXPattern[patterns.length+1]; int i = 0; - rightPatterns[i++] = new BlankPattern(glucose).setTransition(_transition(glucose)); + rightPatterns[i++] = new BlankPattern(lx).setTransition(_transition(lx)); for (LXPattern p : patterns) { rightPatterns[i++] = p; } @@ -111,7 +115,11 @@ LXEffect[] _effectsArray(Effects effects) { } catch (IllegalAccessException iax) {} } return effectList.toArray(new LXEffect[]{}); -} +} + +LXEffect getSelectedEffect() { + return effectsArr[selectedEffect.getValuei()]; +} void logTime(String evt) { int now = millis(); @@ -129,20 +137,29 @@ void setup() { // hint(ENABLE_OPENGL_4X_SMOOTH); // no discernable improvement? logTime("Created viewport"); - // Create the GLucose engine to run the cubes - glucose = new GLucose(this, buildModel()); - lx = glucose.lx; + // Create the model + model = buildModel(); + logTime("Built Model"); + + // LX engine + lx = new LX(this, model); lx.enableKeyboardTempo(); - logTime("Built GLucose engine"); + logTime("Built LX engine"); // Set the patterns LXEngine engine = lx.engine; - engine.setPatterns(patterns = _leftPatterns(glucose)); - engine.addDeck(_rightPatterns(glucose)); + engine.setPatterns(patterns = _leftPatterns(lx)); + engine.addDeck(_rightPatterns(lx)); logTime("Built patterns"); - glucose.setTransitions(transitions(glucose)); + + // Transitions + transitions = transitions(lx); + lx.engine.getDeck(RIGHT_DECK).setFaderTransition(transitions[0]); logTime("Built transitions"); - glucose.lx.addEffects(_effectsArray(effects = new Effects())); + + // Effects + lx.addEffects(effectsArr = _effectsArray(effects = new Effects())); + selectedEffect = new DiscreteParameter("EFFECT", effectsArr.length); logTime("Built effects"); // Preset manager @@ -154,93 +171,160 @@ void setup() { logTime("Setup MIDI devices"); // Build output driver - PandaMapping[] pandaMappings = buildPandaList(); - pandaBoards = new PandaDriver[pandaMappings.length]; - int pbi = 0; - for (PandaMapping pm : pandaMappings) { - pandaBoards[pbi++] = new PandaDriver(pm.ip, glucose.model, pm); + grizzlies = new GrizzlyOutput[]{}; + try { + grizzlies = buildGrizzlies(); + for (LXOutput output : grizzlies) { + lx.addOutput(output); + } + } catch (Exception x) { + x.printStackTrace(); } - mappingTool = new MappingTool(glucose, pandaMappings); - logTime("Built PandaDriver"); + logTime("Built Grizzly Outputs"); + // Mapping tool + mappingTool = new MappingTool(lx); + logTime("Built Mapping Tool"); + // Build overlay UI - debugUI = new DebugUI(pandaMappings); - overlays = new UIContext[] { - uiPatternA = new UIPatternDeck(lx.engine.getDeck(GLucose.LEFT_DECK), "PATTERN A", 4, 4, 140, 324), + UILayer[] layers = new UILayer[] { + // Camera layer + new UICameraLayer(lx.ui) + .setCenter(model.cx, model.cy, model.cz) + .setRadius(290).addComponent(new UICubesLayer()), + + // Left controls + uiPatternA = new UIPatternDeck(lx.ui, lx.engine.getDeck(LEFT_DECK), "PATTERN A", 4, 4, 140, 324), new UIBlendMode(4, 332, 140, 86), new UIEffects(4, 422, 140, 144), new UITempo(4, 570, 140, 50), uiSpeed = new UISpeed(4, 624, 140, 50), - new UIPatternDeck(lx.engine.getDeck(GLucose.RIGHT_DECK), "PATTERN B", width-144, 4, 140, 324), + // Right controls + new UIPatternDeck(lx.ui, lx.engine.getDeck(RIGHT_DECK), "PATTERN B", width-144, 4, 140, 324), uiMidi = new UIMidi(midiEngine, width-144, 332, 140, 158), - new UIOutput(width-144, 494, 140, 106), + new UIOutput(grizzlies, width-144, 494, 140, 106), + // Crossfader uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86), + // Overlays uiDebugText = new UIDebugText(148, height-138, width-304, 44), - uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324), + uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324) }; - uiMapping.setVisible(false); - logTime("Built overlay UI"); + uiMapping.setVisible(false); + for (UILayer layer : layers) { + lx.ui.addLayer(layer); + } + logTime("Built UI"); // Load logo image logo = loadImage("data/logo.png"); - - // Setup camera - midX = TRAILER_WIDTH/2.; - midY = glucose.model.yMax/2; - midZ = TRAILER_DEPTH/2.; - eyeR = -290; - eyeA = .15; - eyeY = midY + 70; - eyeX = midX + eyeR*sin(eyeA); - eyeZ = midZ + eyeR*cos(eyeA); - - // Add mouse scrolling event support - addMouseWheelListener(new java.awt.event.MouseWheelListener() { - public void mouseWheelMoved(java.awt.event.MouseWheelEvent mwe) { - mouseWheel(mwe.getWheelRotation()); - }}); - + logTime("Loaded logo image"); + println("Total setup: " + (millis() - startMillis) + "ms"); - println("Hit the 'p' key to toggle Panda Board output"); + println("Hit the 'o' key to toggle live output"); } +public SCPattern getPattern() { + return (SCPattern) lx.getPattern(); +} + +/** + * Subclass of LXPattern specific to sugar cubes. These patterns + * get access to the state and geometry, and have some + * little helpers for interacting with the model. + */ +public static abstract class SCPattern extends LXPattern { + + protected SCPattern(LX lx) { + super(lx); + } + + /** + * Reset this pattern to its default state. + */ + public final void reset() { + for (LXParameter parameter : getParameters()) { + parameter.reset(); + } + onReset(); + } + + /** + * Subclasses may override to add additional reset functionality. + */ + protected /*abstract*/ void onReset() {} + + /** + * Invoked by the engine when a grid controller button press occurs + * + * @param row Row index on the gird + * @param col Column index on the grid + * @return True if the event was consumed, false otherwise + */ + public boolean gridPressed(int row, int col) { + return false; + } + + /** + * Invoked by the engine when a grid controller button release occurs + * + * @param row Row index on the gird + * @param col Column index on the grid + * @return True if the event was consumed, false otherwise + */ + public boolean gridReleased(int row, int col) { + return false; + } + + /** + * Invoked by engine when this pattern is focused an a midi note is received. + * + * @param note + * @return True if the pattern has consumed this note, false if the top-level + * may handle it + */ + public boolean noteOn(rwmidi.Note note) { + return false; + } + + /** + * Invoked by engine when this pattern is focused an a midi note off is received. + * + * @param note + * @return True if the pattern has consumed this note, false if the top-level + * may handle it + */ + public boolean noteOff(rwmidi.Note note) { + return false; + } + + /** + * Invoked by engine when this pattern is focused an a controller is received + * + * @param note + * @return True if the pattern has consumed this controller, false if the top-level + * may handle it + */ + public boolean controllerChange(rwmidi.Controller controller) { + return false; + } +} + +long simulationNanos = 0; + /** * Core render loop and drawing functionality. */ void draw() { long drawStart = System.nanoTime(); - // Draws the simulation and the 2D UI overlay + // Set background background(40); - - color[] simulationColors; - color[] sendColors; - simulationColors = sendColors = glucose.getColors(); - String displayMode = uiCrossfader.getDisplayMode(); - if (displayMode == "A") { - simulationColors = lx.engine.getDeck(GLucose.LEFT_DECK).getColors(); - } else if (displayMode == "B") { - simulationColors = lx.engine.getDeck(GLucose.RIGHT_DECK).getColors(); - } - if (debugMode) { - debugUI.maskColors(simulationColors); - debugUI.maskColors(sendColors); - } - - long simulationStart = System.nanoTime(); - if (simulationOn) { - drawSimulation(simulationColors); - } - long simulationNanos = System.nanoTime() - simulationStart; - - // 2D Overlay UI - long uiStart = System.nanoTime(); - drawUI(); - long uiNanos = System.nanoTime() - uiStart; + // Send colors + color[] sendColors = lx.getColors(); long gammaStart = System.nanoTime(); // Gamma correction here. Apply a cubic to the brightness // for better representation of dynamic range @@ -250,21 +334,20 @@ void draw() { sendColors[i] = lx.hsb(360.*hsb[0], 100.*hsb[1], 100.*(b*b*b)); } long gammaNanos = System.nanoTime() - gammaStart; - - long sendStart = System.nanoTime(); - for (PandaDriver p : pandaBoards) { - p.send(sendColors); - } - long sendNanos = System.nanoTime() - sendStart; - + + // Always draw FPS meter + drawFPS(); + + // TODO(mcslee): fix long drawNanos = System.nanoTime() - drawStart; - + long uiNanos = 0; + if (diagnosticsOn) { - drawDiagnostics(drawNanos, simulationNanos, uiNanos, gammaNanos, sendNanos); - } + drawDiagnostics(drawNanos, simulationNanos, uiNanos, gammaNanos); + } } -void drawDiagnostics(long drawNanos, long simulationNanos, long uiNanos, long gammaNanos, long sendNanos) { +void drawDiagnostics(long drawNanos, long simulationNanos, long uiNanos, long gammaNanos) { float ws = 4 / 1000000.; int thirtyfps = 1000000000 / 30; int sixtyfps = 1000000000 / 60; @@ -277,7 +360,7 @@ void drawDiagnostics(long drawNanos, long simulationNanos, long uiNanos, long ga noStroke(); int xp = x; float hv = 0; - for (long val : new long[] {lx.timer.drawNanos, simulationNanos, uiNanos, gammaNanos, sendNanos }) { + for (long val : new long[] {lx.timer.drawNanos, simulationNanos, uiNanos, gammaNanos, lx.timer.outputNanos }) { fill(lx.hsb(hv % 360, 100, 80)); rect(xp, y, val * ws, h-1); hv += 140; @@ -321,190 +404,12 @@ void drawDiagnostics(long drawNanos, long simulationNanos, long uiNanos, long ga } } -void drawSimulation(color[] simulationColors) { - camera( - eyeX, eyeY, eyeZ, - midX, midY, midZ, - 0, -1, 0 - ); - - translate(0, 40, 0); - - noStroke(); - fill(#141414); - drawBox(0, -TRAILER_HEIGHT, 0, 0, 0, 0, TRAILER_WIDTH, TRAILER_HEIGHT, TRAILER_DEPTH, TRAILER_HEIGHT/2.); - fill(#070707); - stroke(#222222); - beginShape(); - vertex(0, 0, 0); - vertex(TRAILER_WIDTH, 0, 0); - vertex(TRAILER_WIDTH, 0, TRAILER_DEPTH); - vertex(0, 0, TRAILER_DEPTH); - endShape(); - - // Draw the logo on the front of platform - pushMatrix(); - translate(0, 0, -1); - float s = .07; - scale(s, -s, s); - image(logo, TRAILER_WIDTH/2/s-logo.width/2, TRAILER_HEIGHT/2/s-logo.height/2-2/s); - popMatrix(); - - noStroke(); - if (glucose.model.bassBox.exists) { - drawBassBox(glucose.model.bassBox, false); - } - for (Speaker speaker : glucose.model.speakers) { - drawSpeaker(speaker); - } - for (Cube c : glucose.model.cubes) { - drawCube(c); - } - - noFill(); - strokeWeight(2); - beginShape(POINTS); - for (LXPoint p : glucose.model.points) { - stroke(simulationColors[p.index]); - vertex(p.x, p.y, p.z); - } - endShape(); -} - -void drawBassBox(BassBox b, boolean hasSub) { - - float in = .15; - - if (hasSub) { - noStroke(); - fill(#191919); - pushMatrix(); - translate(b.x + BassBox.EDGE_WIDTH/2., b.y + BassBox.EDGE_HEIGHT/2, b.z + BassBox.EDGE_DEPTH/2.); - box(BassBox.EDGE_WIDTH-20*in, BassBox.EDGE_HEIGHT-20*in, BassBox.EDGE_DEPTH-20*in); - popMatrix(); - } - - noStroke(); - fill(#393939); - drawBox(b.x+in, b.y+in, b.z+in, 0, 0, 0, BassBox.EDGE_WIDTH-in*2, BassBox.EDGE_HEIGHT-in*2, BassBox.EDGE_DEPTH-in*2, Cube.CHANNEL_WIDTH-in); - - pushMatrix(); - translate(b.x+(Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT-in, b.z + BassBox.EDGE_DEPTH/2.); - float lastOffset = 0; - for (float offset : BoothFloor.STRIP_OFFSETS) { - translate(offset - lastOffset, 0, 0); - box(Cube.CHANNEL_WIDTH-in, 0, BassBox.EDGE_DEPTH - 2*in); - lastOffset = offset; - } - popMatrix(); - - pushMatrix(); - translate(b.x + (Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT/2., b.z + in); - for (int j = 0; j < 2; ++j) { - pushMatrix(); - for (int i = 0; i < BassBox.NUM_FRONT_STRUTS; ++i) { - translate(BassBox.FRONT_STRUT_SPACING, 0, 0); - box(Cube.CHANNEL_WIDTH-in, BassBox.EDGE_HEIGHT - in*2, 0); - } - popMatrix(); - translate(0, 0, BassBox.EDGE_DEPTH - 2*in); - } - popMatrix(); - - pushMatrix(); - translate(b.x + in, b.y + BassBox.EDGE_HEIGHT/2., b.z + BassBox.SIDE_STRUT_SPACING + (Cube.CHANNEL_WIDTH-in)/2.); - box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in); - translate(BassBox.EDGE_WIDTH-2*in, 0, 0); - box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in); - popMatrix(); - -} - -void drawCube(Cube c) { - float in = .15; - noStroke(); - fill(#393939); - drawBox(c.x+in, c.y+in, c.z+in, c.rx, c.ry, c.rz, Cube.EDGE_WIDTH-in*2, Cube.EDGE_HEIGHT-in*2, Cube.EDGE_WIDTH-in*2, Cube.CHANNEL_WIDTH-in); -} - -void drawSpeaker(Speaker s) { - float in = .15; - - noStroke(); - fill(#191919); - pushMatrix(); - translate(s.x, s.y, s.z); - rotate(s.ry / 180. * PI, 0, -1, 0); - translate(Speaker.EDGE_WIDTH/2., Speaker.EDGE_HEIGHT/2., Speaker.EDGE_DEPTH/2.); - box(Speaker.EDGE_WIDTH-20*in, Speaker.EDGE_HEIGHT-20*in, Speaker.EDGE_DEPTH-20*in); - translate(0, Speaker.EDGE_HEIGHT/2. + Speaker.EDGE_HEIGHT*.8/2, 0); - - fill(#222222); - box(Speaker.EDGE_WIDTH*.6, Speaker.EDGE_HEIGHT*.8, Speaker.EDGE_DEPTH*.75); - popMatrix(); - - noStroke(); - fill(#393939); - drawBox(s.x+in, s.y+in, s.z+in, 0, s.ry, 0, Speaker.EDGE_WIDTH-in*2, Speaker.EDGE_HEIGHT-in*2, Speaker.EDGE_DEPTH-in*2, Cube.CHANNEL_WIDTH-in); -} - -void drawBox(float x, float y, float z, float rx, float ry, float rz, float xd, float yd, float zd, float sw) { - pushMatrix(); - translate(x, y, z); - rotate(rx / 180. * PI, -1, 0, 0); - rotate(ry / 180. * PI, 0, -1, 0); - rotate(rz / 180. * PI, 0, 0, -1); - for (int i = 0; i < 4; ++i) { - float wid = (i % 2 == 0) ? xd : zd; - - beginShape(); - vertex(0, 0); - vertex(wid, 0); - vertex(wid, yd); - vertex(wid - sw, yd); - vertex(wid - sw, sw); - vertex(0, sw); - endShape(); - beginShape(); - vertex(0, sw); - vertex(0, yd); - vertex(wid - sw, yd); - vertex(wid - sw, yd - sw); - vertex(sw, yd - sw); - vertex(sw, sw); - endShape(); - - translate(wid, 0, 0); - rotate(HALF_PI, 0, -1, 0); - } - popMatrix(); -} - - - -void drawUI() { - camera(); - PGraphicsOpenGL gl = (PGraphicsOpenGL) g; - - //gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT); - //((PGraphicsOpenGL)g).endGL(); - strokeWeight(1); - - if (uiOn) { - for (UIContext context : overlays) { - context.draw(); - } - } - +void drawFPS() { // Always draw FPS meter fill(#555555); textSize(9); textAlign(LEFT, BASELINE); text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4); - - if (debugMode) { - debugUI.draw(); - } } @@ -593,9 +498,10 @@ void keyPressed() { lx.engine.setThreaded(!lx.engine.isThreaded()); } break; + case 'o': case 'p': - for (PandaDriver p : pandaBoards) { - p.toggle(); + for (LXOutput output : grizzlies) { + output.enabled.toggle(); } break; case 'q': @@ -608,64 +514,6 @@ void keyPressed() { simulationOn = !simulationOn; } break; - case 'u': - if (!midiEngine.isQwertyEnabled()) { - uiOn = !uiOn; - } - break; - } -} - -/** - * Top-level mouse event handling - */ -int mx, my; -void mousePressed() { - boolean debugged = false; - if (debugMode) { - debugged = debugUI.mousePressed(); } - if (!debugged) { - for (UIContext context : overlays) { - context.mousePressed(mouseX, mouseY); - } - } - mx = mouseX; - my = mouseY; } -void mouseDragged() { - boolean dragged = false; - for (UIContext context : overlays) { - dragged |= context.mouseDragged(mouseX, mouseY); - } - if (!dragged) { - int dx = mouseX - mx; - int dy = mouseY - my; - mx = mouseX; - my = mouseY; - eyeA += dx*.003; - eyeX = midX + eyeR*sin(eyeA); - eyeZ = midZ + eyeR*cos(eyeA); - eyeY += dy; - } -} - -void mouseReleased() { - for (UIContext context : overlays) { - context.mouseReleased(mouseX, mouseY); - } -} - -void mouseWheel(int delta) { - boolean wheeled = false; - for (UIContext context : overlays) { - wheeled |= context.mouseWheel(mouseX, mouseY, delta); - } - - if (!wheeled) { - eyeR = constrain(eyeR - delta, -500, -80); - eyeX = midX + eyeR*sin(eyeA); - eyeZ = midZ + eyeR*cos(eyeA); - } -}