• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
RenderManager(Renderer renderer)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      */
getPreView(String viewName)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      */
removePreView(ViewPort view)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      */
getMainView(String viewName)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      */
removeMainView(String viewName)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      */
removeMainView(ViewPort view)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      */
getPostView(String viewName)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      */
removePostView(String viewName)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      */
removePostView(ViewPort view)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      */
getPreViews()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      */
getMainViews()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      */
getPostViews()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      */
createPreView(String viewName, Camera cam)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      */
createMainView(String viewName, Camera cam)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      */
createPostView(String viewName, Camera cam)285     public ViewPort createPostView(String viewName, Camera cam) {
286         ViewPort vp = new ViewPort(viewName, cam);
287         postViewPorts.add(vp);
288         return vp;
289     }
290 
notifyReshape(ViewPort vp, int w, int h)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      */
notifyReshape(int w, int h)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      */
updateUniformBindings(List<Uniform> params)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      */
setForcedMaterial(Material mat)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      */
getForcedRenderState()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      */
setForcedRenderState(RenderState forcedRenderState)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      */
setTimer(Timer timer)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      */
getForcedTechnique()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      */
setForcedTechnique(String forcedTechnique)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      */
setAlphaToCoverage(boolean value)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      */
isHandleTranslucentBucket()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      */
setHandleTranslucentBucket(boolean handleTranslucentBucket)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      */
setWorldMatrix(Matrix4f mat)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      */
renderGeometry(Geometry g)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      */
renderGeometryList(GeometryList gl)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      */
renderShadow(Spatial s, RenderQueue rq)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      */
preloadScene(Spatial scene)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      */
renderScene(Spatial scene, ViewPort vp)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      */
getCurrentCamera()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      */
getRenderer()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      */
flushQueue(ViewPort vp)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      */
clearQueue(ViewPort vp)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      */
renderViewPortQueues(ViewPort vp, boolean flush)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      */
renderTranslucentQueue(ViewPort vp)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 
setViewPort(Camera cam)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 
setViewProjection(Camera cam, boolean ortho)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      */
setCamera(Camera cam, boolean ortho)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      */
renderViewPortRaw(ViewPort vp)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      */
renderViewPort(ViewPort vp, float tpf)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      */
render(float tpf, boolean mainFrameBufferActive)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