2 * Copyright (c) 2009-2010 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 package com.jme3.renderer;
34 import com.jme3.material.Material;
35 import com.jme3.material.RenderState;
36 import com.jme3.math.Matrix3f;
37 import com.jme3.math.Matrix4f;
38 import com.jme3.math.Quaternion;
39 import com.jme3.math.Vector2f;
40 import com.jme3.math.Vector3f;
41 import com.jme3.post.SceneProcessor;
42 import com.jme3.renderer.queue.GeometryList;
43 import com.jme3.renderer.queue.RenderQueue;
44 import com.jme3.renderer.queue.RenderQueue.Bucket;
45 import com.jme3.scene.Geometry;
46 import com.jme3.scene.Mesh;
47 import com.jme3.scene.Node;
48 import com.jme3.scene.Spatial;
49 import com.jme3.scene.VertexBuffer;
50 import com.jme3.shader.Uniform;
51 import com.jme3.shader.VarType;
52 import com.jme3.system.NullRenderer;
53 import com.jme3.system.Timer;
54 import com.jme3.util.IntMap.Entry;
55 import com.jme3.util.TempVars;
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.List;
59 import java.util.logging.Logger;
62 * <code>RenderManager</code> is a high-level rendering interface that is
63 * above the Renderer implementation. RenderManager takes care
64 * of rendering the scene graphs attached to each viewport and
65 * handling SceneProcessors.
71 public class RenderManager {
73 private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
74 private Renderer renderer;
76 private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
77 private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
78 private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
79 private Camera prevCam = null;
80 private Material forcedMaterial = null;
81 private String forcedTechnique = null;
82 private RenderState forcedRenderState = null;
83 private boolean shader;
84 private int viewX, viewY, viewWidth, viewHeight;
85 private float near, far;
86 private Matrix4f orthoMatrix = new Matrix4f();
87 private Matrix4f viewMatrix = new Matrix4f();
88 private Matrix4f projMatrix = new Matrix4f();
89 private Matrix4f viewProjMatrix = new Matrix4f();
90 private Matrix4f worldMatrix = new Matrix4f();
91 private Vector3f camUp = new Vector3f(),
92 camLeft = new Vector3f(),
93 camDir = new Vector3f(),
94 camLoc = new Vector3f();
96 private String tmpTech;
99 * Create a high-level rendering interface over the
100 * low-level rendering interface.
103 public RenderManager(Renderer renderer) {
104 this.renderer = renderer;
105 //this.shader = renderer.getCaps().contains(Caps.GLSL100);
108 public ViewPort getPreView(String viewName) {
109 for (int i = 0; i < preViewPorts.size(); i++) {
110 if (preViewPorts.get(i).getName().equals(viewName)) {
111 return preViewPorts.get(i);
117 public boolean removePreView(ViewPort view) {
118 return preViewPorts.remove(view);
121 public ViewPort getMainView(String viewName) {
122 for (int i = 0; i < viewPorts.size(); i++) {
123 if (viewPorts.get(i).getName().equals(viewName)) {
124 return viewPorts.get(i);
130 public boolean removeMainView(String viewName) {
131 for (int i = 0; i < viewPorts.size(); i++) {
132 if (viewPorts.get(i).getName().equals(viewName)) {
140 public boolean removeMainView(ViewPort view) {
141 return viewPorts.remove(view);
144 public ViewPort getPostView(String viewName) {
145 for (int i = 0; i < postViewPorts.size(); i++) {
146 if (postViewPorts.get(i).getName().equals(viewName)) {
147 return postViewPorts.get(i);
153 public boolean removePostView(String viewName) {
154 for (int i = 0; i < postViewPorts.size(); i++) {
155 if (postViewPorts.get(i).getName().equals(viewName)) {
156 postViewPorts.remove(i);
164 public boolean removePostView(ViewPort view) {
165 return postViewPorts.remove(view);
168 public List<ViewPort> getPreViews() {
169 return Collections.unmodifiableList(preViewPorts);
172 public List<ViewPort> getMainViews() {
173 return Collections.unmodifiableList(viewPorts);
176 public List<ViewPort> getPostViews() {
177 return Collections.unmodifiableList(postViewPorts);
181 * Creates a new viewport, to display the given camera's content.
182 * The view will be processed before the primary viewport.
187 public ViewPort createPreView(String viewName, Camera cam) {
188 ViewPort vp = new ViewPort(viewName, cam);
189 preViewPorts.add(vp);
193 public ViewPort createMainView(String viewName, Camera cam) {
194 ViewPort vp = new ViewPort(viewName, cam);
199 public ViewPort createPostView(String viewName, Camera cam) {
200 ViewPort vp = new ViewPort(viewName, cam);
201 postViewPorts.add(vp);
205 private void notifyReshape(ViewPort vp, int w, int h) {
206 List<SceneProcessor> processors = vp.getProcessors();
207 for (SceneProcessor proc : processors) {
208 if (!proc.isInitialized()) {
209 proc.initialize(this, vp);
211 proc.reshape(vp, w, h);
220 public void notifyReshape(int w, int h) {
221 for (ViewPort vp : preViewPorts) {
222 if (vp.getOutputFrameBuffer() == null) {
223 Camera cam = vp.getCamera();
224 cam.resize(w, h, true);
226 notifyReshape(vp, w, h);
228 for (ViewPort vp : viewPorts) {
229 if (vp.getOutputFrameBuffer() == null) {
230 Camera cam = vp.getCamera();
231 cam.resize(w, h, true);
233 notifyReshape(vp, w, h);
235 for (ViewPort vp : postViewPorts) {
236 if (vp.getOutputFrameBuffer() == null) {
237 Camera cam = vp.getCamera();
238 cam.resize(w, h, true);
240 notifyReshape(vp, w, h);
244 public void updateUniformBindings(List<Uniform> params) {
245 // assums worldMatrix is properly set.
246 TempVars vars = TempVars.get();
249 Matrix4f tempMat4 = vars.tempMat4;
250 Matrix3f tempMat3 = vars.tempMat3;
251 Vector2f tempVec2 = vars.vect2d;
252 Quaternion tempVec4 = vars.quat1;
254 for (int i = 0; i < params.size(); i++) {
255 Uniform u = params.get(i);
256 switch (u.getBinding()) {
258 u.setValue(VarType.Matrix4, worldMatrix);
261 u.setValue(VarType.Matrix4, viewMatrix);
263 case ProjectionMatrix:
264 u.setValue(VarType.Matrix4, projMatrix);
266 case ViewProjectionMatrix:
267 u.setValue(VarType.Matrix4, viewProjMatrix);
269 case WorldViewMatrix:
270 tempMat4.set(viewMatrix);
271 tempMat4.multLocal(worldMatrix);
272 u.setValue(VarType.Matrix4, tempMat4);
275 tempMat4.set(viewMatrix);
276 tempMat4.multLocal(worldMatrix);
277 tempMat4.toRotationMatrix(tempMat3);
278 tempMat3.invertLocal();
279 tempMat3.transposeLocal();
280 u.setValue(VarType.Matrix3, tempMat3);
282 case WorldViewProjectionMatrix:
283 tempMat4.set(viewProjMatrix);
284 tempMat4.multLocal(worldMatrix);
285 u.setValue(VarType.Matrix4, tempMat4);
287 case WorldMatrixInverse:
288 tempMat4.multLocal(worldMatrix);
289 tempMat4.invertLocal();
290 u.setValue(VarType.Matrix4, tempMat4);
292 case ViewMatrixInverse:
293 tempMat4.set(viewMatrix);
294 tempMat4.invertLocal();
295 u.setValue(VarType.Matrix4, tempMat4);
297 case ProjectionMatrixInverse:
298 tempMat4.set(projMatrix);
299 tempMat4.invertLocal();
300 u.setValue(VarType.Matrix4, tempMat4);
302 case ViewProjectionMatrixInverse:
303 tempMat4.set(viewProjMatrix);
304 tempMat4.invertLocal();
305 u.setValue(VarType.Matrix4, tempMat4);
307 case WorldViewMatrixInverse:
308 tempMat4.set(viewMatrix);
309 tempMat4.multLocal(worldMatrix);
310 tempMat4.invertLocal();
311 u.setValue(VarType.Matrix4, tempMat4);
313 case NormalMatrixInverse:
314 tempMat4.set(viewMatrix);
315 tempMat4.multLocal(worldMatrix);
316 tempMat4.toRotationMatrix(tempMat3);
317 tempMat3.invertLocal();
318 tempMat3.transposeLocal();
319 tempMat3.invertLocal();
320 u.setValue(VarType.Matrix3, tempMat3);
322 case WorldViewProjectionMatrixInverse:
323 tempMat4.set(viewProjMatrix);
324 tempMat4.multLocal(worldMatrix);
325 tempMat4.invertLocal();
326 u.setValue(VarType.Matrix4, tempMat4);
329 tempVec4.set(viewX, viewY, viewWidth, viewHeight);
330 u.setValue(VarType.Vector4, tempVec4);
333 tempVec2.set(viewWidth, viewHeight);
334 u.setValue(VarType.Vector2, tempVec2);
337 float aspect = ((float) viewWidth) / viewHeight;
338 u.setValue(VarType.Float, aspect);
341 tempVec2.set(near, far);
342 u.setValue(VarType.Vector2, tempVec2);
345 u.setValue(VarType.Vector3, camLoc);
347 case CameraDirection:
348 u.setValue(VarType.Vector3, camDir);
351 u.setValue(VarType.Vector3, camLeft);
354 u.setValue(VarType.Vector3, camUp);
357 u.setValue(VarType.Float, timer.getTimeInSeconds());
360 u.setValue(VarType.Float, timer.getTimePerFrame());
363 u.setValue(VarType.Float, timer.getFrameRate());
368 assert vars.unlock();
372 * Set the material to use to render all future objects.
373 * This overrides the material set on the geometry and renders
374 * with the provided material instead.
375 * Use null to clear the material and return renderer to normal
379 public void setForcedMaterial(Material mat) {
380 forcedMaterial = mat;
383 public RenderState getForcedRenderState() {
384 return forcedRenderState;
387 public void setForcedRenderState(RenderState forcedRenderState) {
388 this.forcedRenderState = forcedRenderState;
391 public void setWorldMatrix(Matrix4f mat) {
393 worldMatrix.set(mat);
395 renderer.setWorldMatrix(mat);
399 public void renderGeometry(Geometry g) {
400 if (g.isIgnoreTransform()) {
401 setWorldMatrix(Matrix4f.IDENTITY);
403 setWorldMatrix(g.getWorldMatrix());
406 //if forcedTechnique we try to force it for render,
407 //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
408 //else the geom is not rendered
409 if (forcedTechnique != null) {
410 if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
411 tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
412 g.getMaterial().selectTechnique(forcedTechnique, this);
413 // use geometry's material
414 g.getMaterial().render(g, this);
415 g.getMaterial().selectTechnique(tmpTech, this);
416 //Reverted this part from revision 6197
417 //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
418 } else if (forcedMaterial != null) {
419 // use forced material
420 forcedMaterial.render(g, this);
422 } else if (forcedMaterial != null) {
423 // use forced material
424 forcedMaterial.render(g, this);
426 g.getMaterial().render(g, this);
428 //re applying default render state at the end of the render to avoid depth write issues, MUST BE A BETTER WAY
429 renderer.applyRenderState(RenderState.DEFAULT);
432 public void renderGeometryList(GeometryList gl) {
433 for (int i = 0; i < gl.size(); i++) {
434 renderGeometry(gl.get(i));
439 * If a spatial is not inside the eye frustum, it
440 * is still rendered in the shadow frustum through this
445 private void renderShadow(Spatial s, RenderQueue rq) {
446 if (s instanceof Node) {
448 List<Spatial> children = n.getChildren();
449 for (int i = 0; i < children.size(); i++) {
450 renderShadow(children.get(i), rq);
452 } else if (s instanceof Geometry) {
453 Geometry gm = (Geometry) s;
455 RenderQueue.ShadowMode shadowMode = s.getShadowMode();
456 if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
457 //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
458 rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
463 public void preloadScene(Spatial scene) {
464 if (scene instanceof Node) {
465 // recurse for all children
466 Node n = (Node) scene;
467 List<Spatial> children = n.getChildren();
468 for (int i = 0; i < children.size(); i++) {
469 preloadScene(children.get(i));
471 } else if (scene instanceof Geometry) {
472 // add to the render queue
473 Geometry gm = (Geometry) scene;
474 if (gm.getMaterial() == null) {
475 throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
478 gm.getMaterial().preload(this);
479 Mesh mesh = gm.getMesh();
481 for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
482 VertexBuffer buf = entry.getValue();
483 if (buf.getData() != null) {
484 renderer.updateBufferData(buf);
497 public void renderScene(Spatial scene, ViewPort vp) {
498 if (scene.getParent() == null) {
499 vp.getCamera().setPlaneState(0);
501 // check culling first.
502 if (!scene.checkCulling(vp.getCamera())) {
503 // move on to shadow-only render
504 if (scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) {
505 renderShadow(scene, vp.getQueue());
510 scene.runControlRender(this, vp);
511 if (scene instanceof Node) {
512 // recurse for all children
513 Node n = (Node) scene;
514 List<Spatial> children = n.getChildren();
515 //saving cam state for culling
516 int camState = vp.getCamera().getPlaneState();
517 for (int i = 0; i < children.size(); i++) {
518 //restoring cam state before proceeding children recusively
519 vp.getCamera().setPlaneState(camState);
520 renderScene(children.get(i), vp);
523 } else if (scene instanceof Geometry) {
525 // add to the render queue
526 Geometry gm = (Geometry) scene;
527 if (gm.getMaterial() == null) {
528 throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
531 vp.getQueue().addToQueue(gm, scene.getQueueBucket());
533 // add to shadow queue if needed
534 RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
535 if (shadowMode != RenderQueue.ShadowMode.Off) {
536 vp.getQueue().addToShadowQueue(gm, shadowMode);
541 public Camera getCurrentCamera() {
545 public Renderer getRenderer() {
550 * Render the given viewport queues, flushing the geometryList
551 * @param vp the viewport
553 public void flushQueue(ViewPort vp) {
554 renderViewPortQueues(vp, true);
557 public void clearQueue(ViewPort vp) {
558 vp.getQueue().clear();
561 //Nehon 08/18/2010 changed flushQueue to renderViewPortQueues with a flush boolean param
563 * Render the given viewport queues
564 * @param vp the viewport
565 * @param flush true to flush geometryList
567 public void renderViewPortQueues(ViewPort vp, boolean flush) {
568 RenderQueue rq = vp.getQueue();
569 Camera cam = vp.getCamera();
570 boolean depthRangeChanged = false;
572 // render opaque objects with default depth range
573 // opaque objects are sorted front-to-back, reducing overdraw
574 rq.renderQueue(Bucket.Opaque, this, cam, flush);
576 // render the sky, with depth range set to the farthest
577 if (!rq.isQueueEmpty(Bucket.Sky)) {
578 renderer.setDepthRange(1, 1);
579 rq.renderQueue(Bucket.Sky, this, cam, flush);
580 depthRangeChanged = true;
584 // transparent objects are last because they require blending with the
585 // rest of the scene's objects. Consequently, they are sorted
587 if (!rq.isQueueEmpty(Bucket.Transparent)) {
588 if (depthRangeChanged) {
589 renderer.setDepthRange(0, 1);
590 depthRangeChanged = false;
593 rq.renderQueue(Bucket.Transparent, this, cam, flush);
596 if (!rq.isQueueEmpty(Bucket.Gui)) {
597 renderer.setDepthRange(0, 0);
598 setCamera(cam, true);
599 rq.renderQueue(Bucket.Gui, this, cam, flush);
600 setCamera(cam, false);
601 depthRangeChanged = true;
604 // restore range to default
605 if (depthRangeChanged) {
606 renderer.setDepthRange(0, 1);
610 private void setViewPort(Camera cam) {
611 // this will make sure to update viewport only if needed
612 if (cam != prevCam || cam.isViewportChanged()) {
613 viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
614 viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
615 viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
616 viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
617 renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
618 renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
619 cam.clearViewportChanged();
622 // float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
623 // float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
624 // float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
625 // float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
627 // orthoMatrix.loadIdentity();
628 // orthoMatrix.setTranslation(translateX, translateY, 0);
629 // orthoMatrix.setScale(scaleX, scaleY, 0);
631 orthoMatrix.loadIdentity();
632 orthoMatrix.setTranslation(-1f, -1f, 0f);
633 orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
637 private void setViewProjection(Camera cam, boolean ortho) {
640 viewMatrix.set(Matrix4f.IDENTITY);
641 projMatrix.set(orthoMatrix);
642 viewProjMatrix.set(orthoMatrix);
644 viewMatrix.set(cam.getViewMatrix());
645 projMatrix.set(cam.getProjectionMatrix());
646 viewProjMatrix.set(cam.getViewProjectionMatrix());
649 camLoc.set(cam.getLocation());
650 cam.getLeft(camLeft);
652 cam.getDirection(camDir);
654 near = cam.getFrustumNear();
655 far = cam.getFrustumFar();
658 renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
660 renderer.setViewProjectionMatrices(cam.getViewMatrix(),
661 cam.getProjectionMatrix());
667 public void setCamera(Camera cam, boolean ortho) {
669 setViewProjection(cam, ortho);
673 * Draws the viewport but doesn't invoke processors.
676 public void renderViewPortRaw(ViewPort vp) {
677 setCamera(vp.getCamera(), false);
678 List<Spatial> scenes = vp.getScenes();
679 for (int i = scenes.size() - 1; i >= 0; i--) {
680 renderScene(scenes.get(i), vp);
685 public void renderViewPort(ViewPort vp, float tpf) {
686 if (!vp.isEnabled()) {
689 List<SceneProcessor> processors = vp.getProcessors();
690 if (processors.size() == 0) {
694 if (processors != null) {
695 for (SceneProcessor proc : processors) {
696 if (!proc.isInitialized()) {
697 proc.initialize(this, vp);
703 renderer.setFrameBuffer(vp.getOutputFrameBuffer());
704 setCamera(vp.getCamera(), false);
705 if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
706 if (vp.isClearColor()) {
707 renderer.setBackgroundColor(vp.getBackgroundColor());
709 renderer.clearBuffers(vp.isClearColor(),
711 vp.isClearStencil());
714 List<Spatial> scenes = vp.getScenes();
715 for (int i = scenes.size() - 1; i >= 0; i--) {
716 renderScene(scenes.get(i), vp);
719 if (processors != null) {
720 for (SceneProcessor proc : processors) {
721 proc.postQueue(vp.getQueue());
727 if (processors != null) {
728 for (SceneProcessor proc : processors) {
729 proc.postFrame(vp.getOutputFrameBuffer());
733 // clear any remaining spatials that were not rendered.
737 public void render(float tpf) {
738 if (renderer instanceof NullRenderer) {
742 this.shader = renderer.getCaps().contains(Caps.GLSL100);
744 for (int i = 0; i < preViewPorts.size(); i++) {
745 renderViewPort(preViewPorts.get(i), tpf);
747 for (int i = 0; i < viewPorts.size(); i++) {
748 renderViewPort(viewPorts.get(i), tpf);
750 for (int i = 0; i < postViewPorts.size(); i++) {
751 renderViewPort(postViewPorts.get(i), tpf);
755 //Remy - 09/14/2010 - added a setter for the timer in order to correctly populate g_Time and g_Tpf in the shaders
756 public void setTimer(Timer timer) {
760 public String getForcedTechnique() {
761 return forcedTechnique;
764 public void setForcedTechnique(String forcedTechnique) {
765 this.forcedTechnique = forcedTechnique;
768 public void setAlphaToCoverage(boolean value) {
769 renderer.setAlphaToCoverage(value);