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