new mappings for Friday sep 20 video shoot
[SugarCubes.git] / _Internals.pde
CommitLineData
49815cc0 1/**
1ecdb44a
MS
2 * DOUBLE BLACK DIAMOND DOUBLE BLACK DIAMOND
3 *
4 * //\\ //\\ //\\ //\\
5 * ///\\\ ///\\\ ///\\\ ///\\\
6 * \\\/// \\\/// \\\/// \\\///
7 * \\// \\// \\// \\//
8 *
9 * EXPERTS ONLY!! EXPERTS ONLY!!
10 *
49815cc0
MS
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.
14 */
15
16import glucose.*;
17import glucose.control.*;
3f8be614 18import glucose.effect.*;
f3f5a876 19import glucose.model.*;
49815cc0 20import glucose.pattern.*;
f3f5a876 21import glucose.transform.*;
49815cc0 22import glucose.transition.*;
49815cc0 23import heronarts.lx.*;
3f8be614 24import heronarts.lx.control.*;
49815cc0 25import heronarts.lx.effect.*;
49815cc0 26import heronarts.lx.modulator.*;
f3f5a876 27import heronarts.lx.pattern.*;
49815cc0
MS
28import heronarts.lx.transition.*;
29import ddf.minim.*;
30import ddf.minim.analysis.*;
31import processing.opengl.*;
5d70e4d7 32import rwmidi.*;
49815cc0
MS
33
34final int VIEWPORT_WIDTH = 900;
35final int VIEWPORT_HEIGHT = 700;
0e3c5542 36
92c06c97 37// The trailer is measured from the outside of the black metal (but not including the higher welded part on the front)
87998ff3
MS
38final float TRAILER_WIDTH = 240;
39final float TRAILER_DEPTH = 97;
40final float TRAILER_HEIGHT = 33;
41
51d0d59a 42int targetFramerate = 60;
49815cc0 43int startMillis, lastMillis;
a898d79b
MS
44
45// Core engine variables
49815cc0
MS
46GLucose glucose;
47HeronLX lx;
48LXPattern[] patterns;
a898d79b 49MappingTool mappingTool;
79ae8245 50PandaDriver[] pandaBoards;
4df91daf
MS
51MidiListener midiQwertyKeys;
52MidiListener midiQwertyAPC;
a898d79b
MS
53
54// Display configuration mode
bf551144 55boolean mappingMode = false;
cc9fcf4b 56boolean debugMode = false;
554e38ff 57DebugUI debugUI;
34327c96
MS
58boolean uiOn = true;
59LXPattern restoreToPattern = null;
4c640acc 60PImage logo;
d626bc9b 61
a898d79b 62// Handles to UI objects
d626bc9b
MS
63UIContext[] overlays;
64UIPatternDeck uiPatternA;
a898d79b 65UICrossfader uiCrossfader;
a8d55ade 66UIMidi uiMidi;
d626bc9b
MS
67UIMapping uiMapping;
68UIDebugText uiDebugText;
cc9fcf4b 69
0a9f99cc 70// Camera variables
bda5421d 71float eyeR, eyeA, eyeX, eyeY, eyeZ, midX, midY, midZ;
0a9f99cc 72
34327c96
MS
73/**
74 * Engine construction and initialization.
75 */
d626bc9b
MS
76LXPattern[] _patterns(GLucose glucose) {
77 LXPattern[] patterns = patterns(glucose);
78 for (LXPattern p : patterns) {
79 p.setTransition(new DissolveTransition(glucose.lx).setDuration(1000));
80 }
81 return patterns;
82}
83
34327c96
MS
84void logTime(String evt) {
85 int now = millis();
86 println(evt + ": " + (now - lastMillis) + "ms");
87 lastMillis = now;
88}
89
49815cc0
MS
90void setup() {
91 startMillis = lastMillis = millis();
92
93 // Initialize the Processing graphics environment
94 size(VIEWPORT_WIDTH, VIEWPORT_HEIGHT, OPENGL);
0e3c5542 95 frameRate(targetFramerate);
3f8be614 96 noSmooth();
49815cc0
MS
97 // hint(ENABLE_OPENGL_4X_SMOOTH); // no discernable improvement?
98 logTime("Created viewport");
99
100 // Create the GLucose engine to run the cubes
186bc4d3 101 glucose = new GLucose(this, buildModel());
49815cc0 102 lx = glucose.lx;
cc9fcf4b 103 lx.enableKeyboardTempo();
49815cc0
MS
104 logTime("Built GLucose engine");
105
106 // Set the patterns
d626bc9b 107 Engine engine = lx.engine;
d626bc9b
MS
108 engine.setPatterns(patterns = _patterns(glucose));
109 engine.addDeck(_patterns(glucose));
49815cc0 110 logTime("Built patterns");
a898d79b
MS
111 glucose.setTransitions(transitions(glucose));
112 logTime("Built transitions");
113 glucose.lx.addEffects(effects(glucose));
49815cc0 114 logTime("Built effects");
e73ef85d
MS
115
116 // Build output driver
186bc4d3 117 PandaMapping[] pandaMappings = buildPandaList();
45f43cc2
MS
118 pandaBoards = new PandaDriver[pandaMappings.length];
119 int pbi = 0;
120 for (PandaMapping pm : pandaMappings) {
44b8de9c 121 pandaBoards[pbi++] = new PandaDriver(pm.ip, glucose.model, pm);
45f43cc2
MS
122 }
123 mappingTool = new MappingTool(glucose, pandaMappings);
124 logTime("Built PandaDriver");
a8d55ade
MS
125
126 // MIDI devices
127 List<MidiListener> midiListeners = new ArrayList<MidiListener>();
4df91daf
MS
128 midiListeners.add(midiQwertyKeys = new MidiListener(MidiListener.KEYS));
129 midiListeners.add(midiQwertyAPC = new MidiListener(MidiListener.APC));
a8d55ade
MS
130 for (MidiInputDevice device : RWMidi.getInputDevices()) {
131 boolean enableDevice = device.getName().contains("APC");
132 midiListeners.add(new MidiListener(device).setEnabled(enableDevice));
133 }
134 SCMidiDevices.initializeStandardDevices(glucose);
135 logTime("Setup MIDI devices");
136
49815cc0 137 // Build overlay UI
45f43cc2 138 debugUI = new DebugUI(pandaMappings);
d626bc9b 139 overlays = new UIContext[] {
a8d55ade
MS
140 uiPatternA = new UIPatternDeck(lx.engine.getDeck(0), "PATTERN A", 4, 4, 140, 324),
141 new UIBlendMode(4, 332, 140, 86),
142 new UIEffects(4, 422, 140, 144),
143 new UITempo(4, 570, 140, 50),
144 new UISpeed(4, 624, 140, 50),
145
146 new UIPatternDeck(lx.engine.getDeck(1), "PATTERN B", width-144, 4, 140, 324),
4df91daf
MS
147 uiMidi = new UIMidi(midiListeners, width-144, 332, 140, 158),
148 new UIOutput(width-144, 494, 140, 106),
d626bc9b 149
a8d55ade 150 uiCrossfader = new UICrossfader(width/2-90, height-90, 180, 86),
d626bc9b 151
a8d55ade
MS
152 uiDebugText = new UIDebugText(148, height-138, width-304, 44),
153 uiMapping = new UIMapping(mappingTool, 4, 4, 140, 324),
d626bc9b
MS
154 };
155 uiMapping.setVisible(false);
49815cc0 156 logTime("Built overlay UI");
4c640acc
MS
157
158 // Load logo image
159 logo = loadImage("data/logo.png");
160
0a9f99cc 161 // Setup camera
d626bc9b 162 midX = TRAILER_WIDTH/2.;
0a9f99cc 163 midY = glucose.model.yMax/2;
e0cea600
MS
164 midZ = TRAILER_DEPTH/2.;
165 eyeR = -290;
0a9f99cc 166 eyeA = .15;
d626bc9b 167 eyeY = midY + 70;
0a9f99cc
MS
168 eyeX = midX + eyeR*sin(eyeA);
169 eyeZ = midZ + eyeR*cos(eyeA);
bda5421d
MS
170 addMouseWheelListener(new java.awt.event.MouseWheelListener() {
171 public void mouseWheelMoved(java.awt.event.MouseWheelEvent mwe) {
172 mouseWheel(mwe.getWheelRotation());
173 }});
174
49815cc0 175 println("Total setup: " + (millis() - startMillis) + "ms");
e73ef85d 176 println("Hit the 'p' key to toggle Panda Board output");
49815cc0
MS
177}
178
a8d55ade 179public class MidiListener extends AbstractScrollItem {
4df91daf
MS
180
181 public static final int MIDI = 0;
182 public static final int KEYS = 1;
183 public static final int APC = 2;
184
34327c96
MS
185 private boolean enabled = false;
186 private final String name;
187
188 MidiListener(MidiInputDevice d) {
4df91daf 189 mode = MIDI;
34327c96
MS
190 d.createInput(this);
191 name = d.getName();
192 }
193
a8d55ade
MS
194 class NoteMeta {
195 int channel;
196 int number;
197 NoteMeta(int channel, int number) {
198 this.channel = channel;
199 this.number = number;
200 }
201 }
202
203 final Map<Character, NoteMeta> keyToNote = new HashMap<Character, NoteMeta>();
204
4df91daf
MS
205 private final int mode;
206 private int octaveShift = 0;
207
208 MidiListener(int mode) {
209 this.mode = mode;
210 switch (mode) {
211 case APC:
212 name = "QWERTY (APC Mode)";
213 mapAPC();
214 break;
215 default:
216 case KEYS:
217 name = "QWERTY (Key Mode)";
218 mapKeys();
219 break;
220 }
221 }
222
223 private void mapAPC() {
a8d55ade
MS
224 mapNote('1', 0, 53);
225 mapNote('2', 1, 53);
226 mapNote('3', 2, 53);
227 mapNote('4', 3, 53);
228 mapNote('5', 4, 53);
229 mapNote('6', 5, 53);
230 mapNote('q', 0, 54);
231 mapNote('w', 1, 54);
232 mapNote('e', 2, 54);
233 mapNote('r', 3, 54);
234 mapNote('t', 4, 54);
235 mapNote('y', 5, 54);
236 mapNote('a', 0, 55);
237 mapNote('s', 1, 55);
238 mapNote('d', 2, 55);
239 mapNote('f', 3, 55);
240 mapNote('g', 4, 55);
241 mapNote('h', 5, 55);
242 mapNote('z', 0, 56);
243 mapNote('x', 1, 56);
244 mapNote('c', 2, 56);
245 mapNote('v', 3, 56);
246 mapNote('b', 4, 56);
247 mapNote('n', 5, 56);
34327c96 248 registerKeyEvent(this);
34327c96
MS
249 }
250
4df91daf
MS
251 private void mapKeys() {
252 int note = 48;
253 mapNote('a', 1, note++);
254 mapNote('w', 1, note++);
255 mapNote('s', 1, note++);
256 mapNote('e', 1, note++);
257 mapNote('d', 1, note++);
258 mapNote('f', 1, note++);
259 mapNote('t', 1, note++);
260 mapNote('g', 1, note++);
261 mapNote('y', 1, note++);
262 mapNote('h', 1, note++);
263 mapNote('u', 1, note++);
264 mapNote('j', 1, note++);
265 mapNote('k', 1, note++);
266 mapNote('o', 1, note++);
267 mapNote('l', 1, note++);
268 registerKeyEvent(this);
269 }
270
a8d55ade
MS
271 void mapNote(char ch, int channel, int number) {
272 keyToNote.put(ch, new NoteMeta(channel, number));
273 }
274
275 public String getLabel() {
34327c96
MS
276 return name;
277 }
278
279 public void keyEvent(KeyEvent e) {
4df91daf
MS
280 if (!enabled) {
281 return;
282 }
a8d55ade
MS
283 char c = Character.toLowerCase(e.getKeyChar());
284 NoteMeta nm = keyToNote.get(c);
285 if (nm != null) {
286 switch (e.getID()) {
287 case KeyEvent.KEY_PRESSED:
4df91daf 288 noteOnReceived(new Note(Note.NOTE_ON, nm.channel, nm.number + octaveShift*12, 127));
34327c96 289 break;
a8d55ade 290 case KeyEvent.KEY_RELEASED:
4df91daf
MS
291 noteOffReceived(new Note(Note.NOTE_OFF, nm.channel, nm.number + octaveShift*12, 0));
292 break;
293 }
294 }
295 if ((mode == KEYS) && (e.getID() == KeyEvent.KEY_PRESSED)) {
296 switch (c) {
297 case 'z':
298 octaveShift = constrain(octaveShift-1, -4, 4);
299 break;
300 case 'x':
301 octaveShift = constrain(octaveShift+1, -4, 4);
34327c96
MS
302 break;
303 }
304 }
305 }
306
a8d55ade
MS
307 public boolean isEnabled() {
308 return enabled;
309 }
310
311 public boolean isSelected() {
312 return enabled;
313 }
314
315 public void onMousePressed() {
316 setEnabled(!enabled);
317 }
318
34327c96
MS
319 public MidiListener setEnabled(boolean enabled) {
320 if (enabled != this.enabled) {
321 this.enabled = enabled;
a8d55ade 322 uiMidi.redraw();
34327c96
MS
323 }
324 return this;
325 }
326
a8d55ade
MS
327 private SCPattern getFocusedPattern() {
328 return (SCPattern) uiMidi.getFocusedDeck().getActivePattern();
329 }
330
331 void programChangeReceived(ProgramChange pc) {
332 if (!enabled) {
333 return;
334 }
9692dc7b
MS
335 if (uiMidi.logMidi()) {
336 println(getLabel() + " :: Program Change :: " + pc.getNumber());
337 }
a8d55ade
MS
338 }
339
34327c96
MS
340 void controllerChangeReceived(rwmidi.Controller cc) {
341 if (!enabled) {
342 return;
343 }
9692dc7b
MS
344 if (uiMidi.logMidi()) {
345 println(getLabel() + " :: Controller :: " + cc.getCC() + ":" + cc.getValue());
346 }
a8d55ade 347 getFocusedPattern().controllerChangeReceived(cc);
cc9fcf4b 348 }
cc9fcf4b 349
34327c96
MS
350 void noteOnReceived(Note note) {
351 if (!enabled) {
352 return;
353 }
9692dc7b
MS
354 if (uiMidi.logMidi()) {
355 println(getLabel() + " :: Note On :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
356 }
a8d55ade 357 getFocusedPattern().noteOnReceived(note);
cc9fcf4b 358 }
cc9fcf4b 359
34327c96
MS
360 void noteOffReceived(Note note) {
361 if (!enabled) {
362 return;
363 }
9692dc7b
MS
364 if (uiMidi.logMidi()) {
365 println(getLabel() + " :: Note Off :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
366 }
a8d55ade 367 getFocusedPattern().noteOffReceived(note);
cc9fcf4b 368 }
cc9fcf4b 369
49815cc0
MS
370}
371
34327c96
MS
372/**
373 * Core render loop and drawing functionality.
374 */
49815cc0 375void draw() {
0a9f99cc
MS
376 // Draws the simulation and the 2D UI overlay
377 background(40);
a898d79b
MS
378 color[] colors = glucose.getColors();
379
380 String displayMode = uiCrossfader.getDisplayMode();
d626bc9b
MS
381 if (displayMode == "A") {
382 colors = lx.engine.getDeck(0).getColors();
383 } else if (displayMode == "B") {
384 colors = lx.engine.getDeck(1).getColors();
385 }
554e38ff
MS
386 if (debugMode) {
387 debugUI.maskColors(colors);
388 }
51d0d59a 389
0a9f99cc
MS
390 camera(
391 eyeX, eyeY, eyeZ,
392 midX, midY, midZ,
393 0, -1, 0
394 );
51d0d59a 395
a8d55ade 396 translate(0, 40, 0);
d626bc9b 397
51d0d59a
MS
398 noStroke();
399 fill(#141414);
87998ff3 400 drawBox(0, -TRAILER_HEIGHT, 0, 0, 0, 0, TRAILER_WIDTH, TRAILER_HEIGHT, TRAILER_DEPTH, TRAILER_HEIGHT/2.);
51d0d59a
MS
401 fill(#070707);
402 stroke(#222222);
0a9f99cc 403 beginShape();
51d0d59a 404 vertex(0, 0, 0);
87998ff3
MS
405 vertex(TRAILER_WIDTH, 0, 0);
406 vertex(TRAILER_WIDTH, 0, TRAILER_DEPTH);
407 vertex(0, 0, TRAILER_DEPTH);
51d0d59a 408 endShape();
4c640acc
MS
409
410 // Draw the logo on the front of platform
411 pushMatrix();
412 translate(0, 0, -1);
413 float s = .07;
414 scale(s, -s, s);
415 image(logo, TRAILER_WIDTH/2/s-logo.width/2, TRAILER_HEIGHT/2/s-logo.height/2-2/s);
416 popMatrix();
0a9f99cc 417
51d0d59a 418 noStroke();
ab77005f
MS
419// drawBassBox(glucose.model.bassBox);
420// for (Speaker s : glucose.model.speakers) {
421// drawSpeaker(s);
422// }
51d0d59a
MS
423 for (Cube c : glucose.model.cubes) {
424 drawCube(c);
425 }
426
0a9f99cc
MS
427 noFill();
428 strokeWeight(2);
429 beginShape(POINTS);
922def98
MS
430 // TODO(mcslee): restore when bassBox/speakers are right again
431 // for (Point p : glucose.model.points) {
432 for (Cube cube : glucose.model.cubes) {
433 for (Point p : cube.points) {
434 stroke(colors[p.index]);
435 vertex(p.fx, p.fy, p.fz);
436 }
0a9f99cc
MS
437 }
438 endShape();
439
d626bc9b 440 // 2D Overlay UI
0a9f99cc 441 drawUI();
d626bc9b
MS
442
443 // Send output colors
444 color[] sendColors = glucose.getColors();
554e38ff 445 if (debugMode) {
d626bc9b 446 debugUI.maskColors(colors);
554e38ff 447 }
a797d019 448
6702151a
MS
449 // Gamma correction here. Apply a cubic to the brightness
450 // for better representation of dynamic range
451 for (int i = 0; i < colors.length; ++i) {
452 float b = brightness(colors[i]) / 100.f;
453 colors[i] = color(
454 hue(colors[i]),
455 saturation(colors[i]),
456 (b*b*b) * 100.
457 );
458 }
e7f14d4d 459
e73ef85d 460 // TODO(mcslee): move into GLucose engine
79ae8245 461 for (PandaDriver p : pandaBoards) {
b58e5a1d 462 p.send(colors);
e73ef85d 463 }
49815cc0
MS
464}
465
92c06c97
MS
466void drawBassBox(BassBox b) {
467 float in = .15;
e76480d4
MS
468
469 noStroke();
470 fill(#191919);
471 pushMatrix();
472 translate(b.x + BassBox.EDGE_WIDTH/2., b.y + BassBox.EDGE_HEIGHT/2, b.z + BassBox.EDGE_DEPTH/2.);
473 box(BassBox.EDGE_WIDTH-20*in, BassBox.EDGE_HEIGHT-20*in, BassBox.EDGE_DEPTH-20*in);
474 popMatrix();
475
476 noStroke();
477 fill(#393939);
92c06c97
MS
478 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);
479
39011e7e
MS
480 pushMatrix();
481 translate(b.x+(Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT-in, b.z + BassBox.EDGE_DEPTH/2.);
482 float lastOffset = 0;
483 for (float offset : BoothFloor.STRIP_OFFSETS) {
484 translate(offset - lastOffset, 0, 0);
485 box(Cube.CHANNEL_WIDTH-in, 0, BassBox.EDGE_DEPTH - 2*in);
486 lastOffset = offset;
487 }
488 popMatrix();
489
92c06c97
MS
490 pushMatrix();
491 translate(b.x + (Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT/2., b.z + in);
492 for (int j = 0; j < 2; ++j) {
493 pushMatrix();
494 for (int i = 0; i < BassBox.NUM_FRONT_STRUTS; ++i) {
495 translate(BassBox.FRONT_STRUT_SPACING, 0, 0);
496 box(Cube.CHANNEL_WIDTH-in, BassBox.EDGE_HEIGHT - in*2, 0);
497 }
498 popMatrix();
499 translate(0, 0, BassBox.EDGE_DEPTH - 2*in);
500 }
501 popMatrix();
502
503 pushMatrix();
504 translate(b.x + in, b.y + BassBox.EDGE_HEIGHT/2., b.z + BassBox.SIDE_STRUT_SPACING + (Cube.CHANNEL_WIDTH-in)/2.);
505 box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
506 translate(BassBox.EDGE_WIDTH-2*in, 0, 0);
507 box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
ab77005f 508 popMatrix();
e76480d4 509
92c06c97
MS
510}
511
51d0d59a 512void drawCube(Cube c) {
254d34c0 513 float in = .15;
e76480d4
MS
514 noStroke();
515 fill(#393939);
254d34c0 516 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);
51d0d59a
MS
517}
518
e76480d4
MS
519void drawSpeaker(Speaker s) {
520 float in = .15;
521
522 noStroke();
523 fill(#191919);
524 pushMatrix();
525 translate(s.x, s.y, s.z);
526 rotate(s.ry / 180. * PI, 0, -1, 0);
527 translate(Speaker.EDGE_WIDTH/2., Speaker.EDGE_HEIGHT/2., Speaker.EDGE_DEPTH/2.);
528 box(Speaker.EDGE_WIDTH-20*in, Speaker.EDGE_HEIGHT-20*in, Speaker.EDGE_DEPTH-20*in);
254fbb68
MS
529 translate(0, Speaker.EDGE_HEIGHT/2. + Speaker.EDGE_HEIGHT*.8/2, 0);
530
531 fill(#222222);
532 box(Speaker.EDGE_WIDTH*.6, Speaker.EDGE_HEIGHT*.8, Speaker.EDGE_DEPTH*.75);
e76480d4
MS
533 popMatrix();
534
e76480d4
MS
535 noStroke();
536 fill(#393939);
537 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);
ab77005f 538}
e76480d4 539
51d0d59a
MS
540void drawBox(float x, float y, float z, float rx, float ry, float rz, float xd, float yd, float zd, float sw) {
541 pushMatrix();
542 translate(x, y, z);
e0cea600 543 rotate(rx / 180. * PI, -1, 0, 0);
51d0d59a 544 rotate(ry / 180. * PI, 0, -1, 0);
e0cea600 545 rotate(rz / 180. * PI, 0, 0, -1);
51d0d59a
MS
546 for (int i = 0; i < 4; ++i) {
547 float wid = (i % 2 == 0) ? xd : zd;
548
549 beginShape();
550 vertex(0, 0);
551 vertex(wid, 0);
552 vertex(wid, yd);
553 vertex(wid - sw, yd);
554 vertex(wid - sw, sw);
555 vertex(0, sw);
556 endShape();
557 beginShape();
558 vertex(0, sw);
559 vertex(0, yd);
560 vertex(wid - sw, yd);
561 vertex(wid - sw, yd - sw);
562 vertex(sw, yd - sw);
563 vertex(sw, sw);
564 endShape();
565
566 translate(wid, 0, 0);
567 rotate(HALF_PI, 0, -1, 0);
568 }
569 popMatrix();
570}
571
49815cc0 572void drawUI() {
d626bc9b
MS
573 camera();
574 javax.media.opengl.GL gl = ((PGraphicsOpenGL)g).beginGL();
575 gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT);
576 ((PGraphicsOpenGL)g).endGL();
577 strokeWeight(1);
578
49815cc0 579 if (uiOn) {
d626bc9b
MS
580 for (UIContext context : overlays) {
581 context.draw();
582 }
583 }
584
585 // Always draw FPS meter
586 fill(#555555);
587 textSize(9);
588 textAlign(LEFT, BASELINE);
589 text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4);
590
591 if (debugMode) {
592 debugUI.draw();
49815cc0 593 }
49815cc0
MS
594}
595
4c640acc 596
34327c96
MS
597/**
598 * Top-level keyboard event handling
599 */
49815cc0 600void keyPressed() {
bf551144 601 if (mappingMode) {
d626bc9b 602 mappingTool.keyPressed(uiMapping);
bf551144 603 }
3f8be614 604 switch (key) {
0e3c5542
MS
605 case '-':
606 case '_':
607 frameRate(--targetFramerate);
608 break;
609 case '=':
610 case '+':
611 frameRate(++targetFramerate);
4c640acc 612 break;
cc9fcf4b 613 case 'd':
4df91daf 614 if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
a8d55ade
MS
615 debugMode = !debugMode;
616 println("Debug output: " + (debugMode ? "ON" : "OFF"));
617 }
554e38ff 618 break;
bf551144 619 case 'm':
4df91daf 620 if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
a8d55ade
MS
621 mappingMode = !mappingMode;
622 uiPatternA.setVisible(!mappingMode);
623 uiMapping.setVisible(mappingMode);
624 if (mappingMode) {
625 restoreToPattern = lx.getPattern();
626 lx.setPatterns(new LXPattern[] { mappingTool });
627 } else {
628 lx.setPatterns(patterns);
629 LXTransition pop = restoreToPattern.getTransition();
630 restoreToPattern.setTransition(null);
631 lx.goPattern(restoreToPattern);
632 restoreToPattern.setTransition(pop);
633 }
bf551144
MS
634 }
635 break;
e73ef85d 636 case 'p':
79ae8245
MS
637 for (PandaDriver p : pandaBoards) {
638 p.toggle();
639 }
cc9fcf4b 640 break;
3f8be614 641 case 'u':
4df91daf
MS
642 if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
643 uiOn = !uiOn;
644 }
3f8be614 645 break;
49815cc0
MS
646 }
647}
648
34327c96
MS
649/**
650 * Top-level mouse event handling
651 */
0a9f99cc 652int mx, my;
0a9f99cc 653void mousePressed() {
d626bc9b
MS
654 boolean debugged = false;
655 if (debugMode) {
656 debugged = debugUI.mousePressed();
657 }
658 if (!debugged) {
659 for (UIContext context : overlays) {
660 context.mousePressed(mouseX, mouseY);
661 }
0a9f99cc 662 }
d626bc9b
MS
663 mx = mouseX;
664 my = mouseY;
0a9f99cc
MS
665}
666
667void mouseDragged() {
d626bc9b
MS
668 boolean dragged = false;
669 for (UIContext context : overlays) {
670 dragged |= context.mouseDragged(mouseX, mouseY);
671 }
672 if (!dragged) {
0a9f99cc
MS
673 int dx = mouseX - mx;
674 int dy = mouseY - my;
675 mx = mouseX;
676 my = mouseY;
677 eyeA += dx*.003;
678 eyeX = midX + eyeR*sin(eyeA);
679 eyeZ = midZ + eyeR*cos(eyeA);
680 eyeY += dy;
681 }
682}
683
684void mouseReleased() {
d626bc9b
MS
685 for (UIContext context : overlays) {
686 context.mouseReleased(mouseX, mouseY);
687 }
0a9f99cc 688}
bda5421d
MS
689
690void mouseWheel(int delta) {
d626bc9b
MS
691 boolean wheeled = false;
692 for (UIContext context : overlays) {
693 wheeled |= context.mouseWheel(mouseX, mouseY, delta);
694 }
695
696 if (!wheeled) {
0ba6ac44
MS
697 eyeR = constrain(eyeR - delta, -500, -80);
698 eyeX = midX + eyeR*sin(eyeA);
699 eyeZ = midZ + eyeR*cos(eyeA);
700 }
bda5421d 701}