more bug fixes in dpat
[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;
30bf6510
MS
51SCMidiInput midiQwertyKeys;
52SCMidiInput 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
30bf6510
MS
127 List<SCMidiInput> midiControllers = new ArrayList<SCMidiInput>();
128 midiControllers.add(midiQwertyKeys = new SCMidiInput(SCMidiInput.KEYS));
129 midiControllers.add(midiQwertyAPC = new SCMidiInput(SCMidiInput.APC));
a8d55ade
MS
130 for (MidiInputDevice device : RWMidi.getInputDevices()) {
131 boolean enableDevice = device.getName().contains("APC");
30bf6510 132 midiControllers.add(new SCMidiInput(device).setEnabled(enableDevice));
a8d55ade
MS
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),
30bf6510 147 uiMidi = new UIMidi(midiControllers, width-144, 332, 140, 158),
4df91daf 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
30bf6510
MS
179public interface SCMidiInputListener {
180 public void onEnabled(SCMidiInput controller, boolean enabled);
181}
182
183public class SCMidiInput extends AbstractScrollItem {
4df91daf
MS
184
185 public static final int MIDI = 0;
186 public static final int KEYS = 1;
187 public static final int APC = 2;
188
34327c96
MS
189 private boolean enabled = false;
190 private final String name;
30bf6510
MS
191 private final int mode;
192 private int octaveShift = 0;
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>();
30bf6510
MS
204
205 final List<SCMidiInputListener> listeners = new ArrayList<SCMidiInputListener>();
a8d55ade 206
30bf6510
MS
207 public SCMidiInput addListener(SCMidiInputListener l) {
208 listeners.add(l);
209 return this;
210 }
211
212 public SCMidiInput removeListener(SCMidiInputListener l) {
213 listeners.remove(l);
214 return this;
215 }
4df91daf 216
30bf6510
MS
217 SCMidiInput(MidiInputDevice d) {
218 mode = MIDI;
219 d.createInput(this);
ec80ebfd 220 name = d.getName().replace("Unknown vendor","");
30bf6510
MS
221 }
222
223 SCMidiInput(int mode) {
4df91daf
MS
224 this.mode = mode;
225 switch (mode) {
226 case APC:
227 name = "QWERTY (APC Mode)";
228 mapAPC();
229 break;
230 default:
231 case KEYS:
232 name = "QWERTY (Key Mode)";
233 mapKeys();
234 break;
235 }
236 }
237
238 private void mapAPC() {
a8d55ade
MS
239 mapNote('1', 0, 53);
240 mapNote('2', 1, 53);
241 mapNote('3', 2, 53);
242 mapNote('4', 3, 53);
243 mapNote('5', 4, 53);
244 mapNote('6', 5, 53);
245 mapNote('q', 0, 54);
246 mapNote('w', 1, 54);
247 mapNote('e', 2, 54);
248 mapNote('r', 3, 54);
249 mapNote('t', 4, 54);
250 mapNote('y', 5, 54);
251 mapNote('a', 0, 55);
252 mapNote('s', 1, 55);
253 mapNote('d', 2, 55);
254 mapNote('f', 3, 55);
255 mapNote('g', 4, 55);
256 mapNote('h', 5, 55);
257 mapNote('z', 0, 56);
258 mapNote('x', 1, 56);
259 mapNote('c', 2, 56);
260 mapNote('v', 3, 56);
261 mapNote('b', 4, 56);
262 mapNote('n', 5, 56);
34327c96 263 registerKeyEvent(this);
34327c96
MS
264 }
265
4df91daf
MS
266 private void mapKeys() {
267 int note = 48;
268 mapNote('a', 1, note++);
269 mapNote('w', 1, note++);
270 mapNote('s', 1, note++);
271 mapNote('e', 1, note++);
272 mapNote('d', 1, note++);
273 mapNote('f', 1, note++);
274 mapNote('t', 1, note++);
275 mapNote('g', 1, note++);
276 mapNote('y', 1, note++);
277 mapNote('h', 1, note++);
278 mapNote('u', 1, note++);
279 mapNote('j', 1, note++);
280 mapNote('k', 1, note++);
281 mapNote('o', 1, note++);
282 mapNote('l', 1, note++);
283 registerKeyEvent(this);
284 }
285
a8d55ade
MS
286 void mapNote(char ch, int channel, int number) {
287 keyToNote.put(ch, new NoteMeta(channel, number));
288 }
289
290 public String getLabel() {
34327c96
MS
291 return name;
292 }
293
294 public void keyEvent(KeyEvent e) {
4df91daf
MS
295 if (!enabled) {
296 return;
297 }
a8d55ade
MS
298 char c = Character.toLowerCase(e.getKeyChar());
299 NoteMeta nm = keyToNote.get(c);
300 if (nm != null) {
301 switch (e.getID()) {
302 case KeyEvent.KEY_PRESSED:
4df91daf 303 noteOnReceived(new Note(Note.NOTE_ON, nm.channel, nm.number + octaveShift*12, 127));
34327c96 304 break;
a8d55ade 305 case KeyEvent.KEY_RELEASED:
4df91daf
MS
306 noteOffReceived(new Note(Note.NOTE_OFF, nm.channel, nm.number + octaveShift*12, 0));
307 break;
308 }
309 }
310 if ((mode == KEYS) && (e.getID() == KeyEvent.KEY_PRESSED)) {
311 switch (c) {
312 case 'z':
313 octaveShift = constrain(octaveShift-1, -4, 4);
314 break;
315 case 'x':
316 octaveShift = constrain(octaveShift+1, -4, 4);
34327c96
MS
317 break;
318 }
319 }
320 }
321
a8d55ade
MS
322 public boolean isEnabled() {
323 return enabled;
324 }
325
326 public boolean isSelected() {
327 return enabled;
328 }
329
330 public void onMousePressed() {
331 setEnabled(!enabled);
332 }
333
30bf6510 334 public SCMidiInput setEnabled(boolean enabled) {
34327c96
MS
335 if (enabled != this.enabled) {
336 this.enabled = enabled;
30bf6510
MS
337 for (SCMidiInputListener l : listeners) {
338 l.onEnabled(this, enabled);
339 }
34327c96
MS
340 }
341 return this;
342 }
343
a8d55ade 344 private SCPattern getFocusedPattern() {
30bf6510
MS
345 Engine.Deck focusedDeck = (uiMidi != null) ? uiMidi.getFocusedDeck() : lx.engine.getDefaultDeck();
346 return (SCPattern) focusedDeck.getActivePattern();
347 }
348
349 private boolean logMidi() {
350 return (uiMidi != null) && uiMidi.logMidi();
a8d55ade
MS
351 }
352
353 void programChangeReceived(ProgramChange pc) {
354 if (!enabled) {
355 return;
356 }
30bf6510 357 if (logMidi()) {
9692dc7b
MS
358 println(getLabel() + " :: Program Change :: " + pc.getNumber());
359 }
a8d55ade
MS
360 }
361
34327c96
MS
362 void controllerChangeReceived(rwmidi.Controller cc) {
363 if (!enabled) {
364 return;
365 }
30bf6510 366 if (logMidi()) {
9692dc7b
MS
367 println(getLabel() + " :: Controller :: " + cc.getCC() + ":" + cc.getValue());
368 }
a8d55ade 369 getFocusedPattern().controllerChangeReceived(cc);
cc9fcf4b 370 }
cc9fcf4b 371
34327c96
MS
372 void noteOnReceived(Note note) {
373 if (!enabled) {
374 return;
375 }
30bf6510 376 if (logMidi()) {
9692dc7b
MS
377 println(getLabel() + " :: Note On :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
378 }
a8d55ade 379 getFocusedPattern().noteOnReceived(note);
cc9fcf4b 380 }
cc9fcf4b 381
34327c96
MS
382 void noteOffReceived(Note note) {
383 if (!enabled) {
384 return;
385 }
30bf6510 386 if (logMidi()) {
9692dc7b
MS
387 println(getLabel() + " :: Note Off :: " + note.getChannel() + ":" + note.getPitch() + ":" + note.getVelocity());
388 }
a8d55ade 389 getFocusedPattern().noteOffReceived(note);
cc9fcf4b 390 }
cc9fcf4b 391
49815cc0
MS
392}
393
34327c96
MS
394/**
395 * Core render loop and drawing functionality.
396 */
49815cc0 397void draw() {
0a9f99cc
MS
398 // Draws the simulation and the 2D UI overlay
399 background(40);
a898d79b
MS
400 color[] colors = glucose.getColors();
401
402 String displayMode = uiCrossfader.getDisplayMode();
d626bc9b
MS
403 if (displayMode == "A") {
404 colors = lx.engine.getDeck(0).getColors();
405 } else if (displayMode == "B") {
406 colors = lx.engine.getDeck(1).getColors();
407 }
554e38ff
MS
408 if (debugMode) {
409 debugUI.maskColors(colors);
410 }
51d0d59a 411
0a9f99cc
MS
412 camera(
413 eyeX, eyeY, eyeZ,
414 midX, midY, midZ,
415 0, -1, 0
416 );
51d0d59a 417
a8d55ade 418 translate(0, 40, 0);
d626bc9b 419
51d0d59a
MS
420 noStroke();
421 fill(#141414);
87998ff3 422 drawBox(0, -TRAILER_HEIGHT, 0, 0, 0, 0, TRAILER_WIDTH, TRAILER_HEIGHT, TRAILER_DEPTH, TRAILER_HEIGHT/2.);
51d0d59a
MS
423 fill(#070707);
424 stroke(#222222);
0a9f99cc 425 beginShape();
51d0d59a 426 vertex(0, 0, 0);
87998ff3
MS
427 vertex(TRAILER_WIDTH, 0, 0);
428 vertex(TRAILER_WIDTH, 0, TRAILER_DEPTH);
429 vertex(0, 0, TRAILER_DEPTH);
51d0d59a 430 endShape();
4c640acc
MS
431
432 // Draw the logo on the front of platform
433 pushMatrix();
434 translate(0, 0, -1);
435 float s = .07;
436 scale(s, -s, s);
437 image(logo, TRAILER_WIDTH/2/s-logo.width/2, TRAILER_HEIGHT/2/s-logo.height/2-2/s);
438 popMatrix();
0a9f99cc 439
51d0d59a 440 noStroke();
ab77005f
MS
441// drawBassBox(glucose.model.bassBox);
442// for (Speaker s : glucose.model.speakers) {
443// drawSpeaker(s);
444// }
51d0d59a
MS
445 for (Cube c : glucose.model.cubes) {
446 drawCube(c);
447 }
448
0a9f99cc
MS
449 noFill();
450 strokeWeight(2);
451 beginShape(POINTS);
922def98
MS
452 // TODO(mcslee): restore when bassBox/speakers are right again
453 // for (Point p : glucose.model.points) {
454 for (Cube cube : glucose.model.cubes) {
455 for (Point p : cube.points) {
456 stroke(colors[p.index]);
457 vertex(p.fx, p.fy, p.fz);
458 }
0a9f99cc
MS
459 }
460 endShape();
461
d626bc9b 462 // 2D Overlay UI
0a9f99cc 463 drawUI();
d626bc9b
MS
464
465 // Send output colors
466 color[] sendColors = glucose.getColors();
554e38ff 467 if (debugMode) {
d626bc9b 468 debugUI.maskColors(colors);
554e38ff 469 }
a797d019 470
6702151a
MS
471 // Gamma correction here. Apply a cubic to the brightness
472 // for better representation of dynamic range
473 for (int i = 0; i < colors.length; ++i) {
474 float b = brightness(colors[i]) / 100.f;
475 colors[i] = color(
476 hue(colors[i]),
477 saturation(colors[i]),
478 (b*b*b) * 100.
479 );
480 }
e7f14d4d 481
e73ef85d 482 // TODO(mcslee): move into GLucose engine
79ae8245 483 for (PandaDriver p : pandaBoards) {
b58e5a1d 484 p.send(colors);
e73ef85d 485 }
49815cc0
MS
486}
487
92c06c97
MS
488void drawBassBox(BassBox b) {
489 float in = .15;
e76480d4
MS
490
491 noStroke();
492 fill(#191919);
493 pushMatrix();
494 translate(b.x + BassBox.EDGE_WIDTH/2., b.y + BassBox.EDGE_HEIGHT/2, b.z + BassBox.EDGE_DEPTH/2.);
495 box(BassBox.EDGE_WIDTH-20*in, BassBox.EDGE_HEIGHT-20*in, BassBox.EDGE_DEPTH-20*in);
496 popMatrix();
497
498 noStroke();
499 fill(#393939);
92c06c97
MS
500 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);
501
39011e7e
MS
502 pushMatrix();
503 translate(b.x+(Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT-in, b.z + BassBox.EDGE_DEPTH/2.);
504 float lastOffset = 0;
505 for (float offset : BoothFloor.STRIP_OFFSETS) {
506 translate(offset - lastOffset, 0, 0);
507 box(Cube.CHANNEL_WIDTH-in, 0, BassBox.EDGE_DEPTH - 2*in);
508 lastOffset = offset;
509 }
510 popMatrix();
511
92c06c97
MS
512 pushMatrix();
513 translate(b.x + (Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT/2., b.z + in);
514 for (int j = 0; j < 2; ++j) {
515 pushMatrix();
516 for (int i = 0; i < BassBox.NUM_FRONT_STRUTS; ++i) {
517 translate(BassBox.FRONT_STRUT_SPACING, 0, 0);
518 box(Cube.CHANNEL_WIDTH-in, BassBox.EDGE_HEIGHT - in*2, 0);
519 }
520 popMatrix();
521 translate(0, 0, BassBox.EDGE_DEPTH - 2*in);
522 }
523 popMatrix();
524
525 pushMatrix();
526 translate(b.x + in, b.y + BassBox.EDGE_HEIGHT/2., b.z + BassBox.SIDE_STRUT_SPACING + (Cube.CHANNEL_WIDTH-in)/2.);
527 box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
528 translate(BassBox.EDGE_WIDTH-2*in, 0, 0);
529 box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
ab77005f 530 popMatrix();
e76480d4 531
92c06c97
MS
532}
533
51d0d59a 534void drawCube(Cube c) {
254d34c0 535 float in = .15;
e76480d4
MS
536 noStroke();
537 fill(#393939);
254d34c0 538 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
539}
540
e76480d4
MS
541void drawSpeaker(Speaker s) {
542 float in = .15;
543
544 noStroke();
545 fill(#191919);
546 pushMatrix();
547 translate(s.x, s.y, s.z);
548 rotate(s.ry / 180. * PI, 0, -1, 0);
549 translate(Speaker.EDGE_WIDTH/2., Speaker.EDGE_HEIGHT/2., Speaker.EDGE_DEPTH/2.);
550 box(Speaker.EDGE_WIDTH-20*in, Speaker.EDGE_HEIGHT-20*in, Speaker.EDGE_DEPTH-20*in);
254fbb68
MS
551 translate(0, Speaker.EDGE_HEIGHT/2. + Speaker.EDGE_HEIGHT*.8/2, 0);
552
553 fill(#222222);
554 box(Speaker.EDGE_WIDTH*.6, Speaker.EDGE_HEIGHT*.8, Speaker.EDGE_DEPTH*.75);
e76480d4
MS
555 popMatrix();
556
e76480d4
MS
557 noStroke();
558 fill(#393939);
559 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 560}
e76480d4 561
51d0d59a
MS
562void drawBox(float x, float y, float z, float rx, float ry, float rz, float xd, float yd, float zd, float sw) {
563 pushMatrix();
564 translate(x, y, z);
e0cea600 565 rotate(rx / 180. * PI, -1, 0, 0);
51d0d59a 566 rotate(ry / 180. * PI, 0, -1, 0);
e0cea600 567 rotate(rz / 180. * PI, 0, 0, -1);
51d0d59a
MS
568 for (int i = 0; i < 4; ++i) {
569 float wid = (i % 2 == 0) ? xd : zd;
570
571 beginShape();
572 vertex(0, 0);
573 vertex(wid, 0);
574 vertex(wid, yd);
575 vertex(wid - sw, yd);
576 vertex(wid - sw, sw);
577 vertex(0, sw);
578 endShape();
579 beginShape();
580 vertex(0, sw);
581 vertex(0, yd);
582 vertex(wid - sw, yd);
583 vertex(wid - sw, yd - sw);
584 vertex(sw, yd - sw);
585 vertex(sw, sw);
586 endShape();
587
588 translate(wid, 0, 0);
589 rotate(HALF_PI, 0, -1, 0);
590 }
591 popMatrix();
592}
593
49815cc0 594void drawUI() {
d626bc9b
MS
595 camera();
596 javax.media.opengl.GL gl = ((PGraphicsOpenGL)g).beginGL();
597 gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT);
598 ((PGraphicsOpenGL)g).endGL();
599 strokeWeight(1);
600
49815cc0 601 if (uiOn) {
d626bc9b
MS
602 for (UIContext context : overlays) {
603 context.draw();
604 }
605 }
606
607 // Always draw FPS meter
608 fill(#555555);
609 textSize(9);
610 textAlign(LEFT, BASELINE);
611 text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4);
612
613 if (debugMode) {
614 debugUI.draw();
49815cc0 615 }
49815cc0
MS
616}
617
4c640acc 618
34327c96
MS
619/**
620 * Top-level keyboard event handling
621 */
49815cc0 622void keyPressed() {
bf551144 623 if (mappingMode) {
d626bc9b 624 mappingTool.keyPressed(uiMapping);
bf551144 625 }
3f8be614 626 switch (key) {
0e3c5542
MS
627 case '-':
628 case '_':
629 frameRate(--targetFramerate);
630 break;
631 case '=':
632 case '+':
633 frameRate(++targetFramerate);
4c640acc 634 break;
cc9fcf4b 635 case 'd':
4df91daf 636 if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
a8d55ade
MS
637 debugMode = !debugMode;
638 println("Debug output: " + (debugMode ? "ON" : "OFF"));
639 }
554e38ff 640 break;
bf551144 641 case 'm':
4df91daf 642 if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
a8d55ade
MS
643 mappingMode = !mappingMode;
644 uiPatternA.setVisible(!mappingMode);
645 uiMapping.setVisible(mappingMode);
646 if (mappingMode) {
647 restoreToPattern = lx.getPattern();
648 lx.setPatterns(new LXPattern[] { mappingTool });
649 } else {
650 lx.setPatterns(patterns);
651 LXTransition pop = restoreToPattern.getTransition();
652 restoreToPattern.setTransition(null);
653 lx.goPattern(restoreToPattern);
654 restoreToPattern.setTransition(pop);
655 }
bf551144
MS
656 }
657 break;
e73ef85d 658 case 'p':
79ae8245
MS
659 for (PandaDriver p : pandaBoards) {
660 p.toggle();
661 }
cc9fcf4b 662 break;
3f8be614 663 case 'u':
4df91daf
MS
664 if (!midiQwertyAPC.isEnabled() && !midiQwertyKeys.isEnabled()) {
665 uiOn = !uiOn;
666 }
3f8be614 667 break;
49815cc0
MS
668 }
669}
670
34327c96
MS
671/**
672 * Top-level mouse event handling
673 */
0a9f99cc 674int mx, my;
0a9f99cc 675void mousePressed() {
d626bc9b
MS
676 boolean debugged = false;
677 if (debugMode) {
678 debugged = debugUI.mousePressed();
679 }
680 if (!debugged) {
681 for (UIContext context : overlays) {
682 context.mousePressed(mouseX, mouseY);
683 }
0a9f99cc 684 }
d626bc9b
MS
685 mx = mouseX;
686 my = mouseY;
0a9f99cc
MS
687}
688
689void mouseDragged() {
d626bc9b
MS
690 boolean dragged = false;
691 for (UIContext context : overlays) {
692 dragged |= context.mouseDragged(mouseX, mouseY);
693 }
694 if (!dragged) {
0a9f99cc
MS
695 int dx = mouseX - mx;
696 int dy = mouseY - my;
697 mx = mouseX;
698 my = mouseY;
699 eyeA += dx*.003;
700 eyeX = midX + eyeR*sin(eyeA);
701 eyeZ = midZ + eyeR*cos(eyeA);
702 eyeY += dy;
703 }
704}
705
706void mouseReleased() {
d626bc9b
MS
707 for (UIContext context : overlays) {
708 context.mouseReleased(mouseX, mouseY);
709 }
0a9f99cc 710}
bda5421d
MS
711
712void mouseWheel(int delta) {
d626bc9b
MS
713 boolean wheeled = false;
714 for (UIContext context : overlays) {
715 wheeled |= context.mouseWheel(mouseX, mouseY, delta);
716 }
717
718 if (!wheeled) {
0ba6ac44
MS
719 eyeR = constrain(eyeR - delta, -500, -80);
720 eyeX = midX + eyeR*sin(eyeA);
721 eyeZ = midZ + eyeR*cos(eyeA);
722 }
bda5421d 723}