blob: 0288bd0039a2698c6eb4a9ddb8eae67f2461b072 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
<<<<<<< HEAD
* Pierre Allard - initial API and implementation
*
=======
* Pierre Allard - initial API and implementation
*
>>>>>>> refs/heads/eclipse_pa
* SPDX-License-Identifier: EPL-1.0
*******************************************************************************/
package org.eclipse.apogy.core.environment.earth.surface.ui.jme3.scene_objects;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import javax.vecmath.Matrix4d;
import javax.vecmath.Vector3d;
import org.eclipse.apogy.common.topology.ApogyCommonTopologyFacade;
import org.eclipse.apogy.common.topology.TransformNode;
import org.eclipse.apogy.common.topology.ui.NodePresentation;
import org.eclipse.apogy.common.topology.ui.jme3.JME3Application;
import org.eclipse.apogy.common.topology.ui.jme3.JME3RenderEngineDelegate;
import org.eclipse.apogy.common.topology.ui.jme3.JME3Utilities;
import org.eclipse.apogy.common.topology.ui.jme3.scene_objects.DefaultJME3SceneObject;
import org.eclipse.apogy.core.environment.StarField;
import org.eclipse.apogy.core.environment.earth.surface.EarthSky;
import org.eclipse.apogy.core.environment.earth.surface.EarthSkyNode;
import org.eclipse.apogy.core.environment.earth.surface.ui.EarthSurfaceUIUtilities;
import org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator;
import org.eclipse.apogy.core.environment.earth.surface.ui.jme3.EarthSurfaceEnvironmentJMEConstants;
import org.eclipse.apogy.core.environment.earth.surface.ui.jme3.EnvironmentUIJME3Utilities;
import org.eclipse.apogy.core.environment.earth.surface.ui.jme3.preferences.ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants;
import org.eclipse.apogy.core.environment.earth.surface.ui.scene_objects.EarthSkySceneObject;
import org.eclipse.apogy.core.environment.ui.StarFieldPresentation;
import org.eclipse.apogy.core.environment.ui.scene_objects.StarFieldSceneObject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jme3.asset.AssetManager;
import com.jme3.asset.plugins.FileLocator;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.shadow.EdgeFilteringMode;
public class EarthSkyNodeJME3Object extends DefaultJME3SceneObject<EarthSkyNode>
implements IPropertyChangeListener, EarthSkySceneObject {
private static final Logger Logger = LoggerFactory.getLogger(EarthSkyNodeJME3Object.class);
private Adapter sunAdapter;
private Adapter moonAdapter;
private EarthSky earthSky;
private AssetManager assetManager;
private static ColorRGBA SUN_SPHERE_COLOR = new ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f); // new ColorRGBA(0.976470588f,
// 0.968627451f, 0.6f, 1.0f);
private static ColorRGBA MOON_SPHERE_COLOR = new ColorRGBA(1f, 1f, 1f, 0.2f);
// Horizon
private boolean horizonVisible = true;
// Sun
private Node sunTransform = null;
private Geometry sunSphere;
private DirectionalLight sunLight;
private boolean sunVisible = false;
private boolean sunShadowsEnabled = true;
private boolean sunCastingShadows = false;
// Moon
private Node moonTransform = null;
private Geometry moonSphere;
private DirectionalLight moonLight;
private boolean moonVisible = false;
private boolean moonShadowsEnabled = true;
private boolean moonCastingShadows = false;
@SuppressWarnings("unused")
private boolean enableBloom = true;
private FilterPostProcessor bloomFilterPostProcessor;
private BloomFilter bloomFilter;
private int shadowMapSize = 2048;
private FilterPostProcessor shadowsFilterPostProcessor;
private DirectionalLightShadowRenderer directionalLightShadowRenderer;
private DirectionalLightShadowFilter directionalLightShadowFilter;
private Spatial sky = null;
private float alpha = 1.0f;
public EarthSkyNodeJME3Object(EarthSkyNode node, JME3RenderEngineDelegate jme3RenderEngineDelegate) {
super(node, jme3RenderEngineDelegate);
this.assetManager = this.jme3Application.getAssetManager();
this.assetManager.registerLocator("/", FileLocator.class);
this.earthSky = (EarthSky) node.getSky();
Job job = new Job("EarthSkyNodeJME3Object initialize.") {
@Override
protected IStatus run(IProgressMonitor monitor) {
EarthSkyNodeJME3Object.this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
// Attaches the Sun.
attachSun();
// Attaches the Moon.
attachMoon();
// Updates Geometry.
requestUpdate();
// Blooming
enableBloom(org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator.getDefault()
.getPreferenceStore().getBoolean(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_BLOOM_ENABLED_ID));
// Shadow map.
setShadowMapSize(org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator.getDefault()
.getPreferenceStore()
.getInt(ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_SHADOW_MAP_SIZE_ID));
// Shadows from Sun
setSunShadowsEnabled(org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator
.getDefault().getPreferenceStore().getBoolean(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_SUN_CAST_SHADOWS_ENABLED_ID));
// Shadows from Moon.
setMoonShadowsEnabled(org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator
.getDefault().getPreferenceStore().getBoolean(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_MOON_CAST_SHADOWS_ENABLED_ID));
// Updates Visibility and Shadows.
updateSunMoonVisibilityAndShadows();
// Listens for changes on the EarthSky.
EarthSkyNodeJME3Object.this.earthSky.getSun().getParent().eAdapters().add(getSunAdapter());
EarthSkyNodeJME3Object.this.earthSky.getMoon().getParent().eAdapters().add(getMoonAdapter());
EarthSkyNodeJME3Object.this.sky = EnvironmentUIJME3Utilities
.createSky(EarthSkyNodeJME3Object.this.assetManager, "Textures/ClearSky/");
EarthSkyNodeJME3Object.this.jme3Application.getSceneRoot()
.attachChild(EarthSkyNodeJME3Object.this.sky);
return null;
}
});
return Status.OK_STATUS;
}
};
job.schedule();
Activator.getDefault().getPreferenceStore().addPropertyChangeListener(this);
}
@Override
public void updateGeometry(float tpf) {
// Updates the Sun and Moon visibility and Shadows Status.
updateSunMoonVisibilityAndShadows();
// Updates the Sun position
updateSun();
// Updates the moon Position.
updateMoon();
}
@Override
public void dispose() {
// Disable Shadows.
setLightSourceCreatingShadow(null);
// Disable bloom.
if (this.jme3Application.getViewPort().getProcessors().contains(getBloomFilterPostProcessor())) {
this.jme3Application.getViewPort().removeProcessor(getBloomFilterPostProcessor());
}
// Detach Sun light
if (this.sunLight != null) {
this.jme3Application.getRootNode().removeLight(this.sunLight);
}
// Detach Moon light
if (this.moonLight != null) {
this.jme3Application.getRootNode().removeLight(this.moonLight);
}
super.dispose();
}
@Override
public void propertyChange(final PropertyChangeEvent event) {
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
if (event.getProperty().compareTo(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_BLOOM_ENABLED_ID) == 0) {
boolean value = org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator.getDefault()
.getPreferenceStore().getBoolean(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_BLOOM_ENABLED_ID);
enableBloom(value);
updateSun();
} else if (event.getProperty().compareTo(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_SHADOW_MAP_SIZE_ID) == 0) {
setShadowMapSize(org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator.getDefault()
.getPreferenceStore()
.getInt(ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_SHADOW_MAP_SIZE_ID));
} else if (event.getProperty().compareTo(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_SUN_CAST_SHADOWS_ENABLED_ID) == 0) {
boolean value = org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator.getDefault()
.getPreferenceStore().getBoolean(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_SUN_CAST_SHADOWS_ENABLED_ID);
setSunShadowsEnabled(value);
} else if (event.getProperty().compareTo(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_MOON_CAST_SHADOWS_ENABLED_ID) == 0) {
boolean value = org.eclipse.apogy.core.environment.earth.surface.ui.jme3.Activator.getDefault()
.getPreferenceStore().getBoolean(
ApogyEarthSurfaceEnvironmentUIJME3PreferencesConstants.DEFAULT_MOON_CAST_SHADOWS_ENABLED_ID);
setMoonShadowsEnabled(value);
}
return null;
}
});
}
@Override
public List<Geometry> getGeometries() {
List<Geometry> geometries = new ArrayList<Geometry>();
if (this.sunSphere != null)
geometries.add(this.sunSphere);
if (this.moonSphere != null)
geometries.add(this.moonSphere);
geometries.addAll(super.getGeometries());
return geometries;
}
@Override
public void setHorizonVisible(final boolean newHorizonVisible) {
// TODO this.horizonVisible = newHorizonVisible;
this.horizonVisible = true;
Logger.info("Setting Horizon visibility to <" + newHorizonVisible + ">.");
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
if (getAttachmentNode() != null) {
if (EarthSkyNodeJME3Object.this.horizonVisible) {
// TODO getAttachmentNode().attachChild(getHorizon());
} else {
// TODO getAttachmentNode().detachChild(getHorizon());
}
} else {
Logger.error("Failed to set Horizon visibility to <" + newHorizonVisible + ">.");
}
return null;
}
});
}
/**
* Enables or disable produced by the Sun.
*
* @param newSunShadowsEnabled Enable flag for Sun shadows.
*/
public void setSunShadowsEnabled(boolean newSunShadowsEnabled) {
this.sunShadowsEnabled = newSunShadowsEnabled;
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
updateSun();
updateMoon();
return null;
}
});
}
/**
* Returns whether or not shadows produced by the Sun are enabled.
*
* @return True if Sun shadows are enabled, false otherwise.
*/
public boolean areSunShadowsEnabled() {
return this.sunShadowsEnabled;
}
/**
* Enables or disable produced by the Moon.
*
* @param newMoonShadowsEnabled Enable flag for Moon shadows.
*/
public void setMoonShadowsEnabled(boolean newMoonShadowsEnabled) {
this.moonShadowsEnabled = newMoonShadowsEnabled;
this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
updateSun();
updateMoon();
return null;
}
});
}
/**
* Returns whether or not shadows produced by the Moon are enabled.
*
* @return True if Moon shadows are enabled, false otherwise.
*/
public boolean areMoonShadowsEnabled() {
return this.moonShadowsEnabled;
}
/**
* Returns whether or not the Sun is producing shadows.
*
* @return True if the Sun produces shadows, false otherwise.
*/
private boolean isSunCastingShadows() {
return this.sunCastingShadows;
}
/**
* Return weather the Sun is currently visible.
*
* @return True if the Sun is visible, false otherwise.
*/
private boolean isSunVisible() {
return this.sunVisible;
}
/**
* Return weather the Moon is currently visible.
*
* @return True if the Moon is visible, false otherwise.
*/
private boolean isMoonVisible() {
return this.moonVisible;
}
/**
* Returns whether or not the Moon is producing shadows.
*
* @return True if the Moon produces shadows, false otherwise.
*/
private boolean isMoonCastingShadows() {
return this.moonCastingShadows;
}
/**
* Updates the Sun and Moon visiblity based on the shadows settings and their
* elevation.
*/
private void updateSunMoonVisibilityAndShadows() {
// Computes Sun Visibility
TransformNode sunTranformNode = (TransformNode) this.earthSky.getSun().getParent();
Matrix4d mSun = ApogyCommonTopologyFacade.INSTANCE.expressNodeInRootFrame(sunTranformNode);
Vector3d vSun = new Vector3d();
mSun.get(vSun);
vSun.normalize();
double rSun = vSun.length();
double sunAltitude = Math.asin(vSun.z / rSun);
// Sun is visible is above the horizon.
this.sunVisible = (sunAltitude > 0);
// Sun cast shadows only if enabled, and the Sun is above horizon
this.sunCastingShadows = areSunShadowsEnabled() && this.sunVisible;
// Computes Moon Visibility
TransformNode moonTranformNode = (TransformNode) this.earthSky.getMoon().getParent();
Matrix4d mMoon = ApogyCommonTopologyFacade.INSTANCE.expressNodeInRootFrame(moonTranformNode);
Vector3d vMoon = new Vector3d();
mMoon.get(vMoon);
vMoon.normalize();
double rMoon = vMoon.length();
double moonAltitude = Math.asin(vMoon.z / rMoon);
// Moon is visible is above the horizon.
this.moonVisible = (moonAltitude > 0);
// Moon cast shadows only if enabled, and the Sun is not visible and the Moon is
// above horizon
this.moonCastingShadows = areMoonShadowsEnabled() && this.moonVisible && !this.sunVisible;
// Updates the light creating shadows.
if (isSunCastingShadows()) {
setLightSourceCreatingShadow(getSunLight());
} else if (isMoonCastingShadows()) {
setLightSourceCreatingShadow(getMoonLight());
} else {
setLightSourceCreatingShadow(null);
}
}
/**
* Selects which light (Sun, Moon, or none) is used to cast shadows.
*
* @param light The light that cast shadows, Can be null.
*/
private void setLightSourceCreatingShadow(DirectionalLight light) {
if (this.jme3Application != null) {
if (light != null) {
if (!this.jme3Application.getViewPort().getProcessors().contains(getDirectionalLightShadowRenderer())) {
this.jme3Application.getViewPort().addProcessor(getDirectionalLightShadowRenderer());
}
if (!this.jme3Application.getViewPort().getProcessors().contains(getSunFilterPostProcessor())) {
this.jme3Application.getViewPort().addProcessor(getSunFilterPostProcessor());
}
getDirectionalLightShadowRenderer().setLight(light);
getDirectionalLightShadowFilter().setLight(light);
} else {
// Removes Shadow Processors.
if (this.jme3Application.getViewPort().getProcessors().contains(getDirectionalLightShadowRenderer())) {
this.jme3Application.getViewPort().removeProcessor(getDirectionalLightShadowRenderer());
}
if (this.jme3Application.getViewPort().getProcessors().contains(getSunFilterPostProcessor())) {
this.jme3Application.getViewPort().removeProcessor(getSunFilterPostProcessor());
}
}
}
}
private void enableBloom(boolean newEnableBloom) {
this.enableBloom = newEnableBloom;
if (this.jme3Application != null) {
if (newEnableBloom) {
if (!this.jme3Application.getViewPort().getProcessors().contains(getBloomFilterPostProcessor())) {
this.jme3Application.getViewPort().addProcessor(getBloomFilterPostProcessor());
}
Logger.info("Enabled Bloom.");
} else {
if (this.jme3Application.getViewPort().getProcessors().contains(getBloomFilterPostProcessor())) {
this.jme3Application.getViewPort().removeProcessor(getBloomFilterPostProcessor());
}
Logger.info("Disabled Bloom.");
}
}
}
private FilterPostProcessor getBloomFilterPostProcessor() {
if (this.bloomFilterPostProcessor == null) {
this.bloomFilterPostProcessor = new FilterPostProcessor(this.assetManager);
this.bloomFilterPostProcessor.addFilter(getBloomFilter());
}
return this.bloomFilterPostProcessor;
}
/**
* Return the BloomFilter used to give the Sun and Moon an aura.
*
* @return The BloomFilter.
*/
private BloomFilter getBloomFilter() {
if (this.bloomFilter == null) {
this.bloomFilter = new BloomFilter(BloomFilter.GlowMode.Objects);
this.bloomFilter.setDownSamplingFactor(2);
this.bloomFilter.setBlurScale(1.5f);
this.bloomFilter.setExposurePower(3.30f);
this.bloomFilter.setExposureCutOff(0.2f);
this.bloomFilter.setBloomIntensity(20f);
}
return this.bloomFilter;
}
private void setShadowMapSize(int newShadowMapSize) {
this.shadowMapSize = newShadowMapSize;
if (this.jme3Application != null) {
if (this.jme3Application.getViewPort().getProcessors().contains(getDirectionalLightShadowRenderer())) {
this.jme3Application.getViewPort().removeProcessor(getDirectionalLightShadowRenderer());
}
if (this.jme3Application.getViewPort().getProcessors().contains(getSunFilterPostProcessor())) {
this.jme3Application.getViewPort().removeProcessor(getSunFilterPostProcessor());
}
this.shadowsFilterPostProcessor = null;
this.directionalLightShadowRenderer = null;
this.directionalLightShadowFilter = null;
// Force shadow filter to be reloaded if required.
updateSunMoonVisibilityAndShadows();
}
Logger.info("Shadow Map Size set to <" + newShadowMapSize + ">.");
}
/**
* Gets the DirectionalLightShadowRenderer used to cast shadow from the Sun or
* the Moon.
*
* @return The DirectionalLightShadowRenderer.
*/
private DirectionalLightShadowRenderer getDirectionalLightShadowRenderer() {
if (this.directionalLightShadowRenderer == null) {
// Shadows from Sun
this.directionalLightShadowRenderer = new DirectionalLightShadowRenderer(this.assetManager,
this.shadowMapSize, 3);
this.directionalLightShadowRenderer.setLight(getSunLight());
this.directionalLightShadowRenderer.setLambda(0.55f);
this.directionalLightShadowRenderer.setShadowIntensity(0.6f);
this.directionalLightShadowRenderer.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
this.directionalLightShadowRenderer.setEnabledStabilization(true);
}
return this.directionalLightShadowRenderer;
}
private FilterPostProcessor getSunFilterPostProcessor() {
if (this.shadowsFilterPostProcessor == null) {
this.shadowsFilterPostProcessor = new FilterPostProcessor(this.assetManager);
this.shadowsFilterPostProcessor.addFilter(getDirectionalLightShadowFilter());
}
return this.shadowsFilterPostProcessor;
}
/**
* Gets the DirectionalLightShadowFilter used to cast shadow from the Sun or the
* Moon.
*
* @return The DirectionalLightShadowFilter.
*/
private DirectionalLightShadowFilter getDirectionalLightShadowFilter() {
if (this.directionalLightShadowFilter == null) {
this.directionalLightShadowFilter = new DirectionalLightShadowFilter(this.assetManager, this.shadowMapSize,
3);
this.directionalLightShadowFilter.setLight(getSunLight());
this.directionalLightShadowFilter.setLambda(0.55f);
this.directionalLightShadowFilter.setShadowIntensity(0.6f);
this.directionalLightShadowFilter.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
this.directionalLightShadowFilter.setEnabled(false);
}
return this.directionalLightShadowFilter;
}
/*--------------------------------------------------------------------------------------------------------------*
* Sun
* --------------------------------------------------------------------------------------------------------------*/
private void attachSun() {
getAttachmentNode().attachChild(getSunTransform());
getSunTransform().attachChild(getSunSphere());
if (this.jme3Application != null) {
this.jme3Application.getRootNode().addLight(getSunLight());
}
}
private void updateSun() {
// Updates first visibility and shadow casting.
updateSunMoonVisibilityAndShadows();
TransformNode sunTranformNode = (TransformNode) this.earthSky.getSun().getParent();
Vector3d v = new Vector3d(sunTranformNode.getPosition().asTuple3d());
v.normalize();
// Update the sun light
updateSunLight();
// Updates sun position.
v.scale(EarthSurfaceEnvironmentJMEConstants.SUN_AND_MOON_RADIUS);
getSunTransform().setLocalTranslation((float) v.x, (float) v.y, (float) v.z);
// Computes the atmosphere and stars transparency.
this.alpha = (float) (1.0 - EarthSurfaceUIUtilities.INSTANCE
.getSkyTransparency(this.earthSky.getSunHorizontalCoordinates().getAltitude()));
;
Material mat = ((Geometry) this.sky).getMaterial();
mat.setFloat("Alpha", this.alpha);
mat.setVector3("SunPosition", getSunTransform().getWorldTranslation());
StarFieldSceneObject starFieldSceneObject = resolveStarFieldSceneObject();
if (starFieldSceneObject != null) {
starFieldSceneObject.setTransparency(this.alpha);
}
}
private StarFieldSceneObject resolveStarFieldSceneObject() {
if (this.earthSky != null) {
StarField node = this.earthSky.getStarField();
if (node != null) {
NodePresentation nodePresentation = org.eclipse.apogy.common.topology.ui.Activator
.getTopologyPresentationRegistry().getPresentationNode(node);
if (nodePresentation instanceof StarFieldPresentation) {
if (nodePresentation.getSceneObject() instanceof StarFieldSceneObject) {
return (StarFieldSceneObject) nodePresentation.getSceneObject();
}
}
}
}
return null;
}
private Node getSunTransform() {
if (this.sunTransform == null) {
this.sunTransform = new Node("Sun Transform");
}
return this.sunTransform;
}
private Geometry getSunSphere() {
if (this.sunSphere == null) {
// float sunRadius = (float)
// (EarthSurfaceEnvironmentJMEConstants.SUN_AND_MOON_RADIUS *
// Math.tan(earthSky.getSunAngularDiameter() / 2.0) );
// The sun does not have to be shown anymore, it is now very small.
float sunRadius = 0.01f;
Sphere sphere = new Sphere(10, 36, sunRadius);
Material mat = new Material(this.assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("GlowColor", SUN_SPHERE_COLOR.clone());
mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
this.sunSphere = new Geometry("Sun", sphere);
this.sunSphere.setMaterial(mat);
this.sunSphere.setShadowMode(ShadowMode.Off);
}
return this.sunSphere;
}
private DirectionalLight getSunLight() {
if (this.sunLight == null) {
TransformNode sunTransformNode = (TransformNode) this.earthSky.getSun().getParent();
float x = (float) sunTransformNode.getPosition().getX();
float y = (float) sunTransformNode.getPosition().getY();
float z = (float) sunTransformNode.getPosition().getZ();
Vector3f direction = new Vector3f(x, y, z);
this.sunLight = new DirectionalLight();
this.sunLight.setName("Sun");
this.sunLight.setDirection(direction.normalize());
this.sunLight.setColor(getSunColor(direction));
}
return this.sunLight;
}
private void updateSunLight() {
TransformNode sunTranformNode = (TransformNode) this.earthSky.getSun().getParent();
Matrix4d m = ApogyCommonTopologyFacade.INSTANCE.expressNodeInRootFrame(sunTranformNode);
Vector3d v = new Vector3d();
m.get(v);
v.normalize();
Vector3f direction = new Vector3f((float) v.x, (float) v.y, (float) v.z);
// Updates color.
ColorRGBA color = getSunColor(direction);
getSunLight().setColor(color);
// Updates direction.
direction.negateLocal();
getSunLight().setDirection(direction.normalize());
// Update Glow Color.
Material mat = getSunSphere().getMaterial().clone();
mat.setColor("GlowColor", color.clone());
getSunSphere().setMaterial(mat);
}
private ColorRGBA getSunColor(Vector3f sunPosition) {
double r = sunPosition.length();
double sunAltitude = Math.asin(sunPosition.z / r);
ColorRGBA sunColor = null;
if (isSunVisible()) {
sunColor = JME3Utilities.convertToColorRGBA(EarthSurfaceUIUtilities.INSTANCE.getSunLightColor(sunAltitude));
} else {
sunColor = ColorRGBA.BlackNoAlpha;
}
return sunColor;
}
/*--------------------------------------------------------------------------------------------------------------*
* Moon
* --------------------------------------------------------------------------------------------------------------*/
private void attachMoon() {
getAttachmentNode().attachChild(getMoonTransform());
getMoonTransform().attachChild(getMoonSphere());
if (this.jme3Application instanceof JME3Application) {
JME3Application viewer = this.jme3Application;
viewer.getRootNode().addLight(getMoonLight());
}
}
private void updateMoon() {
// Updates first visibility and shadow casting.
updateSunMoonVisibilityAndShadows();
// Updates the moon transform.
TransformNode moonTranformNode = (TransformNode) this.earthSky.getMoon().getParent();
Vector3d v = new Vector3d(moonTranformNode.getPosition().asTuple3d());
v.normalize();
v.scale(EarthSurfaceEnvironmentJMEConstants.SUN_AND_MOON_RADIUS);
getMoonTransform().setLocalTranslation((float) v.x, (float) v.y, (float) v.z);
// Update the moon light
updateMoonLight();
}
private void updateMoonLight() {
TransformNode moonTranformNode = (TransformNode) this.earthSky.getMoon().getParent();
Matrix4d m = ApogyCommonTopologyFacade.INSTANCE.expressNodeInRootFrame(moonTranformNode);
Vector3d v = new Vector3d();
m.get(v);
v.normalize();
Vector3f direction = new Vector3f((float) v.x, (float) v.y, (float) v.z);
// Updates color.
ColorRGBA color = getMoonColor(direction);
getMoonLight().setColor(color);
// Update Glow Color.
Material mat = getSunSphere().getMaterial().clone();
mat.setColor("GlowColor", color.clone());
getMoonSphere().setMaterial(mat);
// Updates direction.
direction.negateLocal();
getMoonLight().setDirection(direction.normalize());
}
private DirectionalLight getMoonLight() {
if (this.moonLight == null) {
TransformNode sunTransformNode = (TransformNode) this.earthSky.getMoon().getParent();
float x = (float) sunTransformNode.getPosition().getX();
float y = (float) sunTransformNode.getPosition().getY();
float z = (float) sunTransformNode.getPosition().getZ();
Vector3f direction = new Vector3f(x, y, z);
this.moonLight = new DirectionalLight();
this.moonLight.setName("Moon");
this.moonLight.setDirection(direction.normalize());
this.moonLight.setColor(getMoonColor(direction));
}
return this.moonLight;
}
private ColorRGBA getMoonColor(Vector3f moonPosition) {
ColorRGBA moonColor = null;
// If the sun is up, turn off the moon
if (this.sunVisible) {
moonColor = new ColorRGBA(0, 0, 0, 0);
} else {
double rMoon = moonPosition.length();
double moonAltitude = Math.asin(moonPosition.z / rMoon);
if (isMoonVisible()) {
moonColor = JME3Utilities
.convertToColorRGBA(EarthSurfaceUIUtilities.INSTANCE.getSunLightColor(moonAltitude));
} else {
moonColor = ColorRGBA.BlackNoAlpha;
}
}
return moonColor;
}
private Node getMoonTransform() {
if (this.moonTransform == null) {
this.moonTransform = new Node("Moon Transform");
}
return this.moonTransform;
}
private Geometry getMoonSphere() {
if (this.moonSphere == null) {
float moonRadius = (float) (EarthSurfaceEnvironmentJMEConstants.SUN_AND_MOON_RADIUS
* Math.tan(this.earthSky.getMoonAngularDiameter() / 2.0));
Sphere sphere = new Sphere(10, 36, moonRadius);
Material mat = new Material(this.assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("GlowColor", MOON_SPHERE_COLOR.clone());
mat.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
this.moonSphere = new Geometry("Moon", sphere);
this.moonSphere.setMaterial(mat);
this.moonSphere.setShadowMode(ShadowMode.Off);
}
return this.moonSphere;
}
/*--------------------------------------------------------------------------------------------------------------*
* Adapters
* --------------------------------------------------------------------------------------------------------------*/
private Adapter getSunAdapter() {
if (this.sunAdapter == null) {
this.sunAdapter = new AdapterImpl() {
@Override
public void notifyChanged(org.eclipse.emf.common.notify.Notification msg) {
EarthSkyNodeJME3Object.this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
updateSun();
return null;
}
});
}
};
}
return this.sunAdapter;
}
private Adapter getMoonAdapter() {
if (this.moonAdapter == null) {
this.moonAdapter = new AdapterImpl() {
@Override
public void notifyChanged(org.eclipse.emf.common.notify.Notification msg) {
EarthSkyNodeJME3Object.this.jme3Application.enqueue(new Callable<Object>() {
@Override
public Object call() throws Exception {
updateMoon();
return null;
}
});
}
};
}
return this.moonAdapter;
}
}