1 /* 2 * Copyright (c) 2010, 2013, 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.effect; 27 28 import javafx.beans.Observable; 29 import javafx.beans.property.DoubleProperty; 30 import javafx.beans.property.DoublePropertyBase; 31 import javafx.beans.property.ObjectProperty; 32 import javafx.beans.property.ObjectPropertyBase; 33 import javafx.scene.Node; 34 35 import com.sun.javafx.util.Utils; 36 import com.sun.javafx.effect.EffectDirtyBits; 37 import com.sun.javafx.geom.BaseBounds; 38 import com.sun.javafx.geom.transform.BaseTransform; 39 import com.sun.javafx.scene.BoundsAccessor; 40 import com.sun.scenario.effect.PhongLighting; 41 42 /** 43 * An effect that simulates a light source shining on the given content, 44 * which can be used to give flat objects a more realistic, three-dimensional 45 * appearance. 46 * 47 * <p> 48 * Example: 49 * <pre><code> 50 * Light.Distant light = new Light.Distant(); 51 * light.setAzimuth(-135.0); 52 * 53 * Lighting lighting = new Lighting(); 54 * lighting.setLight(light); 55 * lighting.setSurfaceScale(5.0); 56 * 57 * Text text = new Text(); 58 * text.setText("JavaFX!"); 59 * text.setFill(Color.STEELBLUE); 60 * text.setFont(Font.font(null, FontWeight.BOLD, 60)); 61 * text.setX(10.0); 62 * text.setY(10.0); 63 * text.setTextOrigin(VPos.TOP); 64 * 65 * text.setEffect(lighting); 66 * </pre></code> 67 * <p> The code above produces the following: </p> 68 * <p> 69 * <img * src="doc-files/lighting.png"/> 70 * </p> 71 * @since JavaFX 2.0 72 */ 73 public class Lighting extends Effect { 74 @Override 75 com.sun.scenario.effect.PhongLighting impl_createImpl() { 76 return new PhongLighting(getLightInternal().impl_getImpl()); 77 }; 78 79 /** 80 * Creates a new instance of Lighting with default parameters. 81 */ 82 public Lighting() { 83 Shadow shadow = new Shadow(); 84 shadow.setRadius(10.0f); 85 setBumpInput(shadow); 86 } 87 88 /** 89 * Creates a new instance of Lighting with the specified light. 90 * @param light the light source for this {@code Lighting} effect 91 * @since JavaFX 2.1 92 */ 93 public Lighting(Light light) { 94 Shadow shadow = new Shadow(); 95 shadow.setRadius(10.0f); 96 setBumpInput(shadow); 97 setLight(light); 98 } 99 100 private final Light defaultLight = new Light.Distant(); 101 102 /** 103 * The light source for this {@code Lighting} effect. 104 */ 105 private ObjectProperty<Light> light = new ObjectPropertyBase<Light>(new Light.Distant()) { 106 @Override 107 public void invalidated() { 108 markDirty(EffectDirtyBits.EFFECT_DIRTY); 109 effectBoundsChanged(); 110 } 111 112 @Override 113 public Object getBean() { 114 return Lighting.this; 115 } 116 117 @Override 118 public String getName() { 119 return "light"; 120 } 121 }; 122 123 124 public final void setLight(Light value) { 125 lightProperty().set(value); 126 } 127 128 public final Light getLight() { 129 return light.get(); 130 } 131 132 public final ObjectProperty<Light> lightProperty() { 133 return light; 134 } 135 136 private final LightChangeListener lightChangeListener = new LightChangeListener(); 137 138 /** 139 * @treatAsPrivate implementation detail 140 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 141 */ 142 @Deprecated 143 @Override 144 public Effect impl_copy() { 145 Lighting lighting = new Lighting(this.getLight()); 146 lighting.setBumpInput(this.getBumpInput()); 147 lighting.setContentInput(this.getContentInput()); 148 lighting.setDiffuseConstant(this.getDiffuseConstant()); 149 lighting.setSpecularConstant(this.getSpecularConstant()); 150 lighting.setSpecularExponent(this.getSpecularExponent()); 151 lighting.setSurfaceScale(this.getSurfaceScale()); 152 return lighting; 153 } 154 private class LightChangeListener extends EffectChangeListener { 155 Light light; 156 157 public void register(Light value) { 158 light = value; 159 super.register(light == null ? null : light.effectDirtyProperty()); 160 } 161 162 @Override 163 public void invalidated(Observable valueModel) { 164 if (light.impl_isEffectDirty()) { 165 markDirty(EffectDirtyBits.EFFECT_DIRTY); 166 effectBoundsChanged(); 167 } 168 } 169 }; 170 /** 171 * The optional bump map input. 172 * If not specified, a bump map will be automatically generated 173 * from the default input. 174 * If set to {@code null}, or left unspecified, a graphical image of 175 * the {@code Node} to which the {@code Effect} is attached will be 176 * used to generate a default bump map. 177 * @defaultValue a Shadow effect with a radius of 10 178 */ 179 private ObjectProperty<Effect> bumpInput; 180 181 182 public final void setBumpInput(Effect value) { 183 bumpInputProperty().set(value); 184 } 185 186 public final Effect getBumpInput() { 187 return bumpInput == null ? null : bumpInput.get(); 188 } 189 190 public final ObjectProperty<Effect> bumpInputProperty() { 191 if (bumpInput == null) { 192 bumpInput = new EffectInputProperty("bumpInput"); 193 } 194 return bumpInput; 195 } 196 197 /** 198 * The content input for this {@code Effect}. 199 * If set to {@code null}, or left unspecified, a graphical image of 200 * the {@code Node} to which the {@code Effect} is attached will be 201 * used as the input. 202 * @defaultValue null 203 */ 204 private ObjectProperty<Effect> contentInput; 205 206 207 public final void setContentInput(Effect value) { 208 contentInputProperty().set(value); 209 } 210 211 public final Effect getContentInput() { 212 return contentInput == null ? null : contentInput.get(); 213 } 214 215 public final ObjectProperty<Effect> contentInputProperty() { 216 if (contentInput == null) { 217 contentInput = new EffectInputProperty("contentInput"); 218 } 219 return contentInput; 220 } 221 222 @Override 223 boolean impl_checkChainContains(Effect e) { 224 Effect localBumpInput = getBumpInput(); 225 Effect localContentInput = getContentInput(); 226 if (localContentInput == e || localBumpInput == e) 227 return true; 228 if (localContentInput != null && localContentInput.impl_checkChainContains(e)) 229 return true; 230 if (localBumpInput != null && localBumpInput.impl_checkChainContains(e)) 231 return true; 232 233 return false; 234 } 235 236 /** 237 * The diffuse constant. 238 * <pre> 239 * Min: 0.0 240 * Max: 2.0 241 * Default: 1.0 242 * Identity: n/a 243 * </pre> 244 * @defaultValue 1.0 245 */ 246 private DoubleProperty diffuseConstant; 247 248 249 public final void setDiffuseConstant(double value) { 250 diffuseConstantProperty().set(value); 251 } 252 253 public final double getDiffuseConstant() { 254 return diffuseConstant == null ? 1 : diffuseConstant.get(); 255 } 256 257 public final DoubleProperty diffuseConstantProperty() { 258 if (diffuseConstant == null) { 259 diffuseConstant = new DoublePropertyBase(1) { 260 261 @Override 262 public void invalidated() { 263 markDirty(EffectDirtyBits.EFFECT_DIRTY); 264 } 265 266 @Override 267 public Object getBean() { 268 return Lighting.this; 269 } 270 271 @Override 272 public String getName() { 273 return "diffuseConstant"; 274 } 275 }; 276 } 277 return diffuseConstant; 278 } 279 280 /** 281 * The specular constant. 282 * <pre> 283 * Min: 0.0 284 * Max: 2.0 285 * Default: 0.3 286 * Identity: n/a 287 * </pre> 288 * @defaultValue 0.3 289 */ 290 private DoubleProperty specularConstant; 291 292 293 public final void setSpecularConstant(double value) { 294 specularConstantProperty().set(value); 295 } 296 297 public final double getSpecularConstant() { 298 return specularConstant == null ? 0.3 : specularConstant.get(); 299 } 300 301 public final DoubleProperty specularConstantProperty() { 302 if (specularConstant == null) { 303 specularConstant = new DoublePropertyBase(0.3) { 304 305 @Override 306 public void invalidated() { 307 markDirty(EffectDirtyBits.EFFECT_DIRTY); 308 } 309 310 @Override 311 public Object getBean() { 312 return Lighting.this; 313 } 314 315 @Override 316 public String getName() { 317 return "specularConstant"; 318 } 319 }; 320 } 321 return specularConstant; 322 } 323 324 /** 325 * The specular exponent. 326 * <pre> 327 * Min: 0.0 328 * Max: 40.0 329 * Default: 20.0 330 * Identity: n/a 331 * </pre> 332 * @defaultValue 20.0 333 */ 334 private DoubleProperty specularExponent; 335 336 337 public final void setSpecularExponent(double value) { 338 specularExponentProperty().set(value); 339 } 340 341 public final double getSpecularExponent() { 342 return specularExponent == null ? 20 : specularExponent.get(); 343 } 344 345 public final DoubleProperty specularExponentProperty() { 346 if (specularExponent == null) { 347 specularExponent = new DoublePropertyBase(20) { 348 349 @Override 350 public void invalidated() { 351 markDirty(EffectDirtyBits.EFFECT_DIRTY); 352 } 353 354 @Override 355 public Object getBean() { 356 return Lighting.this; 357 } 358 359 @Override 360 public String getName() { 361 return "specularExponent"; 362 } 363 }; 364 } 365 return specularExponent; 366 } 367 368 /** 369 * The surface scale factor. 370 * <pre> 371 * Min: 0.0 372 * Max: 10.0 373 * Default: 1.5 374 * Identity: n/a 375 * </pre> 376 * @defaultValue 1.5 377 */ 378 private DoubleProperty surfaceScale; 379 380 381 public final void setSurfaceScale(double value) { 382 surfaceScaleProperty().set(value); 383 } 384 385 public final double getSurfaceScale() { 386 return surfaceScale == null ? 1.5 : surfaceScale.get(); 387 } 388 389 public final DoubleProperty surfaceScaleProperty() { 390 if (surfaceScale == null) { 391 surfaceScale = new DoublePropertyBase(1.5) { 392 393 @Override 394 public void invalidated() { 395 markDirty(EffectDirtyBits.EFFECT_DIRTY); 396 } 397 398 @Override 399 public Object getBean() { 400 return Lighting.this; 401 } 402 403 @Override 404 public String getName() { 405 return "surfaceScale"; 406 } 407 }; 408 } 409 return surfaceScale; 410 } 411 412 private Light getLightInternal() { 413 Light localLight = getLight(); 414 return localLight == null ? defaultLight : localLight; 415 } 416 417 @Override 418 void impl_update() { 419 Effect localBumpInput = getBumpInput(); 420 421 if (localBumpInput != null) { 422 localBumpInput.impl_sync(); 423 } 424 425 Effect localContentInput = getContentInput(); 426 if (localContentInput != null) { 427 localContentInput.impl_sync(); 428 } 429 430 PhongLighting peer = (PhongLighting) impl_getImpl(); 431 peer.setBumpInput(localBumpInput == null ? null : localBumpInput.impl_getImpl()); 432 peer.setContentInput(localContentInput == null ? null : localContentInput.impl_getImpl()); 433 peer.setDiffuseConstant((float)Utils.clamp(0, getDiffuseConstant(), 2)); 434 peer.setSpecularConstant((float)Utils.clamp(0, getSpecularConstant(), 2)); 435 peer.setSpecularExponent((float)Utils.clamp(0, getSpecularExponent(), 40)); 436 peer.setSurfaceScale((float)Utils.clamp(0, getSurfaceScale(), 10)); 437 // we don't need to register on default light in case the light is null 438 // because default light never changes 439 lightChangeListener.register(getLight()); 440 441 getLightInternal().impl_sync(); 442 peer.setLight(getLightInternal().impl_getImpl()); 443 } 444 445 /** 446 * @treatAsPrivate implementation detail 447 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 448 */ 449 @Deprecated 450 @Override 451 public BaseBounds impl_getBounds(BaseBounds bounds, 452 BaseTransform tx, 453 Node node, 454 BoundsAccessor boundsAccessor) { 455 return getInputBounds(bounds, tx, node, boundsAccessor, getContentInput()); 456 } 457 }