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