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());
365 case AmbientLightColor:
370 assert vars.unlock();
374 * Set the material to use to render all future objects.
375 * This overrides the material set on the geometry and renders
376 * with the provided material instead.
377 * Use null to clear the material and return renderer to normal
381 public void setForcedMaterial(Material mat) {
382 forcedMaterial = mat;
385 public RenderState getForcedRenderState() {
386 return forcedRenderState;
389 public void setForcedRenderState(RenderState forcedRenderState) {
390 this.forcedRenderState = forcedRenderState;
393 public void setWorldMatrix(Matrix4f mat) {
395 worldMatrix.set(mat);
397 renderer.setWorldMatrix(mat);
401 public void renderGeometry(Geometry g) {
402 if (g.isIgnoreTransform()) {
403 setWorldMatrix(Matrix4f.IDENTITY);
405 setWorldMatrix(g.getWorldMatrix());
408 //if forcedTechnique we try to force it for render,
409 //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
410 //else the geom is not rendered
411 if (forcedTechnique != null) {
412 if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
413 tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
414 g.getMaterial().selectTechnique(forcedTechnique, this);
415 // use geometry's material
416 g.getMaterial().render(g, this);
417 g.getMaterial().selectTechnique(tmpTech, this);
418 //Reverted this part from revision 6197
419 //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
420 } else if (forcedMaterial != null) {
421 // use forced material
422 forcedMaterial.render(g, this);
424 } else if (forcedMaterial != null) {
425 // use forced material
426 forcedMaterial.render(g, this);
428 g.getMaterial().render(g, this);
430 //re applying default render state at the end of the render to avoid depth write issues, MUST BE A BETTER WAY
431 renderer.applyRenderState(RenderState.DEFAULT);
434 public void renderGeometryList(GeometryList gl) {
435 for (int i = 0; i < gl.size(); i++) {
436 renderGeometry(gl.get(i));
442 * If a spatial is not inside the eye frustum, it
443 * is still rendered in the shadow frustum through this
448 private void renderShadow(Spatial s, RenderQueue rq) {
449 if (s instanceof Node) {
451 List<Spatial> children = n.getChildren();
452 for (int i = 0; i < children.size(); i++) {
453 renderShadow(children.get(i), rq);
455 } else if (s instanceof Geometry) {
456 Geometry gm = (Geometry) s;
458 RenderQueue.ShadowMode shadowMode = s.getShadowMode();
459 if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
460 //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
461 rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
466 public void preloadScene(Spatial scene) {
467 if (scene instanceof Node) {
468 // recurse for all children
469 Node n = (Node) scene;
470 List<Spatial> children = n.getChildren();
471 for (int i = 0; i < children.size(); i++) {
472 preloadScene(children.get(i));
474 } else if (scene instanceof Geometry) {
475 // add to the render queue
476 Geometry gm = (Geometry) scene;
477 if (gm.getMaterial() == null) {
478 throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
481 gm.getMaterial().preload(this);
482 Mesh mesh = gm.getMesh();
484 for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
485 VertexBuffer buf = entry.getValue();
486 if (buf.getData() != null) {
487 renderer.updateBufferData(buf);
500 public void renderScene(Spatial scene, ViewPort vp) {
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 for (int i = 0; i < children.size(); i++) {
516 renderScene(children.get(i), vp);
518 } else if (scene instanceof Geometry) {
520 // add to the render queue
521 Geometry gm = (Geometry) scene;
522 if (gm.getMaterial() == null) {
523 throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
526 vp.getQueue().addToQueue(gm, scene.getQueueBucket());
528 // add to shadow queue if needed
529 RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
530 if (shadowMode != RenderQueue.ShadowMode.Off) {
531 vp.getQueue().addToShadowQueue(gm, shadowMode);
536 public Camera getCurrentCamera() {
540 public Renderer getRenderer() {
545 * Render the given viewport queues, flushing the geometryList
546 * @param vp the viewport
548 public void flushQueue(ViewPort vp) {
549 renderViewPortQueues(vp, true);
552 public void clearQueue(ViewPort vp) {
553 vp.getQueue().clear();
556 //Nehon 08/18/2010 changed flushQueue to renderViewPortQueues with a flush boolean param
558 * Render the given viewport queues
559 * @param vp the viewport
560 * @param flush true to flush geometryList
562 public void renderViewPortQueues(ViewPort vp, boolean flush) {
563 RenderQueue rq = vp.getQueue();
564 Camera cam = vp.getCamera();
565 boolean depthRangeChanged = false;
567 // render opaque objects with default depth range
568 // opaque objects are sorted front-to-back, reducing overdraw
569 rq.renderQueue(Bucket.Opaque, this, cam, flush);
571 // render the sky, with depth range set to the farthest
572 if (!rq.isQueueEmpty(Bucket.Sky)) {
573 renderer.setDepthRange(1, 1);
574 rq.renderQueue(Bucket.Sky, this, cam, flush);
575 depthRangeChanged = true;
579 // transparent objects are last because they require blending with the
580 // rest of the scene's objects. Consequently, they are sorted
582 if (!rq.isQueueEmpty(Bucket.Transparent)) {
583 if (depthRangeChanged) {
584 renderer.setDepthRange(0, 1);
585 depthRangeChanged = false;
588 rq.renderQueue(Bucket.Transparent, this, cam, flush);
591 if (!rq.isQueueEmpty(Bucket.Gui)) {
592 renderer.setDepthRange(0, 0);
593 setCamera(cam, true);
594 rq.renderQueue(Bucket.Gui, this, cam, flush);
595 setCamera(cam, false);
596 depthRangeChanged = true;
599 // restore range to default
600 if (depthRangeChanged) {
601 renderer.setDepthRange(0, 1);
605 private void setViewPort(Camera cam) {
606 // this will make sure to update viewport only if needed
607 if (cam != prevCam || cam.isViewportChanged()) {
608 viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
609 viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
610 viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
611 viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
612 renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
613 renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
614 cam.clearViewportChanged();
617 float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
618 float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
619 float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
620 float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
621 orthoMatrix.loadIdentity();
622 orthoMatrix.setTranslation(translateX, translateY, 0);
623 orthoMatrix.setScale(scaleX, scaleY, /*-1f*/ 0f);
624 // System.out.println(orthoMatrix);
628 private void setViewProjection(Camera cam, boolean ortho) {
631 viewMatrix.set(Matrix4f.IDENTITY);
632 projMatrix.set(orthoMatrix);
633 viewProjMatrix.set(orthoMatrix);
635 viewMatrix.set(cam.getViewMatrix());
636 projMatrix.set(cam.getProjectionMatrix());
637 viewProjMatrix.set(cam.getViewProjectionMatrix());
641 camLoc.set(cam.getLocation());
642 cam.getLeft(camLeft);
644 cam.getDirection(camDir);
646 near = cam.getFrustumNear();
647 far = cam.getFrustumFar();
650 renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
652 renderer.setViewProjectionMatrices(cam.getViewMatrix(),
653 cam.getProjectionMatrix());
659 public void setCamera(Camera cam, boolean ortho) {
661 setViewProjection(cam, ortho);
665 * Draws the viewport but doesn't invoke processors.
668 public void renderViewPortRaw(ViewPort vp) {
669 setCamera(vp.getCamera(), false);
670 List<Spatial> scenes = vp.getScenes();
671 for (int i = scenes.size() - 1; i >= 0; i--) {
672 renderScene(scenes.get(i), vp);
677 public void renderViewPort(ViewPort vp, float tpf) {
678 if (!vp.isEnabled()) {
681 List<SceneProcessor> processors = vp.getProcessors();
682 if (processors.size() == 0) {
686 if (processors != null) {
687 for (SceneProcessor proc : processors) {
688 if (!proc.isInitialized()) {
689 proc.initialize(this, vp);
695 renderer.setFrameBuffer(vp.getOutputFrameBuffer());
696 setCamera(vp.getCamera(), false);
697 if (vp.isClearEnabled()) {
698 renderer.setBackgroundColor(vp.getBackgroundColor());
699 renderer.clearBuffers(vp.isClearColor(),
701 vp.isClearStencil());
704 List<Spatial> scenes = vp.getScenes();
705 for (int i = scenes.size() - 1; i >= 0; i--) {
706 renderScene(scenes.get(i), vp);
709 if (processors != null) {
710 for (SceneProcessor proc : processors) {
711 proc.postQueue(vp.getQueue());
717 if (processors != null) {
718 for (SceneProcessor proc : processors) {
719 proc.postFrame(vp.getOutputFrameBuffer());
723 // clear any remaining spatials that were not rendered.
727 public void render(float tpf) {
728 if (renderer instanceof NullRenderer) {
732 this.shader = renderer.getCaps().contains(Caps.GLSL100);
734 for (int i = 0; i < preViewPorts.size(); i++) {
735 renderViewPort(preViewPorts.get(i), tpf);
737 for (int i = 0; i < viewPorts.size(); i++) {
738 renderViewPort(viewPorts.get(i), tpf);
740 for (int i = 0; i < postViewPorts.size(); i++) {
741 renderViewPort(postViewPorts.get(i), tpf);
745 //Remy - 09/14/2010 - added a setter for the timer in order to correctly populate g_Time and g_Tpf in the shaders
746 public void setTimer(Timer timer) {
750 public String getForcedTechnique() {
751 return forcedTechnique;
754 public void setForcedTechnique(String forcedTechnique) {
755 this.forcedTechnique = forcedTechnique;
758 public void setAlphaToCoverage(boolean value) {
759 renderer.setAlphaToCoverage(value);