Source Code Cross Referenced for ViewInfo.java in  » 6.0-JDK-Modules » java-3d » com » sun » j3d » utils » universe » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » java 3d » com.sun.j3d.utils.universe 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: ViewInfo.java,v $
0003:         *
0004:         * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * Redistribution and use in source and binary forms, with or without
0007:         * modification, are permitted provided that the following conditions
0008:         * are met:
0009:         *
0010:         * - Redistribution of source code must retain the above copyright
0011:         *   notice, this list of conditions and the following disclaimer.
0012:         *
0013:         * - Redistribution in binary form must reproduce the above copyright
0014:         *   notice, this list of conditions and the following disclaimer in
0015:         *   the documentation and/or other materials provided with the
0016:         *   distribution.
0017:         *
0018:         * Neither the name of Sun Microsystems, Inc. or the names of
0019:         * contributors may be used to endorse or promote products derived
0020:         * from this software without specific prior written permission.
0021:         *
0022:         * This software is provided "AS IS," without a warranty of any
0023:         * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024:         * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025:         * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026:         * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027:         * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028:         * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029:         * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030:         * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031:         * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032:         * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033:         * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034:         * POSSIBILITY OF SUCH DAMAGES.
0035:         *
0036:         * You acknowledge that this software is not designed, licensed or
0037:         * intended for use in the design, construction, operation or
0038:         * maintenance of any nuclear facility.
0039:         *
0040:         * $Revision: 1.6 $
0041:         * $Date: 2007/02/09 17:20:45 $
0042:         * $State: Exp $
0043:         */
0044:
0045:        package com.sun.j3d.utils.universe;
0046:
0047:        import java.awt.GraphicsConfiguration;
0048:        import java.awt.GraphicsEnvironment;
0049:        import java.awt.Point;
0050:        import java.awt.Rectangle;
0051:        import java.text.DecimalFormat;
0052:        import java.text.FieldPosition;
0053:        import java.util.*;
0054:        import javax.media.j3d.*;
0055:        import javax.vecmath.*;
0056:
0057:        /**
0058:         * Provides methods to extract synchronized transform information from a View.
0059:         * These transforms are derived from application scene graph information, as
0060:         * opposed to similar core Java 3D methods that derive transforms from
0061:         * internally maintained data.	This allows updates to the scene graph to be
0062:         * synchronized with the current view platform position.<p>
0063:         *
0064:         * The architecture of the Java 3D 1.3 sample implementation introduces a
0065:         * frame latency between updates to the application scene graph structure and
0066:         * their effects on internal Java 3D state. <code>getImagePlateToVworld</code>
0067:         * and other methods in the core Java 3D classes use a transform from view
0068:         * platform coordinates to virtual world coordinates that can be out of date
0069:         * with respect to the state of the view platform as set by the application.
0070:         * When an application uses the transforms returned by those methods to update
0071:         * view dependent parts of the scene graph, those updates might not be
0072:         * synchronized with what the viewer actually sees.<p>
0073:         *
0074:         * The methods in this class work around this problem at the expense of
0075:         * querying the application state of the scene graph to get the current
0076:         * transform from view platform to virtual world coordinates.  This can
0077:         * involve a potential performance degradation, however, since the application
0078:         * scene graph state is not designed for high performance queries.  The view
0079:         * platform must also have <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability
0080:         * set, which potentially inhibits internal scene graph optimization.<p>
0081:         *
0082:         * On the other hand, application behaviors that create the view platform
0083:         * transformation directly will have access to it without the need to query it
0084:         * from the scene graph; in that case, the transforms from physical
0085:         * coordinates to view platform coordinates provided by this class are all
0086:         * that are needed.  The <code>ALLOW_LOCAL_TO_VWORLD_READ</code> view platform
0087:         * capability doesn't need to be set for these applications.<p>
0088:         *
0089:         * <b>Other Synchronization Issues</b><p>
0090:         * 
0091:         * Scene graph updates are guaranteed to take effect in the same frame only
0092:         * if run from the processStimulus() method of a Behavior. Updates from
0093:         * multiple behaviors are only guaranteed to take effect in the same frame if
0094:         * they're responding to a WakeupOnElapsedFrames(0) condition.  Use a single
0095:         * behavior to perform view dependent updates if possible; otherwise, use
0096:         * WakeupOnElapsedFrames(0) and set behavior scheduling intervals to ensure
0097:         * that behaviors that need the current view platform transform are run after
0098:         * it's set.  Updating scene graph elements from anything other than the
0099:         * Behavior thread, such as an external input thread or a renderer callback
0100:         * in Canvas3D, will not necessarily be synchronized with rendering.<p>
0101:         * 
0102:         * Direct updates to geometry data have a different frame latency than
0103:         * updates to scene graph transforms and structure. In the Java 3D 1.3
0104:         * architecture, updates to by-reference geometry arrays and texture data have
0105:         * a 1-frame latency, while updates to transforms and scene graph structure
0106:         * have a 2-frame latency.  Because of bug 4799494, which is outstanding
0107:         * in Java 3D 1.3.1, updates to by-copy geometry arrays also have a 1-frame
0108:         * latency.  It is therefore recommended that view dependent scene graph
0109:         * updates be limited to transforms and scene graph structure only.<p>
0110:         *
0111:         * If it is not possible to avoid updating geometry directly, then these
0112:         * updates must be delayed by one frame in order to remain synchronized with
0113:         * the view platform.  This can be accomplished by creating an additional
0114:         * behavior to actually update the geometry, separate from the behavior that
0115:         * computes the changes that need to be made based on current view state.  If
0116:         * the update behavior is awakened by a behavior post from the computing
0117:         * behavior then the update will be delayed by a single frame.<p>
0118:         * 
0119:         * <b>Implementation Notes</b><p>
0120:         * 
0121:         * This utility is essentially a rewrite of a few private Java 3D core
0122:         * classes, but designed for public use and source code availability.  The
0123:         * source code may be helpful in understanding some of the more complex
0124:         * aspects of the view model, especially with regards to various interactions
0125:         * between attributes which are not adequately documented.  None of the actual
0126:         * core Java 3D source code is used, but the code is designed to comply with
0127:         * the view model as defined by the Java 3D Specification, so it can be
0128:         * considered an alternative implementation.  This class will produce the
0129:         * same results as the Java 3D core implementation except for:<p><ul>
0130:         * 
0131:         * <li>The frame latency issue for virtual world transforms.</li><p>
0132:         * 
0133:         * <li>Active clip node status.	 If a clip node is active in the scene graph,
0134:         *     it should override the view's back clip plane.  This class has no such
0135:         *     information, so this can't be implemented.</li><p>
0136:         * 
0137:         * <li>"Infinite" view transforms for background geometry.  These are simply
0138:         *     the rotation components of the normal view transforms with adjusted
0139:         *     clip planes. Again, this function depends upon scene graph content
0140:         *     inaccessible to this class.</li><p>
0141:         * 
0142:         * <li>Small floating point precision differences resulting from the
0143:         *     alternative computations.</li><p>
0144:         * 
0145:         * <li>Bugs in this class and the Java 3D core.</li><p>
0146:         * 
0147:         * <li>Tracked head position.</li></ul><p>
0148:         * 
0149:         * The last item deserves some mention.	 Java 3D provides no way to directly
0150:         * query the tracked head position being used by the renderer.	The View's
0151:         * <code>getUserHeadToVworld</code> method always incorporates a virtual world
0152:         * transform that is out of date with respect to the application scene graph
0153:         * state.  ViewInfo reads data from the head tracking sensor directly, but
0154:         * since head trackers are continuous input devices, getting the same data
0155:         * that the renderer is using is unlikely.  See the source code for the
0156:         * private method <code>getHeadInfo</code> in this class for more information
0157:         * and possible workarounds.<p>
0158:         *
0159:         * <b>Thread Safety</b><p>
0160:         * 
0161:         * All transforms are lazily evaluated.  The <code>updateScreen</code>,
0162:         * <code>updateCanvas</code>, <code>updateViewPlatform</code>,
0163:         * <code>updateView</code>, and <code>updateHead</code> methods just set flags
0164:         * indicating that derived transforms need to be recomputed; they are safe to
0165:         * call from any thread.  <code>updateCanvas</code>, for example, can safely
0166:         * be called from an AWT event listener.<p>
0167:         *
0168:         * Screens and view platforms can be shared between separate views in the Java
0169:         * 3D view model.  To remain accurate, ViewInfo also allows this sharing.
0170:         * Since it is likely that a multi-view application has separate threads
0171:         * managing each view, potential concurrent modification of data associated
0172:         * with a screen or a view platform is internally synchronized in this class.
0173:         * It is safe for each thread to use its own instance of a ViewInfo
0174:         * corresponding to the view it is managing.<p>
0175:         *
0176:         * Otherwise, none of the other methods in this class are internally
0177:         * synchronized.  <i>Except for the update methods mentioned above, a single
0178:         * instance of ViewInfo should not be used by more than one concurrent thread
0179:         * without external synchronization.</i><p>
0180:         *
0181:         * @since Java 3D 1.3.1
0182:         */
0183:        public class ViewInfo {
0184:            private final static boolean verbose = false;
0185:
0186:            /**
0187:             * Indicates that updates to a Screen3D associated with the View should
0188:             * be automatically checked with each call to a public method in this
0189:             * class.
0190:             */
0191:            public final static int SCREEN_AUTO_UPDATE = 1;
0192:
0193:            /**
0194:             * Indicates that updates to a Canvas3D associated with the View should
0195:             * be automatically checked with each call to a public method in this
0196:             * class.
0197:             */
0198:            public final static int CANVAS_AUTO_UPDATE = 2;
0199:
0200:            /**
0201:             * Indicates that updates to the View should be automatically checked
0202:             * with each call to a public method in this class.
0203:             */
0204:            public final static int VIEW_AUTO_UPDATE = 4;
0205:
0206:            /**
0207:             * Indicates that updates to the tracked head position should be
0208:             * automatically checked with each call to a public method in this class.
0209:             */
0210:            public final static int HEAD_AUTO_UPDATE = 8;
0211:
0212:            /**
0213:             * Indicates that updates to the ViewPlatform <code>localToVworld</code>
0214:             * transform should be automatically checked with each call to a public
0215:             * method in this class.  The View must be attached to a ViewPlatform
0216:             * which is part of a live scene graph, and the ViewPlatform node must
0217:             * have its <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
0218:             */
0219:            public final static int PLATFORM_AUTO_UPDATE = 16;
0220:
0221:            //
0222:            // Screen3D and ViewPlatform instances are shared across multiple Views in
0223:            // the Java 3D view model.	Since ViewInfo is per-View and we want to
0224:            // cache screen and platform derived data, we maintain static references
0225:            // to the screens and platforms here.
0226:            // 
0227:            // From a design standpoint our ViewInfo objects should probably be in the
0228:            // scope of an object that encloses these maps so they can be gc'ed
0229:            // properly.  This is cumbersome with the current design constraints, so
0230:            // for now we provide an alternative constructor to override these static
0231:            // maps and a method to explicitly clear them.  The alternative
0232:            // constructor can be used to wrap this class into a multi-view context
0233:            // that provides the maps.
0234:            //
0235:            private static Map staticVpMap = new HashMap();
0236:            private static Map staticSiMap = new HashMap();
0237:
0238:            private Map screenMap = null;
0239:            private Map viewPlatformMap = null;
0240:
0241:            // The target View and some derived data.
0242:            private View view = null;
0243:            private Sensor headTracker = null;
0244:            private boolean useTracking = false;
0245:            private boolean clipVirtual = false;
0246:
0247:            // The current ViewPlatform and Canvas3D information used by this object.
0248:            private ViewPlatformInfo vpi = null;
0249:            private int canvasCount = 0;
0250:            private Map canvasMap = new HashMap();
0251:            private CanvasInfo[] canvasInfo = new CanvasInfo[1];
0252:
0253:            // This View's update flags.  The other update flags are maintained by
0254:            // ScreenInfo, CanvasInfo, and ViewPlatformInfo.
0255:            private boolean updateView = true;
0256:            private boolean updateHead = true;
0257:            private boolean autoUpdate = false;
0258:            private int autoUpdateFlags = 0;
0259:
0260:            // Cached View policies.  
0261:            private int viewPolicy = View.SCREEN_VIEW;
0262:            private int resizePolicy = View.PHYSICAL_WORLD;
0263:            private int movementPolicy = View.PHYSICAL_WORLD;
0264:            private int eyePolicy = View.RELATIVE_TO_FIELD_OF_VIEW;
0265:            private int projectionPolicy = View.PERSPECTIVE_PROJECTION;
0266:            private int frontClipPolicy = View.PHYSICAL_EYE;
0267:            private int backClipPolicy = View.PHYSICAL_EYE;
0268:            private int scalePolicy = View.SCALE_SCREEN_SIZE;
0269:            private boolean coeCentering = true;
0270:
0271:            // This View's cached transforms.  See ScreenInfo, CanvasInfo, and
0272:            // ViewPlatformInfo for the rest of the cached transforms.
0273:            private Transform3D coeToTrackerBase = null;
0274:            private Transform3D headToHeadTracker = null;
0275:
0276:            // These are from the head tracker read.
0277:            private Transform3D headTrackerToTrackerBase = null;
0278:            private Transform3D trackerBaseToHeadTracker = null;
0279:
0280:            // These are derived from the head tracker read.
0281:            private Transform3D headToTrackerBase = null;
0282:            private Transform3D coeToHeadTracker = null;
0283:
0284:            // Cached physical body and environment.
0285:            private PhysicalEnvironment env = null;
0286:            private PhysicalBody body = null;
0287:            private Point3d leftEyeInHead = new Point3d();
0288:            private Point3d rightEyeInHead = new Point3d();
0289:
0290:            // Temporary variables.  These could just be new'ed as needed, but we'll
0291:            // assume that ViewInfo instances are used much more than they're created.
0292:            private Vector3d v3d = new Vector3d();
0293:            private double[] m16d = new double[16];
0294:            private Point3d leftEye = new Point3d();
0295:            private Point3d rightEye = new Point3d();
0296:            private Map newMap = new HashMap();
0297:            private Set newSet = new HashSet();
0298:
0299:            /**
0300:             * Creates a new ViewInfo for the specified View.<p>
0301:             *
0302:             * Applications are responsible for informing this class of changes to the
0303:             * View, its Canvas3D and Screen3D components, the tracked head position,
0304:             * and the ViewPlatform's <code>localToVworld</code> transform.  These
0305:             * notifications are performed with the <code>updateView</code>,
0306:             * <code>updateCanvas</code>, <code>updateScreen</code>,
0307:             * <code>updateHead</code>, and <code>updateViewPlatform</code>
0308:             * methods.<p>
0309:             *
0310:             * The View must be attached to a ViewPlatform.  If the ViewPlatform is
0311:             * attached to a live scene graph, then <code>ALLOW_POLICY_READ</code>
0312:             * capability must be set on the ViewPlatform node.
0313:             * 
0314:             * @param view the View to use
0315:             * @see #updateView
0316:             * @see #updateCanvas updateCanvas(Canvas3D)
0317:             * @see #updateScreen updateScreen(Screen3D)
0318:             * @see #updateHead
0319:             * @see #updateViewPlatform
0320:             */
0321:            public ViewInfo(View view) {
0322:                this (view, 0);
0323:            }
0324:
0325:            /**
0326:             * Creates a new ViewInfo for the specified View.  The View must be
0327:             * attached to a ViewPlatform.  If the ViewPlatform is attached to a live
0328:             * scene graph, then <code>ALLOW_POLICY_READ</code> capability must be set
0329:             * on the ViewPlatform node.
0330:             * 
0331:             * @param view the View to use<p>
0332:             * @param autoUpdateFlags a logical <code>OR</code> of any of the
0333:             *	<code>VIEW_AUTO_UPDATE</code>, <code>CANVAS_AUTO_UPDATE</code>,
0334:             *	<code>SCREEN_AUTO_UPDATE</code>, <code>HEAD_AUTO_UPDATE</code>, or
0335:             *	<code>PLATFORM_AUTO_UPDATE</code> flags to control whether changes to
0336:             *	the View, its Canvas3D or Screen3D components, the tracked head
0337:             *	position, or the ViewPlatform's <code>localToVworld</code> transform
0338:             *	are checked automatically with each call to a public method of this
0339:             *	class; if a flag is not set, then the application must inform this
0340:             *	class of updates to the corresponding data
0341:             */
0342:            public ViewInfo(View view, int autoUpdateFlags) {
0343:                this (view, autoUpdateFlags, staticSiMap, staticVpMap);
0344:            }
0345:
0346:            /**
0347:             * Creates a new ViewInfo for the specified View.  The View must be
0348:             * attached to a ViewPlatform.  If the ViewPlatform is attached to a live
0349:             * scene graph, then <code>ALLOW_POLICY_READ</code> capability must be set
0350:             * on the ViewPlatform node.<p>
0351:             *
0352:             * ViewInfo caches Screen3D and ViewPlatform data, but Screen3D and
0353:             * ViewPlatform instances are shared across multiple Views in the Java 3D
0354:             * view model.  Since ViewInfo is per-View, all ViewInfo constructors
0355:             * except for this one use static references to manage the shared Screen3D
0356:             * and ViewPlatform objects.  In this constructor, however, the caller
0357:             * supplies two Map instances to hold these references for all ViewInfo
0358:             * instances, so static references can be avoided; it can be used to wrap
0359:             * this class into a multi-view context that provides the required
0360:             * maps.<p>
0361:             *
0362:             * Alternatively, the other constructors can be used by calling
0363:             * <code>ViewInfo.clear</code> when done with ViewInfo, or by simply
0364:             * retaining the static references until the JVM exits.<p>
0365:             * 
0366:             * @param view the View to use<p>
0367:             * @param autoUpdateFlags a logical <code>OR</code> of any of the
0368:             *	<code>VIEW_AUTO_UPDATE</code>, <code>CANVAS_AUTO_UPDATE</code>,
0369:             *	<code>SCREEN_AUTO_UPDATE</code>, <code>HEAD_AUTO_UPDATE</code>, or
0370:             *	<code>PLATFORM_AUTO_UPDATE</code> flags to control whether changes to
0371:             *	the View, its Canvas3D or Screen3D components, the tracked head
0372:             *	position, or the ViewPlatform's <code>localToVworld</code> transform
0373:             *	are checked automatically with each call to a public method of this
0374:             *	class; if a flag is not set, then the application must inform this
0375:             *	class of updates to the corresponding data<p>
0376:             * @param screenMap a writeable Map to hold Screen3D information
0377:             * @param viewPlatformMap a writeable Map to hold ViewPlatform information
0378:             */
0379:            public ViewInfo(View view, int autoUpdateFlags, Map screenMap,
0380:                    Map viewPlatformMap) {
0381:
0382:                if (verbose)
0383:                    System.err.println("ViewInfo: init " + hashCode());
0384:                if (view == null)
0385:                    throw new IllegalArgumentException("View is null");
0386:                if (screenMap == null)
0387:                    throw new IllegalArgumentException("screenMap is null");
0388:                if (viewPlatformMap == null)
0389:                    throw new IllegalArgumentException(
0390:                            "viewPlatformMap is null");
0391:
0392:                this .view = view;
0393:                this .screenMap = screenMap;
0394:                this .viewPlatformMap = viewPlatformMap;
0395:
0396:                if (autoUpdateFlags == 0) {
0397:                    this .autoUpdate = false;
0398:                } else {
0399:                    this .autoUpdate = true;
0400:                    this .autoUpdateFlags = autoUpdateFlags;
0401:                }
0402:
0403:                getViewInfo();
0404:            }
0405:
0406:            /**
0407:             * Gets the current transforms from image plate coordinates to view
0408:             * platform coordinates and copies them into the given Transform3Ds.<p>
0409:             * 
0410:             * With a monoscopic canvas the image plate transform is copied to the
0411:             * first argument and the second argument is not used.  For a stereo
0412:             * canvas the first argument receives the left image plate transform, and
0413:             * if the second argument is non-null it receives the right image plate
0414:             * transform.  These transforms are always the same unless a head mounted
0415:             * display driven by a single stereo canvas is in use.
0416:             *
0417:             * @param c3d the Canvas3D associated with the image plate
0418:             * @param ip2vpl the Transform3D to receive the left transform
0419:             * @param ip2vpr the Transform3D to receive the right transform, or null
0420:             */
0421:            public void getImagePlateToViewPlatform(Canvas3D c3d,
0422:                    Transform3D ip2vpl, Transform3D ip2vpr) {
0423:
0424:                CanvasInfo ci = updateCache(c3d, "getImagePlateToViewPlatform",
0425:                        false);
0426:
0427:                getImagePlateToViewPlatform(ci);
0428:                ip2vpl.set(ci.plateToViewPlatform);
0429:                if (ci.useStereo && ip2vpr != null)
0430:                    ip2vpr.set(ci.rightPlateToViewPlatform);
0431:            }
0432:
0433:            private void getImagePlateToViewPlatform(CanvasInfo ci) {
0434:                if (ci.updatePlateToViewPlatform) {
0435:                    if (verbose)
0436:                        System.err.println("updating PlateToViewPlatform");
0437:                    if (ci.plateToViewPlatform == null)
0438:                        ci.plateToViewPlatform = new Transform3D();
0439:
0440:                    getCoexistenceToImagePlate(ci);
0441:                    getViewPlatformToCoexistence(ci);
0442:
0443:                    ci.plateToViewPlatform.mul(ci.coeToPlate,
0444:                            ci.viewPlatformToCoe);
0445:                    ci.plateToViewPlatform.invert();
0446:
0447:                    if (ci.useStereo) {
0448:                        if (ci.rightPlateToViewPlatform == null)
0449:                            ci.rightPlateToViewPlatform = new Transform3D();
0450:
0451:                        ci.rightPlateToViewPlatform.mul(ci.coeToRightPlate,
0452:                                ci.viewPlatformToCoe);
0453:                        ci.rightPlateToViewPlatform.invert();
0454:                    }
0455:                    ci.updatePlateToViewPlatform = false;
0456:                    if (verbose)
0457:                        t3dPrint(ci.plateToViewPlatform, "plateToVp");
0458:                }
0459:            }
0460:
0461:            /**
0462:             * Gets the current transforms from image plate coordinates to virtual
0463:             * world coordinates and copies them into the given Transform3Ds.<p>
0464:             * 
0465:             * With a monoscopic canvas the image plate transform is copied to the
0466:             * first argument and the second argument is not used.  For a stereo
0467:             * canvas the first argument receives the left image plate transform, and
0468:             * if the second argument is non-null it receives the right image plate
0469:             * transform.  These transforms are always the same unless a head mounted
0470:             * display driven by a single stereo canvas is in use.<p>
0471:             *
0472:             * The View must be attached to a ViewPlatform which is part of a live
0473:             * scene graph, and the ViewPlatform node must have its
0474:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
0475:             *
0476:             * @param c3d the Canvas3D associated with the image plate
0477:             * @param ip2vwl the Transform3D to receive the left transform
0478:             * @param ip2vwr the Transform3D to receive the right transform, or null
0479:             */
0480:            public void getImagePlateToVworld(Canvas3D c3d, Transform3D ip2vwl,
0481:                    Transform3D ip2vwr) {
0482:
0483:                CanvasInfo ci = updateCache(c3d, "getImagePlateToVworld", true);
0484:                getImagePlateToVworld(ci);
0485:                ip2vwl.set(ci.plateToVworld);
0486:                if (ci.useStereo && ip2vwr != null)
0487:                    ip2vwr.set(ci.rightPlateToVworld);
0488:            }
0489:
0490:            private void getImagePlateToVworld(CanvasInfo ci) {
0491:                if (ci.updatePlateToVworld) {
0492:                    if (verbose)
0493:                        System.err.println("updating PlateToVworld");
0494:                    if (ci.plateToVworld == null)
0495:                        ci.plateToVworld = new Transform3D();
0496:
0497:                    getImagePlateToViewPlatform(ci);
0498:                    ci.plateToVworld.mul(vpi.viewPlatformToVworld,
0499:                            ci.plateToViewPlatform);
0500:
0501:                    if (ci.useStereo) {
0502:                        if (ci.rightPlateToVworld == null)
0503:                            ci.rightPlateToVworld = new Transform3D();
0504:
0505:                        ci.rightPlateToVworld.mul(vpi.viewPlatformToVworld,
0506:                                ci.rightPlateToViewPlatform);
0507:                    }
0508:                    ci.updatePlateToVworld = false;
0509:                }
0510:            }
0511:
0512:            /**
0513:             * Gets the current transforms from coexistence coordinates to image plate
0514:             * coordinates and copies them into the given Transform3Ds.  The default
0515:             * coexistence centering enable and window movement policies are
0516:             * <code>true</code> and <code>PHYSICAL_WORLD</code> respectively, which
0517:             * will center coexistence coordinates to the middle of the canvas,
0518:             * aligned with the screen (image plate). A movement policy of
0519:             * <code>VIRTUAL_WORLD</code> centers coexistence coordinates to the
0520:             * middle of the screen.<p>
0521:             *
0522:             * If coexistence centering is turned off, then canvases and screens can
0523:             * have arbitrary positions with respect to coexistence, set through the
0524:             * the Screen3D <code>trackerBaseToImagePlate</code> transform and the
0525:             * PhysicalEnvironment <code>coexistenceToTrackerBase</code> transform.
0526:             * These are calibration constants used for multiple fixed screen displays.
0527:             * For head mounted displays the transform is determined by the user head
0528:             * position along with calibration parameters found in Screen3D and
0529:             * PhysicalBody. (See the source code for the private method
0530:             * <code>getEyesHMD</code> for more information).<p>
0531:             *
0532:             * With a monoscopic canvas the image plate transform is copied to the
0533:             * first argument and the second argument is not used.  For a stereo
0534:             * canvas the first argument receives the left image plate transform, and
0535:             * if the second argument is non-null it receives the right image plate
0536:             * transform.  These transforms are always the same unless a head mounted
0537:             * display driven by a single stereo canvas is in use.<p>
0538:             *
0539:             * @param c3d the Canvas3D associated with the image plate
0540:             * @param coe2ipl the Transform3D to receive the left transform
0541:             * @param coe2ipr the Transform3D to receive the right transform, or null
0542:             */
0543:            public void getCoexistenceToImagePlate(Canvas3D c3d,
0544:                    Transform3D coe2ipl, Transform3D coe2ipr) {
0545:
0546:                CanvasInfo ci = updateCache(c3d, "getCoexistenceToImagePlate",
0547:                        false);
0548:                getCoexistenceToImagePlate(ci);
0549:                coe2ipl.set(ci.coeToPlate);
0550:                if (ci.useStereo && coe2ipr != null)
0551:                    coe2ipr.set(ci.coeToRightPlate);
0552:            }
0553:
0554:            private void getCoexistenceToImagePlate(CanvasInfo ci) {
0555:                //
0556:                // This method will always set coeToRightPlate even if stereo is not
0557:                // in use.  This is necessary so that getEyeToImagePlate() can handle
0558:                // a monoscopic view policy of CYCLOPEAN_EYE_VIEW (which averages the
0559:                // left and right eye positions) when the eyepoints are expressed in
0560:                // coexistence coordinates or are derived from the tracked head.
0561:                // 
0562:                if (ci.updateCoeToPlate) {
0563:                    if (verbose)
0564:                        System.err.println("updating CoeToPlate");
0565:                    if (ci.coeToPlate == null) {
0566:                        ci.coeToPlate = new Transform3D();
0567:                        ci.coeToRightPlate = new Transform3D();
0568:                    }
0569:                    if (viewPolicy == View.HMD_VIEW) {
0570:                        // Head mounted displays have their image plates fixed with
0571:                        // respect to the head, so get the head position in
0572:                        // coexistence.
0573:                        ci.coeToPlate.mul(ci.si.headTrackerToLeftPlate,
0574:                                coeToHeadTracker);
0575:                        if (ci.useStereo)
0576:                            // This is the only case in the view model in which the
0577:                            // right plate transform could be different from the left.
0578:                            ci.coeToRightPlate.mul(
0579:                                    ci.si.headTrackerToRightPlate,
0580:                                    coeToHeadTracker);
0581:                        else
0582:                            ci.coeToRightPlate.set(ci.coeToPlate);
0583:                    } else if (coeCentering) {
0584:                        // The default, for fixed single screen displays with no
0585:                        // motion tracking.  The transform is just a translation.
0586:                        if (movementPolicy == View.PHYSICAL_WORLD)
0587:                            // The default.  Coexistence is centered in the window.
0588:                            v3d.set(ci.canvasX + (ci.canvasWidth / 2.0),
0589:                                    ci.canvasY + (ci.canvasHeight / 2.0), 0.0);
0590:                        else
0591:                            // Coexistence is centered in the screen.
0592:                            v3d.set(ci.si.screenWidth / 2.0,
0593:                                    ci.si.screenHeight / 2.0, 0.0);
0594:
0595:                        ci.coeToPlate.set(v3d);
0596:                        ci.coeToRightPlate.set(v3d);
0597:                    } else {
0598:                        // Coexistence centering should be false for multiple fixed
0599:                        // screens and/or motion tracking.  trackerBaseToImagePlate
0600:                        // and coexistenceToTrackerBase are used explicitly.
0601:                        ci.coeToPlate.mul(ci.si.trackerBaseToPlate,
0602:                                coeToTrackerBase);
0603:                        ci.coeToRightPlate.set(ci.coeToPlate);
0604:                    }
0605:                    ci.updateCoeToPlate = false;
0606:                    if (verbose)
0607:                        t3dPrint(ci.coeToPlate, "coeToPlate");
0608:                }
0609:            }
0610:
0611:            /**
0612:             * Gets the current transform from view platform coordinates to
0613:             * coexistence coordinates and copies it into the given transform.	View
0614:             * platform coordinates are always aligned with coexistence coordinates
0615:             * but may differ in scale and in Y and Z offset.  The scale is derived
0616:             * from the window resize and screen scale policies, while the offset is
0617:             * derived from the view attach policy.<p>
0618:             *
0619:             * Java 3D constructs a view from the physical position of the eyes
0620:             * relative to the physical positions of the image plates; it then uses a
0621:             * view platform to position that physical configuration into the virtual
0622:             * world and from there computes the correct projections of the virtual
0623:             * world onto the physical image plates.  Coexistence coordinates are used
0624:             * to place the physical positions of the view platform, eyes, head, image
0625:             * plate, sensors, and tracker base in relation to each other. The view
0626:             * platform is positioned with respect to the virtual world through the
0627:             * scene graph, so the view platform to coexistence transform defines the
0628:             * space in which the virtual world and physical world coexist.<p>
0629:             * 
0630:             * This method requires a Canvas3D.	 A different transform may be returned
0631:             * for each canvas in the view if any of the following apply:<p><ul>
0632:             *
0633:             * <li>The window resize policy is <code>PHYSICAL_WORLD</code>, which
0634:             *	   alters the scale depending upon the width of the canvas.</li><p>
0635:             *
0636:             * <li>The screen scale policy is <code>SCALE_SCREEN_SIZE</code>,
0637:             *	   which alters the scale depending upon the width of the screen
0638:             *	   associated with the canvas.</li><p>
0639:             *
0640:             * <li>A window eyepoint policy of <code>RELATIVE_TO_FIELD_OF_VIEW</code>
0641:             *	   with a view attach policy of <code>NOMINAL_HEAD</code> in effect,
0642:             *	   which sets the view platform Z offset in coexistence coordinates
0643:             *	   based on the width of the canvas.  These are the default policies.
0644:             *	   The offset also follows the width of the canvas when the
0645:             *	   <code>NOMINAL_FEET</code> view attach policy is used.</li></ul>
0646:             *
0647:             * @param c3d the Canvas3D to use
0648:             * @param vp2coe the Transform3D to receive the transform
0649:             */
0650:            public void getViewPlatformToCoexistence(Canvas3D c3d,
0651:                    Transform3D vp2coe) {
0652:
0653:                CanvasInfo ci = updateCache(c3d,
0654:                        "getViewPlatformToCoexistence", false);
0655:
0656:                getViewPlatformToCoexistence(ci);
0657:                vp2coe.set(ci.viewPlatformToCoe);
0658:            }
0659:
0660:            private void getViewPlatformToCoexistence(CanvasInfo ci) {
0661:                if (!ci.updateViewPlatformToCoe)
0662:                    return;
0663:                if (verbose)
0664:                    System.err.println("updating ViewPlatformToCoe");
0665:                if (ci.viewPlatformToCoe == null)
0666:                    ci.viewPlatformToCoe = new Transform3D();
0667:                //
0668:                // The scale from view platform coordinates to coexistence coordinates
0669:                // has two components -- the screen scale and the window scale.	 The
0670:                // window scale only applies if the resize policy is PHYSICAL_WORLD.
0671:                //
0672:                // This scale is not the same as the vworld to view platform scale.
0673:                // The latter is contained in the view platform's localToVworld
0674:                // transform as defined by the scene graph.  The complete scale factor
0675:                // from virtual units to physical units is the product of the vworld
0676:                // to view platform scale and the view platform to coexistence scale.
0677:                //
0678:                getScreenScale(ci);
0679:                if (resizePolicy == View.PHYSICAL_WORLD)
0680:                    ci.viewPlatformToCoe.setScale(ci.screenScale
0681:                            * ci.windowScale);
0682:                else
0683:                    ci.viewPlatformToCoe.setScale(ci.screenScale);
0684:
0685:                if (viewPolicy == View.HMD_VIEW) {
0686:                    // In HMD mode view platform coordinates are the same as
0687:                    // coexistence coordinates, except for scale.
0688:                    ci.updateViewPlatformToCoe = false;
0689:                    return;
0690:                }
0691:
0692:                //
0693:                // Otherwise, get the offset of the origin of view platform
0694:                // coordinates relative to the origin of coexistence.  This is is
0695:                // specified by two policies: the view platform's view attach policy
0696:                // and the physical environment's coexistence center in pworld policy.
0697:                //
0698:                double eyeOffset;
0699:                double eyeHeight = body.getNominalEyeHeightFromGround();
0700:                int viewAttachPolicy = view.getViewPlatform()
0701:                        .getViewAttachPolicy();
0702:                int pworldAttachPolicy = env
0703:                        .getCoexistenceCenterInPworldPolicy();
0704:
0705:                if (eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW)
0706:                    // The view platform origin is the same as the eye position.
0707:                    eyeOffset = ci.getFieldOfViewOffset();
0708:                else
0709:                    // The view platform origin is independent of the eye position.
0710:                    eyeOffset = body.getNominalEyeOffsetFromNominalScreen();
0711:
0712:                if (pworldAttachPolicy == View.NOMINAL_SCREEN) {
0713:                    // The default.  The physical coexistence origin locates the
0714:                    // nominal screen.	This is rarely, if ever, set to anything
0715:                    // else, and the intended effects of the other settings are
0716:                    // not well documented.
0717:                    if (viewAttachPolicy == View.NOMINAL_HEAD) {
0718:                        // The default.	 The view platform origin is at the origin
0719:                        // of the nominal head in coexistence coordinates, offset
0720:                        // from the screen along +Z.  If the window eyepoint
0721:                        // policy is RELATIVE_TO_FIELD_OF_VIEW, then the eyepoint
0722:                        // is the same as the view platform origin.
0723:                        v3d.set(0.0, 0.0, eyeOffset);
0724:                    } else if (viewAttachPolicy == View.NOMINAL_SCREEN) {
0725:                        // View platform and coexistence are the same except for
0726:                        // scale.
0727:                        v3d.set(0.0, 0.0, 0.0);
0728:                    } else {
0729:                        // The view platform origin is at the ground beneath the
0730:                        // head.
0731:                        v3d.set(0.0, -eyeHeight, eyeOffset);
0732:                    }
0733:                } else if (pworldAttachPolicy == View.NOMINAL_HEAD) {
0734:                    // The physical coexistence origin locates the nominal head.
0735:                    if (viewAttachPolicy == View.NOMINAL_HEAD) {
0736:                        // The view platform origin is set to the head;
0737:                        // coexistence and view platform coordinates differ only
0738:                        // in scale.
0739:                        v3d.set(0.0, 0.0, 0.0);
0740:                    } else if (viewAttachPolicy == View.NOMINAL_SCREEN) {
0741:                        // The view platform is set in front of the head, at the
0742:                        // nominal screen location.
0743:                        v3d.set(0.0, 0.0, -eyeOffset);
0744:                    } else {
0745:                        // The view platform origin is at the ground beneath the
0746:                        // head.
0747:                        v3d.set(0.0, -eyeHeight, 0.0);
0748:                    }
0749:                } else {
0750:                    // The physical coexistence origin locates the nominal feet.
0751:                    if (viewAttachPolicy == View.NOMINAL_HEAD) {
0752:                        v3d.set(0.0, eyeHeight, 0.0);
0753:                    } else if (viewAttachPolicy == View.NOMINAL_SCREEN) {
0754:                        v3d.set(0.0, eyeHeight, -eyeOffset);
0755:                    } else {
0756:                        v3d.set(0.0, 0.0, 0.0);
0757:                    }
0758:                }
0759:
0760:                ci.viewPlatformToCoe.setTranslation(v3d);
0761:                ci.updateViewPlatformToCoe = false;
0762:                if (verbose)
0763:                    t3dPrint(ci.viewPlatformToCoe, "vpToCoe");
0764:            }
0765:
0766:            /**
0767:             * Gets the current transform from coexistence coordinates to
0768:             * view platform coordinates and copies it into the given transform.<p>
0769:             *
0770:             * This method requires a Canvas3D.	 The returned transform may differ
0771:             * across canvases for the same reasons as discussed in the description of
0772:             * <code>getViewPlatformToCoexistence</code>.<p>
0773:             *
0774:             * @param c3d the Canvas3D to use
0775:             * @param coe2vp the Transform3D to receive the transform
0776:             * @see #getViewPlatformToCoexistence
0777:             *       getViewPlatformToCoexistence(Canvas3D, Transform3D)
0778:             */
0779:            public void getCoexistenceToViewPlatform(Canvas3D c3d,
0780:                    Transform3D coe2vp) {
0781:
0782:                CanvasInfo ci = updateCache(c3d,
0783:                        "getCoexistenceToViewPlatform", false);
0784:
0785:                getCoexistenceToViewPlatform(ci);
0786:                coe2vp.set(ci.coeToViewPlatform);
0787:            }
0788:
0789:            private void getCoexistenceToViewPlatform(CanvasInfo ci) {
0790:                if (ci.updateCoeToViewPlatform) {
0791:                    if (verbose)
0792:                        System.err.println("updating CoeToViewPlatform");
0793:                    if (ci.coeToViewPlatform == null)
0794:                        ci.coeToViewPlatform = new Transform3D();
0795:
0796:                    getViewPlatformToCoexistence(ci);
0797:                    ci.coeToViewPlatform.invert(ci.viewPlatformToCoe);
0798:
0799:                    ci.updateCoeToViewPlatform = false;
0800:                    if (verbose)
0801:                        t3dPrint(ci.coeToViewPlatform, "coeToVp");
0802:                }
0803:            }
0804:
0805:            /**
0806:             * Gets the current transform from coexistence coordinates to virtual
0807:             * world coordinates and copies it into the given transform.<p>
0808:             *
0809:             * The View must be attached to a ViewPlatform which is part of a live
0810:             * scene graph, and the ViewPlatform node must have its
0811:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.<p>
0812:             *
0813:             * This method requires a Canvas3D.	 The returned transform may differ
0814:             * across canvases for the same reasons as discussed in the description of
0815:             * <code>getViewPlatformToCoexistence</code>.<p>
0816:             *
0817:             * @param c3d the Canvas3D to use
0818:             * @param coe2vw the Transform3D to receive the transform
0819:             * @see #getViewPlatformToCoexistence
0820:             *       getViewPlatformToCoexistence(Canvas3D, Transform3D)
0821:             */
0822:            public void getCoexistenceToVworld(Canvas3D c3d, Transform3D coe2vw) {
0823:
0824:                CanvasInfo ci = updateCache(c3d, "getCoexistenceToVworld", true);
0825:                getCoexistenceToVworld(ci);
0826:                coe2vw.set(ci.coeToVworld);
0827:            }
0828:
0829:            private void getCoexistenceToVworld(CanvasInfo ci) {
0830:                if (ci.updateCoeToVworld) {
0831:                    if (verbose)
0832:                        System.err.println("updating CoexistenceToVworld");
0833:                    if (ci.coeToVworld == null)
0834:                        ci.coeToVworld = new Transform3D();
0835:
0836:                    getCoexistenceToViewPlatform(ci);
0837:                    ci.coeToVworld.mul(vpi.viewPlatformToVworld,
0838:                            ci.coeToViewPlatform);
0839:
0840:                    ci.updateCoeToVworld = false;
0841:                }
0842:            }
0843:
0844:            /**
0845:             * Gets the transforms from eye coordinates to image plate coordinates and
0846:             * copies them into the Transform3Ds specified.<p>
0847:             * 
0848:             * When head tracking is used the eye positions are taken from the head
0849:             * position and set in relation to the image plates with each Screen3D's
0850:             * <code>trackerBaseToImagePlate</code> transform.	Otherwise the window
0851:             * eyepoint policy is used to derive the eyepoint relative to the image
0852:             * plate.  When using a head mounted display the eye position is
0853:             * determined solely by calibration constants in Screen3D and
0854:             * PhysicalBody; see the source code for the private method
0855:             * <code>getEyesHMD</code> for more information.<p>
0856:             * 
0857:             * Eye coordinates are always aligned with image plate coordinates, so
0858:             * these transforms are always just translations.  With a monoscopic
0859:             * canvas the eye transform is copied to the first argument and the second
0860:             * argument is not used.  For a stereo canvas the first argument receives
0861:             * the left eye transform, and if the second argument is non-null it
0862:             * receives the right eye transform.
0863:             *
0864:             * @param c3d the Canvas3D associated with the image plate
0865:             * @param e2ipl the Transform3D to receive left transform
0866:             * @param e2ipr the Transform3D to receive right transform, or null
0867:             */
0868:            public void getEyeToImagePlate(Canvas3D c3d, Transform3D e2ipl,
0869:                    Transform3D e2ipr) {
0870:
0871:                CanvasInfo ci = updateCache(c3d, "getEyeToImagePlate", false);
0872:                getEyeToImagePlate(ci);
0873:                e2ipl.set(ci.eyeToPlate);
0874:                if (ci.useStereo && e2ipr != null)
0875:                    e2ipr.set(ci.rightEyeToPlate);
0876:            }
0877:
0878:            private void getEyeToImagePlate(CanvasInfo ci) {
0879:                if (ci.updateEyeInPlate) {
0880:                    if (verbose)
0881:                        System.err.println("updating EyeInPlate");
0882:                    if (ci.eyeToPlate == null)
0883:                        ci.eyeToPlate = new Transform3D();
0884:
0885:                    if (viewPolicy == View.HMD_VIEW) {
0886:                        getEyesHMD(ci);
0887:                    } else if (useTracking) {
0888:                        getEyesTracked(ci);
0889:                    } else {
0890:                        getEyesFixedScreen(ci);
0891:                    }
0892:                    ci.updateEyeInPlate = false;
0893:                    if (verbose)
0894:                        System.err.println("eyeInPlate: " + ci.eyeInPlate);
0895:                }
0896:            }
0897:
0898:            //
0899:            // Get physical eye positions for head mounted displays.  These are
0900:            // determined solely by the headTrackerToImagePlate and headToHeadTracker
0901:            // calibration constants defined by Screen3D and the PhysicalBody.
0902:            //
0903:            // Note that headTrackerLeftToImagePlate and headTrackerToRightImagePlate
0904:            // should be set according to the *apparent* position and orientation of
0905:            // the image plates, relative to the head and head tracker, as viewed
0906:            // through the HMD optics.	This is also true of the "physical" screen
0907:            // width and height specified by the Screen3D -- they should be the
0908:            // *apparent* width and height as viewed through the HMD optics.  They
0909:            // must be set directly through the Screen3D methods; the default pixel
0910:            // metrics of 90 pixels/inch used by Java 3D aren't appropriate for HMD
0911:            // optics. 
0912:            // 
0913:            // Most HMDs have 100% overlap between the left and right displays; in
0914:            // that case, headTrackerToLeftImagePlate and headTrackerToRightImagePlate
0915:            // should be identical.  The HMD manufacturer's specifications of the
0916:            // optics in terms of field of view, image overlap, and distance to the
0917:            // focal plane should be used to derive these parameters.
0918:            //
0919:            private void getEyesHMD(CanvasInfo ci) {
0920:                if (ci.useStereo) {
0921:                    // This case is for head mounted displays driven by a single
0922:                    // stereo canvas on a single screen.  These use a field sequential
0923:                    // stereo signal to split the left and right images.
0924:                    leftEye.set(leftEyeInHead);
0925:                    headToHeadTracker.transform(leftEye);
0926:                    ci.si.headTrackerToLeftPlate.transform(leftEye,
0927:                            ci.eyeInPlate);
0928:                    rightEye.set(rightEyeInHead);
0929:                    headToHeadTracker.transform(rightEye);
0930:                    ci.si.headTrackerToRightPlate.transform(rightEye,
0931:                            ci.rightEyeInPlate);
0932:                    if (ci.rightEyeToPlate == null)
0933:                        ci.rightEyeToPlate = new Transform3D();
0934:
0935:                    v3d.set(ci.rightEyeInPlate);
0936:                    ci.rightEyeToPlate.set(v3d);
0937:                } else {
0938:                    // This case is for 2-channel head mounted displays driven by two
0939:                    // monoscopic screens, one for each eye.
0940:                    switch (ci.monoscopicPolicy) {
0941:                    case View.LEFT_EYE_VIEW:
0942:                        leftEye.set(leftEyeInHead);
0943:                        headToHeadTracker.transform(leftEye);
0944:                        ci.si.headTrackerToLeftPlate.transform(leftEye,
0945:                                ci.eyeInPlate);
0946:                        break;
0947:                    case View.RIGHT_EYE_VIEW:
0948:                        rightEye.set(rightEyeInHead);
0949:                        headToHeadTracker.transform(rightEye);
0950:                        ci.si.headTrackerToRightPlate.transform(rightEye,
0951:                                ci.eyeInPlate);
0952:                        break;
0953:                    case View.CYCLOPEAN_EYE_VIEW:
0954:                    default:
0955:                        throw new IllegalStateException(
0956:                                "Illegal monoscopic view policy for 2-channel HMD");
0957:                    }
0958:                }
0959:                v3d.set(ci.eyeInPlate);
0960:                ci.eyeToPlate.set(v3d);
0961:            }
0962:
0963:            private void getEyesTracked(CanvasInfo ci) {
0964:                leftEye.set(leftEyeInHead);
0965:                rightEye.set(rightEyeInHead);
0966:                headToTrackerBase.transform(leftEye);
0967:                headToTrackerBase.transform(rightEye);
0968:                if (coeCentering) {
0969:                    // Coexistence and tracker base coordinates are the same.
0970:                    // Centering is normally turned off for tracking.
0971:                    getCoexistenceToImagePlate(ci);
0972:                    ci.coeToPlate.transform(leftEye);
0973:                    ci.coeToRightPlate.transform(rightEye);
0974:                } else {
0975:                    // The normal policy for head tracking.
0976:                    ci.si.trackerBaseToPlate.transform(leftEye);
0977:                    ci.si.trackerBaseToPlate.transform(rightEye);
0978:                }
0979:                setEyeScreenRelative(ci, leftEye, rightEye);
0980:            }
0981:
0982:            private void getEyesFixedScreen(CanvasInfo ci) {
0983:                switch (eyePolicy) {
0984:                case View.RELATIVE_TO_FIELD_OF_VIEW:
0985:                    double z = ci.getFieldOfViewOffset();
0986:                    setEyeWindowRelative(ci, z, z);
0987:                    break;
0988:                case View.RELATIVE_TO_WINDOW:
0989:                    setEyeWindowRelative(ci, ci.leftManualEyeInPlate.z,
0990:                            ci.rightManualEyeInPlate.z);
0991:                    break;
0992:                case View.RELATIVE_TO_SCREEN:
0993:                    setEyeScreenRelative(ci, ci.leftManualEyeInPlate,
0994:                            ci.rightManualEyeInPlate);
0995:                    break;
0996:                case View.RELATIVE_TO_COEXISTENCE:
0997:                    view.getLeftManualEyeInCoexistence(leftEye);
0998:                    view.getRightManualEyeInCoexistence(rightEye);
0999:
1000:                    getCoexistenceToImagePlate(ci);
1001:                    ci.coeToPlate.transform(leftEye);
1002:                    ci.coeToRightPlate.transform(rightEye);
1003:                    setEyeScreenRelative(ci, leftEye, rightEye);
1004:                    break;
1005:                }
1006:            }
1007:
1008:            private void setEyeWindowRelative(CanvasInfo ci, double leftZ,
1009:                    double rightZ) {
1010:
1011:                // Eye position X is offset from the window center.
1012:                double centerX = (ci.canvasX + (ci.canvasWidth / 2.0));
1013:                leftEye.x = centerX + leftEyeInHead.x;
1014:                rightEye.x = centerX + rightEyeInHead.x;
1015:
1016:                // Eye position Y is always the canvas center.
1017:                leftEye.y = rightEye.y = ci.canvasY + (ci.canvasHeight / 2.0);
1018:
1019:                // Eye positions Z are as given.
1020:                leftEye.z = leftZ;
1021:                rightEye.z = rightZ;
1022:
1023:                setEyeScreenRelative(ci, leftEye, rightEye);
1024:            }
1025:
1026:            private void setEyeScreenRelative(CanvasInfo ci, Point3d leftEye,
1027:                    Point3d rightEye) {
1028:                if (ci.useStereo) {
1029:                    ci.eyeInPlate.set(leftEye);
1030:                    ci.rightEyeInPlate.set(rightEye);
1031:
1032:                    if (ci.rightEyeToPlate == null)
1033:                        ci.rightEyeToPlate = new Transform3D();
1034:
1035:                    v3d.set(ci.rightEyeInPlate);
1036:                    ci.rightEyeToPlate.set(v3d);
1037:                } else {
1038:                    switch (ci.monoscopicPolicy) {
1039:                    case View.CYCLOPEAN_EYE_VIEW:
1040:                        ci.eyeInPlate.set((leftEye.x + rightEye.x) / 2.0,
1041:                                (leftEye.y + rightEye.y) / 2.0,
1042:                                (leftEye.z + rightEye.z) / 2.0);
1043:                        break;
1044:                    case View.LEFT_EYE_VIEW:
1045:                        ci.eyeInPlate.set(leftEye);
1046:                        break;
1047:                    case View.RIGHT_EYE_VIEW:
1048:                        ci.eyeInPlate.set(rightEye);
1049:                        break;
1050:                    }
1051:                }
1052:                v3d.set(ci.eyeInPlate);
1053:                ci.eyeToPlate.set(v3d);
1054:            }
1055:
1056:            /**
1057:             * Gets the current transforms from eye coordinates to view platform
1058:             * coordinates and copies them into the given Transform3Ds.<p>
1059:             * 
1060:             * With a monoscopic canvas the eye transform is copied to the first
1061:             * argument and the second argument is not used.  For a stereo canvas the
1062:             * first argument receives the left eye transform, and if the second
1063:             * argument is non-null it receives the right eye transform.<p>
1064:             *
1065:             * This method requires a Canvas3D.	 When using a head mounted display,
1066:             * head tracking with fixed screens, or a window eyepoint policy of
1067:             * <code>RELATIVE_TO_COEXISTENCE</code>, then the transforms returned may
1068:             * be different for each canvas if stereo is not in use and they have
1069:             * different monoscopic view policies.  They may additionally differ in
1070:             * scale across canvases with the <code>PHYSICAL_WORLD</code> window
1071:             * resize policy or the <code>SCALE_SCREEN_SIZE</code> screen scale
1072:             * policy, which alter the scale depending upon the width of the canvas or
1073:             * the width of the screen respectively.<p>
1074:             * 
1075:             * With window eyepoint policies of <code>RELATIVE_TO_FIELD_OF_VIEW</code>,
1076:             * <code>RELATIVE_TO_SCREEN</code>, or <code>RELATIVE_TO_WINDOW</code>,
1077:             * then the transforms returned may differ across canvases due to
1078:             * the following additional conditions:<p><ul>
1079:             * 
1080:             * <li>The window eyepoint policy is <code>RELATIVE_TO_WINDOW</code> or
1081:             *	   <code>RELATIVE_TO_SCREEN</code>, in which case the manual eye
1082:             *	   position in image plate can be set differently for each
1083:             *	   canvas.</li><p>
1084:             *
1085:             * <li>The window eyepoint policy is <code>RELATIVE_TO_FIELD_OF_VIEW</code>
1086:             *	   and the view attach policy is <code>NOMINAL_SCREEN</code>, which
1087:             *	   decouples the view platform's canvas Z offset from the eyepoint's
1088:             *	   canvas Z offset.</li><p> 
1089:             *
1090:             * <li>The eyepoint X and Y coordinates are centered in the canvas with a
1091:             *	   window eyepoint policy of <code>RELATIVE_TO_FIELD_OF_VIEW</code>
1092:             *	   or <code>RELATIVE_TO_WINDOW</code>, and a window movement policy
1093:             *	   of <code>VIRTUAL_WORLD</code> centers the view platform's X and Y
1094:             *	   coordinates to the middle of the screen.</li><p>
1095:             *
1096:             * <li>Coexistence centering is set false, which allows each canvas and
1097:             *	   screen to have a different position with respect to coexistence
1098:             *	   coordinates.</li></ul>
1099:             *
1100:             * @param c3d the Canvas3D to use
1101:             * @param e2vpl the Transform3D to receive the left transform
1102:             * @param e2vpr the Transform3D to receive the right transform, or null
1103:             */
1104:            public void getEyeToViewPlatform(Canvas3D c3d, Transform3D e2vpl,
1105:                    Transform3D e2vpr) {
1106:
1107:                CanvasInfo ci = updateCache(c3d, "getEyeToViewPlatform", false);
1108:                getEyeToViewPlatform(ci);
1109:                e2vpl.set(ci.eyeToViewPlatform);
1110:                if (ci.useStereo && e2vpr != null)
1111:                    e2vpr.set(ci.rightEyeToViewPlatform);
1112:            }
1113:
1114:            private void getEyeToViewPlatform(CanvasInfo ci) {
1115:                if (ci.updateEyeToViewPlatform) {
1116:                    if (verbose)
1117:                        System.err.println("updating EyeToViewPlatform");
1118:                    if (ci.eyeToViewPlatform == null)
1119:                        ci.eyeToViewPlatform = new Transform3D();
1120:
1121:                    getEyeToImagePlate(ci);
1122:                    getImagePlateToViewPlatform(ci);
1123:                    ci.eyeToViewPlatform.mul(ci.plateToViewPlatform,
1124:                            ci.eyeToPlate);
1125:
1126:                    if (ci.useStereo) {
1127:                        if (ci.rightEyeToViewPlatform == null)
1128:                            ci.rightEyeToViewPlatform = new Transform3D();
1129:
1130:                        ci.rightEyeToViewPlatform
1131:                                .mul(ci.rightPlateToViewPlatform,
1132:                                        ci.rightEyeToPlate);
1133:                    }
1134:                    ci.updateEyeToViewPlatform = false;
1135:                    if (verbose)
1136:                        t3dPrint(ci.eyeToViewPlatform, "eyeToVp");
1137:                }
1138:            }
1139:
1140:            /**
1141:             * Gets the current transforms from view platform coordinates to eye
1142:             * coordinates and copies them into the given Transform3Ds.<p>
1143:             * 
1144:             * With a monoscopic canvas the eye transform is copied to the first
1145:             * argument and the second argument is not used.  For a stereo canvas the
1146:             * first argument receives the left eye transform, and if the second
1147:             * argument is non-null it receives the right eye transform.<p>
1148:             *
1149:             * This method requires a Canvas3D.	 The transforms returned may differ
1150:             * across canvases for all the same reasons discussed in the description
1151:             * of <code>getEyeToViewPlatform</code>.
1152:             * 
1153:             * @param c3d the Canvas3D to use
1154:             * @param vp2el the Transform3D to receive the left transform
1155:             * @param vp2er the Transform3D to receive the right transform, or null
1156:             * @see #getEyeToViewPlatform
1157:             *       getEyeToViewPlatform(Canvas3D, Transform3D, Transform3D)
1158:             */
1159:            public void getViewPlatformToEye(Canvas3D c3d, Transform3D vp2el,
1160:                    Transform3D vp2er) {
1161:
1162:                CanvasInfo ci = updateCache(c3d, "getViewPlatformToEye", false);
1163:                getViewPlatformToEye(ci);
1164:                vp2el.set(ci.viewPlatformToEye);
1165:                if (ci.useStereo && vp2er != null)
1166:                    vp2er.set(ci.viewPlatformToRightEye);
1167:            }
1168:
1169:            private void getViewPlatformToEye(CanvasInfo ci) {
1170:                if (ci.updateViewPlatformToEye) {
1171:                    if (verbose)
1172:                        System.err.println("updating ViewPlatformToEye");
1173:                    if (ci.viewPlatformToEye == null)
1174:                        ci.viewPlatformToEye = new Transform3D();
1175:
1176:                    getEyeToViewPlatform(ci);
1177:                    ci.viewPlatformToEye.invert(ci.eyeToViewPlatform);
1178:
1179:                    if (ci.useStereo) {
1180:                        if (ci.viewPlatformToRightEye == null)
1181:                            ci.viewPlatformToRightEye = new Transform3D();
1182:
1183:                        ci.viewPlatformToRightEye
1184:                                .invert(ci.rightEyeToViewPlatform);
1185:                    }
1186:                    ci.updateViewPlatformToEye = false;
1187:                }
1188:            }
1189:
1190:            /**
1191:             * Gets the current transforms from eye coordinates to virtual world
1192:             * coordinates and copies them into the given Transform3Ds.<p>
1193:             * 
1194:             * With a monoscopic canvas the eye transform is copied to the first
1195:             * argument and the second argument is not used.  For a stereo canvas the
1196:             * first argument receives the left eye transform, and if the second
1197:             * argument is non-null it receives the right eye transform.<p>
1198:             *
1199:             * The View must be attached to a ViewPlatform which is part of a live
1200:             * scene graph, and the ViewPlatform node must have its
1201:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.<p>
1202:             *
1203:             * This method requires a Canvas3D.	 The transforms returned may differ
1204:             * across canvases for all the same reasons discussed in the description
1205:             * of <code>getEyeToViewPlatform</code>.  
1206:             *
1207:             * @param c3d the Canvas3D to use
1208:             * @param e2vwl the Transform3D to receive the left transform
1209:             * @param e2vwr the Transform3D to receive the right transform, or null
1210:             * @see #getEyeToViewPlatform
1211:             *       getEyeToViewPlatform(Canvas3D, Transform3D, Transform3D)
1212:             */
1213:            public void getEyeToVworld(Canvas3D c3d, Transform3D e2vwl,
1214:                    Transform3D e2vwr) {
1215:
1216:                CanvasInfo ci = updateCache(c3d, "getEyeToVworld", true);
1217:                getEyeToVworld(ci);
1218:                e2vwl.set(ci.eyeToVworld);
1219:                if (ci.useStereo && e2vwr != null)
1220:                    e2vwr.set(ci.rightEyeToVworld);
1221:            }
1222:
1223:            private void getEyeToVworld(CanvasInfo ci) {
1224:                if (ci.updateEyeToVworld) {
1225:                    if (verbose)
1226:                        System.err.println("updating EyeToVworld");
1227:                    if (ci.eyeToVworld == null)
1228:                        ci.eyeToVworld = new Transform3D();
1229:
1230:                    getEyeToViewPlatform(ci);
1231:                    ci.eyeToVworld.mul(vpi.viewPlatformToVworld,
1232:                            ci.eyeToViewPlatform);
1233:
1234:                    if (ci.useStereo) {
1235:                        if (ci.rightEyeToVworld == null)
1236:                            ci.rightEyeToVworld = new Transform3D();
1237:
1238:                        ci.rightEyeToVworld.mul(vpi.viewPlatformToVworld,
1239:                                ci.rightEyeToViewPlatform);
1240:                    }
1241:                    ci.updateEyeToVworld = false;
1242:                }
1243:            }
1244:
1245:            /**
1246:             * Gets the transforms from eye coordinates to clipping coordinates
1247:             * and copies them into the given Transform3Ds.  These transforms take
1248:             * a viewing volume bounded by the physical canvas edges and the
1249:             * physical front and back clip planes and project it into a range
1250:             * bound to [-1.0 .. +1.0] on each of the X, Y, and Z axes.	 If a
1251:             * perspective projection has been specified then the physical image
1252:             * plate eye location defines the apex of a viewing frustum;
1253:             * otherwise, the orientation of the image plate determines the
1254:             * direction of a parallel projection.<p>
1255:             * 
1256:             * With a monoscopic canvas the projection transform is copied to the
1257:             * first argument and the second argument is not used.  For a stereo
1258:             * canvas the first argument receives the left projection transform,
1259:             * and if the second argument is non-null it receives the right
1260:             * projection transform.<p>
1261:             *
1262:             * If either of the clip policies <code>VIRTUAL_EYE</code> or
1263:             * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
1264:             * to a ViewPlatform that is part of a live scene graph and that has its
1265:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
1266:             * scale factor of 1.0 will be used for the scale factor from virtual
1267:             * world units to view platform units.
1268:             * 
1269:             * @param c3d the Canvas3D to use
1270:             * @param e2ccl the Transform3D to receive left transform
1271:             * @param e2ccr the Transform3D to receive right transform, or null
1272:             */
1273:            public void getProjection(Canvas3D c3d, Transform3D e2ccl,
1274:                    Transform3D e2ccr) {
1275:
1276:                CanvasInfo ci = updateCache(c3d, "getProjection", true);
1277:                getProjection(ci);
1278:                e2ccl.set(ci.projection);
1279:                if (ci.useStereo && e2ccr != null)
1280:                    e2ccr.set(ci.rightProjection);
1281:            }
1282:
1283:            private void getProjection(CanvasInfo ci) {
1284:                if (ci.updateProjection) {
1285:                    if (verbose)
1286:                        System.err.println("updating Projection");
1287:                    if (ci.projection == null)
1288:                        ci.projection = new Transform3D();
1289:
1290:                    getEyeToImagePlate(ci);
1291:                    getClipDistances(ci);
1292:
1293:                    // Note: core Java 3D code insists that the back clip plane
1294:                    // relative to the image plate must be the same left back clip
1295:                    // distance for both the left and right eye.  Not sure why this
1296:                    // should be, but the same is done here for compatibility.
1297:                    double backClip = getBackClip(ci, ci.eyeInPlate);
1298:                    computeProjection(ci, ci.eyeInPlate, getFrontClip(ci,
1299:                            ci.eyeInPlate), backClip, ci.projection);
1300:
1301:                    if (ci.useStereo) {
1302:                        if (ci.rightProjection == null)
1303:                            ci.rightProjection = new Transform3D();
1304:
1305:                        computeProjection(ci, ci.rightEyeInPlate, getFrontClip(
1306:                                ci, ci.rightEyeInPlate), backClip,
1307:                                ci.rightProjection);
1308:                    }
1309:                    ci.updateProjection = false;
1310:                    if (verbose)
1311:                        t3dPrint(ci.projection, "projection");
1312:                }
1313:            }
1314:
1315:            /**
1316:             * Gets the transforms from clipping coordinates to eye coordinates
1317:             * and copies them into the given Transform3Ds.  These transforms take
1318:             * the clip space volume bounded by the range [-1.0 .. + 1.0] on each
1319:             * of the X, Y, and Z and project it into eye coordinates.<p>
1320:             * 
1321:             * With a monoscopic canvas the projection transform is copied to the
1322:             * first argument and the second argument is not used.  For a stereo
1323:             * canvas the first argument receives the left projection transform, and
1324:             * if the second argument is non-null it receives the right projection
1325:             * transform.<p>
1326:             *
1327:             * If either of the clip policies <code>VIRTUAL_EYE</code> or
1328:             * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
1329:             * to a ViewPlatform that is part of a live scene graph and that has its
1330:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
1331:             * scale factor of 1.0 will be used for the scale factor from virtual
1332:             * world units to view platform units.
1333:             * 
1334:             * @param c3d the Canvas3D to use
1335:             * @param cc2el the Transform3D to receive left transform
1336:             * @param cc2er the Transform3D to receive right transform, or null
1337:             */
1338:            public void getInverseProjection(Canvas3D c3d, Transform3D cc2el,
1339:                    Transform3D cc2er) {
1340:
1341:                CanvasInfo ci = updateCache(c3d, "getInverseProjection", true);
1342:                getInverseProjection(ci);
1343:                cc2el.set(ci.inverseProjection);
1344:                if (ci.useStereo && cc2er != null)
1345:                    cc2er.set(ci.inverseRightProjection);
1346:            }
1347:
1348:            private void getInverseProjection(CanvasInfo ci) {
1349:                if (ci.updateInverseProjection) {
1350:                    if (verbose)
1351:                        System.err.println("updating InverseProjection");
1352:                    if (ci.inverseProjection == null)
1353:                        ci.inverseProjection = new Transform3D();
1354:
1355:                    getProjection(ci);
1356:                    ci.inverseProjection.invert(ci.projection);
1357:
1358:                    if (ci.useStereo) {
1359:                        if (ci.inverseRightProjection == null)
1360:                            ci.inverseRightProjection = new Transform3D();
1361:
1362:                        ci.inverseRightProjection.invert(ci.rightProjection);
1363:                    }
1364:                    ci.updateInverseProjection = false;
1365:                }
1366:            }
1367:
1368:            /**
1369:             * Gets the transforms from clipping coordinates to view platform
1370:             * coordinates and copies them into the given Transform3Ds.	 These
1371:             * transforms take the clip space volume bounded by the range
1372:             * [-1.0 .. +1.0] on each of the X, Y, and Z axes and project into
1373:             * the view platform coordinate system.<p>
1374:             * 
1375:             * With a monoscopic canvas the projection transform is copied to the
1376:             * first argument and the second argument is not used.  For a stereo
1377:             * canvas the first argument receives the left projection transform, and
1378:             * if the second argument is non-null it receives the right projection
1379:             * transform.<p>
1380:             *
1381:             * If either of the clip policies <code>VIRTUAL_EYE</code> or
1382:             * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
1383:             * to a ViewPlatform that is part of a live scene graph and that has its
1384:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
1385:             * scale factor of 1.0 will be used for the scale factor from virtual
1386:             * world units to view platform units.
1387:             * 
1388:             * @param c3d the Canvas3D to use
1389:             * @param cc2vpl the Transform3D to receive left transform
1390:             * @param cc2vpr the Transform3D to receive right transform, or null
1391:             */
1392:            public void getInverseViewPlatformProjection(Canvas3D c3d,
1393:                    Transform3D cc2vpl, Transform3D cc2vpr) {
1394:
1395:                CanvasInfo ci = updateCache(c3d,
1396:                        "getInverseViewPlatformProjection", true);
1397:
1398:                getInverseViewPlatformProjection(ci);
1399:                cc2vpl.set(ci.inverseViewPlatformProjection);
1400:                if (ci.useStereo & cc2vpr != null)
1401:                    cc2vpr.set(ci.inverseViewPlatformRightProjection);
1402:            }
1403:
1404:            private void getInverseViewPlatformProjection(CanvasInfo ci) {
1405:                if (ci.updateInverseViewPlatformProjection) {
1406:                    if (verbose)
1407:                        System.err.println("updating InverseVpProjection");
1408:                    if (ci.inverseViewPlatformProjection == null)
1409:                        ci.inverseViewPlatformProjection = new Transform3D();
1410:
1411:                    getInverseProjection(ci);
1412:                    getEyeToViewPlatform(ci);
1413:                    ci.inverseViewPlatformProjection.mul(ci.eyeToViewPlatform,
1414:                            ci.inverseProjection);
1415:
1416:                    if (ci.useStereo) {
1417:                        if (ci.inverseViewPlatformRightProjection == null)
1418:                            ci.inverseViewPlatformRightProjection = new Transform3D();
1419:
1420:                        ci.inverseViewPlatformRightProjection.mul(
1421:                                ci.rightEyeToViewPlatform,
1422:                                ci.inverseRightProjection);
1423:                    }
1424:                    ci.updateInverseVworldProjection = false;
1425:                }
1426:            }
1427:
1428:            /**
1429:             * Gets the transforms from clipping coordinates to virtual world
1430:             * coordinates and copies them into the given Transform3Ds.	 These
1431:             * transforms take the clip space volume bounded by the range
1432:             * [-1.0 .. +1.0] on each of the X, Y, and Z axes and project into
1433:             * the virtual world.<p>
1434:             * 
1435:             * With a monoscopic canvas the projection transform is copied to the
1436:             * first argument and the second argument is not used.  For a stereo
1437:             * canvas the first argument receives the left projection transform, and
1438:             * if the second argument is non-null it receives the right projection
1439:             * transform.<p>
1440:             *
1441:             * The View must be attached to a ViewPlatform which is part of a live
1442:             * scene graph, and the ViewPlatform node must have its
1443:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
1444:             *
1445:             * @param c3d the Canvas3D to use
1446:             * @param cc2vwl the Transform3D to receive left transform
1447:             * @param cc2vwr the Transform3D to receive right transform, or null
1448:             */
1449:            public void getInverseVworldProjection(Canvas3D c3d,
1450:                    Transform3D cc2vwl, Transform3D cc2vwr) {
1451:
1452:                CanvasInfo ci = updateCache(c3d, "getInverseVworldProjection",
1453:                        true);
1454:                getInverseVworldProjection(ci);
1455:                cc2vwl.set(ci.inverseVworldProjection);
1456:                if (ci.useStereo & cc2vwr != null)
1457:                    cc2vwr.set(ci.inverseVworldRightProjection);
1458:            }
1459:
1460:            private void getInverseVworldProjection(CanvasInfo ci) {
1461:                if (ci.updateInverseVworldProjection) {
1462:                    if (verbose)
1463:                        System.err.println("updating InverseVwProjection");
1464:                    if (ci.inverseVworldProjection == null)
1465:                        ci.inverseVworldProjection = new Transform3D();
1466:
1467:                    getInverseViewPlatformProjection(ci);
1468:                    ci.inverseVworldProjection.mul(vpi.viewPlatformToVworld,
1469:                            ci.inverseViewPlatformProjection);
1470:
1471:                    if (ci.useStereo) {
1472:                        if (ci.inverseVworldRightProjection == null)
1473:                            ci.inverseVworldRightProjection = new Transform3D();
1474:
1475:                        ci.inverseVworldRightProjection.mul(
1476:                                vpi.viewPlatformToVworld,
1477:                                ci.inverseViewPlatformRightProjection);
1478:                    }
1479:                    ci.updateInverseVworldProjection = false;
1480:                }
1481:            }
1482:
1483:            //
1484:            // Compute a projection matrix from the given eye position in image plate,
1485:            // the front and back clip Z positions in image plate, and the current
1486:            // canvas position in image plate.
1487:            // 
1488:            private void computeProjection(CanvasInfo ci, Point3d eye,
1489:                    double front, double back, Transform3D p) {
1490:
1491:                // Convert everything to eye coordinates.
1492:                double lx = ci.canvasX - eye.x; // left   (low x)
1493:                double ly = ci.canvasY - eye.y; // bottom (low y)
1494:                double hx = (ci.canvasX + ci.canvasWidth) - eye.x; // right  (high x)
1495:                double hy = (ci.canvasY + ci.canvasHeight) - eye.y; // top    (high y)
1496:                double nz = front - eye.z; // front  (near z)
1497:                double fz = back - eye.z; // back   (far z)
1498:                double iz = -eye.z; // plate  (image z)
1499:
1500:                if (projectionPolicy == View.PERSPECTIVE_PROJECTION)
1501:                    computePerspectiveProjection(lx, ly, hx, hy, iz, nz, fz,
1502:                            m16d);
1503:                else
1504:                    computeParallelProjection(lx, ly, hx, hy, nz, fz, m16d);
1505:
1506:                p.set(m16d);
1507:            }
1508:
1509:            //
1510:            // Compute a perspective projection from the given eye-space bounds.
1511:            //
1512:            private void computePerspectiveProjection(double lx, double ly,
1513:                    double hx, double hy, double iz, double nz, double fz,
1514:                    double[] m) {
1515:                //
1516:                // We first derive the X and Y projection components without regard
1517:                // for Z scaling.  The Z scaling or perspective depth is handled by
1518:                // matrix elements expressed solely in terms of the near and far clip
1519:                // planes.
1520:                //
1521:                // Since the eye is at the origin, the projector for any point V in
1522:                // eye space is just V.  Any point along this ray can be expressed in
1523:                // parametric form as P = tV.  To find the projection onto the plane
1524:                // containing the canvas, find t such that P.z = iz; ie, t = iz/V.z.
1525:                // The projection P is thus [V.x*iz/V.z, V.y*iz/V.z, iz].
1526:                // 
1527:                // This projection can expressed as the following matrix equation:
1528:                //
1529:                //   -iz     0     0     0       V.x
1530:                //    0     -iz    0     0   X   V.y
1531:                //    0      0    -iz    0       V.z
1532:                //    0      0    -1     0        1              {matrix 1}
1533:                //
1534:                // where the matrix elements have been negated so that w is positive.
1535:                // This is mostly by convention, although some hardware won't handle
1536:                // clipping in the -w half-space.
1537:                //
1538:                // After the point has been projected to the image plate, the
1539:                // canvas bounds need to be mapped to the [-1..1] of Java 3D's
1540:                // clipping space.  The scale factor for X is thus 2/(hx - lx); adding
1541:                // the translation results in (V.x - lx)(2/(hx - lx)) - 1, which after
1542:                // some algebra can be confirmed to the same as the following
1543:                // canonical scale/offset form:
1544:                // 
1545:                //   V.x*2/(hx - lx) - (hx + lx)/(hx - lx)
1546:                //
1547:                // Similarly for Y:
1548:                //
1549:                //   V.y*2/(hy - ly) - (hy + ly)/(hy - ly)
1550:                //
1551:                // If we set idx = 1/(hx - lx) and idy = 1/(hy - ly), then we get:
1552:                //
1553:                //   2*V.x*idx - (hx + lx)idx
1554:                //   2*V.y*idy - (hy + ly)idy
1555:                // 
1556:                // These scales and offsets are represented by the following matrix:
1557:                // 
1558:                //   2*idx       0         0  -(hx + lx)*idx
1559:                //     0       2*idy       0  -(hy + ly)*idy
1560:                //     0         0         1         0      
1561:                //     0         0         0         1           {matrix 2} 
1562:                //
1563:                // The result after concatenating the projection transform
1564:                // ({matrix 2} X {matrix 1}):
1565:                // 
1566:                //   -2*iz*idx     0      (hx + lx)*idx    0
1567:                //       0     -2*iz*idy  (hy + ly)*idy    0
1568:                //       0         0           -iz {a}     0 {b}
1569:                //       0         0           -1          0     {matrix 3}
1570:                // 
1571:                // The Z scaling is handled by m[10] ("a") and m[11] ("b"), which must
1572:                // map the range [front..back] to [1..-1] in clipping space.  If ze is
1573:                // the Z coordinate in eye space, and zc is the Z coordinate in
1574:                // clipping space after division by w, then from {matrix 3}:
1575:                // 
1576:                //   zc =  (a*ze + b)/-ze = -(a + b/ze)
1577:                // 
1578:                // We want this to map to +1 when ze is at the near clip plane, and
1579:                // to -1 when ze is at the far clip plane:
1580:                //
1581:                //   -(a + b/nz) = +1
1582:                //   -(a + b/fz) = -1
1583:                //
1584:                // Solving results in:
1585:                //
1586:                //   a = -(nz + fz)/(nz - fz)
1587:                //   b =  (2*nz*fz)/(nz - fz).
1588:                //
1589:                // NOTE: this produces a perspective transform that has matrix
1590:                // components with a different scale than the matrix computed by the
1591:                // Java 3D core.  They do in fact effect the equivalent clipping in 4D
1592:                // homogeneous coordinates and project to the same 3D Euclidean
1593:                // coordinates. m[14] is always -1 in our derivation above.  If the
1594:                // matrix components produced by Java 3D core are divided by its value
1595:                // of -m[14], then both matrices are the same.
1596:                //
1597:                double idx = 1.0 / (hx - lx);
1598:                double idy = 1.0 / (hy - ly);
1599:                double idz = 1.0 / (nz - fz);
1600:
1601:                m[0] = -2.0 * iz * idx;
1602:                m[5] = -2.0 * iz * idy;
1603:                m[2] = (hx + lx) * idx;
1604:                m[6] = (hy + ly) * idy;
1605:                m[10] = -(nz + fz) * idz;
1606:                m[11] = 2.0 * fz * nz * idz;
1607:                m[14] = -1.0;
1608:                m[1] = m[3] = m[4] = m[7] = m[8] = m[9] = m[12] = m[13] = m[15] = 0.0;
1609:            }
1610:
1611:            //
1612:            // Compute a parallel projection from the given eye-space bounds.
1613:            //
1614:            private void computeParallelProjection(double lx, double ly,
1615:                    double hx, double hy, double nz, double fz, double[] m) {
1616:                //
1617:                // A parallel projection in eye space just involves scales and offsets
1618:                // with no w division.	We can use {matrix 2} for the X and Y scales
1619:                // and offsets and then use a linear mapping of the front and back
1620:                // clip distances to the [1..-1] Z clip range.
1621:                //
1622:                double idx = 1.0 / (hx - lx);
1623:                double idy = 1.0 / (hy - ly);
1624:                double idz = 1.0 / (nz - fz);
1625:
1626:                m[0] = 2.0 * idx;
1627:                m[5] = 2.0 * idy;
1628:                m[10] = 2.0 * idz;
1629:                m[3] = -(hx + lx) * idx;
1630:                m[7] = -(hy + ly) * idy;
1631:                m[11] = -(nz + fz) * idz;
1632:                m[15] = 1.0;
1633:                m[1] = m[2] = m[4] = m[6] = m[8] = m[9] = m[12] = m[13] = m[14] = 0.0;
1634:            }
1635:
1636:            //
1637:            // Get front clip plane Z coordinate in image plate space.
1638:            // 
1639:            private double getFrontClip(CanvasInfo ci, Point3d eye) {
1640:                if (frontClipPolicy == View.PHYSICAL_EYE
1641:                        || frontClipPolicy == View.VIRTUAL_EYE) {
1642:                    return eye.z - ci.frontClipDistance;
1643:                } else {
1644:                    return -ci.frontClipDistance;
1645:                }
1646:            }
1647:
1648:            //
1649:            // Get back clip plane Z coordinate in image plate space.
1650:            // 
1651:            private double getBackClip(CanvasInfo ci, Point3d eye) {
1652:                //
1653:                // Note: Clip node status is unavailable here.	If a clip node is
1654:                // active in the scene graph, it should override the view's back
1655:                // clip plane.
1656:                //
1657:                if (backClipPolicy == View.PHYSICAL_EYE
1658:                        || backClipPolicy == View.VIRTUAL_EYE) {
1659:                    return eye.z - ci.backClipDistance;
1660:                } else {
1661:                    return -ci.backClipDistance;
1662:                }
1663:            }
1664:
1665:            //
1666:            // Compute clip distance scale.
1667:            //
1668:            private double getClipScale(CanvasInfo ci, int clipPolicy) {
1669:                if (clipPolicy == View.VIRTUAL_EYE
1670:                        || clipPolicy == View.VIRTUAL_SCREEN) {
1671:                    getScreenScale(ci);
1672:                    if (resizePolicy == View.PHYSICAL_WORLD)
1673:                        return vpi.vworldToViewPlatformScale * ci.screenScale
1674:                                * ci.windowScale;
1675:                    else
1676:                        return vpi.vworldToViewPlatformScale * ci.screenScale;
1677:                } else {
1678:                    if (resizePolicy == View.PHYSICAL_WORLD)
1679:                        return ci.windowScale; // see below
1680:                    else
1681:                        return 1.0;
1682:                }
1683:            }
1684:
1685:            /**
1686:             * Gets the front clip distance scaled to physical meters.  This is useful
1687:             * for ensuring that objects positioned relative to a physical coordinate
1688:             * system (such as eye, image plate, or coexistence) will be within the
1689:             * viewable Z depth.  This distance will be relative to either the eye or
1690:             * the image plate depending upon the front clip policy.<p>
1691:             *
1692:             * Note that this is not necessarily the clip distance as set by
1693:             * <code>setFrontClipDistance</code>, even when the front clip policy
1694:             * is <code>PHYSICAL_SCREEN</code> or <code>PHYSICAL_EYE</code>.  <i>If
1695:             * the window resize policy is <code>PHYSICAL_WORLD</code>, then physical
1696:             * clip distances as specified are in fact scaled by the ratio of the
1697:             * window width to the screen width.</i> The Java 3D view model does this
1698:             * to prevent the physical clip planes from moving with respect to the
1699:             * virtual world when the window is resized.<p>
1700:             *
1701:             * If either of the clip policies <code>VIRTUAL_EYE</code> or
1702:             * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
1703:             * to a ViewPlatform that is part of a live scene graph and that has its
1704:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
1705:             * scale factor of 1.0 will be used for the scale factor from virtual
1706:             * world units to view platform units.
1707:             * 
1708:             * @param c3d the Canvas3D to use
1709:             * @return the physical front clip distance
1710:             */
1711:            public double getPhysicalFrontClipDistance(Canvas3D c3d) {
1712:                CanvasInfo ci = updateCache(c3d,
1713:                        "getPhysicalFrontClipDistance", true);
1714:
1715:                getClipDistances(ci);
1716:                return ci.frontClipDistance;
1717:            }
1718:
1719:            /**
1720:             * Gets the back clip distance scaled to physical meters.  This is useful
1721:             * for ensuring that objects positioned relative to a physical coordinate
1722:             * system (such as eye, image plate, or coexistence) will be within the
1723:             * viewable Z depth.  This distance will be relative to either the eye or
1724:             * the image plate depending upon the back clip policy.<p>
1725:             *
1726:             * Note that this is not necessarily the clip distance as set by
1727:             * <code>setBackClipDistance</code>, even when the back clip policy
1728:             * is <code>PHYSICAL_SCREEN</code> or <code>PHYSICAL_EYE</code>.  <i>If
1729:             * the window resize policy is <code>PHYSICAL_WORLD</code>, then physical
1730:             * clip distances as specified are in fact scaled by the ratio of the
1731:             * window width to the screen width.</i> The Java 3D view model does this
1732:             * to prevent the physical clip planes from moving with respect to the
1733:             * virtual world when the window is resized.<p>
1734:             *
1735:             * If either of the clip policies <code>VIRTUAL_EYE</code> or
1736:             * <code>VIRTUAL_SCREEN</code> are used, then the View should be attached
1737:             * to a ViewPlatform that is part of a live scene graph and that has its
1738:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set; otherwise, a
1739:             * scale factor of 1.0 will be used for the scale factor from virtual
1740:             * world units to view platform units.
1741:             * 
1742:             * @param c3d the Canvas3D to use
1743:             * @return the physical back clip distance
1744:             */
1745:            public double getPhysicalBackClipDistance(Canvas3D c3d) {
1746:                CanvasInfo ci = updateCache(c3d, "getPhysicalBackClipDistance",
1747:                        true);
1748:                getClipDistances(ci);
1749:                return ci.backClipDistance;
1750:            }
1751:
1752:            private void getClipDistances(CanvasInfo ci) {
1753:                if (ci.updateClipDistances) {
1754:                    if (verbose)
1755:                        System.err.println("updating clip distances");
1756:
1757:                    ci.frontClipDistance = view.getFrontClipDistance()
1758:                            * getClipScale(ci, frontClipPolicy);
1759:
1760:                    ci.backClipDistance = view.getBackClipDistance()
1761:                            * getClipScale(ci, backClipPolicy);
1762:
1763:                    ci.updateClipDistances = false;
1764:                    if (verbose) {
1765:                        System.err.println("  front clip distance "
1766:                                + ci.frontClipDistance);
1767:                        System.err.println("  back clip distance  "
1768:                                + ci.backClipDistance);
1769:                    }
1770:                }
1771:            }
1772:
1773:            private void getScreenScale(CanvasInfo ci) {
1774:                if (ci.updateScreenScale) {
1775:                    if (verbose)
1776:                        System.err.println("updating screen scale");
1777:
1778:                    if (scalePolicy == View.SCALE_SCREEN_SIZE)
1779:                        ci.screenScale = ci.si.screenWidth / 2.0;
1780:                    else
1781:                        ci.screenScale = view.getScreenScale();
1782:
1783:                    ci.updateScreenScale = false;
1784:                    if (verbose)
1785:                        System.err.println("screen scale " + ci.screenScale);
1786:                }
1787:            }
1788:
1789:            /**
1790:             * Gets the scale factor from physical meters to view platform units.<p>
1791:             * 
1792:             * This method requires a Canvas3D.	 A different scale may be returned
1793:             * for each canvas in the view if any of the following apply:<p><ul>
1794:             *
1795:             * <li>The window resize policy is <code>PHYSICAL_WORLD</code>, which
1796:             *	   alters the scale depending upon the width of the canvas.</li><p>
1797:             *
1798:             * <li>The screen scale policy is <code>SCALE_SCREEN_SIZE</code>,
1799:             *	   which alters the scale depending upon the width of the screen
1800:             *	   associated with the canvas.</li></ul>
1801:             *
1802:             * @param c3d the Canvas3D to use
1803:             * @return the physical to view platform scale
1804:             */
1805:            public double getPhysicalToViewPlatformScale(Canvas3D c3d) {
1806:                CanvasInfo ci = updateCache(c3d,
1807:                        "getPhysicalToViewPlatformScale", false);
1808:
1809:                getPhysicalToViewPlatformScale(ci);
1810:                return ci.physicalToVpScale;
1811:            }
1812:
1813:            private void getPhysicalToViewPlatformScale(CanvasInfo ci) {
1814:                if (ci.updatePhysicalToVpScale) {
1815:                    if (verbose)
1816:                        System.err.println("updating PhysicalToVp scale");
1817:
1818:                    getScreenScale(ci);
1819:                    if (resizePolicy == View.PHYSICAL_WORLD)
1820:                        ci.physicalToVpScale = 1.0 / (ci.screenScale * ci.windowScale);
1821:                    else
1822:                        ci.physicalToVpScale = 1.0 / ci.screenScale;
1823:
1824:                    ci.updatePhysicalToVpScale = false;
1825:                    if (verbose)
1826:                        System.err.println("PhysicalToVp scale "
1827:                                + ci.physicalToVpScale);
1828:                }
1829:            }
1830:
1831:            /**
1832:             * Gets the scale factor from physical meters to virtual units.<p>
1833:             * 
1834:             * This method requires a Canvas3D.	 A different scale may be returned
1835:             * across canvases for the same reasons as discussed in the description of
1836:             * <code>getPhysicalToViewPlatformScale</code>.<p>
1837:             *
1838:             * The View must be attached to a ViewPlatform which is part of a live
1839:             * scene graph, and the ViewPlatform node must have its
1840:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
1841:             *
1842:             * @param c3d the Canvas3D to use
1843:             * @return the physical to virtual scale
1844:             * @see #getPhysicalToViewPlatformScale
1845:             *       getPhysicalToViewPlatformScale(Canvas3D)
1846:             */
1847:            public double getPhysicalToVirtualScale(Canvas3D c3d) {
1848:                CanvasInfo ci = updateCache(c3d, "getPhysicalToVirtualScale",
1849:                        true);
1850:                getPhysicalToVirtualScale(ci);
1851:                return ci.physicalToVirtualScale;
1852:            }
1853:
1854:            private void getPhysicalToVirtualScale(CanvasInfo ci) {
1855:                if (ci.updatePhysicalToVirtualScale) {
1856:                    if (verbose)
1857:                        System.err.println("updating PhysicalToVirtual scale");
1858:
1859:                    getPhysicalToViewPlatformScale(ci);
1860:                    ci.physicalToVirtualScale = ci.physicalToVpScale
1861:                            / vpi.vworldToViewPlatformScale;
1862:
1863:                    ci.updatePhysicalToVirtualScale = false;
1864:                    if (verbose)
1865:                        System.err.println("PhysicalToVirtual scale "
1866:                                + ci.physicalToVirtualScale);
1867:                }
1868:            }
1869:
1870:            /**
1871:             * Gets the width of the specified canvas scaled to physical meters.  This
1872:             * is derived from the physical screen width as reported by the Screen3D
1873:             * associated with the canvas.  If the screen width is not explicitly set
1874:             * using the <code>setPhysicalScreenWidth</code> method of Screen3D, then
1875:             * Java 3D will derive the screen width based on a screen resolution of 90
1876:             * pixels/inch.
1877:             * 
1878:             * @param c3d the Canvas3D to use
1879:             * @return the width of the canvas scaled to physical meters
1880:             */
1881:            public double getPhysicalWidth(Canvas3D c3d) {
1882:                CanvasInfo ci = updateCache(c3d, "getPhysicalWidth", false);
1883:                return ci.canvasWidth;
1884:            }
1885:
1886:            /**
1887:             * Gets the height of the specified canvas scaled to physical meters.  This
1888:             * is derived from the physical screen height as reported by the Screen3D
1889:             * associated with the canvas.  If the screen height is not explicitly set
1890:             * using the <code>setPhysicalScreenHeight</code> method of Screen3D, then
1891:             * Java 3D will derive the screen height based on a screen resolution of 90
1892:             * pixels/inch.
1893:             * 
1894:             * @param c3d the Canvas3D to use
1895:             * @return the height of the canvas scaled to physical meters
1896:             */
1897:            public double getPhysicalHeight(Canvas3D c3d) {
1898:                CanvasInfo ci = updateCache(c3d, "getPhysicalHeight", false);
1899:                return ci.canvasHeight;
1900:            }
1901:
1902:            /**
1903:             * Gets the location of the specified canvas relative to the image plate
1904:             * origin.  This is derived from the physical screen parameters as
1905:             * reported by the Screen3D associated with the canvas.  If the screen
1906:             * width and height are not explicitly set in Screen3D, then Java 3D will
1907:             * derive those screen parameters based on a screen resolution of 90
1908:             * pixels/inch.
1909:             * 
1910:             * @param c3d the Canvas3D to use
1911:             * @param location the output position, in meters, of the lower-left
1912:             *  corner of the canvas relative to the image plate lower-left corner; Z
1913:             *  is always 0.0
1914:             */
1915:            public void getPhysicalLocation(Canvas3D c3d, Point3d location) {
1916:                CanvasInfo ci = updateCache(c3d, "getPhysicalLocation", false);
1917:                location.set(ci.canvasX, ci.canvasY, 0.0);
1918:            }
1919:
1920:            /**
1921:             * Gets the location of the AWT pixel value and copies it into the
1922:             * specified Point3d.
1923:             *
1924:             * @param c3d the Canvas3D to use
1925:             * @param x the X coordinate of the pixel relative to the upper-left
1926:             *  corner of the canvas
1927:             * @param y the Y coordinate of the pixel relative to the upper-left
1928:             *  corner of the canvas
1929:             * @param location the output position, in meters, relative to the
1930:             *  lower-left corner of the image plate; Z is always 0.0
1931:             */
1932:            public void getPixelLocationInImagePlate(Canvas3D c3d, int x,
1933:                    int y, Point3d location) {
1934:
1935:                CanvasInfo ci = updateCache(c3d,
1936:                        "getPixelLocationInImagePlate", false);
1937:
1938:                location.set(ci.canvasX + ((double) x * ci.si.metersPerPixelX),
1939:                        ci.canvasY - ((double) y * ci.si.metersPerPixelY)
1940:                                + ci.canvasHeight, 0.0);
1941:            }
1942:
1943:            /**
1944:             * Gets a read from the specified sensor and transforms it to virtual
1945:             * world coordinates.  The View must be attached to a ViewPlatform which
1946:             * is part of a live scene graph, and the ViewPlatform node must have its
1947:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.<p>
1948:             *
1949:             * This method requires a Canvas3D.	 The returned transform may differ
1950:             * across canvases for the same reasons as discussed in the description of
1951:             * <code>getViewPlatformToCoexistence</code>.
1952:             *
1953:             * @param sensor the Sensor instance to read
1954:             * @param s2vw the output transform
1955:             * @see #getViewPlatformToCoexistence
1956:             *       getViewPlatformToCoexistence(Canvas3D, Transform3D)
1957:             */
1958:            public void getSensorToVworld(Canvas3D c3d, Sensor sensor,
1959:                    Transform3D s2vw) {
1960:
1961:                CanvasInfo ci = updateCache(c3d, "getSensorToVworld", true);
1962:                getTrackerBaseToVworld(ci);
1963:                sensor.getRead(s2vw);
1964:                s2vw.mul(ci.trackerBaseToVworld, s2vw);
1965:            }
1966:
1967:            /**
1968:             * Gets the transform from tracker base coordinates to view platform
1969:             * coordinates and copies it into the specified Transform3D.<p>
1970:             *
1971:             * This method requires a Canvas3D.	 The returned transform may differ
1972:             * across canvases for the same reasons as discussed in the description of
1973:             * <code>getViewPlatformToCoexistence</code>.
1974:             *
1975:             * @param c3d the Canvas3D to use
1976:             * @param tb2vp the output transform
1977:             * @see #getViewPlatformToCoexistence
1978:             *       getViewPlatformToCoexistence(Canvas3D, Transform3D)
1979:             */
1980:            public void getTrackerBaseToViewPlatform(Canvas3D c3d,
1981:                    Transform3D tb2vp) {
1982:                CanvasInfo ci = updateCache(c3d,
1983:                        "getTrackerBaseToViewPlatform", false);
1984:
1985:                getTrackerBaseToViewPlatform(ci);
1986:                tb2vp.set(ci.trackerBaseToViewPlatform);
1987:            }
1988:
1989:            private void getTrackerBaseToViewPlatform(CanvasInfo ci) {
1990:                if (ci.updateTrackerBaseToViewPlatform) {
1991:                    if (verbose)
1992:                        System.err.println("updating TrackerBaseToVp");
1993:                    if (ci.trackerBaseToViewPlatform == null)
1994:                        ci.trackerBaseToViewPlatform = new Transform3D();
1995:
1996:                    getViewPlatformToCoexistence(ci);
1997:                    ci.trackerBaseToViewPlatform.mul(coeToTrackerBase,
1998:                            ci.viewPlatformToCoe);
1999:
2000:                    ci.trackerBaseToViewPlatform.invert();
2001:                    ci.updateTrackerBaseToViewPlatform = false;
2002:                    if (verbose)
2003:                        t3dPrint(ci.trackerBaseToViewPlatform,
2004:                                "TrackerBaseToViewPlatform");
2005:                }
2006:            }
2007:
2008:            /**
2009:             * Gets the transform from tracker base coordinates to virtual world
2010:             * coordinates and copies it into the specified Transform3D.  The View
2011:             * must be attached to a ViewPlatform which is part of a live scene graph,
2012:             * and the ViewPlatform node must have its
2013:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.<p>
2014:             *
2015:             * This method requires a Canvas3D.	 The returned transform may differ
2016:             * across canvases for the same reasons as discussed in the description of
2017:             * <code>getViewPlatformToCoexistence</code>.
2018:             *
2019:             * @param c3d the Canvas3D to use
2020:             * @param tb2vw the output transform
2021:             * @see #getViewPlatformToCoexistence
2022:             *       getViewPlatformToCoexistence(Canvas3D, Transform3D)
2023:             */
2024:            public void getTrackerBaseToVworld(Canvas3D c3d, Transform3D tb2vw) {
2025:                CanvasInfo ci = updateCache(c3d, "getTrackerBaseToVworld", true);
2026:                getTrackerBaseToVworld(ci);
2027:                tb2vw.set(ci.trackerBaseToVworld);
2028:            }
2029:
2030:            private void getTrackerBaseToVworld(CanvasInfo ci) {
2031:                if (ci.updateTrackerBaseToVworld) {
2032:                    if (verbose)
2033:                        System.err.println("updating TrackerBaseToVworld");
2034:                    if (ci.trackerBaseToVworld == null)
2035:                        ci.trackerBaseToVworld = new Transform3D();
2036:                    //
2037:                    // We compute trackerBaseToViewPlatform and compose with
2038:                    // viewPlatformToVworld instead of computing imagePlateToVworld
2039:                    // and composing with trackerBaseToImagePlate.  That way it works
2040:                    // with HMD and avoids the issue of choosing the left image plate
2041:                    // or right image plate transform.
2042:                    //
2043:                    getTrackerBaseToViewPlatform(ci);
2044:                    ci.trackerBaseToVworld.mul(vpi.viewPlatformToVworld,
2045:                            ci.trackerBaseToViewPlatform);
2046:
2047:                    ci.updateTrackerBaseToVworld = false;
2048:                }
2049:            }
2050:
2051:            /**
2052:             * Release all static memory references held by ViewInfo, if any.  These
2053:             * are the Screen3D and ViewPlatform maps shared by all existing ViewInfo
2054:             * instances if they're not provided by a constructor.  Releasing the
2055:             * screen references effectively releases all canvas references in all
2056:             * ViewInfo instances as well.<p>
2057:             *
2058:             * It is safe to continue using existing ViewInfo instances after calling
2059:             * this method; the data in the released maps will be re-derived as
2060:             * needed.
2061:             */
2062:            public static synchronized void clear() {
2063:                Iterator i = staticVpMap.values().iterator();
2064:                while (i.hasNext())
2065:                    ((ViewPlatformInfo) i.next()).clear();
2066:                staticVpMap.clear();
2067:
2068:                i = staticSiMap.values().iterator();
2069:                while (i.hasNext())
2070:                    ((ScreenInfo) i.next()).clear();
2071:                staticSiMap.clear();
2072:            }
2073:
2074:            /**
2075:             * Arrange for an update of cached screen parameters.  If automatic update
2076:             * has not been enabled, then this method should be called if any of the
2077:             * attributes of the Screen3D have changed.	 This method should also be
2078:             * called if the screen changes pixel resolution.
2079:             *
2080:             * @param s3d the Screen3D to update
2081:             */
2082:            public void updateScreen(Screen3D s3d) {
2083:                if (verbose)
2084:                    System.err.println("updateScreen");
2085:                ScreenInfo si = (ScreenInfo) screenMap.get(s3d);
2086:                if (si != null)
2087:                    si.updateScreen = true;
2088:            }
2089:
2090:            /**
2091:             * Arrange for an update of cached canvas parameters.  If automatic update
2092:             * has not been enabled, then this method should be called if any of the
2093:             * attributes of the Canvas3D have changed.	 These attributes include the
2094:             * canvas position and size, but do <i>not</i> include the attributes of
2095:             * the associated Screen3D, which are cached separately.
2096:             *
2097:             * @param c3d the Canvas3D to update
2098:             */
2099:            public void updateCanvas(Canvas3D c3d) {
2100:                if (verbose)
2101:                    System.err.println("updateCanvas");
2102:                CanvasInfo ci = (CanvasInfo) canvasMap.get(c3d);
2103:                if (ci != null)
2104:                    ci.updateCanvas = true;
2105:            }
2106:
2107:            /**
2108:             * Arrange for an update of cached view parameters.	 If automatic update
2109:             * has not been enabled for the View, then this method should be called if
2110:             * any of the attributes of the View associated with this object have
2111:             * changed.<p>
2112:             * 
2113:             * These do <i>not</i> include the attributes of the existing Canvas3D or
2114:             * Screen3D components of the View, but do include the attributes of all
2115:             * other components such as the PhysicalEnvironment and PhysicalBody, and
2116:             * all attributes of the attached ViewPlatform except for its
2117:             * <code>localToVworld</code> transform.  The screen and canvas components
2118:             * as well as the ViewPlatform's <code>localToVworld</code> are cached
2119:             * separately.<p>
2120:             *
2121:             * This method should also be called if the ViewPlatform is replaced with
2122:             * another using the View's <code>attachViewPlatform</code> method, or if
2123:             * any of the <code>setCanvas3D</code>, <code>addCanvas3D</code>,
2124:             * <code>insertCanvas3D</code>, <code>removeCanvas3D</code>, or
2125:             * <code>removeAllCanvas3Ds</code> methods of View are called to change
2126:             * the View's canvas list.<p>
2127:             *
2128:             * Calling this method causes most transforms to be re-derived.  It should
2129:             * be used only when necessary.
2130:             */
2131:            public void updateView() {
2132:                if (verbose)
2133:                    System.err.println("updateView");
2134:                this .updateView = true;
2135:            }
2136:
2137:            /**
2138:             * Arrange for an update of the cached head position if head tracking is
2139:             * enabled.	 If automatic update has not enabled for the head position,
2140:             * then this method should be called anytime a new head position is to be
2141:             * read.
2142:             */
2143:            public void updateHead() {
2144:                if (verbose)
2145:                    System.err.println("updateHead");
2146:                this .updateHead = true;
2147:            }
2148:
2149:            /**
2150:             * Arrange for an update of the cached <code>localToVworld</code>
2151:             * transform of the view platform.	If automatic update has not been
2152:             * enabled for this transform, then this method should be called anytime
2153:             * the view platform has been repositioned in the virtual world and a
2154:             * transform involving virtual world coordinates is desired.<p>
2155:             *
2156:             * The View must be attached to a ViewPlatform which is part of a live
2157:             * scene graph, and the ViewPlatform node must have its
2158:             * <code>ALLOW_LOCAL_TO_VWORLD_READ</code> capability set.
2159:             */
2160:            public void updateViewPlatform() {
2161:                if (verbose)
2162:                    System.err.println("updateViewPlatform");
2163:                vpi.updateViewPlatformToVworld = true;
2164:            }
2165:
2166:            //
2167:            // Set cache update bits based on auto update flags.
2168:            // VIEW_AUTO_UPDATE is handled in updateCache().
2169:            //
2170:            private void getAutoUpdate(CanvasInfo ci) {
2171:                if ((autoUpdateFlags & SCREEN_AUTO_UPDATE) != 0)
2172:                    ci.si.updateScreen = true;
2173:
2174:                if ((autoUpdateFlags & CANVAS_AUTO_UPDATE) != 0)
2175:                    ci.updateCanvas = true;
2176:
2177:                if ((autoUpdateFlags & PLATFORM_AUTO_UPDATE) != 0)
2178:                    vpi.updateViewPlatformToVworld = true;
2179:
2180:                if ((autoUpdateFlags & HEAD_AUTO_UPDATE) != 0)
2181:                    this .updateHead = true;
2182:            }
2183:
2184:            //
2185:            // Update any changed cached data.	This takes a Canvas3D instance.	 The
2186:            // cache mechanism could have used a Canvas3D index into the View instead,
2187:            // but the direct reference is probably more convenient for applications.
2188:            //
2189:            private CanvasInfo updateCache(Canvas3D c3d, String name,
2190:                    boolean vworld) {
2191:                if (verbose) {
2192:                    System.err.println("updateCache: " + name + " in "
2193:                            + hashCode());
2194:                    System.err.println("  canvas " + c3d.hashCode());
2195:                }
2196:
2197:                // The View may have had Canvas3D instances added or removed, or may
2198:                // have been attached to a different ViewPlatform, so update the view
2199:                // before anything else.
2200:                if (updateView || (autoUpdateFlags & VIEW_AUTO_UPDATE) != 0)
2201:                    getViewInfo();
2202:
2203:                // Now get the CanvasInfo to update.
2204:                CanvasInfo ci = (CanvasInfo) canvasMap.get(c3d);
2205:                if (ci == null)
2206:                    throw new IllegalArgumentException(
2207:                            "Specified Canvas3D is not a component of the View");
2208:
2209:                // Check rest of autoUpdateFlags.
2210:                if (autoUpdate)
2211:                    getAutoUpdate(ci);
2212:
2213:                // Update the screen, canvas, view platform, and head caches.
2214:                if (ci.si.updateScreen)
2215:                    ci.si.getScreenInfo();
2216:
2217:                if (ci.updateCanvas)
2218:                    ci.getCanvasInfo();
2219:
2220:                if (vworld && vpi.updateViewPlatformToVworld)
2221:                    vpi.getViewPlatformToVworld();
2222:
2223:                if (useTracking && updateHead)
2224:                    getHeadInfo();
2225:
2226:                // Return the CanvasInfo instance.
2227:                return ci;
2228:            }
2229:
2230:            //
2231:            // Get physical view parameters and derived data.  This is a fairly
2232:            // heavyweight method -- everything gets marked for update since we don't
2233:            // currently track changes in individual view attributes.  Fortunately
2234:            // there shouldn't be a need to call it very often.
2235:            //
2236:            private void getViewInfo() {
2237:                if (verbose)
2238:                    System.err.println("  getViewInfo");
2239:
2240:                // Check if an update of the Canvas3D collection is needed. 
2241:                if (this .canvasCount != view.numCanvas3Ds()) {
2242:                    this .canvasCount = view.numCanvas3Ds();
2243:                    getCanvases();
2244:                } else {
2245:                    for (int i = 0; i < canvasCount; i++) {
2246:                        if (canvasMap.get(view.getCanvas3D(i)) != canvasInfo[i]) {
2247:                            getCanvases();
2248:                            break;
2249:                        }
2250:                    }
2251:                }
2252:
2253:                // Update the ViewPlatform.
2254:                getViewPlatform();
2255:
2256:                // Update the PhysicalBody and PhysicalEnvironment.
2257:                this .body = view.getPhysicalBody();
2258:                this .env = view.getPhysicalEnvironment();
2259:
2260:                // Use the result of the possibly overridden method useHeadTracking()
2261:                // to determine if head tracking is to be used within ViewInfo.
2262:                this .useTracking = useHeadTracking();
2263:
2264:                // Get the head tracker only if really available.
2265:                if (view.getTrackingEnable() && env.getTrackingAvailable()) {
2266:                    int headIndex = env.getHeadIndex();
2267:                    this .headTracker = env.getSensor(headIndex);
2268:                }
2269:
2270:                // Get the new policies and update data derived from them.
2271:                this .viewPolicy = view.getViewPolicy();
2272:                this .projectionPolicy = view.getProjectionPolicy();
2273:                this .resizePolicy = view.getWindowResizePolicy();
2274:                this .movementPolicy = view.getWindowMovementPolicy();
2275:                this .eyePolicy = view.getWindowEyepointPolicy();
2276:                this .scalePolicy = view.getScreenScalePolicy();
2277:                this .backClipPolicy = view.getBackClipPolicy();
2278:                this .frontClipPolicy = view.getFrontClipPolicy();
2279:
2280:                if (useTracking || viewPolicy == View.HMD_VIEW) {
2281:                    if (this .headToHeadTracker == null)
2282:                        this .headToHeadTracker = new Transform3D();
2283:                    if (this .headTrackerToTrackerBase == null)
2284:                        this .headTrackerToTrackerBase = new Transform3D();
2285:
2286:                    if (viewPolicy == View.HMD_VIEW) {
2287:                        if (this .trackerBaseToHeadTracker == null)
2288:                            this .trackerBaseToHeadTracker = new Transform3D();
2289:                        if (this .coeToHeadTracker == null)
2290:                            this .coeToHeadTracker = new Transform3D();
2291:                    } else {
2292:                        if (this .headToTrackerBase == null)
2293:                            this .headToTrackerBase = new Transform3D();
2294:                    }
2295:
2296:                    body.getLeftEyePosition(this .leftEyeInHead);
2297:                    body.getRightEyePosition(this .rightEyeInHead);
2298:                    body.getHeadToHeadTracker(this .headToHeadTracker);
2299:
2300:                    if (verbose) {
2301:                        System.err.println("    leftEyeInHead  "
2302:                                + leftEyeInHead);
2303:                        System.err.println("    rightEyeInHead "
2304:                                + rightEyeInHead);
2305:                        t3dPrint(headToHeadTracker, "    headToHeadTracker");
2306:                    }
2307:                }
2308:
2309:                if (eyePolicy == View.RELATIVE_TO_WINDOW
2310:                        || eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW) {
2311:                    body.getLeftEyePosition(this .leftEyeInHead);
2312:                    body.getRightEyePosition(this .rightEyeInHead);
2313:                    if (verbose) {
2314:                        System.err.println("    leftEyeInHead  "
2315:                                + leftEyeInHead);
2316:                        System.err.println("    rightEyeInHead "
2317:                                + rightEyeInHead);
2318:                    }
2319:                }
2320:
2321:                if ((env.getCoexistenceCenterInPworldPolicy() != View.NOMINAL_SCREEN)
2322:                        || (viewPolicy == View.HMD_VIEW))
2323:                    this .coeCentering = false;
2324:                else
2325:                    this .coeCentering = view.getCoexistenceCenteringEnable();
2326:
2327:                if (!coeCentering || useTracking) {
2328:                    if (this .coeToTrackerBase == null)
2329:                        this .coeToTrackerBase = new Transform3D();
2330:
2331:                    env.getCoexistenceToTrackerBase(this .coeToTrackerBase);
2332:                    if (verbose)
2333:                        t3dPrint(coeToTrackerBase, "    coeToTrackerBase");
2334:                }
2335:
2336:                if (backClipPolicy == View.VIRTUAL_EYE
2337:                        || backClipPolicy == View.VIRTUAL_SCREEN
2338:                        || frontClipPolicy == View.VIRTUAL_EYE
2339:                        || frontClipPolicy == View.VIRTUAL_SCREEN) {
2340:                    this .clipVirtual = true;
2341:                } else {
2342:                    this .clipVirtual = false;
2343:                }
2344:
2345:                // Propagate view updates to each canvas.
2346:                for (int i = 0; i < canvasCount; i++)
2347:                    this .canvasInfo[i].updateViewDependencies();
2348:
2349:                this .updateView = false;
2350:                if (verbose) {
2351:                    System.err.println("    tracking " + useTracking);
2352:                    System.err.println("    coeCentering " + coeCentering);
2353:                    System.err.println("    clipVirtual " + clipVirtual);
2354:                }
2355:            }
2356:
2357:            // 
2358:            // Each view can have multiple canvases, each with an associated screen.
2359:            // Each canvas is associated with only one view.  Each screen can have
2360:            // multiple canvases that are used across multiple views.  We rebuild the
2361:            // canvas info instead of trying to figure out what canvases have been
2362:            // added or removed from the view.
2363:            // 
2364:            private void getCanvases() {
2365:                if (this .canvasInfo.length < canvasCount) {
2366:                    this .canvasInfo = new CanvasInfo[canvasCount];
2367:                }
2368:
2369:                for (int i = 0; i < canvasCount; i++) {
2370:                    Canvas3D c3d = view.getCanvas3D(i);
2371:                    Screen3D s3d = c3d.getScreen3D();
2372:
2373:                    // Check if we have a new screen.
2374:                    ScreenInfo si = (ScreenInfo) screenMap.get(s3d);
2375:                    if (si == null) {
2376:                        si = new ScreenInfo(s3d, c3d.getGraphicsConfiguration());
2377:                        screenMap.put(s3d, si);
2378:                    }
2379:
2380:                    // Check to see if we've encountered the screen so far in this
2381:                    // loop over the view's canvases.  If not, clear the screen's list
2382:                    // of canvases for this ViewInfo.
2383:                    if (newSet.add(si))
2384:                        si.clear(this );
2385:
2386:                    // Check if this is a new canvas.
2387:                    CanvasInfo ci = (CanvasInfo) canvasMap.get(c3d);
2388:                    if (ci == null)
2389:                        ci = new CanvasInfo(c3d, si);
2390:
2391:                    // Add this canvas to the screen's list for this ViewInfo.
2392:                    si.addCanvasInfo(this , ci);
2393:
2394:                    // Add this canvas to the new canvas map and canvas array.
2395:                    this .newMap.put(c3d, ci);
2396:                    this .canvasInfo[i] = ci;
2397:                }
2398:
2399:                // Null out old references if canvas count shrinks.
2400:                for (int i = canvasCount; i < canvasInfo.length; i++)
2401:                    this .canvasInfo[i] = null;
2402:
2403:                // Update the CanvasInfo map.
2404:                Map tmp = canvasMap;
2405:                this .canvasMap = newMap;
2406:                this .newMap = tmp;
2407:
2408:                // Clear the temporary collections.
2409:                this .newMap.clear();
2410:                this .newSet.clear();
2411:            }
2412:
2413:            //
2414:            // Force the creation of new CanvasInfo instances.  This is called when a
2415:            // screen is removed from the screen map.
2416:            //
2417:            private void clearCanvases() {
2418:                this .canvasCount = 0;
2419:                this .canvasMap.clear();
2420:                this .updateView = true;
2421:            }
2422:
2423:            // 
2424:            // Update the view platform. Each view can be attached to only one, but
2425:            // each view platform can have many views attached.
2426:            // 
2427:            private void getViewPlatform() {
2428:                ViewPlatform vp = view.getViewPlatform();
2429:                if (vp == null)
2430:                    throw new IllegalStateException(
2431:                            "The View must be attached to a ViewPlatform");
2432:
2433:                ViewPlatformInfo tmpVpi = (ViewPlatformInfo) viewPlatformMap
2434:                        .get(vp);
2435:
2436:                if (tmpVpi == null) {
2437:                    // We haven't encountered this ViewPlatform before.
2438:                    tmpVpi = new ViewPlatformInfo(vp);
2439:                    viewPlatformMap.put(vp, tmpVpi);
2440:                }
2441:
2442:                if (this .vpi != tmpVpi) {
2443:                    // ViewPlatform has changed.  Could set an update flag here if it
2444:                    // would be used, but updating the view updates everything anyway.
2445:                    if (this .vpi != null) {
2446:                        // Remove this ViewInfo from the list of Views attached to the
2447:                        // old ViewPlatform.
2448:                        this .vpi.removeViewInfo(this );
2449:                    }
2450:                    this .vpi = tmpVpi;
2451:                    this .vpi.addViewInfo(this );
2452:
2453:                    // updateViewPlatformToVworld is initially set false since the
2454:                    // capability to read the vworld transform may not be
2455:                    // available. If it is, set it here.
2456:                    if (vp.getCapability(Node.ALLOW_LOCAL_TO_VWORLD_READ)) {
2457:                        this .vpi.updateViewPlatformToVworld = true;
2458:                        if (verbose)
2459:                            System.err.println("    vworld read allowed");
2460:                    } else if (verbose)
2461:                        System.err.println("    vworld read disallowed");
2462:                }
2463:            }
2464:
2465:            //
2466:            // Force the creation of a new ViewPlatformInfo when a view platform is
2467:            // removed from the view platform map.
2468:            //
2469:            private void clearViewPlatform() {
2470:                this .updateView = true;
2471:            }
2472:
2473:            //
2474:            // Update vworld dependencies for this ViewInfo -- called by
2475:            // ViewPlatformInfo.getViewPlatformToVworld().
2476:            //
2477:            private void updateVworldDependencies() {
2478:                for (int i = 0; i < canvasCount; i++)
2479:                    this .canvasInfo[i].updateVworldDependencies();
2480:            }
2481:
2482:            /**
2483:             * Returns a reference to a Transform3D containing the current transform
2484:             * from head tracker coordinates to tracker base coordinates.  It is only
2485:             * called if <code>useHeadTracking</code> returns true and a head position
2486:             * update is specified with <code>updateHead</code> or the
2487:             * <code>HEAD_AUTO_UPDATE</code> constructor flag.<p>
2488:             *
2489:             * The default implementation uses the head tracking sensor specified by
2490:             * the View's PhysicalEnvironment, and reads it by calling the sensor's
2491:             * <code>getRead</code> method directly.  The result is a sensor reading
2492:             * that may have been taken at a slightly different time from the one used
2493:             * by the renderer.  This method can be overridden to synchronize the two
2494:             * readings through an external mechanism.
2495:             *
2496:             * @return current head tracker to tracker base transform
2497:             * @see #useHeadTracking
2498:             * @see #updateHead
2499:             * @see #HEAD_AUTO_UPDATE
2500:             */
2501:            protected Transform3D getHeadTrackerToTrackerBase() {
2502:                headTracker.getRead(this .headTrackerToTrackerBase);
2503:                return this .headTrackerToTrackerBase;
2504:            }
2505:
2506:            /**
2507:             * Returns <code>true</code> if head tracking should be used.<p>
2508:             *
2509:             * The default implementation returns <code>true</code> if the View's
2510:             * <code>getTrackingEnable</code> method and the PhysicalEnvironment's
2511:             * <code>getTrackingAvailable</code> method both return <code>true</code>.
2512:             * These are the same conditions under which the Java 3D renderer uses
2513:             * head tracking.  This method can be overridden if there is any need to
2514:             * decouple the head tracking status of ViewInfo from the renderer.
2515:             * 
2516:             * @return <code>true</code> if ViewInfo should use head tracking
2517:             */
2518:            protected boolean useHeadTracking() {
2519:                return view.getTrackingEnable() && env.getTrackingAvailable();
2520:            }
2521:
2522:            //
2523:            // Cache the current tracked head position and derived data.
2524:            // 
2525:            private void getHeadInfo() {
2526:                if (verbose)
2527:                    System.err.println("  getHeadInfo");
2528:
2529:                this .headTrackerToTrackerBase = getHeadTrackerToTrackerBase();
2530:                if (viewPolicy == View.HMD_VIEW) {
2531:                    this .trackerBaseToHeadTracker
2532:                            .invert(headTrackerToTrackerBase);
2533:                    this .coeToHeadTracker.mul(trackerBaseToHeadTracker,
2534:                            coeToTrackerBase);
2535:                } else {
2536:                    this .headToTrackerBase.mul(headTrackerToTrackerBase,
2537:                            headToHeadTracker);
2538:                }
2539:                for (int i = 0; i < canvasCount; i++)
2540:                    this .canvasInfo[i].updateHeadDependencies();
2541:
2542:                this .updateHead = false;
2543:                // 
2544:                // The head position used by the Java 3D renderer isn't accessible
2545:                // in the public API.  A head tracker generates continuous data, so
2546:                // getting the same sensor read as the renderer is unlikely.
2547:                // 
2548:                // Possible workaround: for fixed screens, get the Java 3D
2549:                // renderer's version of plateToVworld and headToVworld by calling
2550:                // Canvas3D.getImagePlateToVworld() and View.getUserHeadToVworld().
2551:                // Although the vworld components will have frame latency, they can
2552:                // be cancelled out by inverting the former transform and
2553:                // multiplying by the latter, resulting in userHeadToImagePlate,
2554:                // which can then be transformed to tracker base coordinates.
2555:                //
2556:                // For head mounted displays, the head to image plate transforms are
2557:                // just calibration constants, so they're of no use.  There are more
2558:                // involved workarounds possible, but one that may work for both fixed
2559:                // screens and HMD is to define a SensorInterposer class that extends
2560:                // Sensor.  Take the View's head tracking sensor, use it to construct
2561:                // a SensorInterposer, and then replace the head tracking sensor with
2562:                // the SensorInterposer.  SensorInterposer can then override the
2563:                // getRead() methods and thus control what the Java 3D renderer gets.
2564:                // getHeadTrackerToTrackerBase() is a protected method in ViewInfo
2565:                // which can be overridden to call a variant of getRead() so that
2566:                // calls from ViewInfo and from the renderer can be distinguished.
2567:                //
2568:                // Even if getting the same head position as used by the renderer is
2569:                // achieved, tracked eye space interactions with objects in the
2570:                // virtual world still can't be synchronized with rendering. This
2571:                // means that objects in the virtual world cannot be made to appear in
2572:                // a fixed position relative to the tracked head position without a
2573:                // frame lag between them.
2574:                // 
2575:                // The reason for this is that the tracked head position used by the
2576:                // Java 3D renderer is updated asynchronously from scene graph
2577:                // updates.  This is done to reduce latency between the user's
2578:                // position and the rendered image, which is directly related to the
2579:                // quality of the immersive virtual reality experience.	 So while an
2580:                // update to the scene graph may have a frame latency before it gets
2581:                // rendered, a change to the user's tracked position is always
2582:                // reflected in the current frame.
2583:                // 
2584:                // This problem can't be fixed without eliminating the frame latency
2585:                // in the Java 3D internal state, although there are possible
2586:                // workarounds at the expense of increased user position latency.
2587:                // These involve disabling tracking, reading the head sensor directly,
2588:                // performing whatever eye space interactions are necessary with the
2589:                // virtual world (using the view platform's current localToVworld),
2590:                // and then propagating the head position change to the renderer
2591:                // manually through a behavior post mechanism that delays it by a
2592:                // frame.
2593:                //
2594:                // For example, with head tracking in a fixed screen environment (such
2595:                // as a CAVE), disable Java 3D head tracking and set the View's window
2596:                // eyepoint policy to RELATIVE_TO_COEXISTENCE.	Read the sensor to get
2597:                // the head position relative to the tracker base, transform it to
2598:                // coexistence coordinates using the inverse of the value of the
2599:                // coexistenceToTrackerBase transform, and then set the eye positions
2600:                // manually with the View's set{Left,Right}ManualEyeInCoexistence
2601:                // methods.  If these method calls are delayed through a behavior post
2602:                // mechanism, then they will be synchronized with the rendering of the
2603:                // scene graph updates.
2604:                //
2605:                // With a head mounted display the sensor can be read directly to get
2606:                // the head position relative to the tracker base.  If Java 3D's head
2607:                // tracking is disabled, it uses identity for the current
2608:                // headTrackerToTrackerBase transform.	It concatenates its inverse,
2609:                // trackerBaseToHeadTracker, with coexistenceToTrackerBase to get the
2610:                // image plate positions in coexistence; the former transform is
2611:                // inaccessible, but the latter can be set through the
2612:                // PhysicalEnvironment.	 So the workaround is to maintain a local copy
2613:                // with the real value of coexistenceToTrackerBase, but set the
2614:                // PhysicalEnvironment copy to the product of the real value and the
2615:                // trackerBaseToHeadTracker inverted from the sensor read.  Like the
2616:                // CAVE example, this update to the View would have to be delayed in
2617:                // order to synchronize with scene graph updates.
2618:                //
2619:                // Another possibility is to put the Java 3D view model in
2620:                // compatibility mode, where it accepts vpcToEye and eyeToCc
2621:                // (projection) directly.  The various view attributes can still be
2622:                // set and accessed, but will be ignored by the Java 3D view model.
2623:                // The ViewInfo methods can be used to compute the view and projection
2624:                // matrices, which can then be delayed to synchronize with the scene
2625:                // graph.
2626:                // 
2627:                // Note that these workarounds could be used to make view-dependent
2628:                // scene graph updates consistent, but they still can't do anything
2629:                // about synchronizing the actual physical position of the user with
2630:                // the rendered images.	 That requires zero latency between position
2631:                // update and scene graph state.
2632:                //
2633:                // Still another possibility: extrapolate the position of the user
2634:                // into the next few frames from a sample of recently recorded
2635:                // positions.  Unfortunately, that is also a very hard problem.  The
2636:                // Java 3D Sensor API is designed to support prediction but it was
2637:                // never realized successfully in the sample implementation.
2638:            }
2639:
2640:            //
2641:            // A per-screen cache, shared between ViewInfo instances.  In the Java 3D
2642:            // view model a single screen can be associated with multiple canvas
2643:            // and view instances.
2644:            //
2645:            private static class ScreenInfo {
2646:                private Screen3D s3d = null;
2647:                private GraphicsConfiguration graphicsConfiguration = null;
2648:                private boolean updateScreen = true;
2649:
2650:                private Map viewInfoMap = new HashMap();
2651:                private List viewInfoList = new LinkedList();
2652:                private Transform3D t3d = new Transform3D();
2653:
2654:                private double screenWidth = 0.0;
2655:                private double screenHeight = 0.0;
2656:                private boolean updateScreenSize = true;
2657:
2658:                private Rectangle screenBounds = null;
2659:                private double metersPerPixelX = 0.0;
2660:                private double metersPerPixelY = 0.0;
2661:                private boolean updatePixelSize = true;
2662:
2663:                // These transforms are pre-allocated here since they are required by
2664:                // some view policies and we don't know what views this screen will be
2665:                // attached to.  Their default identity values are used if not
2666:                // explicitly set. TODO: allocate if needed in getCanvasInfo(), where
2667:                // view information will be available.
2668:                private Transform3D trackerBaseToPlate = new Transform3D();
2669:                private Transform3D headTrackerToLeftPlate = new Transform3D();
2670:                private Transform3D headTrackerToRightPlate = new Transform3D();
2671:                private boolean updateTrackerBaseToPlate = false;
2672:                private boolean updateHeadTrackerToPlate = false;
2673:
2674:                private ScreenInfo(Screen3D s3d, GraphicsConfiguration gc) {
2675:                    this .s3d = s3d;
2676:                    this .graphicsConfiguration = gc;
2677:                    if (verbose)
2678:                        System.err.println("    ScreenInfo: init "
2679:                                + s3d.hashCode());
2680:                }
2681:
2682:                private List getCanvasList(ViewInfo vi) {
2683:                    List canvasList = (List) viewInfoMap.get(vi);
2684:                    if (canvasList == null) {
2685:                        canvasList = new LinkedList();
2686:                        viewInfoMap.put(vi, canvasList);
2687:                        viewInfoList.add(canvasList);
2688:                    }
2689:                    return canvasList;
2690:                }
2691:
2692:                private synchronized void clear(ViewInfo vi) {
2693:                    getCanvasList(vi).clear();
2694:                }
2695:
2696:                private synchronized void clear() {
2697:                    Iterator i = viewInfoMap.keySet().iterator();
2698:                    while (i.hasNext())
2699:                        ((ViewInfo) i.next()).clearCanvases();
2700:                    viewInfoMap.clear();
2701:
2702:                    i = viewInfoList.iterator();
2703:                    while (i.hasNext())
2704:                        ((List) i.next()).clear();
2705:                    viewInfoList.clear();
2706:                }
2707:
2708:                private synchronized void addCanvasInfo(ViewInfo vi,
2709:                        CanvasInfo ci) {
2710:                    getCanvasList(vi).add(ci);
2711:                }
2712:
2713:                // 
2714:                // Get all relevant screen information, find out what changed, and
2715:                // flag derived data.  With normal use it's unlikely that any of the
2716:                // Screen3D attributes will change after the first time this method is
2717:                // called. It's possible that the screen resolution changed or some
2718:                // sort of interactive screen calibration is in process.
2719:                //
2720:                private synchronized void getScreenInfo() {
2721:                    if (verbose)
2722:                        System.err.println("  getScreenInfo " + s3d.hashCode());
2723:
2724:                    // This is used for positioning screens in relation to each other
2725:                    // and must be accurate for good results with multi-screen
2726:                    // displays.  By default the coexistence to tracker base transform
2727:                    // is identity so in that case this transform will also set the
2728:                    // image plate in coexistence coordinates.
2729:                    s3d.getTrackerBaseToImagePlate(t3d);
2730:                    if (!t3d.equals(trackerBaseToPlate)) {
2731:                        this .trackerBaseToPlate.set(t3d);
2732:                        this .updateTrackerBaseToPlate = true;
2733:                        if (verbose)
2734:                            t3dPrint(trackerBaseToPlate,
2735:                                    "    trackerBaseToPlate");
2736:                    }
2737:
2738:                    // This transform and the following are used for head mounted
2739:                    // displays.  They should be based on the *apparent* position of
2740:                    // the screens as viewed through the HMD optics.
2741:                    s3d.getHeadTrackerToLeftImagePlate(t3d);
2742:                    if (!t3d.equals(headTrackerToLeftPlate)) {
2743:                        this .headTrackerToLeftPlate.set(t3d);
2744:                        this .updateHeadTrackerToPlate = true;
2745:                        if (verbose)
2746:                            t3dPrint(headTrackerToLeftPlate,
2747:                                    "    headTrackerToLeftPlate");
2748:                    }
2749:
2750:                    s3d.getHeadTrackerToRightImagePlate(t3d);
2751:                    if (!t3d.equals(headTrackerToRightPlate)) {
2752:                        this .headTrackerToRightPlate.set(t3d);
2753:                        this .updateHeadTrackerToPlate = true;
2754:                        if (verbose)
2755:                            t3dPrint(headTrackerToRightPlate,
2756:                                    "    headTrackerToRightPlate");
2757:                    }
2758:
2759:                    // If the screen width and height in meters are not explicitly set
2760:                    // through the Screen3D, then the Screen3D will assume a pixel
2761:                    // resolution of 90 pixels/inch and compute the dimensions from
2762:                    // the screen resolution.  These dimensions should be measured
2763:                    // accurately for multi-screen displays.  For HMD, these
2764:                    // dimensions should be the *apparent* width and height as viewed
2765:                    // through the HMD optics.
2766:                    double w = s3d.getPhysicalScreenWidth();
2767:                    double h = s3d.getPhysicalScreenHeight();
2768:                    if (w != screenWidth || h != screenHeight) {
2769:                        this .screenWidth = w;
2770:                        this .screenHeight = h;
2771:                        this .updateScreenSize = true;
2772:                        if (verbose) {
2773:                            System.err.println("    screen width  "
2774:                                    + screenWidth);
2775:                            System.err.println("    screen height "
2776:                                    + screenHeight);
2777:                        }
2778:                    }
2779:
2780:                    GraphicsConfiguration gc1 = graphicsConfiguration;
2781:                    // Workaround for Issue 316 - use the default config for screen 0
2782:                    // if the graphics config is null
2783:                    if (gc1 == null) {
2784:                        gc1 = GraphicsEnvironment.getLocalGraphicsEnvironment()
2785:                                .getDefaultScreenDevice()
2786:                                .getDefaultConfiguration();
2787:                    }
2788:                    this .screenBounds = gc1.getBounds();
2789:                    double mpx = screenWidth / (double) screenBounds.width;
2790:                    double mpy = screenHeight / (double) screenBounds.height;
2791:                    if ((mpx != metersPerPixelX) || (mpy != metersPerPixelY)) {
2792:                        this .metersPerPixelX = mpx;
2793:                        this .metersPerPixelY = mpy;
2794:                        this .updatePixelSize = true;
2795:                        if (verbose) {
2796:                            System.err.println("    screen bounds "
2797:                                    + screenBounds);
2798:                            System.err.println("    pixel size X "
2799:                                    + metersPerPixelX);
2800:                            System.err.println("    pixel size Y "
2801:                                    + metersPerPixelY);
2802:                        }
2803:                    }
2804:
2805:                    // Propagate screen updates to each canvas in each ViewInfo.
2806:                    Iterator vi = viewInfoList.iterator();
2807:                    while (vi.hasNext()) {
2808:                        Iterator ci = ((List) vi.next()).iterator();
2809:                        while (ci.hasNext())
2810:                            ((CanvasInfo) ci.next()).updateScreenDependencies();
2811:                    }
2812:
2813:                    this .updateTrackerBaseToPlate = false;
2814:                    this .updateHeadTrackerToPlate = false;
2815:                    this .updateScreenSize = false;
2816:                    this .updatePixelSize = false;
2817:                    this .updateScreen = false;
2818:                }
2819:            }
2820:
2821:            //
2822:            // A per-ViewPlatform cache, shared between ViewInfo instances.  In the
2823:            // Java 3D view model, a view platform may have several views attached to
2824:            // it.  The only view platform data cached here is its localToVworld, the
2825:            // inverse of its localToVworld, and the scale from vworld to view
2826:            // platform coordinates.  The view platform to coexistence transform is
2827:            // cached by the CanvasInfo instances associated with the ViewInfo.
2828:            //
2829:            private static class ViewPlatformInfo {
2830:                private ViewPlatform vp = null;
2831:                private List viewInfo = new LinkedList();
2832:                private double[] m = new double[16];
2833:
2834:                // These transforms are pre-allocated since we don't know what views
2835:                // will be attached.  Their default identity values are used if a
2836:                // vworld dependent computation is requested and no initial update of
2837:                // the view platform was performed; this occurs if the local to vworld
2838:                // read capability isn't set.  TODO: rationalize this and allocate
2839:                // only if necessary.
2840:                private Transform3D viewPlatformToVworld = new Transform3D();
2841:                private Transform3D vworldToViewPlatform = new Transform3D();
2842:                private double vworldToViewPlatformScale = 1.0;
2843:
2844:                // Set these update flags initially false since we might not have the
2845:                // capability to read the vworld transform.
2846:                private boolean updateViewPlatformToVworld = false;
2847:                private boolean updateVworldScale = false;
2848:
2849:                private ViewPlatformInfo(ViewPlatform vp) {
2850:                    this .vp = vp;
2851:                    if (verbose)
2852:                        System.err.println("    ViewPlatformInfo: init "
2853:                                + vp.hashCode());
2854:                }
2855:
2856:                private synchronized void addViewInfo(ViewInfo vi) {
2857:                    this .viewInfo.add(vi);
2858:                }
2859:
2860:                private synchronized void removeViewInfo(ViewInfo vi) {
2861:                    this .viewInfo.remove(vi);
2862:                }
2863:
2864:                private synchronized void clear() {
2865:                    Iterator i = viewInfo.iterator();
2866:                    while (i.hasNext())
2867:                        ((ViewInfo) i.next()).clearViewPlatform();
2868:                    viewInfo.clear();
2869:                }
2870:
2871:                //
2872:                // Get the view platform's current <code>localToVworld</code> and
2873:                // force the update of derived data.
2874:                //
2875:                private synchronized void getViewPlatformToVworld() {
2876:                    if (verbose)
2877:                        System.err.println("  getViewPlatformToVworld "
2878:                                + vp.hashCode());
2879:
2880:                    vp.getLocalToVworld(this .viewPlatformToVworld);
2881:                    this .vworldToViewPlatform.invert(viewPlatformToVworld);
2882:
2883:                    // Get the scale factor from the virtual world to view platform
2884:                    // transform.  Note that this is always a congruent transform. 
2885:                    vworldToViewPlatform.get(m);
2886:                    double newScale = Math.sqrt(m[0] * m[0] + m[1] * m[1]
2887:                            + m[2] * m[2]);
2888:
2889:                    // May need to update clip plane distances if scale changed.  We'll
2890:                    // check with an epsilon commensurate with single precision float.
2891:                    // It would be more efficient to check the square of the distance
2892:                    // and then compute the square root only if different, but that
2893:                    // makes choosing an epsilon difficult.
2894:                    if ((newScale > vworldToViewPlatformScale + 0.0000001)
2895:                            || (newScale < vworldToViewPlatformScale - 0.0000001)) {
2896:                        this .vworldToViewPlatformScale = newScale;
2897:                        this .updateVworldScale = true;
2898:                        if (verbose)
2899:                            System.err.println("    vworld scale "
2900:                                    + vworldToViewPlatformScale);
2901:                    }
2902:
2903:                    // All virtual world transforms must be updated.
2904:                    Iterator i = viewInfo.iterator();
2905:                    while (i.hasNext())
2906:                        ((ViewInfo) i.next()).updateVworldDependencies();
2907:
2908:                    this .updateVworldScale = false;
2909:                    this .updateViewPlatformToVworld = false;
2910:                }
2911:            }
2912:
2913:            //
2914:            // A per-canvas cache.
2915:            // 
2916:            private class CanvasInfo {
2917:                private Canvas3D c3d = null;
2918:                private ScreenInfo si = null;
2919:                private boolean updateCanvas = true;
2920:
2921:                private double canvasX = 0.0;
2922:                private double canvasY = 0.0;
2923:                private boolean updatePosition = true;
2924:
2925:                private double canvasWidth = 0.0;
2926:                private double canvasHeight = 0.0;
2927:                private double windowScale = 0.0;
2928:                private boolean updateWindowScale = true;
2929:
2930:                private double screenScale = 0.0;
2931:                private boolean updateScreenScale = true;
2932:
2933:                private boolean useStereo = false;
2934:                private boolean updateStereo = true;
2935:
2936:                //
2937:                // coeToPlate is the same for each Canvas3D in a Screen3D unless
2938:                // coexistence centering is enabled and the window movement policy is
2939:                // PHYSICAL_WORLD.
2940:                // 
2941:                private Transform3D coeToPlate = null;
2942:                private Transform3D coeToRightPlate = null;
2943:                private boolean updateCoeToPlate = true;
2944:
2945:                //
2946:                // viewPlatformToCoe is the same for each Canvas3D in a View unless
2947:                // the window resize policy is PHYSICAL_WORLD, in which case the scale
2948:                // factor includes the window scale; or if the screen scale policy is
2949:                // SCALE_SCREEN_SIZE, in which case the scale factor depends upon the
2950:                // width of the screen associated with the canvas; or if the window
2951:                // eyepoint policy is RELATIVE_TO_FIELD_OF_VIEW and the view attach
2952:                // policy is not NOMINAL_SCREEN, which will set the view platform
2953:                // origin in coexistence based on the width of the canvas.
2954:                // 
2955:                private Transform3D viewPlatformToCoe = null;
2956:                private Transform3D coeToViewPlatform = null;
2957:                private boolean updateViewPlatformToCoe = true;
2958:                private boolean updateCoeToViewPlatform = true;
2959:
2960:                //
2961:                // plateToViewPlatform is composed from viewPlatformToCoe and
2962:                // coeToPlate.
2963:                //
2964:                private Transform3D plateToViewPlatform = null;
2965:                private Transform3D rightPlateToViewPlatform = null;
2966:                private boolean updatePlateToViewPlatform = true;
2967:
2968:                //
2969:                // trackerBaseToViewPlatform is computed from viewPlatformToCoe and
2970:                // coeToTrackerBase.
2971:                //
2972:                private Transform3D trackerBaseToViewPlatform = null;
2973:                private boolean updateTrackerBaseToViewPlatform = true;
2974:
2975:                //
2976:                // Eye position in image plate is always different for each Canvas3D
2977:                // in a View, unless two or more Canvas3D instances are the same
2978:                // position and size, or two or more Canvas3D instances are using a
2979:                // window eyepoint policy of RELATIVE_TO_SCREEN and have the same
2980:                // settings for the manual eye positions.
2981:                //
2982:                private Point3d eyeInPlate = new Point3d();
2983:                private Point3d rightEyeInPlate = new Point3d();
2984:                private Transform3D eyeToPlate = null;
2985:                private Transform3D rightEyeToPlate = null;
2986:                private boolean updateEyeInPlate = true;
2987:
2988:                private Point3d leftManualEyeInPlate = new Point3d();
2989:                private Point3d rightManualEyeInPlate = new Point3d();
2990:                private boolean updateManualEye = true;
2991:
2992:                private int monoscopicPolicy = -1;
2993:                private boolean updateMonoPolicy = true;
2994:
2995:                //
2996:                // eyeToViewPlatform is computed from eyeToPlate and
2997:                // plateToViewPlatform.
2998:                //
2999:                private Transform3D eyeToViewPlatform = null;
3000:                private Transform3D rightEyeToViewPlatform = null;
3001:                private boolean updateEyeToViewPlatform = true;
3002:
3003:                private Transform3D viewPlatformToEye = null;
3004:                private Transform3D viewPlatformToRightEye = null;
3005:                private boolean updateViewPlatformToEye = true;
3006:
3007:                //
3008:                // The projection transform depends upon eye position in image plate.
3009:                // 
3010:                private Transform3D projection = null;
3011:                private Transform3D rightProjection = null;
3012:                private boolean updateProjection = true;
3013:
3014:                private Transform3D inverseProjection = null;
3015:                private Transform3D inverseRightProjection = null;
3016:                private boolean updateInverseProjection = true;
3017:
3018:                private Transform3D inverseViewPlatformProjection = null;
3019:                private Transform3D inverseViewPlatformRightProjection = null;
3020:                private boolean updateInverseViewPlatformProjection = true;
3021:
3022:                //
3023:                // The physical clip distances can be affected by the canvas width
3024:                // with the PHYSICAL_WORLD resize policy.
3025:                //
3026:                private double frontClipDistance = 0.0;
3027:                private double backClipDistance = 0.0;
3028:                private boolean updateClipDistances = true;
3029:
3030:                //
3031:                // The physical to view platform scale can be affected by the canvas
3032:                // width with the PHYSICAL_WORLD resize policy.
3033:                //
3034:                private double physicalToVpScale = 0.0;
3035:                private double physicalToVirtualScale = 0.0;
3036:                private boolean updatePhysicalToVpScale = true;
3037:                private boolean updatePhysicalToVirtualScale = true;
3038:
3039:                //
3040:                // The vworld transforms require reading the ViewPlaform's
3041:                // localToVworld tranform.
3042:                //
3043:                private Transform3D plateToVworld = null;
3044:                private Transform3D rightPlateToVworld = null;
3045:                private boolean updatePlateToVworld = true;
3046:
3047:                private Transform3D coeToVworld = null;
3048:                private boolean updateCoeToVworld = true;
3049:
3050:                private Transform3D eyeToVworld = null;
3051:                private Transform3D rightEyeToVworld = null;
3052:                private boolean updateEyeToVworld = true;
3053:
3054:                private Transform3D trackerBaseToVworld = null;
3055:                private boolean updateTrackerBaseToVworld = true;
3056:
3057:                private Transform3D inverseVworldProjection = null;
3058:                private Transform3D inverseVworldRightProjection = null;
3059:                private boolean updateInverseVworldProjection = true;
3060:
3061:                private CanvasInfo(Canvas3D c3d, ScreenInfo si) {
3062:                    this .si = si;
3063:                    this .c3d = c3d;
3064:                    if (verbose)
3065:                        System.err.println("    CanvasInfo: init "
3066:                                + c3d.hashCode());
3067:                }
3068:
3069:                private void getCanvasInfo() {
3070:                    if (verbose)
3071:                        System.err.println("  getCanvasInfo " + c3d.hashCode());
3072:                    boolean newStereo = c3d.getStereoEnable()
3073:                            && c3d.getStereoAvailable();
3074:
3075:                    if (useStereo != newStereo) {
3076:                        this .useStereo = newStereo;
3077:                        this .updateStereo = true;
3078:                        if (verbose)
3079:                            System.err.println("    stereo " + useStereo);
3080:                    }
3081:
3082:                    this .canvasWidth = c3d.getWidth() * si.metersPerPixelX;
3083:                    this .canvasHeight = c3d.getHeight() * si.metersPerPixelY;
3084:                    double newScale = canvasWidth / si.screenWidth;
3085:
3086:                    if (windowScale != newScale) {
3087:                        this .windowScale = newScale;
3088:                        this .updateWindowScale = true;
3089:                        if (verbose) {
3090:                            System.err.println("    width  " + canvasWidth);
3091:                            System.err.println("    height " + canvasHeight);
3092:                            System.err.println("    scale  " + windowScale);
3093:                        }
3094:                    }
3095:
3096:                    // For multiple physical screens, AWT returns the canvas location
3097:                    // relative to the origin of the aggregated virtual screen.	 We
3098:                    // need the location relative to the physical screen origin.
3099:                    Point awtLocation = c3d.getLocationOnScreen();
3100:                    int x = awtLocation.x - si.screenBounds.x;
3101:                    int y = awtLocation.y - si.screenBounds.y;
3102:
3103:                    double newCanvasX = si.metersPerPixelX * x;
3104:                    double newCanvasY = si.metersPerPixelY
3105:                            * (si.screenBounds.height - (y + c3d.getHeight()));
3106:
3107:                    if (canvasX != newCanvasX || canvasY != newCanvasY) {
3108:                        this .canvasX = newCanvasX;
3109:                        this .canvasY = newCanvasY;
3110:                        this .updatePosition = true;
3111:                        if (verbose) {
3112:                            System.err.println("    lower left X " + canvasX);
3113:                            System.err.println("    lower left Y " + canvasY);
3114:                        }
3115:                    }
3116:
3117:                    int newMonoPolicy = c3d.getMonoscopicViewPolicy();
3118:                    if (monoscopicPolicy != newMonoPolicy) {
3119:                        this .monoscopicPolicy = newMonoPolicy;
3120:                        this .updateMonoPolicy = true;
3121:
3122:                        if (verbose && !useStereo) {
3123:                            if (monoscopicPolicy == View.LEFT_EYE_VIEW)
3124:                                System.err.println("    left eye view");
3125:                            else if (monoscopicPolicy == View.RIGHT_EYE_VIEW)
3126:                                System.err.println("    right eye view");
3127:                            else
3128:                                System.err.println("    cyclopean view");
3129:                        }
3130:                    }
3131:
3132:                    c3d.getLeftManualEyeInImagePlate(leftEye);
3133:                    c3d.getRightManualEyeInImagePlate(rightEye);
3134:
3135:                    if (!leftEye.equals(leftManualEyeInPlate)
3136:                            || !rightEye.equals(rightManualEyeInPlate)) {
3137:
3138:                        this .leftManualEyeInPlate.set(leftEye);
3139:                        this .rightManualEyeInPlate.set(rightEye);
3140:                        this .updateManualEye = true;
3141:
3142:                        if (verbose
3143:                                && (eyePolicy == View.RELATIVE_TO_WINDOW || eyePolicy == View.RELATIVE_TO_SCREEN)) {
3144:                            System.err.println("    left manual eye in plate  "
3145:                                    + leftManualEyeInPlate);
3146:                            System.err.println("    right manual eye in plate "
3147:                                    + rightManualEyeInPlate);
3148:                        }
3149:                    }
3150:
3151:                    updateCanvasDependencies();
3152:                    this .updateStereo = false;
3153:                    this .updateWindowScale = false;
3154:                    this .updatePosition = false;
3155:                    this .updateMonoPolicy = false;
3156:                    this .updateManualEye = false;
3157:                    this .updateCanvas = false;
3158:                }
3159:
3160:                private double getFieldOfViewOffset() {
3161:                    return 0.5 * canvasWidth
3162:                            / Math.tan(0.5 * view.getFieldOfView());
3163:                }
3164:
3165:                private void updateScreenDependencies() {
3166:                    if (si.updatePixelSize || si.updateScreenSize) {
3167:                        if (eyePolicy == View.RELATIVE_TO_WINDOW
3168:                                || eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW) {
3169:                            // Physical location of the canvas might change without
3170:                            // changing the pixel location.
3171:                            updateEyeInPlate = true;
3172:                        }
3173:                        if (resizePolicy == View.PHYSICAL_WORLD
3174:                                || eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW) {
3175:                            // Could change the window scale or view platform Z offset.
3176:                            updateViewPlatformToCoe = true;
3177:                        }
3178:                        if (resizePolicy == View.PHYSICAL_WORLD) {
3179:                            // Window scale affects the clip distance and the physical
3180:                            // to viewplatform scale.
3181:                            updateClipDistances = true;
3182:                            updatePhysicalToVpScale = true;
3183:                            updatePhysicalToVirtualScale = true;
3184:                        }
3185:                        // Upper right corner of canvas may have moved from eye.
3186:                        updateProjection = true;
3187:                    }
3188:                    if (si.updateScreenSize
3189:                            && scalePolicy == View.SCALE_SCREEN_SIZE) {
3190:                        // Screen scale affects the clip distances and physical to
3191:                        // view platform scale.  The screen scale is also a component
3192:                        // of viewPlatformToCoe.
3193:                        updateScreenScale = true;
3194:                        updateClipDistances = true;
3195:                        updatePhysicalToVpScale = true;
3196:                        updatePhysicalToVirtualScale = true;
3197:                        updateViewPlatformToCoe = true;
3198:                    }
3199:
3200:                    if (viewPolicy == View.HMD_VIEW) {
3201:                        if (si.updateHeadTrackerToPlate) {
3202:                            // Plate moves with respect to the eye and coexistence.
3203:                            updateEyeInPlate = true;
3204:                            updateCoeToPlate = true;
3205:                        }
3206:                    } else if (coeCentering) {
3207:                        if (movementPolicy == View.PHYSICAL_WORLD) {
3208:                            // Coexistence is centered on the canvas.
3209:                            if (si.updatePixelSize || si.updateScreenSize)
3210:                                // Physical location of the canvas might change
3211:                                // without changing the pixel location.
3212:                                updateCoeToPlate = true;
3213:                        } else if (si.updateScreenSize)
3214:                            // Coexistence is centered on the screen.
3215:                            updateCoeToPlate = true;
3216:                    } else if (si.updateTrackerBaseToPlate) {
3217:                        // Image plate has possibly changed location.  Could be
3218:                        // offset by an update to coeToTrackerBase in the
3219:                        // PhysicalEnvironment though.
3220:                        updateCoeToPlate = true;
3221:                    }
3222:
3223:                    if (updateCoeToPlate
3224:                            && eyePolicy == View.RELATIVE_TO_COEXISTENCE) {
3225:                        // Coexistence has moved with respect to plate.
3226:                        updateEyeInPlate = true;
3227:                    }
3228:                    if (updateViewPlatformToCoe) {
3229:                        // Derived transforms.  trackerBaseToViewPlatform is composed
3230:                        // from viewPlatformToCoe and coexistenceToTrackerBase.
3231:                        updateCoeToViewPlatform = true;
3232:                        updateCoeToVworld = true;
3233:                        updateTrackerBaseToViewPlatform = true;
3234:                        updateTrackerBaseToVworld = true;
3235:                    }
3236:                    if (updateCoeToPlate || updateViewPlatformToCoe) {
3237:                        // The image plate to view platform transform is composed from
3238:                        // the coexistence to image plate and view platform to
3239:                        // coexistence transforms, so these need updates as well.
3240:                        updatePlateToViewPlatform = true;
3241:                        updatePlateToVworld = true;
3242:                    }
3243:                    updateEyeDependencies();
3244:                }
3245:
3246:                private void updateEyeDependencies() {
3247:                    if (updateEyeInPlate) {
3248:                        updateEyeToVworld = true;
3249:                        updateProjection = true;
3250:                    }
3251:                    if (updateProjection) {
3252:                        updateInverseProjection = true;
3253:                        updateInverseViewPlatformProjection = true;
3254:                        updateInverseVworldProjection = true;
3255:                    }
3256:                    if (updateEyeInPlate || updatePlateToViewPlatform) {
3257:                        updateViewPlatformToEye = true;
3258:                        updateEyeToViewPlatform = true;
3259:                    }
3260:                }
3261:
3262:                private void updateCanvasDependencies() {
3263:                    if (updateStereo
3264:                            || updateMonoPolicy
3265:                            || (updateManualEye && (eyePolicy == View.RELATIVE_TO_WINDOW || eyePolicy == View.RELATIVE_TO_SCREEN))) {
3266:                        updateEyeInPlate = true;
3267:                    }
3268:                    if (updateWindowScale || updatePosition) {
3269:                        if (coeCentering
3270:                                && movementPolicy == View.PHYSICAL_WORLD) {
3271:                            // Coexistence is centered on the canvas.
3272:                            updateCoeToPlate = true;
3273:                            if (eyePolicy == View.RELATIVE_TO_COEXISTENCE)
3274:                                updateEyeInPlate = true;
3275:                        }
3276:                        if (eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW
3277:                                || eyePolicy == View.RELATIVE_TO_WINDOW)
3278:                            // Eye depends on canvas position and size.
3279:                            updateEyeInPlate = true;
3280:                    }
3281:                    if (updateWindowScale) {
3282:                        if (resizePolicy == View.PHYSICAL_WORLD
3283:                                || eyePolicy == View.RELATIVE_TO_FIELD_OF_VIEW) {
3284:                            // View platform scale and its origin Z offset changed.
3285:                            // trackerBaseToViewPlatform needs viewPlatformToCoe.
3286:                            updateViewPlatformToCoe = true;
3287:                            updateCoeToViewPlatform = true;
3288:                            updateCoeToVworld = true;
3289:                            updateTrackerBaseToViewPlatform = true;
3290:                            updateTrackerBaseToVworld = true;
3291:                        }
3292:                        if (resizePolicy == View.PHYSICAL_WORLD) {
3293:                            // Clip distance and physical to view platform scale are
3294:                            // affected by the window size.
3295:                            updateClipDistances = true;
3296:                            updateProjection = true;
3297:                            updatePhysicalToVpScale = true;
3298:                            updatePhysicalToVirtualScale = true;
3299:                        }
3300:                    }
3301:                    if (updateViewPlatformToCoe || updateCoeToPlate) {
3302:                        // The image plate to view platform transform is composed from
3303:                        // the coexistence to image plate and the view platform to
3304:                        // coexistence transforms, so these need updates.
3305:                        updatePlateToViewPlatform = true;
3306:                        updatePlateToVworld = true;
3307:                    }
3308:                    if (coeCentering && !updateManualEye && !updateWindowScale
3309:                            && (movementPolicy == View.PHYSICAL_WORLD)
3310:                            && (eyePolicy != View.RELATIVE_TO_SCREEN)) {
3311:                        // The canvas may have moved, but the eye, coexistence, and
3312:                        // view platform moved with it.  updateEyeDependencies()
3313:                        // isn't called since it would unnecessarily update the
3314:                        // projection and eyeToViewPlatform transforms.  The tested
3315:                        // policies are all true by default.
3316:                        return;
3317:                    }
3318:                    updateEyeDependencies();
3319:                }
3320:
3321:                //
3322:                // TODO: A brave soul could refine cache updates here.  There are a
3323:                // lot of attributes to monitor, so we just update everything for now.
3324:                //
3325:                private void updateViewDependencies() {
3326:                    // View policy, physical body eye positions, head to head
3327:                    // tracker, window eyepoint policy, field of view, coexistence
3328:                    // centering, or coexistence to image plate may have changed.
3329:                    updateEyeInPlate = true;
3330:
3331:                    // If the eye position in image plate has changed, then the
3332:                    // projection transform may need to be updated.  The projection
3333:                    // policy and clip plane distances and policies may have changed.
3334:                    // The window resize policy and screen scale may have changed,
3335:                    // which affects clip plane distance scaling.
3336:                    updateProjection = true;
3337:                    updateClipDistances = true;
3338:                    updatePhysicalToVpScale = true;
3339:                    updatePhysicalToVirtualScale = true;
3340:
3341:                    // View policy, coexistence to tracker base, coexistence centering
3342:                    // enable, or window movement policy may have changed.
3343:                    updateCoeToPlate = true;
3344:
3345:                    // Screen scale, resize policy, view policy, view platform,
3346:                    // physical body, physical environment, eyepoint policy, or field
3347:                    // of view may have changed.
3348:                    updateViewPlatformToCoe = true;
3349:                    updateCoeToViewPlatform = true;
3350:                    updateCoeToVworld = true;
3351:
3352:                    // The image plate to view platform transform is composed from the
3353:                    // coexistence to image plate and view platform to coexistence
3354:                    // transforms, so these need updates.
3355:                    updatePlateToViewPlatform = true;
3356:                    updatePlateToVworld = true;
3357:
3358:                    // View platform to coexistence or coexistence to tracker base may
3359:                    // have changed.
3360:                    updateTrackerBaseToViewPlatform = true;
3361:                    updateTrackerBaseToVworld = true;
3362:
3363:                    // Screen scale policy or explicit screen scale may have changed.
3364:                    updateScreenScale = true;
3365:
3366:                    // Update transforms derived from eye info.
3367:                    updateEyeDependencies();
3368:                }
3369:
3370:                private void updateHeadDependencies() {
3371:                    if (viewPolicy == View.HMD_VIEW) {
3372:                        // Image plates are fixed relative to the head, so their
3373:                        // positions have changed with respect to coexistence, the
3374:                        // view platform, and the virtual world.  The eyes are fixed
3375:                        // with respect to the image plates, so the projection doesn't
3376:                        // change with respect to them.
3377:                        updateCoeToPlate = true;
3378:                        updatePlateToViewPlatform = true;
3379:                        updatePlateToVworld = true;
3380:                        updateViewPlatformToEye = true;
3381:                        updateEyeToViewPlatform = true;
3382:                        updateEyeToVworld = true;
3383:                        updateInverseViewPlatformProjection = true;
3384:                        updateInverseVworldProjection = true;
3385:                    } else {
3386:                        // Eye positions have changed with respect to the fixed
3387:                        // screens, so the projections must be updated as well as the
3388:                        // positions.
3389:                        updateEyeInPlate = true;
3390:                        updateEyeDependencies();
3391:                    }
3392:                }
3393:
3394:                private void updateVworldDependencies() {
3395:                    updatePlateToVworld = true;
3396:                    updateCoeToVworld = true;
3397:                    updateEyeToVworld = true;
3398:                    updateTrackerBaseToVworld = true;
3399:                    updateInverseVworldProjection = true;
3400:
3401:                    if (vpi.updateVworldScale)
3402:                        updatePhysicalToVirtualScale = true;
3403:
3404:                    if (vpi.updateVworldScale && clipVirtual) {
3405:                        // vworldToViewPlatformScale changed and clip plane distances
3406:                        // are in virtual units.
3407:                        updateProjection = true;
3408:                        updateClipDistances = true;
3409:                        updateInverseProjection = true;
3410:                        updateInverseViewPlatformProjection = true;
3411:                    }
3412:                }
3413:            }
3414:
3415:            /**
3416:             * Prints out the specified transform in a readable format.
3417:             *
3418:             * @param t3d transform to be printed
3419:             * @param name the name of the transform
3420:             */
3421:            private static void t3dPrint(Transform3D t3d, String name) {
3422:                double[] m = new double[16];
3423:                t3d.get(m);
3424:                String[] sa = formatMatrixRows(4, 4, m);
3425:                System.err.println(name);
3426:                for (int i = 0; i < 4; i++)
3427:                    System.err.println(sa[i]);
3428:            }
3429:
3430:            /**
3431:             * Formats a matrix with fixed fractional digits and integer padding to
3432:             * align the decimal points in columns.  Non-negative numbers print up to
3433:             * 7 integer digits, while negative numbers print up to 6 integer digits
3434:             * to account for the negative sign.  6 fractional digits are printed.
3435:             *
3436:             * @param rowCount number of rows in the matrix
3437:             * @param colCount number of columns in the matrix
3438:             * @param m matrix to be formatted
3439:             * @return matrix rows formatted into strings
3440:             */
3441:            private static String[] formatMatrixRows(int rowCount,
3442:                    int colCount, double[] m) {
3443:
3444:                DecimalFormat df = new DecimalFormat("0.000000");
3445:                FieldPosition fp = new FieldPosition(
3446:                        DecimalFormat.INTEGER_FIELD);
3447:                StringBuffer sb0 = new StringBuffer();
3448:                StringBuffer sb1 = new StringBuffer();
3449:                String[] rows = new String[rowCount];
3450:
3451:                for (int i = 0; i < rowCount; i++) {
3452:                    sb0.setLength(0);
3453:                    for (int j = 0; j < colCount; j++) {
3454:                        sb1.setLength(0);
3455:                        df.format(m[i * colCount + j], sb1, fp);
3456:                        int pad = 8 - fp.getEndIndex();
3457:                        for (int k = 0; k < pad; k++) {
3458:                            sb1.insert(0, " ");
3459:                        }
3460:                        sb0.append(sb1);
3461:                    }
3462:                    rows[i] = sb0.toString();
3463:                }
3464:                return rows;
3465:            }
3466:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.