001: /*
002: * $RCSfile: PhysicalBody.java,v $
003: *
004: * Copyright 1996-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.6 $
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.util.ArrayList;
036:
037: /**
038: * This object contains a specification of the user's head.
039: * Attributes of this object are defined in the head coordinate system.
040: * The orgin is defined to be halfway between the left and right eye
041: * in the plane of the face.
042: * The x-axis extends to the right (of the head looking out from the head).
043: * The y-axis extends up. The z-axis extends to the rear of the head.
044: *
045: * @see View
046: */
047:
048: public class PhysicalBody extends Object {
049: // The X offset for each eye is 1/2 of the inter-pupilary distance
050: // This constant specifies the default IPD.
051: private static final double HALF_IPD = 0.033;
052:
053: // These offsets specify the default ear positions relative to the
054: // "center eye".
055: private static final double EAR_X = 0.080;
056: private static final double EAR_Y = -0.030;
057: private static final double EAR_Z = 0.095;
058:
059: /**
060: * The user's left eye's position in head coordinates.
061: */
062: Point3d leftEyePosition = new Point3d(-HALF_IPD, 0.0, 0.0);
063:
064: /**
065: * The user's right eye's position in head coordinates.
066: */
067: Point3d rightEyePosition = new Point3d(HALF_IPD, 0.0, 0.0);
068:
069: /**
070: * The user's left ear's position in head coordinates.
071: */
072: Point3d leftEarPosition = new Point3d(-EAR_X, EAR_Y, EAR_Z);
073:
074: /**
075: * The user's right ear's position in head coordinates.
076: */
077: Point3d rightEarPosition = new Point3d(EAR_X, EAR_Y, EAR_Z);
078:
079: /**
080: * The user's nominal eye height as measured
081: * from the ground plane.
082: */
083: double nominalEyeHeightFromGround = 1.68;
084:
085: /**
086: * The amount to offset the system's
087: * viewpoint from the user's current eye-point. This offset
088: * distance allows an "Over the shoulder" view of the scene
089: * as seen by the user.
090: *
091: * By default, we will use a Z value of 0.4572 meters (18 inches).
092: */
093: double nominalEyeOffsetFromNominalScreen = 0.4572;
094:
095: // Head to head-tracker coordinate system transform.
096: // If head tracking is enabled, this transform is a calibration
097: // constant. If head tracking is not enabled, this transform is
098: // not used.
099: // This is used in both SCREEN_VIEW and HMD_VIEW modes.
100: Transform3D headToHeadTracker = new Transform3D();
101:
102: // A list of View Objects that refer to this
103: ArrayList users = new ArrayList();
104:
105: // Mask that indicates this PhysicalBody's view dependence info. has changed,
106: // and CanvasViewCache may need to recompute the final view matries.
107: int pbDirtyMask = (View.PB_EYE_POSITION_DIRTY
108: | View.PB_EAR_POSITION_DIRTY
109: | View.PB_NOMINAL_EYE_HEIGHT_FROM_GROUND_DIRTY | View.PB_NOMINAL_EYE_OFFSET_FROM_NOMINAL_SCREEN_DIRTY);
110:
111: /**
112: * Constructs a PhysicalBody object with default parameters.
113: * The default values are as follows:
114: * <ul>
115: * left eye position : (-0.033, 0.0, 0.0)<br>
116: * right eye position : (0.033, 0.0, 0.0)<br>
117: * left ear position : (-0.080, -0.030, 0.095)<br>
118: * right ear position : (0.080, -0.030, 0.095)<br>
119: * nominal eye height from ground : 1.68<br>
120: * nominal eye offset from nominal screen : 0.4572<br>
121: * head to head tracker transform : identity<br>
122: * </ul>
123: */
124: public PhysicalBody() {
125: // Just use the defaults
126: initHeadToHeadTracker();
127: }
128:
129: // Add a user to the list of users
130: synchronized void removeUser(View view) {
131: int idx = users.indexOf(view);
132: if (idx >= 0) {
133: users.remove(idx);
134: }
135: }
136:
137: // Add a user to the list of users
138: synchronized void addUser(View view) {
139: int idx = users.indexOf(view);
140: if (idx < 0) {
141: users.add(view);
142: }
143: }
144:
145: // Add a user to the list of users
146: synchronized void notifyUsers() {
147: for (int i = users.size() - 1; i >= 0; i--) {
148: View view = (View) users.get(i);
149: // XXXX: notifyUsers should have a parameter denoting field changed
150: if (view.soundScheduler != null) {
151: view.soundScheduler
152: .setListenerFlag(SoundScheduler.EAR_POSITIONS_CHANGED
153: | SoundScheduler.EYE_POSITIONS_CHANGED);
154: }
155: view.repaint();
156: }
157: }
158:
159: /**
160: * Constructs and initializes a PhysicalBody object from the
161: * specified parameters.
162: * @param leftEyePosition the user's left eye position
163: * @param rightEyePosition the user's right eye position
164: */
165: public PhysicalBody(Point3d leftEyePosition,
166: Point3d rightEyePosition) {
167: this .leftEyePosition.set(leftEyePosition);
168: this .rightEyePosition.set(rightEyePosition);
169: initHeadToHeadTracker();
170: }
171:
172: /**
173: * Constructs and initializes a PhysicalBody object from the
174: * specified parameters.
175: * @param leftEyePosition the user's left eye position
176: * @param rightEyePosition the user's right eye position
177: * @param leftEarPosition the user's left ear position
178: * @param rightEarPosition the user's right ear position
179: */
180: public PhysicalBody(Point3d leftEyePosition,
181: Point3d rightEyePosition, Point3d leftEarPosition,
182: Point3d rightEarPosition) {
183:
184: this .leftEyePosition.set(leftEyePosition);
185: this .rightEyePosition.set(rightEyePosition);
186: this .leftEarPosition.set(leftEarPosition);
187: this .rightEarPosition.set(rightEarPosition);
188: initHeadToHeadTracker();
189: }
190:
191: /**
192: * Returns a string representation of this PhysicalBody's values.
193: */
194:
195: public String toString() {
196: return "eyePosition = (" + this .leftEyePosition + ", "
197: + this .rightEyePosition + ")\n" + "earPosition = ("
198: + this .leftEarPosition + ", " + this .rightEarPosition
199: + ")";
200: }
201:
202: /**
203: * Retrieves the user head object's left eye position and places
204: * that value in the specified object.
205: * @param position the object that will receive the left-eye's position
206: * in head coordinates
207: */
208: public void getLeftEyePosition(Point3d position) {
209: position.set(this .leftEyePosition);
210: }
211:
212: /**
213: * Sets the user head object's left eye position.
214: * @param position the left-eye's position in head coordinates
215: */
216: public void setLeftEyePosition(Point3d position) {
217: synchronized (this ) {
218: this .leftEyePosition.set(position);
219: pbDirtyMask |= View.PB_EYE_POSITION_DIRTY;
220: }
221: notifyUsers();
222: }
223:
224: /**
225: * Retrieves the user head object's right eye position and places
226: * that value in the specified object.
227: * @param position the object that will receive the right-eye's position
228: * in head coordinates
229: */
230: public void getRightEyePosition(Point3d position) {
231: position.set(this .rightEyePosition);
232: }
233:
234: /**
235: * Sets the user head object's right eye position.
236: * @param position the right-eye's position in head coordinates
237: */
238: public void setRightEyePosition(Point3d position) {
239: synchronized (this ) {
240: this .rightEyePosition.set(position);
241: pbDirtyMask |= View.PB_EYE_POSITION_DIRTY;
242: }
243: notifyUsers();
244: }
245:
246: /**
247: * Retrieves the user head object's left ear position and places
248: * that value in the specified object.
249: * @param position the object that will receive the left-ear's position
250: * in head coordinates
251: */
252: public void getLeftEarPosition(Point3d position) {
253: position.set(this .leftEarPosition);
254: }
255:
256: /**
257: * Sets the user head object's left ear position.
258: * @param position the left-ear's position in head coordinates
259: */
260: public void setLeftEarPosition(Point3d position) {
261: synchronized (this ) {
262: this .leftEarPosition.set(position);
263: pbDirtyMask |= View.PB_EAR_POSITION_DIRTY;
264: }
265: notifyUsers();
266: }
267:
268: /**
269: * Retrieves the user head object's right ear position and places
270: * that value in the specified object.
271: * @param position the object that will receive the right-ear's position
272: * in head coordinates
273: */
274: public void getRightEarPosition(Point3d position) {
275: position.set(this .rightEarPosition);
276: }
277:
278: /**
279: * Sets the user head object's right ear position.
280: * @param position the right-ear's position in head coordinates
281: */
282: public void setRightEarPosition(Point3d position) {
283: synchronized (this ) {
284: this .rightEarPosition.set(position);
285: pbDirtyMask |= View.PB_EAR_POSITION_DIRTY;
286: }
287: notifyUsers();
288: }
289:
290: /**
291: * Sets the nominal eye height from the ground plane.
292: * This parameter defines
293: * the distance from the origin of the user's head (the eyepoint) to
294: * the ground.
295: * It is used when the view attach policy is NOMINAL_FEET.
296: * @param height the nominal height of the eye above the ground plane
297: */
298: public void setNominalEyeHeightFromGround(double height) {
299: synchronized (this ) {
300: nominalEyeHeightFromGround = height;
301: pbDirtyMask |= View.PB_NOMINAL_EYE_HEIGHT_FROM_GROUND_DIRTY;
302: }
303: notifyUsers();
304: }
305:
306: /**
307: * Retrieves the nominal eye height from the ground plane.
308: * @return the current nominal eye height above the ground plane
309: */
310: public double getNominalEyeHeightFromGround() {
311: return nominalEyeHeightFromGround;
312: }
313:
314: /**
315: * Sets the nominal eye offset from the display screen.
316: * This parameter defines
317: * the distance from the origin of the user's head (the eyepoint), in it's
318: * nominal position, to
319: * the screen.
320: * It is used when the view attach policy is NOMINAL_HEAD or NOMINAL_FEET.
321: * This value is overridden to be the actual eyepoint when the window
322: * eyepoint policy is RELATIVE_TO_FIELD_OF_VIEW.
323: * @param offset the nominal offset from the eye to the screen
324: */
325: public void setNominalEyeOffsetFromNominalScreen(double offset) {
326: synchronized (this ) {
327: nominalEyeOffsetFromNominalScreen = offset;
328: pbDirtyMask |= View.PB_NOMINAL_EYE_OFFSET_FROM_NOMINAL_SCREEN_DIRTY;
329: }
330: notifyUsers();
331: }
332:
333: /**
334: * Retrieves the nominal eye offset from the display screen.
335: * @return the current nominal offset from the eye to the display screen
336: */
337: public double getNominalEyeOffsetFromNominalScreen() {
338: return nominalEyeOffsetFromNominalScreen;
339: }
340:
341: /**
342: * Sets the head to head-tracker coordinate system transform.
343: * If head tracking is enabled, this transform is a calibration
344: * constant. If head tracking is not enabled, this transform is
345: * not used.
346: * This is used in both SCREEN_VIEW and HMD_VIEW modes.
347: * @param t the new transform
348: * @exception BadTransformException if the transform is not rigid
349: */
350: public void setHeadToHeadTracker(Transform3D t) {
351: if (!t.isRigid()) {
352: throw new BadTransformException(J3dI18N
353: .getString("PhysicalBody0"));
354: }
355: headToHeadTracker.setWithLock(t);
356: notifyUsers();
357: }
358:
359: /**
360: * Retrieves the head to head-tracker coordinate system transform.
361: * @param t the object that will receive the transform
362: */
363: public void getHeadToHeadTracker(Transform3D t) {
364: t.set(headToHeadTracker);
365: }
366:
367: // Initialize the head to head-tracker transform
368: private void initHeadToHeadTracker() {
369: // By default the center of the crystal eyes tracker is 20mm down
370: // and 35 mm closer to the screen from the origin of head coordinates
371: // (the center eye).
372: Vector3d v = new Vector3d(0.0, 0.020, 0.035);
373: headToHeadTracker.set(v);
374: }
375: }
|