diff --git a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java index ba142071f4..2d71d322a3 100644 --- a/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java +++ b/jme3-core/src/main/java/com/jme3/light/DirectionalLight.java @@ -59,6 +59,17 @@ public class DirectionalLight extends Light { * Creates a DirectionalLight */ public DirectionalLight() { + super(); + } + + /** + * Creates a DirectionalLight + * @param global if true, the light affects the entire tree from the root node, + * otherwise it only affects the children of the node in which it is attached. + */ + public DirectionalLight(boolean global) { + this(); + this.global = global; } /** @@ -66,9 +77,21 @@ public DirectionalLight() { * @param direction the light's direction */ public DirectionalLight(Vector3f direction) { + super(); setDirection(direction); } + /** + * Creates a DirectionalLight with the given direction + * @param direction the light's direction + * @param global if true, the light affects the entire tree from the root node, + * otherwise it only affects the children of the node in which it is attached. + */ + public DirectionalLight(Vector3f direction, boolean global) { + this(direction); + this.global = global; + } + /** * Creates a DirectionalLight with the given direction and the given color * @param direction the light's direction @@ -79,6 +102,19 @@ public DirectionalLight(Vector3f direction, ColorRGBA color) { setDirection(direction); } + /** + * Creates a DirectionalLight with the given direction and the given color + * @param direction the light's direction + * @param color the light's color + * @param global if true, the light affects the entire tree from the root node, + * otherwise it only affects the children of the node in which it is attached. + */ + public DirectionalLight(Vector3f direction, ColorRGBA color, boolean global) { + this(direction, color); + this.global = global; + } + + @Override public void computeLastDistance(Spatial owner) { // directional lights are after ambient lights diff --git a/jme3-core/src/main/java/com/jme3/light/Light.java b/jme3-core/src/main/java/com/jme3/light/Light.java index cebb4ae2a0..4694243822 100644 --- a/jme3-core/src/main/java/com/jme3/light/Light.java +++ b/jme3-core/src/main/java/com/jme3/light/Light.java @@ -116,6 +116,7 @@ public int getId(){ * The light name. */ protected String name; + protected boolean global = false; boolean frustumCheckNeeded = true; boolean intersectsFrustum = false; @@ -123,10 +124,30 @@ public int getId(){ protected Light() { } + + protected Light(boolean global) { + this.global = global; + } + + protected Light(ColorRGBA color, boolean global) { + setColor(color); + } + protected Light(ColorRGBA color) { setColor(color); } + + /** + * Returns true if this light affects the entire tree from the root node, + * otherwise returns false, meaning it only affects the children of the node + * in which it is attached. + * @return true if the light is global, otherwise false. + */ + public boolean isGlobal() { + return global; + } + /** * Returns the color of the light. * @@ -265,6 +286,7 @@ public void write(JmeExporter ex) throws IOException { oc.write(color, "color", null); oc.write(enabled, "enabled", true); oc.write(name, "name", null); + oc.write(global, "global", false); } @Override @@ -273,6 +295,7 @@ public void read(JmeImporter im) throws IOException { color = (ColorRGBA) ic.readSavable("color", null); enabled = ic.readBoolean("enabled", true); name = ic.readString("name", null); + global = ic.readBoolean("global", false); } /** diff --git a/jme3-core/src/main/java/com/jme3/light/LightList.java b/jme3-core/src/main/java/com/jme3/light/LightList.java index 97c4c29820..d6179dd986 100644 --- a/jme3-core/src/main/java/com/jme3/light/LightList.java +++ b/jme3-core/src/main/java/com/jme3/light/LightList.java @@ -38,6 +38,7 @@ import com.jme3.util.SortUtil; import java.io.IOException; import java.util.*; +import java.util.function.Predicate; /** * LightList is used internally by {@link Spatial}s to manage @@ -230,6 +231,20 @@ public void sort(boolean transformChanged) { * @param parent the parent's world-space LightList */ public void update(LightList local, LightList parent) { + update(local, parent, null); + } + + + /** + * Updates a "world-space" light list, using the spatial's local-space + * light list, its parent's world-space light list and an optional filter. + * + * @param local the local-space LightList (not null) + * @param parent the parent's world-space LightList + * @param filter an optional filter to apply to the lights + * (null means no filtering) + */ + public void update(LightList local, LightList parent, Predicate filter) { // clear the list as it will be reconstructed // using the arguments clear(); @@ -237,30 +252,32 @@ public void update(LightList local, LightList parent) { while (list.length <= local.listSize) { doubleSize(); } - - // add the lights from the local list - System.arraycopy(local.list, 0, list, 0, local.listSize); - for (int i = 0; i < local.listSize; i++) { -// list[i] = local.list[i]; - distToOwner[i] = Float.NEGATIVE_INFINITY; + + int localListSize = 0;// local.listSize; + for(int i=0;i children = n.getChildren(); + for (int i = 0; i < children.size(); i++) { + Spatial child = children.get(i); + if ((child.refreshFlags & RF_GLOBAL_LIGHTS)!= 0) { + findGlobalLights(child, list); + } + } + } + } + @Override public void updateGeometricState() { if (refreshFlags == 0) { @@ -249,6 +271,16 @@ public void updateGeometricState() { if ((refreshFlags & RF_LIGHTLIST) != 0) { updateWorldLightList(); } + + boolean updateGlobalLights = (refreshFlags & RF_GLOBAL_LIGHTS) != 0; + if (updateGlobalLights){ + // if root node, we collect the global lights + if (getParent() == null){ + findGlobalLights(this, worldLights); + } + refreshFlags &= ~RF_GLOBAL_LIGHTS; + } + if ((refreshFlags & RF_TRANSFORM) != 0) { // combine with parent transforms- same for all spatial // subclasses. @@ -266,6 +298,13 @@ public void updateGeometricState() { // NOTE 9/19/09 // Although it does save a round trip, for (Spatial child : children.getArray()) { + if (updateGlobalLights){ + // we might have new global lights coming from a different + // branch of the scene graph, so we need to propagate the + // refresh flags down to all the children of every branch + child.refreshFlags |= RF_LIGHTLIST; + child.refreshFlags |= RF_GLOBAL_LIGHTS; + } child.updateGeometricState(); } } diff --git a/jme3-core/src/main/java/com/jme3/scene/Spatial.java b/jme3-core/src/main/java/com/jme3/scene/Spatial.java index 2fe1775d3e..384bbf643a 100644 --- a/jme3-core/src/main/java/com/jme3/scene/Spatial.java +++ b/jme3-core/src/main/java/com/jme3/scene/Spatial.java @@ -124,7 +124,8 @@ public enum BatchHint { RF_BOUND = 0x02, RF_LIGHTLIST = 0x04, // changes in light lists RF_CHILD_LIGHTLIST = 0x08, // some child need geometry update - RF_MATPARAM_OVERRIDE = 0x10; + RF_MATPARAM_OVERRIDE = 0x10, + RF_GLOBAL_LIGHTS = 0x20; // world affecting lights changed protected CullHint cullHint = CullHint.Inherit; protected BatchHint batchHint = BatchHint.Inherit; @@ -282,18 +283,39 @@ protected void setTransformRefresh() { setBoundRefresh(); } + protected boolean hasGlobalLights(){ + for(int i = 0;i!l.isGlobal()); refreshFlags &= ~RF_LIGHTLIST; } else { assert (parent.refreshFlags & RF_LIGHTLIST) == 0 : "Illegal light list update. Problem spatial name: " + getName(); - worldLights.update(localLights, parent.worldLights); + worldLights.update(localLights, parent.worldLights, l->!l.isGlobal()); refreshFlags &= ~RF_LIGHTLIST; } } @@ -952,6 +974,9 @@ public void updateGeometricState() { if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) { updateMatParamOverrides(); } + if ((refreshFlags & RF_GLOBAL_LIGHTS) != 0) { + refreshFlags &= ~RF_GLOBAL_LIGHTS; + } assert refreshFlags == 0 : "Illegal refresh flags state: " + refreshFlags + " for spatial " + getName(); } @@ -1200,8 +1225,8 @@ public void addLight(Light light) { * @see Spatial#addLight(com.jme3.light.Light) */ public void removeLight(Light light) { - localLights.remove(light); setLightListRefresh(); + localLights.remove(light); } /** diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/LightsPunctualExtensionLoader.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/LightsPunctualExtensionLoader.java index 5b55c49b2e..9a9ecb7287 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/LightsPunctualExtensionLoader.java +++ b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/LightsPunctualExtensionLoader.java @@ -143,7 +143,7 @@ private SpotLight buildSpotLight(JsonObject obj) { outerConeAngle = FastMath.HALF_PI - 0.000001f; } - SpotLight spotLight = new SpotLight(); + SpotLight spotLight = new SpotLight(true); spotLight.setName(name); spotLight.setColor(color); spotLight.setSpotRange(range); @@ -167,7 +167,7 @@ private DirectionalLight buildDirectionalLight(JsonObject obj) { ColorRGBA color = obj.has("color") ? GltfUtils.getAsColor(obj, "color") : new ColorRGBA(ColorRGBA.White); color = lumensToColor(color, intensity); - DirectionalLight directionalLight = new DirectionalLight(); + DirectionalLight directionalLight = new DirectionalLight(true); directionalLight.setName(name); directionalLight.setColor(color); directionalLight.setDirection(Vector3f.UNIT_Z.negate()); @@ -189,7 +189,7 @@ private PointLight buildPointLight(JsonObject obj) { color = lumensToColor(color, intensity); float range = obj.has("range") ? obj.get("range").getAsFloat() : Float.POSITIVE_INFINITY; - PointLight pointLight = new PointLight(); + PointLight pointLight = new PointLight(true); pointLight.setName(name); pointLight.setColor(color); pointLight.setRadius(range);