diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5fa4b1f72..ebecab28b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ First and foremost, you have to familiarize yourself with Git & GitHub. Dig thro ## Communication -Communication always comes first. **All** code changes and other contributions should start with the [forum](http://hub.jmonkeyengine.org/forum/). Make a thread to explain your change and show us the important bits of your code. If the code is too long to be posted within the forum’s code tags, please paste your code in a Gist or pastebin and link to the submission in your thread. You are required to register on our website in order to create threads. +Communication always comes first. **All** code changes and other contributions should start with the [forum](http://hub.jmonkeyengine.org/). Make a thread to explain your change and show us the important bits of your code. If the code is too long to be posted within the forum’s code tags, please paste your code in a Gist or pastebin and link to the submission in your thread. You are required to register on our website in order to create threads. (We do support login via GitHub though). ### New Contributors @@ -23,7 +23,7 @@ To import the local repository as a project follow these steps: 2. Navigate to the project directory in command line and execute command 'gradle eclipse'. This will load all the dependancies for eclipse. 3. In Eclipse, add the repository as an existing Java Project. - +p.s. We will try hold ourselves to a [certain standard](http://www.defmacro.org/2013/04/03/issue-etiquette.html) when it comes to GitHub etiquette. If at any point we fail to uphold this standard, let us know. #### Core Contributors diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java index ff81b83aa..0f011f8d7 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java @@ -405,8 +405,11 @@ public class PhysicsSpace { ((PhysicsControl) obj).setPhysicsSpace(this); } else if (obj instanceof Spatial) { Spatial node = (Spatial) obj; - PhysicsControl control = node.getControl(PhysicsControl.class); - control.setPhysicsSpace(this); + for (int i = 0; i < node.getNumControls(); i++) { + if (node.getControl(i) instanceof PhysicsControl) { + add(((PhysicsControl) node.getControl(i))); + } + } } else if (obj instanceof PhysicsCollisionObject) { addCollisionObject((PhysicsCollisionObject) obj); } else if (obj instanceof PhysicsJoint) { @@ -438,7 +441,12 @@ public class PhysicsSpace { if (obj instanceof PhysicsControl) { ((PhysicsControl) obj).setPhysicsSpace(null); } else if (obj instanceof Spatial) { - remove(((Spatial) obj).getControl(PhysicsControl.class)); + Spatial node = (Spatial) obj; + for (int i = 0; i < node.getNumControls(); i++) { + if (node.getControl(i) instanceof PhysicsControl) { + remove(((PhysicsControl) node.getControl(i))); + } + } } else if (obj instanceof PhysicsCollisionObject) { removeCollisionObject((PhysicsCollisionObject) obj); } else if (obj instanceof PhysicsJoint) { diff --git a/jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java b/jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java index bfabbee9b..50ace1dab 100644 --- a/jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java +++ b/jme3-core/src/main/java/com/jme3/app/BasicProfilerState.java @@ -61,7 +61,7 @@ public class BasicProfilerState extends BaseAppState { private Geometry background; private float scale = 2; - private ProfilerKeyListener keyListener = new ProfilerKeyListener(); + private final ProfilerKeyListener keyListener = new ProfilerKeyListener(); public BasicProfilerState() { this(false); @@ -84,6 +84,7 @@ public class BasicProfilerState extends BaseAppState { * Sets the vertical scale of the visualization where * each unit is a millisecond. Defaults to 2, ie: a * single millisecond stretches two pixels high. + * @param scale the scale */ public void setGraphScale( float scale ) { if( this.scale == scale ) { @@ -101,6 +102,7 @@ public class BasicProfilerState extends BaseAppState { /** * Sets the number frames displayed and tracked. + * @param count the number of frames */ public void setFrameCount( int count ) { if( profiler.getFrameCount() == count ) { @@ -209,7 +211,7 @@ public class BasicProfilerState extends BaseAppState { } @Override - protected void enable() { + protected void onEnable() { // Set the number of visible frames to the current width of the screen setFrameCount(getApplication().getCamera().getWidth()); @@ -221,7 +223,7 @@ public class BasicProfilerState extends BaseAppState { } @Override - protected void disable() { + protected void onDisable() { getApplication().setAppProfiler(null); graph.removeFromParent(); background.removeFromParent(); @@ -229,6 +231,7 @@ public class BasicProfilerState extends BaseAppState { private class ProfilerKeyListener implements ActionListener { + @Override public void onAction(String name, boolean value, float tpf) { if (!value) { return; diff --git a/jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java b/jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java index 7d0110b96..a8a80f19e 100644 --- a/jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java +++ b/jme3-core/src/main/java/com/jme3/app/state/BaseAppState.java @@ -43,7 +43,7 @@ import java.util.logging.Logger; * A base app state implementation the provides more built-in * management convenience than AbstractAppState, including methods * for enable/disable/initialize state management. - * The abstract enable() and disable() methods are called + * The abstract onEnable() and onDisable() methods are called * appropriately during initialize(), terminate(), or setEnabled() * depending on the mutual state of "initialized" and "enabled". * @@ -52,20 +52,20 @@ import java.util.logging.Logger; * app state is attached. This is useful for resources that might * be expensive to create or load.
* - *enable()/disable() can be used for managing things that + *
onEnable()/onDisable() can be used for managing things that * should only exist while the state is enabled. Prime examples * would be scene graph attachment or input listener attachment.
* - *The base class logic is such that disable() will always be called + *
The base class logic is such that onDisable() will always be called * before cleanup() if the state is enabled. Likewise, enable() * will always be called after initialize() if the state is enable(). - * enable()/disable() are also called appropriate when setEnabled() + * onEnable()/onDisable() are also called appropriate when setEnabled() * is called that changes the enabled state AND if the state is attached. - * In other words, enable()/disable() are only ever called on an already + * In other words, onEnable()/onDisable() are only ever called on an already * attached state.
* *It is technically safe to do all initialization and cleanup in - * the enable()/disable() methods. Choosing to use initialize() + * the onEnable()/onDisable() methods. Choosing to use initialize() * and cleanup() for this is a matter of performance specifics for the * implementor.
* @@ -81,15 +81,17 @@ public abstract class BaseAppState implements AppState { /** * Called during initialization once the app state is - * attached and before enable() is called. + * attached and before onEnable() is called. + * @param app the application */ protected abstract void initialize( Application app ); /** * Called after the app state is detached or during * application shutdown if the state is still attached. - * disable() is called before this cleanup() method if + * onDisable() is called before this cleanup() method if * the state is enabled at the time of cleanup. + * @param app the application */ protected abstract void cleanup( Application app ); @@ -98,21 +100,22 @@ public abstract class BaseAppState implements AppState { * and isEnabled() is true or when the setEnabled() status * changes after the state is attached. */ - protected abstract void enable(); + protected abstract void onEnable(); /** * Called when the state was previously enabled but is * now disabled either because setEnabled(false) was called * or the state is being cleaned up. */ - protected abstract void disable(); + protected abstract void onDisable(); /** * Do not call directly: Called by the state manager to initialize this * state post-attachment. - * This implementation calls initialize(app) and then enable() if the + * This implementation calls initialize(app) and then onEnable() if the * state is enabled. */ + @Override public final void initialize( AppStateManager stateManager, Application app ) { log.log(Level.FINEST, "initialize():{0}", this); @@ -120,11 +123,12 @@ public abstract class BaseAppState implements AppState { initialized = true; initialize(app); if( isEnabled() ) { - log.log(Level.FINEST, "enable():{0}", this); - enable(); + log.log(Level.FINEST, "onEnable():{0}", this); + onEnable(); } } + @Override public final boolean isInitialized() { return initialized; } @@ -141,6 +145,7 @@ public abstract class BaseAppState implements AppState { return getStateManager().getState(type); } + @Override public final void setEnabled( boolean enabled ) { if( this.enabled == enabled ) @@ -149,45 +154,52 @@ public abstract class BaseAppState implements AppState { if( !isInitialized() ) return; if( enabled ) { - log.log(Level.FINEST, "enable():{0}", this); - enable(); + log.log(Level.FINEST, "onEnable():{0}", this); + onEnable(); } else { - log.log(Level.FINEST, "disable():{0}", this); - disable(); + log.log(Level.FINEST, "onDisable():{0}", this); + onDisable(); } } + @Override public final boolean isEnabled() { return enabled; } + @Override public void stateAttached( AppStateManager stateManager ) { } + @Override public void stateDetached( AppStateManager stateManager ) { } + @Override public void update( float tpf ) { } + @Override public void render( RenderManager rm ) { } + @Override public void postRender() { } /** * Do not call directly: Called by the state manager to terminate this * state post-detachment or during state manager termination. - * This implementation calls disable() if the state is enabled and + * This implementation calls onDisable() if the state is enabled and * then cleanup(app). */ + @Override public final void cleanup() { log.log(Level.FINEST, "cleanup():{0}", this); if( isEnabled() ) { - log.log(Level.FINEST, "disable():{0}", this); - disable(); + log.log(Level.FINEST, "onDisable():{0}", this); + onDisable(); } cleanup(app); initialized = false; diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java index 71f4d9422..bdba19fe3 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java @@ -761,6 +761,35 @@ public class BoundingBox extends BoundingVolume { } } + private int collideWithRay(Ray ray) { + TempVars vars = TempVars.get(); + try { + Vector3f diff = vars.vect1.set(ray.origin).subtractLocal(center); + Vector3f direction = vars.vect2.set(ray.direction); + + //float[] t = {0f, Float.POSITIVE_INFINITY}; + float[] t = vars.fWdU; // use one of the tempvars arrays + t[0] = 0; + t[1] = Float.POSITIVE_INFINITY; + + float saveT0 = t[0], saveT1 = t[1]; + boolean notEntirelyClipped = clip(+direction.x, -diff.x - xExtent, t) + && clip(-direction.x, +diff.x - xExtent, t) + && clip(+direction.y, -diff.y - yExtent, t) + && clip(-direction.y, +diff.y - yExtent, t) + && clip(+direction.z, -diff.z - zExtent, t) + && clip(-direction.z, +diff.z - zExtent, t); + + if (notEntirelyClipped && (t[0] != saveT0 || t[1] != saveT1)) { + if (t[1] > t[0]) return 2; + else return 1; + } + return 0; + } finally { + vars.release(); + } + } + public int collideWith(Collidable other, CollisionResults results) { if (other instanceof Ray) { Ray ray = (Ray) other; @@ -777,6 +806,22 @@ public class BoundingBox extends BoundingVolume { throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName()); } } + + @Override + public int collideWith(Collidable other) { + if (other instanceof Ray) { + Ray ray = (Ray) other; + return collideWithRay(ray); + } else if (other instanceof Triangle) { + Triangle t = (Triangle) other; + if (intersects(t.get1(), t.get2(), t.get3())) { + return 1; + } + return 0; + } else { + throw new UnsupportedCollisionException("With: " + other.getClass().getSimpleName()); + } + } /** * C code ported from diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java index 18be750f1..30751e078 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java @@ -792,7 +792,35 @@ public class BoundingSphere extends BoundingVolume { return 1; } } - + + private int collideWithRay(Ray ray) { + TempVars vars = TempVars.get(); + + Vector3f diff = vars.vect1.set(ray.getOrigin()).subtractLocal( + center); + float a = diff.dot(diff) - (getRadius() * getRadius()); + float a1, discr; + if (a <= 0.0) { + // inside sphere + vars.release(); + return 1; + } + + a1 = ray.direction.dot(diff); + vars.release(); + if (a1 >= 0.0) { + return 0; + } + + discr = a1 * a1 - a; + if (discr < 0.0) { + return 0; + } else if (discr >= FastMath.ZERO_TOLERANCE) { + return 2; + } + return 1; + } + private int collideWithTri(Triangle tri, CollisionResults results) { TempVars tvars = TempVars.get(); try { @@ -991,6 +1019,18 @@ public class BoundingSphere extends BoundingVolume { } } + @Override public int collideWith(Collidable other) { + if (other instanceof Ray) { + Ray ray = (Ray) other; + return collideWithRay(ray); + } else if (other instanceof Triangle){ + return super.collideWith(other); + } else { + throw new UnsupportedCollisionException(); + } + } + + @Override public boolean contains(Vector3f point) { return center.distanceSquared(point) < (getRadius() * getRadius()); diff --git a/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java b/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java index 994a74e73..f9be2e573 100644 --- a/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java +++ b/jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java @@ -32,10 +32,12 @@ package com.jme3.bounding; import com.jme3.collision.Collidable; +import com.jme3.collision.CollisionResults; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.Savable; import com.jme3.math.*; +import com.jme3.util.TempVars; import java.io.IOException; import java.nio.FloatBuffer; @@ -322,6 +324,15 @@ public abstract class BoundingVolume implements Savable, Cloneable, Collidable { public void read(JmeImporter e) throws IOException { center = (Vector3f) e.getCapsule(this).readSavable("center", Vector3f.ZERO.clone()); } + + public int collideWith(Collidable other) { + TempVars tempVars = TempVars.get(); + CollisionResults tempResults = tempVars.collisionResults; + tempResults.clear(); + int retval = collideWith(other, tempResults); + tempVars.release(); + return retval; + } } diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index f852fa6f9..74fdbb006 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -935,9 +935,9 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { renderMeshFromGeometry(r, g); } - if (isFirstLight && lightList.size() > 0) { - // There are only ambient lights in the scene. Render - // a dummy "normal light" so we can see the ambient + if (isFirstLight) { + // Either there are no lights at all, or only ambient lights. + // Render a dummy "normal light" so we can see the ambient color. ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList, false)); lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha); lightPos.setValue(VarType.Vector4, nullDirLight); @@ -1056,12 +1056,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { Collection
@@ -690,10 +667,6 @@ public class RenderManager {
// check culling first.
if (!scene.checkCulling(vp.getCamera())) {
- // move on to shadow-only render
- if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint() != Spatial.CullHint.Always) {
- renderShadow(scene, vp.getQueue());
- }
return;
}
@@ -717,12 +690,6 @@ public class RenderManager {
}
vp.getQueue().addToQueue(gm, scene.getQueueBucket());
-
- // add to shadow queue if needed
- RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
- if (shadowMode != RenderQueue.ShadowMode.Off) {
- vp.getQueue().addToShadowQueue(gm, shadowMode);
- }
}
}
@@ -898,8 +865,8 @@ public class RenderManager {
if (cam != prevCam || cam.isViewportChanged()) {
viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
- viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
- viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
+ viewWidth = ((int)(cam.getViewPortRight() * cam.getWidth())) - ((int)(cam.getViewPortLeft() * cam.getWidth()));
+ viewHeight = ((int)(cam.getViewPortTop() * cam.getHeight())) - ((int)(cam.getViewPortBottom() * cam.getHeight()));
uniformBindingManager.setViewPort(viewX, viewY, viewWidth, viewHeight);
renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
index 03f06f5a4..4a55486f6 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
@@ -351,6 +351,7 @@ public class GLRenderer implements Renderer {
if (hasExtension("GL_ARB_texture_non_power_of_two") ||
hasExtension("GL_OES_texture_npot") ||
+ hasExtension("GL_APPLE_texture_2D_limited_npot") ||
caps.contains(Caps.OpenGL30)) {
caps.add(Caps.NonPowerOfTwoTextures);
} else {
diff --git a/jme3-core/src/main/java/com/jme3/renderer/queue/RenderQueue.java b/jme3-core/src/main/java/com/jme3/renderer/queue/RenderQueue.java
index 98323c7a3..ea4215d6c 100644
--- a/jme3-core/src/main/java/com/jme3/renderer/queue/RenderQueue.java
+++ b/jme3-core/src/main/java/com/jme3/renderer/queue/RenderQueue.java
@@ -51,7 +51,6 @@ public class RenderQueue {
private GeometryList translucentList;
private GeometryList skyList;
private GeometryList shadowRecv;
- private GeometryList shadowCast;
/**
* Creates a new RenderQueue, the default {@link GeometryComparator comparators}
@@ -64,7 +63,6 @@ public class RenderQueue {
this.translucentList = new GeometryList(new TransparentComparator());
this.skyList = new GeometryList(new NullComparator());
this.shadowRecv = new GeometryList(new OpaqueComparator());
- this.shadowCast = new GeometryList(new OpaqueComparator());
}
/**
@@ -229,40 +227,6 @@ public class RenderQueue {
}
}
- /**
- * Adds a geometry to a shadow bucket.
- * Note that this operation is done automatically by the
- * {@link RenderManager}. {@link SceneProcessor}s that handle
- * shadow rendering should fetch the queue by using
- * {@link #getShadowQueueContent(com.jme3.renderer.queue.RenderQueue.ShadowMode) },
- * by default no action is taken on the shadow queues.
- *
- * @param g The geometry to add
- * @param shadBucket The shadow bucket type, if it is
- * {@link ShadowMode#CastAndReceive}, it is added to both the cast
- * and the receive buckets.
- */
- public void addToShadowQueue(Geometry g, ShadowMode shadBucket) {
- switch (shadBucket) {
- case Inherit:
- break;
- case Off:
- break;
- case Cast:
- shadowCast.add(g);
- break;
- case Receive:
- shadowRecv.add(g);
- break;
- case CastAndReceive:
- shadowCast.add(g);
- shadowRecv.add(g);
- break;
- default:
- throw new UnsupportedOperationException("Unrecognized shadow bucket type: " + shadBucket);
- }
- }
-
/**
* Adds a geometry to the given bucket.
* The {@link RenderManager} automatically handles this task
@@ -298,14 +262,11 @@ public class RenderQueue {
/**
*
* @param shadBucket The shadow mode to retrieve the {@link GeometryList
- * queue content} for. Only {@link ShadowMode#Cast Cast} and
- * {@link ShadowMode#Receive Receive} are valid.
+ * queue content} for. Only {@link ShadowMode#Receive Receive} is valid.
* @return The cast or receive {@link GeometryList}
*/
public GeometryList getShadowQueueContent(ShadowMode shadBucket) {
switch (shadBucket) {
- case Cast:
- return shadowCast;
case Receive:
return shadowRecv;
default:
@@ -331,19 +292,6 @@ public class RenderQueue {
renderGeometryList(list, rm, cam, clear);
}
- public void renderShadowQueue(ShadowMode shadBucket, RenderManager rm, Camera cam, boolean clear) {
- switch (shadBucket) {
- case Cast:
- renderGeometryList(shadowCast, rm, cam, clear);
- break;
- case Receive:
- renderGeometryList(shadowRecv, rm, cam, clear);
- break;
- default:
- throw new IllegalArgumentException("Unexpected shadow bucket: " + shadBucket);
- }
- }
-
public boolean isQueueEmpty(Bucket bucket) {
switch (bucket) {
case Gui:
@@ -394,7 +342,6 @@ public class RenderQueue {
transparentList.clear();
translucentList.clear();
skyList.clear();
- shadowCast.clear();
shadowRecv.clear();
}
}
diff --git a/jme3-core/src/main/java/com/jme3/scene/Node.java b/jme3-core/src/main/java/com/jme3/scene/Node.java
index 69c9bd018..816140e21 100644
--- a/jme3-core/src/main/java/com/jme3/scene/Node.java
+++ b/jme3-core/src/main/java/com/jme3/scene/Node.java
@@ -39,6 +39,7 @@ import com.jme3.export.JmeImporter;
import com.jme3.export.Savable;
import com.jme3.material.Material;
import com.jme3.util.SafeArrayList;
+import com.jme3.util.TempVars;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -565,6 +566,18 @@ public class Node extends Spatial implements Savable {
public int collideWith(Collidable other, CollisionResults results){
int total = 0;
+
+ // optimization: try collideWith BoundingVolume to avoid possibly redundant tests on children
+ // number 4 in condition is somewhat arbitrary. When there is only one child, the boundingVolume test is redundant at all.
+ // The idea is when there are few children, it can be too expensive to test boundingVolume first.
+ if (children.size() > 4)
+ {
+ BoundingVolume bv = this.getWorldBound();
+ if (bv==null) return 0;
+
+ // collideWith without CollisionResults parameter used to avoid allocation when possible
+ if (bv.collideWith(other) == 0) return 0;
+ }
for (Spatial child : children.getArray()){
total += child.collideWith(other, results);
}
diff --git a/jme3-core/src/main/java/com/jme3/shader/Shader.java b/jme3-core/src/main/java/com/jme3/shader/Shader.java
index 24eca7199..746733e78 100644
--- a/jme3-core/src/main/java/com/jme3/shader/Shader.java
+++ b/jme3-core/src/main/java/com/jme3/shader/Shader.java
@@ -249,6 +249,7 @@ public final class Shader extends NativeObject {
}
public Uniform getUniform(String name){
+ assert name.startsWith("m_") || name.startsWith("g_");
Uniform uniform = uniforms.get(name);
if (uniform == null){
uniform = new Uniform();
diff --git a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java
index ae495b2b6..b78d1e3bd 100644
--- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java
@@ -363,7 +363,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
* @param shadowMapOcculders
* @return
*/
- protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers, GeometryList shadowMapOccluders);
+ protected abstract GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders);
/**
* return the shadow camera to use for rendering the shadow map according
@@ -385,10 +385,10 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
@SuppressWarnings("fallthrough")
public void postQueue(RenderQueue rq) {
- GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
sceneReceivers = rq.getShadowQueueContent(ShadowMode.Receive);
+ lightReceivers.clear();
skipPostPass = false;
- if (sceneReceivers.size() == 0 || occluders.size() == 0 || !checkCulling(viewPort.getCamera())) {
+ if ( !checkCulling(viewPort.getCamera()) ) {
skipPostPass = true;
return;
}
@@ -404,14 +404,12 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
if (debugfrustums) {
doDisplayFrustumDebug(shadowMapIndex);
}
- renderShadowMap(shadowMapIndex, occluders, sceneReceivers);
+ renderShadowMap(shadowMapIndex);
}
debugfrustums = false;
- if (flushQueues) {
- occluders.clear();
- }
+
//restore setting for future rendering
r.setFrameBuffer(viewPort.getOutputFrameBuffer());
renderManager.setForcedMaterial(null);
@@ -420,8 +418,8 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
}
- protected void renderShadowMap(int shadowMapIndex, GeometryList occluders, GeometryList receivers) {
- shadowMapOccluders = getOccludersToRender(shadowMapIndex, occluders, receivers, shadowMapOccluders);
+ protected void renderShadowMap(int shadowMapIndex) {
+ shadowMapOccluders = getOccludersToRender(shadowMapIndex, shadowMapOccluders);
Camera shadowCam = getShadowCam(shadowMapIndex);
//saving light view projection matrix for this split
@@ -473,12 +471,12 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
if (debug) {
displayShadowMap(renderManager.getRenderer());
}
-
+
lightReceivers = getReceivers(sceneReceivers, lightReceivers);
if (lightReceivers.size() != 0) {
//setting params to recieving geometry list
- setMatParams();
+ setMatParams(lightReceivers);
Camera cam = viewPort.getCamera();
//some materials in the scene does not have a post shadow technique so we're using the fall back material
@@ -491,9 +489,6 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
//rendering the post shadow pass
viewPort.getQueue().renderShadowQueue(lightReceivers, renderManager, cam, false);
- if (flushQueues) {
- sceneReceivers.clear();
- }
//resetting renderManager settings
renderManager.setForcedTechnique(null);
@@ -504,6 +499,9 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
clearMatParams();
}
+ if (flushQueues) {
+ sceneReceivers.clear();
+ }
}
/**
@@ -541,10 +539,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
*/
protected abstract void setMaterialParameters(Material material);
- private void setMatParams() {
-
- GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive);
-
+ private void setMatParams(GeometryList l) {
//iteration throught all the geometries of the list to gather the materials
matCache.clear();
@@ -785,4 +780,4 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable
oc.write(flushQueues, "flushQueues", false);
oc.write(edgesThickness, "edgesThickness", 1.0f);
}
-}
\ No newline at end of file
+}
diff --git a/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java
index 50be4c91d..bc4273db7 100644
--- a/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java
@@ -40,8 +40,10 @@ import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.OpaqueComparator;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Spatial;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image.Format;
import com.jme3.texture.Texture2D;
@@ -71,6 +73,9 @@ public class BasicShadowRenderer implements SceneProcessor {
protected Texture2D dummyTex;
private float shadowMapSize;
+ protected GeometryList lightReceivers = new GeometryList(new OpaqueComparator());
+ protected GeometryList shadowOccluders = new GeometryList(new OpaqueComparator());
+
/**
* Creates a BasicShadowRenderer
* @param manager the asset manager
@@ -142,16 +147,10 @@ public class BasicShadowRenderer implements SceneProcessor {
}
public void postQueue(RenderQueue rq) {
- GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
- if (occluders.size() == 0) {
- noOccluders = true;
- return;
- } else {
- noOccluders = false;
+ for (Spatial scene : viewPort.getScenes()) {
+ ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), ShadowMode.Receive, lightReceivers);
}
- GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
-
// update frustum points based on current camera
Camera viewCam = viewPort.getCamera();
ShadowUtil.updateFrustumPoints(viewCam,
@@ -178,15 +177,21 @@ public class BasicShadowRenderer implements SceneProcessor {
shadowCam.updateViewProjection();
// render shadow casters to shadow map
- ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points, shadowMapSize);
-
+ ShadowUtil.updateShadowCamera(viewPort, lightReceivers, shadowCam, points, shadowOccluders, shadowMapSize);
+ if (shadowOccluders.size() == 0) {
+ noOccluders = true;
+ return;
+ } else {
+ noOccluders = false;
+ }
+
Renderer r = renderManager.getRenderer();
renderManager.setCamera(shadowCam, false);
renderManager.setForcedMaterial(preshadowMat);
r.setFrameBuffer(shadowFB);
r.clearBuffers(false, true, false);
- viewPort.getQueue().renderShadowQueue(ShadowMode.Cast, renderManager, shadowCam, true);
+ viewPort.getQueue().renderShadowQueue(shadowOccluders, renderManager, shadowCam, true);
r.setFrameBuffer(viewPort.getOutputFrameBuffer());
renderManager.setForcedMaterial(null);
@@ -205,7 +210,7 @@ public class BasicShadowRenderer implements SceneProcessor {
if (!noOccluders) {
postshadowMat.setMatrix4("LightViewProjectionMatrix", shadowCam.getViewProjectionMatrix());
renderManager.setForcedMaterial(postshadowMat);
- viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, viewPort.getCamera(), true);
+ viewPort.getQueue().renderShadowQueue(lightReceivers, renderManager, viewPort.getCamera(), true);
renderManager.setForcedMaterial(null);
}
}
diff --git a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java
index 9b47848aa..1c5b2617c 100644
--- a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java
@@ -43,7 +43,9 @@ import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
import java.io.IOException;
/**
@@ -173,19 +175,30 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
}
@Override
- protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers, GeometryList shadowMapOccluders) {
+ protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders) {
// update frustum points based on current camera and split
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], 1.0f, points);
- //Updating shadow cam with curent split frustra
- ShadowUtil.updateShadowCamera(sceneOccluders, sceneReceivers, shadowCam, points, shadowMapOccluders, stabilize?shadowMapSize:0);
+ //Updating shadow cam with curent split frustra
+ if (sceneReceivers.size()==0) {
+ for (Spatial scene : viewPort.getScenes()) {
+ ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), RenderQueue.ShadowMode.Receive, sceneReceivers);
+ }
+ }
+ ShadowUtil.updateShadowCamera(viewPort, sceneReceivers, shadowCam, points, shadowMapOccluders, stabilize?shadowMapSize:0);
return shadowMapOccluders;
}
@Override
GeometryList getReceivers(GeometryList sceneReceivers, GeometryList lightReceivers) {
+ if (sceneReceivers.size()==0) {
+ for (Spatial scene : viewPort.getScenes()) {
+ ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), RenderQueue.ShadowMode.Receive, sceneReceivers);
+ }
+ }
+ lightReceivers = sceneReceivers;
return sceneReceivers;
}
diff --git a/jme3-core/src/main/java/com/jme3/shadow/PointLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/PointLightShadowRenderer.java
index 9d80adec8..4ded98b23 100644
--- a/jme3-core/src/main/java/com/jme3/shadow/PointLightShadowRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/shadow/PointLightShadowRenderer.java
@@ -41,8 +41,10 @@ import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
import com.jme3.util.TempVars;
import java.io.IOException;
@@ -129,15 +131,19 @@ public class PointLightShadowRenderer extends AbstractShadowRenderer {
}
@Override
- protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers, GeometryList shadowMapOccluders) {
- ShadowUtil.getGeometriesInCamFrustum(sceneOccluders, shadowCams[shadowMapIndex], shadowMapOccluders);
+ protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders) {
+ for (Spatial scene : viewPort.getScenes()) {
+ ShadowUtil.getGeometriesInCamFrustum(scene, shadowCams[shadowMapIndex], RenderQueue.ShadowMode.Cast, shadowMapOccluders);
+ }
return shadowMapOccluders;
}
@Override
GeometryList getReceivers(GeometryList sceneReceivers, GeometryList lightReceivers) {
lightReceivers.clear();
- ShadowUtil.getGeometriesInLightRadius(sceneReceivers, shadowCams, lightReceivers);
+ for (Spatial scene : viewPort.getScenes()) {
+ ShadowUtil.getLitGeometriesInViewPort(scene, viewPort.getCamera(), shadowCams, RenderQueue.ShadowMode.Receive, lightReceivers);
+ }
return lightReceivers;
}
@@ -207,7 +213,7 @@ public class PointLightShadowRenderer extends AbstractShadowRenderer {
oc.write(light, "light", null);
}
- /**
+ /**
*
* @param viewCam
* @return
@@ -224,6 +230,5 @@ public class PointLightShadowRenderer extends AbstractShadowRenderer {
boolean intersects = light.intersectsFrustum(cam,vars);
vars.release();
return intersects;
-
}
}
diff --git a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java
index 080c91fea..182052e13 100644
--- a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java
@@ -175,6 +175,8 @@ public class PssmShadowRenderer implements SceneProcessor {
protected float fadeLength;
protected boolean applyFadeInfo = false;
+ protected GeometryList lightReceivers = new GeometryList(new OpaqueComparator());
+
/**
* Create a PSSM Shadow Renderer More info on the technique at http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
@@ -385,14 +387,8 @@ public class PssmShadowRenderer implements SceneProcessor {
@SuppressWarnings("fallthrough")
public void postQueue(RenderQueue rq) {
- GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
- if (occluders.size() == 0) {
- return;
- }
-
- GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
- if (receivers.size() == 0) {
- return;
+ for (Spatial scene : viewPort.getScenes()) {
+ ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), ShadowMode.Receive, lightReceivers);
}
Camera viewCam = viewPort.getCamera();
@@ -437,7 +433,7 @@ public class PssmShadowRenderer implements SceneProcessor {
ShadowUtil.updateFrustumPoints(viewCam, splitsArray[i], splitsArray[i + 1], 1.0f, points);
//Updating shadow cam with curent split frustra
- ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points, splitOccluders, shadowMapSize);
+ ShadowUtil.updateShadowCamera(viewPort, lightReceivers, shadowCam, points, splitOccluders, shadowMapSize);
//saving light view projection matrix for this split
lightViewProjectionsMatrices[i].set(shadowCam.getViewProjectionMatrix());
@@ -460,9 +456,7 @@ public class PssmShadowRenderer implements SceneProcessor {
viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCam, true);
}
debugfrustums = false;
- if (flushQueues) {
- occluders.clear();
- }
+
//restore setting for future rendering
r.setFrameBuffer(viewPort.getOutputFrameBuffer());
renderManager.setForcedMaterial(null);
@@ -518,7 +512,7 @@ public class PssmShadowRenderer implements SceneProcessor {
renderManager.setForcedTechnique(postTechniqueName);
//rendering the post shadow pass
- viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues);
+ viewPort.getQueue().renderShadowQueue(lightReceivers, renderManager, cam, true);
//resetting renderManager settings
renderManager.setForcedTechnique(null);
@@ -531,7 +525,7 @@ public class PssmShadowRenderer implements SceneProcessor {
private void setMatParams() {
- GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive);
+ GeometryList l = lightReceivers;
//iteration throught all the geometries of the list to gather the materials
diff --git a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java
index 0795b990b..d97a354f9 100644
--- a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java
+++ b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java
@@ -39,8 +39,12 @@ import com.jme3.math.Transform;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
+import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
import com.jme3.util.TempVars;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -328,31 +332,128 @@ public class ShadowUtil {
}
/**
- * Updates the shadow camera to properly contain the given points (which
- * contain the eye camera frustum corners) and the shadow occluder objects.
- *
- * @param occluders
- * @param receivers
- * @param shadowCam
- * @param points
+ * OccludersExtractor is a helper class to collect splitOccluders from scene recursively.
+ * It utilizes the scene hierarchy, instead of making the huge flat geometries list first.
+ * Instead of adding all geometries from scene to the RenderQueue.shadowCast and checking
+ * all of them one by one against camera frustum the whole Node is checked first
+ * to hopefully avoid the check on its children.
*/
- public static void updateShadowCamera(GeometryList occluders,
- GeometryList receivers,
- Camera shadowCam,
- Vector3f[] points,
- float shadowMapSize) {
- updateShadowCamera(occluders, receivers, shadowCam, points, null, shadowMapSize);
- }
+ public static class OccludersExtractor
+ {
+ // global variables set in order not to have recursive process method with too many parameters
+ Matrix4f viewProjMatrix;
+ public Integer casterCount;
+ BoundingBox splitBB, casterBB;
+ GeometryList splitOccluders;
+ TempVars vars;
+
+ public OccludersExtractor() {}
+
+ // initialize the global OccludersExtractor variables
+ public OccludersExtractor(Matrix4f vpm, int cc, BoundingBox sBB, BoundingBox cBB, GeometryList sOCC, TempVars v) {
+ viewProjMatrix = vpm;
+ casterCount = cc;
+ splitBB = sBB;
+ casterBB = cBB;
+ splitOccluders = sOCC;
+ vars = v;
+ }
+ /**
+ * Check the rootScene against camera frustum and if intersects process it recursively.
+ * The global OccludersExtractor variables need to be initialized first.
+ * Variables are updated and used in {@link ShadowUtil#updateShadowCamera} at last.
+ */
+ public int addOccluders(Spatial scene) {
+ if ( scene != null ) process(scene);
+ return casterCount;
+ }
+
+ private void process(Spatial scene) {
+ if (scene.getCullHint() == Spatial.CullHint.Always) return;
+
+ RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
+ if ( scene instanceof Geometry )
+ {
+ // convert bounding box to light's viewproj space
+ Geometry occluder = (Geometry)scene;
+ if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive
+ && !occluder.isGrouped() && occluder.getWorldBound()!=null) {
+ BoundingVolume bv = occluder.getWorldBound();
+ BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);
+
+ boolean intersects = splitBB.intersects(occBox);
+ if (!intersects && occBox instanceof BoundingBox) {
+ BoundingBox occBB = (BoundingBox) occBox;
+ //Kirill 01/10/2011
+ // Extend the occluder further into the frustum
+ // This fixes shadow dissapearing issues when
+ // the caster itself is not in the view camera
+ // but its shadow is in the camera
+ // The number is in world units
+ occBB.setZExtent(occBB.getZExtent() + 50);
+ occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
+ if (splitBB.intersects(occBB)) {
+ //Nehon : prevent NaN and infinity values to screw the final bounding box
+ if (!Float.isNaN(occBox.getCenter().x) && !Float.isInfinite(occBox.getCenter().x)) {
+ // To prevent extending the depth range too much
+ // We return the bound to its former shape
+ // Before adding it
+ occBB.setZExtent(occBB.getZExtent() - 50);
+ occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));
+ casterBB.mergeLocal(occBox);
+ casterCount++;
+ }
+ if (splitOccluders != null) {
+ splitOccluders.add(occluder);
+ }
+ }
+ } else if (intersects) {
+ casterBB.mergeLocal(occBox);
+ casterCount++;
+ if (splitOccluders != null) {
+ splitOccluders.add(occluder);
+ }
+ }
+ }
+ }
+ else if ( scene instanceof Node && ((Node)scene).getWorldBound()!=null )
+ {
+ Node nodeOcc = (Node)scene;
+ boolean intersects = false;
+ // some
+ BoundingVolume bv = nodeOcc.getWorldBound();
+ BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);
+
+ intersects = splitBB.intersects(occBox);
+ if (!intersects && occBox instanceof BoundingBox) {
+ BoundingBox occBB = (BoundingBox) occBox;
+ //Kirill 01/10/2011
+ // Extend the occluder further into the frustum
+ // This fixes shadow dissapearing issues when
+ // the caster itself is not in the view camera
+ // but its shadow is in the camera
+ // The number is in world units
+ occBB.setZExtent(occBB.getZExtent() + 50);
+ occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
+ intersects = splitBB.intersects(occBB);
+ }
+
+ if ( intersects ) {
+ for (Spatial child : ((Node)scene).getChildren()) {
+ process(child);
+ }
+ }
+ }
+ }
+ }
+
/**
* Updates the shadow camera to properly contain the given points (which
- * contain the eye camera frustum corners) and the shadow occluder objects.
- *
- * @param occluders
- * @param shadowCam
- * @param points
+ * contain the eye camera frustum corners) and the shadow occluder objects
+ * collected through the traverse of the scene hierarchy
*/
- public static void updateShadowCamera(GeometryList occluders,
+ public static void updateShadowCamera(ViewPort viewPort,
GeometryList receivers,
Camera shadowCam,
Vector3f[] points,
@@ -394,48 +495,13 @@ public class ShadowUtil {
}
}
- for (int i = 0; i < occluders.size(); i++) {
- // convert bounding box to light's viewproj space
- Geometry occluder = occluders.get(i);
- BoundingVolume bv = occluder.getWorldBound();
- BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);
-
- boolean intersects = splitBB.intersects(occBox);
- if (!intersects && occBox instanceof BoundingBox) {
- BoundingBox occBB = (BoundingBox) occBox;
- //Kirill 01/10/2011
- // Extend the occluder further into the frustum
- // This fixes shadow dissapearing issues when
- // the caster itself is not in the view camera
- // but its shadow is in the camera
- // The number is in world units
- occBB.setZExtent(occBB.getZExtent() + 50);
- occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
- if (splitBB.intersects(occBB)) {
- //Nehon : prevent NaN and infinity values to screw the final bounding box
- if (!Float.isNaN(occBox.getCenter().x) && !Float.isInfinite(occBox.getCenter().x)) {
- // To prevent extending the depth range too much
- // We return the bound to its former shape
- // Before adding it
- occBB.setZExtent(occBB.getZExtent() - 50);
- occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));
- casterBB.mergeLocal(occBox);
- casterCount++;
- }
- if (splitOccluders != null) {
- splitOccluders.add(occluder);
- }
-
- }
- } else if (intersects) {
- casterBB.mergeLocal(occBox);
- casterCount++;
- if (splitOccluders != null) {
- splitOccluders.add(occluder);
- }
- }
+ // collect splitOccluders through scene recursive traverse
+ OccludersExtractor occExt = new OccludersExtractor(viewProjMatrix, casterCount, splitBB, casterBB, splitOccluders, vars);
+ for (Spatial scene : viewPort.getScenes()) {
+ occExt.addOccluders(scene);
}
-
+ casterCount = occExt.casterCount;
+
//Nehon 08/18/2010 this is to avoid shadow bleeding when the ground is set to only receive shadows
if (casterCount != receiverCount) {
casterBB.setXExtent(casterBB.getXExtent() + 2.0f);
@@ -523,9 +589,8 @@ public class ShadowUtil {
vars.release();
shadowCam.setProjectionMatrix(result);
-
}
-
+
/**
* Populates the outputGeometryList with the geometry of the
* inputGeomtryList that are in the frustum of the given camera
@@ -551,10 +616,76 @@ public class ShadowUtil {
}
+ /**
+ * Populates the outputGeometryList with the rootScene children geometries
+ * that are in the frustum of the given camera
+ *
+ * @param rootScene the rootNode of the scene to traverse
+ * @param camera the camera to check geometries against
+ * @param outputGeometryList the list of all geometries that are in the
+ * camera frustum
+ */
+ public static void getGeometriesInCamFrustum(Spatial rootScene, Camera camera, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) {
+ if (rootScene != null && rootScene instanceof Node) {
+ int planeState = camera.getPlaneState();
+ addGeometriesInCamFrustumFromNode(camera, (Node)rootScene, mode, outputGeometryList);
+ camera.setPlaneState(planeState);
+ }
+ }
+
+ /**
+ * Helper function to distinguish between Occluders and Receivers
+ *
+ * @param shadowMode the ShadowMode tested
+ * @param desired the desired ShadowMode
+ * @return true if tested ShadowMode matches the desired one
+ */
+ static private boolean checkShadowMode(RenderQueue.ShadowMode shadowMode, RenderQueue.ShadowMode desired)
+ {
+ if (shadowMode != RenderQueue.ShadowMode.Off)
+ {
+ switch (desired) {
+ case Cast :
+ return shadowMode==RenderQueue.ShadowMode.Cast || shadowMode==RenderQueue.ShadowMode.CastAndReceive;
+ case Receive:
+ return shadowMode==RenderQueue.ShadowMode.Receive || shadowMode==RenderQueue.ShadowMode.CastAndReceive;
+ case CastAndReceive:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Helper function used to recursively populate the outputGeometryList
+ * with geometry children of scene node
+ *
+ * @param camera
+ * @param scene
+ * @param outputGeometryList
+ */
+ private static void addGeometriesInCamFrustumFromNode(Camera camera, Node scene, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) {
+ if (scene.getCullHint() == Spatial.CullHint.Always) return;
+ camera.setPlaneState(0);
+ if (camera.contains(scene.getWorldBound()) != Camera.FrustumIntersect.Outside) {
+ for (Spatial child: scene.getChildren()) {
+ if (child instanceof Node) addGeometriesInCamFrustumFromNode(camera, (Node)child, mode, outputGeometryList);
+ else if (child instanceof Geometry && child.getCullHint() != Spatial.CullHint.Always) {
+ camera.setPlaneState(0);
+ if (checkShadowMode(child.getShadowMode(), mode) &&
+ !((Geometry)child).isGrouped() &&
+ camera.contains(child.getWorldBound()) != Camera.FrustumIntersect.Outside) {
+ outputGeometryList.add((Geometry)child);
+ }
+ }
+ }
+ }
+ }
+
/**
* Populates the outputGeometryList with the geometry of the
* inputGeomtryList that are in the radius of a light.
- * The array of camera must be an array of 6 cameara initialized so they represent the light viewspace of a pointlight
+ * The array of camera must be an array of 6 cameras initialized so they represent the light viewspace of a pointlight
*
* @param inputGeometryList The list containing all geometry to check
* against the camera frustum
@@ -581,4 +712,54 @@ public class ShadowUtil {
}
}
+
+ /**
+ * Populates the outputGeometryList with the geometries of the children
+ * of OccludersExtractor.rootScene node that are both in the frustum of the given vpCamera and some camera inside cameras array.
+ * The array of cameras must be initialized to represent the light viewspace of some light like pointLight or spotLight
+ *
+ * @param camera the viewPort camera
+ * @param cameras the camera array to check geometries against, representing the light viewspace
+ * @param outputGeometryList the output list of all geometries that are in the camera frustum
+ */
+ public static void getLitGeometriesInViewPort(Spatial rootScene, Camera vpCamera, Camera[] cameras, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) {
+ if (rootScene != null && rootScene instanceof Node) {
+ addGeometriesInCamFrustumAndViewPortFromNode(vpCamera, cameras, (Node)rootScene, mode, outputGeometryList);
+ }
+ }
+ /**
+ * Helper function to recursively collect the geometries for getLitGeometriesInViewPort function.
+ *
+ * @param vpCamera the viewPort camera
+ * @param cameras the camera array to check geometries against, representing the light viewspace
+ * @param scene the Node to traverse or geometry to possibly add
+ * @param outputGeometryList the output list of all geometries that are in the camera frustum
+ */
+ private static void addGeometriesInCamFrustumAndViewPortFromNode(Camera vpCamera, Camera[] cameras, Spatial scene, RenderQueue.ShadowMode mode, GeometryList outputGeometryList) {
+ if (scene.getCullHint() == Spatial.CullHint.Always) return;
+
+ boolean inFrustum = false;
+ for (int j = 0; j < cameras.length && inFrustum == false; j++) {
+ Camera camera = cameras[j];
+ int planeState = camera.getPlaneState();
+ camera.setPlaneState(0);
+ inFrustum = camera.contains(scene.getWorldBound()) != Camera.FrustumIntersect.Outside && scene.checkCulling(vpCamera);
+ camera.setPlaneState(planeState);
+ }
+ if (inFrustum) {
+ if (scene instanceof Node)
+ {
+ Node node = (Node)scene;
+ for (Spatial child: node.getChildren()) {
+ addGeometriesInCamFrustumAndViewPortFromNode(vpCamera, cameras, child, mode, outputGeometryList);
+ }
+ }
+ else if (scene instanceof Geometry) {
+ if (checkShadowMode(scene.getShadowMode(), mode) && !((Geometry)scene).isGrouped() ) {
+ outputGeometryList.add((Geometry)scene);
+ }
+ }
+ }
+ }
+
}
diff --git a/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java
index 1fbcba2e3..e7a3a5407 100644
--- a/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java
+++ b/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java
@@ -43,7 +43,9 @@ import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
import com.jme3.util.TempVars;
import java.io.IOException;
@@ -141,15 +143,21 @@ public class SpotLightShadowRenderer extends AbstractShadowRenderer {
}
@Override
- protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList sceneOccluders, GeometryList sceneReceivers, GeometryList shadowMapOccluders) {
- ShadowUtil.getGeometriesInCamFrustum(sceneOccluders, shadowCam, shadowMapOccluders);
+ protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders) {
+ for (Spatial scene : viewPort.getScenes()) {
+ ShadowUtil.getGeometriesInCamFrustum(scene, shadowCam, RenderQueue.ShadowMode.Cast, shadowMapOccluders);
+ }
return shadowMapOccluders;
}
@Override
GeometryList getReceivers(GeometryList sceneReceivers, GeometryList lightReceivers) {
lightReceivers.clear();
- ShadowUtil.getGeometriesInCamFrustum(sceneReceivers, shadowCam, lightReceivers);
+ Camera[] cameras = new Camera[1];
+ cameras[0] = shadowCam;
+ for (Spatial scene : viewPort.getScenes()) {
+ ShadowUtil.getLitGeometriesInViewPort(scene, viewPort.getCamera(), cameras, RenderQueue.ShadowMode.Receive, lightReceivers);
+ }
return lightReceivers;
}
diff --git a/jme3-core/src/main/java/com/jme3/util/MaterialDebugAppState.java b/jme3-core/src/main/java/com/jme3/util/MaterialDebugAppState.java
index 9fb6c9e54..1c512e41b 100644
--- a/jme3-core/src/main/java/com/jme3/util/MaterialDebugAppState.java
+++ b/jme3-core/src/main/java/com/jme3/util/MaterialDebugAppState.java
@@ -317,7 +317,7 @@ public class MaterialDebugAppState extends AbstractAppState {
if (field.getType().isInstance(p)) {
field.setAccessible(true);
p = (Filter.Pass) field.get(filter);
- if (p.getPassMaterial() != null) {
+ if (p!= null && p.getPassMaterial() != null) {
Material mat = reloadMaterial(p.getPassMaterial());
if (mat == null) {
return;
diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag
index 749602ccd..11834ffc0 100644
--- a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag
+++ b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag
@@ -205,11 +205,6 @@ void main(){
vec2 light = computeLighting(normal, viewDir, lightDir.xyz, lightDir.w * spotFallOff , m_Shininess);
- #ifdef COLORRAMP
- diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
- specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
- #endif
-
// Workaround, since it is not possible to modify varying variables
vec4 SpecularSum2 = vec4(SpecularSum, 1.0);
#ifdef USE_REFLECTION
@@ -221,8 +216,14 @@ void main(){
light.y = 1.0;
#endif
- gl_FragColor.rgb += DiffuseSum.rgb * lightColor.rgb * diffuseColor.rgb * vec3(light.x) +
- SpecularSum2.rgb * specularColor.rgb * vec3(light.y);
+ #ifdef COLORRAMP
+ DiffuseSum2.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
+ SpecularSum2.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
+ light.xy = vec2(1.0);
+ #endif
+
+ gl_FragColor.rgb += DiffuseSum2.rgb * lightColor.rgb * diffuseColor.rgb * vec3(light.x) +
+ SpecularSum2.rgb * lightColor.rgb * specularColor.rgb * vec3(light.y);
}
#endif
diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
index 1303aa2b6..b0d8828a5 100644
--- a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
+++ b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
@@ -160,8 +160,14 @@ void main(){
}
#endif
vec2 v = computeLighting(wvNormal, viewDir, lightDir.xyz, lightDir.w * spotFallOff, m_Shininess);
- diffuseAccum +=v.x * diffuseColor;
- specularAccum += v.y * specularColor;
+
+ #ifdef COLORRAMP
+ diffuseAccum += texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb * diffuseColor;
+ specularAccum += texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb * specularColor;
+ #else
+ diffuseAccum += v.x * diffuseColor;
+ specularAccum += v.y * specularColor;
+ #endif
}
#endif
diff --git a/jme3-core/src/plugins/java/com/jme3/asset/plugins/HttpZipLocator.java b/jme3-core/src/plugins/java/com/jme3/asset/plugins/HttpZipLocator.java
index ab964ab2c..af7026206 100644
--- a/jme3-core/src/plugins/java/com/jme3/asset/plugins/HttpZipLocator.java
+++ b/jme3-core/src/plugins/java/com/jme3/asset/plugins/HttpZipLocator.java
@@ -298,8 +298,8 @@ public class HttpZipLocator implements AssetLocator {
}
public void load(URL url) throws IOException {
- if (!url.getProtocol().equals("http"))
- throw new UnsupportedOperationException();
+ if (!url.getProtocol().equals("http") && !url.getProtocol().equals("https"))
+ throw new UnsupportedOperationException("HttpZipLocator only supports HTTP(S) URLs");
zipUrl = url;
readEndHeader();
diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
index 7ac4dd69f..23310f12f 100644
--- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
+++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
@@ -40,7 +40,6 @@ import com.jme3.material.TechniqueDef.ShadowMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
-import com.jme3.shader.Shader;
import com.jme3.shader.VarType;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
@@ -49,10 +48,8 @@ import com.jme3.texture.image.ColorSpace;
import com.jme3.util.PlaceholderAssets;
import com.jme3.util.blockparser.BlockLanguageParser;
import com.jme3.util.blockparser.Statement;
-
import java.io.IOException;
import java.io.InputStream;
-import java.util.EnumMap;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -60,10 +57,10 @@ import java.util.logging.Logger;
public class J3MLoader implements AssetLoader {
private static final Logger logger = Logger.getLogger(J3MLoader.class.getName());
- // private ErrorLogger errors;
+ // private ErrorLogger errors;
private ShaderNodeLoaderDelegate nodesLoaderDelegate;
boolean isUseNodes = false;
-
+
private AssetManager assetManager;
private AssetKey key;
@@ -71,15 +68,16 @@ public class J3MLoader implements AssetLoader {
private Material material;
private TechniqueDef technique;
private RenderState renderState;
-
- private EnumMap