aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/renderer/queue/RenderQueue.java
blob: dba604ee209fb87849b0c39205c2afc1732060b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
/*
 * 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();
    }
}