changeset 5594:c49d69f83da6

RT-33066: near/far clip for parallel camera picking. Reviewed-by: Chien
author Pavel Safrata <pavel.safrata@oracle.com>
date Thu, 31 Oct 2013 09:29:19 +0100
parents fb3a8f1fc155
children dba0a7b958a5
files apps/toys/FX8-3DFeatures/src/fx83dfeatures/ParallelNearAndFarClipTest.java modules/graphics/src/main/java/com/sun/javafx/geom/PickRay.java modules/graphics/src/main/java/javafx/scene/ParallelCamera.java modules/graphics/src/test/java/javafx/scene/Mouse3DTest.java
diffstat 4 files changed, 195 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/toys/FX8-3DFeatures/src/fx83dfeatures/ParallelNearAndFarClipTest.java	Thu Oct 31 09:29:19 2013 +0100
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package fx83dfeatures;
+
+import javafx.application.Application;
+import javafx.event.EventHandler;
+import javafx.scene.Group;
+import javafx.scene.Scene;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.paint.Color;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+
+public class ParallelNearAndFarClipTest extends Application {
+
+    private static final double OFFSET_PERCENT = 0.01; // 1 percent tolerance
+    private static final int WIDTH = 500;
+    private static final int HEIGHT = 500;
+    private static final double NEAR = 0.1;
+    private static final double FAR = 100.0;
+
+    private Scene createClipPlanes(Stage stage) {
+        stage.setTitle("Parallel Near and Far Clip Test");
+
+        final double tanOfHalfFOV = Math.tan(Math.toRadians(15));
+        final double halfHeight = HEIGHT / 2;
+        final double focalLength = halfHeight / tanOfHalfFOV;
+        final double eyePositionZ = -1.0 * focalLength;
+        final double nearClipDistance = focalLength * NEAR + eyePositionZ;
+        final double farClipDistance = focalLength * FAR + eyePositionZ;
+        final double nearClipDistanceOffset = Math.abs(nearClipDistance * OFFSET_PERCENT);
+        final double farClipDistanceOffset = Math.abs(farClipDistance * OFFSET_PERCENT);
+
+        System.out.println("In scene coordinate: focalLength = " + focalLength
+                + ", nearClipDistance = " + nearClipDistance
+                + ", nearClipDistanceOffset = " + nearClipDistanceOffset
+                + ", farClipDistance = " + farClipDistance
+                + ", farClipDistanceOffset = " + farClipDistanceOffset);
+
+        Rectangle insideRect = new Rectangle(200, 200, Color.GREEN);
+        insideRect.setLayoutX(150);
+        insideRect.setLayoutY(150);
+        insideRect.setId("Green");
+
+        Rectangle insideNearClip = new Rectangle(50, 50, Color.BLUE);
+        insideNearClip.setLayoutX(225);
+        insideNearClip.setLayoutY(225);
+        insideNearClip.setTranslateZ(nearClipDistance + nearClipDistanceOffset);
+        insideNearClip.setId("Blue");
+
+        Rectangle outsideNearClip = new Rectangle(100, 100, Color.YELLOW);
+        outsideNearClip.setLayoutX(200);
+        outsideNearClip.setLayoutY(200);
+        outsideNearClip.setTranslateZ(nearClipDistance - nearClipDistanceOffset);
+        outsideNearClip.setId("Yellow");
+
+        Rectangle insideFarClip = new Rectangle(300, 300, Color.RED);
+        insideFarClip.setTranslateX(100);
+        insideFarClip.setTranslateY(100);
+        insideFarClip.setTranslateZ(farClipDistance - farClipDistanceOffset);
+        insideFarClip.setId("Red");
+
+        Rectangle outsideFarClip = new Rectangle(400, 400, Color.CYAN);
+        outsideFarClip.setTranslateX(50);
+        outsideFarClip.setTranslateY(50);
+        outsideFarClip.setTranslateZ(farClipDistance + farClipDistanceOffset);
+        outsideFarClip.setId("Cyan");
+
+        Group root = new Group();
+
+        // Render in painter order (far to near)
+        root.getChildren().addAll(outsideFarClip, insideFarClip, insideRect, insideNearClip, outsideNearClip);
+
+        // Intentionally set depth buffer to false to reduce test complexity
+        Scene scene = new Scene(root, WIDTH, HEIGHT, false);
+        scene.setOnMousePressed(new EventHandler<MouseEvent>() {
+            @Override public void handle(MouseEvent event) {
+                System.out.println("Clicked: " + event.getTarget());
+            }
+        });
+
+        return scene;
+    }
+
+    @Override public void start(Stage stage) {
+        Scene scene = createClipPlanes(stage);
+        scene.setFill(Color.GRAY);
+        stage.setScene(scene);
+        stage.sizeToScene();
+        stage.setResizable(false);
+        stage.show();
+        System.out.println("You should expect to see 3 overlapping squares in"
+                + " the order of Blue is on top of Green and Green is on top Red.");
+    }
+
+    public static void main(String[] args) {
+        Application.launch(args);
+    }
+
+}
--- a/modules/graphics/src/main/java/com/sun/javafx/geom/PickRay.java	Wed Oct 30 20:25:59 2013 -0700
+++ b/modules/graphics/src/main/java/com/sun/javafx/geom/PickRay.java	Thu Oct 31 09:29:19 2013 +0100
@@ -107,7 +107,7 @@
         final double distanceZ = (viewHeight / 2.0)
                 / Math.tan(Math.toRadians(15.0));
 
-        pickRay.set(x, y, distanceZ, nearClip, farClip);
+        pickRay.set(x, y, distanceZ, nearClip * distanceZ, farClip * distanceZ);
 
         if (cameraTransform != null) {
             pickRay.transform(cameraTransform);
--- a/modules/graphics/src/main/java/javafx/scene/ParallelCamera.java	Wed Oct 30 20:25:59 2013 -0700
+++ b/modules/graphics/src/main/java/javafx/scene/ParallelCamera.java	Thu Oct 31 09:29:19 2013 +0100
@@ -73,8 +73,7 @@
     final PickRay computePickRay(double x, double y, PickRay pickRay) {
         return PickRay.computeParallelPickRay(x, y, getViewHeight(),
                 getCameraTransform(),
-                //TODO: use actual clips after rendering uses them
-                Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, pickRay);
+                getNearClip(), getFarClip(), pickRay);
     }
 
     @Override
--- a/modules/graphics/src/test/java/javafx/scene/Mouse3DTest.java	Wed Oct 30 20:25:59 2013 -0700
+++ b/modules/graphics/src/test/java/javafx/scene/Mouse3DTest.java	Thu Oct 31 09:29:19 2013 +0100
@@ -97,11 +97,9 @@
         scene(group(), cam, true);
         cam.impl_updatePeer();
         PickRay pickRay = cam.computePickRay(100, 200, null);
-        assertTrue(Double.isInfinite(pickRay.getNearClip()));
-        assertTrue(Double.isInfinite(pickRay.getFarClip()));
-        //TODO: replace the conditions by the following:
-//        assertEquals(100.0, pickRay.getNearClip(), 0.01);
-//        assertEquals(200.0, pickRay.getFarClip(), 0.01);
+
+        assertEquals(-PERSPECTIVE_CAMERA_Z * 100, pickRay.getNearClip(), 0.01);
+        assertEquals(-PERSPECTIVE_CAMERA_Z * 200, pickRay.getFarClip(), 0.01);
     }
 
     @Test
@@ -451,14 +449,14 @@
         Box b = box().handleMove(me);
         b.setCullFace(CullFace.NONE);
         Scene s = scene(group(b), parallel(), true);
-        b.setTranslateZ(-1000);
+        b.setTranslateZ(PERSPECTIVE_CAMERA_Z);
         s.impl_processMouseEvent(g.generateMouseEvent(MouseEvent.MOUSE_MOVED, 10, 40));
 
         MouseEvent e = me.event;
         assertNotNull(e);
-        assertCoordinates(e, 10, 40, 10, 40, -200);
+        assertCoordinates(e, 10, 40, 10, 40, 200);
         assertPickResult(e.getPickResult(),
-                b, point(10, 40, -200), 292.82032, NOFACE, point(0.6, 0.7));
+                b, point(10, 40, 200), 200, NOFACE, point(0.4, 0.7));
     }
 
     @Test
@@ -475,7 +473,7 @@
     }
 
     @Test
-    public void shouldPickBoxByParallelCameraFromBehind() {
+    public void shouldNotPickBoxByParallelCameraFromBehind() {
         MouseEventGenerator g = new MouseEventGenerator();
         Box b = box().handleMove(me);
         b.setCullFace(CullFace.NONE);
@@ -484,10 +482,7 @@
         s.impl_processMouseEvent(g.generateMouseEvent(MouseEvent.MOUSE_MOVED, 10, 40));
 
         MouseEvent e = me.event;
-        assertNotNull(e);
-        assertCoordinates(e, 10, 40, 10, 40, -200);
-        assertPickResult(e.getPickResult(),
-                b, point(10, 40, -200), -1707.17968, NOFACE, point(0.6, 0.7));
+        assertNull(e);
     }
 
     @Test
@@ -758,16 +753,16 @@
     public void shouldPickSphereByParallelCameraFromInside() {
         MouseEventGenerator g = new MouseEventGenerator();
         Sphere sph = sphere().handleMove(me);
-        sph.setTranslateZ(-1000);
+        sph.setTranslateZ(PERSPECTIVE_CAMERA_Z + 80);
         sph.setCullFace(CullFace.NONE);
         Scene s = scene(group(sph), parallel(), true);
         s.impl_processMouseEvent(g.generateMouseEvent(MouseEvent.MOUSE_MOVED, 10, 20));
 
         MouseEvent e = me.event;
         assertNotNull(e);
-        assertCoordinates(e, 10, 20, 10, 20, -97.46794);
+        assertCoordinates(e, 10, 20, 10, 20, 97.46794);
         assertPickResult(e.getPickResult(),
-                sph, point(10, 20, -97.46794), 395.35238, NOFACE, point(0.516273, 0.6));
+                sph, point(10, 20, 97.46794), 177.46794, NOFACE, point(0.98372, 0.6));
     }
 
     @Test
@@ -784,19 +779,16 @@
     }
 
     @Test
-    public void shouldPickSphereByParallelCameraFromBehind() {
+    public void shouldNotPickSphereByParallelCameraFromBehind() {
         MouseEventGenerator g = new MouseEventGenerator();
         Sphere sph = sphere().handleMove(me);
-        sph.setTranslateZ(-1098);
+        sph.setTranslateZ(PERSPECTIVE_CAMERA_Z - 98);
         sph.setCullFace(CullFace.NONE);
         Scene s = scene(group(sph), parallel(), true);
         s.impl_processMouseEvent(g.generateMouseEvent(MouseEvent.MOUSE_MOVED, 10, 20));
 
         MouseEvent e = me.event;
-        assertNotNull(e);
-        assertCoordinates(e, 10, 20, 10, 20, -97.46794);
-        assertPickResult(e.getPickResult(),
-                sph, point(10, 20, -97.46794), 297.35238, NOFACE, point(0.516273, 0.6));
+        assertNull(e);
     }
 
     @Test
@@ -1163,16 +1155,16 @@
     public void shouldPickCylinderByParallelCameraFromInside() {
         MouseEventGenerator g = new MouseEventGenerator();
         Cylinder c = cylinder().handleMove(me);
-        c.setTranslateZ(-1000);
+        c.setTranslateZ(PERSPECTIVE_CAMERA_Z + 150);
         c.setCullFace(CullFace.NONE);
         Scene s = scene(group(c), parallel(), true);
         s.impl_processMouseEvent(g.generateMouseEvent(MouseEvent.MOUSE_MOVED, 10, 20));
 
         MouseEvent e = me.event;
         assertNotNull(e);
-        assertCoordinates(e, 10, 20, 10, 20, -48.98979);
+        assertCoordinates(e, 10, 20, 10, 20, 48.98979);
         assertPickResult(e.getPickResult(),
-                c, point(10, 20, -48.98979), 443.83053, NOFACE, point(0.532048, 0.6));
+                c, point(10, 20, 48.98979), 198.9898, NOFACE, point(0.967952, 0.6));
     }
 
     @Test
@@ -1189,19 +1181,16 @@
     }
 
     @Test
-    public void shouldPickCylinderByParallelCameraFromBehind() {
+    public void shouldNotPickCylinderByParallelCameraFromBehind() {
         MouseEventGenerator g = new MouseEventGenerator();
         Cylinder c = cylinder().handleMove(me);
-        c.setTranslateZ(-1049);
+        c.setTranslateZ(PERSPECTIVE_CAMERA_Z - 49);
         c.setCullFace(CullFace.NONE);
         Scene s = scene(group(c), parallel(), true);
         s.impl_processMouseEvent(g.generateMouseEvent(MouseEvent.MOUSE_MOVED, 10, 20));
 
         MouseEvent e = me.event;
-        assertNotNull(e);
-        assertCoordinates(e, 10, 20, 10, 20, -48.98979);
-        assertPickResult(e.getPickResult(),
-                c, point(10, 20, -48.98979), 394.83053, NOFACE, point(0.532048, 0.6));
+        assertNull(e);
     }
 
     @Test
@@ -1521,7 +1510,7 @@
     }
 
     @Test
-    public void shouldPickMeshXYByParallelCameraFromBehind() {
+    public void shouldNotPickMeshXYByParallelCameraFromBehind() {
         MouseEventGenerator g = new MouseEventGenerator();
         Node m = meshXY().handleMove(me);
         m.setTranslateZ(-3000);
@@ -1529,10 +1518,7 @@
         s.impl_processMouseEvent(g.generateMouseEvent(MouseEvent.MOUSE_MOVED, 60, 20));
 
         MouseEvent e = me.event;
-        assertNotNull(e);
-        assertCoordinates(e, 60, 20, 60, 20, 0);
-        assertPickResult(e.getPickResult(),
-                m, point(60, 20, 0), -1507.17968, 0, point(0.6, 0.2));
+        assertNull(e);
     }
 
     @Test
@@ -1548,18 +1534,15 @@
     }
 
     @Test
-    public void shouldPickMeshGeneralByParallelCameraFromBehind() {
+    public void shouldNotPickMeshGeneralByParallelCameraFromBehind() {
         MouseEventGenerator g = new MouseEventGenerator();
         Node m = meshGeneral().handleMove(me);
-        m.setTranslateZ(-1011);
+        m.setTranslateZ(PERSPECTIVE_CAMERA_Z - 11);
         Scene s = scene(group(m), parallel(), true);
         s.impl_processMouseEvent(g.generateMouseEvent(MouseEvent.MOUSE_MOVED, 10, 20));
 
         MouseEvent e = me.event;
-        assertNotNull(e);
-        assertCoordinates(e, 10, 20, 10, 20, 10);
-        assertPickResult(e.getPickResult(),
-                m, point(10, 20, 10), 491.82032, 0, point(0.1, 0.2));
+        assertNull(e);
     }
 
     @Test
@@ -2131,6 +2114,52 @@
         assertNull(me.event);
     }
 
+    @Test
+    public void shouldIgnoreRectangleCloserThanNearClipWithParallelCamera() {
+
+        Camera cam = new ParallelCamera();
+        cam.setNearClip(0.2);
+        double nearClip = PERSPECTIVE_CAMERA_Z * 0.8;
+
+        Rectangle r1 = rect().handleMove(me);
+        r1.setTranslateZ(nearClip - 0.1);
+
+        Rectangle r2 = rect().handleMove(sme);
+        r2.setTranslateZ(nearClip + 0.1);
+
+        Scene s = scene(group(r1, r2), cam, false);
+
+        s.impl_processMouseEvent(MouseEventGenerator.generateMouseEvent(
+                MouseEvent.MOUSE_MOVED, 20, 20));
+
+        assertNull(me.event);
+        assertNotNull(sme.event);
+    }
+
+    @Test
+    public void shouldIgnoreRectangleFartherThanFarClipWithParallelCamera() {
+
+        Camera cam = new ParallelCamera();
+        cam.setNearClip(0.1);
+        cam.setFarClip(0.3);
+        double far = PERSPECTIVE_CAMERA_Z * 0.7;
+
+        Rectangle r = rect().handleMove(me);
+        r.setTranslateZ(far - 0.1);
+
+        Scene s = scene(group(r), cam, true);
+
+        s.impl_processMouseEvent(MouseEventGenerator.generateMouseEvent(
+                MouseEvent.MOUSE_MOVED, 20, 20));
+        assertNotNull(me.event);
+
+        me.clear();
+        r.setTranslateZ(far + 0.1);
+        s.impl_processMouseEvent(MouseEventGenerator.generateMouseEvent(
+                MouseEvent.MOUSE_MOVED, 20, 20));
+        assertNull(me.event);
+    }
+
     /*****************  Scenegraph-generated events ********************/
 
     @Test