/* * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. * */ package javax.media.j3d; import java.awt.Rectangle; import javax.vecmath.Matrix4d; import javax.vecmath.Point2d; import javax.vecmath.Point3d; import javax.vecmath.Point4d; import javax.vecmath.SingularMatrixException; import javax.vecmath.Vector3d; import javax.vecmath.Vector4d; /** * The CanvasViewCache class is used to cache all data, both API data * and derived data, that is dependent on the Canvas3D or Screen3D. * The final view and projection matrices are stored here. */ class CanvasViewCache extends Object { // Used for debugging only private static Object debugLock = new Object(); // The canvas associated with this canvas view cache private Canvas3D canvas; // Mask that indicates this CanvasViewCache view dependence info. has changed, // and CanvasViewCache may need to recompute the final view matries. int cvcDirtyMask = 0; // The screen view cache associated with this canvas view cache private ScreenViewCache screenViewCache; // The view cache associated with this canvas view cache private ViewCache viewCache; // ************* // API/INPUT DATA // ************* // The position and size of the canvas (in pixels) private int awtCanvasX; private int awtCanvasY; private int awtCanvasWidth; private int awtCanvasHeight; // The current RenderBin used for rendering during the frame // associated with this snapshot. private RenderBin renderBin; // Flag indicating whether or not stereo will be used. Computed by // Canvas3D as: useStereo = stereoEnable && stereoAvailable private boolean useStereo; // Current monoscopic view policy from canvas private int monoscopicViewPolicy; // The manual positions of the left and right eyes in image-plate // coordinates. // Note that these values are only used in non-head-tracked mode // when the view's window eyepoint policy is one of RELATIVE_TO_SCREEN // or RELATIVE_TO_WINDOW. private Point3d leftManualEyeInImagePlate = new Point3d(); private Point3d rightManualEyeInImagePlate = new Point3d(); // ************* // DERIVED DATA // ************* // The width and height of the screen in meters (from ScreenViewCache) double physicalScreenWidth; double physicalScreenHeight; // The width and height of the screen in pixels (from ScreenViewCache) int screenWidth; int screenHeight; // Meters per pixel in the X and Y dimension (from ScreenViewCache) double metersPerPixelX; double metersPerPixelY; // The position and size of the canvas (in pixels) private int canvasX; private int canvasY; private int canvasWidth; private int canvasHeight; // Either the Canvas' or the View's monoscopicViewPolicy private int effectiveMonoscopicViewPolicy; // The current cached projection transforms. private Transform3D leftProjection = new Transform3D(); private Transform3D rightProjection = new Transform3D(); private Transform3D infLeftProjection = new Transform3D(); private Transform3D infRightProjection = new Transform3D(); // The current cached viewing transforms. private Transform3D leftVpcToEc = new Transform3D(); private Transform3D rightVpcToEc = new Transform3D(); private Transform3D infLeftVpcToEc = new Transform3D(); private Transform3D infRightVpcToEc = new Transform3D(); // The current cached inverse viewing transforms. private Transform3D leftEcToVpc = new Transform3D(); private Transform3D rightEcToVpc = new Transform3D(); private Transform3D infLeftEcToVpc = new Transform3D(); private Transform3D infRightEcToVpc = new Transform3D(); // Arrays of Vector4d objects that represent the plane equations for // the 6 planes in the viewing frustum in ViewPlatform coordinates. private Vector4d[] leftFrustumPlanes = new Vector4d[6]; private Vector4d[] rightFrustumPlanes = new Vector4d[6]; // Arrays of Vector4d objects that represent the volume of viewing frustum private Point4d leftFrustumPoints[] = new Point4d[8]; private Point4d rightFrustumPoints[] = new Point4d[8]; // Calibration matrix from Screen object for HMD mode using // non-field-sequential stereo private Transform3D headTrackerToLeftImagePlate = new Transform3D(); private Transform3D headTrackerToRightImagePlate = new Transform3D(); // Head tracked version of eye in imageplate private Point3d leftTrackedEyeInImagePlate = new Point3d(); private Point3d rightTrackedEyeInImagePlate = new Point3d(); // Derived version of eye in image plate coordinates private Point3d leftEyeInImagePlate = new Point3d(); private Point3d rightEyeInImagePlate = new Point3d(); private Point3d centerEyeInImagePlate = new Point3d(); // Derived version of nominalEyeOffsetFromNominalScreen private double nominalEyeOffset; // Physical window position,size and center (in image plate coordinates) private double physicalWindowXLeft; private double physicalWindowYBottom; private double physicalWindowXRight; private double physicalWindowYTop; private double physicalWindowWidth; private double physicalWindowHeight; private Point3d physicalWindowCenter = new Point3d(); // Screen scale value from viewCache or from screen size. private double screenScale; // Window scale value that compensates for window size if // the window resize policy is PHYSICAL_WORLD. private double windowScale; // ViewPlatform scale that takes coordinates from view platform // coordinates and scales them to physical coordinates private double viewPlatformScale; // Various derived transforms private Transform3D leftCcToVworld = new Transform3D(); private Transform3D rightCcToVworld = new Transform3D(); private Transform3D coexistenceToLeftPlate = new Transform3D(); private Transform3D coexistenceToRightPlate = new Transform3D(); private Transform3D vpcToCoexistence = new Transform3D(); private Transform3D vpcToLeftPlate = new Transform3D(); private Transform3D vpcToRightPlate = new Transform3D(); private Transform3D leftPlateToVpc = new Transform3D(); private Transform3D rightPlateToVpc = new Transform3D(); private Transform3D vworldToLeftPlate = new Transform3D(); private Transform3D lastVworldToLeftPlate = new Transform3D(); private Transform3D vworldToRightPlate = new Transform3D(); private Transform3D leftPlateToVworld = new Transform3D(); private Transform3D rightPlateToVworld = new Transform3D(); private Transform3D headToLeftImagePlate = new Transform3D(); private Transform3D headToRightImagePlate = new Transform3D(); private Transform3D vworldToTrackerBase = new Transform3D(); private Transform3D tempTrans = new Transform3D(); private Transform3D headToVworld = new Transform3D(); private Vector3d coexistenceCenter = new Vector3d(); // scale for transformimg clip and fog distances private double vworldToCoexistenceScale; private double infVworldToCoexistenceScale; // // Temporary matrices and vectors, so we dont generate garbage // private Transform3D tMat1 = new Transform3D(); private Transform3D tMat2 = new Transform3D(); private Vector3d tVec1 = new Vector3d(); private Vector3d tVec2 = new Vector3d(); private Vector3d tVec3 = new Vector3d(); private Point3d tPnt1 = new Point3d(); private Point3d tPnt2 = new Point3d(); private Matrix4d tMatrix = new Matrix4d(); /** * The view platform transforms. */ private Transform3D vworldToVpc = new Transform3D(); private Transform3D vpcToVworld = new Transform3D(); private Transform3D infVworldToVpc = new Transform3D(); // This flag is used to remember the last time doInfinite flag // is true or not. // If this cache is updated twice, the first time in RenderBin // updateViewCache() and the second time in Renderer with // geometryBackground. The first time will reset the vcDirtyMask // to 0 so that geometry background will not get updated the // second time doComputeDerivedData() is invoked when view change. private boolean lastDoInfinite = false; private boolean updateLastTime = false; void getCanvasPositionAndSize() { if(J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2) { System.err.println("Get canvas position and size"); System.err.println("Before"); System.err.println("Canvas pos = (" + awtCanvasX + ", " + awtCanvasY + "), size = " + awtCanvasWidth + "x" + awtCanvasHeight); System.err.println("After"); } awtCanvasX = canvas.newPosition.x; awtCanvasY = canvas.newPosition.y; awtCanvasWidth = canvas.newSize.width; awtCanvasHeight = canvas.newSize.height; // The following works around problem when awt creates 0-size // window at startup if ((awtCanvasWidth <= 0) || (awtCanvasHeight <= 0)) { awtCanvasWidth = 1; awtCanvasHeight = 1; } if (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1) { System.err.println("Canvas pos = (" + awtCanvasX + ", " + awtCanvasY + "), size = " + awtCanvasWidth + "x" + awtCanvasHeight); } } void computefrustumBBox(BoundingBox frustumBBox) { int i; for(i = 0; i < leftFrustumPoints.length; i++) { if(frustumBBox.lower.x > leftFrustumPoints[i].x) frustumBBox.lower.x = leftFrustumPoints[i].x; if(frustumBBox.lower.y > leftFrustumPoints[i].y) frustumBBox.lower.y = leftFrustumPoints[i].y; if(frustumBBox.lower.z > leftFrustumPoints[i].z) frustumBBox.lower.z = leftFrustumPoints[i].z; if(frustumBBox.upper.x < leftFrustumPoints[i].x) frustumBBox.upper.x = leftFrustumPoints[i].x; if(frustumBBox.upper.y < leftFrustumPoints[i].y) frustumBBox.upper.y = leftFrustumPoints[i].y; if(frustumBBox.upper.z < leftFrustumPoints[i].z) frustumBBox.upper.z = leftFrustumPoints[i].z; } if(useStereo) { for(i = 0; i< rightFrustumPoints.length; i++) { if(frustumBBox.lower.x > rightFrustumPoints[i].x) frustumBBox.lower.x = rightFrustumPoints[i].x; if(frustumBBox.lower.y > rightFrustumPoints[i].y) frustumBBox.lower.y = rightFrustumPoints[i].y; if(frustumBBox.lower.z > rightFrustumPoints[i].z) frustumBBox.lower.z = rightFrustumPoints[i].z; if(frustumBBox.upper.x < rightFrustumPoints[i].x) frustumBBox.upper.x = rightFrustumPoints[i].x; if(frustumBBox.upper.y < rightFrustumPoints[i].y) frustumBBox.upper.y = rightFrustumPoints[i].y; if(frustumBBox.upper.z < rightFrustumPoints[i].z) frustumBBox.upper.z = rightFrustumPoints[i].z; } } } void copyComputedCanvasViewCache(CanvasViewCache cvc, boolean doInfinite) { // For performance reason, only data needed by renderer are copied. // useStereo, // canvasWidth, // canvasHeight, // leftProjection, // rightProjection, // leftVpcToEc, // rightVpcToEc, // leftFrustumPlanes, // rightFrustumPlanes, // vpcToVworld, // vworldToVpc. cvc.useStereo = useStereo; cvc.canvasWidth = canvasWidth; cvc.canvasHeight = canvasHeight; cvc.leftProjection.set(leftProjection); cvc.rightProjection.set(rightProjection); cvc.leftVpcToEc.set(leftVpcToEc) ; cvc.rightVpcToEc.set(rightVpcToEc) ; cvc.vpcToVworld = vpcToVworld; cvc.vworldToVpc.set(vworldToVpc); if (doInfinite) { cvc.infLeftProjection.set(infLeftProjection); cvc.infRightProjection.set(infRightProjection); cvc.infLeftVpcToEc.set(infLeftVpcToEc) ; cvc.infRightVpcToEc.set(infRightVpcToEc) ; cvc.infVworldToVpc.set(infVworldToVpc); } for (int i = 0; i < leftFrustumPlanes.length; i++) { cvc.leftFrustumPlanes[i].x = leftFrustumPlanes[i].x; cvc.leftFrustumPlanes[i].y = leftFrustumPlanes[i].y; cvc.leftFrustumPlanes[i].z = leftFrustumPlanes[i].z; cvc.leftFrustumPlanes[i].w = leftFrustumPlanes[i].w; cvc.rightFrustumPlanes[i].x = rightFrustumPlanes[i].x; cvc.rightFrustumPlanes[i].y = rightFrustumPlanes[i].y; cvc.rightFrustumPlanes[i].z = rightFrustumPlanes[i].z; cvc.rightFrustumPlanes[i].w = rightFrustumPlanes[i].w; } } /** * Take snapshot of all per-canvas API parameters and input values. * NOTE: This is probably not needed, but we'll do it for symmetry * with the ScreenViewCache and ViewCache objects. */ synchronized void snapshot(boolean computeFrustum) { // Issue 109 : determine the the correct index to use -- either the // Renderer or RenderBin int dirtyIndex = computeFrustum ? Canvas3D.RENDER_BIN_DIRTY_IDX : Canvas3D.RENDERER_DIRTY_IDX; synchronized (canvas.dirtyMaskLock) { // Issue 109 : read/clear the dirty bits for the correct index cvcDirtyMask = canvas.cvDirtyMask[dirtyIndex]; canvas.cvDirtyMask[dirtyIndex] = 0; } useStereo = canvas.useStereo; monoscopicViewPolicy = canvas.monoscopicViewPolicy; leftManualEyeInImagePlate.set(canvas.leftManualEyeInImagePlate); rightManualEyeInImagePlate.set(canvas.rightManualEyeInImagePlate); if(( cvcDirtyMask & Canvas3D.MOVED_OR_RESIZED_DIRTY) != 0) { getCanvasPositionAndSize(); } renderBin = canvas.view.renderBin; } /** * Compute derived data using the snapshot of the per-canvas, * per-screen and per-view data. */ synchronized void computeDerivedData(boolean currentFlag, CanvasViewCache cvc, BoundingBox frustumBBox, boolean doInfinite) { if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) { synchronized(debugLock) { System.err.println("------------------------------"); doComputeDerivedData(currentFlag,cvc,frustumBBox,doInfinite); } } else { doComputeDerivedData(currentFlag,cvc,frustumBBox,doInfinite); } } /** * Compute derived data using the snapshot of the per-canvas, * per-screen and per-view data. Caller must synchronize before * calling this method. */ private void doComputeDerivedData(boolean currentFlag, CanvasViewCache cvc, BoundingBox frustumBBox, boolean doInfinite) { // Issue 109 : determine the the correct index to use -- either the // Renderer or RenderBin int dirtyIndex = (frustumBBox != null) ? Canvas3D.RENDER_BIN_DIRTY_IDX : Canvas3D.RENDERER_DIRTY_IDX; int scrvcDirtyMask; // Issue 109 : read/clear the dirty bits for the correct index synchronized (screenViewCache) { scrvcDirtyMask = screenViewCache.scrvcDirtyMask[dirtyIndex]; // reset screen view dirty mask if canvas is offScreen. Note: // there is only one canvas per offscreen, so it is ok to // do the reset here. if (canvas.offScreen) { screenViewCache.scrvcDirtyMask[dirtyIndex] = 0; } } if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { if(cvcDirtyMask != 0) System.err.println("cvcDirtyMask : " + cvcDirtyMask); if(scrvcDirtyMask != 0) System.err.println("scrvcDirtyMask : "+ scrvcDirtyMask); if(viewCache.vcDirtyMask != 0) System.err.println("vcDirtyMask : " + viewCache.vcDirtyMask); } // NOTE: This fix is only fixing the symptoms, but not the // root of the bug. We shouldn't have to check for null here. if(viewCache.vpRetained == null) { System.err.println("CanvasViewCache : Error! viewCache.vpRetained is null"); return; } // This flag is use to force a computation when a ViewPlatformTransform // is detected. No sync. needed. We're doing a read of t/f. // XXXX: Peeking at the dirty flag is a hack. Need to revisit this. boolean vprNotDirty = (viewCache.vpRetained.vprDirtyMask == 0); // Issue 131: If not manual, it has to be considered as an onscreen canvas. if(!canvas.manualRendering && (vprNotDirty) && (cvcDirtyMask == 0) && (scrvcDirtyMask == 0) && (viewCache.vcDirtyMask == 0) && !(updateLastTime && (doInfinite != lastDoInfinite))) { if(frustumBBox != null) computefrustumBBox(frustumBBox); // Copy the computed data into cvc. if(cvc != null) { copyComputedCanvasViewCache(cvc, doInfinite); } lastDoInfinite = doInfinite; updateLastTime = false; return; } lastDoInfinite = doInfinite; updateLastTime = true; if(currentFlag) { vpcToVworld.set(viewCache.vpRetained.getCurrentLocalToVworld(null)); } else { vpcToVworld.set(viewCache.vpRetained.getLastLocalToVworld(null)); } // System.err.println("vpcToVworld is \n" + vpcToVworld); try { vworldToVpc.invert(vpcToVworld); } catch (SingularMatrixException e) { vworldToVpc.setIdentity(); //System.err.println("SingularMatrixException encountered when doing vworldToVpc invert"); } if (doInfinite) { vworldToVpc.getRotation(infVworldToVpc); } // Compute global flags if (monoscopicViewPolicy == View.CYCLOPEAN_EYE_VIEW) effectiveMonoscopicViewPolicy = viewCache.monoscopicViewPolicy; else effectiveMonoscopicViewPolicy = monoscopicViewPolicy; // Recompute info about current canvas window computeCanvasInfo(); // Compute coexistence center (in plate coordinates) computeCoexistenceCenter(); // Get Eye position in image-plate coordinates cacheEyePosition(); // Compute VPC to COE and COE to PLATE transforms computeVpcToCoexistence(); computeCoexistenceToPlate(); // Compute view and projection matrices computeView(doInfinite); computePlateToVworld(); if (!currentFlag) { // save the result for use in RasterRetained computeWinCoord lastVworldToLeftPlate.set(vworldToLeftPlate); } computeHeadToVworld(); if (frustumBBox != null) computefrustumBBox(frustumBBox); // Issue 109: cvc should *always* be null assert cvc == null; if(cvc != null) copyComputedCanvasViewCache(cvc, doInfinite); canvas.canvasDirty |= Canvas3D.VIEW_MATRIX_DIRTY; if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) { // Print some data : System.err.println("useStereo = " + useStereo); System.err.println("leftProjection:\n" + leftProjection); System.err.println("rightProjection:\n " + rightProjection); System.err.println("leftVpcToEc:\n" + leftVpcToEc); System.err.println("rightVpcToEc:\n" + rightVpcToEc); System.err.println("vpcToVworld:\n" + vpcToVworld); System.err.println("vworldToVpc:\n" + vworldToVpc); if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { int i; for (i = 0; i < leftFrustumPlanes.length; i++) { System.err.println("leftFrustumPlanes " + i + " is " + leftFrustumPlanes[i]); } for (i = 0; i < rightFrustumPlanes.length; i++) { System.err.println("rightFrustumPlanes " + i + " is " + rightFrustumPlanes[i]); } } } } private void computeCanvasInfo() { // Copy the screen width and height info into derived parameters physicalScreenWidth = screenViewCache.physicalScreenWidth; physicalScreenHeight = screenViewCache.physicalScreenHeight; screenWidth = screenViewCache.screenWidth; screenHeight = screenViewCache.screenHeight; metersPerPixelX = screenViewCache.metersPerPixelX; metersPerPixelY = screenViewCache.metersPerPixelY; // If a multi-screen virtual device (e.g. Xinerama) is being used, // then awtCanvasX and awtCanvasY are relative to the origin of that // virtual screen. Subtract the origin of the physical screen to // compute the origin in physical (image plate) coordinates. Rectangle screenBounds = canvas.graphicsConfiguration.getBounds(); canvasX = awtCanvasX - screenBounds.x; canvasY = awtCanvasY - screenBounds.y; // Use awtCanvasWidth and awtCanvasHeight as reported. canvasWidth = awtCanvasWidth; canvasHeight = awtCanvasHeight; // Convert the window system ``pixel'' coordinate location and size // of the window into physical units (meters) and coordinate system. // Window width and Height in meters physicalWindowWidth = canvasWidth * metersPerPixelX; physicalWindowHeight = canvasHeight * metersPerPixelY; // Compute the 4 corners of the window in physical units physicalWindowXLeft = metersPerPixelX * (double) canvasX; physicalWindowYBottom = metersPerPixelY * (double)(screenHeight - canvasHeight - canvasY); physicalWindowXRight = physicalWindowXLeft + physicalWindowWidth; physicalWindowYTop = physicalWindowYBottom + physicalWindowHeight; // Cache the physical location of the center of the window physicalWindowCenter.x = physicalWindowXLeft + physicalWindowWidth / 2.0; physicalWindowCenter.y = physicalWindowYBottom + physicalWindowHeight / 2.0; physicalWindowCenter.z = 0.0; if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("Canvas pos = (" + awtCanvasX + ", " + awtCanvasY + "), size = " + awtCanvasWidth + "x" + awtCanvasHeight); System.err.println("Window LL corner (in plate coordinates): " + "(" + physicalWindowXLeft + "," + physicalWindowYBottom + ")"); System.err.println("Window size (in plate coordinates): " + "(" + physicalWindowWidth + "," + physicalWindowHeight + ")"); System.err.println("Window center (in plate coordinates): " + physicalWindowCenter); System.err.println(); } // Compute the view platform scale. This combines // the screen scale and the window scale. computeViewPlatformScale(); if (!viewCache.compatibilityModeEnable && viewCache.viewPolicy == View.HMD_VIEW) { if (!useStereo) { switch(effectiveMonoscopicViewPolicy) { case View.CYCLOPEAN_EYE_VIEW: if(J3dDebug.devPhase) { System.err.println("CanvasViewCache : Should never reach here.\n" + "HMD_VIEW with CYCLOPEAN_EYE_VIEW is not allowed"); } break; case View.LEFT_EYE_VIEW: headTrackerToLeftImagePlate.set(screenViewCache. headTrackerToLeftImagePlate); break; case View.RIGHT_EYE_VIEW: headTrackerToLeftImagePlate.set(screenViewCache. headTrackerToRightImagePlate); break; } } else { headTrackerToLeftImagePlate.set(screenViewCache. headTrackerToLeftImagePlate); headTrackerToRightImagePlate.set(screenViewCache. headTrackerToRightImagePlate); } } } // Routine to compute the center of coexistence coordinates in // imageplate coordinates. Also compute the scale from Vpc private void computeViewPlatformScale() { windowScale = screenScale = 1.0; if (!viewCache.compatibilityModeEnable) { switch (viewCache.screenScalePolicy) { case View.SCALE_SCREEN_SIZE: screenScale = physicalScreenWidth / 2.0; break; case View.SCALE_EXPLICIT: screenScale = viewCache.screenScale; break; } if (viewCache.windowResizePolicy == View.PHYSICAL_WORLD) { windowScale = physicalWindowWidth / physicalScreenWidth; } } viewPlatformScale = windowScale * screenScale; if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("viewCache.windowResizePolicy = " + viewCache.windowResizePolicy); System.err.println("physicalWindowWidth = " + physicalWindowWidth); System.err.println("physicalScreenWidth = " + physicalScreenWidth); System.err.println("windowScale = " + windowScale); System.err.println("screenScale = " + screenScale); System.err.println("viewPlatformScale = " + viewPlatformScale); } } private void cacheEyePosFixedField() { if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) System.err.println("cacheEyePosFixedField:"); // y is always the window center rightEyeInImagePlate.y = leftEyeInImagePlate.y = physicalWindowCenter.y; if (!useStereo) { switch(effectiveMonoscopicViewPolicy) { case View.CYCLOPEAN_EYE_VIEW: leftEyeInImagePlate.x = physicalWindowCenter.x; break; case View.LEFT_EYE_VIEW: leftEyeInImagePlate.x = physicalWindowCenter.x + viewCache.leftEyePosInHead.x; break; case View.RIGHT_EYE_VIEW: leftEyeInImagePlate.x = physicalWindowCenter.x + viewCache.rightEyePosInHead.x; break; } // Set right as well just in case rightEyeInImagePlate.x = leftEyeInImagePlate.x; } else { leftEyeInImagePlate.x = physicalWindowCenter.x + viewCache.leftEyePosInHead.x; rightEyeInImagePlate.x = physicalWindowCenter.x + viewCache.rightEyePosInHead.x; } // // Derive the z distance by constraining the field of view of the // window width to be constant. // rightEyeInImagePlate.z = leftEyeInImagePlate.z = physicalWindowWidth / (2.0 * Math.tan(viewCache.fieldOfView / 2.0)); // Denote that eyes-in-ImagePlate fields have changed so that // these new values can be sent to the AudioDevice if (this.viewCache.view.soundScheduler != null) this.viewCache.view.soundScheduler.setListenerFlag( SoundScheduler.EYE_POSITIONS_CHANGED); } /** * Case of view eye position contrainted to center of window, but * with z distance from plate eye pos. */ private void cacheEyePosWindowRelative() { if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) System.err.println("cacheEyePosWindowRelative:"); // y is always the window center rightEyeInImagePlate.y = leftEyeInImagePlate.y = physicalWindowCenter.y; // z is always from the existing eye pos rightEyeInImagePlate.z = leftEyeInImagePlate.z = leftManualEyeInImagePlate.z; if (!useStereo) { switch(effectiveMonoscopicViewPolicy) { case View.CYCLOPEAN_EYE_VIEW: leftEyeInImagePlate.x = physicalWindowCenter.x; break; case View.LEFT_EYE_VIEW: leftEyeInImagePlate.x = physicalWindowCenter.x + viewCache.leftEyePosInHead.x; break; case View.RIGHT_EYE_VIEW: leftEyeInImagePlate.x = physicalWindowCenter.x + viewCache.rightEyePosInHead.x; break; } // Set right as well just in case rightEyeInImagePlate.x = leftEyeInImagePlate.x; } else { leftEyeInImagePlate.x = physicalWindowCenter.x + viewCache.leftEyePosInHead.x; rightEyeInImagePlate.x = physicalWindowCenter.x + viewCache.rightEyePosInHead.x; // Right z gets its own value rightEyeInImagePlate.z = rightManualEyeInImagePlate.z; } // Denote that eyes-in-ImagePlate fields have changed so that // these new values can be sent to the AudioDevice if (this.viewCache.view.soundScheduler != null) this.viewCache.view.soundScheduler.setListenerFlag( SoundScheduler.EYE_POSITIONS_CHANGED); } /** * Common routine used when head tracking and when using manual * relative_to_screen eyepoint policy. */ private void cacheEyePosScreenRelative(Point3d leftEye, Point3d rightEye) { if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) System.err.println("cacheEyePosScreenRelative:"); if (!useStereo) { switch(effectiveMonoscopicViewPolicy) { case View.CYCLOPEAN_EYE_VIEW: leftEyeInImagePlate.x = (leftEye.x + rightEye.x) / 2.0; leftEyeInImagePlate.y = (leftEye.y + rightEye.y) / 2.0; leftEyeInImagePlate.z = (leftEye.z + rightEye.z) / 2.0; break; case View.LEFT_EYE_VIEW: leftEyeInImagePlate.set(leftEye); break; case View.RIGHT_EYE_VIEW: leftEyeInImagePlate.set(rightEye); break; } // Set right as well just in case rightEyeInImagePlate.set(leftEyeInImagePlate); } else { leftEyeInImagePlate.set(leftEye); rightEyeInImagePlate.set(rightEye); } // Denote that eyes-in-ImagePlate fields have changed so that // these new values can be sent to the AudioDevice if (this.viewCache.view.soundScheduler != null) this.viewCache.view.soundScheduler.setListenerFlag( SoundScheduler.EYE_POSITIONS_CHANGED); } private void cacheEyePosCoexistenceRelative(Point3d leftManualEyeInCoexistence, Point3d rightManualEyeInCoexistence) { tPnt1.set(leftManualEyeInCoexistence); viewCache.coexistenceToTrackerBase.transform(tPnt1); screenViewCache.trackerBaseToImagePlate.transform(tPnt1); tPnt1.add(coexistenceCenter); tPnt2.set(rightManualEyeInCoexistence); viewCache.coexistenceToTrackerBase.transform(tPnt2); screenViewCache.trackerBaseToImagePlate.transform(tPnt2); tPnt2.add(coexistenceCenter); cacheEyePosScreenRelative(tPnt1, tPnt2); } /** * Compute the head-tracked eye position for the right and * left eyes. */ private void computeTrackedEyePosition() { if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("computeTrackedEyePosition:"); System.err.println("viewCache.headTrackerToTrackerBase:"); System.err.println(viewCache.headTrackerToTrackerBase); System.err.println("viewCache.headToHeadTracker:"); System.err.println(viewCache.headToHeadTracker); } if (viewCache.viewPolicy != View.HMD_VIEW) { if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("screenViewCache.trackerBaseToImagePlate:"); System.err.println(screenViewCache.trackerBaseToImagePlate); } headToLeftImagePlate.set(coexistenceCenter); headToLeftImagePlate.mul(screenViewCache.trackerBaseToImagePlate); headToLeftImagePlate.mul(viewCache.headTrackerToTrackerBase); headToLeftImagePlate.mul(viewCache.headToHeadTracker); headToLeftImagePlate.transform(viewCache.leftEyePosInHead, leftTrackedEyeInImagePlate); headToLeftImagePlate.transform(viewCache.rightEyePosInHead, rightTrackedEyeInImagePlate); } else { if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("headTrackerToLeftImagePlate:"); System.err.println(headTrackerToLeftImagePlate); } headToLeftImagePlate.mul(headTrackerToLeftImagePlate, viewCache.headToHeadTracker); headToLeftImagePlate.transform(viewCache.leftEyePosInHead, leftTrackedEyeInImagePlate); if(useStereo) { headToRightImagePlate.mul(headTrackerToRightImagePlate, viewCache.headToHeadTracker); headToRightImagePlate.transform(viewCache.rightEyePosInHead, rightTrackedEyeInImagePlate); } else { // HMD_VIEW with no stereo. headToLeftImagePlate.transform(viewCache.rightEyePosInHead, rightTrackedEyeInImagePlate); } } if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("headToLeftImagePlate:"); System.err.println(headToLeftImagePlate); System.err.println("headToRightImagePlate:"); System.err.println(headToRightImagePlate); } } /** * Routine to cache the current eye position in image plate * coordinates. */ private void cacheEyePosition() { if (viewCache.compatibilityModeEnable) { // XXXX: Compute compatibility mode eye position in ImagePlate??? cacheEyePosScreenRelative(leftManualEyeInImagePlate, rightManualEyeInImagePlate); } else if (viewCache.getDoHeadTracking()) { computeTrackedEyePosition(); cacheEyePosScreenRelative(leftTrackedEyeInImagePlate, rightTrackedEyeInImagePlate); } else { switch (viewCache.windowEyepointPolicy) { case View.RELATIVE_TO_FIELD_OF_VIEW: cacheEyePosFixedField(); break; case View.RELATIVE_TO_WINDOW: cacheEyePosWindowRelative(); break; case View.RELATIVE_TO_SCREEN: cacheEyePosScreenRelative(leftManualEyeInImagePlate, rightManualEyeInImagePlate); break; case View.RELATIVE_TO_COEXISTENCE: cacheEyePosCoexistenceRelative(viewCache.leftManualEyeInCoexistence, viewCache.rightManualEyeInCoexistence); break; } } // Compute center eye centerEyeInImagePlate.add(leftEyeInImagePlate, rightEyeInImagePlate); centerEyeInImagePlate.scale(0.5); // Compute derived value of nominalEyeOffsetFromNominalScreen if (viewCache.windowEyepointPolicy == View.RELATIVE_TO_FIELD_OF_VIEW) nominalEyeOffset = centerEyeInImagePlate.z; else nominalEyeOffset = viewCache.nominalEyeOffsetFromNominalScreen; if ((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) { System.err.println("leftEyeInImagePlate = " + leftEyeInImagePlate); System.err.println("rightEyeInImagePlate = " + rightEyeInImagePlate); System.err.println("centerEyeInImagePlate = " + centerEyeInImagePlate); System.err.println("nominalEyeOffset = " + nominalEyeOffset); System.err.println(); } } private void computePlateToVworld() { if (viewCache.compatibilityModeEnable) { // XXXX: implement this correctly for compat mode leftPlateToVworld.setIdentity(); vworldToLeftPlate.setIdentity(); } else { try { leftPlateToVpc.invert(vpcToLeftPlate); } catch (SingularMatrixException e) { leftPlateToVpc.setIdentity(); /* System.err.println("SingularMatrixException encountered when doing" + " leftPlateToVpc invert"); */ } leftPlateToVworld.mul(vpcToVworld, leftPlateToVpc); vworldToLeftPlate.mul(vpcToLeftPlate, vworldToVpc); if(useStereo) { try { rightPlateToVpc.invert(vpcToRightPlate); } catch (SingularMatrixException e) { rightPlateToVpc.setIdentity(); /* System.err.println("SingularMatrixException encountered when doing" + " rightPlateToVpc invert"); */ } rightPlateToVworld.mul(vpcToVworld, rightPlateToVpc); vworldToRightPlate.mul(vpcToRightPlate, vworldToVpc); } if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("vpcToVworld:"); System.err.println(vpcToVworld); System.err.println("vpcToLeftPlate:"); System.err.println(vpcToLeftPlate); if(useStereo) { System.err.println("vpcToRightPlate:"); System.err.println(vpcToRightPlate); } } } // Denote that eyes-in-ImagePlate fields have changed so that // these new values can be sent to the AudioDevice if (this.viewCache.view.soundScheduler != null) this.viewCache.view.soundScheduler.setListenerFlag( SoundScheduler.IMAGE_PLATE_TO_VWORLD_CHANGED); } private void computeHeadToVworld() { // Concatenate headToLeftImagePlate with leftPlateToVworld if (viewCache.compatibilityModeEnable) { // XXXX: implement this correctly for compat mode headToVworld.setIdentity(); } else { headToVworld.mul(leftPlateToVworld, headToLeftImagePlate); if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("leftPlateToVworld:"); System.err.println(leftPlateToVworld); System.err.println("headToLeftImagePlate:"); System.err.println(headToLeftImagePlate); System.err.println("...gives -> headToVworld:"); System.err.println(headToVworld); } } // Denote that eyes-in-ImagePlate fields have changed so that // these new values can be sent to the AudioDevice if (this.viewCache.view.soundScheduler != null) this.viewCache.view.soundScheduler.setListenerFlag( SoundScheduler.HEAD_TO_VWORLD_CHANGED); } private void computeVpcToCoexistence() { // Create a transform with the view platform to coexistence scale tMat1.set(viewPlatformScale); // XXXX: Is this really correct to ignore HMD? if (viewCache.viewPolicy != View.HMD_VIEW) { switch (viewCache.coexistenceCenterInPworldPolicy) { case View.NOMINAL_SCREEN : switch (viewCache.viewAttachPolicy) { case View.NOMINAL_SCREEN: tMat2.setIdentity(); break; case View.NOMINAL_HEAD: tVec1.set(0.0, 0.0, nominalEyeOffset); tMat2.set(tVec1); break; case View.NOMINAL_FEET: tVec1.set(0.0, -viewCache.nominalEyeHeightFromGround, nominalEyeOffset); tMat2.set(tVec1); break; } break; case View.NOMINAL_HEAD : switch (viewCache.viewAttachPolicy) { case View.NOMINAL_SCREEN: tVec1.set(0.0, 0.0, -nominalEyeOffset); tMat2.set(tVec1); break; case View.NOMINAL_HEAD: tMat2.setIdentity(); break; case View.NOMINAL_FEET: tVec1.set(0.0, -viewCache.nominalEyeHeightFromGround, 0.0); tMat2.set(tVec1); break; } break; case View.NOMINAL_FEET: switch (viewCache.viewAttachPolicy) { case View.NOMINAL_SCREEN: tVec1.set(0.0, viewCache.nominalEyeHeightFromGround, -nominalEyeOffset); tMat2.set(tVec1); break; case View.NOMINAL_HEAD: tVec1.set(0.0, viewCache.nominalEyeHeightFromGround, 0.0); tMat2.set(tVec1); break; case View.NOMINAL_FEET: tMat2.setIdentity(); break; } break; } vpcToCoexistence.mul(tMat2, tMat1); } else { vpcToCoexistence.set(tMat1); } if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("vpcToCoexistence:"); System.err.println(vpcToCoexistence); } } private void computeCoexistenceCenter() { if ((!viewCache.compatibilityModeEnable) && (viewCache.viewPolicy != View.HMD_VIEW) && (viewCache.coexistenceCenteringEnable) && (viewCache.coexistenceCenterInPworldPolicy == View.NOMINAL_SCREEN)) { // Compute the coexistence center in image plate coordinates // Image plate cordinates have their orgin in the lower // left hand corner of the CRT visiable raster. // The nominal coexistence center is at the *center* of // targeted area: either the window or screen, depending // on policy. if (viewCache.windowMovementPolicy == View.VIRTUAL_WORLD) { coexistenceCenter.x = physicalScreenWidth / 2.0; coexistenceCenter.y = physicalScreenHeight / 2.0; coexistenceCenter.z = 0.0; } else { // windowMovementPolicy == PHYSICAL_WORLD coexistenceCenter.x = physicalWindowCenter.x; coexistenceCenter.y = physicalWindowCenter.y; coexistenceCenter.z = 0.0; } } else { coexistenceCenter.set(0.0, 0.0, 0.0); } if(J3dDebug.devPhase) { if (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1) { System.err.println("coexistenceCenter = " + coexistenceCenter); } } } private void computeCoexistenceToPlate() { if (viewCache.compatibilityModeEnable) { // XXXX: implement this correctly coexistenceToLeftPlate.setIdentity(); return; } if (viewCache.viewPolicy != View.HMD_VIEW) { coexistenceToLeftPlate.set(coexistenceCenter); coexistenceToLeftPlate.mul(screenViewCache.trackerBaseToImagePlate); coexistenceToLeftPlate.mul(viewCache.coexistenceToTrackerBase); if(useStereo) { coexistenceToRightPlate.set(coexistenceToLeftPlate); } } else { coexistenceToLeftPlate.mul(headTrackerToLeftImagePlate, viewCache.trackerBaseToHeadTracker); coexistenceToLeftPlate.mul(viewCache.coexistenceToTrackerBase); if(useStereo) { coexistenceToRightPlate.mul(headTrackerToRightImagePlate, viewCache.trackerBaseToHeadTracker); coexistenceToRightPlate.mul(viewCache.coexistenceToTrackerBase); } } if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { System.err.println("coexistenceToLeftPlate:"); System.err.println(coexistenceToLeftPlate); if(useStereo) { System.err.println("coexistenceToRightPlate:"); System.err.println(coexistenceToRightPlate); } } } /** * Computes the viewing matrices. * * computeView computes the following: * *