1 /* 2 * Copyright (c) 2009-2012 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; 33 34 import com.jme3.material.Material; 35 import com.jme3.material.MaterialDef; 36 import com.jme3.material.RenderState; 37 import com.jme3.material.Technique; 38 import com.jme3.math.*; 39 import com.jme3.post.SceneProcessor; 40 import com.jme3.renderer.queue.GeometryList; 41 import com.jme3.renderer.queue.RenderQueue; 42 import com.jme3.renderer.queue.RenderQueue.Bucket; 43 import com.jme3.renderer.queue.RenderQueue.ShadowMode; 44 import com.jme3.scene.*; 45 import com.jme3.shader.Uniform; 46 import com.jme3.shader.UniformBinding; 47 import com.jme3.shader.VarType; 48 import com.jme3.system.NullRenderer; 49 import com.jme3.system.Timer; 50 import com.jme3.util.IntMap.Entry; 51 import com.jme3.util.TempVars; 52 import java.util.ArrayList; 53 import java.util.Collections; 54 import java.util.List; 55 import java.util.logging.Logger; 56 57 /** 58 * <code>RenderManager</code> is a high-level rendering interface that is 59 * above the Renderer implementation. RenderManager takes care 60 * of rendering the scene graphs attached to each viewport and 61 * handling SceneProcessors. 62 * 63 * @see SceneProcessor 64 * @see ViewPort 65 * @see Spatial 66 */ 67 public class RenderManager { 68 69 private static final Logger logger = Logger.getLogger(RenderManager.class.getName()); 70 71 private Renderer renderer; 72 private Timer timer; 73 private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>(); 74 private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>(); 75 private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>(); 76 private Camera prevCam = null; 77 private Material forcedMaterial = null; 78 private String forcedTechnique = null; 79 private RenderState forcedRenderState = null; 80 private boolean shader; 81 private int viewX, viewY, viewWidth, viewHeight; 82 private float near, far; 83 private Matrix4f orthoMatrix = new Matrix4f(); 84 private Matrix4f viewMatrix = new Matrix4f(); 85 private Matrix4f projMatrix = new Matrix4f(); 86 private Matrix4f viewProjMatrix = new Matrix4f(); 87 private Matrix4f worldMatrix = new Matrix4f(); 88 private Vector3f camUp = new Vector3f(), 89 camLeft = new Vector3f(), 90 camDir = new Vector3f(), 91 camLoc = new Vector3f(); 92 //temp technique 93 private String tmpTech; 94 private boolean handleTranlucentBucket = true; 95 96 /** 97 * Create a high-level rendering interface over the 98 * low-level rendering interface. 99 * @param renderer 100 */ 101 public RenderManager(Renderer renderer) { 102 this.renderer = renderer; 103 //this.shader = renderer.getCaps().contains(Caps.GLSL100); 104 } 105 106 /** 107 * Returns the pre ViewPort with the given name. 108 * 109 * @param viewName The name of the pre ViewPort to look up 110 * @return The ViewPort, or null if not found. 111 * 112 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 113 */ 114 public ViewPort getPreView(String viewName) { 115 for (int i = 0; i < preViewPorts.size(); i++) { 116 if (preViewPorts.get(i).getName().equals(viewName)) { 117 return preViewPorts.get(i); 118 } 119 } 120 return null; 121 } 122 123 /** 124 * Removes the specified pre ViewPort. 125 * 126 * @param view The pre ViewPort to remove 127 * @return True if the ViewPort was removed successfully. 128 * 129 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 130 */ 131 public boolean removePreView(ViewPort view) { 132 return preViewPorts.remove(view); 133 } 134 135 /** 136 * Returns the main ViewPort with the given name. 137 * 138 * @param viewName The name of the main ViewPort to look up 139 * @return The ViewPort, or null if not found. 140 * 141 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 142 */ 143 public ViewPort getMainView(String viewName) { 144 for (int i = 0; i < viewPorts.size(); i++) { 145 if (viewPorts.get(i).getName().equals(viewName)) { 146 return viewPorts.get(i); 147 } 148 } 149 return null; 150 } 151 152 /** 153 * Removes the main ViewPort with the specified name. 154 * 155 * @param viewName The main ViewPort name to remove 156 * @return True if the ViewPort was removed successfully. 157 * 158 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 159 */ 160 public boolean removeMainView(String viewName) { 161 for (int i = 0; i < viewPorts.size(); i++) { 162 if (viewPorts.get(i).getName().equals(viewName)) { 163 viewPorts.remove(i); 164 return true; 165 } 166 } 167 return false; 168 } 169 170 /** 171 * Removes the specified main ViewPort. 172 * 173 * @param view The main ViewPort to remove 174 * @return True if the ViewPort was removed successfully. 175 * 176 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 177 */ 178 public boolean removeMainView(ViewPort view) { 179 return viewPorts.remove(view); 180 } 181 182 /** 183 * Returns the post ViewPort with the given name. 184 * 185 * @param viewName The name of the post ViewPort to look up 186 * @return The ViewPort, or null if not found. 187 * 188 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 189 */ 190 public ViewPort getPostView(String viewName) { 191 for (int i = 0; i < postViewPorts.size(); i++) { 192 if (postViewPorts.get(i).getName().equals(viewName)) { 193 return postViewPorts.get(i); 194 } 195 } 196 return null; 197 } 198 199 /** 200 * Removes the post ViewPort with the specified name. 201 * 202 * @param viewName The post ViewPort name to remove 203 * @return True if the ViewPort was removed successfully. 204 * 205 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 206 */ 207 public boolean removePostView(String viewName) { 208 for (int i = 0; i < postViewPorts.size(); i++) { 209 if (postViewPorts.get(i).getName().equals(viewName)) { 210 postViewPorts.remove(i); 211 212 return true; 213 } 214 } 215 return false; 216 } 217 218 /** 219 * Removes the specified post ViewPort. 220 * 221 * @param view The post ViewPort to remove 222 * @return True if the ViewPort was removed successfully. 223 * 224 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 225 */ 226 public boolean removePostView(ViewPort view) { 227 return postViewPorts.remove(view); 228 } 229 230 /** 231 * Returns a read-only list of all pre ViewPorts 232 * @return a read-only list of all pre ViewPorts 233 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera) 234 */ 235 public List<ViewPort> getPreViews() { 236 return Collections.unmodifiableList(preViewPorts); 237 } 238 239 /** 240 * Returns a read-only list of all main ViewPorts 241 * @return a read-only list of all main ViewPorts 242 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera) 243 */ 244 public List<ViewPort> getMainViews() { 245 return Collections.unmodifiableList(viewPorts); 246 } 247 248 /** 249 * Returns a read-only list of all post ViewPorts 250 * @return a read-only list of all post ViewPorts 251 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera) 252 */ 253 public List<ViewPort> getPostViews() { 254 return Collections.unmodifiableList(postViewPorts); 255 } 256 257 /** 258 * Creates a new pre ViewPort, to display the given camera's content. 259 * <p> 260 * The view will be processed before the main and post viewports. 261 */ 262 public ViewPort createPreView(String viewName, Camera cam) { 263 ViewPort vp = new ViewPort(viewName, cam); 264 preViewPorts.add(vp); 265 return vp; 266 } 267 268 /** 269 * Creates a new main ViewPort, to display the given camera's content. 270 * <p> 271 * The view will be processed before the post viewports but after 272 * the pre viewports. 273 */ 274 public ViewPort createMainView(String viewName, Camera cam) { 275 ViewPort vp = new ViewPort(viewName, cam); 276 viewPorts.add(vp); 277 return vp; 278 } 279 280 /** 281 * Creates a new post ViewPort, to display the given camera's content. 282 * <p> 283 * The view will be processed after the pre and main viewports. 284 */ 285 public ViewPort createPostView(String viewName, Camera cam) { 286 ViewPort vp = new ViewPort(viewName, cam); 287 postViewPorts.add(vp); 288 return vp; 289 } 290 291 private void notifyReshape(ViewPort vp, int w, int h) { 292 List<SceneProcessor> processors = vp.getProcessors(); 293 for (SceneProcessor proc : processors) { 294 if (!proc.isInitialized()) { 295 proc.initialize(this, vp); 296 } else { 297 proc.reshape(vp, w, h); 298 } 299 } 300 } 301 302 /** 303 * Internal use only. 304 * Updates the resolution of all on-screen cameras to match 305 * the given width and height. 306 */ 307 public void notifyReshape(int w, int h) { 308 for (ViewPort vp : preViewPorts) { 309 if (vp.getOutputFrameBuffer() == null) { 310 Camera cam = vp.getCamera(); 311 cam.resize(w, h, true); 312 } 313 notifyReshape(vp, w, h); 314 } 315 for (ViewPort vp : viewPorts) { 316 if (vp.getOutputFrameBuffer() == null) { 317 Camera cam = vp.getCamera(); 318 cam.resize(w, h, true); 319 } 320 notifyReshape(vp, w, h); 321 } 322 for (ViewPort vp : postViewPorts) { 323 if (vp.getOutputFrameBuffer() == null) { 324 Camera cam = vp.getCamera(); 325 cam.resize(w, h, true); 326 } 327 notifyReshape(vp, w, h); 328 } 329 } 330 331 /** 332 * Internal use only. 333 * Updates the given list of uniforms with {@link UniformBinding uniform bindings} 334 * based on the current world state. 335 */ 336 public void updateUniformBindings(List<Uniform> params) { 337 // assums worldMatrix is properly set. 338 TempVars vars = TempVars.get(); 339 340 Matrix4f tempMat4 = vars.tempMat4; 341 Matrix3f tempMat3 = vars.tempMat3; 342 Vector2f tempVec2 = vars.vect2d; 343 Quaternion tempVec4 = vars.quat1; 344 345 for (int i = 0; i < params.size(); i++) { 346 Uniform u = params.get(i); 347 switch (u.getBinding()) { 348 case WorldMatrix: 349 u.setValue(VarType.Matrix4, worldMatrix); 350 break; 351 case ViewMatrix: 352 u.setValue(VarType.Matrix4, viewMatrix); 353 break; 354 case ProjectionMatrix: 355 u.setValue(VarType.Matrix4, projMatrix); 356 break; 357 case ViewProjectionMatrix: 358 u.setValue(VarType.Matrix4, viewProjMatrix); 359 break; 360 case WorldViewMatrix: 361 tempMat4.set(viewMatrix); 362 tempMat4.multLocal(worldMatrix); 363 u.setValue(VarType.Matrix4, tempMat4); 364 break; 365 case NormalMatrix: 366 tempMat4.set(viewMatrix); 367 tempMat4.multLocal(worldMatrix); 368 tempMat4.toRotationMatrix(tempMat3); 369 tempMat3.invertLocal(); 370 tempMat3.transposeLocal(); 371 u.setValue(VarType.Matrix3, tempMat3); 372 break; 373 case WorldViewProjectionMatrix: 374 tempMat4.set(viewProjMatrix); 375 tempMat4.multLocal(worldMatrix); 376 u.setValue(VarType.Matrix4, tempMat4); 377 break; 378 case WorldMatrixInverse: 379 tempMat4.set(worldMatrix); 380 tempMat4.invertLocal(); 381 u.setValue(VarType.Matrix4, tempMat4); 382 break; 383 case WorldMatrixInverseTranspose: 384 worldMatrix.toRotationMatrix(tempMat3); 385 tempMat3.invertLocal().transposeLocal(); 386 u.setValue(VarType.Matrix3, tempMat3); 387 break; 388 case ViewMatrixInverse: 389 tempMat4.set(viewMatrix); 390 tempMat4.invertLocal(); 391 u.setValue(VarType.Matrix4, tempMat4); 392 break; 393 case ProjectionMatrixInverse: 394 tempMat4.set(projMatrix); 395 tempMat4.invertLocal(); 396 u.setValue(VarType.Matrix4, tempMat4); 397 break; 398 case ViewProjectionMatrixInverse: 399 tempMat4.set(viewProjMatrix); 400 tempMat4.invertLocal(); 401 u.setValue(VarType.Matrix4, tempMat4); 402 break; 403 case WorldViewMatrixInverse: 404 tempMat4.set(viewMatrix); 405 tempMat4.multLocal(worldMatrix); 406 tempMat4.invertLocal(); 407 u.setValue(VarType.Matrix4, tempMat4); 408 break; 409 case NormalMatrixInverse: 410 tempMat4.set(viewMatrix); 411 tempMat4.multLocal(worldMatrix); 412 tempMat4.toRotationMatrix(tempMat3); 413 tempMat3.invertLocal(); 414 tempMat3.transposeLocal(); 415 tempMat3.invertLocal(); 416 u.setValue(VarType.Matrix3, tempMat3); 417 break; 418 case WorldViewProjectionMatrixInverse: 419 tempMat4.set(viewProjMatrix); 420 tempMat4.multLocal(worldMatrix); 421 tempMat4.invertLocal(); 422 u.setValue(VarType.Matrix4, tempMat4); 423 break; 424 case ViewPort: 425 tempVec4.set(viewX, viewY, viewWidth, viewHeight); 426 u.setValue(VarType.Vector4, tempVec4); 427 break; 428 case Resolution: 429 tempVec2.set(viewWidth, viewHeight); 430 u.setValue(VarType.Vector2, tempVec2); 431 break; 432 case ResolutionInverse: 433 tempVec2.set(1f / viewWidth, 1f / viewHeight); 434 u.setValue(VarType.Vector2, tempVec2); 435 break; 436 case Aspect: 437 float aspect = ((float) viewWidth) / viewHeight; 438 u.setValue(VarType.Float, aspect); 439 break; 440 case FrustumNearFar: 441 tempVec2.set(near, far); 442 u.setValue(VarType.Vector2, tempVec2); 443 break; 444 case CameraPosition: 445 u.setValue(VarType.Vector3, camLoc); 446 break; 447 case CameraDirection: 448 u.setValue(VarType.Vector3, camDir); 449 break; 450 case CameraLeft: 451 u.setValue(VarType.Vector3, camLeft); 452 break; 453 case CameraUp: 454 u.setValue(VarType.Vector3, camUp); 455 break; 456 case Time: 457 u.setValue(VarType.Float, timer.getTimeInSeconds()); 458 break; 459 case Tpf: 460 u.setValue(VarType.Float, timer.getTimePerFrame()); 461 break; 462 case FrameRate: 463 u.setValue(VarType.Float, timer.getFrameRate()); 464 break; 465 } 466 } 467 468 vars.release(); 469 } 470 471 /** 472 * Set the material to use to render all future objects. 473 * This overrides the material set on the geometry and renders 474 * with the provided material instead. 475 * Use null to clear the material and return renderer to normal 476 * functionality. 477 * @param mat The forced material to set, or null to return to normal 478 */ 479 public void setForcedMaterial(Material mat) { 480 forcedMaterial = mat; 481 } 482 483 /** 484 * Returns the forced render state previously set with 485 * {@link #setForcedRenderState(com.jme3.material.RenderState) }. 486 * @return the forced render state 487 */ 488 public RenderState getForcedRenderState() { 489 return forcedRenderState; 490 } 491 492 /** 493 * Set the render state to use for all future objects. 494 * This overrides the render state set on the material and instead 495 * forces this render state to be applied for all future materials 496 * rendered. Set to null to return to normal functionality. 497 * 498 * @param forcedRenderState The forced render state to set, or null 499 * to return to normal 500 */ 501 public void setForcedRenderState(RenderState forcedRenderState) { 502 this.forcedRenderState = forcedRenderState; 503 } 504 505 /** 506 * Set the timer that should be used to query the time based 507 * {@link UniformBinding}s for material world parameters. 508 * 509 * @param timer The timer to query time world parameters 510 */ 511 public void setTimer(Timer timer) { 512 this.timer = timer; 513 } 514 515 /** 516 * Returns the forced technique name set. 517 * 518 * @return the forced technique name set. 519 * 520 * @see #setForcedTechnique(java.lang.String) 521 */ 522 public String getForcedTechnique() { 523 return forcedTechnique; 524 } 525 526 /** 527 * Sets the forced technique to use when rendering geometries. 528 * <p> 529 * If the specified technique name is available on the geometry's 530 * material, then it is used, otherwise, the 531 * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used. 532 * If a forced material is not set and the forced technique name cannot 533 * be found on the material, the geometry will <em>not</em> be rendered. 534 * 535 * @param forcedTechnique The forced technique name to use, set to null 536 * to return to normal functionality. 537 * 538 * @see #renderGeometry(com.jme3.scene.Geometry) 539 */ 540 public void setForcedTechnique(String forcedTechnique) { 541 this.forcedTechnique = forcedTechnique; 542 } 543 544 /** 545 * Enable or disable alpha-to-coverage. 546 * <p> 547 * When alpha to coverage is enabled and the renderer implementation 548 * supports it, then alpha blending will be replaced with alpha dissolve 549 * if multi-sampling is also set on the renderer. 550 * This feature allows avoiding of alpha blending artifacts due to 551 * lack of triangle-level back-to-front sorting. 552 * 553 * @param value True to enable alpha-to-coverage, false otherwise. 554 */ 555 public void setAlphaToCoverage(boolean value) { 556 renderer.setAlphaToCoverage(value); 557 } 558 559 /** 560 * True if the translucent bucket should automatically be rendered 561 * by the RenderManager. 562 * 563 * @return Whether or not the translucent bucket is rendered. 564 * 565 * @see #setHandleTranslucentBucket(boolean) 566 */ 567 public boolean isHandleTranslucentBucket() { 568 return handleTranlucentBucket; 569 } 570 571 /** 572 * Enable or disable rendering of the 573 * {@link Bucket#Translucent translucent bucket} 574 * by the RenderManager. The default is enabled. 575 * 576 * @param handleTranslucentBucket Whether or not the translucent bucket should 577 * be rendered. 578 */ 579 public void setHandleTranslucentBucket(boolean handleTranslucentBucket) { 580 this.handleTranlucentBucket = handleTranslucentBucket; 581 } 582 583 /** 584 * Internal use only. Sets the world matrix to use for future 585 * rendering. This has no effect unless objects are rendered manually 586 * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }. 587 * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will 588 * override this value. 589 * 590 * @param mat The world matrix to set 591 */ 592 public void setWorldMatrix(Matrix4f mat) { 593 if (shader) { 594 worldMatrix.set(mat); 595 } else { 596 renderer.setWorldMatrix(mat); 597 } 598 } 599 600 /** 601 * Renders the given geometry. 602 * <p> 603 * First the proper world matrix is set, if 604 * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform} 605 * feature is enabled, the identity world matrix is used, otherwise, the 606 * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used. 607 * <p> 608 * Once the world matrix is applied, the proper material is chosen for rendering. 609 * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is 610 * set on this RenderManager, then it is used for rendering the geometry, 611 * otherwise, the {@link Geometry#getMaterial() geometry's material} is used. 612 * <p> 613 * If a {@link #setForcedTechnique(java.lang.String) forced technique} is 614 * set on this RenderManager, then it is selected automatically 615 * on the geometry's material and is used for rendering. Otherwise, one 616 * of the {@link MaterialDef#getDefaultTechniques() default techniques} is 617 * used. 618 * <p> 619 * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced 620 * render state} is set on this RenderManager, then it is used 621 * for rendering the material, and the material's own render state is ignored. 622 * Otherwise, the material's render state is used as intended. 623 * 624 * @param g The geometry to render 625 * 626 * @see Technique 627 * @see RenderState 628 * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) 629 * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) 630 */ 631 public void renderGeometry(Geometry g) { 632 if (g.isIgnoreTransform()) { 633 setWorldMatrix(Matrix4f.IDENTITY); 634 } else { 635 setWorldMatrix(g.getWorldMatrix()); 636 } 637 638 //if forcedTechnique we try to force it for render, 639 //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null 640 //else the geom is not rendered 641 if (forcedTechnique != null) { 642 if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) { 643 tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default"; 644 g.getMaterial().selectTechnique(forcedTechnique, this); 645 // use geometry's material 646 g.getMaterial().render(g, this); 647 g.getMaterial().selectTechnique(tmpTech, this); 648 //Reverted this part from revision 6197 649 //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered 650 } else if (forcedMaterial != null) { 651 // use forced material 652 forcedMaterial.render(g, this); 653 } 654 } else if (forcedMaterial != null) { 655 // use forced material 656 forcedMaterial.render(g, this); 657 } else { 658 g.getMaterial().render(g, this); 659 } 660 } 661 662 /** 663 * Renders the given GeometryList. 664 * <p> 665 * For every geometry in the list, the 666 * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called. 667 * 668 * @param gl The geometry list to render. 669 * 670 * @see GeometryList 671 * @see #renderGeometry(com.jme3.scene.Geometry) 672 */ 673 public void renderGeometryList(GeometryList gl) { 674 for (int i = 0; i < gl.size(); i++) { 675 renderGeometry(gl.get(i)); 676 } 677 } 678 679 /** 680 * If a spatial is not inside the eye frustum, it 681 * is still rendered in the shadow frustum (shadow casting queue) 682 * through this recursive method. 683 */ 684 private void renderShadow(Spatial s, RenderQueue rq) { 685 if (s instanceof Node) { 686 Node n = (Node) s; 687 List<Spatial> children = n.getChildren(); 688 for (int i = 0; i < children.size(); i++) { 689 renderShadow(children.get(i), rq); 690 } 691 } else if (s instanceof Geometry) { 692 Geometry gm = (Geometry) s; 693 694 RenderQueue.ShadowMode shadowMode = s.getShadowMode(); 695 if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) { 696 //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue 697 rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast); 698 } 699 } 700 } 701 702 /** 703 * Preloads a scene for rendering. 704 * <p> 705 * After invocation of this method, the underlying 706 * renderer would have uploaded any textures, shaders and meshes 707 * used by the given scene to the video driver. 708 * Using this method is useful when wishing to avoid the initial pause 709 * when rendering a scene for the first time. Note that it is not 710 * guaranteed that the underlying renderer will actually choose to upload 711 * the data to the GPU so some pause is still to be expected. 712 * 713 * @param scene The scene to preload 714 */ 715 public void preloadScene(Spatial scene) { 716 if (scene instanceof Node) { 717 // recurse for all children 718 Node n = (Node) scene; 719 List<Spatial> children = n.getChildren(); 720 for (int i = 0; i < children.size(); i++) { 721 preloadScene(children.get(i)); 722 } 723 } else if (scene instanceof Geometry) { 724 // add to the render queue 725 Geometry gm = (Geometry) scene; 726 if (gm.getMaterial() == null) { 727 throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); 728 } 729 730 gm.getMaterial().preload(this); 731 Mesh mesh = gm.getMesh(); 732 if (mesh != null) { 733 for (VertexBuffer vb : mesh.getBufferList().getArray()) { 734 if (vb.getData() != null) { 735 renderer.updateBufferData(vb); 736 } 737 } 738 } 739 } 740 } 741 742 /** 743 * Flattens the given scene graph into the ViewPort's RenderQueue, 744 * checking for culling as the call goes down the graph recursively. 745 * <p> 746 * First, the scene is checked for culling based on the <code>Spatial</code>s 747 * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint}, 748 * if the camera frustum contains the scene, then this method is recursively 749 * called on its children. 750 * <p> 751 * When the scene's leaves or {@link Geometry geometries} are reached, 752 * they are each enqueued into the 753 * {@link ViewPort#getQueue() ViewPort's render queue}. 754 * <p> 755 * In addition to enqueuing the visible geometries, this method 756 * also scenes which cast or receive shadows, by putting them into the 757 * RenderQueue's 758 * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode) 759 * shadow queue}. Each Spatial which has its 760 * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode} 761 * set to not off, will be put into the appropriate shadow queue, note that 762 * this process does not check for frustum culling on any 763 * {@link ShadowMode#Cast shadow casters}, as they don't have to be 764 * in the eye camera frustum to cast shadows on objects that are inside it. 765 * 766 * @param scene The scene to flatten into the queue 767 * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera} 768 * used for culling and the {@link ViewPort#getQueue() queue} used to 769 * contain the flattened scene graph. 770 */ 771 public void renderScene(Spatial scene, ViewPort vp) { 772 if (scene.getParent() == null) { 773 vp.getCamera().setPlaneState(0); 774 } 775 // check culling first. 776 if (!scene.checkCulling(vp.getCamera())) { 777 // move on to shadow-only render 778 if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) { 779 renderShadow(scene, vp.getQueue()); 780 } 781 return; 782 } 783 784 scene.runControlRender(this, vp); 785 if (scene instanceof Node) { 786 // recurse for all children 787 Node n = (Node) scene; 788 List<Spatial> children = n.getChildren(); 789 //saving cam state for culling 790 int camState = vp.getCamera().getPlaneState(); 791 for (int i = 0; i < children.size(); i++) { 792 //restoring cam state before proceeding children recusively 793 vp.getCamera().setPlaneState(camState); 794 renderScene(children.get(i), vp); 795 796 } 797 } else if (scene instanceof Geometry) { 798 799 // add to the render queue 800 Geometry gm = (Geometry) scene; 801 if (gm.getMaterial() == null) { 802 throw new IllegalStateException("No material is set for Geometry: " + gm.getName()); 803 } 804 805 vp.getQueue().addToQueue(gm, scene.getQueueBucket()); 806 807 // add to shadow queue if needed 808 RenderQueue.ShadowMode shadowMode = scene.getShadowMode(); 809 if (shadowMode != RenderQueue.ShadowMode.Off) { 810 vp.getQueue().addToShadowQueue(gm, shadowMode); 811 } 812 } 813 } 814 815 /** 816 * Returns the camera currently used for rendering. 817 * <p> 818 * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }. 819 * 820 * @return the camera currently used for rendering. 821 */ 822 public Camera getCurrentCamera() { 823 return prevCam; 824 } 825 826 /** 827 * The renderer implementation used for rendering operations. 828 * 829 * @return The renderer implementation 830 * 831 * @see #RenderManager(com.jme3.renderer.Renderer) 832 * @see Renderer 833 */ 834 public Renderer getRenderer() { 835 return renderer; 836 } 837 838 /** 839 * Flushes the ViewPort's {@link ViewPort#getQueue() render queue} 840 * by rendering each of its visible buckets. 841 * By default the queues will automatically be cleared after rendering, 842 * so there's no need to clear them manually. 843 * 844 * @param vp The ViewPort of which the queue will be flushed 845 * 846 * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera) 847 * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList) 848 */ 849 public void flushQueue(ViewPort vp) { 850 renderViewPortQueues(vp, true); 851 } 852 853 /** 854 * Clears the queue of the given ViewPort. 855 * Simply calls {@link RenderQueue#clear() } on the ViewPort's 856 * {@link ViewPort#getQueue() render queue}. 857 * 858 * @param vp The ViewPort of which the queue will be cleared. 859 * 860 * @see RenderQueue#clear() 861 * @see ViewPort#getQueue() 862 */ 863 public void clearQueue(ViewPort vp) { 864 vp.getQueue().clear(); 865 } 866 867 /** 868 * Render the given viewport queues. 869 * <p> 870 * Changes the {@link Renderer#setDepthRange(float, float) depth range} 871 * appropriately as expected by each queue and then calls 872 * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) } 873 * on the queue. Makes sure to restore the depth range to [0, 1] 874 * at the end of the call. 875 * Note that the {@link Bucket#Translucent translucent bucket} is NOT 876 * rendered by this method. Instead the user should call 877 * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) } 878 * after this call. 879 * 880 * @param vp the viewport of which queue should be rendered 881 * @param flush If true, the queues will be cleared after 882 * rendering. 883 * 884 * @see RenderQueue 885 * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort) 886 */ 887 public void renderViewPortQueues(ViewPort vp, boolean flush) { 888 RenderQueue rq = vp.getQueue(); 889 Camera cam = vp.getCamera(); 890 boolean depthRangeChanged = false; 891 892 // render opaque objects with default depth range 893 // opaque objects are sorted front-to-back, reducing overdraw 894 rq.renderQueue(Bucket.Opaque, this, cam, flush); 895 896 // render the sky, with depth range set to the farthest 897 if (!rq.isQueueEmpty(Bucket.Sky)) { 898 renderer.setDepthRange(1, 1); 899 rq.renderQueue(Bucket.Sky, this, cam, flush); 900 depthRangeChanged = true; 901 } 902 903 904 // transparent objects are last because they require blending with the 905 // rest of the scene's objects. Consequently, they are sorted 906 // back-to-front. 907 if (!rq.isQueueEmpty(Bucket.Transparent)) { 908 if (depthRangeChanged) { 909 renderer.setDepthRange(0, 1); 910 depthRangeChanged = false; 911 } 912 913 rq.renderQueue(Bucket.Transparent, this, cam, flush); 914 } 915 916 if (!rq.isQueueEmpty(Bucket.Gui)) { 917 renderer.setDepthRange(0, 0); 918 setCamera(cam, true); 919 rq.renderQueue(Bucket.Gui, this, cam, flush); 920 setCamera(cam, false); 921 depthRangeChanged = true; 922 } 923 924 // restore range to default 925 if (depthRangeChanged) { 926 renderer.setDepthRange(0, 1); 927 } 928 } 929 930 /** 931 * Renders the {@link Bucket#Translucent translucent queue} on the viewPort. 932 * <p> 933 * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) } 934 * is set to true. This method clears the translucent queue after rendering 935 * it. 936 * 937 * @param vp The viewport of which the translucent queue should be rendered. 938 * 939 * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) 940 * @see #setHandleTranslucentBucket(boolean) 941 */ 942 public void renderTranslucentQueue(ViewPort vp) { 943 RenderQueue rq = vp.getQueue(); 944 if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) { 945 rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true); 946 } 947 } 948 949 private void setViewPort(Camera cam) { 950 // this will make sure to update viewport only if needed 951 if (cam != prevCam || cam.isViewportChanged()) { 952 viewX = (int) (cam.getViewPortLeft() * cam.getWidth()); 953 viewY = (int) (cam.getViewPortBottom() * cam.getHeight()); 954 viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth()); 955 viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight()); 956 renderer.setViewPort(viewX, viewY, viewWidth, viewHeight); 957 renderer.setClipRect(viewX, viewY, viewWidth, viewHeight); 958 cam.clearViewportChanged(); 959 prevCam = cam; 960 961 // float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX); 962 // float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY); 963 // float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX); 964 // float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY); 965 // 966 // orthoMatrix.loadIdentity(); 967 // orthoMatrix.setTranslation(translateX, translateY, 0); 968 // orthoMatrix.setScale(scaleX, scaleY, 0); 969 970 orthoMatrix.loadIdentity(); 971 orthoMatrix.setTranslation(-1f, -1f, 0f); 972 orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f); 973 } 974 } 975 976 private void setViewProjection(Camera cam, boolean ortho) { 977 if (shader) { 978 if (ortho) { 979 viewMatrix.set(Matrix4f.IDENTITY); 980 projMatrix.set(orthoMatrix); 981 viewProjMatrix.set(orthoMatrix); 982 } else { 983 viewMatrix.set(cam.getViewMatrix()); 984 projMatrix.set(cam.getProjectionMatrix()); 985 viewProjMatrix.set(cam.getViewProjectionMatrix()); 986 } 987 988 camLoc.set(cam.getLocation()); 989 cam.getLeft(camLeft); 990 cam.getUp(camUp); 991 cam.getDirection(camDir); 992 993 near = cam.getFrustumNear(); 994 far = cam.getFrustumFar(); 995 } else { 996 if (ortho) { 997 renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix); 998 } else { 999 renderer.setViewProjectionMatrices(cam.getViewMatrix(), 1000 cam.getProjectionMatrix()); 1001 } 1002 1003 } 1004 } 1005 1006 /** 1007 * Set the camera to use for rendering. 1008 * <p> 1009 * First, the camera's 1010 * {@link Camera#setViewPort(float, float, float, float) view port parameters} 1011 * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and 1012 * {@link Camera#getProjectionMatrix() projection} matrices are set 1013 * on the renderer. If <code>ortho</code> is <code>true</code>, then 1014 * instead of using the camera's view and projection matrices, an ortho 1015 * matrix is computed and used instead of the view projection matrix. 1016 * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1) 1017 * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1). 1018 * 1019 * @param cam The camera to set 1020 * @param ortho True if to use orthographic projection (for GUI rendering), 1021 * false if to use the camera's view and projection matrices. 1022 */ 1023 public void setCamera(Camera cam, boolean ortho) { 1024 setViewPort(cam); 1025 setViewProjection(cam, ortho); 1026 } 1027 1028 /** 1029 * Draws the viewport but without notifying {@link SceneProcessor scene 1030 * processors} of any rendering events. 1031 * 1032 * @param vp The ViewPort to render 1033 * 1034 * @see #renderViewPort(com.jme3.renderer.ViewPort, float) 1035 */ 1036 public void renderViewPortRaw(ViewPort vp) { 1037 setCamera(vp.getCamera(), false); 1038 List<Spatial> scenes = vp.getScenes(); 1039 for (int i = scenes.size() - 1; i >= 0; i--) { 1040 renderScene(scenes.get(i), vp); 1041 } 1042 flushQueue(vp); 1043 } 1044 1045 /** 1046 * Renders the {@link ViewPort}. 1047 * <p> 1048 * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method 1049 * returns immediately. Otherwise, the ViewPort is rendered by 1050 * the following process:<br> 1051 * <ul> 1052 * <li>All {@link SceneProcessor scene processors} that are attached 1053 * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}. 1054 * </li> 1055 * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method 1056 * is called.</li> 1057 * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer} 1058 * is set on the Renderer</li> 1059 * <li>The camera is set on the renderer, including its view port parameters. 1060 * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li> 1061 * <li>Any buffers that the ViewPort requests to be cleared are cleared 1062 * and the {@link ViewPort#getBackgroundColor() background color} is set</li> 1063 * <li>Every scene that is attached to the ViewPort is flattened into 1064 * the ViewPort's render queue 1065 * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) }) 1066 * </li> 1067 * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) } 1068 * method is called.</li> 1069 * <li>The render queue is sorted and then flushed, sending 1070 * rendering commands to the underlying Renderer implementation. 1071 * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li> 1072 * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) } 1073 * method is called.</li> 1074 * <li>The translucent queue of the ViewPort is sorted and then flushed 1075 * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li> 1076 * <li>If any objects remained in the render queue, they are removed 1077 * from the queue. This is generally objects added to the 1078 * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) 1079 * shadow queue} 1080 * which were not rendered because of a missing shadow renderer.</li> 1081 * </ul> 1082 * 1083 * @param vp 1084 * @param tpf 1085 */ 1086 public void renderViewPort(ViewPort vp, float tpf) { 1087 if (!vp.isEnabled()) { 1088 return; 1089 } 1090 List<SceneProcessor> processors = vp.getProcessors(); 1091 if (processors.isEmpty()) { 1092 processors = null; 1093 } 1094 1095 if (processors != null) { 1096 for (SceneProcessor proc : processors) { 1097 if (!proc.isInitialized()) { 1098 proc.initialize(this, vp); 1099 } 1100 proc.preFrame(tpf); 1101 } 1102 } 1103 1104 renderer.setFrameBuffer(vp.getOutputFrameBuffer()); 1105 setCamera(vp.getCamera(), false); 1106 if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) { 1107 if (vp.isClearColor()) { 1108 renderer.setBackgroundColor(vp.getBackgroundColor()); 1109 } 1110 renderer.clearBuffers(vp.isClearColor(), 1111 vp.isClearDepth(), 1112 vp.isClearStencil()); 1113 } 1114 1115 List<Spatial> scenes = vp.getScenes(); 1116 for (int i = scenes.size() - 1; i >= 0; i--) { 1117 renderScene(scenes.get(i), vp); 1118 } 1119 1120 if (processors != null) { 1121 for (SceneProcessor proc : processors) { 1122 proc.postQueue(vp.getQueue()); 1123 } 1124 } 1125 1126 flushQueue(vp); 1127 1128 if (processors != null) { 1129 for (SceneProcessor proc : processors) { 1130 proc.postFrame(vp.getOutputFrameBuffer()); 1131 } 1132 } 1133 //renders the translucent objects queue after processors have been rendered 1134 renderTranslucentQueue(vp); 1135 // clear any remaining spatials that were not rendered. 1136 clearQueue(vp); 1137 } 1138 1139 /** 1140 * Called by the application to render any ViewPorts 1141 * added to this RenderManager. 1142 * <p> 1143 * Renders any viewports that were added using the following methods: 1144 * <ul> 1145 * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li> 1146 * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li> 1147 * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li> 1148 * </ul> 1149 * 1150 * @param tpf Time per frame value 1151 */ 1152 public void render(float tpf, boolean mainFrameBufferActive) { 1153 if (renderer instanceof NullRenderer) { 1154 return; 1155 } 1156 1157 this.shader = renderer.getCaps().contains(Caps.GLSL100); 1158 1159 for (int i = 0; i < preViewPorts.size(); i++) { 1160 ViewPort vp = preViewPorts.get(i); 1161 if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ 1162 renderViewPort(vp, tpf); 1163 } 1164 } 1165 for (int i = 0; i < viewPorts.size(); i++) { 1166 ViewPort vp = viewPorts.get(i); 1167 if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ 1168 renderViewPort(vp, tpf); 1169 } 1170 } 1171 for (int i = 0; i < postViewPorts.size(); i++) { 1172 ViewPort vp = postViewPorts.get(i); 1173 if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){ 1174 renderViewPort(vp, tpf); 1175 } 1176 } 1177 } 1178 } 1179