001: /*
002: * $RCSfile: FlightBehavior.java,v $
003: *
004: * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.1 $
041: * $Date: 2007/09/25 20:01:19 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.behaviors;
046:
047: import javax.media.j3d.*;
048: import java.util.*;
049: import java.awt.Event;
050: import java.awt.Point;
051: import java.awt.AWTEvent;
052: import java.awt.Dimension;
053: import java.awt.event.KeyEvent;
054: import java.awt.event.MouseEvent;
055: import java.awt.event.ComponentEvent;
056: import java.awt.event.ComponentListener;
057: import java.awt.event.WindowEvent;
058: import javax.vecmath.*;
059:
060: /**
061: * Generalized Behavior Flying through a Universe.
062: *
063: * Either control via the Keyboard with left & right arrows producing roll
064: * and up/down keys producing pitch.
065: *
066: * OR use the mouse for pitch and roll
067: *
068: * In both cases velocity is controlled using keys A and Z to increase and
069: * decrease respectively.
070: *
071: * The mouse motion is proportional to the position of the mouse press the
072: * C key to center the view
073: *
074: * If the shift key is held down while the mouse is moved then their will
075: * be no effect on the behavior. This allows the user to move the mouse
076: * out of the window wihout interfacting with the 3D environment
077: *
078: */
079:
080: public class FlightBehavior extends Behavior implements
081: ComponentListener {
082:
083: TransformGroup tgroup;
084: WakeupCriterion criterion[] = {
085: new WakeupOnAWTEvent(Event.KEY_PRESS),
086: new WakeupOnAWTEvent(MouseEvent.MOUSE_MOVED),
087: new WakeupOnAWTEvent(MouseEvent.MOUSE_PRESSED),
088: new WakeupOnAWTEvent(MouseEvent.MOUSE_DRAGGED),
089: new WakeupOnAWTEvent(ComponentEvent.COMPONENT_RESIZED),
090: new WakeupOnAWTEvent(ComponentEvent.COMPONENT_SHOWN),
091: new WakeupOnAWTEvent(WindowEvent.WINDOW_ACTIVATED),
092: new WakeupOnElapsedTime(5) };
093:
094: WakeupCondition conditions = new WakeupOr(criterion);
095:
096: double MOUSE_MOVEMENT = 1000;
097: double MAX_ANGLE = Math.PI / 16;
098: double acceleration = 0.5;
099: double ROT_ANGLE = 0.01;
100:
101: float MIN_VELOCITY = 0;
102: float MAX_VELOCITY = 20;
103: float VELOCITY_INC = 0.01f; // Velocity change per pixel move in mouse
104: // acceleration mode
105:
106: double rollAngle = 0.0;
107: double pitchAngle = 0.0;
108: double yawAngle = 0.0;
109: double velocity = 0.0;
110: double newVelocity; // Tmp variable
111: double rollAngleDelta = 0.0;
112: double pitchAngleDelta = 0.0;
113: double yawAngleDelta = 0.0;
114:
115: Matrix4d currentMatrix;
116: Transform3D currentView; // Orientation and Translation
117: Transform3D currentOrient; // Orientation
118: Vector3d currentPosn; // Translation
119:
120: Point mousePosn;
121: Point mouseCenter;
122:
123: Matrix4d startOrient;
124: double startX;
125: double startY;
126: double startZ;
127:
128: private Canvas3D canvas; // Keep canvas so we can get Size
129: private Dimension canvasSize;
130:
131: private boolean mousePressed; // Button 1 pressed, FIRE
132:
133: private int mouseAccelerate;
134: private int mouseAccelerateStart; // Initial x pos of mouse
135:
136: private MotionNotifierInterface notifyMe = null;
137:
138: public FlightBehavior(TransformGroup t, Canvas3D canvas) {
139: this (600, 40, 600, 0, 0, 0, t, canvas);
140: }
141:
142: /**
143: * Sets initial viewer position and direction of view to values provided
144: *
145: */
146: public FlightBehavior(double x, double y, double z, double roll,
147: double pitch, double yaw, TransformGroup t, Canvas3D canvas) {
148: super ();
149: startX = x;
150: startY = y;
151: startZ = z;
152: this .currentPosn = new Vector3d(x, y, z);
153:
154: rollRot.rotZ(roll);
155: pitchRot.rotX(pitch);
156: yawRot.rotY(yaw);
157:
158: startOrient = new Matrix4d();
159: startOrient.setIdentity();
160: startOrient.mul(rollRot);
161: startOrient.mul(pitchRot);
162: startOrient.mul(yawRot);
163:
164: this .canvas = canvas;
165: canvas.addComponentListener(this );
166: this .tgroup = t;
167: currentMatrix = new Matrix4d();
168: }
169:
170: public void notifyMe(MotionNotifierInterface not) {
171: this .notifyMe = not;
172: }
173:
174: public void initialize() {
175: initialize(new BoundingSphere(new Point3d(0.0, 0.0, 0.0),
176: Double.MAX_VALUE));
177: }
178:
179: public void initialize(BoundingSphere bounds) {
180:
181: Transform3D initTransform = new Transform3D();
182: Vector3d startPosn = new Vector3d();
183:
184: wakeupOn(conditions);
185: setSchedulingBounds(bounds);
186:
187: // Setup initial Orientation of View
188: currentOrient = new Transform3D(startOrient);
189:
190: currentView = new Transform3D();
191: currentView.setTranslation(currentPosn);
192: currentView.mul(currentOrient);
193:
194: tgroup.setTransform(currentView);
195:
196: canvasSize = new Dimension(1024, 768);
197: mouseCenter = new Point();
198: }
199:
200: public void processStimulus(Enumeration criteria) {
201:
202: WakeupCriterion wakeup;
203: AWTEvent[] evt = null;
204: boolean timer = false;
205:
206: mousePressed = false;
207:
208: while (criteria.hasMoreElements()) {
209: wakeup = (WakeupCriterion) criteria.nextElement();
210: if (wakeup instanceof WakeupOnAWTEvent)
211: evt = ((WakeupOnAWTEvent) wakeup).getAWTEvent();
212: else if (wakeup instanceof WakeupOnElapsedTime)
213: timer = true;
214:
215: if (evt != null)
216: for (int i = 0; i < evt.length; i++) {
217: if (evt[i] instanceof KeyEvent)
218: processKeyEvent((KeyEvent) evt[i]);
219: if (evt[i] instanceof MouseEvent) {
220: processMouseEvent((MouseEvent) evt[i]);
221: timer = true;
222: }
223: if (evt[i] instanceof ComponentEvent
224: || evt[i] instanceof WindowEvent) {
225: canvasSize = canvas.getSize();
226: }
227: }
228:
229: }
230:
231: if (timer) { // If the timer has not expired don't draw
232: calcTransform();
233: tgroup.setTransform(currentView);
234:
235: if (notifyMe != null) {
236: currentView.get(currentMatrix);
237: notifyMe.viewMoved(currentMatrix);
238: }
239: }
240:
241: wakeupOn(conditions);
242: }
243:
244: public void processMouseEvent(MouseEvent evt) {
245: double x, y;
246:
247: if (evt.isShiftDown())
248: return; // Ignore motion if shift is down
249:
250: if (evt.getID() == MouseEvent.MOUSE_PRESSED
251: && evt.getModifiers() == MouseEvent.BUTTON3_MASK)
252: mouseAccelerateStart = evt.getPoint().y;
253:
254: if (evt.getID() == MouseEvent.MOUSE_DRAGGED
255: && evt.getModifiers() == MouseEvent.BUTTON3_MASK) {
256: mouseAccelerate = mouseAccelerateStart - evt.getPoint().y;
257:
258: newVelocity = velocity + mouseAccelerate * VELOCITY_INC;
259: if (newVelocity >= MIN_VELOCITY
260: && newVelocity <= MAX_VELOCITY)
261: velocity = newVelocity;
262:
263: if (notifyMe != null)
264: notifyMe.velocityChanged(velocity);
265: }
266:
267: if (evt.getID() == MouseEvent.MOUSE_PRESSED
268: && evt.getModifiers() == MouseEvent.BUTTON1_MASK)
269: mousePressed = true;
270:
271: if (evt.getID() != MouseEvent.MOUSE_MOVED)
272: return;
273:
274: mousePosn = evt.getPoint();
275:
276: x = mousePosn.x - mouseCenter.x;
277: y = mousePosn.y - mouseCenter.y;
278:
279: rollAngleDelta = -x / MOUSE_MOVEMENT * MAX_ANGLE;
280: pitchAngleDelta = y / MOUSE_MOVEMENT * MAX_ANGLE;
281:
282: }
283:
284: private void processKeyEvent(KeyEvent evt) {
285:
286: int key;
287:
288: key = evt.getKeyCode();
289:
290: if (key == KeyEvent.VK_UP) {
291: pitchAngleDelta -= ROT_ANGLE;
292: } else if (key == KeyEvent.VK_DOWN) {
293: pitchAngleDelta += ROT_ANGLE;
294: } else if (key == KeyEvent.VK_LEFT) {
295: rollAngleDelta += ROT_ANGLE;
296: } else if (key == KeyEvent.VK_RIGHT) {
297: rollAngleDelta -= ROT_ANGLE;
298: } else if (key == KeyEvent.VK_A && velocity <= MAX_VELOCITY) {
299: velocity += acceleration;
300: if (notifyMe != null)
301: notifyMe.velocityChanged(velocity);
302: } else if (key == KeyEvent.VK_Z && velocity >= MIN_VELOCITY) {
303: velocity -= acceleration;
304: if (notifyMe != null)
305: notifyMe.velocityChanged(velocity);
306: } else if (key == KeyEvent.VK_PERIOD)
307: yawAngleDelta += ROT_ANGLE;
308: else if (key == KeyEvent.VK_COMMA)
309: yawAngleDelta -= ROT_ANGLE;
310: else if (key == KeyEvent.VK_C) {
311: rollAngle = 0.0;
312: pitchAngle = 0.0;
313: yawAngle = 0.0;
314: pitchAngleDelta = 0.0;
315: rollAngleDelta = 0.0;
316: yawAngleDelta = 0.0;
317: velocity = 0;
318: currentPosn = new Vector3d(startX, startY, startZ);
319: currentOrient.setIdentity();
320: } else if (key == KeyEvent.VK_H) {
321: rollAngle = 0.0;
322: pitchAngle = 0.0;
323: yawAngle = 0.0;
324: pitchAngleDelta = 0.0;
325: rollAngleDelta = 0.0;
326: yawAngleDelta = 0.0;
327: velocity = 0;
328: }
329:
330: }
331:
332: private final Matrix4d rot = new Matrix4d();
333: private final Matrix4d tmpDirection = new Matrix4d();
334: private final Matrix4d zComp = new Matrix4d(); // Should be a vector but mul not implemented yet
335: private final Vector3d newDirection = new Vector3d();
336: private final Transform3D velocityTrans = new Transform3D();
337: private final Matrix4d rollRot = new Matrix4d();
338: private final Matrix4d pitchRot = new Matrix4d();
339: private final Matrix4d yawRot = new Matrix4d();
340: private final Vector3d zero = new Vector3d();
341:
342: private void calcTransform() {
343:
344: zComp.setRow(2, 0, 0, 1, 0);
345:
346: pitchAngle += pitchAngleDelta;
347: rollAngle += rollAngleDelta;
348: yawAngle += yawAngleDelta;
349:
350: if (pitchAngle < 0)
351: pitchAngle = Math.PI * 2 - pitchAngle;
352: else if (pitchAngle > Math.PI * 2)
353: pitchAngle = pitchAngle - Math.PI * 2;
354:
355: rollRot.rotZ(rollAngleDelta);
356: pitchRot.rotX(pitchAngleDelta);
357: yawRot.rotY(yawAngleDelta);
358:
359: currentOrient.get(rot);
360:
361: velocityTrans.set(-velocity); // Positive velocity moves in -ve Z
362:
363: tmpDirection.mul(rot, zComp);
364: newDirection.x = tmpDirection.m02;
365: newDirection.y = tmpDirection.m12;
366: newDirection.z = tmpDirection.m22;
367:
368: velocityTrans.transform(newDirection);
369:
370: currentPosn.add(newDirection);
371:
372: rot.mul(rollRot);
373: rot.mul(pitchRot);
374: rot.mul(yawRot);
375:
376: currentOrient.set(rot);
377:
378: currentView.set(rot);
379: currentView.setTranslation(currentPosn);
380:
381: //System.out.println("Roll="+rollAngle+" Pitch="+pitchAngle+" Velocity="+velocity+" RollDelta="+rollAngleDelta+" PitchDelta="+pitchAngleDelta+" Coords="+currentPosn);
382:
383: }
384:
385: public void setPosition(Vector3d pos) {
386: currentPosn.x = pos.x;
387: currentPosn.y = pos.y;
388: currentPosn.z = pos.z;
389:
390: calcTransform();
391: tgroup.setTransform(currentView);
392: }
393:
394: public void setAcceleration(double acceleration) {
395: this .acceleration = acceleration;
396: }
397:
398: public final double getVelocity() {
399: return velocity;
400: }
401:
402: public final double getRollAngle() {
403: return rollAngle;
404: }
405:
406: public final double getPitchAngle() {
407: return pitchAngle;
408: }
409:
410: public final double getYawAngle() {
411: return yawAngle;
412: }
413:
414: public final boolean buttonPressed() {
415: return mousePressed;
416: }
417:
418: /**
419: * A movement of the mouse from the center of the screen by <code>mouseMotion
420: * </code> will cause the rotation of <code>angle</code>
421: */
422: public final void setMouseSensitivity(double mouseMotion,
423: double angle) {
424: MOUSE_MOVEMENT = mouseMotion;
425: MAX_ANGLE = angle;
426: }
427:
428: public void componentHidden(ComponentEvent evt) {
429: }
430:
431: public void componentMoved(ComponentEvent evt) {
432: }
433:
434: public void componentResized(ComponentEvent evt) {
435: if (evt.getSource() == canvas) {
436: canvasSize = canvas.getSize();
437: mouseCenter.x = canvasSize.width / 2;
438: mouseCenter.y = canvasSize.height / 2;
439: }
440: }
441:
442: public void componentShown(ComponentEvent evt) {
443: }
444:
445: public void setMinMaxVelocity(float min, float max) {
446: MIN_VELOCITY = min;
447: MAX_VELOCITY = max;
448: }
449:
450: public float getMinVelocity() {
451: return MIN_VELOCITY;
452: }
453:
454: public float getMaxVelocity() {
455: return MAX_VELOCITY;
456: }
457: }
|