/* * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.scenario.effect; import com.sun.javafx.geom.BaseBounds; import com.sun.javafx.geom.DirtyRegionContainer; import com.sun.javafx.geom.DirtyRegionPool; import com.sun.javafx.geom.Point2D; import com.sun.javafx.geom.Rectangle; import com.sun.javafx.geom.transform.BaseTransform; import com.sun.scenario.effect.impl.state.RenderState; import com.sun.scenario.effect.light.Light; /** * An effect that applies diffuse and specular lighting to an arbitrary * input using a positionable light source. */ public class PhongLighting extends CoreEffect { private float surfaceScale; private float diffuseConstant; private float specularConstant; private float specularExponent; private Light light; /** * Constructs a new {@code PhongLighting} effect for the given * {@code Light}, with default values for all other properties, * using the default input for source data. * This is a convenience constructor that automatically generates a * bump map using the default input. * * @param light the light source * @throws IllegalArgumentException if {@code light} is null */ public PhongLighting(Light light) { this(light, new GaussianShadow(10f), DefaultInput); } /** * Constructs a new {@code PhongLighting} effect for the given * {@code Light} and the given bump and content input {@code Effect}s * with default values for all other properties. * * @param light the light source * @param bumpInput the input containing the bump map * @param contentInput the input containing the content data * @throws IllegalArgumentException if {@code light} is null */ public PhongLighting(Light light, Effect bumpInput, Effect contentInput) { super(bumpInput, contentInput); this.surfaceScale = 1f; this.diffuseConstant = 1f; this.specularConstant = 1f; this.specularExponent = 1f; setLight(light); } /** * Returns the bump input for this {@code Effect}. * * @return the bump input for this {@code Effect} */ public final Effect getBumpInput() { return getInputs().get(0); } /** * Sets the bump input for this {@code Effect} to a specific * {@code Effect} or to the default input if {@code input} is * {@code null}. * * @param bumpInput the bump input for this {@code Effect} */ public void setBumpInput(Effect bumpInput) { setInput(0, bumpInput); } /** * Returns the content input for this {@code Effect}. * * @return the content input for this {@code Effect} */ public final Effect getContentInput() { return getInputs().get(1); } private Effect getContentInput(Effect defaultInput) { return getDefaultedInput(1, defaultInput); } /** * Sets the content input for this {@code Effect} to a specific * {@code Effect} or to the default input if {@code input} is * {@code null}. * * @param contentInput the content input for this {@code Effect} */ public void setContentInput(Effect contentInput) { setInput(1, contentInput); } /** * Returns the light source. * * @return the light source */ public Light getLight() { return light; } /** * Sets the light source. * * @param light the light source * @throws IllegalArgumentException if {@code light} is null */ public void setLight(Light light) { if (light == null) { throw new IllegalArgumentException("Light must be non-null"); } this.light = light; updatePeerKey("PhongLighting_" + light.getType().name()); } /** * Returns the diffuse constant. * * @return the diffuse constant value */ public float getDiffuseConstant() { return diffuseConstant; } /** * Sets the diffuse constant. *
     *       Min: 0.0
     *       Max: 2.0
     *   Default: 1.0
     *  Identity: n/a
     * 
* * @param diffuseConstant the diffuse constant value * @throws IllegalArgumentException if {@code diffuseConstant} is outside * the allowable range */ public void setDiffuseConstant(float diffuseConstant) { if (diffuseConstant < 0f || diffuseConstant > 2f) { throw new IllegalArgumentException("Diffuse constant must be in the range [0,2]"); } float old = this.diffuseConstant; this.diffuseConstant = diffuseConstant; } /** * Returns the specular constant. * * @return the specular constant value */ public float getSpecularConstant() { return specularConstant; } /** * Sets the specular constant. *
     *       Min: 0.0
     *       Max: 2.0
     *   Default: 1.0
     *  Identity: n/a
     * 
* * @param specularConstant the specular constant value * @throws IllegalArgumentException if {@code specularConstant} is outside * the allowable range */ public void setSpecularConstant(float specularConstant) { if (specularConstant < 0f || specularConstant > 2f) { throw new IllegalArgumentException("Specular constant must be in the range [0,2]"); } float old = this.specularConstant; this.specularConstant = specularConstant; } /** * Returns the specular exponent. * * @return the specular exponent value */ public float getSpecularExponent() { return specularExponent; } /** * Sets the specular exponent. *
     *       Min:  0.0
     *       Max: 40.0
     *   Default:  1.0
     *  Identity:  n/a
     * 
* * @param specularExponent the specular exponent value * @throws IllegalArgumentException if {@code specularExponent} is outside * the allowable range */ public void setSpecularExponent(float specularExponent) { if (specularExponent < 0f || specularExponent > 40f) { throw new IllegalArgumentException("Specular exponent must be in the range [0,40]"); } float old = this.specularExponent; this.specularExponent = specularExponent; } /** * Returns the surface scale. * * @return the surface scale value */ public float getSurfaceScale() { return surfaceScale; } /** * Sets the surface scale. *
     *       Min:  0.0
     *       Max: 10.0
     *   Default:  1.0
     *  Identity:  n/a
     * 
* * @param surfaceScale the surface scale value * @throws IllegalArgumentException if {@code surfaceScale} is outside * the allowable range */ public void setSurfaceScale(float surfaceScale) { if (surfaceScale < 0f || surfaceScale > 10f) { throw new IllegalArgumentException("Surface scale must be in the range [0,10]"); } float old = this.surfaceScale; this.surfaceScale = surfaceScale; } @Override public BaseBounds getBounds(BaseTransform transform, Effect defaultInput) { // effect inherits its bounds from the content input return getContentInput(defaultInput).getBounds(transform, defaultInput); } @Override public Rectangle getResultBounds(BaseTransform transform, Rectangle outputClip, ImageData... inputDatas) { // result inherits its dimensions from the content input return super.getResultBounds(transform, outputClip, inputDatas[1]); } @Override public Point2D transform(Point2D p, Effect defaultInput) { return getContentInput(defaultInput).transform(p, defaultInput); } @Override public Point2D untransform(Point2D p, Effect defaultInput) { return getContentInput(defaultInput).untransform(p, defaultInput); } @Override public RenderState getRenderState(FilterContext fctx, BaseTransform transform, Rectangle outputClip, Object renderHelper, Effect defaultInput) { // RT-27564 // TODO: Since only the content input is used for the output bounds // we could attempt to factor the bounds of the content input in our // answer for the getInputClip() method of the RenderState, but for // now we will just use (a close copy of) the stock RenderSpaceRenderState object. return new RenderState() { @Override public EffectCoordinateSpace getEffectTransformSpace() { return EffectCoordinateSpace.RenderSpace; } @Override public BaseTransform getInputTransform(BaseTransform filterTransform) { return filterTransform; } @Override public BaseTransform getResultTransform(BaseTransform filterTransform) { return BaseTransform.IDENTITY_TRANSFORM; } @Override public Rectangle getInputClip(int i, Rectangle filterClip) { if (i == 0 && filterClip != null) { Rectangle r = new Rectangle(filterClip); r.grow(1, 1); return r; } return filterClip; } }; } @Override public boolean reducesOpaquePixels() { final Effect contentInput = getContentInput(); return contentInput != null && contentInput.reducesOpaquePixels(); } @Override public DirtyRegionContainer getDirtyRegions(Effect defaultInput, DirtyRegionPool regionPool) { Effect bump = getDefaultedInput(0, defaultInput); DirtyRegionContainer drc1 = bump.getDirtyRegions(defaultInput, regionPool); drc1.grow(1, 1); Effect content = getDefaultedInput(1, defaultInput); DirtyRegionContainer drc2 = content.getDirtyRegions(defaultInput, regionPool); drc1.merge(drc2); regionPool.checkIn(drc2); return drc1; } }