Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene; 027 028import com.sun.javafx.geom.PickRay; 029import com.sun.javafx.geom.Vec3d; 030import com.sun.javafx.geom.transform.Affine3D; 031import com.sun.javafx.geom.transform.GeneralTransform3D; 032import com.sun.javafx.scene.DirtyBits; 033import com.sun.javafx.sg.PGNode; 034import com.sun.javafx.sg.PGPerspectiveCamera; 035import com.sun.javafx.tk.Toolkit; 036import javafx.application.ConditionalFeature; 037import javafx.application.Platform; 038import javafx.beans.property.BooleanProperty; 039import javafx.beans.property.DoubleProperty; 040import javafx.beans.property.SimpleBooleanProperty; 041import javafx.beans.property.SimpleDoubleProperty; 042import sun.util.logging.PlatformLogger; 043 044 045 046/** 047 * Specifies a perspective camera for rendering a scene. 048 * 049 * <p> This camera defines a viewing volume for a perspective projection; 050 * a truncated right pyramid. 051 * The {@code fieldOfView} value can be used to change viewing volume. 052 * This camera is always located at center of the scene and looks along the 053 * positive z-axis. The coordinate system defined by this camera has its 054 * origin in the upper left corner of the panel with the Y-axis pointing 055 * down and the Z axis pointing away from the viewer (into the screen). 056 * 057 * <p> In the default camera, where fixedEyeAtCameraZero is false, the Z value 058 * of the eye position is adjusted in Z such that the projection matrix generated 059 * using the specified {@code fieldOfView} will produce units at 060 * Z = 0 (the projection plane), in device-independent pixels, matches that of 061 * the ParallelCamera. 062 * When the Scene is resized, 063 * the objects in the scene at the projection plane (Z = 0) will stay the same size, 064 * but more or less content of the scene is viewable. 065 * 066 * <p> If fixedEyeAtCameraZero is true, the eye position is fixed at (0, 0, 0) 067 * in the local coordinates of the camera. The projection matrix is generated 068 * using the specified {@code fieldOfView} and the projection volume is mapped 069 * onto the viewport (window) such that it is stretched over more or fewer 070 * device-independent pixels at the projection plane. 071 * When the Scene is resized, 072 * the objects in the scene will shrink or grow proportionally, 073 * but the visible portion of the content is unchanged. 074 * 075 * <p> We recommend setting fixedEyeAtCameraZero to true if you are going to 076 * transform (move) the camera. Transforming the camera when fixedEyeAtCameraZero 077 * is set to false may lead to results that are not intuitive. 078 * 079 * <p> Note that this is a conditional feature. See 080 * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D} 081 * for more information. 082 * 083 * @since JavaFX 1.3 084 */ 085public class PerspectiveCamera extends Camera { 086 087 private boolean fixedEyeAtCameraZero = false; 088 089 // Lookat transform for legacy case 090 private static final Affine3D LOOK_AT_TX = new Affine3D(); 091 092 // Lookat transform for fixedEyeAtCameraZero case 093 private static final Affine3D LOOK_AT_TX_FIXED_EYE = new Affine3D(); 094 095 static { 096 // Compute the legacy look at matrix such that the zero point ends up at 097 // the z=-1 plane. 098 LOOK_AT_TX.setToTranslation(0, 0, -1); 099 // Y-axis pointing down 100 LOOK_AT_TX.rotate(Math.PI, 1, 0, 0); 101 102 // Compute the fixed eye at (0, 0, 0) look at matrix such that the zero point 103 // ends up at the z=0 plane and Y-axis pointing down 104 LOOK_AT_TX_FIXED_EYE.rotate(Math.PI, 1, 0, 0); 105 } 106 107 /** 108 * Specifies the field of view angle of the camera's projection plane, 109 * measured in degrees. 110 * 111 * @defaultValue 30.0 112 */ 113 private DoubleProperty fieldOfView; 114 115 public final void setFieldOfView(double value){ 116 fieldOfViewProperty().set(value); 117 } 118 119 public final double getFieldOfView() { 120 return fieldOfView == null ? 30 : fieldOfView.get(); 121 } 122 123 public final DoubleProperty fieldOfViewProperty() { 124 if (fieldOfView == null) { 125 fieldOfView = new SimpleDoubleProperty(PerspectiveCamera.this, "fieldOfView", 30) { 126 @Override 127 protected void invalidated() { 128 impl_markDirty(DirtyBits.NODE_CAMERA); 129 } 130 }; 131 } 132 return fieldOfView; 133 } 134 135 /** 136 * Defines whether the {@code fieldOfView} property is to apply to the vertical 137 * dimension of the projection plane. If it is false, {@code fieldOfView} is to 138 * apply to the horizontal dimension of the projection plane. 139 * 140 * @defaultValue true 141 * @since JavaFX 8 142 */ 143 private BooleanProperty verticalFieldOfView; 144 145 public final void setVerticalFieldOfView(boolean value) { 146 verticalFieldOfViewProperty().set(value); 147 } 148 149 public final boolean isVerticalFieldOfView() { 150 return verticalFieldOfView == null ? true : verticalFieldOfView.get(); 151 } 152 153 public final BooleanProperty verticalFieldOfViewProperty() { 154 if (verticalFieldOfView == null) { 155 verticalFieldOfView = new SimpleBooleanProperty(PerspectiveCamera.this, "verticalFieldOfView", true) { 156 @Override 157 protected void invalidated() { 158 impl_markDirty(DirtyBits.NODE_CAMERA); 159 } 160 }; 161 } 162 return verticalFieldOfView; 163 } 164 165 public PerspectiveCamera() { 166 this(false); 167 } 168 169 /** 170 * Constructs a PerspectiveCamera with the specified fixedEyeAtCameraZero flag. 171 * 172 * <p> In the default camera, where fixedEyeAtCameraZero is false, the Z value of 173 * the eye position is adjusted in Z such that the projection matrix generated 174 * using the specified {@code fieldOfView} will produce units at 175 * Z = 0 (the projection plane), in device-independent pixels, matches that of 176 * the ParallelCamera. 177 * When the Scene is resized, 178 * the objects in the scene at the projection plane (Z = 0) will stay the same size, 179 * but more or less content of the scene is viewable. 180 * 181 * <p> If fixedEyeAtCameraZero is true, the eye position is fixed at (0, 0, 0) 182 * in the local coordinates of the camera. The projection matrix is generated 183 * using the specified {@code fieldOfView} and the projection volume is mapped 184 * onto the viewport (window) such that it is stretched over more or fewer 185 * device-independent pixels at the projection plane. 186 * When the Scene is resized, 187 * the objects in the scene will shrink or grow proportionally, 188 * but the visible portion of the content is unchanged. 189 * 190 * <p> We recommend setting fixedEyeAtCameraZero to true if you are going to 191 * transform (move) the camera. Transforming the camera when fixedEyeAtCameraZero 192 * is set to false may lead to results that are not intuitive. 193 * 194 * @since JavaFX 8 195 */ 196 public PerspectiveCamera(boolean fixedEyeAtCameraZero) { 197 if (!Platform.isSupported(ConditionalFeature.SCENE3D)) { 198 String logname = PerspectiveCamera.class.getName(); 199 PlatformLogger.getLogger(logname).warning("System can't support " 200 + "ConditionalFeature.SCENE3D"); 201 } 202 this.fixedEyeAtCameraZero = fixedEyeAtCameraZero; 203 } 204 205 public final boolean isFixedEyeAtCameraZero() { 206 return fixedEyeAtCameraZero; 207 } 208 209 @Override 210 final PickRay computePickRay(double x, double y, PickRay pickRay) { 211 212 return PickRay.computePerspectivePickRay(x, y, fixedEyeAtCameraZero, 213 getViewWidth(), getViewHeight(), 214 getFieldOfView(), isVerticalFieldOfView(), 215 getCameraTransform(), 216 //TODO: use actual clips always after rendering uses them 217 fixedEyeAtCameraZero ? getNearClip() : 0.0, 218 fixedEyeAtCameraZero ? getFarClip() : Double.POSITIVE_INFINITY, 219 pickRay); 220 } 221 222 @Override Camera copy() { 223 PerspectiveCamera c = new PerspectiveCamera(fixedEyeAtCameraZero); 224 c.setNearClip(getNearClip()); 225 c.setFarClip(getFarClip()); 226 c.setFieldOfView(getFieldOfView()); 227 return c; 228 } 229 230 /** 231 * @treatAsPrivate implementation detail 232 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 233 */ 234 @Deprecated 235 @Override 236 protected PGNode impl_createPGNode() { 237 PGPerspectiveCamera pgCamera = Toolkit.getToolkit().createPGPerspectiveCamera(fixedEyeAtCameraZero); 238 pgCamera.setNearClip((float) getNearClip()); 239 pgCamera.setFarClip((float) getFarClip()); 240 pgCamera.setFieldOfView((float) getFieldOfView()); 241 return pgCamera; 242 } 243 244 /** 245 * @treatAsPrivate implementation detail 246 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 247 */ 248 @Deprecated 249 @Override 250 public void impl_updatePG() { 251 super.impl_updatePG(); 252 PGPerspectiveCamera pgPerspectiveCamera = (PGPerspectiveCamera)impl_getPGNode(); 253 if (impl_isDirty(DirtyBits.NODE_CAMERA)) { 254 pgPerspectiveCamera.setVerticalFieldOfView(isVerticalFieldOfView()); 255 pgPerspectiveCamera.setFieldOfView((float) getFieldOfView()); 256 } 257 } 258 259 @Override 260 void computeProjectionTransform(GeneralTransform3D proj) { 261 proj.perspective(isVerticalFieldOfView(), Math.toRadians(getFieldOfView()), 262 getViewWidth() / getViewHeight(), getNearClip(), getFarClip()); 263 } 264 265 @Override 266 protected void computeViewTransform(Affine3D view) { 267 268 // In the case of fixedEyeAtCameraZero the camera position is (0,0,0) in 269 // local coord. of the camera node. In non-fixed eye case, the camera 270 // position is (w/2, h/2, h/2/tan) in local coord. of the camera. 271 if (isFixedEyeAtCameraZero()) { 272 view.setTransform(LOOK_AT_TX_FIXED_EYE); 273 } else { 274 final double viewWidth = getViewWidth(); 275 final double viewHeight = getViewHeight(); 276 final boolean verticalFOV = isVerticalFieldOfView(); 277 278 final double aspect = viewWidth / viewHeight; 279 final double tanOfHalfFOV = Math.tan(Math.toRadians(getFieldOfView()) / 2.0); 280 281 // Translate the zero point to the upper-left corner 282 final double xOffset = -tanOfHalfFOV * (verticalFOV ? aspect : 1.0); 283 final double yOffset = tanOfHalfFOV * (verticalFOV ? 1.0 : 1.0 / aspect); 284 285 // Compute scale factor as 2/viewport.width or height, after adjusting for fov 286 final double scale = 2.0 * tanOfHalfFOV / 287 (verticalFOV ? viewHeight : viewWidth); 288 289 view.setToTranslation(xOffset, yOffset, 0.0); 290 view.concatenate(LOOK_AT_TX); 291 view.scale(scale, scale, scale); 292 } 293 } 294 295 @Override 296 Vec3d computePosition(Vec3d position) { 297 if (position == null) { 298 position = new Vec3d(); 299 } 300 301 if (fixedEyeAtCameraZero) { 302 position.set(0.0, 0.0, 0.0); 303 } else { 304 final double halfViewWidth = getViewWidth() / 2.0; 305 final double halfViewHeight = getViewHeight() / 2.0; 306 final double halfViewDim = isVerticalFieldOfView() 307 ? halfViewHeight : halfViewWidth; 308 final double distanceZ = halfViewDim 309 / Math.tan(Math.toRadians(getFieldOfView() / 2.0)); 310 311 position.set(halfViewWidth, halfViewHeight, -distanceZ); 312 } 313 return position; 314 } 315}