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 */
32package com.jme3.renderer.queue;
33
34import com.jme3.post.SceneProcessor;
35import com.jme3.renderer.Camera;
36import com.jme3.renderer.RenderManager;
37import com.jme3.scene.Geometry;
38import 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 */
46public 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     */
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     */
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     */
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     */
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     */
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
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
307    public void renderShadowQueue(GeometryList list, RenderManager rm, Camera cam, boolean clear) {
308        renderGeometryList(list, rm, cam, clear);
309    }
310
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
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
341    public void renderQueue(Bucket bucket, RenderManager rm, Camera cam) {
342        renderQueue(bucket, rm, cam, true);
343    }
344
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
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