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);
415 if (!midiEngine.isQwertyEnabled()) {
416 debugMode = !debugMode;
417 println("Debug output: " + (debugMode ? "ON" : "OFF"));
421 if (!midiEngine.isQwertyEnabled()) {
422 mappingMode = !mappingMode;
423 uiPatternA.setVisible(!mappingMode);
424 uiMapping.setVisible(mappingMode);
426 restoreToPattern = lx.getPattern();
427 lx.setPatterns(new LXPattern[] { mappingTool });
429 lx.setPatterns(patterns);
430 LXTransition pop = restoreToPattern.getTransition();
431 restoreToPattern.setTransition(null);
432 lx.goPattern(restoreToPattern);
433 restoreToPattern.setTransition(pop);
438 for (PandaDriver p : pandaBoards) {
443 if (!midiEngine.isQwertyEnabled()) {
451 * Top-level mouse event handling
454 void mousePressed() {
455 boolean debugged = false;
457 debugged = debugUI.mousePressed();
460 for (UIContext context : overlays) {
461 context.mousePressed(mouseX, mouseY);
468 void mouseDragged() {
469 boolean dragged = false;
470 for (UIContext context : overlays) {
471 dragged |= context.mouseDragged(mouseX, mouseY);
474 int dx = mouseX - mx;
475 int dy = mouseY - my;
479 eyeX = midX + eyeR*sin(eyeA);
480 eyeZ = midZ + eyeR*cos(eyeA);
485 void mouseReleased() {
486 for (UIContext context : overlays) {
487 context.mouseReleased(mouseX, mouseY);
491 void mouseWheel(int delta) {
492 boolean wheeled = false;
493 for (UIContext context : overlays) {
494 wheeled |= context.mouseWheel(mouseX, mouseY, delta);
498 eyeR = constrain(eyeR - delta, -500, -80);
499 eyeX = midX + eyeR*sin(eyeA);
500 eyeZ = midZ + eyeR*cos(eyeA);