2 * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
5 * ///\\\ ///\\\ ///\\\ ///\\\
6 * \\\/// \\\/// \\\/// \\\///
9 * EXPERTS ONLY!! EXPERTS ONLY!!
11 * If you are an artist, you may ignore this file! It just sets
12 * up the framework to run the patterns. Should not need modification
13 * for general animation work.
17 import glucose.control.*;
18 import glucose.effect.*;
19 import glucose.model.*;
20 import glucose.pattern.*;
21 import glucose.transform.*;
22 import glucose.transition.*;
23 import heronarts.lx.*;
24 import heronarts.lx.control.*;
25 import heronarts.lx.effect.*;
26 import heronarts.lx.modulator.*;
27 import heronarts.lx.pattern.*;
28 import heronarts.lx.transition.*;
30 import ddf.minim.analysis.*;
31 import processing.opengl.*;
34 final int VIEWPORT_WIDTH = 900;
35 final int VIEWPORT_HEIGHT = 700;
37 // The trailer is measured from the outside of the black metal (but not including the higher welded part on the front)
38 final float TRAILER_WIDTH = 240;
39 final float TRAILER_DEPTH = 97;
40 final float TRAILER_HEIGHT = 33;
42 int targetFramerate = 60;
43 int startMillis, lastMillis;
45 // Core engine variables
49 MappingTool mappingTool;
50 PandaDriver[] pandaBoards;
51 MidiEngine midiEngine;
53 // Display configuration mode
54 boolean mappingMode = false;
55 boolean debugMode = false;
58 LXPattern restoreToPattern = null;
61 // Handles to UI objects
63 UIPatternDeck uiPatternA;
64 UICrossfader uiCrossfader;
67 UIDebugText uiDebugText;
70 float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ;
73 * Engine construction and initialization.
75 LXPattern[] _patterns(GLucose glucose) {
76 LXPattern[] patterns = patterns(glucose);
77 for (LXPattern p : patterns) {
78 p.setTransition(new DissolveTransition(glucose.lx).setDuration(1000));
83 void logTime(String evt) {
85 println(evt + ": " + (now - lastMillis) + "ms");
90 startMillis = lastMillis = millis();
92 // Initialize the Processing graphics environment
93 size(VIEWPORT_WIDTH, VIEWPORT_HEIGHT, OPENGL);
94 frameRate(targetFramerate);
96 // hint(ENABLE_OPENGL_4X_SMOOTH); // no discernable improvement?
97 logTime("Created viewport");
99 // Create the GLucose engine to run the cubes
100 glucose = new GLucose(this, buildModel());
102 lx.enableKeyboardTempo();
103 logTime("Built GLucose engine");
106 midiEngine = new MidiEngine();
107 logTime("Setup MIDI devices");
110 Engine engine = lx.engine;
111 engine.setPatterns(patterns = _patterns(glucose));
112 engine.addDeck(_patterns(glucose));
113 logTime("Built patterns");
114 glucose.setTransitions(transitions(glucose));
115 logTime("Built transitions");
116 glucose.lx.addEffects(effects(glucose));
117 logTime("Built effects");
119 // Build output driver
120 PandaMapping[] pandaMappings = buildPandaList();
121 pandaBoards = new PandaDriver[pandaMappings.length];
123 for (PandaMapping pm : pandaMappings) {
124 pandaBoards[pbi++] = new PandaDriver(pm.ip, glucose.model, pm);
126 mappingTool = new MappingTool(glucose, pandaMappings);
127 logTime("Built PandaDriver");
130 debugUI = new DebugUI(pandaMappings);
131 overlays = new UIContext[] {
132 uiPatternA = new UIPatternDeck(lx.engine.getDeck(0), "PATTERN A", 4, 4, 140, 324),
133 new UIBlendMode(4, 332, 140, 86),
134 new UIEffects(4, 422, 140, 144),
135 new UITempo(4, 570, 140, 50),
136 new UISpeed(4, 624, 140, 50),
138 new UIPatternDeck(lx.engine.getDeck(1), "PATTERN B", width-144, 4, 140, 324),
139 uiMidi = new UIMidi(midiEngine, width-144, 332, 140, 158),
140 new UIOutput(width-144, 494, 140, 106),
142 uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86),
144 uiDebugText = new UIDebugText(148, height-138, width-304, 44),
145 uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324),
147 uiMapping.setVisible(false);
148 logTime("Built overlay UI");
151 logo = loadImage("data/logo.png");
154 midX = TRAILER_WIDTH/2.;
155 midY = glucose.model.yMax/2;
156 midZ = TRAILER_DEPTH/2.;
160 eyeX = midX + eyeR*sin(eyeA);
161 eyeZ = midZ + eyeR*cos(eyeA);
163 // Add mouse scrolling event support
164 addMouseWheelListener(new java.awt.event.MouseWheelListener() {
165 public void mouseWheelMoved(java.awt.event.MouseWheelEvent mwe) {
166 mouseWheel(mwe.getWheelRotation());
169 println("Total setup: " + (millis() - startMillis) + "ms");
170 println("Hit the 'p' key to toggle Panda Board output");
174 * Core render loop and drawing functionality.
177 // Draws the simulation and the 2D UI overlay
179 color[] colors = glucose.getColors();
181 String displayMode = uiCrossfader.getDisplayMode();
182 if (displayMode == "A") {
183 colors = lx.engine.getDeck(0).getColors();
184 } else if (displayMode == "B") {
185 colors = lx.engine.getDeck(1).getColors();
188 debugUI.maskColors(colors);
201 drawBox(0, -TRAILER_HEIGHT, 0, 0, 0, 0, TRAILER_WIDTH, TRAILER_HEIGHT, TRAILER_DEPTH, TRAILER_HEIGHT/2.);
206 vertex(TRAILER_WIDTH, 0, 0);
207 vertex(TRAILER_WIDTH, 0, TRAILER_DEPTH);
208 vertex(0, 0, TRAILER_DEPTH);
211 // Draw the logo on the front of platform
216 image(logo, TRAILER_WIDTH/2/s-logo.width/2, TRAILER_HEIGHT/2/s-logo.height/2-2/s);
220 // drawBassBox(glucose.model.bassBox);
221 // for (Speaker s : glucose.model.speakers) {
224 for (Cube c : glucose.model.cubes) {
231 // TODO(mcslee): restore when bassBox/speakers are right again
232 // for (Point p : glucose.model.points) {
233 for (Cube cube : glucose.model.cubes) {
234 for (Point p : cube.points) {
235 stroke(colors[p.index]);
236 vertex(p.fx, p.fy, p.fz);
244 // Send output colors
245 color[] sendColors = glucose.getColors();
247 debugUI.maskColors(colors);
250 // Gamma correction here. Apply a cubic to the brightness
251 // for better representation of dynamic range
252 for (int i = 0; i < colors.length; ++i) {
253 float b = brightness(colors[i]) / 100.f;
256 saturation(colors[i]),
261 // TODO(mcslee): move into GLucose engine
262 for (PandaDriver p : pandaBoards) {
267 void drawBassBox(BassBox b) {
273 translate(b.x + BassBox.EDGE_WIDTH/2., b.y + BassBox.EDGE_HEIGHT/2, b.z + BassBox.EDGE_DEPTH/2.);
274 box(BassBox.EDGE_WIDTH-20*in, BassBox.EDGE_HEIGHT-20*in, BassBox.EDGE_DEPTH-20*in);
279 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);
282 translate(b.x+(Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT-in, b.z + BassBox.EDGE_DEPTH/2.);
283 float lastOffset = 0;
284 for (float offset : BoothFloor.STRIP_OFFSETS) {
285 translate(offset - lastOffset, 0, 0);
286 box(Cube.CHANNEL_WIDTH-in, 0, BassBox.EDGE_DEPTH - 2*in);
292 translate(b.x + (Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT/2., b.z + in);
293 for (int j = 0; j < 2; ++j) {
295 for (int i = 0; i < BassBox.NUM_FRONT_STRUTS; ++i) {
296 translate(BassBox.FRONT_STRUT_SPACING, 0, 0);
297 box(Cube.CHANNEL_WIDTH-in, BassBox.EDGE_HEIGHT - in*2, 0);
300 translate(0, 0, BassBox.EDGE_DEPTH - 2*in);
305 translate(b.x + in, b.y + BassBox.EDGE_HEIGHT/2., b.z + BassBox.SIDE_STRUT_SPACING + (Cube.CHANNEL_WIDTH-in)/2.);
306 box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
307 translate(BassBox.EDGE_WIDTH-2*in, 0, 0);
308 box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
313 void drawCube(Cube c) {
317 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);
320 void drawSpeaker(Speaker s) {
326 translate(s.x, s.y, s.z);
327 rotate(s.ry / 180. * PI, 0, -1, 0);
328 translate(Speaker.EDGE_WIDTH/2., Speaker.EDGE_HEIGHT/2., Speaker.EDGE_DEPTH/2.);
329 box(Speaker.EDGE_WIDTH-20*in, Speaker.EDGE_HEIGHT-20*in, Speaker.EDGE_DEPTH-20*in);
330 translate(0, Speaker.EDGE_HEIGHT/2. + Speaker.EDGE_HEIGHT*.8/2, 0);
333 box(Speaker.EDGE_WIDTH*.6, Speaker.EDGE_HEIGHT*.8, Speaker.EDGE_DEPTH*.75);
338 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);
341 void drawBox(float x, float y, float z, float rx, float ry, float rz, float xd, float yd, float zd, float sw) {
344 rotate(rx / 180. * PI, -1, 0, 0);
345 rotate(ry / 180. * PI, 0, -1, 0);
346 rotate(rz / 180. * PI, 0, 0, -1);
347 for (int i = 0; i < 4; ++i) {
348 float wid = (i % 2 == 0) ? xd : zd;
354 vertex(wid - sw, yd);
355 vertex(wid - sw, sw);
361 vertex(wid - sw, yd);
362 vertex(wid - sw, yd - sw);
367 translate(wid, 0, 0);
368 rotate(HALF_PI, 0, -1, 0);
375 javax.media.opengl.GL gl = ((PGraphicsOpenGL)g).beginGL();
376 gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT);
377 ((PGraphicsOpenGL)g).endGL();
381 for (UIContext context : overlays) {
386 // Always draw FPS meter
389 textAlign(LEFT, BASELINE);
390 text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4);
399 * Top-level keyboard event handling
403 mappingTool.keyPressed(uiMapping);
408 frameRate(--targetFramerate);
412 frameRate(++targetFramerate);
418 if (!midiEngine.isQwertyEnabled()) {
419 debugMode = !debugMode;
420 println("Debug output: " + (debugMode ? "ON" : "OFF"));
424 if (!midiEngine.isQwertyEnabled()) {
425 mappingMode = !mappingMode;
426 uiPatternA.setVisible(!mappingMode);
427 uiMapping.setVisible(mappingMode);
429 restoreToPattern = lx.getPattern();
430 lx.setPatterns(new LXPattern[] { mappingTool });
432 lx.setPatterns(patterns);
433 LXTransition pop = restoreToPattern.getTransition();
434 restoreToPattern.setTransition(null);
435 lx.goPattern(restoreToPattern);
436 restoreToPattern.setTransition(pop);
441 for (PandaDriver p : pandaBoards) {
446 if (!midiEngine.isQwertyEnabled()) {
454 * Top-level mouse event handling
457 void mousePressed() {
458 boolean debugged = false;
460 debugged = debugUI.mousePressed();
463 for (UIContext context : overlays) {
464 context.mousePressed(mouseX, mouseY);
471 void mouseDragged() {
472 boolean dragged = false;
473 for (UIContext context : overlays) {
474 dragged |= context.mouseDragged(mouseX, mouseY);
477 int dx = mouseX - mx;
478 int dy = mouseY - my;
482 eyeX = midX + eyeR*sin(eyeA);
483 eyeZ = midZ + eyeR*cos(eyeA);
488 void mouseReleased() {
489 for (UIContext context : overlays) {
490 context.mouseReleased(mouseX, mouseY);
494 void mouseWheel(int delta) {
495 boolean wheeled = false;
496 for (UIContext context : overlays) {
497 wheeled |= context.mouseWheel(mouseX, mouseY, delta);
501 eyeR = constrain(eyeR - delta, -500, -80);
502 eyeX = midX + eyeR*sin(eyeA);
503 eyeZ = midZ + eyeR*cos(eyeA);