/*
 * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.scene.paint;

import com.sun.javafx.beans.event.AbstractNotifyListener;
import com.sun.javafx.scene.paint.MaterialHelper;
import com.sun.javafx.sg.prism.NGPhongMaterial;
import com.sun.javafx.tk.Toolkit;
import javafx.beans.Observable;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.AmbientLight;
import javafx.scene.PointLight;
import javafx.scene.image.Image;

The PhongMaterial class provides definitions of properties that represent a Phong shaded material. It describes the interaction of light with the surface of the Mesh it is applied to. The PhongMaterial reflects light in terms of a diffuse and specular component together with an ambient and a self illumination term. The color of a point on a geometric surface is mathematical function of these four components.

The color is computed by the following equation:


for each ambient light source i {
    ambient += lightColor[i]
 }
for each point light source i {
    diffuse += (L[i] . N) * lightColor[i]
    specular += ((R[i] . V) ^ (specularPower * intensity(specularMap))) * lightColor[i]
 }
color = (ambient + diffuse) * diffuseColor * diffuseMap
            + specular * specularColor * specularMap
            + selfIlluminationMap
where lightColor[i] is the color of light source i,
L[i] is the vector from the surface to light source i,
N is the normal vector (taking into the account the bumpMap if present),
R[i] is the normalized reflection vector for L[i] about the surface normal,
and V is the normalized view vector.
See Also:
Since:JavaFX 8.0
/** * The {@code PhongMaterial} class provides definitions of properties that * represent a Phong shaded material. It describes the interaction of * light with the surface of the {@code Mesh} it is applied to. The {@code PhongMaterial} * reflects light in terms of a diffuse and specular component together with * an ambient and a self illumination term. The color of a point on a geometric * surface is mathematical function of these four components. * <p> * The color is computed by the following equation: * * <pre>{@code * for each ambient light source i { * ambient += lightColor[i] * } * * for each point light source i { * diffuse += (L[i] . N) * lightColor[i] * specular += ((R[i] . V) ^ (specularPower * intensity(specularMap))) * lightColor[i] * } * * color = (ambient + diffuse) * diffuseColor * diffuseMap * + specular * specularColor * specularMap * + selfIlluminationMap * }</pre> * where * {@code lightColor[i]} is the color of light source i,<br> * {@code L[i]} is the vector from the surface to light source i,<br> * {@code N} is the normal vector (taking into the account the bumpMap if present),<br> * {@code R[i]} is the normalized reflection vector for L[i] about the surface normal,<br> * and {@code V} is the normalized view vector. * * @see AmbientLight * @see PointLight * @since JavaFX 8.0 */
public class PhongMaterial extends Material { private boolean diffuseColorDirty = true; private boolean specularColorDirty = true; private boolean specularPowerDirty = true; private boolean diffuseMapDirty = true; private boolean specularMapDirty = true; private boolean bumpMapDirty = true; private boolean selfIlluminationMapDirty = true;
Creates a new instance of PhongMaterial class with a default Color.WHITE diffuseColor property.
/** * Creates a new instance of {@code PhongMaterial} class with a default * Color.WHITE {@code diffuseColor} property. */
public PhongMaterial() { setDiffuseColor(Color.WHITE); }
Creates a new instance of PhongMaterial class using the specified color for its diffuseColor property.
Params:
  • diffuseColor – the color of the diffuseColor property
/** * Creates a new instance of {@code PhongMaterial} class using the specified * color for its {@code diffuseColor} property. * * @param diffuseColor the color of the diffuseColor property */
public PhongMaterial(Color diffuseColor) { setDiffuseColor(diffuseColor); }
Creates a new instance of PhongMaterial class using the specified colors and images for its diffuseColor properties.
Params:
  • diffuseColor – the color of the diffuseColor property
  • diffuseMap – the image of the diffuseMap property
  • specularMap – the image of the specularMap property
  • bumpMap – the image of the bumpMap property
  • selfIlluminationMap – the image of the selfIlluminationMap property
/** * Creates a new instance of {@code PhongMaterial} class using the specified * colors and images for its {@code diffuseColor} properties. * * @param diffuseColor the color of the diffuseColor property * @param diffuseMap the image of the diffuseMap property * @param specularMap the image of the specularMap property * @param bumpMap the image of the bumpMap property * @param selfIlluminationMap the image of the selfIlluminationMap property * */
public PhongMaterial(Color diffuseColor, Image diffuseMap, Image specularMap, Image bumpMap, Image selfIlluminationMap) { setDiffuseColor(diffuseColor); setDiffuseMap(diffuseMap); setSpecularMap(specularMap); setBumpMap(bumpMap); setSelfIlluminationMap(selfIlluminationMap); }
The diffuse color of this PhongMaterial.
@defaultValueColor.WHITE
/** * The diffuse color of this {@code PhongMaterial}. * * @defaultValue Color.WHITE */
private ObjectProperty<Color> diffuseColor; public final void setDiffuseColor(Color value) { diffuseColorProperty().set(value); } public final Color getDiffuseColor() { return diffuseColor == null ? null : diffuseColor.get(); } public final ObjectProperty<Color> diffuseColorProperty() { if (diffuseColor == null) { diffuseColor = new SimpleObjectProperty<Color>(PhongMaterial.this, "diffuseColor") { @Override protected void invalidated() { diffuseColorDirty = true; setDirty(true); } }; } return diffuseColor; }
The specular color of this PhongMaterial.
@defaultValuenull
/** * The specular color of this {@code PhongMaterial}. * * @defaultValue null */
private ObjectProperty<Color> specularColor; public final void setSpecularColor(Color value) { specularColorProperty().set(value); } public final Color getSpecularColor() { return specularColor == null ? null : specularColor.get(); } public final ObjectProperty<Color> specularColorProperty() { if (specularColor == null) { specularColor = new SimpleObjectProperty<Color>(PhongMaterial.this, "specularColor") { @Override protected void invalidated() { specularColorDirty = true; setDirty(true); } }; } return specularColor; }
The specular power of this PhongMaterial.
@defaultValue32.0
/** * The specular power of this {@code PhongMaterial}. * * @defaultValue 32.0 */
private DoubleProperty specularPower; public final void setSpecularPower(double value) { specularPowerProperty().set(value); } public final double getSpecularPower() { return specularPower == null ? 32 : specularPower.get(); } public final DoubleProperty specularPowerProperty() { if (specularPower == null) { specularPower = new SimpleDoubleProperty(PhongMaterial.this, "specularPower", 32.0) { @Override public void invalidated() { specularPowerDirty = true; setDirty(true); } }; } return specularPower; } private final AbstractNotifyListener platformImageChangeListener = new AbstractNotifyListener() { @Override public void invalidated(Observable valueModel) { if (oldDiffuseMap != null && valueModel == Toolkit.getImageAccessor().getImageProperty(oldDiffuseMap)) { diffuseMapDirty = true; } else if (oldSpecularMap != null && valueModel == Toolkit.getImageAccessor().getImageProperty(oldSpecularMap)) { specularMapDirty = true; } else if (oldBumpMap != null && valueModel == Toolkit.getImageAccessor().getImageProperty(oldBumpMap)) { bumpMapDirty = true; } else if (oldSelfIlluminationMap != null && valueModel == Toolkit.getImageAccessor().getImageProperty(oldSelfIlluminationMap)) { selfIlluminationMapDirty = true; } setDirty(true); } };
The diffuse map of this PhongMaterial.
@defaultValuenull
/** * The diffuse map of this {@code PhongMaterial}. * * @defaultValue null */
// TODO: 3D - Texture or Image? For Media it might be better to have it as a Texture private ObjectProperty<Image> diffuseMap; public final void setDiffuseMap(Image value) { diffuseMapProperty().set(value); } public final Image getDiffuseMap() { return diffuseMap == null ? null : diffuseMap.get(); } private Image oldDiffuseMap; public final ObjectProperty<Image> diffuseMapProperty() { if (diffuseMap == null) { diffuseMap = new SimpleObjectProperty<Image>(PhongMaterial.this, "diffuseMap") { private boolean needsListeners = false; @Override public void invalidated() { Image _image = get(); if (needsListeners) { Toolkit.getImageAccessor().getImageProperty(oldDiffuseMap). removeListener(platformImageChangeListener.getWeakListener()); } needsListeners = _image != null && (Toolkit.getImageAccessor().isAnimation(_image) || _image.getProgress() < 1); if (needsListeners) { Toolkit.getImageAccessor().getImageProperty(_image). addListener(platformImageChangeListener.getWeakListener()); } oldDiffuseMap = _image; diffuseMapDirty = true; setDirty(true); } }; } return diffuseMap; }
The specular map of this PhongMaterial.
@defaultValuenull
/** * The specular map of this {@code PhongMaterial}. * * @defaultValue null */
// TODO: 3D - Texture or Image? For Media it might be better to have it as a Texture private ObjectProperty<Image> specularMap; public final void setSpecularMap(Image value) { specularMapProperty().set(value); } public final Image getSpecularMap() { return specularMap == null ? null : specularMap.get(); } private Image oldSpecularMap; public final ObjectProperty<Image> specularMapProperty() { if (specularMap == null) { specularMap = new SimpleObjectProperty<Image>(PhongMaterial.this, "specularMap") { private boolean needsListeners = false; @Override public void invalidated() { Image _image = get(); if (needsListeners) { Toolkit.getImageAccessor().getImageProperty(oldSpecularMap). removeListener(platformImageChangeListener.getWeakListener()); } needsListeners = _image != null && (Toolkit.getImageAccessor().isAnimation(_image) || _image.getProgress() < 1); if (needsListeners) { Toolkit.getImageAccessor().getImageProperty(_image). addListener(platformImageChangeListener.getWeakListener()); } oldSpecularMap = _image; specularMapDirty = true; setDirty(true); } }; } return specularMap; }
The bump map of this PhongMaterial, which is a normal map stored as a RGB Image.
@defaultValuenull
/** * The bump map of this {@code PhongMaterial}, which is a normal map stored * as a RGB {@link Image}. * * @defaultValue null */
// TODO: 3D - Texture or Image? For Media it might be better to have it as a Texture private ObjectProperty<Image> bumpMap; public final void setBumpMap(Image value) { bumpMapProperty().set(value); } public final Image getBumpMap() { return bumpMap == null ? null : bumpMap.get(); } private Image oldBumpMap; public final ObjectProperty<Image> bumpMapProperty() { if (bumpMap == null) { bumpMap = new SimpleObjectProperty<Image>(PhongMaterial.this, "bumpMap") { private boolean needsListeners = false; @Override public void invalidated() { Image _image = get(); if (needsListeners) { Toolkit.getImageAccessor().getImageProperty(oldBumpMap). removeListener(platformImageChangeListener.getWeakListener()); } needsListeners = _image != null && (Toolkit.getImageAccessor().isAnimation(_image) || _image.getProgress() < 1); if (needsListeners) { Toolkit.getImageAccessor().getImageProperty(_image). addListener(platformImageChangeListener.getWeakListener()); } oldBumpMap = _image; bumpMapDirty = true; setDirty(true); } }; } return bumpMap; }
The self illumination map of this PhongMaterial.
@defaultValuenull
/** * The self illumination map of this {@code PhongMaterial}. * * @defaultValue null */
// TODO: 3D - Texture or Image? For Media it might be better to have it as a Texture private ObjectProperty<Image> selfIlluminationMap; public final void setSelfIlluminationMap(Image value) { selfIlluminationMapProperty().set(value); } public final Image getSelfIlluminationMap() { return selfIlluminationMap == null ? null : selfIlluminationMap.get(); } private Image oldSelfIlluminationMap; public final ObjectProperty<Image> selfIlluminationMapProperty() { if (selfIlluminationMap == null) { selfIlluminationMap = new SimpleObjectProperty<Image>(PhongMaterial.this, "selfIlluminationMap") { private boolean needsListeners = false; @Override public void invalidated() { Image _image = get(); if (needsListeners) { Toolkit.getImageAccessor().getImageProperty(oldSelfIlluminationMap). removeListener(platformImageChangeListener.getWeakListener()); } needsListeners = _image != null && (Toolkit.getImageAccessor().isAnimation(_image) || _image.getProgress() < 1); if (needsListeners) { Toolkit.getImageAccessor().getImageProperty(_image). addListener(platformImageChangeListener.getWeakListener()); } oldSelfIlluminationMap = _image; selfIlluminationMapDirty = true; setDirty(true); } }; } return selfIlluminationMap; } @Override void setDirty(boolean value) { super.setDirty(value); if (!value) { diffuseColorDirty = false; specularColorDirty = false; specularPowerDirty = false; diffuseMapDirty = false; specularMapDirty = false; bumpMapDirty = false; selfIlluminationMapDirty = false; } }
The peer node created by the graphics Toolkit/Pipeline implementation
/** The peer node created by the graphics Toolkit/Pipeline implementation */
private NGPhongMaterial peer; @Override NGPhongMaterial getNGMaterial() { if (peer == null) { peer = new NGPhongMaterial(); } return peer; } @Override void updatePG(){ if (!isDirty()) { return; } final NGPhongMaterial pMaterial = MaterialHelper.getNGMaterial(this); if (diffuseColorDirty) { pMaterial.setDiffuseColor(getDiffuseColor() == null ? null : Toolkit.getPaintAccessor().getPlatformPaint(getDiffuseColor())); } if (specularColorDirty) { pMaterial.setSpecularColor(getSpecularColor() == null ? null : Toolkit.getPaintAccessor().getPlatformPaint(getSpecularColor())); } if (specularPowerDirty) { pMaterial.setSpecularPower((float)getSpecularPower()); } if (diffuseMapDirty) { pMaterial.setDiffuseMap(getDiffuseMap() == null ? null : Toolkit.getImageAccessor().getPlatformImage(getDiffuseMap())); } if (specularMapDirty) { pMaterial.setSpecularMap(getSpecularMap() == null ? null : Toolkit.getImageAccessor().getPlatformImage(getSpecularMap())); } if (bumpMapDirty) { pMaterial.setBumpMap(getBumpMap() == null ? null : Toolkit.getImageAccessor().getPlatformImage(getBumpMap())); } if (selfIlluminationMapDirty) { pMaterial.setSelfIllumMap(getSelfIlluminationMap() == null ? null : Toolkit.getImageAccessor().getPlatformImage(getSelfIlluminationMap())); } setDirty(false); } @Override public String toString() { return "PhongMaterial[" + "diffuseColor=" + getDiffuseColor() + ", specularColor=" + getSpecularColor() + ", specularPower=" + getSpecularPower() + ", diffuseMap=" + getDiffuseMap() + ", specularMap=" + getSpecularMap() + ", bumpMap=" + getBumpMap() + ", selfIlluminationMap=" + getSelfIlluminationMap() + "]"; } }