diff --git a/engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java b/engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java index d3705b245..546a416ef 100644 --- a/engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java +++ b/engine/src/desktop/com/jme3/input/awt/AwtKeyInput.java @@ -53,21 +53,27 @@ public class AwtKeyInput implements KeyInput, KeyListener { private static final Logger logger = Logger.getLogger(AwtKeyInput.class.getName()); private RawInputListener listener; - private boolean inited = false; private List eventQueue = new LinkedList(); private Component component; - public AwtKeyInput(Component comp){ - this.component = comp; + public AwtKeyInput(){ } public void initialize() { - inited = true; - component.addKeyListener(this); - - logger.info("Key input initialized."); + } + + public void destroy() { } + public void setInputSource(Component comp){ + if (component != null){ + component.removeKeyListener(this); + eventQueue.clear(); + } + component = comp; + component.addKeyListener(this); + } + public long getInputTimeNanos() { return System.nanoTime(); } @@ -84,15 +90,8 @@ public class AwtKeyInput implements KeyInput, KeyListener { eventQueue.clear(); } - public void destroy() { - inited = false; - - component.removeKeyListener(this); - logger.info("Key input destroyed."); - } - public boolean isInitialized() { - return inited; + return true; } public void setInputListener(RawInputListener listener) { diff --git a/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java b/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java index bbd53454a..3b2900ac5 100644 --- a/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java +++ b/engine/src/desktop/com/jme3/input/awt/AwtMouseInput.java @@ -68,8 +68,6 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe private static final Logger logger = Logger.getLogger(AwtMouseInput.class.getName()); - private boolean inited = false; - private boolean visible = true; private RawInputListener listener; @@ -92,8 +90,7 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe private boolean isRecentering; private int eventsSinceRecenter; - public AwtMouseInput(Component comp) { - this.component = comp; + public AwtMouseInput() { location = new Point(); centerLocation = new Point(); centerLocationOnScreen = new Point(); @@ -105,28 +102,39 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe logger.log(Level.SEVERE, "Could not create a robot, so the mouse cannot be grabbed! ", e); } } + + public void setInputSource(Component comp){ + if (component != null){ + component.removeMouseListener(this); + component.removeMouseMotionListener(this); + component.removeMouseWheelListener(this); + + wheelPos = 0; + isRecentering = false; + eventsSinceRecenter = 0; + lastEventX = 0; + lastEventY = 0; + lastEventWheel = 0; + location = new Point(); + centerLocation = new Point(); + centerLocationOnScreen = new Point(); + lastKnownLocation = new Point(); + } - public void initialize() { - inited = true; + component = comp; component.addMouseListener(this); component.addMouseMotionListener(this); component.addMouseWheelListener(this); + } - logger.info("Mouse input initialized."); + public void initialize() { } public void destroy() { - inited = false; - robot = null; - - component.removeMouseListener(this); - component.removeMouseMotionListener(this); - component.removeMouseWheelListener(this); - logger.info("Mouse input destroyed."); } public boolean isInitialized() { - return inited; + return true; } public void setInputListener(RawInputListener listener){ @@ -139,10 +147,16 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe public void setCursorVisible(boolean visible){ if (this.visible != visible){ - component.setCursor(visible ? null : getTransparentCursor()); this.visible = visible; - if (!visible) - recenterMouse(component); + final boolean newVisible = visible; + SwingUtilities.invokeLater(new Runnable() { + public void run() { + component.setCursor(newVisible ? null : getTransparentCursor()); + if (!newVisible) + recenterMouse(component); + } + }); + } } diff --git a/engine/src/desktop/com/jme3/system/awt/AwtPanel.java b/engine/src/desktop/com/jme3/system/awt/AwtPanel.java new file mode 100644 index 000000000..1cfd6d840 --- /dev/null +++ b/engine/src/desktop/com/jme3/system/awt/AwtPanel.java @@ -0,0 +1,218 @@ +package com.jme3.system.awt; + +import com.jme3.post.SceneProcessor; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.Image.Format; +import com.jme3.util.BufferUtils; +import com.jme3.util.Screenshots; +import java.awt.Canvas; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferStrategy; +import java.awt.image.BufferedImage; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AwtPanel extends Canvas implements SceneProcessor { + + private BufferedImage img; + private FrameBuffer fb; + private ByteBuffer byteBuf; + private boolean activeUpdates = true; + private RenderManager rm; + private ArrayList viewPorts = new ArrayList(); + + // Visibility/drawing vars + private BufferStrategy strategy; + private AffineTransformOp transformOp; + private CyclicBarrier visibleBarrier = new CyclicBarrier(2); + private AtomicBoolean visible = new AtomicBoolean(false); + private boolean glVisible = false; + + // Reshape vars + private int newWidth = 0; + private int newHeight = 0; + private CyclicBarrier reshapeBarrier = new CyclicBarrier(2); + private AtomicBoolean reshapeNeeded = new AtomicBoolean(false); + + public AwtPanel(boolean activeUpdates){ + this.activeUpdates = activeUpdates; + + setIgnoreRepaint(true); + addComponentListener(new ComponentAdapter(){ + @Override + public void componentResized(ComponentEvent e) { + newWidth = Math.max(getWidth(), 1); + newHeight = Math.max(getHeight(), 1); + reshapeNeeded.set(true); + + try { + reshapeBarrier.await(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } catch (BrokenBarrierException ex) { + ex.printStackTrace(); + } + } + }); + } + + @Override + public void addNotify(){ + super.addNotify(); + + try { + createBufferStrategy(2); + strategy = getBufferStrategy(); + visible.set(true); + visibleBarrier.await(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } catch (BrokenBarrierException ex) { + ex.printStackTrace(); + } + + requestFocusInWindow(); + } + + @Override + public void removeNotify(){ + try { + visible.set(false); + visibleBarrier.await(); + strategy = null; + } catch (InterruptedException ex) { + ex.printStackTrace(); + } catch (BrokenBarrierException ex) { + ex.printStackTrace(); + } + + super.removeNotify(); + } + + private void checkVisibility(){ + if (visible.get() != glVisible){ + try { + glVisible = visible.get(); + visibleBarrier.await(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } catch (BrokenBarrierException ex) { + ex.printStackTrace(); + } + } + } + + public void drawFrame(){ + checkVisibility(); + if (!glVisible) + return; + + if (strategy.contentsLost()){ + strategy.dispose(); + createBufferStrategy(2); + strategy = getBufferStrategy(); + System.out.println("BufferStrategy lost!"); + } + + Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics(); + g2d.drawImage(img, transformOp, 0, 0); + g2d.dispose(); + strategy.show(); + } + + public boolean isActiveUpdates() { + return activeUpdates; + } + + public void setActiveUpdates(boolean activeUpdates) { + this.activeUpdates = activeUpdates; + } + + public void attachTo(ViewPort ... vps){ + if (viewPorts.size() > 0){ + for (ViewPort vp : viewPorts){ + vp.setOutputFrameBuffer(null); + } + viewPorts.get(viewPorts.size()-1).removeProcessor(this); + } + + viewPorts.addAll(Arrays.asList(vps)); + viewPorts.get(viewPorts.size()-1).addProcessor(this); + } + + public void initialize(RenderManager rm, ViewPort vp) { + if (this.rm == null){ + // First time called in OGL thread + this.rm = rm; + reshapeInThread(1, 1); + } + } + + private void reshapeInThread(int width, int height) { + byteBuf = BufferUtils.ensureLargeEnough(byteBuf, width * height * 4); + fb = new FrameBuffer(width, height, 1); + fb.setDepthBuffer(Format.Depth); + fb.setColorBuffer(Format.RGB8); + + img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + AffineTransform tx = AffineTransform.getScaleInstance(1, -1); + tx.translate(0, -img.getHeight()); + transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + + for (ViewPort vp : viewPorts){ + vp.setOutputFrameBuffer(fb); + vp.getCamera().resize(width, height, true); + } + } + + public boolean isInitialized() { + return fb != null; + } + + public void preFrame(float tpf) { + } + + public void postQueue(RenderQueue rq) { + } + + public void postFrame(FrameBuffer out) { + if (out != fb) + throw new IllegalStateException("Why did you change the output framebuffer?"); + + if (reshapeNeeded.getAndSet(false)){ + reshapeInThread(newWidth, newHeight); + try { + reshapeBarrier.await(); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } catch (BrokenBarrierException ex) { + ex.printStackTrace(); + } + }else if (activeUpdates){ + byteBuf.clear(); + rm.getRenderer().readFrameBuffer(fb, byteBuf); + Screenshots.convertScreenShot2(byteBuf, img); + drawFrame(); + }else{ + checkVisibility(); + } + } + + public void reshape(ViewPort vp, int w, int h) { + } + + public void cleanup() { + } +} diff --git a/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java b/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java new file mode 100644 index 000000000..5fa0c8cd9 --- /dev/null +++ b/engine/src/desktop/com/jme3/system/awt/AwtPanelsContext.java @@ -0,0 +1,176 @@ +package com.jme3.system.awt; + +import com.jme3.input.JoyInput; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.TouchInput; +import com.jme3.input.awt.AwtKeyInput; +import com.jme3.input.awt.AwtMouseInput; +import com.jme3.renderer.Renderer; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeContext; +import com.jme3.system.JmeSystem; +import com.jme3.system.SystemListener; +import com.jme3.system.Timer; +import java.util.ArrayList; + +public class AwtPanelsContext implements JmeContext { + + protected JmeContext actualContext; + protected AppSettings settings = new AppSettings(true); + protected SystemListener listener; + protected ArrayList panels = new ArrayList(); + protected AwtPanel inputSource; + + protected AwtMouseInput mouseInput = new AwtMouseInput(); + protected AwtKeyInput keyInput = new AwtKeyInput(); + + private class AwtPanelsListener implements SystemListener { + + public void initialize() { + initInThread(); + } + + public void reshape(int width, int height) { + throw new IllegalStateException(); + } + + public void update() { + updateInThread(); + } + + public void requestClose(boolean esc) { + // shouldn't happen + throw new IllegalStateException(); + } + + public void gainFocus() { + // shouldn't happen + throw new IllegalStateException(); + } + + public void loseFocus() { + // shouldn't happen + throw new IllegalStateException(); + } + + public void handleError(String errorMsg, Throwable t) { + listener.handleError(errorMsg, t); + } + + public void destroy() { + destroyInThread(); + } + } + + public void setInputSource(AwtPanel panel){ + if (!panels.contains(panel)) + throw new IllegalArgumentException(); + + inputSource = panel; + mouseInput.setInputSource(panel); + keyInput.setInputSource(panel); + } + + public Type getType() { + return Type.OffscreenSurface; + } + + public void setSystemListener(SystemListener listener) { + this.listener = listener; + } + + public AppSettings getSettings() { + return settings; + } + + public Renderer getRenderer() { + return actualContext.getRenderer(); + } + + public MouseInput getMouseInput() { + return mouseInput; + } + + public KeyInput getKeyInput() { + return keyInput; + } + + public JoyInput getJoyInput() { + return null; + } + + public TouchInput getTouchInput() { + return null; + } + + public Timer getTimer() { + return actualContext.getTimer(); + } + + public boolean isCreated() { + return actualContext != null && actualContext.isCreated(); + } + + public boolean isRenderable() { + return actualContext != null && actualContext.isRenderable(); + } + + public AwtPanelsContext(){ + } + + public AwtPanel createPanel(boolean activeUpdates){ + AwtPanel panel = new AwtPanel(activeUpdates); + panels.add(panel); + return panel; + } + + private void initInThread(){ + listener.initialize(); + } + + private void updateInThread(){ + listener.update(); + } + + private void destroyInThread(){ + listener.destroy(); + } + + public void setSettings(AppSettings settings) { + settings.copyFrom(settings); + if (actualContext != null){ + actualContext.setSettings(settings); + } + } + + public void create(boolean waitFor) { + if (actualContext != null) + throw new IllegalStateException("Already created"); + + actualContext = JmeSystem.newContext(settings, Type.OffscreenSurface); + actualContext.setSystemListener(new AwtPanelsListener()); + actualContext.create(waitFor); + } + + public void destroy(boolean waitFor) { + if (actualContext == null) + throw new IllegalStateException("Not created"); + + // destroy parent context + actualContext.destroy(waitFor); + } + + public void setTitle(String title) { + // not relevant, ignore + } + + public void setAutoFlushFrames(boolean enabled) { + // not relevant, ignore + } + + public void restart() { + // only relevant if changing pixel format. + } + +} diff --git a/engine/src/desktop/com/jme3/util/Screenshots.java b/engine/src/desktop/com/jme3/util/Screenshots.java index 5d75f740c..f2412e5fd 100644 --- a/engine/src/desktop/com/jme3/util/Screenshots.java +++ b/engine/src/desktop/com/jme3/util/Screenshots.java @@ -2,10 +2,36 @@ package com.jme3.util; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; import java.awt.image.WritableRaster; import java.nio.ByteBuffer; public final class Screenshots { + + public static void convertScreenShot2(ByteBuffer bgraBuf, BufferedImage out){ + WritableRaster wr = out.getRaster(); + DataBufferInt db = (DataBufferInt) wr.getDataBuffer(); + + int[] cpuArray = db.getData(); + + bgraBuf.clear(); + bgraBuf.asIntBuffer().get(cpuArray); + +// int width = wr.getWidth(); +// int height = wr.getHeight(); +// +// // flip the components the way AWT likes them +// for (int y = 0; y < height / 2; y++){ +// for (int x = 0; x < width; x++){ +// int inPtr = (y * width + x); +// int outPtr = ((height-y-1) * width + x); +// int pixel = cpuArray[inPtr]; +// cpuArray[inPtr] = cpuArray[outPtr]; +// cpuArray[outPtr] = pixel; +// } +// } + } + public static void convertScreenShot(ByteBuffer bgraBuf, BufferedImage out){ WritableRaster wr = out.getRaster(); DataBufferByte db = (DataBufferByte) wr.getDataBuffer(); diff --git a/engine/src/test/jme3test/awt/TestAwtPanels.java b/engine/src/test/jme3test/awt/TestAwtPanels.java new file mode 100644 index 000000000..115754c47 --- /dev/null +++ b/engine/src/test/jme3test/awt/TestAwtPanels.java @@ -0,0 +1,86 @@ +package jme3test.awt; + +import com.jme3.app.SimpleApplication; +import com.jme3.material.Material; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; +import com.jme3.system.AppSettings; +import com.jme3.system.awt.AwtPanel; +import com.jme3.system.awt.AwtPanelsContext; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +public class TestAwtPanels extends SimpleApplication { + + private static TestAwtPanels app; + private static AwtPanel panel, panel2; + private static int panelsClosed = 0; + + private static void createWindowForPanel(AwtPanel panel, int location){ + JFrame frame = new JFrame("Render Display " + location); + frame.getContentPane().setLayout(new BorderLayout()); + frame.getContentPane().add(panel, BorderLayout.CENTER); + frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + if (++panelsClosed == 2){ + app.stop(); + } + } + }); + frame.pack(); + frame.setLocation(location, Toolkit.getDefaultToolkit().getScreenSize().height - 400); + frame.setVisible(true); + } + + public static void main(String[] args){ + Logger.getLogger("com.jme3").setLevel(Level.WARNING); + + app = new TestAwtPanels(); + app.setShowSettings(false); + AppSettings settings = new AppSettings(true); + settings.setCustomRenderer(AwtPanelsContext.class); + app.setSettings(settings); + app.start(); + + SwingUtilities.invokeLater(new Runnable(){ + public void run(){ + final AwtPanelsContext ctx = (AwtPanelsContext) app.getContext(); + panel = ctx.createPanel(true); + panel.setPreferredSize(new Dimension(400, 300)); + ctx.setInputSource(panel); + + panel2 = ctx.createPanel(true); + panel2.setPreferredSize(new Dimension(400, 300)); + + createWindowForPanel(panel, 300); + createWindowForPanel(panel2, 700); + } + }); + } + + @Override + public void simpleInitApp() { + flyCam.setDragToRotate(true); + + Box b = new Box(Vector3f.ZERO, 1, 1, 1); + Geometry geom = new Geometry("Box", b); + Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setTexture("ColorMap", assetManager.loadTexture("Interface/Logo/Monkey.jpg")); + geom.setMaterial(mat); + rootNode.attachChild(geom); + + panel.attachTo(viewPort); + guiViewPort.setClearFlags(true, true, true); + panel2.attachTo(guiViewPort); + } +} diff --git a/engine/src/test/jme3test/batching/TestCubeCluster.java b/engine/src/test/jme3test/batching/TestCubeCluster.java index cf37870cd..1abb4b933 100644 --- a/engine/src/test/jme3test/batching/TestCubeCluster.java +++ b/engine/src/test/jme3test/batching/TestCubeCluster.java @@ -11,30 +11,22 @@ import com.jme3.input.controls.ActionListener; import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Box; import com.jme3.system.AppSettings; import com.jme3.input.controls.KeyTrigger; import java.util.ArrayList; import java.util.Random; -import com.jme3.light.DirectionalLight; import com.jme3.math.FastMath; -import com.jme3.math.Matrix3f; import com.jme3.math.Quaternion; import com.jme3.post.FilterPostProcessor; import com.jme3.post.filters.BloomFilter; -import com.jme3.post.ssao.SSAOFilter; import com.jme3.scene.BatchedGeometry; import com.jme3.scene.GeometryBatch; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.control.AbstractControl; -import com.jme3.scene.control.Control; import com.jme3.scene.debug.Arrow; import java.util.List; -import jme3test.post.SSAOUI; public class TestCubeCluster extends SimpleApplication {