001: /*
002: * $RCSfile: KeyNavigator.java,v $
003: *
004: * Copyright (c) 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.4 $
041: * $Date: 2007/02/09 17:20:12 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.behaviors.keyboard;
046:
047: import java.io.*;
048: import java.util.*;
049: import javax.media.j3d.*;
050: import javax.vecmath.*;
051: import java.awt.event.*;
052:
053: /**
054: * This is the KeyNavigator class. It accumulates AWT key events (key
055: * press and key release) and computes a new transform based on the
056: * accumulated events and elapsed time.
057: */
058: public class KeyNavigator {
059:
060: private Vector3d navVec;
061: private long time;
062:
063: private Vector3d fwdAcc;
064: private Vector3d bwdAcc;
065: private Vector3d leftAcc;
066: private Vector3d rightAcc;
067: private Vector3d upAcc;
068: private Vector3d downAcc;
069:
070: private Vector3d fwdDrag;
071: private Vector3d bwdDrag;
072: private Vector3d leftDrag;
073: private Vector3d rightDrag;
074: private Vector3d upDrag;
075: private Vector3d downDrag;
076:
077: private double fwdVMax;
078: private double bwdVMax;
079: private double leftVMax;
080: private double rightVMax;
081: private double upVMax;
082: private double downVMax;
083:
084: private float leftRotAngle;
085: private float rightRotAngle;
086: private float upRotAngle;
087: private float downRotAngle;
088:
089: private double mmx;
090:
091: private Vector3d a = new Vector3d();
092: private Vector3d dv = new Vector3d();
093: private Point3d dp = new Point3d();
094: private Quat4d udQuat = new Quat4d();
095: private Quat4d lrQuat = new Quat4d();
096: private Vector3d vpPos = new Vector3d();
097: private double vpScale;
098: private Quat4d vpQuat = new Quat4d();
099: private Matrix4d vpMatrix = new Matrix4d();
100: private Transform3D vpTrans = new Transform3D();
101: private Matrix4d mat = new Matrix4d();
102: private Vector3d nda = new Vector3d();
103: private Vector3d temp = new Vector3d();
104: private Transform3D nominal = new Transform3D();
105: private TransformGroup targetTG;
106:
107: private static final int UP_ARROW = (1 << 0);
108: private static final int DOWN_ARROW = (1 << 1);
109: private static final int LEFT_ARROW = (1 << 2);
110: private static final int RIGHT_ARROW = (1 << 3);
111: private static final int PLUS_SIGN = (1 << 4);
112: private static final int MINUS_SIGN = (1 << 5);
113: private static final int PAGE_UP = (1 << 6);
114: private static final int PAGE_DOWN = (1 << 7);
115: private static final int HOME_DIR = (1 << 8);
116: private static final int HOME_NOMINAL = (1 << 9);
117:
118: private static final int SHIFT = (1 << 10);
119: private static final int ALT = (1 << 11);
120: private static final int META = (1 << 12);
121:
122: private static final int KEY_UP = (1 << 13);
123: private static final int KEY_DOWN = (1 << 14);
124:
125: private int key_state = 0;
126: private int modifier_key_state = 0;
127:
128: /**
129: * Constructs a new key navigator object that operates on the specified
130: * transform group. All parameters are set to their default, idle state.
131: * @param targetTG the target transform group
132: */
133: public KeyNavigator(TransformGroup targetTG) {
134: this .targetTG = targetTG;
135: targetTG.getTransform(nominal);
136:
137: mmx = 128.0;
138: navVec = new Vector3d(0.0, 0.0, 0.0);
139:
140: fwdAcc = new Vector3d(0.0, 0.0, -mmx);
141: bwdAcc = new Vector3d(0.0, 0.0, mmx);
142: leftAcc = new Vector3d(-mmx, 0.0, 0.0);
143: rightAcc = new Vector3d(mmx, 0.0, 0.0);
144: upAcc = new Vector3d(0.0, mmx, 0.0);
145: downAcc = new Vector3d(0.0, -mmx, 0.0);
146:
147: fwdDrag = new Vector3d(0.0, 0.0, mmx);
148: bwdDrag = new Vector3d(0.0, 0.0, -mmx);
149: leftDrag = new Vector3d(mmx, 0.0, 0.0);
150: rightDrag = new Vector3d(-mmx, 0.0, 0.0);
151: upDrag = new Vector3d(0.0, -mmx, 0.0);
152: downDrag = new Vector3d(0.0, mmx, 0.0);
153:
154: fwdVMax = -mmx;
155: bwdVMax = mmx;
156: leftVMax = -mmx;
157: rightVMax = mmx;
158: upVMax = mmx;
159: downVMax = -mmx;
160:
161: leftRotAngle = (float) (-Math.PI * 2.0 / 3.0);
162: rightRotAngle = (float) (Math.PI * 2.0 / 3.0);
163: upRotAngle = (float) (Math.PI * 2.0 / 3.0);
164: downRotAngle = (float) (-Math.PI * 2.0 / 3.0);
165:
166: // Create Timer here.
167: time = System.currentTimeMillis();
168:
169: }
170:
171: private long getDeltaTime() {
172: long newTime = System.currentTimeMillis();
173: long deltaTime = newTime - time;
174: time = newTime;
175: if (deltaTime > 2000)
176: return 0;
177: else
178: return deltaTime;
179: }
180:
181: /* Generate a quaterion as a rotation of radians av about 0/x 1/y 2/z axis */
182: private void genRotQuat(double av, int axis, Quat4d q) {
183: double b;
184:
185: q.x = q.y = q.z = 0.0;
186: q.w = Math.cos(av / 2.0);
187:
188: b = 1.0 - q.w * q.w;
189:
190: if (b > 0.0)
191: b = Math.sqrt(b);
192: else
193: return;
194:
195: if (av < 0.0)
196: b = -b;
197: if (axis == 0)
198: q.x = b;
199: else if (axis == 1)
200: q.y = b;
201: else
202: q.z = b;
203:
204: }
205:
206: private void accKeyAdd(Vector3d a, Vector3d da, Vector3d drag,
207: double scaleVel) {
208:
209: /* Scaling of acceleration due to modification keys */
210: nda.scale(scaleVel, da);
211: /* Addition of sufficent acceleration to counteract drag */
212: nda.sub(drag);
213:
214: /* Summing into overall acceleration */
215: a.add(nda);
216:
217: }
218:
219: /**
220: * Computes a new transform for the next frame based on
221: * the current transform, accumulated keyboard inputs, and
222: * elapsed time. This new transform is written into the target
223: * transform group.
224: * This method should be called once per frame.
225: */
226: public void integrateTransformChanges() {
227: double scaleVel, scaleRot, scaleScale, pre;
228: double udAng, lrAng, r;
229:
230: // Get the current View Platform transform into a transform3D object.
231: targetTG.getTransform(vpTrans);
232: // Extract the position, quaterion, and scale from the transform3D.
233: vpScale = vpTrans.get(vpQuat, vpPos);
234:
235: double deltaTime = (double) getDeltaTime();
236: deltaTime *= 0.001;
237:
238: /* Calculate scale due to modification keys */
239: if ((modifier_key_state & SHIFT) != 0
240: && (modifier_key_state & META) == 0) {
241: scaleVel = 3.0;
242: scaleRot = 2.0;
243: scaleScale = 4.0;
244: } else if ((modifier_key_state & SHIFT) == 0
245: && (modifier_key_state & META) != 0) {
246: scaleVel = 0.1;
247: scaleRot = 0.1;
248: scaleScale = 0.1;
249: } else if ((modifier_key_state & SHIFT) != 0
250: && (modifier_key_state & META) != 0) {
251: scaleVel = 0.3;
252: scaleRot = 0.5;
253: scaleScale = 0.1;
254: } else {
255: scaleRot = scaleVel = 1.0;
256: scaleScale = 4.0;
257: }
258:
259: /*
260: * Processing of rectiliear motion keys.
261: */
262:
263: a.x = a.y = a.z = 0.0; /* acceleration initially 0 */
264:
265: /* Acceleration due to keys being down */
266: if ((key_state & UP_ARROW) != 0
267: && (key_state & DOWN_ARROW) == 0)
268: accKeyAdd(a, fwdAcc, fwdDrag, scaleVel);
269: else if ((key_state & UP_ARROW) == 0
270: && (key_state & DOWN_ARROW) != 0)
271: accKeyAdd(a, bwdAcc, bwdDrag, scaleVel);
272:
273: if (((modifier_key_state & ALT) != 0)
274: && (key_state & LEFT_ARROW) != 0
275: && (key_state & RIGHT_ARROW) == 0) {
276: accKeyAdd(a, leftAcc, leftDrag, scaleVel);
277: } else if (((modifier_key_state & ALT) != 0)
278: && (key_state & LEFT_ARROW) == 0
279: && (key_state & RIGHT_ARROW) != 0)
280: accKeyAdd(a, rightAcc, rightDrag, scaleVel);
281:
282: if (((modifier_key_state & ALT) != 0)
283: && (key_state & PAGE_UP) != 0
284: && (key_state & PAGE_DOWN) == 0)
285: accKeyAdd(a, upAcc, upDrag, scaleVel);
286: else if (((modifier_key_state & ALT) != 0)
287: && (key_state & PAGE_UP) == 0
288: && (key_state & PAGE_DOWN) != 0)
289: accKeyAdd(a, downAcc, downDrag, scaleVel);
290:
291: /*
292: * Drag due to new or existing motion
293: */
294: pre = navVec.z + a.z * deltaTime;
295: if (pre < 0.0) {
296: if (pre + fwdDrag.z * deltaTime < 0.0)
297: a.add(fwdDrag);
298: else
299: a.z -= pre / deltaTime;
300: } else if (pre > 0.0) {
301: if (pre + bwdDrag.z * deltaTime > 0.0)
302: a.add(bwdDrag);
303: else
304: a.z -= pre / deltaTime;
305: }
306:
307: pre = navVec.x + a.x * deltaTime;
308: if (pre < 0.0) {
309: if (pre + leftDrag.x * deltaTime < 0.0)
310: a.add(leftDrag);
311: else
312: a.x -= pre / deltaTime;
313: } else if (pre > 0.0) {
314: if (pre + rightDrag.x * deltaTime > 0.0)
315: a.add(rightDrag);
316: else
317: a.x -= pre / deltaTime;
318: }
319:
320: pre = navVec.y + a.y * deltaTime;
321: if (pre < 0.0) {
322: if (pre + downDrag.y * deltaTime < 0.0)
323: a.add(downDrag);
324: else
325: a.y -= pre / deltaTime;
326: } else if (pre > 0.0) {
327: if (pre + upDrag.y * deltaTime > 0.0)
328: a.add(upDrag);
329: else
330: a.y -= pre / deltaTime;
331: }
332:
333: /* Integration of acceleration to velocity */
334: dv.scale(deltaTime, a);
335: navVec.add(dv);
336:
337: /* Speed limits */
338: if (navVec.z < scaleVel * fwdVMax)
339: navVec.z = scaleVel * fwdVMax;
340: if (navVec.z > scaleVel * bwdVMax)
341: navVec.z = scaleVel * bwdVMax;
342: if (navVec.x < scaleVel * leftVMax)
343: navVec.x = scaleVel * leftVMax;
344: if (navVec.x > scaleVel * rightVMax)
345: navVec.x = scaleVel * rightVMax;
346: if (navVec.y > scaleVel * upVMax)
347: navVec.y = scaleVel * upVMax;
348: if (navVec.y < scaleVel * downVMax)
349: navVec.y = scaleVel * downVMax;
350:
351: /* Integration of velocity to distance */
352: dp.scale(deltaTime, navVec);
353:
354: /* Scale our motion to the current avatar scale */
355: // 1.0 eventually needs to be a more complex value (see hs).
356: // r = workplace_coexistence_to_vworld_ori.scale/
357: // one_to_one_coexistence_to_vworld_ori.scale;
358: r = vpScale / 1.0;
359: dp.scale(r, dp);
360:
361: /*
362: * Processing of rotation motion keys.
363: */
364: udAng = lrAng = 0.0;
365:
366: /* Rotation due to keys being down */
367: if (((modifier_key_state & ALT) == 0)
368: && (key_state & LEFT_ARROW) != 0
369: && (key_state & RIGHT_ARROW) == 0)
370: lrAng = (double) leftRotAngle;
371: else if (((modifier_key_state & ALT) == 0)
372: && (key_state & LEFT_ARROW) == 0
373: && (key_state & RIGHT_ARROW) != 0)
374: lrAng = (double) rightRotAngle;
375:
376: if (((modifier_key_state & ALT) == 0)
377: && (key_state & PAGE_UP) != 0
378: && (key_state & PAGE_DOWN) == 0)
379: udAng = (double) upRotAngle;
380: else if (((modifier_key_state & ALT) == 0)
381: && (key_state & PAGE_UP) == 0
382: && (key_state & PAGE_DOWN) != 0)
383: udAng = (double) downRotAngle;
384:
385: lrAng *= scaleRot;
386: udAng *= scaleRot;
387:
388: /* Scaling of angle change to delta time */
389: lrAng *= deltaTime;
390: udAng *= deltaTime;
391:
392: /* Addition to existing orientation */
393: // vr_quat_inverse(&workplace_coexistence_to_vworld_ori.quat, &vpQuat);
394: // vpQuat gotten at top of method.
395: vpQuat.inverse();
396:
397: if (lrAng != 0.0) {
398: genRotQuat(lrAng, 1, lrQuat);
399: vpQuat.mul(lrQuat, vpQuat);
400: }
401:
402: if (udAng != 0.0) {
403: genRotQuat(udAng, 0, udQuat);
404: vpQuat.mul(udQuat, vpQuat);
405: }
406:
407: /* Rotation of distance vector */
408: vpQuat.inverse();
409: vpQuat.normalize(); /* Improvment over HoloSketch */
410: mat.set(vpQuat);
411: mat.transform(dp);
412:
413: /* Processing of scale */
414: if ((key_state & PLUS_SIGN) != 0) {
415: vpScale *= (1.0 + (scaleScale * deltaTime));
416: if (vpScale > 10e+14)
417: vpScale = 1.0;
418: } else if ((key_state & MINUS_SIGN) != 0) {
419: vpScale /= (1.0 + (scaleScale * deltaTime));
420: if (vpScale < 10e-14)
421: vpScale = 1.0;
422: }
423:
424: // add dp into current vp position.
425: vpPos.add(dp);
426:
427: if ((key_state & HOME_NOMINAL) != 0) {
428: resetVelocity();
429: // Extract the position, quaterion, and scale from the nominal
430: // transform
431: vpScale = nominal.get(vpQuat, vpPos);
432: }
433:
434: /* Final update of view platform */
435: // Put the transform back into the transform group.
436: vpTrans.set(vpQuat, vpPos, vpScale);
437: targetTG.setTransform(vpTrans);
438: }
439:
440: /**
441: * Resets the keyboard navigation velocity to 0.
442: */
443: private void resetVelocity() {
444: navVec.x = navVec.y = navVec.z = 0.0;
445: }
446:
447: /**
448: * Processed a keyboard event. This routine should be called
449: * every time a KEY_PRESSED or KEY_RELEASED event is received.
450: * @param keyEvent the AWT key event
451: */
452: public void processKeyEvent(KeyEvent keyEvent) {
453: int keyCode = keyEvent.getKeyCode();
454: int keyChar = keyEvent.getKeyChar();
455:
456: //System.err.println("keyCode " + keyCode + " keyChar " + keyChar);
457:
458: if (keyEvent.getID() == KeyEvent.KEY_RELEASED) {
459: if (keyChar == '+')
460: key_state &= ~PLUS_SIGN;
461: else
462: switch (keyCode) {
463: case KeyEvent.VK_UP:
464: key_state &= ~UP_ARROW;
465: break;
466: case KeyEvent.VK_DOWN:
467: key_state &= ~DOWN_ARROW;
468: break;
469: case KeyEvent.VK_LEFT:
470: key_state &= ~LEFT_ARROW;
471: break;
472: case KeyEvent.VK_RIGHT:
473: key_state &= ~RIGHT_ARROW;
474: break;
475: case KeyEvent.VK_PAGE_UP:
476: key_state &= ~PAGE_UP;
477: break;
478: case KeyEvent.VK_PAGE_DOWN:
479: key_state &= ~PAGE_DOWN;
480: break;
481: case KeyEvent.VK_EQUALS:
482: key_state &= ~HOME_NOMINAL;
483: break;
484: default:
485: switch (keyChar) {
486: case '-':
487: key_state &= ~MINUS_SIGN;
488: break;
489: }
490: }
491: } else if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
492: if (keyChar == '+')
493: key_state |= PLUS_SIGN;
494: switch (keyCode) {
495: case KeyEvent.VK_UP:
496: key_state |= UP_ARROW;
497: break;
498: case KeyEvent.VK_DOWN:
499: key_state |= DOWN_ARROW;
500: break;
501: case KeyEvent.VK_LEFT:
502: key_state |= LEFT_ARROW;
503: break;
504: case KeyEvent.VK_RIGHT:
505: key_state |= RIGHT_ARROW;
506: break;
507: case KeyEvent.VK_PAGE_UP:
508: key_state |= PAGE_UP;
509: break;
510: case KeyEvent.VK_PAGE_DOWN:
511: key_state |= PAGE_DOWN;
512: break;
513: case KeyEvent.VK_EQUALS:
514: key_state |= HOME_NOMINAL;
515: break;
516: default:
517: switch (keyChar) {
518: case '-':
519: key_state |= MINUS_SIGN;
520: break;
521: }
522: }
523: }
524:
525: /* Check modifier keys */
526: if (keyEvent.isShiftDown())
527: modifier_key_state |= SHIFT;
528: else
529: modifier_key_state &= ~SHIFT;
530:
531: if (keyEvent.isMetaDown())
532: modifier_key_state |= META;
533: else
534: modifier_key_state &= ~META;
535:
536: if (keyEvent.isAltDown())
537: modifier_key_state |= ALT;
538: else
539: modifier_key_state &= ~ALT;
540:
541: //System.err.println("keyCode " + keyEvent.getKeyCode() + " modifiers " + keyEvent.getModifiers());
542: //System.err.println("SHIFT_MASK " + keyEvent.SHIFT_MASK);
543: //System.err.println("CTRL_MASK " + keyEvent.CTRL_MASK);
544: //System.err.println("META_MASK " + keyEvent.META_MASK);
545: //System.err.println("ALT_MASK " + keyEvent.ALT_MASK);
546:
547: }
548: }
|