/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/
package com.ardor3d.scene.state.lwjgl;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import com.ardor3d.light.DirectionalLight;
import com.ardor3d.light.Light;
import com.ardor3d.light.PointLight;
import com.ardor3d.light.SpotLight;
import com.ardor3d.math.ColorRGBA;
import com.ardor3d.math.type.ReadOnlyColorRGBA;
import com.ardor3d.math.type.ReadOnlyMatrix4;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.renderer.Camera;
import com.ardor3d.renderer.ContextManager;
import com.ardor3d.renderer.RenderContext;
import com.ardor3d.renderer.state.LightState;
import com.ardor3d.renderer.state.RenderState.StateType;
import com.ardor3d.renderer.state.record.LightRecord;
import com.ardor3d.renderer.state.record.LightStateRecord;
public abstract class LwjglLightStateUtil {
public static void apply(final LightState state) {
final RenderContext context = ContextManager.getCurrentContext();
final LightStateRecord record = (LightStateRecord) context.getStateRecord(StateType.Light);
context.setCurrentState(StateType.Light, state);
if (state.isEnabled() && LightState.LIGHTS_ENABLED) {
setLightEnabled(true, record);
setTwoSided(state.getTwoSidedLighting(), record);
setLocalViewer(state.getLocalViewer(), record);
if (context.getCapabilities().isOpenGL1_2Supported()) {
setSpecularControl(state.getSeparateSpecular(), record);
}
for (int i = 0, max = state.getNumberOfChildren(); i < max; i++) {
final Light light = state.get(i);
LightRecord lr = record.getLightRecord(i);
// TODO: use the reference to get the lightrecord - rherlitz
if (lr == null) {
lr = new LightRecord();
record.setLightRecord(lr, i);
}
if (light == null) {
setSingleLightEnabled(false, i, record, lr);
} else {
if (light.isEnabled()) {
setLight(i, light, state, record, lr);
} else {
setSingleLightEnabled(false, i, record, lr);
}
}
}
// disable lights at and above the max count in this state
for (int i = state.getNumberOfChildren(); i < LightState.MAX_LIGHTS_ALLOWED; i++) {
LightRecord lr = record.getLightRecord(i);
if (lr == null) {
lr = new LightRecord();
record.setLightRecord(lr, i);
}
setSingleLightEnabled(false, i, record, lr);
}
if ((state.getLightMask() & LightState.MASK_GLOBALAMBIENT) == 0) {
setModelAmbient(record, state.getGlobalAmbient());
} else {
setModelAmbient(record, ColorRGBA.BLACK_NO_ALPHA);
}
} else {
setLightEnabled(false, record);
}
if (!record.isValid()) {
record.validate();
}
}
private static void setLight(final int index, final Light light, final LightState state,
final LightStateRecord record, final LightRecord lr) {
setSingleLightEnabled(true, index, record, lr);
if ((state.getLightMask() & LightState.MASK_AMBIENT) == 0
&& (light.getLightMask() & LightState.MASK_AMBIENT) == 0) {
setAmbient(index, record, light.getAmbient(), lr);
} else {
setAmbient(index, record, ColorRGBA.BLACK_NO_ALPHA, lr);
}
if ((state.getLightMask() & LightState.MASK_DIFFUSE) == 0
&& (light.getLightMask() & LightState.MASK_DIFFUSE) == 0) {
setDiffuse(index, record, light.getDiffuse(), lr);
} else {
setDiffuse(index, record, ColorRGBA.BLACK_NO_ALPHA, lr);
}
if ((state.getLightMask() & LightState.MASK_SPECULAR) == 0
&& (light.getLightMask() & LightState.MASK_SPECULAR) == 0) {
setSpecular(index, record, light.getSpecular(), lr);
} else {
setSpecular(index, record, ColorRGBA.BLACK_NO_ALPHA, lr);
}
if (light.isAttenuate()) {
setAttenuate(true, index, light, record, lr);
} else {
setAttenuate(false, index, light, record, lr);
}
switch (light.getType()) {
case Directional: {
final DirectionalLight dirLight = (DirectionalLight) light;
final ReadOnlyVector3 direction = dirLight.getDirection();
setPosition(index, record, -direction.getXf(), -direction.getYf(), -direction.getZf(), 0, lr);
break;
}
case Point:
case Spot: {
final PointLight pointLight = (PointLight) light;
final ReadOnlyVector3 location = pointLight.getLocation();
setPosition(index, record, location.getXf(), location.getYf(), location.getZf(), 1, lr);
break;
}
}
if (light.getType() == Light.Type.Spot) {
final SpotLight spot = (SpotLight) light;
setSpotCutoff(index, record, spot.getAngle(), lr);
final ReadOnlyVector3 direction = spot.getDirection();
setSpotDirection(index, record, direction.getXf(), direction.getYf(), direction.getZf(), 0);
setSpotExponent(index, record, spot.getExponent(), lr);
} else {
// set the cutoff to 180, which causes the other spot params to be
// ignored.
setSpotCutoff(index, record, 180, lr);
}
}
private static void setSingleLightEnabled(final boolean enable, final int index, final LightStateRecord record,
final LightRecord lr) {
if (!record.isValid() || lr.isEnabled() != enable) {
if (enable) {
GL11.glEnable(GL11.GL_LIGHT0 + index);
} else {
GL11.glDisable(GL11.GL_LIGHT0 + index);
}
lr.setEnabled(enable);
}
}
private static void setLightEnabled(final boolean enable, final LightStateRecord record) {
if (!record.isValid() || record.isEnabled() != enable) {
if (enable) {
GL11.glEnable(GL11.GL_LIGHTING);
} else {
GL11.glDisable(GL11.GL_LIGHTING);
}
record.setEnabled(enable);
}
}
private static void setTwoSided(final boolean twoSided, final LightStateRecord record) {
if (!record.isValid() || record.isTwoSidedOn() != twoSided) {
if (twoSided) {
GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_TRUE);
} else {
GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_FALSE);
}
record.setTwoSidedOn(twoSided);
}
}
private static void setLocalViewer(final boolean localViewer, final LightStateRecord record) {
if (!record.isValid() || record.isLocalViewer() != localViewer) {
if (localViewer) {
GL11.glLightModeli(GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_TRUE);
} else {
GL11.glLightModeli(GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_FALSE);
}
record.setLocalViewer(localViewer);
}
}
private static void setSpecularControl(final boolean separateSpecularOn, final LightStateRecord record) {
if (!record.isValid() || record.isSeparateSpecular() != separateSpecularOn) {
if (separateSpecularOn) {
GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR);
} else {
GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SINGLE_COLOR);
}
record.setSeparateSpecular(separateSpecularOn);
}
}
private static void setModelAmbient(final LightStateRecord record, final ReadOnlyColorRGBA color) {
if (!record.isValid() || !record.globalAmbient.equals(color)) {
record.lightBuffer.clear();
record.lightBuffer.put(color.getRed());
record.lightBuffer.put(color.getGreen());
record.lightBuffer.put(color.getBlue());
record.lightBuffer.put(color.getAlpha());
record.lightBuffer.flip();
GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, record.lightBuffer);
record.globalAmbient.set(color);
}
}
private static void setAmbient(final int index, final LightStateRecord record, final ReadOnlyColorRGBA ambient,
final LightRecord lr) {
if (!record.isValid() || !lr.ambient.equals(ambient)) {
record.lightBuffer.clear();
record.lightBuffer.put(ambient.getRed());
record.lightBuffer.put(ambient.getGreen());
record.lightBuffer.put(ambient.getBlue());
record.lightBuffer.put(ambient.getAlpha());
record.lightBuffer.flip();
GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_AMBIENT, record.lightBuffer);
lr.ambient.set(ambient);
}
}
private static void setDiffuse(final int index, final LightStateRecord record, final ReadOnlyColorRGBA diffuse,
final LightRecord lr) {
if (!record.isValid() || !lr.diffuse.equals(diffuse)) {
record.lightBuffer.clear();
record.lightBuffer.put(diffuse.getRed());
record.lightBuffer.put(diffuse.getGreen());
record.lightBuffer.put(diffuse.getBlue());
record.lightBuffer.put(diffuse.getAlpha());
record.lightBuffer.flip();
GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_DIFFUSE, record.lightBuffer);
lr.diffuse.set(diffuse);
}
}
private static void setSpecular(final int index, final LightStateRecord record, final ReadOnlyColorRGBA specular,
final LightRecord lr) {
if (!record.isValid() || !lr.specular.equals(specular)) {
record.lightBuffer.clear();
record.lightBuffer.put(specular.getRed());
record.lightBuffer.put(specular.getGreen());
record.lightBuffer.put(specular.getBlue());
record.lightBuffer.put(specular.getAlpha());
record.lightBuffer.flip();
GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_SPECULAR, record.lightBuffer);
lr.specular.set(specular);
}
}
private static void setPosition(final int index, final LightStateRecord record, final float positionX,
final float positionY, final float positionZ, final float positionW, final LightRecord lr) {
// From OpenGL Docs:
// The light position is transformed by the contents of the current top
// of the ModelView matrix stack when you specify the light position
// with a call to glLightfv(GL_LIGHT_POSITION,...). If you later change
// the ModelView matrix, such as when the view changes for the next
// frame, the light position isn't automatically retransformed by the
// new contents of the ModelView matrix. If you want to update the
// light's position, you must again specify the light position with a
// call to glLightfv(GL_LIGHT_POSITION,...).
// XXX: This is a hack until we get a better lighting model up
final ReadOnlyMatrix4 modelViewMatrix = Camera.getCurrentCamera().getModelViewMatrix();
if (!record.isValid() || lr.position.getXf() != positionX || lr.position.getYf() != positionY
|| lr.position.getZf() != positionZ || lr.position.getWf() != positionW
|| !lr.modelViewMatrix.equals(modelViewMatrix)) {
record.lightBuffer.clear();
record.lightBuffer.put(positionX);
record.lightBuffer.put(positionY);
record.lightBuffer.put(positionZ);
record.lightBuffer.put(positionW);
record.lightBuffer.flip();
GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_POSITION, record.lightBuffer);
lr.position.set(positionX, positionY, positionZ, positionW);
if (!Camera.getCurrentCamera().isFrameDirty()) {
lr.modelViewMatrix.set(modelViewMatrix);
}
}
}
private static void setSpotDirection(final int index, final LightStateRecord record, final float directionX,
final float directionY, final float directionZ, final float value) {
// From OpenGL Docs:
// The light position is transformed by the contents of the current top
// of the ModelView matrix stack when you specify the light position
// with a call to glLightfv(GL_LIGHT_POSITION,...). If you later change
// the ModelView matrix, such as when the view changes for the next
// frame, the light position isn't automatically retransformed by the
// new contents of the ModelView matrix. If you want to update the
// light's position, you must again specify the light position with a
// call to glLightfv(GL_LIGHT_POSITION,...).
record.lightBuffer.clear();
record.lightBuffer.put(directionX);
record.lightBuffer.put(directionY);
record.lightBuffer.put(directionZ);
record.lightBuffer.put(value);
record.lightBuffer.flip();
GL11.glLight(GL11.GL_LIGHT0 + index, GL11.GL_SPOT_DIRECTION, record.lightBuffer);
}
private static void setConstant(final int index, final float constant, final LightRecord lr, final boolean force) {
if (force || constant != lr.getConstant()) {
GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_CONSTANT_ATTENUATION, constant);
lr.setConstant(constant);
}
}
private static void setLinear(final int index, final float linear, final LightRecord lr, final boolean force) {
if (force || linear != lr.getLinear()) {
GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_LINEAR_ATTENUATION, linear);
lr.setLinear(linear);
}
}
private static void setQuadratic(final int index, final float quad, final LightRecord lr, final boolean force) {
if (force || quad != lr.getQuadratic()) {
GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_QUADRATIC_ATTENUATION, quad);
lr.setQuadratic(quad);
}
}
private static void setAttenuate(final boolean attenuate, final int index, final Light light,
final LightStateRecord record, final LightRecord lr) {
if (attenuate) {
setConstant(index, light.getConstant(), lr, !record.isValid());
setLinear(index, light.getLinear(), lr, !record.isValid());
setQuadratic(index, light.getQuadratic(), lr, !record.isValid());
} else {
setConstant(index, 1, lr, !record.isValid());
setLinear(index, 0, lr, !record.isValid());
setQuadratic(index, 0, lr, !record.isValid());
}
lr.setAttenuate(attenuate);
}
private static void setSpotExponent(final int index, final LightStateRecord record, final float exponent,
final LightRecord lr) {
if (!record.isValid() || lr.getSpotExponent() != exponent) {
GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_SPOT_EXPONENT, exponent);
lr.setSpotExponent(exponent);
}
}
private static void setSpotCutoff(final int index, final LightStateRecord record, final float cutoff,
final LightRecord lr) {
if (!record.isValid() || lr.getSpotCutoff() != cutoff) {
GL11.glLightf(GL11.GL_LIGHT0 + index, GL11.GL_SPOT_CUTOFF, cutoff);
lr.setSpotCutoff(cutoff);
}
}
}