SkyDome - A Skydome with dynamics skylight based on “A practical Analytic Model for Daylight” by A. J. Preetham, Peter Shirley, Brian Smits (University of Utah).
http://www.cs.utah.edu/vissim/papers/sunsky/
See also: com.jme.scene.Skybox
The following is the code for the SkyDome class.
/* * SkyDome.java * */ package yourpackage; import com.jme.bounding.BoundingBox; import com.jme.image.Image; import com.jme.image.Texture; import com.jme.light.DirectionalLight; import com.jme.light.LightNode; import com.jme.math.FastMath; import com.jme.math.Vector3f; import com.jme.renderer.ColorRGBA; import com.jme.scene.Node; import com.jme.scene.SceneElement; import com.jme.scene.Spatial; import com.jme.scene.batch.TriangleBatch; import com.jme.scene.shape.Box; import com.jme.scene.shape.Dome; import com.jme.scene.state.LightState; import com.jme.scene.state.TextureState; import com.jme.scene.state.ZBufferState; import com.jme.system.DisplaySystem; import com.jme.util.TextureManager; import com.jme.util.Timer; import com.jme.util.geom.BufferUtils; import com.jmex.effects.LensFlare; import com.jmex.effects.LensFlareFactory; import java.nio.FloatBuffer; /** * sky gradient based on "A practical analytic model for daylight" * by A. J. Preetham, Peter Shirley, Brian Smits (University of Utah) * @author Highnik */ public class SkyDome extends Node { public static final float INFINITY = 3.3e+38f; public static final float EPSILON = 0.000001f; private Dome dome; private Vector3f cameraPos = new Vector3f(); // shading parameters private float thetaSun; private float phiSun; private float turbidity = 2.0f; private boolean isLinearExpControl; private float exposure = 18.0f; private float overcast; private float gammaCorrection = 2.5f; // time parameters private float timeOfDay = 0.0f; private float julianDay = 0.0f; private float latitude = 0.0f; private float longitude = 0.0f; private float stdMeridian = 0.0f; private float sunnyTime = 12.0f; private float solarDeclination = 0.0f; private float latitudeInRadian = 0.0f; private boolean isNight = false; // timer control. private Timer timer; private float currentTime; private float updateTime = 0.0f; private float timeWarp = 180.0f; private boolean renderRequired = true; // used at update color private float chi; private float zenithLuminance; private float zenithX; private float zenithY; private float[] perezLuminance; private float[] perezX; private float[] perezY; private Vector3f sunDirection = new Vector3f(); private Vector3f sunPosition = new Vector3f(); private ColorXYZ color; private ColorXYZ colorTemp; private TriangleBatch batch; private FloatBuffer colorBuf; private FloatBuffer normalBuf; private Vector3f vertex = new Vector3f(); private float gamma; private float cosTheta; private float cosGamma2; private float x_value; private float y_value; private float yClear; private float yOver; private float _Y; private float _X; private float _Z; private DirectionalLight dr; private LightNode sun; private LensFlare flare; private boolean sunEnabled = true; /** Distribution coefficients for the luminance(Y) distribution function */ private float distributionLuminance[][] = { // Perez distributions { 0.17872f , -1.46303f }, // a = darkening or brightening of the horizon { -0.35540f , 0.42749f }, // b = luminance gradient near the horizon, { -0.02266f , 5.32505f }, // c = relative intensity of the circumsolar region { 0.12064f , -2.57705f }, // d = width of the circumsolar region { -0.06696f , 0.37027f }}; // e = relative backscattered light /** Distribution coefficients for the x distribution function */ private float distributionXcomp[][] = { { -0.01925f , -0.25922f }, { -0.06651f , 0.00081f }, { -0.00041f , 0.21247f }, { -0.06409f , -0.89887f }, { -0.00325f , 0.04517f }}; /** Distribution coefficients for the y distribution function */ private float distributionYcomp[][] = { { -0.01669f , -0.26078f }, { -0.09495f , 0.00921f }, { -0.00792f , 0.21023f }, { -0.04405f , -1.65369f }, { -0.01092f , 0.05291f }}; /** Zenith x value */ private float zenithXmatrix[][] = { { 0.00165f, -0.00375f, 0.00209f, 0.00000f }, { -0.02903f, 0.06377f, -0.03202f, 0.00394f }, { 0.11693f, -0.21196f, 0.06052f, 0.25886f }}; /** Zenith y value */ private float zenithYmatrix[][] = { { 0.00275f, -0.00610f, 0.00317f, 0.00000f }, { -0.04214f, 0.08970f, -0.04153f, 0.00516f }, { 0.15346f, -0.26756f, 0.06670f, 0.26688f }}; /** Creates a new instance of SkyDome */ public SkyDome() { this("SkyDome", 11, 18, 100f); } public SkyDome(String name) { this(name, 11, 18, 100f); } public SkyDome(String name, int planes, int radialSamples, float radius) { this(name, new Vector3f(0, 0, 0), planes, radialSamples, radius); } public SkyDome(String name, Vector3f center, int planes, int radialSamples, float radius) { dome = new Dome(name, center, planes, radialSamples, radius, true); dome.setIsCollidable(false); dome.setSolidColor(ColorRGBA.black); attachChild(dome); timer = Timer.getTimer(); currentTime = timer.getTimeInSeconds(); solarDeclination = calc_solar_declination(julianDay); sunnyTime = calc_sunny_time(latitude, solarDeclination); // create a lens flare effects setupLensFlare(); ZBufferState zbuff = DisplaySystem.getDisplaySystem().getRenderer().createZBufferState(); zbuff.setWritable(false); zbuff.setEnabled(true); zbuff.setFunction(ZBufferState.CF_LEQUAL); setRenderState(zbuff); setCullMode(SceneElement.CULL_NEVER); setLightCombineMode(LightState.OFF); setTextureCombineMode(TextureState.REPLACE); } /** * Set Sun's positon */ public void setSunPosition(Vector3f sunPos) { Vector3f pos = new Vector3f(); pos = FastMath.cartesianToSpherical(sunPos, pos); thetaSun = pos.z; phiSun = pos.y; } /** * Return Sun's position */ public Vector3f getSunPosition() { return sunPosition; } /** * Convert time to sun position * @param time * Sets a time of day between 0 to 24 (6,25 = 6:15 hs) */ public void setSunPosition(float time) { float solarTime, solarAltitude, opp, adj, solarAzimuth, cosSolarDeclination, sinSolarDeclination, sinLatitude, cosLatitude; this.timeOfDay = time; sinLatitude = FastMath.sin(latitudeInRadian); cosLatitude = FastMath.cos(latitudeInRadian); sinSolarDeclination = FastMath.sin(solarDeclination); cosSolarDeclination = FastMath.cos(solarDeclination); // real time solarTime = time + (0.170f * FastMath.sin(4f * FastMath.PI * (julianDay - 80f) / 373f) - 0.129f * FastMath.sin(FastMath.TWO_PI * (julianDay - 8f) / 355f)) + (stdMeridian - longitude) / 15; solarAltitude = FastMath.asin(sinLatitude * sinSolarDeclination - cosLatitude * cosSolarDeclination * FastMath.cos(FastMath.PI * solarTime / sunnyTime)); opp = -cosSolarDeclination * FastMath.sin(FastMath.PI * solarTime / sunnyTime); adj = -(cosLatitude * sinSolarDeclination + sinLatitude * cosSolarDeclination * FastMath.cos(FastMath.PI * solarTime / sunnyTime)); solarAzimuth = FastMath.atan2(opp / adj); if (solarAltitude > 0.0f) { isNight = false; if ((opp < 0.0f && solarAzimuth < 0.0f) || (opp > 0.0f && solarAzimuth > 0.0f)) { solarAzimuth = FastMath.HALF_PI + solarAzimuth; } else { solarAzimuth = FastMath.HALF_PI - solarAzimuth; } phiSun = FastMath.TWO_PI - solarAzimuth; thetaSun = FastMath.HALF_PI - solarAltitude; sunDirection.x = dome.radius; sunDirection.y = phiSun; sunDirection.z = solarAltitude; sunPosition = FastMath.sphericalToCartesian(sunDirection, sunPosition); if (solarAzimuth < 0.0f) { sunPosition.x *= -1; } if (this.isSunEnabled()) sun.setLocalTranslation(sunPosition); } else { isNight = true; } } /** * Return if now is night */ public boolean isNight() { return isNight; } /** * Set Day of year between 0 to 364 */ public void setDay(float julianDay) { this.julianDay = clamp(julianDay, 0.0f, 365.0f); // Solar declination solarDeclination = calc_solar_declination(julianDay); sunnyTime = calc_sunny_time(latitude, solarDeclination); } /** * Get Day of year */ public float getDay() { return julianDay; } /** * Set latitude */ public void setLatitude(float latitude) { this.latitude = clamp(latitude, -90.0f, 90.0f); latitudeInRadian = FastMath.DEG_TO_RAD * latitude; sunnyTime = calc_sunny_time(latitudeInRadian, solarDeclination); } /** * Get latitude */ public float getLatitude() { return latitude; } /** * Set longitude */ public void setLongitude(float longitude) { this.longitude = longitude; } /** * Get longitude */ public float getLongitude() { return longitude; } /** * Set standar meridian * @param stdMeridian * TimeZone * 15 */ public void setStandardMeridian(float stdMeridian) { this.stdMeridian = stdMeridian; } /** * Get standar meridian */ public float getStandardMeridian() { return stdMeridian; } public void setTurbidity(float turbidity ) { this.turbidity = clamp(turbidity, 1.0f, 512.0f); } /** * Set Exposure factor */ public void setExposure(boolean isLinearExpControl, float exposure) { this.isLinearExpControl = isLinearExpControl; this.exposure = 1.0f / clamp(exposure, 1.0f, INFINITY ); } /** * Set Over Cast factor */ public void setOvercastFactor(float overcast) { this.overcast = clamp(overcast, 0.0f, 1.0f ); } /** * Set gamma correction factor */ public void setGammaCorrection(float gamma) { this.gammaCorrection = 1.0f / clamp(gamma, EPSILON, INFINITY ); } /** * Seconds to update */ public void setUpdateTime(float seconds) { this.updateTime = seconds; } public float getUpdateTime() { return updateTime; } /** * if updateTime = 1 and timeWarp = 1, every seconds will be updated */ public void setTimeWarp(float timeWarp) { this.timeWarp = timeWarp; } public float getTimeWarp() { return timeWarp; } public void update() { if (updateTime > 0.0f) { if ((timer.getTimeInSeconds() - currentTime) >= updateTime) { currentTime = timer.getTimeInSeconds(); timeOfDay += updateTime * timeWarp / 3600f; setSunPosition(timeOfDay); renderRequired = true; } } cameraPos = DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation(); dome.setLocalTranslation(new Vector3f(cameraPos.x, 0.0f, cameraPos.z)); } /** * update Sky color */ public void render() { if (! renderRequired) return; if (isNight) { dome.setSolidColor(ColorRGBA.black); return; } // get zenith luminance chi = ( (4.0f / 9.0f) - (turbidity / 120.0f) ) * ( FastMath.PI - (2.0f * thetaSun) ); zenithLuminance = ( (4.0453f * turbidity) - 4.9710f ) * FastMath.tan(chi) - (0.2155f * turbidity) + 2.4192f; if (zenithLuminance < 0.0f) zenithLuminance = -zenithLuminance; // get x / y zenith zenithX = getZenith( zenithXmatrix, thetaSun, turbidity ); zenithY = getZenith( zenithYmatrix, thetaSun, turbidity ); // get perez function parameters perezLuminance = getPerez(distributionLuminance, turbidity ); perezX = getPerez(distributionXcomp, turbidity ); perezY = getPerez(distributionYcomp, turbidity ); // make some precalculation zenithX = perezFunctionO1( perezX, thetaSun, zenithX ); zenithY = perezFunctionO1( perezY, thetaSun, zenithY ); zenithLuminance = perezFunctionO1( perezLuminance, thetaSun, zenithLuminance ); // build sun direction vector sunDirection.x = FastMath.cos(FastMath.HALF_PI - thetaSun) * FastMath.cos(phiSun); sunDirection.y = FastMath.sin(FastMath.HALF_PI - thetaSun); sunDirection.z = FastMath.cos(FastMath.HALF_PI - thetaSun) * FastMath.sin(phiSun); sunDirection.normalize(); // trough all vertices for (int i = 0; i < dome.getBatchCount(); i++) { batch = dome.getBatch(i); normalBuf = batch.getNormalBuffer(); colorBuf = batch.getColorBuffer(); for (int j = 0; j < batch.getVertexCount(); j++) { BufferUtils.populateFromBuffer(vertex, normalBuf, j); // angle between sun and vertex gamma = FastMath.acos(vertex.dot(sunDirection)); if (vertex.y < 0.05f) vertex.y = 0.05f; cosTheta = 1.0f / vertex.y; cosGamma2 = FastMath.sqr(FastMath.cos(gamma)); // Compute x,y values x_value = perezFunctionO2( perezX, cosTheta, gamma, cosGamma2, zenithX ); y_value = perezFunctionO2( perezY, cosTheta, gamma, cosGamma2, zenithY ); // luminance(Y) for clear & overcast sky yClear = perezFunctionO2( perezLuminance, cosTheta, gamma, cosGamma2, zenithLuminance ); yOver = (1.0f + 2.0f * vertex.y) / 3.0f; _Y = FastMath.LERP(overcast, yClear, yOver); _X = (x_value / y_value) * _Y; _Z = ((1.0f - x_value - y_value) / y_value) * _Y; colorTemp = new ColorXYZ(_X, _Y, _Z); color = colorTemp.convertXYZtoRGB(); colorTemp = color.convertRGBtoHSV(); if (isLinearExpControl) { // linear scale colorTemp.setValue(colorTemp.getValue() * exposure); } else { // exp scale colorTemp.setValue(1.0f - FastMath.exp(-exposure * colorTemp.getValue())); } color = colorTemp.convertHSVtoRGB(); // gamma control color.setGammaCorrection(gammaCorrection); // clamp rgb between 0.0 - 1.0 color.clamp(); // change the color BufferUtils.setInBuffer(color.getRGBA(), colorBuf, j); } } renderRequired = false; } /** * Returns a LightNode that represents the Sun */ public LightNode getSun() { return sun; } /** * Set the rootNode to flare */ public void setRootNode(Node value) { if (flare != null) { flare.setRootNode(value); } } /** * Set a intensity to Flare */ public void setIntensity(float value) { if (flare != null) { flare.setIntensity(value); } } /** * Set a target to LightNode */ public void setTarget(Spatial node) { if (sun != null) { sun.setTarget(node); } } public void setSunEnabled(boolean enable) { this.sunEnabled = enable; sun.getLight().setEnabled(enable); } public boolean isSunEnabled() { return sunEnabled; } private float calc_solar_declination(float jDay) { return (0.4093f * FastMath.sin(FastMath.TWO_PI * (284f + jDay) / 365f)); } private float calc_sunny_time(float lat, float solarDeclin) { // Time of hours over horizon float sunnyTime; sunnyTime = (2.0f * FastMath.acos(-FastMath.tan(lat) * FastMath.tan(solarDeclin))); sunnyTime = (sunnyTime * FastMath.RAD_TO_DEG) / 15; return sunnyTime; } /** * Create Lens flare effect */ private void setupLensFlare() { dr = new DirectionalLight(); dr.setEnabled(true); dr.setDiffuse(ColorRGBA.white); dr.setAmbient(ColorRGBA.gray); dr.setDirection(new Vector3f(0.0f, 0.0f, 0.0f)); sun = new LightNode("SunNode", DisplaySystem.getDisplaySystem().getRenderer().createLightState()); sun.setLight(dr); Vector3f min2 = new Vector3f( -0.1f, -0.1f, -0.1f); Vector3f max2 = new Vector3f(0.1f, 0.1f, 0.1f); Box lightBox = new Box("lightbox", min2, max2); lightBox.setModelBound(new BoundingBox()); lightBox.updateModelBound(); sun.attachChild(lightBox); lightBox.setLightCombineMode(LightState.OFF); // Setup the lensflare textures. TextureState[] tex = new TextureState[4]; tex[0] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); tex[0].setTexture( TextureManager.loadTexture( SkyDome.class.getClassLoader().getResource( "resources/images/texture/flare1.png"), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, Image.RGBA8888, 1.0f, true)); tex[0].setEnabled(true); tex[1] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); tex[1].setTexture( TextureManager.loadTexture( SkyDome.class.getClassLoader().getResource( "resources/images/texture/flare2.png"), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR)); tex[1].setEnabled(true); tex[2] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); tex[2].setTexture( TextureManager.loadTexture( SkyDome.class.getClassLoader().getResource( "resources/images/texture/flare3.png"), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR)); tex[2].setEnabled(true); tex[3] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); tex[3].setTexture( TextureManager.loadTexture( SkyDome.class.getClassLoader().getResource( "resources/images/texture/flare4.png"), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR)); tex[3].setEnabled(true); flare = LensFlareFactory.createBasicLensFlare("flare", tex); flare.setIntensity(0.5f); // notice that it comes at the end sun.attachChild(flare); attachChild(sun); } private float[] getPerez(float[][] distribution, float turbidity ) { float[] perez = new float[5]; perez[0] = distribution[0][0] * turbidity + distribution[0][1]; perez[1] = distribution[1][0] * turbidity + distribution[1][1]; perez[2] = distribution[2][0] * turbidity + distribution[2][1]; perez[3] = distribution[3][0] * turbidity + distribution[3][1]; perez[4] = distribution[4][0] * turbidity + distribution[4][1]; return perez; } private float getZenith(float[][] zenithMatrix, float theta, float turbidity) { float theta2 = theta * theta; float theta3 = theta * theta2; return (zenithMatrix[0][0] * theta3 + zenithMatrix[0][1] * theta2 + zenithMatrix[0][2] * theta + zenithMatrix[0][3]) * turbidity * turbidity + (zenithMatrix[1][0] * theta3 + zenithMatrix[1][1] * theta2 + zenithMatrix[1][2] * theta + zenithMatrix[1][3]) * turbidity + (zenithMatrix[2][0] * theta3 + zenithMatrix[2][1] * theta2 + zenithMatrix[2][2] * theta + zenithMatrix[2][3]); } private float perezFunctionO1(float[] perezCoeffs, float thetaSun, float zenithValue ) { float val = (1.0f + perezCoeffs[0] * FastMath.exp(perezCoeffs[1])) * (1.0f + perezCoeffs[2] * FastMath.exp(perezCoeffs[3] * thetaSun ) + perezCoeffs[4] * FastMath.sqr(FastMath.cos(thetaSun))); return zenithValue / val; } private float perezFunctionO2(float[] perezCoeffs, float cosTheta, float gamma, float cosGamma2, float zenithValue ) { return zenithValue * (1.0f + perezCoeffs[0] * FastMath.exp(perezCoeffs[1] * cosTheta )) * (1.0f + perezCoeffs[2] * FastMath.exp(perezCoeffs[3] * gamma) + perezCoeffs[4] * cosGamma2); } /** * clamp the value between min and max values */ private float clamp(float value, float min, float max) { if (value < min) return min; else if (value > max) return max; else return value; } class ColorXYZ { private float x = 0.0f; private float y = 0.0f; private float z = 0.0f; private float r = 0.0f; private float g = 0.0f; private float b = 0.0f; private float a = 1.0f; private float hue = 0.0f; private float saturation = 0.0f; private float value = 0.0f; public ColorXYZ(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public void setValue(float value) { this.value = value; } public float getValue() { return this.value; } public void clamp() { if (r < 0) r = 0; if (g < 0) g = 0; if (b < 0) b = 0; if (r > 1) r = 1; if (g > 1) g = 1; if (b > 1) b = 1; } public void setGammaCorrection(float gammaCorrection) { r = FastMath.pow(r, gammaCorrection); g = FastMath.pow(g, gammaCorrection); b = FastMath.pow(b, gammaCorrection); } /** * Retorna o RGBA color */ public ColorRGBA getRGBA() { return new ColorRGBA(r,g,b,a); } /** * Converte XYZ to RGB color */ public ColorXYZ convertXYZtoRGB() { this.r = 3.240479f * x - 1.537150f * y - 0.498535f * z; this.g = -0.969256f * x + 1.875992f * y + 0.041556f * z; this.b = 0.055648f * x - 0.204043f * y + 1.057311f * z; return this; } /** * Converte RGB to HSV */ public ColorXYZ convertRGBtoHSV() { float minColor = Math.min(Math.min(r,g),b); float maxColor = Math.max(Math.max(r,g),b); float delta = maxColor - minColor; this.value = maxColor; // Value if ( ! (FastMath.abs(maxColor) < EPSILON)) { this.saturation = delta / maxColor; // Saturation } else { // r = g = b = 0 this.saturation = 0.0f; // Saturation = 0 this.hue = -1; // Hue = undefined return this; } if (FastMath.abs(r - maxColor) < EPSILON) this.hue = (g - b) / delta; // between yellow & magenta else if (FastMath.abs(g - maxColor) < EPSILON) this.hue = 2.0f + (b-r) / delta; // between cyan & yellow else this.hue = 4.0f + (r-g) / delta; // between magenta & cyan this.hue *= 60.0f; // degrees if (this.hue < 0.0f ) this.hue += 360.0f; // positive return this; } /** * Converte HSV to RGB */ public ColorXYZ convertHSVtoRGB() { if (FastMath.abs(saturation) < EPSILON) { // achromatic (grey) this.r = value; this.g = value; this.b = value; this.a = value; } hue /= 60.0f; // sector 0 to 5 int sector = (int) FastMath.floor(hue); float f = hue - sector; // factorial part of hue float p = value * (1.0f - saturation); float q = value * (1.0f - saturation * f ); float t = value * (1.0f - saturation * (1.0f - f)); switch (sector) { case 0: this.r = value; this.g = t; this.b = p; break; case 1: this.r = q; this.g = value; this.b = p; break; case 2: this.r = p; this.g = value; this.b = t; break; case 3: this.r = p; this.g = q; this.b = value; break; case 4: this.r = t; this.g = p; this.b = value; break; default: // case 5: this.r = value; this.g = p; this.b = q; break; } return this; } } }
The following is the code for the SkyDome class.
/* * SkyDome.java * */ package yourpackage; import java.nio.FloatBuffer; import com.jme.bounding.BoundingBox; import com.jme.image.Image; import com.jme.image.Texture; import com.jme.light.DirectionalLight; import com.jme.light.LightNode; import com.jme.math.FastMath; import com.jme.math.Vector3f; import com.jme.renderer.ColorRGBA; import com.jme.scene.Node; import com.jme.scene.Spatial; import com.jme.scene.shape.Box; import com.jme.scene.shape.Dome; import com.jme.scene.state.CullState; import com.jme.scene.state.LightState; import com.jme.scene.state.TextureState; import com.jme.scene.state.ZBufferState; import com.jme.system.DisplaySystem; import com.jme.util.TextureManager; import com.jme.util.Timer; import com.jme.util.geom.BufferUtils; import com.jme.util.resource.ResourceLocatorTool; import com.jmex.effects.LensFlare; import com.jmex.effects.LensFlareFactory; /** * sky gradient based on "A practical analytic model for daylight" * by A. J. Preetham, Peter Shirley, Brian Smits (University of Utah) * @author Highnik */ public class SkyDome3 extends Node { public static final float INFINITY = 3.3e+38f; public static final float EPSILON = 0.000001f; private Dome dome; private Vector3f cameraPos = new Vector3f(); // shading parameters private float thetaSun; private float phiSun; private float turbidity = 2.0f; private boolean isLinearExpControl; private float exposure = 18.0f; private float overcast; private float gammaCorrection = 2.5f; // time parameters private float timeOfDay = 0.0f; private float julianDay = 0.0f; private float latitude = 0.0f; private float longitude = 0.0f; private float stdMeridian = 0.0f; private float sunnyTime = 12.0f; private float solarDeclination = 0.0f; private float latitudeInRadian = 0.0f; private boolean isNight = false; // timer control. private Timer timer; private float currentTime; private float updateTime = 0.0f; private float timeWarp = 180.0f; private boolean renderRequired = true; // used at update color private float chi; private float zenithLuminance; private float zenithX; private float zenithY; private float[] perezLuminance; private float[] perezX; private float[] perezY; private Vector3f sunDirection = new Vector3f(); private Vector3f sunPosition = new Vector3f(); private FloatBuffer colorBuf; private FloatBuffer normalBuf; private Vector3f vertex = new Vector3f(); private float gamma; private float cosTheta; private float cosGamma2; private float x_value; private float y_value; private float yClear; private float yOver; private float _Y; private float _X; private float _Z; private DirectionalLight dr; private LightNode sun; private LensFlare flare; private boolean sunEnabled = true; /** Distribution coefficients for the luminance(Y) distribution function */ private float distributionLuminance[][] = { // Perez distributions {0.17872f, -1.46303f}, // a = darkening or brightening of the horizon {-0.35540f, 0.42749f}, // b = luminance gradient near the horizon, {-0.02266f, 5.32505f}, // c = relative intensity of the circumsolar region {0.12064f, -2.57705f}, // d = width of the circumsolar region {-0.06696f, 0.37027f}}; // e = relative backscattered light /** Distribution coefficients for the x distribution function */ private float distributionXcomp[][] = { {-0.01925f, -0.25922f}, {-0.06651f, 0.00081f}, {-0.00041f, 0.21247f}, {-0.06409f, -0.89887f}, {-0.00325f, 0.04517f} }; /** Distribution coefficients for the y distribution function */ private float distributionYcomp[][] = { {-0.01669f, -0.26078f}, {-0.09495f, 0.00921f}, {-0.00792f, 0.21023f}, {-0.04405f, -1.65369f}, {-0.01092f, 0.05291f} }; /** Zenith x value */ private float zenithXmatrix[][] = { {0.00165f, -0.00375f, 0.00209f, 0.00000f}, {-0.02903f, 0.06377f, -0.03202f, 0.00394f}, {0.11693f, -0.21196f, 0.06052f, 0.25886f} }; /** Zenith y value */ private float zenithYmatrix[][] = { {0.00275f, -0.00610f, 0.00317f, 0.00000f}, {-0.04214f, 0.08970f, -0.04153f, 0.00516f}, {0.15346f, -0.26756f, 0.06670f, 0.26688f} }; private LightState lightState; /** Creates a new instance of SkyDome */ public SkyDome3() { this("SkyDome", 11, 18, 100f); } public SkyDome3(String name) { this(name, 11, 18, 100f); } public SkyDome3(String name, int planes, int radialSamples, float radius) { this(name, new Vector3f(0, 0, 0), planes, radialSamples, radius); } public SkyDome3(String name, Vector3f center, int planes, int radialSamples, float radius) { dome = new Dome(name, center, planes, radialSamples, radius, true); dome.setIsCollidable(false); dome.setSolidColor(ColorRGBA.black); attachChild(dome); timer = Timer.getTimer(); currentTime = timer.getTimeInSeconds(); solarDeclination = calc_solar_declination(julianDay); sunnyTime = calc_sunny_time(latitude, solarDeclination); // create a lens flare effects setupLensFlare(); ZBufferState zbuff = DisplaySystem.getDisplaySystem().getRenderer().createZBufferState(); zbuff.setWritable(false); zbuff.setEnabled(true); zbuff.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo); setRenderState(zbuff); setCullHint(CullHint.Never); setLightCombineMode(Spatial.LightCombineMode.Off); setTextureCombineMode(Spatial.TextureCombineMode.Replace); CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState(); cs.setEnabled(true); cs.setCullFace(CullState.Face.None); dome.setRenderState(cs); dome.setCullHint(CullHint.Never); dome.setLightCombineMode(LightCombineMode.Off); dome.setTextureCombineMode(Spatial.TextureCombineMode.Replace); } /** * Set Sun's positon */ public void setSunPosition(Vector3f sunPos) { Vector3f pos = new Vector3f(); pos = FastMath.cartesianToSpherical(sunPos, pos); thetaSun = pos.z; phiSun = pos.y; } /** * Return Sun's position */ public Vector3f getSunPosition() { return sunPosition; } /** * Convert time to sun position * @param time * Sets a time of day between 0 to 24 (6,25 = 6:15 hs) */ public void setSunPosition(float time) { float solarTime, solarAltitude, opp, adj, solarAzimuth, cosSolarDeclination, sinSolarDeclination, sinLatitude, cosLatitude; this.timeOfDay = time; sinLatitude = FastMath.sin(latitudeInRadian); cosLatitude = FastMath.cos(latitudeInRadian); sinSolarDeclination = FastMath.sin(solarDeclination); cosSolarDeclination = FastMath.cos(solarDeclination); // real time solarTime = time + (0.170f * FastMath.sin(4f * FastMath.PI * (julianDay - 80f) / 373f) - 0.129f * FastMath.sin(FastMath.TWO_PI * (julianDay - 8f) / 355f)) + (stdMeridian - longitude) / 15; solarAltitude = FastMath.asin(sinLatitude * sinSolarDeclination - cosLatitude * cosSolarDeclination * FastMath.cos(FastMath.PI * solarTime / sunnyTime)); opp = -cosSolarDeclination * FastMath.sin(FastMath.PI * solarTime / sunnyTime); adj = (cosLatitude * sinSolarDeclination + sinLatitude * cosSolarDeclination * FastMath.cos(FastMath.PI * solarTime / sunnyTime)); solarAzimuth = FastMath.atan2(opp, adj); if (solarAltitude > 0.0f) { isNight = false; if ((opp < 0.0f && solarAzimuth < 0.0f) || (opp > 0.0f && solarAzimuth > 0.0f)) { solarAzimuth = FastMath.HALF_PI + solarAzimuth; } else { solarAzimuth = FastMath.HALF_PI - solarAzimuth; } phiSun = FastMath.TWO_PI - solarAzimuth; thetaSun = FastMath.HALF_PI - solarAltitude; sunDirection.x = dome.getRadius(); sunDirection.y = phiSun; sunDirection.z = solarAltitude; sunPosition = FastMath.sphericalToCartesian(sunDirection, sunPosition); if (this.isSunEnabled()) { sun.setLocalTranslation(sunPosition); } } else { isNight = true; } } /** * Return if now is night */ public boolean isNight() { return isNight; } /** * Set Day of year between 0 to 364 */ public void setDay(float julianDay) { this.julianDay = clamp(julianDay, 0.0f, 365.0f); // Solar declination solarDeclination = calc_solar_declination(julianDay); sunnyTime = calc_sunny_time(latitudeInRadian, solarDeclination); } /** * Get Day of year */ public float getDay() { return julianDay; } /** * Set latitude */ public void setLatitude(float latitude) { this.latitude = clamp(latitude, -90.0f, 90.0f); latitudeInRadian = FastMath.DEG_TO_RAD * latitude; sunnyTime = calc_sunny_time(latitudeInRadian, solarDeclination); } /** * Get latitude */ public float getLatitude() { return latitude; } /** * Set longitude */ public void setLongitude(float longitude) { this.longitude = longitude; } /** * Get longitude */ public float getLongitude() { return longitude; } /** * Set standar meridian * @param stdMeridian * TimeZone * 15 */ public void setStandardMeridian(float stdMeridian) { this.stdMeridian = stdMeridian; } /** * Get standar meridian */ public float getStandardMeridian() { return stdMeridian; } public void setTurbidity(float turbidity) { this.turbidity = clamp(turbidity, 1.0f, 512.0f); } /** * Set Exposure factor */ public void setExposure(boolean isLinearExpControl, float exposure) { this.isLinearExpControl = isLinearExpControl; this.exposure = 1.0f / clamp(exposure, 1.0f, INFINITY); } /** * Set Over Cast factor */ public void setOvercastFactor(float overcast) { this.overcast = clamp(overcast, 0.0f, 1.0f); } /** * Set gamma correction factor */ public void setGammaCorrection(float gamma) { this.gammaCorrection = 1.0f / clamp(gamma, EPSILON, INFINITY); } /** * Seconds to update */ public void setUpdateTime(float seconds) { this.updateTime = seconds; } public float getUpdateTime() { return updateTime; } /** * if updateTime = 1 and timeWarp = 1, every seconds will be updated */ public void setTimeWarp(float timeWarp) { this.timeWarp = timeWarp; } public float getTimeWarp() { return timeWarp; } public void update() { if (updateTime > 0.0f) { if ((timer.getTimeInSeconds() - currentTime) >= updateTime) { currentTime = timer.getTimeInSeconds(); timeOfDay += updateTime * timeWarp / 3600f; setSunPosition(timeOfDay); renderRequired = true; } } cameraPos = DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation(); dome.setLocalTranslation(new Vector3f(cameraPos.x, 0.0f, cameraPos.z)); } /** * update Sky color */ public void render() { if (!renderRequired) { return; } if (isNight) { dome.setSolidColor(ColorRGBA.black); return; } // get zenith luminance chi = ((4.0f / 9.0f) - (turbidity / 120.0f)) * (FastMath.PI - (2.0f * thetaSun)); zenithLuminance = ((4.0453f * turbidity) - 4.9710f) * FastMath.tan(chi) - (0.2155f * turbidity) + 2.4192f; if (zenithLuminance < 0.0f) { zenithLuminance = -zenithLuminance; } // get x / y zenith zenithX = getZenith(zenithXmatrix, thetaSun, turbidity); zenithY = getZenith(zenithYmatrix, thetaSun, turbidity); // get perez function parameters perezLuminance = getPerez(distributionLuminance, turbidity); perezX = getPerez(distributionXcomp, turbidity); perezY = getPerez(distributionYcomp, turbidity); // make some precalculation zenithX = perezFunctionO1(perezX, thetaSun, zenithX); zenithY = perezFunctionO1(perezY, thetaSun, zenithY); zenithLuminance = perezFunctionO1(perezLuminance, thetaSun, zenithLuminance); // build sun direction vector sunDirection.x = FastMath.cos(FastMath.HALF_PI - thetaSun) * FastMath.cos(phiSun); sunDirection.y = FastMath.sin(FastMath.HALF_PI - thetaSun); sunDirection.z = FastMath.cos(FastMath.HALF_PI - thetaSun) * FastMath.sin(phiSun); sunDirection.normalize(); // trough all vertices normalBuf = dome.getNormalBuffer(); colorBuf = dome.getColorBuffer(); for (int i = 0; i < (normalBuf.limit() / 3); i++) { BufferUtils.populateFromBuffer(vertex, normalBuf, i); // angle between sun and vertex gamma = FastMath.acos(vertex.dot(sunDirection)); if (vertex.y < 0.05f) { vertex.y = 0.05f; } cosTheta = 1.0f / vertex.y; cosGamma2 = FastMath.sqr(FastMath.cos(gamma)); // Compute x,y values x_value = perezFunctionO2(perezX, cosTheta, gamma, cosGamma2, zenithX); y_value = perezFunctionO2(perezY, cosTheta, gamma, cosGamma2, zenithY); // luminance(Y) for clear & overcast sky yClear = perezFunctionO2(perezLuminance, cosTheta, gamma, cosGamma2, zenithLuminance); yOver = (1.0f + 2.0f * vertex.y) / 3.0f; _Y = FastMath.LERP(overcast, yClear, yOver); _X = (x_value / y_value) * _Y; _Z = ((1.0f - x_value - y_value) / y_value) * _Y; ColorXYZ color = new ColorXYZ(_X, _Y, _Z); color.convertXYZtoRGB(); color.convertRGBtoHSV(); if (isLinearExpControl) { // linear scale color.setValue(color.getValue() * exposure); } else { // exp scale color.setValue(1.0f - FastMath.exp(-exposure * color.getValue())); } color.convertHSVtoRGB(); // gamma control color.setGammaCorrection(gammaCorrection); // clamp rgb between 0.0 - 1.0 color.clamp(); // change the color BufferUtils.setInBuffer(color.getRGBA(), colorBuf, i); } renderRequired = false; } /** * Returns a LightNode that represents the Sun */ public LightNode getSun() { return sun; } /** * Set the rootNode to flare */ public void setRootNode(Node value) { if (flare != null) { flare.setRootNode(value); } } /** * Set a intensity to Flare */ public void setIntensity(float value) { if (flare != null) { flare.setIntensity(value); } } /** * Attach the lightstate to scene */ public void setTarget(Spatial node) { node.setRenderState(lightState); } public void setSunEnabled(boolean enable) { this.sunEnabled = enable; sun.getLight().setEnabled(enable); } public boolean isSunEnabled() { return sunEnabled; } private float calc_solar_declination(float jDay) { return (0.4093f * FastMath.sin(FastMath.TWO_PI * (284f + jDay) / 365f)); } private float calc_sunny_time(float lat, float solarDeclin) { // Time of hours over horizon sunnyTime = (2.0f * FastMath.acos(-FastMath.tan(lat) * FastMath.tan(solarDeclin))); sunnyTime = (sunnyTime * FastMath.RAD_TO_DEG) / 15; return sunnyTime; } /** * Create Lens flare effect */ private void setupLensFlare() { dr = new DirectionalLight(); dr.setEnabled(true); dr.setDiffuse(ColorRGBA.white); dr.setAmbient(ColorRGBA.gray); dr.setDirection(new Vector3f(0.0f, 0.0f, 0.0f)); lightState = DisplaySystem.getDisplaySystem().getRenderer().createLightState(); lightState.setEnabled(true); lightState.attach(dr); sun = new LightNode("SunNode"); sun.setLight(dr); Vector3f min2 = new Vector3f(-0.1f, -0.1f, -0.1f); Vector3f max2 = new Vector3f(0.1f, 0.1f, 0.1f); Box lightBox = new Box("lightbox", min2, max2); lightBox.setModelBound(new BoundingBox()); lightBox.updateModelBound(); sun.attachChild(lightBox); lightBox.setLightCombineMode(Spatial.LightCombineMode.Off); // Setup the lensflare textures. TextureState[] tex = new TextureState[4]; tex[0] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); tex[0].setTexture( TextureManager.loadTexture( ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, "flare1.png"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, Image.Format.RGBA8, 1.0f, true)); tex[0].setEnabled(true); tex[1] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); tex[1].setTexture( TextureManager.loadTexture( ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, "flare2.png"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear)); tex[1].setEnabled(true); tex[2] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); tex[2].setTexture( TextureManager.loadTexture( ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, "flare3.png"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear)); tex[2].setEnabled(true); tex[3] = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); tex[3].setTexture( TextureManager.loadTexture( ResourceLocatorTool.locateResource(ResourceLocatorTool.TYPE_TEXTURE, "flare4.png"), Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear)); tex[3].setEnabled(true); flare = LensFlareFactory.createBasicLensFlare("flare", tex); flare.setIntensity(0.5f); flare.setModelBound(new BoundingBox()); flare.setCullHint(CullHint.Never); flare.updateModelBound(); flare.setTriangleAccurateOcclusion(true); // notice that it comes at the end sun.attachChild(flare); attachChild(sun); } private float[] getPerez(float[][] distribution, float turbidity) { float[] perez = new float[5]; perez[0] = distribution[0][0] * turbidity + distribution[0][1]; perez[1] = distribution[1][0] * turbidity + distribution[1][1]; perez[2] = distribution[2][0] * turbidity + distribution[2][1]; perez[3] = distribution[3][0] * turbidity + distribution[3][1]; perez[4] = distribution[4][0] * turbidity + distribution[4][1]; return perez; } private float getZenith(float[][] zenithMatrix, float theta, float turbidity) { float theta2 = theta * theta; float theta3 = theta * theta2; return (zenithMatrix[0][0] * theta3 + zenithMatrix[0][1] * theta2 + zenithMatrix[0][2] * theta + zenithMatrix[0][3]) * turbidity * turbidity + (zenithMatrix[1][0] * theta3 + zenithMatrix[1][1] * theta2 + zenithMatrix[1][2] * theta + zenithMatrix[1][3]) * turbidity + (zenithMatrix[2][0] * theta3 + zenithMatrix[2][1] * theta2 + zenithMatrix[2][2] * theta + zenithMatrix[2][3]); } private float perezFunctionO1(float[] perezCoeffs, float thetaSun, float zenithValue) { float val = (1.0f + perezCoeffs[0] * FastMath.exp(perezCoeffs[1])) * (1.0f + perezCoeffs[2] * FastMath.exp(perezCoeffs[3] * thetaSun) + perezCoeffs[4] * FastMath.sqr(FastMath.cos(thetaSun))); return zenithValue / val; } private float perezFunctionO2(float[] perezCoeffs, float cosTheta, float gamma, float cosGamma2, float zenithValue) { return zenithValue * (1.0f + perezCoeffs[0] * FastMath.exp(perezCoeffs[1] * cosTheta)) * (1.0f + perezCoeffs[2] * FastMath.exp(perezCoeffs[3] * gamma) + perezCoeffs[4] * cosGamma2); } /** * clamp the value between min and max values */ private float clamp(float value, float min, float max) { if (value < min) { return min; } else if (value > max) { return max; } else { return value; } } class ColorXYZ { private float x = 0.0f; private float y = 0.0f; private float z = 0.0f; private float r = 0.0f; private float g = 0.0f; private float b = 0.0f; private float a = 1.0f; private float hue = 0.0f; private float saturation = 0.0f; private float value = 0.0f; public ColorXYZ(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public void setValue(float value) { this.value = value; } public float getValue() { return this.value; } public void clamp() { if (r < 0) { r = 0; } if (g < 0) { g = 0; } if (b < 0) { b = 0; } if (r > 1) { r = 1; } if (g > 1) { g = 1; } if (b > 1) { b = 1; } } public void setGammaCorrection(float gammaCorrection) { r = FastMath.pow(r, gammaCorrection); g = FastMath.pow(g, gammaCorrection); b = FastMath.pow(b, gammaCorrection); } /** * Retorna o RGBA color */ public ColorRGBA getRGBA() { return new ColorRGBA(r, g, b, a); } /** * Converte XYZ to RGB color */ public void convertXYZtoRGB() { this.r = 3.240479f * x - 1.537150f * y - 0.498530f * z; this.g = -0.969256f * x + 1.875991f * y + 0.041556f * z; this.b = 0.055648f * x - 0.204043f * y + 1.057311f * z; } /** * Converte RGB to HSV */ public void convertRGBtoHSV() { float minColor = Math.min(Math.min(r, g), b); float maxColor = Math.max(Math.max(r, g), b); float delta = maxColor - minColor; this.value = maxColor; // Value if (!(FastMath.abs(maxColor) < EPSILON)) { this.saturation = delta / maxColor; // Saturation } else { // r = g = b = 0 this.saturation = 0.0f; // Saturation = 0 this.hue = -1; // Hue = undefined return; } if (FastMath.abs(r - maxColor) < EPSILON) { this.hue = (g - b) / delta; } // between yellow & magenta else if (FastMath.abs(g - maxColor) < EPSILON) { this.hue = 2.0f + (b - r) / delta; } // between cyan & yellow else { this.hue = 4.0f + (r - g) / delta; } // between magenta & cyan this.hue *= 60.0f; // degrees if (this.hue < 0.0f) { this.hue += 360.0f; } // positive } /** * Converte HSV to RGB */ public ColorXYZ convertHSVtoRGB() { if (FastMath.abs(saturation) < EPSILON) { // achromatic (grey) this.r = value; this.g = value; this.b = value; this.a = value; } hue /= 60.0f; // sector 0 to 5 int sector = (int) FastMath.floor(hue); float f = hue - sector; // factorial part of hue float p = value * (1.0f - saturation); float q = value * (1.0f - saturation * f); float t = value * (1.0f - saturation * (1.0f - f)); switch (sector) { case 0: this.r = value; this.g = t; this.b = p; break; case 1: this.r = q; this.g = value; this.b = p; break; case 2: this.r = p; this.g = value; this.b = t; break; case 3: this.r = p; this.g = q; this.b = value; break; case 4: this.r = t; this.g = p; this.b = value; break; default: // case 5: this.r = value; this.g = p; this.b = q; break; } return this; } } }
To use SkyDome write the following:
package yourpackage; import com.jme.app.SimpleGame; import com.jme.bounding.BoundingSphere; import com.jme.image.Texture; import com.jme.math.Vector3f; import com.jme.scene.state.CullState; import com.jme.scene.state.TextureState; import com.jme.system.DisplaySystem; import com.jme.util.TextureManager; import com.jmex.terrain.TerrainPage; import com.jmex.terrain.util.FaultFractalHeightMap; import com.jmex.terrain.util.ProceduralTextureGenerator; import javax.swing.ImageIcon; /** * TestSkyDome.java * * @author Highnik */ public class TestSkyDome extends SimpleGame { private SkyDome dome; private TerrainPage terrain; private Vector3f camPos = new Vector3f(); /** * Update time */ protected void simpleUpdate() { camPos.x = cam.getLocation().x; camPos.y = terrain.getHeight(cam.getLocation()) + 10; camPos.z = cam.getLocation().z; cam.setLocation(camPos); dome.update(); } /** * update colors */ protected void simpleRender() { dome.render(); } /* * (non-Javadoc) * * @see com.jme.app.SimpleGame#initGame() */ protected void simpleInitGame() { display.setTitle("TestSkyDome"); lightState.setTwoSidedLighting(true); setupTerrain(); setupSkyDome(); } /** * add terrain */ private void setupTerrain() { CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState(); cs.setCullMode(CullState.CS_BACK); cs.setEnabled(true); rootNode.setRenderState(cs); FaultFractalHeightMap heightMap = new FaultFractalHeightMap(257, 32, 0, 255, 0.75f); Vector3f terrainScale = new Vector3f(10,0.75f,10); heightMap.setHeightScale( 0.001f); terrain = new TerrainPage("Terrain", 33, heightMap.getSize(), terrainScale, heightMap.getHeightMap(), false); terrain.setDetailTexture(1, 128); rootNode.attachChild(terrain); ProceduralTextureGenerator pt = new ProceduralTextureGenerator(heightMap); pt.addTexture(new ImageIcon(TestSkyDome.class.getClassLoader().getResource("jmetest/data/texture/grassb.png")), -128, 0, 155); pt.addTexture(new ImageIcon(TestSkyDome.class.getClassLoader().getResource("jmetest/data/texture/dirt.jpg")), 0, 155, 220); pt.addTexture(new ImageIcon(TestSkyDome.class.getClassLoader().getResource("jmetest/data/texture/highest.jpg")), 155, 220, 512); pt.createTexture(512); TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState(); ts.setEnabled(true); Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, true); ts.setTexture(t1, 0); Texture t2 = TextureManager.loadTexture(TestSkyDome.class.getClassLoader().getResource("jmetest/data/texture/Detail.jpg"), Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR); ts.setTexture(t2, 1); t2.setWrap(Texture.WM_WRAP_S_WRAP_T); t1.setApply(Texture.AM_COMBINE); t1.setCombineFuncRGB(Texture.ACF_MODULATE); t1.setCombineSrc0RGB(Texture.ACS_TEXTURE); t1.setCombineOp0RGB(Texture.ACO_SRC_COLOR); t1.setCombineSrc1RGB(Texture.ACS_PRIMARY_COLOR); t1.setCombineOp1RGB(Texture.ACO_SRC_COLOR); t1.setCombineScaleRGB(1.0f); t2.setApply(Texture.AM_COMBINE); t2.setCombineFuncRGB(Texture.ACF_ADD_SIGNED); t2.setCombineSrc0RGB(Texture.ACS_TEXTURE); t2.setCombineOp0RGB(Texture.ACO_SRC_COLOR); t2.setCombineSrc1RGB(Texture.ACS_PREVIOUS); t2.setCombineOp1RGB(Texture.ACO_SRC_COLOR); t2.setCombineScaleRGB(1.0f); terrain.setRenderState(ts); } /** * Initialize SkyDome */ private void setupSkyDome() { dome = new SkyDome("skyDome", new Vector3f(0.0f,0.0f,0.0f), 11, 18, 850.0f); dome.setModelBound(new BoundingSphere()); dome.updateModelBound(); dome.updateRenderState(); dome.setUpdateTime(5.0f); dome.setTimeWarp(180.0f); dome.setDay(267); dome.setLatitude(-22.9f); dome.setLongitude(-47.083f); dome.setStandardMeridian(-45.0f); dome.setSunPosition(5.75f); // 5:45 am dome.setTurbidity(2.0f); dome.setSunEnabled(true); dome.setExposure(true, 18.0f); dome.setOvercastFactor(0.0f); dome.setGammaCorrection(2.5f); dome.setRootNode(rootNode); dome.setIntensity(1.0f); // setup a target to LightNode, if you dont want terrain with light's effect remove it. dome.setTarget(terrain); rootNode.attachChild(dome); } /** * Entry point */ public static void main(String[] args) { TestSkyDome app = new TestSkyDome(); app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG); app.start(); } }