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