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