- Added SpotLight light type.

- Implemented spot light shading for lighting (pixel and vertex lighting) and terrain shader

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7893 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 14 years ago
parent 41f417570d
commit 99a4b00c15
  1. 22
      engine/src/core-data/Common/MatDefs/Light/Lighting.frag
  2. 30
      engine/src/core-data/Common/MatDefs/Light/Lighting.vert
  3. 31
      engine/src/core/com/jme3/light/PointLight.java
  4. 222
      engine/src/core/com/jme3/light/SpotLight.java
  5. 133
      engine/src/core/com/jme3/material/Material.java
  6. 18
      engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag
  7. 13
      engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert
  8. 159
      engine/src/test/jme3test/light/TestSpotLight.java
  9. 219
      engine/src/test/jme3test/light/TestSpotLightTerrain.java

@ -52,8 +52,9 @@ uniform float m_Shininess;
#ifdef HQ_ATTENUATION #ifdef HQ_ATTENUATION
uniform vec4 g_LightPosition; uniform vec4 g_LightPosition;
varying vec3 lightVec;
#endif #endif
varying vec4 lightVec;
varying vec4 spotVec;
#ifdef USE_REFLECTION #ifdef USE_REFLECTION
uniform float m_ReflectionPower; uniform float m_ReflectionPower;
@ -146,6 +147,23 @@ void main(){
#else #else
vec4 diffuseColor = vec4(1.0); vec4 diffuseColor = vec4(1.0);
#endif #endif
#ifndef VERTEX_LIGHTING
float spotFallOff = 1.0;
if(spotVec.w!=0){
vec3 L=normalize(lightVec.xyz);
vec3 spotdir = normalize(spotVec.xyz);
float curAngleCos = dot(-L, spotdir);
float innerAngleCos = spotVec.w;
float outerAngleCos = lightVec.w;
float innerMinusOuter = innerAngleCos - outerAngleCos;
spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0);
if(spotFallOff<=0.0){
gl_FragColor = AmbientSum * diffuseColor;
return;
}
}
#endif
float alpha = DiffuseSum.a * diffuseColor.a; float alpha = DiffuseSum.a * diffuseColor.a;
#ifdef ALPHAMAP #ifdef ALPHAMAP
alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r; alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r;
@ -202,7 +220,7 @@ void main(){
vec4 lightDir = vLightDir; vec4 lightDir = vLightDir;
lightDir.xyz = normalize(lightDir.xyz); lightDir.xyz = normalize(lightDir.xyz);
vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz); vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz) * spotFallOff;
#ifdef COLORRAMP #ifdef COLORRAMP
diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb; diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb; specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;

@ -13,6 +13,7 @@ uniform float m_Shininess;
uniform vec4 g_LightColor; uniform vec4 g_LightColor;
uniform vec4 g_LightPosition; uniform vec4 g_LightPosition;
uniform vec4 g_LightDirection;
uniform vec4 g_AmbientLightColor; uniform vec4 g_AmbientLightColor;
varying vec2 texCoord; varying vec2 texCoord;
@ -29,9 +30,8 @@ attribute vec3 inPosition;
attribute vec2 inTexCoord; attribute vec2 inTexCoord;
attribute vec3 inNormal; attribute vec3 inNormal;
#ifdef HQ_ATTENUATION varying vec4 lightVec;
varying vec3 lightVec; varying vec4 spotVec;
#endif
#ifdef VERTEX_COLOR #ifdef VERTEX_COLOR
attribute vec4 inColor; attribute vec4 inColor;
@ -81,13 +81,11 @@ attribute vec3 inNormal;
void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){ void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
float posLight = step(0.5, color.w); float posLight = step(0.5, color.w);
vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight); vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
lightVec.xyz = tempVec;
#ifdef ATTENUATION #ifdef ATTENUATION
float dist = length(tempVec); float dist = length(tempVec);
lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0); lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
lightDir.xyz = tempVec / vec3(dist); lightDir.xyz = tempVec / vec3(dist);
#ifdef HQ_ATTENUATION
lightVec = tempVec;
#endif
#else #else
lightDir = vec4(normalize(tempVec), 1.0); lightDir = vec4(normalize(tempVec), 1.0);
#endif #endif
@ -113,11 +111,20 @@ void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4
vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightPos){ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightPos){
vec4 lightDir; vec4 lightDir;
lightComputeDir(wvPos, g_LightColor, wvLightPos, lightDir); lightComputeDir(wvPos, g_LightColor, wvLightPos, lightDir);
float spotFallOff = 1.0;
if(spotVec.w!=0){
vec3 L=normalize(lightVec.xyz);
vec3 spotdir = normalize(spotVec.xyz);
float curAngleCos = dot(-L, spotdir);
float innerAngleCos = spotVec.w;
float outerAngleCos = lightVec.w;
float innerMinusOuter = innerAngleCos - outerAngleCos;
spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0);
}
float diffuseFactor = lightComputeDiffuse(wvNorm, lightDir.xyz); float diffuseFactor = lightComputeDiffuse(wvNorm, lightDir.xyz);
float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess); float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess);
//specularFactor *= step(0.01, diffuseFactor); //specularFactor *= step(0.01, diffuseFactor);
return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w); return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w)*spotFallOff;
} }
#endif #endif
@ -138,7 +145,7 @@ void main(){
//vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.w)); //vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.w));
//wvLightPos.w = lightPos.w; //wvLightPos.w = lightPos.w;
vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz, g_LightColor.w)); vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0)));
wvLightPos.w = g_LightPosition.w; wvLightPos.w = g_LightPosition.w;
vec4 lightColor = g_LightColor; vec4 lightColor = g_LightColor;
@ -166,6 +173,11 @@ void main(){
#endif #endif
#endif #endif
//computing spot direction in view space and unpacking spotlight cos
spotVec=(g_ViewMatrix *vec4(g_LightDirection.xyz,0.0) );
spotVec.w=floor(g_LightDirection.w)*0.001;
lightVec.w = fract(g_LightDirection.w);
lightColor.w = 1.0; lightColor.w = 1.0;
#ifdef MATERIAL_COLORS #ifdef MATERIAL_COLORS
AmbientSum = m_Ambient * g_AmbientLightColor; AmbientSum = m_Ambient * g_AmbientLightColor;

@ -29,7 +29,6 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.light; package com.jme3.light;
import com.jme3.bounding.BoundingVolume; import com.jme3.bounding.BoundingVolume;
@ -55,13 +54,14 @@ public class PointLight extends Light {
protected Vector3f position = new Vector3f(); protected Vector3f position = new Vector3f();
protected float radius = 0; protected float radius = 0;
protected float invRadius = 0;
@Override @Override
public void computeLastDistance(Spatial owner) { public void computeLastDistance(Spatial owner) {
if (owner.getWorldBound() != null){ if (owner.getWorldBound() != null) {
BoundingVolume bv = owner.getWorldBound(); BoundingVolume bv = owner.getWorldBound();
lastDistance = bv.distanceSquaredTo(position); lastDistance = bv.distanceSquaredTo(position);
}else{ } else {
lastDistance = owner.getWorldTranslation().distanceSquared(position); lastDistance = owner.getWorldTranslation().distanceSquared(position);
} }
} }
@ -82,7 +82,7 @@ public class PointLight extends Light {
* *
* @param position the world space position of the light. * @param position the world space position of the light.
*/ */
public void setPosition(Vector3f position){ public void setPosition(Vector3f position) {
this.position.set(position); this.position.set(position);
} }
@ -92,7 +92,7 @@ public class PointLight extends Light {
* *
* @return the radius of the light * @return the radius of the light
*/ */
public float getRadius(){ public float getRadius() {
return radius; return radius;
} }
@ -109,11 +109,24 @@ public class PointLight extends Light {
* *
* @throws IllegalArgumentException If radius is negative * @throws IllegalArgumentException If radius is negative
*/ */
public void setRadius(float radius){ public void setRadius(float radius) {
if (radius < 0) { if (radius < 0) {
throw new IllegalArgumentException("Light radius cannot be negative"); throw new IllegalArgumentException("Light radius cannot be negative");
} }
this.radius = radius; this.radius = radius;
if(radius!=0){
this.invRadius = 1 / radius;
}else{
this.invRadius = 0;
}
}
/**
* for internal use only
* @return the inverse of the radius
*/
public float getInvRadius() {
return invRadius;
} }
@Override @Override
@ -135,6 +148,10 @@ public class PointLight extends Light {
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
position = (Vector3f) ic.readSavable("position", null); position = (Vector3f) ic.readSavable("position", null);
radius = ic.readFloat("radius", 0f); radius = ic.readFloat("radius", 0f);
if(radius!=0){
this.invRadius = 1 / radius;
}else{
this.invRadius = 0;
}
} }
} }

@ -0,0 +1,222 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.light;
import com.jme3.bounding.BoundingVolume;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.export.Savable;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import java.io.IOException;
/**
* Represents a spot light.
* A spot light emmit a cone of light from a position and in a direction.
* It can be used to fake torch lights or car's lights.
* <p>
* In addition to a position and a direction, spot lights also have a range which
* can be used to attenuate the influence of the light depending on the
* distance between the light and the effected object.
* Also the angle of the cone can be tweaked by changing the spot inner angle and the spot outer angle.
* the spot inner angle determin the cone of light where light has full influence.
* the spot outer angle determin the cone global cone of light of the spot light.
* the light intensity slowly decrease between the inner cone and the outer cone.
* @author Nehon
*/
public class SpotLight extends Light implements Savable {
protected Vector3f position = new Vector3f();
protected Vector3f direction = new Vector3f(0,-1,0);
protected float spotInnerAngle = FastMath.QUARTER_PI / 8;
protected float spotOuterAngle = FastMath.QUARTER_PI / 6;
protected float spotRange = 100;
protected float invSpotRange = 1 / 100;
protected float packedAngleCos=0;
public SpotLight() {
super();
computePackedCos();
}
private void computePackedCos() {
float innerCos=FastMath.cos(spotInnerAngle);
float outerCos=FastMath.cos(spotOuterAngle);
packedAngleCos=(int)(innerCos*1000);
packedAngleCos+=outerCos;
}
@Override
protected void computeLastDistance(Spatial owner) {
if (owner.getWorldBound() != null) {
BoundingVolume bv = owner.getWorldBound();
lastDistance = bv.distanceSquaredTo(position);
} else {
lastDistance = owner.getWorldTranslation().distanceSquared(position);
}
}
@Override
public Type getType() {
return Type.Spot;
}
public Vector3f getDirection() {
return direction;
}
public void setDirection(Vector3f direction) {
this.direction.set(direction);
}
public Vector3f getPosition() {
return position;
}
public void setPosition(Vector3f position) {
this.position.set(position);
}
public float getSpotRange() {
return spotRange;
}
/**
* Set the range of the light influence.
* <p>
* Setting a non-zero range indicates the light should use attenuation.
* If a pixel's distance to this light's position
* is greater than the light's range, then the pixel will not be
* effected by this light, if the distance is less than the range, then
* the magnitude of the influence is equal to distance / range.
*
* @param spotRange the range of the light influence.
*
* @throws IllegalArgumentException If spotRange is negative
*/
public void setSpotRange(float spotRange) {
if (spotRange < 0) {
throw new IllegalArgumentException("SpotLight range cannot be negative");
}
this.spotRange = spotRange;
if (spotRange != 0) {
this.invSpotRange = 1 / spotRange;
} else {
this.invSpotRange = 0;
}
}
/**
* for internal use only
* @return the inverse of the spot range
*/
public float getInvSpotRange() {
return invSpotRange;
}
/**
* returns the spot inner angle
* @return the spot inner angle
*/
public float getSpotInnerAngle() {
return spotInnerAngle;
}
/**
* Sets the inner angle of the cone of influence.
* This angle is the angle between the spot direction axis and the inner border of the cone of influence.
* @param spotInnerAngle
*/
public void setSpotInnerAngle(float spotInnerAngle) {
this.spotInnerAngle = spotInnerAngle;
computePackedCos();
}
/**
* returns the spot outer angle
* @return the spot outer angle
*/
public float getSpotOuterAngle() {
return spotOuterAngle;
}
/**
* Sets the outer angle of the cone of influence.
* This angle is the angle between the spot direction axis and the outer border of the cone of influence.
* this should be greater than the inner angle or the result will be unexpected.
* @param spotOuterAngle
*/
public void setSpotOuterAngle(float spotOuterAngle) {
this.spotOuterAngle = spotOuterAngle;
computePackedCos();
}
/**
* for internal use only
* @return the cosines of the inner and outter angle packed in a float
*/
public float getPackedAngleCos() {
return packedAngleCos;
}
@Override
public void write(JmeExporter ex) throws IOException {
super.write(ex);
OutputCapsule oc = ex.getCapsule(this);
oc.write(direction, "direction", new Vector3f());
oc.write(position, "position", new Vector3f());
oc.write(spotInnerAngle, "spotInnerAngle", FastMath.QUARTER_PI / 8);
oc.write(spotOuterAngle, "spotOuterAngle", FastMath.QUARTER_PI / 6);
oc.write(spotRange, "spotRange", 100);
}
@Override
public void read(JmeImporter im) throws IOException {
super.read(im);
InputCapsule ic = im.getCapsule(this);
spotInnerAngle = ic.readFloat("spotInnerAngle", FastMath.QUARTER_PI / 8);
spotOuterAngle = ic.readFloat("spotOuterAngle", FastMath.QUARTER_PI / 6);
direction = (Vector3f) ic.readSavable("direction", new Vector3f());
position = (Vector3f) ic.readSavable("position", new Vector3f());
spotRange = ic.readFloat("spotRange", 100);
if (spotRange != 0) {
this.invSpotRange = 1 / spotRange;
} else {
this.invSpotRange = 0;
}
}
}

@ -1,33 +1,31 @@
/* /*
* Copyright (c) 2009-2010 jMonkeyEngine * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
* All rights reserved. * <p/>
*
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are met:
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* *
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* <p/>
* * Redistributions in binary form must reproduce the above copyright * * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the * notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution. * documentation and/or other materials provided with the distribution.
* * <p/>
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software * may be used to endorse or promote products derived from this software
* without specific prior written permission. * without specific prior written permission.
* * <p/>
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.jme3.material; package com.jme3.material;
@ -46,7 +44,9 @@ import com.jme3.light.DirectionalLight;
import com.jme3.light.Light; import com.jme3.light.Light;
import com.jme3.light.LightList; import com.jme3.light.LightList;
import com.jme3.light.PointLight; import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.TechniqueDef.LightMode; import com.jme3.material.TechniqueDef.LightMode;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion; import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f; import com.jme3.math.Vector4f;
@ -72,12 +72,15 @@ import java.util.logging.Logger;
/** /**
* <code>Material</code> describes the rendering style for a given * <code>Material</code> describes the rendering style for a given
* {@link Geometry}. * {
* * <p/>
* <p>A material is essentially a list of {@link MatParam parameters}, those parameters * @link Geometry}.
* map to uniforms which are defined in a shader. * <p/>
* Setting the parameters can modify the behavior of a shader. * <p>A material is essentially a list of {
* * @link MatParam parameters}, those parameters map to uniforms which are
* defined in a shader. Setting the parameters can modify the behavior of a
* shader.
* <p/>
* @author Kirill Vainer * @author Kirill Vainer
*/ */
public class Material implements Cloneable, Savable, Comparable<Material> { public class Material implements Cloneable, Savable, Comparable<Material> {
@ -96,7 +99,6 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
additiveLight.setBlendMode(RenderState.BlendMode.AlphaAdditive); additiveLight.setBlendMode(RenderState.BlendMode.AlphaAdditive);
additiveLight.setDepthWrite(false); additiveLight.setDepthWrite(false);
} }
private String assetName; private String assetName;
private MaterialDef def; private MaterialDef def;
private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>(); private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
@ -364,7 +366,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} else { } else {
logger.log(Level.WARNING, "Material parameter {0} uses a deprecated naming convention use {1} instead ", new Object[]{name, newName}); logger.log(Level.WARNING, "Material parameter {0} uses a deprecated naming convention use {1} instead ", new Object[]{name, newName});
} }
}else if (paramDef == null){ } else if (paramDef == null) {
throw new IllegalArgumentException("Material parameter is not defined: " + name); throw new IllegalArgumentException("Material parameter is not defined: " + name);
} }
@ -435,8 +437,9 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
name = checkSetParam(null, name); name = checkSetParam(null, name);
MatParamTexture val = getTextureParam(name); MatParamTexture val = getTextureParam(name);
if (val == null) if (val == null) {
throw new IllegalArgumentException("The given texture parameter is not set."); throw new IllegalArgumentException("The given texture parameter is not set.");
}
int texUnit = val.getUnit(); int texUnit = val.getUnit();
paramValues.remove(name); paramValues.remove(name);
@ -625,15 +628,17 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
* </p> * </p>
*/ */
protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) { protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) {
if (numLights == 0){ // this shader does not do lighting, ignore. if (numLights == 0) { // this shader does not do lighting, ignore.
return; return;
} }
LightList lightList = g.getWorldLightList(); LightList lightList = g.getWorldLightList();
Uniform lightColor = shader.getUniform("g_LightColor"); Uniform lightColor = shader.getUniform("g_LightColor");
Uniform lightPos = shader.getUniform("g_LightPosition"); Uniform lightPos = shader.getUniform("g_LightPosition");
Uniform lightDir = shader.getUniform("g_LightDirection");
lightColor.setVector4Length(numLights); lightColor.setVector4Length(numLights);
lightPos.setVector4Length(numLights); lightPos.setVector4Length(numLights);
lightDir.setVector4Length(numLights);
Uniform ambientColor = shader.getUniform("g_AmbientLightColor"); Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList)); ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
@ -662,12 +667,19 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
case Point: case Point:
PointLight pl = (PointLight) l; PointLight pl = (PointLight) l;
Vector3f pos = pl.getPosition(); Vector3f pos = pl.getPosition();
float invRadius = pl.getRadius(); float invRadius = pl.getInvRadius();
if (invRadius != 0) {
invRadius = 1f / invRadius;
}
lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex); lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex);
break; break;
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
lightPos.setVector4InArray(pos2.getX(), pos2.getY(), pos2.getZ(), invRange, lightIndex);
lightDir.setVector4InArray(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos, lightIndex);
break;
case Ambient: case Ambient:
// skip this light. Does not increase lightIndex // skip this light. Does not increase lightIndex
continue; continue;
@ -679,7 +691,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
lightIndex++; lightIndex++;
} }
while (lightIndex < numLights){ while (lightIndex < numLights) {
lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex); lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex); lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
@ -689,6 +701,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
protected void renderMultipassLighting(Shader shader, Geometry g, Renderer r) { protected void renderMultipassLighting(Shader shader, Geometry g, Renderer r) {
LightList lightList = g.getWorldLightList(); LightList lightList = g.getWorldLightList();
Uniform lightDir = shader.getUniform("g_LightDirection");
Uniform lightColor = shader.getUniform("g_LightColor"); Uniform lightColor = shader.getUniform("g_LightColor");
Uniform lightPos = shader.getUniform("g_LightPosition"); Uniform lightPos = shader.getUniform("g_LightPosition");
Uniform ambientColor = shader.getUniform("g_AmbientLightColor"); Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
@ -740,10 +753,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
case Point: case Point:
PointLight pl = (PointLight) l; PointLight pl = (PointLight) l;
Vector3f pos = pl.getPosition(); Vector3f pos = pl.getPosition();
float invRadius = pl.getRadius(); float invRadius = pl.getInvRadius();
if (invRadius != 0) {
invRadius = 1f / invRadius;
}
Quaternion q2; Quaternion q2;
if (lightPos.getValue() != null) { if (lightPos.getValue() != null) {
q2 = (Quaternion) lightPos.getValue(); q2 = (Quaternion) lightPos.getValue();
@ -752,6 +762,31 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
} }
q2.set(pos.getX(), pos.getY(), pos.getZ(), invRadius); q2.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
lightPos.setValue(VarType.Vector4, q2); lightPos.setValue(VarType.Vector4, q2);
break;
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
Quaternion q3,q4;
if (lightPos.getValue() != null) {
q3 = (Quaternion) lightPos.getValue();
} else {
q3 = new Quaternion();
}
q3.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
lightPos.setValue(VarType.Vector4, q3);
if (lightDir.getValue() != null) {
q4 = (Quaternion) lightDir.getValue();
} else {
q4 = new Quaternion();
}
q4.set(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos);
lightDir.setValue(VarType.Vector4, q4);
break; break;
default: default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType()); throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
@ -1019,24 +1054,24 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
boolean enableVcolor = false; boolean enableVcolor = false;
boolean separateTexCoord = false; boolean separateTexCoord = false;
if (im.getFormatVersion() == 0){ if (im.getFormatVersion() == 0) {
// Enable compatibility with old models // Enable compatibility with old models
if (defName.equalsIgnoreCase("Common/MatDefs/Misc/VertexColor.j3md")){ if (defName.equalsIgnoreCase("Common/MatDefs/Misc/VertexColor.j3md")) {
// Using VertexColor, switch to Unshaded and set VertexColor=true // Using VertexColor, switch to Unshaded and set VertexColor=true
enableVcolor = true; enableVcolor = true;
defName = "Common/MatDefs/Misc/Unshaded.j3md"; defName = "Common/MatDefs/Misc/Unshaded.j3md";
}else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md") } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md")
|| defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")){ || defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")) {
// Using SimpleTextured/SolidColor, just switch to Unshaded // Using SimpleTextured/SolidColor, just switch to Unshaded
defName = "Common/MatDefs/Misc/Unshaded.j3md"; defName = "Common/MatDefs/Misc/Unshaded.j3md";
}else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/WireColor.j3md")){ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/WireColor.j3md")) {
// Using WireColor, set wireframe renderstate = true and use Unshaded // Using WireColor, set wireframe renderstate = true and use Unshaded
getAdditionalRenderState().setWireframe(true); getAdditionalRenderState().setWireframe(true);
defName = "Common/MatDefs/Misc/Unshaded.j3md"; defName = "Common/MatDefs/Misc/Unshaded.j3md";
}else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/Unshaded.j3md")){ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/Unshaded.j3md")) {
// Uses unshaded, ensure that the proper param is set // Uses unshaded, ensure that the proper param is set
MatParam value = params.get("SeperateTexCoord"); MatParam value = params.get("SeperateTexCoord");
if (value != null && ((Boolean)value.getValue()) == true){ if (value != null && ((Boolean) value.getValue()) == true) {
params.remove("SeperateTexCoord"); params.remove("SeperateTexCoord");
separateTexCoord = true; separateTexCoord = true;
} }
@ -1066,10 +1101,10 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
paramValues.put(param.getName(), param); paramValues.put(param.getName(), param);
} }
if (enableVcolor){ if (enableVcolor) {
setBoolean("VertexColor", true); setBoolean("VertexColor", true);
} }
if (separateTexCoord){ if (separateTexCoord) {
setBoolean("SeparateTexCoord", true); setBoolean("SeparateTexCoord", true);
} }
} }

@ -12,6 +12,8 @@ varying vec3 vnPosition;
varying vec3 vViewDir; varying vec3 vViewDir;
varying vec4 vLightDir; varying vec4 vLightDir;
varying vec4 vnLightDir; varying vec4 vnLightDir;
varying vec4 lightVec;
varying vec4 spotVec;
#ifdef DIFFUSEMAP #ifdef DIFFUSEMAP
@ -644,6 +646,20 @@ void main(){
vec4 diffuseColor = vec4(1.0); vec4 diffuseColor = vec4(1.0);
#endif #endif
float spotFallOff = 1.0;
if(spotVec.w!=0){
vec3 L=normalize(lightVec.xyz);
vec3 spotdir = normalize(spotVec.xyz);
float curAngleCos = dot(-L, spotdir);
float innerAngleCos = spotVec.w;
float outerAngleCos = lightVec.w;
float innerMinusOuter = innerAngleCos - outerAngleCos;
spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0);
if(spotFallOff<=0.0){
gl_FragColor = AmbientSum * diffuseColor;
return;
}
}
//--------------------- //---------------------
// normal calculations // normal calculations
@ -665,7 +681,7 @@ void main(){
vec4 lightDir = vLightDir; vec4 lightDir = vLightDir;
lightDir.xyz = normalize(lightDir.xyz); lightDir.xyz = normalize(lightDir.xyz);
vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz); vec2 light = computeLighting(vPosition, normal, vViewDir.xyz, lightDir.xyz)*spotFallOff;
vec4 specularColor = vec4(1.0); vec4 specularColor = vec4(1.0);

@ -5,6 +5,7 @@ uniform mat4 g_ViewMatrix;
uniform vec4 g_LightColor; uniform vec4 g_LightColor;
uniform vec4 g_LightPosition; uniform vec4 g_LightPosition;
uniform vec4 g_LightDirection;
uniform vec4 g_AmbientLightColor; uniform vec4 g_AmbientLightColor;
uniform float m_Shininess; uniform float m_Shininess;
@ -23,6 +24,9 @@ varying vec3 vnViewDir;
varying vec4 vLightDir; varying vec4 vLightDir;
varying vec4 vnLightDir; varying vec4 vnLightDir;
varying vec4 lightVec;
varying vec4 spotVec;
varying vec4 AmbientSum; varying vec4 AmbientSum;
varying vec4 DiffuseSum; varying vec4 DiffuseSum;
varying vec4 SpecularSum; varying vec4 SpecularSum;
@ -38,7 +42,7 @@ varying vec4 SpecularSum;
void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){ void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
float posLight = step(0.5, color.w); float posLight = step(0.5, color.w);
vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight); vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
lightVec.xyz = tempVec;
float dist = length(tempVec); float dist = length(tempVec);
lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0); lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
lightDir.xyz = tempVec / vec3(dist); lightDir.xyz = tempVec / vec3(dist);
@ -54,7 +58,7 @@ void main(){
vec3 wvNormal = normalize(g_NormalMatrix * inNormal); vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
vec3 viewDir = normalize(-wvPosition); vec3 viewDir = normalize(-wvPosition);
vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz, g_LightColor.w)); vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0)));
wvLightPos.w = g_LightPosition.w; wvLightPos.w = g_LightPosition.w;
vec4 lightColor = g_LightColor; vec4 lightColor = g_LightColor;
@ -85,6 +89,11 @@ void main(){
#endif #endif
//computing spot direction in view space and unpacking spotlight cos
spotVec=(g_ViewMatrix *vec4(g_LightDirection.xyz,0.0) );
spotVec.w=floor(g_LightDirection.w)*0.001;
lightVec.w = fract(g_LightDirection.w);
AmbientSum = vec4(0.2, 0.2, 0.2, 1.0) * g_AmbientLightColor; // Default: ambient color is dark gray AmbientSum = vec4(0.2, 0.2, 0.2, 1.0) * g_AmbientLightColor; // Default: ambient color is dark gray
DiffuseSum = lightColor; DiffuseSum = lightColor;
SpecularSum = lightColor; SpecularSum = lightColor;

@ -0,0 +1,159 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jme3test.light;
import com.jme3.app.SimpleApplication;
import com.jme3.light.AmbientLight;
import com.jme3.light.SpotLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.TangentBinormalGenerator;
public class TestSpotLight extends SimpleApplication {
private Vector3f lightTarget = new Vector3f(12, 3.5f, 30);
public static void main(String[] args){
TestSpotLight app = new TestSpotLight();
app.start();
}
SpotLight spot;
Geometry lightMdl;
public void setupLighting(){
AmbientLight al=new AmbientLight();
al.setColor(ColorRGBA.White.mult(0.8f));
rootNode.addLight(al);
spot=new SpotLight();
spot.setSpotRange(1000);
spot.setSpotInnerAngle(5*FastMath.DEG_TO_RAD);
spot.setSpotOuterAngle(10*FastMath.DEG_TO_RAD);
spot.setPosition(new Vector3f(77.70334f, 34.013165f, 27.1017f));
spot.setDirection(lightTarget.subtract(spot.getPosition()));
spot.setColor(ColorRGBA.White.mult(2));
rootNode.addLight(spot);
// PointLight pl=new PointLight();
// pl.setPosition(new Vector3f(77.70334f, 34.013165f, 27.1017f));
// pl.setRadius(1000);
// pl.setColor(ColorRGBA.White.mult(2));
// rootNode.addLight(pl);
lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
lightMdl.setLocalTranslation(new Vector3f(77.70334f, 34.013165f, 27.1017f));
lightMdl.setLocalScale(5);
rootNode.attachChild(lightMdl);
// DirectionalLight dl = new DirectionalLight();
// dl.setDirection(lightTarget.subtract(new Vector3f(77.70334f, 34.013165f, 27.1017f)));
// dl.setColor(ColorRGBA.White.mult(2));
// rootNode.addLight(dl);
}
public void setupFloor(){
Material mat = assetManager.loadMaterial("Textures/Terrain/Pond/Pond.j3m");
mat.getTextureParam("DiffuseMap").getTextureValue().setWrap(WrapMode.Repeat);
mat.getTextureParam("NormalMap").getTextureValue().setWrap(WrapMode.Repeat);
// mat.getTextureParam("ParallaxMap").getTextureValue().setWrap(WrapMode.Repeat);
mat.setFloat("Shininess",3);
// mat.setBoolean("VertexLighting", true);
Box floor = new Box(Vector3f.ZERO, 50, 1f, 50);
TangentBinormalGenerator.generate(floor);
floor.scaleTextureCoordinates(new Vector2f(5, 5));
Geometry floorGeom = new Geometry("Floor", floor);
floorGeom.setMaterial(mat);
floorGeom.setShadowMode(ShadowMode.Receive);
rootNode.attachChild(floorGeom);
}
public void setupSignpost(){
Spatial signpost = assetManager.loadModel("Models/Sign Post/Sign Post.mesh.xml");
Material mat = assetManager.loadMaterial("Models/Sign Post/Sign Post.j3m");
// mat.setBoolean("VertexLighting", true);
signpost.setMaterial(mat);
signpost.rotate(0, FastMath.HALF_PI, 0);
signpost.setLocalTranslation(12, 3.5f, 30);
signpost.setLocalScale(4);
signpost.setShadowMode(ShadowMode.CastAndReceive);
TangentBinormalGenerator.generate(signpost);
rootNode.attachChild(signpost);
}
@Override
public void simpleInitApp() {
cam.setLocation(new Vector3f(27.492603f, 29.138166f, -13.232513f));
cam.setRotation(new Quaternion(0.25168246f, -0.10547892f, 0.02760565f, 0.96164864f));
flyCam.setMoveSpeed(30);
setupLighting();
setupFloor();
setupSignpost();
}
float angle;
@Override
public void simpleUpdate(float tpf) {
super.simpleUpdate(tpf);
angle += tpf;
angle %= FastMath.TWO_PI;
spot.setPosition(new Vector3f(FastMath.cos(angle) * 30f, 34.013165f, FastMath.sin(angle) * 30f));
lightMdl.setLocalTranslation(spot.getPosition());
spot.setDirection(lightTarget.subtract(spot.getPosition()));
}
}

@ -0,0 +1,219 @@
/*
* Copyright (c) 2009-2010 jMonkeyEngine
* All rights reserved.
*
* 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 'jMonkeyEngine' 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) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jme3test.light;
import jme3tools.converters.ImageToAwt;
import com.jme3.app.SimpleApplication;
import com.jme3.bounding.BoundingBox;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Sphere;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
/**
* Uses the terrain's lighting texture with normal maps and lights.
*
* @author bowens
*/
public class TestSpotLightTerrain extends SimpleApplication {
private TerrainQuad terrain;
Material matTerrain;
Material matWire;
boolean wireframe = false;
boolean triPlanar = false;
boolean wardiso = false;
boolean minnaert = false;
protected BitmapText hintText;
PointLight pl;
Geometry lightMdl;
private float grassScale = 64;
private float dirtScale = 16;
private float rockScale = 128;
SpotLight sl;
public static void main(String[] args) {
TestSpotLightTerrain app = new TestSpotLightTerrain();
app.start();
}
@Override
public void simpleInitApp() {
makeTerrain();
flyCam.setMoveSpeed(50);
sl = new SpotLight();
sl.setSpotRange(100);
sl.setSpotOuterAngle(20 * FastMath.DEG_TO_RAD);
sl.setSpotInnerAngle(15 * FastMath.DEG_TO_RAD);
sl.setDirection(new Vector3f(-0.39820394f, -0.73094344f, 0.55421597f));
sl.setPosition(new Vector3f(-64.61567f, -87.615425f, -202.41328f));
rootNode.addLight(sl);
AmbientLight ambLight = new AmbientLight();
ambLight.setColor(new ColorRGBA(0.8f, 0.8f, 0.8f, 0.2f));
rootNode.addLight(ambLight);
cam.setLocation(new Vector3f(-41.219646f, -84.8363f, -171.67267f));
cam.setRotation(new Quaternion(-0.04562731f, 0.89917684f, -0.09668826f, -0.4243236f));
sl.setDirection(cam.getDirection());
sl.setPosition(cam.getLocation());
}
@Override
public void simpleUpdate(float tpf) {
super.simpleUpdate(tpf);
sl.setDirection(cam.getDirection());
sl.setPosition(cam.getLocation());
}
private void makeTerrain() {
// TERRAIN TEXTURE material
matTerrain = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
matTerrain.setBoolean("useTriPlanarMapping", false);
matTerrain.setBoolean("WardIso", true);
// ALPHA map (for splat textures)
matTerrain.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alpha1.png"));
matTerrain.setTexture("AlphaMap_1", assetManager.loadTexture("Textures/Terrain/splat/alpha2.png"));
// HEIGHTMAP image (for the terrain heightmap)
Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
// GRASS texture
Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matTerrain.setTexture("DiffuseMap", grass);
matTerrain.setFloat("DiffuseMap_0_scale", grassScale);
// DIRT texture
Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matTerrain.setTexture("DiffuseMap_1", dirt);
matTerrain.setFloat("DiffuseMap_1_scale", dirtScale);
// ROCK texture
Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
matTerrain.setTexture("DiffuseMap_2", rock);
matTerrain.setFloat("DiffuseMap_2_scale", rockScale);
// BRICK texture
Texture brick = assetManager.loadTexture("Textures/Terrain/BrickWall/BrickWall.jpg");
brick.setWrap(WrapMode.Repeat);
matTerrain.setTexture("DiffuseMap_3", brick);
matTerrain.setFloat("DiffuseMap_3_scale", rockScale);
// RIVER ROCK texture
Texture riverRock = assetManager.loadTexture("Textures/Terrain/Pond/Pond.png");
riverRock.setWrap(WrapMode.Repeat);
matTerrain.setTexture("DiffuseMap_4", riverRock);
matTerrain.setFloat("DiffuseMap_4_scale", rockScale);
Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat);
matTerrain.setTexture("NormalMap", normalMap0);
matTerrain.setTexture("NormalMap_1", normalMap2);
matTerrain.setTexture("NormalMap_2", normalMap2);
matTerrain.setTexture("NormalMap_4", normalMap2);
// WIREFRAME material
matWire = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
matWire.getAdditionalRenderState().setWireframe(true);
matWire.setColor("Color", ColorRGBA.Green);
createSky();
// CREATE HEIGHTMAP
AbstractHeightMap heightmap = null;
try {
//heightmap = new HillHeightMap(1025, 1000, 50, 100, (byte) 3);
heightmap = new ImageBasedHeightMap(ImageToAwt.convert(heightMapImage.getImage(), false, true, 0), 1f);
heightmap.load();
} catch (Exception e) {
e.printStackTrace();
}
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());//, new LodPerspectiveCalculatorFactory(getCamera(), 4)); // add this in to see it use entropy for LOD calculations
TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
terrain.addControl(control);
terrain.setMaterial(matTerrain);
terrain.setModelBound(new BoundingBox());
terrain.updateModelBound();
terrain.setLocalTranslation(0, -100, 0);
terrain.setLocalScale(1f, 1f, 1f);
rootNode.attachChild(terrain);
}
private void createSky() {
Texture west = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_west.jpg");
Texture east = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_east.jpg");
Texture north = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_north.jpg");
Texture south = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_south.jpg");
Texture up = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_up.jpg");
Texture down = assetManager.loadTexture("Textures/Sky/Lagoon/lagoon_down.jpg");
Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
rootNode.attachChild(sky);
}
}
Loading…
Cancel
Save