Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 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 java.util.ArrayList; 029import java.util.List; 030 031import com.sun.javafx.geom.BaseBounds; 032import com.sun.javafx.geom.PickRay; 033import com.sun.javafx.geom.transform.BaseTransform; 034import com.sun.javafx.jmx.MXNodeAlgorithm; 035import com.sun.javafx.jmx.MXNodeAlgorithmContext; 036import com.sun.javafx.scene.CssFlags; 037import com.sun.javafx.scene.DirtyBits; 038import com.sun.javafx.scene.SubSceneHelper; 039import com.sun.javafx.scene.input.PickResultChooser; 040import com.sun.javafx.scene.traversal.TraversalEngine; 041import com.sun.javafx.sg.PGLightBase; 042import com.sun.javafx.sg.PGNode; 043import com.sun.javafx.sg.PGSubScene; 044import com.sun.javafx.tk.Toolkit; 045import javafx.application.ConditionalFeature; 046import javafx.application.Platform; 047import javafx.beans.property.DoubleProperty; 048import javafx.beans.property.DoublePropertyBase; 049import javafx.beans.property.ObjectProperty; 050import javafx.beans.property.ObjectPropertyBase; 051import javafx.geometry.NodeOrientation; 052import javafx.geometry.Point3D; 053import javafx.scene.input.PickResult; 054import javafx.scene.paint.Paint; 055import sun.util.logging.PlatformLogger; 056 057/** 058 * The {@code SubScene} class is the container for content in a scene graph. 059 * 060 * @since JavaFX 8 061 */ 062public class SubScene extends Node { 063 064 /** 065 * Creates a SubScene for a specific root Node with a specific size. 066 * 067 * @param root The root node of the scene graph 068 * @param width The width of the scene 069 * @param height The height of the scene 070 * 071 * @throws IllegalStateException if this constructor is called on a thread 072 * other than the JavaFX Application Thread. 073 * @throws NullPointerException if root is null 074 */ 075 public SubScene(Parent root, double width, double height) { 076 setRoot(root); 077 setWidth(width); 078 setHeight(height); 079 } 080 081 /** 082 * Constructs a SubScene consisting of a root, with a dimension of width and 083 * height, specifies whether a depth buffer is created for this scene and 084 * specifies whether scene anti-aliasing is requested. 085 * 086 * @param root The root node of the scene graph 087 * @param width The width of the scene 088 * @param height The height of the scene 089 * @param depthBuffer The depth buffer flag 090 * @param antiAliasing The sub-scene anti-aliasing flag 091 * <p> 092 * The depthBuffer and antiAliasing flags are conditional feature and the default 093 * value for both are false. See 094 * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D} 095 * for more information. 096 * 097 * @throws IllegalStateException if this constructor is called on a thread 098 * other than the JavaFX Application Thread. 099 * @throws NullPointerException if root is null 100 * 101 * @see javafx.scene.Node#setDepthTest(DepthTest) 102 */ 103 public SubScene(Parent root, double width, double height, 104 boolean depthBuffer, boolean antiAliasing) { 105 this(root, width, height); 106 this.depthBuffer = depthBuffer; 107 108 // NOTE: this block will be removed once implement anti-aliasing 109 if (antiAliasing) { 110 String logname = SubScene.class.getName(); 111 PlatformLogger.getLogger(logname).warning("3D anti-aliasing is " 112 + "not supported yet."); 113 } 114 115 if ((depthBuffer || antiAliasing) 116 && !Platform.isSupported(ConditionalFeature.SCENE3D)) { 117 String logname = SubScene.class.getName(); 118 PlatformLogger.getLogger(logname).warning("System can't support " 119 + "ConditionalFeature.SCENE3D"); 120 // TODO: 3D - ignore depthBuffer and antiAliasing at rendering time 121 } 122 //TODO: 3D - verify that depthBuffer is working correctly 123 //TODO: 3D - complete antiAliasing 124 } 125 126 /** 127 * Return true if this {@code SubScene} is anti-aliased otherwise false. 128 */ 129 public boolean isAntiAliasing() { 130 throw new UnsupportedOperationException("Unsupported --- *** isAntiAliasing method ***"); 131 } 132 133 private boolean depthBuffer = false; 134 135 boolean isDepthBufferInteral() { 136 if (!Platform.isSupported(ConditionalFeature.SCENE3D)) { 137 return false; 138 } 139 return depthBuffer; 140 } 141 /** 142 * Defines the root {@code Node} of the SubScene scene graph. 143 * If a {@code Group} is used as the root, the 144 * contents of the scene graph will be clipped by the SubScene's width and height. 145 * 146 * SubScene doesn't accept null root. 147 * 148 */ 149 private ObjectProperty<Parent> root; 150 151 public final void setRoot(Parent value) { 152 rootProperty().set(value); 153 } 154 155 public final Parent getRoot() { 156 return root == null ? null : root.get(); 157 } 158 159 public final ObjectProperty<Parent> rootProperty() { 160 if (root == null) { 161 root = new ObjectPropertyBase<Parent>() { 162 private Parent oldRoot; 163 164 private void forceUnbind() { 165 System.err.println("Unbinding illegal root."); 166 unbind(); 167 } 168 169 @Override 170 protected void invalidated() { 171 Parent _value = get(); 172 173 if (_value == null) { 174 if (isBound()) { forceUnbind(); } 175 throw new NullPointerException("Scene's root cannot be null"); 176 } 177 if (_value.getParent() != null) { 178 if (isBound()) { forceUnbind(); } 179 throw new IllegalArgumentException(_value + 180 "is already inside a scene-graph and cannot be set as root"); 181 } 182 if (_value.getClipParent() != null) { 183 if (isBound()) forceUnbind(); 184 throw new IllegalArgumentException(_value + 185 "is set as a clip on another node, so cannot be set as root"); 186 } 187 if ((_value.getScene() != null && 188 _value.getScene().getRoot() == _value) || 189 (_value.getSubScene() != null && 190 _value.getSubScene().getRoot() == _value && 191 _value.getSubScene() != SubScene.this)) 192 { 193 if (isBound()) { forceUnbind(); } 194 throw new IllegalArgumentException(_value + 195 "is already set as root of another scene or subScene"); 196 } 197 198 // disabled and isTreeVisible properties are inherrited 199 _value.setTreeVisible(impl_isTreeVisible()); 200 _value.setDisabled(isDisabled()); 201 202 if (oldRoot != null) { 203 oldRoot.setScenes(null, null); 204 oldRoot.setImpl_traversalEngine(null); 205 } 206 oldRoot = _value; 207 if (_value.getImpl_traversalEngine() == null) { 208 _value.setImpl_traversalEngine(new TraversalEngine(_value, true)); 209 } 210 _value.getStyleClass().add(0, "root"); 211 _value.setScenes(getScene(), SubScene.this); 212 markDirty(SubSceneDirtyBits.ROOT_SG_DIRTY); 213 _value.resize(getWidth(), getHeight()); // maybe no-op if root is not resizable 214 _value.requestLayout(); 215 } 216 217 @Override 218 public Object getBean() { 219 return SubScene.this; 220 } 221 222 @Override 223 public String getName() { 224 return "root"; 225 } 226 }; 227 } 228 return root; 229 } 230 231 /** 232 * Specifies the type of camera use for rendering this {@code SubScene}. 233 * If {@code camera} is null, a parallel camera is used for rendering. 234 * It is illegal to set a camera that belongs to other {@code Scene} 235 * or {@code SubScene}. 236 * <p> 237 * Note: this is a conditional feature. See 238 * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D} 239 * for more information. 240 * 241 * @defaultValue null 242 */ 243 private ObjectProperty<Camera> camera; 244 245 public final void setCamera(Camera value) { 246 cameraProperty().set(value); 247 } 248 249 public final Camera getCamera() { 250 return camera == null ? null : camera.get(); 251 } 252 253 public final ObjectProperty<Camera> cameraProperty() { 254 if (camera == null) { 255 camera = new ObjectPropertyBase<Camera>() { 256 Camera oldCamera = null; 257 258 @Override 259 protected void invalidated() { 260 Camera _value = get(); 261 if (_value != null) { 262 if (_value instanceof PerspectiveCamera 263 && !Platform.isSupported(ConditionalFeature.SCENE3D)) { 264 String logname = SubScene.class.getName(); 265 PlatformLogger.getLogger(logname).warning("System can't support " 266 + "ConditionalFeature.SCENE3D"); 267 } 268 // Illegal value if it belongs to any scene or other subscene 269 if ((_value.getScene() != null || _value.getSubScene() != null) 270 && (_value.getScene() != getScene() || _value.getSubScene() != SubScene.this)) { 271 throw new IllegalArgumentException(_value 272 + "is already part of other scene or subscene"); 273 } 274 // throws exception if the camera already has a different owner 275 _value.setOwnerSubScene(SubScene.this); 276 _value.setViewWidth(getWidth()); 277 _value.setViewHeight(getHeight()); 278 } 279 markDirty(SubSceneDirtyBits.CAMERA_DIRTY); 280 if (oldCamera != null && oldCamera != _value) { 281 oldCamera.setOwnerSubScene(null); 282 } 283 oldCamera = _value; 284 } 285 286 @Override 287 public Object getBean() { 288 return SubScene.this; 289 } 290 291 @Override 292 public String getName() { 293 return "camera"; 294 } 295 }; 296 } 297 return camera; 298 } 299 300 private Camera defaultCamera; 301 302 Camera getEffectiveCamera() { 303 final Camera cam = getCamera(); 304 if (cam == null 305 || (cam instanceof PerspectiveCamera 306 && !Platform.isSupported(ConditionalFeature.SCENE3D))) { 307 if (defaultCamera == null) { 308 defaultCamera = new ParallelCamera(); 309 defaultCamera.setOwnerSubScene(this); 310 defaultCamera.setViewWidth(getWidth()); 311 defaultCamera.setViewHeight(getHeight()); 312 } 313 return defaultCamera; 314 } 315 316 return cam; 317 } 318 319 // Used by the camera 320 final void markContentDirty() { 321 markDirty(SubSceneDirtyBits.CONTENT_DIRTY); 322 } 323 324 /** 325 * Defines the width of this {@code SubScene} 326 * 327 * @defaultvalue 0.0 328 */ 329 private DoubleProperty width; 330 331 public final void setWidth(double value) { 332 widthProperty().set(value); 333 } 334 335 public final double getWidth() { 336 return width == null ? 0.0 : width.get(); 337 } 338 339 public final DoubleProperty widthProperty() { 340 if (width == null) { 341 width = new DoublePropertyBase() { 342 343 @Override 344 public void invalidated() { 345 final Parent _root = getRoot(); 346 //TODO - use a better method to update mirroring 347 if (_root.getEffectiveNodeOrientation() == NodeOrientation.RIGHT_TO_LEFT) { 348 _root.impl_transformsChanged(); 349 } 350 if (_root.isResizable()) { 351 _root.resize(get() - _root.getLayoutX() - _root.getTranslateX(), _root.getLayoutBounds().getHeight()); 352 } 353 markDirty(SubSceneDirtyBits.SIZE_DIRTY); 354 SubScene.this.impl_geomChanged(); 355 356 getEffectiveCamera().setViewWidth(get()); 357 } 358 359 @Override 360 public Object getBean() { 361 return SubScene.this; 362 } 363 364 @Override 365 public String getName() { 366 return "width"; 367 } 368 }; 369 } 370 return width; 371 } 372 373 /** 374 * Defines the height of this {@code SubScene} 375 * 376 * @defaultvalue 0.0 377 */ 378 private DoubleProperty height; 379 380 public final void setHeight(double value) { 381 heightProperty().set(value); 382 } 383 384 public final double getHeight() { 385 return height == null ? 0.0 : height.get(); 386 } 387 388 public final DoubleProperty heightProperty() { 389 if (height == null) { 390 height = new DoublePropertyBase() { 391 392 @Override 393 public void invalidated() { 394 final Parent _root = getRoot(); 395 if (_root.isResizable()) { 396 _root.resize(_root.getLayoutBounds().getWidth(), get() - _root.getLayoutY() - _root.getTranslateY()); 397 } 398 markDirty(SubSceneDirtyBits.SIZE_DIRTY); 399 SubScene.this.impl_geomChanged(); 400 401 getEffectiveCamera().setViewHeight(get()); 402 } 403 404 @Override 405 public Object getBean() { 406 return SubScene.this; 407 } 408 409 @Override 410 public String getName() { 411 return "height"; 412 } 413 }; 414 } 415 return height; 416 } 417 418 /** 419 * Defines the background fill of this {@code SubScene}. Both a {@code null} 420 * value meaning paint no background and a {@link javafx.scene.paint.Paint} 421 * with transparency are supported. The default value is null. 422 * 423 * @defaultValue null 424 */ 425 private ObjectProperty<Paint> fill; 426 427 public final void setFill(Paint value) { 428 fillProperty().set(value); 429 } 430 431 public final Paint getFill() { 432 return fill == null ? null : fill.get(); 433 } 434 435 public final ObjectProperty<Paint> fillProperty() { 436 if (fill == null) { 437 fill = new ObjectPropertyBase<Paint>(null) { 438 439 @Override 440 protected void invalidated() { 441 markDirty(SubSceneDirtyBits.FILL_DIRTY); 442 } 443 444 @Override 445 public Object getBean() { 446 return SubScene.this; 447 } 448 449 @Override 450 public String getName() { 451 return "fill"; 452 } 453 }; 454 } 455 return fill; 456 } 457 458 /** 459 * @treatAsPrivate implementation detail 460 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 461 */ 462 @Deprecated @Override 463 public void impl_updatePG() { 464 super.impl_updatePG(); 465 466 // TODO deal with clip node 467 468 dirtyNodes = dirtyLayout = false; 469 if (isDirty()) { 470 PGSubScene peer = (PGSubScene) impl_getPGNode(); 471 final Camera cam = getEffectiveCamera(); 472 boolean contentChanged = false; 473 if (cam.getSubScene() == null && 474 isDirty(SubSceneDirtyBits.CONTENT_DIRTY)) { 475 // When camera is not a part of the graph, then its 476 // owner(subscene) must take care of syncing it. And when a 477 // property on the camera changes it will mark subscenes 478 // CONTENT_DIRTY. 479 cam.impl_syncPGNode(); 480 } 481 if (isDirty(SubSceneDirtyBits.FILL_DIRTY)) { 482 Object platformPaint = getFill() == null ? null : 483 Toolkit.getPaintAccessor().getPlatformPaint(getFill()); 484 peer.setFillPaint(platformPaint); 485 contentChanged = true; 486 } 487 peer.setDepthBuffer(isDepthBufferInteral()); 488 if (isDirty(SubSceneDirtyBits.SIZE_DIRTY)) { 489 // Note change in size is a geom change and is handled by peer 490 peer.setWidth((float)getWidth()); 491 peer.setHeight((float)getHeight()); 492 } 493 if (isDirty(SubSceneDirtyBits.CAMERA_DIRTY)) { 494 peer.setCamera(cam.getPlatformCamera()); 495 contentChanged = true; 496 } 497 if (isDirty(SubSceneDirtyBits.ROOT_SG_DIRTY)) { 498 peer.setRoot(getRoot().impl_getPGNode()); 499 contentChanged = true; 500 } 501 contentChanged |= syncLights(); 502 if (contentChanged || isDirty(SubSceneDirtyBits.CONTENT_DIRTY)) { 503 peer.markContentDirty(); 504 } 505 506 clearDirtyBits(); 507 } 508 509 } 510 511 @Override 512 void nodeResolvedOrientationChanged() { 513 getRoot().parentResolvedOrientationInvalidated(); 514 } 515 516 /*********************************************************************** 517 * CSS * 518 **********************************************************************/ 519 /** 520 * @treatAsPrivate implementation detail 521 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 522 */ 523 @Deprecated @Override 524 protected void impl_processCSS() { 525 // Nothing to do... 526 if (cssFlag == CssFlags.CLEAN) { return; } 527 528 if (getRoot().cssFlag == CssFlags.CLEAN) { 529 getRoot().cssFlag = cssFlag; 530 } 531 super.impl_processCSS(); 532 getRoot().processCSS(); 533 } 534 535 @Override 536 void processCSS() { 537 Parent root = getRoot(); 538 if (root.impl_isDirty(DirtyBits.NODE_CSS)) { 539 root.impl_clearDirty(DirtyBits.NODE_CSS); 540 if (cssFlag == CssFlags.CLEAN) { cssFlag = CssFlags.UPDATE; } 541 } 542 super.processCSS(); 543 } 544 545 @Override void updateBounds() { 546 super.updateBounds(); 547 getRoot().updateBounds(); 548 } 549 550 /** 551 * @treatAsPrivate implementation detail 552 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 553 */ 554 @Deprecated @Override 555 protected PGNode impl_createPGNode() { 556 return Toolkit.getToolkit().createPGSubScene(); 557 } 558 559 /** 560 * @treatAsPrivate implementation detail 561 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 562 */ 563 @Deprecated @Override 564 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 565 int w = (int)Math.ceil(width.get()); 566 int h = (int)Math.ceil(height.get()); 567 bounds = bounds.deriveWithNewBounds(0.0f, 0.0f, 0.0f, 568 w, h, 0.0f); 569 bounds = tx.transform(bounds, bounds); 570 return bounds; 571 } 572 573 /*********************************************************************** 574 * Dirty Bits * 575 **********************************************************************/ 576 boolean dirtyLayout = false; 577 void setDirtyLayout(Parent p) { 578 if (!dirtyLayout && p != null && p.getSubScene() == this && 579 this.getScene() != null) { 580 dirtyLayout = true; 581 markDirty(SubSceneDirtyBits.CONTENT_DIRTY); 582 } 583 } 584 585 private boolean dirtyNodes = false; 586 void setDirty(Node n) { 587 if (!dirtyNodes && n != null && n.getSubScene() == this && 588 this.getScene() != null) { 589 dirtyNodes = true; 590 markDirty(SubSceneDirtyBits.CONTENT_DIRTY); 591 } 592 } 593 594 private enum SubSceneDirtyBits { 595 SIZE_DIRTY, 596 FILL_DIRTY, 597 ROOT_SG_DIRTY, 598 CAMERA_DIRTY, 599 LIGHTS_DIRTY, 600 CONTENT_DIRTY; 601 602 private int mask; 603 604 private SubSceneDirtyBits() { mask = 1 << ordinal(); } 605 606 public final int getMask() { return mask; } 607 } 608 609 private int dirtyBits = ~0; 610 611 private void clearDirtyBits() { dirtyBits = 0; } 612 613 private boolean isDirty() { return dirtyBits != 0; } 614 615 // Should not be called directly, instead use markDirty 616 private void setDirty(SubSceneDirtyBits dirtyBit) { 617 this.dirtyBits |= dirtyBit.getMask(); 618 } 619 620 private boolean isDirty(SubSceneDirtyBits dirtyBit) { 621 return ((this.dirtyBits & dirtyBit.getMask()) != 0); 622 } 623 624 private void markDirty(SubSceneDirtyBits dirtyBit) { 625 if (!isDirty()) { 626 // Force SubScene to redraw 627 impl_markDirty(DirtyBits.NODE_CONTENTS); 628 } 629 setDirty(dirtyBit); 630 } 631 632 /*********************************************************************** 633 * Picking * 634 **********************************************************************/ 635 636 /** 637 * @treatAsPrivate implementation detail 638 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 639 */ 640 @Deprecated @Override 641 protected boolean impl_computeContains(double localX, double localY) { 642 if (subSceneComputeContains(localX, localY)) { 643 return true; 644 } else { 645 return getRoot().impl_computeContains(localX, localY); 646 } 647 } 648 649 /** 650 * Determines whether subScene contains the given point. 651 * It does not consider the contained nodes, only subScene's 652 * size and fills. 653 * @param localX horizontal coordinate in the local space of the subScene node 654 * @param localY vertical coordinate in the local space of the subScene node 655 * @return true if the point is inside subScene's area covered by its fill 656 */ 657 private boolean subSceneComputeContains(double localX, double localY) { 658 if (localX < 0 || localY < 0 || localX > getWidth() || localY > getHeight()) { 659 return false; 660 } 661 return getFill() != null; 662 } 663 664 /* 665 * Generates a pick ray based on local coordinates and camera. Then finds a 666 * top-most child node that intersects the pick ray. 667 */ 668 private PickResult pickRootSG(double localX, double localY) { 669 final double viewWidth = getWidth(); 670 final double viewHeight = getHeight(); 671 if (localX < 0 || localY < 0 || localX > viewWidth || localY > viewHeight) { 672 return null; 673 } 674 final PickResultChooser result = new PickResultChooser(); 675 final PickRay pickRay = getEffectiveCamera().computePickRay(localX, localY, new PickRay()); 676 getRoot().impl_pickNode(pickRay, result); 677 return result.toPickResult(); 678 } 679 680 /** 681 * Finds a top-most child node that contains the given local coordinates. 682 * 683 * Returns the picked node, null if no such node was found. 684 * @treatAsPrivate implementation detail 685 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 686 */ 687 @Deprecated @Override 688 protected void impl_pickNodeLocal(PickRay localPickRay, PickResultChooser result) { 689 final double boundsDistance = impl_intersectsBounds(localPickRay); 690 if (!Double.isNaN(boundsDistance) && result.isCloser(boundsDistance)) { 691 final Point3D intersectPt = PickResultChooser.computePoint( 692 localPickRay, boundsDistance); 693 final PickResult subSceneResult = 694 pickRootSG(intersectPt.getX(), intersectPt.getY()); 695 if (subSceneResult != null) { 696 result.offerSubScenePickResult(this, subSceneResult, boundsDistance); 697 } else if (isPickOnBounds() || 698 subSceneComputeContains(intersectPt.getX(), intersectPt.getY())) { 699 result.offer(this, boundsDistance, intersectPt); 700 } 701 } 702 } 703 704 /** 705 * @treatAsPrivate implementation detail 706 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 707 */ 708 @Deprecated @Override 709 public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) { 710 throw new UnsupportedOperationException("Not supported yet."); 711 } 712 713 714 private List<LightBase> lights = new ArrayList<>(); 715 716 // @param light must not be null 717 final void addLight(LightBase light) { 718 if (!lights.contains(light)) { 719 markDirty(SubSceneDirtyBits.LIGHTS_DIRTY); 720 lights.add(light); 721 } 722 } 723 724 final void removeLight(LightBase light) { 725 if (lights.remove(light)) { 726 markDirty(SubSceneDirtyBits.LIGHTS_DIRTY); 727 } 728 } 729 730 /** 731 * PG Light synchronizer. 732 */ 733 private boolean syncLights() { 734 boolean lightOwnerChanged = false; 735 if (!isDirty(SubSceneDirtyBits.LIGHTS_DIRTY)) { 736 return lightOwnerChanged; 737 } 738 PGSubScene pgSubScene = (PGSubScene) impl_getPGNode(); 739 Object peerLights[] = pgSubScene.getLights(); 740 if (!lights.isEmpty() || (peerLights != null)) { 741 if (lights.isEmpty()) { 742 pgSubScene.setLights(null); 743 } else { 744 if (peerLights == null || peerLights.length < lights.size()) { 745 peerLights = new PGLightBase[lights.size()]; 746 } 747 int i = 0; 748 for (; i < lights.size(); i++) { 749 peerLights[i] = lights.get(i).impl_getPGNode(); 750 } 751 // Clear the rest of the list 752 while (i < peerLights.length && peerLights[i] != null) { 753 peerLights[i++] = null; 754 } 755 pgSubScene.setLights(peerLights); 756 } 757 lightOwnerChanged = true; 758 } 759 return lightOwnerChanged; 760 } 761 762 static { 763 // This is used by classes in different packages to get access to 764 // private and package private methods. 765 SubSceneHelper.setSubSceneAccessor(new SubSceneHelper.SubSceneAccessor() { 766 767 @Override 768 public boolean isDepthBuffer(SubScene subScene) { 769 return subScene.isDepthBufferInteral(); 770 }; 771 772 @Override 773 public Camera getEffectiveCamera(SubScene subScene) { 774 return subScene.getEffectiveCamera(); 775 } 776 }); 777 } 778}