1 /*
2  * Copyright (c) 2009-2010 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package com.jme3.renderer.queue;
33 
34 import com.jme3.post.SceneProcessor;
35 import com.jme3.renderer.Camera;
36 import com.jme3.renderer.RenderManager;
37 import com.jme3.scene.Geometry;
38 import com.jme3.scene.Spatial;
39 
40 /**
41  * <code>RenderQueue</code> is used to queue up and sort
42  * {@link Geometry geometries} for rendering.
43  *
44  * @author Kirill Vainer
45  */
46 public class RenderQueue {
47 
48     private GeometryList opaqueList;
49     private GeometryList guiList;
50     private GeometryList transparentList;
51     private GeometryList translucentList;
52     private GeometryList skyList;
53     private GeometryList shadowRecv;
54     private GeometryList shadowCast;
55 
56     /**
57      * Creates a new RenderQueue, the default {@link GeometryComparator comparators}
58      * are used for all {@link GeometryList geometry lists}.
59      */
RenderQueue()60     public RenderQueue() {
61         this.opaqueList = new GeometryList(new OpaqueComparator());
62         this.guiList = new GeometryList(new GuiComparator());
63         this.transparentList = new GeometryList(new TransparentComparator());
64         this.translucentList = new GeometryList(new TransparentComparator());
65         this.skyList = new GeometryList(new NullComparator());
66         this.shadowRecv = new GeometryList(new OpaqueComparator());
67         this.shadowCast = new GeometryList(new OpaqueComparator());
68     }
69 
70     /**
71      * The render queue <code>Bucket</code> specifies the bucket
72      * to which the spatial will be placed when rendered.
73      * <p>
74      * The behavior of the rendering will differ depending on which
75      * bucket the spatial is placed. A spatial's queue bucket can be set
76      * via {@link Spatial#setQueueBucket(com.jme3.renderer.queue.RenderQueue.Bucket) }.
77      */
78     public enum Bucket {
79         /**
80          * The renderer will try to find the optimal order for rendering all
81          * objects using this mode.
82          * You should use this mode for most normal objects, except transparent
83          * ones, as it could give a nice performance boost to your application.
84          */
85         Opaque,
86 
87         /**
88          * This is the mode you should use for object with
89          * transparency in them. It will ensure the objects furthest away are
90          * rendered first. That ensures when another transparent object is drawn on
91          * top of previously drawn objects, you can see those (and the object drawn
92          * using Opaque) through the transparent parts of the newly drawn
93          * object.
94          */
95         Transparent,
96 
97         /**
98          * A special mode used for rendering really far away, flat objects -
99          * e.g. skies. In this mode, the depth is set to infinity so
100          * spatials in this bucket will appear behind everything, the downside
101          * to this bucket is that 3D objects will not be rendered correctly
102          * due to lack of depth testing.
103          */
104         Sky,
105 
106         /**
107          * A special mode used for rendering transparent objects that
108          * should not be effected by {@link SceneProcessor}.
109          * Generally this would contain translucent objects, and
110          * also objects that do not write to the depth buffer such as
111          * particle emitters.
112          */
113         Translucent,
114 
115         /**
116          * This is a special mode, for drawing 2D object
117          * without perspective (such as GUI or HUD parts).
118          * The spatial's world coordinate system has the range
119          * of [0, 0, -1] to [Width, Height, 1] where Width/Height is
120          * the resolution of the screen rendered to. Any spatials
121          * outside of that range are culled.
122          */
123         Gui,
124 
125         /**
126          * A special mode, that will ensure that this spatial uses the same
127          * mode as the parent Node does.
128          */
129         Inherit,
130     }
131 
132     /**
133      * <code>ShadowMode</code> is a marker used to specify how shadow
134      * effects should treat the spatial.
135      */
136     public enum ShadowMode {
137         /**
138          * Disable both shadow casting and shadow receiving for this spatial.
139          * Generally used for special effects like particle emitters.
140          */
141         Off,
142 
143         /**
144          * Enable casting of shadows but not receiving them.
145          */
146         Cast,
147 
148         /**
149          * Enable receiving of shadows but not casting them.
150          */
151         Receive,
152 
153         /**
154          * Enable both receiving and casting of shadows.
155          */
156         CastAndReceive,
157 
158         /**
159          * Inherit the <code>ShadowMode</code> from the parent node.
160          */
161         Inherit
162     }
163 
164     /**
165      *  Sets a different geometry comparator for the specified bucket, one
166      *  of Gui, Opaque, Sky, or Transparent.  The GeometryComparators are
167      *  used to sort the accumulated list of geometries before actual rendering
168      *  occurs.
169      *
170      *  <p>The most significant comparator is the one for the transparent
171      *  bucket since there is no correct way to sort the transparent bucket
172      *  that will handle all geometry all the time.  In certain cases, the
173      *  application may know the best way to sort and now has the option of
174      *  configuring a specific implementation.</p>
175      *
176      *  <p>The default comparators are:</p>
177      *  <ul>
178      *  <li>Bucket.Opaque: {@link com.jme3.renderer.queue.OpaqueComparator} which sorts
179      *                     by material first and front to back within the same material.
180      *  <li>Bucket.Transparent: {@link com.jme3.renderer.queue.TransparentComparator} which
181      *                     sorts purely back to front by leading bounding edge with no material sort.
182      *  <li>Bucket.Translucent: {@link com.jme3.renderer.queue.TransparentComparator} which
183      *                     sorts purely back to front by leading bounding edge with no material sort. this bucket is rendered after post processors.
184      *  <li>Bucket.Sky: {@link com.jme3.renderer.queue.NullComparator} which does no sorting
185      *                     at all.
186      *  <li>Bucket.Gui: {@link com.jme3.renderer.queue.GuiComparator} sorts geometries back to
187      *                     front based on their Z values.
188      */
setGeometryComparator(Bucket bucket, GeometryComparator c)189     public void setGeometryComparator(Bucket bucket, GeometryComparator c) {
190         switch (bucket) {
191             case Gui:
192                 guiList = new GeometryList(c);
193                 break;
194             case Opaque:
195                 opaqueList = new GeometryList(c);
196                 break;
197             case Sky:
198                 skyList = new GeometryList(c);
199                 break;
200             case Transparent:
201                 transparentList = new GeometryList(c);
202                 break;
203             case Translucent:
204                 translucentList = new GeometryList(c);
205                 break;
206             default:
207                 throw new UnsupportedOperationException("Unknown bucket type: " + bucket);
208         }
209     }
210 
211     /**
212      * Adds a geometry to a shadow bucket.
213      * Note that this operation is done automatically by the
214      * {@link RenderManager}. {@link SceneProcessor}s that handle
215      * shadow rendering should fetch the queue by using
216      * {@link #getShadowQueueContent(com.jme3.renderer.queue.RenderQueue.ShadowMode) },
217      * by default no action is taken on the shadow queues.
218      *
219      * @param g The geometry to add
220      * @param shadBucket The shadow bucket type, if it is
221      * {@link ShadowMode#CastAndReceive}, it is added to both the cast
222      * and the receive buckets.
223      */
addToShadowQueue(Geometry g, ShadowMode shadBucket)224     public void addToShadowQueue(Geometry g, ShadowMode shadBucket) {
225         switch (shadBucket) {
226             case Inherit:
227                 break;
228             case Off:
229                 break;
230             case Cast:
231                 shadowCast.add(g);
232                 break;
233             case Receive:
234                 shadowRecv.add(g);
235                 break;
236             case CastAndReceive:
237                 shadowCast.add(g);
238                 shadowRecv.add(g);
239                 break;
240             default:
241                 throw new UnsupportedOperationException("Unrecognized shadow bucket type: " + shadBucket);
242         }
243     }
244 
245     /**
246      * Adds a geometry to the given bucket.
247      * The {@link RenderManager} automatically handles this task
248      * when flattening the scene graph. The bucket to add
249      * the geometry is determined by {@link Geometry#getQueueBucket() }.
250      *
251      * @param g  The geometry to add
252      * @param bucket The bucket to add to, usually
253      * {@link Geometry#getQueueBucket() }.
254      */
addToQueue(Geometry g, Bucket bucket)255     public void addToQueue(Geometry g, Bucket bucket) {
256         switch (bucket) {
257             case Gui:
258                 guiList.add(g);
259                 break;
260             case Opaque:
261                 opaqueList.add(g);
262                 break;
263             case Sky:
264                 skyList.add(g);
265                 break;
266             case Transparent:
267                 transparentList.add(g);
268                 break;
269             case Translucent:
270                 translucentList.add(g);
271                 break;
272             default:
273                 throw new UnsupportedOperationException("Unknown bucket type: " + bucket);
274         }
275     }
276 
277     /**
278      *
279      * @param shadBucket
280      * @return
281      */
getShadowQueueContent(ShadowMode shadBucket)282     public GeometryList getShadowQueueContent(ShadowMode shadBucket) {
283         switch (shadBucket) {
284             case Cast:
285                 return shadowCast;
286             case Receive:
287                 return shadowRecv;
288             default:
289                 throw new IllegalArgumentException("Only Cast or Receive are allowed");
290         }
291     }
292 
renderGeometryList(GeometryList list, RenderManager rm, Camera cam, boolean clear)293     private void renderGeometryList(GeometryList list, RenderManager rm, Camera cam, boolean clear) {
294         list.setCamera(cam); // select camera for sorting
295         list.sort();
296         for (int i = 0; i < list.size(); i++) {
297             Geometry obj = list.get(i);
298             assert obj != null;
299             rm.renderGeometry(obj);
300             obj.queueDistance = Float.NEGATIVE_INFINITY;
301         }
302         if (clear) {
303             list.clear();
304         }
305     }
306 
renderShadowQueue(GeometryList list, RenderManager rm, Camera cam, boolean clear)307     public void renderShadowQueue(GeometryList list, RenderManager rm, Camera cam, boolean clear) {
308         renderGeometryList(list, rm, cam, clear);
309     }
310 
renderShadowQueue(ShadowMode shadBucket, RenderManager rm, Camera cam, boolean clear)311     public void renderShadowQueue(ShadowMode shadBucket, RenderManager rm, Camera cam, boolean clear) {
312         switch (shadBucket) {
313             case Cast:
314                 renderGeometryList(shadowCast, rm, cam, clear);
315                 break;
316             case Receive:
317                 renderGeometryList(shadowRecv, rm, cam, clear);
318                 break;
319             default:
320                 throw new IllegalArgumentException("Unexpected shadow bucket: " + shadBucket);
321         }
322     }
323 
isQueueEmpty(Bucket bucket)324     public boolean isQueueEmpty(Bucket bucket) {
325         switch (bucket) {
326             case Gui:
327                 return guiList.size() == 0;
328             case Opaque:
329                 return opaqueList.size() == 0;
330             case Sky:
331                 return skyList.size() == 0;
332             case Transparent:
333                 return transparentList.size() == 0;
334             case Translucent:
335                 return translucentList.size() == 0;
336             default:
337                 throw new UnsupportedOperationException("Unsupported bucket type: " + bucket);
338         }
339     }
340 
renderQueue(Bucket bucket, RenderManager rm, Camera cam)341     public void renderQueue(Bucket bucket, RenderManager rm, Camera cam) {
342         renderQueue(bucket, rm, cam, true);
343     }
344 
renderQueue(Bucket bucket, RenderManager rm, Camera cam, boolean clear)345     public void renderQueue(Bucket bucket, RenderManager rm, Camera cam, boolean clear) {
346         switch (bucket) {
347             case Gui:
348                 renderGeometryList(guiList, rm, cam, clear);
349                 break;
350             case Opaque:
351                 renderGeometryList(opaqueList, rm, cam, clear);
352                 break;
353             case Sky:
354                 renderGeometryList(skyList, rm, cam, clear);
355                 break;
356             case Transparent:
357                 renderGeometryList(transparentList, rm, cam, clear);
358                 break;
359             case Translucent:
360                 renderGeometryList(translucentList, rm, cam, clear);
361                 break;
362 
363             default:
364                 throw new UnsupportedOperationException("Unsupported bucket type: " + bucket);
365         }
366     }
367 
clear()368     public void clear() {
369         opaqueList.clear();
370         guiList.clear();
371         transparentList.clear();
372         translucentList.clear();
373         skyList.clear();
374         shadowCast.clear();
375         shadowRecv.clear();
376     }
377 }
378