Kaynağa Gözat

- 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

rem..om 14 yıl önce
ebeveyn
işleme
99a4b00c15

+ 21 - 3
engine/src/core-data/Common/MatDefs/Light/Lighting.frag

@@ -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;
@@ -124,7 +125,7 @@ vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec3 w
 
 
 void main(){
 void main(){
     vec2 newTexCoord;
     vec2 newTexCoord;
- 
+     
     #if defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)
     #if defined(PARALLAXMAP) || defined(NORMALMAP_PARALLAX)
        float h;
        float h;
        #ifdef PARALLAXMAP
        #ifdef PARALLAXMAP
@@ -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;

+ 23 - 11
engine/src/core-data/Common/MatDefs/Light/Lighting.vert

@@ -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 vec3 lightVec;
-#endif
+varying vec4 lightVec;
+varying vec4 spotVec;
 
 
 #ifdef VERTEX_COLOR
 #ifdef VERTEX_COLOR
   attribute vec4 inColor;
   attribute vec4 inColor;
@@ -45,7 +45,7 @@ attribute vec3 inNormal;
   #endif
   #endif
   varying vec3 vPosition;
   varying vec3 vPosition;
   varying vec3 vViewDir;
   varying vec3 vViewDir;
-  varying vec4 vLightDir;
+  varying vec4 vLightDir;  
 #endif
 #endif
 
 
 #ifdef USE_REFLECTION
 #ifdef USE_REFLECTION
@@ -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
 
 
@@ -132,13 +139,13 @@ void main(){
    vec3 wvPosition = (g_WorldViewMatrix * pos).xyz;
    vec3 wvPosition = (g_WorldViewMatrix * pos).xyz;
    vec3 wvNormal  = normalize(g_NormalMatrix * inNormal);
    vec3 wvNormal  = normalize(g_NormalMatrix * inNormal);
    vec3 viewDir = normalize(-wvPosition);
    vec3 viewDir = normalize(-wvPosition);
-
+  
        //vec4 lightColor = g_LightColor[gl_InstanceID];
        //vec4 lightColor = g_LightColor[gl_InstanceID];
        //vec4 lightPos   = g_LightPosition[gl_InstanceID];
        //vec4 lightPos   = g_LightPosition[gl_InstanceID];
        //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;

+ 24 - 7
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
  * 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;
+        }
     }
     }
-
 }
 }

+ 222 - 0
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.
+ * <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;
+        }
+    }
+}

+ 110 - 75
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.
+ * <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
- * 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.
+ * <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
- *   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.
+ * <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
- *   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.
+ * <p/>
+ * 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;
 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>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/>
+ * @link Geometry}. 
+ * <p/>
+ * <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>();
@@ -198,7 +200,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
     public Material clone() {
     public Material clone() {
         try {
         try {
             Material mat = (Material) super.clone();
             Material mat = (Material) super.clone();
-            
+
             if (additionalState != null) {
             if (additionalState != null) {
                 mat.additionalState = additionalState.clone();
                 mat.additionalState = additionalState.clone();
             }
             }
@@ -355,7 +357,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
     private String checkSetParam(VarType type, String name) {
     private String checkSetParam(VarType type, String name) {
         MatParam paramDef = def.getMaterialParam(name);
         MatParam paramDef = def.getMaterialParam(name);
         String newName = name;
         String newName = name;
-       
+
         if (paramDef == null && name.startsWith("m_")) {
         if (paramDef == null && name.startsWith("m_")) {
             newName = name.substring(2);
             newName = name.substring(2);
             paramDef = def.getMaterialParam(newName);
             paramDef = def.getMaterialParam(newName);
@@ -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);
         }
         }
 
 
@@ -373,7 +375,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
                     + "type {1} doesn't match definition type {2}",
                     + "type {1} doesn't match definition type {2}",
                     name, type.name(), paramDef.getVarType());
                     name, type.name(), paramDef.getVarType());
         }
         }
-        
+
         return newName;
         return newName;
     }
     }
 
 
@@ -404,9 +406,9 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
      * @param name the name of the parameter to clear
      * @param name the name of the parameter to clear
      */
      */
     public void clearParam(String name) {
     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);
         MatParam matParam = getParam(name);
         if (matParam != null) {
         if (matParam != null) {
             paramValues.remove(name);
             paramValues.remove(name);
@@ -435,9 +437,10 @@ 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);
         nextTexUnit--;
         nextTexUnit--;
@@ -478,7 +481,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
         if (technique != null) {
         if (technique != null) {
             technique.notifySetParam(name, type, nextTexUnit - 1);
             technique.notifySetParam(name, type, nextTexUnit - 1);
         }
         }
-        
+
         // need to recompute sort ID
         // need to recompute sort ID
         sortingId = -1;
         sortingId = -1;
     }
     }
@@ -492,7 +495,7 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
      */
      */
     public void setTexture(String name, Texture value) {
     public void setTexture(String name, Texture value) {
         if (value == null) {
         if (value == null) {
-             // clear it
+            // clear it
             clearTextureParam(name);
             clearTextureParam(name);
             return;
             return;
         }
         }
@@ -625,21 +628,23 @@ 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));
-        
+
         int lightIndex = 0;
         int lightIndex = 0;
-        
+
         for (int i = 0; i < numLights; i++) {
         for (int i = 0; i < numLights; i++) {
             if (lightList.size() <= i) {
             if (lightList.size() <= i) {
                 lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
                 lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
@@ -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();
-                        if (invRadius != 0) {
-                            invRadius = 1f / invRadius;
-                        }
+                        float invRadius = pl.getInvRadius();                        
                         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;
@@ -675,20 +687,21 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
                         throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
                         throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
                 }
                 }
             }
             }
-            
+
             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);
-            
+
             lightIndex++;
             lightIndex++;
         }
         }
     }
     }
 
 
     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();
-                    if (invRadius != 0) {
-                        invRadius = 1f / invRadius;
-                    }
+                    float invRadius = pl.getInvRadius();     
                     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());
@@ -1008,41 +1043,41 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
 
 
     public void read(JmeImporter im) throws IOException {
     public void read(JmeImporter im) throws IOException {
         InputCapsule ic = im.getCapsule(this);
         InputCapsule ic = im.getCapsule(this);
-        
+
         additionalState = (RenderState) ic.readSavable("render_state", null);
         additionalState = (RenderState) ic.readSavable("render_state", null);
         transparent = ic.readBoolean("is_transparent", false);
         transparent = ic.readBoolean("is_transparent", false);
 
 
         // Load the material def
         // Load the material def
         String defName = ic.readString("material_def", null);
         String defName = ic.readString("material_def", null);
         HashMap<String, MatParam> params = (HashMap<String, MatParam>) ic.readStringSavableMap("parameters", null);
         HashMap<String, MatParam> params = (HashMap<String, MatParam>) ic.readStringSavableMap("parameters", null);
-        
+
         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")
-                   || 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
                 // 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;
                 }
                 }
             }
             }
         }
         }
-        
+
         def = (MaterialDef) im.getAssetManager().loadAsset(new AssetKey(defName));
         def = (MaterialDef) im.getAssetManager().loadAsset(new AssetKey(defName));
         paramValues = new ListMap<String, MatParam>();
         paramValues = new ListMap<String, MatParam>();
 
 
@@ -1065,12 +1100,12 @@ public class Material implements Cloneable, Savable, Comparable<Material> {
             param.setName(checkSetParam(param.getVarType(), param.getName()));
             param.setName(checkSetParam(param.getVarType(), param.getName()));
             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);
-        } 
+        }
     }
     }
 }
 }

+ 17 - 1
engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.frag

@@ -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);
 
 

+ 11 - 2
engine/src/terrain/Common/MatDefs/Terrain/TerrainLighting.vert

@@ -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;
 
 
@@ -84,6 +88,11 @@ void main(){
     lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
     lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
 
 
     #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;

+ 159 - 0
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()));     
+    }
+    
+    
+
+}

+ 219 - 0
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);
+    }
+
+  
+}