001: /*
002: * Copyright (c) 2000 Silvere Martin-Michiellot All Rights Reserved.
003: *
004: * Silvere Martin-Michiellot grants you ("Licensee") a non-exclusive,
005: * royalty free, license to use, modify and redistribute this
006: * software in source and binary code form,
007: * provided that i) this copyright notice and license appear on all copies of
008: * the software; and ii) Licensee does not utilize the software in a manner
009: * which is disparaging to Silvere Martin-Michiellot.
010: *
011: * This software is provided "AS IS," without a warranty of any kind. ALL
012: * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
013: * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
014: * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. Silvere Martin-Michiellot
015: * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
016: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
017: * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL
018: * Silvere Martin-Michiellot OR ITS LICENSORS BE LIABLE
019: * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
020: * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
021: * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
022: * OR INABILITY TO USE SOFTWARE, EVEN IF Silvere Martin-Michiellot HAS BEEN
023: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
024: *
025: * This software is not designed or intended for use in on-line control of
026: * aircraft, air traffic, aircraft navigation or aircraft communications; or in
027: * the design, construction, operation or maintenance of any nuclear
028: * facility. Licensee represents and warrants that it will not use or
029: * redistribute the Software for such purposes.
030: *
031: */
032:
033: package com.db.behaviors;
034:
035: // This code is repackaged after the code from David R. Nadeau
036: // Site http://www.sdsc.edu/~nadeau/
037: // Email nadeau@sdsc.edu
038:
039: import java.awt.*;
040: import java.awt.event.*;
041: import java.util.*;
042: import javax.media.j3d.*;
043: import javax.vecmath.*;
044:
045: /**
046: * FlyViewerBehavior is a utility class that creates a "flying style"
047: * navigation semantic.
048: *
049: *
050: */
051: public class FlyViewerBehavior extends ViewerBehavior {
052:
053: // This class is inspired by the MouseBehavior, MouseRotate,
054: // Previous and initial cursor locations
055: protected int previousX = 0;
056: protected int previousY = 0;
057: protected int initialX = 0;
058: protected int initialY = 0;
059:
060: // Saved standard cursor
061: protected Cursor savedCursor = null;
062:
063: public static final double DEFAULT_ZACCELERATION_FACTOR = 0.5;
064:
065: protected double velocity = 0;
066:
067: protected double zAccelerationFactor = DEFAULT_ZACCELERATION_FACTOR;
068:
069: Transform3D transform3 = new Transform3D();
070:
071: /**
072: * Default Rotation and translation scaling factors for
073: * animated movements (Button 1 press).
074: */
075: public static final double DEFAULT_YROTATION_ANIMATION_FACTOR = 0.0002;
076: public static final double DEFAULT_ZTRANSLATION_ANIMATION_FACTOR = 0.01;
077:
078: protected double YRotationAnimationFactor = DEFAULT_YROTATION_ANIMATION_FACTOR;
079: protected double ZTranslationAnimationFactor = DEFAULT_ZTRANSLATION_ANIMATION_FACTOR;
080:
081: /**
082: * Constructs a new walk behavior that converts mouse actions
083: * into rotations and translations. Rotations and translations
084: * are written into a TransformGroup that must be set using the
085: * setTransformGroup method. The cursor will be changed during
086: * mouse actions if the parent frame is set using the
087: * setParentComponent method.
088: *
089: * @return a new WalkViewerBehavior that needs its
090: * TransformGroup and parent Component set
091: */
092: public FlyViewerBehavior() {
093:
094: super ();
095:
096: }
097:
098: /**
099: * Constructs a new walk behavior that converts mouse actions
100: * into rotations and translations. Rotations and translations
101: * are written into a TransformGroup that must be set using the
102: * setTransformGroup method. The cursor will be changed within
103: * the given AWT parent Component during mouse drags.
104: *
105: * @param parent a parent AWT Component within which the cursor
106: * will change during mouse drags
107: *
108: * @return a new WalkViewerBehavior that needs its
109: * TransformGroup and parent Component set
110: */
111: public FlyViewerBehavior(Component parent) {
112:
113: super (parent);
114:
115: }
116:
117: /**
118: * Constructs a new walk behavior that converts mouse actions
119: * into rotations and translations. Rotations and translations
120: * are written into the given TransformGroup. The cursor will
121: * be changed during mouse actions if the parent frame is set
122: * using the setParentComponent method.
123: *
124: * @param transformGroup a TransformGroup whos transform is
125: * read and written by the behavior
126: *
127: * @return a new WalkViewerBehavior that needs its
128: * TransformGroup and parent Component set
129: */
130: public FlyViewerBehavior(TransformGroup transformGroup) {
131:
132: super ();
133: subjectTransformGroup = transformGroup;
134:
135: }
136:
137: /**
138: * Constructs a new walk behavior that converts mouse actions
139: * into rotations and translations. Rotations and translations
140: * are written into the given TransformGroup. The cursor will be
141: * changed within the given AWT parent Component during mouse drags.
142: *
143: * @param transformGroup a TransformGroup whos transform is
144: * read and written by the behavior
145: *
146: * @param parent a parent AWT Component within which the cursor
147: * will change during mouse drags
148: *
149: * @return a new WalkViewerBehavior that needs its
150: * TransformGroup and parent Component set
151: */
152: public FlyViewerBehavior(TransformGroup transformGroup,
153: Component parent) {
154:
155: super (parent);
156: subjectTransformGroup = transformGroup;
157:
158: }
159:
160: public static final double DEFAULT_ZROTATION_FACTOR = 0.005;
161:
162: protected double ZRotationFactor = DEFAULT_ZROTATION_FACTOR;
163:
164: /**
165: * Set the Z rotation scaling factor for Z-axis rotations.
166: *
167: * @param factor The new scaling factor.
168: */
169: public void setZRotationFactor(double factor) {
170:
171: ZRotationFactor = factor;
172:
173: }
174:
175: /**
176: * Get the current Z rotation scaling factor for Z-axis rotations.
177: */
178: public double getZRotationFactor() {
179:
180: return ZRotationFactor;
181:
182: }
183:
184: /**
185: * Set the acceleration scaling factor for Z-axis translations.
186: *
187: * @param factor The new scaling factor.
188: */
189: public void setZAccelerationFactor(double factor) {
190:
191: zAccelerationFactor = factor;
192:
193: }
194:
195: /**
196: * Get the current acceleration scaling factor for Z-axis translations.
197: */
198: public double getZAccelerationFactor() {
199:
200: return zAccelerationFactor;
201:
202: }
203:
204: /**
205: * Respond to a button1 event (press, release, or drag).
206: *
207: * @param mouseEvent A MouseEvent to respond to.
208: */
209: public void onButton1(MouseEvent mev) {
210:
211: if (subjectTransformGroup == null)
212: return;
213:
214: int x = mev.getX();
215: int y = mev.getY();
216:
217: int modifiers = mev.getModifiers();
218: if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
219: velocity += 1;
220: } else {
221: if (velocity > 1) {
222: velocity -= 1;
223: }
224: }
225:
226: if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
227: // Mouse button pressed: record position
228: previousX = x;
229: previousY = y;
230: velocity = 1;
231:
232: // Change to a "move" cursor
233: if (parentComponent != null) {
234: savedCursor = parentComponent.getCursor();
235: parentComponent.setCursor(activeCursor);
236: }
237: return;
238: }
239: if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
240: // Mouse button released: do nothing
241:
242: // Switch the cursor back
243: if (parentComponent != null)
244: parentComponent.setCursor(savedCursor);
245: return;
246: }
247:
248: //
249: // Mouse moved while button down: create a rotation
250: //
251: // Compute the delta in X and Y from the previous
252: // position. Use the delta to compute rotation
253: // angles with the mapping:
254: //
255: // positive X mouse delta --> positive Y-axis rotation
256: // positive Y mouse delta --> positive X-axis rotation
257: //
258: // where positive X mouse movement is to the right, and
259: // positive Y mouse movement is **down** the screen.
260: //
261: int deltaX = x - previousX;
262: int deltaY = y - previousY;
263:
264: if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
265: || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
266: // Deltas are too huge to be believable. Probably a glitch.
267: // Don't record the new XY location, or do anything.
268: return;
269: }
270:
271: double xRotationAngle = deltaY * XRotationFactor;
272: double yRotationAngle = deltaX * YRotationFactor;
273: double zTranslationDistance = velocity * zAccelerationFactor;
274:
275: //
276: // Build transforms
277: //
278: transform1.rotX(xRotationAngle);
279: transform2.rotY(yRotationAngle);
280:
281: translate.set(0.0, 0.0, zTranslationDistance);
282: transform3.set(translate);
283:
284: // Get and save the current transform matrix
285: subjectTransformGroup.getTransform(currentTransform);
286: currentTransform.get(matrix);
287: translate.set(matrix.m03, matrix.m13, matrix.m23);
288:
289: // Translate to the origin, rotate, then translate back
290: currentTransform.setTranslation(origin);
291: currentTransform.mul(transform1, currentTransform);
292: currentTransform.mul(transform2, currentTransform);
293:
294: currentTransform.mul(transform3, currentTransform);
295: currentTransform.setTranslation(translate);
296:
297: // Update the transform group
298: subjectTransformGroup.setTransform(currentTransform);
299:
300: previousX = x;
301: previousY = y;
302: // Translate back from the origin by the original translation
303: // distance, plus the new walk translation... but force walk
304: // to travel on a plane by ignoring the Y component of a
305: // transformed translation vector.
306: currentTransform.transform(translate);
307: translate.x += matrix.m03; // add in existing X translation
308: translate.y = matrix.m13; // use Y translation
309: translate.z += matrix.m23; // add in existing Z translation
310: currentTransform.setTranslation(translate);
311:
312: }
313:
314: /**
315: * Respond to a button2 event (press, release, or drag).
316: *
317: * @param mouseEvent A MouseEvent to respond to.
318: */
319: public void onButton2(MouseEvent mev) {
320:
321: if (subjectTransformGroup == null)
322: return;
323:
324: int x = mev.getX();
325: int y = mev.getY();
326:
327: if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
328: // Mouse button pressed: record position
329: previousX = x;
330: previousY = y;
331:
332: // Change to a "move" cursor
333: if (parentComponent != null) {
334: savedCursor = parentComponent.getCursor();
335: parentComponent.setCursor(activeCursor);
336: }
337: return;
338: }
339: if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
340: // Mouse button released: do nothing
341:
342: // Switch the cursor back
343: if (parentComponent != null)
344: parentComponent.setCursor(savedCursor);
345: return;
346: }
347:
348: //
349: // Mouse moved while button down: create a rotation
350: //
351: // Compute the delta in X and Y from the previous
352: // position. Use the delta to compute rotation
353: // angles with the mapping:
354: //
355: // positive X mouse delta --> positive Y-axis rotation
356: // positive Y mouse delta --> positive X-axis rotation
357: //
358: // where positive X mouse movement is to the right, and
359: // positive Y mouse movement is **down** the screen.
360: //
361: int deltaX = x - previousX;
362: int deltaY = y - previousY;
363:
364: if (deltaX > UNUSUAL_XDELTA || deltaX < -UNUSUAL_XDELTA
365: || deltaY > UNUSUAL_YDELTA || deltaY < -UNUSUAL_YDELTA) {
366: // Deltas are too huge to be believable. Probably a glitch.
367: // Don't record the new XY location, or do anything.
368: return;
369: }
370:
371: double zRotationAngle = deltaX * ZRotationFactor;
372:
373: //
374: // Build transforms
375: //
376: transform1.rotZ(zRotationAngle);
377:
378: // Get and save the current transform matrix
379: subjectTransformGroup.getTransform(currentTransform);
380: currentTransform.get(matrix);
381: translate.set(matrix.m03, matrix.m13, matrix.m23);
382:
383: // Translate to the origin, rotate, then translate back
384: currentTransform.setTranslation(origin);
385: currentTransform.mul(transform1, currentTransform);
386: currentTransform.setTranslation(translate);
387:
388: // Update the transform group
389: subjectTransformGroup.setTransform(currentTransform);
390:
391: previousX = x;
392: previousY = y;
393:
394: }
395:
396: /**
397: * Responds to a button3 event (press, release, or drag).
398: * On a drag, the difference between the current and previous
399: * cursor location provides a delta that controls the amount by
400: * which to translate in X and Y.
401: *
402: * @param mouseEvent the MouseEvent to respond to
403: */
404: public void onButton3(MouseEvent mev) {
405:
406: if (subjectTransformGroup == null)
407: return;
408:
409: int x = mev.getX();
410: int y = mev.getY();
411:
412: if (mev.getID() == MouseEvent.MOUSE_PRESSED) {
413: // Mouse button pressed: record position and change
414: // the wakeup criterion to include elapsed time wakeups
415: // so we can animate.
416: previousX = x;
417: previousY = y;
418: initialX = x;
419: initialY = y;
420:
421: // Change to a "move" cursor
422: if (parentComponent != null) {
423: savedCursor = parentComponent.getCursor();
424: parentComponent.setCursor(activeCursor);
425: }
426: return;
427: }
428: if (mev.getID() == MouseEvent.MOUSE_RELEASED) {
429: // Switch the cursor back
430: if (parentComponent != null)
431: parentComponent.setCursor(savedCursor);
432: return;
433: }
434:
435: int deltaX = previousX - initialX;
436: int deltaY = previousY - initialY;
437:
438: double yRotationAngle = -deltaX * YRotationAnimationFactor;
439: double zTranslationDistance = deltaY
440: * ZTranslationAnimationFactor;
441:
442: //
443: // Build transforms
444: //
445: transform1.rotY(yRotationAngle);
446: translate.set(0.0, 0.0, zTranslationDistance);
447:
448: // Get and save the current transform matrix
449: subjectTransformGroup.getTransform(currentTransform);
450: currentTransform.get(matrix);
451:
452: // Translate to the origin, rotate, then translate back
453: currentTransform.setTranslation(origin);
454: currentTransform.mul(transform1, currentTransform);
455:
456: // Translate back from the origin by the original translation
457: // distance, plus the new walk translation... but force walk
458: // to travel on a plane by ignoring the Y component of a
459: // transformed translation vector.
460: currentTransform.transform(translate);
461: translate.x += matrix.m03; // add in existing X translation
462: translate.y = matrix.m13; // use Y translation
463: translate.z += matrix.m23; // add in existing Z translation
464: currentTransform.setTranslation(translate);
465:
466: // Update the transform group
467: subjectTransformGroup.setTransform(currentTransform);
468:
469: previousX = x;
470: previousY = y;
471:
472: }
473:
474: /**
475: * Respond to an elapsed frames event (assuming subclass has set up a
476: * wakeup criterion for it).
477: *
478: * @param time A WakeupOnElapsedFrames criterion to respond to.
479: */
480: public void onElapsedFrames(WakeupOnElapsedFrames timeEvent) {
481: // Can't happen
482: }
483:
484: }
|