|
@@ -1,1170 +1,1175 @@
|
|
|
-/*
|
|
|
- * Copyright (c) 2009-2012 jMonkeyEngine
|
|
|
- * All rights reserved.
|
|
|
- *
|
|
|
- * Redistribution and use in source and binary forms, with or without
|
|
|
- * modification, are permitted provided that the following conditions are
|
|
|
- * met:
|
|
|
- *
|
|
|
- * * Redistributions of source code must retain the above copyright
|
|
|
- * notice, this list of conditions and the following disclaimer.
|
|
|
- *
|
|
|
- * * Redistributions in binary form must reproduce the above copyright
|
|
|
- * notice, this list of conditions and the following disclaimer in the
|
|
|
- * documentation and/or other materials provided with the distribution.
|
|
|
- *
|
|
|
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
|
- * may be used to endorse or promote products derived from this software
|
|
|
- * without specific prior written permission.
|
|
|
- *
|
|
|
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
- */
|
|
|
-package com.jme3.renderer;
|
|
|
-
|
|
|
-import com.jme3.material.Material;
|
|
|
-import com.jme3.material.MaterialDef;
|
|
|
-import com.jme3.material.RenderState;
|
|
|
-import com.jme3.material.Technique;
|
|
|
-import com.jme3.math.*;
|
|
|
-import com.jme3.post.SceneProcessor;
|
|
|
-import com.jme3.renderer.queue.GeometryList;
|
|
|
-import com.jme3.renderer.queue.RenderQueue;
|
|
|
-import com.jme3.renderer.queue.RenderQueue.Bucket;
|
|
|
-import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
|
|
-import com.jme3.scene.*;
|
|
|
-import com.jme3.shader.Uniform;
|
|
|
-import com.jme3.shader.UniformBinding;
|
|
|
-import com.jme3.shader.VarType;
|
|
|
-import com.jme3.system.NullRenderer;
|
|
|
-import com.jme3.system.Timer;
|
|
|
-import com.jme3.util.IntMap.Entry;
|
|
|
-import com.jme3.util.TempVars;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.List;
|
|
|
-import java.util.logging.Logger;
|
|
|
-
|
|
|
-/**
|
|
|
- * <code>RenderManager</code> is a high-level rendering interface that is
|
|
|
- * above the Renderer implementation. RenderManager takes care
|
|
|
- * of rendering the scene graphs attached to each viewport and
|
|
|
- * handling SceneProcessors.
|
|
|
- *
|
|
|
- * @see SceneProcessor
|
|
|
- * @see ViewPort
|
|
|
- * @see Spatial
|
|
|
- */
|
|
|
-public class RenderManager {
|
|
|
-
|
|
|
- private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
|
|
|
-
|
|
|
- private Renderer renderer;
|
|
|
- private Timer timer;
|
|
|
- private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
|
|
|
- private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
|
|
|
- private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
|
|
|
- private Camera prevCam = null;
|
|
|
- private Material forcedMaterial = null;
|
|
|
- private String forcedTechnique = null;
|
|
|
- private RenderState forcedRenderState = null;
|
|
|
- private boolean shader;
|
|
|
- private int viewX, viewY, viewWidth, viewHeight;
|
|
|
- private float near, far;
|
|
|
- private Matrix4f orthoMatrix = new Matrix4f();
|
|
|
- private Matrix4f viewMatrix = new Matrix4f();
|
|
|
- private Matrix4f projMatrix = new Matrix4f();
|
|
|
- private Matrix4f viewProjMatrix = new Matrix4f();
|
|
|
- private Matrix4f worldMatrix = new Matrix4f();
|
|
|
- private Vector3f camUp = new Vector3f(),
|
|
|
- camLeft = new Vector3f(),
|
|
|
- camDir = new Vector3f(),
|
|
|
- camLoc = new Vector3f();
|
|
|
- //temp technique
|
|
|
- private String tmpTech;
|
|
|
- private boolean handleTranlucentBucket = true;
|
|
|
-
|
|
|
- /**
|
|
|
- * Create a high-level rendering interface over the
|
|
|
- * low-level rendering interface.
|
|
|
- * @param renderer
|
|
|
- */
|
|
|
- public RenderManager(Renderer renderer) {
|
|
|
- this.renderer = renderer;
|
|
|
- //this.shader = renderer.getCaps().contains(Caps.GLSL100);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the pre ViewPort with the given name.
|
|
|
- *
|
|
|
- * @param viewName The name of the pre ViewPort to look up
|
|
|
- * @return The ViewPort, or null if not found.
|
|
|
- *
|
|
|
- * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public ViewPort getPreView(String viewName) {
|
|
|
- for (int i = 0; i < preViewPorts.size(); i++) {
|
|
|
- if (preViewPorts.get(i).getName().equals(viewName)) {
|
|
|
- return preViewPorts.get(i);
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Removes the specified pre ViewPort.
|
|
|
- *
|
|
|
- * @param view The pre ViewPort to remove
|
|
|
- * @return True if the ViewPort was removed successfully.
|
|
|
- *
|
|
|
- * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public boolean removePreView(ViewPort view) {
|
|
|
- return preViewPorts.remove(view);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the main ViewPort with the given name.
|
|
|
- *
|
|
|
- * @param viewName The name of the main ViewPort to look up
|
|
|
- * @return The ViewPort, or null if not found.
|
|
|
- *
|
|
|
- * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public ViewPort getMainView(String viewName) {
|
|
|
- for (int i = 0; i < viewPorts.size(); i++) {
|
|
|
- if (viewPorts.get(i).getName().equals(viewName)) {
|
|
|
- return viewPorts.get(i);
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Removes the main ViewPort with the specified name.
|
|
|
- *
|
|
|
- * @param viewName The main ViewPort name to remove
|
|
|
- * @return True if the ViewPort was removed successfully.
|
|
|
- *
|
|
|
- * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public boolean removeMainView(String viewName) {
|
|
|
- for (int i = 0; i < viewPorts.size(); i++) {
|
|
|
- if (viewPorts.get(i).getName().equals(viewName)) {
|
|
|
- viewPorts.remove(i);
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Removes the specified main ViewPort.
|
|
|
- *
|
|
|
- * @param view The main ViewPort to remove
|
|
|
- * @return True if the ViewPort was removed successfully.
|
|
|
- *
|
|
|
- * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public boolean removeMainView(ViewPort view) {
|
|
|
- return viewPorts.remove(view);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the post ViewPort with the given name.
|
|
|
- *
|
|
|
- * @param viewName The name of the post ViewPort to look up
|
|
|
- * @return The ViewPort, or null if not found.
|
|
|
- *
|
|
|
- * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public ViewPort getPostView(String viewName) {
|
|
|
- for (int i = 0; i < postViewPorts.size(); i++) {
|
|
|
- if (postViewPorts.get(i).getName().equals(viewName)) {
|
|
|
- return postViewPorts.get(i);
|
|
|
- }
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Removes the post ViewPort with the specified name.
|
|
|
- *
|
|
|
- * @param viewName The post ViewPort name to remove
|
|
|
- * @return True if the ViewPort was removed successfully.
|
|
|
- *
|
|
|
- * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public boolean removePostView(String viewName) {
|
|
|
- for (int i = 0; i < postViewPorts.size(); i++) {
|
|
|
- if (postViewPorts.get(i).getName().equals(viewName)) {
|
|
|
- postViewPorts.remove(i);
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Removes the specified post ViewPort.
|
|
|
- *
|
|
|
- * @param view The post ViewPort to remove
|
|
|
- * @return True if the ViewPort was removed successfully.
|
|
|
- *
|
|
|
- * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public boolean removePostView(ViewPort view) {
|
|
|
- return postViewPorts.remove(view);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns a read-only list of all pre ViewPorts
|
|
|
- * @return a read-only list of all pre ViewPorts
|
|
|
- * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public List<ViewPort> getPreViews() {
|
|
|
- return Collections.unmodifiableList(preViewPorts);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns a read-only list of all main ViewPorts
|
|
|
- * @return a read-only list of all main ViewPorts
|
|
|
- * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public List<ViewPort> getMainViews() {
|
|
|
- return Collections.unmodifiableList(viewPorts);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns a read-only list of all post ViewPorts
|
|
|
- * @return a read-only list of all post ViewPorts
|
|
|
- * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
- */
|
|
|
- public List<ViewPort> getPostViews() {
|
|
|
- return Collections.unmodifiableList(postViewPorts);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Creates a new pre ViewPort, to display the given camera's content.
|
|
|
- * <p>
|
|
|
- * The view will be processed before the main and post viewports.
|
|
|
- */
|
|
|
- public ViewPort createPreView(String viewName, Camera cam) {
|
|
|
- ViewPort vp = new ViewPort(viewName, cam);
|
|
|
- preViewPorts.add(vp);
|
|
|
- return vp;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Creates a new main ViewPort, to display the given camera's content.
|
|
|
- * <p>
|
|
|
- * The view will be processed before the post viewports but after
|
|
|
- * the pre viewports.
|
|
|
- */
|
|
|
- public ViewPort createMainView(String viewName, Camera cam) {
|
|
|
- ViewPort vp = new ViewPort(viewName, cam);
|
|
|
- viewPorts.add(vp);
|
|
|
- return vp;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Creates a new post ViewPort, to display the given camera's content.
|
|
|
- * <p>
|
|
|
- * The view will be processed after the pre and main viewports.
|
|
|
- */
|
|
|
- public ViewPort createPostView(String viewName, Camera cam) {
|
|
|
- ViewPort vp = new ViewPort(viewName, cam);
|
|
|
- postViewPorts.add(vp);
|
|
|
- return vp;
|
|
|
- }
|
|
|
-
|
|
|
- private void notifyReshape(ViewPort vp, int w, int h) {
|
|
|
- List<SceneProcessor> processors = vp.getProcessors();
|
|
|
- for (SceneProcessor proc : processors) {
|
|
|
- if (!proc.isInitialized()) {
|
|
|
- proc.initialize(this, vp);
|
|
|
- } else {
|
|
|
- proc.reshape(vp, w, h);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Internal use only.
|
|
|
- * Updates the resolution of all on-screen cameras to match
|
|
|
- * the given width and height.
|
|
|
- */
|
|
|
- public void notifyReshape(int w, int h) {
|
|
|
- for (ViewPort vp : preViewPorts) {
|
|
|
- if (vp.getOutputFrameBuffer() == null) {
|
|
|
- Camera cam = vp.getCamera();
|
|
|
- cam.resize(w, h, true);
|
|
|
- }
|
|
|
- notifyReshape(vp, w, h);
|
|
|
- }
|
|
|
- for (ViewPort vp : viewPorts) {
|
|
|
- if (vp.getOutputFrameBuffer() == null) {
|
|
|
- Camera cam = vp.getCamera();
|
|
|
- cam.resize(w, h, true);
|
|
|
- }
|
|
|
- notifyReshape(vp, w, h);
|
|
|
- }
|
|
|
- for (ViewPort vp : postViewPorts) {
|
|
|
- if (vp.getOutputFrameBuffer() == null) {
|
|
|
- Camera cam = vp.getCamera();
|
|
|
- cam.resize(w, h, true);
|
|
|
- }
|
|
|
- notifyReshape(vp, w, h);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Internal use only.
|
|
|
- * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
|
|
|
- * based on the current world state.
|
|
|
- */
|
|
|
- public void updateUniformBindings(List<Uniform> params) {
|
|
|
- // assums worldMatrix is properly set.
|
|
|
- TempVars vars = TempVars.get();
|
|
|
-
|
|
|
- Matrix4f tempMat4 = vars.tempMat4;
|
|
|
- Matrix3f tempMat3 = vars.tempMat3;
|
|
|
- Vector2f tempVec2 = vars.vect2d;
|
|
|
- Quaternion tempVec4 = vars.quat1;
|
|
|
-
|
|
|
- for (int i = 0; i < params.size(); i++) {
|
|
|
- Uniform u = params.get(i);
|
|
|
- switch (u.getBinding()) {
|
|
|
- case WorldMatrix:
|
|
|
- u.setValue(VarType.Matrix4, worldMatrix);
|
|
|
- break;
|
|
|
- case ViewMatrix:
|
|
|
- u.setValue(VarType.Matrix4, viewMatrix);
|
|
|
- break;
|
|
|
- case ProjectionMatrix:
|
|
|
- u.setValue(VarType.Matrix4, projMatrix);
|
|
|
- break;
|
|
|
- case ViewProjectionMatrix:
|
|
|
- u.setValue(VarType.Matrix4, viewProjMatrix);
|
|
|
- break;
|
|
|
- case WorldViewMatrix:
|
|
|
- tempMat4.set(viewMatrix);
|
|
|
- tempMat4.multLocal(worldMatrix);
|
|
|
- u.setValue(VarType.Matrix4, tempMat4);
|
|
|
- break;
|
|
|
- case NormalMatrix:
|
|
|
- tempMat4.set(viewMatrix);
|
|
|
- tempMat4.multLocal(worldMatrix);
|
|
|
- tempMat4.toRotationMatrix(tempMat3);
|
|
|
- tempMat3.invertLocal();
|
|
|
- tempMat3.transposeLocal();
|
|
|
- u.setValue(VarType.Matrix3, tempMat3);
|
|
|
- break;
|
|
|
- case WorldViewProjectionMatrix:
|
|
|
- tempMat4.set(viewProjMatrix);
|
|
|
- tempMat4.multLocal(worldMatrix);
|
|
|
- u.setValue(VarType.Matrix4, tempMat4);
|
|
|
- break;
|
|
|
- case WorldMatrixInverse:
|
|
|
- tempMat4.multLocal(worldMatrix);
|
|
|
- tempMat4.invertLocal();
|
|
|
- u.setValue(VarType.Matrix4, tempMat4);
|
|
|
- break;
|
|
|
- case ViewMatrixInverse:
|
|
|
- tempMat4.set(viewMatrix);
|
|
|
- tempMat4.invertLocal();
|
|
|
- u.setValue(VarType.Matrix4, tempMat4);
|
|
|
- break;
|
|
|
- case ProjectionMatrixInverse:
|
|
|
- tempMat4.set(projMatrix);
|
|
|
- tempMat4.invertLocal();
|
|
|
- u.setValue(VarType.Matrix4, tempMat4);
|
|
|
- break;
|
|
|
- case ViewProjectionMatrixInverse:
|
|
|
- tempMat4.set(viewProjMatrix);
|
|
|
- tempMat4.invertLocal();
|
|
|
- u.setValue(VarType.Matrix4, tempMat4);
|
|
|
- break;
|
|
|
- case WorldViewMatrixInverse:
|
|
|
- tempMat4.set(viewMatrix);
|
|
|
- tempMat4.multLocal(worldMatrix);
|
|
|
- tempMat4.invertLocal();
|
|
|
- u.setValue(VarType.Matrix4, tempMat4);
|
|
|
- break;
|
|
|
- case NormalMatrixInverse:
|
|
|
- tempMat4.set(viewMatrix);
|
|
|
- tempMat4.multLocal(worldMatrix);
|
|
|
- tempMat4.toRotationMatrix(tempMat3);
|
|
|
- tempMat3.invertLocal();
|
|
|
- tempMat3.transposeLocal();
|
|
|
- tempMat3.invertLocal();
|
|
|
- u.setValue(VarType.Matrix3, tempMat3);
|
|
|
- break;
|
|
|
- case WorldViewProjectionMatrixInverse:
|
|
|
- tempMat4.set(viewProjMatrix);
|
|
|
- tempMat4.multLocal(worldMatrix);
|
|
|
- tempMat4.invertLocal();
|
|
|
- u.setValue(VarType.Matrix4, tempMat4);
|
|
|
- break;
|
|
|
- case ViewPort:
|
|
|
- tempVec4.set(viewX, viewY, viewWidth, viewHeight);
|
|
|
- u.setValue(VarType.Vector4, tempVec4);
|
|
|
- break;
|
|
|
- case Resolution:
|
|
|
- tempVec2.set(viewWidth, viewHeight);
|
|
|
- u.setValue(VarType.Vector2, tempVec2);
|
|
|
- break;
|
|
|
- case Aspect:
|
|
|
- float aspect = ((float) viewWidth) / viewHeight;
|
|
|
- u.setValue(VarType.Float, aspect);
|
|
|
- break;
|
|
|
- case FrustumNearFar:
|
|
|
- tempVec2.set(near, far);
|
|
|
- u.setValue(VarType.Vector2, tempVec2);
|
|
|
- break;
|
|
|
- case CameraPosition:
|
|
|
- u.setValue(VarType.Vector3, camLoc);
|
|
|
- break;
|
|
|
- case CameraDirection:
|
|
|
- u.setValue(VarType.Vector3, camDir);
|
|
|
- break;
|
|
|
- case CameraLeft:
|
|
|
- u.setValue(VarType.Vector3, camLeft);
|
|
|
- break;
|
|
|
- case CameraUp:
|
|
|
- u.setValue(VarType.Vector3, camUp);
|
|
|
- break;
|
|
|
- case Time:
|
|
|
- u.setValue(VarType.Float, timer.getTimeInSeconds());
|
|
|
- break;
|
|
|
- case Tpf:
|
|
|
- u.setValue(VarType.Float, timer.getTimePerFrame());
|
|
|
- break;
|
|
|
- case FrameRate:
|
|
|
- u.setValue(VarType.Float, timer.getFrameRate());
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- vars.release();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set the material to use to render all future objects.
|
|
|
- * This overrides the material set on the geometry and renders
|
|
|
- * with the provided material instead.
|
|
|
- * Use null to clear the material and return renderer to normal
|
|
|
- * functionality.
|
|
|
- * @param mat The forced material to set, or null to return to normal
|
|
|
- */
|
|
|
- public void setForcedMaterial(Material mat) {
|
|
|
- forcedMaterial = mat;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the forced render state previously set with
|
|
|
- * {@link #setForcedRenderState(com.jme3.material.RenderState) }.
|
|
|
- * @return the forced render state
|
|
|
- */
|
|
|
- public RenderState getForcedRenderState() {
|
|
|
- return forcedRenderState;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set the render state to use for all future objects.
|
|
|
- * This overrides the render state set on the material and instead
|
|
|
- * forces this render state to be applied for all future materials
|
|
|
- * rendered. Set to null to return to normal functionality.
|
|
|
- *
|
|
|
- * @param forcedRenderState The forced render state to set, or null
|
|
|
- * to return to normal
|
|
|
- */
|
|
|
- public void setForcedRenderState(RenderState forcedRenderState) {
|
|
|
- this.forcedRenderState = forcedRenderState;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set the timer that should be used to query the time based
|
|
|
- * {@link UniformBinding}s for material world parameters.
|
|
|
- *
|
|
|
- * @param timer The timer to query time world parameters
|
|
|
- */
|
|
|
- public void setTimer(Timer timer) {
|
|
|
- this.timer = timer;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the forced technique name set.
|
|
|
- *
|
|
|
- * @return the forced technique name set.
|
|
|
- *
|
|
|
- * @see #setForcedTechnique(java.lang.String)
|
|
|
- */
|
|
|
- public String getForcedTechnique() {
|
|
|
- return forcedTechnique;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Sets the forced technique to use when rendering geometries.
|
|
|
- * <p>
|
|
|
- * If the specified technique name is available on the geometry's
|
|
|
- * material, then it is used, otherwise, the
|
|
|
- * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.
|
|
|
- * If a forced material is not set and the forced technique name cannot
|
|
|
- * be found on the material, the geometry will <em>not</em> be rendered.
|
|
|
- *
|
|
|
- * @param forcedTechnique The forced technique name to use, set to null
|
|
|
- * to return to normal functionality.
|
|
|
- *
|
|
|
- * @see #renderGeometry(com.jme3.scene.Geometry)
|
|
|
- */
|
|
|
- public void setForcedTechnique(String forcedTechnique) {
|
|
|
- this.forcedTechnique = forcedTechnique;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Enable or disable alpha-to-coverage.
|
|
|
- * <p>
|
|
|
- * When alpha to coverage is enabled and the renderer implementation
|
|
|
- * supports it, then alpha blending will be replaced with alpha dissolve
|
|
|
- * if multi-sampling is also set on the renderer.
|
|
|
- * This feature allows avoiding of alpha blending artifacts due to
|
|
|
- * lack of triangle-level back-to-front sorting.
|
|
|
- *
|
|
|
- * @param value True to enable alpha-to-coverage, false otherwise.
|
|
|
- */
|
|
|
- public void setAlphaToCoverage(boolean value) {
|
|
|
- renderer.setAlphaToCoverage(value);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * True if the translucent bucket should automatically be rendered
|
|
|
- * by the RenderManager.
|
|
|
- *
|
|
|
- * @return Whether or not the translucent bucket is rendered.
|
|
|
- *
|
|
|
- * @see #setHandleTranslucentBucket(boolean)
|
|
|
- */
|
|
|
- public boolean isHandleTranslucentBucket() {
|
|
|
- return handleTranlucentBucket;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Enable or disable rendering of the
|
|
|
- * {@link Bucket#Translucent translucent bucket}
|
|
|
- * by the RenderManager. The default is enabled.
|
|
|
- *
|
|
|
- * @param handleTranslucentBucket Whether or not the translucent bucket should
|
|
|
- * be rendered.
|
|
|
- */
|
|
|
- public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {
|
|
|
- this.handleTranlucentBucket = handleTranslucentBucket;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Internal use only. Sets the world matrix to use for future
|
|
|
- * rendering. This has no effect unless objects are rendered manually
|
|
|
- * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.
|
|
|
- * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will
|
|
|
- * override this value.
|
|
|
- *
|
|
|
- * @param mat The world matrix to set
|
|
|
- */
|
|
|
- public void setWorldMatrix(Matrix4f mat) {
|
|
|
- if (shader) {
|
|
|
- worldMatrix.set(mat);
|
|
|
- } else {
|
|
|
- renderer.setWorldMatrix(mat);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Renders the given geometry.
|
|
|
- * <p>
|
|
|
- * First the proper world matrix is set, if
|
|
|
- * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}
|
|
|
- * feature is enabled, the identity world matrix is used, otherwise, the
|
|
|
- * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used.
|
|
|
- * <p>
|
|
|
- * Once the world matrix is applied, the proper material is chosen for rendering.
|
|
|
- * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is
|
|
|
- * set on this RenderManager, then it is used for rendering the geometry,
|
|
|
- * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.
|
|
|
- * <p>
|
|
|
- * If a {@link #setForcedTechnique(java.lang.String) forced technique} is
|
|
|
- * set on this RenderManager, then it is selected automatically
|
|
|
- * on the geometry's material and is used for rendering. Otherwise, one
|
|
|
- * of the {@link MaterialDef#getDefaultTechniques() default techniques} is
|
|
|
- * used.
|
|
|
- * <p>
|
|
|
- * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced
|
|
|
- * render state} is set on this RenderManager, then it is used
|
|
|
- * for rendering the material, and the material's own render state is ignored.
|
|
|
- * Otherwise, the material's render state is used as intended.
|
|
|
- *
|
|
|
- * @param g The geometry to render
|
|
|
- *
|
|
|
- * @see Technique
|
|
|
- * @see RenderState
|
|
|
- * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
|
|
|
- * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
|
|
|
- */
|
|
|
- public void renderGeometry(Geometry g) {
|
|
|
- if (g.isIgnoreTransform()) {
|
|
|
- setWorldMatrix(Matrix4f.IDENTITY);
|
|
|
- } else {
|
|
|
- setWorldMatrix(g.getWorldMatrix());
|
|
|
- }
|
|
|
-
|
|
|
- //if forcedTechnique we try to force it for render,
|
|
|
- //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
|
|
|
- //else the geom is not rendered
|
|
|
- if (forcedTechnique != null) {
|
|
|
- if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
|
|
|
- tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
|
|
|
- g.getMaterial().selectTechnique(forcedTechnique, this);
|
|
|
- // use geometry's material
|
|
|
- g.getMaterial().render(g, this);
|
|
|
- g.getMaterial().selectTechnique(tmpTech, this);
|
|
|
- //Reverted this part from revision 6197
|
|
|
- //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
|
|
|
- } else if (forcedMaterial != null) {
|
|
|
- // use forced material
|
|
|
- forcedMaterial.render(g, this);
|
|
|
- }
|
|
|
- } else if (forcedMaterial != null) {
|
|
|
- // use forced material
|
|
|
- forcedMaterial.render(g, this);
|
|
|
- } else {
|
|
|
- g.getMaterial().render(g, this);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Renders the given GeometryList.
|
|
|
- * <p>
|
|
|
- * For every geometry in the list, the
|
|
|
- * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.
|
|
|
- *
|
|
|
- * @param gl The geometry list to render.
|
|
|
- *
|
|
|
- * @see GeometryList
|
|
|
- * @see #renderGeometry(com.jme3.scene.Geometry)
|
|
|
- */
|
|
|
- public void renderGeometryList(GeometryList gl) {
|
|
|
- for (int i = 0; i < gl.size(); i++) {
|
|
|
- renderGeometry(gl.get(i));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * If a spatial is not inside the eye frustum, it
|
|
|
- * is still rendered in the shadow frustum (shadow casting queue)
|
|
|
- * through this recursive method.
|
|
|
- */
|
|
|
- private void renderShadow(Spatial s, RenderQueue rq) {
|
|
|
- if (s instanceof Node) {
|
|
|
- Node n = (Node) s;
|
|
|
- List<Spatial> children = n.getChildren();
|
|
|
- for (int i = 0; i < children.size(); i++) {
|
|
|
- renderShadow(children.get(i), rq);
|
|
|
- }
|
|
|
- } else if (s instanceof Geometry) {
|
|
|
- Geometry gm = (Geometry) s;
|
|
|
-
|
|
|
- RenderQueue.ShadowMode shadowMode = s.getShadowMode();
|
|
|
- if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
|
|
|
- //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
|
|
|
- rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Preloads a scene for rendering.
|
|
|
- * <p>
|
|
|
- * After invocation of this method, the underlying
|
|
|
- * renderer would have uploaded any textures, shaders and meshes
|
|
|
- * used by the given scene to the video driver.
|
|
|
- * Using this method is useful when wishing to avoid the initial pause
|
|
|
- * when rendering a scene for the first time. Note that it is not
|
|
|
- * guaranteed that the underlying renderer will actually choose to upload
|
|
|
- * the data to the GPU so some pause is still to be expected.
|
|
|
- *
|
|
|
- * @param scene The scene to preload
|
|
|
- */
|
|
|
- public void preloadScene(Spatial scene) {
|
|
|
- if (scene instanceof Node) {
|
|
|
- // recurse for all children
|
|
|
- Node n = (Node) scene;
|
|
|
- List<Spatial> children = n.getChildren();
|
|
|
- for (int i = 0; i < children.size(); i++) {
|
|
|
- preloadScene(children.get(i));
|
|
|
- }
|
|
|
- } else if (scene instanceof Geometry) {
|
|
|
- // add to the render queue
|
|
|
- Geometry gm = (Geometry) scene;
|
|
|
- if (gm.getMaterial() == null) {
|
|
|
- throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
|
|
|
- }
|
|
|
-
|
|
|
- gm.getMaterial().preload(this);
|
|
|
- Mesh mesh = gm.getMesh();
|
|
|
- if (mesh != null) {
|
|
|
- for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
|
|
|
- VertexBuffer buf = entry.getValue();
|
|
|
- if (buf.getData() != null) {
|
|
|
- renderer.updateBufferData(buf);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Flattens the given scene graph into the ViewPort's RenderQueue,
|
|
|
- * checking for culling as the call goes down the graph recursively.
|
|
|
- * <p>
|
|
|
- * First, the scene is checked for culling based on the <code>Spatial</code>s
|
|
|
- * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},
|
|
|
- * if the camera frustum contains the scene, then this method is recursively
|
|
|
- * called on its children.
|
|
|
- * <p>
|
|
|
- * When the scene's leaves or {@link Geometry geometries} are reached,
|
|
|
- * they are each enqueued into the
|
|
|
- * {@link ViewPort#getQueue() ViewPort's render queue}.
|
|
|
- * <p>
|
|
|
- * In addition to enqueuing the visible geometries, this method
|
|
|
- * also scenes which cast or receive shadows, by putting them into the
|
|
|
- * RenderQueue's
|
|
|
- * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
|
|
|
- * shadow queue}. Each Spatial which has its
|
|
|
- * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
|
|
|
- * set to not off, will be put into the appropriate shadow queue, note that
|
|
|
- * this process does not check for frustum culling on any
|
|
|
- * {@link ShadowMode#Cast shadow casters}, as they don't have to be
|
|
|
- * in the eye camera frustum to cast shadows on objects that are inside it.
|
|
|
- *
|
|
|
- * @param scene The scene to flatten into the queue
|
|
|
- * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}
|
|
|
- * used for culling and the {@link ViewPort#getQueue() queue} used to
|
|
|
- * contain the flattened scene graph.
|
|
|
- */
|
|
|
- public void renderScene(Spatial scene, ViewPort vp) {
|
|
|
- if (scene.getParent() == null) {
|
|
|
- vp.getCamera().setPlaneState(0);
|
|
|
- }
|
|
|
- // check culling first.
|
|
|
- if (!scene.checkCulling(vp.getCamera())) {
|
|
|
- // move on to shadow-only render
|
|
|
- if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {
|
|
|
- renderShadow(scene, vp.getQueue());
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- scene.runControlRender(this, vp);
|
|
|
- if (scene instanceof Node) {
|
|
|
- // recurse for all children
|
|
|
- Node n = (Node) scene;
|
|
|
- List<Spatial> children = n.getChildren();
|
|
|
- //saving cam state for culling
|
|
|
- int camState = vp.getCamera().getPlaneState();
|
|
|
- for (int i = 0; i < children.size(); i++) {
|
|
|
- //restoring cam state before proceeding children recusively
|
|
|
- vp.getCamera().setPlaneState(camState);
|
|
|
- renderScene(children.get(i), vp);
|
|
|
-
|
|
|
- }
|
|
|
- } else if (scene instanceof Geometry) {
|
|
|
-
|
|
|
- // add to the render queue
|
|
|
- Geometry gm = (Geometry) scene;
|
|
|
- if (gm.getMaterial() == null) {
|
|
|
- throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
|
|
|
- }
|
|
|
-
|
|
|
- vp.getQueue().addToQueue(gm, scene.getQueueBucket());
|
|
|
-
|
|
|
- // add to shadow queue if needed
|
|
|
- RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
|
|
|
- if (shadowMode != RenderQueue.ShadowMode.Off) {
|
|
|
- vp.getQueue().addToShadowQueue(gm, shadowMode);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Returns the camera currently used for rendering.
|
|
|
- * <p>
|
|
|
- * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.
|
|
|
- *
|
|
|
- * @return the camera currently used for rendering.
|
|
|
- */
|
|
|
- public Camera getCurrentCamera() {
|
|
|
- return prevCam;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * The renderer implementation used for rendering operations.
|
|
|
- *
|
|
|
- * @return The renderer implementation
|
|
|
- *
|
|
|
- * @see #RenderManager(com.jme3.renderer.Renderer)
|
|
|
- * @see Renderer
|
|
|
- */
|
|
|
- public Renderer getRenderer() {
|
|
|
- return renderer;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}
|
|
|
- * by rendering each of its visible buckets.
|
|
|
- * By default the queues will automatically be cleared after rendering,
|
|
|
- * so there's no need to clear them manually.
|
|
|
- *
|
|
|
- * @param vp The ViewPort of which the queue will be flushed
|
|
|
- *
|
|
|
- * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)
|
|
|
- * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)
|
|
|
- */
|
|
|
- public void flushQueue(ViewPort vp) {
|
|
|
- renderViewPortQueues(vp, true);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Clears the queue of the given ViewPort.
|
|
|
- * Simply calls {@link RenderQueue#clear() } on the ViewPort's
|
|
|
- * {@link ViewPort#getQueue() render queue}.
|
|
|
- *
|
|
|
- * @param vp The ViewPort of which the queue will be cleared.
|
|
|
- *
|
|
|
- * @see RenderQueue#clear()
|
|
|
- * @see ViewPort#getQueue()
|
|
|
- */
|
|
|
- public void clearQueue(ViewPort vp) {
|
|
|
- vp.getQueue().clear();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Render the given viewport queues.
|
|
|
- * <p>
|
|
|
- * Changes the {@link Renderer#setDepthRange(float, float) depth range}
|
|
|
- * appropriately as expected by each queue and then calls
|
|
|
- * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }
|
|
|
- * on the queue. Makes sure to restore the depth range to [0, 1]
|
|
|
- * at the end of the call.
|
|
|
- * Note that the {@link Bucket#Translucent translucent bucket} is NOT
|
|
|
- * rendered by this method. Instead the user should call
|
|
|
- * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }
|
|
|
- * after this call.
|
|
|
- *
|
|
|
- * @param vp the viewport of which queue should be rendered
|
|
|
- * @param flush If true, the queues will be cleared after
|
|
|
- * rendering.
|
|
|
- *
|
|
|
- * @see RenderQueue
|
|
|
- * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)
|
|
|
- */
|
|
|
- public void renderViewPortQueues(ViewPort vp, boolean flush) {
|
|
|
- RenderQueue rq = vp.getQueue();
|
|
|
- Camera cam = vp.getCamera();
|
|
|
- boolean depthRangeChanged = false;
|
|
|
-
|
|
|
- // render opaque objects with default depth range
|
|
|
- // opaque objects are sorted front-to-back, reducing overdraw
|
|
|
- rq.renderQueue(Bucket.Opaque, this, cam, flush);
|
|
|
-
|
|
|
- // render the sky, with depth range set to the farthest
|
|
|
- if (!rq.isQueueEmpty(Bucket.Sky)) {
|
|
|
- renderer.setDepthRange(1, 1);
|
|
|
- rq.renderQueue(Bucket.Sky, this, cam, flush);
|
|
|
- depthRangeChanged = true;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- // transparent objects are last because they require blending with the
|
|
|
- // rest of the scene's objects. Consequently, they are sorted
|
|
|
- // back-to-front.
|
|
|
- if (!rq.isQueueEmpty(Bucket.Transparent)) {
|
|
|
- if (depthRangeChanged) {
|
|
|
- renderer.setDepthRange(0, 1);
|
|
|
- depthRangeChanged = false;
|
|
|
- }
|
|
|
-
|
|
|
- rq.renderQueue(Bucket.Transparent, this, cam, flush);
|
|
|
- }
|
|
|
-
|
|
|
- if (!rq.isQueueEmpty(Bucket.Gui)) {
|
|
|
- renderer.setDepthRange(0, 0);
|
|
|
- setCamera(cam, true);
|
|
|
- rq.renderQueue(Bucket.Gui, this, cam, flush);
|
|
|
- setCamera(cam, false);
|
|
|
- depthRangeChanged = true;
|
|
|
- }
|
|
|
-
|
|
|
- // restore range to default
|
|
|
- if (depthRangeChanged) {
|
|
|
- renderer.setDepthRange(0, 1);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.
|
|
|
- * <p>
|
|
|
- * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }
|
|
|
- * is set to true. This method clears the translucent queue after rendering
|
|
|
- * it.
|
|
|
- *
|
|
|
- * @param vp The viewport of which the translucent queue should be rendered.
|
|
|
- *
|
|
|
- * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)
|
|
|
- * @see #setHandleTranslucentBucket(boolean)
|
|
|
- */
|
|
|
- public void renderTranslucentQueue(ViewPort vp) {
|
|
|
- RenderQueue rq = vp.getQueue();
|
|
|
- if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
|
|
|
- rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void setViewPort(Camera cam) {
|
|
|
- // this will make sure to update viewport only if needed
|
|
|
- if (cam != prevCam || cam.isViewportChanged()) {
|
|
|
- viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
|
|
|
- viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
|
|
|
- viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
|
|
|
- viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
|
|
|
- renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
|
|
|
- renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
|
|
|
- cam.clearViewportChanged();
|
|
|
- prevCam = cam;
|
|
|
-
|
|
|
-// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
|
|
|
-// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
|
|
|
-// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
|
|
|
-// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
|
|
|
-//
|
|
|
-// orthoMatrix.loadIdentity();
|
|
|
-// orthoMatrix.setTranslation(translateX, translateY, 0);
|
|
|
-// orthoMatrix.setScale(scaleX, scaleY, 0);
|
|
|
-
|
|
|
- orthoMatrix.loadIdentity();
|
|
|
- orthoMatrix.setTranslation(-1f, -1f, 0f);
|
|
|
- orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void setViewProjection(Camera cam, boolean ortho) {
|
|
|
- if (shader) {
|
|
|
- if (ortho) {
|
|
|
- viewMatrix.set(Matrix4f.IDENTITY);
|
|
|
- projMatrix.set(orthoMatrix);
|
|
|
- viewProjMatrix.set(orthoMatrix);
|
|
|
- } else {
|
|
|
- viewMatrix.set(cam.getViewMatrix());
|
|
|
- projMatrix.set(cam.getProjectionMatrix());
|
|
|
- viewProjMatrix.set(cam.getViewProjectionMatrix());
|
|
|
- }
|
|
|
-
|
|
|
- camLoc.set(cam.getLocation());
|
|
|
- cam.getLeft(camLeft);
|
|
|
- cam.getUp(camUp);
|
|
|
- cam.getDirection(camDir);
|
|
|
-
|
|
|
- near = cam.getFrustumNear();
|
|
|
- far = cam.getFrustumFar();
|
|
|
- } else {
|
|
|
- if (ortho) {
|
|
|
- renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
|
|
|
- } else {
|
|
|
- renderer.setViewProjectionMatrices(cam.getViewMatrix(),
|
|
|
- cam.getProjectionMatrix());
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Set the camera to use for rendering.
|
|
|
- * <p>
|
|
|
- * First, the camera's
|
|
|
- * {@link Camera#setViewPort(float, float, float, float) view port parameters}
|
|
|
- * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and
|
|
|
- * {@link Camera#getProjectionMatrix() projection} matrices are set
|
|
|
- * on the renderer. If <code>ortho</code> is <code>true</code>, then
|
|
|
- * instead of using the camera's view and projection matrices, an ortho
|
|
|
- * matrix is computed and used instead of the view projection matrix.
|
|
|
- * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)
|
|
|
- * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).
|
|
|
- *
|
|
|
- * @param cam The camera to set
|
|
|
- * @param ortho True if to use orthographic projection (for GUI rendering),
|
|
|
- * false if to use the camera's view and projection matrices.
|
|
|
- */
|
|
|
- public void setCamera(Camera cam, boolean ortho) {
|
|
|
- setViewPort(cam);
|
|
|
- setViewProjection(cam, ortho);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Draws the viewport but without notifying {@link SceneProcessor scene
|
|
|
- * processors} of any rendering events.
|
|
|
- *
|
|
|
- * @param vp The ViewPort to render
|
|
|
- *
|
|
|
- * @see #renderViewPort(com.jme3.renderer.ViewPort, float)
|
|
|
- */
|
|
|
- public void renderViewPortRaw(ViewPort vp) {
|
|
|
- setCamera(vp.getCamera(), false);
|
|
|
- List<Spatial> scenes = vp.getScenes();
|
|
|
- for (int i = scenes.size() - 1; i >= 0; i--) {
|
|
|
- renderScene(scenes.get(i), vp);
|
|
|
- }
|
|
|
- flushQueue(vp);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Renders the {@link ViewPort}.
|
|
|
- * <p>
|
|
|
- * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method
|
|
|
- * returns immediately. Otherwise, the ViewPort is rendered by
|
|
|
- * the following process:<br>
|
|
|
- * <ul>
|
|
|
- * <li>All {@link SceneProcessor scene processors} that are attached
|
|
|
- * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.
|
|
|
- * </li>
|
|
|
- * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method
|
|
|
- * is called.</li>
|
|
|
- * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}
|
|
|
- * is set on the Renderer</li>
|
|
|
- * <li>The camera is set on the renderer, including its view port parameters.
|
|
|
- * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>
|
|
|
- * <li>Any buffers that the ViewPort requests to be cleared are cleared
|
|
|
- * and the {@link ViewPort#getBackgroundColor() background color} is set</li>
|
|
|
- * <li>Every scene that is attached to the ViewPort is flattened into
|
|
|
- * the ViewPort's render queue
|
|
|
- * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })
|
|
|
- * </li>
|
|
|
- * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }
|
|
|
- * method is called.</li>
|
|
|
- * <li>The render queue is sorted and then flushed, sending
|
|
|
- * rendering commands to the underlying Renderer implementation.
|
|
|
- * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>
|
|
|
- * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }
|
|
|
- * method is called.</li>
|
|
|
- * <li>The translucent queue of the ViewPort is sorted and then flushed
|
|
|
- * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
|
|
|
- * <li>If any objects remained in the render queue, they are removed
|
|
|
- * from the queue. This is generally objects added to the
|
|
|
- * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
|
|
|
- * shadow queue}
|
|
|
- * which were not rendered because of a missing shadow renderer.</li>
|
|
|
- * </ul>
|
|
|
- *
|
|
|
- * @param vp
|
|
|
- * @param tpf
|
|
|
- */
|
|
|
- public void renderViewPort(ViewPort vp, float tpf) {
|
|
|
- if (!vp.isEnabled()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- List<SceneProcessor> processors = vp.getProcessors();
|
|
|
- if (processors.isEmpty()) {
|
|
|
- processors = null;
|
|
|
- }
|
|
|
-
|
|
|
- if (processors != null) {
|
|
|
- for (SceneProcessor proc : processors) {
|
|
|
- if (!proc.isInitialized()) {
|
|
|
- proc.initialize(this, vp);
|
|
|
- }
|
|
|
- proc.preFrame(tpf);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- renderer.setFrameBuffer(vp.getOutputFrameBuffer());
|
|
|
- setCamera(vp.getCamera(), false);
|
|
|
- if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
|
|
|
- if (vp.isClearColor()) {
|
|
|
- renderer.setBackgroundColor(vp.getBackgroundColor());
|
|
|
- }
|
|
|
- renderer.clearBuffers(vp.isClearColor(),
|
|
|
- vp.isClearDepth(),
|
|
|
- vp.isClearStencil());
|
|
|
- }
|
|
|
-
|
|
|
- List<Spatial> scenes = vp.getScenes();
|
|
|
- for (int i = scenes.size() - 1; i >= 0; i--) {
|
|
|
- renderScene(scenes.get(i), vp);
|
|
|
- }
|
|
|
-
|
|
|
- if (processors != null) {
|
|
|
- for (SceneProcessor proc : processors) {
|
|
|
- proc.postQueue(vp.getQueue());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- flushQueue(vp);
|
|
|
-
|
|
|
- if (processors != null) {
|
|
|
- for (SceneProcessor proc : processors) {
|
|
|
- proc.postFrame(vp.getOutputFrameBuffer());
|
|
|
- }
|
|
|
- }
|
|
|
- //renders the translucent objects queue after processors have been rendered
|
|
|
- renderTranslucentQueue(vp);
|
|
|
- // clear any remaining spatials that were not rendered.
|
|
|
- clearQueue(vp);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Called by the application to render any ViewPorts
|
|
|
- * added to this RenderManager.
|
|
|
- * <p>
|
|
|
- * Renders any viewports that were added using the following methods:
|
|
|
- * <ul>
|
|
|
- * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
|
|
|
- * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
|
|
|
- * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
|
|
|
- * </ul>
|
|
|
- *
|
|
|
- * @param tpf Time per frame value
|
|
|
- */
|
|
|
- public void render(float tpf, boolean mainFrameBufferActive) {
|
|
|
- if (renderer instanceof NullRenderer) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- this.shader = renderer.getCaps().contains(Caps.GLSL100);
|
|
|
-
|
|
|
- for (int i = 0; i < preViewPorts.size(); i++) {
|
|
|
- ViewPort vp = preViewPorts.get(i);
|
|
|
- if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
|
|
|
- renderViewPort(vp, tpf);
|
|
|
- }
|
|
|
- }
|
|
|
- for (int i = 0; i < viewPorts.size(); i++) {
|
|
|
- ViewPort vp = viewPorts.get(i);
|
|
|
- if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
|
|
|
- renderViewPort(vp, tpf);
|
|
|
- }
|
|
|
- }
|
|
|
- for (int i = 0; i < postViewPorts.size(); i++) {
|
|
|
- ViewPort vp = postViewPorts.get(i);
|
|
|
- if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
|
|
|
- renderViewPort(vp, tpf);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+/*
|
|
|
+ * Copyright (c) 2009-2012 jMonkeyEngine
|
|
|
+ * All rights reserved.
|
|
|
+ *
|
|
|
+ * Redistribution and use in source and binary forms, with or without
|
|
|
+ * modification, are permitted provided that the following conditions are
|
|
|
+ * met:
|
|
|
+ *
|
|
|
+ * * Redistributions of source code must retain the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer.
|
|
|
+ *
|
|
|
+ * * Redistributions in binary form must reproduce the above copyright
|
|
|
+ * notice, this list of conditions and the following disclaimer in the
|
|
|
+ * documentation and/or other materials provided with the distribution.
|
|
|
+ *
|
|
|
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
|
|
+ * may be used to endorse or promote products derived from this software
|
|
|
+ * without specific prior written permission.
|
|
|
+ *
|
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
|
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
+ */
|
|
|
+package com.jme3.renderer;
|
|
|
+
|
|
|
+import com.jme3.material.Material;
|
|
|
+import com.jme3.material.MaterialDef;
|
|
|
+import com.jme3.material.RenderState;
|
|
|
+import com.jme3.material.Technique;
|
|
|
+import com.jme3.math.*;
|
|
|
+import com.jme3.post.SceneProcessor;
|
|
|
+import com.jme3.renderer.queue.GeometryList;
|
|
|
+import com.jme3.renderer.queue.RenderQueue;
|
|
|
+import com.jme3.renderer.queue.RenderQueue.Bucket;
|
|
|
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
|
|
+import com.jme3.scene.*;
|
|
|
+import com.jme3.shader.Uniform;
|
|
|
+import com.jme3.shader.UniformBinding;
|
|
|
+import com.jme3.shader.VarType;
|
|
|
+import com.jme3.system.NullRenderer;
|
|
|
+import com.jme3.system.Timer;
|
|
|
+import com.jme3.util.IntMap.Entry;
|
|
|
+import com.jme3.util.TempVars;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.logging.Logger;
|
|
|
+
|
|
|
+/**
|
|
|
+ * <code>RenderManager</code> is a high-level rendering interface that is
|
|
|
+ * above the Renderer implementation. RenderManager takes care
|
|
|
+ * of rendering the scene graphs attached to each viewport and
|
|
|
+ * handling SceneProcessors.
|
|
|
+ *
|
|
|
+ * @see SceneProcessor
|
|
|
+ * @see ViewPort
|
|
|
+ * @see Spatial
|
|
|
+ */
|
|
|
+public class RenderManager {
|
|
|
+
|
|
|
+ private static final Logger logger = Logger.getLogger(RenderManager.class.getName());
|
|
|
+
|
|
|
+ private Renderer renderer;
|
|
|
+ private Timer timer;
|
|
|
+ private ArrayList<ViewPort> preViewPorts = new ArrayList<ViewPort>();
|
|
|
+ private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
|
|
|
+ private ArrayList<ViewPort> postViewPorts = new ArrayList<ViewPort>();
|
|
|
+ private Camera prevCam = null;
|
|
|
+ private Material forcedMaterial = null;
|
|
|
+ private String forcedTechnique = null;
|
|
|
+ private RenderState forcedRenderState = null;
|
|
|
+ private boolean shader;
|
|
|
+ private int viewX, viewY, viewWidth, viewHeight;
|
|
|
+ private float near, far;
|
|
|
+ private Matrix4f orthoMatrix = new Matrix4f();
|
|
|
+ private Matrix4f viewMatrix = new Matrix4f();
|
|
|
+ private Matrix4f projMatrix = new Matrix4f();
|
|
|
+ private Matrix4f viewProjMatrix = new Matrix4f();
|
|
|
+ private Matrix4f worldMatrix = new Matrix4f();
|
|
|
+ private Vector3f camUp = new Vector3f(),
|
|
|
+ camLeft = new Vector3f(),
|
|
|
+ camDir = new Vector3f(),
|
|
|
+ camLoc = new Vector3f();
|
|
|
+ //temp technique
|
|
|
+ private String tmpTech;
|
|
|
+ private boolean handleTranlucentBucket = true;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Create a high-level rendering interface over the
|
|
|
+ * low-level rendering interface.
|
|
|
+ * @param renderer
|
|
|
+ */
|
|
|
+ public RenderManager(Renderer renderer) {
|
|
|
+ this.renderer = renderer;
|
|
|
+ //this.shader = renderer.getCaps().contains(Caps.GLSL100);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the pre ViewPort with the given name.
|
|
|
+ *
|
|
|
+ * @param viewName The name of the pre ViewPort to look up
|
|
|
+ * @return The ViewPort, or null if not found.
|
|
|
+ *
|
|
|
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public ViewPort getPreView(String viewName) {
|
|
|
+ for (int i = 0; i < preViewPorts.size(); i++) {
|
|
|
+ if (preViewPorts.get(i).getName().equals(viewName)) {
|
|
|
+ return preViewPorts.get(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Removes the specified pre ViewPort.
|
|
|
+ *
|
|
|
+ * @param view The pre ViewPort to remove
|
|
|
+ * @return True if the ViewPort was removed successfully.
|
|
|
+ *
|
|
|
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public boolean removePreView(ViewPort view) {
|
|
|
+ return preViewPorts.remove(view);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the main ViewPort with the given name.
|
|
|
+ *
|
|
|
+ * @param viewName The name of the main ViewPort to look up
|
|
|
+ * @return The ViewPort, or null if not found.
|
|
|
+ *
|
|
|
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public ViewPort getMainView(String viewName) {
|
|
|
+ for (int i = 0; i < viewPorts.size(); i++) {
|
|
|
+ if (viewPorts.get(i).getName().equals(viewName)) {
|
|
|
+ return viewPorts.get(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Removes the main ViewPort with the specified name.
|
|
|
+ *
|
|
|
+ * @param viewName The main ViewPort name to remove
|
|
|
+ * @return True if the ViewPort was removed successfully.
|
|
|
+ *
|
|
|
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public boolean removeMainView(String viewName) {
|
|
|
+ for (int i = 0; i < viewPorts.size(); i++) {
|
|
|
+ if (viewPorts.get(i).getName().equals(viewName)) {
|
|
|
+ viewPorts.remove(i);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Removes the specified main ViewPort.
|
|
|
+ *
|
|
|
+ * @param view The main ViewPort to remove
|
|
|
+ * @return True if the ViewPort was removed successfully.
|
|
|
+ *
|
|
|
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public boolean removeMainView(ViewPort view) {
|
|
|
+ return viewPorts.remove(view);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the post ViewPort with the given name.
|
|
|
+ *
|
|
|
+ * @param viewName The name of the post ViewPort to look up
|
|
|
+ * @return The ViewPort, or null if not found.
|
|
|
+ *
|
|
|
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public ViewPort getPostView(String viewName) {
|
|
|
+ for (int i = 0; i < postViewPorts.size(); i++) {
|
|
|
+ if (postViewPorts.get(i).getName().equals(viewName)) {
|
|
|
+ return postViewPorts.get(i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Removes the post ViewPort with the specified name.
|
|
|
+ *
|
|
|
+ * @param viewName The post ViewPort name to remove
|
|
|
+ * @return True if the ViewPort was removed successfully.
|
|
|
+ *
|
|
|
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public boolean removePostView(String viewName) {
|
|
|
+ for (int i = 0; i < postViewPorts.size(); i++) {
|
|
|
+ if (postViewPorts.get(i).getName().equals(viewName)) {
|
|
|
+ postViewPorts.remove(i);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Removes the specified post ViewPort.
|
|
|
+ *
|
|
|
+ * @param view The post ViewPort to remove
|
|
|
+ * @return True if the ViewPort was removed successfully.
|
|
|
+ *
|
|
|
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public boolean removePostView(ViewPort view) {
|
|
|
+ return postViewPorts.remove(view);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns a read-only list of all pre ViewPorts
|
|
|
+ * @return a read-only list of all pre ViewPorts
|
|
|
+ * @see #createPreView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public List<ViewPort> getPreViews() {
|
|
|
+ return Collections.unmodifiableList(preViewPorts);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns a read-only list of all main ViewPorts
|
|
|
+ * @return a read-only list of all main ViewPorts
|
|
|
+ * @see #createMainView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public List<ViewPort> getMainViews() {
|
|
|
+ return Collections.unmodifiableList(viewPorts);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns a read-only list of all post ViewPorts
|
|
|
+ * @return a read-only list of all post ViewPorts
|
|
|
+ * @see #createPostView(java.lang.String, com.jme3.renderer.Camera)
|
|
|
+ */
|
|
|
+ public List<ViewPort> getPostViews() {
|
|
|
+ return Collections.unmodifiableList(postViewPorts);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates a new pre ViewPort, to display the given camera's content.
|
|
|
+ * <p>
|
|
|
+ * The view will be processed before the main and post viewports.
|
|
|
+ */
|
|
|
+ public ViewPort createPreView(String viewName, Camera cam) {
|
|
|
+ ViewPort vp = new ViewPort(viewName, cam);
|
|
|
+ preViewPorts.add(vp);
|
|
|
+ return vp;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates a new main ViewPort, to display the given camera's content.
|
|
|
+ * <p>
|
|
|
+ * The view will be processed before the post viewports but after
|
|
|
+ * the pre viewports.
|
|
|
+ */
|
|
|
+ public ViewPort createMainView(String viewName, Camera cam) {
|
|
|
+ ViewPort vp = new ViewPort(viewName, cam);
|
|
|
+ viewPorts.add(vp);
|
|
|
+ return vp;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates a new post ViewPort, to display the given camera's content.
|
|
|
+ * <p>
|
|
|
+ * The view will be processed after the pre and main viewports.
|
|
|
+ */
|
|
|
+ public ViewPort createPostView(String viewName, Camera cam) {
|
|
|
+ ViewPort vp = new ViewPort(viewName, cam);
|
|
|
+ postViewPorts.add(vp);
|
|
|
+ return vp;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void notifyReshape(ViewPort vp, int w, int h) {
|
|
|
+ List<SceneProcessor> processors = vp.getProcessors();
|
|
|
+ for (SceneProcessor proc : processors) {
|
|
|
+ if (!proc.isInitialized()) {
|
|
|
+ proc.initialize(this, vp);
|
|
|
+ } else {
|
|
|
+ proc.reshape(vp, w, h);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Internal use only.
|
|
|
+ * Updates the resolution of all on-screen cameras to match
|
|
|
+ * the given width and height.
|
|
|
+ */
|
|
|
+ public void notifyReshape(int w, int h) {
|
|
|
+ for (ViewPort vp : preViewPorts) {
|
|
|
+ if (vp.getOutputFrameBuffer() == null) {
|
|
|
+ Camera cam = vp.getCamera();
|
|
|
+ cam.resize(w, h, true);
|
|
|
+ }
|
|
|
+ notifyReshape(vp, w, h);
|
|
|
+ }
|
|
|
+ for (ViewPort vp : viewPorts) {
|
|
|
+ if (vp.getOutputFrameBuffer() == null) {
|
|
|
+ Camera cam = vp.getCamera();
|
|
|
+ cam.resize(w, h, true);
|
|
|
+ }
|
|
|
+ notifyReshape(vp, w, h);
|
|
|
+ }
|
|
|
+ for (ViewPort vp : postViewPorts) {
|
|
|
+ if (vp.getOutputFrameBuffer() == null) {
|
|
|
+ Camera cam = vp.getCamera();
|
|
|
+ cam.resize(w, h, true);
|
|
|
+ }
|
|
|
+ notifyReshape(vp, w, h);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Internal use only.
|
|
|
+ * Updates the given list of uniforms with {@link UniformBinding uniform bindings}
|
|
|
+ * based on the current world state.
|
|
|
+ */
|
|
|
+ public void updateUniformBindings(List<Uniform> params) {
|
|
|
+ // assums worldMatrix is properly set.
|
|
|
+ TempVars vars = TempVars.get();
|
|
|
+
|
|
|
+ Matrix4f tempMat4 = vars.tempMat4;
|
|
|
+ Matrix3f tempMat3 = vars.tempMat3;
|
|
|
+ Vector2f tempVec2 = vars.vect2d;
|
|
|
+ Quaternion tempVec4 = vars.quat1;
|
|
|
+
|
|
|
+ for (int i = 0; i < params.size(); i++) {
|
|
|
+ Uniform u = params.get(i);
|
|
|
+ switch (u.getBinding()) {
|
|
|
+ case WorldMatrix:
|
|
|
+ u.setValue(VarType.Matrix4, worldMatrix);
|
|
|
+ break;
|
|
|
+ case ViewMatrix:
|
|
|
+ u.setValue(VarType.Matrix4, viewMatrix);
|
|
|
+ break;
|
|
|
+ case ProjectionMatrix:
|
|
|
+ u.setValue(VarType.Matrix4, projMatrix);
|
|
|
+ break;
|
|
|
+ case ViewProjectionMatrix:
|
|
|
+ u.setValue(VarType.Matrix4, viewProjMatrix);
|
|
|
+ break;
|
|
|
+ case WorldViewMatrix:
|
|
|
+ tempMat4.set(viewMatrix);
|
|
|
+ tempMat4.multLocal(worldMatrix);
|
|
|
+ u.setValue(VarType.Matrix4, tempMat4);
|
|
|
+ break;
|
|
|
+ case NormalMatrix:
|
|
|
+ tempMat4.set(viewMatrix);
|
|
|
+ tempMat4.multLocal(worldMatrix);
|
|
|
+ tempMat4.toRotationMatrix(tempMat3);
|
|
|
+ tempMat3.invertLocal();
|
|
|
+ tempMat3.transposeLocal();
|
|
|
+ u.setValue(VarType.Matrix3, tempMat3);
|
|
|
+ break;
|
|
|
+ case WorldViewProjectionMatrix:
|
|
|
+ tempMat4.set(viewProjMatrix);
|
|
|
+ tempMat4.multLocal(worldMatrix);
|
|
|
+ u.setValue(VarType.Matrix4, tempMat4);
|
|
|
+ break;
|
|
|
+ case WorldMatrixInverse:
|
|
|
+ tempMat4.multLocal(worldMatrix);
|
|
|
+ tempMat4.invertLocal();
|
|
|
+ u.setValue(VarType.Matrix4, tempMat4);
|
|
|
+ break;
|
|
|
+ case WorldMatrixInverseTranspose:
|
|
|
+ worldMatrix.toRotationMatrix(tempMat3);
|
|
|
+ tempMat3.invertLocal().transposeLocal();
|
|
|
+ u.setValue(VarType.Matrix3, tempMat3);
|
|
|
+ break;
|
|
|
+ case ViewMatrixInverse:
|
|
|
+ tempMat4.set(viewMatrix);
|
|
|
+ tempMat4.invertLocal();
|
|
|
+ u.setValue(VarType.Matrix4, tempMat4);
|
|
|
+ break;
|
|
|
+ case ProjectionMatrixInverse:
|
|
|
+ tempMat4.set(projMatrix);
|
|
|
+ tempMat4.invertLocal();
|
|
|
+ u.setValue(VarType.Matrix4, tempMat4);
|
|
|
+ break;
|
|
|
+ case ViewProjectionMatrixInverse:
|
|
|
+ tempMat4.set(viewProjMatrix);
|
|
|
+ tempMat4.invertLocal();
|
|
|
+ u.setValue(VarType.Matrix4, tempMat4);
|
|
|
+ break;
|
|
|
+ case WorldViewMatrixInverse:
|
|
|
+ tempMat4.set(viewMatrix);
|
|
|
+ tempMat4.multLocal(worldMatrix);
|
|
|
+ tempMat4.invertLocal();
|
|
|
+ u.setValue(VarType.Matrix4, tempMat4);
|
|
|
+ break;
|
|
|
+ case NormalMatrixInverse:
|
|
|
+ tempMat4.set(viewMatrix);
|
|
|
+ tempMat4.multLocal(worldMatrix);
|
|
|
+ tempMat4.toRotationMatrix(tempMat3);
|
|
|
+ tempMat3.invertLocal();
|
|
|
+ tempMat3.transposeLocal();
|
|
|
+ tempMat3.invertLocal();
|
|
|
+ u.setValue(VarType.Matrix3, tempMat3);
|
|
|
+ break;
|
|
|
+ case WorldViewProjectionMatrixInverse:
|
|
|
+ tempMat4.set(viewProjMatrix);
|
|
|
+ tempMat4.multLocal(worldMatrix);
|
|
|
+ tempMat4.invertLocal();
|
|
|
+ u.setValue(VarType.Matrix4, tempMat4);
|
|
|
+ break;
|
|
|
+ case ViewPort:
|
|
|
+ tempVec4.set(viewX, viewY, viewWidth, viewHeight);
|
|
|
+ u.setValue(VarType.Vector4, tempVec4);
|
|
|
+ break;
|
|
|
+ case Resolution:
|
|
|
+ tempVec2.set(viewWidth, viewHeight);
|
|
|
+ u.setValue(VarType.Vector2, tempVec2);
|
|
|
+ break;
|
|
|
+ case Aspect:
|
|
|
+ float aspect = ((float) viewWidth) / viewHeight;
|
|
|
+ u.setValue(VarType.Float, aspect);
|
|
|
+ break;
|
|
|
+ case FrustumNearFar:
|
|
|
+ tempVec2.set(near, far);
|
|
|
+ u.setValue(VarType.Vector2, tempVec2);
|
|
|
+ break;
|
|
|
+ case CameraPosition:
|
|
|
+ u.setValue(VarType.Vector3, camLoc);
|
|
|
+ break;
|
|
|
+ case CameraDirection:
|
|
|
+ u.setValue(VarType.Vector3, camDir);
|
|
|
+ break;
|
|
|
+ case CameraLeft:
|
|
|
+ u.setValue(VarType.Vector3, camLeft);
|
|
|
+ break;
|
|
|
+ case CameraUp:
|
|
|
+ u.setValue(VarType.Vector3, camUp);
|
|
|
+ break;
|
|
|
+ case Time:
|
|
|
+ u.setValue(VarType.Float, timer.getTimeInSeconds());
|
|
|
+ break;
|
|
|
+ case Tpf:
|
|
|
+ u.setValue(VarType.Float, timer.getTimePerFrame());
|
|
|
+ break;
|
|
|
+ case FrameRate:
|
|
|
+ u.setValue(VarType.Float, timer.getFrameRate());
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ vars.release();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the material to use to render all future objects.
|
|
|
+ * This overrides the material set on the geometry and renders
|
|
|
+ * with the provided material instead.
|
|
|
+ * Use null to clear the material and return renderer to normal
|
|
|
+ * functionality.
|
|
|
+ * @param mat The forced material to set, or null to return to normal
|
|
|
+ */
|
|
|
+ public void setForcedMaterial(Material mat) {
|
|
|
+ forcedMaterial = mat;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the forced render state previously set with
|
|
|
+ * {@link #setForcedRenderState(com.jme3.material.RenderState) }.
|
|
|
+ * @return the forced render state
|
|
|
+ */
|
|
|
+ public RenderState getForcedRenderState() {
|
|
|
+ return forcedRenderState;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the render state to use for all future objects.
|
|
|
+ * This overrides the render state set on the material and instead
|
|
|
+ * forces this render state to be applied for all future materials
|
|
|
+ * rendered. Set to null to return to normal functionality.
|
|
|
+ *
|
|
|
+ * @param forcedRenderState The forced render state to set, or null
|
|
|
+ * to return to normal
|
|
|
+ */
|
|
|
+ public void setForcedRenderState(RenderState forcedRenderState) {
|
|
|
+ this.forcedRenderState = forcedRenderState;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the timer that should be used to query the time based
|
|
|
+ * {@link UniformBinding}s for material world parameters.
|
|
|
+ *
|
|
|
+ * @param timer The timer to query time world parameters
|
|
|
+ */
|
|
|
+ public void setTimer(Timer timer) {
|
|
|
+ this.timer = timer;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the forced technique name set.
|
|
|
+ *
|
|
|
+ * @return the forced technique name set.
|
|
|
+ *
|
|
|
+ * @see #setForcedTechnique(java.lang.String)
|
|
|
+ */
|
|
|
+ public String getForcedTechnique() {
|
|
|
+ return forcedTechnique;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Sets the forced technique to use when rendering geometries.
|
|
|
+ * <p>
|
|
|
+ * If the specified technique name is available on the geometry's
|
|
|
+ * material, then it is used, otherwise, the
|
|
|
+ * {@link #setForcedMaterial(com.jme3.material.Material) forced material} is used.
|
|
|
+ * If a forced material is not set and the forced technique name cannot
|
|
|
+ * be found on the material, the geometry will <em>not</em> be rendered.
|
|
|
+ *
|
|
|
+ * @param forcedTechnique The forced technique name to use, set to null
|
|
|
+ * to return to normal functionality.
|
|
|
+ *
|
|
|
+ * @see #renderGeometry(com.jme3.scene.Geometry)
|
|
|
+ */
|
|
|
+ public void setForcedTechnique(String forcedTechnique) {
|
|
|
+ this.forcedTechnique = forcedTechnique;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Enable or disable alpha-to-coverage.
|
|
|
+ * <p>
|
|
|
+ * When alpha to coverage is enabled and the renderer implementation
|
|
|
+ * supports it, then alpha blending will be replaced with alpha dissolve
|
|
|
+ * if multi-sampling is also set on the renderer.
|
|
|
+ * This feature allows avoiding of alpha blending artifacts due to
|
|
|
+ * lack of triangle-level back-to-front sorting.
|
|
|
+ *
|
|
|
+ * @param value True to enable alpha-to-coverage, false otherwise.
|
|
|
+ */
|
|
|
+ public void setAlphaToCoverage(boolean value) {
|
|
|
+ renderer.setAlphaToCoverage(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * True if the translucent bucket should automatically be rendered
|
|
|
+ * by the RenderManager.
|
|
|
+ *
|
|
|
+ * @return Whether or not the translucent bucket is rendered.
|
|
|
+ *
|
|
|
+ * @see #setHandleTranslucentBucket(boolean)
|
|
|
+ */
|
|
|
+ public boolean isHandleTranslucentBucket() {
|
|
|
+ return handleTranlucentBucket;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Enable or disable rendering of the
|
|
|
+ * {@link Bucket#Translucent translucent bucket}
|
|
|
+ * by the RenderManager. The default is enabled.
|
|
|
+ *
|
|
|
+ * @param handleTranslucentBucket Whether or not the translucent bucket should
|
|
|
+ * be rendered.
|
|
|
+ */
|
|
|
+ public void setHandleTranslucentBucket(boolean handleTranslucentBucket) {
|
|
|
+ this.handleTranlucentBucket = handleTranslucentBucket;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Internal use only. Sets the world matrix to use for future
|
|
|
+ * rendering. This has no effect unless objects are rendered manually
|
|
|
+ * using {@link Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager) }.
|
|
|
+ * Using {@link #renderGeometry(com.jme3.scene.Geometry) } will
|
|
|
+ * override this value.
|
|
|
+ *
|
|
|
+ * @param mat The world matrix to set
|
|
|
+ */
|
|
|
+ public void setWorldMatrix(Matrix4f mat) {
|
|
|
+ if (shader) {
|
|
|
+ worldMatrix.set(mat);
|
|
|
+ } else {
|
|
|
+ renderer.setWorldMatrix(mat);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Renders the given geometry.
|
|
|
+ * <p>
|
|
|
+ * First the proper world matrix is set, if
|
|
|
+ * the geometry's {@link Geometry#setIgnoreTransform(boolean) ignore transform}
|
|
|
+ * feature is enabled, the identity world matrix is used, otherwise, the
|
|
|
+ * geometry's {@link Geometry#getWorldMatrix() world transform matrix} is used.
|
|
|
+ * <p>
|
|
|
+ * Once the world matrix is applied, the proper material is chosen for rendering.
|
|
|
+ * If a {@link #setForcedMaterial(com.jme3.material.Material) forced material} is
|
|
|
+ * set on this RenderManager, then it is used for rendering the geometry,
|
|
|
+ * otherwise, the {@link Geometry#getMaterial() geometry's material} is used.
|
|
|
+ * <p>
|
|
|
+ * If a {@link #setForcedTechnique(java.lang.String) forced technique} is
|
|
|
+ * set on this RenderManager, then it is selected automatically
|
|
|
+ * on the geometry's material and is used for rendering. Otherwise, one
|
|
|
+ * of the {@link MaterialDef#getDefaultTechniques() default techniques} is
|
|
|
+ * used.
|
|
|
+ * <p>
|
|
|
+ * If a {@link #setForcedRenderState(com.jme3.material.RenderState) forced
|
|
|
+ * render state} is set on this RenderManager, then it is used
|
|
|
+ * for rendering the material, and the material's own render state is ignored.
|
|
|
+ * Otherwise, the material's render state is used as intended.
|
|
|
+ *
|
|
|
+ * @param g The geometry to render
|
|
|
+ *
|
|
|
+ * @see Technique
|
|
|
+ * @see RenderState
|
|
|
+ * @see Material#selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
|
|
|
+ * @see Material#render(com.jme3.scene.Geometry, com.jme3.renderer.RenderManager)
|
|
|
+ */
|
|
|
+ public void renderGeometry(Geometry g) {
|
|
|
+ if (g.isIgnoreTransform()) {
|
|
|
+ setWorldMatrix(Matrix4f.IDENTITY);
|
|
|
+ } else {
|
|
|
+ setWorldMatrix(g.getWorldMatrix());
|
|
|
+ }
|
|
|
+
|
|
|
+ //if forcedTechnique we try to force it for render,
|
|
|
+ //if it does not exists in the mat def, we check for forcedMaterial and render the geom if not null
|
|
|
+ //else the geom is not rendered
|
|
|
+ if (forcedTechnique != null) {
|
|
|
+ if (g.getMaterial().getMaterialDef().getTechniqueDef(forcedTechnique) != null) {
|
|
|
+ tmpTech = g.getMaterial().getActiveTechnique() != null ? g.getMaterial().getActiveTechnique().getDef().getName() : "Default";
|
|
|
+ g.getMaterial().selectTechnique(forcedTechnique, this);
|
|
|
+ // use geometry's material
|
|
|
+ g.getMaterial().render(g, this);
|
|
|
+ g.getMaterial().selectTechnique(tmpTech, this);
|
|
|
+ //Reverted this part from revision 6197
|
|
|
+ //If forcedTechnique does not exists, and frocedMaterial is not set, the geom MUST NOT be rendered
|
|
|
+ } else if (forcedMaterial != null) {
|
|
|
+ // use forced material
|
|
|
+ forcedMaterial.render(g, this);
|
|
|
+ }
|
|
|
+ } else if (forcedMaterial != null) {
|
|
|
+ // use forced material
|
|
|
+ forcedMaterial.render(g, this);
|
|
|
+ } else {
|
|
|
+ g.getMaterial().render(g, this);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Renders the given GeometryList.
|
|
|
+ * <p>
|
|
|
+ * For every geometry in the list, the
|
|
|
+ * {@link #renderGeometry(com.jme3.scene.Geometry) } method is called.
|
|
|
+ *
|
|
|
+ * @param gl The geometry list to render.
|
|
|
+ *
|
|
|
+ * @see GeometryList
|
|
|
+ * @see #renderGeometry(com.jme3.scene.Geometry)
|
|
|
+ */
|
|
|
+ public void renderGeometryList(GeometryList gl) {
|
|
|
+ for (int i = 0; i < gl.size(); i++) {
|
|
|
+ renderGeometry(gl.get(i));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * If a spatial is not inside the eye frustum, it
|
|
|
+ * is still rendered in the shadow frustum (shadow casting queue)
|
|
|
+ * through this recursive method.
|
|
|
+ */
|
|
|
+ private void renderShadow(Spatial s, RenderQueue rq) {
|
|
|
+ if (s instanceof Node) {
|
|
|
+ Node n = (Node) s;
|
|
|
+ List<Spatial> children = n.getChildren();
|
|
|
+ for (int i = 0; i < children.size(); i++) {
|
|
|
+ renderShadow(children.get(i), rq);
|
|
|
+ }
|
|
|
+ } else if (s instanceof Geometry) {
|
|
|
+ Geometry gm = (Geometry) s;
|
|
|
+
|
|
|
+ RenderQueue.ShadowMode shadowMode = s.getShadowMode();
|
|
|
+ if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive) {
|
|
|
+ //forcing adding to shadow cast mode, culled objects doesn't have to be in the receiver queue
|
|
|
+ rq.addToShadowQueue(gm, RenderQueue.ShadowMode.Cast);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Preloads a scene for rendering.
|
|
|
+ * <p>
|
|
|
+ * After invocation of this method, the underlying
|
|
|
+ * renderer would have uploaded any textures, shaders and meshes
|
|
|
+ * used by the given scene to the video driver.
|
|
|
+ * Using this method is useful when wishing to avoid the initial pause
|
|
|
+ * when rendering a scene for the first time. Note that it is not
|
|
|
+ * guaranteed that the underlying renderer will actually choose to upload
|
|
|
+ * the data to the GPU so some pause is still to be expected.
|
|
|
+ *
|
|
|
+ * @param scene The scene to preload
|
|
|
+ */
|
|
|
+ public void preloadScene(Spatial scene) {
|
|
|
+ if (scene instanceof Node) {
|
|
|
+ // recurse for all children
|
|
|
+ Node n = (Node) scene;
|
|
|
+ List<Spatial> children = n.getChildren();
|
|
|
+ for (int i = 0; i < children.size(); i++) {
|
|
|
+ preloadScene(children.get(i));
|
|
|
+ }
|
|
|
+ } else if (scene instanceof Geometry) {
|
|
|
+ // add to the render queue
|
|
|
+ Geometry gm = (Geometry) scene;
|
|
|
+ if (gm.getMaterial() == null) {
|
|
|
+ throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
|
|
|
+ }
|
|
|
+
|
|
|
+ gm.getMaterial().preload(this);
|
|
|
+ Mesh mesh = gm.getMesh();
|
|
|
+ if (mesh != null) {
|
|
|
+ for (Entry<VertexBuffer> entry : mesh.getBuffers()) {
|
|
|
+ VertexBuffer buf = entry.getValue();
|
|
|
+ if (buf.getData() != null) {
|
|
|
+ renderer.updateBufferData(buf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Flattens the given scene graph into the ViewPort's RenderQueue,
|
|
|
+ * checking for culling as the call goes down the graph recursively.
|
|
|
+ * <p>
|
|
|
+ * First, the scene is checked for culling based on the <code>Spatial</code>s
|
|
|
+ * {@link Spatial#setCullHint(com.jme3.scene.Spatial.CullHint) cull hint},
|
|
|
+ * if the camera frustum contains the scene, then this method is recursively
|
|
|
+ * called on its children.
|
|
|
+ * <p>
|
|
|
+ * When the scene's leaves or {@link Geometry geometries} are reached,
|
|
|
+ * they are each enqueued into the
|
|
|
+ * {@link ViewPort#getQueue() ViewPort's render queue}.
|
|
|
+ * <p>
|
|
|
+ * In addition to enqueuing the visible geometries, this method
|
|
|
+ * also scenes which cast or receive shadows, by putting them into the
|
|
|
+ * RenderQueue's
|
|
|
+ * {@link RenderQueue#addToShadowQueue(com.jme3.scene.Geometry, com.jme3.renderer.queue.RenderQueue.ShadowMode)
|
|
|
+ * shadow queue}. Each Spatial which has its
|
|
|
+ * {@link Spatial#setShadowMode(com.jme3.renderer.queue.RenderQueue.ShadowMode) shadow mode}
|
|
|
+ * set to not off, will be put into the appropriate shadow queue, note that
|
|
|
+ * this process does not check for frustum culling on any
|
|
|
+ * {@link ShadowMode#Cast shadow casters}, as they don't have to be
|
|
|
+ * in the eye camera frustum to cast shadows on objects that are inside it.
|
|
|
+ *
|
|
|
+ * @param scene The scene to flatten into the queue
|
|
|
+ * @param vp The ViewPort provides the {@link ViewPort#getCamera() camera}
|
|
|
+ * used for culling and the {@link ViewPort#getQueue() queue} used to
|
|
|
+ * contain the flattened scene graph.
|
|
|
+ */
|
|
|
+ public void renderScene(Spatial scene, ViewPort vp) {
|
|
|
+ if (scene.getParent() == null) {
|
|
|
+ vp.getCamera().setPlaneState(0);
|
|
|
+ }
|
|
|
+ // check culling first.
|
|
|
+ if (!scene.checkCulling(vp.getCamera())) {
|
|
|
+ // move on to shadow-only render
|
|
|
+ if ((scene.getShadowMode() != RenderQueue.ShadowMode.Off || scene instanceof Node) && scene.getCullHint()!=Spatial.CullHint.Always) {
|
|
|
+ renderShadow(scene, vp.getQueue());
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ scene.runControlRender(this, vp);
|
|
|
+ if (scene instanceof Node) {
|
|
|
+ // recurse for all children
|
|
|
+ Node n = (Node) scene;
|
|
|
+ List<Spatial> children = n.getChildren();
|
|
|
+ //saving cam state for culling
|
|
|
+ int camState = vp.getCamera().getPlaneState();
|
|
|
+ for (int i = 0; i < children.size(); i++) {
|
|
|
+ //restoring cam state before proceeding children recusively
|
|
|
+ vp.getCamera().setPlaneState(camState);
|
|
|
+ renderScene(children.get(i), vp);
|
|
|
+
|
|
|
+ }
|
|
|
+ } else if (scene instanceof Geometry) {
|
|
|
+
|
|
|
+ // add to the render queue
|
|
|
+ Geometry gm = (Geometry) scene;
|
|
|
+ if (gm.getMaterial() == null) {
|
|
|
+ throw new IllegalStateException("No material is set for Geometry: " + gm.getName());
|
|
|
+ }
|
|
|
+
|
|
|
+ vp.getQueue().addToQueue(gm, scene.getQueueBucket());
|
|
|
+
|
|
|
+ // add to shadow queue if needed
|
|
|
+ RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
|
|
|
+ if (shadowMode != RenderQueue.ShadowMode.Off) {
|
|
|
+ vp.getQueue().addToShadowQueue(gm, shadowMode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the camera currently used for rendering.
|
|
|
+ * <p>
|
|
|
+ * The camera can be set with {@link #setCamera(com.jme3.renderer.Camera, boolean) }.
|
|
|
+ *
|
|
|
+ * @return the camera currently used for rendering.
|
|
|
+ */
|
|
|
+ public Camera getCurrentCamera() {
|
|
|
+ return prevCam;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The renderer implementation used for rendering operations.
|
|
|
+ *
|
|
|
+ * @return The renderer implementation
|
|
|
+ *
|
|
|
+ * @see #RenderManager(com.jme3.renderer.Renderer)
|
|
|
+ * @see Renderer
|
|
|
+ */
|
|
|
+ public Renderer getRenderer() {
|
|
|
+ return renderer;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Flushes the ViewPort's {@link ViewPort#getQueue() render queue}
|
|
|
+ * by rendering each of its visible buckets.
|
|
|
+ * By default the queues will automatically be cleared after rendering,
|
|
|
+ * so there's no need to clear them manually.
|
|
|
+ *
|
|
|
+ * @param vp The ViewPort of which the queue will be flushed
|
|
|
+ *
|
|
|
+ * @see RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera)
|
|
|
+ * @see #renderGeometryList(com.jme3.renderer.queue.GeometryList)
|
|
|
+ */
|
|
|
+ public void flushQueue(ViewPort vp) {
|
|
|
+ renderViewPortQueues(vp, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Clears the queue of the given ViewPort.
|
|
|
+ * Simply calls {@link RenderQueue#clear() } on the ViewPort's
|
|
|
+ * {@link ViewPort#getQueue() render queue}.
|
|
|
+ *
|
|
|
+ * @param vp The ViewPort of which the queue will be cleared.
|
|
|
+ *
|
|
|
+ * @see RenderQueue#clear()
|
|
|
+ * @see ViewPort#getQueue()
|
|
|
+ */
|
|
|
+ public void clearQueue(ViewPort vp) {
|
|
|
+ vp.getQueue().clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Render the given viewport queues.
|
|
|
+ * <p>
|
|
|
+ * Changes the {@link Renderer#setDepthRange(float, float) depth range}
|
|
|
+ * appropriately as expected by each queue and then calls
|
|
|
+ * {@link RenderQueue#renderQueue(com.jme3.renderer.queue.RenderQueue.Bucket, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean) }
|
|
|
+ * on the queue. Makes sure to restore the depth range to [0, 1]
|
|
|
+ * at the end of the call.
|
|
|
+ * Note that the {@link Bucket#Translucent translucent bucket} is NOT
|
|
|
+ * rendered by this method. Instead the user should call
|
|
|
+ * {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) }
|
|
|
+ * after this call.
|
|
|
+ *
|
|
|
+ * @param vp the viewport of which queue should be rendered
|
|
|
+ * @param flush If true, the queues will be cleared after
|
|
|
+ * rendering.
|
|
|
+ *
|
|
|
+ * @see RenderQueue
|
|
|
+ * @see #renderTranslucentQueue(com.jme3.renderer.ViewPort)
|
|
|
+ */
|
|
|
+ public void renderViewPortQueues(ViewPort vp, boolean flush) {
|
|
|
+ RenderQueue rq = vp.getQueue();
|
|
|
+ Camera cam = vp.getCamera();
|
|
|
+ boolean depthRangeChanged = false;
|
|
|
+
|
|
|
+ // render opaque objects with default depth range
|
|
|
+ // opaque objects are sorted front-to-back, reducing overdraw
|
|
|
+ rq.renderQueue(Bucket.Opaque, this, cam, flush);
|
|
|
+
|
|
|
+ // render the sky, with depth range set to the farthest
|
|
|
+ if (!rq.isQueueEmpty(Bucket.Sky)) {
|
|
|
+ renderer.setDepthRange(1, 1);
|
|
|
+ rq.renderQueue(Bucket.Sky, this, cam, flush);
|
|
|
+ depthRangeChanged = true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // transparent objects are last because they require blending with the
|
|
|
+ // rest of the scene's objects. Consequently, they are sorted
|
|
|
+ // back-to-front.
|
|
|
+ if (!rq.isQueueEmpty(Bucket.Transparent)) {
|
|
|
+ if (depthRangeChanged) {
|
|
|
+ renderer.setDepthRange(0, 1);
|
|
|
+ depthRangeChanged = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ rq.renderQueue(Bucket.Transparent, this, cam, flush);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!rq.isQueueEmpty(Bucket.Gui)) {
|
|
|
+ renderer.setDepthRange(0, 0);
|
|
|
+ setCamera(cam, true);
|
|
|
+ rq.renderQueue(Bucket.Gui, this, cam, flush);
|
|
|
+ setCamera(cam, false);
|
|
|
+ depthRangeChanged = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // restore range to default
|
|
|
+ if (depthRangeChanged) {
|
|
|
+ renderer.setDepthRange(0, 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Renders the {@link Bucket#Translucent translucent queue} on the viewPort.
|
|
|
+ * <p>
|
|
|
+ * This call does nothing unless {@link #setHandleTranslucentBucket(boolean) }
|
|
|
+ * is set to true. This method clears the translucent queue after rendering
|
|
|
+ * it.
|
|
|
+ *
|
|
|
+ * @param vp The viewport of which the translucent queue should be rendered.
|
|
|
+ *
|
|
|
+ * @see #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean)
|
|
|
+ * @see #setHandleTranslucentBucket(boolean)
|
|
|
+ */
|
|
|
+ public void renderTranslucentQueue(ViewPort vp) {
|
|
|
+ RenderQueue rq = vp.getQueue();
|
|
|
+ if (!rq.isQueueEmpty(Bucket.Translucent) && handleTranlucentBucket) {
|
|
|
+ rq.renderQueue(Bucket.Translucent, this, vp.getCamera(), true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void setViewPort(Camera cam) {
|
|
|
+ // this will make sure to update viewport only if needed
|
|
|
+ if (cam != prevCam || cam.isViewportChanged()) {
|
|
|
+ viewX = (int) (cam.getViewPortLeft() * cam.getWidth());
|
|
|
+ viewY = (int) (cam.getViewPortBottom() * cam.getHeight());
|
|
|
+ viewWidth = (int) ((cam.getViewPortRight() - cam.getViewPortLeft()) * cam.getWidth());
|
|
|
+ viewHeight = (int) ((cam.getViewPortTop() - cam.getViewPortBottom()) * cam.getHeight());
|
|
|
+ renderer.setViewPort(viewX, viewY, viewWidth, viewHeight);
|
|
|
+ renderer.setClipRect(viewX, viewY, viewWidth, viewHeight);
|
|
|
+ cam.clearViewportChanged();
|
|
|
+ prevCam = cam;
|
|
|
+
|
|
|
+// float translateX = viewWidth == viewX ? 0 : -(viewWidth + viewX) / (viewWidth - viewX);
|
|
|
+// float translateY = viewHeight == viewY ? 0 : -(viewHeight + viewY) / (viewHeight - viewY);
|
|
|
+// float scaleX = viewWidth == viewX ? 1f : 2f / (viewWidth - viewX);
|
|
|
+// float scaleY = viewHeight == viewY ? 1f : 2f / (viewHeight - viewY);
|
|
|
+//
|
|
|
+// orthoMatrix.loadIdentity();
|
|
|
+// orthoMatrix.setTranslation(translateX, translateY, 0);
|
|
|
+// orthoMatrix.setScale(scaleX, scaleY, 0);
|
|
|
+
|
|
|
+ orthoMatrix.loadIdentity();
|
|
|
+ orthoMatrix.setTranslation(-1f, -1f, 0f);
|
|
|
+ orthoMatrix.setScale(2f / cam.getWidth(), 2f / cam.getHeight(), 0f);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void setViewProjection(Camera cam, boolean ortho) {
|
|
|
+ if (shader) {
|
|
|
+ if (ortho) {
|
|
|
+ viewMatrix.set(Matrix4f.IDENTITY);
|
|
|
+ projMatrix.set(orthoMatrix);
|
|
|
+ viewProjMatrix.set(orthoMatrix);
|
|
|
+ } else {
|
|
|
+ viewMatrix.set(cam.getViewMatrix());
|
|
|
+ projMatrix.set(cam.getProjectionMatrix());
|
|
|
+ viewProjMatrix.set(cam.getViewProjectionMatrix());
|
|
|
+ }
|
|
|
+
|
|
|
+ camLoc.set(cam.getLocation());
|
|
|
+ cam.getLeft(camLeft);
|
|
|
+ cam.getUp(camUp);
|
|
|
+ cam.getDirection(camDir);
|
|
|
+
|
|
|
+ near = cam.getFrustumNear();
|
|
|
+ far = cam.getFrustumFar();
|
|
|
+ } else {
|
|
|
+ if (ortho) {
|
|
|
+ renderer.setViewProjectionMatrices(Matrix4f.IDENTITY, orthoMatrix);
|
|
|
+ } else {
|
|
|
+ renderer.setViewProjectionMatrices(cam.getViewMatrix(),
|
|
|
+ cam.getProjectionMatrix());
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the camera to use for rendering.
|
|
|
+ * <p>
|
|
|
+ * First, the camera's
|
|
|
+ * {@link Camera#setViewPort(float, float, float, float) view port parameters}
|
|
|
+ * are applied. Then, the camera's {@link Camera#getViewMatrix() view} and
|
|
|
+ * {@link Camera#getProjectionMatrix() projection} matrices are set
|
|
|
+ * on the renderer. If <code>ortho</code> is <code>true</code>, then
|
|
|
+ * instead of using the camera's view and projection matrices, an ortho
|
|
|
+ * matrix is computed and used instead of the view projection matrix.
|
|
|
+ * The ortho matrix converts from the range (0 ~ Width, 0 ~ Height, -1 ~ +1)
|
|
|
+ * to the clip range (-1 ~ +1, -1 ~ +1, -1 ~ +1).
|
|
|
+ *
|
|
|
+ * @param cam The camera to set
|
|
|
+ * @param ortho True if to use orthographic projection (for GUI rendering),
|
|
|
+ * false if to use the camera's view and projection matrices.
|
|
|
+ */
|
|
|
+ public void setCamera(Camera cam, boolean ortho) {
|
|
|
+ setViewPort(cam);
|
|
|
+ setViewProjection(cam, ortho);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Draws the viewport but without notifying {@link SceneProcessor scene
|
|
|
+ * processors} of any rendering events.
|
|
|
+ *
|
|
|
+ * @param vp The ViewPort to render
|
|
|
+ *
|
|
|
+ * @see #renderViewPort(com.jme3.renderer.ViewPort, float)
|
|
|
+ */
|
|
|
+ public void renderViewPortRaw(ViewPort vp) {
|
|
|
+ setCamera(vp.getCamera(), false);
|
|
|
+ List<Spatial> scenes = vp.getScenes();
|
|
|
+ for (int i = scenes.size() - 1; i >= 0; i--) {
|
|
|
+ renderScene(scenes.get(i), vp);
|
|
|
+ }
|
|
|
+ flushQueue(vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Renders the {@link ViewPort}.
|
|
|
+ * <p>
|
|
|
+ * If the ViewPort is {@link ViewPort#isEnabled() disabled}, this method
|
|
|
+ * returns immediately. Otherwise, the ViewPort is rendered by
|
|
|
+ * the following process:<br>
|
|
|
+ * <ul>
|
|
|
+ * <li>All {@link SceneProcessor scene processors} that are attached
|
|
|
+ * to the ViewPort are {@link SceneProcessor#initialize(com.jme3.renderer.RenderManager, com.jme3.renderer.ViewPort) initialized}.
|
|
|
+ * </li>
|
|
|
+ * <li>The SceneProcessors' {@link SceneProcessor#preFrame(float) } method
|
|
|
+ * is called.</li>
|
|
|
+ * <li>The ViewPort's {@link ViewPort#getOutputFrameBuffer() output framebuffer}
|
|
|
+ * is set on the Renderer</li>
|
|
|
+ * <li>The camera is set on the renderer, including its view port parameters.
|
|
|
+ * (see {@link #setCamera(com.jme3.renderer.Camera, boolean) })</li>
|
|
|
+ * <li>Any buffers that the ViewPort requests to be cleared are cleared
|
|
|
+ * and the {@link ViewPort#getBackgroundColor() background color} is set</li>
|
|
|
+ * <li>Every scene that is attached to the ViewPort is flattened into
|
|
|
+ * the ViewPort's render queue
|
|
|
+ * (see {@link #renderViewPortQueues(com.jme3.renderer.ViewPort, boolean) })
|
|
|
+ * </li>
|
|
|
+ * <li>The SceneProcessors' {@link SceneProcessor#postQueue(com.jme3.renderer.queue.RenderQueue) }
|
|
|
+ * method is called.</li>
|
|
|
+ * <li>The render queue is sorted and then flushed, sending
|
|
|
+ * rendering commands to the underlying Renderer implementation.
|
|
|
+ * (see {@link #flushQueue(com.jme3.renderer.ViewPort) })</li>
|
|
|
+ * <li>The SceneProcessors' {@link SceneProcessor#postFrame(com.jme3.texture.FrameBuffer) }
|
|
|
+ * method is called.</li>
|
|
|
+ * <li>The translucent queue of the ViewPort is sorted and then flushed
|
|
|
+ * (see {@link #renderTranslucentQueue(com.jme3.renderer.ViewPort) })</li>
|
|
|
+ * <li>If any objects remained in the render queue, they are removed
|
|
|
+ * from the queue. This is generally objects added to the
|
|
|
+ * {@link RenderQueue#renderShadowQueue(com.jme3.renderer.queue.RenderQueue.ShadowMode, com.jme3.renderer.RenderManager, com.jme3.renderer.Camera, boolean)
|
|
|
+ * shadow queue}
|
|
|
+ * which were not rendered because of a missing shadow renderer.</li>
|
|
|
+ * </ul>
|
|
|
+ *
|
|
|
+ * @param vp
|
|
|
+ * @param tpf
|
|
|
+ */
|
|
|
+ public void renderViewPort(ViewPort vp, float tpf) {
|
|
|
+ if (!vp.isEnabled()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ List<SceneProcessor> processors = vp.getProcessors();
|
|
|
+ if (processors.isEmpty()) {
|
|
|
+ processors = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (processors != null) {
|
|
|
+ for (SceneProcessor proc : processors) {
|
|
|
+ if (!proc.isInitialized()) {
|
|
|
+ proc.initialize(this, vp);
|
|
|
+ }
|
|
|
+ proc.preFrame(tpf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ renderer.setFrameBuffer(vp.getOutputFrameBuffer());
|
|
|
+ setCamera(vp.getCamera(), false);
|
|
|
+ if (vp.isClearDepth() || vp.isClearColor() || vp.isClearStencil()) {
|
|
|
+ if (vp.isClearColor()) {
|
|
|
+ renderer.setBackgroundColor(vp.getBackgroundColor());
|
|
|
+ }
|
|
|
+ renderer.clearBuffers(vp.isClearColor(),
|
|
|
+ vp.isClearDepth(),
|
|
|
+ vp.isClearStencil());
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Spatial> scenes = vp.getScenes();
|
|
|
+ for (int i = scenes.size() - 1; i >= 0; i--) {
|
|
|
+ renderScene(scenes.get(i), vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (processors != null) {
|
|
|
+ for (SceneProcessor proc : processors) {
|
|
|
+ proc.postQueue(vp.getQueue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ flushQueue(vp);
|
|
|
+
|
|
|
+ if (processors != null) {
|
|
|
+ for (SceneProcessor proc : processors) {
|
|
|
+ proc.postFrame(vp.getOutputFrameBuffer());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //renders the translucent objects queue after processors have been rendered
|
|
|
+ renderTranslucentQueue(vp);
|
|
|
+ // clear any remaining spatials that were not rendered.
|
|
|
+ clearQueue(vp);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Called by the application to render any ViewPorts
|
|
|
+ * added to this RenderManager.
|
|
|
+ * <p>
|
|
|
+ * Renders any viewports that were added using the following methods:
|
|
|
+ * <ul>
|
|
|
+ * <li>{@link #createPreView(java.lang.String, com.jme3.renderer.Camera) }</li>
|
|
|
+ * <li>{@link #createMainView(java.lang.String, com.jme3.renderer.Camera) }</li>
|
|
|
+ * <li>{@link #createPostView(java.lang.String, com.jme3.renderer.Camera) }</li>
|
|
|
+ * </ul>
|
|
|
+ *
|
|
|
+ * @param tpf Time per frame value
|
|
|
+ */
|
|
|
+ public void render(float tpf, boolean mainFrameBufferActive) {
|
|
|
+ if (renderer instanceof NullRenderer) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.shader = renderer.getCaps().contains(Caps.GLSL100);
|
|
|
+
|
|
|
+ for (int i = 0; i < preViewPorts.size(); i++) {
|
|
|
+ ViewPort vp = preViewPorts.get(i);
|
|
|
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
|
|
|
+ renderViewPort(vp, tpf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (int i = 0; i < viewPorts.size(); i++) {
|
|
|
+ ViewPort vp = viewPorts.get(i);
|
|
|
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
|
|
|
+ renderViewPort(vp, tpf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (int i = 0; i < postViewPorts.size(); i++) {
|
|
|
+ ViewPort vp = postViewPorts.get(i);
|
|
|
+ if (vp.getOutputFrameBuffer() != null || mainFrameBufferActive){
|
|
|
+ renderViewPort(vp, tpf);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|