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.BaseBounds; 029import com.sun.javafx.geom.BoxBounds; 030import com.sun.javafx.geom.PickRay; 031import com.sun.javafx.geom.Vec3d; 032import com.sun.javafx.geom.transform.Affine3D; 033import com.sun.javafx.geom.transform.BaseTransform; 034import com.sun.javafx.geom.transform.GeneralTransform3D; 035import com.sun.javafx.geom.transform.NoninvertibleTransformException; 036import com.sun.javafx.jmx.MXNodeAlgorithm; 037import com.sun.javafx.jmx.MXNodeAlgorithmContext; 038import com.sun.javafx.scene.CameraHelper; 039import com.sun.javafx.scene.DirtyBits; 040import com.sun.javafx.sg.PGCamera; 041import javafx.beans.InvalidationListener; 042import javafx.beans.Observable; 043import javafx.beans.property.DoubleProperty; 044import javafx.beans.property.SimpleDoubleProperty; 045import javafx.geometry.Point2D; 046import javafx.geometry.Point3D; 047import javafx.scene.transform.Transform; 048import sun.util.logging.PlatformLogger; 049 050 051/** 052 * Base class for a camera used to render a scene. 053 * 054 * @since JavaFX 1.3 055 */ 056public abstract class Camera extends Node { 057 058 private Affine3D localToSceneTx = new Affine3D(); 059 060 protected Camera() { 061 InvalidationListener dirtyTransformListener = new InvalidationListener() { 062 @Override 063 public void invalidated(Observable observable) { 064 impl_markDirty(DirtyBits.NODE_CAMERA_TRANSFORM); 065 } 066 }; 067 068 this.localToSceneTransformProperty().addListener(dirtyTransformListener); 069 // if camera is removed from scene it needs to stop using its transforms 070 this.sceneProperty().addListener(dirtyTransformListener); 071 } 072 073 // NOTE: farClipInScene and nearClipInScene are valid only if there is no rotation 074 private double farClipInScene; 075 private double nearClipInScene; 076 077 // only one of them can be non-null at a time 078 private Scene ownerScene = null; 079 private SubScene ownerSubScene = null; 080 081 private GeneralTransform3D projViewTx = new GeneralTransform3D(); 082 private GeneralTransform3D projTx = new GeneralTransform3D(); 083 private Affine3D viewTx = new Affine3D(); 084 private double viewWidth = 1.0; 085 private double viewHeight = 1.0; 086 private Vec3d position = new Vec3d(); 087 088 private boolean clipInSceneValid = false; 089 private boolean projViewTxValid = false; 090 private boolean localToSceneValid = false; 091 private boolean sceneToLocalValid = false; 092 093 double getFarClipInScene() { 094 updateClipPlane(); 095 return farClipInScene; 096 } 097 098 double getNearClipInScene() { 099 updateClipPlane(); 100 return nearClipInScene; 101 } 102 103 private void updateClipPlane() { 104 if (!clipInSceneValid) { 105 final Transform localToSceneTransform = getLocalToSceneTransform(); 106 nearClipInScene = localToSceneTransform.transform(0, 0, getNearClip()).getZ(); 107 farClipInScene = localToSceneTransform.transform(0, 0, getFarClip()).getZ(); 108 clipInSceneValid = true; 109 } 110 } 111 112 /** 113 * An affine transform that holds the computed scene-to-local transform. 114 * It is used to convert node to camera coordinate when rotation is involved. 115 */ 116 private Affine3D sceneToLocalTx = new Affine3D(); 117 118 Affine3D getSceneToLocalTransform() { 119 if (!sceneToLocalValid) { 120 sceneToLocalTx.setTransform(getCameraTransform()); 121 try { 122 sceneToLocalTx.invert(); 123 } catch (NoninvertibleTransformException ex) { 124 String logname = Camera.class.getName(); 125 PlatformLogger.getLogger(logname).severe("getSceneToLocalTransform", ex); 126 sceneToLocalTx.setToIdentity(); 127 } 128 sceneToLocalValid = true; 129 } 130 131 return sceneToLocalTx; 132 } 133 134 /** 135 * Specifies the near clipping plane of this {@code Camera} in the eye 136 * coordinate system of this node. Objects closer to the eye than the 137 * {@code nearClip} plane are not drawn. 138 * 139 * @defaultValue 0.1 140 * @since JavaFX 8 141 */ 142 private DoubleProperty nearClip; 143 144 public final void setNearClip(double value){ 145 nearClipProperty().set(value); 146 } 147 148 public final double getNearClip() { 149 return nearClip == null ? 0.1 : nearClip.get(); 150 } 151 152 public final DoubleProperty nearClipProperty() { 153 if (nearClip == null) { 154 nearClip = new SimpleDoubleProperty(Camera.this, "nearClip", 0.1) { 155 @Override 156 protected void invalidated() { 157 clipInSceneValid = false; 158 impl_markDirty(DirtyBits.NODE_CAMERA); 159 } 160 }; 161 } 162 return nearClip; 163 } 164 165 /** 166 * Specifies the far clipping plane of this {@code Camera} in the eye 167 * coordinate system of this node. Objects farther away from the eye than 168 * the {@code farClip} plane are not drawn. 169 * <p> 170 * 171 * @defaultValue 100.0 172 * @since JavaFX 8 173 */ 174 private DoubleProperty farClip; 175 176 public final void setFarClip(double value){ 177 farClipProperty().set(value); 178 } 179 180 public final double getFarClip() { 181 return farClip == null ? 100.0 : farClip.get(); 182 } 183 184 public final DoubleProperty farClipProperty() { 185 if (farClip == null) { 186 farClip = new SimpleDoubleProperty(Camera.this, "farClip", 100.0) { 187 @Override 188 protected void invalidated() { 189 clipInSceneValid = false; 190 impl_markDirty(DirtyBits.NODE_CAMERA); 191 } 192 }; 193 } 194 return farClip; 195 } 196 197 PGCamera getPlatformCamera() { 198 return (PGCamera) impl_getPGNode(); 199 } 200 201 Camera copy() { 202 return this; 203 } 204 205 /** 206 * @treatAsPrivate implementation detail 207 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 208 */ 209 @Deprecated 210 @Override 211 public void impl_updatePG() { 212 super.impl_updatePG(); 213 PGCamera pgCamera = (PGCamera)impl_getPGNode(); 214 if (!impl_isDirtyEmpty()) { 215 if (impl_isDirty(DirtyBits.NODE_CAMERA)) { 216 pgCamera.setNearClip((float) getNearClip()); 217 pgCamera.setFarClip((float) getFarClip()); 218 pgCamera.setViewWidth(getViewWidth()); 219 pgCamera.setViewHeight(getViewHeight()); 220 } 221 if (impl_isDirty(DirtyBits.NODE_CAMERA_TRANSFORM)) { 222 // TODO: 3D - For now, we are treating the scene as world. 223 // This may need to change for the fixed eye position case. 224 pgCamera.setWorldTransform(getCameraTransform()); 225 } 226 227 pgCamera.setProjViewTransform(getProjViewTransform()); 228 229 position = computePosition(position); 230 getCameraTransform().transform(position, position); 231 pgCamera.setPosition(position); 232 } 233 } 234 235 void setViewWidth(double width) { 236 this.viewWidth = width; 237 impl_markDirty(DirtyBits.NODE_CAMERA); 238 } 239 240 double getViewWidth() { 241 return viewWidth; 242 } 243 244 void setViewHeight(double height) { 245 this.viewHeight = height; 246 impl_markDirty(DirtyBits.NODE_CAMERA); 247 } 248 249 double getViewHeight() { 250 return viewHeight; 251 } 252 253 void setOwnerScene(Scene s) { 254 if (s == null) { 255 ownerScene = null; 256 } else if (s != ownerScene) { 257 if (ownerScene != null || ownerSubScene != null) { 258 throw new IllegalArgumentException(this 259 + "is already set as camera in other scene or subscene"); 260 } 261 ownerScene = s; 262 markOwnerDirty(); 263 } 264 } 265 266 void setOwnerSubScene(SubScene s) { 267 if (s == null) { 268 ownerSubScene = null; 269 } else if (s != ownerSubScene) { 270 if (ownerScene != null || ownerSubScene != null) { 271 throw new IllegalArgumentException(this 272 + "is already set as camera in other scene or subscene"); 273 } 274 ownerSubScene = s; 275 markOwnerDirty(); 276 } 277 } 278 279 @Override 280 protected void impl_markDirty(DirtyBits dirtyBit) { 281 super.impl_markDirty(dirtyBit); 282 if (dirtyBit == DirtyBits.NODE_CAMERA_TRANSFORM) { 283 localToSceneValid = false; 284 sceneToLocalValid = false; 285 clipInSceneValid = false; 286 projViewTxValid = false; 287 } else if (dirtyBit == DirtyBits.NODE_CAMERA) { 288 projViewTxValid = false; 289 } 290 markOwnerDirty(); 291 } 292 293 private void markOwnerDirty() { 294 // if the camera is part of the scene/subScene, we don't need to notify 295 // the owner as the camera will be added to its dirty list as usual 296 297 if (ownerScene != null && ownerScene != getScene()) { 298 ownerScene.markCameraDirty(); 299 } 300 if (ownerSubScene != null && ownerSubScene != getSubScene()) { 301 ownerSubScene.markContentDirty(); 302 } 303 } 304 305 /** 306 * Returns the local-to-scene transform of this camera. 307 * Package private, for use in our internal subclasses. 308 * Returns directly the internal instance, it must not be altered. 309 */ 310 Affine3D getCameraTransform() { 311 if (!localToSceneValid) { 312 localToSceneTx.setToIdentity(); 313 getLocalToSceneTransform().impl_apply(localToSceneTx); 314 localToSceneValid = true; 315 } 316 return localToSceneTx; 317 } 318 319 abstract void computeProjectionTransform(GeneralTransform3D proj); 320 abstract void computeViewTransform(Affine3D view); 321 322 /** 323 * Returns the projView transform of this camera. 324 * Package private, for internal use. 325 * Returns directly the internal instance, it must not be altered. 326 */ 327 GeneralTransform3D getProjViewTransform() { 328 if (!projViewTxValid) { 329 computeProjectionTransform(projTx); 330 computeViewTransform(viewTx); 331 332 projViewTx.set(projTx); 333 projViewTx.mul(viewTx); 334 projViewTx.mul(getSceneToLocalTransform()); 335 336 projViewTxValid = true; 337 } 338 339 return projViewTx; 340 } 341 342 /** 343 * Transforms the given 3D point to the flat projected coordinates. 344 */ 345 private Point2D project(Point3D p) { 346 347 final Vec3d vec = getProjViewTransform().transform(new Vec3d( 348 p.getX(), p.getY(), p.getZ())); 349 350 final double halfViewWidth = getViewWidth() / 2.0; 351 final double halfViewHeight = getViewHeight() / 2.0; 352 353 return new Point2D( 354 halfViewWidth * (1 + vec.x), 355 halfViewHeight * (1 - vec.y)); 356 } 357 358 /** 359 * Computes intersection point of the pick ray cast by the given coordinates 360 * and the node's local XY plane. 361 */ 362 private Point2D pickNodeXYPlane(Node node, double x, double y) { 363 final PickRay ray = computePickRay(x, y, null); 364 365 final Affine3D localToScene = new Affine3D(); 366 node.getLocalToSceneTransform().impl_apply(localToScene); 367 368 final Vec3d o = ray.getOriginNoClone(); 369 final Vec3d d = ray.getDirectionNoClone(); 370 371 try { 372 localToScene.inverseTransform(o, o); 373 localToScene.inverseDeltaTransform(d, d); 374 } catch (NoninvertibleTransformException e) { 375 return null; 376 } 377 378 if (almostZero(d.z)) { 379 return null; 380 } 381 382 final double t = -o.z / d.z; 383 return new Point2D(o.x + (d.x * t), o.y + (d.y * t)); 384 } 385 386 /** 387 * Computes intersection point of the pick ray cast by the given coordinates 388 * and the projection plane. 389 */ 390 Point3D pickProjectPlane(double x, double y) { 391 final PickRay ray = computePickRay(x, y, null); 392 final Vec3d p = new Vec3d(); 393 p.add(ray.getOriginNoClone(), ray.getDirectionNoClone()); 394 395 return new Point3D(p.x, p.y, p.z); 396 } 397 398 399 /** 400 * Computes pick ray for the content rendered by this camera. 401 * @param x horizontal coordinate of the pick ray in the projected 402 * view, usually mouse cursor position 403 * @param y vertical coordinate of the pick ray in the projected 404 * view, usually mouse cursor position 405 * @param pickRay pick ray to be reused. New instance is created in case 406 * of null. 407 * @return The PickRay instance computed based on this camera and the given 408 * arguments. 409 */ 410 abstract PickRay computePickRay(double x, double y, PickRay pickRay); 411 412 /** 413 * Computes local position of the camera in the scene. 414 * @param position Position to be reused. New instance is created in case 415 * of null. 416 * @return The position of the camera in the scene in camera local coords 417 */ 418 abstract Vec3d computePosition(Vec3d position); 419 420 /** 421 * @treatAsPrivate implementation detail 422 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 423 */ 424 @Deprecated 425 @Override 426 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 427 return new BoxBounds(0, 0, 0, 0, 0, 0); 428 } 429 430 /** 431 * @treatAsPrivate implementation detail 432 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 433 */ 434 @Deprecated 435 @Override 436 protected boolean impl_computeContains(double localX, double localY) { 437 return false; 438 } 439 440 /** 441 * @treatAsPrivate implementation detail 442 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 443 */ 444 @Deprecated 445 @Override 446 public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) { 447 throw new UnsupportedOperationException("Not supported yet."); 448 } 449 450 451 static { 452 // This is used by classes in different packages to get access to 453 // private and package private methods. 454 CameraHelper.setCameraAccessor(new CameraHelper.CameraAccessor() { 455 456 @Override 457 public Point2D project(Camera camera, Point3D p) { 458 return camera.project(p); 459 } 460 461 @Override 462 public Point2D pickNodeXYPlane(Camera camera, Node node, double x, double y) { 463 return camera.pickNodeXYPlane(node, x, y); 464 } 465 466 @Override 467 public Point3D pickProjectPlane(Camera camera, double x, double y) { 468 return camera.pickProjectPlane(x, y); 469 } 470 }); 471 } 472}