diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
index cd931b367..7709de54b 100644
--- a/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.frag
@@ -52,8 +52,9 @@ uniform float m_Shininess;
#ifdef HQ_ATTENUATION
uniform vec4 g_LightPosition;
-varying vec3 lightVec;
#endif
+varying vec4 lightVec;
+varying vec4 spotVec;
#ifdef USE_REFLECTION
uniform float m_ReflectionPower;
@@ -124,7 +125,7 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
void main(){
vec2 newTexCoord;
-
+
#if defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)
float h;
#ifdef PARALLAXMAP
@@ -146,6 +147,23 @@ void main(){
#else
vec4 diffuseColor = vec4(1.0);
#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;
#ifdef ALPHAMAP
alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r;
@@ -202,7 +220,7 @@ void main(){
vec4 lightDir = vLightDir;
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
diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.vert b/engine/src/core-data/Common/MatDefs/Light/Lighting.vert
index 03996c022..3f2d30930 100644
--- a/engine/src/core-data/Common/MatDefs/Light/Lighting.vert
+++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.vert
@@ -13,6 +13,7 @@ uniform float m_Shininess;
uniform vec4 g_LightColor;
uniform vec4 g_LightPosition;
+uniform vec4 g_LightDirection;
uniform vec4 g_AmbientLightColor;
varying vec2 texCoord;
@@ -29,9 +30,8 @@ attribute vec3 inPosition;
attribute vec2 inTexCoord;
attribute vec3 inNormal;
-#ifdef HQ_ATTENUATION
- varying vec3 lightVec;
-#endif
+varying vec4 lightVec;
+varying vec4 spotVec;
#ifdef VERTEX_COLOR
attribute vec4 inColor;
@@ -45,7 +45,7 @@ attribute vec3 inNormal;
#endif
varying vec3 vPosition;
varying vec3 vViewDir;
- varying vec4 vLightDir;
+ varying vec4 vLightDir;
#endif
#ifdef USE_REFLECTION
@@ -81,13 +81,11 @@ attribute vec3 inNormal;
void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
float posLight = step(0.5, color.w);
vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
+ lightVec.xyz = tempVec;
#ifdef ATTENUATION
float dist = length(tempVec);
lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
lightDir.xyz = tempVec / vec3(dist);
- #ifdef HQ_ATTENUATION
- lightVec = tempVec;
- #endif
#else
lightDir = vec4(normalize(tempVec), 1.0);
#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){
vec4 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 specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess);
//specularFactor *= step(0.01, diffuseFactor);
- return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w);
+ return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w)*spotFallOff;
}
#endif
@@ -132,13 +139,13 @@ void main(){
vec3 wvPosition = (g_WorldViewMatrix * pos).xyz;
vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
vec3 viewDir = normalize(-wvPosition);
-
+
//vec4 lightColor = g_LightColor[gl_InstanceID];
//vec4 lightPos = g_LightPosition[gl_InstanceID];
//vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.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;
vec4 lightColor = g_LightColor;
@@ -166,6 +173,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);
+
lightColor.w = 1.0;
#ifdef MATERIAL_COLORS
AmbientSum = m_Ambient * g_AmbientLightColor;
diff --git a/engine/src/core/com/jme3/light/PointLight.java b/engine/src/core/com/jme3/light/PointLight.java
index 8858060bb..b0c349f9e 100644
--- a/engine/src/core/com/jme3/light/PointLight.java
+++ b/engine/src/core/com/jme3/light/PointLight.java
@@ -29,7 +29,6 @@
* 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;
@@ -55,13 +54,14 @@ public class PointLight extends Light {
protected Vector3f position = new Vector3f();
protected float radius = 0;
+ protected float invRadius = 0;
@Override
public void computeLastDistance(Spatial owner) {
- if (owner.getWorldBound() != null){
+ if (owner.getWorldBound() != null) {
BoundingVolume bv = owner.getWorldBound();
lastDistance = bv.distanceSquaredTo(position);
- }else{
+ } else {
lastDistance = owner.getWorldTranslation().distanceSquared(position);
}
}
@@ -82,7 +82,7 @@ public class PointLight extends Light {
*
* @param position the world space position of the light.
*/
- public void setPosition(Vector3f position){
+ public void setPosition(Vector3f position) {
this.position.set(position);
}
@@ -92,7 +92,7 @@ public class PointLight extends Light {
*
* @return the radius of the light
*/
- public float getRadius(){
+ public float getRadius() {
return radius;
}
@@ -109,11 +109,24 @@ public class PointLight extends Light {
*
* @throws IllegalArgumentException If radius is negative
*/
- public void setRadius(float radius){
+ public void setRadius(float radius) {
if (radius < 0) {
throw new IllegalArgumentException("Light radius cannot be negative");
}
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
@@ -135,6 +148,10 @@ public class PointLight extends Light {
InputCapsule ic = im.getCapsule(this);
position = (Vector3f) ic.readSavable("position", null);
radius = ic.readFloat("radius", 0f);
+ if(radius!=0){
+ this.invRadius = 1 / radius;
+ }else{
+ this.invRadius = 0;
+ }
}
-
}
diff --git a/engine/src/core/com/jme3/light/SpotLight.java b/engine/src/core/com/jme3/light/SpotLight.java
new file mode 100644
index 000000000..332c462be
--- /dev/null
+++ b/engine/src/core/com/jme3/light/SpotLight.java
@@ -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.
+ *
+ * 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.
+ *
+ * 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;
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/material/Material.java b/engine/src/core/com/jme3/material/Material.java
index 987e8d9f1..2211fec41 100644
--- a/engine/src/core/com/jme3/material/Material.java
+++ b/engine/src/core/com/jme3/material/Material.java
@@ -1,33 +1,31 @@
/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
+ * 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.
- *
+ * 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.
- *
+ * 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.
+ * 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.material;
@@ -46,7 +44,9 @@ import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.light.LightList;
import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
import com.jme3.material.TechniqueDef.LightMode;
+import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
@@ -72,12 +72,15 @@ import java.util.logging.Logger;
/**
* Material
describes the rendering style for a given
- * {@link Geometry}.
- *
- * 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.
- *
+ * {
+ *
+ * @link Geometry}.
+ *
+ * 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.
+ *
* @author Kirill Vainer
*/
public class Material implements Cloneable, Savable, Comparable {
@@ -96,7 +99,6 @@ public class Material implements Cloneable, Savable, Comparable {
additiveLight.setBlendMode(RenderState.BlendMode.AlphaAdditive);
additiveLight.setDepthWrite(false);
}
-
private String assetName;
private MaterialDef def;
private ListMap paramValues = new ListMap();
@@ -198,7 +200,7 @@ public class Material implements Cloneable, Savable, Comparable {
public Material clone() {
try {
Material mat = (Material) super.clone();
-
+
if (additionalState != null) {
mat.additionalState = additionalState.clone();
}
@@ -355,7 +357,7 @@ public class Material implements Cloneable, Savable, Comparable {
private String checkSetParam(VarType type, String name) {
MatParam paramDef = def.getMaterialParam(name);
String newName = name;
-
+
if (paramDef == null && name.startsWith("m_")) {
newName = name.substring(2);
paramDef = def.getMaterialParam(newName);
@@ -364,7 +366,7 @@ public class Material implements Cloneable, Savable, Comparable {
} else {
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);
}
@@ -373,7 +375,7 @@ public class Material implements Cloneable, Savable, Comparable {
+ "type {1} doesn't match definition type {2}",
name, type.name(), paramDef.getVarType());
}
-
+
return newName;
}
@@ -404,9 +406,9 @@ public class Material implements Cloneable, Savable, Comparable {
* @param name the name of the parameter to clear
*/
public void clearParam(String name) {
- //On removal, we don't check if the param exists in the paramDef, and just go on with the process.
- // name = checkSetParam(null, name);
-
+ //On removal, we don't check if the param exists in the paramDef, and just go on with the process.
+ // name = checkSetParam(null, name);
+
MatParam matParam = getParam(name);
if (matParam != null) {
paramValues.remove(name);
@@ -435,9 +437,10 @@ public class Material implements Cloneable, Savable, Comparable {
name = checkSetParam(null, name);
MatParamTexture val = getTextureParam(name);
- if (val == null)
+ if (val == null) {
throw new IllegalArgumentException("The given texture parameter is not set.");
-
+ }
+
int texUnit = val.getUnit();
paramValues.remove(name);
nextTexUnit--;
@@ -478,7 +481,7 @@ public class Material implements Cloneable, Savable, Comparable {
if (technique != null) {
technique.notifySetParam(name, type, nextTexUnit - 1);
}
-
+
// need to recompute sort ID
sortingId = -1;
}
@@ -492,7 +495,7 @@ public class Material implements Cloneable, Savable, Comparable {
*/
public void setTexture(String name, Texture value) {
if (value == null) {
- // clear it
+ // clear it
clearTextureParam(name);
return;
}
@@ -625,21 +628,23 @@ public class Material implements Cloneable, Savable, Comparable {
*
*/
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;
}
LightList lightList = g.getWorldLightList();
Uniform lightColor = shader.getUniform("g_LightColor");
Uniform lightPos = shader.getUniform("g_LightPosition");
+ Uniform lightDir = shader.getUniform("g_LightDirection");
lightColor.setVector4Length(numLights);
- lightPos.setVector4Length(numLights);
+ lightPos.setVector4Length(numLights);
+ lightDir.setVector4Length(numLights);
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
-
+
int lightIndex = 0;
-
+
for (int i = 0; i < numLights; i++) {
if (lightList.size() <= i) {
lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
@@ -662,12 +667,19 @@ public class Material implements Cloneable, Savable, Comparable {
case Point:
PointLight pl = (PointLight) l;
Vector3f pos = pl.getPosition();
- float invRadius = pl.getRadius();
- if (invRadius != 0) {
- invRadius = 1f / invRadius;
- }
+ float invRadius = pl.getInvRadius();
lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex);
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:
// skip this light. Does not increase lightIndex
continue;
@@ -675,20 +687,21 @@ public class Material implements Cloneable, Savable, Comparable {
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
}
}
-
+
lightIndex++;
}
-
- while (lightIndex < numLights){
+
+ while (lightIndex < numLights) {
lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
-
+
lightIndex++;
}
}
protected void renderMultipassLighting(Shader shader, Geometry g, Renderer r) {
LightList lightList = g.getWorldLightList();
+ Uniform lightDir = shader.getUniform("g_LightDirection");
Uniform lightColor = shader.getUniform("g_LightColor");
Uniform lightPos = shader.getUniform("g_LightPosition");
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
@@ -740,10 +753,7 @@ public class Material implements Cloneable, Savable, Comparable {
case Point:
PointLight pl = (PointLight) l;
Vector3f pos = pl.getPosition();
- float invRadius = pl.getRadius();
- if (invRadius != 0) {
- invRadius = 1f / invRadius;
- }
+ float invRadius = pl.getInvRadius();
Quaternion q2;
if (lightPos.getValue() != null) {
q2 = (Quaternion) lightPos.getValue();
@@ -752,6 +762,31 @@ public class Material implements Cloneable, Savable, Comparable {
}
q2.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
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;
default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
@@ -1008,41 +1043,41 @@ public class Material implements Cloneable, Savable, Comparable {
public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this);
-
+
additionalState = (RenderState) ic.readSavable("render_state", null);
transparent = ic.readBoolean("is_transparent", false);
// Load the material def
String defName = ic.readString("material_def", null);
HashMap params = (HashMap) ic.readStringSavableMap("parameters", null);
-
+
boolean enableVcolor = false;
boolean separateTexCoord = false;
-
- if (im.getFormatVersion() == 0){
+
+ if (im.getFormatVersion() == 0) {
// 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
enableVcolor = true;
defName = "Common/MatDefs/Misc/Unshaded.j3md";
- }else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md")
- || defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")){
+ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md")
+ || defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")) {
// Using SimpleTextured/SolidColor, just switch to Unshaded
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
getAdditionalRenderState().setWireframe(true);
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
MatParam value = params.get("SeperateTexCoord");
- if (value != null && ((Boolean)value.getValue()) == true){
+ if (value != null && ((Boolean) value.getValue()) == true) {
params.remove("SeperateTexCoord");
separateTexCoord = true;
}
}
}
-
+
def = (MaterialDef) im.getAssetManager().loadAsset(new AssetKey(defName));
paramValues = new ListMap();
@@ -1065,12 +1100,12 @@ public class Material implements Cloneable, Savable, Comparable {
param.setName(checkSetParam(param.getVarType(), param.getName()));
paramValues.put(param.getName(), param);
}
-
- if (enableVcolor){
+
+ if (enableVcolor) {
setBoolean("VertexColor", true);
}
- if (separateTexCoord){
+ if (separateTexCoord) {
setBoolean("SeparateTexCoord", true);
- }
+ }
}
}
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag
index c4d3bdfb0..5e26a1011 100644
--- a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag
+++ b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag
@@ -12,6 +12,8 @@ varying vec3 vnPosition;
varying vec3 vViewDir;
varying vec4 vLightDir;
varying vec4 vnLightDir;
+varying vec4 lightVec;
+varying vec4 spotVec;
#ifdef DIFFUSEMAP
@@ -644,6 +646,20 @@ void main(){
vec4 diffuseColor = vec4(1.0);
#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
@@ -665,7 +681,7 @@ void main(){
vec4 lightDir = vLightDir;
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);
diff --git a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert
index b73e7e210..f77eedb1e 100644
--- a/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert
+++ b/engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert
@@ -5,6 +5,7 @@ uniform mat4 g_ViewMatrix;
uniform vec4 g_LightColor;
uniform vec4 g_LightPosition;
+uniform vec4 g_LightDirection;
uniform vec4 g_AmbientLightColor;
uniform float m_Shininess;
@@ -23,6 +24,9 @@ varying vec3 vnViewDir;
varying vec4 vLightDir;
varying vec4 vnLightDir;
+varying vec4 lightVec;
+varying vec4 spotVec;
+
varying vec4 AmbientSum;
varying vec4 DiffuseSum;
varying vec4 SpecularSum;
@@ -38,7 +42,7 @@ varying vec4 SpecularSum;
void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
float posLight = step(0.5, color.w);
vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
-
+ lightVec.xyz = tempVec;
float dist = length(tempVec);
lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
lightDir.xyz = tempVec / vec3(dist);
@@ -54,7 +58,7 @@ void main(){
vec3 wvNormal = normalize(g_NormalMatrix * inNormal);
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;
vec4 lightColor = g_LightColor;
@@ -84,6 +88,11 @@ void main(){
lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
#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
DiffuseSum = lightColor;
diff --git a/engine/src/test/jme3test/light/TestSpotLight.java b/engine/src/test/jme3test/light/TestSpotLight.java
new file mode 100644
index 000000000..b971bc806
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestSpotLight.java
@@ -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()));
+ }
+
+
+
+}
diff --git a/engine/src/test/jme3test/light/TestSpotLightTerrain.java b/engine/src/test/jme3test/light/TestSpotLightTerrain.java
new file mode 100644
index 000000000..58a5c2199
--- /dev/null
+++ b/engine/src/test/jme3test/light/TestSpotLightTerrain.java
@@ -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);
+ }
+
+
+}