001: /*
002: * $RCSfile: PhysicalEnvironment.java,v $
003: *
004: * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
006: *
007: * This code is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU General Public License version 2 only, as
009: * published by the Free Software Foundation. Sun designates this
010: * particular file as subject to the "Classpath" exception as provided
011: * by Sun in the LICENSE file that accompanied this code.
012: *
013: * This code is distributed in the hope that it will be useful, but WITHOUT
014: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: * version 2 for more details (a copy is included in the LICENSE file that
017: * accompanied this code).
018: *
019: * You should have received a copy of the GNU General Public License version
020: * 2 along with this work; if not, write to the Free Software Foundation,
021: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
022: *
023: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
024: * CA 95054 USA or visit www.sun.com if you need additional information or
025: * have any questions.
026: *
027: * $Revision: 1.5 $
028: * $Date: 2008/02/28 20:17:27 $
029: * $State: Exp $
030: */
031:
032: package javax.media.j3d;
033:
034: import javax.vecmath.*;
035: import java.awt.*;
036: import java.util.*;
037:
038: /**
039: * This object contains a specification of the physical environment in
040: * which the view will be generated. It is used to set up input
041: * devices (sensors) for head-tracking and other uses, and the audio
042: * output device. Sensors are indexed starting at zero.
043: *
044: * @see View
045: */
046:
047: public class PhysicalEnvironment extends Object {
048: /**
049: * The Sensor Index associated with the Head
050: */
051: int HeadIndex = 0;
052:
053: // The Sensor index associated with the Right Hand
054: int RightHandIndex = 1;
055:
056: // The Sensor index associated with the Left Hand
057: int LeftHandIndex = 2;
058:
059: // The current Dominant Hand Sensor Index
060: int DominantHandIndex = 1;
061:
062: // The current Non Dominant Hand Sensor Index
063: int NonDominantHandIndex = 2;
064:
065: //
066: // Coexistence coordinate system to tracker-base coordinate
067: // system transform. If head tracking is enabled, this transform
068: // is a calibration constant. If head tracking is not enabled,
069: // this transform is not used.
070: // This is used in both SCREEN_VIEW and HMD_VIEW modes.
071: //
072: Transform3D coexistenceToTrackerBase = new Transform3D();
073:
074: //
075: // Indicates whether the underlying hardware implementation
076: // supports tracking.
077: //
078: boolean trackingAvailable = false;
079:
080: // The view associated with this physical environment
081: // View view;
082:
083: //
084: // This variable specifies the policy Java 3D will use in placing
085: // the user's eye position relative to the user's head position
086: // (NOMINAL_SCREEN, NOMINAL_HEAD, or NOMINAL_FEET).
087: // It is used in the calibration process.
088: //
089: // TODO: this needs better explanation in the spec
090: int coexistenceCenterInPworldPolicy = View.NOMINAL_SCREEN;
091:
092: // Mask that indicates this PhysicalEnv's view dependence info. has changed,
093: // and CanvasViewCache may need to recompute the final view matries.
094: int peDirtyMask = (View.PE_COE_TO_TRACKER_BASE_DIRTY
095: | View.PE_TRACKING_AVAILABLE_DIRTY | View.PE_COE_CENTER_IN_PWORLD_POLICY_DIRTY);
096:
097: //// /**
098: //// * The offset in the user's dominant-hand-tracker coordinates
099: //// * to that hand's hot spot. This value is a calibration constant.
100: //// */
101: //// Vector3d dominantHandTrackerHotspotOffset;
102: ////
103: //// /**
104: //// * The offset in the user's non-dominant-hand-tracker coordinates
105: //// * to that hand's hot spot. This value is a calibration constant.
106: //// */
107: //// Vector3d nondominantHandTrackerHotspotOffset;
108:
109: //
110: // The number of sensor stored within the PhysicalEnvironment
111: //
112: int sensorCount;
113:
114: //
115: // Array of sensors
116: //
117: Sensor[] sensors;
118:
119: // Audio device associated with this PhysicalEnvironment
120: AudioDevice audioDevice = null;
121:
122: boolean sensorListChanged = false;
123:
124: Sensor[] sensorList = null;
125:
126: // A list of View Objects that refer to this
127: ArrayList users = new ArrayList();
128:
129: // Scheduler for input devices
130: InputDeviceScheduler inputsched;
131:
132: // store all inputDevices
133: Vector devices = new Vector(1);
134:
135: // Number of active view users
136: int activeViewRef = 0;
137:
138: // Hashtable that maps a PhysicalEnvironment to its InputDeviceScheduler
139: static Hashtable physicalEnvMap = new Hashtable();
140:
141: /**
142: * Constructs a PhysicalEnvironment object with default parameters.
143: * The default values are as follows:
144: * <ul>
145: * sensor count : 3<br>
146: * sensors : null (for all array elements)<br>
147: * head index : 0<br>
148: * right hand index : 1<br>
149: * left hand index : 2<br>
150: * dominant hand index : 1<br>
151: * nondominant hand index : 2<br>
152: * tracking available : false<br>
153: * audio device : null<br>
154: * input device list : empty<br>
155: * coexistence to tracker base transform : identity<br>
156: * coexistence center in pworld policy : View.NOMINAL_SCREEN<br>
157: * </ul>
158: */
159: public PhysicalEnvironment() {
160: this (3);
161: }
162:
163: // Add a user to the list of users
164: synchronized void removeUser(View view) {
165: int idx = users.indexOf(view);
166: if (idx >= 0) {
167: users.remove(idx);
168: }
169: }
170:
171: // Add a user to the list of users
172: synchronized void addUser(View view) {
173: int idx = users.indexOf(view);
174: if (idx < 0) {
175: users.add(view);
176: }
177: }
178:
179: // Add a user to the list of users
180: synchronized void notifyUsers() {
181: for (int i = users.size() - 1; i >= 0; i--) {
182: View view = (View) users.get(i);
183: view.repaint();
184: }
185: }
186:
187: /**
188: * Constructs and initializes a PhysicalEnvironment object with
189: * the specified number of sensors.
190: * @param sensorCount the number of sensors to create.
191: */
192: public PhysicalEnvironment(int sensorCount) {
193: this .sensorCount = sensorCount;
194: sensors = new Sensor[sensorCount];
195: for (int i = sensorCount - 1; i >= 0; i--) {
196: sensors[i] = null;
197: }
198: }
199:
200: /**
201: * Returns copy of Sensor references. Returns null for zero
202: * sensors, so user of method must check for null. Also, any of
203: * these sensors could be null.
204: */
205: Sensor[] getSensorList() {
206: synchronized (sensors) {
207: if (sensorListChanged) { // note: this is a rare case
208: sensorList = new Sensor[sensors.length];
209: for (int i = 0; i < sensors.length; i++) {
210: sensorList[i] = sensors[i];
211: }
212: sensorListChanged = false;
213:
214: }
215: return sensorList;
216: }
217: }
218:
219: /**
220: * Sets the specified AudioDevice object as the device through
221: * which audio rendering for this PhysicalEnvironment will be
222: * performed.
223: * @param device audio device object to be associated with this
224: * PhysicalEnvironment
225: */
226: public void setAudioDevice(AudioDevice device) {
227: audioDevice = device;
228: }
229:
230: /**
231: * Gets the audioDevice for this PhysicalEnvironment.
232: * @return audio device object associated with this PhysicalEnvironment
233: */
234: public AudioDevice getAudioDevice() {
235: return audioDevice;
236: }
237:
238: /**
239: * Create an enumerator that produces all input devices.
240: * @return an enumerator of all available devices
241: */
242: public Enumeration getAllInputDevices() {
243: return devices.elements();
244: }
245:
246: /**
247: * Add an input device to the list of input devices. User is
248: * responsible for initializing the device and setting the
249: * processing mode (streaming or polling).
250: * @param device the device to be added to the list of input devices
251: * @exception IllegalArgumentException if InputDevice.getProcessingMode()
252: * does not return one of BLOCKING, NON_BLOCKING, or DEMAND_DRIVEN.
253: */
254: public void addInputDevice(InputDevice device) {
255:
256: int driver_type = device.getProcessingMode();
257:
258: if ((driver_type == InputDevice.BLOCKING)
259: || (driver_type == InputDevice.NON_BLOCKING)
260: || (driver_type == InputDevice.DEMAND_DRIVEN)) {
261: synchronized (devices) {
262: devices.add(device);
263: if (inputsched != null) {
264: inputsched.addInputDevice(device);
265: }
266: }
267: } else {
268: throw new IllegalArgumentException(J3dI18N
269: .getString("PhysicalEnvironment0"));
270: }
271: }
272:
273: /**
274: * Remove an input device from the list of input devices.
275: * User is responsible for closing out the device and releasing
276: * the device resources.
277: * @param device the device to be removed
278: */
279: public void removeInputDevice(InputDevice device) {
280: devices.remove(device);
281: synchronized (devices) {
282: if (inputsched != null) {
283: inputsched.removeInputDevice(device);
284: }
285: }
286: }
287:
288: /**
289: * Sets the index of the head to the specified sensor index.
290: * @param index the new sensor index of the head
291: */
292: public void setHeadIndex(int index) {
293: HeadIndex = index;
294: synchronized (this ) {
295: computeTrackingAvailable();
296: peDirtyMask |= View.PE_TRACKING_AVAILABLE_DIRTY;
297: }
298: notifyUsers();
299: }
300:
301: /**
302: * Gets the sensor index of the head.
303: * @return the sensor index of the head
304: */
305: public int getHeadIndex() {
306: return HeadIndex;
307: }
308:
309: /**
310: * Sets the index of the right hand to the specified sensor index.
311: * @param index the new sensor index of the right hand
312: */
313: public void setRightHandIndex(int index) {
314: RightHandIndex = index;
315: notifyUsers();
316: }
317:
318: /**
319: * Gets the sensor index of the right hand.
320: * @return the sensor index of the right hand
321: */
322: public int getRightHandIndex() {
323: return RightHandIndex;
324: }
325:
326: /**
327: * Sets the index of the left hand to the specified sensor index.
328: * @param index the new sensor index of the left hand
329: */
330: public void setLeftHandIndex(int index) {
331: LeftHandIndex = index;
332: notifyUsers();
333: }
334:
335: /**
336: * Gets the sensor index of the left hand.
337: * @return the sensor index of the left hand
338: */
339: public int getLeftHandIndex() {
340: return LeftHandIndex;
341: }
342:
343: /**
344: * Sets the index of the dominant hand to the specified sensor index.
345: * @param index the new sensor index of the dominant hand
346: */
347: public void setDominantHandIndex(int index) {
348: DominantHandIndex = index;
349: notifyUsers();
350: }
351:
352: /**
353: * Gets the sensor index of the dominant hand.
354: * @return the sensor index of the dominant hand
355: */
356: public int getDominantHandIndex() {
357: return DominantHandIndex;
358: }
359:
360: /**
361: * Sets the index of the non-dominant hand to the specified sensor index.
362: * @param index the new sensor index of the non dominant hand
363: */
364: public void setNonDominantHandIndex(int index) {
365: NonDominantHandIndex = index;
366: notifyUsers();
367: }
368:
369: /**
370: * Gets the sensor index of the non-dominant hand.
371: * @return the sensor index of the non dominant hand
372: */
373: public int getNonDominantHandIndex() {
374: return NonDominantHandIndex;
375: }
376:
377: /**
378: * Set the sensor specified by the index to sensor provided; sensors are
379: * indexed starting at 0. All sensors must be registered via this
380: * method.
381: * @param index the sensor's index
382: * @param sensor the new sensor
383: */
384: public void setSensor(int index, Sensor sensor) {
385: synchronized (sensors) {
386: sensors[index] = sensor;
387: sensorListChanged = true;
388: }
389: synchronized (this ) {
390: computeTrackingAvailable();
391: peDirtyMask |= View.PE_TRACKING_AVAILABLE_DIRTY;
392: }
393:
394: notifyUsers();
395: }
396:
397: /**
398: * Gets the sensor specified by the index; sensors are indexed starting
399: * at 0.
400: * @param index the sensor's index
401: */
402: public Sensor getSensor(int index) {
403: // not synchronized, since the only way to write to sensors is
404: // via a public API call, and user shouldn't call Sensor with
405: // two threads
406: return sensors[index];
407: }
408:
409: /**
410: * Sets the coexistence coordinate system to tracker-base coordinate
411: * system transform. If head tracking is enabled, this transform
412: * is a calibration constant. If head tracking is not enabled,
413: * this transform is not used.
414: * This is used in both SCREEN_VIEW and HMD_VIEW modes.
415: * @param t the new transform
416: * @exception BadTransformException if the transform is not rigid
417: */
418: public void setCoexistenceToTrackerBase(Transform3D t) {
419: if (!t.isRigid()) {
420: throw new BadTransformException(J3dI18N
421: .getString("PhysicalEnvironment1"));
422: }
423: synchronized (this ) {
424: coexistenceToTrackerBase.setWithLock(t);
425: peDirtyMask |= View.PE_COE_TO_TRACKER_BASE_DIRTY;
426: }
427:
428: notifyUsers();
429: }
430:
431: /**
432: * Retrieves the coexistence coordinate system to tracker-base
433: * coordinate system transform and copies it into the specified
434: * Transform3D object.
435: * @param t the object that will receive the transform
436: */
437: public void getCoexistenceToTrackerBase(Transform3D t) {
438: t.set(coexistenceToTrackerBase);
439: }
440:
441: /**
442: * Returns a status flag indicating whether or not tracking
443: * is available.
444: * @return a flag telling whether tracking is available
445: */
446: public boolean getTrackingAvailable() {
447: return this .trackingAvailable;
448: }
449:
450: /**
451: * Sets the coexistence center in physical world policy.
452: * This setting determines how Java 3D places the
453: * user's eye point as a function of head position during the
454: * calibration process, one of View.NOMINAL_SCREEN,
455: * View.NOMINAL_HEAD, or View.NOMINAL_FEET.
456: * The default policy is View.NOMINAL_SCREEN.
457: * @param policy the new policy
458: */
459: public void setCoexistenceCenterInPworldPolicy(int policy) {
460: switch (policy) {
461: case View.NOMINAL_SCREEN:
462: case View.NOMINAL_HEAD:
463: case View.NOMINAL_FEET:
464: break;
465:
466: default:
467: throw new IllegalArgumentException(J3dI18N
468: .getString("PhysicalEnvironment2"));
469: }
470:
471: synchronized (this ) {
472: this .coexistenceCenterInPworldPolicy = policy;
473: peDirtyMask |= View.PE_COE_CENTER_IN_PWORLD_POLICY_DIRTY;
474: }
475: notifyUsers();
476: }
477:
478: /**
479: * Returns the current coexistence center in physical world policy.
480: * @return one of: View.NOMINAL_SCREEN, View.NOMINAL_HEAD, or
481: * View.NOMINAL_FEET
482: */
483: public int getCoexistenceCenterInPworldPolicy() {
484: return this .coexistenceCenterInPworldPolicy;
485: }
486:
487: /**
488: * Get the current sensor count.
489: * @return the number of sensor objects per PhysicalEnvironment object
490: */
491: public int getSensorCount() {
492: return sensorCount;
493: }
494:
495: /**
496: * Set the number of sensor objects per PhysicalEnvironmnet. This is a
497: * calibration parameter that should be set before setting any sensors
498: * in the PhysicalEnvironment object. This call associates 'count'
499: * Sensors with this object, and they are indexed from 0 to count-1.
500: * @param count the new sensor count
501: */
502: public void setSensorCount(int count) {
503:
504: Sensor[] tmp = new Sensor[count];
505: int i = 0;
506:
507: synchronized (sensors) {
508: int min = Math.min(count, sensorCount);
509: while (i < min) {
510: tmp[i] = sensors[i++];
511: }
512: while (i < count) {
513: tmp[i++] = null;
514: }
515: sensorCount = count;
516: sensorListChanged = true;
517: sensors = tmp;
518: }
519: notifyUsers();
520: }
521:
522: // (re-)compute the tracking available flag
523: private void computeTrackingAvailable() {
524: synchronized (sensors) {
525: trackingAvailable = ((HeadIndex < sensors.length) && (sensors[HeadIndex] != null));
526: }
527: }
528:
529: }
|