/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2013, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotoolkit.display3d.scene.light;
import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import javax.vecmath.Vector3f;
import java.nio.FloatBuffer;
/**
* @author Thomas Rouby (Geomatys))
*/
public class Light {
// GL_LIGHT0 <= light < GL_MAX_LIGHTS
private final int light;
// GL_AMBIENT params value
private FloatBuffer ambient;
// GL_DIFFUSE params value
private FloatBuffer diffuse;
// GL_SPECULAR params value
private FloatBuffer specular;
// GL_POSITION params value
private FloatBuffer position;
// GL_SPOT_DIRECTION params value
private FloatBuffer spot_direction;
// GL_SPOT_EXPONENT params value
private float spot_exponent = Float.NaN;
// GL_SPOT_CUTOFF params value
private float spot_cutoff = Float.NaN;
// GL_CONSTANT_ATTENUATION params value
private float constant_attenuation = Float.NaN;
// GL_LINEAR_ATTENUATION params value
private float linear_attenuation = Float.NaN;
// GL_QUADRATIC_ATTENUATION params value
private float quadratic_attenuation = Float.NaN;
/**
* @param light Specifies a light.
* The number of lights depends on the implementation, but at least eight lights are supported.
* They are identified by symbolic names of the form GL_LIGHT i, where i ranges from 0 to the value of GL_MAX_LIGHTS - 1.
* @link http://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
*/
public Light(int light) {
this.light = light;
}
/**
* GL_AMBIENT params value
*
* @param ambient contains floating-point values that specify the ambient RGBA intensity of the light.
* The initial opengl ambient light intensity is (0, 0, 0, 1).
*/
public void setAmbient(float[] ambient) {
this.ambient = Buffers.newDirectFloatBuffer(ambient);
}
/**
* GL_DIFFUSE params value
*
* @param diffuse contains floating-point values that specify the diffuse RGBA intensity of the light.
* The initial value for GL_LIGHT0 is (1, 1, 1, 1), for other lights, the initial value is (0, 0, 0, 1).
*/
public void setDiffuse(float[] diffuse) {
this.diffuse = Buffers.newDirectFloatBuffer(diffuse);
}
/**
* GL_SPECULAR params value
*
* @param specular contains four floating-point values that specify the specular RGBA intensity of the light.
* The initial value for GL_LIGHT0 is (1, 1, 1, 1), for other lights, the initial value is (0, 0, 0, 1).
*/
public void setSpecular(float[] specular) {
this.specular = Buffers.newDirectFloatBuffer(specular);
}
/**
* GL_POSITION params value
*
* @param position contains three floating-point values that specify the position of the light in homogeneous object coordinates.
* <p/>
* The position is transformed by the modelview matrix when glLight is called (just as if it were a point), and it is stored in eye coordinates.
* If the w component of the position is 0, the light is treated as a directional source.
* Diffuse and specular lighting calculations take the light's direction, but not its actual position, into account, and attenuation is disabled.
* Otherwise, diffuse and specular lighting calculations are based on the actual location of the light in eye coordinates, and attenuation is enabled.
* The initial position is (0, 0, 1, 0);
* thus, the initial light source is directional, parallel to, and in the direction of the -z axis.
* @param directional if true, the light is directional, else positional
*/
public void setPosition(float[] position, boolean directional) {
this.position = Buffers.newDirectFloatBuffer(new float[]{position[0], position[1], position[2], (directional) ? (0.0f) : (1.0f)});
}
/**
* GL_POSITION params value
*
* normalize position before set to buffer.
*
* @param position
* @param directional
*/
public void setPosition(Vector3f position, boolean directional) {
final Vector3f pos = new Vector3f();
pos.normalize(position);
this.setPosition(new float[]{pos.x, pos.y, pos.z}, directional);
}
/**
* GL_SPOT_DIRECTION params value
*
* @param spot_direction contains three floating-point values that specify the direction of the light in homogeneous object coordinates.
* <p/>
* The spot direction is transformed by the upper 3x3 of the modelview matrix
* when glLight is called, and it is stored in eye coordinates.
* It is significant only when GL_SPOT_CUTOFF is not 180, which it is initially.
* The initial direction is (0,0,-1).
*/
public void setSpotDirection(float[] spot_direction) {
this.spot_direction = Buffers.newDirectFloatBuffer(spot_direction);
}
/**
* GL_SPOT_EXPONENT params value
*
* @param spot_exponent is a single floating-point value that specifies the intensity distribution of the light.
* Only values in the range [0,128] are accepted.
* <p/>
* Effective light intensity is attenuated by the cosine of the angle between
* the direction of the light and the direction from the light to the vertex
* being lighted, raised to the power of the spot exponent.
* Thus, higher spot exponents result in a more focused light source,
* regardless of the spot cutoff angle (see GL_SPOT_CUTOFF, next paragraph).
* The initial spot exponent is 0, resulting in uniform light distribution.
*/
public void setSpotExponent(float spot_exponent) {
this.spot_exponent = spot_exponent;
}
/**
* GL_SPOT_CUTOFF params value
*
* @param spot_cutoff is a single floating-point value that specifies the maximum spread angle of a light source.
* Only values in the range [0,90] and the special value 180 are accepted.
* If the angle between the direction of the light and the direction from the
* light to the vertex being lighted is greater than the spot cutoff angle,
* the light is completely masked.
* Otherwise, its intensity is controlled by the spot exponent and the attenuation factors.
* The initial spot cutoff is 180, resulting in uniform light distribution.
*/
public void setSpotCutoff(float spot_cutoff) {
this.spot_cutoff = spot_cutoff;
}
/**
* GL_CONSTANT_ATTENUATION params value
*
* @param constant_attenuation is a single floating-point value that specifies one of the three light attenuation factors.
* Only nonnegative values are accepted.
* If the light is positional, rather than directional, its intensity is attenuated by the reciprocal
* of the sum of the constant factor, the linear factor times the distance between the light
* and the vertex being lighted, and the quadratic factor times the square of the same distance.
* The initial attenuation factors are (1, 0, 0), resulting in no attenuation.
*/
public void setConstantAttenuation(float constant_attenuation) {
this.constant_attenuation = constant_attenuation;
}
/**
* GL_LINEAR_ATTENUATION params value
*
* @param linear_attenuation is a single floating-point value that specifies one of the three light attenuation factors.
* Only nonnegative values are accepted.
* If the light is positional, rather than directional, its intensity is attenuated by the reciprocal
* of the sum of the constant factor, the linear factor times the distance between the light
* and the vertex being lighted, and the quadratic factor times the square of the same distance.
* The initial attenuation factors are (1, 0, 0), resulting in no attenuation.
*/
public void setLinearAttenuation(float linear_attenuation) {
this.linear_attenuation = linear_attenuation;
}
/**
* GL_QUADRATIC_ATTENUATION params value
*
* @param quadratic_attenuation is a single floating-point value that specifies one of the three light attenuation factors.
* Only nonnegative values are accepted.
* If the light is positional, rather than directional, its intensity is attenuated by the reciprocal
* of the sum of the constant factor, the linear factor times the distance between the light
* and the vertex being lighted, and the quadratic factor times the square of the same distance.
* The initial attenuation factors are (1, 0, 0), resulting in no attenuation.
*/
public void setQuadraticAttenuation(float quadratic_attenuation) {
this.quadratic_attenuation = quadratic_attenuation;
}
/**
* Call of glLight on set values
*
* @param glDrawable the glDrawable content the gl where to call glLight methods
*/
public void update(GLAutoDrawable glDrawable) {
final GL gl = glDrawable.getGL();
if (gl instanceof GL2) {
final GL2 gl2 = gl.getGL2();
if (this.ambient != null)
gl2.glLightfv(this.light, GL2.GL_AMBIENT, this.ambient);
if (this.diffuse != null)
gl2.glLightfv(this.light, GL2.GL_DIFFUSE, this.diffuse);
if (this.specular != null)
gl2.glLightfv(this.light, GL2.GL_SPECULAR, this.specular);
if (this.position != null)
gl2.glLightfv(this.light, GL2.GL_POSITION, this.position);
if (this.spot_direction != null)
gl2.glLightfv(this.light, GL2.GL_SPOT_DIRECTION, this.spot_direction);
if (!Float.isNaN(this.spot_exponent))
gl2.glLightf(this.light, GL2.GL_SPOT_EXPONENT, this.spot_exponent);
if (!Float.isNaN(this.spot_cutoff))
gl2.glLightf(this.light, GL2.GL_SPOT_CUTOFF, this.spot_cutoff);
if (!Float.isNaN(this.constant_attenuation))
gl2.glLightf(this.light, GL2.GL_CONSTANT_ATTENUATION, this.constant_attenuation);
if (!Float.isNaN(this.linear_attenuation))
gl2.glLightf(this.light, GL2.GL_LINEAR_ATTENUATION, this.linear_attenuation);
if (!Float.isNaN(this.quadratic_attenuation))
gl2.glLightf(this.light, GL2.GL_QUADRATIC_ATTENUATION, this.quadratic_attenuation);
}
}
}