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.BufferUtils;
59 import com.jme3.util.IntMap.Entry;
60 import com.jme3.util.TempVars;
61 import java.nio.FloatBuffer;
62 import java.util.ArrayList;
63 import java.util.Collections;
64 import java.util.List;
65 import java.util.logging.Logger;
68 * <code>RenderManager</code> is a high-level rendering interface that is
69 * above the Renderer implementation. RenderManager takes care
70 * of rendering the scene graphs attached to each viewport and
71 * handling SceneProcessors.
77 public class RenderManager {
79 private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
80 private Renderer renderer;
82 private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
83 private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
84 private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
85 private Camera prevCam = null;
86 private Material forcedMaterial = null;
87 private String forcedTechnique = null;
88 private RenderState forcedRenderState = null;
89 private boolean shader;
90 private int viewX, viewY, viewWidth, viewHeight;
91 private float near, far;
92 private Matrix4f orthoMatrix = new Matrix4f();
93 // private FloatBuffer orthoMatrixBuf = BufferUtils.createFloatBuffer(16);
94 private Matrix4f viewMatrix = new Matrix4f();
95 private FloatBuffer viewMatrixBuf = BufferUtils.createFloatBuffer(16);
96 private Matrix4f projMatrix = new Matrix4f();
97 private FloatBuffer projMatrixBuf = BufferUtils.createFloatBuffer(16);
98 private Matrix4f viewProjMatrix = new Matrix4f();
99 private FloatBuffer viewProjMatrixBuf = BufferUtils.createFloatBuffer(16);
100 private Matrix4f worldMatrix = new Matrix4f();
101 private FloatBuffer worldMatrixBuf = BufferUtils.createFloatBuffer(16);
102 private FloatBuffer worldViewMatrixBuf = BufferUtils.createFloatBuffer(16);
103 private boolean worldViewMatrixBufDirty = true;
104 private FloatBuffer worldViewProjectionMatrixBuf = BufferUtils.createFloatBuffer(16);
105 private boolean worldViewProjectionMatrixBufDirty = true;
106 private FloatBuffer normalMatrixBuf = BufferUtils.createFloatBuffer(9);
107 private boolean normalMatrixBufDirty = true;
108 private Vector3f camUp = new Vector3f(),
109 camLeft = new Vector3f(),
110 camDir = new Vector3f(),
111 camLoc = new Vector3f();
113 private String tmpTech;
114 private boolean handleTranlucentBucket = true;
117 * Create a high-level rendering interface over the
118 * low-level rendering interface.
121 public RenderManager(Renderer renderer) {
122 this.renderer = renderer;
123 //this.shader = renderer.getCaps().contains(Caps.GLSL100);
127 * Returns the pre ViewPort with the given name.
129 * @param viewName The name of the pre ViewPort to look up
130 * @return The ViewPort, or null if not found.
132 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
134 public ViewPort getPreView(String viewName) {
135 for (int i = 0; i < preViewPorts.size(); i++) {
136 if (preViewPorts.get(i).getName().equals(viewName)) {
137 return preViewPorts.get(i);
144 * Removes the specified pre ViewPort.
146 * @param view The pre ViewPort to remove
147 * @return True if the ViewPort was removed successfully.
149 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
151 public boolean removePreView(ViewPort view) {
152 return preViewPorts.remove(view);
156 * Returns the main ViewPort with the given name.
158 * @param viewName The name of the main ViewPort to look up
159 * @return The ViewPort, or null if not found.
161 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
163 public ViewPort getMainView(String viewName) {
164 for (int i = 0; i < viewPorts.size(); i++) {
165 if (viewPorts.get(i).getName().equals(viewName)) {
166 return viewPorts.get(i);
173 * Removes the main ViewPort with the specified name.
175 * @param view The main ViewPort name to remove
176 * @return True if the ViewPort was removed successfully.
178 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
180 public boolean removeMainView(String viewName) {
181 for (int i = 0; i < viewPorts.size(); i++) {
182 if (viewPorts.get(i).getName().equals(viewName)) {
191 * Removes the specified main ViewPort.
193 * @param view The main ViewPort to remove
194 * @return True if the ViewPort was removed successfully.
196 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
198 public boolean removeMainView(ViewPort view) {
199 return viewPorts.remove(view);
203 * Returns the post ViewPort with the given name.
205 * @param viewName The name of the post ViewPort to look up
206 * @return The ViewPort, or null if not found.
208 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
210 public ViewPort getPostView(String viewName) {
211 for (int i = 0; i < postViewPorts.size(); i++) {
212 if (postViewPorts.get(i).getName().equals(viewName)) {
213 return postViewPorts.get(i);
220 * Removes the post ViewPort with the specified name.
222 * @param view The post ViewPort name to remove
223 * @return True if the ViewPort was removed successfully.
225 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
227 public boolean removePostView(String viewName) {
228 for (int i = 0; i < postViewPorts.size(); i++) {
229 if (postViewPorts.get(i).getName().equals(viewName)) {
230 postViewPorts.remove(i);
239 * Removes the specified post ViewPort.
241 * @param view The post ViewPort to remove
242 * @return True if the ViewPort was removed successfully.
244 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
246 public boolean removePostView(ViewPort view) {
247 return postViewPorts.remove(view);
251 * Returns a read-only list of all pre ViewPorts
252 * @return a read-only list of all pre ViewPorts
253 * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
255 public List<ViewPort> getPreViews() {
256 return Collections.unmodifiableList(preViewPorts);
260 * Returns a read-only list of all main ViewPorts
261 * @return a read-only list of all main ViewPorts
262 * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
264 public List<ViewPort> getMainViews() {
265 return Collections.unmodifiableList(viewPorts);
269 * Returns a read-only list of all post ViewPorts
270 * @return a read-only list of all post ViewPorts
271 * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
273 public List<ViewPort> getPostViews() {
274 return Collections.unmodifiableList(postViewPorts);
278 * Creates a new pre ViewPort, to display the given camera's content.
280 * The view will be processed before the main and post viewports.
282 public ViewPort createPreView(String viewName, Camera cam) {
283 ViewPort vp = new ViewPort(viewName, cam);
284 preViewPorts.add(vp);
289 * Creates a new main ViewPort, to display the given camera's content.
291 * The view will be processed before the post viewports but after
294 public ViewPort createMainView(String viewName, Camera cam) {
295 ViewPort vp = new ViewPort(viewName, cam);
301 * Creates a new post ViewPort, to display the given camera's content.
303 * The view will be processed after the pre and main viewports.
305 public ViewPort createPostView(String viewName, Camera cam) {
306 ViewPort vp = new ViewPort(viewName, cam);
307 postViewPorts.add(vp);
311 private void notifyReshape(ViewPort vp, int w, int h) {
312 List<SceneProcessor> processors = vp.getProcessors();
313 for (SceneProcessor proc : processors) {
314 if (!proc.isInitialized()) {
315 proc.initialize(this, vp);
317 proc.reshape(vp, w, h);
324 * Updates the resolution of all on-screen cameras to match
325 * the given width and height.
327 public void notifyReshape(int w, int h) {
328 for (ViewPort vp : preViewPorts) {
329 if (vp.getOutputFrameBuffer() == null) {
330 Camera cam = vp.getCamera();
331 cam.resize(w, h, true);
333 notifyReshape(vp, w, h);
335 for (ViewPort vp : viewPorts) {
336 if (vp.getOutputFrameBuffer() == null) {
337 Camera cam = vp.getCamera();
338 cam.resize(w, h, true);
340 notifyReshape(vp, w, h);
342 for (ViewPort vp : postViewPorts) {
343 if (vp.getOutputFrameBuffer() == null) {
344 Camera cam = vp.getCamera();
345 cam.resize(w, h, true);
347 notifyReshape(vp, w, h);
353 * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
354 * based on the current world state.
356 public void updateUniformBindings(List<Uniform> params) {
357 // assums worldMatrix is properly set.
358 TempVars vars = TempVars.get();
360 Matrix4f tempMat4 = vars.tempMat4;
361 Matrix3f tempMat3 = vars.tempMat3;
362 Vector2f tempVec2 = vars.vect2d;
363 Quaternion tempVec4 = vars.quat1;
365 for (int i = 0; i < params.size(); i++) {
366 Uniform u = params.get(i);
367 switch (u.getBinding()) {
369 u.setValue(VarType.Matrix4, worldMatrixBuf);
372 u.setValue(VarType.Matrix4, viewMatrix);
374 case ProjectionMatrix:
375 u.setValue(VarType.Matrix4, projMatrixBuf);
377 case ViewProjectionMatrix:
378 u.setValue(VarType.Matrix4, viewProjMatrixBuf);
380 case WorldViewMatrix:
381 // tempMat4.set(viewMatrix);
382 // tempMat4.multLocal(worldMatrix);
383 // u.setValue(VarType.Matrix4, tempMat4);
384 u.setValue(VarType.Matrix4, getWorldViewMatrixBuf());
387 tempMat4.set(viewMatrix);
388 tempMat4.multLocal(worldMatrix);
389 tempMat4.toRotationMatrix(tempMat3);
390 tempMat3.invertLocal();
391 tempMat3.transposeLocal();
392 u.setValue(VarType.Matrix3, tempMat3);
394 case WorldViewProjectionMatrix:
395 // tempMat4.set(viewProjMatrix);
396 // tempMat4.multLocal(worldMatrix);
397 // u.setValue(VarType.Matrix4, tempMat4);
398 u.setValue(VarType.Matrix4, getWorldViewProjectionMatrixBuf());
400 case WorldMatrixInverse:
401 tempMat4.multLocal(worldMatrix);
402 tempMat4.invertLocal();
403 u.setValue(VarType.Matrix4, tempMat4);
405 case ViewMatrixInverse:
406 tempMat4.set(viewMatrix);
407 tempMat4.invertLocal();
408 u.setValue(VarType.Matrix4, tempMat4);
410 case ProjectionMatrixInverse:
411 tempMat4.set(projMatrix);
412 tempMat4.invertLocal();
413 u.setValue(VarType.Matrix4, tempMat4);
415 case ViewProjectionMatrixInverse:
416 tempMat4.set(viewProjMatrix);
417 tempMat4.invertLocal();
418 u.setValue(VarType.Matrix4, tempMat4);
420 case WorldViewMatrixInverse:
421 tempMat4.set(viewMatrix);
422 tempMat4.multLocal(worldMatrix);
423 tempMat4.invertLocal();
424 u.setValue(VarType.Matrix4, tempMat4);
426 case NormalMatrixInverse:
427 tempMat4.set(viewMatrix);
428 tempMat4.multLocal(worldMatrix);
429 tempMat4.toRotationMatrix(tempMat3);
430 tempMat3.invertLocal();
431 tempMat3.transposeLocal();
432 tempMat3.invertLocal();
433 u.setValue(VarType.Matrix3, tempMat3);
435 case WorldViewProjectionMatrixInverse:
436 tempMat4.set(viewProjMatrix);
437 tempMat4.multLocal(worldMatrix);
438 tempMat4.invertLocal();
439 u.setValue(VarType.Matrix4, tempMat4);
442 tempVec4.set(viewX, viewY, viewWidth, viewHeight);
443 u.setValue(VarType.Vector4, tempVec4);
446 tempVec2.set(viewWidth, viewHeight);
447 u.setValue(VarType.Vector2, tempVec2);
450 float aspect = ((float) viewWidth) / viewHeight;
451 u.setValue(VarType.Float, aspect);
454 tempVec2.set(near, far);
455 u.setValue(VarType.Vector2, tempVec2);
458 u.setValue(VarType.Vector3, camLoc);
460 case CameraDirection:
461 u.setValue(VarType.Vector3, camDir);
464 u.setValue(VarType.Vector3, camLeft);
467 u.setValue(VarType.Vector3, camUp);
470 u.setValue(VarType.Float, timer.getTimeInSeconds());
473 u.setValue(VarType.Float, timer.getTimePerFrame());
476 u.setValue(VarType.Float, timer.getFrameRate());
485 * Set the material to use to render all future objects.
486 * This overrides the material set on the geometry and renders
487 * with the provided material instead.
488 * Use null to clear the material and return renderer to normal
490 * @param mat The forced material to set, or null to return to normal
492 public void setForcedMaterial(Material mat) {
493 forcedMaterial = mat;
497 * Returns the forced render state previously set with
498 * {@link #setForcedRenderState(com.jme3.material.RenderState) }.
499 * @return the forced render state
501 public RenderState getForcedRenderState() {
502 return forcedRenderState;
506 * Set the render state to use for all future objects.
507 * This overrides the render state set on the material and instead
508 * forces this render state to be applied for all future materials
509 * rendered. Set to null to return to normal functionality.
511 * @param forcedRenderState The forced render state to set, or null
512 * to return to normal
514 public void setForcedRenderState(RenderState forcedRenderState) {
515 this.forcedRenderState = forcedRenderState;
519 * Set the timer that should be used to query the time based
520 * {@link UniformBinding}s for material world parameters.
522 * @param timer The timer to query time world parameters
524 public void setTimer(Timer timer) {
529 * Returns the forced technique name set.
531 * @return the forced technique name set.
533 * @see #setForcedTechnique(java.lang.String)
535 public String getForcedTechnique() {
536 return forcedTechnique;
540 * Sets the forced technique to use when rendering geometries.
542 * If the specified technique name is available on the geometry's
543 * material, then it is used, otherwise, the
544 * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.
545 * If a forced material is not set and the forced technique name cannot
546 * be found on the material, the geometry will <em>not</em> be rendered.
548 * @param forcedTechnique The forced technique name to use, set to null
549 * to return to normal functionality.
551 * @see #renderGeometry(com.jme3.scene.Geometry)
553 public void setForcedTechnique(String forcedTechnique) {
554 this.forcedTechnique = forcedTechnique;
558 * Enable or disable alpha-to-coverage.
560 * When alpha to coverage is enabled and the renderer implementation
561 * supports it, then alpha blending will be replaced with alpha dissolve
562 * if multi-sampling is also set on the renderer.
563 * This feature allows avoiding of alpha blending artifacts due to
564 * lack of triangle-level back-to-front sorting.
566 * @param value True to enable alpha-to-coverage, false otherwise.
568 public void setAlphaToCoverage(boolean value) {
569 renderer.setAlphaToCoverage(value);
573 * True if the translucent bucket should automatically be rendered
574 * by the RenderManager.
576 * @return Whether or not the translucent bucket is rendered.
578 * @see #setHandleTranslucentBucket(boolean)
580 public boolean isHandleTranslucentBucket() {
581 return handleTranlucentBucket;
585 * Enable or disable rendering of the
586 * {@link Bucket#Translucent translucent bucket}
587 * by the RenderManager. The default is enabled.
589 * @param handleTranslucentBucket Whether or not the translucent bucket should
592 public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {
593 this.handleTranlucentBucket = handleTranslucentBucket;
597 * Internal use only. Sets the world matrix to use for future
598 * rendering. This has no effect unless objects are rendered manually
599 * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.
600 * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will
601 * override this value.
603 * @param mat The world matrix to set
605 public void setWorldMatrix(Matrix4f mat) {
607 worldMatrix.set(mat);
608 worldMatrix.fillFloatBuffer(worldMatrixBuf, true);
609 worldMatrixBuf.position(0);
610 worldViewProjectionMatrixBufDirty = true;
611 normalMatrixBufDirty = true;
613 renderer.setWorldMatrix(mat);
618 * Renders the given geometry.
620 * First the proper world matrix is set, if
621 * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}
622 * feature is enabled, the identity world matrix is used, otherwise, the
623 * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used.
625 * Once the world matrix is applied, the proper material is chosen for rendering.
626 * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is
627 * set on this RenderManager, then it is used for rendering the geometry,
628 * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.
630 * If a {@link #setForcedTechnique(java.lang.String) forced technique} is
631 * set on this RenderManager, then it is selected automatically
632 * on the geometry's material and is used for rendering. Otherwise, one
633 * of the {@link MaterialDef#getDefaultTechniques() default techniques} is
636 * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced
637 * render state} is set on this RenderManager, then it is used
638 * for rendering the material, and the material's own render state is ignored.
639 * Otherwise, the material's render state is used as intended.
641 * @param g The geometry to render
645 * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
646 * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
648 public void renderGeometry(Geometry g) {
649 if (g.isIgnoreTransform()) {
650 setWorldMatrix(Matrix4f.IDENTITY);
652 setWorldMatrix(g.getWorldMatrix());
655 //if forcedTechnique we try to force it for render,
656 //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
657 //else the geom is not rendered
658 if (forcedTechnique != null) {
659 if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
660 tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
661 g.getMaterial().selectTechnique(forcedTechnique, this);
662 // use geometry's material
663 g.getMaterial().render(g, this);
664 g.getMaterial().selectTechnique(tmpTech, this);
665 //Reverted this part from revision 6197
666 //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
667 } else if (forcedMaterial != null) {
668 // use forced material
669 forcedMaterial.render(g, this);
671 } else if (forcedMaterial != null) {
672 // use forced material
673 forcedMaterial.render(g, this);
675 g.getMaterial().render(g, this);
680 * Renders the given GeometryList.
682 * For every geometry in the list, the
683 * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.
685 * @param gl The geometry list to render.
688 * @see #renderGeometry(com.jme3.scene.Geometry)
690 public void renderGeometryList(GeometryList gl) {
691 for (int i = 0; i < gl.size(); i++) {
692 renderGeometry(gl.get(i));
697 * If a spatial is not inside the eye frustum, it
698 * is still rendered in the shadow frustum (shadow casting queue)
699 * through this recursive method.
701 private void renderShadow(Spatial s, RenderQueue rq) {
702 if (s instanceof Node) {
704 List<Spatial> children = n.getChildren();
705 for (int i = 0; i < children.size(); i++) {
706 renderShadow(children.get(i), rq);
708 } else if (s instanceof Geometry) {
709 Geometry gm = (Geometry) s;
711 RenderQueue.ShadowMode shadowMode = s.getShadowMode();
712 if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
713 //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
714 rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
720 * Preloads a scene for rendering.
722 * After invocation of this method, the underlying
723 * renderer would have uploaded any textures, shaders and meshes
724 * used by the given scene to the video driver.
725 * Using this method is useful when wishing to avoid the initial pause
726 * when rendering a scene for the first time. Note that it is not
727 * guaranteed that the underlying renderer will actually choose to upload
728 * the data to the GPU so some pause is still to be expected.
730 * @param scene The scene to preload
732 public void preloadScene(Spatial scene) {
733 if (scene instanceof Node) {
734 // recurse for all children
735 Node n = (Node) scene;
736 List<Spatial> children = n.getChildren();
737 for (int i = 0; i < children.size(); i++) {
738 preloadScene(children.get(i));
740 } else if (scene instanceof Geometry) {
741 // add to the render queue
742 Geometry gm = (Geometry) scene;
743 if (gm.getMaterial() == null) {
744 throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
747 gm.getMaterial().preload(this);
748 Mesh mesh = gm.getMesh();
750 for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
751 VertexBuffer buf = entry.getValue();
752 if (buf.getData() != null) {
753 renderer.updateBufferData(buf);
761 * Flattens the given scene graph into the ViewPort's RenderQueue,
762 * checking for culling as the call goes down the graph recursively.
764 * First, the scene is checked for culling based on the <code>Spatial</code>s
765 * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},
766 * if the camera frustum contains the scene, then this method is recursively
767 * called on its children.
769 * When the scene's leaves or {@link Geometry geometries} are reached,
770 * they are each enqueued into the
771 * {@link ViewPort#getQueue() ViewPort's render queue}.
773 * In addition to enqueuing the visible geometries, this method
774 * also scenes which cast or receive shadows, by putting them into the
776 * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
777 * shadow queue}. Each Spatial which has its
778 * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
779 * set to not off, will be put into the appropriate shadow queue, note that
780 * this process does not check for frustum culling on any
781 * {@link ShadowMode#Cast shadow casters}, as they don't have to be
782 * in the eye camera frustum to cast shadows on objects that are inside it.
784 * @param scene The scene to flatten into the queue
785 * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}
786 * used for culling and the {@link ViewPort#getQueue() queue} used to
787 * contain the flattened scene graph.
789 public void renderScene(Spatial scene, ViewPort vp) {
790 if (scene.getParent() == null) {
791 vp.getCamera().setPlaneState(0);
793 // check culling first.
794 if (!scene.checkCulling(vp.getCamera())) {
795 // move on to shadow-only render
796 if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {
797 renderShadow(scene, vp.getQueue());
802 scene.runControlRender(this, vp);
803 if (scene instanceof Node) {
804 // recurse for all children
805 Node n = (Node) scene;
806 List<Spatial> children = n.getChildren();
807 //saving cam state for culling
808 int camState = vp.getCamera().getPlaneState();
809 for (int i = 0; i < children.size(); i++) {
810 //restoring cam state before proceeding children recusively
811 vp.getCamera().setPlaneState(camState);
812 renderScene(children.get(i), vp);
815 } else if (scene instanceof Geometry) {
817 // add to the render queue
818 Geometry gm = (Geometry) scene;
819 if (gm.getMaterial() == null) {
820 throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
823 vp.getQueue().addToQueue(gm, scene.getQueueBucket());
825 // add to shadow queue if needed
826 RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
827 if (shadowMode != RenderQueue.ShadowMode.Off) {
828 vp.getQueue().addToShadowQueue(gm, shadowMode);
834 * Returns the camera currently used for rendering.
836 * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.
838 * @return the camera currently used for rendering.
840 public Camera getCurrentCamera() {
845 * The renderer implementation used for rendering operations.
847 * @return The renderer implementation
849 * @see #RenderManager(com.jme3.renderer.Renderer)
852 public Renderer getRenderer() {
857 * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}
858 * by rendering each of its visible buckets.
859 * By default the queues will automatically be cleared after rendering,
860 * so there's no need to clear them manually.
862 * @param vp The ViewPort of which the queue will be flushed
864 * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)
865 * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)
867 public void flushQueue(ViewPort vp) {
868 renderViewPortQueues(vp, true);
872 * Clears the queue of the given ViewPort.
873 * Simply calls {@link RenderQueue#clear() } on the ViewPort's
874 * {@link ViewPort#getQueue() render queue}.
876 * @param vp The ViewPort of which the queue will be cleared.
878 * @see RenderQueue#clear()
879 * @see ViewPort#getQueue()
881 public void clearQueue(ViewPort vp) {
882 vp.getQueue().clear();
886 * Render the given viewport queues.
888 * Changes the {@link Renderer#setDepthRange(float, float) depth range}
889 * appropriately as expected by each queue and then calls
890 * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }
891 * on the queue. Makes sure to restore the depth range to [0, 1]
892 * at the end of the call.
893 * Note that the {@link Bucket#Translucent translucent bucket} is NOT
894 * rendered by this method. Instead the user should call
895 * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }
898 * @param vp the viewport of which queue should be rendered
899 * @param flush If true, the queues will be cleared after
903 * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)
905 public void renderViewPortQueues(ViewPort vp, boolean flush) {
906 RenderQueue rq = vp.getQueue();
907 Camera cam = vp.getCamera();
908 boolean depthRangeChanged = false;
910 // render opaque objects with default depth range
911 // opaque objects are sorted front-to-back, reducing overdraw
912 rq.renderQueue(Bucket.Opaque, this, cam, flush);
914 // render the sky, with depth range set to the farthest
915 if (!rq.isQueueEmpty(Bucket.Sky)) {
916 renderer.setDepthRange(1, 1);
917 rq.renderQueue(Bucket.Sky, this, cam, flush);
918 depthRangeChanged = true;
922 // transparent objects are last because they require blending with the
923 // rest of the scene's objects. Consequently, they are sorted
925 if (!rq.isQueueEmpty(Bucket.Transparent)) {
926 if (depthRangeChanged) {
927 renderer.setDepthRange(0, 1);
928 depthRangeChanged = false;
931 rq.renderQueue(Bucket.Transparent, this, cam, flush);
934 if (!rq.isQueueEmpty(Bucket.Gui)) {
935 // renderer.setDepthRange(0, 0);
936 setCamera(cam, true);
937 rq.renderQueue(Bucket.Gui, this, cam, flush);
938 setCamera(cam, false);
939 depthRangeChanged = true;
942 // restore range to default
943 if (depthRangeChanged) {
944 renderer.setDepthRange(0, 1);
949 * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.
951 * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }
952 * is set to true. This method clears the translucent queue after rendering
955 * @param vp The viewport of which the translucent queue should be rendered.
957 * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)
958 * @see #setHandleTranslucentBucket(boolean)
960 public void renderTranslucentQueue(ViewPort vp) {
961 RenderQueue rq = vp.getQueue();
962 if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
963 rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
967 private void setViewPort(Camera cam) {
968 // this will make sure to update viewport only if needed
969 if (cam != prevCam || cam.isViewportChanged()) {
970 viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
971 viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
972 viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
973 viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
974 renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
975 // renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
976 cam.clearViewportChanged();
979 // float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
980 // float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
981 // float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
982 // float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
984 // orthoMatrix.loadIdentity();
985 // orthoMatrix.setTranslation(translateX, translateY, 0);
986 // orthoMatrix.setScale(scaleX, scaleY, 0);
988 orthoMatrix.loadIdentity();
989 orthoMatrix.setTranslation(-1f, -1f, 0f);
990 orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
991 // orthoMatrix.fillFloatBuffer(orthoMatrixBuf, true);
995 private void setViewProjection(Camera cam, boolean ortho) {
998 viewMatrix.set(Matrix4f.IDENTITY);
999 projMatrix.set(orthoMatrix);
1000 viewProjMatrix.set(orthoMatrix);
1002 viewMatrix.set(cam.getViewMatrix());
1003 projMatrix.set(cam.getProjectionMatrix());
1004 viewProjMatrix.set(cam.getViewProjectionMatrix());
1006 // viewMatrix.fillFloatBuffer(viewMatrixBuf, true);
1007 // viewMatrixBuf.position(0);
1008 projMatrix.fillFloatBuffer(projMatrixBuf, true);
1009 projMatrixBuf.position(0);
1010 viewProjMatrix.fillFloatBuffer(viewProjMatrixBuf, true);
1011 viewProjMatrixBuf.position(0);
1012 worldViewMatrixBufDirty = true;
1013 worldViewProjectionMatrixBufDirty = true;
1014 normalMatrixBufDirty = true;
1015 camLoc.set(cam.getLocation());
1016 cam.getLeft(camLeft);
1018 cam.getDirection(camDir);
1020 near = cam.getFrustumNear();
1021 far = cam.getFrustumFar();
1024 renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
1026 renderer.setViewProjectionMatrices(cam.getViewMatrix(),
1027 cam.getProjectionMatrix());
1032 private FloatBuffer getWorldViewMatrixBuf() {
1033 if (worldViewMatrixBufDirty) {
1034 TempVars tmp = TempVars.get();
1035 tmp.tempMat4.set(viewMatrix);
1036 tmp.tempMat4.multLocal(worldMatrix);
1037 tmp.tempMat4.fillFloatBuffer(worldViewMatrixBuf, true);
1038 worldViewMatrixBuf.position(0);
1040 worldViewMatrixBufDirty = false;
1042 return worldViewMatrixBuf;
1044 private FloatBuffer getWorldViewProjectionMatrixBuf() {
1045 if (worldViewProjectionMatrixBufDirty) {
1046 TempVars tmp = TempVars.get();
1047 tmp.tempMat4.set(viewProjMatrix);
1048 tmp.tempMat4.multLocal(worldMatrix);
1049 tmp.tempMat4.fillFloatBuffer(worldViewProjectionMatrixBuf, true);
1050 worldViewProjectionMatrixBuf.position(0);
1052 worldViewProjectionMatrixBufDirty = false;
1054 return worldViewProjectionMatrixBuf;
1056 private FloatBuffer getNormalMatrixBuf() {
1057 if (normalMatrixBufDirty) {
1058 TempVars tmp = TempVars.get();
1059 tmp.tempMat4.set(viewMatrix);
1060 tmp.tempMat4.multLocal(worldMatrix);
1061 tmp.tempMat4.toRotationMatrix(tmp.tempMat3);
1062 tmp.tempMat3.invertLocal();
1063 tmp.tempMat3.transposeLocal();
1064 tmp.tempMat3.fillFloatBuffer(normalMatrixBuf, true);
1065 normalMatrixBuf.position(0);
1067 normalMatrixBufDirty = false;
1069 return worldViewProjectionMatrixBuf;
1072 * Set the camera to use for rendering.
1074 * First, the camera's
1075 * {@link Camera#setViewPort(float, float, float, float) view port parameters}
1076 * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and
1077 * {@link Camera#getProjectionMatrix() projection} matrices are set
1078 * on the renderer. If <code>ortho</code> is <code>true</code>, then
1079 * instead of using the camera's view and projection matrices, an ortho
1080 * matrix is computed and used instead of the view projection matrix.
1081 * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)
1082 * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).
1084 * @param cam The camera to set
1085 * @param ortho True if to use orthographic projection (for GUI rendering),
1086 * false if to use the camera's view and projection matrices.
1088 public void setCamera(Camera cam, boolean ortho) {
1090 setViewProjection(cam, ortho);
1094 * Draws the viewport but without notifying {@link SceneProcessor scene
1095 * processors} of any rendering events.
1097 * @param vp The ViewPort to render
1099 * @see #renderViewPort(com.jme3.renderer.ViewPort, float)
1101 public void renderViewPortRaw(ViewPort vp) {
1102 setCamera(vp.getCamera(), false);
1103 List<Spatial> scenes = vp.getScenes();
1104 for (int i = scenes.size() - 1; i >= 0; i--) {
1105 renderScene(scenes.get(i), vp);
1111 * Renders the {@link ViewPort}.
1113 * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method
1114 * returns immediately. Otherwise, the ViewPort is rendered by
1115 * the following process:<br>
1117 * <li>All {@link SceneProcessor scene processors} that are attached
1118 * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.
1120 * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method
1122 * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}
1123 * is set on the Renderer</li>
1124 * <li>The camera is set on the renderer, including its view port parameters.
1125 * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>
1126 * <li>Any buffers that the ViewPort requests to be cleared are cleared
1127 * and the {@link ViewPort#getBackgroundColor() background color} is set</li>
1128 * <li>Every scene that is attached to the ViewPort is flattened into
1129 * the ViewPort's render queue
1130 * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })
1132 * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }
1133 * method is called.</li>
1134 * <li>The render queue is sorted and then flushed, sending
1135 * rendering commands to the underlying Renderer implementation.
1136 * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>
1137 * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }
1138 * method is called.</li>
1139 * <li>The translucent queue of the ViewPort is sorted and then flushed
1140 * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
1141 * <li>If any objects remained in the render queue, they are removed
1142 * from the queue. This is generally objects added to the
1143 * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
1145 * which were not rendered because of a missing shadow renderer.</li>
1151 public void renderViewPort(ViewPort vp, float tpf) {
1152 if (!vp.isEnabled()) {
1155 List<SceneProcessor> processors = vp.getProcessors();
1156 if (processors.isEmpty()) {
1160 if (processors != null) {
1161 int procSize = processors.size();
1162 // for (SceneProcessor proc : processors) {
1163 for(int i=0;i<procSize;i++) {
1164 SceneProcessor proc = processors.get(i);
1165 if (!proc.isInitialized()) {
1166 proc.initialize(this, vp);
1172 renderer.setFrameBuffer(vp.getOutputFrameBuffer());
1173 setCamera(vp.getCamera(), false);
1174 if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
1175 if (vp.isClearColor()) {
1176 renderer.setBackgroundColor(vp.getBackgroundColor());
1178 renderer.clearBuffers(vp.isClearColor(),
1180 vp.isClearStencil());
1183 List<Spatial> scenes = vp.getScenes();
1184 for (int i = scenes.size() - 1; i >= 0; i--) {
1185 renderScene(scenes.get(i), vp);
1188 if (processors != null) {
1189 int procSize = processors.size();
1190 // for (SceneProcessor proc : processors) {
1191 for(int i=0;i<procSize;i++) {
1192 SceneProcessor proc = processors.get(i);
1193 proc.postQueue(vp.getQueue());
1199 if (processors != null) {
1200 int procSize = processors.size();
1201 // for (SceneProcessor proc : processors) {
1202 for(int i=0;i<procSize;i++) {
1203 SceneProcessor proc = processors.get(i);
1204 proc.postFrame(vp.getOutputFrameBuffer());
1207 //renders the translucent objects queue after processors have been rendered
1208 renderTranslucentQueue(vp);
1209 // clear any remaining spatials that were not rendered.
1214 * Called by the application to render any ViewPorts
1215 * added to this RenderManager.
1217 * Renders any viewports that were added using the following methods:
1219 * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
1220 * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
1221 * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
1224 * @param tpf Time per frame value
1226 public void render(float tpf, boolean mainFrameBufferActive) {
1227 if (renderer instanceof NullRenderer) {
1231 this.shader = renderer.getCaps().contains(Caps.GLSL100);
1233 for (int i = 0; i < preViewPorts.size(); i++) {
1234 ViewPort vp = preViewPorts.get(i);
1235 if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
1236 renderViewPort(vp, tpf);
1239 for (int i = 0; i < viewPorts.size(); i++) {
1240 ViewPort vp = viewPorts.get(i);
1241 if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
1242 renderViewPort(vp, tpf);
1245 for (int i = 0; i < postViewPorts.size(); i++) {
1246 ViewPort vp = postViewPorts.get(i);
1247 if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
1248 renderViewPort(vp, tpf);