/**
* Copyright (c) 2003-2009, Xith3D Project Group all rights reserved.
*
* Portions based on the Java3D interface, Copyright by Sun Microsystems.
* Many thanks to the developers of Java3D and Sun Microsystems for their
* innovation and design.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
package org.xith3d.scenegraph;
import org.jagatoo.opengl.enums.ColorTarget;
import org.openmali.vecmath2.Colorf;
import org.xith3d.render.CanvasPeer;
import org.xith3d.render.states.StateNode;
import org.xith3d.render.states.StateTrackable;
import org.xith3d.utility.comparator.ComparatorHelper;
/**
* The Material object defines the appearance of an object under illumination.
* If the Material object in an Appearance object is null, lighting is disabled
* for all nodes that use that Appearance object.
*
* The properties that can be set for a Material object are:
* <ul><li>Ambient color - the ambient RGB color reflected off the surface
* of the material.
* The range of values is 0f to 1f. The default ambient color is (.2f, .2f, .2f).</li>
* <li>Diffuse color - the RGB color of the material when illuminated.
* The range of values is 0f to 1f. The default diffuse color is (1f, 1f, 1f).</li>
* <li>Specular color - the RGB specular color of the material (highlights).
* The range of values is 0f to 1f. The default specular color is (1f, 1f, 1f).</li>
* <li>Emissive color - the RGB color of the light the material emits, if any.
* The range of values is 0f to 1f. The default emissive color is (0f, 0f, 0f).</li>
* <li>Shininess - the material's shininess, in the range [1f, 128f]
* with 1f being not shiny and 128f being very shiny. Values outside this range are
* clamped. The default value for the material's shininess is 64f.</li></ul>
*
* The Material object also enables or disables lighting.
*
* @author David Yazel
* @author Marvin Froehlich (aka Qudus)
*/
public class Material extends NodeComponent implements StateTrackable< Material >
{
private StateNode stateNode = null;
private long stateId = -1L;
/**
* The desired ambient color.
*/
private final Colorf ambient = new Colorf( 0.2f, 0.2f, 0.2f );
/**
* The desired emissive color.
*/
private final Colorf emissive = new Colorf( 0f, 0f, 0f );
/**
* The desired diffuse color.
*/
private final Colorf diffuse = new Colorf( 1f, 1f, 1f );
/**
* The desired specular color.
*/
private final Colorf specular = new Colorf( 1f, 1f, 1f );
/**
* The desired shininess.
*/
private float shininess = 64f;
/**
* Is lighting enabled.
*/
private boolean lightingEnabled = true;
/**
* Is normalizing normals enabled.
*/
private boolean normalizeNormals = true;
/**
* @see ColorTarget#NONE
*/
public static final ColorTarget NONE = ColorTarget.NONE;
/**
* @see ColorTarget#AMBIENT
*/
public static final ColorTarget AMBIENT = ColorTarget.AMBIENT;
/**
* @see ColorTarget#EMISSIVE
*/
public static final ColorTarget EMISSIVE = ColorTarget.EMISSIVE;
/**
* @see ColorTarget#DIFFUSE
*/
public static final ColorTarget DIFFUSE = ColorTarget.DIFFUSE;
/**
* @see ColorTarget#SPECULAR
*/
public static final ColorTarget SPECULAR = ColorTarget.SPECULAR;
/**
* @see ColorTarget#AMBIENT_AND_DIFFUSE
*/
public static final ColorTarget AMBIENT_AND_DIFFUSE = ColorTarget.AMBIENT_AND_DIFFUSE;
private ColorTarget colorTarget = ColorTarget.NONE;
/**
* Sets the ambient color value.
*/
public final void setAmbientColor( Colorf color )
{
this.ambient.set( color );
setChanged( true );
}
/**
* Sets the ambient color value.
*
* @param r
* @param g
* @param b
*/
public final void setAmbientColor( float r, float g, float b )
{
this.ambient.set( r, g, b );
setChanged( true );
}
public final void getAmbientColor( Colorf c )
{
c.set( ambient );
}
/**
* @return the ambient color value.
*/
public final Colorf getAmbientColor()
{
return ( ambient.getReadOnly() );
}
/**
* Sets the emissive color value.
*
* @param r
* @param g
* @param b
*/
public final void setEmissiveColor( Colorf color )
{
this.emissive.set( color );
setChanged( true );
}
public final void setEmissiveColor( float r, float g, float b )
{
this.emissive.set( r, g, b );
setChanged( true );
}
public final void getEmissiveColor( Colorf c )
{
c.set( this.emissive );
}
/**
* @return the emissive color value.
*/
public final Colorf getEmissiveColor()
{
return ( emissive.getReadOnly() );
}
/**
* Sets the diffuse color value.
*/
public final void setDiffuseColor( Colorf color )
{
this.diffuse.set( color );
setChanged( true );
}
/**
* Sets the diffuse color value.
*
* @param r
* @param g
* @param b
*/
public final void setDiffuseColor( float r, float g, float b )
{
this.diffuse.set( r, g, b );
setChanged( true );
}
public final void getDiffuseColor( Colorf c )
{
c.set( diffuse );
}
/**
* @return the diffuse color value.
*/
public final Colorf getDiffuseColor()
{
return ( diffuse.getReadOnly() );
}
/**
* Sets the specular color value.
*/
public final void setSpecularColor( Colorf color )
{
this.specular.set( color );
setChanged( true );
}
/**
* Sets the specular color value.
*
* @param r
* @param g
* @param b
*/
public final void setSpecularColor( float r, float g, float b )
{
this.specular.set( r, g, b );
setChanged( true );
}
public final void getSpecularColor( Colorf c )
{
c.set( this.specular );
}
/**
* @return the specular color value.
*/
public final Colorf getSpecularColor()
{
return ( specular.getReadOnly() );
}
/**
* Sets the shininess value.<br />
* This value must be in the interval [0, 128].
*
* @param shininess
*/
public final void setShininess( float shininess )
{
shininess = Math.max( 0f, Math.min( shininess, 128f ) );
this.shininess = shininess;
setChanged( true );
}
/**
* @return the shininess value.
*/
public final float getShininess()
{
return ( shininess );
}
/**
* Sets the color target for per-vertex colors. When lighting is enabled and
* per-vertex colors are present (and not ignored) in the geometry for a
* given Shape3D node, those per-vertex colors are used in place of the
* specified material color(s) for this Material object.
*
* If no per-vertex colors are present in geometry, color from
* ColoringAttributes used in place of per-vertex colors and will substitute
* the material color specified by color target.
*
* The color target is ignored when lighting is disabled.
*
* The default target is NONE, which causes only colors from this Material
* to be used for lighting calculations.
*
* @param colorTarget
*/
public final void setColorTarget( ColorTarget colorTarget )
{
this.colorTarget = colorTarget;
setChanged( true );
}
/**
* @return the current ColorTarget for this material.
*/
public final ColorTarget getColorTarget()
{
return ( colorTarget );
}
/**
* Sets flag indicating the normal vecrots should be normalized after
* transformations applied.
*
* Normals specified in the shape geomerty need not have unit length. If
* normalization is enabled, then normals are normalized after
* transformation. By default, normalization is enabled.
*
* @param normalizeNormals New value for normal normalization flag
*/
public final void setNormalizeNormals( boolean normalizeNormals )
{
this.normalizeNormals = normalizeNormals;
setChanged( true );
}
/**
* Retrieves the current value of normal normalization flag
*
* @return Normal normalization flag currently set for this material
*/
public final boolean getNormalizeNormals()
{
return ( normalizeNormals );
}
/**
* Turns lighting on or off.
*/
public final void setLightingEnabled( boolean enabled )
{
this.lightingEnabled = enabled;
setChanged( true );
}
/**
* @return Is lighting enabled or not.
*/
public final boolean isLightingEnabled()
{
return ( lightingEnabled );
}
/**
* Used by the render engine to set the state id for the node.
*
* @param stateNode
*/
public final void setStateNode( StateNode stateNode )
{
this.stateNode = stateNode;
this.stateId = stateNode.getId();
}
/**
* @return the id of the object. This should return -1 if there is no
* assigned id.
*/
public final StateNode getStateNode()
{
return ( stateNode );
}
public final long getStateId()
{
return ( stateId );
}
/**
* {@inheritDoc}
*/
public Material getCopy()
{
return ( cloneNodeComponent( true ) );
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals( Object o )
{
if ( this == o )
return ( true );
if ( !( o instanceof Material ) )
return ( false );
final Material om = (Material)o;
if ( !ambient.equals( om.getAmbientColor() ) )
return ( false );
if ( !diffuse.equals( om.getDiffuseColor() ) )
return ( false );
if ( !emissive.equals( om.getEmissiveColor() ) )
return ( false );
if ( !specular.equals( om.getSpecularColor() ) )
return ( false );
if ( shininess != om.shininess )
return ( false );
if ( lightingEnabled != om.lightingEnabled )
return ( false );
if ( colorTarget != om.colorTarget )
return ( false );
if ( normalizeNormals != om.normalizeNormals )
return ( false );
return ( true );
}
/**
* {@inheritDoc}
*/
public int compareTo( Material o )
{
if ( this == o )
return ( 0 );
int val;
val = ComparatorHelper.compare( ambient, o.ambient );
if ( val != 0 )
return ( val );
val = ComparatorHelper.compare( diffuse, o.diffuse );
if ( val != 0 )
return ( val );
val = ComparatorHelper.compare( emissive, o.emissive );
if ( val != 0 )
return ( val );
val = ComparatorHelper.compare( specular, o.specular );
if ( val != 0 )
return ( val );
if ( shininess < o.shininess )
return ( -1 );
else if ( shininess > o.shininess )
return ( 1 );
int al = ( lightingEnabled ) ? 1 : 0;
int bl = ( o.lightingEnabled ) ? 1 : 0;
if ( al < bl )
return ( -1 );
else if ( al > bl )
return ( 1 );
if ( colorTarget.ordinal() < o.colorTarget.ordinal() )
return ( -1 );
else if ( colorTarget.ordinal() > o.colorTarget.ordinal() )
return ( 1 );
al = ( normalizeNormals ) ? 1 : 0;
bl = ( o.normalizeNormals ) ? 1 : 0;
if ( al < bl )
return ( -1 );
else if ( al > bl )
return ( 1 );
return ( 0 );
}
/**
* {@inheritDoc}
*/
@Override
protected void duplicateNodeComponent( NodeComponent original, boolean forceDuplicate )
{
super.duplicateNodeComponent( original, forceDuplicate );
Material o = (Material)original;
if ( forceDuplicate )
{
o.getAmbientColor( ambient );
o.getEmissiveColor( emissive );
o.getDiffuseColor( diffuse );
o.getSpecularColor( specular );
}
else
{
setAmbientColor( o.getAmbientColor() );
setEmissiveColor( o.getEmissiveColor() );
setDiffuseColor( o.getDiffuseColor() );
setSpecularColor( o.getSpecularColor() );
}
setShininess( o.getShininess() );
setLightingEnabled( o.isLightingEnabled() );
setNormalizeNormals( o.getNormalizeNormals() );
}
/**
* {@inheritDoc}
*/
@Override
public Material cloneNodeComponent( boolean forceDuplicate )
{
Material m = new Material();
m.duplicateNodeComponent( this, forceDuplicate );
return m;
}
/**
* {@inheritDoc}
*/
@Override
public void freeOpenGLResources( CanvasPeer canvasPeer )
{
}
/**
* {@inheritDoc}
*/
@Override
public String toString()
{
return ( this.getClass().getName() + " \"" + this.getName() + "\"\n" +
" ambient: " + this.ambient + "\n" +
" diffuse: " + this.diffuse + "\n" +
" emissive: " + this.emissive + "\n" +
" specular: " + this.specular + "\n" +
" shininess: " + this.shininess + "\n" +
" ColorTarget: " + this.colorTarget + "\n" +
" norm-normals: " + this.normalizeNormals + "\n" +
" lighting-enabled: " + this.lightingEnabled
);
}
/**
* Constructs a new Material object.
*/
public Material()
{
super( false );
}
public Material( boolean lightEnabled )
{
this();
this.lightingEnabled = lightEnabled;
}
public Material( boolean lightEnabled, float shininess )
{
this( lightEnabled );
this.shininess = shininess;
}
public Material( float shininess )
{
this( true, shininess );
}
/**
* Constructs a new Material object.
*/
public Material( Colorf ambientColor, Colorf emissiveColor, Colorf diffuseColor, Colorf specularColor, float shininess )
{
this();
if ( ambientColor != null )
this.ambient.set( ambientColor );
if ( emissiveColor != null )
this.emissive.set( emissiveColor );
if ( diffuseColor != null )
this.diffuse.set( diffuseColor );
if ( specularColor != null )
this.specular.set( specularColor );
this.shininess = shininess;
}
/**
* Constructs a new Material object.
*/
public Material( Colorf ambientColor, Colorf emissiveColor, Colorf diffuseColor, Colorf specularColor, float shininess, boolean lightingEnabled )
{
this( ambientColor, emissiveColor, diffuseColor, specularColor, shininess );
this.lightingEnabled = lightingEnabled;
}
/**
* Constructs a new Material object.
*/
public Material( Colorf ambientColor, Colorf emissiveColor, Colorf diffuseColor, Colorf specularColor, float shininess, ColorTarget colorTarget, boolean lightingEnabled )
{
this( ambientColor, emissiveColor, diffuseColor, specularColor, shininess );
this.colorTarget = colorTarget;
this.lightingEnabled = lightingEnabled;
}
/**
* Constructs a new Material object.
*/
public Material( Colorf ambientColor, Colorf emissiveColor, Colorf diffuseColor, Colorf specularColor, float shininess, ColorTarget colorTarget, boolean normalizeNormals, boolean lightingEnabled )
{
this( ambientColor, emissiveColor, diffuseColor, specularColor, shininess );
this.colorTarget = colorTarget;
this.normalizeNormals = normalizeNormals;
this.lightingEnabled = lightingEnabled;
}
}