diff options
Diffstat (limited to 'src/javax/media/j3d/CanvasViewCache.java')
-rw-r--r-- | src/javax/media/j3d/CanvasViewCache.java | 2044 |
1 files changed, 2044 insertions, 0 deletions
diff --git a/src/javax/media/j3d/CanvasViewCache.java b/src/javax/media/j3d/CanvasViewCache.java new file mode 100644 index 0000000..0360d3c --- /dev/null +++ b/src/javax/media/j3d/CanvasViewCache.java @@ -0,0 +1,2044 @@ +/* + * 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: + * + * <ul> + * left (& right) eye viewing matrices (only left is valid for mono view) + * </ul> + * + * This call works for both fixed screen and HMD displays. + */ + private void computeView(boolean doInfinite) { + int backClipPolicy; + double Fl, Fr, B, scale, backClipDistance; + + // compute scale used for transforming clip and fog distances + vworldToCoexistenceScale = vworldToVpc.getDistanceScale() + * vpcToCoexistence.getDistanceScale(); + if(doInfinite) { + infVworldToCoexistenceScale = infVworldToVpc.getDistanceScale() + * vpcToCoexistence.getDistanceScale(); + } + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("vworldToCoexistenceScale = " + + vworldToCoexistenceScale); + } + + // compute coexistenceToVworld transform -- dirty bit candidate!! + tempTrans.mul(viewCache.coexistenceToTrackerBase, vpcToCoexistence); + vworldToTrackerBase.mul(tempTrans, vworldToVpc); + + // If we are in compatibility mode, compute the view and + // projection matrices accordingly + if (viewCache.compatibilityModeEnable) { + leftProjection.set(viewCache.compatLeftProjection); + leftVpcToEc.set(viewCache.compatVpcToEc); + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) { + System.err.println("Left projection and view matrices"); + System.err.println("ecToCc (leftProjection) :"); + System.err.println(leftProjection); + System.err.println("vpcToEc:"); + System.err.println(leftVpcToEc); + } + + computeFrustumPlanes(leftProjection, leftVpcToEc, + leftFrustumPlanes, leftFrustumPoints, + leftCcToVworld); + + if(useStereo) { + rightProjection.set(viewCache.compatRightProjection); + rightVpcToEc.set(viewCache.compatVpcToEc); + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) { + System.err.println("Right projection and view matrices"); + System.err.println("ecToCc:"); + System.err.println("vpcToEc:"); + System.err.println(rightVpcToEc); + } + + computeFrustumPlanes(rightProjection, rightVpcToEc, + rightFrustumPlanes, rightFrustumPoints, + rightCcToVworld); + } + + return; + } + + // + // The clipping plane distances are set from the internal policy. + // + // Note that the plane distance follows the standard Z axis + // convention, e.g. negative numbers further away. + // Note that for policy from eye, the distance is negative in + // the direction of z in front of the eye. + // Note that for policy from screen, the distance is negative for + // locations behind the screen, and positive in front. + // + // The distance attributes are measured either in physical (plate) + // units, or vworld units. + // + + // Compute scale factor for front clip plane computation + if (viewCache.frontClipPolicy == View.VIRTUAL_EYE || + viewCache.frontClipPolicy == View.VIRTUAL_SCREEN) { + scale = vworldToCoexistenceScale; + } + else { + scale = windowScale; + } + + // Set left and right front clipping plane distances. + if(viewCache.frontClipPolicy == View.PHYSICAL_EYE || + viewCache.frontClipPolicy == View.VIRTUAL_EYE) { + Fl = leftEyeInImagePlate.z + + scale * -viewCache.frontClipDistance; + Fr = rightEyeInImagePlate.z + + scale * -viewCache.frontClipDistance; + } + else { + Fl = scale * -viewCache.frontClipDistance; + Fr = scale * -viewCache.frontClipDistance; + } + + // if there is an active clip node, use it and ignore the view's + // backclip + if ((renderBin != null) && (renderBin.backClipActive)) { + backClipPolicy = View.VIRTUAL_EYE; + backClipDistance = renderBin.backClipDistanceInVworld; + } else { + backClipPolicy = viewCache.backClipPolicy; + backClipDistance = viewCache.backClipDistance; + } + + // Compute scale factor for rear clip plane computation + if (backClipPolicy == View.VIRTUAL_EYE || + backClipPolicy == View.VIRTUAL_SCREEN) { + scale = vworldToCoexistenceScale; + } + else { + scale = windowScale; + } + + // Set left and right rear clipping plane distnaces. + if(backClipPolicy == View.PHYSICAL_EYE || + backClipPolicy == View.VIRTUAL_EYE) { + // Yes, left for both left and right rear. + B = leftEyeInImagePlate.z + + scale * -backClipDistance; + } + else { + B = scale * -backClipDistance; + } + + // XXXX: Can optimize for HMD case. + if (true /*viewCache.viewPolicy != View.HMD_VIEW*/) { + + // Call buildProjView to build the projection and view matrices. + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("Left projection and view matrices"); + System.err.println("Fl " + Fl + " B " + B); + System.err.println("leftEyeInImagePlate\n" + leftEyeInImagePlate); + System.err.println("Before : leftProjection\n" + leftProjection); + System.err.println("Before leftVpcToEc\n" + leftVpcToEc); + } + + buildProjView(leftEyeInImagePlate, coexistenceToLeftPlate, + vpcToLeftPlate, Fl, B, leftProjection, leftVpcToEc, false); + + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("After : leftProjection\n" + leftProjection); + System.err.println("After leftVpcToEc\n" + leftVpcToEc); + } + + computeFrustumPlanes(leftProjection, leftVpcToEc, + leftFrustumPlanes, leftFrustumPoints, + leftCcToVworld); + + if(useStereo) { + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) + System.err.println("Right projection and view matrices"); + + buildProjView(rightEyeInImagePlate, coexistenceToRightPlate, + vpcToRightPlate, Fr, B, rightProjection, + rightVpcToEc, false); + + computeFrustumPlanes(rightProjection, rightVpcToEc, + rightFrustumPlanes, rightFrustumPoints, + rightCcToVworld); + } + + // + // Now to compute the left (& right) eye (and infinite) + // viewing matrices. + if(doInfinite) { + // Call buildProjView separately for infinite view + buildProjView(leftEyeInImagePlate, coexistenceToLeftPlate, + vpcToLeftPlate, leftEyeInImagePlate.z - 0.05, + leftEyeInImagePlate.z - 1.5, + infLeftProjection, infLeftVpcToEc, true); + + if(useStereo) { + buildProjView(rightEyeInImagePlate, coexistenceToRightPlate, + vpcToRightPlate, rightEyeInImagePlate.z - 0.05, + rightEyeInImagePlate.z - 1.5, + infRightProjection, infRightVpcToEc, true); + + } + } + } + // XXXX: The following code has never been ported +// else { +// Point3d cen_eye; +// +// // HMD case. Just concatenate the approprate matrices together. +// // Additional work just for now +// +// compute_lr_plate_to_cc( &cen_eye, Fl, B, 0, &vb, 0); +// +// if(useStereo) { +// mat_mul_dpt(&right_eye_pos_in_head, +// head_to_right_plate, &cen_eye); +// compute_lr_plate_to_cc( &cen_eye, Fr, B, +// 1, &vb, 0); +// } +// +// // Make sure that coexistence_to_plate is current. +// // (It is usually constant for fixed plates, always varies for HMDs.) +// // For HMD case, computes finial matrices that will be used. +// // +// computeCoexistenceToPlate(); +// } + + } + + /** + * Debugging routine to analyze the projection matrix. + */ + private void analyzeProjection(Transform3D p, double xMax) { + if (viewCache.projectionPolicy == View.PARALLEL_PROJECTION) + System.err.println("PARALLEL_PROJECTION ="); + else + System.err.println("PERSPECTIVE_PROJECTION ="); + + System.err.println(p); + + double projectionPlaneZ = ((p.mat[0] * xMax + p.mat[3] - p.mat[15]) / + (p.mat[14] - p.mat[2])); + + System.err.println("projection plane at z = " + projectionPlaneZ); + } + + /** + * buildProjView creates a projection and viewing matrix. + * + * Inputs: + * ep : eye point, in plate coordinates + * coe2Plate : matrix from coexistence to image plate. + * F, B : front, back clipping planes, in plate coordinates + * doInfinite : flag to indicate ``at infinity'' view desired + * + * Output: + * vpc2Plate : matric from vpc to image plate. + * ecToCc : projection matrix from Eye Coordinates (EC) + * to Clipping Coordinates (CC) + * vpcToEc : view matrix from ViewPlatform Coordinates (VPC) + * to Eye Coordinates (EC) + */ + private void buildProjView(Point3d ep, + Transform3D coe2Plate, + Transform3D vpc2Plate, + double F, + double B, + Transform3D ecToCc, + Transform3D vpcToEc, + boolean doInfinite) { + + // Lx,Ly Hx,Hy will be adjusted window boundaries + double Lx, Hx, Ly, Hy; + Lx = physicalWindowXLeft; Hx = physicalWindowXRight; + Ly = physicalWindowYBottom; Hy = physicalWindowYTop; + + ecToCc.setIdentity(); + + + // XXXX: we have no concept of glass correction in the Java 3D API + // + // Correction in apparent 3D position of window due to glass/CRT + // and spherical/cylinderical curvarure of CRT. + // This boils down to producing modified values of Lx Ly Hx Hy + // and is different for hot spot vs. window center corrections. + // + /* XXXX: + double cx, cy; + if(viewPolicy != HMD_VIEW && enable_crt_glass_correction) { + if (correction_point == CORRECTION_POINT_WINDOW_CENTER) { + correct_crt( ep, Lx, Ly, &cx, &cy); Lx = cx; Ly = cy; + correct_crt( ep, Hx, Hy, &cx, &cy); Hx = cx; Hy = cy; + } + else { // must be hot spot correction + // Not real code yet, for now just do same as above. + correct_crt( ep, Lx, Ly, &cx, &cy); Lx = cx; Ly = cy; + correct_crt( ep, Hx, Hy, &cx, &cy); Hx = cx; Hy = cy; + } + } + */ + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("ep = " + ep); + System.err.println("Lx = " + Lx + ", Hx = " + Hx); + System.err.println("Ly = " + Ly + ", Hy = " + Hy); + System.err.println("F = " + F + ", B = " + B); + } + + // Compute the proper projection equation. Note that we + // do this in two steps: first we generate ImagePlateToCc, + // then we translate this by EcToPlate, resulting in a + // projection from EctoCc. + // + // A more efficient (and more accurate) approach would be to + // modify the equations below to directly project from EcToCc. + + if (viewCache.projectionPolicy == View.PARALLEL_PROJECTION) { + double inv_dx, inv_dy, inv_dz; + inv_dx = 1.0 / (Hx - Lx); + inv_dy = 1.0 / (Hy - Ly); + inv_dz = 1.0 / (F - B); + + ecToCc.mat[0] = 2.0 * inv_dx; + ecToCc.mat[3] = -(Hx + Lx) * inv_dx; + ecToCc.mat[5] = 2.0 * inv_dy; + ecToCc.mat[7] = -(Hy + Ly) * inv_dy; + ecToCc.mat[10] = 2.0 * inv_dz; + ecToCc.mat[11] = -(F + B) * inv_dz; + } + else { + double sxy, rzb, inv_dx, inv_dy; + + inv_dx = 1.0 / (Hx - Lx); + inv_dy = 1.0 / (Hy - Ly); + rzb = 1.0/(ep.z - B); + sxy = ep.z*rzb; + + ecToCc.mat[0] = sxy*2.0*inv_dx; + ecToCc.mat[5] = sxy*2.0*inv_dy; + + ecToCc.mat[2] = rzb*(Hx+Lx - 2.0*ep.x)*inv_dx; + ecToCc.mat[6] = rzb*(Hy+Ly - 2.0*ep.y)*inv_dy; + ecToCc.mat[10] = rzb*(B+F-2*ep.z)/(B-F); + ecToCc.mat[14] = -rzb; + + ecToCc.mat[3] = sxy*(-Hx-Lx)*inv_dx; + ecToCc.mat[7] = sxy*(-Hy-Ly)*inv_dy; + ecToCc.mat[11] = rzb*(B - ep.z - B*(B+F - 2*ep.z)/(B-F)); + ecToCc.mat[15] = sxy; + } + + // Since we set the matrix elements ourselves, we need to set the + // type field. A value of 0 means a non-affine matrix. + ecToCc.setOrthoDirtyBit(); + + // EC to ImagePlate matrix is a simple translation. + tVec1.set(ep.x, ep.y, ep.z); + tMat1.set(tVec1); + ecToCc.mul(tMat1); + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("ecToCc:"); + analyzeProjection(ecToCc, Hx); + } + + if(!doInfinite) { + // View matrix is: + // [plateToEc] [coexistence_to_plate] [vpc_to_coexistence] + // where vpc_to_coexistence includes the viewPlatformScale + + // First compute ViewPlatform to Plate + vpc2Plate.mul(coe2Plate, vpcToCoexistence); + + // ImagePlate to EC matrix is a simple translation. + tVec1.set(-ep.x, -ep.y, -ep.z); + tMat1.set(tVec1); + vpcToEc.mul(tMat1, vpc2Plate); + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + System.err.println("vpcToEc:"); + System.err.println(vpcToEc); + } + } + else { + // Final infinite composite is: + // [coexistence_to_eye] [vpc_to_coexistence (vom)] + // (does vworld_to_coe_scale_factor get used here??? ) + // + // The method is to relocate the coexistence org centered on + // the eye rather than the window center (via coexistence_to_eye). + // Computationaly simpler simplifed equation form may exist. + + // coexistence to eye is a simple translation. +/* + tVec1.set(ep.x, ep.y, ep.z); + tMat1.set(tVec1); + vpcToEc.mul(tMat1, vpcToCoexistence); + // First compute ViewPlatform to Plate + vpcToPlate.mul(coexistenceToPlatevpcToPlate, vpcToCoexistence); +*/ + + // ImagePlate to EC matrix is a simple translation. + tVec1.set(-ep.x, -ep.y, -ep.z); + tMat1.set(tVec1); + tMat1.mul(tMat1, vpc2Plate); + tMat1.getRotation(vpcToEc); // use only rotation component of transform + + } + + } + + /** + * Compute the plane equations for the frustum in ViewPlatform + * coordinates, plus its viewing frustum points. ccToVworld will + * be cached - used by Canavs3D.getInverseVworldProjection(). + */ + private void computeFrustumPlanes(Transform3D ecToCc, + Transform3D vpcToEc, + Vector4d [] frustumPlanes, + Point4d [] frustumPoints, + Transform3D ccToVworld) { + + // Compute the inverse of the Vworld to Cc transform. This + // gives us the Cc to Vworld transform. + tMat2.mul(ecToCc, vpcToEc); + ccToVworld.mul(tMat2, vworldToVpc); + // System.err.println("ccToVworld = " + ccToVworld); + try { + ccToVworld.invert(); + } + catch (SingularMatrixException e) { + ccToVworld.setIdentity(); + // System.err.println("SingularMatrixException encountered when doing invert in computeFrustumPlanes"); + } + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_2)) { + Transform3D t = new Transform3D(); + t.mul(ecToCc, vpcToEc); + t.mul(vworldToVpc); + System.err.println("\nvworldToCc = " + t); + System.err.println("ccToVworld = " + ccToVworld); + t.mul(ccToVworld); + System.err.println("vworldToCc * ccToVworld = " + t); + } + + // Transform the 8 corners of the viewing frustum into Vpc + frustumPoints[0].set(-1.0, -1.0, 1.0, 1.0); // lower-left-front + frustumPoints[1].set(-1.0, 1.0, 1.0, 1.0); // upper-left-front + frustumPoints[2].set( 1.0, 1.0, 1.0, 1.0); // upper-right-front + frustumPoints[3].set( 1.0, -1.0, 1.0, 1.0); // lower-right-front + frustumPoints[4].set(-1.0, -1.0, -1.0, 1.0); // lower-left-back + frustumPoints[5].set(-1.0, 1.0, -1.0, 1.0); // upper-left-back + frustumPoints[6].set( 1.0, 1.0, -1.0, 1.0); // upper-right-back + frustumPoints[7].set( 1.0, -1.0, -1.0, 1.0); // lower-right-back + + ccToVworld.get(tMatrix); + int i; + for (i = 0; i < frustumPoints.length; i++) { + tMatrix.transform(frustumPoints[i]); + double w_inv = 1.0 / frustumPoints[i].w; + frustumPoints[i].x *= w_inv; + frustumPoints[i].y *= w_inv; + frustumPoints[i].z *= w_inv; + } + + // Now compute the 6 plane equations + // left + computePlaneEq(frustumPoints[0], frustumPoints[4], + frustumPoints[5], frustumPoints[1], + frustumPlanes[0]); + + // right + computePlaneEq(frustumPoints[3], frustumPoints[2], + frustumPoints[6], frustumPoints[7], + frustumPlanes[1]); + + // top + computePlaneEq(frustumPoints[1], frustumPoints[5], + frustumPoints[6], frustumPoints[2], + frustumPlanes[2]); + + // bottom + computePlaneEq(frustumPoints[0], frustumPoints[3], + frustumPoints[7], frustumPoints[4], + frustumPlanes[3]); + + // front + computePlaneEq(frustumPoints[0], frustumPoints[1], + frustumPoints[2], frustumPoints[3], + frustumPlanes[4]); + + // back + computePlaneEq(frustumPoints[4], frustumPoints[7], + frustumPoints[6], frustumPoints[5], + frustumPlanes[5]); + + //System.err.println("left plane = " + frustumPlanes[0]); + //System.err.println("right plane = " + frustumPlanes[1]); + //System.err.println("top plane = " + frustumPlanes[2]); + //System.err.println("bottom plane = " + frustumPlanes[3]); + //System.err.println("front plane = " + frustumPlanes[4]); + //System.err.println("back plane = " + frustumPlanes[5]); + } + + private void computePlaneEq(Point4d p1, Point4d p2, Point4d p3, Point4d p4, + Vector4d planeEq) { + tVec1.x = p3.x - p1.x; + tVec1.y = p3.y - p1.y; + tVec1.z = p3.z - p1.z; + + tVec2.x = p2.x - p1.x; + tVec2.y = p2.y - p1.y; + tVec2.z = p2.z - p1.z; + + tVec3.cross(tVec2, tVec1); + tVec3.normalize(); + planeEq.x = tVec3.x; + planeEq.y = tVec3.y; + planeEq.z = tVec3.z; + planeEq.w = -(planeEq.x * p1.x + planeEq.y * p1.y + planeEq.z * p1.z); + } + + // Get methods for returning derived data values. + // Eventually, these get functions will cause some of the parameters + // to be lazily evaluated. + // + // NOTE: in the case of Transform3D, and Tuple objects, a reference + // to the actual derived data is returned. In these cases, the caller + // must ensure that the returned data is not modified. + // + // NOTE: the snapshot and computeDerivedData methods are synchronized. + // Callers of the following methods that can run asynchronously with + // the renderer must call these methods and copy the data from within + // a synchronized block on the canvas view cache object. + + int getCanvasX() { + return canvasX; + } + + int getCanvasY() { + return canvasY; + } + + int getCanvasWidth() { + return canvasWidth; + } + + int getCanvasHeight() { + return canvasHeight; + } + + double getPhysicalWindowWidth() { + return physicalWindowWidth; + } + + double getPhysicalWindowHeight() { + return physicalWindowHeight; + } + + boolean getUseStereo() { + return useStereo; + } + + Transform3D getLeftProjection() { + return leftProjection; + } + + Transform3D getRightProjection() { + return rightProjection; + } + + Transform3D getLeftVpcToEc() { + return leftVpcToEc; + } + + Transform3D getRightVpcToEc() { + return rightVpcToEc; + } + + Transform3D getLeftEcToVpc() { + return leftEcToVpc; + } + + Transform3D getRightEcToVpc() { + return rightEcToVpc; + } + + Transform3D getInfLeftProjection() { + return infLeftProjection; + } + + Transform3D getInfRightProjection() { + return infLeftProjection; + } + + Transform3D getInfLeftVpcToEc() { + return infLeftVpcToEc; + } + + Transform3D getInfRightVpcToEc() { + return infRightVpcToEc; + } + + Transform3D getInfLeftEcToVpc() { + return infLeftEcToVpc; + } + + Transform3D getInfgRightEcToVpc() { + return infRightEcToVpc; + } + + Transform3D getInfVworldToVpc() { + return infVworldToVpc; + } + + Transform3D getLeftCcToVworld() { + return leftCcToVworld; + } + + Transform3D getRightCcToVworld() { + return rightCcToVworld; + } + + Transform3D getImagePlateToVworld() { + // XXXX: Document -- This will return the transform of left plate. + return leftPlateToVworld; + } + + + + Transform3D getLastVworldToImagePlate() { + // XXXX: Document -- This will return the transform of left plate. + return lastVworldToLeftPlate; + + } + + Transform3D getVworldToImagePlate() { + // XXXX: Document -- This will return the transform of left plate. + return vworldToLeftPlate; + } + + Transform3D getVworldToTrackerBase() { + return vworldToTrackerBase; + } + + double getVworldToCoexistenceScale() { + return vworldToCoexistenceScale; + } + + double getInfVworldToCoexistenceScale() { + return infVworldToCoexistenceScale; + } + + Point3d getLeftEyeInImagePlate() { + return leftEyeInImagePlate; + } + + Point3d getRightEyeInImagePlate() { + return rightEyeInImagePlate; + } + + Point3d getCenterEyeInImagePlate() { + return centerEyeInImagePlate; + } + + Transform3D getHeadToVworld() { + return headToVworld; + } + + Transform3D getVpcToVworld() { + return vpcToVworld; + } + + Transform3D getVworldToVpc() { + return vworldToVpc; + } + + + // Transform the specified X point in AWT window-relative coordinates + // to image plate coordinates + double getWindowXInImagePlate(double x) { + double xScreen = x + (double)canvasX; + return metersPerPixelX * xScreen; + } + + // Transform the specified Y point in AWT window-relative coordinates + // to image plate coordinates + double getWindowYInImagePlate(double y) { + double yScreen = y + (double)canvasY; + return metersPerPixelY * ((double)(screenHeight - 1) - yScreen); + } + + Vector4d[] getLeftFrustumPlanesInVworld() { + return leftFrustumPlanes; + } + + Vector4d[] getRightFrustumPlanesInVworld() { + return rightFrustumPlanes; + } + + + void getPixelLocationInImagePlate(double x, double y, double z, + Point3d imagePlatePoint) { + + double screenx = (x + canvasX)*metersPerPixelX; + double screeny = (screenHeight - 1 - canvasY - y)*metersPerPixelY; + + if ((viewCache.projectionPolicy == View.PERSPECTIVE_PROJECTION) && + (centerEyeInImagePlate.z != 0)) { + double zScale = 1.0 - z/centerEyeInImagePlate.z; + imagePlatePoint.x = (screenx - centerEyeInImagePlate.x)*zScale + + centerEyeInImagePlate.x; + imagePlatePoint.y = (screeny - centerEyeInImagePlate.y)*zScale + + centerEyeInImagePlate.y; + } else { + imagePlatePoint.x = screenx; + imagePlatePoint.y = screeny; + } + imagePlatePoint.z = z; + } + + /** + * Projects the specified point from image plate coordinates + * into AWT pixel coordinates. + */ + void getPixelLocationFromImagePlate(Point3d imagePlatePoint, + Point2d pixelLocation) { + + double screenX, screenY; + + if(viewCache.projectionPolicy == View.PERSPECTIVE_PROJECTION) { + // get the vector from centerEyeInImagePlate to imagePlatePoint + tVec1.sub(imagePlatePoint, centerEyeInImagePlate); + + // Scale this vector to make it end at the projection plane. + // Scale is ratio : + // eye->imagePlate Plane dist / eye->imagePlatePt dist + // eye dist to plane is eyePos.z (eye is in +z space) + // image->eye dist is -tVec1.z (image->eye is in -z dir) + //System.err.println("eye dist = " + (centerEyeInImagePlate.z)); + //System.err.println("image dist = " + (-tVec1.z)); + if (tVec1.z != 0) { + double zScale = centerEyeInImagePlate.z / (-tVec1.z); + screenX = centerEyeInImagePlate.x + tVec1.x * zScale; + screenY = centerEyeInImagePlate.y + tVec1.y * zScale; + + } else { + screenX = imagePlatePoint.x; + screenY = imagePlatePoint.y; + } + + } else { + screenX = imagePlatePoint.x; + screenY = imagePlatePoint.y; + } + + //System.err.println("screenX = " + screenX + " screenY = " + screenY); + // Note: screenPt is in image plate coords, at z=0 + + // Transform from image plate coords to screen coords + pixelLocation.x = (screenX / screenViewCache.metersPerPixelX) - canvasX; + pixelLocation.y = screenViewCache.screenHeight - 1 - + (screenY / screenViewCache.metersPerPixelY) - canvasY; + //System.err.println("pixelLocation = " + pixelLocation); + } + + /** + * Constructs and initializes a CanvasViewCache object. + * Note that the canvas, screen, screenCache, view, and + * viewCache parameters are all fixed at construction time + * and must be non-null. + */ + CanvasViewCache(Canvas3D canvas, + ScreenViewCache screenViewCache, + ViewCache viewCache) { + + this.canvas = canvas; + this.screenViewCache = screenViewCache; + this.viewCache = viewCache; + + // Set up the initial plane equations + int i; + for (i = 0; i < leftFrustumPlanes.length; i++) { + leftFrustumPlanes[i] = new Vector4d(); + rightFrustumPlanes[i] = new Vector4d(); + } + + for (i = 0; i < leftFrustumPoints.length; i++) { + leftFrustumPoints[i] = new Point4d(); + rightFrustumPoints[i] = new Point4d(); + } + + // canvas is null in Renderer copyOfCvCache + if (canvas != null) { + leftEyeInImagePlate.set(canvas.leftManualEyeInImagePlate); + rightEyeInImagePlate.set(canvas.rightManualEyeInImagePlate); + centerEyeInImagePlate.add(leftEyeInImagePlate, + rightEyeInImagePlate); + centerEyeInImagePlate.scale(0.5); + } + + if((J3dDebug.devPhase) && (J3dDebug.canvasViewCache >= J3dDebug.LEVEL_1)) + System.err.println("Constructed a CanvasViewCache"); + } + + synchronized void setCanvas(Canvas3D c) { + canvas = c; + } + + synchronized void setScreenViewCache(ScreenViewCache svc) { + screenViewCache = svc; + } + + synchronized void setViewCache(ViewCache vc) { + viewCache = vc; + } +} |