diff --git a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java b/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java index 3b18bce1a2..b17b22aaf9 100644 --- a/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java +++ b/jme3-core/src/main/java/com/jme3/texture/FrameBuffer.java @@ -363,7 +363,8 @@ protected FrameBuffer(FrameBuffer src) { * * @param format The format to use for the depth buffer. * @throws IllegalArgumentException If format is not a depth format. - * @deprecated Use setDepthTarget + * @deprecated Use + * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferBufferTarget)} */ @Deprecated public void setDepthBuffer(Image.Format format) { @@ -656,7 +657,8 @@ public void addColorTexture(TextureCubeMap tex, TextureCubeMap.Face face) { * Set the depth texture to use for this framebuffer. * * @param tex The color texture to set. - * @deprecated Use setDepthTarget + * @deprecated Use + * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferTextureTarget)} */ @Deprecated public void setDepthTexture(Texture2D tex) { @@ -677,7 +679,8 @@ public void setDepthTexture(Texture2D tex) { * * @param tex the TextureArray to apply * @param layer (default=-1) - * @deprecated Use setDepthTarget + * @deprecated Use + * {@link #setDepthTarget(com.jme3.texture.FrameBuffer.FrameBufferTextureTarget)} */ @Deprecated public void setDepthTexture(TextureArray tex, int layer) { diff --git a/jme3-desktop/src/main/java/com/jme3/input/AWTKeyInput.java b/jme3-desktop/src/main/java/com/jme3/input/AWTKeyInput.java index 2867921ef0..b4c5059a07 100644 --- a/jme3-desktop/src/main/java/com/jme3/input/AWTKeyInput.java +++ b/jme3-desktop/src/main/java/com/jme3/input/AWTKeyInput.java @@ -31,18 +31,17 @@ */ package com.jme3.input; +import com.jme3.input.event.KeyInputEvent; import java.awt.Component; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; +import java.util.Deque; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; -import com.jme3.input.KeyInput; -import com.jme3.input.event.KeyInputEvent; import com.jme3.system.AWTContext; - /** * The implementation of the {@link KeyInput} dedicated to AWT {@link Component component}. *

@@ -166,11 +165,11 @@ public class AWTKeyInput extends AWTInput implements KeyInput, KeyListener{ KEY_CODE_TO_JME.put(KeyEvent.VK_META, KEY_RCONTROL); } - private final LinkedList keyInputEvents; + private final Deque keyInputEvents; public AWTKeyInput(AWTContext context) { super(context); - keyInputEvents = new LinkedList(); + keyInputEvents = new LinkedList<>(); } @Override diff --git a/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java b/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java index 72b75a5d38..4caef0ca12 100644 --- a/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java +++ b/jme3-desktop/src/main/java/com/jme3/input/AWTMouseInput.java @@ -31,22 +31,21 @@ */ package com.jme3.input; +import com.jme3.cursors.plugins.JmeCursor; +import com.jme3.input.event.MouseButtonEvent; +import com.jme3.input.event.MouseMotionEvent; import java.awt.Component; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; +import java.util.Deque; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; -import com.jme3.cursors.plugins.JmeCursor; -import com.jme3.input.MouseInput; -import com.jme3.input.event.MouseButtonEvent; -import com.jme3.input.event.MouseMotionEvent; import com.jme3.system.AWTContext; - /** * The implementation of the {@link MouseInput} dedicated to AWT {@link Component component}. *

@@ -70,9 +69,9 @@ public class AWTMouseInput extends AWTInput implements MouseInput, MouseListener */ private static final int WHEEL_SCALE = 10; - private final LinkedList mouseMotionEvents; + private final Deque mouseMotionEvents; - private final LinkedList mouseButtonEvents; + private final Deque mouseButtonEvents; private int mouseX; private int mouseY; @@ -80,8 +79,8 @@ public class AWTMouseInput extends AWTInput implements MouseInput, MouseListener public AWTMouseInput(AWTContext context) { super(context); - mouseMotionEvents = new LinkedList(); - mouseButtonEvents = new LinkedList(); + mouseMotionEvents = new LinkedList<>(); + mouseButtonEvents = new LinkedList<>(); } @Override diff --git a/jme3-desktop/src/main/java/com/jme3/input/awt/AwtKeyInput.java b/jme3-desktop/src/main/java/com/jme3/input/awt/AwtKeyInput.java index 6c84aaae22..1ce0aeb694 100644 --- a/jme3-desktop/src/main/java/com/jme3/input/awt/AwtKeyInput.java +++ b/jme3-desktop/src/main/java/com/jme3/input/awt/AwtKeyInput.java @@ -39,6 +39,7 @@ import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.BitSet; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -53,10 +54,10 @@ public class AwtKeyInput implements KeyInput, KeyListener { private static final Logger logger = Logger.getLogger(AwtKeyInput.class.getName()); - private final ArrayList eventQueue = new ArrayList<>(); + private final List eventQueue = new ArrayList<>(); private RawInputListener listener; private Component component; - private BitSet keyStateSet = new BitSet(0xFF); + private final BitSet keyStateSet = new BitSet(0xFF); public AwtKeyInput(){ } diff --git a/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java b/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java index 4e4bc2248b..80101df89c 100644 --- a/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java +++ b/jme3-desktop/src/main/java/com/jme3/input/awt/AwtMouseInput.java @@ -64,8 +64,8 @@ public class AwtMouseInput implements MouseInput, MouseListener, MouseWheelListe private Component component; - private final ArrayList eventQueue = new ArrayList<>(); - private final ArrayList eventQueueCopy = new ArrayList<>(); + private final java.util.List eventQueue = new ArrayList<>(); + private final java.util.List eventQueueCopy = new ArrayList<>(); private int lastEventX; private int lastEventY; diff --git a/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java b/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java index 3125e1f751..a234775bdb 100644 --- a/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java +++ b/jme3-desktop/src/main/java/com/jme3/system/awt/AwtPanel.java @@ -37,7 +37,6 @@ 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.*; @@ -57,6 +56,8 @@ public class AwtPanel extends Canvas implements SceneProcessor { + private static final Logger logger = Logger.getLogger(AwtPanel.class.getName()); + private boolean attachAsMain = false; private BufferedImage img; @@ -66,19 +67,19 @@ public class AwtPanel extends Canvas implements SceneProcessor { private IntBuffer intBuf; private RenderManager rm; private PaintMode paintMode; - private ArrayList viewPorts = new ArrayList<>(); + private final java.util.List viewPorts = new ArrayList<>(); // Visibility/drawing vars private BufferStrategy strategy; private AffineTransformOp transformOp; - private AtomicBoolean hasNativePeer = new AtomicBoolean(false); - private AtomicBoolean showing = new AtomicBoolean(false); - private AtomicBoolean repaintRequest = new AtomicBoolean(false); + private final AtomicBoolean hasNativePeer = new AtomicBoolean(false); + private final AtomicBoolean showing = new AtomicBoolean(false); + private final AtomicBoolean repaintRequest = new AtomicBoolean(false); // Reshape vars private int newWidth = 1; private int newHeight = 1; - private AtomicBoolean reshapeNeeded = new AtomicBoolean(false); + private final AtomicBoolean reshapeNeeded = new AtomicBoolean(false); private final Object lock = new Object(); public AwtPanel(PaintMode paintMode) { @@ -180,7 +181,7 @@ public void drawFrameInThread() { BufferCapabilities.FlipContents.UNDEFINED) ); } catch (AWTException ex) { - ex.printStackTrace(); + logger.log(Level.WARNING, "Failed to create buffer strategy!", ex); } strategy = getBufferStrategy(); } @@ -190,7 +191,7 @@ public void drawFrameInThread() { do { Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics(); if (g2d == null) { - Logger.getLogger(AwtPanel.class.getName()).log(Level.WARNING, "OGL: DrawGraphics was null."); + logger.log(Level.WARNING, "OGL: DrawGraphics was null."); return; } @@ -210,7 +211,7 @@ public boolean isActiveDrawing() { } public void attachTo(boolean overrideMainFramebuffer, ViewPort... vps) { - if (viewPorts.size() > 0) { + if (!viewPorts.isEmpty()) { for (ViewPort vp : viewPorts) { vp.setOutputFrameBuffer(null); } @@ -242,8 +243,8 @@ private void reshapeInThread(int width, int height) { } fb = new FrameBuffer(width, height, 1); - fb.setDepthBuffer(Format.Depth); - fb.setColorBuffer(Format.RGB8); + fb.setDepthTarget(FrameBuffer.FrameBufferTarget.newTarget(com.jme3.texture.Image.Format.Depth)); + fb.addColorTarget(FrameBuffer.FrameBufferTarget.newTarget(com.jme3.texture.Image.Format.RGB8)); fb.setSrgb(srgb); if (attachAsMain) { diff --git a/jme3-lwjgl3/build.gradle b/jme3-lwjgl3/build.gradle index c3cb630545..f035ac7229 100644 --- a/jme3-lwjgl3/build.gradle +++ b/jme3-lwjgl3/build.gradle @@ -1,5 +1,6 @@ dependencies { api project(':jme3-core') + api project(':jme3-desktop') api "org.lwjgl:lwjgl:${lwjgl3Version}" api "org.lwjgl:lwjgl-glfw:${lwjgl3Version}" diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java new file mode 100644 index 0000000000..0614cba16c --- /dev/null +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2009-2022 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.system.lwjgl; + +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.awt.AwtKeyInput; +import com.jme3.input.awt.AwtMouseInput; +import com.jme3.system.AppSettings; +import com.jme3.system.JmeCanvasContext; +import com.jme3.system.JmeContext.Type; +import com.jme3.texture.FrameBuffer; +import com.jme3.texture.FrameBuffer.FrameBufferTarget; +import com.jme3.texture.Image; +import com.jme3.util.BufferUtils; +import com.jme3.util.Screenshots; +import java.awt.AWTException; +import java.awt.BufferCapabilities; +import java.awt.Canvas; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.ImageCapabilities; +import java.awt.RenderingHints; +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.nio.IntBuffer; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class LwjglCanvas extends LwjglWindow implements JmeCanvasContext, Runnable { + + private static final Logger logger = Logger.getLogger(LwjglCanvas.class.getName()); + + private final Canvas canvas; + + private BufferedImage img; + private FrameBuffer fb; + + private ByteBuffer byteBuf; + private IntBuffer intBuf; + + private BufferStrategy strategy; + private AffineTransformOp transformOp; + private final AtomicBoolean hasNativePeer = new AtomicBoolean(false); + private final AtomicBoolean showing = new AtomicBoolean(false); + + private int width = 1; + private int height = 1; + private AtomicBoolean needResize = new AtomicBoolean(false); + private final Object lock = new Object(); + + private AwtKeyInput keyInput; + private AwtMouseInput mouseInput; + + public LwjglCanvas() { + super(Type.Canvas); + + canvas = new Canvas() { + @Override + public void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + synchronized (lock) { + g2d.drawImage(img, transformOp, 0, 0); + } + } + + @Override + public void addNotify() { + super.addNotify(); + + synchronized (lock) { + hasNativePeer.set(true); + } + + requestFocusInWindow(); + } + + @Override + public void removeNotify() { + synchronized (lock) { + hasNativePeer.set(false); + } + + super.removeNotify(); + } + }; + canvas.addComponentListener(new ComponentAdapter() { + + @Override + public void componentResized(ComponentEvent e) { + synchronized (lock) { + int newWidth = Math.max(canvas.getWidth(), 1); + int newHeight = Math.max(canvas.getHeight(), 1); + if (width != newWidth || height != newHeight) { + width = newWidth; + height = newHeight; + needResize.set(true); + } + } + } + }); + canvas.setFocusable(true); + canvas.setIgnoreRepaint(true); + } + + @Override + public Canvas getCanvas() { + return canvas; + } + + @Override + protected void showWindow() { + } + + @Override + protected void setWindowIcon(final AppSettings settings) { + } + + @Override + public void setTitle(String title) { + } + + @Override + public KeyInput getKeyInput() { + if (keyInput == null) { + keyInput = new AwtKeyInput(); + keyInput.setInputSource(canvas); + } + + return keyInput; + } + + @Override + public MouseInput getMouseInput() { + if (mouseInput == null) { + mouseInput = new AwtMouseInput(); + mouseInput.setInputSource(canvas); + } + + return mouseInput; + } + + public boolean checkVisibilityState() { + if (!hasNativePeer.get()) { + synchronized (lock) { + if (strategy != null) { + strategy.dispose(); + strategy = null; + } + } + return false; + } + + boolean currentShowing = canvas.isShowing(); + showing.set(currentShowing); + return currentShowing; + } + + @Override + protected void destroyContext() { + synchronized (lock) { + destroyFrameBuffer(); + img = null; + byteBuf = null; + intBuf = null; + } + + super.destroyContext(); + } + + @Override + protected void createContext(AppSettings settings) { + super.createContext(settings); + + if (renderer != null) { + createFrameBuffer(width, height); + } + } + + @Override + protected void runLoop() { + if (needResize.get()) { + needResize.set(false); + listener.reshape(width, height); + createFrameBuffer(width, height); + } + + if (!checkVisibilityState()) { + return; + } + + super.runLoop(); + + drawFrameInThread(); + } + + public void drawFrameInThread() { + + // Convert screenshot + byteBuf.clear(); + renderer.readFrameBuffer(fb, byteBuf); + Screenshots.convertScreenShot2(intBuf, img); + + synchronized (lock) { + // All operations on strategy should be synchronized (?) + if (strategy == null) { + try { + canvas.createBufferStrategy(1, + new BufferCapabilities( + new ImageCapabilities(true), + new ImageCapabilities(true), + BufferCapabilities.FlipContents.UNDEFINED) + ); + } catch (AWTException ex) { + logger.log(Level.SEVERE, "Failed to create buffer strategy!", ex); + } + strategy = canvas.getBufferStrategy(); + } + + // Draw screenshot + do { + do { + Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics(); + if (g2d == null) { + logger.log(Level.WARNING, "OGL: DrawGraphics was null."); + return; + } + + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + + g2d.drawImage(img, transformOp, 0, 0); + g2d.dispose(); + strategy.show(); + } while (strategy.contentsRestored()); + } while (strategy.contentsLost()); + } + } + + private void createFrameBuffer(int width, int height) { + byteBuf = BufferUtils.ensureLargeEnough(byteBuf, width * height * 4); + intBuf = byteBuf.asIntBuffer(); + + destroyFrameBuffer(); + + fb = new FrameBuffer(width, height, settings.getSamples()); + fb.setDepthTarget(FrameBufferTarget.newTarget(Image.Format.Depth)); + fb.addColorTarget(FrameBufferTarget.newTarget(Image.Format.RGB8)); + fb.setSrgb(settings.isGammaCorrection()); + + renderer.setMainFrameBufferOverride(fb); + + img = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR); + + AffineTransform tx = AffineTransform.getScaleInstance(1, -1); + tx.translate(0, -img.getHeight()); + transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + } + + public void destroyFrameBuffer() { + if (fb != null) { + fb.dispose(); + fb = null; + } + } +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java index 96895b2557..6dc2d71851 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglOffscreenBuffer.java @@ -31,6 +31,12 @@ */ package com.jme3.system.lwjgl; +import com.jme3.input.JoyInput; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.TouchInput; +import com.jme3.input.dummy.DummyKeyInput; +import com.jme3.input.dummy.DummyMouseInput; import com.jme3.system.AppSettings; import com.jme3.system.JmeContext; @@ -39,6 +45,9 @@ */ public class LwjglOffscreenBuffer extends LwjglWindow { + private KeyInput keyInput; + private MouseInput mouseInput; + public LwjglOffscreenBuffer() { super(JmeContext.Type.OffscreenSurface); } @@ -50,4 +59,36 @@ protected void showWindow() { @Override protected void setWindowIcon(final AppSettings settings) { } + + @Override + public void setTitle(String title) { + } + + @Override + public MouseInput getMouseInput() { + if (mouseInput == null) { + mouseInput = new DummyMouseInput(); + } + + return mouseInput; + } + + @Override + public KeyInput getKeyInput() { + if (keyInput == null) { + keyInput = new DummyKeyInput(); + } + + return keyInput; + } + + @Override + public JoyInput getJoyInput() { + return null; + } + + @Override + public TouchInput getTouchInput() { + return null; + } } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java index 9962b7218d..1fa1ff0b51 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java @@ -46,15 +46,6 @@ import com.jme3.system.NanoTimer; import com.jme3.util.BufferUtils; import com.jme3.util.SafeArrayList; -import org.lwjgl.Version; -import org.lwjgl.glfw.GLFWErrorCallback; -import org.lwjgl.glfw.GLFWFramebufferSizeCallback; -import org.lwjgl.glfw.GLFWImage; -import org.lwjgl.glfw.GLFWVidMode; -import org.lwjgl.glfw.GLFWWindowFocusCallback; -import org.lwjgl.glfw.GLFWWindowSizeCallback; -import org.lwjgl.system.Platform; - import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.nio.ByteBuffer; @@ -64,10 +55,17 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; - +import org.lwjgl.Version; import static org.lwjgl.glfw.GLFW.*; +import org.lwjgl.glfw.GLFWErrorCallback; +import org.lwjgl.glfw.GLFWFramebufferSizeCallback; +import org.lwjgl.glfw.GLFWImage; +import org.lwjgl.glfw.GLFWVidMode; +import org.lwjgl.glfw.GLFWWindowFocusCallback; +import org.lwjgl.glfw.GLFWWindowSizeCallback; import static org.lwjgl.opengl.GL11.GL_FALSE; import static org.lwjgl.system.MemoryUtil.NULL; +import org.lwjgl.system.Platform; /** * A wrapper class over the GLFW framework in LWJGL 3. @@ -149,8 +147,8 @@ public abstract class LwjglWindow extends LwjglContext implements Runnable { protected boolean allowSwapBuffers = false; // temp variables used for glfw calls - private int width[] = new int[1]; - private int height[] = new int[1]; + private final int width[] = new int[1]; + private final int height[] = new int[1]; // state maintained by updateSizes() private int oldFramebufferWidth; @@ -291,6 +289,8 @@ public void invoke(int error, long description) { requestWidth = videoMode.width(); requestHeight = videoMode.height(); } + oldFramebufferHeight = requestHeight; + oldFramebufferWidth = requestWidth; window = glfwCreateWindow(requestWidth, requestHeight, settings.getTitle(), monitor, NULL); if (window == NULL) { throw new RuntimeException("Failed to create the GLFW window");