Add MIDI inputs to UI, with focused pattern deck to route input
[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),
144 uiMidi = new UIMidi(midiListeners, width-144, 332, 140, 136),
145 new UIOutput(width-144, 472, 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 }
273 println("PC: " + pc.toString());
274 }
275
34327c96
MS
276 void controllerChangeReceived(rwmidi.Controller cc) {
277 if (!enabled) {
278 return;
279 }
cc9fcf4b 280 println("CC: " + cc.toString());
a8d55ade 281 getFocusedPattern().controllerChangeReceived(cc);
cc9fcf4b 282 }
cc9fcf4b 283
34327c96
MS
284 void noteOnReceived(Note note) {
285 if (!enabled) {
286 return;
287 }
cc9fcf4b 288 println("Note On: " + note.toString());
a8d55ade
MS
289 getFocusedPattern().noteOnReceived(note);
290
cc9fcf4b 291 }
cc9fcf4b 292
34327c96
MS
293 void noteOffReceived(Note note) {
294 if (!enabled) {
295 return;
296 }
cc9fcf4b 297 println("Note Off: " + note.toString());
a8d55ade 298 getFocusedPattern().noteOffReceived(note);
cc9fcf4b 299 }
cc9fcf4b 300
49815cc0
MS
301}
302
34327c96
MS
303/**
304 * Core render loop and drawing functionality.
305 */
49815cc0 306void draw() {
0a9f99cc
MS
307 // Draws the simulation and the 2D UI overlay
308 background(40);
a898d79b
MS
309 color[] colors = glucose.getColors();
310
311 String displayMode = uiCrossfader.getDisplayMode();
d626bc9b
MS
312 if (displayMode == "A") {
313 colors = lx.engine.getDeck(0).getColors();
314 } else if (displayMode == "B") {
315 colors = lx.engine.getDeck(1).getColors();
316 }
554e38ff
MS
317 if (debugMode) {
318 debugUI.maskColors(colors);
319 }
51d0d59a 320
0a9f99cc
MS
321 camera(
322 eyeX, eyeY, eyeZ,
323 midX, midY, midZ,
324 0, -1, 0
325 );
51d0d59a 326
a8d55ade 327 translate(0, 40, 0);
d626bc9b 328
51d0d59a
MS
329 noStroke();
330 fill(#141414);
87998ff3 331 drawBox(0, -TRAILER_HEIGHT, 0, 0, 0, 0, TRAILER_WIDTH, TRAILER_HEIGHT, TRAILER_DEPTH, TRAILER_HEIGHT/2.);
51d0d59a
MS
332 fill(#070707);
333 stroke(#222222);
0a9f99cc 334 beginShape();
51d0d59a 335 vertex(0, 0, 0);
87998ff3
MS
336 vertex(TRAILER_WIDTH, 0, 0);
337 vertex(TRAILER_WIDTH, 0, TRAILER_DEPTH);
338 vertex(0, 0, TRAILER_DEPTH);
51d0d59a 339 endShape();
0a9f99cc 340
51d0d59a 341 noStroke();
ab77005f
MS
342// drawBassBox(glucose.model.bassBox);
343// for (Speaker s : glucose.model.speakers) {
344// drawSpeaker(s);
345// }
51d0d59a
MS
346 for (Cube c : glucose.model.cubes) {
347 drawCube(c);
348 }
349
0a9f99cc
MS
350 noFill();
351 strokeWeight(2);
352 beginShape(POINTS);
922def98
MS
353 // TODO(mcslee): restore when bassBox/speakers are right again
354 // for (Point p : glucose.model.points) {
355 for (Cube cube : glucose.model.cubes) {
356 for (Point p : cube.points) {
357 stroke(colors[p.index]);
358 vertex(p.fx, p.fy, p.fz);
359 }
0a9f99cc
MS
360 }
361 endShape();
362
d626bc9b 363 // 2D Overlay UI
0a9f99cc 364 drawUI();
d626bc9b
MS
365
366 // Send output colors
367 color[] sendColors = glucose.getColors();
554e38ff 368 if (debugMode) {
d626bc9b 369 debugUI.maskColors(colors);
554e38ff 370 }
a797d019 371
6702151a
MS
372 // Gamma correction here. Apply a cubic to the brightness
373 // for better representation of dynamic range
374 for (int i = 0; i < colors.length; ++i) {
375 float b = brightness(colors[i]) / 100.f;
376 colors[i] = color(
377 hue(colors[i]),
378 saturation(colors[i]),
379 (b*b*b) * 100.
380 );
381 }
e7f14d4d 382
e73ef85d 383 // TODO(mcslee): move into GLucose engine
79ae8245 384 for (PandaDriver p : pandaBoards) {
b58e5a1d 385 p.send(colors);
e73ef85d 386 }
49815cc0
MS
387}
388
92c06c97
MS
389void drawBassBox(BassBox b) {
390 float in = .15;
e76480d4
MS
391
392 noStroke();
393 fill(#191919);
394 pushMatrix();
395 translate(b.x + BassBox.EDGE_WIDTH/2., b.y + BassBox.EDGE_HEIGHT/2, b.z + BassBox.EDGE_DEPTH/2.);
396 box(BassBox.EDGE_WIDTH-20*in, BassBox.EDGE_HEIGHT-20*in, BassBox.EDGE_DEPTH-20*in);
397 popMatrix();
398
399 noStroke();
400 fill(#393939);
92c06c97
MS
401 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);
402
39011e7e
MS
403 pushMatrix();
404 translate(b.x+(Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT-in, b.z + BassBox.EDGE_DEPTH/2.);
405 float lastOffset = 0;
406 for (float offset : BoothFloor.STRIP_OFFSETS) {
407 translate(offset - lastOffset, 0, 0);
408 box(Cube.CHANNEL_WIDTH-in, 0, BassBox.EDGE_DEPTH - 2*in);
409 lastOffset = offset;
410 }
411 popMatrix();
412
92c06c97
MS
413 pushMatrix();
414 translate(b.x + (Cube.CHANNEL_WIDTH-in)/2., b.y + BassBox.EDGE_HEIGHT/2., b.z + in);
415 for (int j = 0; j < 2; ++j) {
416 pushMatrix();
417 for (int i = 0; i < BassBox.NUM_FRONT_STRUTS; ++i) {
418 translate(BassBox.FRONT_STRUT_SPACING, 0, 0);
419 box(Cube.CHANNEL_WIDTH-in, BassBox.EDGE_HEIGHT - in*2, 0);
420 }
421 popMatrix();
422 translate(0, 0, BassBox.EDGE_DEPTH - 2*in);
423 }
424 popMatrix();
425
426 pushMatrix();
427 translate(b.x + in, b.y + BassBox.EDGE_HEIGHT/2., b.z + BassBox.SIDE_STRUT_SPACING + (Cube.CHANNEL_WIDTH-in)/2.);
428 box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
429 translate(BassBox.EDGE_WIDTH-2*in, 0, 0);
430 box(0, BassBox.EDGE_HEIGHT - in*2, Cube.CHANNEL_WIDTH-in);
ab77005f 431 popMatrix();
e76480d4 432
92c06c97
MS
433}
434
51d0d59a 435void drawCube(Cube c) {
254d34c0 436 float in = .15;
e76480d4
MS
437 noStroke();
438 fill(#393939);
254d34c0 439 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
440}
441
e76480d4
MS
442void drawSpeaker(Speaker s) {
443 float in = .15;
444
445 noStroke();
446 fill(#191919);
447 pushMatrix();
448 translate(s.x, s.y, s.z);
449 rotate(s.ry / 180. * PI, 0, -1, 0);
450 translate(Speaker.EDGE_WIDTH/2., Speaker.EDGE_HEIGHT/2., Speaker.EDGE_DEPTH/2.);
451 box(Speaker.EDGE_WIDTH-20*in, Speaker.EDGE_HEIGHT-20*in, Speaker.EDGE_DEPTH-20*in);
254fbb68
MS
452 translate(0, Speaker.EDGE_HEIGHT/2. + Speaker.EDGE_HEIGHT*.8/2, 0);
453
454 fill(#222222);
455 box(Speaker.EDGE_WIDTH*.6, Speaker.EDGE_HEIGHT*.8, Speaker.EDGE_DEPTH*.75);
e76480d4
MS
456 popMatrix();
457
e76480d4
MS
458 noStroke();
459 fill(#393939);
460 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 461}
e76480d4 462
51d0d59a
MS
463void drawBox(float x, float y, float z, float rx, float ry, float rz, float xd, float yd, float zd, float sw) {
464 pushMatrix();
465 translate(x, y, z);
e0cea600 466 rotate(rx / 180. * PI, -1, 0, 0);
51d0d59a 467 rotate(ry / 180. * PI, 0, -1, 0);
e0cea600 468 rotate(rz / 180. * PI, 0, 0, -1);
51d0d59a
MS
469 for (int i = 0; i < 4; ++i) {
470 float wid = (i % 2 == 0) ? xd : zd;
471
472 beginShape();
473 vertex(0, 0);
474 vertex(wid, 0);
475 vertex(wid, yd);
476 vertex(wid - sw, yd);
477 vertex(wid - sw, sw);
478 vertex(0, sw);
479 endShape();
480 beginShape();
481 vertex(0, sw);
482 vertex(0, yd);
483 vertex(wid - sw, yd);
484 vertex(wid - sw, yd - sw);
485 vertex(sw, yd - sw);
486 vertex(sw, sw);
487 endShape();
488
489 translate(wid, 0, 0);
490 rotate(HALF_PI, 0, -1, 0);
491 }
492 popMatrix();
493}
494
49815cc0 495void drawUI() {
d626bc9b
MS
496 camera();
497 javax.media.opengl.GL gl = ((PGraphicsOpenGL)g).beginGL();
498 gl.glClear(javax.media.opengl.GL.GL_DEPTH_BUFFER_BIT);
499 ((PGraphicsOpenGL)g).endGL();
500 strokeWeight(1);
501
49815cc0 502 if (uiOn) {
d626bc9b
MS
503 for (UIContext context : overlays) {
504 context.draw();
505 }
506 }
507
508 // Always draw FPS meter
509 fill(#555555);
510 textSize(9);
511 textAlign(LEFT, BASELINE);
512 text("FPS: " + ((int) (frameRate*10)) / 10. + " / " + targetFramerate + " (-/+)", 4, height-4);
513
514 if (debugMode) {
515 debugUI.draw();
49815cc0 516 }
49815cc0
MS
517}
518
34327c96
MS
519/**
520 * Top-level keyboard event handling
521 */
49815cc0 522void keyPressed() {
bf551144 523 if (mappingMode) {
d626bc9b 524 mappingTool.keyPressed(uiMapping);
bf551144 525 }
3f8be614 526 switch (key) {
0e3c5542
MS
527 case '-':
528 case '_':
529 frameRate(--targetFramerate);
530 break;
531 case '=':
532 case '+':
533 frameRate(++targetFramerate);
534 break;
cc9fcf4b 535 case 'd':
a8d55ade
MS
536 if (!midiQwerty.isEnabled()) {
537 debugMode = !debugMode;
538 println("Debug output: " + (debugMode ? "ON" : "OFF"));
539 }
554e38ff 540 break;
bf551144 541 case 'm':
a8d55ade
MS
542 if (!midiQwerty.isEnabled()) {
543 mappingMode = !mappingMode;
544 uiPatternA.setVisible(!mappingMode);
545 uiMapping.setVisible(mappingMode);
546 if (mappingMode) {
547 restoreToPattern = lx.getPattern();
548 lx.setPatterns(new LXPattern[] { mappingTool });
549 } else {
550 lx.setPatterns(patterns);
551 LXTransition pop = restoreToPattern.getTransition();
552 restoreToPattern.setTransition(null);
553 lx.goPattern(restoreToPattern);
554 restoreToPattern.setTransition(pop);
555 }
bf551144
MS
556 }
557 break;
e73ef85d 558 case 'p':
79ae8245
MS
559 for (PandaDriver p : pandaBoards) {
560 p.toggle();
561 }
cc9fcf4b 562 break;
3f8be614
MS
563 case 'u':
564 uiOn = !uiOn;
565 break;
49815cc0
MS
566 }
567}
568
34327c96
MS
569/**
570 * Top-level mouse event handling
571 */
0a9f99cc 572int mx, my;
0a9f99cc 573void mousePressed() {
d626bc9b
MS
574 boolean debugged = false;
575 if (debugMode) {
576 debugged = debugUI.mousePressed();
577 }
578 if (!debugged) {
579 for (UIContext context : overlays) {
580 context.mousePressed(mouseX, mouseY);
581 }
0a9f99cc 582 }
d626bc9b
MS
583 mx = mouseX;
584 my = mouseY;
0a9f99cc
MS
585}
586
587void mouseDragged() {
d626bc9b
MS
588 boolean dragged = false;
589 for (UIContext context : overlays) {
590 dragged |= context.mouseDragged(mouseX, mouseY);
591 }
592 if (!dragged) {
0a9f99cc
MS
593 int dx = mouseX - mx;
594 int dy = mouseY - my;
595 mx = mouseX;
596 my = mouseY;
597 eyeA += dx*.003;
598 eyeX = midX + eyeR*sin(eyeA);
599 eyeZ = midZ + eyeR*cos(eyeA);
600 eyeY += dy;
601 }
602}
603
604void mouseReleased() {
d626bc9b
MS
605 for (UIContext context : overlays) {
606 context.mouseReleased(mouseX, mouseY);
607 }
0a9f99cc 608}
bda5421d
MS
609
610void mouseWheel(int delta) {
d626bc9b
MS
611 boolean wheeled = false;
612 for (UIContext context : overlays) {
613 wheeled |= context.mouseWheel(mouseX, mouseY, delta);
614 }
615
616 if (!wheeled) {
0ba6ac44
MS
617 eyeR = constrain(eyeR - delta, -500, -80);
618 eyeX = midX + eyeR*sin(eyeA);
619 eyeZ = midZ + eyeR*cos(eyeA);
620 }
bda5421d 621}