1 /* 2 * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene; 27 28 import com.sun.javafx.collections.TrackableObservableList; 29 import com.sun.javafx.geom.BaseBounds; 30 import com.sun.javafx.geom.BoxBounds; 31 import com.sun.javafx.geom.transform.Affine3D; 32 import com.sun.javafx.geom.transform.BaseTransform; 33 import com.sun.javafx.jmx.MXNodeAlgorithm; 34 import com.sun.javafx.jmx.MXNodeAlgorithmContext; 35 import com.sun.javafx.scene.DirtyBits; 36 import com.sun.javafx.scene.transform.TransformHelper; 37 import com.sun.javafx.sg.prism.NGLightBase; 38 import com.sun.javafx.tk.Toolkit; 39 import javafx.application.ConditionalFeature; 40 import javafx.application.Platform; 41 import javafx.beans.InvalidationListener; 42 import javafx.beans.Observable; 43 import javafx.beans.property.BooleanProperty; 44 import javafx.beans.property.ObjectProperty; 45 import javafx.beans.property.SimpleBooleanProperty; 46 import javafx.beans.property.SimpleObjectProperty; 47 import javafx.collections.ListChangeListener.Change; 48 import javafx.collections.ObservableList; 49 import javafx.scene.paint.Color; 50 import javafx.scene.shape.Shape3D; 51 import sun.util.logging.PlatformLogger; 52 53 /** 54 * The {@code LightBase} class provides definitions of common properties for 55 * objects that represent a form of Light source. These properties 56 * include: 57 * <ul> 58 * <li>The color that defines color of the light source. 59 * </ul> 60 * 61 * Note that this is a conditional feature. See 62 * {@link javafx.application.ConditionalFeature#SCENE3D ConditionalFeature.SCENE3D} 63 * for more information. 64 * 65 * @since JavaFX 8.0 66 */ 67 public abstract class LightBase extends Node { 68 69 private Affine3D localToSceneTx = new Affine3D(); 70 71 /** 72 * Creates a new instance of {@code LightBase} class with a default Color.WHITE light source. 73 */ 74 protected LightBase() { 75 this(Color.WHITE); 76 } 77 78 /** 79 * Creates a new instance of {@code LightBase} class using the specified color. 80 * 81 * @param color the color of the light source 82 */ 83 protected LightBase(Color color) { 84 if (!Platform.isSupported(ConditionalFeature.SCENE3D)) { 85 String logname = LightBase.class.getName(); 86 PlatformLogger.getLogger(logname).warning("System can't support " 87 + "ConditionalFeature.SCENE3D"); 88 } 89 90 setColor(color); 91 this.localToSceneTransformProperty().addListener(observable -> impl_markDirty(DirtyBits.NODE_LIGHT_TRANSFORM)); 92 } 93 94 /** 95 * Specifies the color of light source. 96 * 97 * @defaultValue null 98 */ 99 private ObjectProperty<Color> color; 100 101 public final void setColor(Color value) { 102 colorProperty().set(value); 103 } 104 105 public final Color getColor() { 106 return color == null ? null : color.get(); 107 } 108 109 public final ObjectProperty<Color> colorProperty() { 110 if (color == null) { 111 color = new SimpleObjectProperty<Color>(LightBase.this, "color") { 112 @Override 113 protected void invalidated() { 114 impl_markDirty(DirtyBits.NODE_LIGHT); 115 } 116 }; 117 } 118 return color; 119 } 120 121 /** 122 * Defines the light on or off. 123 * 124 * @defaultValue true 125 */ 126 private BooleanProperty lightOn; 127 128 public final void setLightOn(boolean value) { 129 lightOnProperty().set(value); 130 } 131 132 public final boolean isLightOn() { 133 return lightOn == null ? true : lightOn.get(); 134 } 135 136 public final BooleanProperty lightOnProperty() { 137 if (lightOn == null) { 138 lightOn = new SimpleBooleanProperty(LightBase.this, "lightOn", true) { 139 @Override 140 protected void invalidated() { 141 impl_markDirty(DirtyBits.NODE_LIGHT); 142 } 143 }; 144 } 145 return lightOn; 146 } 147 148 private ObservableList<Node> scope; 149 150 /** 151 * Gets the list of nodes that specifies the 152 * hierarchical scope of this Light. If the scope list is empty, 153 * the Light node has universe scope: all nodes under it's scene 154 * are affected by it. If the scope list is non-empty, only those 155 * 3D Shape nodes in the scope list and under the Group nodes in the 156 * scope list are affected by this Light node. 157 */ 158 public ObservableList<Node> getScope() { 159 if (scope == null) { 160 scope = new TrackableObservableList<Node>() { 161 162 @Override 163 protected void onChanged(Change<Node> c) { 164 impl_markDirty(DirtyBits.NODE_LIGHT_SCOPE); 165 while (c.next()) { 166 for (Node node : c.getRemoved()) { 167 // Update the removed nodes 168 if (node instanceof Parent || node instanceof Shape3D) { 169 markChildrenDirty(node); 170 } 171 } 172 for (Node node : c.getAddedSubList()) { 173 if (node instanceof Parent || node instanceof Shape3D) { 174 markChildrenDirty(node); 175 } 176 } 177 } 178 } 179 }; 180 } 181 return scope; 182 } 183 184 @Override 185 void scenesChanged(final Scene newScene, final SubScene newSubScene, 186 final Scene oldScene, final SubScene oldSubScene) { 187 // This light is owned by the Scene/SubScene, and thus must change 188 // accordingly. Note lights can owned by either a Scene or SubScene, 189 // but not both. 190 if (oldSubScene != null) { 191 oldSubScene.removeLight(this); 192 } else if (oldScene != null) { 193 oldScene.removeLight(this); 194 } 195 if (newSubScene != null) { 196 newSubScene.addLight(this); 197 } else if (newScene != null) { 198 newScene.addLight(this); 199 } 200 } 201 202 private void markOwnerDirty() { 203 // if the light is part of the scene/subScene, we will need to notify 204 // the owner to mark the entire scene/subScene dirty. 205 SubScene subScene = getSubScene(); 206 if (subScene != null) { 207 subScene.markContentDirty(); 208 } else { 209 Scene scene = getScene(); 210 if (scene != null) { 211 scene.setNeedsRepaint(); 212 } 213 } 214 } 215 216 private void markChildrenDirty(Node node) { 217 if (node instanceof Shape3D) { 218 // Dirty using a lightweight DirtyBits.NODE_DRAWMODE bit 219 ((Shape3D) node).impl_markDirty(DirtyBits.NODE_DRAWMODE); 220 } else if (node instanceof Parent) { 221 for (Node child : ((Parent) node).getChildren()) { 222 markChildrenDirty(child); 223 } 224 } 225 } 226 227 /** 228 * @treatAsPrivate implementation detail 229 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 230 */ 231 @Deprecated 232 @Override 233 protected void impl_markDirty(DirtyBits dirtyBit) { 234 super.impl_markDirty(dirtyBit); 235 if ((scope == null) || getScope().isEmpty()) { 236 // This light affect the entire scene/subScene 237 markOwnerDirty(); 238 } else if (dirtyBit != DirtyBits.NODE_LIGHT_SCOPE) { 239 // Skip NODE_LIGHT_SCOPE dirty since it is processed on scope change. 240 ObservableList<Node> tmpScope = getScope(); 241 for (int i = 0, max = tmpScope.size(); i < max; i++) { 242 markChildrenDirty(tmpScope.get(i)); 243 } 244 } 245 } 246 247 /** 248 * @treatAsPrivate implementation detail 249 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 250 */ 251 @Deprecated 252 @Override 253 public void impl_updatePeer() { 254 super.impl_updatePeer(); 255 NGLightBase peer = impl_getPeer(); 256 if (impl_isDirty(DirtyBits.NODE_LIGHT)) { 257 peer.setColor((getColor() == null) ? 258 Toolkit.getPaintAccessor().getPlatformPaint(Color.WHITE) 259 : Toolkit.getPaintAccessor().getPlatformPaint(getColor())); 260 peer.setLightOn(isLightOn()); 261 } 262 263 if (impl_isDirty(DirtyBits.NODE_LIGHT_SCOPE)) { 264 if (scope != null) { 265 ObservableList<Node> tmpScope = getScope(); 266 if (tmpScope.isEmpty()) { 267 peer.setScope(null); 268 } else { 269 Object ngList[] = new Object[tmpScope.size()]; 270 for (int i = 0; i < tmpScope.size(); i++) { 271 Node n = tmpScope.get(i); 272 ngList[i] = n.impl_getPeer(); 273 } 274 peer.setScope(ngList); 275 } 276 } 277 } 278 279 if (impl_isDirty(DirtyBits.NODE_LIGHT_TRANSFORM)) { 280 localToSceneTx.setToIdentity(); 281 TransformHelper.apply(getLocalToSceneTransform(), localToSceneTx); 282 // TODO: 3D - For now, we are treating the scene as world. This may need to change 283 // for the fixed eye position case. 284 peer.setWorldTransform(localToSceneTx); 285 } 286 } 287 288 /** 289 * @treatAsPrivate implementation detail 290 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 291 */ 292 @Deprecated 293 @Override 294 public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) { 295 // TODO: 3D - Check is this the right default 296 return new BoxBounds(); 297 } 298 299 /** 300 * @treatAsPrivate implementation detail 301 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 302 */ 303 @Deprecated 304 @Override 305 protected boolean impl_computeContains(double localX, double localY) { 306 // TODO: 3D - Check is this the right default 307 return false; 308 } 309 310 /** 311 * @treatAsPrivate implementation detail 312 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 313 */ 314 @Deprecated 315 @Override 316 public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) { 317 throw new UnsupportedOperationException("Not supported yet."); 318 } 319 320 }