Forráskód Böngészése

add support of DepthStencil into *Renderer
+ a test app
- only tested on desktop with Lwjgl

David Bernard 11 éve
szülő
commit
37da17e3eb

+ 20 - 4
jme3-android/src/main/java/com/jme3/renderer/android/OGLESShaderRenderer.java

@@ -36,8 +36,18 @@ import android.os.Build;
 import com.jme3.asset.AndroidImageInfo;
 import com.jme3.light.LightList;
 import com.jme3.material.RenderState;
-import com.jme3.math.*;
-import com.jme3.renderer.*;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.IDList;
+import com.jme3.renderer.RenderContext;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.Statistics;
 import com.jme3.renderer.android.TextureUtil.AndroidGLImageFormat;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.Mesh.Mode;
@@ -58,7 +68,11 @@ import com.jme3.texture.Texture.WrapAxis;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.ListMap;
 import com.jme3.util.NativeObjectManager;
-import java.nio.*;
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
 import java.util.EnumSet;
 import java.util.List;
 import java.util.logging.Level;
@@ -1313,8 +1327,10 @@ public class OGLESShaderRenderer implements Renderer {
 
     private int convertAttachmentSlot(int attachmentSlot) {
         // can also add support for stencil here
-        if (attachmentSlot == -100) {
+        if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
             return GLES20.GL_DEPTH_ATTACHMENT;
+//        if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) {
+//            return GLES30.GL_DEPTH_STENCIL_ATTACHMENT;
         } else if (attachmentSlot == 0) {
             return GLES20.GL_COLOR_ATTACHMENT0;
         } else {

+ 6 - 3
jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java

@@ -72,6 +72,9 @@ import java.util.ArrayList;
  * @author Kirill Vainer
  */
 public class FrameBuffer extends NativeObject {
+    public static int SLOT_UNDEF = -1;
+    public static int SLOT_DEPTH = -100;
+    public static int SLOT_DEPTH_STENCIL = -101;
 
     private int width = 0;
     private int height = 0;
@@ -91,7 +94,7 @@ public class FrameBuffer extends NativeObject {
         Texture tex;
         Image.Format format;
         int id = -1;
-        int slot = -1;
+        int slot = SLOT_UNDEF;
         int face = -1;
         
         /**
@@ -212,7 +215,7 @@ public class FrameBuffer extends NativeObject {
             throw new IllegalArgumentException("Depth buffer format must be depth.");
             
         depthBuf = new RenderBuffer();
-        depthBuf.slot = -100; // -100 == special slot for DEPTH_BUFFER
+        depthBuf.slot = format.isDepthStencilFormat() ?  SLOT_DEPTH_STENCIL : SLOT_DEPTH;
         depthBuf.format = format;
     }
 
@@ -404,7 +407,7 @@ public class FrameBuffer extends NativeObject {
         checkSetTexture(tex, true);
         
         depthBuf = new RenderBuffer();
-        depthBuf.slot = -100; // indicates GL_DEPTH_ATTACHMENT
+        depthBuf.slot = img.getFormat().isDepthStencilFormat() ?  SLOT_DEPTH_STENCIL : SLOT_DEPTH;
         depthBuf.tex = tex;
         depthBuf.format = img.getFormat();
     }

+ 14 - 1
jme3-core/src/main/java/com/jme3/texture/Image.java

@@ -31,7 +31,11 @@
  */
 package com.jme3.texture;
 
-import com.jme3.export.*;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.export.Savable;
 import com.jme3.math.FastMath;
 import com.jme3.renderer.Caps;
 import com.jme3.renderer.Renderer;
@@ -349,6 +353,13 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
             return isDepth;
         }
 
+        /**
+         * @return True if this format is a depth + stencil (packed) format, false otherwise.
+         */
+        boolean isDepthStencilFormat() {
+            return this == Depth24Stencil8;
+        }
+
         /**
          * @return True if this is a compressed image format, false if
          * uncompressed.
@@ -365,6 +376,8 @@ public class Image extends NativeObject implements Savable /*, Cloneable*/ {
             return isFloatingPoint;
         }
 
+
+
     }
 
     // image attributes

+ 297 - 0
jme3-examples/src/main/java/jme3test/renderer/TestDepthStencil.java

@@ -0,0 +1,297 @@
+/*
+ * 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 jme3test.renderer;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState.StencilOperation;
+import com.jme3.material.RenderState.TestFunction;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.control.AbstractControl;
+import com.jme3.scene.shape.Box;
+import com.jme3.system.AppSettings;
+import com.jme3.system.JmeContext.Type;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture2D;
+import com.jme3.util.BufferUtils;
+import com.jme3.util.Screenshots;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+/**
+ * This test renders a scene to an offscreen framebuffer, then copies
+ * the contents to a Swing JFrame. Note that some parts are done inefficently,
+ * this is done to make the code more readable.
+ */
+public class TestDepthStencil extends SimpleApplication implements SceneProcessor {
+    private static String TOGGLE_STENCIL = "TOGGLE_STENCIL";
+    
+    private Geometry offBox;
+    private float angle = 0;
+
+    private FrameBuffer offBuffer;
+    private ViewPort offView;
+    private Texture2D offTex;
+    private Camera offCamera;
+    private ImageDisplay display;
+    private boolean enableStencil = false;
+
+    private static final int width = 800, height = 600;
+
+    private final ByteBuffer cpuBuf = BufferUtils.createByteBuffer(width * height * 4);
+    private final byte[] cpuArray = new byte[width * height * 4];
+    private final BufferedImage image = new BufferedImage(width, height,
+                                            BufferedImage.TYPE_4BYTE_ABGR);
+
+    private class ImageDisplay extends JPanel {
+
+        private long t;
+        private long total;
+        private int frames;
+        private int fps;
+
+        @Override
+        public void paintComponent(Graphics gfx) {
+            super.paintComponent(gfx);
+            Graphics2D g2d = (Graphics2D) gfx;
+
+            if (t == 0)
+                t = timer.getTime();
+
+//            g2d.setBackground(Color.BLACK);
+//            g2d.clearRect(0,0,width,height);
+
+            synchronized (image){
+                g2d.drawImage(image, null, 0, 0);
+            }
+
+            long t2 = timer.getTime();
+            long dt = t2 - t;
+            total += dt;
+            frames ++;
+            t = t2;
+
+            if (total > 1000){
+                fps = frames;
+                total = 0;
+                frames = 0;
+            }
+
+            g2d.setColor(Color.white);
+            g2d.drawString("FPS: "+fps, 0, getHeight() - 100);
+            g2d.drawString("Toggle Stencil : [SPACE] (" + enableStencil + ")", 0, getHeight() - 10);
+        }
+    }
+
+    public static void main(String[] args){
+        TestDepthStencil app = new TestDepthStencil();
+        app.setPauseOnLostFocus(false);
+        AppSettings settings = new AppSettings(true);
+        settings.setResolution(1, 1);
+        app.setSettings(settings);
+        app.start(Type.OffscreenSurface);
+    }
+
+    public void createDisplayFrame(){
+        SwingUtilities.invokeLater(new Runnable(){
+            public void run(){
+                final JFrame frame = new JFrame("Render Display");
+                display = new ImageDisplay();
+                display.setPreferredSize(new Dimension(width, height));
+                frame.getContentPane().add(display);
+                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+                frame.addWindowListener(new WindowAdapter(){
+                    public void windowClosed(WindowEvent e){
+                        stop();
+                    }
+                });
+                frame.addKeyListener(new KeyListener() {
+                    public void keyTyped(KeyEvent ke) {
+                    }
+
+                    public void keyPressed(KeyEvent ke) {
+                    }
+
+                    public void keyReleased(KeyEvent ke) {
+                        if (ke.getKeyCode() == KeyEvent.VK_SPACE) {
+                            enableStencil = !enableStencil;
+                        }else if (ke.getKeyCode() == KeyEvent.VK_ESCAPE) {
+                            frame.setVisible(false);
+                        }
+                    }
+                });
+                frame.pack();
+                frame.setLocationRelativeTo(null);
+                frame.setResizable(false);
+                frame.setVisible(true);
+            }
+        });
+    }
+
+    public void updateImageContents(){
+        cpuBuf.clear();
+        renderer.readFrameBuffer(offBuffer, cpuBuf);
+
+        synchronized (image) {
+            Screenshots.convertScreenShot(cpuBuf, image);    
+        }
+
+        if (display != null)
+            display.repaint();
+    }
+
+    public void setupOffscreenView(){
+        offCamera = new Camera(width, height);
+
+        // create a pre-view. a view that is rendered before the main view
+        offView = renderManager.createPreView("Offscreen View", offCamera);
+        offView.setBackgroundColor(ColorRGBA.DarkGray);
+        offView.setClearFlags(true, true, true);
+        
+        // this will let us know when the scene has been rendered to the 
+        // frame buffer
+        offView.addProcessor(this);
+
+        // create offscreen framebuffer
+        offBuffer = new FrameBuffer(width, height, 1);
+
+        //setup framebuffer's cam
+        offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f);
+        offCamera.setLocation(new Vector3f(0f, 0f, -5f));
+        offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
+
+        //setup framebuffer's texture
+//        offTex = new Texture2D(width, height, Format.RGBA8);
+
+        //setup framebuffer to use renderbuffer
+        // this is faster for gpu -> cpu copies
+        offBuffer.setDepthBuffer(Format.Depth24Stencil8);
+        offBuffer.setColorBuffer(Format.RGBA8);
+//        offBuffer.setColorTexture(offTex);
+        
+        //set viewport to render to offscreen framebuffer
+        offView.setOutputFrameBuffer(offBuffer);
+
+        // setup framebuffer's scene
+        Box boxMesh = new Box(Vector3f.ZERO, 1,1,1);
+        final Material material = assetManager.loadMaterial("Interface/Logo/Logo.j3m");
+        offBox = new Geometry("box", boxMesh);
+        offBox.setMaterial(material);
+        offBox.addControl(new AbstractControl() {
+            @Override
+            protected void controlUpdate(float tpf) {
+		material.getAdditionalRenderState().setStencil(enableStencil,
+                    StencilOperation.Keep, StencilOperation.Keep, StencilOperation.Keep,
+                    StencilOperation.Keep, StencilOperation.Keep, StencilOperation.Keep,
+                    TestFunction.Never, TestFunction.Never
+                    //TestFunction.Always, TestFunction.Always
+		);
+            }
+
+            @Override
+            protected void controlRender(RenderManager rm, ViewPort vp) {
+            }
+        });
+
+        // attach the scene to the viewport to be rendered
+        offView.attachScene(offBox);
+    }
+
+    @Override
+    public void simpleInitApp() {
+        setupOffscreenView();
+        createDisplayFrame();
+    }
+
+    @Override
+    public void simpleUpdate(float tpf){
+        Quaternion q = new Quaternion();
+        angle += tpf;
+        angle %= FastMath.TWO_PI;
+        q.fromAngles(angle, 0, angle);
+
+        offBox.setLocalRotation(q);
+        offBox.updateLogicalState(tpf);
+        offBox.updateGeometricState();
+    }
+
+    public void initialize(RenderManager rm, ViewPort vp) {
+    }
+
+    public void reshape(ViewPort vp, int w, int h) {
+    }
+
+    public boolean isInitialized() {
+        return true;
+    }
+
+    public void preFrame(float tpf) {
+    }
+
+    public void postQueue(RenderQueue rq) {
+    }
+
+    /**
+     * Update the CPU image's contents after the scene has
+     * been rendered to the framebuffer.
+     */
+    public void postFrame(FrameBuffer out) {
+        updateImageContents();
+    }
+
+    public void cleanup() {
+    }
+
+
+}

+ 1 - 1
jme3-ios/src/main/java/com/jme3/renderer/ios/IGLESShaderRenderer.java

@@ -2235,7 +2235,7 @@ public class IGLESShaderRenderer implements Renderer {
 
     private int convertAttachmentSlot(int attachmentSlot) {
         // can also add support for stencil here
-        if (attachmentSlot == -100) {
+        if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
             return JmeIosGLES.GL_DEPTH_ATTACHMENT;
         } else if (attachmentSlot == 0) {
             return JmeIosGLES.GL_COLOR_ATTACHMENT0;

+ 17 - 18
jme3-jogl/src/main/java/com/jme3/renderer/jogl/JoglRenderer.java

@@ -33,20 +33,26 @@ package com.jme3.renderer.jogl;
 
 import com.jme3.light.LightList;
 import com.jme3.material.RenderState;
-import com.jme3.material.RenderState.StencilOperation;
-import com.jme3.material.RenderState.TestFunction;
-import com.jme3.math.*;
-import com.jme3.renderer.*;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.math.Vector4f;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.IDList;
+import com.jme3.renderer.RenderContext;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.RendererException;
+import com.jme3.renderer.Statistics;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.Mesh.Mode;
 import com.jme3.scene.VertexBuffer;
-import com.jme3.scene.VertexBuffer.Format;
 import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.VertexBuffer.Usage;
 import com.jme3.shader.Attribute;
 import com.jme3.shader.Shader;
 import com.jme3.shader.Shader.ShaderSource;
-import com.jme3.shader.Shader.ShaderType;
 import com.jme3.shader.Uniform;
 import com.jme3.texture.FrameBuffer;
 import com.jme3.texture.FrameBuffer.RenderBuffer;
@@ -56,16 +62,6 @@ import com.jme3.texture.Texture.WrapAxis;
 import com.jme3.util.BufferUtils;
 import com.jme3.util.ListMap;
 import com.jme3.util.NativeObjectManager;
-
-import java.nio.*;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import jme3tools.converters.MipMapGenerator;
-import jme3tools.shader.ShaderDebug;
-
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.nio.FloatBuffer;
@@ -74,7 +70,6 @@ import java.util.EnumSet;
 import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-
 import javax.media.nativewindow.NativeWindowFactory;
 import javax.media.opengl.GL;
 import javax.media.opengl.GL2;
@@ -84,6 +79,8 @@ import javax.media.opengl.GL2ES3;
 import javax.media.opengl.GL2GL3;
 import javax.media.opengl.GL3;
 import javax.media.opengl.GLContext;
+import jme3tools.converters.MipMapGenerator;
+import jme3tools.shader.ShaderDebug;
 
 public class JoglRenderer implements Renderer {
 
@@ -1508,8 +1505,10 @@ public class JoglRenderer implements Renderer {
     
     private int convertAttachmentSlot(int attachmentSlot) {
         // can also add support for stencil here
-        if (attachmentSlot == -100) {
+        if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
             return GL.GL_DEPTH_ATTACHMENT;
+        } else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) {
+            return GL2ES3.GL_DEPTH_STENCIL_ATTACHMENT;
         } else if (attachmentSlot < 0 || attachmentSlot >= 16) {
             throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
         }

+ 4 - 1
jme3-lwjgl/src/main/java/com/jme3/renderer/lwjgl/LwjglRenderer.java

@@ -65,6 +65,7 @@ import jme3tools.converters.MipMapGenerator;
 import jme3tools.shader.ShaderDebug;
 
 import static org.lwjgl.opengl.ARBDrawInstanced.*;
+import static org.lwjgl.opengl.ARBFramebufferObject.*;
 import static org.lwjgl.opengl.ARBInstancedArrays.*;
 import static org.lwjgl.opengl.ARBMultisample.*;
 import static org.lwjgl.opengl.ARBTextureMultisample.*;
@@ -1418,8 +1419,10 @@ public class LwjglRenderer implements Renderer {
 
     private int convertAttachmentSlot(int attachmentSlot) {
         // can also add support for stencil here
-        if (attachmentSlot == -100) {
+        if (attachmentSlot == FrameBuffer.SLOT_DEPTH) {
             return GL_DEPTH_ATTACHMENT_EXT;
+        } else if (attachmentSlot == FrameBuffer.SLOT_DEPTH_STENCIL) {
+            return GL_DEPTH_STENCIL_ATTACHMENT;
         } else if (attachmentSlot < 0 || attachmentSlot >= 16) {
             throw new UnsupportedOperationException("Invalid FBO attachment slot: " + attachmentSlot);
         }