1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package jme3test.post; 34 35 import com.jme3.app.SimpleApplication; 36 import com.jme3.material.Material; 37 import com.jme3.math.ColorRGBA; 38 import com.jme3.math.FastMath; 39 import com.jme3.math.Quaternion; 40 import com.jme3.math.Vector3f; 41 import com.jme3.post.SceneProcessor; 42 import com.jme3.renderer.Camera; 43 import com.jme3.renderer.RenderManager; 44 import com.jme3.renderer.ViewPort; 45 import com.jme3.renderer.queue.RenderQueue; 46 import com.jme3.scene.Geometry; 47 import com.jme3.scene.shape.Box; 48 import com.jme3.system.AppSettings; 49 import com.jme3.system.JmeContext.Type; 50 import com.jme3.texture.FrameBuffer; 51 import com.jme3.texture.Image.Format; 52 import com.jme3.texture.Texture2D; 53 import com.jme3.util.BufferUtils; 54 import com.jme3.util.Screenshots; 55 import java.awt.Color; 56 import java.awt.Dimension; 57 import java.awt.Graphics; 58 import java.awt.Graphics2D; 59 import java.awt.event.WindowAdapter; 60 import java.awt.event.WindowEvent; 61 import java.awt.image.BufferedImage; 62 import java.nio.ByteBuffer; 63 import javax.swing.JFrame; 64 import javax.swing.JPanel; 65 import javax.swing.SwingUtilities; 66 67 /** 68 * This test renders a scene to an offscreen framebuffer, then copies 69 * the contents to a Swing JFrame. Note that some parts are done inefficently, 70 * this is done to make the code more readable. 71 */ 72 public class TestRenderToMemory extends SimpleApplication implements SceneProcessor { 73 74 private Geometry offBox; 75 private float angle = 0; 76 77 private FrameBuffer offBuffer; 78 private ViewPort offView; 79 private Texture2D offTex; 80 private Camera offCamera; 81 private ImageDisplay display; 82 83 private static final int width = 800, height = 600; 84 85 private final ByteBuffer cpuBuf = BufferUtils.createByteBuffer(width * height * 4); 86 private final byte[] cpuArray = new byte[width * height * 4]; 87 private final BufferedImage image = new BufferedImage(width, height, 88 BufferedImage.TYPE_4BYTE_ABGR); 89 90 private class ImageDisplay extends JPanel { 91 92 private long t; 93 private long total; 94 private int frames; 95 private int fps; 96 97 @Override paintComponent(Graphics gfx)98 public void paintComponent(Graphics gfx) { 99 super.paintComponent(gfx); 100 Graphics2D g2d = (Graphics2D) gfx; 101 102 if (t == 0) 103 t = timer.getTime(); 104 105 // g2d.setBackground(Color.BLACK); 106 // g2d.clearRect(0,0,width,height); 107 108 synchronized (image){ 109 g2d.drawImage(image, null, 0, 0); 110 } 111 112 long t2 = timer.getTime(); 113 long dt = t2 - t; 114 total += dt; 115 frames ++; 116 t = t2; 117 118 if (total > 1000){ 119 fps = frames; 120 total = 0; 121 frames = 0; 122 } 123 124 g2d.setColor(Color.white); 125 g2d.drawString("FPS: "+fps, 0, getHeight() - 100); 126 } 127 } 128 main(String[] args)129 public static void main(String[] args){ 130 TestRenderToMemory app = new TestRenderToMemory(); 131 app.setPauseOnLostFocus(false); 132 AppSettings settings = new AppSettings(true); 133 settings.setResolution(1, 1); 134 app.setSettings(settings); 135 app.start(Type.OffscreenSurface); 136 } 137 createDisplayFrame()138 public void createDisplayFrame(){ 139 SwingUtilities.invokeLater(new Runnable(){ 140 public void run(){ 141 JFrame frame = new JFrame("Render Display"); 142 display = new ImageDisplay(); 143 display.setPreferredSize(new Dimension(width, height)); 144 frame.getContentPane().add(display); 145 frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 146 frame.addWindowListener(new WindowAdapter(){ 147 public void windowClosed(WindowEvent e){ 148 stop(); 149 } 150 }); 151 frame.pack(); 152 frame.setLocationRelativeTo(null); 153 frame.setResizable(false); 154 frame.setVisible(true); 155 } 156 }); 157 } 158 updateImageContents()159 public void updateImageContents(){ 160 cpuBuf.clear(); 161 renderer.readFrameBuffer(offBuffer, cpuBuf); 162 163 synchronized (image) { 164 Screenshots.convertScreenShot(cpuBuf, image); 165 } 166 167 if (display != null) 168 display.repaint(); 169 } 170 setupOffscreenView()171 public void setupOffscreenView(){ 172 offCamera = new Camera(width, height); 173 174 // create a pre-view. a view that is rendered before the main view 175 offView = renderManager.createPreView("Offscreen View", offCamera); 176 offView.setBackgroundColor(ColorRGBA.DarkGray); 177 offView.setClearFlags(true, true, true); 178 179 // this will let us know when the scene has been rendered to the 180 // frame buffer 181 offView.addProcessor(this); 182 183 // create offscreen framebuffer 184 offBuffer = new FrameBuffer(width, height, 1); 185 186 //setup framebuffer's cam 187 offCamera.setFrustumPerspective(45f, 1f, 1f, 1000f); 188 offCamera.setLocation(new Vector3f(0f, 0f, -5f)); 189 offCamera.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y); 190 191 //setup framebuffer's texture 192 // offTex = new Texture2D(width, height, Format.RGBA8); 193 194 //setup framebuffer to use renderbuffer 195 // this is faster for gpu -> cpu copies 196 offBuffer.setDepthBuffer(Format.Depth); 197 offBuffer.setColorBuffer(Format.RGBA8); 198 // offBuffer.setColorTexture(offTex); 199 200 //set viewport to render to offscreen framebuffer 201 offView.setOutputFrameBuffer(offBuffer); 202 203 // setup framebuffer's scene 204 Box boxMesh = new Box(Vector3f.ZERO, 1,1,1); 205 Material material = assetManager.loadMaterial("Interface/Logo/Logo.j3m"); 206 offBox = new Geometry("box", boxMesh); 207 offBox.setMaterial(material); 208 209 // attach the scene to the viewport to be rendered 210 offView.attachScene(offBox); 211 } 212 213 @Override simpleInitApp()214 public void simpleInitApp() { 215 setupOffscreenView(); 216 createDisplayFrame(); 217 } 218 219 @Override simpleUpdate(float tpf)220 public void simpleUpdate(float tpf){ 221 Quaternion q = new Quaternion(); 222 angle += tpf; 223 angle %= FastMath.TWO_PI; 224 q.fromAngles(angle, 0, angle); 225 226 offBox.setLocalRotation(q); 227 offBox.updateLogicalState(tpf); 228 offBox.updateGeometricState(); 229 } 230 initialize(RenderManager rm, ViewPort vp)231 public void initialize(RenderManager rm, ViewPort vp) { 232 } 233 reshape(ViewPort vp, int w, int h)234 public void reshape(ViewPort vp, int w, int h) { 235 } 236 isInitialized()237 public boolean isInitialized() { 238 return true; 239 } 240 preFrame(float tpf)241 public void preFrame(float tpf) { 242 } 243 postQueue(RenderQueue rq)244 public void postQueue(RenderQueue rq) { 245 } 246 247 /** 248 * Update the CPU image's contents after the scene has 249 * been rendered to the framebuffer. 250 */ postFrame(FrameBuffer out)251 public void postFrame(FrameBuffer out) { 252 updateImageContents(); 253 } 254 cleanup()255 public void cleanup() { 256 } 257 258 259 } 260