changeset 2547:0f328860af65

RT-17401: 3D geometry support -- initial commit, only d3d is implemented Contributed-by: jada
author kcr
date Thu, 07 Feb 2013 17:13:12 -0800
parents 4446f7960af3
children bdecfce65a2f
files javafx-geom/src/com/sun/javafx/geom/CameraImpl.java javafx-geom/src/com/sun/javafx/geom/ParallelCameraImpl.java javafx-geom/src/com/sun/javafx/geom/PerspectiveCameraImpl.java javafx-geom/src/com/sun/javafx/geom/PickRay.java javafx-geom/src/com/sun/javafx/geom/transform/GeneralTransform3D.java javafx-sg-common/src/com/sun/javafx/sg/PGAmbientLight.java javafx-sg-common/src/com/sun/javafx/sg/PGBox.java javafx-sg-common/src/com/sun/javafx/sg/PGCamera.java javafx-sg-common/src/com/sun/javafx/sg/PGCylinder.java javafx-sg-common/src/com/sun/javafx/sg/PGLightBase.java javafx-sg-common/src/com/sun/javafx/sg/PGMeshView.java javafx-sg-common/src/com/sun/javafx/sg/PGParallelCamera.java javafx-sg-common/src/com/sun/javafx/sg/PGPerspectiveCamera.java javafx-sg-common/src/com/sun/javafx/sg/PGPhongMaterial.java javafx-sg-common/src/com/sun/javafx/sg/PGPointLight.java javafx-sg-common/src/com/sun/javafx/sg/PGShape3D.java javafx-sg-common/src/com/sun/javafx/sg/PGSphere.java javafx-sg-common/src/com/sun/javafx/sg/PGTriangleMesh.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGAmbientLight.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGBox.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGCamera.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGCylinder.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGGroup.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGLightBase.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGMeshView.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGNode.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGParallelCamera.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPerspectiveCamera.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPhongMaterial.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPointLight.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGRegion.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGShape3D.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGSphere.java javafx-sg-prism/src/com/sun/javafx/sg/prism/NGTriangleMesh.java javafx-sg-prism/test/com/sun/javafx/sg/prism/TestGraphics.java javafx-ui-common/src/com/sun/javafx/scene/DirtyBits.java javafx-ui-common/src/com/sun/javafx/scene/EnteredExitedHandler.java javafx-ui-common/src/com/sun/javafx/scene/input/InputEventUtils.java javafx-ui-common/src/com/sun/javafx/scene/input/PickResultChooser.java javafx-ui-common/src/com/sun/javafx/tk/DummyToolkit.java javafx-ui-common/src/com/sun/javafx/tk/TKScene.java javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java javafx-ui-common/src/javafx/scene/AmbientLight.java javafx-ui-common/src/javafx/scene/Camera.java javafx-ui-common/src/javafx/scene/LightBase.java javafx-ui-common/src/javafx/scene/Node.java javafx-ui-common/src/javafx/scene/ParallelCamera.java javafx-ui-common/src/javafx/scene/Parent.java javafx-ui-common/src/javafx/scene/PerspectiveCamera.java javafx-ui-common/src/javafx/scene/PointLight.java javafx-ui-common/src/javafx/scene/Scene.java javafx-ui-common/src/javafx/scene/SubScene.java javafx-ui-common/src/javafx/scene/input/ContextMenuEvent.java javafx-ui-common/src/javafx/scene/input/DragEvent.java javafx-ui-common/src/javafx/scene/input/GestureEvent.java javafx-ui-common/src/javafx/scene/input/KeyEvent.java javafx-ui-common/src/javafx/scene/input/MouseDragEvent.java javafx-ui-common/src/javafx/scene/input/MouseEvent.java javafx-ui-common/src/javafx/scene/input/PickResult.java javafx-ui-common/src/javafx/scene/input/RotateEvent.java javafx-ui-common/src/javafx/scene/input/ScrollEvent.java javafx-ui-common/src/javafx/scene/input/SwipeEvent.java javafx-ui-common/src/javafx/scene/input/TouchPoint.java javafx-ui-common/src/javafx/scene/input/ZoomEvent.java javafx-ui-common/src/javafx/scene/layout/Region.java javafx-ui-common/src/javafx/scene/paint/Material.java javafx-ui-common/src/javafx/scene/paint/PhongMaterial.java javafx-ui-common/src/javafx/scene/shape/Box.java javafx-ui-common/src/javafx/scene/shape/CullFace.java javafx-ui-common/src/javafx/scene/shape/Cylinder.java javafx-ui-common/src/javafx/scene/shape/DrawMode.java javafx-ui-common/src/javafx/scene/shape/Mesh.java javafx-ui-common/src/javafx/scene/shape/MeshView.java javafx-ui-common/src/javafx/scene/shape/PredefinedMeshManager.java javafx-ui-common/src/javafx/scene/shape/Shape3D.java javafx-ui-common/src/javafx/scene/shape/Sphere.java javafx-ui-common/src/javafx/scene/shape/TriangleMesh.java javafx-ui-common/test/unit/javafx/scene/MouseTest.java javafx-ui-common/test/unit/javafx/scene/MouseTest3D.java javafx-ui-common/test/unit/javafx/scene/NodeTest.java javafx-ui-common/test/unit/javafx/scene/Scenegraph_eventHandlers_Test.java javafx-ui-common/test/unit/javafx/scene/input/ContextMenuEventTest.java javafx-ui-common/test/unit/javafx/scene/input/DragAndDropTest.java javafx-ui-common/test/unit/javafx/scene/input/MouseDragEventTest.java javafx-ui-common/test/unit/javafx/scene/input/RotateEventTest.java javafx-ui-common/test/unit/javafx/scene/input/ScrollEventTest.java javafx-ui-common/test/unit/javafx/scene/input/SwipeEventTest.java javafx-ui-common/test/unit/javafx/scene/input/TestNode.java javafx-ui-common/test/unit/javafx/scene/input/TouchEventTest.java javafx-ui-common/test/unit/javafx/scene/input/ZoomEventTest.java javafx-ui-controls/test/com/sun/javafx/scene/control/skin/ScrollPaneSkinTest.java javafx-ui-controls/test/com/sun/javafx/scene/control/skin/VirtualFlowTest.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubBox.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubCylinder.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubMeshView.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubScene.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubShape3D.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubSphere.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubToolkit.java test-stub-toolkit/src/com/sun/javafx/pgstub/StubTriangleMesh.java
diffstat 100 files changed, 10039 insertions(+), 492 deletions(-) [+]
line wrap: on
line diff
--- a/javafx-geom/src/com/sun/javafx/geom/CameraImpl.java	Thu Feb 07 16:35:06 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2010, 2012, 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 com.sun.javafx.geom;
-
-public interface CameraImpl {
-
-}
--- a/javafx-geom/src/com/sun/javafx/geom/ParallelCameraImpl.java	Thu Feb 07 16:35:06 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2010, 2012, 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 com.sun.javafx.geom;
-
-public interface ParallelCameraImpl extends CameraImpl {
-
-}
--- a/javafx-geom/src/com/sun/javafx/geom/PerspectiveCameraImpl.java	Thu Feb 07 16:35:06 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2010, 2012, 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 com.sun.javafx.geom;
-
-public interface PerspectiveCameraImpl extends CameraImpl {
-
-    public void setFieldOfView(float fieldOfView);
-}
--- a/javafx-geom/src/com/sun/javafx/geom/PickRay.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-geom/src/com/sun/javafx/geom/PickRay.java	Thu Feb 07 17:13:12 2013 -0800
@@ -450,7 +450,6 @@
         t.transform(origin, origin);
         t.transform(endPt, endPt);
         direction.sub(endPt, origin);
-        direction.normalize();
     }
 
     public PickRay project(BaseTransform inversetx,
--- a/javafx-geom/src/com/sun/javafx/geom/transform/GeneralTransform3D.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-geom/src/com/sun/javafx/geom/transform/GeneralTransform3D.java	Thu Feb 07 17:13:12 2013 -0800
@@ -251,11 +251,12 @@
      * to Clipping Coordinates (CC).
      * Note that the field of view is specified in radians.
      *
-     * @param fovy specifies the field of view in the y direction, in radians
+     * @param verticalFOV specifies whether the fov is vertical (Y direction).
+     * 
+     * @param fov specifies the field of view in radians
      *
-     * @param aspect specifies the aspect ratio and thus the field of
-     * view in the x direction. The aspect ratio is the ratio of x to y,
-     * or width to height.
+     * @param aspect specifies the aspect ratio. The aspect ratio is the ratio
+     * of width to height.
      *
      * @param zNear the distance to the frustum's near clipping plane.
      * This value must be positive, (the value -zNear is the location of the
@@ -265,19 +266,20 @@
      *
      * @return this transform
      */
-    public GeneralTransform3D perspective(double fovy, double aspect, double zNear, double zFar) {
+    public GeneralTransform3D perspective(boolean verticalFOV, 
+            double fov, double aspect, double zNear, double zFar) {
         double sine;
         double cotangent;
         double deltaZ;
-        double half_fov = fovy * 0.5;
+        double half_fov = fov * 0.5;
 
         deltaZ = zFar - zNear;
         sine = Math.sin(half_fov);
 
         cotangent = Math.cos(half_fov) / sine;
 
-        mat[0] = cotangent / aspect;
-        mat[5] = cotangent;
+        mat[0] = verticalFOV ? cotangent / aspect : cotangent;
+        mat[5] = verticalFOV ? cotangent : cotangent * aspect;
         mat[10] = -(zFar + zNear) / deltaZ;
         mat[11] = -2.0 * zNear * zFar / deltaZ;
         mat[14] = -1.0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGAmbientLight.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,32 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGAmbientLight extends PGLightBase {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGBox.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,32 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGBox extends PGShape3D {
+    public void updateMesh(PGTriangleMesh mesh);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGCamera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,36 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+import com.sun.javafx.geom.transform.Affine3D;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGCamera extends PGNode {
+    public void setNearClip(float nearClip);
+    public void setFarClip(float farClip);
+    public void setWorldTransform(Affine3D localToWorldTx);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGCylinder.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,32 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGCylinder extends PGShape3D {
+    public void updateMesh(PGTriangleMesh mesh);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGLightBase.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,36 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+import com.sun.javafx.geom.transform.Affine3D;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGLightBase extends PGNode {
+    public void setColor(Object value);
+    public void setLightOn(boolean value);
+    public void setWorldTransform(Affine3D localToSceneTx);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGMeshView.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,33 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGMeshView extends PGShape3D {
+
+    public void setMesh(PGTriangleMesh mesh);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGParallelCamera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,32 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGParallelCamera extends PGCamera {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGPerspectiveCamera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,35 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGPerspectiveCamera extends PGCamera {
+
+    public void setFieldOfView(float floatValue);
+    public void setVerticalFieldOfView(boolean value);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGPhongMaterial.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,38 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGPhongMaterial {
+    public void setDiffuseColor(Object diffuseColor);
+    public void setSpecularColor(Object specularColor);
+    public void setSpecularPower(float specularPower);
+    public void setDiffuseMap(Object diffuseMap);
+    public void setSpecularMap(Object specularMap);
+    public void setBumpMap(Object bumpMap);
+    public void setSelfIlluminationMap(Object selfIlluminationMap);    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGPointLight.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,32 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGPointLight extends PGLightBase {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGShape3D.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,35 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGShape3D extends PGNode {
+
+    public void setMaterial(PGPhongMaterial material);
+    public void setDrawMode(Object drawMode);
+    public void setCullFace(Object cullFace);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGSphere.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,32 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGSphere extends PGShape3D {
+    public void updateMesh(PGTriangleMesh mesh);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-common/src/com/sun/javafx/sg/PGTriangleMesh.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,39 @@
+/*
+ * 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 com.sun.javafx.sg;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public interface PGTriangleMesh {
+    public void setPoints(float[] points);
+    public void setPoints(int index, float[] points, int start, int length);
+    public void setTexCoords(float[] texCoords);
+    public void setTexCoords(int index, float[] texCoords, int start, int length);
+    public void setFaces(int[] faces);
+    public void setFaces(int index, int[] faces, int start, int length);
+    public void setFaceSmoothingGroups(int[] faceSmoothingGroups);
+    public void setFaceSmoothingGroups(int index, int[] faceSmoothingGroups, int start, int length);    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGAmbientLight.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,37 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGAmbientLight;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGAmbientLight extends NGLightBase implements PGAmbientLight {
+
+    public NGAmbientLight() {
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGBox.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,51 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGBox;
+import com.sun.javafx.sg.PGTriangleMesh;
+import com.sun.prism.MeshView;
+import com.sun.prism.ResourceFactory;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGBox extends NGShape3D implements PGBox {
+
+    private PGTriangleMesh mesh;
+
+    protected MeshView getNativeObject(ResourceFactory rf) {
+        if (getMesh() == null) {
+            setMesh(mesh);
+        }
+        return super.getNativeObject(rf);
+    }
+
+    @Override
+    public void updateMesh(PGTriangleMesh mesh) {
+        this.mesh = mesh;
+        invalidate();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGCamera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,80 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.geom.transform.Affine3D;
+import com.sun.javafx.sg.PGCamera;
+import com.sun.prism.Graphics;
+import com.sun.prism.camera.PrismCameraImpl;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+abstract public class NGCamera extends NGNode implements PGCamera {
+
+    private PrismCameraImpl cameraImpl;
+
+    @Override NodeType getNodeType() {
+        return NodeType.NODE_NONE;
+    }
+
+    @Override protected void doRender(Graphics g) {
+    }
+
+    @Override protected void renderContent(Graphics g) {
+    }
+
+    @Override protected boolean hasOverlappingContents() {
+        return false;
+    }
+
+    public void setNearClip(float nearClip) {
+        getCameraImpl().setNearClip(nearClip);
+    }
+
+    public void setFarClip(float farClip) {
+        getCameraImpl().setFarClip(farClip);
+    }
+
+    @Override
+    public void setWorldTransform(Affine3D localToWorldTx) {
+        getCameraImpl().setWorldTransform(localToWorldTx);
+    }
+    
+    
+    public void setCameraImpl(PrismCameraImpl cameraImpl) {
+        this.cameraImpl = cameraImpl;
+    }
+    
+    public PrismCameraImpl getCameraImpl() {
+        return cameraImpl;
+    }
+
+    @Override
+    public void release() {
+        // TODO: 3D - Need to release native resources
+//        System.err.println("NGCamera: Need to release native resources");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGCylinder.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,51 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGCylinder;
+import com.sun.javafx.sg.PGTriangleMesh;
+import com.sun.prism.MeshView;
+import com.sun.prism.ResourceFactory;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGCylinder extends NGShape3D implements PGCylinder {
+
+    private PGTriangleMesh mesh;
+
+    protected MeshView getNativeObject(ResourceFactory rf) {
+        if (getMesh() == null) {
+            setMesh(mesh);
+        }
+        return super.getNativeObject(rf);
+    }
+
+    @Override
+    public void updateMesh(PGTriangleMesh mesh) {
+        this.mesh = mesh;
+        invalidate();
+    }
+}
--- a/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGGroup.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGGroup.java	Thu Feb 07 17:13:12 2013 -0800
@@ -471,4 +471,9 @@
                              mzx, mzy, mzz, mzt);
         }
     }
+
+    @Override
+    NodeType getNodeType() {
+        return NodeType.NODE_NONE;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGLightBase.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,97 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.geom.transform.Affine3D;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.sg.PGLightBase;
+import com.sun.javafx.tk.Toolkit;
+import com.sun.prism.Graphics;
+import com.sun.prism.paint.Color;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGLightBase extends NGNode implements PGLightBase {
+    // TODO: 3D - Should the default be White or null?
+    private Color color = Color.WHITE;
+    private boolean lightOn = true;
+    private Affine3D worldTransform;
+    
+    protected NGLightBase() {     
+    }
+
+    @Override
+    NodeType getNodeType() {
+        return NodeType.NODE_NONE;
+    }
+    
+    @Override
+    public void setTransformMatrix(BaseTransform tx) {
+        super.setTransformMatrix(tx);
+        Toolkit.getToolkit().setLightsDirty(true);
+    }
+
+    @Override
+    protected void doRender(Graphics g) {}
+    
+    @Override protected void renderContent(Graphics g) {}
+
+    @Override protected boolean hasOverlappingContents() {
+        return false;
+    }
+    public Color getColor() {
+        return color;
+    }
+
+    public void setColor(Object value) {
+        this.color = (Color) value;
+        //TODO: 3D - Need to evaluate this pattern
+        Toolkit.getToolkit().setLightsDirty(true);
+        visualsChanged();
+    }
+
+    public boolean isLightOn() {
+        return lightOn;
+    }
+
+    public void setLightOn(boolean value) {
+        lightOn = value;
+    }
+
+    public Affine3D getWorldTransform() {
+        return worldTransform;
+    }
+
+    public void setWorldTransform(Affine3D localToSceneTx) {
+        this.worldTransform = localToSceneTx;        
+    }
+
+    @Override
+    public void release() {
+        // TODO: 3D - Need to release native resources
+//        System.err.println("NGLightBase: Need to release native resources");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGMeshView.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,40 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGMeshView;
+import com.sun.javafx.sg.PGTriangleMesh;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGMeshView extends NGShape3D implements PGMeshView {
+
+    public void setMesh(PGTriangleMesh pgtm) {
+        // NOTE: Implementation has been promopted to NGShape3D so as to
+        // share NGTriangleMesh between predefined 3D shapes
+        super.setMesh(pgtm);
+    }
+}
--- a/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGNode.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGNode.java	Thu Feb 07 17:13:12 2013 -0800
@@ -335,8 +335,32 @@
      *                                                                         *
      **************************************************************************/
 
+    // TODO: 3D - Reconsider the NodeType interface.
+    // At the very least it needs to be documented.
+    enum NodeType {
+        NODE_NONE, NODE_2D, NODE_3D
+    }
+
+    // TODO: 3D - Reconsider the NodeType interface.
+    // At the very least it needs to be documented.
+    NodeType getNodeType() {
+        return NodeType.NODE_2D;
+    }
+    
+    protected final boolean configureRenderer(Graphics g) {
+        switch (getNodeType()) {
+            case NODE_2D: return g.beginRender2D();
+            case NODE_3D: return g.beginRender3D();
+        }
+        return true;
+    }
+    
     @Override
     protected void doRender(Graphics g) {
+        // check if the renderer configured properly
+        if (!configureRenderer(g))
+            return;
+        
         boolean preCullingTurnedOff = false;
         if (PrismSettings.dirtyOptsEnabled) {
             if (g.hasPreCullingBits()) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGParallelCamera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,41 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGParallelCamera;
+import com.sun.prism.camera.PrismParallelCameraImpl;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGParallelCamera extends NGCamera implements PGParallelCamera {
+
+    public NGParallelCamera() {
+        //TODO: 3D - Need to merge the logic in PrismParallelCameraImpl to NGParallelCamera
+        //      We shouldn't need PrismParallelCameraImpl class in the future.
+        setCameraImpl(PrismParallelCameraImpl.getInstance());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPerspectiveCamera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,49 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGPerspectiveCamera;
+import com.sun.prism.camera.PrismPerspectiveCameraImpl;
+
+/**
+ * TODO: 3D - Need documentation
+ * (RT-26534) Implement 3D Camera support for FX 8
+ */
+public class NGPerspectiveCamera extends NGCamera implements PGPerspectiveCamera {
+
+    public NGPerspectiveCamera(boolean fixedEyePosition) {
+        setCameraImpl(new PrismPerspectiveCameraImpl(fixedEyePosition));       
+    }
+
+    public void setFieldOfView(float fieldOfView) {
+        // System.err.println("NGPerspectiveCamera.setFieldOfView() fieldOfView = " + fieldOfView);
+        ((PrismPerspectiveCameraImpl) getCameraImpl()).setFieldOfView(fieldOfView);
+    }
+
+    public void setVerticalFieldOfView(boolean verticalFieldOfView) {
+        ((PrismPerspectiveCameraImpl) getCameraImpl()).setVerticalFieldOfView(verticalFieldOfView);
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPhongMaterial.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,157 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGPhongMaterial;
+import com.sun.prism.Image;
+import com.sun.prism.Material;
+import com.sun.prism.PhongMaterial;
+import com.sun.prism.ResourceFactory;
+import com.sun.prism.Texture;
+import com.sun.prism.paint.Color;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGPhongMaterial implements PGPhongMaterial {
+// Default values in 3D prototype    
+//    private Color diffuseColor = Color.RED;
+//    private Color specularColor = Color.WHITE;
+//    private float specularPower = 0.5f;
+    
+    private Color diffuseColor;
+    private Color specularColor;
+    private float specularPower;
+    private Image diffuseMap;
+    private Image specularMap;
+    private Image bumpMap;
+    private Image selfIlluminationMap;
+    private PhongMaterial nativeMaterial;
+    private boolean diffuseColorDirty = true;
+    private boolean specularColorDirty = true;
+    private boolean specularPowerDirty = true;
+    private boolean diffuseMapDirty = true;
+    private boolean specularMapDirty = true;
+    private boolean bumpMapDirty = true;
+    private boolean selfIlluminationMapDirty = true;
+
+    Material getNativeMaterial(ResourceFactory f) {
+        if (nativeMaterial == null) {
+            nativeMaterial = f.get3DFactory().createPhongMaterial();
+        }
+        updateNativeObject(f);
+        return nativeMaterial;
+    }
+
+    public void setNativeMaterial(PhongMaterial nativeMaterial) {
+        this.nativeMaterial = nativeMaterial;
+    }
+
+    static Texture getCachedTexture(ResourceFactory f, Image img) {
+        return img != null ? f.getCachedTexture(img, Texture.WrapMode.CLAMP_TO_EDGE) : null;
+    }
+    
+    void updateNativeObject(ResourceFactory f) {
+        
+        if (diffuseColorDirty) {
+            if (diffuseColor != null) {
+                nativeMaterial.setSolidColor(
+                        diffuseColor.getRed(), diffuseColor.getGreen(),
+                        diffuseColor.getBlue(), diffuseColor.getAlpha());
+            } else {
+                nativeMaterial.setSolidColor(0, 0, 0, 0);
+            }
+            diffuseColorDirty = false;
+        }
+
+        if (diffuseMapDirty) {
+            nativeMaterial.setMap(PhongMaterial.mapDiffuse, getCachedTexture(f, diffuseMap));
+            diffuseMapDirty = false;
+        }
+        if (bumpMapDirty) {
+            nativeMaterial.setMap(PhongMaterial.mapBump, getCachedTexture(f, bumpMap));
+            bumpMapDirty = false;
+        }
+        if (selfIlluminationMapDirty) {
+            nativeMaterial.setMap(PhongMaterial.mapSelfIlum, getCachedTexture(f, selfIlluminationMap));
+            selfIlluminationMapDirty = false;
+        }
+        if (specularMapDirty || specularColorDirty || specularPowerDirty) {
+            Image specular = specularMap;
+            if (specular == null && specularColor != null) {
+                int ia = (int) (255.0 * specularPower);
+                int ir = (int) (255.0 * specularColor.getRed());
+                int ig = (int) (255.0 * specularColor.getGreen());
+                int ib = (int) (255.0 * specularColor.getBlue());
+                int pixel = (ia << 24) | (ir << 16) | (ig << 8) | (ib << 0);
+
+                if (ir != 0 || ig != 0 || ib != 0) {
+                    specular = Image.fromIntArgbPreData(
+                            new int[]{pixel, pixel, pixel, pixel}, 2, 2);
+                }
+            }
+            nativeMaterial.setMap(PhongMaterial.mapSpecular, getCachedTexture(f, specular));
+            specularColorDirty = false;
+            specularPowerDirty = false;
+            specularMapDirty = false;
+        }
+    }
+
+    public void setDiffuseColor(Object diffuseColor) {
+        this.diffuseColor = (Color)diffuseColor;
+        diffuseColorDirty = true;
+    }
+
+    public void setSpecularColor(Object specularColor) {
+        this.specularColor = (Color)specularColor;
+        specularColorDirty = true;
+    }
+
+    public void setSpecularPower(float specularPower) {
+        this.specularPower = specularPower;
+        specularPowerDirty = true;
+    }
+
+    public void setDiffuseMap(Object diffuseMap) {
+        this.diffuseMap = (Image)diffuseMap;
+        diffuseMapDirty = true;
+    }
+
+    public void setSpecularMap(Object specularMap) {
+        this.specularMap = (Image)specularMap;
+        specularMapDirty = true;
+    }
+
+    public void setBumpMap(Object bumpMap) {
+        this.bumpMap = (Image)bumpMap;
+        bumpMapDirty = true;
+    }
+
+    public void setSelfIlluminationMap(Object selfIlluminationMap) {
+        this.selfIlluminationMap = (Image)selfIlluminationMap;
+        selfIlluminationMapDirty = true;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGPointLight.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,37 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGPointLight;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGPointLight extends NGLightBase implements PGPointLight {
+
+    public NGPointLight() {
+    }
+
+}
--- a/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGRegion.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGRegion.java	Thu Feb 07 17:13:12 2013 -0800
@@ -1774,4 +1774,9 @@
         }
         return ratio;
     }
+
+    @Override
+    NodeType getNodeType() {
+        return NodeType.NODE_2D;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGShape3D.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,182 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.geom.transform.Affine3D;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.sg.PGPhongMaterial;
+import com.sun.javafx.sg.PGShape3D;
+import com.sun.javafx.sg.PGTriangleMesh;
+import com.sun.javafx.tk.Toolkit;
+import com.sun.prism.Graphics;
+import com.sun.prism.Material;
+import com.sun.prism.MeshFactory;
+import com.sun.prism.MeshView;
+import com.sun.prism.ResourceFactory;
+import javafx.scene.shape.CullFace;
+import javafx.scene.shape.DrawMode;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public abstract class NGShape3D extends NGNode implements PGShape3D {
+    protected NGPhongMaterial material;
+    protected DrawMode drawMode;
+    protected CullFace cullFace;
+    protected boolean materialDirty = false;
+    protected boolean drawModeDirty = false;
+    protected boolean cullFaceDirty = false;
+    private NGTriangleMesh triangleMesh;
+    private MeshView nativeObject;
+
+    public void setMaterial(PGPhongMaterial material) {
+        this.material = (NGPhongMaterial) material;
+        materialDirty = true;
+        visualsChanged();
+    }
+    public void setDrawMode(Object drawMode) {
+        this.drawMode = (DrawMode) drawMode;
+        drawModeDirty = true;
+        visualsChanged();
+    }
+
+    public void setCullFace(Object cullFace) {
+        this.cullFace = (CullFace) cullFace;
+        cullFaceDirty = true;
+        visualsChanged();
+    }
+
+    protected void invalidate() {
+        setMesh(null);
+    }
+    
+    protected MeshView getNativeObject(ResourceFactory rf) {
+
+        if (nativeObject == null && triangleMesh != null) {
+            MeshFactory meshFactory = rf.get3DFactory();
+            nativeObject = meshFactory.createMeshView(triangleMesh.getNativeObject(meshFactory));
+            setMaterial(material);
+            setDrawMode(drawMode);
+            setCullFace(cullFace);
+        }
+
+        if (nativeObject == null) {
+            return null;
+        }
+
+        triangleMesh.updateNativeIfNeeded();
+
+        Material mtl =  material.getNativeMaterial(rf);
+        if (materialDirty) {
+            nativeObject.setMaterial(mtl);
+            materialDirty = false;
+        }
+
+        if (cullFaceDirty) {
+            nativeObject.setCullingMode(cullFace.ordinal());
+            cullFaceDirty = false;
+        }
+
+        if (drawModeDirty) {
+            // This need to change once the wire up is working
+            nativeObject.setWireframe(drawMode == DrawMode.LINE);
+            drawModeDirty = false;
+        }
+
+        Toolkit tk = Toolkit.getToolkit();
+        for(int i=0; i < tk.getLightsInScene().size(); i++) {
+            // Need to make changes to support AmbientLight too.
+            // Will do this once we get the first rendering up.
+            NGPointLight light = (NGPointLight)tk.getLightsInScene().get(i);
+            Affine3D lightWT = light.getWorldTransform();
+            nativeObject.setLight(i,
+                    (float)lightWT.getMxt(),
+                    (float)lightWT.getMyt(),
+                    (float)lightWT.getMzt(),
+                    light.getColor().getRed(),
+                    light.getColor().getGreen(),
+                    light.getColor().getBlue(), 1.0f);
+        }
+
+        return nativeObject;
+    }
+
+    protected void setMesh(PGTriangleMesh triangleMesh) {
+        this.triangleMesh = (NGTriangleMesh)triangleMesh;
+        nativeObject = null;
+        visualsChanged();
+    }
+
+    protected NGTriangleMesh getMesh() {
+        return triangleMesh;
+    }
+
+    // for renderContent temp
+    private static final float tempTx[] = new float[12];
+
+    // TODO: 3D HACK - Remove this temporary method. 
+    // DELETE THIS ONCE D3D native clean up is done.
+    // This copy with tranpose operation should be done in the native d3d pipe.    
+    private void setMatrixAs3x4TempTx(BaseTransform bTx) {
+        assert tempTx.length == 12;
+        tempTx[0] = (float)bTx.getMxx(); tempTx[3] = (float)bTx.getMxy(); tempTx[6] = (float)bTx.getMxz();
+        tempTx[1] = (float)bTx.getMyx(); tempTx[4] = (float)bTx.getMyy(); tempTx[7] = (float)bTx.getMyz();
+        tempTx[2] = (float)bTx.getMzx(); tempTx[5] = (float)bTx.getMzy(); tempTx[8] = (float)bTx.getMzz();
+        tempTx[9] = (float)bTx.getMxt(); tempTx[10] = (float)bTx.getMyt(); tempTx[11] = (float)bTx.getMzt();
+    }
+    
+    protected void renderContent(Graphics g) {
+
+        if (material == null) {
+            return;
+        }
+
+        MeshView mView = getNativeObject(g.getResourceFactory());
+
+        if (mView != null) {
+            // TODO: 3D - Replace mView.setPos with setWorldTransform
+            // mView.setWorldTransform(g.getTransformNoClone());
+            setMatrixAs3x4TempTx(g.getTransformNoClone());
+            mView.setPos(tempTx);
+            g.draw3DObject(mView);
+        }
+    }
+
+    @Override
+    NodeType getNodeType() {
+        return NodeType.NODE_3D;
+    }
+
+    @Override
+    protected boolean hasOverlappingContents() {
+        return false;
+    }
+
+    @Override
+    public void release() {
+        // TODO: 3D - Need to release native resources
+//        System.err.println("NGShape3D: Need to release native resources");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGSphere.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,51 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGSphere;
+import com.sun.javafx.sg.PGTriangleMesh;
+import com.sun.prism.MeshView;
+import com.sun.prism.ResourceFactory;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGSphere extends NGShape3D implements PGSphere {
+
+    private PGTriangleMesh mesh;
+    
+    protected MeshView getNativeObject(ResourceFactory rf) {
+        if (getMesh() == null) {
+            setMesh(mesh);
+        }
+        return super.getNativeObject(rf);
+    }
+
+    @Override
+    public void updateMesh(PGTriangleMesh mesh) {
+        this.mesh = mesh;
+        invalidate();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-sg-prism/src/com/sun/javafx/sg/prism/NGTriangleMesh.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,202 @@
+/*
+ * 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 com.sun.javafx.sg.prism;
+
+import com.sun.javafx.sg.PGTriangleMesh;
+import com.sun.prism.Mesh;
+import com.sun.prism.MeshFactory;
+import javafx.scene.shape.TriangleMesh;
+
+/**
+ * TODO: 3D - Need documentation
+ */
+public class NGTriangleMesh implements PGTriangleMesh {
+    private boolean meshDirty = true;
+    private Mesh nativeObject;
+
+    private float[] points;     // x,y,z interleaved
+    private float[] texCoords;  // u,v interleaved
+    private int[] faces;        // v1,v2,v3 interleaved (where v = {point, texCoord})
+    private int[] faceSmoothingGroups; // face smoothing group 
+    
+    protected Mesh getNativeObject(MeshFactory meshFactory) {
+        if (nativeObject == null) {
+            nativeObject = meshFactory.createMesh();
+            meshDirty = true;
+        }
+        updateNativeIfNeeded();
+        return nativeObject;        
+    }
+
+    public void updateNativeIfNeeded() {
+        if (meshDirty) {
+            Mesh.Geometry g = new Mesh.Geometry();
+            g.pos = points;
+            g.uv = texCoords;
+            g.faces = faces;
+            g.smoothing = faceSmoothingGroups;
+            if (!nativeObject.buildGeometry(g)) {
+                throw new RuntimeException("nativeObject.buildGeometry failed");
+            }
+            meshDirty = false;
+        }
+    }
+
+    // Note: This method is intentionally made package scope for security
+    // reason. It is created for internal use only. 
+    // Do not make it a public method without careful consideration.
+    void setPointsByRef(float[] points) {
+        meshDirty = true;
+        this.points = points;
+    }
+
+    // Note: This method is intentionally made package scope for security
+    // reason. It is created for internal use only. 
+    // Do not make it a public method without careful consideration.
+    void setTexCoordsByRef(float[] texCoords) {
+        meshDirty = true;
+        this.texCoords = texCoords;
+    }
+    
+    // Note: This method is intentionally made package scope for security
+    // reason. It is created for internal use only. 
+    // Do not make it a public method without careful consideration.
+    void setFacesByRef(int[] faces) {
+        meshDirty = true;
+        this.faces = faces;
+    }
+
+    // Note: This method is intentionally made package scope for security
+    // reason. It is created for internal use only. 
+    // Do not make it a public method without careful consideration.
+    void setFaceSmoothingGroupsByRef(int[] faceSmoothingGroups) {
+        meshDirty = true;
+        this.faceSmoothingGroups = faceSmoothingGroups;
+    }
+
+    public void setPoints(float[] points) {
+        meshDirty = true;
+        if (points == null) {
+            this.points = null;
+            return;
+        }
+        if ((this.points == null) || (this.points.length != points.length)) {
+            this.points = new float[points.length];
+        }
+        System.arraycopy(points, 0, this.points, 0, this.points.length);      
+    }
+
+    public void setPoints(int index, float[] points, int start, int length) {
+        meshDirty = true;
+        if (points == null) {
+            this.points = null;
+            return;
+        }
+
+        // Range check were done in the FX layer.
+        int startOffset = start * TriangleMesh.NUM_COMPONENTS_PER_POINT;
+        int indexOffset = index * TriangleMesh.NUM_COMPONENTS_PER_POINT;
+        int lengthInFloatUnit = length * TriangleMesh.NUM_COMPONENTS_PER_POINT;
+        System.arraycopy(points, startOffset, this.points, indexOffset, lengthInFloatUnit);
+    }
+
+    public void setTexCoords(float[] texCoords) {
+        meshDirty = true;
+        if (texCoords == null) {
+            this.texCoords = null;
+            return;
+        }
+        if ((this.texCoords == null) || (this.texCoords.length != texCoords.length)) {
+            this.texCoords = new float[texCoords.length];
+        }
+        System.arraycopy(texCoords, 0, this.texCoords, 0, this.texCoords.length);
+    }
+
+    public void setTexCoords(int index, float[] texCoords, int start, int length) {
+        meshDirty = true;
+        if (texCoords == null) {
+            this.texCoords = null;
+            return;
+        }
+
+        // Range check were done in the FX layer.
+        int startOffset = start * TriangleMesh.NUM_COMPONENTS_PER_TEXCOORD;
+        int indexOffset = index * TriangleMesh.NUM_COMPONENTS_PER_TEXCOORD;
+        int lengthInFloatUnit = length * TriangleMesh.NUM_COMPONENTS_PER_TEXCOORD;
+        System.arraycopy(texCoords, startOffset, this.texCoords, indexOffset, lengthInFloatUnit);
+    }
+
+    public void setFaces(int[] faces) {
+        meshDirty = true;
+        if (faces == null) {
+            this.faces = null;
+            return;
+        }
+        if ((this.faces == null) || (this.faces.length != faces.length)) {
+            this.faces = new int[faces.length];
+        }
+        System.arraycopy(faces, 0, this.faces, 0, this.faces.length);
+    }
+
+    public void setFaces(int index, int[] faces, int start, int length) {
+        meshDirty = true;
+        if (faces == null) {
+            this.faces = null;
+            return;
+        }
+
+        // Range check were done in the FX layer.
+        int startOffset = start * TriangleMesh.NUM_COMPONENTS_PER_FACE;
+        int lengthInIntUnit = length * TriangleMesh.NUM_COMPONENTS_PER_FACE;
+        int indexOffset = index * TriangleMesh.NUM_COMPONENTS_PER_FACE;
+        System.arraycopy(faces, startOffset, this.faces, indexOffset, lengthInIntUnit);
+    }
+
+    public void setFaceSmoothingGroups(int[] faceSmoothingGroups) {
+        meshDirty = true;
+        if (faceSmoothingGroups == null) {
+            this.faceSmoothingGroups = null;
+            return;
+        }
+        if ((this.faceSmoothingGroups == null) || 
+                (this.faceSmoothingGroups.length != faceSmoothingGroups.length)) {
+            this.faceSmoothingGroups = new int[faceSmoothingGroups.length];
+        }
+        System.arraycopy(faceSmoothingGroups, 0, this.faceSmoothingGroups, 0,
+                this.faceSmoothingGroups.length);
+    }
+
+    public void setFaceSmoothingGroups(int index, int[] faceSmoothingGroups, int start, int length) {
+        meshDirty = true;
+        if (faceSmoothingGroups == null) {
+            this.faceSmoothingGroups = null;
+            return;
+        }
+
+        // Range check were done in the FX layer.
+        System.arraycopy(faceSmoothingGroups, start, this.faceSmoothingGroups, index, length);
+    }
+    
+}
--- a/javafx-sg-prism/test/com/sun/javafx/sg/prism/TestGraphics.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-sg-prism/test/com/sun/javafx/sg/prism/TestGraphics.java	Thu Feb 07 17:13:12 2013 -0800
@@ -36,6 +36,7 @@
 import com.sun.prism.Graphics;
 import com.sun.prism.Image;
 import com.sun.prism.MediaFrame;
+import com.sun.prism.MeshFactory;
 import com.sun.prism.PixelFormat;
 import com.sun.prism.Presentable;
 import com.sun.prism.PresentableState;
@@ -205,6 +206,10 @@
         @Override public void removeFactoryListener(ResourceFactoryListener l) { }
         @Override public RenderingContext createRenderingContext(PresentableState pstate) { return null; }
         @Override public void dispose() { }
+
+        @Override public MeshFactory get3DFactory() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
     }
     
     private static class TestRenderTarget implements RenderTarget {
--- a/javafx-ui-common/src/com/sun/javafx/scene/DirtyBits.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/com/sun/javafx/scene/DirtyBits.java	Thu Feb 07 17:13:12 2013 -0800
@@ -42,7 +42,9 @@
     NODE_CSS,
 
     // Dirty bits for various subclasses of Node
-    NODE_GEOMETRY,  // Used by ImageView, MediaView, and subclasses of Shape
+    NODE_GEOMETRY,  // Used by ImageView, MediaView, and subclasses of Shape and Shape3D
+    NODE_CULLFACE, // Used by Shape3D
+    NODE_DRAWMODE, // Used by Shape3D
     NODE_SMOOTH,    // Used by ImageView, MediaView, and subclasses of Shape
     NODE_VIEWPORT,  // Used by ImageView and MediaView
     NODE_CONTENTS,  // Used by ImageView, MediaView, Text, WebView, and subclasses of Shape
@@ -75,6 +77,22 @@
     // Dirty bits for various subclasses of Effect
     EFFECT_EFFECT,    // Used when Effect is dirty
 
+    // Dirty bits for Camera class
+    NODE_CAMERA,
+    NODE_CAMERA_TRANSFORM,
+    
+    // Dirty bits for Light class
+    NODE_LIGHT,
+    NODE_LIGHT_TRANSFORM,
+    
+    // Dirty bits for Material class (non Node type)
+    MATERIAL,
+    MATERIAL_PROPERTY,
+
+    // Dirty bits for Mesh class (non Node type)
+    MESH,
+    MESH_GEOM,
+
     // NOTE: The following MUST be the last enum value in this class. The ordinal
     // of this enum indicates the number of dirty bits in this set, exclusive of
     // the MAX_DIRTY bit itself, which will never be set or tested.
--- a/javafx-ui-common/src/com/sun/javafx/scene/EnteredExitedHandler.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/com/sun/javafx/scene/EnteredExitedHandler.java	Thu Feb 07 17:13:12 2013 -0800
@@ -62,18 +62,14 @@
 
             if (event.getEventType() == MouseDragEvent.MOUSE_DRAG_ENTERED_TARGET) {
                 eventTypeModified = true;
-                return MouseEvent.copyForMouseDragEvent((MouseDragEvent) event,
-                                eventSource, event.getTarget(),
-                                MouseDragEvent.MOUSE_DRAG_ENTERED,
-                                ((MouseDragEvent) event).getGestureSource());
+                return ((MouseDragEvent) event).copyFor(eventSource,
+                        event.getTarget(), MouseDragEvent.MOUSE_DRAG_ENTERED);
             }
 
             if (event.getEventType() == MouseDragEvent.MOUSE_DRAG_EXITED_TARGET) {
                 eventTypeModified = true;
-                return MouseEvent.copyForMouseDragEvent((MouseDragEvent) event,
-                                eventSource, event.getTarget(),
-                                MouseDragEvent.MOUSE_DRAG_EXITED,
-                                ((MouseDragEvent) event).getGestureSource());
+                return ((MouseDragEvent) event).copyFor(eventSource,
+                        event.getTarget(), MouseDragEvent.MOUSE_DRAG_EXITED);
             }
 
             if (event.getEventType() == DragEvent.DRAG_ENTERED_TARGET) {
@@ -108,18 +104,14 @@
 
             if (event.getEventType() == MouseDragEvent.MOUSE_DRAG_ENTERED) {
                 eventTypeModified = true;
-                return MouseEvent.copyForMouseDragEvent((MouseDragEvent) event,
-                                eventSource, event.getTarget(),
-                                MouseDragEvent.MOUSE_DRAG_ENTERED_TARGET,
-                                ((MouseDragEvent) event).getGestureSource());
+                return ((MouseDragEvent) event).copyFor(eventSource,
+                        event.getTarget(), MouseDragEvent.MOUSE_DRAG_ENTERED_TARGET);
             }
 
             if (event.getEventType() == MouseDragEvent.MOUSE_DRAG_EXITED) {
                 eventTypeModified = true;
-                return MouseEvent.copyForMouseDragEvent((MouseDragEvent) event,
-                                eventSource, event.getTarget(),
-                                MouseDragEvent.MOUSE_DRAG_EXITED_TARGET,
-                                ((MouseDragEvent) event).getGestureSource());
+                return ((MouseDragEvent) event).copyFor(eventSource,
+                        event.getTarget(), MouseDragEvent.MOUSE_DRAG_EXITED_TARGET);
             }
 
             if (event.getEventType() == DragEvent.DRAG_ENTERED) {
--- a/javafx-ui-common/src/com/sun/javafx/scene/input/InputEventUtils.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/com/sun/javafx/scene/input/InputEventUtils.java	Thu Feb 07 17:13:12 2013 -0800
@@ -27,10 +27,10 @@
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.ArrayList;
 import java.util.Collections;
-import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
 import javafx.scene.Node;
+import javafx.scene.input.PickResult;
 import javafx.scene.input.TransferMode;
 
 /**
@@ -45,51 +45,61 @@
      * @param newSource Node to whose coordinate system to recompute
      * @return the recomputed coordinates
      */
-    public static Point2D recomputeCoordinates(Point2D coordinates,
-            Object oldSource, Object newSource) {
+    public static Point3D recomputeCoordinates(PickResult result,
+            Object newSource) {
 
-        final Node oldSourceNode =
-                (oldSource instanceof Node) ? (Node) oldSource : null;
+        Point3D coordinates = result.getIntersectedPoint();
+        if (coordinates == null) {
+            return new Point3D(Double.NaN, Double.NaN, Double.NaN);
+        }
+
+        final Node oldSourceNode = result.getIntersectedNode();
 
         final Node newSourceNode =
                 (newSource instanceof Node) ? (Node) newSource : null;
 
         double newX = coordinates.getX();
         double newY = coordinates.getY();
+        double newZ = coordinates.getZ();
 
         if (newSourceNode != null) {
             if (oldSourceNode != null) {
-                Point2D pt = oldSourceNode.localToScene(newX, newY);
+                Point3D pt = oldSourceNode.localToScene(newX, newY, newZ);
                 pt = newSourceNode.sceneToLocal(pt);
                 if (pt != null) {
                     newX = pt.getX();
                     newY = pt.getY();
+                    newZ = pt.getZ();
                 } else {
                     newX = Double.NaN;
                     newY = Double.NaN;
+                    newZ = Double.NaN;
                 }
             } else {
                 // assume that since no node was in the evt, then it was in
                 // terms of the scene
-                Point2D pt = newSourceNode.sceneToLocal(newX, newY);
+                Point3D pt = newSourceNode.sceneToLocal(newX, newY, newZ);
                 if (pt != null) {
                     newX = pt.getX();
                     newY = pt.getY();
+                    newZ = pt.getZ();
                 } else {
                     newX = Double.NaN;
                     newY = Double.NaN;
+                    newZ = Double.NaN;
                 }
             }
         } else {
             if (oldSourceNode != null) {
                 // recomputing from old source's local bounds to scene bounds
-                Point2D pt = oldSourceNode.localToScene(newX, newY);
+                Point3D pt = oldSourceNode.localToScene(newX, newY, newZ);
                 newX = pt.getX();
                 newY = pt.getY();
+                newZ = pt.getZ();
             }
         }
 
-        return new Point2D(newX, newY);
+        return new Point3D(newX, newY, newZ);
     }
 
     private static final List<TransferMode> TM_ANY =
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/com/sun/javafx/scene/input/PickResultChooser.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,190 @@
+/*
+ * 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 com.sun.javafx.scene.input;
+
+import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.geom.Vec3d;
+import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
+import javafx.scene.Node;
+import javafx.scene.input.PickResult;
+
+/**
+ * Used during 3D picking process to determine the best pick result.
+ */
+public class PickResultChooser {
+
+    private double distance = Double.POSITIVE_INFINITY;
+    private Node node;
+    private int face = -1;
+    private Point3D point;
+    private Point2D texCoord;
+    boolean empty = true;
+
+    /**
+     * Helper method for computing intersected point.
+     * This method would fit better to PickRay but it cannot work with
+     * Point3D (dependency issues).
+     *
+     * @param ray Pick ray used for picking
+     * @param distance Distance measured in ray direction magnitudes
+     * @return the intersection point
+     */
+    public static Point3D computePoint(PickRay ray, double distance) {
+        Vec3d origin = ray.getOriginNoClone();
+        Vec3d dir = ray.getDirectionNoClone();
+
+        return new Point3D(
+                origin.x + dir.x * distance,
+                origin.y + dir.y * distance,
+                origin.z + dir.z * distance);
+    }
+
+    /**
+     * Converts the current content of this instance to the unmodifiable
+     * PickResult.
+     * @return PickResult containing the current values of this chooser
+     */
+    public PickResult toPickResult() {
+        if (empty) {
+            return null;
+        }
+        return new PickResult(node, point, distance, face, texCoord);
+    }
+
+    /**
+     * Returns true if the given distance is smaller than the distance stored
+     * in this instance.
+     * @param distance The distance to compare
+     * @return true if the given distance is smaller
+     */
+    public boolean isCloser(double distance) {
+        return distance < this.distance || empty;
+    }
+
+    /**
+     * Returns true if there is no intersection stored in this instance.
+     * @return true if there is no intersection stored in this instance.
+     */
+    public boolean isEmpty() {
+        return empty;
+    }
+
+    /**
+     * Offers an intersection. If the given intersection is closer to the camera
+     * than the current one (the distance is smaller), this instance is updated
+     * to hold the given values.
+     * @param node The intersected node
+     * @param distance The intersected distance measured in pickRay direction magnitudes
+     * @param face The intersected face
+     * @param point The intersection point
+     * @param texCoord The intersected texture coordinates
+     * @return true if the offered intersection has been used
+     */
+    public boolean offer(Node node, double distance, int face, Point3D point, Point2D texCoord) {
+        if (distance < this.distance || empty) {
+            this.node = node;
+            this.distance = distance;
+            this.face = face;
+            this.point = point;
+            this.texCoord = texCoord;
+            this.empty = false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Offers an intersection with a non-Shape3D object. This method is used
+     * for 2D objects and for 3D objects with pickOnBounds==true; in both cases
+     * face and texCoord make no sense.
+     *
+     * If the given intersection is closer to the camera
+     * than the current one (the distance is smaller), this instance is updated
+     * to hold the given values.
+     * @param node The intersected node
+     * @param distance The intersected distance measured in pickRay direction magnitudes
+     * @param point The intersection point
+     * @return true if the offered intersection has been used
+     */
+    public boolean offer(Node node, double distance, Point3D point) {
+        if (distance < this.distance || empty) {
+            this.node = node;
+            this.distance = distance;
+            this.face = PickResult.FACE_UNDEFINED;
+            this.point = point;
+            this.texCoord = null;
+            this.empty = false;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the intersected Node
+     *
+     * @return the picked Node
+     */
+    public final Node getIntersectedNode() {
+        return node;
+    }
+
+    /**
+     * Returns the intersected distance between camera position and the picked Node
+     *
+     * @return the distance from camera to the intersection
+     */
+    public final double getIntersectedDistance() {
+        return distance;
+    }
+
+    /**
+     * Returns the intersected face of the picked Node
+     *
+     * @return the picked face
+     */
+    public final int getIntersectedFace() {
+        return face;
+     }
+
+     /**
+     * Return the intersected point in local coordinate of the picked Node
+     *
+     * @return new Point3D presenting the intersected point
+     */
+    public final Point3D getIntersectedPoint() {
+        return point;
+    }
+
+    /**
+     * Return the intersected texture coordinates of the picked Node
+     *
+     * return new Point2D presenting the intersected TexCoord
+     */
+    public final javafx.geometry.Point2D getIntersectedTexCoord() {
+        return texCoord;
+    }
+}
--- a/javafx-ui-common/src/com/sun/javafx/tk/DummyToolkit.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/com/sun/javafx/tk/DummyToolkit.java	Thu Feb 07 17:13:12 2013 -0800
@@ -47,9 +47,7 @@
 import javafx.stage.Modality;
 import javafx.stage.StageStyle;
 import com.sun.javafx.embed.HostInterface;
-import com.sun.javafx.geom.ParallelCameraImpl;
 import com.sun.javafx.geom.Path2D;
-import com.sun.javafx.geom.PerspectiveCameraImpl;
 import com.sun.javafx.geom.Shape;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.perf.PerformanceTracker;
@@ -57,15 +55,24 @@
 import com.sun.javafx.runtime.async.AsyncOperationListener;
 import com.sun.javafx.scene.text.HitInfo;
 import com.sun.javafx.scene.text.TextLayoutFactory;
+import com.sun.javafx.sg.PGAmbientLight;
 import com.sun.javafx.sg.PGArc;
+import com.sun.javafx.sg.PGBox;
 import com.sun.javafx.sg.PGCanvas;
 import com.sun.javafx.sg.PGCircle;
 import com.sun.javafx.sg.PGCubicCurve;
+import com.sun.javafx.sg.PGCylinder;
 import com.sun.javafx.sg.PGEllipse;
 import com.sun.javafx.sg.PGGroup;
 import com.sun.javafx.sg.PGImageView;
+import com.sun.javafx.sg.PGLightBase;
 import com.sun.javafx.sg.PGLine;
+import com.sun.javafx.sg.PGMeshView;
+import com.sun.javafx.sg.PGParallelCamera;
 import com.sun.javafx.sg.PGPath;
+import com.sun.javafx.sg.PGPerspectiveCamera;
+import com.sun.javafx.sg.PGPhongMaterial;
+import com.sun.javafx.sg.PGPointLight;
 import com.sun.javafx.sg.PGPolygon;
 import com.sun.javafx.sg.PGPolyline;
 import com.sun.javafx.sg.PGQuadCurve;
@@ -75,7 +82,9 @@
 import com.sun.javafx.sg.PGShape.StrokeLineCap;
 import com.sun.javafx.sg.PGShape.StrokeLineJoin;
 import com.sun.javafx.sg.PGShape.StrokeType;
+import com.sun.javafx.sg.PGSphere;
 import com.sun.javafx.sg.PGText;
+import com.sun.javafx.sg.PGTriangleMesh;
 import com.sun.scenario.DelayedRunnable;
 import com.sun.scenario.animation.AbstractMasterTimer;
 import com.sun.scenario.effect.FilterContext;
@@ -194,16 +203,6 @@
     }
 
     @Override
-    public PerspectiveCameraImpl createPerspectiveCamera() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
-    public ParallelCameraImpl createParallelCamera() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    @Override
     protected Object createColorPaint(Color paint) {
         throw new UnsupportedOperationException("Not supported yet.");
     }
@@ -486,4 +485,69 @@
     public void requestNextPulse() {
         throw new UnsupportedOperationException("Not supported yet.");
     }
+
+    @Override
+    public PGBox createPGBox() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGCylinder createPGCylinder() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGSphere createPGSphere() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGTriangleMesh createPGTriangleMesh() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGMeshView createPGMeshView() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGPhongMaterial createPGPhongMaterial() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGAmbientLight createPGAmbientLight() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGPointLight createPGPointLight() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGParallelCamera createPGParallelCamera() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public PGPerspectiveCamera createPGPerspectiveCamera(boolean fixedEyePosition) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public List<PGLightBase> getLightsInScene() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public boolean isLightsDirty() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public void setLightsDirty(boolean lightsDirty) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
 }
--- a/javafx-ui-common/src/com/sun/javafx/tk/TKScene.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/com/sun/javafx/tk/TKScene.java	Thu Feb 07 17:13:12 2013 -0800
@@ -24,8 +24,8 @@
  */
 package com.sun.javafx.tk;
 
-import com.sun.javafx.geom.CameraImpl;
 import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.sg.PGCamera;
 import com.sun.javafx.sg.PGNode;
 import javafx.scene.input.Dragboard;
 
@@ -64,7 +64,7 @@
 
     public void markDirty();
 
-    public void setCamera(CameraImpl camera);
+    public void setCamera(PGCamera camera);
 
     public PickRay computePickRay(float x, float y, PickRay pickRay);
 
--- a/javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/com/sun/javafx/tk/Toolkit.java	Thu Feb 07 17:13:12 2013 -0800
@@ -65,10 +65,7 @@
 import com.sun.javafx.PlatformUtil;
 import com.sun.javafx.beans.event.AbstractNotifyListener;
 import com.sun.javafx.embed.HostInterface;
-import com.sun.javafx.geom.CameraImpl;
-import com.sun.javafx.geom.ParallelCameraImpl;
 import com.sun.javafx.geom.Path2D;
-import com.sun.javafx.geom.PerspectiveCameraImpl;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.jmx.HighlightRegion;
 import com.sun.javafx.perf.PerformanceTracker;
@@ -79,6 +76,7 @@
 import com.sun.javafx.scene.text.HitInfo;
 import com.sun.javafx.scene.text.TextLayoutFactory;
 import com.sun.javafx.sg.PGArc;
+import com.sun.javafx.sg.PGBox;
 import com.sun.javafx.sg.PGCanvas;
 import com.sun.javafx.sg.PGCircle;
 import com.sun.javafx.sg.PGCubicCurve;
@@ -96,6 +94,17 @@
 import com.sun.javafx.sg.PGSVGPath;
 import com.sun.javafx.sg.PGShape;
 import com.sun.javafx.sg.PGText;
+import com.sun.javafx.sg.PGAmbientLight;
+import com.sun.javafx.sg.PGCamera;
+import com.sun.javafx.sg.PGCylinder;
+import com.sun.javafx.sg.PGLightBase;
+import com.sun.javafx.sg.PGMeshView;
+import com.sun.javafx.sg.PGParallelCamera;
+import com.sun.javafx.sg.PGPerspectiveCamera;
+import com.sun.javafx.sg.PGPhongMaterial;
+import com.sun.javafx.sg.PGPointLight;
+import com.sun.javafx.sg.PGSphere;
+import com.sun.javafx.sg.PGTriangleMesh;
 import com.sun.scenario.DelayedRunnable;
 import com.sun.scenario.animation.AbstractMasterTimer;
 import com.sun.scenario.effect.AbstractShadow.ShadowMode;
@@ -498,9 +507,6 @@
     //to be used for testing only
     public abstract void waitFor(Task t);
 
-    public abstract PerspectiveCameraImpl createPerspectiveCamera();
-    public abstract ParallelCameraImpl createParallelCamera();
-
     private Object checkSingleColor(List<Stop> stops) {
         if (stops.size() == 2) {
             Color c = stops.get(0).getColor();
@@ -633,6 +639,30 @@
     public abstract Object createSVGPathObject(SVGPath svgpath);
     public abstract Path2D createSVGPath2D(SVGPath svgpath);
 
+    // 3D API support for FX 8
+    // Shapes and mesh
+    public abstract PGBox createPGBox();
+    public abstract PGCylinder createPGCylinder();
+    public abstract PGSphere createPGSphere();
+    public abstract PGTriangleMesh createPGTriangleMesh();
+    public abstract PGMeshView createPGMeshView();
+
+    // Material
+    public abstract PGPhongMaterial createPGPhongMaterial();
+    
+    // Lights
+    public abstract PGAmbientLight createPGAmbientLight();
+    public abstract PGPointLight createPGPointLight();   
+    
+    // Cameras
+    public abstract PGParallelCamera createPGParallelCamera();   
+    public abstract PGPerspectiveCamera createPGPerspectiveCamera(boolean fixedEyePosition);
+    
+    // TODO: Need to evaluate the lighting logic
+    public abstract List<PGLightBase> getLightsInScene();
+    public abstract boolean isLightsDirty();
+    public abstract void setLightsDirty(boolean lightsDirty);
+
     /**
      * Tests whether the pixel on the given coordinates in the given image
      * is non-empty (not fully transparent). Return value is not defined
@@ -721,7 +751,7 @@
         // Rendering parameters either from Scene or SnapShotParams
         public boolean depthBuffer;
         public Object platformPaint;
-        public CameraImpl camera;
+        public PGCamera camera;
 
         // PlatformImage into which to render or null
         public Object platformImage;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/AmbientLight.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,77 @@
+/*
+ * 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 javafx.scene;
+
+import com.sun.javafx.sg.PGAmbientLight;
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.tk.Toolkit;
+import javafx.scene.paint.Color;
+
+/**
+ * Defines an ambient light source object. Ambient light is a light source
+ * that seems to come from all directions.
+ *
+ * @since JavaFX 8
+ */
+public class AmbientLight extends LightBase {
+    /**
+     * Creates a new instance of {@code AmbientLight} class with a default Color.WHITE light source.
+     */
+    public AmbientLight() {
+        super();
+    }
+
+    /**
+     * Creates a new instance of {@code AmbientLight} class using the specified color.
+     * 
+     * @param color the color of the light source
+     */
+    public AmbientLight(Color color) {
+        super(color);
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected PGNode impl_createPGNode() {
+        return Toolkit.getToolkit().createPGAmbientLight();
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public void impl_updatePG() {
+        super.impl_updatePG();
+        PGAmbientLight pgAmbientLight = (PGAmbientLight) impl_getPGNode();
+        pgAmbientLight.setColor((getColor() == null) ? null
+                : Toolkit.getPaintAccessor().getPlatformPaint(getColor()));
+    }
+}
--- a/javafx-ui-common/src/javafx/scene/Camera.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/Camera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -24,11 +24,18 @@
  */
 package javafx.scene;
 
-import javafx.beans.property.BooleanProperty;
-import javafx.beans.property.SimpleBooleanProperty;
-import javafx.beans.value.ObservableBooleanValue;
-
-import com.sun.javafx.geom.CameraImpl;
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.BoxBounds;
+import com.sun.javafx.geom.transform.Affine3D;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.jmx.MXNodeAlgorithm;
+import com.sun.javafx.jmx.MXNodeAlgorithmContext;
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.sg.PGCamera;
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
 
 
 /**
@@ -36,37 +43,135 @@
  *
  * @since JavaFX 1.3
  */
-public abstract class Camera {
+public abstract class Camera extends Node {
+   
+    private Affine3D localToSceneTx = new Affine3D();
 
-    private CameraImpl platformCamera;
-
-    CameraImpl getPlatformCamera() {
-        if (platformCamera == null) {
-            platformCamera = createPlatformCamera();
-        }
-        return platformCamera;
+    protected Camera() {
+        this.localToSceneTransformProperty().addListener(new InvalidationListener() {
+            @Override
+            public void invalidated(Observable observable) {
+                impl_markDirty(DirtyBits.NODE_CAMERA_TRANSFORM);
+            }
+        });
     }
 
-    abstract CameraImpl createPlatformCamera();
+    /**
+     * Specifies the near clipping plane of this {@code Camera} in the local
+     * coordinate system of this node.
+     *
+     * @defaultValue 0.1
+     * @since JavaFX 8
+     */
+    private DoubleProperty nearClip;
 
-    abstract void update();
-    private BooleanProperty dirty = new SimpleBooleanProperty(this, "dirty");
-
-    final ObservableBooleanValue dirtyProperty() { return dirty; }
-
-    final boolean isDirty() {
-        return dirty.get();
+    public final void setNearClip(double value){
+        nearClipProperty().set(value);
     }
 
-    final void markDirty() {
-        dirty.set(true);
+    public final double getNearClip() {
+        return nearClip == null ? 0.1 : nearClip.get();
     }
 
-    final void clearDirty() {
-        dirty.set(false);
+    public final DoubleProperty nearClipProperty() {
+        if (nearClip == null) {
+            nearClip = new SimpleDoubleProperty(Camera.this, "nearClip", 0.1) {
+                @Override
+                protected void invalidated() {
+                    impl_markDirty(DirtyBits.NODE_CAMERA);
+                }
+            };
+        }
+        return nearClip;
+    }
+
+    /**
+     * Specifies the far clipping plane of this {@code Camera} in the local
+     * coordinate system of this node.
+     * <p>
+     *
+     * @defaultValue 100.0
+     * @since JavaFX 8
+     */
+    private DoubleProperty farClip;
+
+    public final void setFarClip(double value){
+        farClipProperty().set(value);
+    }
+
+    public final double getFarClip() {
+        return farClip == null ? 100.0 : farClip.get();
+    }
+
+    public final DoubleProperty farClipProperty() {
+        if (farClip == null) {
+            farClip = new SimpleDoubleProperty(Camera.this, "farClip", 100.0) {
+                @Override
+                protected void invalidated() {
+                    impl_markDirty(DirtyBits.NODE_CAMERA);
+                }
+            };
+        }
+        return farClip;
+    }
+    
+    PGCamera getPlatformCamera() {
+        return (PGCamera) impl_getPGNode();
     }
 
     Camera copy() {
         return this;
     }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public void impl_updatePG() {
+        super.impl_updatePG();
+        PGCamera pgCamera = (PGCamera)impl_getPGNode();
+        if (impl_isDirty(DirtyBits.NODE_CAMERA)) {
+            pgCamera.setNearClip((float) getNearClip());
+            pgCamera.setFarClip((float) getFarClip());
+        }
+        if (impl_isDirty(DirtyBits.NODE_CAMERA_TRANSFORM)) {
+            localToSceneTx.setToIdentity();
+            getLocalToSceneTransform().impl_apply(localToSceneTx);
+            // TODO: 3D - For now, we are treating the scene as world.
+            // This may need to change for the fixed eye position case.
+            pgCamera.setWorldTransform(localToSceneTx);
+        }
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+        return new BoxBounds(0, 0, 0, 0, 0, 0);
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeContains(double localX, double localY) {
+        return false;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/LightBase.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,236 @@
+/*
+ * 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 javafx.scene;
+
+import com.sun.javafx.collections.TrackableObservableList;
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.BoxBounds;
+import com.sun.javafx.geom.transform.Affine3D;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.jmx.MXNodeAlgorithm;
+import com.sun.javafx.jmx.MXNodeAlgorithmContext;
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.sg.PGLightBase;
+import com.sun.javafx.tk.Toolkit;
+import javafx.beans.InvalidationListener;
+import javafx.beans.Observable;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.collections.ListChangeListener.Change;
+import javafx.collections.ObservableList;
+import javafx.scene.paint.Color;
+
+/**
+ * The {@code LightBase} class provides definitions of common properties for
+ * objects that represent a form of Light source.  These properties
+ * include:
+ * <ul>
+ * <li>The color that defines color of the light source.
+ * </ul>
+ *
+ * @since JavaFX 8
+ */
+public abstract class LightBase extends Node {
+    /*
+     *    A Light source is a Node
+          LightBase is an abstract base class for other Light classes
+          Support AmbientLight, PointLight and DirectionalLight.
+          No plan to add SpotLight in FX8
+          No plan to include attenuation in light computation in FX8
+          Scoping
+            Spacial vs. hierarchical (or explicit) scoping
+            Spacial is hard to do it right
+            Explicit is the preferred approach
+            Add a default light when no light is specified in a 3D scene (principle of least surprise)
+ 
+     */
+    
+    private Affine3D localToSceneTx = new Affine3D();
+
+    /**
+     * Creates a new instance of {@code LightBase} class with a default Color.WHITE light source.
+     */
+    protected LightBase() {
+        this(Color.WHITE);
+    }
+
+    /**
+     * Creates a new instance of {@code LightBase} class using the specified color.
+     * 
+     * @param color the color of the light source
+     */
+    protected LightBase(Color color) {
+        setColor(color);
+        this.localToSceneTransformProperty().addListener(new InvalidationListener() {
+            @Override
+            public void invalidated(Observable observable) {
+                impl_markDirty(DirtyBits.NODE_LIGHT_TRANSFORM);
+            }
+        });
+    }
+    
+    /**
+     * Specifies the color of light source.
+     *
+     * @defaultValue null
+     */
+    private ObjectProperty<Color> color;
+
+    public final void setColor(Color value) {
+        colorProperty().set(value);
+    }
+
+    public final Color getColor() {
+        return color == null ? null : color.get();
+    }
+
+    public final ObjectProperty<Color> colorProperty() {
+        if (color == null) {
+            color = new SimpleObjectProperty<Color>(LightBase.this, "color") {
+                @Override
+                protected void invalidated() {
+                    impl_markDirty(DirtyBits.NODE_LIGHT);
+                }                
+            };
+        }
+        return color;
+    }
+
+    /**
+     * Defines the light on or off.
+     *
+     * @defaultValue true
+     */
+    private BooleanProperty lightOn;
+
+    public final void setLightOn(boolean value) {
+        lightOnProperty().set(value);
+    }
+
+    public final boolean isLightOn() {
+        return lightOn == null ? true : lightOn.get();
+    }
+
+    public final BooleanProperty lightOnProperty() {
+        if (lightOn == null) {
+            lightOn = new SimpleBooleanProperty(LightBase.this, "lightOn", true) {
+                @Override
+                protected void invalidated() {
+                    impl_markDirty(DirtyBits.NODE_LIGHT);
+                }
+            };
+        }
+        return lightOn;
+    }
+
+    private ObservableList<Node> scope;
+    
+    /**
+     * Gets the list of nodes that specifies the
+     * hierarchical scope of this Light. If the scope list is empty, 
+     * the Light node has universe scope: all nodes under it's scene
+     * are affected by it. If the scope list is non-empty, only those
+     * 3D Shape nodes in the scope list and under the Group nodes in the
+     * scope list are affected by this Light node.
+     */
+    public ObservableList<Node> getScope() {
+        if (scope == null) {
+            scope = new TrackableObservableList<Node>() {
+
+                @Override
+                protected void onChanged(Change<Node> c) {
+                    while (c.next()) {
+                        for (Node node : c.getRemoved()) {
+//                            node.impl_remove(Node.this);
+                        }
+                        for (Node node : c.getAddedSubList()) {
+//                            node.impl_add(LightBase.this);
+                        }
+                    }
+                }
+            };
+        }
+
+        return scope;
+    }
+    
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public void impl_updatePG() {
+        super.impl_updatePG();
+        PGLightBase pgLightBase = (PGLightBase) impl_getPGNode();
+        if (impl_isDirty(DirtyBits.NODE_LIGHT)) {
+            pgLightBase.setColor((getColor() == null) ? null
+                    : Toolkit.getPaintAccessor().getPlatformPaint(getColor()));
+            //TODO: 3D - Handle light on/off
+        }
+        if (impl_isDirty(DirtyBits.NODE_LIGHT_TRANSFORM)) {
+            localToSceneTx.setToIdentity();
+            getLocalToSceneTransform().impl_apply(localToSceneTx);
+            // TODO: 3D - For now, we are treating the scene as world. This may need to change
+            // for the fixed eye position case.
+            pgLightBase.setWorldTransform(localToSceneTx);
+        }
+    }
+
+     /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+        // TODO: 3D - Check is this the right default
+        return new BoxBounds();
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeContains(double localX, double localY) {
+        // TODO: 3D - Check is this the right default
+        return false;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+    
+}
--- a/javafx-ui-common/src/javafx/scene/Node.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/Node.java	Thu Feb 07 17:13:12 2013 -0800
@@ -130,6 +130,7 @@
 import com.sun.javafx.geom.transform.Affine3D;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.geom.transform.NoninvertibleTransformException;
+import com.sun.javafx.geom.Vec3d;
 import com.sun.javafx.jmx.MXNodeAlgorithm;
 import com.sun.javafx.jmx.MXNodeAlgorithmContext;
 import sun.util.logging.PlatformLogger;
@@ -140,13 +141,14 @@
 import com.sun.javafx.scene.DirtyBits;
 import com.sun.javafx.scene.EventHandlerProperties;
 import com.sun.javafx.scene.NodeEventDispatcher;
+import com.sun.javafx.scene.input.PickResultChooser;
 import com.sun.javafx.scene.transform.TransformUtils;
 import com.sun.javafx.scene.traversal.Direction;
 import com.sun.javafx.sg.PGNode;
 import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.ReadOnlyDoubleProperty;
+import javafx.beans.property.ReadOnlyDoubleWrapper;
 import javafx.css.StyleableProperty;
-import javafx.scene.transform.Affine;
-import javafx.scene.transform.NonInvertibleTransformException;
 import javafx.geometry.NodeOrientation;
 import javafx.stage.Stage;
 import javafx.stage.Window;
@@ -2800,6 +2802,21 @@
 
     /* *************************************************************************
      *                                                                         *
+     * LOD Helper related APIs 
+     * TODO: (RT-26535) Implement LOD helper
+     *                                                                         *
+     **************************************************************************/    
+    /**
+     * Returns the area of this {@code Node} projected onto the 
+     * physical screen in pixel units.
+     */
+    public double computeAreaInScreen() {
+        // TODO: Implement computeAreaInScreen
+        return 0.0; // For now
+    }
+    
+    /* *************************************************************************
+     *                                                                         *
      * Bounds related APIs                                                     *
      *                                                                         *
      **************************************************************************/
@@ -3720,8 +3737,38 @@
         return sceneToLocal(scenePoint.getX(), scenePoint.getY());
     }
 
-    // Why is this method private?
-    private Point3D sceneToLocal(double x, double y, double z) throws NoninvertibleTransformException{
+    /**
+     * Transforms a point from the coordinate space of the {@link javafx.scene.Scene}
+     * into the local coordinate space of this {@code Node}.
+     * @param scenePoint a point on a Scene
+     * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
+     * Null is also returned if the transformation from local to Scene is not invertible.
+     */
+    public Point3D sceneToLocal(Point3D scenePoint) {
+        return sceneToLocal(scenePoint.getX(), scenePoint.getY(), scenePoint.getZ());
+    }
+
+    /**
+     * Transforms a point from the coordinate space of the {@link javafx.scene.Scene}
+     * into the local coordinate space of this {@code Node}.
+     * @param sceneX x coordinate of a point on a Scene
+     * @param sceneY y coordinate of a point on a Scene
+     * @param sceneZ z coordinate of a point on a Scene
+     * @return local Node's coordinates of the point or null if Node is not in a {@link Window}.
+     * Null is also returned if the transformation from local to Scene is not invertible.
+     */
+    public Point3D sceneToLocal(double sceneX, double sceneY, double sceneZ) {
+        try {
+            return sceneToLocal0(sceneX, sceneY, sceneZ);
+        } catch (NoninvertibleTransformException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Internal method to transform a point from scene to local coordinates.
+     */
+    private Point3D sceneToLocal0(double x, double y, double z) throws NoninvertibleTransformException {
         final com.sun.javafx.geom.Vec3d tempV3D =
                 TempState.getInstance().vec3d;
         tempV3D.set(x, y, z);
@@ -3750,14 +3797,14 @@
             return createBoundingBox(p1, p2, p3, p4);
         }
         try {
-            Point3D p1 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
-            Point3D p2 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
-            Point3D p3 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
-            Point3D p4 = sceneToLocal(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
-            Point3D p5 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
-            Point3D p6 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
-            Point3D p7 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
-            Point3D p8 = sceneToLocal(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
+            Point3D p1 = sceneToLocal0(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
+            Point3D p2 = sceneToLocal0(sceneBounds.getMinX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
+            Point3D p3 = sceneToLocal0(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
+            Point3D p4 = sceneToLocal0(sceneBounds.getMinX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
+            Point3D p5 = sceneToLocal0(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMinZ());
+            Point3D p6 = sceneToLocal0(sceneBounds.getMaxX(), sceneBounds.getMaxY(), sceneBounds.getMaxZ());
+            Point3D p7 = sceneToLocal0(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMinZ());
+            Point3D p8 = sceneToLocal0(sceneBounds.getMaxX(), sceneBounds.getMinY(), sceneBounds.getMaxZ());
             return createBoundingBox(p1, p2, p3, p4, p5, p6, p7, p8);
         } catch (NoninvertibleTransformException e) {
             return null;
@@ -3842,7 +3889,19 @@
         return localToScene(localPoint.getX(), localPoint.getY());
     }
 
-    private Point3D localToScene(double x, double y, double z) {
+    /**
+     * Transforms a point from the local coordinate space of this {@code Node}
+     * into the coordinate space of its {@link javafx.scene.Scene}.
+     */
+    public Point3D localToScene(Point3D localPoint) {
+        return localToScene(localPoint.getX(), localPoint.getY(), localPoint.getZ());
+    }
+
+    /**
+     * Transforms a point from the local coordinate space of this {@code Node}
+     * into the coordinate space of its {@link javafx.scene.Scene}.
+     */
+    public Point3D localToScene(double x, double y, double z) {
         final com.sun.javafx.geom.Vec3d tempV3D =
                 TempState.getInstance().vec3d;
         tempV3D.set(x, y, z);
@@ -3904,10 +3963,22 @@
         return parentToLocal(parentPoint.getX(), parentPoint.getY());
     }
 
-    private Point3D parentToLocal(double x, double y, double z) {
+    /**
+     * Transforms a point from the coordinate space of the parent into the
+     * local coordinate space of this {@code Node}.
+     */
+    public Point3D parentToLocal(Point3D parentPoint) {
+        return parentToLocal(parentPoint.getX(), parentPoint.getY(), parentPoint.getZ());
+    }
+
+    /**
+     * Transforms a point from the coordinate space of the parent into the
+     * local coordinate space of this {@code Node}.
+     */
+    public Point3D parentToLocal(double parentX, double parentY, double parentZ) {
         final com.sun.javafx.geom.Vec3d tempV3D =
                 TempState.getInstance().vec3d;
-        tempV3D.set(x, y, z);
+        tempV3D.set(parentX, parentY, parentZ);
         try {
             parentToLocal(tempV3D);
         } catch (NoninvertibleTransformException e) {
@@ -3963,7 +4034,19 @@
         return localToParent(localPoint.getX(), localPoint.getY());
     }
 
-    private Point3D localToParent(double x, double y, double z) {
+    /**
+     * Transforms a point from the local coordinate space of this {@code Node}
+     * into the coordinate space of its parent.
+     */
+    public Point3D localToParent(Point3D localPoint) {
+        return localToParent(localPoint.getX(), localPoint.getY(), localPoint.getZ());
+    }
+
+    /**
+     * Transforms a point from the local coordinate space of this {@code Node}
+     * into the coordinate space of its parent.
+     */
+    public Point3D localToParent(double x, double y, double z) {
         final com.sun.javafx.geom.Vec3d tempV3D =
                 TempState.getInstance().vec3d;
         tempV3D.set(x, y, z);
@@ -4179,11 +4262,11 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    public final Node impl_pickNode(double parentX, double parentY) {
+    public final void impl_pickNode(double parentX, double parentY, PickResultChooser result) {
 
         // In some conditions we can omit picking this node or subgraph
         if (!isVisible() || isDisable() || isMouseTransparent()) {
-            return null;
+            return;
         }
 
         final com.sun.javafx.geom.Point2D tempPt =
@@ -4194,13 +4277,13 @@
         try {
             parentToLocal(tempPt);
         } catch (NoninvertibleTransformException e) {
-            return null;
+            return;
         }
 
         // Delegate to a function which can be overridden by subclasses which
         // actually does the pick. The implementation is markedly different
         // for leaf nodes vs. parent nodes vs. region nodes.
-        return impl_pickNodeLocal(tempPt.x, tempPt.y);
+        impl_pickNodeLocal(tempPt.x, tempPt.y, result);
     }
 
     /**
@@ -4211,11 +4294,10 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    protected Node impl_pickNodeLocal(double localX, double localY) {
+    protected void impl_pickNodeLocal(double localX, double localY, PickResultChooser result) {
         if (contains(localX, localY)) {
-            return this;
-        }
-        return null;
+            result.offer(this, Double.POSITIVE_INFINITY, new Point3D(localX, localY, 0));
+        }
     }
 
     /**
@@ -4226,26 +4308,23 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    protected Node impl_pickNodeLocal(PickRay localPickRay) {
-        if (impl_intersects(localPickRay)) {
-            return this;
-        }
-        return null;
+    protected void impl_pickNodeLocal(PickRay localPickRay, PickResultChooser result) {
+        impl_intersects(localPickRay, result);
     }
 
     /**
      * Finds a top-most child node that intersects the given ray.
      *
-     * Returns the picked node, null if no such node was found.
+     * The result argument is used for storing the picking result.
      * @treatAsPrivate implementation detail
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    public final Node impl_pickNode(PickRay pickRay) {
+    public final void impl_pickNode(PickRay pickRay, PickResultChooser result) {
 
         // In some conditions we can omit picking this node or subgraph
         if (!isVisible() || isDisable() || isMouseTransparent()) {
-            return null;
+            return;
         }
 
         final BaseTransform tempPickTx = TempState.getInstance().pickTx;
@@ -4262,7 +4341,7 @@
         // Delegate to a function which can be overridden by subclasses which
         // actually does the pick. The implementation is markedly different
         // for leaf nodes vs. parent nodes vs. region nodes.
-        return impl_pickNodeLocal(localPickRay);
+        impl_pickNodeLocal(localPickRay, result);
     }
 
     /**
@@ -4271,6 +4350,9 @@
      * shape of this {@code Node}. Note that this method does not take visibility
      * into account; the test is based on the geometry of this {@code Node} only.
      * <p>
+     * The pickResult is updated if the found intersection is closer than
+     * the currently held one.
+     * <p>
      * Note that this is a conditional feature. See
      * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
      * for more information.
@@ -4279,7 +4361,31 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    protected final boolean impl_intersects(PickRay pickRay) {
+    protected final boolean impl_intersects(PickRay pickRay, PickResultChooser pickResult) {
+        double boundsDistance = impl_intersectsBounds(pickRay);
+        if (boundsDistance >= 0.0) {
+            if (isPickOnBounds()) {
+                pickResult.offer(this, boundsDistance, PickResultChooser.computePoint(pickRay, boundsDistance));
+                return true;
+            } else {
+                return impl_computeIntersects(pickRay, pickResult);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Computes the intersection of the pickRay with this node.
+     * The pickResult argument is updated if the found intersection
+     * is closer than the passed one. On the other hand, the return value
+     * specifies whether the intersection exists, regardless of its comparison
+     * with the given pickResult.
+     *
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) {
         double origZ = pickRay.getOriginNoClone().z;
         double dirZ = pickRay.getDirectionNoClone().z;
         // Handle the case where pickRay is almost parallel to the Z-plane
@@ -4289,8 +4395,94 @@
         double t = -origZ / dirZ;
         double x = pickRay.getOriginNoClone().x + (pickRay.getDirectionNoClone().x * t);
         double y = pickRay.getOriginNoClone().y + (pickRay.getDirectionNoClone().y * t);
-        return contains((float) x, (float) y); // was contentContains, the difference is that effect / clip are included
-    }
+
+        if (contains((float) x, (float) y)) {
+            pickResult.offer(this, t, PickResultChooser.computePoint(pickRay, t));
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Computes the intersection of the pickRay with the bounds of this node.
+     * The return value is the distance between the camera and the intersection
+     * point, measured in pickRay direction magnitudes. If there is
+     * no intersection, it returns a negative value.
+     *
+     * @param pickRay The pick ray
+     * @return Distance of the intersection point, a negative number if there
+     *         is no intersection
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    protected final double impl_intersectsBounds(PickRay pickRay) {
+
+        final Vec3d dir = pickRay.getDirectionNoClone();
+        final double invDirX = dir.x == 0 ? Double.POSITIVE_INFINITY : (1.0 / dir.x);
+        final double invDirY = dir.y == 0 ? Double.POSITIVE_INFINITY : (1.0 / dir.y);
+        final double invDirZ = dir.z == 0 ? Double.POSITIVE_INFINITY : (1.0 / dir.z);
+        final Vec3d origin = pickRay.getOriginNoClone();
+        final double originX = origin.x;
+        final double originY = origin.y;
+        final double originZ = origin.z;
+        final boolean signX = invDirX < 0.0;
+        final boolean signY = invDirY < 0.0;
+        final boolean signZ = invDirZ < 0.0;
+
+        final TempState tempState = TempState.getInstance();
+        BaseBounds tempBounds = tempState.bounds;
+
+        tempBounds = getLocalBounds(tempBounds,
+                                    BaseTransform.IDENTITY_TRANSFORM);
+
+        final double minX = tempBounds.getMinX();
+        final double minY = tempBounds.getMinY();
+        final double minZ = tempBounds.getMinZ();
+        final double maxX = tempBounds.getMaxX();
+        final double maxY = tempBounds.getMaxY();
+        final double maxZ = tempBounds.getMaxZ();
+
+        double tmin = ((signX ? maxX : minX) - originX) * invDirX;
+        double tmax = ((signX ? minX : maxX) - originX) * invDirX;
+        final double tymin = ((signY ? maxY : minY) - originY) * invDirY;
+        final double tymax = ((signY ? minY : maxY) - originY) * invDirY;
+
+        if ((tmin > tymax) || (tymin > tmax)) {
+            return -1.0;
+        }
+        if (tymin > tmin) {
+            tmin = tymin;
+        }
+        if (tymax < tmax) {
+            tmax = tymax;
+        }
+
+        final double tzmin = ((signZ ? maxZ : minZ) - originZ) * invDirZ;
+        final double tzmax = ((signZ ? minZ : maxZ) - originZ) * invDirZ;
+
+        if ((tmin > tzmax) || (tzmin > tmax)) {
+            return -1;
+        }
+        if (tzmin > tmin) {
+            tmin = tzmin;
+        }
+        if (tzmax < tmax) {
+            tmax = tzmax;
+        }
+
+        if (tmin < 0.0) {
+            if (tmax >= 0.0) {
+                // we are inside bounds
+                return 0.0;
+            } else {
+                return -1.0;
+            }
+        }
+
+        return tmin;
+    }
+
 
     // Good to find a home for commonly use util. code such as EPS.
     // and almostZero. This code currently defined in multiple places,
--- a/javafx-ui-common/src/javafx/scene/ParallelCamera.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/ParallelCamera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -25,7 +25,8 @@
 
 package javafx.scene;
 
-import com.sun.javafx.geom.CameraImpl;
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.sg.PGParallelCamera;
 import com.sun.javafx.tk.Toolkit;
 
 /**
@@ -45,12 +46,24 @@
 public class ParallelCamera extends Camera {
 
     @Override
-    CameraImpl createPlatformCamera() {
-        return Toolkit.getToolkit().createParallelCamera();
+    Camera copy() {
+        ParallelCamera c = new ParallelCamera();
+        c.setNearClip(getNearClip());
+        c.setFarClip(getFarClip());
+        return c;
     }
 
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
     @Override
-    void update() {
+    protected PGNode impl_createPGNode() {
+        PGParallelCamera pgCamera = Toolkit.getToolkit().createPGParallelCamera();
+        pgCamera.setNearClip((float) getNearClip());
+        pgCamera.setFarClip((float) getFarClip());
+        return pgCamera;
     }
 
 }
--- a/javafx-ui-common/src/javafx/scene/Parent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/Parent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -52,9 +52,11 @@
 import com.sun.javafx.geom.transform.NoninvertibleTransformException;
 import com.sun.javafx.jmx.MXNodeAlgorithm;
 import com.sun.javafx.jmx.MXNodeAlgorithmContext;
+import javafx.geometry.Point3D;
 import sun.util.logging.PlatformLogger;
 import com.sun.javafx.scene.CssFlags;
 import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.scene.input.PickResultChooser;
 import com.sun.javafx.scene.traversal.TraversalEngine;
 import com.sun.javafx.sg.PGGroup;
 import com.sun.javafx.sg.PGNode;
@@ -665,19 +667,18 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    @Override protected Node impl_pickNodeLocal(double localX, double localY) {
+    @Override protected void impl_pickNodeLocal(double localX, double localY, PickResultChooser result) {
         if (containsBounds(localX, localY)) {
             for (int i = children.size()-1; i >= 0; i--) {
-                Node picked = children.get(i).impl_pickNode(localX, localY);
-                if (picked != null) {
-                    return picked;
+                children.get(i).impl_pickNode(localX, localY, result);
+                if (!result.isEmpty()) {
+                    return;
                 }
             }
             if (isPickOnBounds()) {
-                return this;
+                result.offer(this, Double.POSITIVE_INFINITY, new Point3D(localX, localY, 0));
             }
         }
-        return null;
     }
 
     /**
@@ -685,15 +686,23 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    @Override protected Node impl_pickNodeLocal(PickRay pickRay) {
-        for (int i = children.size()-1; i >= 0; i--) {
-            Node picked = children.get(i).impl_pickNode(pickRay);
+    @Override protected void impl_pickNodeLocal(PickRay pickRay, PickResultChooser result) {
 
-            if (picked != null) {
-                return picked;
+        double boundsDistance = impl_intersectsBounds(pickRay);
+
+        if (boundsDistance >= 0) {
+            final boolean checkAll = getScene().isDepthBuffer();
+            for (int i = children.size()-1; i >= 0; i--) {
+                children.get(i).impl_pickNode(pickRay, result);
+                if (!checkAll && !result.isEmpty()) {
+                    return;
+                }
+            }
+
+            if (isPickOnBounds()) {
+                result.offer(this, boundsDistance, PickResultChooser.computePoint(pickRay, boundsDistance));
             }
         }
-        return null;
     }
 
     @Override boolean isConnected() {
--- a/javafx-ui-common/src/javafx/scene/PerspectiveCamera.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/PerspectiveCamera.java	Thu Feb 07 17:13:12 2013 -0800
@@ -25,12 +25,14 @@
 
 package javafx.scene;
 
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.sg.PGPerspectiveCamera;
+import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.BooleanProperty;
 import javafx.beans.property.DoubleProperty;
-import javafx.beans.property.DoublePropertyBase;
-
-import com.sun.javafx.geom.CameraImpl;
-import com.sun.javafx.geom.PerspectiveCameraImpl;
-import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleDoubleProperty;
 
 
 
@@ -49,13 +51,17 @@
  * @since JavaFX 1.3
  */
 public  class PerspectiveCamera extends Camera {   
+
+    private boolean fixedEyePosition = false;
+
     /**
-     * Specifies the vertical angle of the camera's projection.
+     * Specifies the field of view angle of the camera's projection plane,
+     * measured in degrees.
      *
      * @defaultValue 30.0
      */
     private DoubleProperty fieldOfView;
-
+    
     public final void setFieldOfView(double value){
         fieldOfViewProperty().set(value);
     }
@@ -66,48 +72,99 @@
 
     public final DoubleProperty fieldOfViewProperty() {
         if (fieldOfView == null) {
-            fieldOfView = new DoublePropertyBase(30) {
-
+            fieldOfView = new SimpleDoubleProperty(PerspectiveCamera.this, "fieldOfView", 30) {
                 @Override
                 protected void invalidated() {
-                    markDirty();
-                }
-
-                @Override
-                public Object getBean() {
-                    return PerspectiveCamera.this;
-                }
-
-                @Override
-                public String getName() {
-                    return "fieldOfView";
+                    impl_markDirty(DirtyBits.NODE_CAMERA);
                 }
             };
         }
         return fieldOfView;
     }
 
-    public PerspectiveCamera() {
-        markDirty();
+    /**
+     * Defines whether the {@code fieldOfView} property is to apply to the vertical 
+     * dimension of the projection plane. If it is false, {@code fieldOfView} is to 
+     * apply to the horizontal dimension of the projection plane.
+     *
+     * @defaultValue true
+     * @since JavaFX 8
+     */
+    private BooleanProperty verticalFieldOfView;
+
+    public final void setVerticalFieldOfView(boolean value) {
+        verticalFieldOfViewProperty().set(value);
     }
 
-    @Override
-    CameraImpl createPlatformCamera() {
-        return Toolkit.getToolkit().createPerspectiveCamera();
+    public final boolean isVerticalFieldOfView() {
+        return verticalFieldOfView == null ? true : verticalFieldOfView.get();
     }
 
-    @Override void update() {
-        if (isDirty()) {
-            PerspectiveCameraImpl perspectiveCameraImpl = (PerspectiveCameraImpl) getPlatformCamera();
-            perspectiveCameraImpl.setFieldOfView((float)getFieldOfView());
-            clearDirty();
+    public final BooleanProperty verticalFieldOfViewProperty() {
+        if (verticalFieldOfView == null) {
+            verticalFieldOfView = new SimpleBooleanProperty(PerspectiveCamera.this, "verticalFieldOfView", true) {
+                @Override
+                protected void invalidated() {
+                    impl_markDirty(DirtyBits.NODE_CAMERA);
+                }
+            };
         }
+        return verticalFieldOfView;
+    }
+
+    public PerspectiveCamera() {
+    }
+
+   /**
+    * Construct a PerspectiveCamera that may fix its eye position at (0, 0, 0),
+    * in its coordinate space, regardless in the change in the dimension
+    * of the projection area (or Window resize) if {@code fixedEyePosition} is true. 
+    *
+    * @since JavaFX 8
+    */
+    public PerspectiveCamera(boolean fixedEyePosition) {
+        this.fixedEyePosition = fixedEyePosition;
+    }
+
+    public final boolean isFixedEyePosition() {
+        return fixedEyePosition;
     }
 
     @Override Camera copy() {
-        PerspectiveCamera c = new PerspectiveCamera();
+        PerspectiveCamera c = new PerspectiveCamera(fixedEyePosition);
+        c.setNearClip(getNearClip());
+        c.setFarClip(getFarClip());
         c.setFieldOfView(getFieldOfView());
         return c;
     }
 
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected PGNode impl_createPGNode() {
+        PGPerspectiveCamera pgCamera = Toolkit.getToolkit().createPGPerspectiveCamera(fixedEyePosition);    
+        pgCamera.setNearClip((float) getNearClip());
+        pgCamera.setFarClip((float) getFarClip());
+        pgCamera.setFieldOfView((float) getFieldOfView());
+        return pgCamera;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public void impl_updatePG() {
+        super.impl_updatePG();
+//        System.err.println("XXXXXXXX PerspectiveCamera.impl_updatePG() XXXXXXXX");
+        PGPerspectiveCamera pgPerspectiveCamera = (PGPerspectiveCamera)impl_getPGNode();
+        if (impl_isDirty(DirtyBits.NODE_CAMERA)) {
+            pgPerspectiveCamera.setVerticalFieldOfView(isVerticalFieldOfView());
+            pgPerspectiveCamera.setFieldOfView((float) getFieldOfView());
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/PointLight.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,78 @@
+/*
+ * 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 javafx.scene;
+
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.sg.PGPointLight;
+import com.sun.javafx.tk.Toolkit;
+import javafx.scene.paint.Color;
+
+/**
+ * Defines a point light source object. An attenuated light source that has a
+ * fixed point in space and radiates light equally in all directions
+ * away from itself.
+ *
+ * @since JavaFX 8
+ */
+public class PointLight extends LightBase {
+    /**
+     * Creates a new instance of {@code PointLight} class with a default Color.WHITE light source.
+     */
+    public PointLight() {
+        super();
+    }
+
+    /**
+     * Creates a new instance of {@code PointLight} class using the specified color.
+     * 
+     * @param color the color of the light source
+     */
+    public PointLight(Color color) {
+        super(color);
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected PGNode impl_createPGNode() {
+        return Toolkit.getToolkit().createPGPointLight();
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public void impl_updatePG() {
+        super.impl_updatePG();
+        PGPointLight pgPointLight = (PGPointLight) impl_getPGNode();
+        pgPointLight.setColor((getColor() == null) ? null
+                : Toolkit.getPaintAccessor().getPlatformPaint(getColor()));
+    }
+}
--- a/javafx-ui-common/src/javafx/scene/Scene.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/Scene.java	Thu Feb 07 17:13:12 2013 -0800
@@ -39,6 +39,7 @@
 import java.util.Set;
 
 import com.sun.javafx.runtime.SystemProperties;
+import com.sun.javafx.scene.input.PickResultChooser;
 import javafx.animation.KeyFrame;
 import javafx.animation.Timeline;
 import javafx.beans.DefaultProperty;
@@ -58,6 +59,7 @@
 import javafx.event.EventTarget;
 import javafx.event.EventType;
 import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
 import javafx.scene.input.ContextMenuEvent;
 import javafx.scene.input.DragEvent;
 import javafx.scene.input.Dragboard;
@@ -72,6 +74,7 @@
 import javafx.scene.input.MouseButton;
 import javafx.scene.input.MouseDragEvent;
 import javafx.scene.input.MouseEvent;
+import javafx.scene.input.PickResult;
 import javafx.scene.input.RotateEvent;
 import javafx.scene.input.ScrollEvent;
 import javafx.scene.input.SwipeEvent;
@@ -95,6 +98,7 @@
 import com.sun.javafx.cursor.CursorFrame;
 import com.sun.javafx.event.EventQueue;
 import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.geom.Vec3d;
 import com.sun.javafx.geom.transform.BaseTransform;
 import sun.util.logging.PlatformLogger;
 import com.sun.javafx.perf.PerformanceTracker;
@@ -292,6 +296,41 @@
     public Scene(Parent root, @Default("-1") double width, @Default("-1") double height, boolean depthBuffer) {
         this(root, width, height, Color.WHITE, depthBuffer);
     }
+    
+    /**
+     * Constructs a scene consisting of a root, with a dimension of width and
+     * height, specifies whether a depth buffer is created for this scene and 
+     * specifies whether scene anti-aliasing is requested.
+     *
+     * @param root The root node of the scene graph
+     * @param width The width of the scene
+     * @param height The height of the scene
+     * @param depthBuffer The depth buffer flag
+     * @param antiAliasing The scene anti-aliasing flag
+     * <p>
+     * The depthBuffer and antiAliasing flags are conditional feature and the default 
+     * value for both are false. See
+     * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
+     * for more information.
+     *
+     * @throws IllegalStateException if this constructor is called on a thread
+     * other than the JavaFX Application Thread.
+     * @throws NullPointerException if root is null
+     *
+     * @see javafx.scene.Node#setDepthTest(DepthTest)
+     */
+    public Scene(Parent root, @Default("-1") double width, @Default("-1") double height,
+            boolean depthBuffer, boolean antiAliasing) {
+
+        // TODO: Support scene anti-aliasing using MSAA.
+        this(root, width, height, Color.WHITE, depthBuffer);
+
+        try {
+            throw new UnsupportedOperationException("Unsupported Scene constructor --- *** antiAliasing ***");
+        } catch (UnsupportedOperationException ex) {
+            ex.printStackTrace();
+        }
+    }
 
     private Scene(Parent root, double width, double height,
             @Default("javafx.scene.paint.Color.WHITE") Paint fill,
@@ -582,6 +621,14 @@
     }
 
     /**
+     * Return true if this {@code Scene} is anti-aliased otherwise false.
+     */
+    public boolean isAntiAliasing() {
+        //TODO: 3D - Implement this method.
+        return false; // For now
+    }
+
+    /**
      * The {@code Window} for this {@code Scene}
      */
     private ReadOnlyObjectWrapper<Window> window;
@@ -879,20 +926,12 @@
         return camera == null ? null : camera.get();
     }
 
-    private Camera oldCamera;
     public final ObjectProperty<Camera> cameraProperty() {
         if (camera == null) {
             camera = new ObjectPropertyBase<Camera>() {
 
                 @Override
                 protected void invalidated() {
-                    if (oldCamera != null) {
-                        oldCamera.dirtyProperty().removeListener(cameraChangeListener.getWeakListener());
-                    }
-                    oldCamera = get();
-                    if (get() != null) {
-                        get().dirtyProperty().addListener(cameraChangeListener.getWeakListener());
-                    }
                     markDirty(DirtyBits.CAMERA_DIRTY);
                 }
 
@@ -910,16 +949,6 @@
         return camera;
     }
 
-    private final AbstractNotifyListener cameraChangeListener =
-            new AbstractNotifyListener() {
-
-        @Override public void invalidated(Observable valueModel) {
-            if (getCamera().isDirty()) {
-                markDirty(DirtyBits.CAMERA_DIRTY);
-            }
-        }
-    };
-
     /**
      * Defines the background fill of this {@code Scene}. Both a {@code null}
      * value meaning paint no background and a {@link javafx.scene.paint.Paint}
@@ -1113,7 +1142,7 @@
         context.root = root.impl_getPGNode();
         context.platformPaint = fill == null ? null : tk.getPaint(fill);
         if (camera != null) {
-            camera.update();
+            camera.impl_updatePG();
             context.camera = camera.getPlatformCamera();
         } else {
             context.camera = null;
@@ -1542,8 +1571,8 @@
     }
 
     private void processMenuEvent(double x2, double y2, double xAbs, double yAbs, boolean isKeyboardTrigger) {
-        final EventTarget eventTarget;
-        if (!isKeyboardTrigger) Scene.inMousePick = true;
+        EventTarget eventTarget = null;
+        Scene.inMousePick = true;
         if (isKeyboardTrigger) {
             Node sceneFocusOwner = getFocusOwner();
 
@@ -1564,13 +1593,20 @@
 
             xAbs = x2 + xOffset;
             yAbs = y2 + yOffset;
-            
-        } else {
-            eventTarget = pick(x2, y2);
-        }
+        }
+
+        final PickResult res = pick(x2, y2);
+
+        if (!isKeyboardTrigger) {
+            eventTarget = res.getIntersectedNode();
+            if (eventTarget == null) {
+                eventTarget = this;
+            }
+        }
+
         if (eventTarget != null) {
             ContextMenuEvent context = new ContextMenuEvent(ContextMenuEvent.CONTEXT_MENU_REQUESTED,
-                    x2, y2, xAbs, yAbs, isKeyboardTrigger);
+                    x2, y2, xAbs, yAbs, isKeyboardTrigger, res);
             Event.fireEvent(eventTarget, context);
         }
         if (!isKeyboardTrigger) Scene.inMousePick = false;
@@ -1591,7 +1627,10 @@
         if (gesture.target != null && (!gesture.finished || e.isInertia())) {
             pickedTarget = gesture.target;
         } else {
-            pickedTarget = pick(e.getX(), e.getY());
+            pickedTarget = e.getPickResult().getIntersectedNode();
+            if (pickedTarget == null) {
+                pickedTarget = this;
+            }
         }
 
         if (e.getEventType() == ZoomEvent.ZOOM_STARTED ||
@@ -1626,7 +1665,10 @@
         for (TouchPoint tp : touchPoints) {
             EventTarget pickedTarget = touchTargets.get(tp.getId());
             if (pickedTarget == null) {
-                pickedTarget = pick(tp.getX(), tp.getY());
+                pickedTarget = tp.getPickResult().getIntersectedNode();
+                if (pickedTarget == null) {
+                    pickedTarget = this;
+                }
             } else {
                 tp.grab(pickedTarget);
             }
@@ -1696,45 +1738,64 @@
      */
     Node test_pick(double x, double y) {
         inMousePick = true;
-        Node pickedNode = mouseHandler.pickNode(x, y);
+        PickResult result = mouseHandler.pickNode(x, y);
         inMousePick = false;
-        return pickedNode;
-    }
-
-    private EventTarget pick(final double x, final double y) {
+        if (result != null) {
+            return result.getIntersectedNode();
+        }
+        return null;
+    }
+
+    private PickResult pick(final double x, final double y) {
         pick(tmpTargetWrapper, x, y);
-        return tmpTargetWrapper.getEventTarget();
+        return tmpTargetWrapper.getResult();
+    }
+
+    private boolean isInScene(double x, double y) {
+        if (x < 0 || y < 0 || x > getWidth() || y > getHeight())  {
+            return false;
+        }
+
+        Window w = getWindow();
+        if (w instanceof Stage
+                && ((Stage) w).getStyle() == StageStyle.TRANSPARENT
+                && getFill() == null) {
+            return false;
+        }
+
+        return true;
     }
 
     private void pick(TargetWrapper target, final double x, final double y) {
-        Node n = null;
-
         if (pickingCamera instanceof PerspectiveCamera) {
             final PickRay pickRay = new PickRay();
             Scene.this.impl_peer.computePickRay(
                     (float)x, (float)y, pickRay);
-            n = mouseHandler.pickNode(pickRay);
+            final double mag = pickRay.getDirectionNoClone().length();
+            pickRay.getDirectionNoClone().normalize();
+            final PickResult res = mouseHandler.pickNode(pickRay);
+            if (res != null) {
+                target.setNodeResult(res);
+            } else {
+                //TODO: is this the intersection with projection plane?
+                Vec3d o = pickRay.getOriginNoClone();
+                Vec3d d = pickRay.getDirectionNoClone();
+                target.setSceneResult(new PickResult(
+                        null, new Point3D(
+                        o.x + mag * d.x,
+                        o.y + mag * d.y,
+                        o.z + mag * d.z), mag),
+                        isInScene(x, y) ? this : null);
+            }
         } else {
-            n = mouseHandler.pickNode(x, y);
-        }
-
-        if (n != null) {
-            target.setNode(n);
-        } else if (
-                x >= 0 && y >= 0 &&
-                x <= getWidth() &&
-                y <= getHeight())  {
-
-            Window w = getWindow();
-            if (w instanceof Stage
-                    && ((Stage) w).getStyle() == StageStyle.TRANSPARENT
-                    && getFill() == null) {
-                target.clear();
+            final PickResult res = mouseHandler.pickNode(x, y);
+            if (res != null) {
+                target.setNodeResult(res);
+            } else {
+                target.setSceneResult(new PickResult(null, new Point3D(x, y, 0),
+                        Double.POSITIVE_INFINITY),
+                        isInScene(x, y) ? this : null);
             }
-
-            target.setScene(this);
-        } else {
-            target.clear();
         }
     }
 
@@ -2134,14 +2195,14 @@
 
             // new camera was set on the scene
             if (isDirty(DirtyBits.CAMERA_DIRTY)) {
-                if (getCamera() != null) {
-                    getCamera().update();
-                    impl_peer.setCamera(getCamera().getPlatformCamera());
-                    pickingCamera = getCamera();
-                } else {
-                    impl_peer.setCamera(null);
-                    pickingCamera = null;
-                }
+                Camera camera = getCamera();
+                pickingCamera = camera;
+                if (camera != null) {
+                    camera.impl_updatePG();
+                    impl_peer.setCamera(camera.getPlatformCamera());
+                 } else {
+                     impl_peer.setCamera(null);
+                 }
             }
 
             clearDirty();
@@ -2371,7 +2432,8 @@
                     _direct, _inertia,
                     scrollX * xMultiplier, scrollY * yMultiplier,
                     totalScrollX * xMultiplier, totalScrollY * yMultiplier,
-                    xUnits, xText, yUnits, yText, touchCount), scrollGesture);
+                    xUnits, xText, yUnits, yText, touchCount, pick(x, y)),
+                    scrollGesture);
         }
 
         @Override
@@ -2403,7 +2465,7 @@
                     x, y, screenX, screenY,
                     _shiftDown, _controlDown, _altDown, _metaDown, 
                     _direct, _inertia,
-                    zoomFactor, totalZoomFactor),
+                    zoomFactor, totalZoomFactor, pick(x, y)),
                     zoomGesture);
         }
 
@@ -2434,7 +2496,7 @@
             Scene.this.processGestureEvent(new RotateEvent(
                     eventType, x, y, screenX, screenY,
                     _shiftDown, _controlDown, _altDown, _metaDown, 
-                    _direct, _inertia, angle, totalAngle),
+                    _direct, _inertia, angle, totalAngle, pick(x, y)),
                     rotateGesture);
 
         }
@@ -2459,7 +2521,8 @@
 
             Scene.this.processGestureEvent(new SwipeEvent(
                     eventType, x, y, screenX, screenY,
-                    _shiftDown, _controlDown, _altDown, _metaDown, _direct, touchCount),
+                    _shiftDown, _controlDown, _altDown, _metaDown, _direct, 
+                    touchCount, pick(x, y)),
                     swipeGesture);
         }
 
@@ -2496,7 +2559,7 @@
             }
 
             touchPoints[order] = TouchPoint.impl_touchPoint(id, state,
-                    x, y, xAbs, yAbs);
+                    x, y, xAbs, yAbs, pick(x, y));
         }
 
         @Override
@@ -2548,7 +2611,8 @@
                 dndGesture = new DnDGesture();
             }
             DragEvent dragEvent =
-                    new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, transferMode, null, null);
+                    new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, 
+                            transferMode, null, null, pick(x, y));
             return dndGesture.processTargetEnterOver(dragEvent);
         }
 
@@ -2561,7 +2625,8 @@
                 return null;
             } else {
                 DragEvent dragEvent =
-                        new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, transferMode, null, null);
+                        new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, 
+                                transferMode, null, null, pick(x, y));
                 return dndGesture.processTargetEnterOver(dragEvent);
             }
         }
@@ -2572,7 +2637,8 @@
                 System.err.println("GOT A dragExit when dndGesture is null!");
             } else {
                 DragEvent dragEvent =
-                        new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, null, null, null);
+                        new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, 
+                                null, null, null, pick(x, y));
                 dndGesture.processTargetExit(dragEvent);
                 if (dndGesture.source == null) {
                     dndGesture = null;
@@ -2590,7 +2656,8 @@
                 return null;
             } else {
                 DragEvent dragEvent =
-                        new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, null, null, null);
+                        new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, 
+                                null, null, null, pick(x, y));
                 TransferMode tm = dndGesture.processTargetDrop(dragEvent);
                 if (dndGesture.source == null) {
                     dndGesture = null;
@@ -2608,9 +2675,9 @@
            dndGesture = new DnDGesture();
            dndGesture.dragboard = dragboard;
            // TODO: support mouse buttons in DragEvent
-           DragEvent dragEvent = new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, null, null, null);
-           final EventTarget pickedNode = pick(dragEvent.getX(), dragEvent.getY());
-           dndGesture.processRecognized(pickedNode, dragEvent);
+           DragEvent dragEvent = new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, 
+                   null, null, null, pick(x, y));
+           dndGesture.processRecognized(dragEvent);
            dndGesture = null;
         }
     }
@@ -2740,7 +2807,7 @@
          * the publicly visible drag and drop API, as it is responsible for calling
          * the Node.onDragSourceRecognized function.
          */
-        private boolean processRecognized(EventTarget target, DragEvent de) {
+        private boolean processRecognized(DragEvent de) {
             MouseEvent me = new MouseEvent(
                     MouseEvent.DRAG_DETECTED, de.getX(), de.getY(),
                     de.getSceneX(), de.getScreenY(), MouseButton.PRIMARY, 1,
@@ -2748,7 +2815,8 @@
 
             processingDragDetected();
 
-            fireEvent(target, me);
+            final EventTarget target = de.getPickResult().getIntersectedNode();
+            fireEvent(target != null ? target : Scene.this, me);
 
             dragDetectedProcessed();
 
@@ -2777,7 +2845,7 @@
         }
 
         private TransferMode processTargetEnterOver(DragEvent de) {
-            pick(tmpTargetWrapper, de.getX(), de.getY());
+            pick(tmpTargetWrapper, de.getSceneX(), de.getSceneY());
             final EventTarget pickedTarget = tmpTargetWrapper.getEventTarget();
 
             if (dragboard == null) {
@@ -2826,7 +2894,7 @@
         }
 
         private TransferMode processTargetDrop(DragEvent de) {
-            pick(tmpTargetWrapper, de.getX(), de.getY());
+            pick(tmpTargetWrapper, de.getSceneX(), de.getSceneY());
             final EventTarget pickedTarget = tmpTargetWrapper.getEventTarget();
 
             de = de.copyFor(de.getSource(), pickedTarget, source,
@@ -2913,7 +2981,8 @@
 
                 // cancel drag and drop
                 DragEvent de = new DragEvent(
-                        source, source, DragEvent.DRAG_DONE, dragboard, 0, 0, 0, 0, null, source, null);
+                        source, source, DragEvent.DRAG_DONE, dragboard, 0, 0, 0, 0, 
+                        null, source, null, null);
                 if (source != null) {
                     Event.fireEvent(source, de);
                 }
@@ -2983,7 +3052,8 @@
         {
             if (dndGesture != null) {
                 DragEvent dragEvent =
-                        new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, transferMode, null, null);
+                        new DragEvent(DragEvent.ANY, dragboard, x, y, screenX, screenY, 
+                        transferMode, null, null, null);
                 dndGesture.processDropEnd(dragEvent);
                 dndGesture = null;
             }
@@ -3098,12 +3168,12 @@
                 lastPress = cc;
             }
 
-            return new MouseEvent(e.getEventType(), e.getX(), e.getY(),
+            return new MouseEvent(e.getEventType(), e.getSceneX(), e.getSceneY(),
                     e.getScreenX(), e.getScreenY(), e.getButton(),
                     cc != null && e.getEventType() != MouseEvent.MOUSE_MOVED ? cc.get() : 0,
                     e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(),
                     e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(),
-                    e.isSynthesized(), e.isPopupTrigger(), still);
+                    e.isSynthesized(), e.isPopupTrigger(), still, e.getPickResult());
         }
 
         private void postProcess(MouseEvent e, TargetWrapper target, TargetWrapper pickedTarget) {
@@ -3130,7 +3200,7 @@
                             cc.get(),
                             e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(),
                             e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(),
-                            e.isSynthesized(), e.isPopupTrigger(), lastPress.isStill());
+                            e.isSynthesized(), e.isPopupTrigger(), lastPress.isStill(), e.getPickResult());
                     Event.fireEvent(clickedTarget, click);
                 }
             }
@@ -3212,7 +3282,7 @@
                 Event.fireEvent(entered, MouseEvent.copyForMouseDragEvent(e,
                         entered, entered,
                         MouseDragEvent.MOUSE_DRAG_EXITED_TARGET,
-                        fullPDRSource));
+                        fullPDRSource, e.getPickResult()));
             }
             fullPDRSource = null;
             fullPDRCurrentEventTargets.clear();
@@ -3305,9 +3375,17 @@
                 middleButtonDown = e.isMiddleButtonDown();
             }
 
-            if (e.getEventType() != MouseEvent.MOUSE_EXITED) {
-                pick(tmpTargetWrapper, e.getX(), e.getY());
-            } else {
+            pick(tmpTargetWrapper, e.getSceneX(), e.getSceneY());
+            PickResult res = tmpTargetWrapper.getResult();
+            if (res != null) {
+                e = new MouseEvent(e.getEventType(), e.getSceneX(), e.getSceneY(),
+                    e.getScreenX(), e.getScreenY(), e.getButton(), e.getClickCount(),
+                    e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(),
+                    e.isPrimaryButtonDown(), e.isMiddleButtonDown(), e.isSecondaryButtonDown(),
+                    e.isSynthesized(), e.isPopupTrigger(), e.isStillSincePress(), res);
+            }
+
+            if (e.getEventType() == MouseEvent.MOUSE_EXITED) {
                 tmpTargetWrapper.clear();
             }
 
@@ -3393,7 +3471,8 @@
 
         private void processFullPDR(MouseEvent e, boolean onPulse) {
 
-            pick(fullPDRTmpTargetWrapper, e.getX(), e.getY());
+            pick(fullPDRTmpTargetWrapper, e.getSceneX(), e.getSceneY());
+            final PickResult result = fullPDRTmpTargetWrapper.getResult();
 
             final EventTarget eventTarget = fullPDRTmpTargetWrapper.getEventTarget();
 
@@ -3417,7 +3496,7 @@
                     Event.fireEvent(exitedEventTarget, MouseEvent.copyForMouseDragEvent(e,
                             exitedEventTarget, exitedEventTarget,
                             MouseDragEvent.MOUSE_DRAG_EXITED_TARGET,
-                            fullPDRSource));
+                            fullPDRSource, result));
                 }
 
                 for (; j >= 0; j--) {
@@ -3425,7 +3504,7 @@
                     Event.fireEvent(enteredEventTarget, MouseEvent.copyForMouseDragEvent(e,
                             enteredEventTarget, enteredEventTarget,
                             MouseDragEvent.MOUSE_DRAG_ENTERED_TARGET,
-                            fullPDRSource));
+                            fullPDRSource, result));
                 }
 
                 fullPDRCurrentTarget = eventTarget;
@@ -3440,13 +3519,13 @@
                     Event.fireEvent(eventTarget, MouseEvent.copyForMouseDragEvent(e,
                             eventTarget, eventTarget,
                             MouseDragEvent.MOUSE_DRAG_OVER,
-                            fullPDRSource));
+                            fullPDRSource, result));
                 }
                 if (e.getEventType() == MouseEvent.MOUSE_RELEASED) {
                     Event.fireEvent(eventTarget, MouseEvent.copyForMouseDragEvent(e,
                             eventTarget, eventTarget,
                             MouseDragEvent.MOUSE_DRAG_RELEASED,
-                            fullPDRSource));
+                            fullPDRSource, result));
                 }
             }
         }
@@ -3479,12 +3558,16 @@
             }
         }
 
-        private Node pickNode(double x, double y) {
-            return Scene.this.getRoot().impl_pickNode(x, y);
-        }
-
-        private Node pickNode(PickRay pickRay) {
-            return Scene.this.getRoot().impl_pickNode(pickRay);
+        private PickResult pickNode(double x, double y) {
+            PickResultChooser r = new PickResultChooser();
+            Scene.this.getRoot().impl_pickNode(x, y, r);
+            return r.toPickResult();
+        }
+
+        private PickResult pickNode(PickRay pickRay) {
+            PickResultChooser r = new PickResultChooser();
+            Scene.this.getRoot().impl_pickNode(pickRay, r);
+            return r.toPickResult();
         }
     }
 
@@ -5510,6 +5593,7 @@
     private static class TargetWrapper {
         private Scene scene;
         private Node node;
+        private PickResult result;
 
         /**
          * Fills the list with the target and all its parents (including scene)
@@ -5546,19 +5630,33 @@
 
         public void clear() {
             set(null, null);
-        }
-
-        public void setScene(Scene scene) {
-            set(null, scene);
-        }
-
-        public void setNode(Node node) {
-            set(node, node.getScene());
+            result = null;
+        }
+
+        public void setNodeResult(PickResult result) {
+            if (result != null) {
+                this.result = result;
+                final Node n = result.getIntersectedNode();
+                set(n, n.getScene());
+            }
+        }
+
+        // Pass null scene if the mouse is outside of the window content
+        public void setSceneResult(PickResult result, Scene scene) {
+            if (result != null) {
+                this.result = result;
+                set(null, scene);
+            }
+        }
+
+        public PickResult getResult() {
+            return result;
         }
 
         public void copy(TargetWrapper tw) {
             node = tw.node;
             scene = tw.scene;
+            result = tw.result;
         }
 
         private void set(Node n, Scene s) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/SubScene.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,327 @@
+/*
+ * 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 javafx.scene;
+
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.jmx.MXNodeAlgorithm;
+import com.sun.javafx.jmx.MXNodeAlgorithmContext;
+import com.sun.javafx.sg.PGNode;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.DoublePropertyBase;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ObjectPropertyBase;
+import javafx.scene.paint.Color;
+import javafx.scene.paint.Paint;
+
+/**
+ * The {@code SubScene} class is the container for content in a scene graph.
+ * 
+ * @since JavaFX 8
+ */
+public class SubScene extends Node {
+
+    /**
+     * Creates a SubScene for a specific root Node with a specific size.
+     *
+     * @param root The root node of the scene graph
+     * @param width The width of the scene
+     * @param height The height of the scene
+     *
+     * @throws IllegalStateException if this constructor is called on a thread
+     * other than the JavaFX Application Thread.
+     * @throws NullPointerException if root is null
+     */
+    public SubScene(Parent root, double width, double height) {
+        throw new UnsupportedOperationException("*** 3D Work is still in progress ... ***");
+    }
+
+    /**
+     * Constructs a SubScene consisting of a root, with a dimension of width and
+     * height, specifies whether a depth buffer is created for this scene and 
+     * specifies whether scene anti-aliasing is requested.
+     *
+     * @param root The root node of the scene graph
+     * @param width The width of the scene
+     * @param height The height of the scene
+     * @param depthBuffer The depth buffer flag
+     * @param antiAliasing The sub-scene anti-aliasing flag
+     * <p>
+     * The depthBuffer and antiAliasing flags are conditional feature and the default 
+     * value for both are false. See
+     * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
+     * for more information.
+     *
+     * @throws IllegalStateException if this constructor is called on a thread
+     * other than the JavaFX Application Thread.
+     * @throws NullPointerException if root is null
+     *
+     * @see javafx.scene.Node#setDepthTest(DepthTest)
+     */
+    public SubScene(Parent root, double width, double height,
+            boolean depthBuffer, boolean antiAliasing) {
+        throw new UnsupportedOperationException("*** 3D Work is still in progress ... ***");
+    }
+
+    /**
+     * Return true if this {@code SubScene} is anti-aliased otherwise false.
+     */
+    public boolean isAntiAliasing() {
+        throw new UnsupportedOperationException("Unsupported --- *** isAntiAliasing method ***");        
+    }
+
+    /**
+     * Defines the root {@code Node} of the SubScene scene graph.
+     * If a {@code Group} is used as the root, the
+     * contents of the scene graph will be clipped by the SubScene's width and height.
+     * 
+     * SubScene doesn't accept null root.
+     * 
+     */
+    private ObjectProperty<Parent> root;
+
+    public final void setRoot(Parent value) {
+        rootProperty().set(value);
+    }
+
+    public final Parent getRoot() {
+        return root == null ? null : root.get();
+    }
+
+    public final ObjectProperty<Parent> rootProperty() {
+        if (root == null) {
+            root = new ObjectPropertyBase<Parent>() {
+
+                @Override
+                protected void invalidated() {
+                }
+
+                @Override
+                public Object getBean() {
+                    return SubScene.this;
+                }
+
+                @Override
+                public String getName() {
+                    return "root";
+                }
+            };
+        }
+        return root;
+    }
+
+    /**
+     * Specifies the type of camera use for rendering this {@code SubScene}.
+     * If {@code camera} is null, a parallel camera is used for rendering.
+     * <p>
+     * Note: this is a conditional feature. See
+     * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D}
+     * for more information.
+     *
+     * @defaultValue null
+     */
+    private ObjectProperty<Camera> camera;
+
+    public final void setCamera(Camera value) {
+        cameraProperty().set(value);
+    }
+
+    public final Camera getCamera() {
+        return camera == null ? null : camera.get();
+    }
+
+    public final ObjectProperty<Camera> cameraProperty() {
+        if (camera == null) {
+            camera = new ObjectPropertyBase<Camera>() {
+
+                @Override
+                protected void invalidated() {
+                }
+
+                @Override
+                public Object getBean() {
+                    return SubScene.this;
+                }
+
+                @Override
+                public String getName() {
+                    return "camera";
+                }
+            };
+        }
+        return camera;
+    }
+
+    /**
+     * Defines the width of this {@code SubScene}
+     * 
+     * @defaultvalue 0.0
+     */
+    private DoubleProperty width;
+
+    public final void setWidth(double value) {
+        widthProperty().set(value);
+    }
+
+    public final double getWidth() {
+        return width == null ? 0.0 : width.get();
+    }
+
+    public final DoubleProperty widthProperty() {
+        if (width == null) {
+            width = new DoublePropertyBase() {
+
+                @Override
+                public void invalidated() {
+                }
+
+                @Override
+                public Object getBean() {
+                    return SubScene.this;
+                }
+
+                @Override
+                public String getName() {
+                    return "width";
+                }
+            };
+        }
+        return width;
+    }   
+    
+    /**
+     * Defines the height of this {@code SubScene}
+     *
+     * @defaultvalue 0.0
+     */
+    private DoubleProperty height;
+
+    public final void setHeight(double value) {
+        heightProperty().set(value);
+    }
+
+    public final double getHeight() {
+        return height == null ? 0.0 : height.get();
+    }
+
+    public final DoubleProperty heightProperty() {
+        if (height == null) {
+            height = new DoublePropertyBase() {
+
+                @Override
+                public void invalidated() {
+                }
+
+                @Override
+                public Object getBean() {
+                    return SubScene.this;
+                }
+
+                @Override
+                public String getName() {
+                    return "height";
+                }
+            };
+        }
+        return height;
+    }
+
+    /**
+     * Defines the background fill of this {@code SubScene}. Both a {@code null}
+     * value meaning paint no background and a {@link javafx.scene.paint.Paint}
+     * with transparency are supported, but what is painted behind it will
+     * depend on the platform.  The default value is COLOR.TRANSPARENT.
+     *
+     * @defaultValue WHITE
+     */
+    private ObjectProperty<Paint> fill;
+
+    public final void setFill(Paint value) {
+        fillProperty().set(value);
+    }
+
+    public final Paint getFill() {
+        return fill == null ? Color.TRANSPARENT : fill.get();
+    }
+
+    public final ObjectProperty<Paint> fillProperty() {
+        if (fill == null) {
+            fill = new ObjectPropertyBase<Paint>(Color.TRANSPARENT) {
+
+                @Override
+                protected void invalidated() {
+                }
+
+                @Override
+                public Object getBean() {
+                    return SubScene.this;
+                }
+
+                @Override
+                public String getName() {
+                    return "fill";
+                }
+            };
+        }
+        return fill;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated    @Override
+    protected PGNode impl_createPGNode() {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated    @Override
+    public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated    @Override
+    protected boolean impl_computeContains(double localX, double localY) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated    @Override
+    public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+    
+}
--- a/javafx-ui-common/src/javafx/scene/input/ContextMenuEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/ContextMenuEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -30,7 +30,7 @@
 import java.io.IOException;
 import javafx.event.EventTarget;
 import javafx.event.EventType;
-import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
 import javafx.scene.Node;
 
 // PENDING_DOC_REVIEW
@@ -61,43 +61,50 @@
      * @param source the source of the event. Can be null.
      * @param target the target of the event. Can be null.
      * @param eventType The type of the event.
-     * @param x The x with respect to the source. Should be in scene coordinates if source == null or source is not a Node.
-     * @param y The y with respect to the source. Should be in scene coordinates if source == null or source is not a Node.
+     * @param x The x with respect to the scene
+     * @param y The y with respect to the scene
      * @param screenX The x coordinate relative to screen.
      * @param screenY The y coordinate relative to screen.
      * @param keyboardTrigger true if this event was triggered by keyboard.
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and the target
      */
     public ContextMenuEvent(Object source, EventTarget target, EventType<ContextMenuEvent> eventType, double x, double y,
-            double screenX, double screenY, boolean keyboardTrigger) {
+            double screenX, double screenY, boolean keyboardTrigger,
+            PickResult pickResult) {
         super(source, target, eventType);
+        this.screenX = screenX;
+        this.screenY = screenY;
+        this.sceneX = x;
+        this.sceneY = y;
         this.x = x;
         this.y = y;
-        if (source != null && source instanceof Node) {
-            Node sourceNode = (Node) source;
-            Point2D localToScene = sourceNode.localToScene(x, y);
-            this.sceneX = localToScene.getX();
-            this.sceneY = localToScene.getY();
-        } else {
-            this.sceneX = x;
-            this.sceneY = y;
-        }
-        this.screenX = screenX;
-        this.screenY = screenY;
+        this.pickResult = pickResult != null ? pickResult : new PickResult(target, x, y);
+        final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
+        this.x = p.getX();
+        this.y = p.getY();
+        this.z = p.getZ();
         this.keyboardTrigger = keyboardTrigger;
      }
 
     /**
      * Constructs new ContextMenu event with empty source and target.
      * @param eventType The type of the event.
-     * @param x The x with respect to the screen.
-     * @param y The y with respect to the screen.
+     * @param x The x with respect to the scene.
+     * @param y The y with respect to the scene.
      * @param screenX The x coordinate relative to screen.
      * @param screenY The y coordinate relative to screen.
      * @param keyboardTrigger true if this event was triggered by keyboard.
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      */
     public ContextMenuEvent(EventType<ContextMenuEvent> eventType, double x, double y,
-            double screenX, double screenY, boolean keyboardTrigger) {
-        this(null, null, eventType, x, y, screenX, screenY, keyboardTrigger);
+            double screenX, double screenY, boolean keyboardTrigger,
+            PickResult pickResult) {
+        this(null, null, eventType, x, y, screenX, screenY, keyboardTrigger,
+                pickResult);
     }
 
     /**
@@ -108,11 +115,12 @@
      */
     private void recomputeCoordinatesToSource(ContextMenuEvent newEvent, Object newSource) {
 
-        final Point2D newCoordinates = InputEventUtils.recomputeCoordinates(
-                new Point2D(sceneX, sceneY), null, newSource);
+        final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
+                pickResult, newSource);
 
         newEvent.x = newCoordinates.getX();
         newEvent.y = newCoordinates.getY();
+        newEvent.z = newCoordinates.getZ();
     }
 
     @Override
@@ -180,6 +188,23 @@
     }
 
     /**
+     * Depth z position of the event relative to the
+     * origin of the MouseEvent's node.
+     */
+    private transient double z;
+
+    /**
+     * Depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     *
+     * @return depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     */
+    public final double getZ() {
+        return z;
+    }
+
+    /**
      * Absolute horizontal x position of the event.
      */
     private final double screenX;
@@ -224,6 +249,8 @@
      * the boundsInParent of the root-most parent of the ContextMenuEvent's node.
      * For more information about this event's coordinate semantics please see
      * the general description of {@link ContextMenuEvent}.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      *
      * @return horizontal position of the event relative to the
      * origin of the {@code Scene} that contains the ContextMenuEvent's source
@@ -247,6 +274,8 @@
      * the boundsInParent of the root-most parent of the ContextMenuEvent's node.
      * For more information about this event's coordinate semantics please see
      * the general description of {@link ContextMenuEvent}.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      *
      * @return vertical position of the event relative to the
      * origin of the {@code Scene} that contains the ContextMenuEvent's source
@@ -256,6 +285,21 @@
     }
 
     /**
+     * Information about the pick if the picked {@code Node} is a
+     * {@code Shape3D} node and its pickOnBounds is false.
+     */
+    private PickResult pickResult;
+
+    /**
+     * Returns information about the pick.
+     *
+     * @return new PickResult object that contains information about the pick
+     */
+    public final PickResult getPickResult() {
+        return pickResult;
+    }
+
+    /**
      * Returns a string representation of this {@code ContextMenuEvent} object.
      * @return a string representation of this {@code ContextMenuEvent} object.
      */
@@ -267,7 +311,9 @@
         sb.append(", eventType = ").append(getEventType());
         sb.append(", consumed = ").append(isConsumed());
 
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
--- a/javafx-ui-common/src/javafx/scene/input/DragEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/DragEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -30,7 +30,7 @@
 import javafx.event.Event;
 import javafx.event.EventTarget;
 import javafx.event.EventType;
-import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
 
 import com.sun.javafx.scene.input.InputEventUtils;
 import java.io.IOException;
@@ -372,11 +372,14 @@
      * @param transferMode the transfer mode of the event.
      * @param gestureSource the source of the DnD gesture of the event.
      * @param gestureTarget the target of the DnD gesture of the event.
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and the target
      */
     public DragEvent(Object source, EventTarget target, EventType<DragEvent> eventType, Dragboard dragboard,
             double x, double y,
             double screenX, double screenY, TransferMode transferMode,
-            Object gestureSource, Object gestureTarget) {
+            Object gestureSource, Object gestureTarget, PickResult pickResult) {
         super(source, target, eventType);
         this.gestureSource = gestureSource;
         this.gestureTarget = gestureTarget;
@@ -388,6 +391,12 @@
         this.sceneY = y;
         this.transferMode = transferMode;
         this.dragboard = dragboard;
+        this.pickResult = pickResult != null ? pickResult : new PickResult(
+                eventType == DRAG_DONE ? null : target, x, y);
+        final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
+        this.x = p.getX();
+        this.y = p.getY();
+        this.z = p.getZ();
     }
 
     /**
@@ -401,13 +410,16 @@
      * @param transferMode the transfer mode of the event.
      * @param gestureSource the source of the DnD gesture of the event.
      * @param gestureTarget the target of the DnD gesture of the event.
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      */
     public DragEvent(EventType<DragEvent> eventType, Dragboard dragboard,
             double x, double y,
             double screenX, double screenY, TransferMode transferMode,
-            Object gestureSource, Object gestureTarget) {
+            Object gestureSource, Object gestureTarget, PickResult pickResult) {
         this(null, null, eventType, dragboard, x, y, screenX, screenY, transferMode,
-                gestureSource, gestureTarget);
+                gestureSource, gestureTarget, pickResult);
     }
 
     /**
@@ -423,11 +435,12 @@
             return;
         }
 
-        final Point2D newCoordinates = InputEventUtils.recomputeCoordinates(
-                new Point2D(sceneX, sceneY), null, newSource);
+        final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
+                pickResult, newSource);
 
         newEvent.x = newCoordinates.getX();
         newEvent.y = newCoordinates.getY();
+        newEvent.z = newCoordinates.getZ();
     }
     
     @Override
@@ -490,6 +503,23 @@
     }
 
     /**
+     * Depth z position of the event relative to the
+     * origin of the MouseEvent's node.
+     */
+    private transient double z;
+
+    /**
+     * Depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     *
+     * @return depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     */
+    public final double getZ() {
+        return z;
+    }
+
+    /**
      * Absolute horizontal x position of the event.
      */
     private final double screenX;
@@ -528,6 +558,8 @@
      * origin of the {@code Scene} that contains the DragEvent's source.
      * If the node is not in a {@code Scene}, then the value is relative to
      * the boundsInParent of the root-most parent of the DragEvent's node.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      * 
      * @return horizontal position of the event relative to the
      * origin of the {@code Scene} that contains the DragEvent's source
@@ -549,6 +581,8 @@
      * origin of the {@code Scene} that contains the DragEvent's source.
      * If the node is not in a {@code Scene}, then the value is relative to
      * the boundsInParent of the root-most parent of the DragEvent's node.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      * 
      * @return vertical position of the event relative to the
      * origin of the {@code Scene} that contains the DragEvent's source
@@ -558,6 +592,21 @@
     }
 
     /**
+     * Information about the pick if the picked {@code Node} is a
+     * {@code Shape3D} node and its pickOnBounds is false.
+     */
+    private PickResult pickResult;
+
+    /**
+     * Returns information about the pick.
+     *
+     * @return new PickResult object that contains information about the pick
+     */
+    public final PickResult getPickResult() {
+        return pickResult;
+    }
+
+    /**
      * The source object of the drag and drop gesture.
      * Gesture source is the object that started drag and drop operation.
      * The value {@code null} is valid in the case that the gesture comes
--- a/javafx-ui-common/src/javafx/scene/input/GestureEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/GestureEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -31,7 +31,7 @@
 import javafx.event.Event;
 import javafx.event.EventTarget;
 import javafx.event.EventType;
-import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
 
 /**
  * An event indicating gesture input. Gestures are typically caused by
@@ -61,7 +61,7 @@
      */
     @Deprecated
     protected GestureEvent(final EventType<? extends GestureEvent> eventType) {
-        this(eventType, 0, 0, 0, 0, false, false, false, false, false, false);
+        this(eventType, 0, 0, 0, 0, false, false, false, false, false, false, null);
     }
 
     /**
@@ -94,11 +94,14 @@
      * @param metaDown true if meta modifier was pressed.
      * @param direct true if the event was caused by direct input device. See {@link #isDirect() }
      * @param inertia if represents inertia of an already finished gesture.
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and the target
      */
     protected GestureEvent(Object source, EventTarget target, final EventType<? extends GestureEvent> eventType,
             double x, double y, double screenX, double screenY,
             boolean shiftDown, boolean controlDown, boolean altDown,
-            boolean metaDown, boolean direct, boolean inertia) {
+            boolean metaDown, boolean direct, boolean inertia, PickResult pickResult) {
         super(source, target, eventType);
         this.x = x;
         this.y = y;
@@ -112,6 +115,11 @@
         this.metaDown = metaDown;
         this.direct = direct;
         this.inertia = inertia;
+        this.pickResult = pickResult != null ? pickResult : new PickResult(target, x, y);
+        final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
+        this.x = p.getX();
+        this.y = p.getY();
+        this.z = p.getZ();
     }
 
     /**
@@ -127,12 +135,17 @@
      * @param metaDown true if meta modifier was pressed.
      * @param direct true if the event was caused by direct input device. See {@link #isDirect() }
      * @param inertia if represents inertia of an already finished gesture.
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      */
     protected GestureEvent(final EventType<? extends GestureEvent> eventType,
             double x, double y, double screenX, double screenY,
             boolean shiftDown, boolean controlDown, boolean altDown,
-            boolean metaDown, boolean direct, boolean inertia) {
-        this(null, null, eventType, x, y, screenX, screenY, shiftDown, controlDown, altDown, metaDown, direct, inertia);
+            boolean metaDown, boolean direct, boolean inertia,
+            PickResult pickResult) {
+        this(null, null, eventType, x, y, screenX, screenY, shiftDown, controlDown,
+                altDown, metaDown, direct, inertia, pickResult);
     }
 
     /**
@@ -143,11 +156,12 @@
      */
     private void recomputeCoordinatesToSource(GestureEvent newEvent, Object newSource) {
 
-        final Point2D newCoordinates = InputEventUtils.recomputeCoordinates(
-                new Point2D(sceneX, sceneY), null, newSource);
+        final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
+                pickResult, newSource);
 
         newEvent.x = newCoordinates.getX();
         newEvent.y = newCoordinates.getY();
+        newEvent.z = newCoordinates.getZ();
     }
 
     /**
@@ -190,6 +204,23 @@
         return y;
     }
 
+    /**
+     * Depth z position of the event relative to the
+     * origin of the MouseEvent's node.
+     */
+    private transient double z;
+
+    /**
+     * Depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     *
+     * @return depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     */
+    public final double getZ() {
+        return z;
+    }
+
     private final double screenX;
 
     /**
@@ -221,6 +252,8 @@
      * origin of the {@code Scene} that contains the event's source.
      * If the node is not in a {@code Scene}, then the value is relative to
      * the boundsInParent of the root-most parent of the event's node.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      *
      * @return the horizontal position of the event relative to the
      * origin of the {@code Scene} that contains the event's source
@@ -238,6 +271,8 @@
      * origin of the {@code Scene} that contains the event's source.
      * If the node is not in a {@code Scene}, then the value is relative to
      * the boundsInParent of the root-most parent of the event's node.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      *
      * @return the vertical position of the event relative to the
      * origin of the {@code Scene} that contains the event's source
@@ -315,6 +350,21 @@
     }
 
     /**
+     * Information about the pick if the picked {@code Node} is a
+     * {@code Shape3D} node and its pickOnBounds is false.
+     */
+    private PickResult pickResult;
+
+    /**
+     * Returns information about the pick.
+     *
+     * @return new PickResult object that contains information about the pick
+     */
+    public final PickResult getPickResult() {
+        return pickResult;
+    }
+
+    /**
      * Indicates whether or not the host platform common shortcut modifier is
      * down on this event. This common shortcut modifier is a modifier key which
      * is used commonly in shortcuts on the host platform. It is for example
@@ -354,7 +404,8 @@
         sb.append(", eventType = ").append(getEventType());
         sb.append(", consumed = ").append(isConsumed());
 
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
         sb.append(isDirect() ? ", direct" : ", indirect");
 
         if (isInertia()) {
@@ -376,6 +427,7 @@
         if (isShortcutDown()) {
             sb.append(", shortcutDown");
         }
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
--- a/javafx-ui-common/src/javafx/scene/input/KeyEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/KeyEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -166,7 +166,7 @@
                         shiftDown, controlDown, altDown, metaDown, false, false,
                         scrollX, scrollY, 0, 0,
                         xTextUnits, xText, yTextUnits, yText,
-                        0);
+                        0, null);
             }
         };
         FXRobotHelper.setInputAccessor(a);
--- a/javafx-ui-common/src/javafx/scene/input/MouseDragEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/MouseDragEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -145,6 +145,9 @@
      * @param secondaryButtonDown true if secondary button was pressed.
      * @param synthesized if this event was synthesized
      * @param popupTrigger whether this event denotes a popup trigger for current platform
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and target
      * @param gestureSource source object of the ongoing gesture.
      */
     public MouseDragEvent(Object source, EventTarget target, EventType<MouseDragEvent> eventType,
@@ -152,12 +155,12 @@
             MouseButton button, int clickCount,
             boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown,
             boolean primaryButtonDown, boolean middleButtonDown, boolean secondaryButtonDown,
-            boolean synthesized, boolean popupTrigger,
+            boolean synthesized, boolean popupTrigger, PickResult pickResult,
             Object gestureSource) {
         super(source, target, eventType, x, y, screenX, screenY, button,
                 clickCount, shiftDown, controlDown, altDown, metaDown,
                 primaryButtonDown, middleButtonDown, secondaryButtonDown,
-                synthesized, popupTrigger, false);
+                synthesized, popupTrigger, false, pickResult);
         this.gestureSource = gestureSource;
     }
 
@@ -180,6 +183,9 @@
      * @param secondaryButtonDown true if secondary button was pressed.
      * @param synthesized if this event was synthesized
      * @param popupTrigger whether this event denotes a popup trigger for current platform
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      * @param gestureSource source object of the ongoing gesture.
      */
     public MouseDragEvent(EventType<MouseDragEvent> eventType,
@@ -187,11 +193,12 @@
             MouseButton button, int clickCount,
             boolean shiftDown, boolean controlDown, boolean altDown, boolean metaDown,
             boolean primaryButtonDown, boolean middleButtonDown, boolean secondaryButtonDown,
-            boolean synthesized, boolean popupTrigger,
+            boolean synthesized, boolean popupTrigger, PickResult pickResult,
             Object gestureSource) {
         this(null, null, eventType, x, y, screenX, screenY, button, clickCount,
                 shiftDown, controlDown, altDown, metaDown, primaryButtonDown,
-                middleButtonDown, secondaryButtonDown, synthesized, popupTrigger, gestureSource);
+                middleButtonDown, secondaryButtonDown, synthesized, popupTrigger, 
+                pickResult, gestureSource);
      }
 
    
@@ -220,7 +227,8 @@
         sb.append(", eventType = ").append(getEventType());
         sb.append(", consumed = ").append(isConsumed());
 
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
 
         if (getButton() != null) {
             sb.append(", button = ").append(getButton());
@@ -255,6 +263,7 @@
         if (isSynthesized()) {
             sb.append(", synthesized");
         }
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
--- a/javafx-ui-common/src/javafx/scene/input/MouseEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/MouseEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -30,7 +30,7 @@
 import javafx.event.Event;
 import javafx.event.EventTarget;
 import javafx.event.EventType;
-import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
 import javafx.scene.Node;
 
 import com.sun.javafx.scene.input.InputEventUtils;
@@ -252,11 +252,12 @@
      */
     void recomputeCoordinatesToSource(MouseEvent oldEvent, Object newSource) {
 
-        final Point2D newCoordinates = InputEventUtils.recomputeCoordinates(
-                new Point2D(oldEvent.x, oldEvent.y), oldEvent.source, newSource);
+        final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
+                pickResult, newSource);
 
         x = newCoordinates.getX();
         y = newCoordinates.getY();
+        z = newCoordinates.getZ();
     }
 
     @Override
@@ -358,7 +359,7 @@
         this(eventType, x, y, screenX, screenY, button, clickCount,
                 shiftDown, controlDown, altDown, metaDown,
                 primaryButtonDown, middleButtonDown, secondaryButtonDown,
-                synthesized, popupTrigger, false);
+                synthesized, popupTrigger, false, null);
     }
 
     /**
@@ -380,6 +381,7 @@
      * @param synthesized if this event was synthesized
      * @param popupTrigger whether this event denotes a popup trigger for current platform
      * @param stillSincePress see {@link #isStillSincePress() }
+     * @param pickResult pick result
      */
     public MouseEvent(
             EventType<? extends MouseEvent> eventType,
@@ -396,11 +398,12 @@
             boolean secondaryButtonDown,
             boolean synthesized,
             boolean popupTrigger,
-            boolean stillSincePress) {
+            boolean stillSincePress,
+            PickResult pickResult) {
         this(null, null, eventType, x, y, screenX, screenY, button, clickCount,
                 shiftDown, controlDown, altDown, metaDown,
                 primaryButtonDown, middleButtonDown, secondaryButtonDown,
-                synthesized, popupTrigger, stillSincePress);
+                synthesized, popupTrigger, stillSincePress, pickResult);
     }
 
     /**
@@ -424,6 +427,9 @@
      * @param synthesized if this event was synthesized
      * @param popupTrigger whether this event denotes a popup trigger for current platform
      * @param stillSincePress see {@link #isStillSincePress() }
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and target
      */
     public MouseEvent(Object source, EventTarget target,
             EventType<? extends MouseEvent> eventType,
@@ -440,7 +446,8 @@
             boolean secondaryButtonDown,
             boolean synthesized,
             boolean popupTrigger,
-            boolean stillSincePress) {
+            boolean stillSincePress,
+            PickResult pickResult) {
         super(source, target, eventType);
         this.x = x;
         this.y = y;
@@ -460,6 +467,12 @@
         this.synthesized = synthesized;
         this.stillSincePress = stillSincePress;
         this.popupTrigger = popupTrigger;
+        this.pickResult = pickResult;
+        this.pickResult = pickResult != null ? pickResult : new PickResult(target, x, y);
+        final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
+        this.x = p.getX();
+        this.y = p.getY();
+        this.z = p.getZ();
     }
 
     /**
@@ -469,18 +482,22 @@
      * @param target the new target of the copied event
      * @param type the new MouseDragEvent type
      * @param gestureSource the new source of the gesture
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      * @return new MouseDragEvent that was created from MouseEvent
      */
     public static MouseDragEvent copyForMouseDragEvent(
             MouseEvent e,
             Object source, EventTarget target,
             EventType<MouseDragEvent> type,
-            Object gestureSource) {
+            Object gestureSource, PickResult pickResult) {
         MouseDragEvent ev = new MouseDragEvent(source, target,
-                type, e.x, e.y, e.screenX, e.screenY,
+                type, e.sceneX, e.sceneY, e.screenX, e.screenY,
                 e.button, e.clickCount, e.shiftDown, e.controlDown,
                 e.altDown, e.metaDown, e.primaryButtonDown, e.middleButtonDown,
-                e.secondaryButtonDown, e.synthesized, e.popupTrigger, gestureSource);
+                e.secondaryButtonDown, e.synthesized, e.popupTrigger,
+                pickResult, gestureSource);
         ev.recomputeCoordinatesToSource(e, source);
         return ev;
     }
@@ -543,6 +560,23 @@
     }
 
     /**
+     * Depth z position of the event relative to the
+     * origin of the MouseEvent's node.
+     */
+    private transient double z;
+
+    /**
+     * Depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     *
+     * @return depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     */
+    public final double getZ() {
+        return z;
+    }
+
+    /**
      * Absolute horizontal x position of the event.
      */
     private final double screenX;
@@ -581,6 +615,8 @@
      * origin of the {@code Scene} that contains the MouseEvent's source.
      * If the node is not in a {@code Scene}, then the value is relative to
      * the boundsInParent of the root-most parent of the MouseEvent's node.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      * 
      * @return horizontal position of the event relative to the
      * origin of the {@code Scene} that contains the MouseEvent's source
@@ -602,6 +638,8 @@
      * origin of the {@code Scene} that contains the MouseEvent's source.
      * If the node is not in a {@code Scene}, then the value is relative to
      * the boundsInParent of the root-most parent of the MouseEvent's node.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      * 
      * @return vertical position of the event relative to the
      * origin of the {@code Scene} that contains the MouseEvent's source
@@ -869,7 +907,8 @@
         sb.append(", eventType = ").append(getEventType());
         sb.append(", consumed = ").append(isConsumed());
 
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
 
         if (getButton() != null) {
             sb.append(", button = ").append(getButton());
@@ -904,11 +943,27 @@
         if (isSynthesized()) {
             sb.append(", synthesized");
         }
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
 
     /**
+     * Information about the pick if the picked {@code Node} is a
+     * {@code Shape3D} node and its pickOnBounds is false.
+     */
+    private PickResult pickResult;
+
+    /**
+     * Returns information about the pick.
+     * 
+     * @return new PickResult object that contains information about the pick
+     */
+    public final PickResult getPickResult() {
+        return pickResult;
+    }
+    
+    /**
      * These properties need to live in a separate object shared among all the
      * copied events to make sure that the values are propagated to the
      * original event.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/input/PickResult.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,165 @@
+/*
+ * 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 javafx.scene.input;
+
+import javafx.event.EventTarget;
+import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
+import javafx.scene.Node;
+
+/**
+ * A container object that contains the result of a pick event
+ */
+public class PickResult {
+
+    /**
+     * An undefined face. This value is used for the intersected face
+     * if the picked node has no user-specified faces.
+     */
+    public static final int FACE_UNDEFINED = -1;
+
+    private Node node;
+    private Point3D point;
+    private double distance = Double.POSITIVE_INFINITY;
+    private int face = -1;
+    private Point2D texCoord;
+
+    /**
+     * Creates a new instance of PickResult.
+     * @param node The intersected node
+     * @param point The intersected point in local coordinate of the picked Node
+     * @param distance The intersected distance between camera position and the picked Node
+     * @param face The intersected face of the picked Node
+     * @param texCoord The intersected texture coordinates of the picked Node
+     */
+    public PickResult(Node node, Point3D point, double distance, int face, Point2D texCoord) {
+        this.node = node;
+        this.point = point;
+        this.distance = distance;
+        this.face = face;
+        this.texCoord = texCoord;
+    }
+
+    /**
+     * Creates a new instance of PickResult for a non-3d-shape target.
+     * Sets face to FACE_UNDEFINED and texCoord to null.
+     * @param node The intersected node
+     * @param point The intersected point in local coordinate of the picked Node
+     * @param distance The intersected distance between camera position and the picked Node
+     */
+    public PickResult(Node node, Point3D point, double distance) {
+        this.node = node;
+        this.point = point;
+        this.distance = distance;
+        this.face = FACE_UNDEFINED;
+        this.texCoord = null;
+    }
+
+    /**
+     * Creates a pick result for a 2D case where no additional information is needed.
+     * Converts the given scene coordinates to the target's local coordinate space
+     * and stores the value as the intersected point. Sets intersected node
+     * to the given target, distance to POSITIVE_INFINITY,
+     * face to FACE_UNDEFINED and texCoord to null.
+     * @param target The picked target (null in case of a Scene)
+     * @param sceneX The scene X coordinate
+     * @param sceneY The scene Y coordinate
+     */
+    public PickResult(EventTarget target, double sceneX, double sceneY) {
+        this(target instanceof Node ? (Node) target : null,
+                target instanceof Node ? ((Node) target).sceneToLocal(sceneX, sceneY, 0) : new Point3D(sceneX, sceneY, 0),
+                Double.POSITIVE_INFINITY);
+    }
+
+    /**
+     * Returns the intersected node.
+     * Returns null if there was no intersection with any node and the scene
+     * was picked.
+     *
+     * @return the picked node or null if no node was picked
+     */
+    public final Node getIntersectedNode() {
+        return node;
+    }
+
+    /**
+     * Returns the intersected point in local coordinate of the picked Node.
+     * If no node was picked, it returns the intersected point with the
+     * projection plane.
+     *
+     * @return new Point3D presenting the intersected point
+     */
+    public final Point3D getIntersectedPoint() {
+        return point;
+    }
+
+    /**
+     * Returns the intersected distance between camera position 
+     * and the intersected point. Returns POSITIVE_INFINITY in case of
+     * parallel camera.
+     *
+     * @return the distance from camera to the intersection
+     */
+    public final double getIntersectedDistance() {
+        return distance;
+    }
+
+    /**
+     * Returns the intersected face of the picked Node, FACE_UNDEFINED
+     *         if the node doesn't have user-specified faces
+     *         or was picked on bounds.
+     * 
+     * @return the picked face
+     */
+    public final int getIntersectedFace() {
+        return face;
+     }
+
+    /**
+     * Return the intersected texture coordinates of the picked 3d shape.
+     * If the picked target is not Shape3D or has pickOnBounds==true,
+     * it returns null.
+     *
+     * return new Point2D presenting the intersected TexCoord
+     */
+    public final Point2D getIntersectedTexCoord() {
+        return texCoord;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("PickResult [");
+        sb.append("node = ").append(getIntersectedNode())
+                .append(", point = ").append(getIntersectedPoint())
+                .append(", distance = ").append(getIntersectedDistance());
+        if (getIntersectedFace() != FACE_UNDEFINED) {
+                sb.append(", face = ").append(getIntersectedFace());
+        }
+        if (getIntersectedTexCoord() != null) {
+                sb.append(", texCoord = ").append(getIntersectedTexCoord());
+        }
+        return sb.toString();
+    }
+}
--- a/javafx-ui-common/src/javafx/scene/input/RotateEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/RotateEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -102,6 +102,9 @@
      * @param inertia if represents inertia of an already finished gesture.
      * @param angle the rotational angle
      * @param totalAngle the cumulative rotational angle
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and the target
      */
     public RotateEvent(Object source, EventTarget target,
             final EventType<RotateEvent> eventType,
@@ -112,10 +115,12 @@
             boolean altDown,
             boolean metaDown,
             boolean direct,
-            boolean inertia, double angle, double totalAngle) {
+            boolean inertia, double angle, double totalAngle,
+            PickResult pickResult) {
 
         super(source, target, eventType, x, y, screenX, screenY,
-                shiftDown, controlDown, altDown, metaDown, direct, inertia);
+                shiftDown, controlDown, altDown, metaDown, direct, inertia,
+                pickResult);
         this.angle = angle;
         this.totalAngle = totalAngle;
     }
@@ -135,6 +140,9 @@
      * @param inertia if represents inertia of an already finished gesture.
      * @param angle the rotational angle
      * @param totalAngle the cumulative rotational angle
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      */
     public RotateEvent(final EventType<RotateEvent> eventType,
             double x, double y,
@@ -144,9 +152,10 @@
             boolean altDown,
             boolean metaDown,
             boolean direct,
-            boolean inertia, double angle, double totalAngle) {
+            boolean inertia, double angle, double totalAngle,
+            PickResult pickResult) {
         this(null, null, eventType, x, y, screenX, screenY, shiftDown, controlDown,
-                altDown, metaDown, direct, inertia, angle, totalAngle);
+                altDown, metaDown, direct, inertia, angle, totalAngle, pickResult);
     }
 
     private final double angle;
@@ -187,7 +196,8 @@
 
         sb.append(", angle = ").append(getAngle());
         sb.append(", totalAngle = ").append(getTotalAngle());
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
         sb.append(isDirect() ? ", direct" : ", indirect");
 
         if (isInertia()) {
@@ -209,6 +219,7 @@
         if (isShortcutDown()) {
             sb.append(", shortcutDown");
         }
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
--- a/javafx-ui-common/src/javafx/scene/input/ScrollEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/ScrollEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -151,6 +151,9 @@
      * @param textDeltaYUnits units for vertical text-based scroll amount
      * @param textDeltaY vertical text-based scroll amount
      * @param touchCount number of touch points
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and the target
      */
     public ScrollEvent(Object source, EventTarget target,
             final EventType<ScrollEvent> eventType,
@@ -166,10 +169,11 @@
             double totalDeltaX, double totalDeltaY,
             HorizontalTextScrollUnits textDeltaXUnits, double textDeltaX,
             VerticalTextScrollUnits textDeltaYUnits, double textDeltaY,
-            int touchCount) {
+            int touchCount, PickResult pickResult) {
 
         super(source, target, eventType, x, y, screenX, screenY,
-                shiftDown, controlDown, altDown, metaDown, direct, inertia);
+                shiftDown, controlDown, altDown, metaDown, direct, inertia,
+                pickResult);
         this.deltaX = deltaX;
         this.deltaY = deltaY;
         this.totalDeltaX = totalDeltaX;
@@ -203,6 +207,9 @@
      * @param textDeltaYUnits units for vertical text-based scroll amount
      * @param textDeltaY vertical text-based scroll amount
      * @param touchCount number of touch points
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      */
     public ScrollEvent(final EventType<ScrollEvent> eventType,
             double x, double y,
@@ -217,10 +224,12 @@
             double gestureDeltaX, double gestureDeltaY,
             HorizontalTextScrollUnits textDeltaXUnits, double textDeltaX,
             VerticalTextScrollUnits textDeltaYUnits, double textDeltaY,
-            int touchCount) {
+            int touchCount,
+            PickResult pickResult) {
         this(null, null, eventType, x, y, screenX, screenY, shiftDown, controlDown,
                 altDown, metaDown, direct, inertia, deltaX, deltaY, gestureDeltaX,
-                gestureDeltaY, textDeltaXUnits, textDeltaX, textDeltaYUnits, textDeltaY, touchCount);
+                gestureDeltaY, textDeltaXUnits, textDeltaX, textDeltaYUnits, textDeltaY, 
+                touchCount, pickResult);
     }
     
     
@@ -391,7 +400,8 @@
         sb.append(", textDeltaYUnits = ").append(getTextDeltaYUnits())
                 .append(", textDeltaY = ").append(getTextDeltaY());
         sb.append(", touchCount = ").append(getTouchCount());
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
         sb.append(isDirect() ? ", direct" : ", indirect");
 
         if (isInertia()) {
@@ -413,6 +423,7 @@
         if (isShortcutDown()) {
             sb.append(", shortcutDown");
         }
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
--- a/javafx-ui-common/src/javafx/scene/input/SwipeEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/SwipeEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -104,6 +104,9 @@
      * @param metaDown true if meta modifier was pressed.
      * @param direct true if the event was caused by direct input device. See {@link #isDirect() }
      * @param touchCount number of touch points
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and the target
      */
     public SwipeEvent(Object source, EventTarget target,
             final EventType<SwipeEvent> eventType,
@@ -114,10 +117,12 @@
             boolean altDown,
             boolean metaDown,
             boolean direct,
-            int touchCount) {
+            int touchCount,
+            PickResult pickResult) {
 
         super(source, target, eventType, x, y, screenX, screenY,
-                shiftDown, controlDown, altDown, metaDown, direct, false);
+                shiftDown, controlDown, altDown, metaDown, direct, false,
+                pickResult);
         this.touchCount = touchCount;
     }
 
@@ -134,6 +139,9 @@
      * @param metaDown true if meta modifier was pressed.
      * @param direct true if the event was caused by direct input device. See {@link #isDirect() }
      * @param touchCount number of touch points
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      */
     public SwipeEvent(final EventType<SwipeEvent> eventType,
             double x, double y,
@@ -143,9 +151,10 @@
             boolean altDown,
             boolean metaDown,
             boolean direct,
-            int touchCount) {
+            int touchCount,
+            PickResult pickResult) {
         this(null, null, eventType, x, y, screenX, screenY, shiftDown, controlDown,
-                altDown, metaDown, direct, touchCount);
+                altDown, metaDown, direct, touchCount, pickResult);
     }
 
     private final int touchCount;
@@ -171,7 +180,8 @@
         sb.append(", consumed = ").append(isConsumed());
         sb.append(", touchCount = ").append(getTouchCount());
 
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
         sb.append(isDirect() ? ", direct" : ", indirect");
 
         if (isShiftDown()) {
@@ -189,6 +199,7 @@
         if (isShortcutDown()) {
             sb.append(", shortcutDown");
         }
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
--- a/javafx-ui-common/src/javafx/scene/input/TouchPoint.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/TouchPoint.java	Thu Feb 07 17:13:12 2013 -0800
@@ -29,7 +29,7 @@
 import java.io.IOException;
 import java.io.Serializable;
 import javafx.event.EventTarget;
-import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
 import javafx.scene.Node;
 import javafx.scene.Scene;
 
@@ -58,7 +58,7 @@
     private transient Object source;
 
     private TouchPoint(int id, State state, double x, double y, double screenX,
-            double screenY) {
+            double screenY, PickResult pickResult) {
         this.target = null;
         this.id = id;
         this.state = state;
@@ -68,6 +68,11 @@
         this.sceneY = y;
         this.screenX = screenX;
         this.screenY = screenY;
+        this.pickResult = pickResult != null ? pickResult : new PickResult(target, x, y);
+        final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
+        this.x = p.getX();
+        this.y = p.getY();
+        this.z = p.getZ();
     }
 
     /**
@@ -78,11 +83,12 @@
      */
     void recomputeToSource(Object oldSource, Object newSource) {
 
-        final Point2D newCoordinates = InputEventUtils.recomputeCoordinates(
-                new Point2D(sceneX, sceneY), null, newSource);
+        final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
+                pickResult, newSource);
 
         x = newCoordinates.getX();
         y = newCoordinates.getY();
+        z = newCoordinates.getZ();
 
         source = newSource;
     }
@@ -227,6 +233,23 @@
         return y;
     }
 
+    /**
+     * Depth z position of the event relative to the
+     * origin of the MouseEvent's node.
+     */
+    private transient double z;
+
+    /**
+     * Depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     *
+     * @return depth position of the event relative to the
+     * origin of the MouseEvent's source.
+     */
+    public final double getZ() {
+        return z;
+    }
+
     private double screenX;
 
     /**
@@ -254,6 +277,8 @@
      * origin of the {@code Scene} that contains the TouchEvent's source.
      * If the node is not in a {@code Scene}, then the value is relative to
      * the boundsInParent of the root-most parent of the TouchEvent's node.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      *
      * @return the horizontal position of the touch point relative to the
      * origin of the {@code Scene} that contains the TouchEvent's source
@@ -269,6 +294,8 @@
      * origin of the {@code Scene} that contains the TouchEvent's source.
      * If the node is not in a {@code Scene}, then the value is relative to
      * the boundsInParent of the root-most parent of the TouchEvent's node.
+     * Note that in 3D scene, this represents the flat coordinates after
+     * applying the projection transformations.
      *
      * @return the vertical position of the touch point relative to the
      * origin of the {@code Scene} that contains the TouchEvent's source
@@ -278,6 +305,21 @@
     }
 
     /**
+     * Information about the pick if the picked {@code Node} is a
+     * {@code Shape3D} node and its pickOnBounds is false.
+     */
+    private PickResult pickResult;
+
+    /**
+     * Returns information about the pick.
+     *
+     * @return new PickResult object that contains information about the pick
+     */
+    public final PickResult getPickResult() {
+        return pickResult;
+    }
+
+    /**
      * Gets event target on which the touch event carrying this touch point
      * is fired.
      * @return Event target for this touch point
@@ -296,7 +338,9 @@
         sb.append("state = ").append(getState());
         sb.append(", id = ").append(getId());
         sb.append(", target = ").append(getTarget());
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
@@ -307,8 +351,9 @@
      */
     @Deprecated
     public static TouchPoint impl_touchPoint(
-            int id, State state, double x, double y, double screenX, double screenY) {
-        return new TouchPoint(id, state, x, y, screenX, screenY);
+            int id, State state, double x, double y, double screenX, double screenY,
+            PickResult pickResult) {
+        return new TouchPoint(id, state, x, y, screenX, screenY, pickResult);
     }
 
     private void readObject(java.io.ObjectInputStream in)
--- a/javafx-ui-common/src/javafx/scene/input/ZoomEvent.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/input/ZoomEvent.java	Thu Feb 07 17:13:12 2013 -0800
@@ -103,6 +103,9 @@
      * @param inertia if represents inertia of an already finished gesture.
      * @param zoomFactor zoom amount
      * @param totalZoomFactor cumulative zoom amount
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates and the target
      */
     public ZoomEvent(Object source, EventTarget target, final EventType<ZoomEvent> eventType,
             double x, double y,
@@ -114,10 +117,11 @@
             boolean direct,
             boolean inertia,
             double zoomFactor,
-            double totalZoomFactor) {
+            double totalZoomFactor,
+            PickResult pickResult) {
 
         super(source, target, eventType, x, y, screenX, screenY,
-                shiftDown, controlDown, altDown, metaDown, direct, inertia);
+                shiftDown, controlDown, altDown, metaDown, direct, inertia, pickResult);
         this.zoomFactor = zoomFactor;
         this.totalZoomFactor = totalZoomFactor;
     }
@@ -138,6 +142,9 @@
      * @param inertia if represents inertia of an already finished gesture.
      * @param zoomFactor zoom amount
      * @param totalZoomFactor cumulative zoom amount
+     * @param pickResult pick result. Can be null, in this case a 2D pick result
+     *                   without any further values is constructed
+     *                   based on the scene coordinates
      */
     public ZoomEvent(final EventType<ZoomEvent> eventType,
             double x, double y,
@@ -149,9 +156,11 @@
             boolean direct,
             boolean inertia,
             double zoomFactor,
-            double totalZoomFactor) {
+            double totalZoomFactor,
+            PickResult pickResult) {
         this(null, null, eventType, x, y, screenX, screenY, shiftDown, controlDown,
-                altDown, metaDown, direct, inertia, zoomFactor, totalZoomFactor);
+                altDown, metaDown, direct, inertia, zoomFactor, totalZoomFactor,
+                pickResult);
     }
 
     private final double zoomFactor;
@@ -194,7 +203,8 @@
 
         sb.append(", zoomFactor = ").append(getZoomFactor());
         sb.append(", totalZoomFactor = ").append(getTotalZoomFactor());
-        sb.append(", x = ").append(getX()).append(", y = ").append(getY());
+        sb.append(", x = ").append(getX()).append(", y = ").append(getY())
+                .append(", z = ").append(getZ());
         sb.append(isDirect() ? ", direct" : ", indirect");
 
         if (isInertia()) {
@@ -216,6 +226,7 @@
         if (isShortcutDown()) {
             sb.append(", shortcutDown");
         }
+        sb.append(", pickResult = ").append(getPickResult());
 
         return sb.append("]").toString();
     }
--- a/javafx-ui-common/src/javafx/scene/layout/Region.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/src/javafx/scene/layout/Region.java	Thu Feb 07 17:13:12 2013 -0800
@@ -47,6 +47,7 @@
 import javafx.geometry.HPos;
 import javafx.geometry.Insets;
 import javafx.geometry.Orientation;
+import javafx.geometry.Point3D;
 import javafx.geometry.VPos;
 import javafx.scene.Node;
 import javafx.scene.Parent;
@@ -61,6 +62,7 @@
 import com.sun.javafx.geom.PickRay;
 import com.sun.javafx.geom.transform.BaseTransform;
 import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.scene.input.PickResultChooser;
 import com.sun.javafx.sg.PGNode;
 import com.sun.javafx.sg.PGRegion;
 import com.sun.javafx.tk.Toolkit;
@@ -2148,20 +2150,19 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    @Override protected Node impl_pickNodeLocal(double localX, double localY) {
+    @Override protected void impl_pickNodeLocal(double localX, double localY, PickResultChooser result) {
         if (containsBounds(localX, localY)) {
             ObservableList<Node> children = getChildren();
             for (int i = children.size() - 1; i >= 0; i--) {
-                Node picked = children.get(i).impl_pickNode(localX, localY);
-                if (picked != null) {
-                    return picked;
+                children.get(i).impl_pickNode(localX, localY, result);
+                if (!result.isEmpty()) {
+                    return;
                 }
             }
             if (contains(localX, localY)) {
-                return this;
+                result.offer(this, Double.POSITIVE_INFINITY, new Point3D(localX, localY, 0));
             }
         }
-        return null;
     }
 
     /**
@@ -2170,22 +2171,23 @@
      * @deprecated This is an internal API that is not intended for use and will be removed in the next version
      */
     @Deprecated
-    @Override protected Node impl_pickNodeLocal(PickRay pickRay) {
-        if (impl_intersects(pickRay)) {
+    @Override protected void impl_pickNodeLocal(PickRay pickRay, PickResultChooser result) {
+
+        double boundsDistance = impl_intersectsBounds(pickRay);
+
+        if (boundsDistance >= 0) {
+            final boolean checkAll = getScene().isDepthBuffer();
+
             ObservableList<Node> children = getChildren();
-
-            for (int i = children.size() - 1; i >= 0; i--) {
-                Node picked = children.get(i).impl_pickNode(pickRay);
-
-                if (picked != null) {
-                    return picked;
+            for (int i = children.size()-1; i >= 0; i--) {
+                children.get(i).impl_pickNode(pickRay, result);
+                if (!checkAll && !result.isEmpty()) {
+                    return;
                 }
             }
 
-            return this;
+            result.offer(this, boundsDistance, PickResultChooser.computePoint(pickRay, boundsDistance));
         }
-
-        return null;
     }
 
     private Bounds boundingBox;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/paint/Material.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,84 @@
+/*
+ * 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 javafx.scene.paint;
+
+import com.sun.javafx.sg.PGPhongMaterial;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+
+/**
+ * Base class for representing the material of a 3D surface.
+ * 
+ * @since JavaFX 8
+ */
+public abstract class Material {
+    /*
+     *     Material (including Shaders and Textures)
+     Material is not Paint
+     PhongMaterial maybe the first and only material in FX8 (see 3D conceptual implementation for details)
+     Bump map: Normal Map and Height Map -- We may generate a Normal Map when given a Height Map
+     Displacement map? Not in FX8 -- May do Parallex correction mapping to improve quality at performance cost
+     Support auto generated Mipmap
+     No plan to support Multi-texture
+     */
+
+    protected Material() {
+    }
+    
+    // Material isn't a Node. It can't use the standard dirtyBits pattern that is 
+    // in Node
+    private final BooleanProperty dirty = new SimpleBooleanProperty(true);
+
+    final boolean isDirty() {
+        return dirty.getValue();
+    }
+
+    void setDirty(boolean value) {
+        dirty.setValue(value);
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    public final BooleanProperty impl_dirtyProperty() {
+        return dirty;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    abstract public void impl_updatePG();
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    abstract public PGPhongMaterial impl_getPGMaterial();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/paint/PhongMaterial.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,364 @@
+/*
+ * 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 javafx.scene.paint;
+
+import com.sun.javafx.sg.PGPhongMaterial;
+import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.scene.image.Image;
+
+/**
+ * The {@code PhongMaterial} class provides definitions of properties that 
+ * represent a form of Phong shaded material.
+ *
+ * @since JavaFX 8
+ */
+public class PhongMaterial extends Material {
+
+    private boolean diffuseColorDirty = true;
+    private boolean specularColorDirty = true;
+    private boolean specularPowerDirty = true;
+    private boolean diffuseMapDirty = true;
+    private boolean specularMapDirty = true;
+    private boolean bumpMapDirty = true;
+    private boolean selfIlluminationMapDirty = true;
+
+    /**
+     * Creates a new instance of {@code PhongMaterial} class.
+     */
+    public PhongMaterial() {
+        // TODO: 3D - Need to document this ...
+        setDiffuseColor(Color.LIGHTGRAY);        
+    }
+
+    // TODO: 3D - Need to document this ...
+    public PhongMaterial(Color diffuseColor) {
+        setDiffuseColor(diffuseColor);
+    }
+
+    // TODO: 3D - Need to document this ...
+    public PhongMaterial(Color diffuseColor, Image diffuseMap,
+            Image specularMap, Image bumpMap, Image selfIlluminationMap) {
+        setDiffuseColor(diffuseColor);
+        setDiffuseMap(diffuseMap);
+        setSpecularMap(specularMap);
+        setBumpMap(bumpMap);
+        setSelfIlluminationMap(selfIlluminationMap);
+    }
+
+    /**
+     * Specifies the diffuse color of this Material.
+     *
+     * @defaultValue null
+     */
+    private ObjectProperty<Color> diffuseColor;
+
+    public final void setDiffuseColor(Color value) {
+        diffuseColorProperty().set(value);
+    }
+
+    public final Color getDiffuseColor() {
+        return diffuseColor == null ? null : diffuseColor.get();
+    }
+
+    public final ObjectProperty<Color> diffuseColorProperty() {
+        if (diffuseColor == null) {
+            diffuseColor = new SimpleObjectProperty<Color>(PhongMaterial.this,
+                    "diffuseColor") {
+                @Override
+                protected void invalidated() {
+                    diffuseColorDirty = true;
+                    setDirty(true);
+                }
+            };
+        }
+        return diffuseColor;
+    }
+    
+    /**
+     * Specifies the specular color of this Material.
+     *
+     * @defaultValue null
+     */
+    private ObjectProperty<Color> specularColor;
+
+    public final void setSpecularColor(Color value) {
+        specularColorProperty().set(value);
+    }
+
+    public final Color getSpecularColor() {
+        return specularColor == null ? null : specularColor.get();
+    }
+
+    public final ObjectProperty<Color> specularColorProperty() {
+        if (specularColor == null) {
+            specularColor = new SimpleObjectProperty<Color>(PhongMaterial.this,
+                    "specularColor") {
+                @Override
+                protected void invalidated() {
+                    specularColorDirty = true;
+                    setDirty(true);
+                }
+            };
+        }
+        return specularColor;
+    }
+
+    /**
+     * Defines the specular power of this Material.
+     *
+     * @defaultValue 1.0
+     */
+    private DoubleProperty specularPower;
+
+    public final void setSpecularPower(double value) {
+        specularPowerProperty().set(value);
+    }
+
+    public final double getSpecularPower() {
+        return specularPower == null ? 1 : specularPower.get();
+    }
+
+    public final DoubleProperty specularPowerProperty() {
+        if (specularPower == null) {
+            specularPower = new SimpleDoubleProperty(PhongMaterial.this, 
+                    "specularPower", 1.0) {
+                @Override
+                public void invalidated() {
+                    specularPowerDirty = true;
+                    setDirty(true);
+                }
+            };
+        }
+        return specularPower;
+    }
+
+    /**
+     * The diffuse map of this {@code PhongMaterial).
+     *
+     * @defaultValue null
+     */
+    // TODO: 3D - Texture or Image? For Media it might be better to have it as a Texture
+    private ObjectProperty<Image> diffuseMap;
+
+    public final void setDiffuseMap(Image value) {
+        diffuseMapProperty().set(value);
+    }
+
+    public final Image getDiffuseMap() {
+        return diffuseMap == null ? null : diffuseMap.get();
+    }
+
+    public final ObjectProperty<Image> diffuseMapProperty() {
+        if (diffuseMap == null) {
+            diffuseMap = new SimpleObjectProperty<Image>(PhongMaterial.this,
+                    "diffuseMap") {
+                @Override
+                public void invalidated() {
+                    diffuseMapDirty = true;
+                    setDirty(true);
+                }
+            };
+        }
+        return diffuseMap;
+    }
+
+    /**
+     * The specular map of this {@code PhongMaterial).
+     *
+     * @defaultValue null
+     */
+    // TODO: 3D - Texture or Image? For Media it might be better to have it as a Texture
+    private ObjectProperty<Image> specularMap;
+
+    public final void setSpecularMap(Image value) {
+        diffuseMapProperty().set(value);
+    }
+
+    public final Image getSpecularMap() {
+        return specularMap == null ? null : specularMap.get();
+    }
+
+    public final ObjectProperty<Image> specularMapProperty() {
+        if (specularMap == null) {
+            specularMap = new SimpleObjectProperty<Image>(PhongMaterial.this,
+                    "specularMap") {
+                @Override
+                public void invalidated() {
+                    specularMapDirty = true;
+                    setDirty(true);
+                }
+            };
+        }
+        return specularMap;
+    }
+
+    /**
+     * The bump map of this {@code PhongMaterial).
+     *
+     * @defaultValue null
+     */
+    // TODO: 3D - Texture or Image? For Media it might be better to have it as a Texture
+    private ObjectProperty<Image> bumpMap;
+
+    public final void setBumpMap(Image value) {
+        bumpMapProperty().set(value);
+    }
+
+    public final Image getBumpMap() {
+        return bumpMap == null ? null : bumpMap.get();
+    }
+
+    public final ObjectProperty<Image> bumpMapProperty() {
+        if (bumpMap == null) {
+            bumpMap = new SimpleObjectProperty<Image>(PhongMaterial.this,
+                    "bumpMap") {
+                @Override
+                public void invalidated() {
+                    bumpMapDirty = true;
+                    setDirty(true);
+                }
+            };
+        }
+        return bumpMap;
+    }
+
+    /**
+     * The self illumination map of this {@code PhongMaterial).
+     *
+     * @defaultValue null
+     */
+    // TODO: 3D - Texture or Image? For Media it might be better to have it as a Texture
+    private ObjectProperty<Image> selfIlluminationMap;
+
+    public final void setSelfIlluminationMap(Image value) {
+        selfIlluminationMapProperty().set(value);
+    }
+ 
+    public final Image getSelfIlluminationMap() {
+        return selfIlluminationMap == null ? null : selfIlluminationMap.get();
+    }
+    
+    public final ObjectProperty<Image> selfIlluminationMapProperty() {
+        if (selfIlluminationMap == null) {
+            selfIlluminationMap = new SimpleObjectProperty<Image>(PhongMaterial.this,
+                    "selfIlluminationMap") {
+                @Override
+                public void invalidated() {
+                    selfIlluminationMapDirty = true;
+                    setDirty(true);
+                }
+            };
+        }
+        return selfIlluminationMap;
+    }
+
+    @Override
+    void setDirty(boolean value) {
+        super.setDirty(value);
+        if (!value) {
+            diffuseColorDirty = false;
+            specularColorDirty = false;
+            specularPowerDirty = false;
+            diffuseMapDirty = false;
+            specularMapDirty = false;
+            bumpMapDirty = false;
+            selfIlluminationMapDirty = false;
+        }
+    }
+    
+    /** The peer node created by the graphics Toolkit/Pipeline implementation */
+    private PGPhongMaterial peer;
+    
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public PGPhongMaterial impl_getPGMaterial() {
+        if (peer == null) {
+            peer = Toolkit.getToolkit().createPGPhongMaterial();
+        }
+        return peer;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public void impl_updatePG(){
+        if (!isDirty()) {
+            return;
+        }
+
+        PGPhongMaterial pMaterial = (PGPhongMaterial) impl_getPGMaterial();
+        if (diffuseColorDirty) {
+            pMaterial.setDiffuseColor(getDiffuseColor() == null ? null
+                    : Toolkit.getPaintAccessor().getPlatformPaint(getDiffuseColor()));
+        }
+        if (specularColorDirty) {
+            pMaterial.setSpecularColor(getSpecularColor() == null ? null
+                    : Toolkit.getPaintAccessor().getPlatformPaint(getSpecularColor()));
+        }
+        if (specularPowerDirty) {
+            pMaterial.setSpecularPower((float)getSpecularPower());
+        }
+        if (diffuseMapDirty) {
+            pMaterial.setDiffuseMap(getDiffuseMap()
+                    == null ? null : getDiffuseMap().impl_getPlatformImage());
+        }
+        if (specularMapDirty) {
+            pMaterial.setSpecularMap(getSpecularMap()
+                    == null ? null : getSpecularMap().impl_getPlatformImage());
+        }
+        if (bumpMapDirty) {
+            pMaterial.setBumpMap(getBumpMap()
+                    == null ? null : getBumpMap().impl_getPlatformImage());
+        }
+        if (selfIlluminationMapDirty) {
+            pMaterial.setSelfIlluminationMap(getSelfIlluminationMap()
+                    == null ? null : getSelfIlluminationMap().impl_getPlatformImage());
+        }
+
+        setDirty(false);
+    }
+
+    @Override public String toString() {
+        return "PGPhongMaterial{" + "diffuseColor=" + diffuseColor +
+                ", specularColor=" + specularColor +
+                ", specularPower=" + specularPower +
+                ", diffuseMap=" + diffuseMap +
+                ", specularMap=" + specularMap +
+                ", bumpMap=" + bumpMap +
+                ", selfIlluminationMap=" + selfIlluminationMap + '}';
+    }
+
+}       
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/Box.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,361 @@
+/*
+ * 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 javafx.scene.shape;
+
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.geom.Vec3d;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.scene.input.PickResultChooser;
+import com.sun.javafx.sg.PGBox;
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
+import javafx.scene.input.PickResult;
+
+/**
+ * The {@code Box} class defines a 3 dimensional box with the specified size.
+ * A {@code Box} is a 3D geometry primitive created with a given depth, width,
+ * and height. It is centered at the origin.
+ *
+ * @since JavaFX 8    
+ */
+public class Box extends Shape3D {
+
+    private TriangleMesh mesh;
+
+    /**
+     * Creates a new instance of {@code Box} of dimension 2 by 2 by 2.
+     */
+    
+    public static final double DEFAULT_SIZE = 2;
+    
+    public Box() {
+        this(DEFAULT_SIZE, DEFAULT_SIZE, DEFAULT_SIZE);
+    }
+
+    /**
+     * Creates a new instance of {@code Box} of dimension width by height 
+     * by depth.
+     */
+    public Box(double width, double height, double depth) {
+        setWidth(width);
+        setHeight(height);
+        setDepth(depth);
+    }
+    
+    /**
+     * Defines the depth or the Z dimension of the Box.
+     *
+     * @defaultValue 2.0
+     */
+    private DoubleProperty depth;
+
+    public final void setDepth(double value) {
+        depthProperty().set(value);
+    }
+
+    public final double getDepth() {
+        return depth == null ? 2 : depth.get();
+    }
+
+    public final DoubleProperty depthProperty() {
+        if (depth == null) {
+            depth = new SimpleDoubleProperty(Box.this, "depth", DEFAULT_SIZE) {
+                @Override
+                public void invalidated() {
+                    impl_markDirty(DirtyBits.MESH_GEOM);
+                    manager.invalidateBoxMesh(key);
+                    key = 0;
+                }
+            };
+        }
+        return depth;
+    }
+
+    /**
+     * Defines the height or the Y dimension of the Box.
+     *
+     * @defaultValue 2.0
+     */
+    private DoubleProperty height;
+
+    public final void setHeight(double value) {
+        heightProperty().set(value);
+    }
+
+    public final double getHeight() {
+        return height == null ? 2 : height.get();
+    }
+
+    public final DoubleProperty heightProperty() {
+        if (height == null) {
+            height = new SimpleDoubleProperty(Box.this, "height", DEFAULT_SIZE) {
+                @Override
+                public void invalidated() {
+                    impl_markDirty(DirtyBits.MESH_GEOM);
+                    manager.invalidateBoxMesh(key);
+                    key = 0;
+                }
+            };
+        }
+        return height;
+    }
+
+    /**
+     * Defines the width or the X dimension of the Box.
+     *
+     * @defaultValue 2.0
+     */
+    private DoubleProperty width;
+
+    public final void setWidth(double value) {
+        widthProperty().set(value);
+    }
+
+    public final double getWidth() {
+        return width == null ? 2 : width.get();
+    }
+
+    public final DoubleProperty widthProperty() {
+        if (width == null) {
+            width = new SimpleDoubleProperty(Box.this, "width", DEFAULT_SIZE) {
+                @Override
+                public void invalidated() {
+                    impl_markDirty(DirtyBits.MESH_GEOM);
+                    manager.invalidateBoxMesh(key);
+                    key = 0;
+                }
+            };
+        }
+        return width;
+    }
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected PGNode impl_createPGNode() {
+        return Toolkit.getToolkit().createPGBox();
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    public void impl_updatePG() {
+        super.impl_updatePG();
+        if (impl_isDirty(DirtyBits.MESH_GEOM)) {
+            PGBox pgBox = (PGBox) impl_getPGNode();
+            if (key == 0) {
+                key = generateKey((float) getWidth(), 
+                                  (float) getHeight(),
+                                  (float) getDepth());
+            }
+            mesh = manager.getBoxMesh((float) getWidth(), 
+                                      (float) getHeight(), 
+                                      (float) getDepth(),
+                                      key);
+            mesh.impl_updatePG();
+            pgBox.updateMesh(mesh.impl_getPGTriangleMesh());
+        }
+    }
+    
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+        float hw = (float) getWidth() * 0.5f;
+        float hh = (float) getHeight() * 0.5f;
+        float hd = (float) getDepth() * 0.5f;
+        
+        bounds = bounds.deriveWithNewBounds(-hw, -hh, -hd, hw, hh, hd);
+        bounds = tx.transform(bounds, bounds);
+        return bounds;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeContains(double localX, double localY) {
+        double w = getWidth();
+        double h = getHeight();
+        return -w <= localX && localX <= w && 
+                -h <= localY && localY <= h;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) {
+
+        final double w = getWidth();
+        final double h = getHeight();
+        final double d = getDepth();
+        final double hWidth = w / 2.0;
+        final double hHeight = h / 2.0;
+        final double hDepth = d / 2.0;
+        final Vec3d dir = pickRay.getDirectionNoClone();
+        final double invDirX = dir.x == 0 ? Double.POSITIVE_INFINITY : (1.0 / dir.x);
+        final double invDirY = dir.y == 0 ? Double.POSITIVE_INFINITY : (1.0 / dir.y);
+        final double invDirZ = dir.z == 0 ? Double.POSITIVE_INFINITY : (1.0 / dir.z);
+        final Vec3d origin = pickRay.getOriginNoClone();
+        final double originX = origin.x;
+        final double originY = origin.y;
+        final double originZ = origin.z;
+        final boolean signX = invDirX < 0.0;
+        final boolean signY = invDirY < 0.0;
+        final boolean signZ = invDirZ < 0.0;
+
+        double t0 = ((signX ? hWidth : -hWidth) - originX) * invDirX;
+        double t1 = ((signX ? -hWidth : hWidth) - originX) * invDirX;
+        char side0 = 'x';
+        char side1 = 'x';
+
+        final double ty0 = ((signY ? hHeight : -hHeight) - originY) * invDirY;
+        final double ty1 = ((signY ? -hHeight : hHeight) - originY) * invDirY;
+
+        if ((t0 > ty1) || (ty0 > t1)) {
+            return false;
+        }
+        if (ty0 > t0) {
+            side0 = 'y';
+            t0 = ty0;
+        }
+        if (ty1 < t1) {
+            side1 = 'y';
+            t1 = ty1;
+        }
+
+        double tz0 = ((signZ ? hDepth : -hDepth) - originZ) * invDirZ;
+        double tz1 = ((signZ ? -hDepth : hDepth) - originZ) * invDirZ;
+
+        if ((t0 > tz1) || (tz0 > t1)) {
+            return false;
+        }
+        if (tz0 > t0) {
+            side0 = signZ ? 'Z' : 'z';
+            t0 = tz0;
+        }
+        if (tz1 < t1) {
+            side1 = signZ ? 'z' : 'Z';
+            t1 = tz1;
+        }
+
+        char side = side0;
+        double t = t0;
+        final CullFace cullFace = getCullFace();
+        if (t0 < 0.0 || cullFace == CullFace.FRONT) {
+            if (t1 >= 0.0 && cullFace != CullFace.BACK) {
+                side = side1;
+                t = t1;
+            } else {
+                return false;
+            }
+        }
+
+        if (pickResult.isCloser(t)) {
+            Point3D point = PickResultChooser.computePoint(pickRay, t);
+
+            Point2D txtCoords = null;
+
+            if (side == 'x') {
+                txtCoords = new Point2D(
+                        0.5 - point.getY() / h,
+                        0.5 - point.getZ() / d);
+            } else if (side == 'y') {
+                txtCoords = new Point2D(
+                        0.5 - point.getX() / w,
+                        0.5 - point.getZ() / d);
+            } else if (side == 'z') {
+                txtCoords = new Point2D(
+                        0.5 - point.getX() / w,
+                        0.5 - point.getY() / h);
+            } else if (side == 'Z') {
+                txtCoords = new Point2D(
+                        0.5 - point.getY() / h,
+                        0.5 - point.getX() / w);
+            }
+
+            pickResult.offer(this, t, PickResult.FACE_UNDEFINED, point, txtCoords);
+        }
+        
+        return true;
+    }
+
+    static TriangleMesh createMesh(float w, float h, float d) {
+
+        if (w * h * d == 0) {
+            return null;
+        }
+
+        float hw = w / 2f;
+        float hh = h / 2f;
+        float hd = d / 2f;
+
+        float points[] = {
+            hw, hh, hd, hw, hh, -hd, hw, -hh, hd, hw, -hh, -hd,
+            -hw, hh, hd, -hw, hh, -hd, -hw, -hh, hd, -hw, -hh, -hd };
+
+        float texCoords[] = {0, 0, 0, 1, 1, 0, 1, 1};
+
+        int faceSmoothingGroups[] = {
+            1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4
+        };
+
+        int faces[] = {
+            0, 0, 2, 2, 1, 1, 2, 2, 3, 3, 1, 1, 4, 0, 5, 1, 6, 2, 6, 2, 5, 1, 7, 3,
+            0, 0, 1, 1, 4, 2, 4, 2, 1, 1, 5, 3, 2, 0, 6, 2, 3, 1, 3, 1, 6, 2, 7, 3,
+            0, 0, 4, 1, 2, 2, 2, 2, 4, 1, 6, 3, 1, 0, 3, 1, 5, 2, 5, 2, 3, 1, 7, 3,};
+
+        TriangleMesh mesh = new TriangleMesh(points, texCoords, faces);
+        mesh.setFaceSmoothingGroups(faceSmoothingGroups);
+
+        return mesh;
+    }
+
+    private static int generateKey(float w, float h, float d) {
+        int hash = 3;
+        hash = 97 * hash + Float.floatToIntBits(w);
+        hash = 97 * hash + Float.floatToIntBits(h);
+        hash = 97 * hash + Float.floatToIntBits(d);
+        return hash;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/CullFace.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,51 @@
+/*
+ * 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 javafx.scene.shape;
+
+/**
+ * Face culling setting for use with {@code Shape3D.cullFace}
+ *
+ * @see Shape3D#cullFaceProperty
+ * @since JavaFX 8
+ */
+public enum CullFace {
+
+    /**
+     * Do not perform any face culling.
+     */
+    NONE,
+
+    /**
+     * Cull all back-facing polygons. BACK is defined as clockwise winding
+     * order.
+     */
+    BACK,
+
+    /**
+     * Cull all front-facing polygons. FRONT is defined as counter-clockwise
+     * winding order.
+     */
+    FRONT
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/Cylinder.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,499 @@
+/*
+ * 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 javafx.scene.shape;
+
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.geom.Vec3d;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.scene.input.PickResultChooser;
+import com.sun.javafx.sg.PGCylinder;
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
+import javafx.scene.input.PickResult;
+import javafx.scene.transform.Rotate;
+
+/**
+ * The {@code Cylinder} class defines a 3 dimensional cylinder with the specified size.
+ * A {@code Cylinder} is a 3D geometry primitive created with a given radius and height.
+ * It is centered at the origin.
+ *
+ * @since JavaFX 8    
+ */
+public class Cylinder extends Shape3D {
+
+    static final int DEFAULT_DIVISIONS = 64;
+    static final double DEFAULT_RADIUS = 1;
+    static final double DEFAULT_HEIGHT = 2;
+    
+    private int divisions = DEFAULT_DIVISIONS;
+    private TriangleMesh mesh;
+    
+    /**  
+     * Creates a new instance of {@code Cylinder} of radius of 1.0 and height of 2.0.
+     * Resolution defaults to 15 divisions along X and Z axis.
+     */
+    public Cylinder() {
+        this(DEFAULT_RADIUS, DEFAULT_HEIGHT, DEFAULT_DIVISIONS);
+    }
+
+    /**
+     * Creates a new instance of {@code Cylinder} of a given radius and height.
+     * Resolution defaults to 15 divisions along X and Z axis.
+     * 
+     * @param radius Radius
+     * @param height Height
+     */
+    public Cylinder (double radius, double height) {
+        this(radius, height, DEFAULT_DIVISIONS);
+    }
+
+    /**
+     * Creates a new instance of {@code Cylinder} of a given radius, height, and
+     * divisions. Resolution defaults to 15 divisions along X and Z axis.
+     * 
+     * @param radius Radius
+     * @param height Height
+     * @param divisions Divisions 
+     */
+    public Cylinder (double radius, double height, int divisions) {
+        this.divisions = divisions;
+        setRadius(radius);
+        setHeight(height);
+    }
+    
+    /**
+     * Defines the height or the Y dimension of the Cylinder.
+     *
+     * @defaultValue 2.0
+     */
+    private DoubleProperty height;
+
+    public final void setHeight(double value) {
+        heightProperty().set(value);
+    }
+
+    public final double getHeight() {
+        return height == null ? 2 : height.get();
+    }
+
+    public final DoubleProperty heightProperty() {
+        if (height == null) {
+            height = new SimpleDoubleProperty(Cylinder.this, "height", DEFAULT_HEIGHT) {
+                @Override
+                public void invalidated() {
+                    impl_markDirty(DirtyBits.MESH_GEOM);
+                    manager.invalidateCylinderMesh(key);
+                    key = 0;
+                }
+            };
+        }
+        return height;
+    }
+
+    /**
+     * Defines the radius in the Z plane of the Cylinder.
+     *
+     * @defaultValue 1.0
+     */
+    private DoubleProperty radius;
+
+    public final void setRadius(double value) {
+        radiusProperty().set(value);
+    }
+
+    public final double getRadius() {
+        return radius == null ? 1 : radius.get();
+    }
+
+    public final DoubleProperty radiusProperty() {
+        if (radius == null) {
+            radius = new SimpleDoubleProperty(Cylinder.this, "radius", DEFAULT_RADIUS) {
+                @Override
+                public void invalidated() {
+                    impl_markDirty(DirtyBits.MESH_GEOM);
+                    manager.invalidateCylinderMesh(key);
+                    key = 0;
+                }
+            };
+        }
+        return radius;
+    }
+
+    /**
+     * Retrieves the divisions attribute use to generate this cylinder.
+     *
+     * @return the divisions attribute.
+     */
+    public int getDivisions() {
+        return divisions;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated   
+    @Override
+    public void impl_updatePG() {
+        super.impl_updatePG();
+        if (impl_isDirty(DirtyBits.MESH_GEOM)) {
+            PGCylinder pgCylinder = (PGCylinder) impl_getPGNode();
+            if (key == 0) {
+                key = generateKey((float) getHeight(), 
+                                  (float) getRadius(),
+                                  divisions);
+            }
+            mesh = manager.getCylinderMesh((float) getHeight(), (float) getRadius(), divisions, key);
+            mesh.impl_updatePG();
+            pgCylinder.updateMesh(mesh.impl_getPGTriangleMesh());
+        }
+    }
+    
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected PGNode impl_createPGNode() {
+        return Toolkit.getToolkit().createPGCylinder();
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+        float r = (float) getRadius();
+        float hh = (float) getHeight() * 0.5f;
+        
+        bounds = bounds.deriveWithNewBounds(-r, -hh, -r, r, hh, r);
+        bounds = tx.transform(bounds, bounds);
+        return bounds;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeContains(double localX, double localY) {
+        double w = getRadius();
+        double hh = getHeight()*.5f;
+        return -w <= localX && localX <= w && 
+                -hh <= localY && localY <= hh;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) {
+
+        final boolean exactPicking = divisions < DEFAULT_DIVISIONS && mesh != null;
+
+        final double r = getRadius();
+        final Vec3d dir = pickRay.getDirectionNoClone();
+        final double dirX = dir.x;
+        final double dirY = dir.y;
+        final double dirZ = dir.z;
+        final Vec3d origin = pickRay.getOriginNoClone();
+        final double originX = origin.x;
+        final double originY = origin.y;
+        final double originZ = origin.z;
+        final double h = getHeight();
+        final double halfHeight = h / 2.0;
+        final CullFace cullFace = getCullFace();
+
+        // Check the open cylinder first
+
+        // Coeficients of a quadratic equation desribing intersection with an infinite cylinder
+        final double a = dirX * dirX + dirZ * dirZ;
+        final double b = 2 * (dirX * originX + dirZ * originZ);
+        final double c = originX * originX + originZ * originZ - r * r;
+
+        final double discriminant = b * b - 4 * a * c;
+
+        double t0, t1, t = Double.POSITIVE_INFINITY;
+
+        if (discriminant >= 0 && (dirX != 0.0 || dirZ != 0.0)) {
+            // the line hits the infinite cylinder
+
+            final double distSqrt = Math.sqrt(discriminant);
+            final double q = (b < 0) ? (-b - distSqrt) / 2.0 : (-b + distSqrt) / 2.0;
+
+            t0 = q / a;
+            t1 = c / q;
+
+            if (t0 > t1) {
+                double temp = t0;
+                t0 = t1;
+                t1 = temp;
+            }
+
+            // let's see if the hit is in front of us and withing the cylinder's height
+            final double y0 = originY + t0 * dirY;
+            if (t0 < 0 || y0 < -halfHeight || y0 > halfHeight || cullFace == CullFace.FRONT) {
+                final double y1 = originY + t1 * dirY;
+                if (t1 >= 0 && y1 >= -halfHeight && y1 <= halfHeight) {
+                    if (cullFace != CullFace.BACK || exactPicking) {
+                        // t0 is outside or behind but t1 hits.
+
+                        // We need to do the exact picking even if the back wall
+                        // is culled because the front facing triangles may
+                        // still be in front of us
+                        t = t1;
+                    }
+                } // else no hit (but we need to check the caps)
+            } else {
+                // t0 hits the height in front of us
+                t = t0;
+            }
+        }
+
+        // Now check the caps
+        
+        // if we already know we are going to do the exact picking,
+        // there is no need to check the caps
+        
+        boolean cap = false;
+        if (t == Double.POSITIVE_INFINITY || !exactPicking) {
+            final double tBottom = (-halfHeight - originY) / dirY;
+            final double tTop = (halfHeight - originY) / dirY;
+
+            if (tBottom < tTop) {
+                t0 = tBottom;
+                t1 = tTop;
+            } else {
+                t0 = tTop;
+                t1 = tBottom;
+            }
+
+            if (t0 > 0 && t0 < t && cullFace != CullFace.FRONT) {
+                final double tX = originX + dirX * t0;
+                final double tZ = originZ + dirZ * t0;
+                if (tX * tX + tZ * tZ <= r * r) {
+                    cap = true;
+                    t = t0;
+                }
+            }
+
+            if (t1 > 0 && t1 < t && (cullFace != CullFace.BACK || exactPicking)) {
+                final double tX = originX + dirX * t1;
+                final double tZ = originZ + dirZ * t1;
+                if (tX * tX + tZ * tZ <= r * r) {
+                    cap = true;
+                    t = t1;
+                }
+            }
+        }
+
+        if (t == Double.POSITIVE_INFINITY) {
+            // no hit
+            return false;
+        }
+
+        if (exactPicking) {
+            return mesh.impl_computeIntersects(pickRay, pickResult, this, cullFace, false);
+        }
+
+        if (pickResult.isCloser(t)) {
+            final Point3D point = PickResultChooser.computePoint(pickRay, t);
+
+            Point2D txCoords;
+            if (cap) {
+                txCoords = new Point2D(
+                        0.5 + point.getX() / (2 * r),
+                        0.5 + point.getZ() / (2 * r));
+            } else {
+                final Point3D proj = new Point3D(point.getX(), 0, point.getZ());
+                final Point3D cross = proj.crossProduct(Rotate.Z_AXIS);
+                double angle = proj.angle(Rotate.Z_AXIS);
+                if (cross.getY() > 0) {
+                    angle = 360 - angle;
+                }
+                txCoords = new Point2D(angle / 360, 0.5 - point.getY() / h);
+            }
+
+            pickResult.offer(this, t, PickResult.FACE_UNDEFINED, point, txCoords);
+        }
+        return true;
+    }
+
+    static TriangleMesh createMesh(int div, float h, float r) {
+
+        if (h * r == 0 || div < 3) {
+            return null;
+        }
+
+        final int nPonits = (div + 1) * 2 + 2;
+        final int tcCount = (div + 1) * 3 + 1;
+        final int faceCount = div * 4;
+
+        float textureDelta = 1.f / 256;
+
+        float dA = 1.f / div;
+        h *= .5f;
+
+        float points[] = new float[nPonits * 3];
+        float tPoints[] = new float[tcCount * 2];
+        int faces[] = new int[faceCount * 6];
+        int smoothing[] = new int[faceCount];
+
+        int pPos = 0, tPos = 0;
+
+        for (int i = 0; i <= div; ++i) {
+            double a = (i < div) ? dA * i * 2 * Math.PI : 0;
+
+            points[pPos + 0] = (float) (Math.sin(a) * r);
+            points[pPos + 2] = (float) (Math.cos(a) * r);
+            points[pPos + 1] = h;
+            tPoints[tPos + 0] = dA * i;
+            tPoints[tPos + 1] = textureDelta;
+            pPos += 3; tPos += 2;
+        }
+
+        for (int i = 0; i <= div; ++i) {
+            double a = (i < div) ? dA * i * 2 * Math.PI : 0;
+            points[pPos + 0] = (float) (Math.sin(a) * r);
+            points[pPos + 2] = (float) (Math.cos(a) * r);
+            points[pPos + 1] = -h;
+            tPoints[tPos + 0] = dA * i;
+            tPoints[tPos + 1] = 1 - textureDelta;
+            pPos += 3; tPos += 2;
+        }
+
+        // add cap central points
+        points[pPos + 0] = 0;
+        points[pPos + 1] = h;
+        points[pPos + 2] = 0;
+        points[pPos + 3] = 0;
+        points[pPos + 4] = -h;
+        points[pPos + 5] = 0;
+        pPos += 6;
+
+        // add cap central points
+        for (int i = 0; i <= div; ++i) {
+            double a = (i < div) ? dA * i * 2 * Math.PI : 0;
+            tPoints[tPos + 0] = (float) (Math.sin(a) * 0.5f) + 0.5f;
+            tPoints[tPos + 1] = (float) (Math.cos(a) * 0.5f) + 0.5f;
+            tPos += 2;
+        }
+
+        tPoints[tPos + 0] = .5f;
+        tPoints[tPos + 1] = .5f;
+        tPos += 2;
+
+        int fIndex = 0;
+
+        // build body faces
+        for (int p0 = 0; p0 != div; ++p0) {
+            int p1 = p0 + 1;
+            int p2 = p0 + div + 1;
+            int p3 = p1 + div + 1;
+
+            // add p0, p1, p2
+            faces[fIndex+0] = p0;
+            faces[fIndex+1] = p0;
+            faces[fIndex+2] = p2;
+            faces[fIndex+3] = p2;
+            faces[fIndex+4] = p1;
+            faces[fIndex+5] = p1;
+            fIndex += 6;
+
+            // add p3, p2, p1
+            // *faces++ = SmFace(p3,p1,p2, p3,p1,p2, 1);
+            faces[fIndex+0] = p3;
+            faces[fIndex+1] = p3;
+            faces[fIndex+2] = p1;
+            faces[fIndex+3] = p1;
+            faces[fIndex+4] = p2;
+            faces[fIndex+5] = p2;
+            fIndex += 6;
+
+        }
+        // build cap faces
+        int tStart = (div + 1) * 2, t1 = (div + 1) * 3, p1 = (div + 1) * 2;
+
+        for (int p0 = 0; p0 != div; ++p0) {
+            int p2 = p0 + 1, t0 = tStart + p0, t2 = t0 + 1;
+            // add p0, p1, p2
+
+            faces[fIndex+0] = p0;
+            faces[fIndex+1] = t0;
+            faces[fIndex+2] = p2;
+            faces[fIndex+3] = t2;
+            faces[fIndex+4] = p1;
+            faces[fIndex+5] = t1;
+            fIndex += 6;
+        }
+
+        p1 = (div + 1) * 2 + 1;
+
+        for (int p0 = 0; p0 != div; ++p0) {
+            int p2 = p0 + 1 + div + 1, t0 = tStart + p0, t2 = t0 + 1;
+            //*faces++ = SmFace(p0+div+1,p1,p2, t0,t1,t2, 2);
+
+            faces[fIndex+0] = p0 + div + 1;
+            faces[fIndex+1] = t0;
+            faces[fIndex+2] = p1;
+            faces[fIndex+3] = t1;
+            faces[fIndex+4] = p2;
+            faces[fIndex+5] = t2;
+            fIndex += 6;
+        }
+
+        for (int i = 0; i < div * 2; ++i) {
+            smoothing[i] = 1;
+        }
+        for (int i = div * 2; i < div * 4; ++i) {
+            smoothing[i] = 2;
+        }
+
+        TriangleMesh m = new TriangleMesh(points, tPoints, faces);
+        m.setFaceSmoothingGroups(smoothing);
+
+        return m;
+    }
+
+    private static int generateKey(float h, float r, int div) {
+        int hash = 7;
+        hash = 47 * hash + Float.floatToIntBits(h);
+        hash = 47 * hash + Float.floatToIntBits(r);
+        hash = 47 * hash + div;
+        return hash;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/DrawMode.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,48 @@
+/*
+ * 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 javafx.scene.shape;
+
+/**
+ * Defines how the polygon is drawn when use with {@code Shape3D.drawMode}
+ *
+ * @see Shape3D#drawModeProperty
+ * @since JavaFX 8
+ */
+public enum DrawMode {
+
+    // TODO: 3D - Alternative name RasterizationMode
+    /**
+     * Render polygonal primitives as lines drawn between consecutive
+     * vertices of the polygon.
+     */
+    // Wireframe
+    LINE,
+
+    /**
+     * Render polygonal primitives by filling the interior of the polygon.
+     */
+    // Solid
+    FILL
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/Mesh.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,90 @@
+/*
+ * 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 javafx.scene.shape;
+
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.scene.input.PickResultChooser;
+import com.sun.javafx.sg.PGTriangleMesh;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.scene.Node;
+
+/**
+ * Base class for representing a 3D geometric surface.
+ * 
+ * @since JavaFX 8
+ */
+public abstract class Mesh {
+
+    protected Mesh() {
+    }
+    
+    // Mesh isn't a Node. It can't use the standard dirtyBits pattern that is 
+    // in Node
+    // TODO: 3D - Material and Mesh have similar pattern. We should look into creating
+    // a "NodeComponent" class if more non-Node classes are needed.
+    
+    // Material isn't a Node. It can't use the standard dirtyBits pattern that is 
+    // in Node
+    private final BooleanProperty dirty = new SimpleBooleanProperty(true);
+
+    final boolean isDirty() {
+        return dirty.getValue();
+    }
+
+    void setDirty(boolean value) {
+        dirty.setValue(value);
+    }
+
+    final BooleanProperty dirtyProperty() {
+        return dirty;
+    }
+
+    // We only support one type of mesh for FX 8.
+    abstract PGTriangleMesh getPGMesh();
+    abstract void impl_updatePG();
+
+    abstract BaseBounds computeBounds(BaseBounds b);
+
+    /**
+     * Picking implementation.
+     * @param pickRay The pick ray
+     * @param pickResult The pick result to be updated (if a closer intersection is found)
+     * @param candidate The Node that owns this mesh to be filled in the pick
+     *                  result in case a closer intersection is found
+     * @param cullFace The cull face of the node that owns this mesh
+     * @param reportFace Whether to report the hit face
+     * @return true if the pickRay intersects this mesh (regardless of whether
+     *              the pickResult has been updated)
+     *
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    abstract protected boolean impl_computeIntersects(PickRay pickRay,
+            PickResultChooser pickResult, Node candidate, CullFace cullFace,
+            boolean reportFace);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/MeshView.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,168 @@
+/*
+ * 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 javafx.scene.shape;
+
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.scene.input.PickResultChooser;
+import com.sun.javafx.sg.PGMeshView;
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+
+/**
+ * The {@code MeshView} class defines a surface with the specified 3D
+ * mesh data.
+ *
+ * @since JavaFX 8    
+ */
+public class MeshView extends Shape3D {
+
+    /**
+     * Creates a new instance of {@code MeshView} class.
+     */
+    public MeshView() {
+    }
+
+    /**
+     * Creates a new instance of {@code MeshView} class with the specified {@code Mesh}
+     * surface.
+     */
+    public MeshView(Mesh mesh) {
+        setMesh(mesh);
+    }
+
+    /**
+     * Specifies the 3D mesh data of this {@code MeshView}.
+     *
+     * @defaultValue null
+     */
+    private ObjectProperty<Mesh> mesh;
+
+    public final void setMesh(Mesh value) {
+        meshProperty().set(value);
+    }
+
+    public final Mesh getMesh() {
+        return mesh == null ? null : mesh.get();
+    }
+
+    private final ChangeListener<Boolean> meshListener = new ChangeListener<Boolean>() {
+        @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+            if (newValue) {
+                impl_markDirty(DirtyBits.MESH_GEOM);
+                impl_geomChanged();
+            }
+        }
+    };
+
+    public final ObjectProperty<Mesh> meshProperty() {
+        if (mesh == null) {
+            mesh = new SimpleObjectProperty<Mesh>(MeshView.this, "mesh") {
+                private Mesh old = null;
+
+                @Override
+                protected void invalidated() {
+                    if (old != null) {
+                        old.dirtyProperty().removeListener(meshListener);
+                    }
+                    Mesh newMesh = get();
+                    if (newMesh != null) {
+                        newMesh.dirtyProperty().addListener(meshListener);
+                    }
+                    impl_markDirty(DirtyBits.MESH);
+                    impl_markDirty(DirtyBits.MESH_GEOM);
+                    impl_geomChanged();
+                    old = newMesh;
+                }
+            };
+        }
+        return mesh;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override public void impl_updatePG() {
+        super.impl_updatePG();
+        PGMeshView pgMeshView = (PGMeshView)impl_getPGNode();
+        if (impl_isDirty(DirtyBits.MESH_GEOM) && getMesh() != null) {
+            getMesh().impl_updatePG();
+        }
+        if (impl_isDirty(DirtyBits.MESH)) {
+            pgMeshView.setMesh((getMesh() == null) ? null : getMesh().getPGMesh());
+        }
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected PGNode impl_createPGNode() {
+        return Toolkit.getToolkit().createPGMeshView();
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+        bounds = mesh.get().computeBounds(bounds);
+        bounds = tx.transform(bounds, bounds);
+        return bounds;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeContains(double localX, double localY) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Override
+    @Deprecated
+    protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) {
+        return getMesh().impl_computeIntersects(pickRay, pickResult, this, getCullFace(), true);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/PredefinedMeshManager.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,179 @@
+/*
+ * 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 javafx.scene.shape;
+
+import java.util.HashMap;
+
+final class PredefinedMeshManager {
+
+    private static final PredefinedMeshManager INSTANCE = new PredefinedMeshManager();
+    private static final int INITAL_CAPACITY = 17; // TODO
+    private static final float LOAD_FACTOR = 0.75f;
+    
+    private HashMap<Integer, TriangleMesh> boxCache = null;
+    private HashMap<Integer, TriangleMesh> sphereCache = null;
+    private HashMap<Integer, TriangleMesh> cylinderCache = null;
+
+    private PredefinedMeshManager() {}
+
+    static PredefinedMeshManager getInstance() {
+        return INSTANCE;
+    }
+
+    synchronized TriangleMesh getBoxMesh(float w, float h, float d, int key) {
+        if (boxCache == null) {
+            boxCache = BoxCacheLoader.INSTANCE;
+        }
+
+        TriangleMesh mesh = boxCache.get(key);
+        if (mesh == null) {
+            mesh = Box.createMesh(w, h, d);
+            boxCache.put(key, mesh);
+        } else {
+            mesh.incRef();
+        }
+        return mesh;
+    }
+
+    synchronized TriangleMesh getSphereMesh(float r, int div, int key) {
+        if (sphereCache == null) {
+            sphereCache = SphereCacheLoader.INSTANCE;
+        }
+
+        TriangleMesh mesh = sphereCache.get(key);
+        if (mesh == null) {
+            mesh = Sphere.createMesh(div, r);
+            sphereCache.put(key, mesh);
+        } else {
+            mesh.incRef();
+        }
+        return mesh;
+    }
+
+    synchronized TriangleMesh getCylinderMesh(float h, float r, int div, int key) {
+        if (cylinderCache == null) {
+            cylinderCache = CylinderCacheLoader.INSTANCE;
+        }
+
+        TriangleMesh mesh = cylinderCache.get(key);
+        if (mesh == null) {
+            mesh = Cylinder.createMesh(div, h, r);
+            cylinderCache.put(key, mesh);
+        } else {
+            mesh.incRef();
+        }
+        return mesh;
+    }
+
+    synchronized void invalidateBoxMesh(int key) {
+        if (boxCache != null) {
+            TriangleMesh mesh = boxCache.get(key);
+            if (mesh != null) {
+                mesh.decRef();
+                int count = mesh.getRefCount();
+                if (count == 0) {
+                    boxCache.remove(key);
+                }
+            }
+        }
+    }
+
+    synchronized void invalidateSphereMesh(int key) {
+        if (sphereCache != null) {
+            TriangleMesh mesh = sphereCache.get(key);
+            if (mesh != null) {
+                mesh.decRef();
+                int count = mesh.getRefCount();
+                if (count == 0) {
+                    sphereCache.remove(key);
+                }
+            }
+        }
+    }
+
+    synchronized void invalidateCylinderMesh(int key) {
+        if (cylinderCache != null) {
+            TriangleMesh mesh = cylinderCache.get(key);
+            if (mesh != null) {
+                mesh.decRef();
+                int count = mesh.getRefCount();
+                if (count == 0) {
+                    cylinderCache.remove(key);
+                }
+            }
+        }
+    }
+
+    synchronized void dispose() {
+        // just clearing references to them
+        if (boxCache != null) {
+            boxCache.clear();
+        }
+        if (sphereCache != null) {
+            sphereCache.clear();
+        }
+        if (cylinderCache != null) {
+            cylinderCache.clear();
+        }
+    }
+
+    // for testing purpose
+    synchronized void printStats() {
+        if (boxCache != null) {
+            System.out.println("BoxCache size:  " +  boxCache.size());
+        }
+        
+        if (sphereCache != null) {
+            System.out.println("SphereCache size:    " + sphereCache.size());
+        }
+
+        if (cylinderCache != null) {
+            System.out.println("CylinderCache size:    " + cylinderCache.size());
+        }
+    }
+
+    private final static class BoxCacheLoader {
+
+        // lazy & thread-safe instantiation
+        private static final HashMap<Integer, TriangleMesh>
+                INSTANCE = new HashMap<Integer, TriangleMesh>(INITAL_CAPACITY, LOAD_FACTOR);
+    }
+
+    private final static class SphereCacheLoader {
+
+        // lazy & thread-safe instantiation
+        private static final HashMap<Integer, TriangleMesh>
+                INSTANCE = new HashMap<Integer, TriangleMesh>(INITAL_CAPACITY, LOAD_FACTOR);
+    }
+
+    private final static class CylinderCacheLoader {
+
+        // lazy & thread-safe instantiation
+        private static final HashMap<Integer, TriangleMesh>
+                INSTANCE = new HashMap<Integer, TriangleMesh>(INITAL_CAPACITY, LOAD_FACTOR);
+    }
+
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/Shape3D.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,237 @@
+/*
+ * 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 javafx.scene.shape;
+
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.BoxBounds;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.jmx.MXNodeAlgorithm;
+import com.sun.javafx.jmx.MXNodeAlgorithmContext;
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.sg.PGPhongMaterial;
+import com.sun.javafx.sg.PGShape3D;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.Node;
+import javafx.scene.paint.Material;
+
+
+/**
+ * The {@code Shape3D} base class provides definitions of common properties for
+ * objects that represent some form of 3D geometric shape.  These properties
+ * include:
+ * <ul>
+ * <li>The {@link Material} to be applied to the fillable interior of the
+ * shape or the outline of the shape (see {@link #setMaterial}).
+ * <li>The draw model properties that defines how to render its geometry (see {@link #setDrawMode}).
+ * <li>The face culling properties that defines which face to cull (see {@link #setCullFace}).
+ * </ul>
+ *
+ * @since JavaFX 8
+ */
+public abstract class Shape3D extends Node {
+    // NOTE: Need a way to specify shape tessellation resolution, may use metric relate to window resolution
+    // Will not support dynamic refinement in FX8
+    
+    // TODO: 3D - May provide user convenient utility to compose images in a single image for shapes such as Box or Cylinder
+
+    protected Shape3D() {
+    }
+
+    PredefinedMeshManager manager = PredefinedMeshManager.getInstance();
+    int key = 0;
+
+    /**
+     * Defines the material this {@code Shape3D}.
+     * The default material is null. XXX Info about null Material XXX
+     *
+     * @defaultValue null
+     */
+    private ObjectProperty<Material> material;
+
+    public final void setMaterial(Material value) {
+        materialProperty().set(value);
+    }
+
+    public final Material getMaterial() {
+        return material == null ? null : material.get();
+    }
+
+    private final ChangeListener<Boolean> materialListener = new ChangeListener<Boolean>() {
+        @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+            if (newValue) {
+                impl_markDirty(DirtyBits.MATERIAL_PROPERTY);
+                //impl_geomChanged();
+            }
+        }
+    };
+ 
+    public final ObjectProperty<Material> materialProperty() {
+        if (material == null) {
+            material = new SimpleObjectProperty<Material>(Shape3D.this,
+                    "material") {
+
+                private Material old = null;
+                @Override protected void invalidated() {
+                    if (old != null) {
+                        old.impl_dirtyProperty().removeListener(materialListener);
+                    }
+                    Material newMaterial = get();
+                    if (newMaterial != null) {
+                        newMaterial.impl_dirtyProperty().addListener(materialListener);
+                    }
+                    impl_markDirty(DirtyBits.MATERIAL);
+                    impl_geomChanged();
+                    old = newMaterial;
+                }
+            };
+        }
+        return material;
+    }
+
+    /**
+     * Defines the drawMode this {@code Shape3D}.
+     *
+     * @defaultValue DrawMode.FILL
+     */
+    private ObjectProperty<DrawMode> drawMode;
+
+    public final void setDrawMode(DrawMode value) {
+        drawModeProperty().set(value);
+    }
+
+    public final DrawMode getDrawMode() {
+        return drawMode == null ? DrawMode.FILL : drawMode.get();
+    }
+
+    public final ObjectProperty<DrawMode> drawModeProperty() {
+        if (drawMode == null) {
+            drawMode = new SimpleObjectProperty<DrawMode>(Shape3D.this,
+                    "drawMode", DrawMode.FILL) {
+
+                @Override
+                protected void invalidated() {
+                    impl_markDirty(DirtyBits.NODE_DRAWMODE);
+                }
+            };
+        }
+        return drawMode;
+    }  
+
+    /**
+     * Defines the drawMode this {@code Shape3D}.
+     *
+     * @defaultValue CullFace.BACK
+     */
+    private ObjectProperty<CullFace> cullFace;
+
+    public final void setCullFace(CullFace value) {
+        cullFaceProperty().set(value);
+    }
+
+    public final CullFace getCullFace() {
+        return cullFace == null ? CullFace.BACK : cullFace.get();
+    }
+
+    public final ObjectProperty<CullFace> cullFaceProperty() {
+        if (cullFace == null) {
+            cullFace = new SimpleObjectProperty<CullFace>(Shape3D.this,
+                    "cullFace", CullFace.BACK) {
+
+                @Override
+                protected void invalidated() {
+                    impl_markDirty(DirtyBits.NODE_CULLFACE);
+                }
+            };
+        }
+        return cullFace;
+    }  
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+        // TODO: 3D - Evaluate this logic
+        return new BoxBounds(0, 0, 0, 0, 0, 0);
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeContains(double localX, double localY) {
+        return false;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public void impl_updatePG() {
+        super.impl_updatePG();
+
+        // TODO: 3D - Why do we have separate dirty bits for MATERIAL
+        // and MATERIAL_PROPERTY ?
+        Material material = getMaterial();
+        if (impl_isDirty(DirtyBits.MATERIAL_PROPERTY) && material != null) {
+            material.impl_updatePG();
+        }
+        PGShape3D pgShape3D = (PGShape3D) impl_getPGNode();
+        if (impl_isDirty(DirtyBits.MATERIAL)) {
+            if (material != null) {
+                material.impl_updatePG(); // new material should be updated
+                pgShape3D.setMaterial((PGPhongMaterial) material.impl_getPGMaterial());
+            } else {
+                pgShape3D.setMaterial(null);
+            }
+        }
+        if (impl_isDirty(DirtyBits.NODE_DRAWMODE)) {
+            pgShape3D.setDrawMode(getDrawMode());
+        }
+        if (impl_isDirty(DirtyBits.NODE_CULLFACE)) {
+            pgShape3D.setCullFace(getCullFace());
+        }
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/Sphere.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,405 @@
+/*
+ * 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 javafx.scene.shape;
+
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.geom.Vec3d;
+import com.sun.javafx.geom.transform.BaseTransform;
+import com.sun.javafx.scene.DirtyBits;
+import com.sun.javafx.scene.input.PickResultChooser;
+import com.sun.javafx.sg.PGNode;
+import com.sun.javafx.sg.PGSphere;
+import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
+import javafx.scene.input.PickResult;
+import javafx.scene.transform.Rotate;
+
+/**
+ * The {@code Sphere} class defines a 3 dimensional sphere with the specified size.
+ * A {@code Sphere} is a 3D geometry primitive created with a given radius.
+ * It is centered at the origin.
+ *
+ * @since JavaFX 8    
+ */
+public class Sphere extends Shape3D {
+
+    static final int DEFAULT_DIVISIONS = 64;
+    static final double DEFAULT_RADIUS = 1;
+    private int divisions = DEFAULT_DIVISIONS;
+    private TriangleMesh mesh;
+
+    /**  
+     * Creates a new instance of {@code Sphere} of radius of 1.0.
+     * The resolution defaults to MID_RESOLUTION divisions along sphere's axes.
+     */
+    public Sphere() {
+        this(DEFAULT_RADIUS, DEFAULT_DIVISIONS);
+    }
+
+    /**
+     * Creates a new instance of {@code Sphere} of a given radius.
+     * The resolution defaults to MID_RESOLUTION divisions along sphere's axes.
+     *
+     * @param radius Radius
+     */
+    public Sphere(double radius) {
+        this(radius, DEFAULT_DIVISIONS);
+    }
+
+    /**  
+     * Creates a new instance of {@code Sphere} of a given radius and number
+     * of divisions.
+     * The resolution is defined in terms of number of subdivisions along the
+     * sphere's axes. More divisions lead to more finely tesselated objects.
+     * 
+     * @param radius Radius
+     * @param divisions Divisions     
+     */
+    public Sphere(double radius, int divisions) {
+        this.divisions = divisions;
+        setRadius(radius);
+    }
+
+    /**
+     * Defines the radius of the Sphere.
+     *
+     * @defaultValue 1.0
+     */
+    private DoubleProperty radius;
+
+    public final void setRadius(double value) {
+        radiusProperty().set(value);
+    }
+
+    public final double getRadius() {
+        return radius == null ? 1 : radius.get();
+    }
+
+    public final DoubleProperty radiusProperty() {
+        if (radius == null) {
+            radius = new SimpleDoubleProperty(Sphere.this, "radius", DEFAULT_RADIUS) {
+                @Override
+                public void invalidated() {
+                    impl_markDirty(DirtyBits.MESH_GEOM);
+                    manager.invalidateSphereMesh(key);
+                    key = 0;
+                }
+            };
+        }
+        return radius;
+    }
+
+    /**
+     * Retrieves the divisions attribute use to generate this sphere.
+     *
+     * @return the divisions attribute.
+     */
+    public int getDivisions() {
+        return divisions;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected PGNode impl_createPGNode() {
+        return Toolkit.getToolkit().createPGSphere();
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    public void impl_updatePG() {
+        super.impl_updatePG();
+        if (impl_isDirty(DirtyBits.MESH_GEOM)) {
+            PGSphere pgSphere = (PGSphere) impl_getPGNode();
+            if (key == 0) {
+                key = generateKey((float) getRadius(), divisions);
+            }
+            mesh = manager.getSphereMesh((float) getRadius(), divisions, key);
+            mesh.impl_updatePG();
+            pgSphere.updateMesh(mesh.impl_getPGTriangleMesh());
+        }
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
+        float r = (float) getRadius();
+        
+        bounds = bounds.deriveWithNewBounds(-r, -r, -r, r, r ,r);
+        bounds = tx.transform(bounds, bounds);
+        return bounds;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeContains(double localX, double localY) {
+        double r = getRadius();
+        double n2 = localX * localX + localY * localY;
+        return n2 <= r * r;
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult) {
+
+        final boolean exactPicking = divisions < DEFAULT_DIVISIONS && mesh != null;
+
+        final double r = getRadius();
+        final Vec3d dir = pickRay.getDirectionNoClone();
+        final double dirX = dir.x;
+        final double dirY = dir.y;
+        final double dirZ = dir.z;
+        final Vec3d origin = pickRay.getOriginNoClone();
+        final double originX = origin.x;
+        final double originY = origin.y;
+        final double originZ = origin.z;
+
+        // Coeficients of a quadratic equation desribing intersection with sphere
+        final double a = dirX * dirX + dirY * dirY + dirZ * dirZ;
+        final double b = 2 * (dirX * originX + dirY * originY + dirZ * originZ);
+        final double c = originX * originX + originY * originY + originZ * originZ - r * r;
+
+        final double discriminant = b * b - 4 * a * c;
+        if (discriminant < 0) {
+            // No real roots of the equation, missed the shape
+            return false;
+        }
+
+        final double distSqrt = Math.sqrt(discriminant);
+        final double q = (b < 0) ? (-b - distSqrt) / 2.0 : (-b + distSqrt) / 2.0;
+
+        double t0 = q / a;
+        double t1 = c / q;
+
+        if (t0 > t1) {
+            final double temp = t0;
+            t0 = t1;
+            t1 = temp;
+        }
+
+        if (t1 < 0) {
+            // the sphere is behind us
+            return false;
+        }
+
+        double t = t0;
+        final CullFace cullFace = getCullFace();
+        if (t0 < 0 || cullFace == CullFace.FRONT) {
+            if (getCullFace() != CullFace.BACK) {
+                // picking the back wall
+                t = t1;
+            } else {
+                // we are inside the sphere with the back wall culled, but the
+                // exact picking still needs to be done because the front faced
+                // triangles may still be in front of us
+                if (!exactPicking) {
+                    return false;
+                }
+            }
+        }
+
+        if (exactPicking) {
+            return mesh.impl_computeIntersects(pickRay, pickResult, this, cullFace, false);
+        }
+
+        if (pickResult.isCloser(t)) {
+            final Point3D point = PickResultChooser.computePoint(pickRay, t);
+
+            // computing texture coords
+            final Point3D proj = new Point3D(point.getX(), 0, point.getZ());
+            final Point3D cross = proj.crossProduct(Rotate.Z_AXIS);
+            double angle = proj.angle(Rotate.Z_AXIS);
+            if (cross.getY() > 0) {
+                angle = 360 - angle;
+            }
+            Point2D txtCoords = new Point2D(angle / 360, 0.5 + point.getY() / (2 * r));
+
+            pickResult.offer(this, t, PickResult.FACE_UNDEFINED, point, txtCoords);
+        }
+        return true;
+    }
+
+    private static int correctDivisions(int div) {
+        return ((div + 3) / 4) * 4;
+    }
+
+    static TriangleMesh createMesh(int div, float r) {
+        div = correctDivisions(div);
+
+        if (div==0 || r==0) {
+            return null;
+        }
+
+        final int div2 = div / 2;
+
+        final int nPoints = (div + 1) * (div2 - 1) + 2;
+        final int nTPoints = (div + 1) * (div2 - 1) + div * 2;
+        final int nFaces = div * (div2 - 2) * 2 + div * 2;
+
+        final float rDiv = 1.f / div;
+
+        float points[] = new float[nPoints * 3];
+        float tPoints[] = new float[nTPoints * 2];
+        int faces[] = new int[nFaces * 6];
+        int smoothing[] = new int[nFaces];
+
+        int pPos = 0, tPos = 0;
+
+        for (int y = 0; y < div2 - 1; ++y) {
+            float va = rDiv * (y + 1 - div2 / 2) * 2 * (float) Math.PI;
+            float sin_va = (float) Math.sin(va);
+            float cos_va = (float) Math.cos(va);
+
+            float ty = 0.5f + sin_va * 0.5f;
+            for (int i = 0; i <= div; ++i) {
+                double a = (i < div) ? rDiv * i * 2 * (float) Math.PI : 0;
+                float hSin = (float) Math.sin(a);
+                float hCos = (float) Math.cos(a);
+                points[pPos + 0] = hSin * cos_va * r;
+                points[pPos + 2] = hCos * cos_va * r;
+                points[pPos + 1] = sin_va * r;
+                tPoints[tPos + 0] = rDiv * i;
+                tPoints[tPos + 1] = ty;
+                pPos += 3; tPos += 2;
+            }
+        }
+
+        points[pPos + 0] = 0;
+        points[pPos + 1] = -r;
+        points[pPos + 2] = 0;
+        points[pPos + 3] = 0;
+        points[pPos + 4] = r;
+        points[pPos + 5] = 0;
+        pPos += 6;
+
+        int pS = (div2 - 1) * (div + 1);
+
+        float textureDelta = 1.f / 256;
+        for (int i = 0; i != div; ++i) {
+            tPoints[tPos + 0] = rDiv * (0.5f + i);
+            tPoints[tPos + 1] = textureDelta;
+            tPos += 2;
+        }
+
+        for (int i = 0; i != div; ++i) {
+            tPoints[tPos + 0] = rDiv * (0.5f + i);
+            tPoints[tPos + 1] = 1 - textureDelta;
+            tPos += 2;
+        }
+
+        int fIndex = 0;
+        for (int y = 0; y < div2 - 2; ++y) {
+            for (int x = 0; x < div; ++x) {
+                int p0 = y * (div + 1) + x;
+                int p1 = p0 + 1;
+                int p2 = p0 + (div + 1);
+                int p3 = p1 + (div + 1);
+
+                // add p0, p1, p2
+                faces[fIndex+0] = p0;
+                faces[fIndex+1] = p0;
+                faces[fIndex+2] = p1;
+                faces[fIndex+3] = p1;
+                faces[fIndex+4] = p2;
+                faces[fIndex+5] = p2;
+                fIndex += 6;
+
+                // add p3, p2, p1
+                faces[fIndex+0] = p3;
+                faces[fIndex+1] = p3;
+                faces[fIndex+2] = p2;
+                faces[fIndex+3] = p2;
+                faces[fIndex+4] = p1;
+                faces[fIndex+5] = p1;
+                fIndex += 6;
+            }
+        }
+
+        int p0 = pS;
+        int tB = pS;
+        for (int x = 0; x != div; ++x) {
+            int p2 = x, p1 = x + 1, t0 = tB + x;
+            faces[fIndex+0] = p0;
+            faces[fIndex+1] = t0;
+            faces[fIndex+2] = p1;
+            faces[fIndex+3] = p1;
+            faces[fIndex+4] = p2;
+            faces[fIndex+5] = p2;
+            fIndex += 6;
+        }
+
+        p0 = p0 + 1;
+        tB = tB + div;
+        int pB = (div2 - 2) * (div + 1);
+
+        for (int x = 0; x != div; ++x) {
+            int p1 = pB + x, p2 = pB + x + 1, t0 = tB + x;
+            faces[fIndex+0] = p0;
+            faces[fIndex+1] = t0;
+            faces[fIndex+2] = p1;
+            faces[fIndex+3] = p1;
+            faces[fIndex+4] = p2;
+            faces[fIndex+5] = p2;
+            fIndex += 6;
+        }
+
+        for (int i = 0; i < nFaces; ++i) {
+            smoothing[i] = 1;
+        }
+
+        TriangleMesh m = new TriangleMesh(points, tPoints, faces);
+        m.setFaceSmoothingGroups(smoothing);
+        return m;
+    }
+
+    private static int generateKey(float r, int div) {
+        int hash = 5;
+        hash = 23 * hash + Float.floatToIntBits(r);
+        hash = 23 * hash + div;
+        return hash;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/src/javafx/scene/shape/TriangleMesh.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,1044 @@
+/*
+ * 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 javafx.scene.shape;
+
+import com.sun.javafx.geom.BaseBounds;
+import com.sun.javafx.geom.BoxBounds;
+import com.sun.javafx.geom.PickRay;
+import com.sun.javafx.geom.Vec3d;
+import com.sun.javafx.scene.input.PickResultChooser;
+import com.sun.javafx.sg.PGTriangleMesh;
+import com.sun.javafx.tk.Toolkit;
+import javafx.beans.property.ReadOnlyIntegerProperty;
+import javafx.beans.property.ReadOnlyIntegerWrapper;
+import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
+import javafx.scene.Node;
+import javafx.scene.input.PickResult;
+import javafx.scene.transform.Affine;
+import javafx.scene.transform.NonInvertibleTransformException;
+import javafx.scene.transform.Rotate;
+
+/**
+ * Defines a 3D geometric object contains separate arrays of points, 
+ * texture coordinates, and faces that describe a triangulated 
+ * geometric mesh.
+ *<p>
+ * Note that the term point, as used in the method names and method
+ * descriptions, actually refers to a set of x, y, and z point
+ * representing the position of a single vertex. The term points (plural) is
+ * used to indicate sets of x, y, and z points for multiple vertices.
+ * Similarly, the term texCoord is used to indicate a set of u and v texture 
+ * coordinates for a single vertex, while the term texCoords (plural) is used
+ * to indicate sets of u and v texture coordinates for multiple vertices.
+ * Lastly, the term face is used to indicate 3 set of interleaving points 
+ * and texture coordinates that together represent the geometric topology of a 
+ * single triangle, while the term faces (plural) is used to indicate sets of 
+ * triangles (each represent by a face).
+ * <p>
+ * For example, the faces that represent a single textured rectangle, using 2 triangles,
+ * has the following data order: [
+ * <p>
+ * p0, t0, p1, t1, p3, t3,  // First triangle of a textured rectangle
+ * <p>
+ * p1, t1, p2, t2, p3, t3   // Second triangle of a textured rectangle
+ * <p>
+ * ]
+ * <p>
+ * where p0, p1, p2 and p3 are indices into the points array, and t0, t1, t2
+ * and t3 are indices into the texCoords array.
+ * 
+ * @since JavaFX 8
+ */
+public class TriangleMesh extends Mesh {
+    /*
+     * setSmoothingBit - 32 bit (see existing 3D concept implementation of 
+     * smoothness by grouping for details)
+        opposite faces can share the same bit
+     */
+
+    public static final int NUM_COMPONENTS_PER_POINT = 3;
+    public static final int NUM_COMPONENTS_PER_TEXCOORD = 2;
+    public static final int NUM_COMPONENTS_PER_FACE = 6;
+
+    private float[] points;
+    private float[] texCoords;
+    private int[] faces;
+    private int[] faceSmoothingGroups;
+
+    private boolean pointsDirty = true;
+    private boolean texCoordsDirty = true;
+    private boolean facesDirty = true;
+    private boolean fsgDirty = true;
+
+    // Partial Update constants and variables
+    private static final int RANGE_INDEX = 0;
+    private static final int RANGE_START = 1;
+    private static final int RANGE_LENGTH = 2;
+    private static final int MAX_RANGE_SIZE = 3;
+    private int refCount = 1;
+    private boolean pointUpdateRange = false;   
+    private int[] pointRangeInfos;
+    private boolean texCoordUpdateRange = false;
+    private int[] texCoordRangeInfos;
+    private boolean faceUpdateRange = false;
+    private int[] faceRangeInfos;
+    private boolean fsgUpdateRange = false;
+    private int[] fsgRangeInfos;
+
+    private BaseBounds cachedBounds;
+
+    /**
+     * Creates a new instance of {@code TriangleMesh} class.
+     */
+    public TriangleMesh() {
+    }
+    
+    /**
+     * Creates a new instance of {@code TriangleMesh} class.
+     * TODO: 3D - doc. follows array semantic
+     *
+     * @param points points array (points.length must be divisible by NUM_COMPONENTS_PER_POINT)
+     * @param texCoords texCoords array (texCoords.length must be divisible by
+     * NUM_COMPONENTS_PER_TEXCOORD)
+     * @param faces faces (or triangles) array (faces.length must be divisible
+     * by NUM_COMPONENTS_PER_FACE)
+     */
+    public TriangleMesh(float[] points, float[] texCoords, int[] faces) {
+        setPoints(points);
+        setTexCoords(texCoords);
+        setFaces(faces);
+    }
+
+    /**
+     * The total number of points of this {@code TriangleMesh}
+     */
+    private ReadOnlyIntegerWrapper pointCount;
+
+    final void setPointCount(int value) {
+        pointCountPropertyImpl().set(value);
+    }
+
+    /**
+     * Retrieve total number of points of this {@code TriangleMesh}
+     *
+     * @return the total number of points
+     */
+    public final int getPointCount() {
+        return pointCount == null ? 0 : pointCount.get();
+    }
+
+    public ReadOnlyIntegerProperty pointCountProperty() {
+        return pointCountPropertyImpl().getReadOnlyProperty();
+    }
+
+    private ReadOnlyIntegerWrapper pointCountPropertyImpl() {
+        if (pointCount == null) {
+            pointCount = new ReadOnlyIntegerWrapper(this, "pointCount");
+        }
+        return pointCount;
+    }
+
+    /**
+     * The total number of texture coordinates of this {@code TriangleMesh}
+     */
+    private ReadOnlyIntegerWrapper texCoordCount;
+
+    final void setTexCoordCount(int value) {
+        texCoordCountPropertyImpl().set(value);
+    }
+
+    /**
+     * Retrieve total number of texture coordinates of this {@code TriangleMesh}
+     *
+     * @return the total number of texture coordinates
+     */
+    public final int getTexCoordCount() {
+        return texCoordCount == null ? 0 : texCoordCount.get();
+    }
+
+    public ReadOnlyIntegerProperty texCoordCountProperty() {
+        return texCoordCountPropertyImpl().getReadOnlyProperty();
+    }
+
+    private ReadOnlyIntegerWrapper texCoordCountPropertyImpl() {
+        if (texCoordCount == null) {
+            texCoordCount = new ReadOnlyIntegerWrapper(this, "texCoordCount");
+        }
+        return texCoordCount;
+    }
+
+    /**
+     * The total number of faces of this {@code TriangleMesh}
+     */
+    private ReadOnlyIntegerWrapper faceCount;
+
+    final void setFaceCount(int value) {
+        faceCountPropertyImpl().set(value);
+    }
+
+    /**
+     * Retrieve total number of faces of this {@code TriangleMesh}
+     *
+     * @return the total number of faces
+     */
+    public final int getFaceCount() {
+        return faceCount == null ? 0 : faceCount.get();
+    }
+
+    public ReadOnlyIntegerProperty faceCountProperty() {
+        return faceCountPropertyImpl().getReadOnlyProperty();
+    }
+
+    private ReadOnlyIntegerWrapper faceCountPropertyImpl() {
+        if (faceCount == null) {
+            faceCount = new ReadOnlyIntegerWrapper(this, "faceCount");
+        }
+        return faceCount;
+    }
+    
+    /**
+     * The total number of faceSmoothingGroups of this {@code TriangleMesh}
+     */
+    private ReadOnlyIntegerWrapper faceSmoothingGroupCount;
+
+    final void setFaceSmoothingGroupCount(int value) {
+        faceSmoothingGroupCountPropertyImpl().set(value);
+    }
+
+    /**
+     * Retrieve total number of faceSmoothingGroups of this {@code TriangleMesh}
+     *
+     * @return the total number of faceSmoothingGroups
+     */
+    public final int getFaceSmoothingGroupCount() {
+        return faceSmoothingGroupCount == null ? 0 : faceSmoothingGroupCount.get();
+    }
+
+    public ReadOnlyIntegerProperty faceSmoothingGroupCountProperty() {
+        return faceSmoothingGroupCountPropertyImpl().getReadOnlyProperty();
+    }
+
+    private ReadOnlyIntegerWrapper faceSmoothingGroupCountPropertyImpl() {
+        if (faceSmoothingGroupCount == null) {
+            faceSmoothingGroupCount = new ReadOnlyIntegerWrapper(this, "faceSmoothingGroupCount");
+        }
+        return faceSmoothingGroupCount;
+    }
+
+    /**
+     * Sets the points of this {@code TriangleMesh}
+     * 
+     * @param points source array of NUM_COMPONENTS_PER_POINT * n values containing n new points.
+     */
+    public final void setPoints(float[] points) {
+        // Check that points.length is divisible by NUM_COMPONENTS_PER_POINT
+        if ((points.length % NUM_COMPONENTS_PER_POINT) != 0) {
+            throw new IllegalArgumentException("points.length has to be divisible by NUM_COMPONENTS_PER_POINT." 
+                    + " It is to store multiple x, y, and z coordinates of this mesh");
+        }
+
+        if ((this.points == null) || (this.points.length < points.length)) {
+            this.points = new float[points.length];
+        }
+        System.arraycopy(points, 0, this.points, 0, points.length);
+        // Store the valid point count.
+        // Note this.points.length can be bigger than points.length.
+        setPointCount(points.length / NUM_COMPONENTS_PER_POINT);
+
+        pointsDirty = true;
+        setDirty(true);
+    }
+    
+    /**
+     * Sets the points associated with this {@code TriangleMesh}
+     * starting at the specified {@code index} using data in {@code points} 
+     * starting at index {@code start} for {@code length} number of points.
+     * 
+     * @param index the starting destination index in this TriangleMesh's points array
+     * @param points source array of floats containing the new points
+     * @param start starting source index in the points array.
+     * @param length number of point elements to be copied.
+     */
+    public final void setPoints(int index, float[] points,
+                      int start, int length) {
+
+        if (index < 0 || start < 0 || length < 0) {
+            throw new IllegalArgumentException("index, start and length have to be non-zero");
+        }
+        int startOffset = start * NUM_COMPONENTS_PER_POINT;
+        int lengthInFloatUnit = length * NUM_COMPONENTS_PER_POINT;
+        if ((startOffset >= points.length) || ((startOffset + lengthInFloatUnit) > points.length)) {
+            throw new IllegalArgumentException("start or (start + length) is out of range for input points");
+        }
+        int indexOffset = index * NUM_COMPONENTS_PER_POINT;
+        int pointCountInFloatUnit = getPointCount() * NUM_COMPONENTS_PER_POINT;
+        if ((indexOffset >= pointCountInFloatUnit) || 
+                ((indexOffset + lengthInFloatUnit) > pointCountInFloatUnit)) {
+            throw new IllegalArgumentException("index or (index + length) is out of range for this triangle mesh's points");
+        }
+        System.arraycopy(points, startOffset, this.points, indexOffset, lengthInFloatUnit);
+
+        pointsDirty = pointUpdateRange = true;
+        if (pointRangeInfos == null) {
+            pointRangeInfos = new int[MAX_RANGE_SIZE];
+        }
+        pointRangeInfos[RANGE_INDEX] = index;
+        pointRangeInfos[RANGE_START] = start;
+        pointRangeInfos[RANGE_LENGTH] = length;
+        setDirty(true);
+    }
+    
+    /**
+     * Gets the points of this {@code TriangleMesh}
+     *
+     * @param points a float array that will receive the points
+     * if it not null and has sufficient capacity.
+     * @return a float array of points
+     */
+    public final float[] getPoints(float[] points) {
+        if (this.points == null) {
+            return null;
+        }
+        int pointCountInFloatUnit = getPointCount() * NUM_COMPONENTS_PER_POINT;
+        if ((points == null) || (pointCountInFloatUnit > points.length)) {
+            points = new float[pointCountInFloatUnit];
+        }
+        System.arraycopy(this.points, 0, points, 0, pointCountInFloatUnit);
+        return points;
+    }
+
+    /**
+     * Gets the points associated with this {@code TriangleMesh} starting at the
+     * specified {@code index} for {@code length} number of points.
+     * 
+     * @param index starting source points index in this {@code TriangleMesh}
+     * @param points destination array that will receive this {@code TriangleMesh}'s points data
+     * @param length number of point elements to be copied
+     * @return a float array of points
+     */
+    public final float[] getPoints(int index, float[] points, int length) {
+        if (index < 0 || length < 0) {
+            throw new IllegalArgumentException("index and length have to be non-zero");
+        }
+
+        int lengthInFloatUnit = length * NUM_COMPONENTS_PER_POINT;
+        if (lengthInFloatUnit > points.length) {
+            throw new IllegalArgumentException("length is out of range for input points");
+        }
+        int indexOffset = index * NUM_COMPONENTS_PER_POINT;
+        int pointCountInFloatUnit = getPointCount() * NUM_COMPONENTS_PER_POINT;
+        if ((indexOffset >= pointCountInFloatUnit) || 
+                ((indexOffset + lengthInFloatUnit) > pointCountInFloatUnit)) {
+            throw new IllegalArgumentException("index or (index + length) is out of range for this triangle mesh's points");
+        }
+
+        if (this.points == null) {
+            return null;
+        }
+        System.arraycopy(this.points, indexOffset, points, 0, lengthInFloatUnit);
+        return points;
+    }
+    
+    /**
+     * Sets the texture coordinates of this {@code TriangleMesh}.
+     * 
+     * @param texCoords source array of NUM_COMPONENTS_PER_TEXCOORD * n values containing n new texCoords.
+     */
+    public final void setTexCoords(float[] texCoords) {
+        // Check that texCoords.length is divisible by NUM_COMPONENTS_PER_TEXCOORD
+        if ((texCoords.length % NUM_COMPONENTS_PER_TEXCOORD) != 0) {
+            throw new IllegalArgumentException("texCoords.length has to be divisible by NUM_COMPONENTS_PER_TEXCOORD."
+                    +" It is to store multiple u and v texture coordinates of this mesh");
+        }
+
+        if ((this.texCoords == null) || (this.texCoords.length < texCoords.length)) {
+            this.texCoords = new float[texCoords.length];
+        }
+        System.arraycopy(texCoords, 0, this.texCoords, 0, texCoords.length);
+        // Store the valid texCoords count.
+        // Note this.texCoords.length can be bigger than texCoords.length.
+        setTexCoordCount(texCoords.length / NUM_COMPONENTS_PER_TEXCOORD);
+
+        texCoordsDirty = true;
+        setDirty(true);
+    }
+
+    /**
+     * Sets the texture coordinates associated with this {@code TriangleMesh}
+     * starting at the specified {@code index} using data in {@code texCoords}
+     * starting at index {@code start} for {@code length} number of texCoords.
+     * 
+     * @param index the starting destination index in this TriangleMesh's texCoords array
+     * @param texCoords an float array containing the new texture coordinates
+     * @param start starting source index in the texture coordinates array
+     * @param length number of texCoord elements to be copied.
+     */
+    public final void setTexCoords(int index, float[] texCoords, int start,
+            int length) {
+        if (index < 0 || start < 0 || length < 0) {
+            throw new IllegalArgumentException("index, start and length have to be non-zero");
+        }
+        int startOffset = start * NUM_COMPONENTS_PER_TEXCOORD;
+        int lengthInFloatUnit = length * NUM_COMPONENTS_PER_TEXCOORD;
+        if ((startOffset >= texCoords.length) || ((startOffset + lengthInFloatUnit) > texCoords.length)) {
+            throw new IllegalArgumentException("start or (start + length) is out of range for input texCoords");
+        }
+        int indexOffset = index * NUM_COMPONENTS_PER_TEXCOORD;
+        int texCoordCountInFloatUnit = getTexCoordCount() * NUM_COMPONENTS_PER_TEXCOORD;
+        if ((indexOffset >= texCoordCountInFloatUnit) || 
+                ((indexOffset + lengthInFloatUnit) > texCoordCountInFloatUnit)) {
+            throw new IllegalArgumentException("index or (index + length) is out of range for this triangle mesh's texCoords");
+        }
+        System.arraycopy(texCoords, startOffset, this.texCoords, indexOffset, lengthInFloatUnit);
+      
+        texCoordsDirty = texCoordUpdateRange = true;
+        if (texCoordRangeInfos == null) {
+            texCoordRangeInfos = new int[MAX_RANGE_SIZE];
+        }
+        texCoordRangeInfos[RANGE_INDEX] = index;
+        texCoordRangeInfos[RANGE_START] = start;
+        texCoordRangeInfos[RANGE_LENGTH] = length;
+        setDirty(true);
+    }
+
+    /**
+     * Gets the texture coordinates of this {@code TriangleMesh}.
+     *
+     * @param texCoords a float array that will receive the texture coordinates
+     * if it not null and has sufficient capacity
+     * @return a float array of texture coordinates
+     */
+    public final float[] getTexCoords(float[] texCoords) {
+        if (this.texCoords == null) {
+            return null;
+        }
+
+        int texCoordCountInFloatUnit = getTexCoordCount() * NUM_COMPONENTS_PER_TEXCOORD;
+        if ((texCoords == null) || (texCoordCountInFloatUnit > texCoords.length)) {
+            texCoords = new float[texCoordCountInFloatUnit];
+        }
+        System.arraycopy(this.texCoords, 0, texCoords, 0, texCoordCountInFloatUnit);
+        return texCoords;
+    }
+
+    /**
+     * Gets the texture coordinates associated with this {@code TriangleMesh}
+     * starting at the specified {@code index} for {@code length} number of
+     * texCoords.
+     * 
+     * @param index starting source texCoords index in this {@code TriangleMesh}
+     * @param texCoords destination array that will receive this {@code TriangleMesh}'s texCoords data 
+     * @param length number of texCoord elements to be copied
+     * @return a float array of texture coordinates
+     */
+    public final float[] getTexCoords(int index, float[] texCoords, int length) {
+        if (index < 0 || length < 0) {
+            throw new IllegalArgumentException("index and length have to be non-zero");
+        }
+
+        int lengthInFloatUnit = length * NUM_COMPONENTS_PER_TEXCOORD;
+        if (lengthInFloatUnit > texCoords.length) {
+            throw new IllegalArgumentException("length is out of range for input texCoords");
+        }
+        int indexOffset = index * NUM_COMPONENTS_PER_TEXCOORD;
+        int texCoordCountInFloatUnit = getTexCoordCount() * NUM_COMPONENTS_PER_TEXCOORD;
+        if ((indexOffset >= texCoordCountInFloatUnit) || 
+                ((indexOffset + lengthInFloatUnit) > texCoordCountInFloatUnit)) {
+            throw new IllegalArgumentException("index or (index + length) is out of range for this triangle mesh's texCoords");
+        }
+
+        if (this.texCoords == null) {
+            return null;
+        }
+        System.arraycopy(this.texCoords, indexOffset, texCoords, 0, lengthInFloatUnit);
+        return texCoords;
+    }
+
+    /**
+     * Sets the faces, indices into the points and texCoords arrays,
+     * associated with this {@code TriangleMesh}.
+     * 
+     * @param faces source array of NUM_COMPONENTS_PER_FACE * n indices 
+     * (3 point indices and 3 texCood indices) containing n new faces
+     */
+    public final void setFaces(int[] faces) {
+        // Check that faces.length is divisible by NUM_COMPONENTS_PER_FACE
+        if ((faces.length % NUM_COMPONENTS_PER_FACE) != 0) {
+            throw new IllegalArgumentException("faces.length has to be divisible by NUM_COMPONENTS_PER_FACE.");
+        }
+
+        if ((this.faces == null) || (this.faces.length < faces.length)) {
+            this.faces = new int[faces.length];
+        }
+        System.arraycopy(faces, 0, this.faces, 0, faces.length);
+        // Store the valid face count.
+        // Note this.faces.length can be bigger than faces.length.
+        setFaceCount(faces.length / NUM_COMPONENTS_PER_FACE);
+
+        facesDirty = true;
+        setDirty(true);
+    }
+
+    /**
+     * Sets the faces, indices into the points and texCoords arrays, 
+     * associated with this {@code TriangleMesh}
+     * starting at the specified{@code index} using data in {@code faces} 
+     * starting at index {@code start} for {@code length} number of faces.
+     * 
+     * @param index the starting destination index in this TriangleMesh's faces array
+     * @param faces an int array containing the new interleaved vertices
+     * @param start starting source index in the faces array.
+     * @param length number of interleaved vertex elements to be copied
+     */
+    public final void setFaces(int index, int[] faces, int start, int length) {
+        if (index < 0 || start < 0 || length < 0) {
+            throw new IllegalArgumentException("index, start and length have to be non-zero");
+        }
+        int startOffset = start * NUM_COMPONENTS_PER_FACE;
+        int lengthInIntUnit = length * NUM_COMPONENTS_PER_FACE;
+        if ((startOffset >= faces.length) || ((startOffset + lengthInIntUnit) > faces.length)) {
+            throw new IllegalArgumentException("start or (start + length) is out of range for input faces");
+        }
+        int indexOffset = index * NUM_COMPONENTS_PER_FACE;
+        int faceCountInIntUnit = getFaceCount() * NUM_COMPONENTS_PER_FACE;
+        if ((indexOffset >= faceCountInIntUnit) || 
+                ((indexOffset + lengthInIntUnit) > faceCountInIntUnit)) {
+            throw new IllegalArgumentException("index or (index + length) is out of range for this triangle mesh's faces");
+        }
+        System.arraycopy(faces, startOffset, this.faces, indexOffset, lengthInIntUnit);
+
+        facesDirty = faceUpdateRange = true;
+        if (faceRangeInfos == null) {
+            faceRangeInfos = new int[MAX_RANGE_SIZE];
+        }
+        faceRangeInfos[RANGE_INDEX] = index;
+        faceRangeInfos[RANGE_START] = start;
+        faceRangeInfos[RANGE_LENGTH] = length;
+        setDirty(true);
+    }
+
+    /**
+     * Gets the faces, indices into the points and texCoords arrays, of this 
+     * {@code TriangleMesh}
+     *
+     * @param faces an int array that will receive the faces if it not null and 
+     * has sufficient capacity.
+     * @return an int array of faces
+     * 
+     */
+    public final int[] getFaces(int[] faces) {
+        if (this.faces == null) {
+            return null;
+        }
+
+        int faceCountInIntUnit = getFaceCount() * NUM_COMPONENTS_PER_FACE;
+        if ((faces == null) || (faceCountInIntUnit > faces.length)) {
+            faces = new int[faceCountInIntUnit];
+        }
+        System.arraycopy(this.faces, 0, faces, 0, faceCountInIntUnit);
+        return faces;
+    }
+
+    /**
+     * Gets the faces, indices into the points and texCoords arrays,
+     * associated with this {@code TriangleMesh} starting at the specified
+     * {@code index} for {@code length} number of faces.
+     * 
+     * @param index starting source faces index in this {@code TriangleMesh}
+     * @param faces destination array that will receive this {@code TriangleMesh}'s faces data
+     * @param length number of face elements to be copied
+     * @return an int array of faces
+     */
+    public final int[] getFaces(int index, int[] faces, int length) {
+        if (index < 0 || length < 0) {
+            throw new IllegalArgumentException("index and length have to be non-zero");
+        }
+
+        int lengthInIntUnit = length * NUM_COMPONENTS_PER_FACE;
+        if (lengthInIntUnit > faces.length) {
+            throw new IllegalArgumentException("length is out of range for input faces");
+        }
+        int indexOffset = index * NUM_COMPONENTS_PER_FACE;
+        int faceCountInIntUnit = getFaceCount() * NUM_COMPONENTS_PER_FACE;
+        if ((indexOffset >= faceCountInIntUnit) || 
+                ((indexOffset + lengthInIntUnit) > faceCountInIntUnit)) {
+            throw new IllegalArgumentException("index or (index + length) is out of range for this triangle mesh's faces");
+        }
+
+        if (this.faces == null) {
+            return null;
+        }
+        System.arraycopy(this.faces, indexOffset, faces, 0, lengthInIntUnit);
+        return faces;
+    }
+    
+    /**
+     * Sets the face smoothing group for each face in this {@code TriangleMesh}
+     * 
+     * TODO: 3D - if faceSmoothingGroups is null (default) --> smooth faces
+     * Note: faceSmoothingGroups.length must be equal to faces.length/NUM_COMPONENTS_PER_FACE.
+     * Error: Throw exception?
+     */
+    public final void setFaceSmoothingGroups(int[] faceSmoothingGroups) {
+        // Check that faceSmoothingGroups.length is 1/NUM_COMPONENTS_PER_FACE of faces.length
+        if (faceSmoothingGroups.length != (faces.length / NUM_COMPONENTS_PER_FACE)) {
+            throw new IllegalArgumentException("faceSmoothingGroups.length has to be equal to (faces.length / NUM_COMPONENTS_PER_FACE).");
+        }
+
+        if ((this.faceSmoothingGroups == null) || 
+                (this.faceSmoothingGroups.length < faceSmoothingGroups.length)) {
+            this.faceSmoothingGroups = new int[faceSmoothingGroups.length];
+        }
+        System.arraycopy(faceSmoothingGroups, 0, this.faceSmoothingGroups, 0, faceSmoothingGroups.length);
+        // Store the valid faceSmoothingGroup count.
+        // Note this.faceSmoothingGroups.length can be bigger than faceSmoothingGroups.length.
+        setFaceSmoothingGroupCount(faceSmoothingGroups.length);
+
+        fsgDirty = true;
+        setDirty(true);
+    }
+
+    /**
+     * Sets the faceSmoothingGroups associated with this {@code TriangleMesh}
+     * starting at the specified {@code index} using data in {@code faceSmoothingGroups} 
+     * starting at index {@code start} for {@code length} number of faceSmoothingGroups.
+     * 
+     * @param index the starting destination index in this TriangleMesh's faceSmoothingGroups array
+     * @param points source array of floats containing the new faceSmoothingGroups
+     * @param start starting source index in the faceSmoothingGroups array.
+     * @param length number of faceSmoothingGroup elements to be copied.
+     */
+    public final void setFaceSmoothingGroups(int index, int[] faceSmoothingGroups,
+                      int start, int length) {
+
+        if (index < 0 || start < 0 || length < 0) {
+            throw new IllegalArgumentException("index, start and length have to be non-zero");
+        }
+        
+        if ((start >= faceSmoothingGroups.length) || ((start + length) > faceSmoothingGroups.length)) {
+            throw new IllegalArgumentException("start or (start + length) is out of range for input faceSmoothingGroups");
+        }
+        int fsgCount = getFaceSmoothingGroupCount();
+        if ((index >= fsgCount) || 
+                ((index + length) > fsgCount)) {
+            throw new IllegalArgumentException("index or (index + length) is out of range for this triangle mesh's faceSmoothingGroups");
+        }
+        System.arraycopy(faceSmoothingGroups, start, this.faceSmoothingGroups, index, length);
+
+        fsgDirty = fsgUpdateRange = true;
+        if (fsgRangeInfos == null) {
+            fsgRangeInfos = new int[MAX_RANGE_SIZE];
+        }
+        fsgRangeInfos[RANGE_INDEX] = index;
+        fsgRangeInfos[RANGE_START] = start;
+        fsgRangeInfos[RANGE_LENGTH] = length;
+        setDirty(true);
+    }
+
+    /**
+     * Gets the face smoothing group for each face in this {@code TriangleMesh}
+     * @return an int array to smoothing group bits for each face
+     */
+    public final int[] getFaceSmoothingGroups(int[] faceSmoothingGroups) {
+        if (this.faceSmoothingGroups == null) {
+            return null;
+        }
+
+        int fsgCount = getFaceSmoothingGroupCount();
+        if ((faceSmoothingGroups == null) || 
+                (fsgCount > faceSmoothingGroups.length)) {
+            faceSmoothingGroups = new int[fsgCount];
+        }
+        System.arraycopy(this.faceSmoothingGroups, 0, faceSmoothingGroups, 0, fsgCount);
+        return faceSmoothingGroups;
+    }
+
+    /**
+     * Gets the face smoothing group for each face in this {@code TriangleMesh}
+     * starting at the specified {@code index} for {@code length} number of face
+     * smoothing groups.
+     * 
+     * @param index starting source face smoothing groups index in this {@code TriangleMesh}
+     * @param faceSmoothingGroups destination array that will receive this 
+     * {@code TriangleMesh}'s faceSmoothingGroups data
+     * @param length number of faceSmoothingGroup elements to be copied
+     * @return an int array of faceSmoothingGroups
+     */
+    public final int[] getFaceSmoothingGroups(int index, int[] faceSmoothingGroups, int length) {
+        if (index < 0 || length < 0) {
+            throw new IllegalArgumentException("index and length have to be non-zero");
+        }
+
+        if (length > faceSmoothingGroups.length) {
+            throw new IllegalArgumentException("length is out of range for input faceSmoothingGroups");
+        }
+        
+        int fsgCount = getFaceSmoothingGroupCount();
+        if ((index >= fsgCount) || ((index + length) > fsgCount)) {
+            throw new IllegalArgumentException("index or (index + length) is out of range for this triangle mesh's faceSmoothingGroups");
+        }
+
+        if (this.faceSmoothingGroups == null) {
+            return null;
+        }
+        System.arraycopy(this.faceSmoothingGroups, index, faceSmoothingGroups, 0, length);
+        return faceSmoothingGroups;
+    }
+
+    @Override
+    void setDirty(boolean value) {
+        super.setDirty(value);
+        if (!value) { // false
+            pointsDirty = false;
+            texCoordsDirty = false;
+            facesDirty = false;
+            fsgDirty = false;
+            pointUpdateRange = false;
+            texCoordUpdateRange = false;
+            faceUpdateRange = false;
+            fsgUpdateRange = false;
+            // We don't clear up XXXPartialUpdateInfos array since we will
+            // overwrite every element when we update the array.   
+        }
+    }
+
+    int getRefCount() {
+        return refCount;
+    }
+
+    synchronized void incRef() {
+        this.refCount += 1;
+    }
+
+    synchronized void decRef() {
+        this.refCount -= 1;
+        if (this.refCount == 0) {
+            release();
+        }
+    }
+
+    void release(){
+        // TODO: 3D - release native resoure
+    }
+
+    private PGTriangleMesh peer;
+    
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    /** The peer node created by the graphics Toolkit/Pipeline implementation */
+    PGTriangleMesh impl_getPGTriangleMesh() {
+        if (peer == null) {
+            peer = Toolkit.getToolkit().createPGTriangleMesh();
+        }
+        return peer;
+    }
+
+    @Override
+    PGTriangleMesh getPGMesh() {
+        return impl_getPGTriangleMesh();
+    }
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Deprecated
+    @Override
+    void impl_updatePG() {
+        if (!isDirty()) {
+            return;
+        }
+
+        PGTriangleMesh pgTriMesh = impl_getPGTriangleMesh();
+        // sync points 
+        if (pointsDirty) {
+            if (pointUpdateRange) {
+                pgTriMesh.setPoints(pointRangeInfos[RANGE_INDEX],
+                        points, pointRangeInfos[RANGE_START],
+                        pointRangeInfos[RANGE_LENGTH]);
+            } else {
+                pgTriMesh.setPoints(points);
+            }
+        }
+        // sync texCoords
+        if (texCoordsDirty) {
+            if (texCoordUpdateRange) {
+                pgTriMesh.setTexCoords(texCoordRangeInfos[RANGE_INDEX],
+                        texCoords, texCoordRangeInfos[RANGE_START],
+                        texCoordRangeInfos[RANGE_LENGTH]);
+            } else {
+                pgTriMesh.setTexCoords(texCoords);
+            }
+        }
+        // sync faces
+        if (facesDirty) {
+            if (faceUpdateRange) {
+                pgTriMesh.setFaces(faceRangeInfos[RANGE_INDEX],
+                        faces, faceRangeInfos[RANGE_START],
+                        faceRangeInfos[RANGE_LENGTH]);
+            } else {
+                pgTriMesh.setFaces(faces);
+            }
+        }
+        // sync faceSmoothingGroups
+        if (fsgDirty) {
+            if (fsgUpdateRange) {
+                pgTriMesh.setFaces(fsgRangeInfos[RANGE_INDEX],
+                        faceSmoothingGroups, fsgRangeInfos[RANGE_START],
+                        fsgRangeInfos[RANGE_LENGTH]);
+            } else {
+                pgTriMesh.setFaceSmoothingGroups(faceSmoothingGroups);
+            }
+        }
+
+        setDirty(false);
+    }
+
+    @Override
+    BaseBounds computeBounds(BaseBounds bounds) {
+        if (isDirty() || cachedBounds == null) {
+            cachedBounds = new BoxBounds();
+
+            final double len = points.length;
+            for (int i = 0; i < len; i += 3) {
+                cachedBounds.add(points[i], points[i + 1], points[i + 2]);
+            }
+        }
+        return bounds.deriveWithNewBounds(cachedBounds);
+    }
+
+    /**
+     * Computes the centroid of the given triangle
+     * @param v0 vertex of the triangle
+     * @param v1 vertex of the triangle
+     * @param v2 vertex of the triangle
+     * @return the triangle centroid
+     */
+    private Point3D computeCentroid(Point3D v0, Point3D v1, Point3D v2) {
+        Point3D center = v1.midpoint(v2);
+
+        Point3D vec = center.subtract(v0);
+        return v0.add(new Point3D(vec.getX() / 3.0, vec.getY() / 3.0, vec.getZ() / 3.0));
+    }
+
+    /**
+     * Computes the centroid of the given triangle
+     * @param v0 vertex of the triangle
+     * @param v1 vertex of the triangle
+     * @param v2 vertex of the triangle
+     * @return the triangle centroid
+     */
+    private Point2D computeCentroid(Point2D v0, Point2D v1, Point2D v2) {
+        Point2D center = v1.midpoint(v2);
+
+        Point2D vec = center.subtract(v0);
+        return v0.add(new Point2D(vec.getX() / 3.0, vec.getY() / 3.0));
+    }
+
+    /**
+     * Computes intersection of a pick ray and a single triangle face.
+     *
+     * It takes pickRay, origin and dir. The latter two can be of course obtained
+     * from the pickRay, but we need them to be converted to Point3D and don't
+     * want to do that for all faces. Therefore the conversion is done just once
+     * and passed to the method for all the faces.
+     *
+     * @param pickRay pick ray
+     * @param origin pick ray's origin
+     * @param dir pick ray's direction
+     * @param faceIndex index of the face to test
+     * @param cullFace cull face of the Node (and thus the tested face)
+     * @param candidate the owner node (for the possible placement to the result)
+     * @param reportFace whether or not to report he hit face
+     * @param result the pick result to be updated if a closer intersection is found
+     * @return true if the pick ray intersects with the face (regardless of whether
+     *              the result has been updated)
+     */
+    private boolean computeIntersectsFace(
+            PickRay pickRay, Point3D origin, Point3D dir, int faceIndex,
+            CullFace cullFace, Node candidate, boolean reportFace, PickResultChooser result) {
+
+        final int v0Idx = faces[faceIndex] * 3;
+        final int v1Idx = faces[faceIndex + 2] * 3;
+        final int v2Idx = faces[faceIndex + 4] * 3;
+
+        final Point3D v0 = new Point3D(points[v0Idx], points[v0Idx + 1], points[v0Idx + 2]);
+        final Point3D v1 = new Point3D(points[v1Idx], points[v1Idx + 1], points[v1Idx + 2]);
+        final Point3D v2 = new Point3D(points[v2Idx], points[v2Idx + 1], points[v2Idx + 2]);
+
+        final Point3D e1 = v1.subtract(v0);
+        final Point3D e2 = v2.subtract(v0);
+
+        final Point3D h = dir.crossProduct(e2);
+
+        final double a = e1.dotProduct(h);
+        if (a == 0.0) {
+            return false;
+        }
+        final double f = 1.0 / a;
+
+        final Point3D s = origin.subtract(v0);
+
+        final double u = f * (s.dotProduct(h));
+
+        if (u < 0.0 || u > 1.0) {
+            return false;
+        }
+
+        Point3D q = s.crossProduct(e1);
+        double v = f * dir.dotProduct(q);
+
+        if (v < 0.0 || u + v > 1.0) {
+            return false;
+        }
+
+        final double t = f * e2.dotProduct(q);
+
+        if (t >= 0) {
+            if (cullFace != CullFace.NONE) {
+                final Point3D normal = e1.crossProduct(e2);
+                final double nangle = normal.angle(
+                        new Point3D(-dir.getX(), -dir.getY(), -dir.getZ()));
+                if ((nangle >= 90 || cullFace != CullFace.BACK) &&
+                        (nangle <= 90 || cullFace != CullFace.FRONT)) {
+                    // hit culled face
+                    return false;
+                }
+            }
+
+            if (!result.isCloser(t)) {
+                // it intersects, but we already have a better (closer) result
+                // so we can omit the point and texture computation
+                return true;
+            }
+
+            Point3D point = PickResultChooser.computePoint(pickRay, t);
+
+            // Now compute texture mapping. First rotate the triangle
+            // so that we can compute in 2D
+
+            final Point3D centroid = computeCentroid(v0, v1, v2);
+            final Point3D cv0 = v0.subtract(centroid);
+            final Point3D cv1 = v1.subtract(centroid);
+            final Point3D cv2 = v2.subtract(centroid);
+
+            final Point3D ce1 = cv1.subtract(cv0);
+            final Point3D ce2 = cv2.subtract(cv0);
+            Point3D n = ce1.crossProduct(ce2);
+            if (n.getZ() < 0) {
+                n = new Point3D(-n.getX(), -n.getY(), -n.getZ());
+            }
+            final Point3D ax = n.crossProduct(Rotate.Z_AXIS);
+            final double angle = Math.atan2(ax.magnitude(), n.dotProduct(Rotate.Z_AXIS));
+
+            Rotate r = new Rotate(Math.toDegrees(angle), ax);
+            final Point3D crv0 = r.transform(cv0);
+            final Point3D crv1 = r.transform(cv1);
+            final Point3D crv2 = r.transform(cv2);
+            final Point3D rPoint = r.transform(point.subtract(centroid));
+
+            final Point2D flatV0 = new Point2D(crv0.getX(), crv0.getY());
+            final Point2D flatV1 = new Point2D(crv1.getX(), crv1.getY());
+            final Point2D flatV2 = new Point2D(crv2.getX(), crv2.getY());
+            final Point2D flatPoint = new Point2D(rPoint.getX(), rPoint.getY());
+
+            // Obtain the texture triangle
+
+            final int t0Idx = faces[faceIndex + 1] * 2;
+            final int t1Idx = faces[faceIndex + 3] * 2;
+            final int t2Idx = faces[faceIndex + 5] * 2;
+
+            final Point2D u0 = new Point2D(texCoords[t0Idx], texCoords[t0Idx + 1]);
+            final Point2D u1 = new Point2D(texCoords[t1Idx], texCoords[t1Idx + 1]);
+            final Point2D u2 = new Point2D(texCoords[t2Idx], texCoords[t2Idx + 1]);
+
+            final Point2D txCentroid = computeCentroid(u0, u1, u2);
+
+            final Point2D cu0 = u0.subtract(txCentroid);
+            final Point2D cu1 = u1.subtract(txCentroid);
+            final Point2D cu2 = u2.subtract(txCentroid);
+
+            // Find the transform between the two triangles
+
+            final Affine src = new Affine(
+                    flatV0.getX(), flatV1.getX(), flatV2.getX(),
+                    flatV0.getY(), flatV1.getY(), flatV2.getY());
+            final Affine trg = new Affine(
+                    cu0.getX(), cu1.getX(), cu2.getX(),
+                    cu0.getY(), cu1.getY(), cu2.getY());
+
+            Point2D txCoords = null;
+
+            try {
+                src.invert();
+                trg.append(src);
+                txCoords = txCentroid.add(trg.transform(flatPoint));
+            } catch (NonInvertibleTransformException e) {
+                // Can't compute texture mapping, probably the coordinates
+                // don't make sense. Ignore it and return null tex coords.
+            }
+
+            result.offer(candidate, t, 
+                    reportFace ? faceIndex / NUM_COMPONENTS_PER_FACE : PickResult.FACE_UNDEFINED,
+                    point, txCoords);
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * @treatAsPrivate implementation detail
+     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
+     */
+    @Override
+    @Deprecated
+    protected boolean impl_computeIntersects(PickRay pickRay, PickResultChooser pickResult, 
+            Node candidate, CullFace cullFace, boolean reportFace) {
+
+        boolean found = false;
+        final int size = faces.length;
+
+        final Vec3d o = pickRay.getOriginNoClone();
+        final Point3D origin = new Point3D(o.x, o.y, o.z);
+
+        final Vec3d d = pickRay.getDirectionNoClone();
+        final Point3D dir = new Point3D(d.x, d.y, d.z);
+
+        for (int i = 0; i < size; i += NUM_COMPONENTS_PER_FACE) {
+            if (computeIntersectsFace(pickRay, origin, dir, i, cullFace, candidate, 
+                    reportFace, pickResult)) {
+                found = true;
+            }
+        }
+
+        return found;
+    }
+}
--- a/javafx-ui-common/test/unit/javafx/scene/MouseTest.java	Thu Feb 07 16:35:06 2013 -0800
+++ b/javafx-ui-common/test/unit/javafx/scene/MouseTest.java	Thu Feb 07 17:13:12 2013 -0800
@@ -962,6 +962,19 @@
         assertTrue(scene.smallSquareTracker.released);
     }
 
+    @Test
+    public void topMostNodeShouldBePickedWithDepthBuffer() {
+        SimpleTestScene scene = new SimpleTestScene(true);
+        MouseEventGenerator generator = new MouseEventGenerator();
+
+        scene.processEvent(generator.generateMouseEvent(
+                MouseEvent.MOUSE_MOVED, 250, 250));
+
+        assertFalse(scene.smallSquareTracker.wasMoved());
+        assertTrue(scene.bigSquareTracker.wasMoved());
+        assertTrue(scene.groupTracker.wasMoved());
+    }
+
     private static class SimpleTestScene {
 
         MouseEventTracker sceneTracker;
@@ -972,12 +985,20 @@
         private Scene scene;
 
         public SimpleTestScene() {
+            this(false);
+        }
+
+        public SimpleTestScene(boolean depthBuffer) {
             final Group root = new Group();
-            scene = new Scene(root, 400, 400);
+            scene = new Scene(root, 400, 400, depthBuffer);
+            if (depthBuffer) {
+                scene.setCamera(new PerspectiveCamera());
+            }
 
             Group group = new Group();
             Rectangle bigSquare = new Rectangle(100, 100, 200, 200);
             Rectangle smallSquare = new Rectangle(200, 200, 100, 100);
+            bigSquare.setTranslateZ(-1);
 
             smallSquare.setCursor(Cursor.HAND);
             group.setCursor(Cursor.TEXT);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javafx-ui-common/test/unit/javafx/scene/MouseTest3D.java	Thu Feb 07 17:13:12 2013 -0800
@@ -0,0 +1,1363 @@
+/*
+ * Copyright (c) 2012, 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 javafx.scene;
+
+import com.sun.javafx.test.MouseEventGenerator;
+import javafx.event.Event;
+import javafx.event.EventHandler;
+import javafx.event.EventType;
+import javafx.geometry.Point2D;
+import javafx.geometry.Point3D;
+import javafx.scene.input.MouseDragEvent;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.input.PickResult;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.shape.Box;
+import javafx.scene.shape.CullFace;
+import javafx.scene.shape.Cylinder;
+import javafx.scene.shape.MeshView;
+import javafx.scene.shape.Sphere;
+import javafx.scene.shape.TriangleMesh;
+import javafx.scene.transform.Rotate;
+import javafx.stage.Stage;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+public class MouseTest3D {
+