2 * Copyright (c) 2009-2010 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
32 package com.jme3.renderer;
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.Matrix3f;
39 import com.jme3.math.Matrix4f;
40 import com.jme3.math.Quaternion;
41 import com.jme3.math.Vector2f;
42 import com.jme3.math.Vector3f;
43 import com.jme3.post.SceneProcessor;
44 import com.jme3.renderer.queue.GeometryList;
45 import com.jme3.renderer.queue.RenderQueue;
46 import com.jme3.renderer.queue.RenderQueue.Bucket;
47 import com.jme3.renderer.queue.RenderQueue.ShadowMode;
48 import com.jme3.scene.Geometry;
49 import com.jme3.scene.Mesh;
50 import com.jme3.scene.Node;
51 import com.jme3.scene.Spatial;
52 import com.jme3.scene.VertexBuffer;
53 import com.jme3.shader.Uniform;
54 import com.jme3.shader.UniformBinding;
55 import com.jme3.shader.VarType;
56 import com.jme3.system.NullRenderer;
57 import com.jme3.system.Timer;
58 import com.jme3.util.IntMap.Entry;
59 import com.jme3.util.TempVars;
60 import java.util.ArrayList;
61 import java.util.Collections;
62 import java.util.List;
63 import java.util.logging.Logger;
66 * <code>RenderManager</code> is a high-level rendering interface that is
67 * above the Renderer implementation. RenderManager takes care
68 * of rendering the scene graphs attached to each viewport and
69 * handling SceneProcessors.
75 public class RenderManager {
77 private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
79 private Renderer renderer;
81 private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
82 private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
83 private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
84 private Camera prevCam = null;
85 private Material forcedMaterial = null;
86 private String forcedTechnique = null;
87 private RenderState forcedRenderState = null;
88 private boolean shader;
89 private int viewX, viewY, viewWidth, viewHeight;
90 private float near, far;
91 private Matrix4f orthoMatrix = new Matrix4f();
92 private Matrix4f viewMatrix = new Matrix4f();
93 private Matrix4f projMatrix = new Matrix4f();
94 private Matrix4f viewProjMatrix = new Matrix4f();
95 private Matrix4f worldMatrix = new Matrix4f();
96 private Vector3f camUp = new Vector3f(),
97 camLeft = new Vector3f(),
98 camDir = new Vector3f(),
99 camLoc = new Vector3f();
101 private String tmpTech;
102 private boolean handleTranlucentBucket = true;
105 * Create a high-level rendering interface over the
106 * low-level rendering interface.
109 public RenderManager(Renderer renderer) {
110 this.renderer = renderer;
111 //this.shader = renderer.getCaps().contains(Caps.GLSL100);
115 * Returns the pre ViewPort with the given name.
117 * @param viewName The name of the pre ViewPort to look up
118 * @return The ViewPort, or null if not found.
120 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
122 public ViewPort getPreView(String viewName) {
123 for (int i = 0; i < preViewPorts.size(); i++) {
124 if (preViewPorts.get(i).getName().equals(viewName)) {
125 return preViewPorts.get(i);
132 * Removes the specified pre ViewPort.
134 * @param view The pre ViewPort to remove
135 * @return True if the ViewPort was removed successfully.
137 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
139 public boolean removePreView(ViewPort view) {
140 return preViewPorts.remove(view);
144 * Returns the main ViewPort with the given name.
146 * @param viewName The name of the main ViewPort to look up
147 * @return The ViewPort, or null if not found.
149 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
151 public ViewPort getMainView(String viewName) {
152 for (int i = 0; i < viewPorts.size(); i++) {
153 if (viewPorts.get(i).getName().equals(viewName)) {
154 return viewPorts.get(i);
161 * Removes the main ViewPort with the specified name.
163 * @param view The main ViewPort name to remove
164 * @return True if the ViewPort was removed successfully.
166 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
168 public boolean removeMainView(String viewName) {
169 for (int i = 0; i < viewPorts.size(); i++) {
170 if (viewPorts.get(i).getName().equals(viewName)) {
179 * Removes the specified main ViewPort.
181 * @param view The main ViewPort to remove
182 * @return True if the ViewPort was removed successfully.
184 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
186 public boolean removeMainView(ViewPort view) {
187 return viewPorts.remove(view);
191 * Returns the post ViewPort with the given name.
193 * @param viewName The name of the post ViewPort to look up
194 * @return The ViewPort, or null if not found.
196 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
198 public ViewPort getPostView(String viewName) {
199 for (int i = 0; i < postViewPorts.size(); i++) {
200 if (postViewPorts.get(i).getName().equals(viewName)) {
201 return postViewPorts.get(i);
208 * Removes the post ViewPort with the specified name.
210 * @param view The post ViewPort name to remove
211 * @return True if the ViewPort was removed successfully.
213 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
215 public boolean removePostView(String viewName) {
216 for (int i = 0; i < postViewPorts.size(); i++) {
217 if (postViewPorts.get(i).getName().equals(viewName)) {
218 postViewPorts.remove(i);
227 * Removes the specified post ViewPort.
229 * @param view The post ViewPort to remove
230 * @return True if the ViewPort was removed successfully.
232 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
234 public boolean removePostView(ViewPort view) {
235 return postViewPorts.remove(view);
239 * Returns a read-only list of all pre ViewPorts
240 * @return a read-only list of all pre ViewPorts
241 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
243 public List<ViewPort> getPreViews() {
244 return Collections.unmodifiableList(preViewPorts);
248 * Returns a read-only list of all main ViewPorts
249 * @return a read-only list of all main ViewPorts
250 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
252 public List<ViewPort> getMainViews() {
253 return Collections.unmodifiableList(viewPorts);
257 * Returns a read-only list of all post ViewPorts
258 * @return a read-only list of all post ViewPorts
259 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
261 public List<ViewPort> getPostViews() {
262 return Collections.unmodifiableList(postViewPorts);
266 * Creates a new pre ViewPort, to display the given camera's content.
268 * The view will be processed before the main and post viewports.
270 public ViewPort createPreView(String viewName, Camera cam) {
271 ViewPort vp = new ViewPort(viewName, cam);
272 preViewPorts.add(vp);
277 * Creates a new main ViewPort, to display the given camera's content.
279 * The view will be processed before the post viewports but after
282 public ViewPort createMainView(String viewName, Camera cam) {
283 ViewPort vp = new ViewPort(viewName, cam);
289 * Creates a new post ViewPort, to display the given camera's content.
291 * The view will be processed after the pre and main viewports.
293 public ViewPort createPostView(String viewName, Camera cam) {
294 ViewPort vp = new ViewPort(viewName, cam);
295 postViewPorts.add(vp);
299 private void notifyReshape(ViewPort vp, int w, int h) {
300 List<SceneProcessor> processors = vp.getProcessors();
301 for (SceneProcessor proc : processors) {
302 if (!proc.isInitialized()) {
303 proc.initialize(this, vp);
305 proc.reshape(vp, w, h);
312 * Updates the resolution of all on-screen cameras to match
313 * the given width and height.
315 public void notifyReshape(int w, int h) {
316 for (ViewPort vp : preViewPorts) {
317 if (vp.getOutputFrameBuffer() == null) {
318 Camera cam = vp.getCamera();
319 cam.resize(w, h, true);
321 notifyReshape(vp, w, h);
323 for (ViewPort vp : viewPorts) {
324 if (vp.getOutputFrameBuffer() == null) {
325 Camera cam = vp.getCamera();
326 cam.resize(w, h, true);
328 notifyReshape(vp, w, h);
330 for (ViewPort vp : postViewPorts) {
331 if (vp.getOutputFrameBuffer() == null) {
332 Camera cam = vp.getCamera();
333 cam.resize(w, h, true);
335 notifyReshape(vp, w, h);
341 * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
342 * based on the current world state.
344 public void updateUniformBindings(List<Uniform> params) {
345 // assums worldMatrix is properly set.
346 TempVars vars = TempVars.get();
349 Matrix4f tempMat4 = vars.tempMat4;
350 Matrix3f tempMat3 = vars.tempMat3;
351 Vector2f tempVec2 = vars.vect2d;
352 Quaternion tempVec4 = vars.quat1;
354 for (int i = 0; i < params.size(); i++) {
355 Uniform u = params.get(i);
356 switch (u.getBinding()) {
358 u.setValue(VarType.Matrix4, worldMatrix);
361 u.setValue(VarType.Matrix4, viewMatrix);
363 case ProjectionMatrix:
364 u.setValue(VarType.Matrix4, projMatrix);
366 case ViewProjectionMatrix:
367 u.setValue(VarType.Matrix4, viewProjMatrix);
369 case WorldViewMatrix:
370 tempMat4.set(viewMatrix);
371 tempMat4.multLocal(worldMatrix);
372 u.setValue(VarType.Matrix4, tempMat4);
375 tempMat4.set(viewMatrix);
376 tempMat4.multLocal(worldMatrix);
377 tempMat4.toRotationMatrix(tempMat3);
378 tempMat3.invertLocal();
379 tempMat3.transposeLocal();
380 u.setValue(VarType.Matrix3, tempMat3);
382 case WorldViewProjectionMatrix:
383 tempMat4.set(viewProjMatrix);
384 tempMat4.multLocal(worldMatrix);
385 u.setValue(VarType.Matrix4, tempMat4);
387 case WorldMatrixInverse:
388 tempMat4.multLocal(worldMatrix);
389 tempMat4.invertLocal();
390 u.setValue(VarType.Matrix4, tempMat4);
392 case ViewMatrixInverse:
393 tempMat4.set(viewMatrix);
394 tempMat4.invertLocal();
395 u.setValue(VarType.Matrix4, tempMat4);
397 case ProjectionMatrixInverse:
398 tempMat4.set(projMatrix);
399 tempMat4.invertLocal();
400 u.setValue(VarType.Matrix4, tempMat4);
402 case ViewProjectionMatrixInverse:
403 tempMat4.set(viewProjMatrix);
404 tempMat4.invertLocal();
405 u.setValue(VarType.Matrix4, tempMat4);
407 case WorldViewMatrixInverse:
408 tempMat4.set(viewMatrix);
409 tempMat4.multLocal(worldMatrix);
410 tempMat4.invertLocal();
411 u.setValue(VarType.Matrix4, tempMat4);
413 case NormalMatrixInverse:
414 tempMat4.set(viewMatrix);
415 tempMat4.multLocal(worldMatrix);
416 tempMat4.toRotationMatrix(tempMat3);
417 tempMat3.invertLocal();
418 tempMat3.transposeLocal();
419 tempMat3.invertLocal();
420 u.setValue(VarType.Matrix3, tempMat3);
422 case WorldViewProjectionMatrixInverse:
423 tempMat4.set(viewProjMatrix);
424 tempMat4.multLocal(worldMatrix);
425 tempMat4.invertLocal();
426 u.setValue(VarType.Matrix4, tempMat4);
429 tempVec4.set(viewX, viewY, viewWidth, viewHeight);
430 u.setValue(VarType.Vector4, tempVec4);
433 tempVec2.set(viewWidth, viewHeight);
434 u.setValue(VarType.Vector2, tempVec2);
437 float aspect = ((float) viewWidth) / viewHeight;
438 u.setValue(VarType.Float, aspect);
441 tempVec2.set(near, far);
442 u.setValue(VarType.Vector2, tempVec2);
445 u.setValue(VarType.Vector3, camLoc);
447 case CameraDirection:
448 u.setValue(VarType.Vector3, camDir);
451 u.setValue(VarType.Vector3, camLeft);
454 u.setValue(VarType.Vector3, camUp);
457 u.setValue(VarType.Float, timer.getTimeInSeconds());
460 u.setValue(VarType.Float, timer.getTimePerFrame());
463 u.setValue(VarType.Float, timer.getFrameRate());
468 assert vars.unlock();
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
477 * @param mat The forced material to set, or null to return to normal
479 public void setForcedMaterial(Material mat) {
480 forcedMaterial = mat;
484 * Returns the forced render state previously set with
485 * {@link #setForcedRenderState(com.jme3.material.RenderState) }.
486 * @return the forced render state
488 public RenderState getForcedRenderState() {
489 return forcedRenderState;
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.
498 * @param forcedRenderState The forced render state to set, or null
499 * to return to normal
501 public void setForcedRenderState(RenderState forcedRenderState) {
502 this.forcedRenderState = forcedRenderState;
506 * Set the timer that should be used to query the time based
507 * {@link UniformBinding}s for material world parameters.
509 * @param timer The timer to query time world parameters
511 public void setTimer(Timer timer) {
516 * Returns the forced technique name set.
518 * @return the forced technique name set.
520 * @see #setForcedTechnique(java.lang.String)
522 public String getForcedTechnique() {
523 return forcedTechnique;
527 * Sets the forced technique to use when rendering geometries.
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.
535 * @param forcedTechnique The forced technique name to use, set to null
536 * to return to normal functionality.
538 * @see #renderGeometry(com.jme3.scene.Geometry)
540 public void setForcedTechnique(String forcedTechnique) {
541 this.forcedTechnique = forcedTechnique;
545 * Enable or disable alpha-to-coverage.
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.
553 * @param value True to enable alpha-to-coverage, false otherwise.
555 public void setAlphaToCoverage(boolean value) {
556 renderer.setAlphaToCoverage(value);
560 * True if the translucent bucket should automatically be rendered
561 * by the RenderManager.
563 * @return Whether or not the translucent bucket is rendered.
565 * @see #setHandleTranslucentBucket(boolean)
567 public boolean isHandleTranslucentBucket() {
568 return handleTranlucentBucket;
572 * Enable or disable rendering of the
573 * {@link Bucket#Translucent translucent bucket}
574 * by the RenderManager. The default is enabled.
576 * @param handleTranslucentBucket Whether or not the translucent bucket should
579 public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {
580 this.handleTranlucentBucket = handleTranslucentBucket;
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.
590 * @param mat The world matrix to set
592 public void setWorldMatrix(Matrix4f mat) {
594 worldMatrix.set(mat);
596 renderer.setWorldMatrix(mat);
601 * Renders the given geometry.
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.
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.
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
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.
624 * @param g The geometry to render
628 * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
629 * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
631 public void renderGeometry(Geometry g) {
632 if (g.isIgnoreTransform()) {
633 setWorldMatrix(Matrix4f.IDENTITY);
635 setWorldMatrix(g.getWorldMatrix());
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);
654 } else if (forcedMaterial != null) {
655 // use forced material
656 forcedMaterial.render(g, this);
658 g.getMaterial().render(g, this);
663 * Renders the given GeometryList.
665 * For every geometry in the list, the
666 * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.
668 * @param gl The geometry list to render.
671 * @see #renderGeometry(com.jme3.scene.Geometry)
673 public void renderGeometryList(GeometryList gl) {
674 for (int i = 0; i < gl.size(); i++) {
675 renderGeometry(gl.get(i));
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.
684 private void renderShadow(Spatial s, RenderQueue rq) {
685 if (s instanceof Node) {
687 List<Spatial> children = n.getChildren();
688 for (int i = 0; i < children.size(); i++) {
689 renderShadow(children.get(i), rq);
691 } else if (s instanceof Geometry) {
692 Geometry gm = (Geometry) s;
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);
703 * Preloads a scene for rendering.
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.
713 * @param scene The scene to preload
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));
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());
730 gm.getMaterial().preload(this);
731 Mesh mesh = gm.getMesh();
733 for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
734 VertexBuffer buf = entry.getValue();
735 if (buf.getData() != null) {
736 renderer.updateBufferData(buf);
744 * Flattens the given scene graph into the ViewPort's RenderQueue,
745 * checking for culling as the call goes down the graph recursively.
747 * First, the scene is checked for culling based on the <code>Spatial</code>s
748 * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},
749 * if the camera frustum contains the scene, then this method is recursively
750 * called on its children.
752 * When the scene's leaves or {@link Geometry geometries} are reached,
753 * they are each enqueued into the
754 * {@link ViewPort#getQueue() ViewPort's render queue}.
756 * In addition to enqueuing the visible geometries, this method
757 * also scenes which cast or receive shadows, by putting them into the
759 * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
760 * shadow queue}. Each Spatial which has its
761 * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
762 * set to not off, will be put into the appropriate shadow queue, note that
763 * this process does not check for frustum culling on any
764 * {@link ShadowMode#Cast shadow casters}, as they don't have to be
765 * in the eye camera frustum to cast shadows on objects that are inside it.
767 * @param scene The scene to flatten into the queue
768 * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}
769 * used for culling and the {@link ViewPort#getQueue() queue} used to
770 * contain the flattened scene graph.
772 public void renderScene(Spatial scene, ViewPort vp) {
773 if (scene.getParent() == null) {
774 vp.getCamera().setPlaneState(0);
776 // check culling first.
777 if (!scene.checkCulling(vp.getCamera())) {
778 // move on to shadow-only render
779 if (scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) {
780 renderShadow(scene, vp.getQueue());
785 scene.runControlRender(this, vp);
786 if (scene instanceof Node) {
787 // recurse for all children
788 Node n = (Node) scene;
789 List<Spatial> children = n.getChildren();
790 //saving cam state for culling
791 int camState = vp.getCamera().getPlaneState();
792 for (int i = 0; i < children.size(); i++) {
793 //restoring cam state before proceeding children recusively
794 vp.getCamera().setPlaneState(camState);
795 renderScene(children.get(i), vp);
798 } else if (scene instanceof Geometry) {
800 // add to the render queue
801 Geometry gm = (Geometry) scene;
802 if (gm.getMaterial() == null) {
803 throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
806 vp.getQueue().addToQueue(gm, scene.getQueueBucket());
808 // add to shadow queue if needed
809 RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
810 if (shadowMode != RenderQueue.ShadowMode.Off) {
811 vp.getQueue().addToShadowQueue(gm, shadowMode);
817 * Returns the camera currently used for rendering.
819 * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.
821 * @return the camera currently used for rendering.
823 public Camera getCurrentCamera() {
828 * The renderer implementation used for rendering operations.
830 * @return The renderer implementation
832 * @see #RenderManager(com.jme3.renderer.Renderer)
835 public Renderer getRenderer() {
840 * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}
841 * by rendering each of its visible buckets.
842 * By default the queues will automatically be cleared after rendering,
843 * so there's no need to clear them manually.
845 * @param vp The ViewPort of which the queue will be flushed
847 * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)
848 * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)
850 public void flushQueue(ViewPort vp) {
851 renderViewPortQueues(vp, true);
855 * Clears the queue of the given ViewPort.
856 * Simply calls {@link RenderQueue#clear() } on the ViewPort's
857 * {@link ViewPort#getQueue() render queue}.
859 * @param vp The ViewPort of which the queue will be cleared.
861 * @see RenderQueue#clear()
862 * @see ViewPort#getQueue()
864 public void clearQueue(ViewPort vp) {
865 vp.getQueue().clear();
869 * Render the given viewport queues.
871 * Changes the {@link Renderer#setDepthRange(float, float) depth range}
872 * appropriately as expected by each queue and then calls
873 * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }
874 * on the queue. Makes sure to restore the depth range to [0, 1]
875 * at the end of the call.
876 * Note that the {@link Bucket#Translucent translucent bucket} is NOT
877 * rendered by this method. Instead the user should call
878 * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }
881 * @param vp the viewport of which queue should be rendered
882 * @param flush If true, the queues will be cleared after
886 * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)
888 public void renderViewPortQueues(ViewPort vp, boolean flush) {
889 RenderQueue rq = vp.getQueue();
890 Camera cam = vp.getCamera();
891 boolean depthRangeChanged = false;
893 // render opaque objects with default depth range
894 // opaque objects are sorted front-to-back, reducing overdraw
895 rq.renderQueue(Bucket.Opaque, this, cam, flush);
897 // render the sky, with depth range set to the farthest
898 if (!rq.isQueueEmpty(Bucket.Sky)) {
899 renderer.setDepthRange(1, 1);
900 rq.renderQueue(Bucket.Sky, this, cam, flush);
901 depthRangeChanged = true;
905 // transparent objects are last because they require blending with the
906 // rest of the scene's objects. Consequently, they are sorted
908 if (!rq.isQueueEmpty(Bucket.Transparent)) {
909 if (depthRangeChanged) {
910 renderer.setDepthRange(0, 1);
911 depthRangeChanged = false;
914 rq.renderQueue(Bucket.Transparent, this, cam, flush);
917 if (!rq.isQueueEmpty(Bucket.Gui)) {
918 renderer.setDepthRange(0, 0);
919 setCamera(cam, true);
920 rq.renderQueue(Bucket.Gui, this, cam, flush);
921 setCamera(cam, false);
922 depthRangeChanged = true;
925 // restore range to default
926 if (depthRangeChanged) {
927 renderer.setDepthRange(0, 1);
932 * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.
934 * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }
935 * is set to true. This method clears the translucent queue after rendering
938 * @param vp The viewport of which the translucent queue should be rendered.
940 * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)
941 * @see #setHandleTranslucentBucket(boolean)
943 public void renderTranslucentQueue(ViewPort vp) {
944 RenderQueue rq = vp.getQueue();
945 if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
946 rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
950 private void setViewPort(Camera cam) {
951 // this will make sure to update viewport only if needed
952 if (cam != prevCam || cam.isViewportChanged()) {
953 viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
954 viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
955 viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
956 viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
957 renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
958 renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
959 cam.clearViewportChanged();
962 // float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
963 // float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
964 // float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
965 // float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
967 // orthoMatrix.loadIdentity();
968 // orthoMatrix.setTranslation(translateX, translateY, 0);
969 // orthoMatrix.setScale(scaleX, scaleY, 0);
971 orthoMatrix.loadIdentity();
972 orthoMatrix.setTranslation(-1f, -1f, 0f);
973 orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
977 private void setViewProjection(Camera cam, boolean ortho) {
980 viewMatrix.set(Matrix4f.IDENTITY);
981 projMatrix.set(orthoMatrix);
982 viewProjMatrix.set(orthoMatrix);
984 viewMatrix.set(cam.getViewMatrix());
985 projMatrix.set(cam.getProjectionMatrix());
986 viewProjMatrix.set(cam.getViewProjectionMatrix());
989 camLoc.set(cam.getLocation());
990 cam.getLeft(camLeft);
992 cam.getDirection(camDir);
994 near = cam.getFrustumNear();
995 far = cam.getFrustumFar();
998 renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
1000 renderer.setViewProjectionMatrices(cam.getViewMatrix(),
1001 cam.getProjectionMatrix());
1008 * Set the camera to use for rendering.
1010 * First, the camera's
1011 * {@link Camera#setViewPort(float, float, float, float) view port parameters}
1012 * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and
1013 * {@link Camera#getProjectionMatrix() projection} matrices are set
1014 * on the renderer. If <code>ortho</code> is <code>true</code>, then
1015 * instead of using the camera's view and projection matrices, an ortho
1016 * matrix is computed and used instead of the view projection matrix.
1017 * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)
1018 * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).
1020 * @param cam The camera to set
1021 * @param ortho True if to use orthographic projection (for GUI rendering),
1022 * false if to use the camera's view and projection matrices.
1024 public void setCamera(Camera cam, boolean ortho) {
1026 setViewProjection(cam, ortho);
1030 * Draws the viewport but without notifying {@link SceneProcessor scene
1031 * processors} of any rendering events.
1033 * @param vp The ViewPort to render
1035 * @see #renderViewPort(com.jme3.renderer.ViewPort, float)
1037 public void renderViewPortRaw(ViewPort vp) {
1038 setCamera(vp.getCamera(), false);
1039 List<Spatial> scenes = vp.getScenes();
1040 for (int i = scenes.size() - 1; i >= 0; i--) {
1041 renderScene(scenes.get(i), vp);
1047 * Renders the {@link ViewPort}.
1049 * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method
1050 * returns immediately. Otherwise, the ViewPort is rendered by
1051 * the following process:<br>
1053 * <li>All {@link SceneProcessor scene processors} that are attached
1054 * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.
1056 * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method
1058 * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}
1059 * is set on the Renderer</li>
1060 * <li>The camera is set on the renderer, including its view port parameters.
1061 * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>
1062 * <li>Any buffers that the ViewPort requests to be cleared are cleared
1063 * and the {@link ViewPort#getBackgroundColor() background color} is set</li>
1064 * <li>Every scene that is attached to the ViewPort is flattened into
1065 * the ViewPort's render queue
1066 * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })
1068 * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }
1069 * method is called.</li>
1070 * <li>The render queue is sorted and then flushed, sending
1071 * rendering commands to the underlying Renderer implementation.
1072 * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>
1073 * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }
1074 * method is called.</li>
1075 * <li>The translucent queue of the ViewPort is sorted and then flushed
1076 * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
1077 * <li>If any objects remained in the render queue, they are removed
1078 * from the queue. This is generally objects added to the
1079 * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
1081 * which were not rendered because of a missing shadow renderer.</li>
1087 public void renderViewPort(ViewPort vp, float tpf) {
1088 if (!vp.isEnabled()) {
1091 List<SceneProcessor> processors = vp.getProcessors();
1092 if (processors.isEmpty()) {
1096 if (processors != null) {
1097 for (SceneProcessor proc : processors) {
1098 if (!proc.isInitialized()) {
1099 proc.initialize(this, vp);
1105 renderer.setFrameBuffer(vp.getOutputFrameBuffer());
1106 setCamera(vp.getCamera(), false);
1107 if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
1108 if (vp.isClearColor()) {
1109 renderer.setBackgroundColor(vp.getBackgroundColor());
1111 renderer.clearBuffers(vp.isClearColor(),
1113 vp.isClearStencil());
1116 List<Spatial> scenes = vp.getScenes();
1117 for (int i = scenes.size() - 1; i >= 0; i--) {
1118 renderScene(scenes.get(i), vp);
1121 if (processors != null) {
1122 for (SceneProcessor proc : processors) {
1123 proc.postQueue(vp.getQueue());
1129 if (processors != null) {
1130 for (SceneProcessor proc : processors) {
1131 proc.postFrame(vp.getOutputFrameBuffer());
1134 //renders the translucent objects queue after processors have been rendered
1135 renderTranslucentQueue(vp);
1136 // clear any remaining spatials that were not rendered.
1141 * Called by the application to render any ViewPorts
1142 * added to this RenderManager.
1144 * Renders any viewports that were added using the following methods:
1146 * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
1147 * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
1148 * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
1151 * @param tpf Time per frame value
1153 public void render(float tpf) {
1154 if (renderer instanceof NullRenderer) {
1158 this.shader = renderer.getCaps().contains(Caps.GLSL100);
1160 for (int i = 0; i < preViewPorts.size(); i++) {
1161 renderViewPort(preViewPorts.get(i), tpf);
1163 for (int i = 0; i < viewPorts.size(); i++) {
1164 renderViewPort(viewPorts.get(i), tpf);
1166 for (int i = 0; i < postViewPorts.size(); i++) {
1167 renderViewPort(postViewPorts.get(i), tpf);