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