| /* |
| * Copyright (c) 2009-2010 jMonkeyEngine |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package com.jme3.renderer.queue; |
| |
| import com.jme3.post.SceneProcessor; |
| import com.jme3.renderer.Camera; |
| import com.jme3.renderer.RenderManager; |
| import com.jme3.scene.Geometry; |
| import com.jme3.scene.Spatial; |
| |
| /** |
| * <code>RenderQueue</code> is used to queue up and sort |
| * {@link Geometry geometries} for rendering. |
| * |
| * @author Kirill Vainer |
| */ |
| public class RenderQueue { |
| |
| private GeometryList opaqueList; |
| private GeometryList guiList; |
| private GeometryList transparentList; |
| private GeometryList translucentList; |
| private GeometryList skyList; |
| private GeometryList shadowRecv; |
| private GeometryList shadowCast; |
| |
| /** |
| * Creates a new RenderQueue, the default {@link GeometryComparator comparators} |
| * are used for all {@link GeometryList geometry lists}. |
| */ |
| public RenderQueue() { |
| this.opaqueList = new GeometryList(new OpaqueComparator()); |
| this.guiList = new GeometryList(new GuiComparator()); |
| this.transparentList = new GeometryList(new TransparentComparator()); |
| this.translucentList = new GeometryList(new TransparentComparator()); |
| this.skyList = new GeometryList(new NullComparator()); |
| this.shadowRecv = new GeometryList(new OpaqueComparator()); |
| this.shadowCast = new GeometryList(new OpaqueComparator()); |
| } |
| |
| /** |
| * The render queue <code>Bucket</code> specifies the bucket |
| * to which the spatial will be placed when rendered. |
| * <p> |
| * The behavior of the rendering will differ depending on which |
| * bucket the spatial is placed. A spatial's queue bucket can be set |
| * via {@link Spatial#setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket) }. |
| */ |
| public enum Bucket { |
| /** |
| * The renderer will try to find the optimal order for rendering all |
| * objects using this mode. |
| * You should use this mode for most normal objects, except transparent |
| * ones, as it could give a nice performance boost to your application. |
| */ |
| Opaque, |
| |
| /** |
| * This is the mode you should use for object with |
| * transparency in them. It will ensure the objects furthest away are |
| * rendered first. That ensures when another transparent object is drawn on |
| * top of previously drawn objects, you can see those (and the object drawn |
| * using Opaque) through the transparent parts of the newly drawn |
| * object. |
| */ |
| Transparent, |
| |
| /** |
| * A special mode used for rendering really far away, flat objects - |
| * e.g. skies. In this mode, the depth is set to infinity so |
| * spatials in this bucket will appear behind everything, the downside |
| * to this bucket is that 3D objects will not be rendered correctly |
| * due to lack of depth testing. |
| */ |
| Sky, |
| |
| /** |
| * A special mode used for rendering transparent objects that |
| * should not be effected by {@link SceneProcessor}. |
| * Generally this would contain translucent objects, and |
| * also objects that do not write to the depth buffer such as |
| * particle emitters. |
| */ |
| Translucent, |
| |
| /** |
| * This is a special mode, for drawing 2D object |
| * without perspective (such as GUI or HUD parts). |
| * The spatial's world coordinate system has the range |
| * of [0, 0, -1] to [Width, Height, 1] where Width/Height is |
| * the resolution of the screen rendered to. Any spatials |
| * outside of that range are culled. |
| */ |
| Gui, |
| |
| /** |
| * A special mode, that will ensure that this spatial uses the same |
| * mode as the parent Node does. |
| */ |
| Inherit, |
| } |
| |
| /** |
| * <code>ShadowMode</code> is a marker used to specify how shadow |
| * effects should treat the spatial. |
| */ |
| public enum ShadowMode { |
| /** |
| * Disable both shadow casting and shadow receiving for this spatial. |
| * Generally used for special effects like particle emitters. |
| */ |
| Off, |
| |
| /** |
| * Enable casting of shadows but not receiving them. |
| */ |
| Cast, |
| |
| /** |
| * Enable receiving of shadows but not casting them. |
| */ |
| Receive, |
| |
| /** |
| * Enable both receiving and casting of shadows. |
| */ |
| CastAndReceive, |
| |
| /** |
| * Inherit the <code>ShadowMode</code> from the parent node. |
| */ |
| Inherit |
| } |
| |
| /** |
| * Sets a different geometry comparator for the specified bucket, one |
| * of Gui, Opaque, Sky, or Transparent. The GeometryComparators are |
| * used to sort the accumulated list of geometries before actual rendering |
| * occurs. |
| * |
| * <p>The most significant comparator is the one for the transparent |
| * bucket since there is no correct way to sort the transparent bucket |
| * that will handle all geometry all the time. In certain cases, the |
| * application may know the best way to sort and now has the option of |
| * configuring a specific implementation.</p> |
| * |
| * <p>The default comparators are:</p> |
| * <ul> |
| * <li>Bucket.Opaque: {@link com.jme3.renderer.queue.OpaqueComparator} which sorts |
| * by material first and front to back within the same material. |
| * <li>Bucket.Transparent: {@link com.jme3.renderer.queue.TransparentComparator} which |
| * sorts purely back to front by leading bounding edge with no material sort. |
| * <li>Bucket.Translucent: {@link com.jme3.renderer.queue.TransparentComparator} which |
| * sorts purely back to front by leading bounding edge with no material sort. this bucket is rendered after post processors. |
| * <li>Bucket.Sky: {@link com.jme3.renderer.queue.NullComparator} which does no sorting |
| * at all. |
| * <li>Bucket.Gui: {@link com.jme3.renderer.queue.GuiComparator} sorts geometries back to |
| * front based on their Z values. |
| */ |
| public void setGeometryComparator(Bucket bucket, GeometryComparator c) { |
| switch (bucket) { |
| case Gui: |
| guiList = new GeometryList(c); |
| break; |
| case Opaque: |
| opaqueList = new GeometryList(c); |
| break; |
| case Sky: |
| skyList = new GeometryList(c); |
| break; |
| case Transparent: |
| transparentList = new GeometryList(c); |
| break; |
| case Translucent: |
| translucentList = new GeometryList(c); |
| break; |
| default: |
| throw new UnsupportedOperationException("Unknown bucket type: " + bucket); |
| } |
| } |
| |
| /** |
| * 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 |
| * when flattening the scene graph. The bucket to add |
| * the geometry is determined by {@link Geometry#getQueueBucket() }. |
| * |
| * @param g The geometry to add |
| * @param bucket The bucket to add to, usually |
| * {@link Geometry#getQueueBucket() }. |
| */ |
| public void addToQueue(Geometry g, Bucket bucket) { |
| switch (bucket) { |
| case Gui: |
| guiList.add(g); |
| break; |
| case Opaque: |
| opaqueList.add(g); |
| break; |
| case Sky: |
| skyList.add(g); |
| break; |
| case Transparent: |
| transparentList.add(g); |
| break; |
| case Translucent: |
| translucentList.add(g); |
| break; |
| default: |
| throw new UnsupportedOperationException("Unknown bucket type: " + bucket); |
| } |
| } |
| |
| /** |
| * |
| * @param shadBucket |
| * @return |
| */ |
| public GeometryList getShadowQueueContent(ShadowMode shadBucket) { |
| switch (shadBucket) { |
| case Cast: |
| return shadowCast; |
| case Receive: |
| return shadowRecv; |
| default: |
| throw new IllegalArgumentException("Only Cast or Receive are allowed"); |
| } |
| } |
| |
| private void renderGeometryList(GeometryList list, RenderManager rm, Camera cam, boolean clear) { |
| list.setCamera(cam); // select camera for sorting |
| list.sort(); |
| for (int i = 0; i < list.size(); i++) { |
| Geometry obj = list.get(i); |
| assert obj != null; |
| rm.renderGeometry(obj); |
| obj.queueDistance = Float.NEGATIVE_INFINITY; |
| } |
| if (clear) { |
| list.clear(); |
| } |
| } |
| |
| public void renderShadowQueue(GeometryList list, RenderManager rm, Camera cam, boolean clear) { |
| 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: |
| return guiList.size() == 0; |
| case Opaque: |
| return opaqueList.size() == 0; |
| case Sky: |
| return skyList.size() == 0; |
| case Transparent: |
| return transparentList.size() == 0; |
| case Translucent: |
| return translucentList.size() == 0; |
| default: |
| throw new UnsupportedOperationException("Unsupported bucket type: " + bucket); |
| } |
| } |
| |
| public void renderQueue(Bucket bucket, RenderManager rm, Camera cam) { |
| renderQueue(bucket, rm, cam, true); |
| } |
| |
| public void renderQueue(Bucket bucket, RenderManager rm, Camera cam, boolean clear) { |
| switch (bucket) { |
| case Gui: |
| renderGeometryList(guiList, rm, cam, clear); |
| break; |
| case Opaque: |
| renderGeometryList(opaqueList, rm, cam, clear); |
| break; |
| case Sky: |
| renderGeometryList(skyList, rm, cam, clear); |
| break; |
| case Transparent: |
| renderGeometryList(transparentList, rm, cam, clear); |
| break; |
| case Translucent: |
| renderGeometryList(translucentList, rm, cam, clear); |
| break; |
| |
| default: |
| throw new UnsupportedOperationException("Unsupported bucket type: " + bucket); |
| } |
| } |
| |
| public void clear() { |
| opaqueList.clear(); |
| guiList.clear(); |
| transparentList.clear(); |
| translucentList.clear(); |
| skyList.clear(); |
| shadowCast.clear(); |
| shadowRecv.clear(); |
| } |
| } |